-
Notifications
You must be signed in to change notification settings - Fork 239
feat(compass-collection): manage faker mapping request state in redux CLOUDP-333850 #7251
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 39 commits
910c1af
5034086
8a2a096
b7c5461
cc950ca
0f56ee3
99c2881
f7d352d
77af388
6e1bb35
051f90e
1e05219
e5fc781
a3d3f98
aebff92
0de12bc
203ee82
47c1f3b
549d838
b65be4b
95d94b8
b91a1b6
5d39dee
ca06674
d630729
e88a7f7
a444035
8f9aaf9
44fa4c0
3e43c3f
4a5dc46
5d0f7c5
e539d11
d840ffc
6d022af
01372f5
2e1ca5c
7206585
d2182b0
21ef538
5716285
7a56c6e
b17d1f2
ef2652f
c0a306e
025d2fe
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,7 +1,36 @@ | ||
| import type { MockDataSchemaResponse } from '@mongodb-js/compass-generative-ai'; | ||
|
|
||
| export enum MockDataGeneratorStep { | ||
| SCHEMA_CONFIRMATION = 'SCHEMA_CONFIRMATION', | ||
| SCHEMA_EDITOR = 'SCHEMA_EDITOR', | ||
| DOCUMENT_COUNT = 'DOCUMENT_COUNT', | ||
| PREVIEW_DATA = 'PREVIEW_DATA', | ||
| GENERATE_DATA = 'GENERATE_DATA', | ||
| } | ||
|
|
||
| type MockDataGeneratorIdleState = { | ||
| status: 'idle'; | ||
| }; | ||
|
|
||
| type MockDataGeneratorInProgressState = { | ||
| status: 'in-progress'; | ||
| requestId: string; | ||
| }; | ||
|
|
||
| type MockDataGeneratorCompletedState = { | ||
| status: 'completed'; | ||
| fakerSchema: MockDataSchemaResponse; | ||
| requestId: string; | ||
| }; | ||
|
|
||
| type MockDataGeneratorErrorState = { | ||
| status: 'error'; | ||
| error: unknown; | ||
| requestId: string; | ||
| }; | ||
|
|
||
| export type MockDataGeneratorState = | ||
| | MockDataGeneratorIdleState | ||
| | MockDataGeneratorInProgressState | ||
| | MockDataGeneratorCompletedState | ||
| | MockDataGeneratorErrorState; |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,17 +1,22 @@ | ||
| import type { Reducer, AnyAction, Action } from 'redux'; | ||
| import { analyzeDocuments } from 'mongodb-schema'; | ||
| import { UUID } from 'bson'; | ||
|
|
||
| import type { CollectionMetadata } from 'mongodb-collection-model'; | ||
| import type { ThunkAction } from 'redux-thunk'; | ||
| import type AppRegistry from '@mongodb-js/compass-app-registry'; | ||
| import type { ConnectionInfo } from '@mongodb-js/connection-info'; | ||
| import type { workspacesServiceLocator } from '@mongodb-js/compass-workspaces/provider'; | ||
| import type { CollectionSubtab } from '@mongodb-js/compass-workspaces'; | ||
| import type { DataService } from '@mongodb-js/compass-connections/provider'; | ||
| import type { CollectionSubtab } from '@mongodb-js/compass-workspaces'; | ||
| import type { AtlasAiService } from '@mongodb-js/compass-generative-ai/provider'; | ||
| import type { experimentationServiceLocator } from '@mongodb-js/compass-telemetry/provider'; | ||
| import { type Logger, mongoLogId } from '@mongodb-js/compass-logging/provider'; | ||
| import { type PreferencesAccess } from 'compass-preferences-model/provider'; | ||
| import type { AtlasAiService } from '@mongodb-js/compass-generative-ai/provider'; | ||
|
|
||
| import type { | ||
| MockDataSchemaRequest, | ||
| MockDataSchemaResponse, | ||
| } from '@mongodb-js/compass-generative-ai'; | ||
| import { isInternalFieldPath } from 'hadron-document'; | ||
| import toNS from 'mongodb-ns'; | ||
| import { | ||
|
|
@@ -27,6 +32,7 @@ import { calculateSchemaDepth } from '../calculate-schema-depth'; | |
| import { processSchema } 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'; | ||
|
|
||
| const DEFAULT_SAMPLE_SIZE = 100; | ||
|
|
||
|
|
@@ -63,11 +69,11 @@ type CollectionThunkAction<R, A extends AnyAction = AnyAction> = ThunkAction< | |
| { | ||
| localAppRegistry: AppRegistry; | ||
| dataService: DataService; | ||
| atlasAiService: AtlasAiService; | ||
| workspaces: ReturnType<typeof workspacesServiceLocator>; | ||
| experimentationServices: ReturnType<typeof experimentationServiceLocator>; | ||
| logger: Logger; | ||
| preferences: PreferencesAccess; | ||
| atlasAiService: AtlasAiService; | ||
| }, | ||
| A | ||
| >; | ||
|
|
@@ -82,9 +88,10 @@ export type CollectionState = { | |
| isModalOpen: boolean; | ||
| currentStep: MockDataGeneratorStep; | ||
| }; | ||
| fakerSchemaGeneration: MockDataGeneratorState; | ||
| }; | ||
|
|
||
| enum CollectionActions { | ||
| export enum CollectionActions { | ||
| CollectionMetadataFetched = 'compass-collection/CollectionMetadataFetched', | ||
| SchemaAnalysisStarted = 'compass-collection/SchemaAnalysisStarted', | ||
| SchemaAnalysisFinished = 'compass-collection/SchemaAnalysisFinished', | ||
|
|
@@ -94,6 +101,9 @@ enum CollectionActions { | |
| MockDataGeneratorModalClosed = 'compass-collection/MockDataGeneratorModalClosed', | ||
| MockDataGeneratorNextButtonClicked = 'compass-collection/MockDataGeneratorNextButtonClicked', | ||
| MockDataGeneratorPreviousButtonClicked = 'compass-collection/MockDataGeneratorPreviousButtonClicked', | ||
| FakerMappingGenerationStarted = 'compass-collection/FakerMappingGenerationStarted', | ||
| FakerMappingGenerationCompleted = 'compass-collection/FakerMappingGenerationCompleted', | ||
| FakerMappingGenerationFailed = 'compass-collection/FakerMappingGenerationFailed', | ||
| } | ||
|
|
||
| interface CollectionMetadataFetchedAction { | ||
|
|
@@ -140,6 +150,23 @@ interface MockDataGeneratorPreviousButtonClickedAction { | |
| type: CollectionActions.MockDataGeneratorPreviousButtonClicked; | ||
| } | ||
|
|
||
| export interface FakerMappingGenerationStartedAction { | ||
| type: CollectionActions.FakerMappingGenerationStarted; | ||
| requestId: string; | ||
| } | ||
|
|
||
| export interface FakerMappingGenerationCompletedAction { | ||
| type: CollectionActions.FakerMappingGenerationCompleted; | ||
| fakerSchema: MockDataSchemaResponse; | ||
| requestId: string; | ||
| } | ||
|
|
||
| export interface FakerMappingGenerationFailedAction { | ||
| type: CollectionActions.FakerMappingGenerationFailed; | ||
| error: string; | ||
| requestId: string; | ||
| } | ||
|
|
||
| const reducer: Reducer<CollectionState, Action> = ( | ||
| state = { | ||
| // TODO(COMPASS-7782): use hook to get the workspace tab id instead | ||
|
|
@@ -153,6 +180,9 @@ const reducer: Reducer<CollectionState, Action> = ( | |
| isModalOpen: false, | ||
| currentStep: MockDataGeneratorStep.SCHEMA_CONFIRMATION, | ||
| }, | ||
| fakerSchemaGeneration: { | ||
| status: 'idle', | ||
| }, | ||
| }, | ||
| action | ||
| ) => { | ||
|
|
@@ -256,6 +286,9 @@ const reducer: Reducer<CollectionState, Action> = ( | |
| ...state.mockDataGenerator, | ||
| isModalOpen: false, | ||
| }, | ||
| fakerSchemaGeneration: { | ||
| status: 'idle', | ||
| }, | ||
| }; | ||
| } | ||
|
|
||
|
|
@@ -333,6 +366,75 @@ const reducer: Reducer<CollectionState, Action> = ( | |
| }; | ||
| } | ||
|
|
||
| if ( | ||
| isAction<FakerMappingGenerationStartedAction>( | ||
| action, | ||
| CollectionActions.FakerMappingGenerationStarted | ||
| ) | ||
| ) { | ||
| if ( | ||
| state.mockDataGenerator.currentStep !== | ||
| MockDataGeneratorStep.SCHEMA_CONFIRMATION | ||
| ) { | ||
| return state; | ||
| } | ||
|
|
||
| if ( | ||
| state.fakerSchemaGeneration.status === 'in-progress' || | ||
| state.fakerSchemaGeneration.status === 'completed' | ||
| ) { | ||
| return state; | ||
| } | ||
kpamaran marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
|
||
| return { | ||
| ...state, | ||
| fakerSchemaGeneration: { | ||
| status: 'in-progress', | ||
| requestId: action.requestId, | ||
| }, | ||
| }; | ||
| } | ||
|
|
||
| if ( | ||
| isAction<FakerMappingGenerationCompletedAction>( | ||
| action, | ||
| CollectionActions.FakerMappingGenerationCompleted | ||
| ) | ||
| ) { | ||
| if (state.fakerSchemaGeneration.status !== 'in-progress') { | ||
| return state; | ||
| } | ||
|
|
||
| return { | ||
| ...state, | ||
| fakerSchemaGeneration: { | ||
| status: 'completed', | ||
| fakerSchema: action.fakerSchema, | ||
| requestId: action.requestId, | ||
| }, | ||
| }; | ||
| } | ||
|
|
||
| if ( | ||
| isAction<FakerMappingGenerationFailedAction>( | ||
| action, | ||
| CollectionActions.FakerMappingGenerationFailed | ||
| ) | ||
| ) { | ||
| if (state.fakerSchemaGeneration.status !== 'in-progress') { | ||
| return state; | ||
| } | ||
|
|
||
| return { | ||
| ...state, | ||
| fakerSchemaGeneration: { | ||
| status: 'error', | ||
| error: action.error, | ||
| requestId: action.requestId, | ||
| }, | ||
| }; | ||
| } | ||
|
|
||
| return state; | ||
| }; | ||
|
|
||
|
|
@@ -480,6 +582,86 @@ export const analyzeCollectionSchema = (): CollectionThunkAction< | |
| }; | ||
| }; | ||
|
|
||
| export const generateFakerMappings = ( | ||
| connectionInfo: ConnectionInfo | ||
| ): CollectionThunkAction<Promise<void>> => { | ||
| return async ( | ||
| dispatch, | ||
| getState, | ||
| { logger, atlasAiService, preferences } | ||
| ) => { | ||
| const { schemaAnalysis, fakerSchemaGeneration, namespace } = getState(); | ||
| if (schemaAnalysis.status !== SCHEMA_ANALYSIS_STATE_COMPLETE) { | ||
| logger.log.warn( | ||
| mongoLogId(1_001_000_305), | ||
| 'Collection', | ||
| 'Cannot call `generateFakeMappings` unless schema analysis is complete' | ||
| ); | ||
| return; | ||
| } | ||
|
|
||
| if (fakerSchemaGeneration.status === 'in-progress') { | ||
| logger.debug( | ||
| 'Faker mapping generation is already in progress, skipping new generation.' | ||
| ); | ||
| return; | ||
| } | ||
|
|
||
| const includeSampleValues = | ||
| preferences.getPreferences().enableGenAISampleDocumentPassing; | ||
|
|
||
| // todo: dedup/abort requests using requestId (CLOUDP-333850) | ||
| const requestId = new UUID().toString(); | ||
|
||
|
|
||
| try { | ||
| logger.debug('Generating faker mappings'); | ||
|
|
||
| const { database, collection } = toNS(namespace); | ||
|
|
||
| dispatch({ | ||
| type: CollectionActions.FakerMappingGenerationStarted, | ||
| requestId: requestId, | ||
| }); | ||
|
|
||
| const mockDataSchemaRequest: MockDataSchemaRequest = { | ||
| databaseName: database, | ||
| collectionName: collection, | ||
| schema: schemaAnalysis.processedSchema, | ||
| validationRules: schemaAnalysis.schemaMetadata.validationRules, | ||
| includeSampleValues, | ||
| requestId, | ||
| }; | ||
|
|
||
| const response = await atlasAiService.getMockDataSchema( | ||
| mockDataSchemaRequest, | ||
| connectionInfo | ||
| ); | ||
| dispatch({ | ||
| type: CollectionActions.FakerMappingGenerationCompleted, | ||
| fakerSchema: response, | ||
| requestId: requestId, | ||
| }); | ||
| } catch (e) { | ||
| const errorMessage = e instanceof Error ? e.stack : String(e); | ||
|
|
||
| logger.log.error( | ||
| mongoLogId(1_001_000_312), | ||
| 'Collection', | ||
| 'Failed to generate faker.js mappings', | ||
| { | ||
| message: errorMessage, | ||
| namespace, | ||
| } | ||
| ); | ||
| dispatch({ | ||
| type: CollectionActions.FakerMappingGenerationFailed, | ||
| error: 'faker mapping request failed', | ||
| requestId, | ||
| }); | ||
| } | ||
| }; | ||
| }; | ||
|
|
||
| export type CollectionTabPluginMetadata = CollectionMetadata & { | ||
| /** | ||
| * Initial query for the query bar | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.