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..07c26457ba9 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
@@ -2,7 +2,9 @@ import {
Banner,
BannerVariant,
Body,
+ Code,
css,
+ Label,
Option,
palette,
Select,
@@ -11,6 +13,7 @@ import {
import React from 'react';
import { UNRECOGNIZED_FAKER_METHOD } from '../../modules/collection-tab';
import type { MongoDBFieldType } from '@mongodb-js/compass-generative-ai';
+import type { FakerArg } from './script-generation-utils';
const fieldMappingSelectorsStyles = css({
width: '50%',
@@ -24,16 +27,48 @@ const labelStyles = css({
fontWeight: 600,
});
+const stringifyFakerArg = (arg: FakerArg): string => {
+ if (typeof arg === 'object' && arg !== null && 'json' in arg) {
+ try {
+ return JSON.stringify(JSON.parse(arg.json));
+ } catch {
+ return '';
+ }
+ }
+
+ // Handle arrays recursively
+ if (Array.isArray(arg)) {
+ return `[${arg.map(stringifyFakerArg).join(', ')}]`;
+ }
+
+ if (typeof arg === 'string') {
+ return JSON.stringify(arg);
+ }
+
+ // Numbers and booleans
+ return String(arg);
+};
+
+const formatFakerFunctionCallWithArgs = (
+ fakerFunction: string,
+ fakerArgs: FakerArg[]
+) => {
+ const parsedFakerArgs = fakerArgs.map(stringifyFakerArg);
+ return `faker.${fakerFunction}(${parsedFakerArgs.join(', ')})`;
+};
+
interface Props {
activeJsonType: string;
activeFakerFunction: string;
onJsonTypeSelect: (jsonType: MongoDBFieldType) => void;
+ activeFakerArgs: FakerArg[];
onFakerFunctionSelect: (fakerFunction: string) => void;
}
const FakerMappingSelector = ({
activeJsonType,
activeFakerFunction,
+ activeFakerArgs,
onJsonTypeSelect,
onFakerFunctionSelect,
}: Props) => {
@@ -66,13 +101,29 @@ const FakerMappingSelector = ({
))}
- {activeFakerFunction === UNRECOGNIZED_FAKER_METHOD && (
+ {activeFakerFunction === UNRECOGNIZED_FAKER_METHOD ? (
Please select a function or we will default fill this field with the
string "Unrecognized"
+ ) : (
+ <>
+
+
+ {formatFakerFunctionCallWithArgs(
+ activeFakerFunction,
+ activeFakerArgs
+ )}
+
+ >
)}
- {/* TODO(CLOUDP-344400): Render faker function parameters once we have a way to validate them. */}
);
};
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..55ebe80bd0b 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
@@ -64,6 +64,7 @@ const FakerSchemaEditorContent = ({
const activeJsonType = fakerSchemaFormValues[activeField]?.mongoType;
const activeFakerFunction = fakerSchemaFormValues[activeField]?.fakerMethod;
+ const activeFakerArgs = fakerSchemaFormValues[activeField]?.fakerArgs;
const resetIsSchemaConfirmed = () => {
onSchemaConfirmed(false);
@@ -109,6 +110,7 @@ const FakerSchemaEditorContent = ({
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 a03ed42f29a..43be38d08b0 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
@@ -71,7 +71,7 @@ describe('MockDataGeneratorModal', () => {
const store = createStore(
collectionTabReducer,
- initialState,
+ initialState as any,
applyMiddleware(thunk.withExtraArgument(mockServices))
);
@@ -545,6 +545,111 @@ describe('MockDataGeneratorModal', () => {
);
});
+ it('displays preview of the faker call without args when the args are invalid', async () => {
+ const largeLengthArgs = Array.from({ length: 11 }, () => 'testArg');
+ const mockServices = createMockServices();
+ mockServices.atlasAiService.getMockDataSchema = () =>
+ Promise.resolve({
+ fields: [
+ {
+ fieldPath: 'name',
+ mongoType: 'String',
+ fakerMethod: 'person.firstName',
+ fakerArgs: largeLengthArgs,
+ isArray: false,
+ probability: 1.0,
+ },
+ {
+ fieldPath: 'age',
+ mongoType: 'Int32',
+ fakerMethod: 'number.int',
+ fakerArgs: [
+ {
+ json: JSON.stringify({
+ a: largeLengthArgs,
+ }),
+ },
+ ],
+ isArray: false,
+ probability: 1.0,
+ },
+ {
+ fieldPath: 'username',
+ mongoType: 'String',
+ fakerMethod: 'string.alpha',
+ // large string
+ fakerArgs: ['a'.repeat(1001)],
+ isArray: false,
+ probability: 1.0,
+ },
+ {
+ fieldPath: 'avatar',
+ mongoType: 'String',
+ fakerMethod: 'image.url',
+ fakerArgs: [
+ {
+ json: JSON.stringify({
+ width: 100_000,
+ height: 100_000,
+ }),
+ },
+ ],
+ isArray: false,
+ probability: 1.0,
+ },
+ ],
+ });
+
+ await renderModal({
+ mockServices,
+ schemaAnalysis: {
+ ...defaultSchemaAnalysisState,
+ processedSchema: {
+ name: {
+ type: 'String',
+ probability: 1.0,
+ },
+ age: {
+ type: 'Int32',
+ probability: 1.0,
+ },
+ username: {
+ type: 'String',
+ probability: 1.0,
+ },
+ avatar: {
+ type: 'String',
+ probability: 1.0,
+ },
+ },
+ },
+ });
+
+ // advance to the schema editor step
+ userEvent.click(screen.getByText('Confirm'));
+ await waitFor(() => {
+ expect(screen.getByTestId('faker-schema-editor')).to.exist;
+ });
+
+ userEvent.click(screen.getByText('name'));
+ expect(screen.getByTestId('faker-function-call-preview')).to.exist;
+ expect(screen.queryByText(/testArg/)).to.not.exist;
+
+ userEvent.click(screen.getByText('age'));
+ expect(screen.getByTestId('faker-function-call-preview')).to.exist;
+ expect(screen.queryByText(/testArg/)).to.not.exist;
+
+ userEvent.click(screen.getByText('username'));
+ expect(screen.queryByText(/aaaaaaa/)).to.not.exist;
+ expect(screen.getByTestId('faker-function-call-preview')).to.exist;
+
+ userEvent.click(screen.getByText('avatar'));
+ expect(screen.getByTestId('faker-function-call-preview')).to.exist;
+ expect(screen.queryByText(/width/)).to.not.exist;
+ expect(screen.queryByText(/height/)).to.not.exist;
+ expect(screen.queryByText(/100000/)).to.not.exist;
+ });
+
it('disables the Next button when the faker schema mapping is not confirmed', async () => {
await renderModal({
mockServices: mockServicesWithMockDataResponse,
diff --git a/packages/compass-collection/src/components/mock-data-generator-modal/script-generation-utils.ts b/packages/compass-collection/src/components/mock-data-generator-modal/script-generation-utils.ts
index 15687a4b76d..3138663fd55 100644
--- a/packages/compass-collection/src/components/mock-data-generator-modal/script-generation-utils.ts
+++ b/packages/compass-collection/src/components/mock-data-generator-modal/script-generation-utils.ts
@@ -2,7 +2,12 @@ import type { MongoDBFieldType } from '@mongodb-js/compass-generative-ai';
import type { FakerFieldMapping } from './types';
import { prettify } from '@mongodb-js/compass-editor';
-export type FakerArg = string | number | boolean | { json: string };
+export type FakerArg =
+ | string
+ | number
+ | boolean
+ | { json: string }
+ | FakerArg[];
const DEFAULT_ARRAY_LENGTH = 3;
diff --git a/packages/compass-collection/src/components/mock-data-generator-modal/utils.spec.ts b/packages/compass-collection/src/components/mock-data-generator-modal/utils.spec.ts
new file mode 100644
index 00000000000..d73e62f85b2
--- /dev/null
+++ b/packages/compass-collection/src/components/mock-data-generator-modal/utils.spec.ts
@@ -0,0 +1,251 @@
+import { expect } from 'chai';
+import { areFakerArgsValid, isValidFakerMethod } from './utils';
+
+import Sinon from 'sinon';
+import { faker } from '@faker-js/faker/locale/en';
+import { createNoopLogger } from '@mongodb-js/compass-logging/provider';
+
+describe('Mock Data Generator Utils', () => {
+ const sandbox = Sinon.createSandbox();
+ const logger = createNoopLogger();
+
+ afterEach(() => {
+ sandbox.restore();
+ });
+
+ describe('areFakerArgsValid', () => {
+ it('returns true for empty array', () => {
+ expect(areFakerArgsValid([])).to.be.true;
+ });
+
+ it('returns false if top-level args count exceeds max faker args count', () => {
+ expect(areFakerArgsValid([1, 2, 3])).to.be.false;
+ });
+
+ it('returns true for valid numbers, strings, booleans', () => {
+ expect(areFakerArgsValid([1, 'foo'])).to.be.true;
+ expect(areFakerArgsValid([true, false])).to.be.true;
+ });
+
+ it('returns false for non-finite numbers', () => {
+ expect(areFakerArgsValid([Infinity])).to.be.false;
+ expect(areFakerArgsValid([NaN])).to.be.false;
+ });
+
+ it('returns false for strings exceeding max faker string length', () => {
+ const longStr = 'a'.repeat(1001);
+ expect(areFakerArgsValid([longStr])).to.be.false;
+ });
+
+ it('returns false for numbers that are too large', () => {
+ expect(areFakerArgsValid([10001])).to.be.false;
+ expect(areFakerArgsValid([-10001])).to.be.false;
+ expect(areFakerArgsValid([{ json: { length: 10001 } } as any])).to.be
+ .false;
+ expect(areFakerArgsValid([{ json: { length: -10001 } } as any])).to.be
+ .false;
+ expect(
+ areFakerArgsValid([{ json: { width: 10001, height: 10001 } } as any])
+ ).to.be.false;
+ });
+
+ it('returns true for nested valid arrays', () => {
+ expect(
+ areFakerArgsValid([
+ [1, 'foo', true],
+ [2, false],
+ ])
+ ).to.be.true;
+ });
+
+ it('returns false for nested arrays exceeding max array length', () => {
+ const nested = [Array(11).fill(1)]; // nested array has 11 elements > MAX_ARRAY_LENGTH (10)
+ expect(areFakerArgsValid(nested)).to.be.false;
+ });
+
+ it('returns true for exactly 2 top-level arguments', () => {
+ expect(areFakerArgsValid(['arg1', 'arg2'])).to.be.true;
+ });
+
+ it('returns true for nested arrays within the limit', () => {
+ const nested = [Array(10).fill(1)];
+ expect(areFakerArgsValid(nested)).to.be.true;
+ });
+
+ it('returns true for valid object with json property', () => {
+ const obj = { json: JSON.stringify({ a: 1, b: 'foo', c: true }) };
+ expect(areFakerArgsValid([obj])).to.be.true;
+ });
+
+ it('returns false for object with invalid json property', () => {
+ const obj = { json: '{invalid json}' };
+ expect(areFakerArgsValid([obj])).to.be.false;
+ });
+
+ it('returns false for object with json property containing invalid values', () => {
+ const obj = { json: JSON.stringify({ a: Infinity }) };
+ expect(areFakerArgsValid([obj])).to.be.false;
+ });
+
+ it('returns false for unrecognized argument types', () => {
+ expect(areFakerArgsValid([undefined as any])).to.be.false;
+ expect(areFakerArgsValid([null as any])).to.be.false;
+ expect(areFakerArgsValid([(() => {}) as any])).to.be.false;
+ });
+
+ it('returns true for deeply nested valid structures', () => {
+ const obj = {
+ json: JSON.stringify({
+ a: [1, { json: JSON.stringify({ b: 'foo' }) }],
+ }),
+ };
+ expect(areFakerArgsValid([obj])).to.be.true;
+ });
+
+ it('returns false for deeply nested invalid structures', () => {
+ const obj = {
+ json: JSON.stringify({
+ a: [1, { json: JSON.stringify({ b: Infinity }) }],
+ }),
+ };
+ expect(areFakerArgsValid([obj])).to.be.false;
+ });
+
+ it('returns false for deeply nested invalid structures with max depth', () => {
+ const obj = {
+ json: JSON.stringify({
+ a: [
+ 1,
+ {
+ json: JSON.stringify({
+ b: 2,
+ c: {
+ json: JSON.stringify({
+ d: 3,
+ }),
+ },
+ }),
+ },
+ ],
+ }),
+ };
+ expect(areFakerArgsValid([obj])).to.be.false;
+ });
+ });
+
+ describe('isValidFakerMethod', () => {
+ it('returns false for invalid method format', () => {
+ sandbox.stub(faker.internet, 'email');
+
+ expect(isValidFakerMethod('invalidMethod', [], logger)).to.deep.equal({
+ isValid: false,
+ fakerArgs: [],
+ });
+ expect(
+ isValidFakerMethod('internet.email.extra', [], logger)
+ ).to.deep.equal({
+ isValid: false,
+ fakerArgs: [],
+ });
+ });
+
+ it('returns false for non-existent faker module', () => {
+ expect(isValidFakerMethod('notamodule.email', [], logger)).to.deep.equal({
+ isValid: false,
+ fakerArgs: [],
+ });
+ });
+
+ it('returns false for non-existent faker method', () => {
+ expect(
+ isValidFakerMethod('internet.notamethod', [], logger)
+ ).to.deep.equal({
+ isValid: false,
+ fakerArgs: [],
+ });
+ });
+
+ it('returns true for valid method without arguments', () => {
+ sandbox.stub(faker.internet, 'email').returns('test@test.com');
+
+ const result = isValidFakerMethod('internet.email', [], logger);
+ expect(result.isValid).to.be.true;
+ expect(result.fakerArgs).to.deep.equal([]);
+ });
+
+ it('returns true for valid method with valid arguments', () => {
+ sandbox.stub(faker.person, 'firstName');
+
+ const result = isValidFakerMethod('person.firstName', ['female'], logger);
+ expect(result.isValid).to.be.true;
+ expect(result.fakerArgs).to.deep.equal(['female']);
+ });
+
+ it('returns true for valid method with no args if args are invalid but fallback works', () => {
+ sandbox.stub(faker.internet, 'email').returns('test@test.com');
+
+ const result = isValidFakerMethod('internet.email', [], logger);
+ expect(result.isValid).to.be.true;
+ expect(result.fakerArgs).to.deep.equal([]);
+ });
+
+ it('returns true valid method with invalid arguments and strips args', () => {
+ sandbox.stub(faker.date, 'month').returns('February');
+
+ const result = isValidFakerMethod(
+ 'date.month',
+ [{ foo: 'bar' } as any],
+ logger
+ );
+ expect(result.isValid).to.be.true;
+ expect(result.fakerArgs).to.deep.equal([]);
+ });
+
+ it('returns false for helpers methods except arrayElement', () => {
+ expect(isValidFakerMethod('helpers.fake', [], logger)).to.deep.equal({
+ isValid: false,
+ fakerArgs: [],
+ });
+ expect(isValidFakerMethod('helpers.slugify', [], logger)).to.deep.equal({
+ isValid: false,
+ fakerArgs: [],
+ });
+ });
+
+ it('returns true for helpers.arrayElement with valid arguments', () => {
+ sandbox.stub(faker.helpers, 'arrayElement').returns('a');
+
+ const arr = ['a', 'b', 'c'];
+ const result = isValidFakerMethod('helpers.arrayElement', [arr], logger);
+ expect(result.isValid).to.be.true;
+ expect(result.fakerArgs).to.deep.equal([arr]);
+ });
+
+ it('returns false for helpers.arrayElement with invalid arguments', () => {
+ // Exceeding max args length
+ const arr = Array(11).fill('x');
+ const result = isValidFakerMethod('helpers.arrayElement', [arr], logger);
+ expect(result.isValid).to.be.false;
+ expect(result.fakerArgs).to.deep.equal([]);
+ });
+
+ it('returns true for valid method with invalid fakerArgs and strips args', () => {
+ sandbox.stub(faker.person, 'firstName').returns('a');
+
+ // Passing Infinity as argument
+ const result = isValidFakerMethod('person.firstName', [Infinity], logger);
+ expect(result.isValid).to.be.true;
+ expect(result.fakerArgs).to.deep.equal([]);
+ });
+
+ it('returns false when calling a faker method fails', () => {
+ sandbox
+ .stub(faker.person, 'firstName')
+ .throws(new Error('Invalid faker method'));
+
+ const result = isValidFakerMethod('person.firstName', [], logger);
+ expect(result.isValid).to.be.false;
+ expect(result.fakerArgs).to.deep.equal([]);
+ });
+ });
+});
diff --git a/packages/compass-collection/src/components/mock-data-generator-modal/utils.ts b/packages/compass-collection/src/components/mock-data-generator-modal/utils.ts
new file mode 100644
index 00000000000..7349f260804
--- /dev/null
+++ b/packages/compass-collection/src/components/mock-data-generator-modal/utils.ts
@@ -0,0 +1,198 @@
+import { type Logger, mongoLogId } from '@mongodb-js/compass-logging/provider';
+import type { FakerArg } from './script-generation-utils';
+import { faker } from '@faker-js/faker/locale/en';
+
+const MAX_FAKER_ARGS_COUNT = 2;
+const MAX_ARRAY_LENGTH = 10;
+const MAX_FAKER_STRING_LENGTH = 1000;
+const MAX_FAKER_ARGS_DEPTH = 3;
+const MAX_FAKER_NUMBER_SIZE = 10000;
+
+/**
+ * Checks if the provided faker arguments are valid.
+ * - Numbers must be finite
+ * - Strings must not exceed max length
+ * - Arrays must not exceed max length and all elements must be valid
+ * - Objects must have a 'json' property that is a valid JSON string and all values must be valid
+ */
+export function areFakerArgsValid(
+ fakerArgs: FakerArg[],
+ depth: number = 0
+): boolean {
+ if (depth > MAX_FAKER_ARGS_DEPTH) {
+ return false;
+ }
+ if (fakerArgs.length === 0) {
+ return true;
+ }
+ // Check top-level argument count (max 2 for faker functions)
+ if (depth === 0 && fakerArgs.length > MAX_FAKER_ARGS_COUNT) {
+ return false;
+ }
+ // Check array length for nested arrays
+ if (depth > 0 && fakerArgs.length > MAX_ARRAY_LENGTH) {
+ return false;
+ }
+ for (const arg of fakerArgs) {
+ if (arg === null || arg === undefined) {
+ return false;
+ } else if (typeof arg === 'boolean') {
+ // booleans are always valid, continue
+ continue;
+ } else if (typeof arg === 'number') {
+ if (!Number.isFinite(arg) || Math.abs(arg) > MAX_FAKER_NUMBER_SIZE) {
+ return false;
+ }
+ } else if (typeof arg === 'string') {
+ if (arg.length > MAX_FAKER_STRING_LENGTH) {
+ return false;
+ }
+ } else if (Array.isArray(arg)) {
+ if (!areFakerArgsValid(arg, depth + 1)) {
+ return false;
+ }
+ } else if (
+ typeof arg === 'object' &&
+ arg !== null &&
+ 'json' in arg &&
+ typeof arg.json === 'string'
+ ) {
+ try {
+ const parsedJson = JSON.parse(arg.json);
+ if (!areFakerArgsValid(Object.values(parsedJson), depth + 1)) {
+ return false;
+ }
+ } catch {
+ return false;
+ }
+ } else {
+ // Unrecognized argument type
+ return false;
+ }
+ }
+ return true;
+}
+
+/**
+ * Checks if the method exists and is callable on the faker object.
+ *
+ * Note: Only supports the format `module.method` (e.g., `internet.email`).
+ * - Nested modules are not supported.
+ * - Most methods in the `helpers` module are not supported due to their complex argument requirements. @see {@link https://fakerjs.dev/api/helpers.html}
+ * - If the method call with provided args fails, it retries without args before marking as invalid.
+ *
+ * @see {@link https://fakerjs.dev/api/}
+ */
+export function isValidFakerMethod(
+ fakerMethod: string,
+ fakerArgs: FakerArg[],
+ logger: Logger
+): {
+ isValid: boolean;
+ fakerArgs: FakerArg[];
+} {
+ const moduleAndMethod = getFakerModuleAndMethod(fakerMethod);
+ if (!moduleAndMethod) {
+ return { isValid: false, fakerArgs: [] };
+ }
+ const { moduleName, methodName, fakerModule } = moduleAndMethod;
+
+ if (
+ isAllowedFakerFn(moduleName, methodName) &&
+ canInvokeFakerMethod(fakerModule, methodName)
+ ) {
+ const callableFakerMethod = (
+ fakerModule as Record any>
+ )[methodName];
+ return tryInvokeFakerMethod(
+ callableFakerMethod,
+ fakerMethod,
+ fakerArgs,
+ logger
+ );
+ } else {
+ return { isValid: false, fakerArgs: [] };
+ }
+}
+
+function getFakerModuleAndMethod(method: string) {
+ const parts = method.split('.');
+ if (parts.length !== 2) {
+ return null;
+ }
+ const [moduleName, methodName] = parts;
+ const fakerModule = (faker as unknown as Record)[moduleName];
+ return { moduleName, methodName, fakerModule };
+}
+
+function isAllowedFakerFn(moduleName: string, methodName: string) {
+ if (moduleName !== 'helpers') {
+ // Non-helper modules are allowed
+ return true;
+ } else {
+ // If helpers module, only array helpers are allowed
+ return methodName === 'arrayElement' || methodName === 'arrayElements';
+ }
+}
+
+function canInvokeFakerMethod(fakerModule: unknown, methodName: string) {
+ return (
+ fakerModule !== null &&
+ fakerModule !== undefined &&
+ typeof fakerModule === 'object' &&
+ typeof (fakerModule as Record)[methodName] === 'function'
+ );
+}
+
+function tryInvokeFakerMethod(
+ callable: (...args: readonly unknown[]) => unknown,
+ fakerMethod: string,
+ args: FakerArg[],
+ logger: Logger
+) {
+ // If args are present and safe, try calling with args
+ if (args.length > 0 && areFakerArgsValid(args)) {
+ try {
+ const usableArgs = prepareFakerArgs(args);
+ callable(...usableArgs);
+ return { isValid: true, fakerArgs: args };
+ } catch {
+ // Call with args failed. Fall through to trying without args
+ }
+ }
+
+ // Try without args (either because args were invalid or args failed)
+ try {
+ callable();
+ return { isValid: true, fakerArgs: [] };
+ } catch (error) {
+ // Calling the method without arguments failed.
+ logger.log.warn(
+ mongoLogId(1_001_000_377),
+ 'Collection',
+ 'Invalid faker method',
+ { error, fakerMethod, fakerArgs: args }
+ );
+ return { isValid: false, fakerArgs: [] };
+ }
+}
+
+/**
+ * Prepares the faker args to ensure we can call the method with the args.
+ * Objects with a 'json' property are parsed into a JSON object.
+ * @example
+ * [
+ * { json: '{"a": 1}' },
+ * { json: '{"b": 2}' },
+ * ]
+ * becomes
+ * [ { a: 1 }, { b: 2 } ]
+ */
+function prepareFakerArgs(args: FakerArg[]) {
+ return args.map((arg) => {
+ if (typeof arg === 'object' && arg !== null && 'json' in arg) {
+ return JSON.parse(arg.json);
+ }
+ return arg;
+ });
+}
diff --git a/packages/compass-collection/src/modules/collection-tab.ts b/packages/compass-collection/src/modules/collection-tab.ts
index 2433de40329..a1bfe9866e5 100644
--- a/packages/compass-collection/src/modules/collection-tab.ts
+++ b/packages/compass-collection/src/modules/collection-tab.ts
@@ -42,7 +42,7 @@ import type {
MockDataGeneratorState,
} from '../components/mock-data-generator-modal/types';
-import { faker } from '@faker-js/faker/locale/en';
+import { isValidFakerMethod } from '../components/mock-data-generator-modal/utils';
const DEFAULT_SAMPLE_SIZE = 100;
@@ -729,38 +729,6 @@ function transformFakerSchemaToObject(
return result;
}
-/**
- * Checks if the method exists and is callable on the faker object.
- *
- * Note: Only supports the format `module.method` (e.g., `internet.email`).
- * Nested modules or other formats are not supported.
- * @see {@link https://fakerjs.dev/api/}
- */
-function isValidFakerMethod(fakerMethod: string): boolean {
- const parts = fakerMethod.split('.');
-
- // Validate format: exactly module.method
- if (parts.length !== 2) {
- return false;
- }
-
- const [moduleName, methodName] = parts;
-
- try {
- const fakerModule = (faker as unknown as Record)[
- moduleName
- ];
- return (
- fakerModule !== null &&
- fakerModule !== undefined &&
- typeof fakerModule === 'object' &&
- typeof (fakerModule as Record)[methodName] === 'function'
- );
- } catch {
- return false;
- }
-}
-
/**
* Validates a given faker schema against an input schema.
*
@@ -788,8 +756,16 @@ const validateFakerSchema = (
probability: inputSchema[fieldPath].probability,
};
// Validate the faker method
- if (isValidFakerMethod(fakerMapping.fakerMethod)) {
- result[fieldPath] = fakerMapping;
+ const { isValid: isValidMethod, fakerArgs } = isValidFakerMethod(
+ fakerMapping.fakerMethod,
+ fakerMapping.fakerArgs,
+ logger
+ );
+ if (isValidMethod) {
+ result[fieldPath] = {
+ ...fakerMapping,
+ fakerArgs,
+ };
} else {
logger.log.warn(
mongoLogId(1_001_000_372),