Skip to content

Commit 7252fe3

Browse files
authored
Link bundle resources to their source definitions (#1598)
## Changes WIP: need to update and merge this CLI PR databricks/cli#2189 We execute summary with `--include-locations` to get the source locations of the resources. Jobs, Tasks, Pipelines, or Unknown resources now can have an icon/context-menu that opens their location. https://github.com/user-attachments/assets/ee38c4df-93be-48b0-b818-6667e30a80fe <img width="524" alt="Screenshot 2025-03-06 at 13 39 46" src="https://github.com/user-attachments/assets/c2a447f0-d6f0-4163-aab5-2caf52e78c21" /> <img width="582" alt="Screenshot 2025-03-06 at 13 39 54" src="https://github.com/user-attachments/assets/c6f5282e-850c-455d-84ab-af4faf0af30f" /> <img width="476" alt="Screenshot 2025-03-06 at 13 40 10" src="https://github.com/user-attachments/assets/edb899f6-dabf-4109-8b3a-9c0a5bae4c1b" /> ## Tests A few unit tests for now, will add an integration test in a follow up.
1 parent f0ba4b4 commit 7252fe3

File tree

14 files changed

+295
-19
lines changed

14 files changed

+295
-19
lines changed

packages/databricks-vscode/package.json

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -166,6 +166,12 @@
166166
"icon": "$(link-external)",
167167
"category": "Databricks"
168168
},
169+
{
170+
"command": "databricks.utils.goToDefinition",
171+
"title": "Go to definition",
172+
"icon": "$(go-to-file)",
173+
"category": "Databricks"
174+
},
169175
{
170176
"command": "databricks.wsfs.refresh",
171177
"title": "Refresh workspace filesystem view",
@@ -580,6 +586,16 @@
580586
"when": "viewItem =~ /^databricks.*\\.(has-url).*$/ && databricks.context.bundle.deploymentState == idle",
581587
"group": "navigation_2@0"
582588
},
589+
{
590+
"command": "databricks.utils.goToDefinition",
591+
"when": "viewItem =~ /^databricks.*\\.(has-source-location).*$/",
592+
"group": "inline@1"
593+
},
594+
{
595+
"command": "databricks.utils.goToDefinition",
596+
"when": "viewItem =~ /^databricks.*\\.(has-source-location).*$/",
597+
"group": "navigation_2@0"
598+
},
583599
{
584600
"command": "databricks.bundle.selectActiveProjectFolder",
585601
"when": "viewItem =~ /^databricks.configuration.activeProjectFolder/ && databricks.context.bundle.deploymentState == idle",
@@ -822,6 +838,10 @@
822838
"command": "databricks.utils.openExternal",
823839
"when": "false"
824840
},
841+
{
842+
"command": "databricks.utils.goToDefinition",
843+
"when": "false"
844+
},
825845
{
826846
"command": "databricks.call",
827847
"when": "false"

packages/databricks-vscode/src/bundle/models/BundleRemoteStateModel.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,13 @@ export type BundleRemoteState = BundleTarget & {
2525
};
2626
};
2727
};
28+
__locations?: {
29+
version: number;
30+
files: string[];
31+
locations: {
32+
[key: string]: number[][];
33+
};
34+
};
2835
};
2936

3037
/* eslint-enable @typescript-eslint/naming-convention */

