From 10ee2e7e6576bdc601b184f4da03230397c5c19b Mon Sep 17 00:00:00 2001 From: Paula Stachova Date: Wed, 22 Oct 2025 18:47:25 +0200 Subject: [PATCH 1/4] fix: store diagram with database COMPASS-9718 --- .../src/components/diagram-card.spec.tsx | 2 +- .../src/components/diagram-card.tsx | 6 ++-- .../src/components/diagram-editor.spec.tsx | 2 ++ .../components/saved-diagrams-list.spec.tsx | 3 ++ .../src/components/saved-diagrams-list.tsx | 35 +++---------------- .../src/services/data-model-storage.ts | 1 + .../open-and-download-diagram.spec.ts | 8 ++++- .../src/services/open-and-download-diagram.ts | 34 +++++++++++++----- .../src/store/analysis-process.ts | 2 ++ .../src/store/diagram.spec.ts | 15 ++++++-- .../src/store/diagram.ts | 18 +++++----- .../src/store/export-diagram.ts | 2 +- .../data-model-with-relationships.json | 1 + .../tests/data-modeling-tab.test.ts | 4 ++- 14 files changed, 75 insertions(+), 58 deletions(-) diff --git a/packages/compass-data-modeling/src/components/diagram-card.spec.tsx b/packages/compass-data-modeling/src/components/diagram-card.spec.tsx index bd888b75007..246eca5b2a5 100644 --- a/packages/compass-data-modeling/src/components/diagram-card.spec.tsx +++ b/packages/compass-data-modeling/src/components/diagram-card.spec.tsx @@ -9,6 +9,7 @@ describe('DiagramCard', () => { diagram: { id: 'test-diagram', connectionId: 'test-connection', + database: 'someDatabase', name: 'Test Diagram', createdAt: '2021-10-01T00:00:00.000Z', updatedAt: '2023-10-03T00:00:00.000', @@ -31,7 +32,6 @@ describe('DiagramCard', () => { }, }, ] as [Edit], - databases: 'someDatabase', }, onOpen: () => {}, onDelete: () => {}, diff --git a/packages/compass-data-modeling/src/components/diagram-card.tsx b/packages/compass-data-modeling/src/components/diagram-card.tsx index 3df4dc4241f..dda15de5fd7 100644 --- a/packages/compass-data-modeling/src/components/diagram-card.tsx +++ b/packages/compass-data-modeling/src/components/diagram-card.tsx @@ -78,9 +78,7 @@ export function DiagramCard({ onRename, onDelete, }: { - diagram: MongoDBDataModelDescription & { - databases: string; - }; + diagram: MongoDBDataModelDescription; onOpen: (diagram: MongoDBDataModelDescription) => void; onRename: (id: string) => void; onDelete: (id: string) => void; @@ -132,7 +130,7 @@ export function DiagramCard({ color={palette.gray.dark1} className={namespaceIconStyles} > - {diagram.databases} + {diagram.database}
Last modified: {formattedDate} diff --git a/packages/compass-data-modeling/src/components/diagram-editor.spec.tsx b/packages/compass-data-modeling/src/components/diagram-editor.spec.tsx index 13d88b4898d..75aa2b3198c 100644 --- a/packages/compass-data-modeling/src/components/diagram-editor.spec.tsx +++ b/packages/compass-data-modeling/src/components/diagram-editor.spec.tsx @@ -58,6 +58,7 @@ const storageItems: MongoDBDataModelDescription[] = [ }, ], connectionId: null, + database: 'db1', }, { id: 'new-diagram-id', @@ -91,6 +92,7 @@ const storageItems: MongoDBDataModelDescription[] = [ }, ], connectionId: null, + database: 'db1', }, ]; diff --git a/packages/compass-data-modeling/src/components/saved-diagrams-list.spec.tsx b/packages/compass-data-modeling/src/components/saved-diagrams-list.spec.tsx index 2ac39169e8f..143f3518781 100644 --- a/packages/compass-data-modeling/src/components/saved-diagrams-list.spec.tsx +++ b/packages/compass-data-modeling/src/components/saved-diagrams-list.spec.tsx @@ -37,6 +37,7 @@ const storageItems: MongoDBDataModelDescription[] = [ }, ], connectionId: null, + database: 'db1', }, { id: '2', @@ -63,6 +64,7 @@ const storageItems: MongoDBDataModelDescription[] = [ }, ], connectionId: null, + database: 'db2', }, { id: '3', @@ -89,6 +91,7 @@ const storageItems: MongoDBDataModelDescription[] = [ }, ], connectionId: null, + database: 'db3', }, ]; diff --git a/packages/compass-data-modeling/src/components/saved-diagrams-list.tsx b/packages/compass-data-modeling/src/components/saved-diagrams-list.tsx index bf6687c3c52..e58233d7490 100644 --- a/packages/compass-data-modeling/src/components/saved-diagrams-list.tsx +++ b/packages/compass-data-modeling/src/components/saved-diagrams-list.tsx @@ -16,7 +16,6 @@ import { import { useDataModelSavedItems } from '../provider'; import { deleteDiagram, - selectCurrentModel, openDiagram, openDiagramFromFile, renameDiagram, @@ -27,7 +26,6 @@ import SchemaVisualizationIcon from './icons/schema-visualization'; import FlexibilityIcon from './icons/flexibility'; import { CARD_HEIGHT, CARD_WIDTH, DiagramCard } from './diagram-card'; import { DiagramListToolbar } from './diagram-list-toolbar'; -import toNS from 'mongodb-ns'; import { ImportDiagramButton } from './import-diagram-button'; const sortBy = [ @@ -181,18 +179,6 @@ const DiagramListEmptyContent: React.FunctionComponent<{ ); }; -const getDatabase = ( - modelDescription: MongoDBDataModelDescription -): string[] => { - try { - return selectCurrentModel(modelDescription.edits).collections.map( - ({ ns }) => toNS(ns).database - ); - } catch { - return []; - } -}; - export const SavedDiagramsList: React.FunctionComponent<{ onCreateDiagramClick: () => void; onOpenDiagramClick: (diagram: MongoDBDataModelDescription) => void; @@ -207,30 +193,17 @@ export const SavedDiagramsList: React.FunctionComponent<{ onImportDiagramClick, }) => { const { items, status } = useDataModelSavedItems(); - const decoratedItems = useMemo< - (MongoDBDataModelDescription & { - databases: string; - })[] - >(() => { - return items.map((item) => { - const databases = new Set(getDatabase(item)); - return { - ...item, - databases: Array.from(databases).join(', '), - }; - }); - }, [items]); const [search, setSearch] = useState(''); const filteredItems = useMemo(() => { try { const regex = new RegExp(search, 'i'); - return decoratedItems.filter( - (x) => regex.test(x.name) || (x.databases && regex.test(x.databases)) + return items.filter( + (x) => regex.test(x.name) || (x.database && regex.test(x.database)) ); } catch { - return decoratedItems; + return items; } - }, [decoratedItems, search]); + }, [items, search]); const [sortControls, sortState] = useSortControls(sortBy); const sortedItems = useSortedItems(filteredItems, sortState); diff --git a/packages/compass-data-modeling/src/services/data-model-storage.ts b/packages/compass-data-modeling/src/services/data-model-storage.ts index 684fa89f4f5..2314a3378f9 100644 --- a/packages/compass-data-modeling/src/services/data-model-storage.ts +++ b/packages/compass-data-modeling/src/services/data-model-storage.ts @@ -180,6 +180,7 @@ export const MongoDBDataModelDescriptionSchema = z.object({ * anything that would require re-fetching data associated with the diagram */ connectionId: z.string().nullable(), + database: z.string(), edits: EditListSchema, createdAt: z.string().datetime(), updatedAt: z.string().datetime(), diff --git a/packages/compass-data-modeling/src/services/open-and-download-diagram.spec.ts b/packages/compass-data-modeling/src/services/open-and-download-diagram.spec.ts index bf75795762f..5883423922c 100644 --- a/packages/compass-data-modeling/src/services/open-and-download-diagram.spec.ts +++ b/packages/compass-data-modeling/src/services/open-and-download-diagram.spec.ts @@ -9,15 +9,18 @@ import FlightDiagram from '../../test/fixtures/data-model-with-relationships.jso describe('open-and-download-diagram', function () { it('should return correct content to download', function () { const fileName = 'test-diagram'; + const database = 'test-database'; const { edits, ...restOfContent } = getDownloadDiagramContent( fileName, - FlightDiagram.edits as any + FlightDiagram.edits as any, + database ); expect(restOfContent).to.deep.equal({ type: 'Compass Data Modeling Diagram', version: 1, name: fileName, + database, }); const decodedEdits = JSON.parse( @@ -132,6 +135,7 @@ describe('open-and-download-diagram', function () { version: 1, type: 'Compass Data Modeling Diagram', name: 'Test Diagram', + database: 'test', edits: Buffer.from( JSON.stringify([{ type: 'NonExistent' }]) ).toString('base64'), @@ -146,6 +150,7 @@ describe('open-and-download-diagram', function () { version: 1, type: 'Compass Data Modeling Diagram', name: 'Test Diagram', + database: 'test', edits: Buffer.from( JSON.stringify([ { @@ -179,6 +184,7 @@ describe('open-and-download-diagram', function () { version: 1, type: 'Compass Data Modeling Diagram', name: 'Test Diagram', + database: 'test-database', edits: Buffer.from(JSON.stringify(FlightDiagram.edits)).toString( 'base64' ), diff --git a/packages/compass-data-modeling/src/services/open-and-download-diagram.ts b/packages/compass-data-modeling/src/services/open-and-download-diagram.ts index 3add9f83edd..9ec64bf5aa9 100644 --- a/packages/compass-data-modeling/src/services/open-and-download-diagram.ts +++ b/packages/compass-data-modeling/src/services/open-and-download-diagram.ts @@ -6,9 +6,19 @@ import { z } from '@mongodb-js/compass-user-data'; const kCurrentVersion = 1; const kFileTypeDescription = 'Compass Data Modeling Diagram'; -export function downloadDiagram(fileName: string, edits: Edit[]) { +export function downloadDiagram( + fileName: string, + edits: Edit[], + database: string +) { const blob = new Blob( - [JSON.stringify(getDownloadDiagramContent(fileName, edits), null, 2)], + [ + JSON.stringify( + getDownloadDiagramContent(fileName, edits, database), + null, + 2 + ), + ], { type: 'application/json', } @@ -19,18 +29,25 @@ export function downloadDiagram(fileName: string, edits: Edit[]) { }); } -export function getDownloadDiagramContent(name: string, edits: Edit[]) { +export function getDownloadDiagramContent( + name: string, + edits: Edit[], + database: string +) { return { type: kFileTypeDescription, version: kCurrentVersion, name, + database, edits: Buffer.from(JSON.stringify(edits)).toString('base64'), }; } -export async function getDiagramContentsFromFile( - file: File -): Promise<{ name: string; edits: [SetModelEdit, ...Edit[]] }> { +export async function getDiagramContentsFromFile(file: File): Promise<{ + name: string; + edits: [SetModelEdit, ...Edit[]]; + database: string; +}> { const reader = new FileReader(); return new Promise((resolve, reject) => { reader.onload = (event) => { @@ -48,9 +65,9 @@ export async function getDiagramContentsFromFile( throw new Error('Unsupported diagram file format'); } - const { name, edits } = parsedContent; + const { name, edits, database } = parsedContent; - if (!name || !edits || typeof edits !== 'string') { + if (!name || !edits || typeof edits !== 'string' || !database) { throw new Error('Diagram file is missing required fields'); } @@ -63,6 +80,7 @@ export async function getDiagramContentsFromFile( return resolve({ name: parsedContent.name, edits: [validEdits[0] as SetModelEdit, ...validEdits.slice(1)], + database: parsedContent.database, }); } catch (error) { const message = diff --git a/packages/compass-data-modeling/src/store/analysis-process.ts b/packages/compass-data-modeling/src/store/analysis-process.ts index f6ec701e0db..46a417720d6 100644 --- a/packages/compass-data-modeling/src/store/analysis-process.ts +++ b/packages/compass-data-modeling/src/store/analysis-process.ts @@ -71,6 +71,7 @@ export type AnalysisFinishedAction = { type: AnalysisProcessActionTypes.ANALYSIS_FINISHED; name: string; connectionId: string; + database: string; collections: { ns: string; schema: MongoDBJSONSchema; @@ -315,6 +316,7 @@ export function startAnalysis( type: AnalysisProcessActionTypes.ANALYSIS_FINISHED, name, connectionId, + database, collections: collections.map((coll) => { const node = positioned.nodes.find((node) => { return node.id === coll.ns; diff --git a/packages/compass-data-modeling/src/store/diagram.spec.ts b/packages/compass-data-modeling/src/store/diagram.spec.ts index 5c7d1f99bb5..fcdebba0c84 100644 --- a/packages/compass-data-modeling/src/store/diagram.spec.ts +++ b/packages/compass-data-modeling/src/store/diagram.spec.ts @@ -19,6 +19,10 @@ import type { } from '../services/data-model-storage'; import { UUID } from 'bson'; import Sinon from 'sinon'; +import { + type AnalysisFinishedAction, + AnalysisProcessActionTypes, +} from './analysis-process'; const model: StaticModel = { collections: [ @@ -61,6 +65,7 @@ const loadedDiagram: MongoDBDataModelDescription = { id: 'diagram-id', name: 'diagram-name', connectionId: 'connection-id', + database: 'db', createdAt: '2023-10-01T00:00:00.000Z', updatedAt: '2023-10-05T00:00:00.000Z', edits: [{ type: 'SetModel', model } as Edit], @@ -81,6 +86,7 @@ describe('Data Modeling store', function () { const newDiagram = { name: 'New Diagram', connectionId: 'connection-id', + database: 'db', collections: [ { ns: 'db.collection1', @@ -95,14 +101,16 @@ describe('Data Modeling store', function () { ], relations: model.relationships, }; - store.dispatch({ - type: 'data-modeling/analysis-stats/ANALYSIS_FINISHED', + const analysisFinishedAction: AnalysisFinishedAction = { + type: AnalysisProcessActionTypes.ANALYSIS_FINISHED, ...newDiagram, - }); + }; + store.dispatch(analysisFinishedAction); const initialDiagram = getCurrentDiagramFromState(store.getState()); expect(initialDiagram.name).to.equal(newDiagram.name); expect(initialDiagram.connectionId).to.equal(newDiagram.connectionId); + expect(initialDiagram.database).to.equal(newDiagram.database); expect(initialDiagram.edits).to.have.length(1); expect(initialDiagram.edits[0].type).to.equal('SetModel'); const initialEdit = initialDiagram.edits[0] as Extract< @@ -133,6 +141,7 @@ describe('Data Modeling store', function () { expect(diagram.id).to.equal(loadedDiagram.id); expect(diagram.name).to.equal(loadedDiagram.name); expect(diagram.connectionId).to.equal(loadedDiagram.connectionId); + expect(diagram.database).to.equal(loadedDiagram.database); expect(diagram.edits).to.deep.equal(loadedDiagram.edits); }); }); diff --git a/packages/compass-data-modeling/src/store/diagram.ts b/packages/compass-data-modeling/src/store/diagram.ts index 82931084993..9a1f99697d1 100644 --- a/packages/compass-data-modeling/src/store/diagram.ts +++ b/packages/compass-data-modeling/src/store/diagram.ts @@ -28,7 +28,6 @@ import { } from '../services/open-and-download-diagram'; import type { MongoDBJSONSchema } from 'mongodb-schema'; import { collectionToBaseNodeForLayout } from '../utils/nodes-and-edges'; -import toNS from 'mongodb-ns'; import { getFieldFromSchema, getSchemaWithNewTypes, @@ -174,6 +173,7 @@ export const diagramReducer: Reducer = ( isNewlyCreated: true, name: action.name, connectionId: action.connectionId, + database: action.database, createdAt: new Date().toISOString(), updatedAt: new Date().toISOString(), edits: { @@ -696,7 +696,7 @@ export function openDiagramFromFile( ): DataModelingThunkAction, OpenDiagramAction> { return async (dispatch, getState, { dataModelStorage, track, openToast }) => { try { - const { name, edits } = await getDiagramContentsFromFile(file); + const { name, edits, database } = await getDiagramContentsFromFile(file); const existingDiagramNames = (await dataModelStorage.loadAll()).map( (diagram) => diagram.name @@ -706,6 +706,7 @@ export function openDiagramFromFile( id: new UUID().toString(), name: getDiagramName(existingDiagramNames, name), connectionId: null, + database, createdAt: new Date().toISOString(), updatedAt: new Date().toISOString(), edits, @@ -875,9 +876,9 @@ function getPositionForNewCollection( } function getNameForNewCollection( + database: string, existingCollections: DataModelCollection[] ): string { - const database = toNS(existingCollections[0]?.ns).database; // TODO: again, what if there just isn't anything const baseName = `${database}.new-collection`; let counter = 1; let newName = baseName; @@ -898,10 +899,10 @@ export function addCollection( ApplyEditAction | RevertFailedEditAction | CollectionSelectedAction > { return (dispatch, getState, { track }) => { - const existingCollections = selectCurrentModelFromState( - getState() - ).collections; - if (!ns) ns = getNameForNewCollection(existingCollections); + const state = getState(); + const database = getCurrentDiagramFromState(state).database; + const existingCollections = selectCurrentModelFromState(state).collections; + if (!ns) ns = getNameForNewCollection(database, existingCollections); if (!position) { position = getPositionForNewCollection(existingCollections, { ns, @@ -979,13 +980,14 @@ export function getCurrentDiagramFromState( const { id, connectionId, + database, name, createdAt, updatedAt, edits: { current: edits }, } = state.diagram; - return { id, connectionId, name, edits, createdAt, updatedAt }; + return { id, connectionId, name, database, edits, createdAt, updatedAt }; } const selectCurrentDiagramFromState = memoize(getCurrentDiagramFromState); diff --git a/packages/compass-data-modeling/src/store/export-diagram.ts b/packages/compass-data-modeling/src/store/export-diagram.ts index c9c10d18fd6..1e6711b4ea5 100644 --- a/packages/compass-data-modeling/src/store/export-diagram.ts +++ b/packages/compass-data-modeling/src/store/export-diagram.ts @@ -129,7 +129,7 @@ export function exportDiagram( cancelController.signal ); } else if (exportFormat === 'diagram') { - downloadDiagram(diagram.name, diagram.edits.current); + downloadDiagram(diagram.name, diagram.edits.current, diagram.database); } else { throw new Error(`Unsupported export format: ${exportFormat}`); } diff --git a/packages/compass-data-modeling/test/fixtures/data-model-with-relationships.json b/packages/compass-data-modeling/test/fixtures/data-model-with-relationships.json index 23a672f1c8c..509ca1d833f 100644 --- a/packages/compass-data-modeling/test/fixtures/data-model-with-relationships.json +++ b/packages/compass-data-modeling/test/fixtures/data-model-with-relationships.json @@ -2,6 +2,7 @@ "id": "26fea481-14a0-40de-aa8e-b3ef22afcf1b", "connectionId": "108acc00-4d7b-4f56-be19-05c7288da71a", "name": "Flights and countries", + "database": "flights", "edits": [ { "id": "5e16572a-6978-4669-8103-e1f087b412cd", diff --git a/packages/compass-e2e-tests/tests/data-modeling-tab.test.ts b/packages/compass-e2e-tests/tests/data-modeling-tab.test.ts index 67dbe7e5f54..2b2ec05e273 100644 --- a/packages/compass-e2e-tests/tests/data-modeling-tab.test.ts +++ b/packages/compass-e2e-tests/tests/data-modeling-tab.test.ts @@ -538,11 +538,12 @@ describe('Data Modeling tab', function () { it('exports the data model to compass format and imports it back', async function () { const dataModelName = 'Test Export Model - Save-Open'; + const databaseName = 'test'; exportFileName = `${dataModelName}.mdm`; await setupDiagram(browser, { diagramName: dataModelName, connectionName: DEFAULT_CONNECTION_NAME_1, - databaseName: 'test', + databaseName, }); const dataModelEditor = browser.$(Selectors.DataModelEditor); @@ -573,6 +574,7 @@ describe('Data Modeling tab', function () { const model = JSON.parse(content); expect(model.name).to.equal(dataModelName); + expect(model.database).to.equal(databaseName); const edits = JSON.parse( Buffer.from(model.edits, 'base64').toString('utf-8') From e461e8e079d5acfa0e9a30357cde46262108faf2 Mon Sep 17 00:00:00 2001 From: Paula Stachova Date: Wed, 22 Oct 2025 18:53:34 +0200 Subject: [PATCH 2/4] Update packages/compass-data-modeling/src/components/saved-diagrams-list.tsx Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- .../src/components/saved-diagrams-list.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/compass-data-modeling/src/components/saved-diagrams-list.tsx b/packages/compass-data-modeling/src/components/saved-diagrams-list.tsx index e58233d7490..43a0e70d5d0 100644 --- a/packages/compass-data-modeling/src/components/saved-diagrams-list.tsx +++ b/packages/compass-data-modeling/src/components/saved-diagrams-list.tsx @@ -198,7 +198,7 @@ export const SavedDiagramsList: React.FunctionComponent<{ try { const regex = new RegExp(search, 'i'); return items.filter( - (x) => regex.test(x.name) || (x.database && regex.test(x.database)) + (x) => regex.test(x.name) || regex.test(x.database) ); } catch { return items; From 5b76efb07d4bf6dccf1313b153b66c3e100605af Mon Sep 17 00:00:00 2001 From: Paula Stachova Date: Wed, 22 Oct 2025 19:50:37 +0200 Subject: [PATCH 3/4] style --- .../src/components/saved-diagrams-list.tsx | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/packages/compass-data-modeling/src/components/saved-diagrams-list.tsx b/packages/compass-data-modeling/src/components/saved-diagrams-list.tsx index 43a0e70d5d0..840d3439294 100644 --- a/packages/compass-data-modeling/src/components/saved-diagrams-list.tsx +++ b/packages/compass-data-modeling/src/components/saved-diagrams-list.tsx @@ -197,9 +197,7 @@ export const SavedDiagramsList: React.FunctionComponent<{ const filteredItems = useMemo(() => { try { const regex = new RegExp(search, 'i'); - return items.filter( - (x) => regex.test(x.name) || regex.test(x.database) - ); + return items.filter((x) => regex.test(x.name) || regex.test(x.database)); } catch { return items; } From 5814c86549236f90eb7ab86e7c3f35182cb230c6 Mon Sep 17 00:00:00 2001 From: Paula Stachova Date: Thu, 23 Oct 2025 11:07:42 +0200 Subject: [PATCH 4/4] z preprocess db --- .../src/services/data-model-storage.ts | 45 ++++++++++++------- 1 file changed, 30 insertions(+), 15 deletions(-) diff --git a/packages/compass-data-modeling/src/services/data-model-storage.ts b/packages/compass-data-modeling/src/services/data-model-storage.ts index 2314a3378f9..ab1c09edbc7 100644 --- a/packages/compass-data-modeling/src/services/data-model-storage.ts +++ b/packages/compass-data-modeling/src/services/data-model-storage.ts @@ -1,4 +1,5 @@ import { z } from '@mongodb-js/compass-user-data'; +import toNS from 'mongodb-ns'; import type { MongoDBJSONSchema } from 'mongodb-schema'; export const FieldPathSchema = z.array(z.string()); @@ -170,21 +171,35 @@ export const validateEdit = ( } }; -export const MongoDBDataModelDescriptionSchema = z.object({ - id: z.string(), - name: z.string(), - /** - * Connection id associated with the data model at the moment of configuring - * and analyzing. No connection id means diagram was imported and not attached - * to a connection. Practically speaking it just means that we can't do - * anything that would require re-fetching data associated with the diagram - */ - connectionId: z.string().nullable(), - database: z.string(), - edits: EditListSchema, - createdAt: z.string().datetime(), - updatedAt: z.string().datetime(), -}); +export const MongoDBDataModelDescriptionSchema = z.preprocess( + (val) => { + const model = val as Record; + if ( + !model.database && + Array.isArray(model.edits) && + model.edits.length > 0 + ) { + // Infer database from the first collection's namespace in the 'SetModel' edit + model.database = toNS(model.edits?.[0].model.collections[0].ns).database; + } + return model; + }, + z.object({ + id: z.string(), + name: z.string(), + /** + * Connection id associated with the data model at the moment of configuring + * and analyzing. No connection id means diagram was imported and not attached + * to a connection. Practically speaking it just means that we can't do + * anything that would require re-fetching data associated with the diagram + */ + connectionId: z.string().nullable(), + database: z.string(), + edits: EditListSchema, + createdAt: z.string().datetime(), + updatedAt: z.string().datetime(), + }) +); export type MongoDBDataModelDescription = z.output< typeof MongoDBDataModelDescriptionSchema