Skip to content

Commit c4485c1

Browse files
jcobisncarbon
authored andcommitted
WIP
1 parent 9057d30 commit c4485c1

File tree

2 files changed

+94
-41
lines changed

2 files changed

+94
-41
lines changed

packages/compass-collection/src/components/mock-data-generator-modal/types.ts

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ type MockDataGeneratorInProgressState = {
1919

2020
type MockDataGeneratorCompletedState = {
2121
status: 'completed';
22-
fakerSchema: FakerSchemaMapping[];
22+
fakerSchema: FakerSchema;
2323
requestId: string;
2424
};
2525

@@ -36,3 +36,12 @@ export type MockDataGeneratorState =
3636
| MockDataGeneratorErrorState;
3737

3838
export type FakerSchemaMapping = MockDataSchemaResponse['fields'][number];
39+
40+
export type FakerFieldMapping = {
41+
mongoType: string;
42+
fakerMethod: string;
43+
fakerArgs: any[];
44+
probability: number;
45+
};
46+
47+
export type FakerSchema = Record<string, FakerFieldMapping>;

packages/compass-collection/src/modules/collection-tab.ts

Lines changed: 84 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ import type { Document, MongoError } from 'mongodb';
3737
import { MockDataGeneratorStep } from '../components/mock-data-generator-modal/types';
3838
import type {
3939
FakerSchemaMapping,
40+
FakerSchema,
4041
MockDataGeneratorState,
4142
} from '../components/mock-data-generator-modal/types';
4243

@@ -181,7 +182,7 @@ export interface FakerMappingGenerationStartedAction {
181182

182183
export interface FakerMappingGenerationCompletedAction {
183184
type: CollectionActions.FakerMappingGenerationCompleted;
184-
fakerSchema: FakerSchemaMapping[];
185+
fakerSchema: FakerSchema;
185186
requestId: string;
186187
}
187188

@@ -695,64 +696,107 @@ export const cancelSchemaAnalysis = (): CollectionThunkAction<void> => {
695696
};
696697
};
697698

699+
/**
700+
* Transforms LLM array format to keyed object structure.
701+
* Moves fieldPath from object property to object key.
702+
*/
703+
function transformFakerSchemaToObject(
704+
fakerSchema: FakerSchemaMapping[]
705+
): FakerSchema {
706+
const result: FakerSchema = {};
707+
708+
for (const field of fakerSchema) {
709+
const { fieldPath, ...fieldMapping } = field;
710+
result[fieldPath] = fieldMapping;
711+
}
712+
713+
return result;
714+
}
715+
716+
/**
717+
* Checks if the method exists and is callable on the faker object.
718+
*/
719+
function isValidFakerMethod(fakerMethod: string): boolean {
720+
const parts = fakerMethod.split('.');
721+
722+
// Validate format: exactly module.method
723+
if (parts.length !== 2) {
724+
return false;
725+
}
726+
727+
const [moduleName, methodName] = parts;
728+
729+
try {
730+
const fakerModule = (faker as unknown as Record<string, unknown>)[
731+
moduleName
732+
];
733+
return (
734+
fakerModule !== null &&
735+
fakerModule !== undefined &&
736+
typeof fakerModule === 'object' &&
737+
typeof (fakerModule as Record<string, unknown>)[methodName] === 'function'
738+
);
739+
} catch {
740+
return false;
741+
}
742+
}
743+
698744
/**
699745
* Validates a given faker schema against an input schema.
700746
*
701-
* - Filters out fields from the faker schema that do not exist in the input schema.
702-
* - Validates the `fakerMethod` for each field, marking it as unrecognized if invalid.
703-
* - Adds any unmapped input schema fields to the result with an unrecognized faker method.
747+
* - Transforms LLM array format to keyed object structure
748+
* - Validates the `fakerMethod` for each field, marking it as unrecognized if invalid
749+
* - Adds any unmapped input schema fields to the result with an unrecognized faker method
704750
*
705751
* @param inputSchema - The schema definition for the input, mapping field names to their metadata.
706-
* @param fakerSchema - The array of faker schema mappings to validate and map.
752+
* @param fakerSchemaArray - The array of faker schema mappings from LLM to validate and map.
707753
* @param logger - Logger instance used to log warnings for invalid faker methods.
708-
* @returns An array of validated faker schema mappings, including all input schema fields.
754+
* @returns A keyed object of validated faker schema mappings, with one-to-one fields with input schema.
709755
*/
710756
const validateFakerSchema = (
711757
inputSchema: Record<string, FieldInfo>,
712-
fakerSchema: FakerSchemaMapping[],
758+
fakerSchemaArray: FakerSchemaMapping[],
713759
logger: Logger
714-
): FakerSchemaMapping[] => {
715-
const inputSchemaFields = Object.keys(inputSchema);
716-
const validatedFakerSchema = fakerSchema
717-
// Drop fields that don't match the input schema structure
718-
.filter((field) => inputSchema[field.fieldPath])
719-
.map((field) => {
720-
const { fakerMethod } = field;
721-
722-
// validate faker method
723-
const [moduleName, methodName, ...rest] = fakerMethod.split('.');
724-
if (
725-
rest.length > 0 ||
726-
typeof (faker as any)[moduleName]?.[methodName] !== 'function'
727-
) {
760+
): FakerSchema => {
761+
// Transform to keyed object structure
762+
const fakerSchemaRaw = transformFakerSchemaToObject(fakerSchemaArray);
763+
764+
const result: FakerSchema = {};
765+
766+
// Process all input schema fields in a single O(n) pass
767+
for (const fieldPath of Object.keys(inputSchema)) {
768+
const fakerMapping = fakerSchemaRaw[fieldPath];
769+
770+
if (fakerMapping) {
771+
// Validate the faker method
772+
if (isValidFakerMethod(fakerMapping.fakerMethod)) {
773+
result[fieldPath] = fakerMapping;
774+
} else {
728775
logger.log.warn(
729776
mongoLogId(1_001_000_372),
730777
'Collection',
731778
'Invalid faker method',
732-
{ fakerMethod }
779+
{ fakerMethod: fakerMapping.fakerMethod }
733780
);
734-
return {
735-
...field,
781+
result[fieldPath] = {
782+
mongoType: fakerMapping.mongoType,
736783
fakerMethod: UNRECOGNIZED_FAKER_METHOD,
784+
fakerArgs: fakerMapping.fakerArgs,
785+
probability: fakerMapping.probability,
737786
};
738787
}
788+
} else {
789+
// Field not mapped by LLM - add default
790+
result[fieldPath] = {
791+
mongoType: inputSchema[fieldPath].type,
792+
fakerMethod: UNRECOGNIZED_FAKER_METHOD,
793+
fakerArgs: [],
794+
probability: 1,
795+
};
796+
}
797+
}
739798

740-
return field;
741-
});
742-
const unmappedInputFields = inputSchemaFields.filter(
743-
(field) =>
744-
!validatedFakerSchema.find(({ fieldPath }) => fieldPath === field)
745-
);
746-
// Default unmapped input fields to "Unrecognized" faker method
747-
const unmappedFields = unmappedInputFields.map((field) => ({
748-
fieldPath: field,
749-
fakerMethod: UNRECOGNIZED_FAKER_METHOD,
750-
mongoType: inputSchema[field].type,
751-
fakerArgs: [],
752-
probability: 1,
753-
}));
754-
755-
return [...validatedFakerSchema, ...unmappedFields];
799+
return result;
756800
};
757801

758802
export const generateFakerMappings = (): CollectionThunkAction<

0 commit comments

Comments
 (0)