From dbeb25602c13792a2ad2c8a73ef4464179406bfe Mon Sep 17 00:00:00 2001 From: Kyle W Lai Date: Mon, 6 Oct 2025 12:58:10 -0400 Subject: [PATCH 1/3] Add mappings --- .../collection-header-actions.tsx | 6 +- .../mock-data-generator-modal/constants.ts | 70 +++++++++++++++++++ .../faker-mapping-selector.tsx | 25 +++++-- .../faker-schema-editor-screen.tsx | 5 ++ .../raw-schema-confirmation-screen.tsx | 17 +++-- .../src/stores/collection-tab.ts | 7 +- .../src/atlas-ai-service.ts | 3 +- packages/compass-generative-ai/src/index.ts | 2 + 8 files changed, 113 insertions(+), 22 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 613ab6375e0..d4c2a515085 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 @@ -102,9 +102,9 @@ const CollectionHeaderActions: React.FunctionComponent< const { database, collection } = toNS(namespace); // Check if user is in treatment group for Mock Data Generator experiment - const isInMockDataTreatmentVariant = - mockDataGeneratorAssignment?.assignment?.assignmentData?.variant === - ExperimentTestGroup.mockDataGeneratorVariant; + const isInMockDataTreatmentVariant = true; // EDITBACK + // mockDataGeneratorAssignment?.assignment?.assignmentData?.variant === + // ExperimentTestGroup.mockDataGeneratorVariant; const shouldShowMockDataButton = isInMockDataTreatmentVariant && diff --git a/packages/compass-collection/src/components/mock-data-generator-modal/constants.ts b/packages/compass-collection/src/components/mock-data-generator-modal/constants.ts index 10c3c707087..9d4ad07fe29 100644 --- a/packages/compass-collection/src/components/mock-data-generator-modal/constants.ts +++ b/packages/compass-collection/src/components/mock-data-generator-modal/constants.ts @@ -1,3 +1,4 @@ +import type { MongoDBFieldType } from '@mongodb-js/compass-generative-ai'; import { MockDataGeneratorStep } from './types'; export const StepButtonLabelMap = { @@ -10,3 +11,72 @@ export const StepButtonLabelMap = { export const DEFAULT_DOCUMENT_COUNT = 1000; export const MAX_DOCUMENT_COUNT = 100000; + +/** + * Map of MongoDB types to available Faker methods. + * Not all Faker methods are included here. + * More can be found in the Faker.js API: https://fakerjs.dev/api/ + */ +export const MONGO_TYPE_TO_FAKER_METHODS: Record< + MongoDBFieldType, + Array +> = { + String: [ + 'lorem.word', + 'lorem.words', + 'lorem.sentence', + 'lorem.paragraph', + 'person.firstName', + 'person.lastName', + 'person.fullName', + 'person.jobTitle', + 'internet.email', + 'internet.url', + 'internet.domainName', + 'internet.userName', + 'phone.number', + 'location.city', + 'location.country', + 'location.streetAddress', + 'location.zipCode', + 'location.state', + 'company.name', + 'company.catchPhrase', + 'color.human', + 'commerce.productName', + 'commerce.department', + 'finance.accountName', + 'finance.currencyCode', + 'git.commitSha', + 'string.uuid', + 'string.alpha', + 'string.alphanumeric', + ], + Number: [ + 'number.int', + 'number.float', + 'finance.amount', + 'location.latitude', + 'location.longitude', + ], + Int32: ['number.int', 'finance.amount'], + Long: ['number.int', 'number.bigInt'], + Decimal128: ['number.float', 'finance.amount'], + Boolean: ['datatype.boolean'], + Date: [ + 'date.recent', + 'date.past', + 'date.future', + 'date.anytime', + 'date.birthdate', + ], + Timestamp: ['date.recent', 'date.past', 'date.future', 'date.anytime'], + ObjectId: ['database.mongodbObjectId'], + Binary: ['string.hexadecimal', 'string.binary'], + RegExp: ['lorem.word', 'string.alpha'], + Code: ['lorem.sentence', 'lorem.paragraph', 'git.commitMessage'], + MinKey: ['number.int'], + MaxKey: ['number.int'], + Symbol: ['lorem.word', 'string.symbol'], + DBRef: ['database.mongodbObjectId'], +}; diff --git a/packages/compass-collection/src/components/mock-data-generator-modal/faker-mapping-selector.tsx b/packages/compass-collection/src/components/mock-data-generator-modal/faker-mapping-selector.tsx index b6c1aa726b0..cd1673cc92b 100644 --- a/packages/compass-collection/src/components/mock-data-generator-modal/faker-mapping-selector.tsx +++ b/packages/compass-collection/src/components/mock-data-generator-modal/faker-mapping-selector.tsx @@ -8,9 +8,11 @@ import { Select, spacing, } from '@mongodb-js/compass-components'; -import React from 'react'; +import React, { useMemo } from 'react'; import { UNRECOGNIZED_FAKER_METHOD } from '../../modules/collection-tab'; import type { MongoDBFieldType } from '@mongodb-js/compass-generative-ai'; +import { MongoDBFieldTypeValues } from '@mongodb-js/compass-generative-ai'; +import { MONGO_TYPE_TO_FAKER_METHODS } from './constants'; const fieldMappingSelectorsStyles = css({ width: '50%', @@ -37,6 +39,17 @@ const FakerMappingSelector = ({ onJsonTypeSelect, onFakerFunctionSelect, }: Props) => { + const fakerMethodOptions = useMemo(() => { + const methods = + MONGO_TYPE_TO_FAKER_METHODS[activeJsonType as MongoDBFieldType] || []; + + if (methods.includes(activeFakerFunction)) { + return methods; + } + + return [activeFakerFunction, ...methods]; + }, [activeJsonType, activeFakerFunction]); + return (
Mapping @@ -46,8 +59,7 @@ const FakerMappingSelector = ({ value={activeJsonType} onChange={(value) => onJsonTypeSelect(value as MongoDBFieldType)} > - {/* TODO(CLOUDP-344400) : Make the select input editable and render other options depending on the JSON type selected */} - {[activeJsonType].map((type) => ( + {Object.values(MongoDBFieldTypeValues).map((type) => ( @@ -59,10 +71,9 @@ const FakerMappingSelector = ({ value={activeFakerFunction} onChange={onFakerFunctionSelect} > - {/* TODO(CLOUDP-344400): Make the select input editable and render other JSON types */} - {[activeFakerFunction].map((field) => ( - ))} diff --git a/packages/compass-collection/src/components/mock-data-generator-modal/faker-schema-editor-screen.tsx b/packages/compass-collection/src/components/mock-data-generator-modal/faker-schema-editor-screen.tsx index 39b6a5d7a27..42cf78831d9 100644 --- a/packages/compass-collection/src/components/mock-data-generator-modal/faker-schema-editor-screen.tsx +++ b/packages/compass-collection/src/components/mock-data-generator-modal/faker-schema-editor-screen.tsx @@ -14,6 +14,7 @@ import FieldSelector from './schema-field-selector'; import FakerMappingSelector from './faker-mapping-selector'; import type { FakerSchema, MockDataGeneratorState } from './types'; import type { MongoDBFieldType } from '@mongodb-js/compass-generative-ai'; +import { getDefaultFakerMethod } from './script-generation-utils'; const containerStyles = css({ display: 'flex', @@ -72,11 +73,15 @@ const FakerSchemaEditorContent = ({ const onJsonTypeSelect = (newJsonType: MongoDBFieldType) => { const currentMapping = fakerSchemaFormValues[activeField]; if (currentMapping) { + // When MongoDB type changes, update the faker method to a default for that type + const defaultFakerMethod = getDefaultFakerMethod(newJsonType); setFakerSchemaFormValues({ ...fakerSchemaFormValues, [activeField]: { ...currentMapping, mongoType: newJsonType, + fakerMethod: defaultFakerMethod, + fakerArgs: [], // Reset args when changing type }, }); resetIsSchemaConfirmed(); 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 e55182e192c..2e82c1cd3b1 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,6 +9,7 @@ import { BannerVariant, Body, DocumentList, + useDarkMode, } from '@mongodb-js/compass-components'; import { usePreference } from 'compass-preferences-model/provider'; @@ -23,11 +24,14 @@ interface RawSchemaConfirmationScreenProps { fakerSchemaGenerationStatus: MockDataGeneratorState['status']; } -const documentContainerStyles = css({ - backgroundColor: palette.gray.light3, - border: `1px solid ${palette.gray.light2}`, - borderRadius: spacing[400], -}); +const getDocumentContainerStyles = (isDarkMode: boolean) => + css({ + backgroundColor: isDarkMode ? palette.gray.dark3 : palette.gray.light3, + border: `1px solid ${ + isDarkMode ? palette.gray.dark2 : palette.gray.light2 + }`, + borderRadius: spacing[400], + }); const documentStyles = css({ padding: `${spacing[400]}px ${spacing[900]}px`, @@ -52,6 +56,7 @@ const RawSchemaConfirmationScreen = ({ const enableSampleDocumentPassing = usePreference( 'enableGenAISampleDocumentPassing' ); + const isDarkMode = useDarkMode(); const subtitleText = enableSampleDocumentPassing ? 'Sample Documents Collected' @@ -69,7 +74,7 @@ const RawSchemaConfirmationScreen = ({ {subtitleText} {descriptionText} -
+
Date: Mon, 6 Oct 2025 13:30:01 -0400 Subject: [PATCH 2/3] Cleanup --- .../collection-header-actions.tsx | 6 +++--- packages/compass-collection/src/stores/collection-tab.ts | 7 ++++--- 2 files changed, 7 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 d4c2a515085..613ab6375e0 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 @@ -102,9 +102,9 @@ const CollectionHeaderActions: React.FunctionComponent< const { database, collection } = toNS(namespace); // Check if user is in treatment group for Mock Data Generator experiment - const isInMockDataTreatmentVariant = true; // EDITBACK - // mockDataGeneratorAssignment?.assignment?.assignmentData?.variant === - // ExperimentTestGroup.mockDataGeneratorVariant; + const isInMockDataTreatmentVariant = + mockDataGeneratorAssignment?.assignment?.assignmentData?.variant === + ExperimentTestGroup.mockDataGeneratorVariant; const shouldShowMockDataButton = isInMockDataTreatmentVariant && diff --git a/packages/compass-collection/src/stores/collection-tab.ts b/packages/compass-collection/src/stores/collection-tab.ts index 968f673baee..fc404e20c1f 100644 --- a/packages/compass-collection/src/stores/collection-tab.ts +++ b/packages/compass-collection/src/stores/collection-tab.ts @@ -170,9 +170,10 @@ export function activatePlugin( ExperimentTestName.mockDataGenerator, false // Don't track "Experiment Viewed" event here ); - return true; // EDITBACK - // assignment?.assignmentData?.variant === - // ExperimentTestGroup.mockDataGeneratorVariant + return ( + assignment?.assignmentData?.variant === + ExperimentTestGroup.mockDataGeneratorVariant + ); } catch (error) { // On error, default to not running schema analysis logger.debug( From 73f6b49a8ddcf72240c3dc46d3f018ec9181c42d Mon Sep 17 00:00:00 2001 From: Kyle W Lai Date: Mon, 6 Oct 2025 15:31:13 -0400 Subject: [PATCH 3/3] Add unit tests --- .../faker-mapping-selector.spec.tsx | 152 ++++++++++++++++++ 1 file changed, 152 insertions(+) create mode 100644 packages/compass-collection/src/components/mock-data-generator-modal/faker-mapping-selector.spec.tsx diff --git a/packages/compass-collection/src/components/mock-data-generator-modal/faker-mapping-selector.spec.tsx b/packages/compass-collection/src/components/mock-data-generator-modal/faker-mapping-selector.spec.tsx new file mode 100644 index 00000000000..87184817b8c --- /dev/null +++ b/packages/compass-collection/src/components/mock-data-generator-modal/faker-mapping-selector.spec.tsx @@ -0,0 +1,152 @@ +import { expect } from 'chai'; +import React from 'react'; +import { + screen, + render, + cleanup, + waitFor, + userEvent, +} from '@mongodb-js/testing-library-compass'; +import sinon from 'sinon'; +import FakerMappingSelector from './faker-mapping-selector'; +import { UNRECOGNIZED_FAKER_METHOD } from '../../modules/collection-tab'; +import { MONGO_TYPE_TO_FAKER_METHODS } from './constants'; +import { MongoDBFieldTypeValues } from '@mongodb-js/compass-generative-ai'; + +const mockActiveJsonType = MongoDBFieldTypeValues.String; +const mockActiveFakerFunction = 'lorem.word'; +const onJsonTypeSelectStub = sinon.stub(); +const onFakerFunctionSelectStub = sinon.stub(); + +describe('FakerMappingSelector', () => { + afterEach(() => { + cleanup(); + }); + + it('should display all MongoDB types in the dropdown', async () => { + // Check that all MongoDB types from the constant are present + const mongoTypes = Object.keys(MongoDBFieldTypeValues); + + render( + + ); + + const jsonTypeSelect = screen.getByLabelText('JSON Type'); + userEvent.click(jsonTypeSelect); + + for (const type of mongoTypes) { + await waitFor(() => { + expect(screen.getByRole('option', { name: type })).to.exist; + }); + } + }); + + describe('should display faker methods for each MongoDB type', () => { + Object.entries(MONGO_TYPE_TO_FAKER_METHODS).forEach( + ([mongoType, methods]) => { + it(`should display faker methods for ${mongoType}`, () => { + const firstMethod = methods[0]; + + render( + + ); + + const fakerFunctionSelect = screen.getByLabelText('Faker Function'); + userEvent.click(fakerFunctionSelect); + + methods.forEach((method) => { + expect(screen.getByRole('option', { name: method })).to.exist; + }); + }); + } + ); + }); + + it('should call onJsonTypeSelect when MongoDB type changes', async () => { + render( + + ); + + const jsonTypeSelect = screen.getByLabelText('JSON Type'); + userEvent.click(jsonTypeSelect); + + const numberOption = await screen.findByRole('option', { name: 'Number' }); + userEvent.click(numberOption); + + expect(onJsonTypeSelectStub).to.have.been.calledOnceWith('Number'); + }); + + it('should call onFakerFunctionSelect when faker function changes', async () => { + render( + + ); + + const fakerFunctionSelect = screen.getByLabelText('Faker Function'); + userEvent.click(fakerFunctionSelect); + + const emailOption = await screen.findByRole('option', { + name: 'internet.email', + }); + userEvent.click(emailOption); + + expect(onFakerFunctionSelectStub).to.have.been.calledOnceWith( + 'internet.email' + ); + }); + + it('should show warning banner when faker method is unrecognized', () => { + render( + + ); + + expect( + screen.getByText( + /Please select a function or we will default fill this field/ + ) + ).to.exist; + }); + + it('should not show warning banner when faker method is recognized', () => { + render( + + ); + + expect( + screen.queryByText( + /Please select a function or we will default fill this field/ + ) + ).to.not.exist; + }); +});