Skip to content

Commit 1e939ef

Browse files
authored
feat(save-user-data): create a user data abstract class COMPASS-9543 (#7103)
* feat(save-user-data): create a user data abstract class * fix: rm defaults and add semaphore to FileUserData
1 parent 120fab3 commit 1e939ef

File tree

11 files changed

+102
-67
lines changed

11 files changed

+102
-67
lines changed

packages/atlas-service/src/secret-store.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
1-
import { UserData, z } from '@mongodb-js/compass-user-data';
1+
import { FileUserData, z } from '@mongodb-js/compass-user-data';
22
import { safeStorage } from 'electron';
33

44
const AtlasPluginStateSchema = z.string().optional();
55

66
export class SecretStore {
7-
private readonly userData: UserData<typeof AtlasPluginStateSchema>;
7+
private readonly userData: FileUserData<typeof AtlasPluginStateSchema>;
88
private readonly fileName = 'AtlasPluginState';
99
constructor(basePath?: string) {
10-
this.userData = new UserData(AtlasPluginStateSchema, {
10+
this.userData = new FileUserData(AtlasPluginStateSchema, {
1111
subdir: 'AtlasState',
1212
basePath,
1313
});

packages/compass-data-modeling/src/services/data-model-storage-electron.tsx

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import React from 'react';
2-
import { UserData } from '@mongodb-js/compass-user-data';
2+
import { FileUserData } from '@mongodb-js/compass-user-data';
33
import type {
44
DataModelStorage,
55
MongoDBDataModelDescription,
@@ -8,9 +8,11 @@ import { MongoDBDataModelDescriptionSchema } from './data-model-storage';
88
import { DataModelStorageServiceProvider } from '../provider';
99

1010
class DataModelStorageElectron implements DataModelStorage {
11-
private readonly userData: UserData<typeof MongoDBDataModelDescriptionSchema>;
11+
private readonly userData: FileUserData<
12+
typeof MongoDBDataModelDescriptionSchema
13+
>;
1214
constructor(basePath?: string) {
13-
this.userData = new UserData(MongoDBDataModelDescriptionSchema, {
15+
this.userData = new FileUserData(MongoDBDataModelDescriptionSchema, {
1416
subdir: 'DataModelDescriptions',
1517
basePath,
1618
});

packages/compass-preferences-model/src/preferences-persistent-storage.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { type z, UserData } from '@mongodb-js/compass-user-data';
1+
import { type z, FileUserData } from '@mongodb-js/compass-user-data';
22
import {
33
getDefaultsForStoredPreferences,
44
getPreferencesValidator,
@@ -20,12 +20,12 @@ export type PreferencesSafeStorage = {
2020
export class PersistentStorage implements PreferencesStorage {
2121
private readonly file = 'General';
2222
private readonly defaultPreferences = getDefaultsForStoredPreferences();
23-
private readonly userData: UserData<StoredPreferencesValidator>;
23+
private readonly userData: FileUserData<StoredPreferencesValidator>;
2424
private preferences: StoredPreferences = getDefaultsForStoredPreferences();
2525
private safeStorage?: PreferencesSafeStorage;
2626

2727
constructor(basePath?: string, safeStorage?: PreferencesSafeStorage) {
28-
this.userData = new UserData(getPreferencesValidator(), {
28+
this.userData = new FileUserData(getPreferencesValidator(), {
2929
subdir: 'AppPreferences',
3030
basePath,
3131
});

packages/compass-preferences-model/src/user-storage.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { z } from '@mongodb-js/compass-user-data';
22
import { UUID } from 'bson';
3-
import { UserData } from '@mongodb-js/compass-user-data';
3+
import { FileUserData } from '@mongodb-js/compass-user-data';
44

55
const UserSchema = z.object({
66
id: z.string().uuid(),
@@ -24,9 +24,9 @@ export interface UserStorage {
2424
}
2525

2626
export class UserStorageImpl implements UserStorage {
27-
private readonly userData: UserData<typeof UserSchema>;
27+
private readonly userData: FileUserData<typeof UserSchema>;
2828
constructor(basePath?: string) {
29-
this.userData = new UserData(UserSchema, {
29+
this.userData = new FileUserData(UserSchema, {
3030
subdir: 'Users',
3131
basePath,
3232
});

packages/compass-shell/src/modules/history-storage.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
import { getAppName } from '@mongodb-js/compass-utils';
2-
import { UserData, z } from '@mongodb-js/compass-user-data';
2+
import { FileUserData, z } from '@mongodb-js/compass-user-data';
33

44
export class HistoryStorage {
55
fileName = 'shell-history';
66
userData;
77

88
constructor(basePath?: string) {
9-
this.userData = new UserData(z.string().array(), {
9+
this.userData = new FileUserData(z.string().array(), {
1010
// Todo: https://jira.mongodb.org/browse/COMPASS-7080
1111
subdir: getAppName() ?? '',
1212
basePath,
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
export type { Stats, ReadAllResult, ReadAllWithStatsResult } from './user-data';
2-
export { UserData } from './user-data';
2+
export { IUserData, FileUserData } from './user-data';
33
export { z } from 'zod';

packages/compass-user-data/src/user-data.spec.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import { Stats } from 'fs';
33
import os from 'os';
44
import path from 'path';
55
import { expect } from 'chai';
6-
import { UserData, type UserDataOptions } from './user-data';
6+
import { FileUserData, type FileUserDataOptions } from './user-data';
77
import { z, type ZodError } from 'zod';
88

99
type ValidatorOptions = {
@@ -44,11 +44,11 @@ describe('user-data', function () {
4444

4545
const getUserData = (
4646
userDataOpts: Partial<
47-
UserDataOptions<z.input<ReturnType<typeof getTestSchema>>>
47+
FileUserDataOptions<z.input<ReturnType<typeof getTestSchema>>>
4848
> = {},
4949
validatorOpts: ValidatorOptions = {}
5050
) => {
51-
return new UserData(getTestSchema(validatorOpts), {
51+
return new FileUserData(getTestSchema(validatorOpts), {
5252
subdir,
5353
basePath: tmpDir,
5454
...userDataOpts,

packages/compass-user-data/src/user-data.ts

Lines changed: 53 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -12,14 +12,19 @@ type SerializeContent<I> = (content: I) => string;
1212
type DeserializeContent = (content: string) => unknown;
1313
type GetFileName = (id: string) => string;
1414

15-
export type UserDataOptions<Input> = {
15+
export type FileUserDataOptions<Input> = {
1616
subdir: string;
1717
basePath?: string;
1818
serialize?: SerializeContent<Input>;
1919
deserialize?: DeserializeContent;
2020
getFileName?: GetFileName;
2121
};
2222

23+
export type AtlasUserDataOptions<Input> = {
24+
serialize?: SerializeContent<Input>;
25+
deserialize?: DeserializeContent;
26+
};
27+
2328
type ReadOptions = {
2429
ignoreErrors: boolean;
2530
};
@@ -63,28 +68,54 @@ export interface ReadAllWithStatsResult<T extends z.Schema> {
6368
errors: Error[];
6469
}
6570

66-
export class UserData<T extends z.Schema> {
71+
export abstract class IUserData<T extends z.Schema> {
72+
protected readonly validator: T;
73+
protected readonly serialize: SerializeContent<z.input<T>>;
74+
protected readonly deserialize: DeserializeContent;
75+
76+
constructor(
77+
validator: T,
78+
{
79+
serialize = (content: z.input<T>) => JSON.stringify(content, null, 2),
80+
deserialize = JSON.parse,
81+
}: {
82+
serialize?: SerializeContent<z.input<T>>;
83+
deserialize?: DeserializeContent;
84+
} = {}
85+
) {
86+
this.validator = validator;
87+
this.serialize = serialize;
88+
this.deserialize = deserialize;
89+
}
90+
91+
abstract write(id: string, content: z.input<T>): Promise<boolean>;
92+
abstract delete(id: string): Promise<boolean>;
93+
abstract readAll(options?: ReadOptions): Promise<ReadAllResult<T>>;
94+
abstract updateAttributes(
95+
id: string,
96+
data: Partial<z.input<T>>
97+
): Promise<z.output<T>>;
98+
}
99+
100+
export class FileUserData<T extends z.Schema> extends IUserData<T> {
67101
private readonly subdir: string;
68102
private readonly basePath?: string;
69-
private readonly serialize: SerializeContent<z.input<T>>;
70-
private readonly deserialize: DeserializeContent;
71103
private readonly getFileName: GetFileName;
72-
private readonly semaphore = new Semaphore(100);
104+
protected readonly semaphore = new Semaphore(100);
73105

74106
constructor(
75-
private readonly validator: T,
107+
validator: T,
76108
{
77109
subdir,
78110
basePath,
79-
serialize = (content: z.input<T>) => JSON.stringify(content, null, 2),
80-
deserialize = JSON.parse,
111+
serialize,
112+
deserialize,
81113
getFileName = (id) => `${id}.json`,
82-
}: UserDataOptions<z.input<T>>
114+
}: FileUserDataOptions<z.input<T>>
83115
) {
116+
super(validator, { serialize, deserialize });
84117
this.subdir = subdir;
85118
this.basePath = basePath;
86-
this.deserialize = deserialize;
87-
this.serialize = serialize;
88119
this.getFileName = getFileName;
89120
}
90121

@@ -290,4 +321,15 @@ export class UserData<T extends z.Schema> {
290321
) {
291322
return (await this.readOneWithStats(id, options))?.[0];
292323
}
324+
325+
async updateAttributes(
326+
id: string,
327+
data: Partial<z.input<T>>
328+
): Promise<z.output<T>> {
329+
await this.write(id, {
330+
...((await this.readOne(id)) ?? {}),
331+
...data,
332+
});
333+
return await this.readOne(id);
334+
}
293335
}

packages/connection-storage/src/compass-main-connection-storage.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ import type {
2424
ImportConnectionOptions,
2525
ExportConnectionOptions,
2626
} from './import-export-connection';
27-
import { UserData, z } from '@mongodb-js/compass-user-data';
27+
import { FileUserData, z } from '@mongodb-js/compass-user-data';
2828
import type {
2929
ConnectionStorage,
3030
AutoConnectPreferences,
@@ -86,7 +86,7 @@ const ConnectionSchema: z.Schema<ConnectionWithLegacyProps> = z
8686
.passthrough();
8787

8888
class CompassMainConnectionStorage implements ConnectionStorage {
89-
private readonly userData: UserData<typeof ConnectionSchema>;
89+
private readonly userData: FileUserData<typeof ConnectionSchema>;
9090

9191
private readonly version = 1;
9292
private readonly maxAllowedRecentConnections = 10;
@@ -95,7 +95,7 @@ class CompassMainConnectionStorage implements ConnectionStorage {
9595
private readonly ipcMain: ConnectionStorageIPCMain,
9696
basePath?: string
9797
) {
98-
this.userData = new UserData(ConnectionSchema, {
98+
this.userData = new FileUserData(ConnectionSchema, {
9999
subdir: 'Connections',
100100
basePath,
101101
});

packages/my-queries-storage/src/compass-pipeline-storage.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
11
import type { Stats } from '@mongodb-js/compass-user-data';
2-
import { UserData } from '@mongodb-js/compass-user-data';
2+
import { FileUserData } from '@mongodb-js/compass-user-data';
33
import { PipelineSchema } from './pipeline-storage-schema';
44
import type { SavedPipeline } from './pipeline-storage-schema';
55
import type { PipelineStorage } from './pipeline-storage';
66

77
export class CompassPipelineStorage implements PipelineStorage {
8-
private readonly userData: UserData<typeof PipelineSchema>;
8+
private readonly userData: FileUserData<typeof PipelineSchema>;
99
constructor(basePath?: string) {
10-
this.userData = new UserData(PipelineSchema, {
10+
this.userData = new FileUserData(PipelineSchema, {
1111
subdir: 'SavedPipelines',
1212
basePath,
1313
});

0 commit comments

Comments
 (0)