Skip to content

Commit

Permalink
Make all models inherit from a common base class (#994)
Browse files Browse the repository at this point in the history
## Changes
<!-- Summary of your changes that are easy to understand -->

## Tests
<!-- How is this tested? -->
  • Loading branch information
kartikgupta-db authored Jan 10, 2024
1 parent 3c578aa commit 7acb093
Show file tree
Hide file tree
Showing 15 changed files with 357 additions and 408 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,10 @@ const rule = ESLintUtils.RuleCreator.withoutDocs({

const exists = node.parent.parent.body.some((element) => {
return (
element.type ===
AST_NODE_TYPES.PropertyDefinition &&
(element.type ===
AST_NODE_TYPES.PropertyDefinition ||
element.type ===
AST_NODE_TYPES.TSAbstractPropertyDefinition) &&
element.key.type === AST_NODE_TYPES.Identifier &&
element.key.name === mutexName
);
Expand Down
110 changes: 41 additions & 69 deletions packages/databricks-vscode/src/bundle/models/BundlePreValidateModel.ts
Original file line number Diff line number Diff line change
@@ -1,69 +1,37 @@
import {Disposable, Uri} from "vscode";
import {Uri} from "vscode";
import {BundleFileSet, BundleWatcher} from "..";
import {BundleTarget} from "../types";
import {CachedValue} from "../../locking/CachedValue";
import {
BundlePreValidateConfig,
isBundlePreValidateConfigKey,
} from "../../configuration/types";
import {BaseModelWithStateCache} from "../../configuration/models/BaseModelWithStateCache";
import {UrlUtils} from "../../utils";
import {onError} from "../../utils/onErrorDecorator";
import {Mutex} from "../../locking";

export type BundlePreValidateState = {
host?: URL;
mode?: "development" | "staging" | "production";
authParams?: Record<string, string | undefined>;
} & BundleTarget;

/**
* Reads and writes bundle configs. This class does not notify when the configs change.
* We use the BundleWatcher to notify when the configs change.
*/
export class BundlePreValidateModel implements Disposable {
private disposables: Disposable[] = [];

private readonly stateCache = new CachedValue<
BundlePreValidateConfig | undefined
>(async () => {
if (this.target === undefined) {
return undefined;
}
return this.readState(this.target);
});

public readonly onDidChange = this.stateCache.onDidChange;

export class BundlePreValidateModel extends BaseModelWithStateCache<BundlePreValidateState> {
protected mutex = new Mutex();
private target: string | undefined;

private readonly readerMapping: Record<
keyof BundlePreValidateConfig,
(
t?: BundleTarget
) => Promise<
BundlePreValidateConfig[keyof BundlePreValidateConfig] | undefined
>
> = {
authParams: this.getAuthParams,
mode: this.getMode,
host: this.getHost,
};

constructor(
private readonly bundleFileSet: BundleFileSet,
private readonly bunldeFileWatcher: BundleWatcher
) {
super();
this.disposables.push(
this.bunldeFileWatcher.onDidChange(async () => {
await this.stateCache.refresh();
})
);
}

private async getHost(target?: BundleTarget) {
return target?.workspace?.host;
}

private async getMode(target?: BundleTarget) {
return target?.mode;
}

/* eslint-disable @typescript-eslint/no-unused-vars */
private async getAuthParams(target?: BundleTarget) {
return undefined;
}
/* eslint-enable @typescript-eslint/no-unused-vars */

get targets() {
return this.bundleFileSet.bundleDataCache.value.then(
(data) => data?.targets
Expand All @@ -87,33 +55,41 @@ export class BundlePreValidateModel implements Disposable {
await this.stateCache.refresh();
}

private async readState(target: string) {
const configs = {} as any;
const targetObject = (await this.bundleFileSet.bundleDataCache.value)
.targets?.[target];
protected readStateFromTarget(
target: BundleTarget
): BundlePreValidateState {
return {
...target,
host: UrlUtils.normalizeHost(target?.workspace?.host ?? ""),
mode: target?.mode as BundlePreValidateState["mode"],
authParams: undefined,
};
}

for (const key of Object.keys(this.readerMapping)) {
if (!isBundlePreValidateConfigKey(key)) {
continue;
}
configs[key] = await this.readerMapping[key](targetObject);
@onError({popup: {prefix: "Failed to parse bundle yaml"}})
@Mutex.synchronise("mutex")
protected async readState() {
if (this.target === undefined) {
return {};
}
return configs as BundlePreValidateConfig;

const targetObject = (await this.bundleFileSet.bundleDataCache.value)
.targets?.[this.target];

return this.readStateFromTarget(targetObject ?? {});
}

public async getFileToWrite<T extends keyof BundlePreValidateConfig>(
key: T
) {
public async getFileToWrite(key: string) {
const filesWithTarget: Uri[] = [];
const filesWithConfig = (
await this.bundleFileSet.findFile(async (data, file) => {
const bundleTarget = data.targets?.[this.target ?? ""];
if (bundleTarget) {
filesWithTarget.push(file);
if (bundleTarget === undefined) {
return false;
}
if (
(await this.readerMapping[key](bundleTarget)) === undefined
) {
filesWithTarget.push(file);

if (this.readStateFromTarget(bundleTarget) === undefined) {
return false;
}
return true;
Expand All @@ -135,10 +111,6 @@ export class BundlePreValidateModel implements Disposable {
return [...filesWithConfig, ...filesWithTarget][0];
}

public async load() {
return await this.stateCache.value;
}

public dispose() {
this.disposables.forEach((d) => d.dispose());
}
Expand Down
102 changes: 18 additions & 84 deletions packages/databricks-vscode/src/bundle/models/BundleValidateModel.ts
Original file line number Diff line number Diff line change
@@ -1,78 +1,37 @@
import {Disposable, Uri, EventEmitter} from "vscode";
import {Uri} from "vscode";
import {BundleWatcher} from "../BundleWatcher";
import {AuthProvider} from "../../configuration/auth/AuthProvider";
import {Mutex} from "../../locking";
import {CliWrapper} from "../../cli/CliWrapper";
import {BundleTarget} from "../types";
import {CachedValue} from "../../locking/CachedValue";
import {onError} from "../../utils/onErrorDecorator";
import lodash from "lodash";
import {workspaceConfigs} from "../../vscode-objs/WorkspaceConfigs";
import {BundleValidateConfig} from "../../configuration/types";
import {BaseModelWithStateCache} from "../../configuration/models/BaseModelWithStateCache";

type BundleValidateState = BundleValidateConfig & BundleTarget;

export class BundleValidateModel implements Disposable {
private disposables: Disposable[] = [];
private mutex = new Mutex();
export type BundleValidateState = {
clusterId?: string;
remoteRootPath?: string;
} & BundleTarget;

export class BundleValidateModel extends BaseModelWithStateCache<BundleValidateState> {
private target: string | undefined;
private authProvider: AuthProvider | undefined;

private readonly stateCache = new CachedValue<
BundleValidateState | undefined
>(this.readState.bind(this));

private readonly onDidChangeKeyEmitters = new Map<
keyof BundleValidateState,
EventEmitter<void>
>();

onDidChangeKey(key: keyof BundleValidateState) {
if (!this.onDidChangeKeyEmitters.has(key)) {
this.onDidChangeKeyEmitters.set(key, new EventEmitter());
}
return this.onDidChangeKeyEmitters.get(key)!.event;
}

public onDidChange = this.stateCache.onDidChange;
protected mutex = new Mutex();

constructor(
private readonly bundleWatcher: BundleWatcher,
private readonly cli: CliWrapper,
private readonly workspaceFolder: Uri
) {
super();
this.disposables.push(
this.bundleWatcher.onDidChange(async () => {
await this.stateCache.refresh();
}),
// Emit an event for each key that changes
this.stateCache.onDidChange(async ({oldValue, newValue}) => {
for (const key of Object.keys({
...oldValue,
...newValue,
}) as (keyof BundleValidateState)[]) {
if (
oldValue === null ||
!lodash.isEqual(oldValue?.[key], newValue?.[key])
) {
this.onDidChangeKeyEmitters.get(key)?.fire();
}
}
})
);
}

private readerMapping: {
[K in keyof BundleValidateState]: (
t?: BundleTarget
) => BundleValidateState[K];
} = {
clusterId: (target) => target?.bundle?.compute_id,
workspaceFsPath: (target) => target?.workspace?.file_path,
resources: (target) => target?.resources,
};

@Mutex.synchronise("mutex")
public async setTarget(target: string | undefined) {
if (this.target === target) {
Expand All @@ -93,14 +52,13 @@ export class BundleValidateModel implements Disposable {
}
}

@onError({popup: {prefix: "Failed to read bundle config."}})
@Mutex.synchronise("mutex")
private async readState() {
@onError({popup: {prefix: "Failed to parse bundle validate ouput"}})
protected async readState(): Promise<BundleValidateState> {
if (this.target === undefined || this.authProvider === undefined) {
return;
return {};
}

const targetObject = JSON.parse(
const validateOutput = JSON.parse(
await this.cli.bundleValidate(
this.target,
this.authProvider,
Expand All @@ -109,35 +67,11 @@ export class BundleValidateModel implements Disposable {
)
) as BundleTarget;

const configs: any = {};

for (const key of Object.keys(
this.readerMapping
) as (keyof BundleValidateState)[]) {
configs[key] = this.readerMapping[key]?.(targetObject);
}

return {...configs, ...targetObject} as BundleValidateState;
}

@Mutex.synchronise("mutex")
public async load<T extends keyof BundleValidateState>(
keys: T[] = []
): Promise<Partial<Pick<BundleValidateState, T>> | undefined> {
if (keys.length === 0) {
return await this.stateCache.value;
}

const target = await this.stateCache.value;
const configs: Partial<{
[K in T]: BundleValidateState[K];
}> = {};

for (const key of keys) {
configs[key] = this.readerMapping[key]?.(target);
}

return configs;
return {
clusterId: validateOutput?.bundle?.compute_id,
remoteRootPath: validateOutput?.workspace?.file_path,
...validateOutput,
};
}

dispose() {
Expand Down
22 changes: 11 additions & 11 deletions packages/databricks-vscode/src/configuration/ConnectionManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ export class ConnectionManager implements Disposable {
popup: {prefix: "Error attaching sync destination: "},
})
private async updateSyncDestinationMapper() {
const workspacePath = await this.configModel.get("workspaceFsPath");
const workspacePath = await this.configModel.get("remoteRootPath");
const remoteUri = workspacePath
? new RemoteUri(workspacePath)
: undefined;
Expand Down Expand Up @@ -116,19 +116,16 @@ export class ConnectionManager implements Disposable {
await this.loginWithSavedAuth();

this.disposables.push(
this.configModel.onDidChange("workspaceFsPath")(
this.configModel.onDidChangeKey("remoteRootPath")(
this.updateSyncDestinationMapper,
this
),
this.configModel.onDidChange("clusterId")(
this.configModel.onDidChangeKey("clusterId")(
this.updateClusterManager,
this
),
this.configModel.onDidChange("target")(
this.loginWithSavedAuth,
this
),
this.configModel.onDidChange("authParams")(async () => {
this.configModel.onDidChangeTarget(this.loginWithSavedAuth, this),
this.configModel.onDidChangeKey("authParams")(async () => {
const config = await this.configModel.getS("authParams");
if (config === undefined) {
return;
Expand Down Expand Up @@ -290,7 +287,10 @@ export class ConnectionManager implements Disposable {
return;
}

const config = await configureWorkspaceWizard(this.cli, host);
const config = await configureWorkspaceWizard(
this.cli,
host.toString()
);
if (!config) {
return;
}
Expand Down Expand Up @@ -326,14 +326,14 @@ export class ConnectionManager implements Disposable {
);
return;
}
await this.configModel.set("workspaceFsPath", remoteWorkspace.path);
await this.configModel.set("remoteRootPath", remoteWorkspace.path);
}

@onError({
popup: {prefix: "Can't detach sync destination. "},
})
async detachSyncDestination(): Promise<void> {
await this.configModel.set("workspaceFsPath", undefined);
await this.configModel.set("remoteRootPath", undefined);
}

private updateState(newState: ConnectionState) {
Expand Down
Loading

0 comments on commit 7acb093

Please sign in to comment.