mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 01:38:56 -04:00
[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:
parent
95635d3244
commit
b33a0c4a5a
7 changed files with 143 additions and 5 deletions
|
@ -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',
|
||||
|
|
|
@ -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();
|
||||
});
|
||||
});
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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();
|
||||
});
|
||||
});
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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.',
|
||||
}
|
||||
);
|
||||
|
|
|
@ -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%',
|
||||
},
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue