Skip to content

Commit d0fe631

Browse files
Feat(vscode): Add the Table Diff view in the extension
1 parent 317df56 commit d0fe631

File tree

20 files changed

+2349
-13
lines changed

20 files changed

+2349
-13
lines changed

sqlmesh/lsp/api.py

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
CustomMethodRequestBaseClass,
1414
CustomMethodResponseBaseClass,
1515
)
16-
from web.server.models import LineageColumn, Model
16+
from web.server.models import LineageColumn, Model, TableDiff
1717

1818
API_FEATURE = "sqlmesh/api"
1919

@@ -25,7 +25,7 @@ class ApiRequest(CustomMethodRequestBaseClass):
2525
"""
2626

2727
requestId: str
28-
url: str
28+
endpoint: str
2929
method: t.Optional[str] = "GET"
3030
params: t.Optional[t.Dict[str, t.Any]] = None
3131
body: t.Optional[t.Dict[str, t.Any]] = None
@@ -74,3 +74,11 @@ class ApiResponseGetColumnLineage(BaseAPIResponse):
7474
"""
7575

7676
data: t.Dict[str, t.Dict[str, LineageColumn]]
77+
78+
79+
class ApiResponseGetTableDiff(BaseAPIResponse):
80+
"""
81+
Response from the SQLMesh API for the get_table_diff endpoint.
82+
"""
83+
84+
data: t.Optional[TableDiff]

