diff --git a/x-pack/packages/kbn-elastic-assistant/impl/assistant/common/components/assistant_settings_management/inline_actions/index.tsx b/x-pack/packages/kbn-elastic-assistant/impl/assistant/common/components/assistant_settings_management/inline_actions/index.tsx index 06e0c8ebcc9777..7a2da0d22fc3eb 100644 --- a/x-pack/packages/kbn-elastic-assistant/impl/assistant/common/components/assistant_settings_management/inline_actions/index.tsx +++ b/x-pack/packages/kbn-elastic-assistant/impl/assistant/common/components/assistant_settings_management/inline_actions/index.tsx @@ -37,6 +37,7 @@ export const useInlineActions = ( actions: [ { name: i18n.EDIT_BUTTON, + 'data-test-subj': 'edit-button', description: i18n.EDIT_BUTTON, icon: 'pencil', type: 'icon', diff --git a/x-pack/packages/kbn-elastic-assistant/impl/knowledge_base/knowledge_base_settings_management/index.test.tsx b/x-pack/packages/kbn-elastic-assistant/impl/knowledge_base/knowledge_base_settings_management/index.test.tsx index 86cc30ea029433..1b1cabd7867396 100644 --- a/x-pack/packages/kbn-elastic-assistant/impl/knowledge_base/knowledge_base_settings_management/index.test.tsx +++ b/x-pack/packages/kbn-elastic-assistant/impl/knowledge_base/knowledge_base_settings_management/index.test.tsx @@ -55,6 +55,7 @@ const mockDataViews = { { name: 'field-2', esTypes: ['text'] }, { name: 'field-3', esTypes: ['semantic_text'] }, ]), + getExistingIndices: jest.fn().mockResolvedValue(['index-2']), } as unknown as DataViewsContract; const queryClient = new QueryClient(); const wrapper = (props: { children: React.ReactNode }) => ( @@ -65,7 +66,22 @@ const wrapper = (props: { children: React.ReactNode }) => ( describe('KnowledgeBaseSettingsManagement', () => { const mockData = [ { id: '1', name: 'Test Entry 1', type: 'document', kbResource: 'user', users: [{ id: 'hi' }] }, - { id: '2', name: 'Test Entry 2', type: 'index', kbResource: 'global', users: [] }, + { + id: '2', + name: 'Test Entry 2', + type: 'index', + kbResource: 'global', + users: [], + index: 'missing-index', + }, + { + id: '3', + name: 'Test Entry 3', + type: 'index', + kbResource: 'private', + users: [{ id: 'fake-user' }], + index: 'index-2', + }, ]; beforeEach(() => { @@ -241,4 +257,24 @@ describe('KnowledgeBaseSettingsManagement', () => { }); expect(screen.queryByTestId('delete-entry-confirmation')).not.toBeInTheDocument(); }); + + it('shows warning icon for index entries with missing indices', async () => { + render(, { + wrapper, + }); + + await waitFor(() => expect(screen.getByTestId('missing-index-icon')).toBeInTheDocument()); + + expect(screen.getAllByTestId('missing-index-icon').length).toEqual(1); + + fireEvent.mouseOver(screen.getByTestId('missing-index-icon')); + + await waitFor(() => screen.getByTestId('missing-index-tooltip')); + + expect( + screen.getByText( + 'The index assigned to this knowledge base entry is unavailable. Check the permissions on the configured index, or that the index has not been deleted. You can update the index to be used for this knowledge entry, or delete the entry entirely.' + ) + ).toBeInTheDocument(); + }); }); diff --git a/x-pack/packages/kbn-elastic-assistant/impl/knowledge_base/knowledge_base_settings_management/index.tsx b/x-pack/packages/kbn-elastic-assistant/impl/knowledge_base/knowledge_base_settings_management/index.tsx index e3a86c62d1222d..3633a935a3beab 100644 --- a/x-pack/packages/kbn-elastic-assistant/impl/knowledge_base/knowledge_base_settings_management/index.tsx +++ b/x-pack/packages/kbn-elastic-assistant/impl/knowledge_base/knowledge_base_settings_management/index.tsx @@ -30,6 +30,7 @@ import { } from '@kbn/elastic-assistant-common'; import { css } from '@emotion/react'; import { DataViewsContract } from '@kbn/data-views-plugin/public'; +import useAsync from 'react-use/lib/useAsync'; import { KnowledgeBaseTour } from '../../tour/knowledge_base'; import { AlertsSettingsManagement } from '../../assistant/settings/alerts_settings/alerts_settings_management'; import { useKnowledgeBaseEntries } from '../../assistant/api/knowledge_base/entries/use_knowledge_base_entries'; @@ -173,10 +174,22 @@ export const KnowledgeBaseSettingsManagement: React.FC = React.memo(({ d toasts, enabled: enableKnowledgeBaseByDefault, }); + + const { value: existingIndices } = useAsync(() => { + const indices: string[] = []; + entries.data.forEach((entry) => { + if (entry.type === 'index') { + indices.push(entry.index); + } + }); + return dataViews.getExistingIndices(indices); + }, [entries.data]); + const { getColumns } = useKnowledgeBaseTable(); const columns = useMemo( () => getColumns({ + existingIndices, isDeleteEnabled: (entry: KnowledgeBaseEntryResponse) => { return ( !isSystemEntry(entry) && (isGlobalEntry(entry) ? hasManageGlobalKnowledgeBase : true) @@ -197,7 +210,7 @@ export const KnowledgeBaseSettingsManagement: React.FC = React.memo(({ d openFlyout(); }, }), - [entries.data, getColumns, hasManageGlobalKnowledgeBase, openFlyout] + [entries.data, existingIndices, getColumns, hasManageGlobalKnowledgeBase, openFlyout] ); // Refresh button diff --git a/x-pack/packages/kbn-elastic-assistant/impl/knowledge_base/knowledge_base_settings_management/index_entry_editor.test.tsx b/x-pack/packages/kbn-elastic-assistant/impl/knowledge_base/knowledge_base_settings_management/index_entry_editor.test.tsx index e4656b10d1d31c..faa4653c9beabd 100644 --- a/x-pack/packages/kbn-elastic-assistant/impl/knowledge_base/knowledge_base_settings_management/index_entry_editor.test.tsx +++ b/x-pack/packages/kbn-elastic-assistant/impl/knowledge_base/knowledge_base_settings_management/index_entry_editor.test.tsx @@ -22,6 +22,7 @@ describe('IndexEntryEditor', () => { { name: 'field-2', esTypes: ['text'] }, { name: 'field-3', esTypes: ['semantic_text'] }, ]), + getExistingIndices: jest.fn().mockResolvedValue(['index-1']), } as unknown as DataViewsContract; const defaultProps = { @@ -147,4 +148,20 @@ describe('IndexEntryEditor', () => { expect(getByRole('combobox', { name: i18n.ENTRY_FIELD_PLACEHOLDER })).toBeDisabled(); }); }); + + it('fetches index options and updates on selection 2', async () => { + (mockDataViews.getExistingIndices as jest.Mock).mockResolvedValue([]); + const { getByText } = render( + + ); + + await waitFor(() => { + expect(mockDataViews.getExistingIndices).toHaveBeenCalled(); + }); + + expect(getByText("Index doesn't exist")).toBeInTheDocument(); + }); }); diff --git a/x-pack/packages/kbn-elastic-assistant/impl/knowledge_base/knowledge_base_settings_management/index_entry_editor.tsx b/x-pack/packages/kbn-elastic-assistant/impl/knowledge_base/knowledge_base_settings_management/index_entry_editor.tsx index 550861bcbffd91..b5e1c278e2ddb3 100644 --- a/x-pack/packages/kbn-elastic-assistant/impl/knowledge_base/knowledge_base_settings_management/index_entry_editor.tsx +++ b/x-pack/packages/kbn-elastic-assistant/impl/knowledge_base/knowledge_base_settings_management/index_entry_editor.tsx @@ -96,6 +96,12 @@ export const IndexEntryEditor: React.FC = React.memo( })); }, [dataViews]); + const { value: isMissingIndex } = useAsync(async () => { + if (!entry?.index?.length) return false; + + return !(await dataViews.getExistingIndices([entry.index])).length; + }, [entry?.index]); + const indexFields = useAsync( async () => dataViews.getFieldsForWildcard({ @@ -251,11 +257,17 @@ export const IndexEntryEditor: React.FC = React.memo( fullWidth /> - + {i18n.MISSING_INDEX_ERROR}} + > { ); }; +const NameColumn = ({ + entry, + existingIndices, +}: { + entry: KnowledgeBaseEntryResponse; + existingIndices?: string[]; +}) => { + let showMissingIndexWarning = false; + if (existingIndices && entry.type === 'index') { + showMissingIndexWarning = !existingIndices.includes(entry.index); + } + return ( + <> + {entry.name} + {showMissingIndexWarning && ( + + + + )} + + ); +}; + export const useKnowledgeBaseTable = () => { const getActions = useInlineActions(); @@ -97,11 +137,13 @@ export const useKnowledgeBaseTable = () => { const getColumns = useCallback( ({ + existingIndices, isDeleteEnabled, isEditEnabled, onDeleteActionClicked, onEditActionClicked, }: { + existingIndices?: string[]; isDeleteEnabled: (entry: KnowledgeBaseEntryResponse) => boolean; isEditEnabled: (entry: KnowledgeBaseEntryResponse) => boolean; onDeleteActionClicked: (entry: KnowledgeBaseEntryResponse) => void; @@ -115,7 +157,9 @@ export const useKnowledgeBaseTable = () => { }, { name: i18n.COLUMN_NAME, - render: ({ name }: KnowledgeBaseEntryResponse) => name, + render: (entry: KnowledgeBaseEntryResponse) => ( + + ), sortable: ({ name }: KnowledgeBaseEntryResponse) => name, width: '30%', },