Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
54e8f33
chore(atlas-service): make sure config takes env changes into account
gribnoysup Oct 23, 2025
0b9ae60
chore(web): remove sandbox method from main bundle; remove unnecessar…
gribnoysup Oct 23, 2025
64a1955
chore(generative-ai): use connectionInfo.atlasMetadata to branch url …
gribnoysup Oct 23, 2025
1d9071b
chore(assistant): make sure assistant url is handled dynamically
gribnoysup Oct 23, 2025
6d9006f
chore(e2e): make sure setFeature is consistently executed in web
gribnoysup Oct 23, 2025
da325db
chore(e2e): add setEnv behavior for desktop
gribnoysup Oct 23, 2025
3f4d5cb
chore(e2e): correctly handle CORS in atlas mock backend; add mock for…
gribnoysup Oct 23, 2025
db8be89
chore(e2e): remove unnecessary env checks, consolidate shared env and…
gribnoysup Oct 23, 2025
ae28cf2
chore(e2e): clean up ai tests; re-enable for web
gribnoysup Oct 23, 2025
5c04f65
chore(generative-ai): adjust tests for new method behavior
gribnoysup Oct 23, 2025
555c565
chore(assistant): use a completely fake placeholder url instead of a …
gribnoysup Oct 23, 2025
0334ce8
chore(assistant): adjust unit test
gribnoysup Oct 23, 2025
84adb85
Merge remote-tracking branch 'origin/main' into fix-set-env-set-featu…
gribnoysup Oct 24, 2025
7632d88
Merge branch 'main' into fix-set-env-set-feature-mock-server-e2e-re-e…
gribnoysup Oct 24, 2025
113d620
chore(components, preferences): replace DISABLE_GUIDE_CUES env with p…
gribnoysup Oct 24, 2025
3333565
Merge branch 'main' into fix-set-env-set-feature-mock-server-e2e-re-e…
gribnoysup Oct 27, 2025
8a4d41a
chore(components): reomve disable_guide_cue env from tests
gribnoysup Oct 27, 2025
d4fda21
chore(e2e): consolidate common preferences setting in init
gribnoysup Oct 27, 2025
1bbc5d6
chore(web, e2e): expose preferences fully, log changed value for debu…
gribnoysup Oct 27, 2025
eb3550b
fix(web-sandbox): use useCallback for setPreferencesAccess
gribnoysup Oct 27, 2025
dec7040
chore(web): remove unused dep
gribnoysup Oct 27, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions packages/atlas-service/src/atlas-service.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,9 +29,10 @@ function getAtlasService(
const atlasService = new AtlasService(
authService,
preferences,
createNoopLogger()
createNoopLogger(),
undefined,
ATLAS_CONFIG
);
atlasService['config'] = ATLAS_CONFIG;
return atlasService;
}

Expand Down
11 changes: 7 additions & 4 deletions packages/atlas-service/src/atlas-service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,14 +52,17 @@ function getAutomationAgentClusterId(
}

