Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
6330adc
Add UI changes for supporting data retention editing
sabarasaba Sep 20, 2023
2bb02ce
commit with @elastic email
sabarasaba Sep 20, 2023
2b23bd6
Fix translations
sabarasaba Sep 21, 2023
f2b59a8
Thighten up validation
sabarasaba Sep 21, 2023
059d55a
Deserialize initial value and use as default
sabarasaba Sep 22, 2023
f0c7c01
[CI] Auto-commit changed files from 'node scripts/precommit_hook.js -…
kibanamachine Sep 22, 2023
f818f60
Add support for infinite retention periods
sabarasaba Sep 22, 2023
e3339ed
Fix small UX bugs
sabarasaba Sep 22, 2023
0442fb2
Fix linter issues
sabarasaba Sep 22, 2023
dadf69d
Fix duped id
sabarasaba Sep 22, 2023
9d76ce3
Add configuredByILM warning
sabarasaba Sep 25, 2023
bd34162
[CI] Auto-commit changed files from 'node scripts/precommit_hook.js -…
kibanamachine Sep 25, 2023
eda56f1
Add api integration and unit tests
sabarasaba Sep 25, 2023
9b21888
[CI] Auto-commit changed files from 'node scripts/precommit_hook.js -…
kibanamachine Sep 25, 2023
bd7d92f
Finish up adding cits
sabarasaba Sep 26, 2023
9f092b1
Add functional tests
sabarasaba Sep 26, 2023
30dc3ab
[CI] Auto-commit changed files from 'node scripts/precommit_hook.js -…
kibanamachine Sep 26, 2023
0bd951e
Rename test subj
sabarasaba Sep 26, 2023
4ea8edb
Add missing test
sabarasaba Sep 26, 2023
d1ea8a1
[CI] Auto-commit changed files from 'node scripts/precommit_hook.js -…
kibanamachine Sep 26, 2023
827bcf9
Fix paths
sabarasaba Sep 26, 2023
0f2ac96
[CI] Auto-commit changed files from 'node scripts/precommit_hook.js -…
kibanamachine Sep 26, 2023
422f9c1
Add checks for when form is dirty
sabarasaba Sep 27, 2023
18b232a
[CI] Auto-commit changed files from 'node scripts/precommit_hook.js -…
kibanamachine Sep 27, 2023
e24856d
Address CR changes
sabarasaba Sep 28, 2023
b87173a
Fix lint error
sabarasaba Sep 28, 2023
98fa96c
Address copy review
sabarasaba Sep 28, 2023
9065764
Disable editing of data retention when DS is managed by ILM
sabarasaba Sep 29, 2023
8771d59
Add permissions check for ds
sabarasaba Sep 29, 2023
55a1c1f
[CI] Auto-commit changed files from 'node scripts/precommit_hook.js -…
kibanamachine Sep 29, 2023
8288f71
[CI] Auto-commit changed files from 'node scripts/eslint --no-cache -…
kibanamachine Sep 29, 2023
5258cd4
Address CR changes
sabarasaba Sep 29, 2023
68afc92
[CI] Auto-commit changed files from 'node scripts/precommit_hook.js -…
kibanamachine Sep 29, 2023
8189921
Add missing privilege
sabarasaba Sep 29, 2023
1eb7855
Fix import path
sabarasaba Sep 29, 2023
3d6e055
Merge branch 'main' into index_management-ds_edit_data_retention
kibanamachine Sep 29, 2023
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
1 change: 1 addition & 0 deletions packages/kbn-doc-links/src/get_doc_links.ts
Original file line number Diff line number Diff line change
Expand Up @@ -413,6 +413,7 @@ export const getDocLinks = ({ kibanaBranch }: GetDocLinkOptions): DocLinks => {
secureCluster: `${ELASTICSEARCH_DOCS}secure-cluster.html`,
shardAllocationSettings: `${ELASTICSEARCH_DOCS}modules-cluster.html#cluster-shard-allocation-settings`,
sortSearch: `${ELASTICSEARCH_DOCS}sort-search-results.html`,
tutorialUpdateExistingDataStream: `${ELASTICSEARCH_DOCS}tutorial-manage-existing-data-stream.html`,
transportSettings: `${ELASTICSEARCH_DOCS}modules-network.html#common-network-settings`,
typesRemoval: `${ELASTICSEARCH_DOCS}removal-of-types.html`,
setupUpgrade: `${ELASTICSEARCH_DOCS}setup-upgrade.html`,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,18 @@ const registerHttpRequestMockHelpers = (
const setDeleteDataStreamResponse = (response?: HttpResponse, error?: ResponseError) =>
mockResponse('POST', `${API_BASE_PATH}/delete_data_streams`, response, error);

const setEditDataRetentionResponse = (
dataStreamId: string,
response?: HttpResponse,
error?: ResponseError
) =>
mockResponse(
'PUT',
`${API_BASE_PATH}/data_streams/${encodeURIComponent(dataStreamId)}/data_retention`,
response,
error
);

const setDeleteTemplateResponse = (response?: HttpResponse, error?: ResponseError) =>
mockResponse('POST', `${API_BASE_PATH}/delete_index_templates`, response, error);

Expand Down Expand Up @@ -196,6 +208,7 @@ const registerHttpRequestMockHelpers = (
setLoadDataStreamResponse,
setDeleteDataStreamResponse,
setDeleteTemplateResponse,
setEditDataRetentionResponse,
setLoadTemplateResponse,
setCreateTemplateResponse,
setLoadIndexSettingsResponse,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -92,5 +92,14 @@ export type TestSubjects =
| 'createAndExecuteButton'
| 'enrichPolicySummaryList'
| 'requestBody'
| 'editDataRetentionButton'
| 'errorWhenCreatingCallout'
| 'manageDataStreamButton'
| 'dataRetentionValue'
| 'policyNameField'
| 'configuredByILMWarning'
| 'show-filters-button'
| 'filter-option-h'
| 'infiniteRetentionPeriod.input'
| 'saveButton'
| 'createIndexSaveButton';
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ export interface DataStreamsTabTestBed extends TestBed<TestSubjects> {
selectDataStream: (name: string, selected: boolean) => void;
clickConfirmDelete: () => void;
clickDeleteDataStreamButton: () => void;
clickEditDataRetentionButton: () => void;
clickDetailPanelIndexTemplateLink: () => void;
};
findDeleteActionAt: (index: number) => ReactWrapper;
Expand Down Expand Up @@ -176,8 +177,13 @@ export const setup = async (
};

const clickDeleteDataStreamButton = () => {
const { find } = testBed;
find('deleteDataStreamButton').simulate('click');
testBed.find('manageDataStreamButton').simulate('click');
Comment thread
sabarasaba marked this conversation as resolved.
testBed.find('deleteDataStreamButton').simulate('click');
};

const clickEditDataRetentionButton = () => {
testBed.find('manageDataStreamButton').simulate('click');
testBed.find('editDataRetentionButton').simulate('click');
};

const clickDetailPanelIndexTemplateLink = async () => {
Expand Down Expand Up @@ -236,6 +242,7 @@ export const setup = async (
selectDataStream,
clickConfirmDelete,
clickDeleteDataStreamButton,
clickEditDataRetentionButton,
clickDetailPanelIndexTemplateLink,
},
findDeleteActionAt,
Expand Down Expand Up @@ -267,6 +274,7 @@ export const createDataStreamPayload = (dataStream: Partial<DataStream>): DataSt
maxTimeStamp: 420,
privileges: {
delete_index: true,
manage_data_stream_lifecycle: true,
},
hidden: false,
lifecycle: {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@

import { act } from 'react-dom/test-utils';
import { createMemoryHistory } from 'history';
import { notificationServiceMock } from '@kbn/core/public/mocks';

import {
breadcrumbService,
Expand All @@ -15,6 +16,7 @@ import {
import { API_BASE_PATH } from '../../../common/constants';
import * as fixtures from '../../../test/fixtures';
import { setupEnvironment } from '../helpers';
import { notificationService } from '../../../public/application/services/notification';

import {
DataStreamsTabTestBed,
Expand Down Expand Up @@ -134,6 +136,8 @@ describe('Data Streams tab', () => {
});

describe('when there are data streams', () => {
const notificationsServiceMock = notificationServiceMock.createStartContract();

beforeEach(async () => {
const {
setLoadIndicesResponse,
Expand Down Expand Up @@ -169,7 +173,13 @@ describe('Data Streams tab', () => {
setLoadTemplatesResponse({ templates: [indexTemplate], legacyTemplates: [] });
setLoadTemplateResponse(indexTemplate.name, indexTemplate);

testBed = await setup(httpSetup, { history: createMemoryHistory() });
notificationService.setup(notificationsServiceMock);
testBed = await setup(httpSetup, {
history: createMemoryHistory(),
services: {
notificationService,
},
});
await act(async () => {
testBed.actions.goToDataStreamsList();
});
Expand Down Expand Up @@ -327,6 +337,64 @@ describe('Data Streams tab', () => {
);
});

describe('update data retention', () => {
test('can set data retention period', async () => {
const {
actions: { clickNameAt, clickEditDataRetentionButton },
} = testBed;

await clickNameAt(0);

clickEditDataRetentionButton();

httpRequestsMockHelpers.setEditDataRetentionResponse('dataStream1', {
success: true,
});

// set data retention value
testBed.form.setInputValue('dataRetentionValue', '7');
// Set data retention unit
testBed.find('show-filters-button').simulate('click');
testBed.find('filter-option-h').simulate('click');

await act(async () => {
testBed.find('saveButton').simulate('click');
});
testBed.component.update();

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

test('allows to set infinite retention period', async () => {
const {
actions: { clickNameAt, clickEditDataRetentionButton },
} = testBed;

await clickNameAt(0);

clickEditDataRetentionButton();

httpRequestsMockHelpers.setEditDataRetentionResponse('dataStream1', {
success: true,
});

testBed.form.toggleEuiSwitch('infiniteRetentionPeriod.input');

await act(async () => {
testBed.find('saveButton').simulate('click');
});
testBed.component.update();

expect(httpSetup.put).toHaveBeenLastCalledWith(
`${API_BASE_PATH}/data_streams/dataStream1/data_retention`,
expect.objectContaining({ body: JSON.stringify({}) })
);
});
});

test('clicking index template name navigates to the index template details', async () => {
const {
actions: { clickNameAt, clickDetailPanelIndexTemplateLink },
Expand Down Expand Up @@ -423,6 +491,33 @@ describe('Data Streams tab', () => {
expect(findDetailPanelIlmPolicyLink().prop('href')).toBe('/test/my_ilm_policy');
});

test('with ILM updating data retention should be disabled', async () => {
const { setLoadDataStreamsResponse, setLoadDataStreamResponse } = httpRequestsMockHelpers;

const dataStreamForDetailPanel = createDataStreamPayload({
name: 'dataStream1',
ilmPolicyName: 'my_ilm_policy',
});

setLoadDataStreamsResponse([dataStreamForDetailPanel]);
setLoadDataStreamResponse(dataStreamForDetailPanel.name, dataStreamForDetailPanel);

testBed = await setup(httpSetup, {
history: createMemoryHistory(),
url: urlServiceMock,
});
await act(async () => {
testBed.actions.goToDataStreamsList();
});
testBed.component.update();

const { actions } = testBed;
await actions.clickNameAt(0);

testBed.find('manageDataStreamButton').simulate('click');
expect(testBed.find('editDataRetentionButton').exists()).toBeFalsy();
});

test('with an ILM url locator and no ILM policy', async () => {
const { setLoadDataStreamsResponse, setLoadDataStreamResponse } = httpRequestsMockHelpers;

Expand Down Expand Up @@ -567,15 +662,29 @@ describe('Data Streams tab', () => {

const dataStreamWithDelete = createDataStreamPayload({
name: 'dataStreamWithDelete',
privileges: { delete_index: true },
privileges: { delete_index: true, manage_data_stream_lifecycle: true },
});
const dataStreamNoDelete = createDataStreamPayload({
name: 'dataStreamNoDelete',
privileges: { delete_index: false },
privileges: { delete_index: false, manage_data_stream_lifecycle: true },
});
const dataStreamNoEditRetention = createDataStreamPayload({
name: 'dataStreamNoEditRetention',
privileges: { delete_index: true, manage_data_stream_lifecycle: false },
});

const dataStreamNoPermissions = createDataStreamPayload({
name: 'dataStreamNoPermissions',
privileges: { delete_index: false, manage_data_stream_lifecycle: false },
});

beforeEach(async () => {
setLoadDataStreamsResponse([dataStreamWithDelete, dataStreamNoDelete]);
setLoadDataStreamsResponse([
dataStreamWithDelete,
dataStreamNoDelete,
dataStreamNoEditRetention,
dataStreamNoPermissions,
]);

testBed = await setup(httpSetup, { history: createMemoryHistory(), url: urlServiceMock });
await act(async () => {
Expand All @@ -590,6 +699,8 @@ describe('Data Streams tab', () => {

expect(tableCellsValues).toEqual([
['', 'dataStreamNoDelete', 'green', '1', '7d', ''],
['', 'dataStreamNoEditRetention', 'green', '1', '7d', 'Delete'],
['', 'dataStreamNoPermissions', 'green', '1', '7d', ''],
['', 'dataStreamWithDelete', 'green', '1', '7d', 'Delete'],
]);
});
Expand All @@ -610,26 +721,51 @@ describe('Data Streams tab', () => {
expect(find('deleteDataStreamsButton').exists()).toBeTruthy();
});

test('displays delete button in detail panel', async () => {
test('hides delete button in detail panel', async () => {
const {
actions: { clickNameAt },
find,
} = testBed;
setLoadDataStreamResponse(dataStreamWithDelete.name, dataStreamWithDelete);
setLoadDataStreamResponse(dataStreamNoDelete.name, dataStreamNoDelete);
await clickNameAt(0);

testBed.find('manageDataStreamButton').simulate('click');
expect(find('deleteDataStreamButton').exists()).toBeFalsy();
});

test('hides edit data retention button if no permissions', async () => {
const {
actions: { clickNameAt },
find,
} = testBed;
setLoadDataStreamResponse(dataStreamNoEditRetention.name, dataStreamNoEditRetention);
await clickNameAt(1);

expect(find('deleteDataStreamButton').exists()).toBeTruthy();
testBed.find('manageDataStreamButton').simulate('click');
expect(find('editDataRetentionButton').exists()).toBeFalsy();
});

test('hides delete button in detail panel', async () => {
test('hides manage button if no permissions', async () => {
const {
actions: { clickNameAt },
find,
} = testBed;
setLoadDataStreamResponse(dataStreamNoDelete.name, dataStreamNoDelete);
await clickNameAt(0);
setLoadDataStreamResponse(dataStreamNoPermissions.name, dataStreamNoPermissions);
await clickNameAt(2);

expect(find('deleteDataStreamButton').exists()).toBeFalsy();
expect(find('manageDataStreamButton').exists()).toBeFalsy();
});

test('displays delete button in detail panel', async () => {
const {
actions: { clickNameAt },
find,
} = testBed;
setLoadDataStreamResponse(dataStreamWithDelete.name, dataStreamWithDelete);
await clickNameAt(3);

testBed.find('manageDataStreamButton').simulate('click');
expect(find('deleteDataStreamButton').exists()).toBeTruthy();
});
});
});
Expand Down
2 changes: 1 addition & 1 deletion x-pack/plugins/index_management/common/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,6 @@

export { API_BASE_PATH, INTERNAL_API_BASE_PATH, BASE_PATH, MAJOR_VERSION } from './constants';

export { getTemplateParameter } from './lib';
export { getTemplateParameter, splitSizeAndUnits } from './lib';

export * from './types';
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
/*
* 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 { splitSizeAndUnits } from './data_stream_serialization';

describe('Data stream serialization', () => {
test('can split size and units from lifecycle string', () => {
expect(splitSizeAndUnits('1h')).toEqual({ size: '1', unit: 'h' });
expect(splitSizeAndUnits('20micron')).toEqual({ size: '20', unit: 'micron' });
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -54,3 +54,19 @@ export function deserializeDataStreamList(
): DataStream[] {
return dataStreamsFromEs.map((dataStream) => deserializeDataStream(dataStream));
}

export const splitSizeAndUnits = (field: string): { size: string; unit: string } => {
Comment thread
sabarasaba marked this conversation as resolved.
let size = '';
let unit = '';

const result = /(\d+)(\w+)/.exec(field);
if (result) {
size = result[1];
unit = result[2];
}

return {
size,
unit,
};
};
6 changes: 5 additions & 1 deletion x-pack/plugins/index_management/common/lib/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,11 @@
* 2.0.
*/

export { deserializeDataStream, deserializeDataStreamList } from './data_stream_serialization';
export {
deserializeDataStream,
deserializeDataStreamList,
splitSizeAndUnits,
} from './data_stream_serialization';

export {
deserializeTemplate,
Expand Down
Loading