Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Index Management] Add bulk edit data retention #203083

Merged
Merged
Show file tree
Hide file tree
Changes from 12 commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
1a6dfb5
[Index Management] Add bulk edit data retention
ElenaStoeva Dec 5, 2024
4026250
Refactor edit data retention modal
ElenaStoeva Dec 5, 2024
15d9353
Refactor toast messages
ElenaStoeva Dec 5, 2024
d19c660
Add help text with maximum data retention displayed
ElenaStoeva Dec 5, 2024
a7888ac
Add affected data streams callout
ElenaStoeva Dec 5, 2024
3ca38c6
Only display delete button for deletable data streams
ElenaStoeva Dec 5, 2024
5e34c64
Fix type errors
ElenaStoeva Dec 5, 2024
94fd0a1
Update i18n translations
ElenaStoeva Dec 5, 2024
05c9961
Update and add api_integration tests
ElenaStoeva Dec 9, 2024
a467f74
Fix jest tests
ElenaStoeva Dec 9, 2024
fb839e4
[CI] Auto-commit changed files from 'node scripts/yarn_deduplicate'
kibanamachine Dec 9, 2024
a8946ed
[CI] Auto-commit changed files from 'node scripts/eslint --no-cache -…
kibanamachine Dec 9, 2024
812a79b
Only show error callout when infinite data retention is switched off
ElenaStoeva Dec 11, 2024
dc4a8e2
Make delete button red
ElenaStoeva Dec 11, 2024
fe0e1e1
Make project settings link load in a new tab
ElenaStoeva Dec 12, 2024
51a2fb2
Fix width issue
ElenaStoeva Dec 12, 2024
134dc1d
Fix logic for finding affected data streams
ElenaStoeva Dec 12, 2024
efa522d
Fix logic for bulk or single edit
ElenaStoeva Dec 12, 2024
2282883
Revert translation changes to resolve merge conflicts
ElenaStoeva Dec 12, 2024
8418e8c
Return i18n translations back
ElenaStoeva Dec 12, 2024
277838a
Fix type error
ElenaStoeva Dec 12, 2024
f95bbd7
Add some fixes
ElenaStoeva Dec 12, 2024
265a0b2
Merge branch 'main' into data-streams/bulk-edit-data-retention
ElenaStoeva Dec 16, 2024
efd7ca1
Fix i18n errors
ElenaStoeva Dec 16, 2024
86baf7c
Fix jest tests
ElenaStoeva Dec 18, 2024
6feec4b
Fix serverless api integration tests
ElenaStoeva Dec 18, 2024
dc21c91
Fix type errors
ElenaStoeva Dec 18, 2024
208bc58
Fix jest test
ElenaStoeva Dec 18, 2024
1c9ecd7
Merge branch 'main' into data-streams/bulk-edit-data-retention
ElenaStoeva Dec 18, 2024
4ebb176
Fix width change and validations
ElenaStoeva Dec 18, 2024
06012ca
[CI] Auto-commit changed files from 'node scripts/eslint --no-cache -…
kibanamachine Dec 18, 2024
429e891
Add more FTRs
ElenaStoeva Dec 19, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ export interface DataStreamsTabTestBed extends TestBed<TestSubjects> {
clickDeleteDataStreamButton: () => void;
clickEditDataRetentionButton: () => void;
clickDetailPanelIndexTemplateLink: () => void;
clickManageDataStreamsButton: () => void;
};
findDeleteActionAt: (index: number) => ReactWrapper;
findDeleteConfirmationModal: () => ReactWrapper;
Expand Down Expand Up @@ -210,6 +211,10 @@ export const setup = async (
component.update();
};

const clickManageDataStreamsButton = () => {
testBed.find('dataStreamActionsPopoverButton').simulate('click');
};

