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

Add Managed label to data streams and a view switch for the table #83049

Merged
merged 11 commits into from
Nov 18, 2020
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
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 @@ -21,6 +21,7 @@ export type TestSubjects =
| 'filterList.filterItem'
| 'ilmPolicyLink'
| 'includeStatsSwitch'
| 'viewManagedSwitch'
| 'indexTable'
| 'indexTableIncludeHiddenIndicesToggle'
| 'indexTableIndexNameLink'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ export interface DataStreamsTabTestBed extends TestBed<TestSubjects> {
goToDataStreamsList: () => void;
clickEmptyPromptIndexTemplateLink: () => void;
clickIncludeStatsSwitch: () => void;
clickViewManagedSwitch: () => void;
clickReloadButton: () => void;
clickNameAt: (index: number) => void;
clickIndicesAt: (index: number) => void;
Expand Down Expand Up @@ -83,6 +84,11 @@ export const setup = async (overridingDependencies: any = {}): Promise<DataStrea
find('includeStatsSwitch').simulate('click');
};

const clickViewManagedSwitch = () => {
const { find } = testBed;
find('viewManagedSwitch').simulate('click');
};

const clickReloadButton = () => {
const { find } = testBed;
find('reloadButton').simulate('click');
Expand Down Expand Up @@ -170,6 +176,7 @@ export const setup = async (overridingDependencies: any = {}): Promise<DataStrea
goToDataStreamsList,
clickEmptyPromptIndexTemplateLink,
clickIncludeStatsSwitch,
clickViewManagedSwitch,
clickReloadButton,
clickNameAt,
clickIndicesAt,
Expand All @@ -187,8 +194,8 @@ export const setup = async (overridingDependencies: any = {}): Promise<DataStrea
};
};

