Skip to content

Commit 125a2ec

Browse files
Add a model for remote state (BundleRemoteStateModel) (#1006)
## Changes * Add a model for the remote state. * Expose `remoteStateConfig` from `ConfigModel`. ## Tests <!-- How is this tested? -->
1 parent ea3ed45 commit 125a2ec

File tree

7 files changed

+167
-3
lines changed

7 files changed

+167
-3
lines changed

packages/databricks-vscode/package.json

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -658,6 +658,11 @@
658658
"databricks.ipythonDir": {
659659
"type": "string",
660660
"description": "Absolute path to a directory for storing IPython files. Defaults to IPYTHONDIR environment variable (if set) or ~/.ipython."
661+
},
662+
"databricks.bundle.remoteStateRefreshInterval": {
663+
"type": "number",
664+
"default": 5,
665+
"description": "The interval in minutes at which the remote state of the bundle is refreshed."
661666
}
662667
}
663668
}
Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
import {Uri} from "vscode";
2+
import {CliWrapper} from "../../cli/CliWrapper";
3+
import {BaseModelWithStateCache} from "../../configuration/models/BaseModelWithStateCache";
4+
import {Mutex} from "../../locking";
5+
6+
import {BundleTarget} from "../types";
7+
import {AuthProvider} from "../../configuration/auth/AuthProvider";
8+
import lodash from "lodash";
9+
import {WorkspaceConfigs} from "../../vscode-objs/WorkspaceConfigs";
10+
import {withLogContext} from "@databricks/databricks-sdk/dist/logging";
11+
import {Loggers} from "../../logger";
12+
import {Context, context} from "@databricks/databricks-sdk";
13+
14+
/* eslint-disable @typescript-eslint/naming-convention */
15+
type Resources = Required<BundleTarget>["resources"];
16+
type Resource<K extends keyof Required<Resources>> = Required<Resources>[K];
17+
18+
export type BundleRemoteState = BundleTarget & {
19+
resources?: Resources & {
20+
[r in keyof Resources]?: {
21+
[k in keyof Resource<r>]?: Resource<r>[k] & {
22+
id?: string;
23+
modified_status?: "CREATED" | "DELETED" | "UPDATED";
24+
};
25+
};
26+
};
27+
};
28+
/* eslint-enable @typescript-eslint/naming-convention */
29+
30+
export class BundleRemoteStateModel extends BaseModelWithStateCache<BundleRemoteState> {
31+
private target: string | undefined;
32+
private authProvider: AuthProvider | undefined;
33+
protected mutex = new Mutex();
34+
private refreshInterval: NodeJS.Timeout | undefined;
35+
36+
constructor(
37+
private readonly cli: CliWrapper,
38+
private readonly workspaceFolder: Uri,
39+
private readonly workspaceConfigs: WorkspaceConfigs
40+
) {
41+
super();
42+
}
43+
44+
@withLogContext(Loggers.Extension)
45+
public init(@context ctx?: Context) {
46+
this.refreshInterval = setInterval(async () => {
47+
try {
48+
await this.stateCache.refresh();
49+
} catch (e) {
50+
ctx?.logger?.error("Unable to refresh bundle remote state", e);
51+
}
52+
}, this.workspaceConfigs.bundleRemoteStateRefreshInterval);
53+
}
54+
55+
@Mutex.synchronise("mutex")
56+
public async setTarget(target: string | undefined) {
57+
if (this.target === target) {
58+
return;
59+
}
60+
this.target = target;
61+
this.authProvider = undefined;
62+
await this.stateCache.refresh();
63+
}
64+
65+
@Mutex.synchronise("mutex")
66+
public async setAuthProvider(authProvider: AuthProvider | undefined) {
67+
if (
68+
!lodash.isEqual(this.authProvider?.toJSON(), authProvider?.toJSON())
69+
) {
70+
this.authProvider = authProvider;
71+
await this.stateCache.refresh();
72+
}
73+
}
74+
75+
protected async readState(): Promise<BundleRemoteState> {
76+
if (this.target === undefined || this.authProvider === undefined) {
77+
return {};
78+
}
79+
80+
return JSON.parse(
81+
await this.cli.bundleSummarise(
82+
this.target,
83+
this.authProvider,
84+
this.workspaceFolder,
85+
this.workspaceConfigs.databrickscfgLocation
86+
)
87+
);
88+
}
89+
90+
dispose() {
91+
super.dispose();
92+
if (this.refreshInterval !== undefined) {
93+
clearInterval(this.refreshInterval);
94+
}
95+
}
96+
}
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
export {type BundleSchema} from "./BundleSchema";
2-
32
import {BundleSchema} from "./BundleSchema";
3+
44
export type BundleTarget = Required<BundleSchema>["targets"][string];

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

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -194,4 +194,30 @@ export class CliWrapper {
194194
}
195195
return stdout;
196196
}
197+
198+
async bundleSummarise(
199+
target: string,
200+
authProvider: AuthProvider,
201+
workspaceFolder: Uri,
202+
configfilePath?: string
203+
) {
204+
const {stdout, stderr} = await execFile(
205+
this.cliPath,
206+
["bundle", "summarise", "--target", target],
207+
{
208+
cwd: workspaceFolder.fsPath,
209+
env: {
210+
...EnvVarGenerators.getEnvVarsForCli(configfilePath),
211+
...EnvVarGenerators.getProxyEnvVars(),
212+
...authProvider.toEnv(),
213+
},
214+
shell: true,
215+
}
216+
);
217+
218+
if (stderr !== "") {
219+
throw new Error(stderr);
220+
}
221+
return stdout;
222+
}
197223
}

