From e0d6e747e285f381f2e43662c381f3d4fa1cddfa Mon Sep 17 00:00:00 2001 From: Jacob Samuel Lu Date: Mon, 4 Aug 2025 15:40:32 -0400 Subject: [PATCH 01/11] WIP --- packages/compass-collection/src/index.ts | 6 ++++ .../src/modules/collection-tab.ts | 2 ++ .../src/stores/collection-tab.ts | 34 ++++++++++++++++++- .../src/experimentation-provider.tsx | 2 +- packages/compass-telemetry/src/index.ts | 1 + packages/compass-telemetry/src/provider.tsx | 18 +++++++++- 6 files changed, 60 insertions(+), 3 deletions(-) diff --git a/packages/compass-collection/src/index.ts b/packages/compass-collection/src/index.ts index dc087a96e0b..100d733e4e7 100644 --- a/packages/compass-collection/src/index.ts +++ b/packages/compass-collection/src/index.ts @@ -6,10 +6,13 @@ import { dataServiceLocator, type DataServiceLocator, type DataService, + connectionInfoRefLocator, } from '@mongodb-js/compass-connections/provider'; import { collectionModelLocator } from '@mongodb-js/compass-app-stores/provider'; import type { WorkspacePlugin } from '@mongodb-js/compass-workspaces'; import { workspacesServiceLocator } from '@mongodb-js/compass-workspaces/provider'; +import { experimentationServiceLocator } from '@mongodb-js/compass-telemetry/provider'; +import { createLoggerLocator } from '@mongodb-js/compass-logging/provider'; import { CollectionWorkspaceTitle, CollectionPluginTitleComponent, @@ -29,6 +32,9 @@ export const WorkspaceTab: WorkspacePlugin = { dataService: dataServiceLocator as DataServiceLocator, collection: collectionModelLocator, workspaces: workspacesServiceLocator, + experimentationServices: experimentationServiceLocator, + connectionInfoRef: connectionInfoRefLocator, + logger: createLoggerLocator('COMPASS-COLLECTION'), } ), content: CollectionTab, diff --git a/packages/compass-collection/src/modules/collection-tab.ts b/packages/compass-collection/src/modules/collection-tab.ts index cfa162fd848..f1defac01af 100644 --- a/packages/compass-collection/src/modules/collection-tab.ts +++ b/packages/compass-collection/src/modules/collection-tab.ts @@ -5,6 +5,7 @@ import type AppRegistry from '@mongodb-js/compass-app-registry'; 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 { experimentationServiceLocator } from '@mongodb-js/compass-telemetry'; function isAction( action: AnyAction, @@ -20,6 +21,7 @@ type CollectionThunkAction = ThunkAction< localAppRegistry: AppRegistry; dataService: DataService; workspaces: ReturnType; + experimentationServices: ReturnType; }, A >; diff --git a/packages/compass-collection/src/stores/collection-tab.ts b/packages/compass-collection/src/stores/collection-tab.ts index d04359af108..97d99771bd5 100644 --- a/packages/compass-collection/src/stores/collection-tab.ts +++ b/packages/compass-collection/src/stores/collection-tab.ts @@ -9,6 +9,9 @@ import reducer, { import type { Collection } from '@mongodb-js/compass-app-stores/provider'; import type { ActivateHelpers } from '@mongodb-js/compass-app-registry'; import type { workspacesServiceLocator } from '@mongodb-js/compass-workspaces/provider'; +import type { experimentationServiceLocator } from '@mongodb-js/compass-telemetry'; +import type { connectionInfoRefLocator } from '@mongodb-js/compass-connections/provider'; +import type { Logger } from '@mongodb-js/compass-logging/provider'; export type CollectionTabOptions = { /** @@ -31,18 +34,27 @@ export type CollectionTabServices = { collection: Collection; localAppRegistry: AppRegistry; workspaces: ReturnType; + experimentationServices: ReturnType; + connectionInfoRef: ReturnType; + logger: Logger; }; export function activatePlugin( { namespace, editViewName, tabId }: CollectionTabOptions, services: CollectionTabServices, { on, cleanup }: ActivateHelpers -) { +): { + store: ReturnType; + deactivate: () => void; +} { const { dataService, collection: collectionModel, localAppRegistry, workspaces, + experimentationServices, + connectionInfoRef, + logger, } = services; if (!collectionModel) { @@ -64,6 +76,7 @@ export function activatePlugin( dataService, workspaces, localAppRegistry, + experimentationServices, }) ) ); @@ -86,6 +99,25 @@ export function activatePlugin( void collectionModel.fetchMetadata({ dataService }).then((metadata) => { store.dispatch(collectionMetadataFetched(metadata)); + + // Assign experiment for Mock Data Generator (Atlas-only) + if ( + experimentationServices && + experimentationServices.assignExperiment && + connectionInfoRef.current?.atlasMetadata?.clusterName // Ensures we only assign in Atlas + ) { + void experimentationServices + .assignExperiment('mock-data-generator', { + team: 'data-explorer', + }) + .catch((error) => { + logger.debug('Mock Data Generator experiment assignment failed', { + experiment: 'mock-data-generator', + namespace: namespace, + error: error.message, + }); + }); + } }); return { diff --git a/packages/compass-telemetry/src/experimentation-provider.tsx b/packages/compass-telemetry/src/experimentation-provider.tsx index 6609565fa0a..6e1b0f0ae91 100644 --- a/packages/compass-telemetry/src/experimentation-provider.tsx +++ b/packages/compass-telemetry/src/experimentation-provider.tsx @@ -34,7 +34,7 @@ const initialContext: CompassExperimentationProviderContextValue = { }, }; -const ExperimentationContext = +export const ExperimentationContext = createContext(initialContext); // Provider component that accepts MMS experiment utils as props diff --git a/packages/compass-telemetry/src/index.ts b/packages/compass-telemetry/src/index.ts index e7cadf9d208..46e3ae7cc80 100644 --- a/packages/compass-telemetry/src/index.ts +++ b/packages/compass-telemetry/src/index.ts @@ -7,3 +7,4 @@ export type { } from './types'; export { CompassExperimentationProvider } from './experimentation-provider'; +export { experimentationServiceLocator } from './provider'; diff --git a/packages/compass-telemetry/src/provider.tsx b/packages/compass-telemetry/src/provider.tsx index 3ff409becb3..3ff8017d044 100644 --- a/packages/compass-telemetry/src/provider.tsx +++ b/packages/compass-telemetry/src/provider.tsx @@ -1,9 +1,11 @@ -import React, { useRef } from 'react'; +import React, { useRef, useContext } from 'react'; import { createServiceLocator } from '@mongodb-js/compass-app-registry'; import { createTrack, type TelemetryServiceOptions } from './generic-track'; import { useLogger } from '@mongodb-js/compass-logging/provider'; import type { TrackFunction } from './types'; import { TestName } from './growth-experiments'; +import { ExperimentationContext } from './experimentation-provider'; +import type { types } from '@mongodb-js/mdb-experiment-js'; const noop = () => { // noop @@ -47,6 +49,20 @@ export function useTelemetry(): TrackFunction { return track; } +// Service locator for experimentation services (non-component access) +export const experimentationServiceLocator = createServiceLocator( + function useExperimentationServices(): { + assignExperiment: ( + experimentName: string, + options?: types.AssignOptions + ) => Promise; + } { + const { assignExperiment } = useContext(ExperimentationContext); + return { assignExperiment }; + }, + 'experimentationServiceLocator' +); + type FirstArgument = F extends (...args: [infer A, ...any]) => any ? A : F extends { new (...args: [infer A, ...any]): any } From 57c2bb58f321f1ed78b4c1e651fb747c9de6f494 Mon Sep 17 00:00:00 2001 From: Jacob Samuel Lu Date: Mon, 4 Aug 2025 15:51:39 -0400 Subject: [PATCH 02/11] Org level flag and tests --- packages/compass-collection/src/index.ts | 2 + .../src/stores/collection-tab.spec.ts | 167 +++++++++++++++++- .../src/stores/collection-tab.ts | 11 +- 3 files changed, 177 insertions(+), 3 deletions(-) diff --git a/packages/compass-collection/src/index.ts b/packages/compass-collection/src/index.ts index 100d733e4e7..90f8281e51c 100644 --- a/packages/compass-collection/src/index.ts +++ b/packages/compass-collection/src/index.ts @@ -13,6 +13,7 @@ import type { WorkspacePlugin } from '@mongodb-js/compass-workspaces'; import { workspacesServiceLocator } from '@mongodb-js/compass-workspaces/provider'; import { experimentationServiceLocator } from '@mongodb-js/compass-telemetry/provider'; import { createLoggerLocator } from '@mongodb-js/compass-logging/provider'; +import { preferencesLocator } from 'compass-preferences-model/provider'; import { CollectionWorkspaceTitle, CollectionPluginTitleComponent, @@ -35,6 +36,7 @@ export const WorkspaceTab: WorkspacePlugin = { experimentationServices: experimentationServiceLocator, connectionInfoRef: connectionInfoRefLocator, logger: createLoggerLocator('COMPASS-COLLECTION'), + preferences: preferencesLocator, } ), content: CollectionTab, diff --git a/packages/compass-collection/src/stores/collection-tab.spec.ts b/packages/compass-collection/src/stores/collection-tab.spec.ts index 69bc0ff56e5..994434599a6 100644 --- a/packages/compass-collection/src/stores/collection-tab.spec.ts +++ b/packages/compass-collection/src/stores/collection-tab.spec.ts @@ -6,6 +6,10 @@ import Sinon from 'sinon'; import AppRegistry from '@mongodb-js/compass-app-registry'; import { expect } from 'chai'; import type { workspacesServiceLocator } from '@mongodb-js/compass-workspaces/provider'; +import type { experimentationServiceLocator } from '@mongodb-js/compass-telemetry'; +import type { connectionInfoRefLocator } from '@mongodb-js/compass-connections/provider'; +import { createNoopLogger } from '@mongodb-js/compass-logging/provider'; +import { ReadOnlyPreferenceAccess } from 'compass-preferences-model/provider'; const defaultMetadata = { namespace: 'test.foo', @@ -32,6 +36,32 @@ const mockCollection = { }, }; +const mockAtlasConnectionInfo = { + current: { + id: 'test-connection', + title: 'Test Connection', + connectionOptions: { + connectionString: 'mongodb://localhost:27017', + }, + atlasMetadata: { + clusterName: 'test-cluster', + projectId: 'test-project', + orgId: 'test-org', + clusterUniqueId: 'test-cluster-unique-id', + clusterType: 'REPLICASET' as const, + clusterState: 'IDLE' as const, + metricsId: 'test-metrics-id', + metricsType: 'replicaSet' as const, + regionalBaseUrl: null, + instanceSize: 'M10', + supports: { + globalWrites: false, + rollingIndexes: true, + }, + }, + }, +}; + describe('Collection Tab Content store', function () { const sandbox = Sinon.createSandbox(); @@ -42,7 +72,19 @@ describe('Collection Tab Content store', function () { const configureStore = async ( options: Partial = {}, - workspaces: Partial> = {} + workspaces: Partial> = {}, + experimentationServices: Partial< + ReturnType + > = {}, + connectionInfoRef: Partial< + ReturnType + > = {}, + logger = createNoopLogger('COMPASS-COLLECTION-TEST'), + preferences = new ReadOnlyPreferenceAccess({ + enableGenAIFeatures: true, + enableGenAIFeaturesAtlasOrg: true, + cloudFeatureRolloutAccess: { GEN_AI_COMPASS: true }, + }) ) => { ({ store, deactivate } = activatePlugin( { @@ -54,6 +96,10 @@ describe('Collection Tab Content store', function () { localAppRegistry, collection: mockCollection as any, workspaces: workspaces as any, + experimentationServices: experimentationServices as any, + connectionInfoRef: connectionInfoRef as any, + logger, + preferences, }, { on() {}, cleanup() {} } as any )); @@ -76,11 +122,128 @@ describe('Collection Tab Content store', function () { const store = await configureStore(undefined, { openCollectionWorkspaceSubtab, }); - store.dispatch(selectTab('Documents')); + store.dispatch(selectTab('Documents') as any); expect(openCollectionWorkspaceSubtab).to.have.been.calledWith( 'workspace-tab-id', 'Documents' ); }); }); + + describe('experimentation integration', function () { + it('should assign experiment when Atlas metadata is available', async function () { + const assignExperiment = sandbox.spy(() => Promise.resolve(null)); + + await configureStore( + undefined, + {}, + { assignExperiment }, + mockAtlasConnectionInfo + ); + + await waitFor(() => { + expect(assignExperiment).to.have.been.calledOnceWith( + 'mock-data-generator', + { + team: 'data-explorer', + } + ); + }); + }); + + it('should not assign experiment when Atlas metadata is missing', async function () { + const assignExperiment = sandbox.spy(() => Promise.resolve(null)); + const mockConnectionInfoRef = { + current: { + id: 'test-connection', + title: 'Test Connection', + connectionOptions: { + connectionString: 'mongodb://localhost:27017', + }, + // No atlasMetadata + }, + }; + + await configureStore( + undefined, + {}, + { assignExperiment }, + mockConnectionInfoRef + ); + + // Wait a bit to ensure assignment would have happened if it was going to + await new Promise((resolve) => setTimeout(resolve, 50)); + expect(assignExperiment).to.not.have.been.called; + }); + + it('should handle missing experimentationServices gracefully and initialize successfully', async function () { + const store = await configureStore( + undefined, + {}, + undefined, // No experimentationServices provided + mockAtlasConnectionInfo + ); + + // Store should still be functional despite missing experimentationServices + await waitFor(() => { + expect(store.getState()) + .to.have.property('metadata') + .deep.eq(defaultMetadata); + }); + }); + + it('should not assign experiment when AI features are disabled at the org level', async function () { + const assignExperiment = sandbox.spy(() => Promise.resolve(null)); + + const mockPreferences = new ReadOnlyPreferenceAccess({ + enableGenAIFeatures: true, + enableGenAIFeaturesAtlasOrg: false, // Disabled at org level + cloudFeatureRolloutAccess: { GEN_AI_COMPASS: true }, + }); + + const store = await configureStore( + undefined, + {}, + { assignExperiment }, + mockAtlasConnectionInfo, + undefined, + mockPreferences + ); + + // Wait a bit to ensure assignment would have happened if it was going to + await new Promise((resolve) => setTimeout(resolve, 50)); + expect(assignExperiment).to.not.have.been.called; + + // Store should still be functional + await waitFor(() => { + expect(store.getState()) + .to.have.property('metadata') + .deep.eq(defaultMetadata); + }); + }); + + it('should handle assignment errors gracefully', async function () { + const assignExperiment = sandbox.spy(() => + Promise.reject(new Error('Assignment failed')) + ); + + await configureStore( + undefined, + {}, + { assignExperiment }, + mockAtlasConnectionInfo + ); + + await waitFor(() => { + expect(assignExperiment).to.have.been.calledOnce; + }); + + // Store should still be functional despite assignment error + await waitFor(() => { + expect(store.getState()) + .to.have.property('metadata') + .deep.eq(defaultMetadata); + }); + }); + }); }); diff --git a/packages/compass-collection/src/stores/collection-tab.ts b/packages/compass-collection/src/stores/collection-tab.ts index 97d99771bd5..da04eea221d 100644 --- a/packages/compass-collection/src/stores/collection-tab.ts +++ b/packages/compass-collection/src/stores/collection-tab.ts @@ -12,6 +12,10 @@ import type { workspacesServiceLocator } from '@mongodb-js/compass-workspaces/pr import type { experimentationServiceLocator } from '@mongodb-js/compass-telemetry'; import type { connectionInfoRefLocator } from '@mongodb-js/compass-connections/provider'; import type { Logger } from '@mongodb-js/compass-logging/provider'; +import { + isAIFeatureEnabled, + type PreferencesAccess, +} from 'compass-preferences-model/provider'; export type CollectionTabOptions = { /** @@ -37,6 +41,7 @@ export type CollectionTabServices = { experimentationServices: ReturnType; connectionInfoRef: ReturnType; logger: Logger; + preferences: PreferencesAccess; }; export function activatePlugin( @@ -55,6 +60,7 @@ export function activatePlugin( experimentationServices, connectionInfoRef, logger, + preferences, } = services; if (!collectionModel) { @@ -101,10 +107,13 @@ export function activatePlugin( store.dispatch(collectionMetadataFetched(metadata)); // Assign experiment for Mock Data Generator (Atlas-only) + // Only assign when experimentationServices.assignExperiment is initialized + // and the org-level setting for AI features is enabled if ( experimentationServices && experimentationServices.assignExperiment && - connectionInfoRef.current?.atlasMetadata?.clusterName // Ensures we only assign in Atlas + connectionInfoRef.current?.atlasMetadata?.clusterName && // Ensures we only assign in Atlas + isAIFeatureEnabled(preferences.getPreferences()) // org-level AI features setting ) { void experimentationServices .assignExperiment('mock-data-generator', { From 1a32e5100bf4c674dbd690b3d55d251232990621 Mon Sep 17 00:00:00 2001 From: Jacob Samuel Lu Date: Mon, 4 Aug 2025 16:05:47 -0400 Subject: [PATCH 03/11] WIP --- .../src/stores/collection-tab.ts | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/packages/compass-collection/src/stores/collection-tab.ts b/packages/compass-collection/src/stores/collection-tab.ts index da04eea221d..24577f4f1af 100644 --- a/packages/compass-collection/src/stores/collection-tab.ts +++ b/packages/compass-collection/src/stores/collection-tab.ts @@ -106,22 +106,22 @@ export function activatePlugin( void collectionModel.fetchMetadata({ dataService }).then((metadata) => { store.dispatch(collectionMetadataFetched(metadata)); - // Assign experiment for Mock Data Generator (Atlas-only) - // Only assign when experimentationServices.assignExperiment is initialized + // Assign experiment for Mock Data Generator: + // Only assign when experimentationServices.assignExperiment is initialized, + // we're connected to Atlas, // and the org-level setting for AI features is enabled if ( - experimentationServices && - experimentationServices.assignExperiment && + experimentationServices?.assignExperiment && // Ensures experimentation services are available connectionInfoRef.current?.atlasMetadata?.clusterName && // Ensures we only assign in Atlas - isAIFeatureEnabled(preferences.getPreferences()) // org-level AI features setting + isAIFeatureEnabled(preferences.getPreferences()) // Ensures org-level AI features setting is enabled ) { void experimentationServices .assignExperiment('mock-data-generator', { - team: 'data-explorer', + team: 'Atlas Growth', }) .catch((error) => { logger.debug('Mock Data Generator experiment assignment failed', { - experiment: 'mock-data-generator', + experiment: 'MOCK_DATA_GENERATOR', namespace: namespace, error: error.message, }); From 4a75007fd174d36e7377fd05daa50f8bc6d762ce Mon Sep 17 00:00:00 2001 From: Jacob Samuel Lu Date: Mon, 4 Aug 2025 16:39:20 -0400 Subject: [PATCH 04/11] WIP --- .../compass-collection/src/stores/collection-tab.spec.ts | 5 +++-- packages/compass-collection/src/stores/collection-tab.ts | 7 ++++--- .../compass-telemetry/src/experimentation-provider.tsx | 5 +++-- packages/compass-telemetry/src/growth-experiments.ts | 1 + packages/compass-telemetry/src/provider.tsx | 2 +- 5 files changed, 12 insertions(+), 8 deletions(-) diff --git a/packages/compass-collection/src/stores/collection-tab.spec.ts b/packages/compass-collection/src/stores/collection-tab.spec.ts index 994434599a6..339a6ac9654 100644 --- a/packages/compass-collection/src/stores/collection-tab.spec.ts +++ b/packages/compass-collection/src/stores/collection-tab.spec.ts @@ -10,6 +10,7 @@ import type { experimentationServiceLocator } from '@mongodb-js/compass-telemetr import type { connectionInfoRefLocator } from '@mongodb-js/compass-connections/provider'; import { createNoopLogger } from '@mongodb-js/compass-logging/provider'; import { ReadOnlyPreferenceAccess } from 'compass-preferences-model/provider'; +import { TestName } from '../../../compass-telemetry/src/growth-experiments'; const defaultMetadata = { namespace: 'test.foo', @@ -143,9 +144,9 @@ describe('Collection Tab Content store', function () { await waitFor(() => { expect(assignExperiment).to.have.been.calledOnceWith( - 'mock-data-generator', + TestName.mockDataGenerator, { - team: 'data-explorer', + team: 'Atlas Growth', } ); }); diff --git a/packages/compass-collection/src/stores/collection-tab.ts b/packages/compass-collection/src/stores/collection-tab.ts index 24577f4f1af..1658eda8706 100644 --- a/packages/compass-collection/src/stores/collection-tab.ts +++ b/packages/compass-collection/src/stores/collection-tab.ts @@ -16,6 +16,7 @@ import { isAIFeatureEnabled, type PreferencesAccess, } from 'compass-preferences-model/provider'; +import { TestName } from '../../../compass-telemetry/src/growth-experiments'; export type CollectionTabOptions = { /** @@ -116,14 +117,14 @@ export function activatePlugin( isAIFeatureEnabled(preferences.getPreferences()) // Ensures org-level AI features setting is enabled ) { void experimentationServices - .assignExperiment('mock-data-generator', { + .assignExperiment(TestName.mockDataGenerator, { team: 'Atlas Growth', }) .catch((error) => { logger.debug('Mock Data Generator experiment assignment failed', { - experiment: 'MOCK_DATA_GENERATOR', + experiment: TestName.mockDataGenerator, namespace: namespace, - error: error.message, + error: error instanceof Error ? error.message : String(error), }); }); } diff --git a/packages/compass-telemetry/src/experimentation-provider.tsx b/packages/compass-telemetry/src/experimentation-provider.tsx index 6e1b0f0ae91..dd5a1dab1a5 100644 --- a/packages/compass-telemetry/src/experimentation-provider.tsx +++ b/packages/compass-telemetry/src/experimentation-provider.tsx @@ -1,15 +1,16 @@ import React, { createContext, useContext, useRef } from 'react'; import type { types } from '@mongodb-js/mdb-experiment-js'; import type { typesReact } from '@mongodb-js/mdb-experiment-js/react'; +import type { TestName } from './growth-experiments'; type UseAssignmentHook = ( - experimentName: string, + experimentName: TestName, trackIsInSample: boolean, options?: typesReact.UseAssignmentOptions ) => typesReact.UseAssignmentResponse; type AssignExperimentFn = ( - experimentName: string, + experimentName: TestName, options?: types.AssignOptions ) => Promise; diff --git a/packages/compass-telemetry/src/growth-experiments.ts b/packages/compass-telemetry/src/growth-experiments.ts index dfe4d52cd11..766e5160d7a 100644 --- a/packages/compass-telemetry/src/growth-experiments.ts +++ b/packages/compass-telemetry/src/growth-experiments.ts @@ -1,3 +1,4 @@ export enum TestName { earlyJourneyIndexesGuidance = 'EARLY_JOURNEY_INDEXES_GUIDANCE_20250328', + mockDataGenerator = 'MOCK_DATA_GENERATOR_20251001', } diff --git a/packages/compass-telemetry/src/provider.tsx b/packages/compass-telemetry/src/provider.tsx index 3ff8017d044..00ef3387185 100644 --- a/packages/compass-telemetry/src/provider.tsx +++ b/packages/compass-telemetry/src/provider.tsx @@ -53,7 +53,7 @@ export function useTelemetry(): TrackFunction { export const experimentationServiceLocator = createServiceLocator( function useExperimentationServices(): { assignExperiment: ( - experimentName: string, + experimentName: TestName, options?: types.AssignOptions ) => Promise; } { From 41957c698fc9bf3c5bf64260cb54fd1095a2b04b Mon Sep 17 00:00:00 2001 From: Jacob Samuel Lu Date: Tue, 5 Aug 2025 11:23:20 -0400 Subject: [PATCH 05/11] Absolute import --- packages/compass-collection/src/stores/collection-tab.spec.ts | 2 +- packages/compass-collection/src/stores/collection-tab.ts | 2 +- packages/compass-telemetry/src/index.ts | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/compass-collection/src/stores/collection-tab.spec.ts b/packages/compass-collection/src/stores/collection-tab.spec.ts index 339a6ac9654..7b299b07abf 100644 --- a/packages/compass-collection/src/stores/collection-tab.spec.ts +++ b/packages/compass-collection/src/stores/collection-tab.spec.ts @@ -10,7 +10,7 @@ import type { experimentationServiceLocator } from '@mongodb-js/compass-telemetr import type { connectionInfoRefLocator } from '@mongodb-js/compass-connections/provider'; import { createNoopLogger } from '@mongodb-js/compass-logging/provider'; import { ReadOnlyPreferenceAccess } from 'compass-preferences-model/provider'; -import { TestName } from '../../../compass-telemetry/src/growth-experiments'; +import { TestName } from '@mongodb-js/compass-telemetry/provider'; const defaultMetadata = { namespace: 'test.foo', diff --git a/packages/compass-collection/src/stores/collection-tab.ts b/packages/compass-collection/src/stores/collection-tab.ts index 1658eda8706..c38efb08d1a 100644 --- a/packages/compass-collection/src/stores/collection-tab.ts +++ b/packages/compass-collection/src/stores/collection-tab.ts @@ -16,7 +16,7 @@ import { isAIFeatureEnabled, type PreferencesAccess, } from 'compass-preferences-model/provider'; -import { TestName } from '../../../compass-telemetry/src/growth-experiments'; +import { TestName } from '@mongodb-js/compass-telemetry/provider'; export type CollectionTabOptions = { /** diff --git a/packages/compass-telemetry/src/index.ts b/packages/compass-telemetry/src/index.ts index 46e3ae7cc80..c06bd40d778 100644 --- a/packages/compass-telemetry/src/index.ts +++ b/packages/compass-telemetry/src/index.ts @@ -7,4 +7,4 @@ export type { } from './types'; export { CompassExperimentationProvider } from './experimentation-provider'; -export { experimentationServiceLocator } from './provider'; +export { experimentationServiceLocator, TestName } from './provider'; From 66413a09a787cd34e44aa5184c63aa1a11cbb716 Mon Sep 17 00:00:00 2001 From: Jacob Samuel Lu Date: Tue, 5 Aug 2025 11:26:18 -0400 Subject: [PATCH 06/11] Remove unneeded check --- packages/compass-collection/src/stores/collection-tab.ts | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/packages/compass-collection/src/stores/collection-tab.ts b/packages/compass-collection/src/stores/collection-tab.ts index c38efb08d1a..c43cba825a8 100644 --- a/packages/compass-collection/src/stores/collection-tab.ts +++ b/packages/compass-collection/src/stores/collection-tab.ts @@ -107,12 +107,9 @@ export function activatePlugin( void collectionModel.fetchMetadata({ dataService }).then((metadata) => { store.dispatch(collectionMetadataFetched(metadata)); - // Assign experiment for Mock Data Generator: - // Only assign when experimentationServices.assignExperiment is initialized, - // we're connected to Atlas, - // and the org-level setting for AI features is enabled + // Assign experiment for Mock Data Generator + // Only assign when we're connected to Atlas and the org-level setting for AI features is enabled if ( - experimentationServices?.assignExperiment && // Ensures experimentation services are available connectionInfoRef.current?.atlasMetadata?.clusterName && // Ensures we only assign in Atlas isAIFeatureEnabled(preferences.getPreferences()) // Ensures org-level AI features setting is enabled ) { From a16f2c9ef0891d0815055321060b0e1e0a0179cb Mon Sep 17 00:00:00 2001 From: Jacob Samuel Lu Date: Tue, 5 Aug 2025 11:37:32 -0400 Subject: [PATCH 07/11] Comment address --- .../src/stores/collection-tab.spec.ts | 16 ---------------- packages/compass-telemetry/src/index.ts | 2 +- 2 files changed, 1 insertion(+), 17 deletions(-) diff --git a/packages/compass-collection/src/stores/collection-tab.spec.ts b/packages/compass-collection/src/stores/collection-tab.spec.ts index 7b299b07abf..c2ef285ade1 100644 --- a/packages/compass-collection/src/stores/collection-tab.spec.ts +++ b/packages/compass-collection/src/stores/collection-tab.spec.ts @@ -177,22 +177,6 @@ describe('Collection Tab Content store', function () { expect(assignExperiment).to.not.have.been.called; }); - it('should handle missing experimentationServices gracefully and initialize successfully', async function () { - const store = await configureStore( - undefined, - {}, - undefined, // No experimentationServices provided - mockAtlasConnectionInfo - ); - - // Store should still be functional despite missing experimentationServices - await waitFor(() => { - expect(store.getState()) - .to.have.property('metadata') - .deep.eq(defaultMetadata); - }); - }); - it('should not assign experiment when AI features are disabled at the org level', async function () { const assignExperiment = sandbox.spy(() => Promise.resolve(null)); diff --git a/packages/compass-telemetry/src/index.ts b/packages/compass-telemetry/src/index.ts index c06bd40d778..02c526a3ac7 100644 --- a/packages/compass-telemetry/src/index.ts +++ b/packages/compass-telemetry/src/index.ts @@ -7,4 +7,4 @@ export type { } from './types'; export { CompassExperimentationProvider } from './experimentation-provider'; -export { experimentationServiceLocator, TestName } from './provider'; +export { TestName } from './provider'; From 73a6ea89eee4224bf7e17dc8ccd21fb537e8c054 Mon Sep 17 00:00:00 2001 From: Jacob Samuel Lu Date: Tue, 5 Aug 2025 11:41:42 -0400 Subject: [PATCH 08/11] Change export --- packages/compass-telemetry/src/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/compass-telemetry/src/index.ts b/packages/compass-telemetry/src/index.ts index 02c526a3ac7..66440b317d7 100644 --- a/packages/compass-telemetry/src/index.ts +++ b/packages/compass-telemetry/src/index.ts @@ -7,4 +7,4 @@ export type { } from './types'; export { CompassExperimentationProvider } from './experimentation-provider'; -export { TestName } from './provider'; +export { TestName } from './growth-experiments'; From 36817d3bb0a66c3e3796fe9ba8ac94aa28c347f9 Mon Sep 17 00:00:00 2001 From: Jacob Samuel Lu Date: Tue, 5 Aug 2025 15:49:25 -0400 Subject: [PATCH 09/11] Comment --- packages/compass-telemetry/src/provider.tsx | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/packages/compass-telemetry/src/provider.tsx b/packages/compass-telemetry/src/provider.tsx index 00ef3387185..f51c269d4d9 100644 --- a/packages/compass-telemetry/src/provider.tsx +++ b/packages/compass-telemetry/src/provider.tsx @@ -49,14 +49,16 @@ export function useTelemetry(): TrackFunction { return track; } +export interface ExperimentationServices { + assignExperiment: ( + experimentName: TestName, + options?: types.AssignOptions + ) => Promise; +} + // Service locator for experimentation services (non-component access) export const experimentationServiceLocator = createServiceLocator( - function useExperimentationServices(): { - assignExperiment: ( - experimentName: TestName, - options?: types.AssignOptions - ) => Promise; - } { + function useExperimentationServices(): ExperimentationServices { const { assignExperiment } = useContext(ExperimentationContext); return { assignExperiment }; }, From b7250847503bfa88a18318caf346168308851820 Mon Sep 17 00:00:00 2001 From: Jacob Samuel Lu Date: Wed, 6 Aug 2025 11:04:25 -0400 Subject: [PATCH 10/11] Change exports --- packages/compass-collection/src/modules/collection-tab.ts | 2 +- packages/compass-collection/src/stores/collection-tab.ts | 2 +- packages/compass-telemetry/src/index.ts | 1 + 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/packages/compass-collection/src/modules/collection-tab.ts b/packages/compass-collection/src/modules/collection-tab.ts index f1defac01af..12655bc0dce 100644 --- a/packages/compass-collection/src/modules/collection-tab.ts +++ b/packages/compass-collection/src/modules/collection-tab.ts @@ -5,7 +5,7 @@ import type AppRegistry from '@mongodb-js/compass-app-registry'; 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 { experimentationServiceLocator } from '@mongodb-js/compass-telemetry'; +import type { experimentationServiceLocator } from '@mongodb-js/compass-telemetry/provider'; function isAction( action: AnyAction, diff --git a/packages/compass-collection/src/stores/collection-tab.ts b/packages/compass-collection/src/stores/collection-tab.ts index c43cba825a8..3b69e9c372c 100644 --- a/packages/compass-collection/src/stores/collection-tab.ts +++ b/packages/compass-collection/src/stores/collection-tab.ts @@ -9,7 +9,7 @@ import reducer, { import type { Collection } from '@mongodb-js/compass-app-stores/provider'; import type { ActivateHelpers } from '@mongodb-js/compass-app-registry'; import type { workspacesServiceLocator } from '@mongodb-js/compass-workspaces/provider'; -import type { experimentationServiceLocator } from '@mongodb-js/compass-telemetry'; +import type { experimentationServiceLocator } from '@mongodb-js/compass-telemetry/provider'; import type { connectionInfoRefLocator } from '@mongodb-js/compass-connections/provider'; import type { Logger } from '@mongodb-js/compass-logging/provider'; import { diff --git a/packages/compass-telemetry/src/index.ts b/packages/compass-telemetry/src/index.ts index 66440b317d7..5cb7b8b0b50 100644 --- a/packages/compass-telemetry/src/index.ts +++ b/packages/compass-telemetry/src/index.ts @@ -7,4 +7,5 @@ export type { } from './types'; export { CompassExperimentationProvider } from './experimentation-provider'; +export { experimentationServiceLocator } from './provider'; export { TestName } from './growth-experiments'; From 97a3614317b2b725a5696aab0cf993070329870c Mon Sep 17 00:00:00 2001 From: Jacob Samuel Lu Date: Wed, 6 Aug 2025 11:25:24 -0400 Subject: [PATCH 11/11] Rename existing test name enum --- .../src/stores/collection-tab.spec.ts | 4 ++-- .../compass-collection/src/stores/collection-tab.ts | 6 +++--- .../create-index-modal/create-index-modal.tsx | 4 ++-- .../compass-telemetry/src/experimentation-provider.tsx | 6 +++--- packages/compass-telemetry/src/growth-experiments.ts | 2 +- packages/compass-telemetry/src/index.ts | 2 +- packages/compass-telemetry/src/provider.tsx | 10 +++++----- 7 files changed, 17 insertions(+), 17 deletions(-) diff --git a/packages/compass-collection/src/stores/collection-tab.spec.ts b/packages/compass-collection/src/stores/collection-tab.spec.ts index c2ef285ade1..6a5394bdef9 100644 --- a/packages/compass-collection/src/stores/collection-tab.spec.ts +++ b/packages/compass-collection/src/stores/collection-tab.spec.ts @@ -10,7 +10,7 @@ import type { experimentationServiceLocator } from '@mongodb-js/compass-telemetr import type { connectionInfoRefLocator } from '@mongodb-js/compass-connections/provider'; import { createNoopLogger } from '@mongodb-js/compass-logging/provider'; import { ReadOnlyPreferenceAccess } from 'compass-preferences-model/provider'; -import { TestName } from '@mongodb-js/compass-telemetry/provider'; +import { ExperimentTestName } from '@mongodb-js/compass-telemetry/provider'; const defaultMetadata = { namespace: 'test.foo', @@ -144,7 +144,7 @@ describe('Collection Tab Content store', function () { await waitFor(() => { expect(assignExperiment).to.have.been.calledOnceWith( - TestName.mockDataGenerator, + ExperimentTestName.mockDataGenerator, { team: 'Atlas Growth', } diff --git a/packages/compass-collection/src/stores/collection-tab.ts b/packages/compass-collection/src/stores/collection-tab.ts index 3b69e9c372c..925572bf7e3 100644 --- a/packages/compass-collection/src/stores/collection-tab.ts +++ b/packages/compass-collection/src/stores/collection-tab.ts @@ -16,7 +16,7 @@ import { isAIFeatureEnabled, type PreferencesAccess, } from 'compass-preferences-model/provider'; -import { TestName } from '@mongodb-js/compass-telemetry/provider'; +import { ExperimentTestName } from '@mongodb-js/compass-telemetry/provider'; export type CollectionTabOptions = { /** @@ -114,12 +114,12 @@ export function activatePlugin( isAIFeatureEnabled(preferences.getPreferences()) // Ensures org-level AI features setting is enabled ) { void experimentationServices - .assignExperiment(TestName.mockDataGenerator, { + .assignExperiment(ExperimentTestName.mockDataGenerator, { team: 'Atlas Growth', }) .catch((error) => { logger.debug('Mock Data Generator experiment assignment failed', { - experiment: TestName.mockDataGenerator, + experiment: ExperimentTestName.mockDataGenerator, namespace: namespace, error: error instanceof Error ? error.message : String(error), }); diff --git a/packages/compass-indexes/src/components/create-index-modal/create-index-modal.tsx b/packages/compass-indexes/src/components/create-index-modal/create-index-modal.tsx index b49fe7b8ae7..4cbf69711d1 100644 --- a/packages/compass-indexes/src/components/create-index-modal/create-index-modal.tsx +++ b/packages/compass-indexes/src/components/create-index-modal/create-index-modal.tsx @@ -24,7 +24,7 @@ import { useTrackOnChange, type TrackFunction, useFireExperimentViewed, - TestName, + ExperimentTestName, useTelemetry, } from '@mongodb-js/compass-telemetry/provider'; import { useConnectionInfoRef } from '@mongodb-js/compass-connections/provider'; @@ -91,7 +91,7 @@ function CreateIndexModal({ usePreference('showIndexesGuidanceVariant') && enableInIndexesGuidanceExp; useFireExperimentViewed({ - testName: TestName.earlyJourneyIndexesGuidance, + testName: ExperimentTestName.earlyJourneyIndexesGuidance, shouldFire: enableInIndexesGuidanceExp && isVisible, }); diff --git a/packages/compass-telemetry/src/experimentation-provider.tsx b/packages/compass-telemetry/src/experimentation-provider.tsx index dd5a1dab1a5..3055fd255a3 100644 --- a/packages/compass-telemetry/src/experimentation-provider.tsx +++ b/packages/compass-telemetry/src/experimentation-provider.tsx @@ -1,16 +1,16 @@ import React, { createContext, useContext, useRef } from 'react'; import type { types } from '@mongodb-js/mdb-experiment-js'; import type { typesReact } from '@mongodb-js/mdb-experiment-js/react'; -import type { TestName } from './growth-experiments'; +import type { ExperimentTestName } from './growth-experiments'; type UseAssignmentHook = ( - experimentName: TestName, + experimentName: ExperimentTestName, trackIsInSample: boolean, options?: typesReact.UseAssignmentOptions ) => typesReact.UseAssignmentResponse; type AssignExperimentFn = ( - experimentName: TestName, + experimentName: ExperimentTestName, options?: types.AssignOptions ) => Promise; diff --git a/packages/compass-telemetry/src/growth-experiments.ts b/packages/compass-telemetry/src/growth-experiments.ts index 766e5160d7a..87437d07e5b 100644 --- a/packages/compass-telemetry/src/growth-experiments.ts +++ b/packages/compass-telemetry/src/growth-experiments.ts @@ -1,4 +1,4 @@ -export enum TestName { +export enum ExperimentTestName { earlyJourneyIndexesGuidance = 'EARLY_JOURNEY_INDEXES_GUIDANCE_20250328', mockDataGenerator = 'MOCK_DATA_GENERATOR_20251001', } diff --git a/packages/compass-telemetry/src/index.ts b/packages/compass-telemetry/src/index.ts index 5cb7b8b0b50..248c878dc21 100644 --- a/packages/compass-telemetry/src/index.ts +++ b/packages/compass-telemetry/src/index.ts @@ -8,4 +8,4 @@ export type { export { CompassExperimentationProvider } from './experimentation-provider'; export { experimentationServiceLocator } from './provider'; -export { TestName } from './growth-experiments'; +export { ExperimentTestName } from './growth-experiments'; diff --git a/packages/compass-telemetry/src/provider.tsx b/packages/compass-telemetry/src/provider.tsx index f51c269d4d9..9eea405ad2e 100644 --- a/packages/compass-telemetry/src/provider.tsx +++ b/packages/compass-telemetry/src/provider.tsx @@ -3,7 +3,7 @@ import { createServiceLocator } from '@mongodb-js/compass-app-registry'; import { createTrack, type TelemetryServiceOptions } from './generic-track'; import { useLogger } from '@mongodb-js/compass-logging/provider'; import type { TrackFunction } from './types'; -import { TestName } from './growth-experiments'; +import { ExperimentTestName } from './growth-experiments'; import { ExperimentationContext } from './experimentation-provider'; import type { types } from '@mongodb-js/mdb-experiment-js'; @@ -51,7 +51,7 @@ export function useTelemetry(): TrackFunction { export interface ExperimentationServices { assignExperiment: ( - experimentName: TestName, + experimentName: ExperimentTestName, options?: types.AssignOptions ) => Promise; } @@ -128,7 +128,7 @@ export function useTrackOnChange( * * @example * useFireExperimentViewed({ - * testName: TestName.earlyJourneyIndexesGuidance, + * testName: ExperimentTestName.earlyJourneyIndexesGuidance, * shouldFire: enableInIndexesGuidanceExp , * }); */ @@ -136,7 +136,7 @@ export const useFireExperimentViewed = ({ testName, shouldFire = true, }: { - testName: TestName; + testName: ExperimentTestName; shouldFire?: boolean; }) => { useTrackOnChange( @@ -154,4 +154,4 @@ export const useFireExperimentViewed = ({ }; export type { TrackFunction }; -export { TestName }; +export { ExperimentTestName };