export const createDataStreamPayload = (name: string): DataStream => ({
name,
export const createDataStreamPayload = (dataStream: Partial<DataStream>): DataStream => ({
name: 'my-data-stream',
timeStampField: { name: '@timestamp' },
indices: [
{
Expand All @@ -201,6 +208,7 @@ export const createDataStreamPayload = (name: string): DataStream => ({
indexTemplateName: 'indexTemplate',
storageSize: '1b',
maxTimeStamp: 420,
...dataStream,
});

export const createDataStreamBackingIndex = (indexName: string, dataStreamName: string) => ({
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -96,10 +96,10 @@ describe('Data Streams tab', () => {
createNonDataStreamIndex('non-data-stream-index'),
]);

const dataStreamForDetailPanel = createDataStreamPayload('dataStream1');
const dataStreamForDetailPanel = createDataStreamPayload({ name: 'dataStream1' });
setLoadDataStreamsResponse([
dataStreamForDetailPanel,
createDataStreamPayload('dataStream2'),
createDataStreamPayload({ name: 'dataStream2' }),
]);
setLoadDataStreamResponse(dataStreamForDetailPanel);

Expand Down Expand Up @@ -260,9 +260,9 @@ describe('Data Streams tab', () => {
createDataStreamBackingIndex('data-stream-index2', 'dataStream2'),
]);

const dataStreamDollarSign = createDataStreamPayload('%dataStream');
setLoadDataStreamsResponse([dataStreamDollarSign]);
setLoadDataStreamResponse(dataStreamDollarSign);
const dataStreamPercentSign = createDataStreamPayload({ name: '%dataStream' });
setLoadDataStreamsResponse([dataStreamPercentSign]);
setLoadDataStreamResponse(dataStreamPercentSign);

testBed = await setup({
history: createMemoryHistory(),
Expand Down Expand Up @@ -300,10 +300,10 @@ describe('Data Streams tab', () => {
test('with an ILM url generator and an ILM policy', async () => {
const { setLoadDataStreamsResponse, setLoadDataStreamResponse } = httpRequestsMockHelpers;

const dataStreamForDetailPanel = {
...createDataStreamPayload('dataStream1'),
const dataStreamForDetailPanel = createDataStreamPayload({
name: 'dataStream1',
ilmPolicyName: 'my_ilm_policy',
};
});
setLoadDataStreamsResponse([dataStreamForDetailPanel]);
setLoadDataStreamResponse(dataStreamForDetailPanel);

Expand All @@ -324,7 +324,7 @@ describe('Data Streams tab', () => {
test('with an ILM url generator and no ILM policy', async () => {
const { setLoadDataStreamsResponse, setLoadDataStreamResponse } = httpRequestsMockHelpers;

const dataStreamForDetailPanel = createDataStreamPayload('dataStream1');
const dataStreamForDetailPanel = createDataStreamPayload({ name: 'dataStream1' });
setLoadDataStreamsResponse([dataStreamForDetailPanel]);
setLoadDataStreamResponse(dataStreamForDetailPanel);

Expand All @@ -346,10 +346,10 @@ describe('Data Streams tab', () => {
test('without an ILM url generator and with an ILM policy', async () => {
const { setLoadDataStreamsResponse, setLoadDataStreamResponse } = httpRequestsMockHelpers;

const dataStreamForDetailPanel = {
...createDataStreamPayload('dataStream1'),
const dataStreamForDetailPanel = createDataStreamPayload({
name: 'dataStream1',
ilmPolicyName: 'my_ilm_policy',
};
});
setLoadDataStreamsResponse([dataStreamForDetailPanel]);
setLoadDataStreamResponse(dataStreamForDetailPanel);

Expand All @@ -368,4 +368,58 @@ describe('Data Streams tab', () => {
expect(findDetailPanelIlmPolicyName().contains('my_ilm_policy')).toBeTruthy();
});
});

describe('managed data streams', () => {
const nonBreakingSpace = ' ';
beforeEach(async () => {
const managedDataStream = createDataStreamPayload({
name: 'managed-data-stream',
_meta: {
package: 'test',
managed: true,
managed_by: 'ingest-manager',
},
});
const nonManagedDataStream = createDataStreamPayload({ name: 'non-managed-data-stream' });
httpRequestsMockHelpers.setLoadDataStreamsResponse([managedDataStream, nonManagedDataStream]);

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

test('listed in the table with Managed label', () => {
const { table } = testBed;
const { tableCellsValues } = table.getMetaData('dataStreamTable');

expect(tableCellsValues).toEqual([
['', `managed-data-stream${nonBreakingSpace}Managed`, 'green', '1', 'Delete'],
['', 'non-managed-data-stream', 'green', '1', 'Delete'],
]);
});

test('turning of "View managed" switch hides managed data streams', async () => {
yuliacech marked this conversation as resolved.
Show resolved Hide resolved
const { exists, actions, component, table } = testBed;
let { tableCellsValues } = table.getMetaData('dataStreamTable');

expect(tableCellsValues).toEqual([
['', `managed-data-stream${nonBreakingSpace}Managed`, 'green', '1', 'Delete'],
['', 'non-managed-data-stream', 'green', '1', 'Delete'],
]);

expect(exists('viewManagedSwitch')).toBe(true);

await act(async () => {
actions.clickViewManagedSwitch();
});
component.update();

({ tableCellsValues } = table.getMetaData('dataStreamTable'));
expect(tableCellsValues).toEqual([['', 'non-managed-data-stream', 'green', '1', 'Delete']]);
});
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,9 @@ describe('<IndexManagementHome />', () => {
// The detail panel should still appear even if there are no data streams.
httpRequestsMockHelpers.setLoadDataStreamsResponse([]);

httpRequestsMockHelpers.setLoadDataStreamResponse(createDataStreamPayload('dataStream1'));
httpRequestsMockHelpers.setLoadDataStreamResponse(
createDataStreamPayload({ name: 'dataStream1' })
);

testBed = await setup({
history: createMemoryHistory(),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ export function deserializeDataStream(dataStreamFromEs: DataStreamFromEs): DataS
ilm_policy: ilmPolicyName,
store_size: storageSize,
maximum_timestamp: maxTimeStamp,
_meta,
} = dataStreamFromEs;

return {
Expand All @@ -35,6 +36,7 @@ export function deserializeDataStream(dataStreamFromEs: DataStreamFromEs): DataS
ilmPolicyName,
storageSize,
maxTimeStamp,
_meta,
};
}

Expand Down
10 changes: 10 additions & 0 deletions x-pack/plugins/index_management/common/types/data_streams.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,22 @@ interface TimestampFieldFromEs {

type TimestampField = TimestampFieldFromEs;

interface MetaFieldFromEs {
managed_by: string;
package: any;
managed: boolean;
}

type MetaField = MetaFieldFromEs;

export type HealthFromEs = 'GREEN' | 'YELLOW' | 'RED';

export interface DataStreamFromEs {
name: string;
timestamp_field: TimestampFieldFromEs;
indices: DataStreamIndexFromEs[];
generation: number;
_meta?: MetaFieldFromEs;
status: HealthFromEs;
template: string;
ilm_policy?: string;
Expand All @@ -41,6 +50,7 @@ export interface DataStream {
ilmPolicyName?: string;
storageSize?: string;
maxTimeStamp?: number;
_meta?: MetaField;
}

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

import { DataStream } from '../../../common';

export const isManagedByIngestManager = (dataStream: DataStream): boolean => {
return Boolean(dataStream._meta?.managed && dataStream._meta?.managed_by === 'ingest-manager');
};

export const filterDataStreams = (dataStreams: DataStream[], managed: boolean): DataStream[] => {
return dataStreams.filter((dataStream: DataStream) =>
managed ? isManagedByIngestManager(dataStream) : !isManagedByIngestManager(dataStream)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a little hard for me to parse. If I'm understanding correctly, it looks like managed is only ever going to be false.

const filteredDataStreams = isIncludeManagedChecked
      ? dataStreams
      : filterDataStreams(dataStreams, isIncludeManagedChecked);

If that's correct, could the filter be simplified to this?

return dataStreams.filter((dataStream: DataStream) => isManagedByIngestManager(dataStream) === false)

);
};
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ import { documentationService } from '../../../services/documentation';
import { Section } from '../home';
import { DataStreamTable } from './data_stream_table';
import { DataStreamDetailPanel } from './data_stream_detail_panel';
import { filterDataStreams } from '../../../lib/data_streams';

interface MatchParams {
dataStreamName?: string;
Expand All @@ -52,6 +53,7 @@ export const DataStreamList: React.FunctionComponent<RouteComponentProps<MatchPa
} = useAppContext();

const [isIncludeStatsChecked, setIsIncludeStatsChecked] = useState(false);
const [isIncludeManagedChecked, setIsIncludeManagedChecked] = useState(true);
const { error, isLoading, data: dataStreams, resendRequest: reload } = useLoadDataStreams({
includeStats: isIncludeStatsChecked,
});
Expand Down Expand Up @@ -147,11 +149,13 @@ export const DataStreamList: React.FunctionComponent<RouteComponentProps<MatchPa
/>
);
} else if (Array.isArray(dataStreams) && dataStreams.length > 0) {
const filteredDataStreams = isIncludeManagedChecked
? dataStreams
: filterDataStreams(dataStreams, isIncludeManagedChecked);
content = (
<>
<EuiFlexGroup alignItems="center" justifyContent="spaceBetween">
<EuiFlexItem>
{/* TODO: Add a switch for toggling on data streams created by Ingest Manager */}
<EuiText color="subdued">
<FormattedMessage
id="xpack.idxMgmt.dataStreamList.dataStreamsDescription"
Expand Down Expand Up @@ -202,6 +206,16 @@ export const DataStreamList: React.FunctionComponent<RouteComponentProps<MatchPa
</EuiFlexItem>
</EuiFlexGroup>
</EuiFlexItem>
<EuiFlexItem grow={false}>
<EuiSwitch
label={i18n.translate('xpack.idxMgmt.dataStreamListControls.viewManagedSwitchLabel', {
defaultMessage: 'View managed',
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Screen Shot 2020-11-12 at 1 03 02 PM

WDYT about changing this to "Include managed" to align with the other toggle?

})}
checked={isIncludeManagedChecked}
onChange={(e) => setIsIncludeManagedChecked(e.target.checked)}
data-test-subj="viewManagedSwitch"
/>
</EuiFlexItem>
</EuiFlexGroup>

<EuiSpacer size="l" />
Expand All @@ -212,7 +226,7 @@ export const DataStreamList: React.FunctionComponent<RouteComponentProps<MatchPa
? `name="${attemptToURIDecode(dataStreamName)}"`
: ''
}
dataStreams={dataStreams}
dataStreams={filteredDataStreams}
reload={reload}
history={history as ScopedHistory}
includeStats={isIncludeStatsChecked}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,10 @@
* you may not use this file except in compliance with the Elastic License.
*/

import React, { useState } from 'react';
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, EuiBadge } from '@elastic/eui';
import { ScopedHistory } from 'kibana/public';

import { DataStream } from '../../../../../../common/types';
Expand All @@ -16,6 +16,7 @@ import { getDataStreamDetailsLink, getIndexListUri } from '../../../../services/
import { DataHealth } from '../../../../components';
import { DeleteDataStreamConfirmationModal } from '../delete_data_stream_confirmation_modal';
import { humanizeTimeStamp } from '../humanize_time_stamp';
import { isManagedByIngestManager } from '../../../../lib/data_streams';

interface Props {
dataStreams?: DataStream[];
Expand Down Expand Up @@ -44,14 +45,27 @@ export const DataStreamTable: React.FunctionComponent<Props> = ({
}),
truncateText: true,
sortable: true,
render: (name: DataStream['name']) => {
render: (name: DataStream['name'], dataStream: DataStream) => {
return (
<EuiLink
data-test-subj="nameLink"
{...reactRouterNavigate(history, getDataStreamDetailsLink(name))}
>
{name}
</EuiLink>
<Fragment>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(nit) you can use shorthand <></> for fragments.

<EuiLink
data-test-subj="nameLink"
{...reactRouterNavigate(history, getDataStreamDetailsLink(name))}
>
{name}
</EuiLink>
{isManagedByIngestManager(dataStream) ? (
<Fragment>
&nbsp;
<EuiBadge color="hollow">
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What do you think about adding a tooltip or title to the badge to explain that "managed" means managed by Ingest Manager, specifically? Or maybe add this to the switch?

<FormattedMessage
id="xpack.idxMgmt.dataStreamList.table.managedDataStreamBadge"
defaultMessage="Managed"
/>
</EuiBadge>
</Fragment>
) : null}
</Fragment>
);
},
});
Expand Down Expand Up @@ -121,7 +135,7 @@ export const DataStreamTable: React.FunctionComponent<Props> = ({
name: i18n.translate('xpack.idxMgmt.dataStreamList.table.actionDeleteText', {
defaultMessage: 'Delete',
}),
description: i18n.translate('xpack.idxMgmt.dataStreamList.table.actionDeleteDecription', {
description: i18n.translate('xpack.idxMgmt.dataStreamList.table.actionDeleteDescription', {
defaultMessage: 'Delete this data stream',
}),
icon: 'trash',
Expand Down
1 change: 0 additions & 1 deletion x-pack/plugins/translations/translations/ja-JP.json
Original file line number Diff line number Diff line change
Expand Up @@ -8144,7 +8144,6 @@
"xpack.idxMgmt.dataStreamList.loadingDataStreamsErrorMessage": "データストリームの読み込み中にエラーが発生",
"xpack.idxMgmt.dataStreamList.reloadDataStreamsButtonLabel": "再読み込み",
"xpack.idxMgmt.dataStreamList.table.actionColumnTitle": "アクション",
"xpack.idxMgmt.dataStreamList.table.actionDeleteDecription": "このデータストリームを削除",
"xpack.idxMgmt.dataStreamList.table.actionDeleteText": "削除",
"xpack.idxMgmt.dataStreamList.table.deleteDataStreamsButtonLabel": "{count, plural, one {個のデータストリーム} other {個のデータストリーム}}を削除",
"xpack.idxMgmt.dataStreamList.table.healthColumnTitle": "ヘルス",
Expand Down
1 change: 0 additions & 1 deletion x-pack/plugins/translations/translations/zh-CN.json
Original file line number Diff line number Diff line change
Expand Up @@ -8152,7 +8152,6 @@
"xpack.idxMgmt.dataStreamList.loadingDataStreamsErrorMessage": "加载数据流时出错",
"xpack.idxMgmt.dataStreamList.reloadDataStreamsButtonLabel": "重新加载",
"xpack.idxMgmt.dataStreamList.table.actionColumnTitle": "操作",
"xpack.idxMgmt.dataStreamList.table.actionDeleteDecription": "删除此数据流",
"xpack.idxMgmt.dataStreamList.table.actionDeleteText": "删除",
"xpack.idxMgmt.dataStreamList.table.deleteDataStreamsButtonLabel": "删除{count, plural, one {数据流} other {数据流} }",
"xpack.idxMgmt.dataStreamList.table.healthColumnTitle": "运行状况",
Expand Down