packages/databricks-vscode/src/configuration/models/ConfigModel.ts

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,10 @@ import {
1818
BundleValidateState,
1919
} from "../../bundle/models/BundleValidateModel";
2020
import {CustomWhenContext} from "../../vscode-objs/CustomWhenContext";
21+
import {
22+
BundleRemoteState,
23+
BundleRemoteStateModel,
24+
} from "../../bundle/models/BundleRemoteStateModel";
2125

2226
const defaults: ConfigState = {
2327
mode: "development",
@@ -42,6 +46,7 @@ type ConfigState = Pick<
4246
OverrideableConfigState & {
4347
preValidateConfig?: BundlePreValidateState;
4448
validateConfig?: BundleValidateState;
49+
remoteStateConfig?: BundleRemoteState;
4550
overrides?: OverrideableConfigState;
4651
};
4752

@@ -92,6 +97,7 @@ export class ConfigModel implements Disposable {
9297
preValidateConfig: bundlePreValidateConfig,
9398
validateConfig: bundleValidateConfig,
9499
overrides,
100+
remoteStateConfig: await this.bundleRemoteStateModel.load(),
95101
};
96102
}
97103

@@ -114,6 +120,7 @@ export class ConfigModel implements Disposable {
114120
private readonly bundleValidateModel: BundleValidateModel,
115121
private readonly overrideableConfigModel: OverrideableConfigModel,
116122
private readonly bundlePreValidateModel: BundlePreValidateModel,
123+
private readonly bundleRemoteStateModel: BundleRemoteStateModel,
117124
private readonly vscodeWhenContext: CustomWhenContext,
118125
private readonly stateStorage: StateStorage
119126
) {
@@ -132,13 +139,17 @@ export class ConfigModel implements Disposable {
132139
//refresh cache to trigger onDidChange event
133140
this.configCache.refresh();
134141
})
135-
)
142+
),
143+
this.bundleRemoteStateModel.onDidChange(async () => {
144+
await this.configCache.refresh();
145+
})
136146
);
137147
}
138148

139149
@onError({popup: {prefix: "Failed to initialize configs."}})
140150
public async init() {
141151
await this.readTarget();
152+
this.bundleRemoteStateModel.init();
142153
}
143154

144155
get targets() {

packages/databricks-vscode/src/extension.ts

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@ import {BundleValidateModel} from "./bundle/models/BundleValidateModel";
5858
import {ConfigModel} from "./configuration/models/ConfigModel";
5959
import {OverrideableConfigModel} from "./configuration/models/OverrideableConfigModel";
6060
import {BundlePreValidateModel} from "./bundle/models/BundlePreValidateModel";
61+
import {BundleRemoteStateModel} from "./bundle/models/BundleRemoteStateModel";
6162

6263
const customWhenContext = new CustomWhenContext();
6364

@@ -166,7 +167,6 @@ export async function activate(
166167
const cli = new CliWrapper(context, cliLogFilePath);
167168
const bundleFileSet = new BundleFileSet(workspace.workspaceFolders[0].uri);
168169
const bundleFileWatcher = new BundleWatcher(bundleFileSet);
169-
context.subscriptions.push(bundleFileWatcher);
170170
const bundleValidateModel = new BundleValidateModel(
171171
bundleFileWatcher,
172172
cli,
@@ -178,10 +178,16 @@ export async function activate(
178178
bundleFileSet,
179179
bundleFileWatcher
180180
);
181+
const bundleRemoteStateModel = new BundleRemoteStateModel(
182+
cli,
183+
workspaceUri,
184+
workspaceConfigs
185+
);
181186
const configModel = new ConfigModel(
182187
bundleValidateModel,
183188
overrideableConfigModel,
184189
bundlePreValidateModel,
190+
bundleRemoteStateModel,
185191
customWhenContext,
186192
stateStorage
187193
);
@@ -193,6 +199,14 @@ export async function activate(
193199
customWhenContext
194200
);
195201
context.subscriptions.push(
202+
bundleFileWatcher,
203+
bundleValidateModel,
204+
overrideableConfigModel,
205+
bundlePreValidateModel,
206+
bundleRemoteStateModel,
207+
configModel,
208+
configModel,
209+
connectionManager,
196210
connectionManager.onDidChangeState(async (state) => {
197211
telemetry.setMetadata(
198212
Metadata.USER,

packages/databricks-vscode/src/vscode-objs/WorkspaceConfigs.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import {ConfigurationTarget, workspace} from "vscode";
22
import {SyncDestinationType} from "../sync/SyncDestination";
3+
import {Time, TimeUnits} from "@databricks/databricks-sdk";
34

45
export const workspaceConfigs = {
56
get maxFieldLength() {
@@ -148,4 +149,15 @@ export const workspaceConfigs = {
148149
}
149150
return dir;
150151
},
152+
153+
get bundleRemoteStateRefreshInterval(): number {
154+
const config =
155+
workspace
156+
.getConfiguration("databricks")
157+
.get<number>("bundle.remoteStateRefreshInterval") ?? 5;
158+
159+
return new Time(config, TimeUnits.minutes).toMillSeconds().value;
160+
},
151161
};
162+
163+
export type WorkspaceConfigs = typeof workspaceConfigs;

0 commit comments

Comments
 (0)