[8.16] [Security GenAI] When indices referenced in KB index entries are deleted from OUTSIDE the AI Assistant KB UI, there is not indication to the user (#197156) (#197722) (#197894)

# Backport

This will backport the following commits from `main` to `8.16`:
- [[Security GenAI] When indices referenced in KB index entries are
deleted from OUTSIDE the AI Assistant KB UI, there is not indication to
the user (#197156)
(#197722)](https://github.com/elastic/kibana/pull/197722)

<!--- Backport version: 9.4.3 -->

### Questions ?
Please refer to the [Backport tool
documentation](https://github.com/sqren/backport)

<!--BACKPORT [{"author":{"name":"Ievgen
Sorokopud","email":"ievgen.sorokopud@elastic.co"},"sourceCommit":{"committedDate":"2024-10-25T19:17:57Z","message":"[Security
GenAI] When indices referenced in KB index entries are deleted from
OUTSIDE the AI Assistant KB UI, there is not indication to the user
(#197156) (#197722)\n\n## Summary\r\n\r\nBug
https://github.com/elastic/kibana/issues/197156\r\n\r\nThis is a UI part
of the bug that warns a user about missing indices\r\nused in knowledge
base entries.\r\n\r\n### To test\r\n\r\n1. Add an index entry that uses
existing index\r\n2. Remove that index\r\n3. Go back to knowledge base
entries page\r\n4. You should see warning icon next to the name of the
index entry which\r\nuses removed index. Also, when you edit that entry
you will see `Index\r\ndoesn't exist` error next to the `Index` field in
the flyout\r\n\r\n<img width=\"1458\" alt=\"Screenshot 2024-10-24 at 19
54
36\"\r\nsrc=\"https://github.com/user-attachments/assets/7d4468f9-fada-4416-9480-99bfca3de220\">\r\n\r\n<img
width=\"615\" alt=\"Screenshot 2024-10-24 at 19 54
52\"\r\nsrc=\"https://github.com/user-attachments/assets/fd9bbe80-0a3c-40b8-909a-93f8082e69eb\">\r\n\r\n###
Checklist\r\n\r\nDelete any items that are not applicable to this
PR.\r\n\r\n- [x] Any text added follows [EUI's
writing\r\nguidelines](https://elastic.github.io/eui/#/guidelines/writing),
uses\r\nsentence case text and includes
[i18n\r\nsupport](https://github.com/elastic/kibana/blob/main/packages/kbn-i18n/README.md)\r\n-
[x] [Unit or
functional\r\ntests](https://www.elastic.co/guide/en/kibana/master/development-tests.html)\r\nwere
updated or added to match the most common
scenarios","sha":"a1d755a675b05269f167d160f4c0c28a83d5e4d5","branchLabelMapping":{"^v9.0.0$":"main","^v8.17.0$":"8.x","^v(\\d+).(\\d+).\\d+$":"$1.$2"}},"sourcePullRequest":{"labels":["release_note:skip","v9.0.0","Team:
SecuritySolution","backport:prev-minor","Team:Security Generative
AI","v8.16.0"],"title":"[Security GenAI] When indices referenced in KB
index entries are deleted from OUTSIDE the AI Assistant KB UI, there is
not indication to the user
(#197156)","number":197722,"url":"https://github.com/elastic/kibana/pull/197722","mergeCommit":{"message":"[Security
GenAI] When indices referenced in KB index entries are deleted from
OUTSIDE the AI Assistant KB UI, there is not indication to the user
(#197156) (#197722)\n\n## Summary\r\n\r\nBug
https://github.com/elastic/kibana/issues/197156\r\n\r\nThis is a UI part
of the bug that warns a user about missing indices\r\nused in knowledge
base entries.\r\n\r\n### To test\r\n\r\n1. Add an index entry that uses
existing index\r\n2. Remove that index\r\n3. Go back to knowledge base
entries page\r\n4. You should see warning icon next to the name of the
index entry which\r\nuses removed index. Also, when you edit that entry
you will see `Index\r\ndoesn't exist` error next to the `Index` field in
the flyout\r\n\r\n<img width=\"1458\" alt=\"Screenshot 2024-10-24 at 19
54
36\"\r\nsrc=\"https://github.com/user-attachments/assets/7d4468f9-fada-4416-9480-99bfca3de220\">\r\n\r\n<img
width=\"615\" alt=\"Screenshot 2024-10-24 at 19 54
52\"\r\nsrc=\"https://github.com/user-attachments/assets/fd9bbe80-0a3c-40b8-909a-93f8082e69eb\">\r\n\r\n###
Checklist\r\n\r\nDelete any items that are not applicable to this
PR.\r\n\r\n- [x] Any text added follows [EUI's
writing\r\nguidelines](https://elastic.github.io/eui/#/guidelines/writing),
uses\r\nsentence case text and includes
[i18n\r\nsupport](https://github.com/elastic/kibana/blob/main/packages/kbn-i18n/README.md)\r\n-
[x] [Unit or
functional\r\ntests](https://www.elastic.co/guide/en/kibana/master/development-tests.html)\r\nwere
updated or added to match the most common
scenarios","sha":"a1d755a675b05269f167d160f4c0c28a83d5e4d5"}},"sourceBranch":"main","suggestedTargetBranches":["8.16"],"targetPullRequestStates":[{"branch":"main","label":"v9.0.0","branchLabelMappingKey":"^v9.0.0$","isSourceBranch":true,"state":"MERGED","url":"https://github.com/elastic/kibana/pull/197722","number":197722,"mergeCommit":{"message":"[Security
GenAI] When indices referenced in KB index entries are deleted from
OUTSIDE the AI Assistant KB UI, there is not indication to the user
(#197156) (#197722)\n\n## Summary\r\n\r\nBug
https://github.com/elastic/kibana/issues/197156\r\n\r\nThis is a UI part
of the bug that warns a user about missing indices\r\nused in knowledge
base entries.\r\n\r\n### To test\r\n\r\n1. Add an index entry that uses
existing index\r\n2. Remove that index\r\n3. Go back to knowledge base
entries page\r\n4. You should see warning icon next to the name of the
index entry which\r\nuses removed index. Also, when you edit that entry
you will see `Index\r\ndoesn't exist` error next to the `Index` field in
the flyout\r\n\r\n<img width=\"1458\" alt=\"Screenshot 2024-10-24 at 19
54
36\"\r\nsrc=\"https://github.com/user-attachments/assets/7d4468f9-fada-4416-9480-99bfca3de220\">\r\n\r\n<img
width=\"615\" alt=\"Screenshot 2024-10-24 at 19 54
52\"\r\nsrc=\"https://github.com/user-attachments/assets/fd9bbe80-0a3c-40b8-909a-93f8082e69eb\">\r\n\r\n###
Checklist\r\n\r\nDelete any items that are not applicable to this
PR.\r\n\r\n- [x] Any text added follows [EUI's
writing\r\nguidelines](https://elastic.github.io/eui/#/guidelines/writing),
uses\r\nsentence case text and includes
[i18n\r\nsupport](https://github.com/elastic/kibana/blob/main/packages/kbn-i18n/README.md)\r\n-
[x] [Unit or
functional\r\ntests](https://www.elastic.co/guide/en/kibana/master/development-tests.html)\r\nwere
updated or added to match the most common
scenarios","sha":"a1d755a675b05269f167d160f4c0c28a83d5e4d5"}},{"branch":"8.16","label":"v8.16.0","branchLabelMappingKey":"^v(\\d+).(\\d+).\\d+$","isSourceBranch":false,"state":"NOT_CREATED"}]}]
BACKPORT-->

Co-authored-by: Ievgen Sorokopud <ievgen.sorokopud@elastic.co>
This commit is contained in:
Kibana Machine 2024-10-26 07:58:22 +11:00 committed by GitHub
parent 95635d3244
commit b33a0c4a5a
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
7 changed files with 143 additions and 5 deletions

View file

@ -37,6 +37,7 @@ export const useInlineActions = <T extends { isDefault?: boolean | undefined }>(
actions: [
{
name: i18n.EDIT_BUTTON,
'data-test-subj': 'edit-button',
description: i18n.EDIT_BUTTON,
icon: 'pencil',
type: 'icon',

View file

@ -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(<KnowledgeBaseSettingsManagement dataViews={mockDataViews} />, {
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();
});
});

View file

@ -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<Params> = 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<Params> = React.memo(({ d
openFlyout();
},
}),
[entries.data, getColumns, hasManageGlobalKnowledgeBase, openFlyout]
[entries.data, existingIndices, getColumns, hasManageGlobalKnowledgeBase, openFlyout]
);
// Refresh button

View file

@ -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(
<IndexEntryEditor
{...defaultProps}
entry={{ ...defaultProps.entry, index: 'missing-index' }}
/>
);
await waitFor(() => {
expect(mockDataViews.getExistingIndices).toHaveBeenCalled();
});
expect(getByText("Index doesn't exist")).toBeInTheDocument();
});
});

View file

@ -96,6 +96,12 @@ export const IndexEntryEditor: React.FC<Props> = 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<Props> = React.memo(
fullWidth
/>
</EuiFormRow>
<EuiFormRow label={i18n.ENTRY_INDEX_NAME_INPUT_LABEL} fullWidth>
<EuiFormRow
label={i18n.ENTRY_INDEX_NAME_INPUT_LABEL}
fullWidth
isInvalid={isMissingIndex}
error={isMissingIndex && <>{i18n.MISSING_INDEX_ERROR}</>}
>
<EuiComboBox
data-test-subj="index-combobox"
aria-label={i18n.ENTRY_INDEX_NAME_INPUT_LABEL}
isClearable={true}
isInvalid={isMissingIndex}
singleSelection={{ asPlainText: true }}
onCreateOption={onCreateIndexOption}
fullWidth

View file

@ -335,3 +335,18 @@ export const PRIVATE = i18n.translate(
defaultMessage: 'Private',
}
);
export const MISSING_INDEX_ERROR = i18n.translate(
'xpack.elasticAssistant.assistant.settings.knowledgeBaseSettingsManagement.missingIndexError',
{
defaultMessage: `Index doesn't exist`,
}
);
export const MISSING_INDEX_TOOLTIP_CONTENT = i18n.translate(
'xpack.elasticAssistant.assistant.settings.knowledgeBaseSettingsManagement.missingIndexTootipContent',
{
defaultMessage:
'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.',
}
);

View file

@ -5,7 +5,14 @@
* 2.0.
*/
import { EuiAvatar, EuiBadge, EuiBasicTableColumn, EuiIcon, EuiText } from '@elastic/eui';
import {
EuiAvatar,
EuiBadge,
EuiBasicTableColumn,
EuiIcon,
EuiText,
EuiToolTip,
} from '@elastic/eui';
import { css } from '@emotion/react';
import React, { useCallback, useMemo } from 'react';
import { FormattedDate } from '@kbn/i18n-react';
@ -77,6 +84,39 @@ const AuthorColumn = ({ entry }: { entry: KnowledgeBaseEntryResponse }) => {
);
};
const NameColumn = ({
entry,
existingIndices,
}: {
entry: KnowledgeBaseEntryResponse;
existingIndices?: string[];
}) => {
let showMissingIndexWarning = false;
if (existingIndices && entry.type === 'index') {
showMissingIndexWarning = !existingIndices.includes(entry.index);
}
return (
<>
<EuiText size={'s'}>{entry.name}</EuiText>
{showMissingIndexWarning && (
<EuiToolTip
data-test-subj="missing-index-tooltip"
content={i18n.MISSING_INDEX_TOOLTIP_CONTENT}
>
<EuiIcon
data-test-subj="missing-index-icon"
type="warning"
color="danger"
css={css`
margin-left: 10px;
`}
/>
</EuiToolTip>
)}
</>
);
};
export const useKnowledgeBaseTable = () => {
const getActions = useInlineActions<KnowledgeBaseEntryResponse & { isDefault?: undefined }>();
@ -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) => (
<NameColumn entry={entry} existingIndices={existingIndices} />
),
sortable: ({ name }: KnowledgeBaseEntryResponse) => name,
width: '30%',
},