From 3cf163e6a5f8b70f2a2dd3451959c77d5a668638 Mon Sep 17 00:00:00 2001 From: Kevin Pamaran Date: Mon, 8 Sep 2025 17:23:45 -0400 Subject: [PATCH 01/22] implement raw schema confirmation screen --- ...tor.tsx => faker-schema-editor-screen.tsx} | 4 +- .../mock-data-generator-modal.spec.tsx | 3 +- .../mock-data-generator-modal.tsx | 6 +- .../raw-schema-confirmation-screen.tsx | 131 ++++++++++++++++++ .../raw-schema-confirmation.tsx | 52 ------- .../to-user-friendly-field-info.ts | 56 ++++++++ 6 files changed, 194 insertions(+), 58 deletions(-) rename packages/compass-collection/src/components/mock-data-generator-modal/{faker-schema-editor.tsx => faker-schema-editor-screen.tsx} (71%) create mode 100644 packages/compass-collection/src/components/mock-data-generator-modal/raw-schema-confirmation-screen.tsx delete mode 100644 packages/compass-collection/src/components/mock-data-generator-modal/raw-schema-confirmation.tsx create mode 100644 packages/compass-collection/src/components/mock-data-generator-modal/to-user-friendly-field-info.ts diff --git a/packages/compass-collection/src/components/mock-data-generator-modal/faker-schema-editor.tsx b/packages/compass-collection/src/components/mock-data-generator-modal/faker-schema-editor-screen.tsx similarity index 71% rename from packages/compass-collection/src/components/mock-data-generator-modal/faker-schema-editor.tsx rename to packages/compass-collection/src/components/mock-data-generator-modal/faker-schema-editor-screen.tsx index f95105066d8..c888cafdcc7 100644 --- a/packages/compass-collection/src/components/mock-data-generator-modal/faker-schema-editor.tsx +++ b/packages/compass-collection/src/components/mock-data-generator-modal/faker-schema-editor-screen.tsx @@ -1,7 +1,7 @@ import React from 'react'; // TODO: More to come from CLOUDP-333853, CLOUDP-333854 -const FakerSchemaEditor = () => { +const FakerSchemaEditorScreen = () => { return (
Schema Editor Content Placeholder @@ -9,4 +9,4 @@ const FakerSchemaEditor = () => { ); }; -export default FakerSchemaEditor; +export default FakerSchemaEditorScreen; diff --git a/packages/compass-collection/src/components/mock-data-generator-modal/mock-data-generator-modal.spec.tsx b/packages/compass-collection/src/components/mock-data-generator-modal/mock-data-generator-modal.spec.tsx index 9d0dd164906..0d0b1346939 100644 --- a/packages/compass-collection/src/components/mock-data-generator-modal/mock-data-generator-modal.spec.tsx +++ b/packages/compass-collection/src/components/mock-data-generator-modal/mock-data-generator-modal.spec.tsx @@ -222,7 +222,8 @@ describe('MockDataGeneratorModal', () => { expect(screen.queryByTestId('faker-schema-editor')).to.not.exist; }); - // todo: assert a user-friendly error is displayed (CLOUDP-333852) + expect(screen.getByText('LLM Request failed. Please confirm again.')).to + .exist; }); // todo: assert that closing then re-opening the modal after an LLM err removes the err message diff --git a/packages/compass-collection/src/components/mock-data-generator-modal/mock-data-generator-modal.tsx b/packages/compass-collection/src/components/mock-data-generator-modal/mock-data-generator-modal.tsx index d09861a6b42..011bf7d748d 100644 --- a/packages/compass-collection/src/components/mock-data-generator-modal/mock-data-generator-modal.tsx +++ b/packages/compass-collection/src/components/mock-data-generator-modal/mock-data-generator-modal.tsx @@ -21,8 +21,8 @@ import { generateFakerMappings, mockDataGeneratorPreviousButtonClicked, } from '../../modules/collection-tab'; -import { default as SchemaConfirmationScreen } from './raw-schema-confirmation'; -import FakerSchemaEditor from './faker-schema-editor'; +import RawSchemaConfirmationScreen from './raw-schema-confirmation-screen'; +import FakerSchemaEditor from './faker-schema-editor-screen'; const footerStyles = css` flex-direction: row; @@ -65,7 +65,7 @@ const MockDataGeneratorModal = ({ let stepContent: React.ReactNode; if (currentStep === MockDataGeneratorStep.SCHEMA_CONFIRMATION) { - stepContent = ; + stepContent = ; } if (currentStep === MockDataGeneratorStep.SCHEMA_EDITOR) { diff --git a/packages/compass-collection/src/components/mock-data-generator-modal/raw-schema-confirmation-screen.tsx b/packages/compass-collection/src/components/mock-data-generator-modal/raw-schema-confirmation-screen.tsx new file mode 100644 index 00000000000..39c0e744290 --- /dev/null +++ b/packages/compass-collection/src/components/mock-data-generator-modal/raw-schema-confirmation-screen.tsx @@ -0,0 +1,131 @@ +import React from 'react'; +import { connect } from 'react-redux'; + +import { + css, + palette, + spacing, + Banner, + BannerVariant, + Code, + Body, + Subtitle, +} from '@mongodb-js/compass-components'; + +import { usePreference } from 'compass-preferences-model/provider'; +import toUserFriendlyFieldInfo from './to-user-friendly-field-info'; +import type { CollectionState } from '../../modules/collection-tab'; +import type { SchemaAnalysisState } from '../../schema-analysis-types'; +import type { MockDataGeneratorState } from './types'; + +interface RawSchemaConfirmationScreenProps { + schemaAnalysis: SchemaAnalysisState; + namespace: string; + fakerSchemaGenerationStatus: MockDataGeneratorState['status']; +} + +const namespaceStyles = css({ + marginTop: spacing[100], + marginBottom: spacing[500], +}); + +const headingStyles = css({ + marginBottom: spacing[100], +}); + +const descriptionStyles = css({ + marginBottom: spacing[300], +}); + +const codeStyles = css({ + maxHeight: '30vh', +}); + +const errorBannerStyles = css({ + marginTop: spacing[400], +}); + +const errorBannerTextStyles = css({ + color: palette.red.dark2, +}); + +// Note: Currently a placeholder. The final contents will be addressed by CLOUDP-333852 +const RawSchemaConfirmationScreen = ( + props: RawSchemaConfirmationScreenProps +) => { + let enableSampleDocumentPassing = usePreference( + 'enableGenAISampleDocumentPassing' + ); + + const subtitleText = enableSampleDocumentPassing + ? 'Sample Documents Collected' + : 'Document Schema Identified'; + + const descriptionText = enableSampleDocumentPassing + ? 'A sample of document values from your collection will be sent to an LLM for processing.' + : 'We have identified the following schema from your documents. This schema will be sent to an LLM for processing.'; + + // todo: should establish if unfinished schema analysis edge case should be handled within the modal or + // from the button opening the modal + const schemaAnalysisIncompletePlaceholder = ( + Schema analysis has yet to complete successfully. + ); + + return ( +
+ {props.schemaAnalysis.status === 'complete' ? ( + <> + {props.namespace} + + {subtitleText} + + {descriptionText} + + {enableSampleDocumentPassing + ? JSON.stringify(props.schemaAnalysis.sampleDocument, null, 4) + : JSON.stringify( + toUserFriendlyFieldInfo(props.schemaAnalysis.processedSchema), + null, + 4 + )} + + {props.fakerSchemaGenerationStatus === 'error' && ( + + + LLM Request failed. Please confirm again. + + + )} + + ) : ( + schemaAnalysisIncompletePlaceholder + )} +
+ ); +}; + +const mapStateToProps = (state: CollectionState) => { + const schemaAnalysis = state.schemaAnalysis; + const fakerSchemaGenerationStatus = state.fakerSchemaGeneration.status; + + return { + schemaAnalysis, + namespace: state.namespace, + fakerSchemaGenerationStatus, + }; +}; + +const ConnectedRawSchemaConfirmationScreen = connect( + mapStateToProps, + {} +)(RawSchemaConfirmationScreen); + +export default ConnectedRawSchemaConfirmationScreen; diff --git a/packages/compass-collection/src/components/mock-data-generator-modal/raw-schema-confirmation.tsx b/packages/compass-collection/src/components/mock-data-generator-modal/raw-schema-confirmation.tsx deleted file mode 100644 index 6848d5b8c97..00000000000 --- a/packages/compass-collection/src/components/mock-data-generator-modal/raw-schema-confirmation.tsx +++ /dev/null @@ -1,52 +0,0 @@ -import React from 'react'; -import { connect } from 'react-redux'; - -import { Code, Body, Subtitle } from '@mongodb-js/compass-components'; - -import type { CollectionState } from '../../modules/collection-tab'; -import type { FieldInfo } from '../../schema-analysis-types'; - -interface RawSchemaConfirmationProps { - schemaContent: Record | null; - namespace: string; -} - -// Note: Currently a placeholder. The final contents will be addressed by CLOUDP-333852 -const RawSchemaConfirmation = (props: RawSchemaConfirmationProps) => { - // this will change - const codeContent = props.schemaContent - ? JSON.stringify(props.schemaContent, null, 4) - : 'No schema data available'; - - return ( -
- {props.namespace} - Document Schema Identified - - We have identified the following schema from your documents. This schema - will be sent to an LLM for processing. - - - {codeContent} - -
- ); -}; - -const mapStateToProps = (state: CollectionState) => { - const schemaContent = - state.schemaAnalysis.status === 'complete' - ? state.schemaAnalysis.processedSchema - : null; - return { - schemaContent, - namespace: state.namespace, - }; -}; - -const ConnectedRawSchemaConfirmation = connect( - mapStateToProps, - {} -)(RawSchemaConfirmation); - -export default ConnectedRawSchemaConfirmation; diff --git a/packages/compass-collection/src/components/mock-data-generator-modal/to-user-friendly-field-info.ts b/packages/compass-collection/src/components/mock-data-generator-modal/to-user-friendly-field-info.ts new file mode 100644 index 00000000000..e0a7f296ac0 --- /dev/null +++ b/packages/compass-collection/src/components/mock-data-generator-modal/to-user-friendly-field-info.ts @@ -0,0 +1,56 @@ +import { processSchema } from '../../transform-schema-to-field-info'; +import type { FieldInfo } from '../../schema-analysis-types'; + +type UserFriendlyFieldInfoNode = + | { [field: string]: UserFriendlyFieldInfoNode } + | FieldInfo['type']; +export type UserFriendlyFieldInfoTree = { + [field: string]: UserFriendlyFieldInfoNode; +}; + +/** + * Usage is for display purposes only. The result is derived from the work of `processSchema`, + * instead of directly derived from the `Schema` type from `mongodb-schema`, ensuring that what + * the user sees in `RawSchemaConfirmationScreen` is constrained to what the LLM processes. + */ +export default function toUserFriendlyFieldInfo( + input: ReturnType +): UserFriendlyFieldInfoTree { + // ensure parent nodes are created before their children + const sortedFieldPaths = Object.keys(input).sort( + (f1, f2) => countSeparators(f1) - countSeparators(f2) + ); + + // Assumes "." and "[]" placement is valid + const result: UserFriendlyFieldInfoTree = {}; + for (const path of sortedFieldPaths) { + const fieldParts = path.split('.'); + + let node = result; + for (let i = 0; i < fieldParts.length; i++) { + const part = fieldParts[i]; + + if (i === fieldParts.length - 1) { + node[part] = input[path].type; + break; + } + + if (typeof node[part] !== 'object' || node[part] === null) { + node[part] = {}; + } + node = node[part]; + } + } + return result; +} + +/** + * note: assumes `processSchema` constructs field paths in this manner: + * - 1+ "[]" can only appear at the end of field paths + * - field keys do not contain "." or "[]" + */ +function countSeparators(input: string): number { + const c1 = input.split('.').length - 1; + const c2 = input.split('[]').length - 1; + return c1 + c2; +} From 7fbb335db6743b6456ed2a13cebc4edf4eda360a Mon Sep 17 00:00:00 2001 From: Kevin Pamaran Date: Tue, 9 Sep 2025 14:43:13 -0400 Subject: [PATCH 02/22] shorthand schema render logic in confirmation step --- .../mock-data-generator-modal.spec.tsx | 37 ++- .../raw-schema-confirmation-screen.tsx | 24 +- .../to-simplified-field-info.spec.ts | 256 ++++++++++++++++++ ...ld-info.ts => to-simplified-field-info.ts} | 17 +- .../transform-schema-to-field-info.spec.ts | 145 ++++++++++ .../src/transform-schema-to-field-info.ts | 40 +++ 6 files changed, 493 insertions(+), 26 deletions(-) create mode 100644 packages/compass-collection/src/components/mock-data-generator-modal/to-simplified-field-info.spec.ts rename packages/compass-collection/src/components/mock-data-generator-modal/{to-user-friendly-field-info.ts => to-simplified-field-info.ts} (64%) diff --git a/packages/compass-collection/src/components/mock-data-generator-modal/mock-data-generator-modal.spec.tsx b/packages/compass-collection/src/components/mock-data-generator-modal/mock-data-generator-modal.spec.tsx index 4c234d25851..f70f4a3b34d 100644 --- a/packages/compass-collection/src/components/mock-data-generator-modal/mock-data-generator-modal.spec.tsx +++ b/packages/compass-collection/src/components/mock-data-generator-modal/mock-data-generator-modal.spec.tsx @@ -20,10 +20,12 @@ describe('MockDataGeneratorModal', () => { async function renderModal({ isOpen = true, currentStep = MockDataGeneratorStep.SCHEMA_CONFIRMATION, + enableGenAISampleDocumentPassing = false, mockServices = createMockServices(), connectionInfo, }: { isOpen?: boolean; + enableGenAISampleDocumentPassing?: boolean; currentStep?: MockDataGeneratorStep; mockServices?: any; connectionInfo?: ConnectionInfo; @@ -63,7 +65,12 @@ describe('MockDataGeneratorModal', () => { , - connectionInfo + connectionInfo, + { + preferences: { + enableGenAISampleDocumentPassing, + }, + } ); } @@ -109,6 +116,34 @@ describe('MockDataGeneratorModal', () => { expect(screen.queryByTestId('generate-mock-data-modal')).to.not.exist; }); + it('uses the appropriate copy when Generative AI sample document passing is enabled', async () => { + await renderModal({ enableGenAISampleDocumentPassing: true }); + expect(screen.queryByText('Sample Documents Collected')).to.exist; + expect( + screen.queryByText( + 'A sample of document values from your collection will be sent to an LLM for processing.' + ) + ).to.exist; + // fragment from { "name": "John" } + expect(screen.queryByText('"John"')).to.exist; + expect(screen.queryByText('"String"')).to.not.exist; + }); + + it('uses the appropriate copy when Generative AI sample document passing is disabled', async () => { + await renderModal(); + expect(screen.queryByText('Document Schema Identified')).to.exist; + expect( + screen.queryByText( + 'We have identified the following schema from your documents. This schema will be sent to an LLM for processing.' + ) + ).to.exist; + // fragment from { "name": "String" } + expect(screen.queryByText('"String"')).to.exist; + expect(screen.queryByText('"John"')).to.not.exist; + + screen.debug(); + }); + it('closes the modal when the close button is clicked', async () => { await renderModal(); diff --git a/packages/compass-collection/src/components/mock-data-generator-modal/raw-schema-confirmation-screen.tsx b/packages/compass-collection/src/components/mock-data-generator-modal/raw-schema-confirmation-screen.tsx index 39c0e744290..0e4930612ab 100644 --- a/packages/compass-collection/src/components/mock-data-generator-modal/raw-schema-confirmation-screen.tsx +++ b/packages/compass-collection/src/components/mock-data-generator-modal/raw-schema-confirmation-screen.tsx @@ -9,11 +9,10 @@ import { BannerVariant, Code, Body, - Subtitle, } from '@mongodb-js/compass-components'; import { usePreference } from 'compass-preferences-model/provider'; -import toUserFriendlyFieldInfo from './to-user-friendly-field-info'; +import toSimplifiedFieldInfo from './to-simplified-field-info'; import type { CollectionState } from '../../modules/collection-tab'; import type { SchemaAnalysisState } from '../../schema-analysis-types'; import type { MockDataGeneratorState } from './types'; @@ -49,11 +48,12 @@ const errorBannerTextStyles = css({ color: palette.red.dark2, }); -// Note: Currently a placeholder. The final contents will be addressed by CLOUDP-333852 -const RawSchemaConfirmationScreen = ( - props: RawSchemaConfirmationScreenProps -) => { - let enableSampleDocumentPassing = usePreference( +const RawSchemaConfirmationScreen = ({ + schemaAnalysis, + namespace, + fakerSchemaGenerationStatus, +}: RawSchemaConfirmationScreenProps) => { + const enableSampleDocumentPassing = usePreference( 'enableGenAISampleDocumentPassing' ); @@ -73,9 +73,9 @@ const RawSchemaConfirmationScreen = ( return (
- {props.schemaAnalysis.status === 'complete' ? ( + {schemaAnalysis.status === 'complete' ? ( <> - {props.namespace} + {namespace} {descriptionText} {enableSampleDocumentPassing - ? JSON.stringify(props.schemaAnalysis.sampleDocument, null, 4) + ? JSON.stringify(schemaAnalysis.sampleDocument, null, 4) : JSON.stringify( - toUserFriendlyFieldInfo(props.schemaAnalysis.processedSchema), + toSimplifiedFieldInfo(schemaAnalysis.processedSchema), null, 4 )} - {props.fakerSchemaGenerationStatus === 'error' && ( + {fakerSchemaGenerationStatus === 'error' && ( ): UserFriendlyFieldInfoTree { // ensure parent nodes are created before their children @@ -21,7 +20,6 @@ export default function toUserFriendlyFieldInfo( (f1, f2) => countSeparators(f1) - countSeparators(f2) ); - // Assumes "." and "[]" placement is valid const result: UserFriendlyFieldInfoTree = {}; for (const path of sortedFieldPaths) { const fieldParts = path.split('.'); @@ -44,13 +42,6 @@ export default function toUserFriendlyFieldInfo( return result; } -/** - * note: assumes `processSchema` constructs field paths in this manner: - * - 1+ "[]" can only appear at the end of field paths - * - field keys do not contain "." or "[]" - */ function countSeparators(input: string): number { - const c1 = input.split('.').length - 1; - const c2 = input.split('[]').length - 1; - return c1 + c2; + return input.split('.').length - 1; } diff --git a/packages/compass-collection/src/transform-schema-to-field-info.spec.ts b/packages/compass-collection/src/transform-schema-to-field-info.spec.ts index 04002bcda05..7486601ce64 100644 --- a/packages/compass-collection/src/transform-schema-to-field-info.spec.ts +++ b/packages/compass-collection/src/transform-schema-to-field-info.spec.ts @@ -1105,4 +1105,149 @@ describe('processSchema', function () { }, }); }); + + /** + * Verifies malformed field paths can be caught by bugs in the construction logic. + * These are unlikely to occur with valid `Schema` inputs to `processSchema`. + */ + describe('validateFieldPath error conditions', function () { + it('throws error for incomplete brackets in middle of field part', function () { + const schema: Schema = { + fields: [ + { + name: 'field[invalid', // Incomplete bracket + path: ['field[invalid'], + count: 1, + type: ['String'], + probability: 1.0, + hasDuplicates: false, + types: [ + { + name: 'String', + bsonType: 'String', + path: ['field[invalid'], + count: 1, + probability: 1.0, + values: ['test'], + }, + ], + }, + ], + count: 1, + }; + + expect(() => processSchema(schema)).to.throw( + 'invalid fieldPath "field[invalid": "[]" can only appear at the end of field parts' + ); + }); + + it('throws error for brackets in middle of field part', function () { + const schema: Schema = { + fields: [ + { + name: 'field[]invalid', // Brackets in middle + path: ['field[]invalid'], + count: 1, + type: ['String'], + probability: 1.0, + hasDuplicates: false, + types: [ + { + name: 'String', + bsonType: 'String', + path: ['field[]invalid'], + count: 1, + probability: 1.0, + values: ['test'], + }, + ], + }, + ], + count: 1, + }; + + expect(() => processSchema(schema)).to.throw( + 'invalid fieldPath "field[]invalid": "[]" can only appear at the end of field parts' + ); + }); + + it('throws error for empty field parts', function () { + const schema: Schema = { + fields: [ + { + name: 'parent', + path: ['parent'], + count: 1, + type: ['Document'], + probability: 1.0, + hasDuplicates: false, + types: [ + { + name: 'Document', + bsonType: 'Document', + path: ['parent'], + count: 1, + probability: 1.0, + fields: [ + { + name: '', // Empty field name + path: ['parent', ''], + count: 1, + type: ['String'], + probability: 1.0, + hasDuplicates: false, + types: [ + { + name: 'String', + bsonType: 'String', + path: ['parent', ''], + count: 1, + probability: 1.0, + values: ['test'], + }, + ], + }, + ], + }, + ], + }, + ], + count: 1, + }; + + expect(() => processSchema(schema)).to.throw( + 'invalid fieldPath "parent.": field parts cannot be empty' + ); + }); + + it('throws error for field part that is only "[]"', function () { + const schema: Schema = { + fields: [ + { + name: '[]', // Field name is just "[]" + path: ['[]'], + count: 1, + type: ['String'], + probability: 1.0, + hasDuplicates: false, + types: [ + { + name: 'String', + bsonType: 'String', + path: ['[]'], + count: 1, + probability: 1.0, + values: ['test'], + }, + ], + }, + ], + count: 1, + }; + + expect(() => processSchema(schema)).to.throw( + "expected fieldPath to have a non-empty part before '[]'" + ); + }); + }); }); diff --git a/packages/compass-collection/src/transform-schema-to-field-info.ts b/packages/compass-collection/src/transform-schema-to-field-info.ts index 9d782c7a187..b7e9b6bfff5 100644 --- a/packages/compass-collection/src/transform-schema-to-field-info.ts +++ b/packages/compass-collection/src/transform-schema-to-field-info.ts @@ -122,6 +122,8 @@ function isPrimitiveSchemaType(type: SchemaType): type is PrimitiveSchemaType { /** * Transforms a raw mongodb-schema Schema into a flat Record * using dot notation for nested fields and bracket notation for arrays. + * + * The result is used for the Mock Data Generator LLM call. */ export function processSchema(schema: Schema): Record { const result: Record = {}; @@ -135,6 +137,11 @@ export function processSchema(schema: Schema): Record { processNamedField(field, '', result); } + // post-processing validation + for (const fieldPath of Object.keys(result)) { + validateFieldPath(fieldPath); + } + return result; } @@ -221,3 +228,36 @@ function getMostFrequentType(types: SchemaType[]): SchemaType | null { return validTypes[0] || null; } + +/** + * Note: This validation is takes an extra defensive stance. As illustrated by the unit tests, malformed + * inputs are required to simulate these unlikely errors. + */ +function validateFieldPath(fieldPath: string) { + const parts = fieldPath.split('.'); + + for (const part of parts) { + if (part === '') { + throw new Error( + `invalid fieldPath "${fieldPath}": field parts cannot be empty` + ); + } + + if (part.replaceAll('[]', '') === '') { + throw Error("expected fieldPath to have a non-empty part before '[]'"); + } + + // Check that [] only appears as complete pairs and only at the end of the part + // Remove all trailing [] pairs and check if any [] remains in the middle + // Regex breakdown: (\[\])+$ + // (\[\]) - matches exactly the characters "[]" (brackets are escaped) + // + - one or more times + // $ - at the end of the string + const remaining = part.replace(/(\[\])+$/, ''); + if (remaining.includes('[') || remaining.includes(']')) { + throw new Error( + `invalid fieldPath "${fieldPath}": "[]" can only appear at the end of field parts` + ); + } + } +} From 2dfa7201348d6a5da5943b8b4d31ebd6f1307802 Mon Sep 17 00:00:00 2001 From: Kevin Pamaran Date: Tue, 9 Sep 2025 15:03:43 -0400 Subject: [PATCH 03/22] nit --- .../mock-data-generator-modal.spec.tsx | 2 -- .../raw-schema-confirmation-screen.tsx | 21 +++++-------------- 2 files changed, 5 insertions(+), 18 deletions(-) diff --git a/packages/compass-collection/src/components/mock-data-generator-modal/mock-data-generator-modal.spec.tsx b/packages/compass-collection/src/components/mock-data-generator-modal/mock-data-generator-modal.spec.tsx index f70f4a3b34d..b52a7e292bb 100644 --- a/packages/compass-collection/src/components/mock-data-generator-modal/mock-data-generator-modal.spec.tsx +++ b/packages/compass-collection/src/components/mock-data-generator-modal/mock-data-generator-modal.spec.tsx @@ -140,8 +140,6 @@ describe('MockDataGeneratorModal', () => { // fragment from { "name": "String" } expect(screen.queryByText('"String"')).to.exist; expect(screen.queryByText('"John"')).to.not.exist; - - screen.debug(); }); it('closes the modal when the close button is clicked', async () => { diff --git a/packages/compass-collection/src/components/mock-data-generator-modal/raw-schema-confirmation-screen.tsx b/packages/compass-collection/src/components/mock-data-generator-modal/raw-schema-confirmation-screen.tsx index 0e4930612ab..4c67a3e2a53 100644 --- a/packages/compass-collection/src/components/mock-data-generator-modal/raw-schema-confirmation-screen.tsx +++ b/packages/compass-collection/src/components/mock-data-generator-modal/raw-schema-confirmation-screen.tsx @@ -24,16 +24,12 @@ interface RawSchemaConfirmationScreenProps { } const namespaceStyles = css({ - marginTop: spacing[100], - marginBottom: spacing[500], -}); - -const headingStyles = css({ - marginBottom: spacing[100], + marginTop: spacing[200], + marginBottom: spacing[400], }); const descriptionStyles = css({ - marginBottom: spacing[300], + marginBottom: spacing[200], }); const codeStyles = css({ @@ -53,9 +49,7 @@ const RawSchemaConfirmationScreen = ({ namespace, fakerSchemaGenerationStatus, }: RawSchemaConfirmationScreenProps) => { - const enableSampleDocumentPassing = usePreference( - 'enableGenAISampleDocumentPassing' - ); + const enableSampleDocumentPassing = true; const subtitleText = enableSampleDocumentPassing ? 'Sample Documents Collected' @@ -76,12 +70,7 @@ const RawSchemaConfirmationScreen = ({ {schemaAnalysis.status === 'complete' ? ( <> {namespace} - + {subtitleText} {descriptionText} From 0518349b4c3bf16f0fafade367c9a4749b14a1a1 Mon Sep 17 00:00:00 2001 From: Kevin Pamaran Date: Tue, 9 Sep 2025 15:19:45 -0400 Subject: [PATCH 04/22] move tests --- .../mock-data-generator-modal.spec.tsx | 52 +++++++++---------- 1 file changed, 26 insertions(+), 26 deletions(-) diff --git a/packages/compass-collection/src/components/mock-data-generator-modal/mock-data-generator-modal.spec.tsx b/packages/compass-collection/src/components/mock-data-generator-modal/mock-data-generator-modal.spec.tsx index b52a7e292bb..353649eed00 100644 --- a/packages/compass-collection/src/components/mock-data-generator-modal/mock-data-generator-modal.spec.tsx +++ b/packages/compass-collection/src/components/mock-data-generator-modal/mock-data-generator-modal.spec.tsx @@ -116,32 +116,6 @@ describe('MockDataGeneratorModal', () => { expect(screen.queryByTestId('generate-mock-data-modal')).to.not.exist; }); - it('uses the appropriate copy when Generative AI sample document passing is enabled', async () => { - await renderModal({ enableGenAISampleDocumentPassing: true }); - expect(screen.queryByText('Sample Documents Collected')).to.exist; - expect( - screen.queryByText( - 'A sample of document values from your collection will be sent to an LLM for processing.' - ) - ).to.exist; - // fragment from { "name": "John" } - expect(screen.queryByText('"John"')).to.exist; - expect(screen.queryByText('"String"')).to.not.exist; - }); - - it('uses the appropriate copy when Generative AI sample document passing is disabled', async () => { - await renderModal(); - expect(screen.queryByText('Document Schema Identified')).to.exist; - expect( - screen.queryByText( - 'We have identified the following schema from your documents. This schema will be sent to an LLM for processing.' - ) - ).to.exist; - // fragment from { "name": "String" } - expect(screen.queryByText('"String"')).to.exist; - expect(screen.queryByText('"John"')).to.not.exist; - }); - it('closes the modal when the close button is clicked', async () => { await renderModal(); @@ -237,6 +211,32 @@ describe('MockDataGeneratorModal', () => { ).to.equal('true'); }); + it('uses the appropriate copy when Generative AI sample document passing is enabled', async () => { + await renderModal({ enableGenAISampleDocumentPassing: true }); + expect(screen.queryByText('Sample Documents Collected')).to.exist; + expect( + screen.queryByText( + 'A sample of document values from your collection will be sent to an LLM for processing.' + ) + ).to.exist; + // fragment from { "name": "John" } + expect(screen.queryByText('"John"')).to.exist; + expect(screen.queryByText('"String"')).to.not.exist; + }); + + it('uses the appropriate copy when Generative AI sample document passing is disabled', async () => { + await renderModal(); + expect(screen.queryByText('Document Schema Identified')).to.exist; + expect( + screen.queryByText( + 'We have identified the following schema from your documents. This schema will be sent to an LLM for processing.' + ) + ).to.exist; + // fragment from { "name": "String" } + expect(screen.queryByText('"String"')).to.exist; + expect(screen.queryByText('"John"')).to.not.exist; + }); + it('renders the faker schema editor when the confirm button is clicked', async () => { await renderModal(); From be20bc1b821d94731ea6dd0421d5bae7a87648c1 Mon Sep 17 00:00:00 2001 From: Kevin Pamaran Date: Tue, 9 Sep 2025 15:22:27 -0400 Subject: [PATCH 05/22] revert --- .../raw-schema-confirmation-screen.tsx | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/packages/compass-collection/src/components/mock-data-generator-modal/raw-schema-confirmation-screen.tsx b/packages/compass-collection/src/components/mock-data-generator-modal/raw-schema-confirmation-screen.tsx index 4c67a3e2a53..b677969c320 100644 --- a/packages/compass-collection/src/components/mock-data-generator-modal/raw-schema-confirmation-screen.tsx +++ b/packages/compass-collection/src/components/mock-data-generator-modal/raw-schema-confirmation-screen.tsx @@ -49,7 +49,9 @@ const RawSchemaConfirmationScreen = ({ namespace, fakerSchemaGenerationStatus, }: RawSchemaConfirmationScreenProps) => { - const enableSampleDocumentPassing = true; + const enableSampleDocumentPassing = usePreference( + 'enableGenAISampleDocumentPassing' + ); const subtitleText = enableSampleDocumentPassing ? 'Sample Documents Collected' From 7873b6eff251aeb715070a39af14b546981561d8 Mon Sep 17 00:00:00 2001 From: Kevin Pamaran Date: Tue, 9 Sep 2025 15:38:06 -0400 Subject: [PATCH 06/22] nit --- .../mock-data-generator-modal.spec.tsx | 7 +++++-- .../raw-schema-confirmation-screen.tsx | 6 +++--- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/packages/compass-collection/src/components/mock-data-generator-modal/mock-data-generator-modal.spec.tsx b/packages/compass-collection/src/components/mock-data-generator-modal/mock-data-generator-modal.spec.tsx index 353649eed00..aa39f0408cf 100644 --- a/packages/compass-collection/src/components/mock-data-generator-modal/mock-data-generator-modal.spec.tsx +++ b/packages/compass-collection/src/components/mock-data-generator-modal/mock-data-generator-modal.spec.tsx @@ -211,6 +211,11 @@ describe('MockDataGeneratorModal', () => { ).to.equal('true'); }); + it('displays the namespace', async () => { + await renderModal(); + expect(screen.queryByText('test.collection')).to.exist; + }); + it('uses the appropriate copy when Generative AI sample document passing is enabled', async () => { await renderModal({ enableGenAISampleDocumentPassing: true }); expect(screen.queryByText('Sample Documents Collected')).to.exist; @@ -266,8 +271,6 @@ describe('MockDataGeneratorModal', () => { expect(screen.getByText('LLM Request failed. Please confirm again.')).to .exist; }); - - // todo: assert that closing then re-opening the modal after an LLM err removes the err message }); describe('on the generate data step', () => { diff --git a/packages/compass-collection/src/components/mock-data-generator-modal/raw-schema-confirmation-screen.tsx b/packages/compass-collection/src/components/mock-data-generator-modal/raw-schema-confirmation-screen.tsx index b677969c320..ea65be095b3 100644 --- a/packages/compass-collection/src/components/mock-data-generator-modal/raw-schema-confirmation-screen.tsx +++ b/packages/compass-collection/src/components/mock-data-generator-modal/raw-schema-confirmation-screen.tsx @@ -61,10 +61,10 @@ const RawSchemaConfirmationScreen = ({ ? 'A sample of document values from your collection will be sent to an LLM for processing.' : 'We have identified the following schema from your documents. This schema will be sent to an LLM for processing.'; - // todo: should establish if unfinished schema analysis edge case should be handled within the modal or - // from the button opening the modal + // (CLOUDP-333853) todo: disable button that opens modal and update tooltip while schema analysis is underway. + // after the todo this state should not be reachable const schemaAnalysisIncompletePlaceholder = ( - Schema analysis has yet to complete successfully. + We are analyzing your collection. ); return ( From 3ce421acd75b25a1b96143ce1cbc6207fc0ba353 Mon Sep 17 00:00:00 2001 From: Kevin Pamaran Date: Tue, 9 Sep 2025 16:23:50 -0400 Subject: [PATCH 07/22] nit --- .../collection-header-actions.tsx | 2 ++ .../raw-schema-confirmation-screen.tsx | 9 ++------- 2 files changed, 4 insertions(+), 7 deletions(-) diff --git a/packages/compass-collection/src/components/collection-header-actions/collection-header-actions.tsx b/packages/compass-collection/src/components/collection-header-actions/collection-header-actions.tsx index e8204823d5c..ee4c46251eb 100644 --- a/packages/compass-collection/src/components/collection-header-actions/collection-header-actions.tsx +++ b/packages/compass-collection/src/components/collection-header-actions/collection-header-actions.tsx @@ -145,6 +145,8 @@ const CollectionHeaderActions: React.FunctionComponent<
} > + // todo within (CLOUDP-333853): update disabled open-modal button + tooltip to communicate if schema analysis is incomplete {exceedsMaxNestingDepth && 'At this time we are unable to generate mock data for collections that have deeply nested documents'} {isCollectionEmpty && diff --git a/packages/compass-collection/src/components/mock-data-generator-modal/raw-schema-confirmation-screen.tsx b/packages/compass-collection/src/components/mock-data-generator-modal/raw-schema-confirmation-screen.tsx index ea65be095b3..2f57c904847 100644 --- a/packages/compass-collection/src/components/mock-data-generator-modal/raw-schema-confirmation-screen.tsx +++ b/packages/compass-collection/src/components/mock-data-generator-modal/raw-schema-confirmation-screen.tsx @@ -61,12 +61,6 @@ const RawSchemaConfirmationScreen = ({ ? 'A sample of document values from your collection will be sent to an LLM for processing.' : 'We have identified the following schema from your documents. This schema will be sent to an LLM for processing.'; - // (CLOUDP-333853) todo: disable button that opens modal and update tooltip while schema analysis is underway. - // after the todo this state should not be reachable - const schemaAnalysisIncompletePlaceholder = ( - We are analyzing your collection. - ); - return (
{schemaAnalysis.status === 'complete' ? ( @@ -97,7 +91,8 @@ const RawSchemaConfirmationScreen = ({ )} ) : ( - schemaAnalysisIncompletePlaceholder + // Not reachable since schema analysis must be finished before the modal can be opened + We are analyzing your collection. )}
); From adf4eacdda344122be76006ee382f29618491767 Mon Sep 17 00:00:00 2001 From: Kevin Pamaran Date: Tue, 9 Sep 2025 16:26:36 -0400 Subject: [PATCH 08/22] nit --- .../collection-header-actions/collection-header-actions.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/compass-collection/src/components/collection-header-actions/collection-header-actions.tsx b/packages/compass-collection/src/components/collection-header-actions/collection-header-actions.tsx index ee4c46251eb..e0548a0d924 100644 --- a/packages/compass-collection/src/components/collection-header-actions/collection-header-actions.tsx +++ b/packages/compass-collection/src/components/collection-header-actions/collection-header-actions.tsx @@ -145,8 +145,8 @@ const CollectionHeaderActions: React.FunctionComponent<
} > - // todo within (CLOUDP-333853): update disabled open-modal button - tooltip to communicate if schema analysis is incomplete + {/* todo within (CLOUDP-333853): update disabled open-modal button + tooltip to communicate if schema analysis is incomplete */} {exceedsMaxNestingDepth && 'At this time we are unable to generate mock data for collections that have deeply nested documents'} {isCollectionEmpty && From 05aa795ce9c085805aab2faabefb8da9e5d53970 Mon Sep 17 00:00:00 2001 From: Kevin Pamaran Date: Wed, 10 Sep 2025 16:42:19 -0400 Subject: [PATCH 09/22] address smaller feedback --- .../collection-header-actions.tsx | 6 ++- .../mock-data-generator-modal.spec.tsx | 14 +++---- .../mock-data-generator-modal.tsx | 15 +++++++ .../raw-schema-confirmation-screen.tsx | 42 +++++++++---------- .../transform-schema-to-field-info.spec.ts | 10 ++--- .../src/transform-schema-to-field-info.ts | 8 ++-- 6 files changed, 55 insertions(+), 40 deletions(-) diff --git a/packages/compass-collection/src/components/collection-header-actions/collection-header-actions.tsx b/packages/compass-collection/src/components/collection-header-actions/collection-header-actions.tsx index e0548a0d924..4c6b133cda0 100644 --- a/packages/compass-collection/src/components/collection-header-actions/collection-header-actions.tsx +++ b/packages/compass-collection/src/components/collection-header-actions/collection-header-actions.tsx @@ -95,12 +95,14 @@ const CollectionHeaderActions: React.FunctionComponent< mockDataGeneratorAssignment?.assignment?.assignmentData?.variant === ExperimentTestGroup.mockDataGeneratorVariant; - const shouldShowMockDataButton = + let shouldShowMockDataButton = isInMockDataTreatmentVariant && atlasMetadata && // Only show in Atlas !isReadonly && // Don't show for readonly collections (views) !sourceName; // sourceName indicates it's a view + // shouldShowMockDataButton = true; + const exceedsMaxNestingDepth = analyzedSchemaDepth > MAX_COLLECTION_NESTING_DEPTH; @@ -145,7 +147,7 @@ const CollectionHeaderActions: React.FunctionComponent< } > - {/* todo within (CLOUDP-333853): update disabled open-modal button + {/* TODO(CLOUDP-333853): update disabled open-modal button tooltip to communicate if schema analysis is incomplete */} {exceedsMaxNestingDepth && 'At this time we are unable to generate mock data for collections that have deeply nested documents'} diff --git a/packages/compass-collection/src/components/mock-data-generator-modal/mock-data-generator-modal.spec.tsx b/packages/compass-collection/src/components/mock-data-generator-modal/mock-data-generator-modal.spec.tsx index aa39f0408cf..7fec9d737fd 100644 --- a/packages/compass-collection/src/components/mock-data-generator-modal/mock-data-generator-modal.spec.tsx +++ b/packages/compass-collection/src/components/mock-data-generator-modal/mock-data-generator-modal.spec.tsx @@ -213,32 +213,32 @@ describe('MockDataGeneratorModal', () => { it('displays the namespace', async () => { await renderModal(); - expect(screen.queryByText('test.collection')).to.exist; + expect(screen.getByText('test.collection')).to.exist; }); it('uses the appropriate copy when Generative AI sample document passing is enabled', async () => { await renderModal({ enableGenAISampleDocumentPassing: true }); - expect(screen.queryByText('Sample Documents Collected')).to.exist; + expect(screen.getByText('Sample Documents Collected')).to.exist; expect( - screen.queryByText( - 'A sample of document values from your collection will be sent to an LLM for processing.' + screen.getByText( + 'A sample of documents from your collection will be sent to an LLM for processing.' ) ).to.exist; // fragment from { "name": "John" } - expect(screen.queryByText('"John"')).to.exist; + expect(screen.getByText('"John"')).to.exist; expect(screen.queryByText('"String"')).to.not.exist; }); it('uses the appropriate copy when Generative AI sample document passing is disabled', async () => { await renderModal(); - expect(screen.queryByText('Document Schema Identified')).to.exist; + expect(screen.getByText('Document Schema Identified')).to.exist; expect( screen.queryByText( 'We have identified the following schema from your documents. This schema will be sent to an LLM for processing.' ) ).to.exist; // fragment from { "name": "String" } - expect(screen.queryByText('"String"')).to.exist; + expect(screen.getByText('"String"')).to.exist; expect(screen.queryByText('"John"')).to.not.exist; }); diff --git a/packages/compass-collection/src/components/mock-data-generator-modal/mock-data-generator-modal.tsx b/packages/compass-collection/src/components/mock-data-generator-modal/mock-data-generator-modal.tsx index 2d7cdfb0854..5f6982de0cb 100644 --- a/packages/compass-collection/src/components/mock-data-generator-modal/mock-data-generator-modal.tsx +++ b/packages/compass-collection/src/components/mock-data-generator-modal/mock-data-generator-modal.tsx @@ -3,6 +3,7 @@ import { connect } from 'react-redux'; import { css, + Body, Button, ButtonVariant, ModalBody, @@ -36,6 +37,11 @@ const rightButtonsStyles = css` flex-direction: row; `; +const namespaceStyles = css({ + marginTop: spacing[200], + marginBottom: spacing[400], +}); + interface Props { isOpen: boolean; onClose: () => void; @@ -43,6 +49,7 @@ interface Props { onNextStep: () => void; onConfirmSchema: () => Promise; onPreviousStep: () => void; + namespace: string; } const MockDataGeneratorModal = ({ @@ -52,6 +59,7 @@ const MockDataGeneratorModal = ({ onNextStep, onConfirmSchema, onPreviousStep, + namespace, }: Props) => { const modalBodyContent = useMemo(() => { switch (currentStep) { @@ -78,6 +86,9 @@ const MockDataGeneratorModal = ({ } }; + const shouldShowNamespace = + currentStep !== MockDataGeneratorStep.GENERATE_DATA; + return ( + {shouldShowNamespace && ( + {namespace} + )}
{modalBodyContent}
@@ -120,6 +134,7 @@ const MockDataGeneratorModal = ({ const mapStateToProps = (state: CollectionState) => ({ isOpen: state.mockDataGenerator.isModalOpen, currentStep: state.mockDataGenerator.currentStep, + namespace: state.namespace, }); const ConnectedMockDataGeneratorModal = connect(mapStateToProps, { diff --git a/packages/compass-collection/src/components/mock-data-generator-modal/raw-schema-confirmation-screen.tsx b/packages/compass-collection/src/components/mock-data-generator-modal/raw-schema-confirmation-screen.tsx index 2f57c904847..b5a75c9e893 100644 --- a/packages/compass-collection/src/components/mock-data-generator-modal/raw-schema-confirmation-screen.tsx +++ b/packages/compass-collection/src/components/mock-data-generator-modal/raw-schema-confirmation-screen.tsx @@ -7,7 +7,6 @@ import { spacing, Banner, BannerVariant, - Code, Body, } from '@mongodb-js/compass-components'; @@ -16,26 +15,23 @@ import toSimplifiedFieldInfo from './to-simplified-field-info'; import type { CollectionState } from '../../modules/collection-tab'; import type { SchemaAnalysisState } from '../../schema-analysis-types'; import type { MockDataGeneratorState } from './types'; +import { Document } from '@mongodb-js/compass-crud'; interface RawSchemaConfirmationScreenProps { schemaAnalysis: SchemaAnalysisState; - namespace: string; fakerSchemaGenerationStatus: MockDataGeneratorState['status']; } -const namespaceStyles = css({ - marginTop: spacing[200], - marginBottom: spacing[400], +const documentContainerStyles = css({ + backgroundColor: palette.gray.light3, + border: `1px solid ${palette.gray.light2}`, + borderRadius: spacing[400], }); const descriptionStyles = css({ marginBottom: spacing[200], }); -const codeStyles = css({ - maxHeight: '30vh', -}); - const errorBannerStyles = css({ marginTop: spacing[400], }); @@ -46,39 +42,40 @@ const errorBannerTextStyles = css({ const RawSchemaConfirmationScreen = ({ schemaAnalysis, - namespace, fakerSchemaGenerationStatus, }: RawSchemaConfirmationScreenProps) => { - const enableSampleDocumentPassing = usePreference( + let enableSampleDocumentPassing = usePreference( 'enableGenAISampleDocumentPassing' ); + // enableSampleDocumentPassing = false; + const subtitleText = enableSampleDocumentPassing ? 'Sample Documents Collected' : 'Document Schema Identified'; const descriptionText = enableSampleDocumentPassing - ? 'A sample of document values from your collection will be sent to an LLM for processing.' + ? 'A sample of documents from your collection will be sent to an LLM for processing.' : 'We have identified the following schema from your documents. This schema will be sent to an LLM for processing.'; return (
{schemaAnalysis.status === 'complete' ? ( <> - {namespace} {subtitleText} {descriptionText} - - {enableSampleDocumentPassing - ? JSON.stringify(schemaAnalysis.sampleDocument, null, 4) - : JSON.stringify( - toSimplifiedFieldInfo(schemaAnalysis.processedSchema), - null, - 4 - )} - +
+ +
{fakerSchemaGenerationStatus === 'error' && ( { return { schemaAnalysis, - namespace: state.namespace, fakerSchemaGenerationStatus, }; }; diff --git a/packages/compass-collection/src/transform-schema-to-field-info.spec.ts b/packages/compass-collection/src/transform-schema-to-field-info.spec.ts index 7486601ce64..b99620a8ef4 100644 --- a/packages/compass-collection/src/transform-schema-to-field-info.spec.ts +++ b/packages/compass-collection/src/transform-schema-to-field-info.spec.ts @@ -1137,7 +1137,7 @@ describe('processSchema', function () { }; expect(() => processSchema(schema)).to.throw( - 'invalid fieldPath "field[invalid": "[]" can only appear at the end of field parts' + "invalid fieldPath 'field[invalid': '[]' can only appear at the end of field parts" ); }); @@ -1167,7 +1167,7 @@ describe('processSchema', function () { }; expect(() => processSchema(schema)).to.throw( - 'invalid fieldPath "field[]invalid": "[]" can only appear at the end of field parts' + "invalid fieldPath 'field[]invalid': '[]' can only appear at the end of field parts" ); }); @@ -1216,11 +1216,11 @@ describe('processSchema', function () { }; expect(() => processSchema(schema)).to.throw( - 'invalid fieldPath "parent.": field parts cannot be empty' + "invalid fieldPath 'parent.': field parts cannot be empty" ); }); - it('throws error for field part that is only "[]"', function () { + it('throws error for a field part that only contains "[]"', function () { const schema: Schema = { fields: [ { @@ -1246,7 +1246,7 @@ describe('processSchema', function () { }; expect(() => processSchema(schema)).to.throw( - "expected fieldPath to have a non-empty part before '[]'" + "invalid fieldPath '[]': field parts must have characters other than '[]'" ); }); }); diff --git a/packages/compass-collection/src/transform-schema-to-field-info.ts b/packages/compass-collection/src/transform-schema-to-field-info.ts index b7e9b6bfff5..262c7aab4a9 100644 --- a/packages/compass-collection/src/transform-schema-to-field-info.ts +++ b/packages/compass-collection/src/transform-schema-to-field-info.ts @@ -239,12 +239,14 @@ function validateFieldPath(fieldPath: string) { for (const part of parts) { if (part === '') { throw new Error( - `invalid fieldPath "${fieldPath}": field parts cannot be empty` + `invalid fieldPath '${fieldPath}': field parts cannot be empty` ); } if (part.replaceAll('[]', '') === '') { - throw Error("expected fieldPath to have a non-empty part before '[]'"); + throw new Error( + `invalid fieldPath '${fieldPath}': field parts must have characters other than '[]'` + ); } // Check that [] only appears as complete pairs and only at the end of the part @@ -256,7 +258,7 @@ function validateFieldPath(fieldPath: string) { const remaining = part.replace(/(\[\])+$/, ''); if (remaining.includes('[') || remaining.includes(']')) { throw new Error( - `invalid fieldPath "${fieldPath}": "[]" can only appear at the end of field parts` + `invalid fieldPath '${fieldPath}': '[]' can only appear at the end of field parts` ); } } From 5c8e35cd4e85e242789b652726e561da39f177af Mon Sep 17 00:00:00 2001 From: Kevin Pamaran Date: Wed, 10 Sep 2025 16:45:40 -0400 Subject: [PATCH 10/22] nit --- .../collection-header-actions/collection-header-actions.tsx | 4 +--- .../raw-schema-confirmation-screen.tsx | 4 +--- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/packages/compass-collection/src/components/collection-header-actions/collection-header-actions.tsx b/packages/compass-collection/src/components/collection-header-actions/collection-header-actions.tsx index 4c6b133cda0..36321c6e051 100644 --- a/packages/compass-collection/src/components/collection-header-actions/collection-header-actions.tsx +++ b/packages/compass-collection/src/components/collection-header-actions/collection-header-actions.tsx @@ -95,14 +95,12 @@ const CollectionHeaderActions: React.FunctionComponent< mockDataGeneratorAssignment?.assignment?.assignmentData?.variant === ExperimentTestGroup.mockDataGeneratorVariant; - let shouldShowMockDataButton = + const shouldShowMockDataButton = isInMockDataTreatmentVariant && atlasMetadata && // Only show in Atlas !isReadonly && // Don't show for readonly collections (views) !sourceName; // sourceName indicates it's a view - // shouldShowMockDataButton = true; - const exceedsMaxNestingDepth = analyzedSchemaDepth > MAX_COLLECTION_NESTING_DEPTH; diff --git a/packages/compass-collection/src/components/mock-data-generator-modal/raw-schema-confirmation-screen.tsx b/packages/compass-collection/src/components/mock-data-generator-modal/raw-schema-confirmation-screen.tsx index b5a75c9e893..7d9143a7241 100644 --- a/packages/compass-collection/src/components/mock-data-generator-modal/raw-schema-confirmation-screen.tsx +++ b/packages/compass-collection/src/components/mock-data-generator-modal/raw-schema-confirmation-screen.tsx @@ -44,12 +44,10 @@ const RawSchemaConfirmationScreen = ({ schemaAnalysis, fakerSchemaGenerationStatus, }: RawSchemaConfirmationScreenProps) => { - let enableSampleDocumentPassing = usePreference( + const enableSampleDocumentPassing = usePreference( 'enableGenAISampleDocumentPassing' ); - // enableSampleDocumentPassing = false; - const subtitleText = enableSampleDocumentPassing ? 'Sample Documents Collected' : 'Document Schema Identified'; From cf25bab79d2bc543d44ccb317e66d154855027bc Mon Sep 17 00:00:00 2001 From: Kevin Pamaran Date: Wed, 10 Sep 2025 16:51:45 -0400 Subject: [PATCH 11/22] rename --- .../to-simplified-field-info.spec.ts | 10 +++++----- .../to-simplified-field-info.ts | 6 +++--- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/packages/compass-collection/src/components/mock-data-generator-modal/to-simplified-field-info.spec.ts b/packages/compass-collection/src/components/mock-data-generator-modal/to-simplified-field-info.spec.ts index 3b50cfa7a70..36040a301ef 100644 --- a/packages/compass-collection/src/components/mock-data-generator-modal/to-simplified-field-info.spec.ts +++ b/packages/compass-collection/src/components/mock-data-generator-modal/to-simplified-field-info.spec.ts @@ -1,6 +1,6 @@ import { expect } from 'chai'; import toSimplifiedFieldInfo from './to-simplified-field-info'; -import type { UserFriendlyFieldInfoTree } from './to-simplified-field-info'; +import type { SimplifiedFieldInfoTree } from './to-simplified-field-info'; describe('toSimplifiedFieldInfo', function () { it('simple case with minimal nesting and no arrays', function () { @@ -39,7 +39,7 @@ describe('toSimplifiedFieldInfo', function () { const result = toSimplifiedFieldInfo(input); - const expected: UserFriendlyFieldInfoTree = { + const expected: SimplifiedFieldInfoTree = { user: { name: 'String', age: 'Number', @@ -93,7 +93,7 @@ describe('toSimplifiedFieldInfo', function () { const result = toSimplifiedFieldInfo(input); - const expected: UserFriendlyFieldInfoTree = { + const expected: SimplifiedFieldInfoTree = { 'tags[]': 'String', 'scores[]': 'Number', 'matrix[][]': 'Number', @@ -141,7 +141,7 @@ describe('toSimplifiedFieldInfo', function () { const result = toSimplifiedFieldInfo(input); - const expected: UserFriendlyFieldInfoTree = { + const expected: SimplifiedFieldInfoTree = { 'items[]': { id: 'Number', name: 'String', @@ -224,7 +224,7 @@ describe('toSimplifiedFieldInfo', function () { const result = toSimplifiedFieldInfo(input); - const expected: UserFriendlyFieldInfoTree = { + const expected: SimplifiedFieldInfoTree = { 'cube[][][]': 'Number', 'matrix[][]': { x: 'Number', diff --git a/packages/compass-collection/src/components/mock-data-generator-modal/to-simplified-field-info.ts b/packages/compass-collection/src/components/mock-data-generator-modal/to-simplified-field-info.ts index 432871b2150..52265340ac9 100644 --- a/packages/compass-collection/src/components/mock-data-generator-modal/to-simplified-field-info.ts +++ b/packages/compass-collection/src/components/mock-data-generator-modal/to-simplified-field-info.ts @@ -4,7 +4,7 @@ import type { FieldInfo } from '../../schema-analysis-types'; type UserFriendlyFieldInfoNode = | { [field: string]: UserFriendlyFieldInfoNode } | FieldInfo['type']; -export type UserFriendlyFieldInfoTree = { +export type SimplifiedFieldInfoTree = { [field: string]: UserFriendlyFieldInfoNode; }; @@ -14,13 +14,13 @@ export type UserFriendlyFieldInfoTree = { */ export default function toSimplifiedFieldInfo( input: ReturnType -): UserFriendlyFieldInfoTree { +): SimplifiedFieldInfoTree { // ensure parent nodes are created before their children const sortedFieldPaths = Object.keys(input).sort( (f1, f2) => countSeparators(f1) - countSeparators(f2) ); - const result: UserFriendlyFieldInfoTree = {}; + const result: SimplifiedFieldInfoTree = {}; for (const path of sortedFieldPaths) { const fieldParts = path.split('.'); From c13f44b6e19c0d7e0f0a0d4214bb37d047d9a453 Mon Sep 17 00:00:00 2001 From: Kevin Pamaran Date: Wed, 10 Sep 2025 18:08:36 -0400 Subject: [PATCH 12/22] define and handle unsupported state --- .../collection-header-actions.spec.tsx | 33 +++++++++++++++++++ .../collection-header-actions.tsx | 6 ++++ .../collection-header/collection-header.tsx | 9 +++++ .../to-simplified-field-info.ts | 3 +- .../src/modules/collection-tab.ts | 29 +++++++++++++++- .../src/schema-analysis-types.ts | 2 +- .../src/transform-schema-to-field-info.ts | 27 +++++++++++++-- 7 files changed, 103 insertions(+), 6 deletions(-) diff --git a/packages/compass-collection/src/components/collection-header-actions/collection-header-actions.spec.tsx b/packages/compass-collection/src/components/collection-header-actions/collection-header-actions.spec.tsx index 1f98a73199d..05ddc16c916 100644 --- a/packages/compass-collection/src/components/collection-header-actions/collection-header-actions.spec.tsx +++ b/packages/compass-collection/src/components/collection-header-actions/collection-header-actions.spec.tsx @@ -61,6 +61,7 @@ describe('CollectionHeaderActions [Component]', function () { hasSchemaAnalysisData={true} analyzedSchemaDepth={2} schemaAnalysisStatus="complete" + schemaAnalysisError={null} {...props} /> @@ -294,5 +295,37 @@ describe('CollectionHeaderActions [Component]', function () { expect(button).to.exist; expect(button).to.have.attribute('aria-disabled', 'true'); }); + + it('should show an error banner when the schema is in an unsupported state', async function () { + mockUseAssignment.returns({ + assignment: { + assignmentData: { + variant: 'mockDataGeneratorVariant', + }, + }, + }); + + await renderCollectionHeaderActions( + { + namespace: 'test.collection', + isReadonly: false, + hasSchemaAnalysisData: false, + schemaAnalysisStatus: 'error', + schemaAnalysisError: { + errorType: 'unsupportedState', + errorMessage: 'Unsupported state', + }, + onOpenMockDataModal: sinon.stub(), + }, + {}, + atlasConnectionInfo + ); + + const button = screen.getByTestId( + 'collection-header-generate-mock-data-button' + ); + expect(button).to.exist; + expect(button).to.have.attribute('aria-disabled', 'true'); + }); }); }); diff --git a/packages/compass-collection/src/components/collection-header-actions/collection-header-actions.tsx b/packages/compass-collection/src/components/collection-header-actions/collection-header-actions.tsx index 36321c6e051..d885764f8c8 100644 --- a/packages/compass-collection/src/components/collection-header-actions/collection-header-actions.tsx +++ b/packages/compass-collection/src/components/collection-header-actions/collection-header-actions.tsx @@ -21,6 +21,7 @@ import { import { SCHEMA_ANALYSIS_STATE_ANALYZING, type SchemaAnalysisStatus, + type SchemaAnalysisError, } from '../../schema-analysis-types'; /** @@ -57,6 +58,7 @@ type CollectionHeaderActionsProps = { sourcePipeline?: unknown[]; onOpenMockDataModal: () => void; hasSchemaAnalysisData: boolean; + schemaAnalysisError: SchemaAnalysisError | null; analyzedSchemaDepth: number; schemaAnalysisStatus: SchemaAnalysisStatus | null; }; @@ -73,6 +75,7 @@ const CollectionHeaderActions: React.FunctionComponent< hasSchemaAnalysisData, analyzedSchemaDepth, schemaAnalysisStatus, + schemaAnalysisError, }: CollectionHeaderActionsProps) => { const connectionInfo = useConnectionInfo(); const { id: connectionId, atlasMetadata } = connectionInfo; @@ -151,6 +154,9 @@ const CollectionHeaderActions: React.FunctionComponent< 'At this time we are unable to generate mock data for collections that have deeply nested documents'} {isCollectionEmpty && 'Please add data to your collection to generate similar mock documents'} + {schemaAnalysisError && + schemaAnalysisError.errorType === 'unsupportedState' && + 'This collection has a field with a name that contains a ".", which mock data generation does not support at this time.'} )} {atlasMetadata && ( diff --git a/packages/compass-collection/src/components/collection-header/collection-header.tsx b/packages/compass-collection/src/components/collection-header/collection-header.tsx index 7aca29141b7..1219529954c 100644 --- a/packages/compass-collection/src/components/collection-header/collection-header.tsx +++ b/packages/compass-collection/src/components/collection-header/collection-header.tsx @@ -24,7 +24,9 @@ import { openMockDataGeneratorModal } from '../../modules/collection-tab'; import type { CollectionState } from '../../modules/collection-tab'; import { SCHEMA_ANALYSIS_STATE_COMPLETE, + SCHEMA_ANALYSIS_STATE_ERROR, type SchemaAnalysisStatus, + type SchemaAnalysisError, } from '../../schema-analysis-types'; const collectionHeaderStyles = css({ @@ -70,6 +72,7 @@ type CollectionHeaderProps = { hasSchemaAnalysisData: boolean; analyzedSchemaDepth: number; schemaAnalysisStatus: SchemaAnalysisStatus | null; + schemaAnalysisError: SchemaAnalysisError | null; }; const getInsightsForPipeline = (pipeline: any[], isAtlas: boolean) => { @@ -108,6 +111,7 @@ const CollectionHeader: React.FunctionComponent = ({ hasSchemaAnalysisData, analyzedSchemaDepth, schemaAnalysisStatus, + schemaAnalysisError, }) => { const darkMode = useDarkMode(); const showInsights = usePreference('showInsights'); @@ -188,6 +192,7 @@ const CollectionHeader: React.FunctionComponent = ({ hasSchemaAnalysisData={hasSchemaAnalysisData} analyzedSchemaDepth={analyzedSchemaDepth} schemaAnalysisStatus={schemaAnalysisStatus} + schemaAnalysisError={schemaAnalysisError} />
@@ -199,6 +204,10 @@ const mapStateToProps = (state: CollectionState) => { const { schemaAnalysis } = state; return { + schemaAnalysisError: + schemaAnalysis && schemaAnalysis.status === SCHEMA_ANALYSIS_STATE_ERROR + ? schemaAnalysis.error + : null, hasSchemaAnalysisData: schemaAnalysis && schemaAnalysis.status === SCHEMA_ANALYSIS_STATE_COMPLETE && diff --git a/packages/compass-collection/src/components/mock-data-generator-modal/to-simplified-field-info.ts b/packages/compass-collection/src/components/mock-data-generator-modal/to-simplified-field-info.ts index 52265340ac9..37d6cc5c69c 100644 --- a/packages/compass-collection/src/components/mock-data-generator-modal/to-simplified-field-info.ts +++ b/packages/compass-collection/src/components/mock-data-generator-modal/to-simplified-field-info.ts @@ -1,3 +1,4 @@ +import { FIELD_NAME_SEPARATOR } from '../../transform-schema-to-field-info'; import type { processSchema } from '../../transform-schema-to-field-info'; import type { FieldInfo } from '../../schema-analysis-types'; @@ -22,7 +23,7 @@ export default function toSimplifiedFieldInfo( const result: SimplifiedFieldInfoTree = {}; for (const path of sortedFieldPaths) { - const fieldParts = path.split('.'); + const fieldParts = path.split(FIELD_NAME_SEPARATOR); let node = result; for (let i = 0; i < fieldParts.length; i++) { diff --git a/packages/compass-collection/src/modules/collection-tab.ts b/packages/compass-collection/src/modules/collection-tab.ts index e1f3afa2eac..e102098882b 100644 --- a/packages/compass-collection/src/modules/collection-tab.ts +++ b/packages/compass-collection/src/modules/collection-tab.ts @@ -32,7 +32,10 @@ import { type FieldInfo, } from '../schema-analysis-types'; import { calculateSchemaDepth } from '../calculate-schema-depth'; -import { processSchema } from '../transform-schema-to-field-info'; +import { + processSchema, + ProcessSchemaUnsupportedStateError, +} from '../transform-schema-to-field-info'; import type { Document, MongoError } from 'mongodb'; import { MockDataGeneratorStep } from '../components/mock-data-generator-modal/types'; import type { MockDataGeneratorState } from '../components/mock-data-generator-modal/types'; @@ -51,6 +54,13 @@ function isAction( const ERROR_CODE_MAX_TIME_MS_EXPIRED = 50; function getErrorDetails(error: Error): SchemaAnalysisError { + if (error instanceof ProcessSchemaUnsupportedStateError) { + return { + errorType: 'unsupportedState', + errorMessage: error.message, + }; + } + const errorCode = (error as MongoError).code; const errorMessage = error.message || 'Unknown error'; let errorType: SchemaAnalysisError['errorType'] = 'general'; @@ -648,6 +658,23 @@ export const analyzeCollectionSchema = (): CollectionThunkAction< return; } + if (err instanceof ProcessSchemaUnsupportedStateError) { + logger.log.error( + mongoLogId(1_001_000_365), + 'Collection', + 'Schema analysis failed due to unsupported state', + { + namespace, + error: err.message, + } + ); + dispatch({ + type: CollectionActions.SchemaAnalysisFailed, + error: err, + }); + return; + } + logger.log.error( mongoLogId(1_001_000_363), 'Collection', diff --git a/packages/compass-collection/src/schema-analysis-types.ts b/packages/compass-collection/src/schema-analysis-types.ts index 83a501ceb20..286fc166158 100644 --- a/packages/compass-collection/src/schema-analysis-types.ts +++ b/packages/compass-collection/src/schema-analysis-types.ts @@ -22,7 +22,7 @@ export type SchemaAnalysisStartedState = { export type SchemaAnalysisError = { errorMessage: string; - errorType: 'timeout' | 'highComplexity' | 'general'; + errorType: 'timeout' | 'highComplexity' | 'general' | 'unsupportedState'; }; export type SchemaAnalysisErrorState = { diff --git a/packages/compass-collection/src/transform-schema-to-field-info.ts b/packages/compass-collection/src/transform-schema-to-field-info.ts index 262c7aab4a9..3f386f43c02 100644 --- a/packages/compass-collection/src/transform-schema-to-field-info.ts +++ b/packages/compass-collection/src/transform-schema-to-field-info.ts @@ -43,6 +43,21 @@ import { * Maximum number of sample values to include for each field */ const MAX_SAMPLE_VALUES = 10; +export const FIELD_NAME_SEPARATOR = '.'; + +export class ProcessSchemaUnsupportedStateError extends Error { + constructor(message: string) { + super(message); + this.name = 'ProcessSchemaUnsupportedStateError'; + } +} + +export class ProcessSchemaValidationError extends Error { + constructor(message: string) { + super(message); + this.name = 'ProcessSchemaValidationError'; + } +} /** * Converts a BSON value to its primitive JavaScript equivalent @@ -163,6 +178,12 @@ function processNamedField( return; } + if (field.name.includes(FIELD_NAME_SEPARATOR)) { + throw new ProcessSchemaUnsupportedStateError( + `no support for field names that contain a '.' ; field name: '${field.name}'` + ); + } + const currentPath = pathPrefix ? `${pathPrefix}.${field.name}` : field.name; // Process based on the type @@ -238,13 +259,13 @@ function validateFieldPath(fieldPath: string) { for (const part of parts) { if (part === '') { - throw new Error( + throw new ProcessSchemaValidationError( `invalid fieldPath '${fieldPath}': field parts cannot be empty` ); } if (part.replaceAll('[]', '') === '') { - throw new Error( + throw new ProcessSchemaValidationError( `invalid fieldPath '${fieldPath}': field parts must have characters other than '[]'` ); } @@ -257,7 +278,7 @@ function validateFieldPath(fieldPath: string) { // $ - at the end of the string const remaining = part.replace(/(\[\])+$/, ''); if (remaining.includes('[') || remaining.includes(']')) { - throw new Error( + throw new ProcessSchemaValidationError( `invalid fieldPath '${fieldPath}': '[]' can only appear at the end of field parts` ); } From 833fd37c619c8e51ca88b957c768236628ed59de Mon Sep 17 00:00:00 2001 From: Kevin Pamaran Date: Wed, 10 Sep 2025 18:22:41 -0400 Subject: [PATCH 13/22] nit --- .../mock-data-generator-modal/to-simplified-field-info.ts | 2 +- .../compass-collection/src/transform-schema-to-field-info.ts | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/compass-collection/src/components/mock-data-generator-modal/to-simplified-field-info.ts b/packages/compass-collection/src/components/mock-data-generator-modal/to-simplified-field-info.ts index 37d6cc5c69c..c6b8a9656a8 100644 --- a/packages/compass-collection/src/components/mock-data-generator-modal/to-simplified-field-info.ts +++ b/packages/compass-collection/src/components/mock-data-generator-modal/to-simplified-field-info.ts @@ -44,5 +44,5 @@ export default function toSimplifiedFieldInfo( } function countSeparators(input: string): number { - return input.split('.').length - 1; + return input.split(FIELD_NAME_SEPARATOR).length - 1; } diff --git a/packages/compass-collection/src/transform-schema-to-field-info.ts b/packages/compass-collection/src/transform-schema-to-field-info.ts index 3f386f43c02..a8ae435080c 100644 --- a/packages/compass-collection/src/transform-schema-to-field-info.ts +++ b/packages/compass-collection/src/transform-schema-to-field-info.ts @@ -180,7 +180,7 @@ function processNamedField( if (field.name.includes(FIELD_NAME_SEPARATOR)) { throw new ProcessSchemaUnsupportedStateError( - `no support for field names that contain a '.' ; field name: '${field.name}'` + `no support for field names that contain a '${FIELD_NAME_SEPARATOR}' ; field name: '${field.name}'` ); } @@ -255,7 +255,7 @@ function getMostFrequentType(types: SchemaType[]): SchemaType | null { * inputs are required to simulate these unlikely errors. */ function validateFieldPath(fieldPath: string) { - const parts = fieldPath.split('.'); + const parts = fieldPath.split(FIELD_NAME_SEPARATOR); for (const part of parts) { if (part === '') { From 78e3f5a29465efa38bd826e63c32e3a02ad696f2 Mon Sep 17 00:00:00 2001 From: Kevin Pamaran Date: Wed, 10 Sep 2025 18:24:50 -0400 Subject: [PATCH 14/22] downgrade log severity --- packages/compass-collection/src/modules/collection-tab.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/compass-collection/src/modules/collection-tab.ts b/packages/compass-collection/src/modules/collection-tab.ts index e102098882b..49d403f90b3 100644 --- a/packages/compass-collection/src/modules/collection-tab.ts +++ b/packages/compass-collection/src/modules/collection-tab.ts @@ -659,7 +659,7 @@ export const analyzeCollectionSchema = (): CollectionThunkAction< } if (err instanceof ProcessSchemaUnsupportedStateError) { - logger.log.error( + logger.log.info( mongoLogId(1_001_000_365), 'Collection', 'Schema analysis failed due to unsupported state', From ef9dfc8761cf826f6597ad7ca901a929d16db5ad Mon Sep 17 00:00:00 2001 From: Kevin Pamaran Date: Wed, 10 Sep 2025 18:34:31 -0400 Subject: [PATCH 15/22] add compass-crud as a dep to compass-collection --- package-lock.json | 2 ++ packages/compass-collection/package.json | 1 + 2 files changed, 3 insertions(+) diff --git a/package-lock.json b/package-lock.json index 9b2e71e0417..e7091fe5597 100644 --- a/package-lock.json +++ b/package-lock.json @@ -47723,6 +47723,7 @@ "@mongodb-js/compass-app-stores": "^7.58.1", "@mongodb-js/compass-components": "^1.50.1", "@mongodb-js/compass-connections": "^1.72.1", + "@mongodb-js/compass-crud": "^13.72.1", "@mongodb-js/compass-generative-ai": "^0.52.1", "@mongodb-js/compass-logging": "^1.7.13", "@mongodb-js/compass-telemetry": "^1.14.1", @@ -61217,6 +61218,7 @@ "@mongodb-js/compass-app-stores": "^7.58.1", "@mongodb-js/compass-components": "^1.50.1", "@mongodb-js/compass-connections": "^1.72.1", + "@mongodb-js/compass-crud": "^13.72.1", "@mongodb-js/compass-generative-ai": "^0.52.1", "@mongodb-js/compass-logging": "^1.7.13", "@mongodb-js/compass-telemetry": "^1.14.1", diff --git a/packages/compass-collection/package.json b/packages/compass-collection/package.json index 31bdfbcd877..d0b3821d6b4 100644 --- a/packages/compass-collection/package.json +++ b/packages/compass-collection/package.json @@ -52,6 +52,7 @@ "@mongodb-js/compass-app-stores": "^7.58.1", "@mongodb-js/compass-components": "^1.50.1", "@mongodb-js/compass-connections": "^1.72.1", + "@mongodb-js/compass-crud": "^13.72.1", "@mongodb-js/compass-generative-ai": "^0.52.1", "@mongodb-js/compass-logging": "^1.7.13", "@mongodb-js/compass-telemetry": "^1.14.1", From 65b9a9ee629eda4205561dfcc29042a899e5d85a Mon Sep 17 00:00:00 2001 From: Kevin Pamaran Date: Wed, 10 Sep 2025 19:00:52 -0400 Subject: [PATCH 16/22] remove validation --- .../transform-schema-to-field-info.spec.ts | 60 ------------------- .../src/transform-schema-to-field-info.ts | 13 ---- 2 files changed, 73 deletions(-) diff --git a/packages/compass-collection/src/transform-schema-to-field-info.spec.ts b/packages/compass-collection/src/transform-schema-to-field-info.spec.ts index b99620a8ef4..06bd64de345 100644 --- a/packages/compass-collection/src/transform-schema-to-field-info.spec.ts +++ b/packages/compass-collection/src/transform-schema-to-field-info.spec.ts @@ -1111,66 +1111,6 @@ describe('processSchema', function () { * These are unlikely to occur with valid `Schema` inputs to `processSchema`. */ describe('validateFieldPath error conditions', function () { - it('throws error for incomplete brackets in middle of field part', function () { - const schema: Schema = { - fields: [ - { - name: 'field[invalid', // Incomplete bracket - path: ['field[invalid'], - count: 1, - type: ['String'], - probability: 1.0, - hasDuplicates: false, - types: [ - { - name: 'String', - bsonType: 'String', - path: ['field[invalid'], - count: 1, - probability: 1.0, - values: ['test'], - }, - ], - }, - ], - count: 1, - }; - - expect(() => processSchema(schema)).to.throw( - "invalid fieldPath 'field[invalid': '[]' can only appear at the end of field parts" - ); - }); - - it('throws error for brackets in middle of field part', function () { - const schema: Schema = { - fields: [ - { - name: 'field[]invalid', // Brackets in middle - path: ['field[]invalid'], - count: 1, - type: ['String'], - probability: 1.0, - hasDuplicates: false, - types: [ - { - name: 'String', - bsonType: 'String', - path: ['field[]invalid'], - count: 1, - probability: 1.0, - values: ['test'], - }, - ], - }, - ], - count: 1, - }; - - expect(() => processSchema(schema)).to.throw( - "invalid fieldPath 'field[]invalid': '[]' can only appear at the end of field parts" - ); - }); - it('throws error for empty field parts', function () { const schema: Schema = { fields: [ diff --git a/packages/compass-collection/src/transform-schema-to-field-info.ts b/packages/compass-collection/src/transform-schema-to-field-info.ts index a8ae435080c..060fc80d345 100644 --- a/packages/compass-collection/src/transform-schema-to-field-info.ts +++ b/packages/compass-collection/src/transform-schema-to-field-info.ts @@ -269,18 +269,5 @@ function validateFieldPath(fieldPath: string) { `invalid fieldPath '${fieldPath}': field parts must have characters other than '[]'` ); } - - // Check that [] only appears as complete pairs and only at the end of the part - // Remove all trailing [] pairs and check if any [] remains in the middle - // Regex breakdown: (\[\])+$ - // (\[\]) - matches exactly the characters "[]" (brackets are escaped) - // + - one or more times - // $ - at the end of the string - const remaining = part.replace(/(\[\])+$/, ''); - if (remaining.includes('[') || remaining.includes(']')) { - throw new ProcessSchemaValidationError( - `invalid fieldPath '${fieldPath}': '[]' can only appear at the end of field parts` - ); - } } } From e411dd94ace6dcc745095a9d7914ed2708f87507 Mon Sep 17 00:00:00 2001 From: Kevin Pamaran Date: Thu, 11 Sep 2025 12:07:19 -0400 Subject: [PATCH 17/22] avoid cyclical dep --- package-lock.json | 2 -- packages/compass-collection/package.json | 1 - .../raw-schema-confirmation-screen.tsx | 30 ++++++++++++------- 3 files changed, 19 insertions(+), 14 deletions(-) diff --git a/package-lock.json b/package-lock.json index bf033141b80..71a96cf34f9 100644 --- a/package-lock.json +++ b/package-lock.json @@ -47721,7 +47721,6 @@ "@mongodb-js/compass-app-stores": "^7.59.0", "@mongodb-js/compass-components": "^1.51.0", "@mongodb-js/compass-connections": "^1.73.0", - "@mongodb-js/compass-crud": "^13.72.1", "@mongodb-js/compass-generative-ai": "^0.53.0", "@mongodb-js/compass-logging": "^1.7.14", "@mongodb-js/compass-telemetry": "^1.15.0", @@ -61217,7 +61216,6 @@ "@mongodb-js/compass-app-stores": "^7.59.0", "@mongodb-js/compass-components": "^1.51.0", "@mongodb-js/compass-connections": "^1.73.0", - "@mongodb-js/compass-crud": "^13.72.1", "@mongodb-js/compass-generative-ai": "^0.53.0", "@mongodb-js/compass-logging": "^1.7.14", "@mongodb-js/compass-telemetry": "^1.15.0", diff --git a/packages/compass-collection/package.json b/packages/compass-collection/package.json index 245eb3bab39..2b85d985f9d 100644 --- a/packages/compass-collection/package.json +++ b/packages/compass-collection/package.json @@ -52,7 +52,6 @@ "@mongodb-js/compass-app-stores": "^7.59.0", "@mongodb-js/compass-components": "^1.51.0", "@mongodb-js/compass-connections": "^1.73.0", - "@mongodb-js/compass-crud": "^13.72.1", "@mongodb-js/compass-generative-ai": "^0.53.0", "@mongodb-js/compass-logging": "^1.7.14", "@mongodb-js/compass-telemetry": "^1.15.0", diff --git a/packages/compass-collection/src/components/mock-data-generator-modal/raw-schema-confirmation-screen.tsx b/packages/compass-collection/src/components/mock-data-generator-modal/raw-schema-confirmation-screen.tsx index 7d9143a7241..bd4008692b3 100644 --- a/packages/compass-collection/src/components/mock-data-generator-modal/raw-schema-confirmation-screen.tsx +++ b/packages/compass-collection/src/components/mock-data-generator-modal/raw-schema-confirmation-screen.tsx @@ -8,6 +8,7 @@ import { Banner, BannerVariant, Body, + DocumentList, } from '@mongodb-js/compass-components'; import { usePreference } from 'compass-preferences-model/provider'; @@ -15,18 +16,23 @@ import toSimplifiedFieldInfo from './to-simplified-field-info'; import type { CollectionState } from '../../modules/collection-tab'; import type { SchemaAnalysisState } from '../../schema-analysis-types'; import type { MockDataGeneratorState } from './types'; -import { Document } from '@mongodb-js/compass-crud'; +import HadronDocument from 'hadron-document'; interface RawSchemaConfirmationScreenProps { schemaAnalysis: SchemaAnalysisState; fakerSchemaGenerationStatus: MockDataGeneratorState['status']; } -const documentContainerStyles = css({ - backgroundColor: palette.gray.light3, - border: `1px solid ${palette.gray.light2}`, - borderRadius: spacing[400], -}); +// note: the "> div" selector works around the lack of a className prop on HadronDocument +const documentContainerStyles = css` + background-color: ${palette.gray.light3}; + border: 1px solid ${palette.gray.light2}; + border-radius: ${spacing[400]}px; + + > div { + padding: ${spacing[400]}px ${spacing[900]}px; + } +`; const descriptionStyles = css({ marginBottom: spacing[200], @@ -65,12 +71,14 @@ const RawSchemaConfirmationScreen = ({ {descriptionText}
-
From df6c2a6518de51ee8e78b94c194e7740cf27b01f Mon Sep 17 00:00:00 2001 From: Kevin Pamaran Date: Thu, 11 Sep 2025 12:07:54 -0400 Subject: [PATCH 18/22] increase readability given multipel tooltip messages --- .../collection-header-actions.tsx | 37 +++++++++++++++---- 1 file changed, 30 insertions(+), 7 deletions(-) diff --git a/packages/compass-collection/src/components/collection-header-actions/collection-header-actions.tsx b/packages/compass-collection/src/components/collection-header-actions/collection-header-actions.tsx index d885764f8c8..38ac74d9d84 100644 --- a/packages/compass-collection/src/components/collection-header-actions/collection-header-actions.tsx +++ b/packages/compass-collection/src/components/collection-header-actions/collection-header-actions.tsx @@ -36,6 +36,14 @@ const collectionHeaderActionsStyles = css({ gap: spacing[200], }); +const tooltipMessageStyles = css({ + display: 'block', + marginBottom: spacing[100], + '&:last-child': { + marginBottom: 0, + }, +}); + function buildChartsUrl( groupId: string, clusterName: string, @@ -150,13 +158,28 @@ const CollectionHeaderActions: React.FunctionComponent< > {/* TODO(CLOUDP-333853): update disabled open-modal button tooltip to communicate if schema analysis is incomplete */} - {exceedsMaxNestingDepth && - 'At this time we are unable to generate mock data for collections that have deeply nested documents'} - {isCollectionEmpty && - 'Please add data to your collection to generate similar mock documents'} - {schemaAnalysisError && - schemaAnalysisError.errorType === 'unsupportedState' && - 'This collection has a field with a name that contains a ".", which mock data generation does not support at this time.'} + <> + {exceedsMaxNestingDepth && ( + + At this time we are unable to generate mock data for collections + that have deeply nested documents. + + )} + {isCollectionEmpty && ( + + Please add data to your collection to generate similar mock + documents. + + )} + {schemaAnalysisError && + schemaAnalysisError.errorType === 'unsupportedState' && ( + + This collection has a field with a name that contains a + ".", which mock data generation does not support at + this time. + + )} + )} {atlasMetadata && ( From 36342139e25f6271516638bad48198791455a60b Mon Sep 17 00:00:00 2001 From: Kevin Pamaran Date: Fri, 12 Sep 2025 10:44:53 -0400 Subject: [PATCH 19/22] add className prop to HadronDocument --- .../raw-schema-confirmation-screen.tsx | 18 +++++++++--------- .../src/components/document-list/document.tsx | 4 +++- 2 files changed, 12 insertions(+), 10 deletions(-) diff --git a/packages/compass-collection/src/components/mock-data-generator-modal/raw-schema-confirmation-screen.tsx b/packages/compass-collection/src/components/mock-data-generator-modal/raw-schema-confirmation-screen.tsx index bd4008692b3..e55182e192c 100644 --- a/packages/compass-collection/src/components/mock-data-generator-modal/raw-schema-confirmation-screen.tsx +++ b/packages/compass-collection/src/components/mock-data-generator-modal/raw-schema-confirmation-screen.tsx @@ -23,16 +23,15 @@ interface RawSchemaConfirmationScreenProps { fakerSchemaGenerationStatus: MockDataGeneratorState['status']; } -// note: the "> div" selector works around the lack of a className prop on HadronDocument -const documentContainerStyles = css` - background-color: ${palette.gray.light3}; - border: 1px solid ${palette.gray.light2}; - border-radius: ${spacing[400]}px; +const documentContainerStyles = css({ + backgroundColor: palette.gray.light3, + border: `1px solid ${palette.gray.light2}`, + borderRadius: spacing[400], +}); - > div { - padding: ${spacing[400]}px ${spacing[900]}px; - } -`; +const documentStyles = css({ + padding: `${spacing[400]}px ${spacing[900]}px`, +}); const descriptionStyles = css({ marginBottom: spacing[200], @@ -72,6 +71,7 @@ const RawSchemaConfirmationScreen = ({ {descriptionText}
void; query?: Record; + className?: string; }> = ({ value: document, editable = false, @@ -96,6 +97,7 @@ const HadronDocument: React.FunctionComponent<{ extraGutterWidth, onUpdateQuery, query, + className = '', }) => { const { elements, visibleElements } = useHadronDocument(document); const [autoFocus, setAutoFocus] = useState<{ @@ -130,7 +132,7 @@ const HadronDocument: React.FunctionComponent<{ ); return ( -
+
Date: Fri, 12 Sep 2025 15:27:38 -0400 Subject: [PATCH 20/22] fix grammar --- .../compass-collection/src/transform-schema-to-field-info.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/compass-collection/src/transform-schema-to-field-info.ts b/packages/compass-collection/src/transform-schema-to-field-info.ts index 060fc80d345..f88dfb1e6a1 100644 --- a/packages/compass-collection/src/transform-schema-to-field-info.ts +++ b/packages/compass-collection/src/transform-schema-to-field-info.ts @@ -251,7 +251,7 @@ function getMostFrequentType(types: SchemaType[]): SchemaType | null { } /** - * Note: This validation is takes an extra defensive stance. As illustrated by the unit tests, malformed + * Note: This validation takes a defensive stance. As illustrated by the unit tests, malformed * inputs are required to simulate these unlikely errors. */ function validateFieldPath(fieldPath: string) { From 90ef737a7775e1e9f4f67a98d29651897fc1eb59 Mon Sep 17 00:00:00 2001 From: Kevin Pamaran Date: Mon, 15 Sep 2025 10:37:35 -0400 Subject: [PATCH 21/22] Remove error handling branch --- .../src/modules/collection-tab.ts | 17 ----------------- 1 file changed, 17 deletions(-) diff --git a/packages/compass-collection/src/modules/collection-tab.ts b/packages/compass-collection/src/modules/collection-tab.ts index 49d403f90b3..8c7323045f6 100644 --- a/packages/compass-collection/src/modules/collection-tab.ts +++ b/packages/compass-collection/src/modules/collection-tab.ts @@ -658,23 +658,6 @@ export const analyzeCollectionSchema = (): CollectionThunkAction< return; } - if (err instanceof ProcessSchemaUnsupportedStateError) { - logger.log.info( - mongoLogId(1_001_000_365), - 'Collection', - 'Schema analysis failed due to unsupported state', - { - namespace, - error: err.message, - } - ); - dispatch({ - type: CollectionActions.SchemaAnalysisFailed, - error: err, - }); - return; - } - logger.log.error( mongoLogId(1_001_000_363), 'Collection', From e0181f02da4e7101a26e73c748b7af858a2ced39 Mon Sep 17 00:00:00 2001 From: Kevin Pamaran Date: Mon, 15 Sep 2025 11:33:05 -0400 Subject: [PATCH 22/22] fix sentence --- .../mock-data-generator-modal/to-simplified-field-info.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/compass-collection/src/components/mock-data-generator-modal/to-simplified-field-info.ts b/packages/compass-collection/src/components/mock-data-generator-modal/to-simplified-field-info.ts index c6b8a9656a8..056dad9d670 100644 --- a/packages/compass-collection/src/components/mock-data-generator-modal/to-simplified-field-info.ts +++ b/packages/compass-collection/src/components/mock-data-generator-modal/to-simplified-field-info.ts @@ -11,7 +11,7 @@ export type SimplifiedFieldInfoTree = { /** * Usage is for display purposes only. The result is derived from the work of `processSchema`, - * ensuring that what is a simplification of what the LLM processes. + * ensuring that the user sees a simplification of what the LLM processes. */ export default function toSimplifiedFieldInfo( input: ReturnType