Skip to content

Commit 7acb093

Browse files
Make all models inherit from a common base class (#994)
## Changes <!-- Summary of your changes that are easy to understand --> ## Tests <!-- How is this tested? -->
1 parent 3c578aa commit 7acb093

File tree

15 files changed

+357
-408
lines changed

15 files changed

+357
-408
lines changed

packages/databricks-vscode/eslint-local-rules/rules/mutexSynchronisedDecorator.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,8 +33,10 @@ const rule = ESLintUtils.RuleCreator.withoutDocs({
3333

3434
const exists = node.parent.parent.body.some((element) => {
3535
return (
36-
element.type ===
37-
AST_NODE_TYPES.PropertyDefinition &&
36+
(element.type ===
37+
AST_NODE_TYPES.PropertyDefinition ||
38+
element.type ===
39+
AST_NODE_TYPES.TSAbstractPropertyDefinition) &&
3840
element.key.type === AST_NODE_TYPES.Identifier &&
3941
element.key.name === mutexName
4042
);

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

Lines changed: 41 additions & 69 deletions
Original file line numberDiff line numberDiff line change
@@ -1,69 +1,37 @@
1-
import {Disposable, Uri} from "vscode";
1+
import {Uri} from "vscode";
22
import {BundleFileSet, BundleWatcher} from "..";
33
import {BundleTarget} from "../types";
4-
import {CachedValue} from "../../locking/CachedValue";
5-
import {
6-
BundlePreValidateConfig,
7-
isBundlePreValidateConfigKey,
8-
} from "../../configuration/types";
4+
import {BaseModelWithStateCache} from "../../configuration/models/BaseModelWithStateCache";
5+
import {UrlUtils} from "../../utils";
6+
import {onError} from "../../utils/onErrorDecorator";
7+
import {Mutex} from "../../locking";
8+
9+
export type BundlePreValidateState = {
10+
host?: URL;
11+
mode?: "development" | "staging" | "production";
12+
authParams?: Record<string, string | undefined>;
13+
} & BundleTarget;
14+
915
/**
1016
* Reads and writes bundle configs. This class does not notify when the configs change.
1117
* We use the BundleWatcher to notify when the configs change.
1218
*/
13-
export class BundlePreValidateModel implements Disposable {
14-
private disposables: Disposable[] = [];
15-
16-
private readonly stateCache = new CachedValue<
17-
BundlePreValidateConfig | undefined
18-
>(async () => {
19-
if (this.target === undefined) {
20-
return undefined;
21-
}
22-
return this.readState(this.target);
23-
});
24-
25-
public readonly onDidChange = this.stateCache.onDidChange;
26-
19+
export class BundlePreValidateModel extends BaseModelWithStateCache<BundlePreValidateState> {
20+
protected mutex = new Mutex();
2721
private target: string | undefined;
2822

29-
private readonly readerMapping: Record<
30-
keyof BundlePreValidateConfig,
31-
(
32-
t?: BundleTarget
33-
) => Promise<
34-
BundlePreValidateConfig[keyof BundlePreValidateConfig] | undefined
35-
>
36-
> = {
37-
authParams: this.getAuthParams,
38-
mode: this.getMode,
39-
host: this.getHost,
40-
};
41-
4223
constructor(
4324
private readonly bundleFileSet: BundleFileSet,
4425
private readonly bunldeFileWatcher: BundleWatcher
4526
) {
27+
super();
4628
this.disposables.push(
4729
this.bunldeFileWatcher.onDidChange(async () => {
4830
await this.stateCache.refresh();
4931
})
5032
);
5133
}
5234

53-
private async getHost(target?: BundleTarget) {
54-
return target?.workspace?.host;
55-
}
56-
57-
private async getMode(target?: BundleTarget) {
58-
return target?.mode;
59-
}
60-
61-
/* eslint-disable @typescript-eslint/no-unused-vars */
62-
private async getAuthParams(target?: BundleTarget) {
63-
return undefined;
64-
}
65-
/* eslint-enable @typescript-eslint/no-unused-vars */
66-
6735
get targets() {
6836
return this.bundleFileSet.bundleDataCache.value.then(
6937
(data) => data?.targets
@@ -87,33 +55,41 @@ export class BundlePreValidateModel implements Disposable {
8755
await this.stateCache.refresh();
8856
}
8957

90-
private async readState(target: string) {
91-
const configs = {} as any;
92-
const targetObject = (await this.bundleFileSet.bundleDataCache.value)
93-
.targets?.[target];
58+
protected readStateFromTarget(
59+
target: BundleTarget
60+
): BundlePreValidateState {
61+
return {
62+
...target,
63+
host: UrlUtils.normalizeHost(target?.workspace?.host ?? ""),
64+
mode: target?.mode as BundlePreValidateState["mode"],
65+
authParams: undefined,
66+
};
67+
}
9468

95-
for (const key of Object.keys(this.readerMapping)) {
96-
if (!isBundlePreValidateConfigKey(key)) {
97-
continue;
98-
}
99-
configs[key] = await this.readerMapping[key](targetObject);
69+
@onError({popup: {prefix: "Failed to parse bundle yaml"}})
70+
@Mutex.synchronise("mutex")
71+
protected async readState() {
72+
if (this.target === undefined) {
73+
return {};
10074
}
101-
return configs as BundlePreValidateConfig;
75+
76+
const targetObject = (await this.bundleFileSet.bundleDataCache.value)
77+
.targets?.[this.target];
78+
79+
return this.readStateFromTarget(targetObject ?? {});
10280
}
10381

104-
public async getFileToWrite<T extends keyof BundlePreValidateConfig>(
105-
key: T
106-
) {
82+
public async getFileToWrite(key: string) {
10783
const filesWithTarget: Uri[] = [];
10884
const filesWithConfig = (
10985
await this.bundleFileSet.findFile(async (data, file) => {
11086
const bundleTarget = data.targets?.[this.target ?? ""];
111-
if (bundleTarget) {
112-
filesWithTarget.push(file);
87+
if (bundleTarget === undefined) {
88+
return false;
11389
}
114-
if (
115-
(await this.readerMapping[key](bundleTarget)) === undefined
116-
) {
90+
filesWithTarget.push(file);
91+
92+
if (this.readStateFromTarget(bundleTarget) === undefined) {
11793
return false;
11894
}
11995
return true;
@@ -135,10 +111,6 @@ export class BundlePreValidateModel implements Disposable {
135111
return [...filesWithConfig, ...filesWithTarget][0];
136112
}
137113

138-
public async load() {
139-
return await this.stateCache.value;
140-
}
141-
142114
public dispose() {
143115
this.disposables.forEach((d) => d.dispose());
144116
}

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

Lines changed: 18 additions & 84 deletions
Original file line numberDiff line numberDiff line change
@@ -1,78 +1,37 @@
1-
import {Disposable, Uri, EventEmitter} from "vscode";
1+
import {Uri} from "vscode";
22
import {BundleWatcher} from "../BundleWatcher";
33
import {AuthProvider} from "../../configuration/auth/AuthProvider";
44
import {Mutex} from "../../locking";
55
import {CliWrapper} from "../../cli/CliWrapper";
66
import {BundleTarget} from "../types";
7-
import {CachedValue} from "../../locking/CachedValue";
87
import {onError} from "../../utils/onErrorDecorator";
98
import lodash from "lodash";
109
import {workspaceConfigs} from "../../vscode-objs/WorkspaceConfigs";
11-
import {BundleValidateConfig} from "../../configuration/types";
10+
import {BaseModelWithStateCache} from "../../configuration/models/BaseModelWithStateCache";
1211

13-
type BundleValidateState = BundleValidateConfig & BundleTarget;
14-
15-
export class BundleValidateModel implements Disposable {
16-
private disposables: Disposable[] = [];
17-
private mutex = new Mutex();
12+
export type BundleValidateState = {
13+
clusterId?: string;
14+
remoteRootPath?: string;
15+
} & BundleTarget;
1816

17+
export class BundleValidateModel extends BaseModelWithStateCache<BundleValidateState> {
1918
private target: string | undefined;
2019
private authProvider: AuthProvider | undefined;
21-
22-
private readonly stateCache = new CachedValue<
23-
BundleValidateState | undefined
24-
>(this.readState.bind(this));
25-
26-
private readonly onDidChangeKeyEmitters = new Map<
27-
keyof BundleValidateState,
28-
EventEmitter<void>
29-
>();
30-
31-
onDidChangeKey(key: keyof BundleValidateState) {
32-
if (!this.onDidChangeKeyEmitters.has(key)) {
33-
this.onDidChangeKeyEmitters.set(key, new EventEmitter());
34-
}
35-
return this.onDidChangeKeyEmitters.get(key)!.event;
36-
}
37-
38-
public onDidChange = this.stateCache.onDidChange;
20+
protected mutex = new Mutex();
3921

4022
constructor(
4123
private readonly bundleWatcher: BundleWatcher,
4224
private readonly cli: CliWrapper,
4325
private readonly workspaceFolder: Uri
4426
) {
27+
super();
4528
this.disposables.push(
4629
this.bundleWatcher.onDidChange(async () => {
4730
await this.stateCache.refresh();
48-
}),
49-
// Emit an event for each key that changes
50-
this.stateCache.onDidChange(async ({oldValue, newValue}) => {
51-
for (const key of Object.keys({
52-
...oldValue,
53-
...newValue,
54-
}) as (keyof BundleValidateState)[]) {
55-
if (
56-
oldValue === null ||
57-
!lodash.isEqual(oldValue?.[key], newValue?.[key])
58-
) {
59-
this.onDidChangeKeyEmitters.get(key)?.fire();
60-
}
61-
}
6231
})
6332
);
6433
}
6534

66-
private readerMapping: {
67-
[K in keyof BundleValidateState]: (
68-
t?: BundleTarget
69-
) => BundleValidateState[K];
70-
} = {
71-
clusterId: (target) => target?.bundle?.compute_id,
72-
workspaceFsPath: (target) => target?.workspace?.file_path,
73-
resources: (target) => target?.resources,
74-
};
75-
7635
@Mutex.synchronise("mutex")
7736
public async setTarget(target: string | undefined) {
7837
if (this.target === target) {
@@ -93,14 +52,13 @@ export class BundleValidateModel implements Disposable {
9352
}
9453
}
9554

96-
@onError({popup: {prefix: "Failed to read bundle config."}})
97-
@Mutex.synchronise("mutex")
98-
private async readState() {
55+
@onError({popup: {prefix: "Failed to parse bundle validate ouput"}})
56+
protected async readState(): Promise<BundleValidateState> {
9957
if (this.target === undefined || this.authProvider === undefined) {
100-
return;
58+
return {};
10159
}
10260

103-
const targetObject = JSON.parse(
61+
const validateOutput = JSON.parse(
10462
await this.cli.bundleValidate(
10563
this.target,
10664
this.authProvider,
@@ -109,35 +67,11 @@ export class BundleValidateModel implements Disposable {
10967
)
11068
) as BundleTarget;
11169

112-
const configs: any = {};
113-
114-
for (const key of Object.keys(
115-
this.readerMapping
116-
) as (keyof BundleValidateState)[]) {
117-
configs[key] = this.readerMapping[key]?.(targetObject);
118-
}
119-
120-
return {...configs, ...targetObject} as BundleValidateState;
121-
}
122-
123-
@Mutex.synchronise("mutex")
124-
public async load<T extends keyof BundleValidateState>(
125-
keys: T[] = []
126-
): Promise<Partial<Pick<BundleValidateState, T>> | undefined> {
127-
if (keys.length === 0) {
128-
return await this.stateCache.value;
129-
}
130-
131-
const target = await this.stateCache.value;
132-
const configs: Partial<{
133-
[K in T]: BundleValidateState[K];
134-
}> = {};
135-
136-
for (const key of keys) {
137-
configs[key] = this.readerMapping[key]?.(target);
138-
}
139-
140-
return configs;
70+
return {
71+
clusterId: validateOutput?.bundle?.compute_id,
72+
remoteRootPath: validateOutput?.workspace?.file_path,
73+
...validateOutput,
74+
};
14175
}
14276

14377
dispose() {

packages/databricks-vscode/src/configuration/ConnectionManager.ts

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ export class ConnectionManager implements Disposable {
5757
popup: {prefix: "Error attaching sync destination: "},
5858
})
5959
private async updateSyncDestinationMapper() {
60-
const workspacePath = await this.configModel.get("workspaceFsPath");
60+
const workspacePath = await this.configModel.get("remoteRootPath");
6161
const remoteUri = workspacePath
6262
? new RemoteUri(workspacePath)
6363
: undefined;
@@ -116,19 +116,16 @@ export class ConnectionManager implements Disposable {
116116
await this.loginWithSavedAuth();
117117

118118
this.disposables.push(
119-
this.configModel.onDidChange("workspaceFsPath")(
119+
this.configModel.onDidChangeKey("remoteRootPath")(
120120
this.updateSyncDestinationMapper,
121121
this
122122
),
123-
this.configModel.onDidChange("clusterId")(
123+
this.configModel.onDidChangeKey("clusterId")(
124124
this.updateClusterManager,
125125
this
126126
),
127-
this.configModel.onDidChange("target")(
128-
this.loginWithSavedAuth,
129-
this
130-
),
131-
this.configModel.onDidChange("authParams")(async () => {
127+
this.configModel.onDidChangeTarget(this.loginWithSavedAuth, this),
128+
this.configModel.onDidChangeKey("authParams")(async () => {
132129
const config = await this.configModel.getS("authParams");
133130
if (config === undefined) {
134131
return;
@@ -290,7 +287,10 @@ export class ConnectionManager implements Disposable {
290287
return;
291288
}
292289

293-
const config = await configureWorkspaceWizard(this.cli, host);
290+
const config = await configureWorkspaceWizard(
291+
this.cli,
292+
host.toString()
293+
);
294294
if (!config) {
295295
return;
296296
}
@@ -326,14 +326,14 @@ export class ConnectionManager implements Disposable {
326326
);
327327
return;
328328
}
329-
await this.configModel.set("workspaceFsPath", remoteWorkspace.path);
329+
await this.configModel.set("remoteRootPath", remoteWorkspace.path);
330330
}
331331

332332
@onError({
333333
popup: {prefix: "Can't detach sync destination. "},
334334
})
335335
async detachSyncDestination(): Promise<void> {
336-
await this.configModel.set("workspaceFsPath", undefined);
336+
await this.configModel.set("remoteRootPath", undefined);
337337
}
338338

339339
private updateState(newState: ConnectionState) {

0 commit comments

Comments
 (0)