Skip to content

Commit 59ffc14

Browse files
authored
chore(compass-preferences-model): use optInGenAIFeatures for Compass COMPASS-9593 (#7129)
1 parent 55f125a commit 59ffc14

File tree

11 files changed

+109
-56
lines changed

11 files changed

+109
-56
lines changed

packages/compass-e2e-tests/tests/atlas-cloud/collection-ai-query.test.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ describe('Collection ai query', function () {
4949
true
5050
);
5151
await browser.setFeature('enableGenAIFeaturesAtlasOrg', true);
52-
await browser.setFeature('optInDataExplorerGenAIFeatures', true);
52+
await browser.setFeature('optInGenAIFeatures', true);
5353
});
5454

5555
describe('on the documents tab', function () {
@@ -170,7 +170,7 @@ describe('Collection ai query', function () {
170170
true
171171
);
172172
await browser.setFeature('enableGenAIFeaturesAtlasOrg', false);
173-
await browser.setFeature('optInDataExplorerGenAIFeatures', true);
173+
await browser.setFeature('optInGenAIFeatures', true);
174174
});
175175

176176
it('should not show the gen ai intro button', async function () {

packages/compass-generative-ai/src/atlas-ai-service.spec.ts

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -321,6 +321,54 @@ describe('AtlasAiService', function () {
321321
});
322322
});
323323
});
324+
325+
describe('optIntoGenAIFeatures', function () {
326+
beforeEach(async function () {
327+
// Reset preferences
328+
await preferences.savePreferences({
329+
optInGenAIFeatures: false,
330+
enableUnauthenticatedGenAI: true,
331+
});
332+
});
333+
334+
afterEach(async function () {
335+
await preferences.savePreferences({
336+
enableUnauthenticatedGenAI: false,
337+
});
338+
});
339+
340+
it('should save preference when cloud preset', async function () {
341+
const fetchStub = sandbox.stub().resolves(makeResponse({}));
342+
global.fetch = fetchStub;
343+
344+
await atlasAiService.optIntoGenAIFeatures();
345+
346+
// In Data Explorer, make a POST request to cloud endpoint and save preference
347+
if (apiURLPreset === 'cloud') {
348+
// Verify fetch was called with correct parameters
349+
expect(fetchStub).to.have.been.calledOnce;
350+
351+
expect(fetchStub).to.have.been.calledWith(
352+
'/cloud/settings/optInDataExplorerGenAIFeatures',
353+
{
354+
method: 'POST',
355+
headers: {
356+
'Content-Type': 'application/x-www-form-urlencoded',
357+
Accept: 'application/json',
358+
},
359+
body: new URLSearchParams([['value', 'true']]),
360+
}
361+
);
362+
} else {
363+
// In Compass, no fetch is made, only stored locally
364+
expect(fetchStub).to.not.have.been.called;
365+
}
366+
367+
// Verify preference was saved
368+
const currentPreferences = preferences.getPreferences();
369+
expect(currentPreferences.optInGenAIFeatures).to.equal(true);
370+
});
371+
});
324372
});
325373
}
326374
});

packages/compass-generative-ai/src/atlas-ai-service.ts

Lines changed: 23 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,9 @@ import type { ConnectionInfo } from '@mongodb-js/compass-connections/provider';
99
import type { Document } from 'mongodb';
1010
import type { Logger } from '@mongodb-js/compass-logging';
1111
import { EJSON } from 'bson';
12-
import { signIntoAtlasWithModalPrompt } from './store/atlas-signin-reducer';
1312
import { getStore } from './store/atlas-ai-store';
1413
import { optIntoGenAIWithModalPrompt } from './store/atlas-optin-reducer';
14+
import { signIntoAtlasWithModalPrompt } from './store/atlas-signin-reducer';
1515

