From be01217b1967bb31ce06f6530bdd4c0705a6e5f9 Mon Sep 17 00:00:00 2001 From: Alison Goryachev Date: Mon, 18 Sep 2023 12:37:02 -0400 Subject: [PATCH] [Index Management] Support data retention on Data Streams tab (#165263) --- x-pack/plugins/index_management/README.md | 29 +++- .../home/data_streams_tab.helpers.ts | 22 ++- .../home/data_streams_tab.test.ts | 44 +++--- .../common/lib/data_stream_serialization.ts | 10 +- .../common/types/data_streams.ts | 42 +++--- .../index_management/common/types/index.ts | 2 +- .../data_stream_detail_panel.tsx | 129 +++++++++++------- .../data_stream_table/data_stream_table.tsx | 33 ++++- .../api/data_streams/register_get_route.ts | 70 ++++------ .../translations/translations/fr-FR.json | 1 - .../translations/translations/ja-JP.json | 1 - .../translations/translations/zh-CN.json | 1 - .../index_management/data_streams.ts | 9 ++ 13 files changed, 239 insertions(+), 154 deletions(-) diff --git a/x-pack/plugins/index_management/README.md b/x-pack/plugins/index_management/README.md index b50309ac36099..8ac2837a683b6 100644 --- a/x-pack/plugins/index_management/README.md +++ b/x-pack/plugins/index_management/README.md @@ -49,11 +49,34 @@ POST %25%7B%5B%40metadata%5D%5Bbeat%5D%7D-%25%7B%5B%40metadata%5D%5Bversion%5D%7 } ``` +Create a data stream configured with data stream lifecyle. + +``` +PUT _index_template/my-index-template +{ + "index_patterns": ["my-data-stream*"], + "data_stream": { }, + "priority": 500, + "template": { + "lifecycle": { + "data_retention": "7d" + } + }, + "_meta": { + "description": "Template with data stream lifecycle" + } +} +``` + +``` +PUT _data_stream/my-data-stream +``` + ## Index templates tab ### Quick steps for testing -**Legacy index templates** are only shown in the UI on stateful *and* if a user has existing legacy index templates. You can test this functionality by creating one in Console: +**Legacy index templates** are only shown in the UI on stateful _and_ if a user has existing legacy index templates. You can test this functionality by creating one in Console: ``` PUT _template/template_1 @@ -67,6 +90,7 @@ On serverless, Elasticsearch does not support legacy index templates and therefo To test **Cloud-managed templates**: 1. Add `cluster.metadata.managed_index_templates` setting via Dev Tools: + ``` PUT /_cluster/settings { @@ -77,6 +101,7 @@ PUT /_cluster/settings ``` 2. Create a template with the format: `.cloud-` via Dev Tools. + ``` PUT _template/.cloud-example { @@ -101,4 +126,4 @@ In 7.x, the UI supports types defined as part of the mappings for legacy index t } } } -``` \ No newline at end of file +``` diff --git a/x-pack/plugins/index_management/__jest__/client_integration/home/data_streams_tab.helpers.ts b/x-pack/plugins/index_management/__jest__/client_integration/home/data_streams_tab.helpers.ts index e38359a6d9f37..d9e6694d8ba8d 100644 --- a/x-pack/plugins/index_management/__jest__/client_integration/home/data_streams_tab.helpers.ts +++ b/x-pack/plugins/index_management/__jest__/client_integration/home/data_streams_tab.helpers.ts @@ -8,7 +8,6 @@ import { act } from 'react-dom/test-utils'; import { ReactWrapper } from 'enzyme'; -import { EuiDescriptionListDescription } from '@elastic/eui'; import { registerTestBed, TestBed, @@ -43,8 +42,9 @@ export interface DataStreamsTabTestBed extends TestBed { findDetailPanelTitle: () => string; findEmptyPromptIndexTemplateLink: () => ReactWrapper; findDetailPanelIlmPolicyLink: () => ReactWrapper; - findDetailPanelIlmPolicyName: () => ReactWrapper; + findDetailPanelIlmPolicyDetail: () => ReactWrapper; findDetailPanelIndexTemplateLink: () => ReactWrapper; + findDetailPanelDataRetentionDetail: () => ReactWrapper; } export const setup = async ( @@ -211,10 +211,14 @@ export const setup = async ( return find('indexTemplateLink'); }; - const findDetailPanelIlmPolicyName = () => { - const descriptionList = testBed.component.find(EuiDescriptionListDescription); - // ilm policy is the last in the details list - return descriptionList.last(); + const findDetailPanelIlmPolicyDetail = () => { + const { find } = testBed; + return find('ilmPolicyDetail'); + }; + + const findDetailPanelDataRetentionDetail = () => { + const { find } = testBed; + return find('dataRetentionDetail'); }; return { @@ -240,8 +244,9 @@ export const setup = async ( findDetailPanelTitle, findEmptyPromptIndexTemplateLink, findDetailPanelIlmPolicyLink, - findDetailPanelIlmPolicyName, + findDetailPanelIlmPolicyDetail, findDetailPanelIndexTemplateLink, + findDetailPanelDataRetentionDetail, }; }; @@ -264,6 +269,9 @@ export const createDataStreamPayload = (dataStream: Partial): DataSt delete_index: true, }, hidden: false, + lifecycle: { + data_retention: '7d', + }, ...dataStream, }); diff --git a/x-pack/plugins/index_management/__jest__/client_integration/home/data_streams_tab.test.ts b/x-pack/plugins/index_management/__jest__/client_integration/home/data_streams_tab.test.ts index 208bec0a1d6e6..f4e8221be1093 100644 --- a/x-pack/plugins/index_management/__jest__/client_integration/home/data_streams_tab.test.ts +++ b/x-pack/plugins/index_management/__jest__/client_integration/home/data_streams_tab.test.ts @@ -170,8 +170,8 @@ describe('Data Streams tab', () => { const { tableCellsValues } = table.getMetaData('dataStreamTable'); expect(tableCellsValues).toEqual([ - ['', 'dataStream1', 'green', '1', 'Delete'], - ['', 'dataStream2', 'green', '1', 'Delete'], + ['', 'dataStream1', 'green', '1', '7d', 'Delete'], + ['', 'dataStream2', 'green', '1', '7d', 'Delete'], ]); }); @@ -209,8 +209,8 @@ describe('Data Streams tab', () => { // The table renders with the stats columns though. const { tableCellsValues } = table.getMetaData('dataStreamTable'); expect(tableCellsValues).toEqual([ - ['', 'dataStream1', 'green', 'December 31st, 1969 7:00:00 PM', '5b', '1', 'Delete'], - ['', 'dataStream2', 'green', 'December 31st, 1969 7:00:00 PM', '1kb', '1', 'Delete'], + ['', 'dataStream1', 'green', 'December 31st, 1969 7:00:00 PM', '5b', '1', '7d', 'Delete'], + ['', 'dataStream2', 'green', 'December 31st, 1969 7:00:00 PM', '1kb', '1', '7d', 'Delete'], ]); }); @@ -229,8 +229,8 @@ describe('Data Streams tab', () => { // the human-readable string values. const { tableCellsValues } = table.getMetaData('dataStreamTable'); expect(tableCellsValues).toEqual([ - ['', 'dataStream1', 'green', 'December 31st, 1969 7:00:00 PM', '5b', '1', 'Delete'], - ['', 'dataStream2', 'green', 'December 31st, 1969 7:00:00 PM', '1kb', '1', 'Delete'], + ['', 'dataStream1', 'green', 'December 31st, 1969 7:00:00 PM', '5b', '1', '7d', 'Delete'], + ['', 'dataStream2', 'green', 'December 31st, 1969 7:00:00 PM', '1kb', '1', '7d', 'Delete'], ]); }); @@ -335,6 +335,12 @@ describe('Data Streams tab', () => { expect(find('summaryTab').exists()).toBeTruthy(); expect(find('title').text().trim()).toBe('indexTemplate'); }); + + test('shows data retention detail when configured', async () => { + const { actions, findDetailPanelDataRetentionDetail } = testBed; + await actions.clickNameAt(0); + expect(findDetailPanelDataRetentionDetail().exists()).toBeTruthy(); + }); }); }); @@ -423,10 +429,10 @@ describe('Data Streams tab', () => { }); testBed.component.update(); - const { actions, findDetailPanelIlmPolicyLink, findDetailPanelIlmPolicyName } = testBed; + const { actions, findDetailPanelIlmPolicyLink, findDetailPanelIlmPolicyDetail } = testBed; await actions.clickNameAt(0); expect(findDetailPanelIlmPolicyLink().exists()).toBeFalsy(); - expect(findDetailPanelIlmPolicyName().contains('None')).toBeTruthy(); + expect(findDetailPanelIlmPolicyDetail().exists()).toBeFalsy(); }); test('without an ILM url locator and with an ILM policy', async () => { @@ -453,10 +459,10 @@ describe('Data Streams tab', () => { }); testBed.component.update(); - const { actions, findDetailPanelIlmPolicyLink, findDetailPanelIlmPolicyName } = testBed; + const { actions, findDetailPanelIlmPolicyLink, findDetailPanelIlmPolicyDetail } = testBed; await actions.clickNameAt(0); expect(findDetailPanelIlmPolicyLink().exists()).toBeFalsy(); - expect(findDetailPanelIlmPolicyName().contains('my_ilm_policy')).toBeTruthy(); + expect(findDetailPanelIlmPolicyDetail().contains('my_ilm_policy')).toBeTruthy(); }); }); @@ -489,8 +495,8 @@ describe('Data Streams tab', () => { const { tableCellsValues } = table.getMetaData('dataStreamTable'); expect(tableCellsValues).toEqual([ - ['', `managed-data-stream${nonBreakingSpace}Fleet-managed`, 'green', '1', 'Delete'], - ['', 'non-managed-data-stream', 'green', '1', 'Delete'], + ['', `managed-data-stream${nonBreakingSpace}Fleet-managed`, 'green', '1', '7d', 'Delete'], + ['', 'non-managed-data-stream', 'green', '1', '7d', 'Delete'], ]); }); @@ -499,14 +505,16 @@ describe('Data Streams tab', () => { let { tableCellsValues } = table.getMetaData('dataStreamTable'); expect(tableCellsValues).toEqual([ - ['', `managed-data-stream${nonBreakingSpace}Fleet-managed`, 'green', '1', 'Delete'], - ['', 'non-managed-data-stream', 'green', '1', 'Delete'], + ['', `managed-data-stream${nonBreakingSpace}Fleet-managed`, 'green', '1', '7d', 'Delete'], + ['', 'non-managed-data-stream', 'green', '1', '7d', 'Delete'], ]); actions.toggleViewFilterAt(0); ({ tableCellsValues } = table.getMetaData('dataStreamTable')); - expect(tableCellsValues).toEqual([['', 'non-managed-data-stream', 'green', '1', 'Delete']]); + expect(tableCellsValues).toEqual([ + ['', 'non-managed-data-stream', 'green', '1', '7d', 'Delete'], + ]); }); }); @@ -537,7 +545,7 @@ describe('Data Streams tab', () => { const { tableCellsValues } = table.getMetaData('dataStreamTable'); expect(tableCellsValues).toEqual([ - ['', `hidden-data-stream${nonBreakingSpace}Hidden`, 'green', '1', 'Delete'], + ['', `hidden-data-stream${nonBreakingSpace}Hidden`, 'green', '1', '7d', 'Delete'], ]); }); }); @@ -570,8 +578,8 @@ describe('Data Streams tab', () => { const { tableCellsValues } = table.getMetaData('dataStreamTable'); expect(tableCellsValues).toEqual([ - ['', 'dataStreamNoDelete', 'green', '1', ''], - ['', 'dataStreamWithDelete', 'green', '1', 'Delete'], + ['', 'dataStreamNoDelete', 'green', '1', '7d', ''], + ['', 'dataStreamWithDelete', 'green', '1', '7d', 'Delete'], ]); }); diff --git a/x-pack/plugins/index_management/common/lib/data_stream_serialization.ts b/x-pack/plugins/index_management/common/lib/data_stream_serialization.ts index 5c27c16946325..78593b40b5eaa 100644 --- a/x-pack/plugins/index_management/common/lib/data_stream_serialization.ts +++ b/x-pack/plugins/index_management/common/lib/data_stream_serialization.ts @@ -5,9 +5,9 @@ * 2.0. */ -import { DataStream, DataStreamFromEs, Health } from '../types'; +import { DataStream, EnhancedDataStreamFromEs, Health } from '../types'; -export function deserializeDataStream(dataStreamFromEs: DataStreamFromEs): DataStream { +export function deserializeDataStream(dataStreamFromEs: EnhancedDataStreamFromEs): DataStream { const { name, timestamp_field: timeStampField, @@ -22,6 +22,7 @@ export function deserializeDataStream(dataStreamFromEs: DataStreamFromEs): DataS _meta, privileges, hidden, + lifecycle, } = dataStreamFromEs; return { @@ -44,9 +45,12 @@ export function deserializeDataStream(dataStreamFromEs: DataStreamFromEs): DataS _meta, privileges, hidden, + lifecycle, }; } -export function deserializeDataStreamList(dataStreamsFromEs: DataStreamFromEs[]): DataStream[] { +export function deserializeDataStreamList( + dataStreamsFromEs: EnhancedDataStreamFromEs[] +): DataStream[] { return dataStreamsFromEs.map((dataStream) => deserializeDataStream(dataStream)); } diff --git a/x-pack/plugins/index_management/common/types/data_streams.ts b/x-pack/plugins/index_management/common/types/data_streams.ts index 7ac08f6684466..88e4bc237e819 100644 --- a/x-pack/plugins/index_management/common/types/data_streams.ts +++ b/x-pack/plugins/index_management/common/types/data_streams.ts @@ -5,20 +5,20 @@ * 2.0. */ +import { + ByteSize, + IndicesDataLifecycleWithRollover, + IndicesDataStream, + IndicesDataStreamsStatsDataStreamsStatsItem, + Metadata, +} from '@elastic/elasticsearch/lib/api/types'; + interface TimestampFieldFromEs { name: string; } type TimestampField = TimestampFieldFromEs; -interface MetaFromEs { - managed_by: string; - package: any; - managed: boolean; -} - -type Meta = MetaFromEs; - interface PrivilegesFromEs { delete_index: boolean; } @@ -27,20 +27,13 @@ type Privileges = PrivilegesFromEs; export type HealthFromEs = 'GREEN' | 'YELLOW' | 'RED'; -export interface DataStreamFromEs { - name: string; - timestamp_field: TimestampFieldFromEs; - indices: DataStreamIndexFromEs[]; - generation: number; - _meta?: MetaFromEs; - status: HealthFromEs; - template: string; - ilm_policy?: string; - store_size?: string; - store_size_bytes?: number; - maximum_timestamp?: number; - privileges: PrivilegesFromEs; - hidden: boolean; +export interface EnhancedDataStreamFromEs extends IndicesDataStream { + store_size?: IndicesDataStreamsStatsDataStreamsStatsItem['store_size']; + store_size_bytes?: IndicesDataStreamsStatsDataStreamsStatsItem['store_size_bytes']; + maximum_timestamp?: IndicesDataStreamsStatsDataStreamsStatsItem['maximum_timestamp']; + privileges: { + delete_index: boolean; + }; } export interface DataStreamIndexFromEs { @@ -58,12 +51,13 @@ export interface DataStream { health: Health; indexTemplateName: string; ilmPolicyName?: string; - storageSize?: string; + storageSize?: ByteSize; storageSizeBytes?: number; maxTimeStamp?: number; - _meta?: Meta; + _meta?: Metadata; privileges: Privileges; hidden: boolean; + lifecycle?: IndicesDataLifecycleWithRollover; } export interface DataStreamIndex { diff --git a/x-pack/plugins/index_management/common/types/index.ts b/x-pack/plugins/index_management/common/types/index.ts index ce5d96a842366..f3f2315cdd15b 100644 --- a/x-pack/plugins/index_management/common/types/index.ts +++ b/x-pack/plugins/index_management/common/types/index.ts @@ -13,7 +13,7 @@ export * from './mappings'; export * from './templates'; -export type { DataStreamFromEs, Health, DataStream, DataStreamIndex } from './data_streams'; +export type { EnhancedDataStreamFromEs, Health, DataStream, DataStreamIndex } from './data_streams'; export * from './component_templates'; diff --git a/x-pack/plugins/index_management/public/application/sections/home/data_stream_list/data_stream_detail_panel/data_stream_detail_panel.tsx b/x-pack/plugins/index_management/public/application/sections/home/data_stream_list/data_stream_detail_panel/data_stream_detail_panel.tsx index d3d8b0bc64baf..28fc4a5f15218 100644 --- a/x-pack/plugins/index_management/public/application/sections/home/data_stream_list/data_stream_detail_panel/data_stream_detail_panel.tsx +++ b/x-pack/plugins/index_management/public/application/sections/home/data_stream_list/data_stream_detail_panel/data_stream_detail_panel.tsx @@ -5,7 +5,7 @@ * 2.0. */ -import React, { useState } from 'react'; +import React, { useState, Fragment } from 'react'; import { i18n } from '@kbn/i18n'; import { EuiButton, @@ -41,24 +41,16 @@ interface DetailsListProps { name: string; toolTip: string; content: any; + dataTestSubj: string; }>; } const DetailsList: React.FunctionComponent = ({ details }) => { - const groups: any[] = []; - let items: any[]; + const descriptionListItems = details.map((detail, index) => { + const { name, toolTip, content, dataTestSubj } = detail; - details.forEach((detail, index) => { - const { name, toolTip, content } = detail; - - if (index % 2 === 0) { - items = []; - - groups.push({items}); - } - - items.push( - + return ( + {name} @@ -69,12 +61,27 @@ const DetailsList: React.FunctionComponent = ({ details }) => - {content} - + + {content} + + ); }); - return {groups}; + const midpoint = Math.ceil(descriptionListItems.length / 2); + const descriptionListColumnOne = descriptionListItems.slice(0, midpoint); + const descriptionListColumnTwo = descriptionListItems.slice(-midpoint); + + return ( + + + {descriptionListColumnOne} + + + {descriptionListColumnTwo} + + + ); }; interface Props { @@ -123,23 +130,64 @@ export const DataStreamDetailPanel: React.FunctionComponent = ({ ilmPolicyName, storageSize, maxTimeStamp, + lifecycle, } = dataStream; - const details = [ + + const getManagementDetails = () => { + const managementDetails = []; + + if (lifecycle?.data_retention) { + managementDetails.push({ + name: i18n.translate('xpack.idxMgmt.dataStreamDetailPanel.dataRetentionTitle', { + defaultMessage: 'Data retention', + }), + toolTip: i18n.translate('xpack.idxMgmt.dataStreamDetailPanel.dataRetentionToolTip', { + defaultMessage: 'The amount of time to retain the data in the data stream.', + }), + content: lifecycle.data_retention, + dataTestSubj: 'dataRetentionDetail', + }); + } + + if (ilmPolicyName) { + managementDetails.push({ + name: i18n.translate('xpack.idxMgmt.dataStreamDetailPanel.ilmPolicyTitle', { + defaultMessage: 'Index lifecycle policy', + }), + toolTip: i18n.translate('xpack.idxMgmt.dataStreamDetailPanel.ilmPolicyToolTip', { + defaultMessage: `The index lifecycle policy that manages the data in the data stream.`, + }), + content: ilmPolicyLink ? ( + + {ilmPolicyName} + + ) : ( + ilmPolicyName + ), + dataTestSubj: 'ilmPolicyDetail', + }); + } + + return managementDetails; + }; + + const defaultDetails = [ { name: i18n.translate('xpack.idxMgmt.dataStreamDetailPanel.healthTitle', { defaultMessage: 'Health', }), toolTip: i18n.translate('xpack.idxMgmt.dataStreamDetailPanel.healthToolTip', { - defaultMessage: `The health of the data stream's current backing indices`, + defaultMessage: `The health of the data stream's current backing indices.`, }), content: , + dataTestSubj: 'healthDetail', }, { name: i18n.translate('xpack.idxMgmt.dataStreamDetailPanel.maxTimeStampTitle', { defaultMessage: 'Last updated', }), toolTip: i18n.translate('xpack.idxMgmt.dataStreamDetailPanel.maxTimeStampToolTip', { - defaultMessage: 'The most recent document to be added to the data stream', + defaultMessage: 'The most recent document to be added to the data stream.', }), content: maxTimeStamp ? ( humanizeTimeStamp(maxTimeStamp) @@ -150,22 +198,24 @@ export const DataStreamDetailPanel: React.FunctionComponent = ({ })} ), + dataTestSubj: 'lastUpdatedDetail', }, { name: i18n.translate('xpack.idxMgmt.dataStreamDetailPanel.storageSizeTitle', { defaultMessage: 'Storage size', }), toolTip: i18n.translate('xpack.idxMgmt.dataStreamDetailPanel.storageSizeToolTip', { - defaultMessage: `Total size of all shards in the data stream’s backing indices`, + defaultMessage: `The total size of all shards in the data stream’s backing indices.`, }), content: storageSize, + dataTestSubj: 'storageSizeDetail', }, { name: i18n.translate('xpack.idxMgmt.dataStreamDetailPanel.indicesTitle', { defaultMessage: 'Indices', }), toolTip: i18n.translate('xpack.idxMgmt.dataStreamDetailPanel.indicesToolTip', { - defaultMessage: `The data stream's current backing indices`, + defaultMessage: `The data stream's current backing indices.`, }), content: ( = ({ {indices.length} ), + dataTestSubj: 'indicesDetail', }, { name: i18n.translate('xpack.idxMgmt.dataStreamDetailPanel.timestampFieldTitle', { defaultMessage: 'Timestamp field', }), toolTip: i18n.translate('xpack.idxMgmt.dataStreamDetailPanel.timestampFieldToolTip', { - defaultMessage: 'Timestamp field shared by all documents in the data stream', + defaultMessage: 'The timestamp field shared by all documents in the data stream.', }), content: timeStampField.name, + dataTestSubj: 'timestampDetail', }, { name: i18n.translate('xpack.idxMgmt.dataStreamDetailPanel.generationTitle', { defaultMessage: 'Generation', }), toolTip: i18n.translate('xpack.idxMgmt.dataStreamDetailPanel.generationToolTip', { - defaultMessage: 'Cumulative count of backing indices created for the data stream', + defaultMessage: 'The number of backing indices generated for the data stream.', }), content: generation, + dataTestSubj: 'generationDetail', }, { name: i18n.translate('xpack.idxMgmt.dataStreamDetailPanel.indexTemplateTitle', { @@ -202,7 +255,7 @@ export const DataStreamDetailPanel: React.FunctionComponent = ({ }), toolTip: i18n.translate('xpack.idxMgmt.dataStreamDetailPanel.indexTemplateToolTip', { defaultMessage: - 'The index template that configured the data stream and configures its backing indices', + 'The index template that configured the data stream and configures its backing indices.', }), content: ( = ({ {indexTemplateName} ), - }, - { - name: i18n.translate('xpack.idxMgmt.dataStreamDetailPanel.ilmPolicyTitle', { - defaultMessage: 'Index lifecycle policy', - }), - toolTip: i18n.translate('xpack.idxMgmt.dataStreamDetailPanel.ilmPolicyToolTip', { - defaultMessage: `The index lifecycle policy that manages the data stream's data`, - }), - content: - ilmPolicyName && ilmPolicyLink ? ( - - {ilmPolicyName} - - ) : ( - ilmPolicyName || ( - - {i18n.translate('xpack.idxMgmt.dataStreamDetailPanel.ilmPolicyContentNoneMessage', { - defaultMessage: `None`, - })} - - ) - ), + dataTestSubj: 'indexTemplateDetail', }, ]; + const managementDetails = getManagementDetails(); + const details = [...defaultDetails, ...managementDetails]; + content = ; } diff --git a/x-pack/plugins/index_management/public/application/sections/home/data_stream_list/data_stream_table/data_stream_table.tsx b/x-pack/plugins/index_management/public/application/sections/home/data_stream_list/data_stream_table/data_stream_table.tsx index 8e070ae7fe125..d67922f3cdcbc 100644 --- a/x-pack/plugins/index_management/public/application/sections/home/data_stream_list/data_stream_table/data_stream_table.tsx +++ b/x-pack/plugins/index_management/public/application/sections/home/data_stream_list/data_stream_table/data_stream_table.tsx @@ -8,7 +8,14 @@ import React, { useState, Fragment } from 'react'; import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n-react'; -import { EuiInMemoryTable, EuiBasicTableColumn, EuiButton, EuiLink } from '@elastic/eui'; +import { + EuiInMemoryTable, + EuiBasicTableColumn, + EuiButton, + EuiLink, + EuiIcon, + EuiToolTip, +} from '@elastic/eui'; import { ScopedHistory } from '@kbn/core/public'; import { DataStream } from '../../../../../../common/types'; @@ -71,7 +78,6 @@ export const DataStreamTable: React.FunctionComponent = ({ render: (health: DataStream['health']) => { return ; }, - width: '100px', }); if (includeStats) { @@ -80,7 +86,6 @@ export const DataStreamTable: React.FunctionComponent = ({ name: i18n.translate('xpack.idxMgmt.dataStreamList.table.maxTimeStampColumnTitle', { defaultMessage: 'Last updated', }), - width: '300px', truncateText: true, sortable: true, render: (maxTimeStamp: DataStream['maxTimeStamp']) => @@ -120,6 +125,28 @@ export const DataStreamTable: React.FunctionComponent = ({ ), }); + columns.push({ + field: 'lifecycle', + name: ( + + + {i18n.translate('xpack.idxMgmt.dataStreamList.table.dataRetentionColumnTitle', { + defaultMessage: 'Data retention', + })}{' '} + + + + ), + truncateText: true, + sortable: true, + render: (lifecycle: DataStream['lifecycle']) => lifecycle?.data_retention, + }); + columns.push({ name: i18n.translate('xpack.idxMgmt.dataStreamList.table.actionColumnTitle', { defaultMessage: 'Actions', diff --git a/x-pack/plugins/index_management/server/routes/api/data_streams/register_get_route.ts b/x-pack/plugins/index_management/server/routes/api/data_streams/register_get_route.ts index a562cc5f4cc52..0b33fb2a2b5ad 100644 --- a/x-pack/plugins/index_management/server/routes/api/data_streams/register_get_route.ts +++ b/x-pack/plugins/index_management/server/routes/api/data_streams/register_get_route.ts @@ -8,55 +8,28 @@ import { schema, TypeOf } from '@kbn/config-schema'; import { IScopedClusterClient } from '@kbn/core/server'; +import { + IndicesDataStream, + IndicesDataStreamsStatsDataStreamsStatsItem, + SecurityHasPrivilegesResponse, +} from '@elastic/elasticsearch/lib/api/types'; import { deserializeDataStream, deserializeDataStreamList } from '../../../../common/lib'; -import { DataStreamFromEs } from '../../../../common/types'; +import { EnhancedDataStreamFromEs } from '../../../../common/types'; import { RouteDependencies } from '../../../types'; import { addBasePath } from '..'; -interface PrivilegesFromEs { - username: string; - has_all_requested: boolean; - cluster: Record; - index: Record>; - application: Record; -} - -interface StatsFromEs { - data_stream: string; - store_size: string; - store_size_bytes: number; - maximum_timestamp: number; -} - const enhanceDataStreams = ({ dataStreams, dataStreamsStats, dataStreamsPrivileges, }: { - dataStreams: DataStreamFromEs[]; - dataStreamsStats?: StatsFromEs[]; - dataStreamsPrivileges?: PrivilegesFromEs; -}): DataStreamFromEs[] => { - return dataStreams.map((dataStream: DataStreamFromEs) => { - let enhancedDataStream = { ...dataStream }; - - if (dataStreamsStats) { - // eslint-disable-next-line @typescript-eslint/naming-convention - const { store_size, store_size_bytes, maximum_timestamp } = - dataStreamsStats.find( - ({ data_stream: statsName }: { data_stream: string }) => statsName === dataStream.name - ) || {}; - - enhancedDataStream = { - ...enhancedDataStream, - store_size, - store_size_bytes, - maximum_timestamp, - }; - } - - enhancedDataStream = { - ...enhancedDataStream, + dataStreams: IndicesDataStream[]; + dataStreamsStats?: IndicesDataStreamsStatsDataStreamsStatsItem[]; + dataStreamsPrivileges?: SecurityHasPrivilegesResponse; +}): EnhancedDataStreamFromEs[] => { + return dataStreams.map((dataStream) => { + const enhancedDataStream: EnhancedDataStreamFromEs = { + ...dataStream, privileges: { delete_index: dataStreamsPrivileges ? dataStreamsPrivileges.index[dataStream.name].delete_index @@ -64,6 +37,17 @@ const enhanceDataStreams = ({ }, }; + if (dataStreamsStats) { + const currentDataStreamStats: IndicesDataStreamsStatsDataStreamsStatsItem | undefined = + dataStreamsStats.find(({ data_stream: statsName }) => statsName === dataStream.name); + + if (currentDataStreamStats) { + enhancedDataStream.store_size = currentDataStreamStats.store_size; + enhancedDataStream.store_size_bytes = currentDataStreamStats.store_size_bytes; + enhancedDataStream.maximum_timestamp = currentDataStreamStats.maximum_timestamp; + } + } + return enhancedDataStream; }); }; @@ -125,11 +109,8 @@ export function registerGetAllRoute({ router, lib: { handleEsError }, config }: } const enhancedDataStreams = enhanceDataStreams({ - // @ts-expect-error DataStreamFromEs conflicts with @elastic/elasticsearch IndicesGetDataStreamIndicesGetDataStreamItem dataStreams, - // @ts-expect-error StatsFromEs conflicts with @elastic/elasticsearch IndicesDataStreamsStatsDataStreamsStatsItem dataStreamsStats, - // @ts-expect-error PrivilegesFromEs conflicts with @elastic/elasticsearch ApplicationsPrivileges dataStreamsPrivileges, }); @@ -164,11 +145,8 @@ export function registerGetOneRoute({ router, lib: { handleEsError }, config }: } const enhancedDataStreams = enhanceDataStreams({ - // @ts-expect-error DataStreamFromEs conflicts with @elastic/elasticsearch IndicesGetDataStreamIndicesGetDataStreamItem dataStreams, - // @ts-expect-error StatsFromEs conflicts with @elastic/elasticsearch IndicesDataStreamsStatsDataStreamsStatsItem dataStreamsStats, - // @ts-expect-error PrivilegesFromEs conflicts with @elastic/elasticsearch ApplicationsPrivileges dataStreamsPrivileges, }); const body = deserializeDataStream(enhancedDataStreams[0]); diff --git a/x-pack/plugins/translations/translations/fr-FR.json b/x-pack/plugins/translations/translations/fr-FR.json index fb4219e6052f4..50e6763aad805 100644 --- a/x-pack/plugins/translations/translations/fr-FR.json +++ b/x-pack/plugins/translations/translations/fr-FR.json @@ -17347,7 +17347,6 @@ "xpack.idxMgmt.dataStreamDetailPanel.generationToolTip": "Nombre cumulatif d'index de sauvegarde créés pour le flux de données", "xpack.idxMgmt.dataStreamDetailPanel.healthTitle": "Intégrité", "xpack.idxMgmt.dataStreamDetailPanel.healthToolTip": "Intégrité des index de sauvegarde actuels du flux de données", - "xpack.idxMgmt.dataStreamDetailPanel.ilmPolicyContentNoneMessage": "Aucun", "xpack.idxMgmt.dataStreamDetailPanel.ilmPolicyTitle": "Stratégie de cycle de vie des index", "xpack.idxMgmt.dataStreamDetailPanel.ilmPolicyToolTip": "Stratégie de cycle de vie de l'index qui gère les données du flux de données", "xpack.idxMgmt.dataStreamDetailPanel.indexTemplateTitle": "Modèle d'index", diff --git a/x-pack/plugins/translations/translations/ja-JP.json b/x-pack/plugins/translations/translations/ja-JP.json index 9c1bd25593a47..d2b3c60becf20 100644 --- a/x-pack/plugins/translations/translations/ja-JP.json +++ b/x-pack/plugins/translations/translations/ja-JP.json @@ -17361,7 +17361,6 @@ "xpack.idxMgmt.dataStreamDetailPanel.generationToolTip": "データストリームに作成されたバッキングインデックスの累積数", "xpack.idxMgmt.dataStreamDetailPanel.healthTitle": "ヘルス", "xpack.idxMgmt.dataStreamDetailPanel.healthToolTip": "データストリームの現在のバッキングインデックスのヘルス", - "xpack.idxMgmt.dataStreamDetailPanel.ilmPolicyContentNoneMessage": "なし", "xpack.idxMgmt.dataStreamDetailPanel.ilmPolicyTitle": "インデックスライフサイクルポリシー", "xpack.idxMgmt.dataStreamDetailPanel.ilmPolicyToolTip": "データストリームのデータを管理するインデックスライフサイクルポリシー", "xpack.idxMgmt.dataStreamDetailPanel.indexTemplateTitle": "インデックステンプレート", diff --git a/x-pack/plugins/translations/translations/zh-CN.json b/x-pack/plugins/translations/translations/zh-CN.json index 75fcb531da66a..21a7856a7baa3 100644 --- a/x-pack/plugins/translations/translations/zh-CN.json +++ b/x-pack/plugins/translations/translations/zh-CN.json @@ -17361,7 +17361,6 @@ "xpack.idxMgmt.dataStreamDetailPanel.generationToolTip": "为数据流创建的后备索引的累积计数", "xpack.idxMgmt.dataStreamDetailPanel.healthTitle": "运行状况", "xpack.idxMgmt.dataStreamDetailPanel.healthToolTip": "数据流的当前后备索引的运行状况", - "xpack.idxMgmt.dataStreamDetailPanel.ilmPolicyContentNoneMessage": "无", "xpack.idxMgmt.dataStreamDetailPanel.ilmPolicyTitle": "索引生命周期策略", "xpack.idxMgmt.dataStreamDetailPanel.ilmPolicyToolTip": "用于管理数据流数据的索引生命周期策略", "xpack.idxMgmt.dataStreamDetailPanel.indexTemplateTitle": "索引模板", diff --git a/x-pack/test/api_integration/apis/management/index_management/data_streams.ts b/x-pack/test/api_integration/apis/management/index_management/data_streams.ts index 0117204343285..520396ad46283 100644 --- a/x-pack/test/api_integration/apis/management/index_management/data_streams.ts +++ b/x-pack/test/api_integration/apis/management/index_management/data_streams.ts @@ -112,6 +112,9 @@ export default function ({ getService }: FtrProviderContext) { expect(testDataStream).to.eql({ name: testDataStreamName, + lifecycle: { + enabled: true, + }, privileges: { delete_index: true, }, @@ -166,6 +169,9 @@ export default function ({ getService }: FtrProviderContext) { indexTemplateName: testDataStreamName, maxTimeStamp: 0, hidden: false, + lifecycle: { + enabled: true, + }, }); }); @@ -197,6 +203,9 @@ export default function ({ getService }: FtrProviderContext) { indexTemplateName: testDataStreamName, maxTimeStamp: 0, hidden: false, + lifecycle: { + enabled: true, + }, }); }); });