sqlmesh/lsp/custom.py

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -143,3 +143,35 @@ class FormatProjectResponse(CustomMethodResponseBaseClass):
143143
"""
144144

145145
pass
146+
147+
148+
GET_ENVIRONMENTS_FEATURE = "sqlmesh/get_environments"
149+
150+
151+
class GetEnvironmentsRequest(CustomMethodRequestBaseClass):
152+
"""
153+
Request to get all environments in the current project.
154+
"""
155+
156+
pass
157+
158+
159+
class EnvironmentInfo(PydanticModel):
160+
"""
161+
Information about an environment.
162+
"""
163+
164+
name: str
165+
snapshots: t.List[str]
166+
start_at: str
167+
plan_id: str
168+
169+
170+
class GetEnvironmentsResponse(CustomMethodResponseBaseClass):
171+
"""
172+
Response containing all environments in the current project.
173+
"""
174+
175+
environments: t.Dict[str, EnvironmentInfo]
176+
pinned_environments: t.Set[str]
177+
default_target_environment: str

sqlmesh/lsp/main.py

Lines changed: 83 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,12 +15,15 @@
1515
from pygls.server import LanguageServer
1616
from sqlmesh._version import __version__
1717
from sqlmesh.core.context import Context
18+
from sqlmesh.core import constants as c
19+
from sqlmesh.utils.date import to_timestamp
1820
from sqlmesh.lsp.api import (
1921
API_FEATURE,
2022
ApiRequest,
2123
ApiResponseGetColumnLineage,
2224
ApiResponseGetLineage,
2325
ApiResponseGetModels,
26+
ApiResponseGetTableDiff,
2427
)
2528
from sqlmesh.lsp.completions import get_sql_completions
2629
from sqlmesh.lsp.context import (
@@ -33,6 +36,7 @@
3336
RENDER_MODEL_FEATURE,
3437
SUPPORTED_METHODS_FEATURE,
3538
FORMAT_PROJECT_FEATURE,
39+
GET_ENVIRONMENTS_FEATURE,
3640
AllModelsRequest,
3741
AllModelsResponse,
3842
AllModelsForRenderRequest,
@@ -45,6 +49,9 @@
4549
FormatProjectRequest,
4650
FormatProjectResponse,
4751
CustomMethod,
52+
GetEnvironmentsRequest,
53+
GetEnvironmentsResponse,
54+
EnvironmentInfo,
4855
)
4956
from sqlmesh.lsp.hints import get_hints
5057
from sqlmesh.lsp.reference import (
@@ -58,6 +65,7 @@
5865
from sqlmesh.lsp.uri import URI
5966
from web.server.api.endpoints.lineage import column_lineage, model_lineage
6067
from web.server.api.endpoints.models import get_models
68+
from web.server.api.endpoints.table_diff import get_table_diff
6169
from typing import Union
6270
from dataclasses import dataclass
6371

@@ -121,6 +129,7 @@ def __init__(
121129
API_FEATURE: self._custom_api,
122130
SUPPORTED_METHODS_FEATURE: self._custom_supported_methods,
123131
FORMAT_PROJECT_FEATURE: self._custom_format_project,
132+
GET_ENVIRONMENTS_FEATURE: self._custom_get_environments,
124133
}
125134

126135
# Register LSP features (e.g., formatting, hover, etc.)
@@ -169,13 +178,67 @@ def _custom_format_project(
169178
ls.log_trace(f"Error formatting project: {e}")
170179
return FormatProjectResponse()
171180

181+
def _custom_get_environments(
182+
self, ls: LanguageServer, params: GetEnvironmentsRequest
183+
) -> GetEnvironmentsResponse:
184+
"""Get all environments in the current project."""
185+
try:
186+
context = self._context_get_or_load()
187+
environments = {}
188+
189+
# Get environments from state
190+
for env in context.context.state_reader.get_environments():
191+
environments[env.name] = EnvironmentInfo(
192+
name=env.name,
193+
snapshots=[s.fingerprint.to_identifier() for s in env.snapshots],
194+
start_at=str(to_timestamp(env.start_at)),
195+
plan_id=env.plan_id or "",
196+
)
197+
198+
# Add prod if not present (mirroring web/server/api/endpoints/environments.py)
199+
if c.PROD not in environments:
200+
environments[c.PROD] = EnvironmentInfo(
201+
name=c.PROD,
202+
snapshots=[],
203+
start_at=str(to_timestamp(c.EPOCH)),
204+
plan_id="",
205+
)
206+
207+
# Add default target environment if not present
208+
if context.context.config.default_target_environment not in environments:
209+
environments[context.context.config.default_target_environment] = EnvironmentInfo(
210+
name=context.context.config.default_target_environment,
211+
snapshots=[],
212+
start_at=str(to_timestamp(c.EPOCH)),
213+
plan_id="",
214+
)
215+
216+
return GetEnvironmentsResponse(
217+
environments=environments,
218+
pinned_environments=context.context.config.pinned_environments,
219+
default_target_environment=context.context.config.default_target_environment,
220+
)
221+
except Exception as e:
222+
ls.log_trace(f"Error getting environments: {e}")
223+
return GetEnvironmentsResponse(
224+
response_error=str(e),
225+
environments={},
226+
pinned_environments=set(),
227+
default_target_environment="",
228+
)
229+
172230
def _custom_api(
173231
self, ls: LanguageServer, request: ApiRequest
174-
) -> t.Union[ApiResponseGetModels, ApiResponseGetColumnLineage, ApiResponseGetLineage]:
232+
) -> t.Union[
233+
ApiResponseGetModels,
234+
ApiResponseGetColumnLineage,
235+
ApiResponseGetLineage,
236+
ApiResponseGetTableDiff,
237+
]:
175238
ls.log_trace(f"API request: {request}")
176239
context = self._context_get_or_load()
177240

178-
parsed_url = urllib.parse.urlparse(request.url)
241+
parsed_url = urllib.parse.urlparse(request.endpoint)
179242
path_parts = parsed_url.path.strip("/").split("/")
180243

181244
if request.method == "GET":
@@ -203,7 +266,24 @@ def _custom_api(
203266
)
204267
return ApiResponseGetColumnLineage(data=column_lineage_response)
205268

206-
raise NotImplementedError(f"API request not implemented: {request.url}")
269+
if path_parts[:2] == ["api", "table_diff"]:
270+
# /api/table_diff
271+
params = request.params
272+
table_diff_result = get_table_diff(
273+
source=getattr(params, "source", "") if params else "",
274+
target=getattr(params, "target", "") if params else "",
275+
on=getattr(params, "on", None) if params else None,
276+
model_or_snapshot=getattr(params, "model_or_snapshot", None)
277+
if params
278+
else None,
279+
where=getattr(params, "where", None) if params else None,
280+
temp_schema=getattr(params, "temp_schema", None) if params else None,
281+
limit=getattr(params, "limit", 20) if params else 20,
282+
context=context.context,
283+
)
284+
return ApiResponseGetTableDiff(data=table_diff_result)
285+
286+
raise NotImplementedError(f"API request not implemented: {request.endpoint}")
207287

208288
def _custom_supported_methods(
209289
self, ls: LanguageServer, params: SupportedMethodsRequest

vscode/bus/src/callbacks.ts

Lines changed: 44 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,13 +51,56 @@ export type RPCMethods = {
5151
}
5252
api_query: {
5353
params: {
54-
url: string
54+
endpoint: string
5555
method: string
5656
params: any
5757
body: any
5858
}
5959
result: any
6060
}
61+
get_selected_model: {
62+
params: {}
63+
result: {
64+
selectedModel?: any
65+
}
66+
}
67+
get_all_models: {
68+
params: {}
69+
result: {
70+
ok: boolean
71+
models?: any[]
72+
error?: string
73+
}
74+
}
75+
set_selected_model: {
76+
params: {
77+
model: any
78+
}
79+
result: {
80+
ok: boolean
81+
selectedModel?: any
82+
}
83+
}
84+
get_environments: {
85+
params: {}
86+
result: {
87+
ok: boolean
88+
environments?: Record<string, any>
89+
error?: string
90+
}
91+
}
92+
run_table_diff: {
93+
params: {
94+
sourceModel: string
95+
sourceEnvironment: string
96+
targetEnvironment: string
97+
}
98+
result: {
99+
ok: boolean
100+
data?: any
101+
error?: string
102+
}
103+
}
61104
} & RPCMethodsShape
62105

63106
export type RPCRequest = {
Lines changed: 3 additions & 0 deletions
Loading

vscode/extension/package.json

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,8 @@
5252
{
5353
"id": "sqlmesh.lineage",
5454
"name": "",
55-
"type": "webview"
55+
"type": "webview",
56+
"icon": "./assets/images/dag.svg"
5657
}
5758
]
5859
},
@@ -103,6 +104,12 @@
103104
"command": "sqlmesh.stop",
104105
"title": "SQLMesh: Stop Server",
105106
"description": "SQLMesh"
107+
},
108+
{
109+
"command": "sqlmesh.showTableDiff",
110+
"title": "SQLMesh: Show Table Diff",
111+
"description": "SQLMesh",
112+
"icon": "$(diff)"
106113
}
107114
],
108115
"menus": {
@@ -111,6 +118,11 @@
111118
"command": "sqlmesh.renderModel",
112119
"when": "resourceExtname == .sql",
113120
"group": "navigation"
121+
},
122+
{
123+
"command": "sqlmesh.showTableDiff",
124+
"when": "resourceExtname == .sql",
125+
"group": "navigation"
114126
}
115127
]
116128
}

0 commit comments

Comments
 (0)