Skip to content

Commit 18c3f4a

Browse files
committed
Working with build time impls
1 parent fc98fd8 commit 18c3f4a

File tree

10 files changed

+515
-20
lines changed

10 files changed

+515
-20
lines changed

packages/compass-web/src/entrypoint.tsx

Lines changed: 7 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -64,12 +64,11 @@ import { WorkspaceTab as MyQueriesWorkspace } from '@mongodb-js/compass-saved-ag
6464
import { useCompassWebPreferences } from './preferences';
6565
import { DataModelingWorkspaceTab as DataModelingWorkspace } from '@mongodb-js/compass-data-modeling';
6666
import { DataModelStorageServiceProviderInMemory } from '@mongodb-js/compass-data-modeling/web';
67-
// My Queries storage (web variant uses Atlas user data backend)
6867
import {
69-
CompassFavoriteQueryStorage,
70-
CompassPipelineStorage,
71-
CompassRecentQueryStorage,
72-
} from '@mongodb-js/my-queries-storage';
68+
WebCompassFavoriteQueryStorage,
69+
WebCompassPipelineStorage,
70+
WebCompassRecentQueryStorage,
71+
} from '@mongodb-js/my-queries-storage/web';
7372
import {
7473
PipelineStorageProvider,
7574
FavoriteQueryStorageProvider,
@@ -123,8 +122,7 @@ const WithStorageProviders = createServiceProvider(
123122

124123
const pipelineStorage = useRef<PipelineStorageAccess>({
125124
getStorage(options) {
126-
return new CompassPipelineStorage({
127-
...options,
125+
return new WebCompassPipelineStorage({
128126
orgId,
129127
projectId,
130128
getResourceUrl,
@@ -134,8 +132,7 @@ const WithStorageProviders = createServiceProvider(
134132
});
135133
const favoriteQueryStorage = useRef<FavoriteQueryStorageAccess>({
136134
getStorage(options) {
137-
return new CompassFavoriteQueryStorage({
138-
...options,
135+
return new WebCompassFavoriteQueryStorage({
139136
orgId,
140137
projectId,
141138
getResourceUrl,
@@ -145,8 +142,7 @@ const WithStorageProviders = createServiceProvider(
145142
});
146143
const recentQueryStorage = useRef<RecentQueryStorageAccess>({
147144
getStorage(options) {
148-
return new CompassRecentQueryStorage({
149-
...options,
145+
return new WebCompassRecentQueryStorage({
150146
orgId,
151147
projectId,
152148
getResourceUrl,

packages/compass/src/app/components/entrypoint.tsx

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,10 @@ import {
99
} from '@mongodb-js/atlas-service/provider';
1010
import { AtlasAiServiceProvider } from '@mongodb-js/compass-generative-ai/provider';
1111
import {
12-
CompassFavoriteQueryStorage,
13-
CompassPipelineStorage,
14-
CompassRecentQueryStorage,
15-
} from '@mongodb-js/my-queries-storage';
12+
ElectronCompassFavoriteQueryStorage,
13+
ElectronCompassPipelineStorage,
14+
ElectronCompassRecentQueryStorage,
15+
} from '@mongodb-js/my-queries-storage/electron';
1616
import {
1717
PipelineStorageProvider,
1818
FavoriteQueryStorageProvider,
@@ -74,17 +74,17 @@ export const WithAtlasProviders: React.FC = ({ children }) => {
7474
export const WithStorageProviders: React.FC = ({ children }) => {
7575
const pipelineStorage = useRef<PipelineStorageAccess>({
7676
getStorage(options) {
77-
return new CompassPipelineStorage(options);
77+
return new ElectronCompassPipelineStorage(options);
7878
},
7979
});
8080
const favoriteQueryStorage = useRef<FavoriteQueryStorageAccess>({
8181
getStorage(options) {
82-
return new CompassFavoriteQueryStorage(options);
82+
return new ElectronCompassFavoriteQueryStorage(options);
8383
},
8484
});
8585
const recentQueryStorage = useRef<RecentQueryStorageAccess>({
8686
getStorage(options) {
87-
return new CompassRecentQueryStorage(options);
87+
return new ElectronCompassRecentQueryStorage(options);
8888
},
8989
});
9090
return (

packages/my-queries-storage/package.json

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,11 +30,15 @@
3030
"import": "./dist/.esm-wrapper.mjs",
3131
"require": "./dist/index.js"
3232
},
33-
"./provider": "./dist/provider.js"
33+
"./provider": "./dist/provider.js",
34+
"./web": "./dist/web-exports.js",
35+
"./electron": "./dist/electron-exports.js"
3436
},
3537
"compass:exports": {
3638
".": "./src/index.ts",
37-
"./provider": "./src/provider.ts"
39+
"./provider": "./src/provider.ts",
40+
"./web": "./src/web-exports.ts",
41+
"./electron": "./src/electron-exports.ts"
3842
},
3943
"types": "./dist/index.d.ts",
4044
"scripts": {
Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
import { FileUserData } from '@mongodb-js/compass-user-data';
2+
import { PipelineSchema } from './pipeline-storage-schema';
3+
import type { SavedPipeline } from './pipeline-storage-schema';
4+
import type { PipelineStorageInterface } from './storage-interfaces';
5+
6+
export type ElectronPipelineStorageOptions = {
7+
basePath?: string;
8+
};
9+
10+
export class ElectronCompassPipelineStorage
11+
implements PipelineStorageInterface
12+
{
13+
private readonly userData: FileUserData<typeof PipelineSchema>;
14+
15+
constructor(options: ElectronPipelineStorageOptions = {}) {
16+
this.userData = new FileUserData(PipelineSchema, 'SavedPipelines', {
17+
basePath: options.basePath,
18+
});
19+
}
20+
21+
async loadAll(): Promise<SavedPipeline[]> {
22+
try {
23+
const { data } = await this.userData.readAll();
24+
return data;
25+
} catch {
26+
return [];
27+
}
28+
}
29+
30+
/** loads all pipelines that satisfy `predicate` */
31+
loadMany(
32+
predicate: (arg0: SavedPipeline) => boolean
33+
): Promise<SavedPipeline[]> {
34+
return this.loadAll().then((pipelines) => pipelines.filter(predicate));
35+
}
36+
37+
async createOrUpdate(
38+
id: string,
39+
attributes: Omit<SavedPipeline, 'lastModified'>
40+
): Promise<boolean> {
41+
const pipelineExists = Boolean(await this.userData.readOne(id));
42+
return await (pipelineExists
43+
? this.updateAttributes(id, attributes)
44+
: this.create(attributes));
45+
}
46+
47+
async create(data: Omit<SavedPipeline, 'lastModified'>): Promise<boolean> {
48+
try {
49+
await this.userData.write(data.id, {
50+
...data,
51+
lastModified: Date.now(),
52+
});
53+
return true;
54+
} catch {
55+
return false;
56+
}
57+
}
58+
59+
async updateAttributes(
60+
id: string,
61+
attributes: Partial<SavedPipeline>
62+
): Promise<boolean> {
63+
try {
64+
await this.userData.write(id, {
65+
...(await this.userData.readOne(id)),
66+
...attributes,
67+
lastModified: Date.now(),
68+
});
69+
return true;
70+
} catch {
71+
return false;
72+
}
73+
}
74+
75+
async delete(id: string): Promise<void> {
76+
await this.userData.delete(id);
77+
}
78+
}
Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
import { EJSON } from 'bson';
2+
import { AtlasUserData } from '@mongodb-js/compass-user-data';
3+
import { PipelineSchema } from './pipeline-storage-schema';
4+
import type { SavedPipeline } from './pipeline-storage-schema';
5+
import type { PipelineStorageInterface } from './storage-interfaces';
6+
7+
export type WebPipelineStorageOptions = {
8+
orgId: string;
9+
projectId: string;
10+
getResourceUrl: (path?: string) => string;
11+
authenticatedFetch: (
12+
url: RequestInfo | URL,
13+
options?: RequestInit
14+
) => Promise<Response>;
15+
};
16+
17+
export class WebCompassPipelineStorage implements PipelineStorageInterface {
18+
private readonly userData: AtlasUserData<typeof PipelineSchema>;
19+
20+
constructor(options: WebPipelineStorageOptions) {
21+
this.userData = new AtlasUserData(PipelineSchema, 'favoriteAggregations', {
22+
orgId: options.orgId,
23+
projectId: options.projectId,
24+
getResourceUrl: options.getResourceUrl,
25+
authenticatedFetch: options.authenticatedFetch,
26+
serialize: (content) => EJSON.stringify(content),
27+
deserialize: (content: string) => EJSON.parse(content),
28+
});
29+
}
30+
31+
async loadAll(): Promise<SavedPipeline[]> {
32+
try {
33+
const { data } = await this.userData.readAll();
34+
return data;
35+
} catch {
36+
return [];
37+
}
38+
}
39+
40+
/** loads all pipelines that satisfy `predicate` */
41+
loadMany(
42+
predicate: (arg0: SavedPipeline) => boolean
43+
): Promise<SavedPipeline[]> {
44+
return this.loadAll().then((pipelines) => pipelines.filter(predicate));
45+
}
46+
47+
async createOrUpdate(
48+
id: string,
49+
attributes: Omit<SavedPipeline, 'lastModified'>
50+
): Promise<boolean> {
51+
const pipelineExists = Boolean(await this.userData.readOne(id));
52+
return await (pipelineExists
53+
? this.updateAttributes(id, attributes)
54+
: this.create(attributes));
55+
}
56+
57+
async create(data: Omit<SavedPipeline, 'lastModified'>): Promise<boolean> {
58+
try {
59+
await this.userData.write(data.id, {
60+
...data,
61+
lastModified: Date.now(),
62+
});
63+
return true;
64+
} catch {
65+
return false;
66+
}
67+
}
68+
69+
async updateAttributes(
70+
id: string,
71+
attributes: Partial<SavedPipeline>
72+
): Promise<boolean> {
73+
try {
74+
await this.userData.write(id, {
75+
...(await this.userData.readOne(id)),
76+
...attributes,
77+
lastModified: Date.now(),
78+
});
79+
return true;
80+
} catch {
81+
return false;
82+
}
83+
}
84+
85+
async delete(id: string): Promise<void> {
86+
await this.userData.delete(id);
87+
}
88+
}
Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
import { ObjectId, EJSON, UUID } from 'bson';
2+
import { type z } from '@mongodb-js/compass-user-data';
3+
import { FileUserData } from '@mongodb-js/compass-user-data';
4+
import { RecentQuerySchema, FavoriteQuerySchema } from './query-storage-schema';
5+
import type {
6+
RecentQueryStorageInterface,
7+
FavoriteQueryStorageInterface,
8+
} from './storage-interfaces';
9+
10+
export type ElectronQueryStorageOptions = {
11+
basepath?: string;
12+
};
13+
14+
export abstract class ElectronCompassQueryStorage<TSchema extends z.Schema> {
15+
protected readonly userData: FileUserData<TSchema>;
16+
17+
constructor(
18+
schemaValidator: TSchema,
19+
protected readonly dataType: string,
20+
protected readonly options: ElectronQueryStorageOptions
21+
) {
22+
this.userData = new FileUserData(schemaValidator, dataType, {
23+
basePath: options.basepath,
24+
serialize: (content) => EJSON.stringify(content, undefined, 2),
25+
deserialize: (content: string) => EJSON.parse(content),
26+
});
27+
}
28+
29+
async loadAll(namespace?: string): Promise<z.output<TSchema>[]> {
30+
try {
31+
const { data } = await this.userData.readAll();
32+
const sortedData = data
33+
.sort((a, b) => {
34+
return b._lastExecuted.getTime() - a._lastExecuted.getTime();
35+
})
36+
.filter((x) => !namespace || x._ns === namespace);
37+
return sortedData;
38+
} catch {
39+
return [];
40+
}
41+
}
42+
43+
async write(id: string, content: z.input<TSchema>): Promise<boolean> {
44+
return await this.userData.write(id, content);
45+
}
46+
47+
async delete(id: string): Promise<boolean> {
48+
return await this.userData.delete(id);
49+
}
50+
51+
async updateAttributes(
52+
id: string,
53+
data: Partial<z.input<TSchema>>
54+
): Promise<boolean> {
55+
return await this.userData.updateAttributes(id, data);
56+
}
57+
58+
abstract saveQuery(data: Partial<z.input<TSchema>>): Promise<void>;
59+
}
60+
61+
export class ElectronCompassRecentQueryStorage
62+
extends ElectronCompassQueryStorage<typeof RecentQuerySchema>
63+
implements RecentQueryStorageInterface
64+
{
65+
private readonly maxAllowedQueries = 30;
66+
67+
constructor(options: ElectronQueryStorageOptions = {}) {
68+
super(RecentQuerySchema, 'RecentQueries', options);
69+
}
70+
71+
async saveQuery(
72+
data: Omit<z.input<typeof RecentQuerySchema>, '_id' | '_lastExecuted'>
73+
): Promise<void> {
74+
const recentQueries = await this.loadAll();
75+
if (recentQueries.length >= this.maxAllowedQueries) {
76+
const lastRecent = recentQueries[recentQueries.length - 1];
77+
await this.delete(lastRecent._id);
78+
}
79+
const _id = new UUID().toString();
80+
const recentQuery = {
81+
...data,
82+
_id,
83+
_lastExecuted: new Date(),
84+
};
85+
await this.userData.write(_id, recentQuery);
86+
}
87+
}
88+
89+
export class ElectronCompassFavoriteQueryStorage
90+
extends ElectronCompassQueryStorage<typeof FavoriteQuerySchema>
91+
implements FavoriteQueryStorageInterface
92+
{
93+
constructor(options: ElectronQueryStorageOptions = {}) {
94+
super(FavoriteQuerySchema, 'FavoriteQueries', options);
95+
}
96+
97+
async saveQuery(
98+
data: Omit<
99+
z.input<typeof FavoriteQuerySchema>,
100+
'_id' | '_lastExecuted' | '_dateModified' | '_dateSaved'
101+
>,
102+
_id?: string
103+
): Promise<void> {
104+
_id ??= new ObjectId().toHexString();
105+
const favoriteQuery = {
106+
...data,
107+
_id,
108+
_lastExecuted: new Date(),
109+
_dateSaved: new Date(),
110+
};
111+
await this.userData.write(_id, favoriteQuery);
112+
}
113+
}

0 commit comments

Comments
 (0)