1616
type GenerativeAiInput = {
1717
userInput: string;
@@ -277,6 +277,10 @@ export class AtlasAiService {
277277
}
278278

279279
async ensureAiFeatureAccess({ signal }: { signal?: AbortSignal } = {}) {
280+
if (this.preferences.getPreferences().enableUnauthenticatedGenAI) {
281+
return getStore().dispatch(optIntoGenAIWithModalPrompt({ signal }));
282+
}
283+
280284
// When the ai feature is attempted to be opened we make sure
281285
// the user is signed into Atlas and opted in.
282286

@@ -391,23 +395,25 @@ export class AtlasAiService {
391395
);
392396
}
393397

394-
// Performs a post request to atlas to set the user opt in preference to true.
395-
async optIntoGenAIFeaturesAtlas() {
396-
await this.atlasService.authenticatedFetch(
397-
this.atlasService.cloudEndpoint(
398-
'/settings/optInDataExplorerGenAIFeatures'
399-
),
400-
{
401-
method: 'POST',
402-
headers: {
403-
'Content-Type': 'application/x-www-form-urlencoded',
404-
Accept: 'application/json',
405-
},
406-
body: new URLSearchParams([['value', 'true']]),
407-
}
408-
);
398+
async optIntoGenAIFeatures() {
399+
if (this.apiURLPreset === 'cloud') {
400+
// Performs a post request to Atlas to set the user opt in preference to true.
401+
await this.atlasService.authenticatedFetch(
402+
this.atlasService.cloudEndpoint(
403+
'settings/optInDataExplorerGenAIFeatures'
404+
),
405+
{
406+
method: 'POST',
407+
headers: {
408+
'Content-Type': 'application/x-www-form-urlencoded',
409+
Accept: 'application/json',
410+
},
411+
body: new URLSearchParams([['value', 'true']]),
412+
}
413+
);
414+
}
409415
await this.preferences.savePreferences({
410-
optInDataExplorerGenAIFeatures: true,
416+
optInGenAIFeatures: true,
411417
});
412418
}
413419

packages/compass-generative-ai/src/store/atlas-optin-reducer.spec.ts

Lines changed: 16 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ describe('atlasOptInReducer', function () {
2020
beforeEach(async function () {
2121
mockPreferences = await createSandboxFromDefaultPreferences();
2222
await mockPreferences.savePreferences({
23-
optInDataExplorerGenAIFeatures: false,
23+
optInGenAIFeatures: false,
2424
});
2525
});
2626

@@ -31,7 +31,7 @@ describe('atlasOptInReducer', function () {
3131
describe('optIn', function () {
3232
it('should check state and set state to success if already opted in', async function () {
3333
const mockAtlasAiService = {
34-
optIntoGenAIFeaturesAtlas: sandbox.stub().resolves({ sub: '1234' }),
34+
optIntoGenAIFeatures: sandbox.stub().resolves({ sub: '1234' }),
3535
};
3636
const store = configureStore({
3737
atlasAuthService: {} as any,
@@ -45,8 +45,7 @@ describe('atlasOptInReducer', function () {
4545
);
4646
void store.dispatch(atlasAiServiceOptedIn());
4747
await store.dispatch(optIn());
48-
expect(mockAtlasAiService.optIntoGenAIFeaturesAtlas).not.to.have.been
49-
.called;
48+
expect(mockAtlasAiService.optIntoGenAIFeatures).not.to.have.been.called;
5049
expect(store.getState().optIn).to.have.nested.property(
5150
'state',
5251
'optin-success'
@@ -55,7 +54,7 @@ describe('atlasOptInReducer', function () {
5554

5655
it('should start opt in, and set state to success', async function () {
5756
const mockAtlasAiService = {
58-
optIntoGenAIFeaturesAtlas: sandbox.stub().resolves({ sub: '1234' }),
57+
optIntoGenAIFeatures: sandbox.stub().resolves({ sub: '1234' }),
5958
};
6059
const store = configureStore({
6160
atlasAuthService: {} as any,
@@ -69,8 +68,7 @@ describe('atlasOptInReducer', function () {
6968
);
7069
void store.dispatch(optIntoGenAIWithModalPrompt()).catch(() => {});
7170
await store.dispatch(optIn());
72-
expect(mockAtlasAiService.optIntoGenAIFeaturesAtlas).to.have.been
73-
.calledOnce;
71+
expect(mockAtlasAiService.optIntoGenAIFeatures).to.have.been.calledOnce;
7472
expect(store.getState().optIn).to.have.nested.property(
7573
'state',
7674
'optin-success'
@@ -81,13 +79,13 @@ describe('atlasOptInReducer', function () {
8179
beforeEach(async function () {
8280
await mockPreferences.savePreferences({
8381
enableGenAIFeaturesAtlasProject: false,
84-
optInDataExplorerGenAIFeatures: true,
82+
optInGenAIFeatures: true,
8583
});
8684
});
8785

8886
it('should start the opt in flow', async function () {
8987
const mockAtlasAiService = {
90-
optIntoGenAIFeaturesAtlas: sandbox.stub().resolves({ sub: '1234' }),
88+
optIntoGenAIFeatures: sandbox.stub().resolves({ sub: '1234' }),
9189
};
9290
const store = configureStore({
9391
atlasAuthService: {} as any,
@@ -101,8 +99,7 @@ describe('atlasOptInReducer', function () {
10199
);
102100
void store.dispatch(optIntoGenAIWithModalPrompt()).catch(() => {});
103101
await store.dispatch(optIn());
104-
expect(mockAtlasAiService.optIntoGenAIFeaturesAtlas).to.have.been
105-
.calledOnce;
102+
expect(mockAtlasAiService.optIntoGenAIFeatures).to.have.been.calledOnce;
106103
expect(store.getState().optIn).to.have.nested.property(
107104
'state',
108105
'optin-success'
@@ -112,9 +109,7 @@ describe('atlasOptInReducer', function () {
112109

113110
it('should fail opt in if opt in failed', async function () {
114111
const mockAtlasAiService = {
115-
optIntoGenAIFeaturesAtlas: sandbox
116-
.stub()
117-
.rejects(new Error('Whooops!')),
112+
optIntoGenAIFeatures: sandbox.stub().rejects(new Error('Whooops!')),
118113
};
119114
const store = configureStore({
120115
atlasAuthService: {} as any,
@@ -127,8 +122,7 @@ describe('atlasOptInReducer', function () {
127122
// Avoid unhandled rejections.
128123
AttemptStateMap.get(attemptId)?.promise.catch(() => {});
129124
await optInPromise;
130-
expect(mockAtlasAiService.optIntoGenAIFeaturesAtlas).to.have.been
131-
.calledOnce;
125+
expect(mockAtlasAiService.optIntoGenAIFeatures).to.have.been.calledOnce;
132126
expect(store.getState().optIn).to.have.nested.property('state', 'error');
133127
});
134128
});
@@ -153,7 +147,7 @@ describe('atlasOptInReducer', function () {
153147

154148
it('should cancel opt in if opt in is in progress', async function () {
155149
const mockAtlasAiService = {
156-
optIntoGenAIFeaturesAtlas: sandbox
150+
optIntoGenAIFeatures: sandbox
157151
.stub()
158152
.callsFake(({ signal }: { signal: AbortSignal }) => {
159153
return new Promise((resolve, reject) => {
@@ -183,10 +177,10 @@ describe('atlasOptInReducer', function () {
183177
});
184178
});
185179

186-
describe('optIntoAtlasWithModalPrompt', function () {
180+
describe('optIntoGenAIWithModalPrompt', function () {
187181
it('should resolve when user finishes opt in with prompt flow', async function () {
188182
const mockAtlasAiService = {
189-
optIntoGenAIFeaturesAtlas: sandbox.stub().resolves({ sub: '1234' }),
183+
optIntoGenAIFeatures: sandbox.stub().resolves({ sub: '1234' }),
190184
};
191185
const store = configureStore({
192186
atlasAuthService: {} as any,
@@ -203,7 +197,7 @@ describe('atlasOptInReducer', function () {
203197

204198
it('should reject if opt in flow fails', async function () {
205199
const mockAtlasAiService = {
206-
optIntoGenAIFeaturesAtlas: sandbox.stub().rejects(new Error('Whoops!')),
200+
optIntoGenAIFeatures: sandbox.stub().rejects(new Error('Whoops!')),
207201
};
208202
const store = configureStore({
209203
atlasAuthService: {} as any,
@@ -226,7 +220,7 @@ describe('atlasOptInReducer', function () {
226220

227221
it('should reject if user dismissed the modal', async function () {
228222
const mockAtlasAiService = {
229-
optIntoGenAIFeaturesAtlas: sandbox.stub().resolves({ sub: '1234' }),
223+
optIntoGenAIFeatures: sandbox.stub().resolves({ sub: '1234' }),
230224
};
231225
const store = configureStore({
232226
atlasAuthService: {} as any,
@@ -249,7 +243,7 @@ describe('atlasOptInReducer', function () {
249243

250244
it('should reject if provided signal was aborted', async function () {
251245
const mockAtlasAiService = {
252-
optIntoGenAIFeaturesAtlas: sandbox.stub().resolves({ sub: '1234' }),
246+
optIntoGenAIFeatures: sandbox.stub().resolves({ sub: '1234' }),
253247
};
254248
const store = configureStore({
255249
atlasAuthService: {} as any,

packages/compass-generative-ai/src/store/atlas-optin-reducer.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -228,7 +228,7 @@ export const optIntoGenAIWithModalPrompt = ({
228228
const { state } = getState().optIn;
229229
if (
230230
(state === 'optin-success' ||
231-
preferences.getPreferences().optInDataExplorerGenAIFeatures) &&
231+
preferences.getPreferences().optInGenAIFeatures) &&
232232
preferences.getPreferences().enableGenAIFeaturesAtlasProject
233233
) {
234234
return Promise.resolve();
@@ -265,7 +265,7 @@ export const optIn = (): GenAIAtlasOptInThunkAction<Promise<void>> => {
265265

266266
try {
267267
throwIfAborted(signal);
268-
await atlasAiService.optIntoGenAIFeaturesAtlas();
268+
await atlasAiService.optIntoGenAIFeatures();
269269
dispatch(atlasAiServiceOptedIn());
270270
resolve();
271271
} catch (err) {

packages/compass-preferences-model/src/compass-web-preferences-access.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import { getActiveUser } from './utils';
77

88
const editablePreferences: (keyof UserPreferences)[] = [
99
// Value can change from false to true during allocation / checking
10-
'optInDataExplorerGenAIFeatures',
10+
'optInGenAIFeatures',
1111
'cloudFeatureRolloutAccess',
1212
// TODO(COMPASS-9353): Provide a standard for updating Compass preferences in web
1313
'enableIndexesGuidanceExp',

packages/compass-preferences-model/src/feature-flags.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ export type FeatureFlags = {
2828
showIndexesGuidanceVariant: boolean;
2929
enableContextMenus: boolean;
3030
enableSearchActivationProgramP1: boolean;
31+
enableUnauthenticatedGenAI: boolean;
3132
};
3233

3334
export const featureFlags: Required<{
@@ -150,6 +151,13 @@ export const featureFlags: Required<{
150151
},
151152
},
152153

154+
enableUnauthenticatedGenAI: {
155+
stage: 'development',
156+
description: {
157+
short: 'Enable GenAI for unauthenticated users',
158+
},
159+
},
160+
153161
/**
154162
* Feature flag for CLOUDP-308952.
155163
*/

packages/compass-preferences-model/src/preferences-schema.tsx

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,7 @@ export type UserConfigurablePreferences = PermanentFeatureFlags &
8585
| 'web-sandbox-atlas-dev'
8686
| 'web-sandbox-atlas-qa'
8787
| 'web-sandbox-atlas';
88-
optInDataExplorerGenAIFeatures: boolean;
88+
optInGenAIFeatures: boolean;
8989
// Features that are enabled by default in Compass, but are disabled in Data
9090
// Explorer
9191
enableExplainPlan: boolean;
@@ -811,17 +811,16 @@ export const storedUserPreferencesProps: Required<{
811811
.default('atlas'),
812812
type: 'string',
813813
},
814-
optInDataExplorerGenAIFeatures: {
814+
optInGenAIFeatures: {
815815
ui: true,
816816
cli: false,
817817
global: false,
818818
description: {
819-
short: 'User Opt-in for Data Explorer Gen AI Features',
819+
short: 'User or Client Opt-in for Gen AI Features',
820820
},
821-
validator: z.boolean().default(true),
821+
validator: z.boolean().default(false),
822822
type: 'boolean',
823823
},
824-
825824
enableAtlasSearchIndexes: {
826825
ui: true,
827826
cli: true,

packages/compass-web/sandbox/index.tsx

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ const App = () => {
4848
enableGenAIFeaturesAtlasProject,
4949
enableGenAISampleDocumentPassingOnAtlasProject,
5050
enableGenAIFeaturesAtlasOrg,
51-
optInDataExplorerGenAIFeatures,
51+
optInGenAIFeatures,
5252
} = projectParams ?? {};
5353

5454
const atlasServiceSandboxBackendVariant =
@@ -135,8 +135,7 @@ const App = () => {
135135
isAtlas && !!enableGenAISampleDocumentPassingOnAtlasProject,
136136
enableGenAIFeaturesAtlasOrg:
137137
isAtlas && !!enableGenAIFeaturesAtlasOrg,
138-
optInDataExplorerGenAIFeatures:
139-
isAtlas && !!optInDataExplorerGenAIFeatures,
138+
optInGenAIFeatures: isAtlas && !!optInGenAIFeatures,
140139
enableDataModeling: true,
141140
}}
142141
onTrack={sandboxTelemetry.track}

packages/compass-web/sandbox/sandbox-atlas-sign-in.tsx

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ type ProjectParams = {
1919
enableGenAIFeaturesAtlasProject: boolean;
2020
enableGenAISampleDocumentPassingOnAtlasProject: boolean;
2121
enableGenAIFeaturesAtlasOrg: boolean;
22-
optInDataExplorerGenAIFeatures: boolean;
22+
optInGenAIFeatures: boolean;
2323
};
2424

2525
type AtlasLoginReturnValue =
@@ -129,8 +129,7 @@ export function useAtlasProxySignIn(): AtlasLoginReturnValue {
129129
projectId,
130130
csrfToken,
131131
csrfTime,
132-
optInDataExplorerGenAIFeatures:
133-
isOptedIntoDataExplorerGenAIFeatures,
132+
optInGenAIFeatures: isOptedIntoDataExplorerGenAIFeatures,
134133
enableGenAIFeaturesAtlasOrg: genAIFeaturesEnabled,
135134
enableGenAISampleDocumentPassingOnAtlasProject:
136135
groupEnabledFeatureFlags.includes(

0 commit comments

Comments
 (0)