diff --git a/common/constants/llm.ts b/common/constants/llm.ts
index 3216967c..6e21fd94 100644
--- a/common/constants/llm.ts
+++ b/common/constants/llm.ts
@@ -16,9 +16,12 @@ export const ASSISTANT_API = {
ABORT_AGENT_EXECUTION: `${API_BASE}/abort`,
REGENERATE: `${API_BASE}/regenerate`,
TRACE: `${API_BASE}/trace`,
+ ACCOUNT: `${API_BASE}/account`,
} as const;
export const NOTEBOOK_API = {
CREATE_NOTEBOOK: `${NOTEBOOK_PREFIX}/note`,
SET_PARAGRAPH: `${NOTEBOOK_PREFIX}/set_paragraphs/`,
};
+
+export const DEFAULT_USER_NAME = 'User';
diff --git a/opensearch_dashboards.json b/opensearch_dashboards.json
index f286b75a..1b5cbb9d 100644
--- a/opensearch_dashboards.json
+++ b/opensearch_dashboards.json
@@ -11,7 +11,6 @@
"opensearchDashboardsUtils"
],
"optionalPlugins": [
- "securityDashboards",
"dataSource",
"dataSourceManagement"
],
diff --git a/public/chat_header_button.test.tsx b/public/chat_header_button.test.tsx
index b5aa5254..f6ebae40 100644
--- a/public/chat_header_button.test.tsx
+++ b/public/chat_header_button.test.tsx
@@ -92,11 +92,10 @@ describe('', () => {
render(
);
@@ -142,11 +141,10 @@ describe('', () => {
render(
);
screen.getByLabelText('chat input').focus();
@@ -166,11 +164,10 @@ describe('', () => {
render(
);
expect(screen.getByLabelText('chat input')).not.toHaveFocus();
@@ -183,29 +180,14 @@ describe('', () => {
expect(screen.getByLabelText('chat input')).toHaveFocus();
});
- it('should disable chat input when no access', () => {
- render(
-
- );
- expect(screen.getByLabelText('chat input')).toBeDisabled();
- });
-
it('should not focus on chat input when no access and pressing global shortcut', () => {
render(
);
expect(screen.getByLabelText('chat input')).not.toHaveFocus();
diff --git a/public/chat_header_button.tsx b/public/chat_header_button.tsx
index 2f83e388..1c8240de 100644
--- a/public/chat_header_button.tsx
+++ b/public/chat_header_button.tsx
@@ -27,7 +27,6 @@ import { MountPointPortal } from '../../../src/plugins/opensearch_dashboards_rea
interface HeaderChatButtonProps {
application: ApplicationStart;
- userHasAccess: boolean;
messageRenderers: Record;
actionExecutors: Record;
assistantActions: AssistantActions;
@@ -74,7 +73,6 @@ export const HeaderChatButton = (props: HeaderChatButtonProps) => {
flyoutFullScreen,
setFlyoutVisible,
setFlyoutComponent,
- userHasAccess: props.userHasAccess,
messageRenderers: props.messageRenderers,
actionExecutors: props.actionExecutors,
currentAccount: props.currentAccount,
@@ -92,7 +90,6 @@ export const HeaderChatButton = (props: HeaderChatButtonProps) => {
flyoutFullScreen,
selectedTabId,
preSelectedTabId,
- props.userHasAccess,
props.messageRenderers,
props.actionExecutors,
props.currentAccount,
@@ -157,9 +154,6 @@ export const HeaderChatButton = (props: HeaderChatButtonProps) => {
}, []);
useEffect(() => {
- if (!props.userHasAccess) {
- return;
- }
const onGlobalMouseUp = (e: KeyboardEvent) => {
if (e.ctrlKey && e.key === '/') {
inputRef.current?.focus();
@@ -170,7 +164,7 @@ export const HeaderChatButton = (props: HeaderChatButtonProps) => {
return () => {
document.removeEventListener('keydown', onGlobalMouseUp);
};
- }, [props.userHasAccess]);
+ }, []);
useEffect(() => {
const handleSuggestion = (event: { suggestion: string }) => {
@@ -235,7 +229,6 @@ export const HeaderChatButton = (props: HeaderChatButtonProps) => {
)}
}
- disabled={!props.userHasAccess}
/>
diff --git a/public/components/__tests__/chat_window_header_title.test.tsx b/public/components/__tests__/chat_window_header_title.test.tsx
index 5c87cd55..ec3ece97 100644
--- a/public/components/__tests__/chat_window_header_title.test.tsx
+++ b/public/components/__tests__/chat_window_header_title.test.tsx
@@ -22,7 +22,11 @@ import { DataSourceServiceMock } from '../../services/data_source_service.mock';
const setup = ({
messages = [],
...rest
-}: { messages?: IMessage[]; conversationId?: string | undefined } = {}) => {
+}: {
+ messages?: IMessage[];
+ conversationId?: string | undefined;
+ dataSource?: DataSourceServiceMock;
+} = {}) => {
const useCoreMock = {
services: {
...coreMock.createStart(),
@@ -38,7 +42,7 @@ const setup = ({
}),
reload: jest.fn(),
},
- dataSource: new DataSourceServiceMock(),
+ dataSource: rest.dataSource ?? new DataSourceServiceMock(false),
},
};
useCoreMock.services.http.put.mockImplementation(() => Promise.resolve());
@@ -170,6 +174,19 @@ describe('', () => {
});
});
+ it('should hide save to notebook button when MDS enabled', async () => {
+ const { renderResult } = setup({
+ messages: [{ type: 'input', contentType: 'text', content: 'foo' }],
+ dataSource: new DataSourceServiceMock(true),
+ });
+
+ fireEvent.click(renderResult.getByLabelText('toggle chat context menu'));
+
+ expect(
+ renderResult.queryByRole('button', { name: 'Save to notebook' })
+ ).not.toBeInTheDocument();
+ });
+
it('should disable "Save to notebook" button when message does not include input', async () => {
const { renderResult } = setup({
messages: [{ type: 'output', content: 'bar', contentType: 'markdown' }],
diff --git a/public/components/chat_window_header_title.tsx b/public/components/chat_window_header_title.tsx
index b8c2523d..316f4bf8 100644
--- a/public/components/chat_window_header_title.tsx
+++ b/public/components/chat_window_header_title.tsx
@@ -12,7 +12,7 @@ import {
EuiButtonIcon,
EuiToolTip,
} from '@elastic/eui';
-import React, { useCallback, useState } from 'react';
+import React, { useCallback, useMemo, useState } from 'react';
import { useChatContext } from '../contexts';
import { useChatActions, useChatState, useSaveChat } from '../hooks';
import { NotebookNameModal } from './notebook/notebook_name_modal';
@@ -57,6 +57,11 @@ export const ChatWindowHeaderTitle = React.memo(() => {
[chatContext, core.services.conversations]
);
+ const displayNotebookFeature = useMemo(() => {
+ // Notebook dashboard API doesn't support MDS for now, so we hide saving to notebook feature when MDS enabled.
+ return !core.services.dataSource.isMDSEnabled();
+ }, [core.services.dataSource.isMDSEnabled]);
+
const handleSaveNotebookModalClose = () => {
setSaveNotebookModalOpen(false);
};
@@ -87,17 +92,19 @@ export const ChatWindowHeaderTitle = React.memo(() => {
>
New conversation
,
- {
- closePopover();
- setSaveNotebookModalOpen(true);
- }}
- // User only can save conversation when he send a message at least.
- disabled={chatState.messages.every((item) => item.type !== 'input')}
- >
- Save to notebook
- ,
+ displayNotebookFeature && (
+ {
+ closePopover();
+ setSaveNotebookModalOpen(true);
+ }}
+ // User only can save conversation when he send a message at least.
+ disabled={chatState.messages.every((item) => item.type !== 'input')}
+ >
+ Save to notebook
+
+ ),
];
return (
diff --git a/public/contexts/__tests__/chat_context.test.tsx b/public/contexts/__tests__/chat_context.test.tsx
index 2500c5ca..696b377b 100644
--- a/public/contexts/__tests__/chat_context.test.tsx
+++ b/public/contexts/__tests__/chat_context.test.tsx
@@ -17,10 +17,9 @@ describe('useChatContext', () => {
flyoutFullScreen: true,
setFlyoutVisible: jest.fn(),
setFlyoutComponent: jest.fn(),
- userHasAccess: true,
messageRenderers: {},
actionExecutors: {},
- currentAccount: { username: 'foo', tenant: '' },
+ currentAccount: { username: 'foo' },
setTitle: jest.fn(),
setInteractionId: jest.fn(),
};
diff --git a/public/contexts/chat_context.tsx b/public/contexts/chat_context.tsx
index f7bc516c..747f6484 100644
--- a/public/contexts/chat_context.tsx
+++ b/public/contexts/chat_context.tsx
@@ -18,7 +18,6 @@ export interface IChatContext {
flyoutFullScreen: boolean;
setFlyoutVisible: React.Dispatch>;
setFlyoutComponent: React.Dispatch>;
- userHasAccess: boolean;
messageRenderers: Record;
actionExecutors: Record;
currentAccount?: UserAccount;
diff --git a/public/hooks/use_chat_actions.test.tsx b/public/hooks/use_chat_actions.test.tsx
index 6418ae31..26ca0096 100644
--- a/public/hooks/use_chat_actions.test.tsx
+++ b/public/hooks/use_chat_actions.test.tsx
@@ -76,11 +76,9 @@ describe('useChatActions hook', () => {
setInteractionId: setInteractionIdMock,
flyoutVisible: false,
flyoutFullScreen: false,
- userHasAccess: false,
messageRenderers: {},
currentAccount: {
username: '',
- tenant: '',
},
};
diff --git a/public/plugin.tsx b/public/plugin.tsx
index a42449e5..8e5c1151 100644
--- a/public/plugin.tsx
+++ b/public/plugin.tsx
@@ -32,6 +32,7 @@ import {
} from './services';
import { ConfigSchema } from '../common/types/config';
import { DataSourceService } from './services/data_source_service';
+import { ASSISTANT_API, DEFAULT_USER_NAME } from '../common/constants/llm';
export const [getCoreStart, setCoreStart] = createGetterSetter('CoreStart');
@@ -46,7 +47,7 @@ export const IncontextInsightComponent: React.FC<{ props: any }> = (props) => (
);
interface UserAccountResponse {
- data: { roles: string[]; user_name: string; user_requested_tenant?: string };
+ user_name: string;
}
export class AssistantPlugin
@@ -76,27 +77,15 @@ export class AssistantPlugin
const actionExecutors: Record = {};
const assistantActions: AssistantActions = {} as AssistantActions;
/**
- * Returns {@link UserAccountResponse}. Provides default roles and user
- * name if security plugin call fails.
+ * Returns {@link UserAccountResponse}. Provides user name.
*/
- const getAccount: () => Promise = (() => {
- let account: UserAccountResponse;
- return async () => {
- if (setupDeps.securityDashboards === undefined)
- return { data: { roles: ['all_access'], user_name: 'dashboards_user' } };
- if (account === undefined) {
- account = await core.http
- .get('/api/v1/configuration/account')
- .catch((e) => {
- console.error(`Failed to request user account information: ${String(e.body || e)}`);
- return { data: { roles: [], user_name: '' } };
- });
- }
- return account;
- };
- })();
- const checkAccess = (account: Awaited>) =>
- account.data.roles.some((role) => ['all_access', 'assistant_user'].includes(role));
+ const getAccount: () => Promise = async () => {
+ const account = await core.http.get(ASSISTANT_API.ACCOUNT).catch((e) => {
+ console.error(`Failed to request user account information: ${String(e.body || e)}`);
+ return { user_name: DEFAULT_USER_NAME };
+ });
+ return account;
+ };
const dataSourceSetupResult = this.dataSourceService.setup({
uiSettings: core.uiSettings,
@@ -116,8 +105,7 @@ export class AssistantPlugin
dataSource: this.dataSourceService,
});
const account = await getAccount();
- const username = account.data.user_name;
- const tenant = account.data.user_requested_tenant ?? '';
+ const username = account.user_name;
this.incontextInsightRegistry?.setIsEnabled(this.config.incontextInsight.enabled);
coreStart.chrome.navControls.registerRight({
@@ -126,11 +114,10 @@ export class AssistantPlugin
),
@@ -152,7 +139,6 @@ export class AssistantPlugin
actionExecutors[actionType] = execute;
},
chatEnabled: () => this.config.chat.enabled,
- userHasAccess: async () => await getAccount().then(checkAccess),
assistantActions,
registerIncontextInsight: this.incontextInsightRegistry.register.bind(
this.incontextInsightRegistry
diff --git a/public/services/data_source_service.mock.ts b/public/services/data_source_service.mock.ts
index 40fc2018..0ca0bea1 100644
--- a/public/services/data_source_service.mock.ts
+++ b/public/services/data_source_service.mock.ts
@@ -4,12 +4,23 @@
*/
export class DataSourceServiceMock {
- constructor() {}
+ private _isMDSEnabled = true;
+ constructor(isMDSEnabled?: boolean) {
+ this._isMDSEnabled = isMDSEnabled ?? true;
+ }
getDataSourceQuery() {
+ const result = this._isMDSEnabled
+ ? {
+ dataSourceId: '',
+ }
+ : {};
return new Promise((resolve) => {
- resolve({ dataSourceId: '' });
+ resolve(result);
});
- // return { dataSourceId: '' };
+ }
+
+ isMDSEnabled() {
+ return this._isMDSEnabled;
}
}
diff --git a/public/tabs/chat/chat_page.tsx b/public/tabs/chat/chat_page.tsx
index 97da71db..b74f86cd 100644
--- a/public/tabs/chat/chat_page.tsx
+++ b/public/tabs/chat/chat_page.tsx
@@ -60,7 +60,7 @@ export const ChatPage: React.FC = (props) => {
diff --git a/public/tabs/chat/chat_page_content.test.tsx b/public/tabs/chat/chat_page_content.test.tsx
index 67051717..784383e7 100644
--- a/public/tabs/chat/chat_page_content.test.tsx
+++ b/public/tabs/chat/chat_page_content.test.tsx
@@ -45,7 +45,6 @@ describe('', () => {
},
currentAccount: {
username: 'test_user',
- tenant: 'private',
},
});
diff --git a/public/types.ts b/public/types.ts
index 9f92ab1a..ca6505b2 100644
--- a/public/types.ts
+++ b/public/types.ts
@@ -34,7 +34,6 @@ export interface AssistantPluginStartDependencies {
export interface AssistantPluginSetupDependencies {
embeddable: EmbeddableSetup;
- securityDashboards?: {};
dataSourceManagement?: DataSourceManagementPluginSetup;
}
@@ -46,10 +45,6 @@ export interface AssistantSetup {
* Returns true if chat UI is enabled.
*/
chatEnabled: () => boolean;
- /**
- * Returns true if current user has permission to use assistant features.
- */
- userHasAccess: () => Promise;
assistantActions: Omit;
registerIncontextInsight: IncontextInsightRegistry['register'];
renderIncontextInsight: (component: React.ReactNode) => React.ReactNode;
@@ -61,7 +56,6 @@ export interface AssistantStart {
export interface UserAccount {
username: string;
- tenant: string;
}
export interface ChatConfig {
diff --git a/server/plugin.ts b/server/plugin.ts
index 26b86e17..5918864a 100644
--- a/server/plugin.ts
+++ b/server/plugin.ts
@@ -12,10 +12,10 @@ import {
Plugin,
PluginInitializerContext,
} from '../../../src/core/server';
-import { setupRoutes } from './routes/index';
import { AssistantPluginSetup, AssistantPluginStart, MessageParser } from './types';
import { BasicInputOutputParser } from './parsers/basic_input_output_parser';
import { VisualizationCardParser } from './parsers/visualization_card_parser';
+import { registerChatRoutes } from './routes/chat_routes';
export class AssistantPlugin implements Plugin {
private readonly logger: Logger;
@@ -42,8 +42,9 @@ export class AssistantPlugin implements Plugin ({
diff --git a/server/routes/chat_routes.ts b/server/routes/chat_routes.ts
index e0809be5..dd985179 100644
--- a/server/routes/chat_routes.ts
+++ b/server/routes/chat_routes.ts
@@ -12,7 +12,7 @@ import {
IRouter,
RequestHandlerContext,
} from '../../../../src/core/server';
-import { ASSISTANT_API } from '../../common/constants/llm';
+import { ASSISTANT_API, DEFAULT_USER_NAME } from '../../common/constants/llm';
import { OllyChatService } from '../services/chat/olly_chat_service';
import { AgentFrameworkStorageService } from '../services/storage/agent_framework_storage_service';
import { RoutesOptions } from '../types';
@@ -152,6 +152,11 @@ const feedbackRoute = {
},
};
+const accountRoute = {
+ path: `${ASSISTANT_API.ACCOUNT}`,
+ validate: {},
+};
+
export function registerChatRoutes(router: IRouter, routeOptions: RoutesOptions) {
const createStorageService = async (context: RequestHandlerContext, dataSourceId?: string) =>
new AgentFrameworkStorageService(
@@ -430,4 +435,31 @@ export function registerChatRoutes(router: IRouter, routeOptions: RoutesOptions)
}
}
);
+
+ router.get(
+ accountRoute,
+ async (
+ context,
+ request,
+ response
+ ): Promise> => {
+ try {
+ const auth = routeOptions.auth.get<{
+ authInfo?: {
+ user_name?: string;
+ };
+ }>(request);
+ return response.ok({
+ body: {
+ user_name: auth?.state?.authInfo?.user_name ?? DEFAULT_USER_NAME,
+ },
+ });
+ } catch (error) {
+ context.assistant_plugin.logger.error(error);
+ return response.ok({
+ body: { user_name: DEFAULT_USER_NAME },
+ });
+ }
+ }
+ );
}
diff --git a/server/routes/index.ts b/server/routes/index.ts
deleted file mode 100644
index 6fc930e0..00000000
--- a/server/routes/index.ts
+++ /dev/null
@@ -1,12 +0,0 @@
-/*
- * Copyright OpenSearch Contributors
- * SPDX-License-Identifier: Apache-2.0
- */
-
-import { RoutesOptions } from '../types';
-import { IRouter } from '../../../../src/core/server';
-import { registerChatRoutes } from './chat_routes';
-
-export function setupRoutes(router: IRouter, routeOptions: RoutesOptions) {
- registerChatRoutes(router, routeOptions);
-}
diff --git a/server/types.ts b/server/types.ts
index b2067477..a47b42bd 100644
--- a/server/types.ts
+++ b/server/types.ts
@@ -4,7 +4,7 @@
*/
import { IMessage, Interaction } from '../common/types/chat_saved_object_attributes';
-import { Logger } from '../../../src/core/server';
+import { Logger, HttpAuth } from '../../../src/core/server';
export interface AssistantPluginSetup {
registerMessageParser: (message: MessageParser) => void;
@@ -40,6 +40,7 @@ export interface MessageParser {
export interface RoutesOptions {
messageParsers: MessageParser[];
+ auth: HttpAuth;
}
declare module '../../../src/core/server' {