packages/databricks-vscode/src/cli/CliWrapper.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -531,6 +531,7 @@ export class CliWrapper {
531531
[
532532
"bundle",
533533
"summary",
534+
"--include-locations",
534535
"--target",
535536
target,
536537
// Forces the CLI to regenerate local terraform state and pull the remote state.

packages/databricks-vscode/src/extension.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -859,6 +859,11 @@ export async function activate(
859859
utilCommands.openExternalCommand(),
860860
utilCommands
861861
),
862+
telemetry.registerCommand(
863+
"databricks.utils.goToDefinition",
864+
utilCommands.goToDefinition(),
865+
utilCommands
866+
),
862867
telemetry.registerCommand(
863868
"databricks.utils.copy",
864869
utilCommands.copyToClipboardCommand(),

packages/databricks-vscode/src/ui/bundle-resource-explorer/JobTreeNode.ts

Lines changed: 25 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import {ExtensionContext} from "vscode";
1+
import {ExtensionContext, Location} from "vscode";
22
import {BundleRemoteState} from "../../bundle/models/BundleRemoteStateModel";
33
import {
44
BundleResourceExplorerResource,
@@ -13,9 +13,21 @@ import {JobRunStatusTreeNode} from "./JobRunStatusTreeNode";
1313
import {JobRunStatus} from "../../bundle/run/JobRunStatus";
1414
import {TaskTreeNode} from "./TaskTreeNode";
1515
import {TaskHeaderTreeNode} from "./TaskHeaderTreeNode";
16+
import {getSourceLocation} from "./utils/SourceLocationUtils";
1617

1718
export class JobTreeNode implements BundleResourceExplorerTreeNode {
1819
readonly type = "jobs";
20+
21+
constructor(
22+
private readonly context: ExtensionContext,
23+
private readonly bundleRunStatusManager: BundleRunStatusManager,
24+
private readonly connectionManager: ConnectionManager,
25+
public readonly resourceKey: string,
26+
public readonly data: BundleResourceExplorerResource<"jobs">,
27+
private readonly locations: BundleRemoteState["__locations"],
28+
public parent?: BundleResourceExplorerTreeNode
29+
) {}
30+
1931
get url(): string | undefined {
2032
const host = this.connectionManager.databricksWorkspace?.host;
2133

@@ -26,14 +38,13 @@ export class JobTreeNode implements BundleResourceExplorerTreeNode {
2638
return `${host.toString()}#job/${this.data.id}`;
2739
}
2840

29-
constructor(
30-
private readonly context: ExtensionContext,
31-
private readonly bundleRunStatusManager: BundleRunStatusManager,
32-
private readonly connectionManager: ConnectionManager,
33-
public readonly resourceKey: string,
34-
public readonly data: BundleResourceExplorerResource<"jobs">,
35-
public parent?: BundleResourceExplorerTreeNode
36-
) {}
41+
get sourceLocation(): Location | undefined {
42+
return getSourceLocation(
43+
this.locations,
44+
this.connectionManager.projectRoot,
45+
this.resourceKey
46+
);
47+
}
3748

3849
get isRunning() {
3950
const runner = this.bundleRunStatusManager.runStatuses.get(
@@ -49,6 +60,7 @@ export class JobTreeNode implements BundleResourceExplorerTreeNode {
4960
resourceType: this.type,
5061
running: this.isRunning,
5162
hasUrl: this.url !== undefined,
63+
hasSourceLocation: this.sourceLocation !== undefined,
5264
cancellable: this.isRunning,
5365
nodeType: this.type,
5466
modifiedStatus: this.data.modified_status,
@@ -85,10 +97,11 @@ export class JobTreeNode implements BundleResourceExplorerTreeNode {
8597
this.connectionManager,
8698
task,
8799
this.resourceKey,
88-
`${this.resourceKey}.tasks.[${i}].${task.task_key}`,
100+
`${this.resourceKey}.tasks[${i}]`,
89101
this,
90102
this.data.id,
91-
runMonitor
103+
runMonitor,
104+
this.locations
92105
);
93106
}),
94107
this
@@ -116,6 +129,7 @@ export class JobTreeNode implements BundleResourceExplorerTreeNode {
116129
connectionManager,
117130
`jobs.${jobKey}`,
118131
jobs[jobKey],
132+
remoteStateConfig.__locations,
119133
undefined
120134
);
121135
});

packages/databricks-vscode/src/ui/bundle-resource-explorer/PipelineTreeNode.ts

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,14 +7,14 @@ import {
77
import {BundleRunStatusManager} from "../../bundle/run/BundleRunStatusManager";
88
import {ContextUtils} from "./utils";
99
import {DecorationUtils} from "../utils";
10-
1110
import {ConnectionManager} from "../../configuration/ConnectionManager";
1211
import {PipelineRunStatus} from "../../bundle/run/PipelineRunStatus";
1312
import {TreeItemTreeNode} from "../TreeItemTreeNode";
1413
import {PipelineRunStatusTreeNode} from "./PipelineRunStatusTreeNode";
15-
import {ThemeIcon} from "vscode";
14+
import {ThemeIcon, Location} from "vscode";
1615
import {PipelineDatasetsTreeNode} from "./PipelineDatasetsTreeNode";
1716
import {BundlePipelinesManager} from "../../bundle/BundlePipelinesManager";
17+
import {getSourceLocation} from "./utils/SourceLocationUtils";
1818

1919
export class PipelineTreeNode implements BundleResourceExplorerTreeNode {
2020
readonly type = "pipelines";
@@ -28,12 +28,21 @@ export class PipelineTreeNode implements BundleResourceExplorerTreeNode {
2828
return `${host.toString()}#joblist/pipelines/${this.data.id}`;
2929
}
3030

31+
get sourceLocation(): Location | undefined {
32+
return getSourceLocation(
33+
this.locations,
34+
this.connectionManager.projectRoot,
35+
this.resourceKey
36+
);
37+
}
38+
3139
constructor(
3240
private readonly connectionManager: ConnectionManager,
3341
private readonly bundleRunStatusManager: BundleRunStatusManager,
3442
private readonly pipelinesManager: BundlePipelinesManager,
3543
public readonly resourceKey: string,
3644
public readonly data: BundleResourceExplorerResource<"pipelines">,
45+
private readonly locations: BundleRemoteState["__locations"],
3746
public parent?: BundleResourceExplorerTreeNode
3847
) {}
3948

@@ -51,6 +60,7 @@ export class PipelineTreeNode implements BundleResourceExplorerTreeNode {
5160
resourceType: this.type,
5261
running: isRunning,
5362
hasUrl: this.url !== undefined,
63+
hasSourceLocation: this.sourceLocation !== undefined,
5464
cancellable: isRunning,
5565
nodeType: this.type,
5666
modifiedStatus: this.data.modified_status,
@@ -139,6 +149,7 @@ export class PipelineTreeNode implements BundleResourceExplorerTreeNode {
139149
pipelinesManager,
140150
`pipelines.${pipelineKey}`,
141151
pipelines[pipelineKey],
152+
remoteStateConfig.__locations,
142153
undefined
143154
);
144155
});

packages/databricks-vscode/src/ui/bundle-resource-explorer/ResourceTypeHeaderTreeNode.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,7 @@ export class ResourceTypeHeaderTreeNode
116116
}
117117

118118
const unknownResourceGroups = UnknownResourceTreeNode.getRootGroups(
119+
connectionManager,
119120
bundleRemoteState,
120121
KNOWN_RESOURCE_TYPES
121122
);

packages/databricks-vscode/src/ui/bundle-resource-explorer/TaskTreeNode.ts

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,19 @@ import {
44
BundleResourceExplorerTreeItem,
55
BundleResourceExplorerTreeNode,
66
} from "./types";
7-
import {ExtensionContext, TreeItemCollapsibleState, ThemeIcon} from "vscode";
7+
import {
8+
ExtensionContext,
9+
TreeItemCollapsibleState,
10+
ThemeIcon,
11+
Location,
12+
} from "vscode";
813
import {JobRunStatus} from "../../bundle/run/JobRunStatus";
914
import {ConnectionManager} from "../../configuration/ConnectionManager";
1015
import {jobs} from "@databricks/databricks-sdk";
1116
import {TaskRunStatusTreeNode} from "./TaskRunStatusTreeNode";
1217
import {ContextUtils} from "./utils";
18+
import {BundleRemoteState} from "../../bundle/models/BundleRemoteStateModel";
19+
import {getSourceLocation} from "./utils/SourceLocationUtils";
1320

1421
type Task = Required<BundleResourceExplorerResource<"jobs">>["tasks"][number];
1522
type TaskType = keyof {
@@ -18,9 +25,11 @@ type TaskType = keyof {
1825

1926
export class TaskTreeNode implements BundleResourceExplorerTreeNode {
2027
readonly type = "task";
28+
2129
get taskKey(): string {
2230
return this.data.task_key;
2331
}
32+
2433
get runDetails(): jobs.RunTask | undefined {
2534
return this.runMonitor?.data?.tasks?.find(
2635
(t) => t.task_key === this.taskKey
@@ -37,6 +46,15 @@ export class TaskTreeNode implements BundleResourceExplorerTreeNode {
3746
this.taskKey
3847
}`;
3948
}
49+
50+
get sourceLocation(): Location | undefined {
51+
return getSourceLocation(
52+
this.locations,
53+
this.connectionManager.projectRoot,
54+
this.resourceKey
55+
);
56+
}
57+
4058
constructor(
4159
private readonly context: ExtensionContext,
4260
private readonly connectionManager: ConnectionManager,
@@ -45,7 +63,8 @@ export class TaskTreeNode implements BundleResourceExplorerTreeNode {
4563
public readonly resourceKey: string,
4664
public parent: BundleResourceExplorerTreeNode,
4765
public readonly jobId?: string,
48-
public readonly runMonitor?: JobRunStatus
66+
public readonly runMonitor?: JobRunStatus,
67+
private readonly locations?: BundleRemoteState["__locations"]
4968
) {}
5069

5170
private getIconPathForType(taskType: string) {
@@ -135,6 +154,7 @@ export class TaskTreeNode implements BundleResourceExplorerTreeNode {
135154
nodeType: this.type,
136155
running: this.isRunning,
137156
hasUrl: this.url !== undefined,
157+
hasSourceLocation: this.sourceLocation !== undefined,
138158
cancellable: false,
139159
}),
140160
collapsibleState:

packages/databricks-vscode/src/ui/bundle-resource-explorer/UnknownResourceTreeNode.ts

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,9 @@ import {
66
} from "./types";
77
import {ContextUtils} from "./utils";
88
import {DecorationUtils} from "../utils";
9-
import {TreeItemCollapsibleState} from "vscode";
9+
import {TreeItemCollapsibleState, Location} from "vscode";
10+
import {ConnectionManager} from "../../configuration/ConnectionManager";
11+
import {getSourceLocation} from "./utils/SourceLocationUtils";
1012

1113
type UnknownResourcesMap = Map<
1214
BundleResourceExplorerResourceKey,
@@ -17,16 +19,26 @@ export class UnknownResourceTreeNode implements BundleResourceExplorerTreeNode {
1719
readonly type = "unknown_resource";
1820

1921
constructor(
22+
private readonly connectionManager: ConnectionManager,
2023
public readonly resourceType: BundleResourceExplorerResourceKey,
2124
public readonly resourceKey: string,
2225
public readonly data: any,
26+
private readonly locations: BundleRemoteState["__locations"],
2327
public parent?: BundleResourceExplorerTreeNode
2428
) {}
2529

2630
get url(): string | undefined {
2731
return this.data.url;
2832
}
2933

34+
get sourceLocation(): Location | undefined {
35+
return getSourceLocation(
36+
this.locations,
37+
this.connectionManager.projectRoot,
38+
`${this.resourceType}.${this.resourceKey}`
39+
);
40+
}
41+
3042
getTreeItem(): BundleResourceExplorerTreeItem {
3143
const name =
3244
this.data.name ??
@@ -42,6 +54,7 @@ export class UnknownResourceTreeNode implements BundleResourceExplorerTreeNode {
4254
resourceType: this.resourceType,
4355
nodeType: "unknown_resource",
4456
hasUrl: this.url !== undefined,
57+
hasSourceLocation: this.sourceLocation !== undefined,
4558
modifiedStatus: this.data.modified_status,
4659
}),
4760
resourceUri: DecorationUtils.getModifiedStatusDecoration(
@@ -57,6 +70,7 @@ export class UnknownResourceTreeNode implements BundleResourceExplorerTreeNode {
5770
}
5871

5972
static getRootGroups(
73+
connectionManager: ConnectionManager,
6074
bundleRemoteState: BundleRemoteState,
6175
knownResourceTypes: readonly string[]
6276
): UnknownResourcesMap {
@@ -77,9 +91,11 @@ export class UnknownResourceTreeNode implements BundleResourceExplorerTreeNode {
7791
type,
7892
Object.keys(resources).map((key) => {
7993
return new UnknownResourceTreeNode(
94+
connectionManager,
8095
type,
8196
key,
82-
resources[key]
97+
resources[key],
98+
bundleRemoteState.__locations
8399
);
84100
})
85101
);

packages/databricks-vscode/src/ui/bundle-resource-explorer/types.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import {TreeItem} from "vscode";
1+
import {Location, TreeItem} from "vscode";
22
import {BundleRemoteState} from "../../bundle/models/BundleRemoteStateModel";
33
import {Resource, ResourceKey} from "../../bundle/types";
44

@@ -25,6 +25,8 @@ export interface BundleResourceExplorerTreeNode {
2525
| "job_run_status"
2626
| "task_header";
2727
parent?: BundleResourceExplorerTreeNode;
28+
url?: string;
29+
sourceLocation?: Location;
2830
getTreeItem(): BundleResourceExplorerTreeItem;
2931
getChildren():
3032
| BundleResourceExplorerTreeNode[]

0 commit comments

Comments
 (0)