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 06e0c8ebcc97..7a2da0d22fc3 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 86cc30ea0294..1b1cabd78673 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 e3a86c62d122..3633a935a3be 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 e4656b10d1d3..faa4653c9bea 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 550861bcbffd..b5e1c278e2dd 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%', },