export class AtlasService {
private config: AtlasServiceConfig;
constructor(
private readonly authService: AtlasAuthService,
private readonly preferences: PreferencesAccess,
private readonly logger: Logger,
private readonly options?: AtlasServiceOptions
) {
this.config = getAtlasConfig(preferences);
private readonly options?: AtlasServiceOptions,
private readonly defaultConfigOverride?: AtlasServiceConfig
) {}
// Config value is dynamic to make sure that process.env overrides are taken
// into account in runtime
get config(): AtlasServiceConfig {
return this.defaultConfigOverride ?? getAtlasConfig(this.preferences);
}
adminApiEndpoint(path?: string): string {
return `${this.config.atlasApiBaseUrl}${normalizePath(path)}`;
Expand Down
1 change: 1 addition & 0 deletions packages/atlas-service/src/util.ts
Original file line number Diff line number Diff line change
Expand Up @@ -255,6 +255,7 @@ export function getAtlasConfig(
const { atlasServiceBackendPreset } = preferences.getPreferences();
const envConfig = {
atlasApiBaseUrl: process.env.COMPASS_ATLAS_SERVICE_UNAUTH_BASE_URL_OVERRIDE,
cloudBaseUrl: process.env.COMPASS_CLOUD_BASE_URL_OVERRIDE,
atlasLogin: {
clientId: process.env.COMPASS_CLIENT_ID_OVERRIDE,
issuer: process.env.COMPASS_OIDC_ISSUER_OVERRIDE,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ import { CompassAssistantDrawer } from './compass-assistant-drawer';
import { createBrokenTransport, createMockChat } from '../test/utils';
import type { AtlasAiService } from '@mongodb-js/compass-generative-ai/provider';
import type { TrackFunction } from '@mongodb-js/compass-telemetry';
import { createLogger } from '@mongodb-js/compass-logging';
import { createNoopLogger } from '@mongodb-js/compass-logging/provider';

function createMockProvider({
mockAtlasService,
Expand Down Expand Up @@ -480,7 +480,7 @@ describe('CompassAssistantProvider', function () {
.stub()
.returns('https://localhost:3000'),
} as unknown as AtlasService,
logger: createLogger('COMPASS-ASSISTANT-TEST'),
logger: createNoopLogger(),
track: track as unknown as TrackFunction,
});
await renderOpenAssistantDrawer({
Expand Down Expand Up @@ -654,47 +654,42 @@ describe('CompassAssistantProvider', function () {
});
});

let sandbox: sinon.SinonSandbox;

beforeEach(function () {
sandbox = sinon.createSandbox();
});

afterEach(function () {
if (sandbox) {
sandbox.reset();
}
});

it('uses the Atlas Service assistantApiEndpoint', async function () {
const mockAtlasService = {
assistantApiEndpoint: sinon
assistantApiEndpoint: sandbox
.stub()
.returns('https://example.com/assistant/api/v1'),
};

const mockAtlasAiService = {
ensureAiFeatureAccess: sinon.stub().callsFake(() => {
return Promise.resolve();
}),
};

const mockAtlasAuthService = {};

const MockedProvider = CompassAssistantProvider.withMockServices({
const chat = createDefaultChat({
originForPrompt: 'foo',
appNameForPrompt: 'bar',
atlasService: mockAtlasService as unknown as AtlasService,
atlasAiService: mockAtlasAiService as unknown as AtlasAiService,
atlasAuthService: mockAtlasAuthService as unknown as AtlasAuthService,
logger: createNoopLogger(),
track: () => undefined,
});

render(
<DrawerContentProvider>
<DrawerAnchor />
<MockedProvider
originForPrompt="mongodb-compass"
appNameForPrompt="MongoDB Compass"
/>
</DrawerContentProvider>,
{
preferences: {
enableAIAssistant: true,
enableGenAIFeatures: true,
enableGenAIFeaturesAtlasOrg: true,
cloudFeatureRolloutAccess: { GEN_AI_COMPASS: true },
},
}
);
const fetchStub = sandbox
.stub(globalThis, 'fetch')
.resolves({ ok: true, headers: [] } as any);

await waitFor(() => {
expect(mockAtlasService.assistantApiEndpoint.calledOnce).to.be.true;
});
await chat.sendMessage({ text: 'hello' });

expect(mockAtlasService.assistantApiEndpoint.calledOnce).to.be.true;
expect(fetchStub.lastCall.args[0]).to.eq(
'https://example.com/assistant/api/v1/responses'
);
});
});
16 changes: 15 additions & 1 deletion packages/compass-assistant/src/compass-assistant-provider.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -334,6 +334,7 @@ export function createDefaultChat({
transport: Chat<AssistantMessage>['transport'];
};
}): Chat<AssistantMessage> {
const initialBaseUrl = 'http://PLACEHOLDER_BASE_URL_TO_BE_REPLACED.invalid';
return new Chat({
transport:
options?.transport ??
Expand All @@ -343,8 +344,21 @@ export function createDefaultChat({
target: appNameForPrompt,
}),
model: createOpenAI({
baseURL: atlasService.assistantApiEndpoint(),
baseURL: initialBaseUrl,
apiKey: '',
fetch(url, init) {
return globalThis.fetch(
// The `baseUrl` can be dynamically changed, but `createOpenAI`
// constructor doesn't allow us to change it after initial call.
// Instead we're going to update it every time the fetch call
// happens
String(url).replace(
initialBaseUrl,
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we just use a static string as the initial base URL? I'd be inclined to do that so that this actually fails loudly if for some reason we still end up reaching out to the original endpoint

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, not a bad idea! I was considering this, but wasn't totally sure. Will update 👍

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done in 555c565

atlasService.assistantApiEndpoint()
),
init
);
},
}).responses('mongodb-chat-latest'),
}),
onError: (err: Error) => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,11 @@ type CompassComponentsProviderProps = {
* Set to disable context menus in the application.
*/
disableContextMenus?: boolean;

/**
* Set to disable all guide cues in the application.
*/
disableGuideCues?: boolean;
} & {
onNextGuideGue?: GuideCueProviderProps['onNext'];
onNextGuideCueGroup?: GuideCueProviderProps['onNextGroup'];
Expand Down Expand Up @@ -128,7 +133,8 @@ export const CompassComponentsProvider = ({
utmMedium,
stackedElementsZIndex,
popoverPortalContainer: _popoverPortalContainer,
disableContextMenus,
disableContextMenus = false,
disableGuideCues = false,
...signalHooksProviderProps
}: CompassComponentsProviderProps) => {
const darkMode = useDarkMode(_darkMode);
Expand Down Expand Up @@ -164,6 +170,7 @@ export const CompassComponentsProvider = ({
utmMedium={utmMedium}
>
<GuideCueProvider
enabled={!disableGuideCues}
onNext={onNextGuideGue}
onNextGroup={onNextGuideCueGroup}
>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -604,10 +604,8 @@ describe('GuideCueService', function () {
});

context('when disabled', function () {
const initialValue = process.env.DISABLE_GUIDE_CUES;
before(function () {
process.env.DISABLE_GUIDE_CUES = 'true';

guideCueService.enabled = false;
guideCueService.addCue({ cueId: '1', isIntersecting: true, step: 1 });
guideCueService.addCue({
cueId: '3',
Expand All @@ -624,7 +622,7 @@ describe('GuideCueService', function () {
});

after(function () {
process.env.DISABLE_GUIDE_CUES = initialValue;
guideCueService.enabled = true;
});

it('does not add a cue', function () {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,12 +38,14 @@ export class GuideCueService extends EventTarget {
private _activeGroupId: GroupName | null = null;
private _activeCue: Cue | null = null;

enabled = true;

constructor(private readonly _storage: GuideCueStorage) {
super();
}

addCue(cue: Omit<Cue, 'isVisited'>) {
if (process.env.DISABLE_GUIDE_CUES === 'true') {
if (!this.enabled) {
return;
}
const cueIndex = this.getCueIndex(cue.cueId, cue.groupId);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,10 +33,16 @@ export type GroupCue = Cue & {
type GuideCueContextValue = {
onNext?: (cue: Cue) => void;
onNextGroup?: (groupCue: GroupCue) => void;
enabled?: boolean;
};
const GuideCueContext = React.createContext<GuideCueContextValue>({});

const GuideCueContext = React.createContext<GuideCueContextValue>({
enabled: true,
});

export const GuideCueProvider: React.FC<GuideCueContextValue> = ({
children,
enabled = true,
...callbacks
}) => {
const callbacksRef = useRef(callbacks);
Expand All @@ -49,9 +55,13 @@ export const GuideCueProvider: React.FC<GuideCueContextValue> = ({
onNextGroup(groupCue: GroupCue) {
callbacksRef.current.onNextGroup?.(groupCue);
},
enabled,
}),
[]
[enabled]
);
useEffect(() => {
guideCueService.enabled = enabled;
}, [enabled]);
return (
<GuideCueContext.Provider value={value}>
{children}
Expand Down Expand Up @@ -253,7 +263,7 @@ export const GuideCue = <T extends HTMLElement>({

return (
<>
{readyToRender && (
{context.enabled && readyToRender && (
<LGGuideCue
{...restOfCueProps}
open={isCueOpen}
Expand Down
35 changes: 28 additions & 7 deletions packages/compass-e2e-tests/helpers/commands/get-feature.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,37 @@
import type { CompassBrowser } from '../compass-browser';
import type { UserPreferences } from 'compass-preferences-model';
import { isTestingWeb } from '../test-runner-context';

export async function getFeature<K extends keyof UserPreferences>(
browser: CompassBrowser,
name: K
): Promise<UserPreferences[K]> {
return await browser.execute(async (_name) => {
return (
// eslint-disable-next-line @typescript-eslint/no-require-imports
(await require('electron').ipcRenderer.invoke('compass:get-preferences'))[
_name
]
return (await getFeatures(browser))[name];
}

export async function getFeatures(
browser: CompassBrowser
): Promise<UserPreferences> {
if (isTestingWeb()) {
// When running in Compass web we cannot use the IPC to read the
// preferences so we use a global function
await browser.waitUntil(async () => {
return await browser.execute(() => {
return (
Symbol.for('@compass-web-sandbox-preferences-access') in globalThis
);
});
});
return await browser.execute(() => {
return (globalThis as any)[
Symbol.for('@compass-web-sandbox-preferences-access')
].getPreferences();
});
}
return await browser.execute(async () => {
// eslint-disable-next-line @typescript-eslint/no-require-imports
return await require('electron').ipcRenderer.invoke(
'compass:get-preferences'
);
}, name);
});
}
36 changes: 23 additions & 13 deletions packages/compass-e2e-tests/helpers/commands/set-env.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,7 @@ import type { CompassBrowser } from '../compass-browser';
import { isTestingWeb } from '../test-runner-context';

/**
* Sets an environment variable override in Compass Web.
* This is only supported in Compass Web tests, not in Compass Desktop.
* Sets an environment variable override in Compass. This works the same way both for Compass desktop and web runtimes
*
* @example
* // Set the Atlas service URL override in a test
Expand All @@ -20,24 +19,35 @@ export async function setEnv(
browser: CompassBrowser,
key: string,
value: string
): Promise<void> {
): Promise<Record<string, string>> {
// In web, use injected function to set the env
if (isTestingWeb()) {
// When running in Compass web we use a global function to set env vars
await browser.execute(
await browser.waitUntil(async () => {
return await browser.execute(() => {
return Symbol.for('@compass-web-sandbox-set-env') in globalThis;
});
});
return await browser.execute(
(_key, _value) => {
const kSandboxSetEnvFn = Symbol.for('@compass-web-sandbox-set-env');
// eslint-disable-next-line @typescript-eslint/no-explicit-any
(globalThis as any)[kSandboxSetEnvFn]?.(_key, _value);
return (globalThis as any)[kSandboxSetEnvFn](_key, _value) as Record<
string,
string
>;
},
key,
value
);
} else {
// In electron, just set the existing global var
return await browser.execute(
(_key, _value) => {
process.env[_key] = _value;
return process.env as Record<string, string>;
},
key,
value
);
return;
}

// When running in Compass desktop, we can't dynamically change env vars
// after the process has started, so we throw an error
throw new Error(
'setEnv is only supported in Compass web. For Compass desktop, set environment variables before starting the app.'
);
}
Loading
Loading