Skip to content

Commit dd76f3f

Browse files
authored
fix: store diagram with database COMPASS-9718 (#7489)
1 parent 6cff528 commit dd76f3f

14 files changed

+103
-73
lines changed

packages/compass-data-modeling/src/components/diagram-card.spec.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ describe('DiagramCard', () => {
99
diagram: {
1010
id: 'test-diagram',
1111
connectionId: 'test-connection',
12+
database: 'someDatabase',
1213
name: 'Test Diagram',
1314
createdAt: '2021-10-01T00:00:00.000Z',
1415
updatedAt: '2023-10-03T00:00:00.000',
@@ -31,7 +32,6 @@ describe('DiagramCard', () => {
3132
},
3233
},
3334
] as [Edit],
34-
databases: 'someDatabase',
3535
},
3636
onOpen: () => {},
3737
onDelete: () => {},

packages/compass-data-modeling/src/components/diagram-card.tsx

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -78,9 +78,7 @@ export function DiagramCard({
7878
onRename,
7979
onDelete,
8080
}: {
81-
diagram: MongoDBDataModelDescription & {
82-
databases: string;
83-
};
81+
diagram: MongoDBDataModelDescription;
8482
onOpen: (diagram: MongoDBDataModelDescription) => void;
8583
onRename: (id: string) => void;
8684
onDelete: (id: string) => void;
@@ -132,7 +130,7 @@ export function DiagramCard({
132130
color={palette.gray.dark1}
133131
className={namespaceIconStyles}
134132
></Icon>
135-
<span className={namespaceNameStyles}>{diagram.databases}</span>
133+
<span className={namespaceNameStyles}>{diagram.database}</span>
136134
</div>
137135
<div className={lastModifiedLabel}>
138136
Last&nbsp;modified: {formattedDate}

packages/compass-data-modeling/src/components/diagram-editor.spec.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@ const storageItems: MongoDBDataModelDescription[] = [
5858
},
5959
],
6060
connectionId: null,
61+
database: 'db1',
6162
},
6263
{
6364
id: 'new-diagram-id',
@@ -91,6 +92,7 @@ const storageItems: MongoDBDataModelDescription[] = [
9192
},
9293
],
9394
connectionId: null,
95+
database: 'db1',
9496
},
9597
];
9698

packages/compass-data-modeling/src/components/saved-diagrams-list.spec.tsx

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ const storageItems: MongoDBDataModelDescription[] = [
3737
},
3838
],
3939
connectionId: null,
40+
database: 'db1',
4041
},
4142
{
4243
id: '2',
@@ -63,6 +64,7 @@ const storageItems: MongoDBDataModelDescription[] = [
6364
},
6465
],
6566
connectionId: null,
67+
database: 'db2',
6668
},
6769
{
6870
id: '3',
@@ -89,6 +91,7 @@ const storageItems: MongoDBDataModelDescription[] = [
8991
},
9092
],
9193
connectionId: null,
94+
database: 'db3',
9295
},
9396
];
9497

packages/compass-data-modeling/src/components/saved-diagrams-list.tsx

