From 8d3096133043f4e621e7581bb507df03abeda9da Mon Sep 17 00:00:00 2001 From: gagik Date: Mon, 1 Sep 2025 16:07:03 +0200 Subject: [PATCH 1/8] chore(compass-assistant): hide the assistant buttons when AI features are disabled COMPASS-9760 --- .../src/compass-assistant-drawer.tsx | 2 +- .../src/compass-assistant-provider.spec.tsx | 90 +++++++++++++++++++ .../src/compass-assistant-provider.tsx | 43 ++++----- .../connection-status-notifications.tsx | 46 ++++------ .../src/stores/connections-store-redux.ts | 33 ++++--- .../src/components/explain-plan-modal.tsx | 2 +- 6 files changed, 146 insertions(+), 70 deletions(-) diff --git a/packages/compass-assistant/src/compass-assistant-drawer.tsx b/packages/compass-assistant/src/compass-assistant-drawer.tsx index 48bfa2df701..5201f1ba5d3 100644 --- a/packages/compass-assistant/src/compass-assistant-drawer.tsx +++ b/packages/compass-assistant/src/compass-assistant-drawer.tsx @@ -49,7 +49,7 @@ export const CompassAssistantDrawer: React.FunctionComponent<{ 'data-testid': 'assistant-confirm-clear-chat-modal', }); if (confirmed) { - clearChat(); + clearChat?.(); } }, [clearChat]); diff --git a/packages/compass-assistant/src/compass-assistant-provider.spec.tsx b/packages/compass-assistant/src/compass-assistant-provider.spec.tsx index 31c9083827c..6cf2b1579b9 100644 --- a/packages/compass-assistant/src/compass-assistant-provider.spec.tsx +++ b/packages/compass-assistant/src/compass-assistant-provider.spec.tsx @@ -1,6 +1,7 @@ import React from 'react'; import { render, + renderHook, screen, userEvent, waitFor, @@ -10,6 +11,7 @@ import { import { AssistantProvider, CompassAssistantProvider, + useAssistantActions, type AssistantMessage, } from './compass-assistant-provider'; import { expect } from 'chai'; @@ -41,6 +43,94 @@ const TestComponent: React.FunctionComponent<{ ); }; +describe('useAssistantActions', function () { + const createWrapper = (chat: Chat) => { + function TestWrapper({ children }: { children: React.ReactNode }) { + return ( + + {children} + + ); + } + return TestWrapper; + }; + + it('returns empty object when AI features are disabled via isAIFeatureEnabled', function () { + const { result } = renderHook(() => useAssistantActions(), { + wrapper: createWrapper(createMockChat({ messages: [] })), + preferences: { + enableAIAssistant: true, + // These control isAIFeatureEnabled + enableGenAIFeatures: false, + enableGenAIFeaturesAtlasOrg: true, + cloudFeatureRolloutAccess: { GEN_AI_COMPASS: true }, + }, + }); + + expect(result.current).to.deep.equal({}); + }); + + it('returns empty object when enableGenAIFeaturesAtlasOrg is disabled', function () { + const { result } = renderHook(() => useAssistantActions(), { + wrapper: createWrapper(createMockChat({ messages: [] })), + preferences: { + enableAIAssistant: true, + enableGenAIFeatures: true, + enableGenAIFeaturesAtlasOrg: false, + cloudFeatureRolloutAccess: { GEN_AI_COMPASS: true }, + }, + }); + + expect(result.current).to.deep.equal({}); + }); + + it('returns empty object when cloudFeatureRolloutAccess is disabled', function () { + const { result } = renderHook(() => useAssistantActions(), { + wrapper: createWrapper(createMockChat({ messages: [] })), + preferences: { + enableAIAssistant: true, + enableGenAIFeatures: true, + enableGenAIFeaturesAtlasOrg: true, + cloudFeatureRolloutAccess: { GEN_AI_COMPASS: false }, + }, + }); + + expect(result.current).to.deep.equal({}); + }); + + it('returns empty object when enableAIAssistant preference is disabled', function () { + const { result } = renderHook(() => useAssistantActions(), { + wrapper: createWrapper(createMockChat({ messages: [] })), + preferences: { + enableAIAssistant: false, + enableGenAIFeatures: true, + enableGenAIFeaturesAtlasOrg: true, + cloudFeatureRolloutAccess: { GEN_AI_COMPASS: true }, + }, + }); + + expect(result.current).to.deep.equal({}); + }); + + it('returns actions when both AI features and assistant flag are enabled', function () { + const { result } = renderHook(() => useAssistantActions(), { + wrapper: createWrapper(createMockChat({ messages: [] })), + preferences: { + enableAIAssistant: true, + enableGenAIFeatures: true, + enableGenAIFeaturesAtlasOrg: true, + cloudFeatureRolloutAccess: { GEN_AI_COMPASS: true }, + }, + }); + + expect(Object.keys(result.current)).to.have.length.greaterThan(0); + expect(result.current.interpretExplainPlan).to.be.a('function'); + expect(result.current.interpretConnectionError).to.be.a('function'); + expect(result.current.tellMoreAboutInsight).to.be.a('function'); + expect(result.current.clearChat).to.be.a('function'); + }); +}); + describe('AssistantProvider', function () { const mockMessages: AssistantMessage[] = [ { diff --git a/packages/compass-assistant/src/compass-assistant-provider.tsx b/packages/compass-assistant/src/compass-assistant-provider.tsx index 0226e89ca8d..472e8168276 100644 --- a/packages/compass-assistant/src/compass-assistant-provider.tsx +++ b/packages/compass-assistant/src/compass-assistant-provider.tsx @@ -16,7 +16,10 @@ import { type EntryPointMessage, type ProactiveInsightsContext, } from './prompts'; -import { usePreference } from 'compass-preferences-model/provider'; +import { + useIsAIFeatureEnabled, + usePreference, +} from 'compass-preferences-model/provider'; import { createLoggerLocator } from '@mongodb-js/compass-logging/provider'; import type { ConnectionInfo } from '@mongodb-js/connection-info'; import { useTelemetry } from '@mongodb-js/compass-telemetry/provider'; @@ -37,22 +40,22 @@ export const AssistantContext = createContext( ); type AssistantActionsContextType = { - interpretExplainPlan: ({ + interpretExplainPlan?: ({ namespace, explainPlan, }: { namespace: string; explainPlan: string; }) => void; - interpretConnectionError: ({ + interpretConnectionError?: ({ connectionInfo, error, }: { connectionInfo: ConnectionInfo; error: Error; }) => void; - clearChat: () => void; - tellMoreAboutInsight: (context: ProactiveInsightsContext) => void; + clearChat?: () => void; + tellMoreAboutInsight?: (context: ProactiveInsightsContext) => void; }; export const AssistantActionsContext = createContext({ @@ -62,29 +65,21 @@ export const AssistantActionsContext = clearChat: () => {}, }); -export function useAssistantActions(): AssistantActionsContextType & { - isAssistantEnabled: boolean; -} { - const isAssistantEnabled = usePreference('enableAIAssistant'); +export function useAssistantActions(): AssistantActionsContextType { + const isAIFeatureEnabled = useIsAIFeatureEnabled(); + const isAssistantFlagEnabled = usePreference('enableAIAssistant'); + const actions = useContext(AssistantActionsContext); + if (!isAIFeatureEnabled || !isAssistantFlagEnabled) { + return {}; + } - return { - ...useContext(AssistantActionsContext), - isAssistantEnabled, - }; + return actions; } export const compassAssistantServiceLocator = createServiceLocator(function () { - const { isAssistantEnabled, ...actions } = useAssistantActions(); + const actions = useAssistantActions(); - const assistantEnabledRef = useRef(isAssistantEnabled); - assistantEnabledRef.current = isAssistantEnabled; - - return { - ...actions, - getIsAssistantEnabled() { - return assistantEnabledRef.current; - }, - }; + return actions; }, 'compassAssistantLocator'); export type CompassAssistantService = ReturnType< @@ -160,7 +155,7 @@ export const CompassAssistantProvider = registerCompassPlugin( transport: new DocsProviderTransport({ baseUrl: atlasService.assistantApiEndpoint(), }), - onError: (err) => { + onError: (err: Error) => { logger.log.error( logger.mongoLogId(1_001_000_370), 'Assistant', diff --git a/packages/compass-connections/src/components/connection-status-notifications.tsx b/packages/compass-connections/src/components/connection-status-notifications.tsx index 51669ce8a06..fe3d47c54c6 100644 --- a/packages/compass-connections/src/components/connection-status-notifications.tsx +++ b/packages/compass-connections/src/components/connection-status-notifications.tsx @@ -41,10 +41,8 @@ export function getConnectingStatusText(connectionInfo: ConnectionInfo) { type ConnectionErrorToastBodyProps = { info?: ConnectionInfo | null; error: Error; - showReviewButton: boolean; - showDebugButton: boolean; - onReview: () => void; - onDebug: () => void; + onReview?: () => void; + onDebug?: () => void; }; const connectionErrorToastStyles = css({ @@ -94,8 +92,6 @@ const debugActionStyles = css({ function ConnectionErrorToastBody({ info, error, - showReviewButton, - showDebugButton, onReview, onDebug, }: ConnectionErrorToastBodyProps): React.ReactElement { @@ -111,7 +107,7 @@ function ConnectionErrorToastBody({ {error.message} - {info && showReviewButton && ( + {info && onReview && (