const findDetailPanel = () => {
const { find } = testBed;
return find('dataStreamDetailPanel');
Expand Down Expand Up @@ -258,6 +263,7 @@ export const setup = async (
clickDeleteDataStreamButton,
clickEditDataRetentionButton,
clickDetailPanelIndexTemplateLink,
clickManageDataStreamsButton,
},
findDeleteActionAt,
findDeleteConfirmationModal,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -557,8 +557,10 @@ describe('Data Streams tab', () => {
testBed.component.update();

expect(httpSetup.put).toHaveBeenLastCalledWith(
`${API_BASE_PATH}/data_streams/dataStream1/data_retention`,
expect.objectContaining({ body: JSON.stringify({ dataRetention: '7h' }) })
`${API_BASE_PATH}/data_streams/data_retention`,
expect.objectContaining({
body: JSON.stringify({ dataRetention: '7h', dataStreams: ['dataStream1'] }),
})
);
});

Expand All @@ -583,8 +585,10 @@ describe('Data Streams tab', () => {
testBed.component.update();

expect(httpSetup.put).toHaveBeenLastCalledWith(
`${API_BASE_PATH}/data_streams/dataStream1/data_retention`,
expect.objectContaining({ body: JSON.stringify({ enabled: false }) })
`${API_BASE_PATH}/data_streams/data_retention`,
expect.objectContaining({
body: JSON.stringify({ enabled: false, dataStreams: ['dataStream1'] }),
})
);
});

Expand All @@ -609,8 +613,8 @@ describe('Data Streams tab', () => {
testBed.component.update();

expect(httpSetup.put).toHaveBeenLastCalledWith(
`${API_BASE_PATH}/data_streams/dataStream1/data_retention`,
expect.objectContaining({ body: JSON.stringify({}) })
`${API_BASE_PATH}/data_streams/data_retention`,
expect.objectContaining({ body: JSON.stringify({ dataStreams: ['dataStream1'] }) })
);
});
});
Expand Down Expand Up @@ -1028,17 +1032,20 @@ describe('Data Streams tab', () => {

test('displays/hides delete action depending on data streams privileges', async () => {
const {
actions: { selectDataStream },
actions: { selectDataStream, clickManageDataStreamsButton },
find,
} = testBed;

selectDataStream('dataStreamNoDelete', true);
clickManageDataStreamsButton();
expect(find('deleteDataStreamsButton').exists()).toBeFalsy();

selectDataStream('dataStreamWithDelete', true);
clickManageDataStreamsButton();
expect(find('deleteDataStreamsButton').exists()).toBeFalsy();

selectDataStream('dataStreamNoDelete', false);
clickManageDataStreamsButton();
expect(find('deleteDataStreamsButton').exists()).toBeTruthy();
});

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/

import { EuiButton, EuiContextMenu, EuiPopover } from '@elastic/eui';
import React, { useState } from 'react';
import { FormattedMessage } from '@kbn/i18n-react';
import { EuiContextMenuPanelItemDescriptor } from '@elastic/eui/src/components/context_menu/context_menu';
import { i18n } from '@kbn/i18n';

interface Props {
dataStreamActions: EuiContextMenuPanelItemDescriptor[];
selectedDataStreamsCount: number;
}

export const DataStreamActionsMenu = ({ dataStreamActions, selectedDataStreamsCount }: Props) => {
const [isPopoverOpen, setIsPopoverOpen] = useState(false);

const popoverButton = (
<EuiButton
data-test-subj="dataStreamActionsPopoverButton"
onClick={() => setIsPopoverOpen(!isPopoverOpen)}
iconType="arrowDown"
iconSide="right"
fill={true}
>
<FormattedMessage
id="xpack.idxMgmt.dataStreamList.table.manageDataStreamsButtonLabel"
defaultMessage="Manage {selectedDataStreamsCount, plural, one {data stream} other {{selectedDataStreamsCount} data streams}}"
values={{ selectedDataStreamsCount }}
/>
</EuiButton>
);

return (
<EuiPopover
id="dataStreamActionsPopover"
button={popoverButton}
isOpen={isPopoverOpen}
closePopover={() => setIsPopoverOpen(false)}
panelPaddingSize="none"
anchorPosition="rightUp"
repositionOnScroll={true}
>
<EuiContextMenu
data-test-subj="dataStreamActionsContextMenu"
initialPanelId={0}
panels={[
{
id: 0,
title: i18n.translate(
'xpack.idxMgmt.dataStreamList.table.dataStreamsContextMenuPanelLabel',
{
defaultMessage: 'Data stream options',
}
),
items: dataStreamActions,
},
]}
/>
</EuiPopover>
);
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/

export { DataStreamActionsMenu } from './data_stream_actions_menu';
Original file line number Diff line number Diff line change
Expand Up @@ -550,7 +550,7 @@ export const DataStreamDetailPanel: React.FunctionComponent<Props> = ({
}}
ilmPolicyName={dataStream?.ilmPolicyName}
ilmPolicyLink={ilmPolicyLink}
dataStream={dataStream}
dataStreams={[dataStream]}
/>
)}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import {
import { ScopedHistory } from '@kbn/core/public';
import { useEuiTablePersist } from '@kbn/shared-ux-table-persist';

import { EuiContextMenuPanelItemDescriptor } from '@elastic/eui/src/components/context_menu/context_menu';
import { MAX_DATA_RETENTION } from '../../../../../../common/constants';
import { useAppContext } from '../../../../app_context';
import { DataStream } from '../../../../../../common/types';
Expand All @@ -39,6 +40,8 @@ import { isDataStreamFullyManagedByILM } from '../../../../lib/data_streams';
import { indexModeLabels } from '../../../../lib/index_mode_labels';
import { FilterListButton, Filters } from '../../components';
import { type DataStreamFilterName } from '../data_stream_list';
import { DataStreamActionsMenu } from '../data_stream_actions_menu';
import { EditDataRetentionModal } from '../edit_data_retention_modal';

interface TableDataStream extends DataStream {
isDataStreamFullyManagedByILM: boolean;
Expand Down Expand Up @@ -70,6 +73,9 @@ export const DataStreamTable: React.FunctionComponent<Props> = ({
}) => {
const [selection, setSelection] = useState<DataStream[]>([]);
const [dataStreamsToDelete, setDataStreamsToDelete] = useState<string[]>([]);
const [dataStreamsToEditDataRetention, setDataStreamsToEditDataRetention] = useState<
DataStream[]
>([]);
const { config } = useAppContext();

const data = useMemo(() => {
Expand Down Expand Up @@ -284,25 +290,37 @@ export const DataStreamTable: React.FunctionComponent<Props> = ({
onSelectionChange: setSelection,
};

const dataStreamActions: EuiContextMenuPanelItemDescriptor[] = [
{
name: i18n.translate('xpack.idxMgmt.dataStreamList.table.bulkEditDataRetentionButtonLabel', {
defaultMessage: 'Edit data retention',
}),
icon: 'pencil',
onClick: () => setDataStreamsToEditDataRetention(selection),
},
];

if (selection.every((dataStream: DataStream) => dataStream.privileges.delete_index)) {
dataStreamActions.push({
name: i18n.translate('xpack.idxMgmt.dataStreamList.table.deleteDataStreamsButtonLabel', {
defaultMessage: 'Delete data streams',
}),
icon: 'trash',
onClick: () => setDataStreamsToDelete(selection.map(({ name }: DataStream) => name)),
});
}

const searchConfig = {
query: filters,
box: {
incremental: true,
},
toolsLeft:
selection.length > 0 &&
selection.every((dataStream: DataStream) => dataStream.privileges.delete_index) ? (
<EuiButton
data-test-subj="deleteDataStreamsButton"
onClick={() => setDataStreamsToDelete(selection.map(({ name }: DataStream) => name))}
color="danger"
>
<FormattedMessage
id="xpack.idxMgmt.dataStreamList.table.deleteDataStreamsButtonLabel"
defaultMessage="Delete {count, plural, one {data stream} other {data streams} }"
values={{ count: selection.length }}
/>
</EuiButton>
selection.length > 0 ? (
<DataStreamActionsMenu
dataStreamActions={dataStreamActions}
selectedDataStreamsCount={selection.length}
/>
) : undefined,
toolsRight: [
<EuiFlexGroup gutterSize="s">
Expand Down Expand Up @@ -365,6 +383,18 @@ export const DataStreamTable: React.FunctionComponent<Props> = ({

return (
<>
{dataStreamsToEditDataRetention && dataStreamsToEditDataRetention.length > 0 ? (
<EditDataRetentionModal
onClose={(res) => {
if (res && res.hasUpdatedDataRetention) {
reload();
} else {
setDataStreamsToEditDataRetention([]);
}
}}
dataStreams={dataStreamsToEditDataRetention}
/>
) : null}
{dataStreamsToDelete && dataStreamsToDelete.length > 0 ? (
<DeleteDataStreamConfirmationModal
onClose={(res) => {
Expand Down
Loading