Lines changed: 3 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@ import {
1616
import { useDataModelSavedItems } from '../provider';
1717
import {
1818
deleteDiagram,
19-
selectCurrentModel,
2019
openDiagram,
2120
openDiagramFromFile,
2221
renameDiagram,
@@ -27,7 +26,6 @@ import SchemaVisualizationIcon from './icons/schema-visualization';
2726
import FlexibilityIcon from './icons/flexibility';
2827
import { CARD_HEIGHT, CARD_WIDTH, DiagramCard } from './diagram-card';
2928
import { DiagramListToolbar } from './diagram-list-toolbar';
30-
import toNS from 'mongodb-ns';
3129
import { ImportDiagramButton } from './import-diagram-button';
3230

3331
const sortBy = [
@@ -181,18 +179,6 @@ const DiagramListEmptyContent: React.FunctionComponent<{
181179
);
182180
};
183181

184-
const getDatabase = (
185-
modelDescription: MongoDBDataModelDescription
186-
): string[] => {
187-
try {
188-
return selectCurrentModel(modelDescription.edits).collections.map(
189-
({ ns }) => toNS(ns).database
190-
);
191-
} catch {
192-
return [];
193-
}
194-
};
195-
196182
export const SavedDiagramsList: React.FunctionComponent<{
197183
onCreateDiagramClick: () => void;
198184
onOpenDiagramClick: (diagram: MongoDBDataModelDescription) => void;
@@ -207,30 +193,15 @@ export const SavedDiagramsList: React.FunctionComponent<{
207193
onImportDiagramClick,
208194
}) => {
209195
const { items, status } = useDataModelSavedItems();
210-
const decoratedItems = useMemo<
211-
(MongoDBDataModelDescription & {
212-
databases: string;
213-
})[]
214-
>(() => {
215-
return items.map((item) => {
216-
const databases = new Set(getDatabase(item));
217-
return {
218-
...item,
219-
databases: Array.from(databases).join(', '),
220-
};
221-
});
222-
}, [items]);
223196
const [search, setSearch] = useState('');
224197
const filteredItems = useMemo(() => {
225198
try {
226199
const regex = new RegExp(search, 'i');
227-
return decoratedItems.filter(
228-
(x) => regex.test(x.name) || (x.databases && regex.test(x.databases))
229-
);
200+
return items.filter((x) => regex.test(x.name) || regex.test(x.database));
230201
} catch {
231-
return decoratedItems;
202+
return items;
232203
}
233-
}, [decoratedItems, search]);
204+
}, [items, search]);
234205
const [sortControls, sortState] = useSortControls(sortBy);
235206
const sortedItems = useSortedItems(filteredItems, sortState);
236207

packages/compass-data-modeling/src/services/data-model-storage.ts

Lines changed: 30 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { z } from '@mongodb-js/compass-user-data';
2+
import toNS from 'mongodb-ns';
23
import type { MongoDBJSONSchema } from 'mongodb-schema';
34

45
export const FieldPathSchema = z.array(z.string());
@@ -170,20 +171,35 @@ export const validateEdit = (
170171
}
171172
};
172173

173-
export const MongoDBDataModelDescriptionSchema = z.object({
174-
id: z.string(),
175-
name: z.string(),
176-
/**
177-
* Connection id associated with the data model at the moment of configuring
178-
* and analyzing. No connection id means diagram was imported and not attached
179-
* to a connection. Practically speaking it just means that we can't do
180-
* anything that would require re-fetching data associated with the diagram
181-
*/
182-
connectionId: z.string().nullable(),
183-
edits: EditListSchema,
184-
createdAt: z.string().datetime(),
185-
updatedAt: z.string().datetime(),
186-
});
174+
export const MongoDBDataModelDescriptionSchema = z.preprocess(
175+
(val) => {
176+
const model = val as Record<string, unknown>;
177+
if (
178+
!model.database &&
179+
Array.isArray(model.edits) &&
180+
model.edits.length > 0
181+
) {
182+
// Infer database from the first collection's namespace in the 'SetModel' edit
183+
model.database = toNS(model.edits?.[0].model.collections[0].ns).database;
184+
}
185+
return model;
186+
},
187+
z.object({
188+
id: z.string(),
189+
name: z.string(),
190+
/**
191+
* Connection id associated with the data model at the moment of configuring
192+
* and analyzing. No connection id means diagram was imported and not attached
193+
* to a connection. Practically speaking it just means that we can't do
194+
* anything that would require re-fetching data associated with the diagram
195+
*/
196+
connectionId: z.string().nullable(),
197+
database: z.string(),
198+
edits: EditListSchema,
199+
createdAt: z.string().datetime(),
200+
updatedAt: z.string().datetime(),
201+
})
202+
);
187203

188204
export type MongoDBDataModelDescription = z.output<
189205
typeof MongoDBDataModelDescriptionSchema

packages/compass-data-modeling/src/services/open-and-download-diagram.spec.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,15 +9,18 @@ import FlightDiagram from '../../test/fixtures/data-model-with-relationships.jso
99
describe('open-and-download-diagram', function () {
1010
it('should return correct content to download', function () {
1111
const fileName = 'test-diagram';
12+
const database = 'test-database';
1213

1314
const { edits, ...restOfContent } = getDownloadDiagramContent(
1415
fileName,
15-
FlightDiagram.edits as any
16+
FlightDiagram.edits as any,
17+
database
1618
);
1719
expect(restOfContent).to.deep.equal({
1820
type: 'Compass Data Modeling Diagram',
1921
version: 1,
2022
name: fileName,
23+
database,
2124
});
2225

2326
const decodedEdits = JSON.parse(
@@ -132,6 +135,7 @@ describe('open-and-download-diagram', function () {
132135
version: 1,
133136
type: 'Compass Data Modeling Diagram',
134137
name: 'Test Diagram',
138+
database: 'test',
135139
edits: Buffer.from(
136140
JSON.stringify([{ type: 'NonExistent' }])
137141
).toString('base64'),
@@ -146,6 +150,7 @@ describe('open-and-download-diagram', function () {
146150
version: 1,
147151
type: 'Compass Data Modeling Diagram',
148152
name: 'Test Diagram',
153+
database: 'test',
149154
edits: Buffer.from(
150155
JSON.stringify([
151156
{
@@ -179,6 +184,7 @@ describe('open-and-download-diagram', function () {
179184
version: 1,
180185
type: 'Compass Data Modeling Diagram',
181186
name: 'Test Diagram',
187+
database: 'test-database',
182188
edits: Buffer.from(JSON.stringify(FlightDiagram.edits)).toString(
183189
'base64'
184190
),

packages/compass-data-modeling/src/services/open-and-download-diagram.ts

Lines changed: 26 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,19 @@ import { z } from '@mongodb-js/compass-user-data';
66
const kCurrentVersion = 1;
77
const kFileTypeDescription = 'Compass Data Modeling Diagram';
88

9-
export function downloadDiagram(fileName: string, edits: Edit[]) {
9+
export function downloadDiagram(
10+
fileName: string,
11+
edits: Edit[],
12+
database: string
13+
) {
1014
const blob = new Blob(
11-
[JSON.stringify(getDownloadDiagramContent(fileName, edits), null, 2)],
15+
[
16+
JSON.stringify(
17+
getDownloadDiagramContent(fileName, edits, database),
18+
null,
19+
2
20+
),
21+
],
1222
{
1323
type: 'application/json',
1424
}
@@ -19,18 +29,25 @@ export function downloadDiagram(fileName: string, edits: Edit[]) {
1929
});
2030
}
2131

22-
export function getDownloadDiagramContent(name: string, edits: Edit[]) {
32+
export function getDownloadDiagramContent(
33+
name: string,
34+
edits: Edit[],
35+
database: string
36+
) {
2337
return {
2438
type: kFileTypeDescription,
2539
version: kCurrentVersion,
2640
name,
41+
database,
2742
edits: Buffer.from(JSON.stringify(edits)).toString('base64'),
2843
};
2944
}
3045

31-
export async function getDiagramContentsFromFile(
32-
file: File
33-
): Promise<{ name: string; edits: [SetModelEdit, ...Edit[]] }> {
46+
export async function getDiagramContentsFromFile(file: File): Promise<{
47+
name: string;
48+
edits: [SetModelEdit, ...Edit[]];
49+
database: string;
50+
}> {
3451
const reader = new FileReader();
3552
return new Promise((resolve, reject) => {
3653
reader.onload = (event) => {
@@ -48,9 +65,9 @@ export async function getDiagramContentsFromFile(
4865
throw new Error('Unsupported diagram file format');
4966
}
5067

51-
const { name, edits } = parsedContent;
68+
const { name, edits, database } = parsedContent;
5269

53-
if (!name || !edits || typeof edits !== 'string') {
70+
if (!name || !edits || typeof edits !== 'string' || !database) {
5471
throw new Error('Diagram file is missing required fields');
5572
}
5673

@@ -63,6 +80,7 @@ export async function getDiagramContentsFromFile(
6380
return resolve({
6481
name: parsedContent.name,
6582
edits: [validEdits[0] as SetModelEdit, ...validEdits.slice(1)],
83+
database: parsedContent.database,
6684
});
6785
} catch (error) {
6886
const message =

packages/compass-data-modeling/src/store/analysis-process.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,7 @@ export type AnalysisFinishedAction = {
7171
type: AnalysisProcessActionTypes.ANALYSIS_FINISHED;
7272
name: string;
7373
connectionId: string;
74+
database: string;
7475
collections: {
7576
ns: string;
7677
schema: MongoDBJSONSchema;
@@ -315,6 +316,7 @@ export function startAnalysis(
315316
type: AnalysisProcessActionTypes.ANALYSIS_FINISHED,
316317
name,
317318
connectionId,
319+
database,
318320
collections: collections.map((coll) => {
319321
const node = positioned.nodes.find((node) => {
320322
return node.id === coll.ns;

packages/compass-data-modeling/src/store/diagram.spec.ts

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,10 @@ import type {
1919
} from '../services/data-model-storage';
2020
import { UUID } from 'bson';
2121
import Sinon from 'sinon';
22+
import {
23+
type AnalysisFinishedAction,
24+
AnalysisProcessActionTypes,
25+
} from './analysis-process';
2226

2327
const model: StaticModel = {
2428
collections: [
@@ -61,6 +65,7 @@ const loadedDiagram: MongoDBDataModelDescription = {
6165
id: 'diagram-id',
6266
name: 'diagram-name',
6367
connectionId: 'connection-id',
68+
database: 'db',
6469
createdAt: '2023-10-01T00:00:00.000Z',
6570
updatedAt: '2023-10-05T00:00:00.000Z',
6671
edits: [{ type: 'SetModel', model } as Edit],
@@ -81,6 +86,7 @@ describe('Data Modeling store', function () {
8186
const newDiagram = {
8287
name: 'New Diagram',
8388
connectionId: 'connection-id',
89+
database: 'db',
8490
collections: [
8591
{
8692
ns: 'db.collection1',
@@ -95,14 +101,16 @@ describe('Data Modeling store', function () {
95101
],
96102
relations: model.relationships,
97103
};
98-
store.dispatch({
99-
type: 'data-modeling/analysis-stats/ANALYSIS_FINISHED',
104+
const analysisFinishedAction: AnalysisFinishedAction = {
105+
type: AnalysisProcessActionTypes.ANALYSIS_FINISHED,
100106
...newDiagram,
101-
});
107+
};
108+
store.dispatch(analysisFinishedAction);
102109

103110
const initialDiagram = getCurrentDiagramFromState(store.getState());
104111
expect(initialDiagram.name).to.equal(newDiagram.name);
105112
expect(initialDiagram.connectionId).to.equal(newDiagram.connectionId);
113+
expect(initialDiagram.database).to.equal(newDiagram.database);
106114
expect(initialDiagram.edits).to.have.length(1);
107115
expect(initialDiagram.edits[0].type).to.equal('SetModel');
108116
const initialEdit = initialDiagram.edits[0] as Extract<
@@ -133,6 +141,7 @@ describe('Data Modeling store', function () {
133141
expect(diagram.id).to.equal(loadedDiagram.id);
134142
expect(diagram.name).to.equal(loadedDiagram.name);
135143
expect(diagram.connectionId).to.equal(loadedDiagram.connectionId);
144+
expect(diagram.database).to.equal(loadedDiagram.database);
136145
expect(diagram.edits).to.deep.equal(loadedDiagram.edits);
137146
});
138147
});

0 commit comments

Comments
 (0)