From 11407572f40cc2bd3cf9a804e1b673d82d3fb2d2 Mon Sep 17 00:00:00 2001 From: Jean-Louis Leysens Date: Tue, 2 Jun 2020 13:31:16 +0200 Subject: [PATCH 01/10] First iteration of data streams in index management - Added the data streams column - Moved state down to component (using withRouter) - Removed previous middleware for syncing url hash param - call history.push to update the url in the component --- .../sections/home/index_list/index_list.js | 3 +- .../index_table/index_table.container.js | 21 +++---- .../index_list/index_table/index_table.js | 56 +++++++++++++------ .../store/actions/reload_indices.js | 4 +- .../application/store/actions/table_state.js | 4 -- .../application/store/middlewares/index.ts | 7 --- .../sync_url_hash_query_param.js.ts | 22 -------- .../application/store/reducers/table_state.js | 10 ---- .../application/store/selectors/index.js | 38 ++++++++----- .../public/application/store/store.js | 3 +- .../server/lib/fetch_indices.ts | 1 + .../plugins/index_management/server/types.ts | 1 + 12 files changed, 76 insertions(+), 94 deletions(-) delete mode 100644 x-pack/plugins/index_management/public/application/store/middlewares/index.ts delete mode 100644 x-pack/plugins/index_management/public/application/store/middlewares/sync_url_hash_query_param.js.ts diff --git a/x-pack/plugins/index_management/public/application/sections/home/index_list/index_list.js b/x-pack/plugins/index_management/public/application/sections/home/index_list/index_list.js index 4b58ff0de17ee..36999bc40e2a1 100644 --- a/x-pack/plugins/index_management/public/application/sections/home/index_list/index_list.js +++ b/x-pack/plugins/index_management/public/application/sections/home/index_list/index_list.js @@ -12,11 +12,10 @@ export function IndexList({ match: { params: { filter }, }, - location, }) { return (
- +
); diff --git a/x-pack/plugins/index_management/public/application/sections/home/index_list/index_table/index_table.container.js b/x-pack/plugins/index_management/public/application/sections/home/index_list/index_table/index_table.container.js index 44d811f490d9d..9a44a02a44f87 100644 --- a/x-pack/plugins/index_management/public/application/sections/home/index_list/index_table/index_table.container.js +++ b/x-pack/plugins/index_management/public/application/sections/home/index_list/index_table/index_table.container.js @@ -5,13 +5,13 @@ */ import { connect } from 'react-redux'; +import { withRouter } from 'react-router-dom'; import { getDetailPanelIndexName, getPageOfIndices, getPager, getFilter, isDetailPanelOpen, - showHiddenIndices, getSortField, isSortAscending, getIndicesAsArray, @@ -26,7 +26,6 @@ import { pageChanged, pageSizeChanged, sortChanged, - showHiddenIndicesChanged, loadIndices, reloadIndices, toggleChanged, @@ -34,15 +33,14 @@ import { import { IndexTable as PresentationComponent } from './index_table'; -const mapStateToProps = (state) => { +const mapStateToProps = (state, props) => { return { allIndices: getIndicesAsArray(state), isDetailPanelOpen: isDetailPanelOpen(state), detailPanelIndexName: getDetailPanelIndexName(state), - indices: getPageOfIndices(state), - pager: getPager(state), + indices: getPageOfIndices(state, props), + pager: getPager(state, props), filter: getFilter(state), - showHiddenIndices: showHiddenIndices(state), sortField: getSortField(state), isSortAscending: isSortAscending(state), indicesLoading: indicesLoading(state), @@ -65,9 +63,6 @@ const mapDispatchToProps = (dispatch) => { sortChanged: (sortField, isSortAscending) => { dispatch(sortChanged({ sortField, isSortAscending })); }, - showHiddenIndicesChanged: (showHiddenIndices) => { - dispatch(showHiddenIndicesChanged({ showHiddenIndices })); - }, toggleChanged: (toggleName, toggleValue) => { dispatch(toggleChanged({ toggleName, toggleValue })); }, @@ -80,10 +75,12 @@ const mapDispatchToProps = (dispatch) => { loadIndices: () => { dispatch(loadIndices()); }, - reloadIndices: () => { - dispatch(reloadIndices()); + reloadIndices: (indexNames) => { + dispatch(reloadIndices(indexNames)); }, }; }; -export const IndexTable = connect(mapStateToProps, mapDispatchToProps)(PresentationComponent); +export const IndexTable = withRouter( + connect(mapStateToProps, mapDispatchToProps)(PresentationComponent) +); diff --git a/x-pack/plugins/index_management/public/application/sections/home/index_list/index_table/index_table.js b/x-pack/plugins/index_management/public/application/sections/home/index_list/index_table/index_table.js index 0d005b2864863..dc96346a04d00 100644 --- a/x-pack/plugins/index_management/public/application/sections/home/index_list/index_table/index_table.js +++ b/x-pack/plugins/index_management/public/application/sections/home/index_list/index_table/index_table.js @@ -8,7 +8,7 @@ import React, { Component, Fragment } from 'react'; import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n/react'; import { Route } from 'react-router-dom'; -import { parse } from 'query-string'; +import * as qs from 'query-string'; import { EuiButton, @@ -66,6 +66,9 @@ const HEADERS = { size: i18n.translate('xpack.idxMgmt.indexTable.headers.storageSizeHeader', { defaultMessage: 'Storage size', }), + dataStream: i18n.translate('xpack.idxMgmt.indexTable.headers.dataStreamHeader', { + defaultMessage: 'Data stream', + }), }; export class IndexTable extends Component { @@ -91,20 +94,19 @@ export class IndexTable extends Component { super(props); this.state = { + showHiddenIndices: false, + dataStreamFilter: undefined, selectedIndicesMap: {}, }; } componentDidMount() { this.props.loadIndices(); - this.interval = setInterval(this.props.reloadIndices, REFRESH_RATE_INDEX_LIST); - const { - filterChanged, - filterFromURI, - showHiddenIndicesChanged, - showHiddenIndices, - location, - } = this.props; + this.interval = setInterval( + () => this.props.reloadIndices(this.props.indices.map((i) => i.name)), + REFRESH_RATE_INDEX_LIST + ); + const { filterChanged, filterFromURI } = this.props; if (filterFromURI) { const decodedFilter = decodeURIComponent(filterFromURI); @@ -116,17 +118,37 @@ export class IndexTable extends Component { this.setState({ filterError: e }); } } + this.syncUrlParams(); + } + componentWillUnmount() { + clearInterval(this.interval); + } + componentDidUpdate() { + this.syncUrlParams(); + } + + syncUrlParams() { + const { location } = this.props; // Check if the we have the includeHidden query param - const { includeHidden } = parse((location && location.search) || ''); + const { includeHidden } = qs.parse((location && location.search) || ''); const nextValue = includeHidden === 'true'; - if (nextValue !== showHiddenIndices) { - showHiddenIndicesChanged(nextValue); + if (this.state.includeHidden !== nextValue) { + this.setState({ includeHidden: nextValue }); } } - componentWillUnmount() { - clearInterval(this.interval); + + setIncludeHiddenParam(hidden) { + const { pathname, search } = this.props.location; + const params = qs.parse(search); + if (hidden) { + params.includeHidden = 'true'; + } else { + delete params.includeHidden; + } + this.props.history.push(pathname + '?' + qs.stringify(params)); } + onSort = (column) => { const { sortField, isSortAscending, sortChanged } = this.props; @@ -416,8 +438,6 @@ export class IndexTable extends Component { render() { const { filter, - showHiddenIndices, - showHiddenIndicesChanged, indices, loadIndices, indicesLoading, @@ -477,8 +497,8 @@ export class IndexTable extends Component { showHiddenIndicesChanged(event.target.checked)} + checked={this.state.includeHidden} + onChange={(event) => this.setIncludeHiddenParam(event.target.checked)} label={ async (dispatch, getState) => { +export const reloadIndices = (indexNames) => async (dispatch) => { let indices; - indexNames = indexNames || getIndexNamesForCurrentPage(getState()); try { indices = await request(indexNames); } catch (error) { diff --git a/x-pack/plugins/index_management/public/application/store/actions/table_state.js b/x-pack/plugins/index_management/public/application/store/actions/table_state.js index 70e0de74d0278..2127d8cd36b1e 100644 --- a/x-pack/plugins/index_management/public/application/store/actions/table_state.js +++ b/x-pack/plugins/index_management/public/application/store/actions/table_state.js @@ -17,8 +17,4 @@ export const pageSizeChanged = createAction('INDEX_MANAGEMENT_PAGE_SIZE_CHANGED' export const sortChanged = createAction('INDEX_MANAGEMENT_SORT_CHANGED'); -export const showHiddenIndicesChanged = createAction( - 'INDEX_MANAGEMENT_SHOW_HIDDEN_INDICES_CHANGED' -); - export const toggleChanged = createAction('INDEX_MANAGEMENT_TOGGLE_CHANGED'); diff --git a/x-pack/plugins/index_management/public/application/store/middlewares/index.ts b/x-pack/plugins/index_management/public/application/store/middlewares/index.ts deleted file mode 100644 index 06d77a9c3d348..0000000000000 --- a/x-pack/plugins/index_management/public/application/store/middlewares/index.ts +++ /dev/null @@ -1,7 +0,0 @@ -/* - * 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. - */ - -export { syncUrlHashQueryParam } from './sync_url_hash_query_param.js'; diff --git a/x-pack/plugins/index_management/public/application/store/middlewares/sync_url_hash_query_param.js.ts b/x-pack/plugins/index_management/public/application/store/middlewares/sync_url_hash_query_param.js.ts deleted file mode 100644 index 145b4b6c9a8bc..0000000000000 --- a/x-pack/plugins/index_management/public/application/store/middlewares/sync_url_hash_query_param.js.ts +++ /dev/null @@ -1,22 +0,0 @@ -/* - * 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 * as q from 'query-string'; -import { Middleware } from 'redux'; -// @ts-ignore -import { showHiddenIndicesChanged } from '../actions'; - -export const syncUrlHashQueryParam: Middleware = () => (next) => (action) => { - if (action.type === String(showHiddenIndicesChanged)) { - const { url, query } = q.parseUrl(window.location.hash); - if (action.payload.showHiddenIndices) { - query.includeHidden = 'true'; - } else { - delete query.includeHidden; - } - window.location.hash = url + '?' + q.stringify(query); - } - next(action); -}; diff --git a/x-pack/plugins/index_management/public/application/store/reducers/table_state.js b/x-pack/plugins/index_management/public/application/store/reducers/table_state.js index e90fa72aa62fe..bfb37aa56b129 100644 --- a/x-pack/plugins/index_management/public/application/store/reducers/table_state.js +++ b/x-pack/plugins/index_management/public/application/store/reducers/table_state.js @@ -10,7 +10,6 @@ import { pageChanged, pageSizeChanged, sortChanged, - showHiddenIndicesChanged, toggleChanged, } from '../actions'; @@ -20,7 +19,6 @@ export const defaultTableState = { currentPage: 0, sortField: 'index.name', isSortAscending: true, - showHiddenIndices: false, }; export const tableState = handleActions( @@ -33,14 +31,6 @@ export const tableState = handleActions( currentPage: 0, }; }, - [showHiddenIndicesChanged](state, action) { - const { showHiddenIndices } = action.payload; - - return { - ...state, - showHiddenIndices, - }; - }, [toggleChanged](state, action) { const { toggleName, toggleValue } = action.payload; const toggleNameToVisibleMap = { ...state.toggleNameToVisibleMap }; diff --git a/x-pack/plugins/index_management/public/application/store/selectors/index.js b/x-pack/plugins/index_management/public/application/store/selectors/index.js index c1011680d4da2..4518be5d200d5 100644 --- a/x-pack/plugins/index_management/public/application/store/selectors/index.js +++ b/x-pack/plugins/index_management/public/application/store/selectors/index.js @@ -6,6 +6,7 @@ import { Pager, EuiSearchBar } from '@elastic/eui'; import { createSelector } from 'reselect'; +import * as qs from 'query-string'; import { indexStatusLabels } from '../../lib/index_status_labels'; import { sortTable } from '../../services'; @@ -35,6 +36,7 @@ export const getIndexByIndexName = (state, name) => getIndices(state)[name]; export const getFilteredIds = (state) => state.indices.filteredIds; export const getRowStatuses = (state) => state.rowStatus; export const getTableState = (state) => state.tableState; +export const getTableLocationProp = (_, props) => props.location; export const getAllIds = (state) => state.indices.allIds; export const getIndexStatusByIndexName = (state, indexName) => { const indices = getIndices(state); @@ -79,18 +81,35 @@ const filterByToggles = (indices, toggleNameToVisibleMap) => { }); }); }; + const getFilteredIndices = createSelector( getIndices, getAllIds, getTableState, - (indices, allIds, tableState) => { + getTableLocationProp, + (indices, allIds, tableState, tableLocation) => { let indexArray = allIds.map((indexName) => indices[indexName]); indexArray = filterByToggles(indexArray, tableState.toggleNameToVisibleMap); - const systemFilteredIndexes = tableState.showHiddenIndices - ? indexArray - : indexArray.filter((index) => !(index.name + '').startsWith('.') && !index.hidden); + const { includeHidden: includeHiddenParam, dataStreams: dataStreamsParam } = qs.parse( + tableLocation.search + ); + const includeHidden = includeHiddenParam === 'true'; + const dataStreamsFilter = dataStreamsParam ? dataStreamsParam.split(',') : undefined; + const shouldFilterDataStreams = dataStreamsFilter && dataStreamsFilter.length; + const filteredIndices = indexArray.filter((index) => { + let include = true; + if (!includeHidden) { + include = !(index.name + '').startsWith('.') && !index.hidden; + } + + if (include && shouldFilterDataStreams) { + return dataStreamsFilter.includes(index.dataStream); + } + + return include; + }); const filter = tableState.filter || EuiSearchBar.Query.MATCH_ALL; - return EuiSearchBar.Query.execute(filter, systemFilteredIndexes, { + return EuiSearchBar.Query.execute(filter, filteredIndices, { defaultFields: defaultFilterFields, }); } @@ -133,10 +152,6 @@ export const getPageOfIndices = createSelector( } ); -export const getIndexNamesForCurrentPage = createSelector(getPageOfIndices, (pageOfIndices) => { - return pageOfIndices.map((index) => index.name); -}); - export const getHasNextPage = createSelector(getPager, (pager) => { return pager.hasNextPage; }); @@ -151,11 +166,6 @@ export const getCurrentPage = createSelector(getPager, (pager) => { export const getFilter = createSelector(getTableState, ({ filter }) => filter); -export const showHiddenIndices = createSelector( - getTableState, - ({ showHiddenIndices }) => showHiddenIndices -); - export const isSortAscending = createSelector( getTableState, ({ isSortAscending }) => isSortAscending diff --git a/x-pack/plugins/index_management/public/application/store/store.js b/x-pack/plugins/index_management/public/application/store/store.js index d2f24d50941c6..b189a7cf38938 100644 --- a/x-pack/plugins/index_management/public/application/store/store.js +++ b/x-pack/plugins/index_management/public/application/store/store.js @@ -9,7 +9,6 @@ import thunk from 'redux-thunk'; import { defaultTableState } from './reducers/table_state'; import { getReducer } from './reducers/'; -import { syncUrlHashQueryParam } from './middlewares'; export function indexManagementStore(services) { const toggleNameToVisibleMap = {}; @@ -17,7 +16,7 @@ export function indexManagementStore(services) { toggleNameToVisibleMap[toggleExtension.name] = false; }); const initialState = { tableState: { ...defaultTableState, toggleNameToVisibleMap } }; - const enhancers = [applyMiddleware(thunk, syncUrlHashQueryParam)]; + const enhancers = [applyMiddleware(thunk)]; window.__REDUX_DEVTOOLS_EXTENSION__ && enhancers.push(window.__REDUX_DEVTOOLS_EXTENSION__()); return createStore(getReducer(services), initialState, compose(...enhancers)); diff --git a/x-pack/plugins/index_management/server/lib/fetch_indices.ts b/x-pack/plugins/index_management/server/lib/fetch_indices.ts index b52a63a414967..793da238dc55b 100644 --- a/x-pack/plugins/index_management/server/lib/fetch_indices.ts +++ b/x-pack/plugins/index_management/server/lib/fetch_indices.ts @@ -87,6 +87,7 @@ async function fetchIndicesCall( isFrozen: hit.sth === 'true', // sth value coming back as a string from ES aliases: aliases.length ? aliases : 'none', hidden: index.settings.index.hidden === 'true', + dataStream: index.data_stream, }; }); } diff --git a/x-pack/plugins/index_management/server/types.ts b/x-pack/plugins/index_management/server/types.ts index b7b1c57b6f04e..7a3e49c48cdb0 100644 --- a/x-pack/plugins/index_management/server/types.ts +++ b/x-pack/plugins/index_management/server/types.ts @@ -32,6 +32,7 @@ export interface Index { size: any; isFrozen: boolean; aliases: string | string[]; + dataStream?: string; [key: string]: any; } From 1de542a83a85ad59d9ee00d8cb232227c2b467ad Mon Sep 17 00:00:00 2001 From: Jean-Louis Leysens Date: Tue, 2 Jun 2020 14:12:50 +0200 Subject: [PATCH 02/10] Updated jest tests --- .../client_integration/helpers/index.ts | 2 +- .../client_integration/helpers/mocks.ts | 10 +++ .../__jest__/client_integration/home.test.ts | 6 +- .../application/store/selectors/index.d.ts | 2 + .../application/store/selectors/index.js | 16 +---- .../store/selectors/indices_filter.test.ts | 66 +++++++++++++++++++ 6 files changed, 83 insertions(+), 19 deletions(-) create mode 100644 x-pack/plugins/index_management/__jest__/client_integration/helpers/mocks.ts create mode 100644 x-pack/plugins/index_management/public/application/store/selectors/indices_filter.test.ts diff --git a/x-pack/plugins/index_management/__jest__/client_integration/helpers/index.ts b/x-pack/plugins/index_management/__jest__/client_integration/helpers/index.ts index 66021b531919a..7bce79b2ff17d 100644 --- a/x-pack/plugins/index_management/__jest__/client_integration/helpers/index.ts +++ b/x-pack/plugins/index_management/__jest__/client_integration/helpers/index.ts @@ -3,7 +3,7 @@ * or more contributor license agreements. Licensed under the Elastic License; * you may not use this file except in compliance with the Elastic License. */ - +import './mocks'; import { setup as homeSetup } from './home.helpers'; import { setup as templateCreateSetup } from './template_create.helpers'; import { setup as templateCloneSetup } from './template_clone.helpers'; diff --git a/x-pack/plugins/index_management/__jest__/client_integration/helpers/mocks.ts b/x-pack/plugins/index_management/__jest__/client_integration/helpers/mocks.ts new file mode 100644 index 0000000000000..6260a987e270a --- /dev/null +++ b/x-pack/plugins/index_management/__jest__/client_integration/helpers/mocks.ts @@ -0,0 +1,10 @@ +/* + * 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. + */ + +(window as any).Worker = class Worker { + onmessage() {} + postMessage() {} +}; diff --git a/x-pack/plugins/index_management/__jest__/client_integration/home.test.ts b/x-pack/plugins/index_management/__jest__/client_integration/home.test.ts index f5af11330a6d8..ceff8cb39eb55 100644 --- a/x-pack/plugins/index_management/__jest__/client_integration/home.test.ts +++ b/x-pack/plugins/index_management/__jest__/client_integration/home.test.ts @@ -44,16 +44,14 @@ describe('', () => { }); }); - test('sets the hash query param base on include hidden indices toggle', () => { + test('toggles the include hidden button through URL hash correctly', () => { const { actions } = testBed; expect(actions.getIncludeHiddenIndicesToggleStatus()).toBe(true); - expect(window.location.hash.includes('includeHidden=true')).toBe(true); actions.clickIncludeHiddenIndicesToggle(); - expect(window.location.hash.includes('includeHidden=true')).toBe(false); + expect(actions.getIncludeHiddenIndicesToggleStatus()).toBe(false); // Note: this test modifies the shared location.hash state, we put it back the way it was actions.clickIncludeHiddenIndicesToggle(); expect(actions.getIncludeHiddenIndicesToggleStatus()).toBe(true); - expect(window.location.hash.includes('includeHidden=true')).toBe(true); }); test('should set the correct app title', () => { diff --git a/x-pack/plugins/index_management/public/application/store/selectors/index.d.ts b/x-pack/plugins/index_management/public/application/store/selectors/index.d.ts index 999b11c5d6665..37aaea0e7e3d8 100644 --- a/x-pack/plugins/index_management/public/application/store/selectors/index.d.ts +++ b/x-pack/plugins/index_management/public/application/store/selectors/index.d.ts @@ -6,3 +6,5 @@ import { ExtensionsService } from '../../../services'; export declare function setExtensionsService(extensionsService: ExtensionsService): any; + +export const getFilteredIndices: (state: any, props: any) => any; diff --git a/x-pack/plugins/index_management/public/application/store/selectors/index.js b/x-pack/plugins/index_management/public/application/store/selectors/index.js index 4518be5d200d5..5b84b23237e9f 100644 --- a/x-pack/plugins/index_management/public/application/store/selectors/index.js +++ b/x-pack/plugins/index_management/public/application/store/selectors/index.js @@ -82,7 +82,7 @@ const filterByToggles = (indices, toggleNameToVisibleMap) => { }); }; -const getFilteredIndices = createSelector( +export const getFilteredIndices = createSelector( getIndices, getAllIds, getTableState, @@ -95,7 +95,7 @@ const getFilteredIndices = createSelector( ); const includeHidden = includeHiddenParam === 'true'; const dataStreamsFilter = dataStreamsParam ? dataStreamsParam.split(',') : undefined; - const shouldFilterDataStreams = dataStreamsFilter && dataStreamsFilter.length; + const shouldFilterDataStreams = Boolean(dataStreamsFilter && dataStreamsFilter.length); const filteredIndices = indexArray.filter((index) => { let include = true; if (!includeHidden) { @@ -152,18 +152,6 @@ export const getPageOfIndices = createSelector( } ); -export const getHasNextPage = createSelector(getPager, (pager) => { - return pager.hasNextPage; -}); - -export const getHasPreviousPage = createSelector(getPager, (pager) => { - return pager.hasPreviousPage; -}); - -export const getCurrentPage = createSelector(getPager, (pager) => { - return pager.currentPage; -}); - export const getFilter = createSelector(getTableState, ({ filter }) => filter); export const isSortAscending = createSelector( diff --git a/x-pack/plugins/index_management/public/application/store/selectors/indices_filter.test.ts b/x-pack/plugins/index_management/public/application/store/selectors/indices_filter.test.ts new file mode 100644 index 0000000000000..d77b1be9f0547 --- /dev/null +++ b/x-pack/plugins/index_management/public/application/store/selectors/indices_filter.test.ts @@ -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; + * you may not use this file except in compliance with the Elastic License. + */ +import { ExtensionsService } from '../../../services'; +import { getFilteredIndices, setExtensionsService } from '.'; +// @ts-ignore +import { defaultTableState } from '../reducers/table_state'; + +describe('getFilteredIndices selector', () => { + let extensionService: ExtensionsService; + beforeAll(() => { + extensionService = new ExtensionsService(); + extensionService.setup(); + setExtensionsService(extensionService); + }); + + const state = { + tableState: { ...defaultTableState }, + indices: { + byId: { + test: { name: 'index1', hidden: true, dataStream: '123' }, + anotherTest: { name: 'index2', hidden: false }, + aTest: { name: 'index3', dataStream: 'abc' }, + aFinalTest: { name: '.index4' }, + }, + allIds: ['test', 'anotherTest', 'aTest', 'aFinalTest'], + }, + }; + + it('filters out hidden indices', () => { + expect(getFilteredIndices(state, { location: { search: '' } })).toEqual([ + { name: 'index2', hidden: false }, + { name: 'index3', dataStream: 'abc' }, + ]); + }); + + it('includes hidden indices', () => { + expect(getFilteredIndices(state, { location: { search: '?includeHidden=true' } })).toEqual([ + { name: 'index1', hidden: true, dataStream: '123' }, + { name: 'index2', hidden: false }, + { name: 'index3', dataStream: 'abc' }, + { name: '.index4' }, + ]); + }); + + it('filters based on data stream', () => { + expect(getFilteredIndices(state, { location: { search: '?dataStreams=abc' } })).toEqual([ + // We don't expect to see a case like this in the wild because data stream backing indicies + // are always hidden, but our logic can handle it + { name: 'index3', dataStream: 'abc' }, + ]); + }); + + it('filters based on data stream and includes hidden indices', () => { + expect( + getFilteredIndices(state, { + location: { search: '?includeHidden=true&dataStreams=123,abc,does-not-exist' }, + }) + ).toEqual([ + { name: 'index1', hidden: true, dataStream: '123' }, + { name: 'index3', dataStream: 'abc' }, + ]); + }); +}); From a48df33066cf0bcb7b67691db8aa2d0fffb1fd7e Mon Sep 17 00:00:00 2001 From: Jean-Louis Leysens Date: Tue, 2 Jun 2020 14:20:40 +0200 Subject: [PATCH 03/10] refactor: includeHidden -> includeHiddenIndices --- .../home/index_list/index_table/index_table.js | 17 ++++++++--------- .../public/application/store/selectors/index.js | 2 +- .../store/selectors/indices_filter.test.ts | 6 ++++-- 3 files changed, 13 insertions(+), 12 deletions(-) diff --git a/x-pack/plugins/index_management/public/application/sections/home/index_list/index_table/index_table.js b/x-pack/plugins/index_management/public/application/sections/home/index_list/index_table/index_table.js index dc96346a04d00..360a39971b535 100644 --- a/x-pack/plugins/index_management/public/application/sections/home/index_list/index_table/index_table.js +++ b/x-pack/plugins/index_management/public/application/sections/home/index_list/index_table/index_table.js @@ -94,7 +94,7 @@ export class IndexTable extends Component { super(props); this.state = { - showHiddenIndices: false, + includeHiddenIndices: false, dataStreamFilter: undefined, selectedIndicesMap: {}, }; @@ -130,11 +130,10 @@ export class IndexTable extends Component { syncUrlParams() { const { location } = this.props; - // Check if the we have the includeHidden query param - const { includeHidden } = qs.parse((location && location.search) || ''); - const nextValue = includeHidden === 'true'; - if (this.state.includeHidden !== nextValue) { - this.setState({ includeHidden: nextValue }); + const { includeHiddenIndices } = qs.parse((location && location.search) || ''); + const nextValue = includeHiddenIndices === 'true'; + if (this.state.includeHiddenIndices !== nextValue) { + this.setState({ includeHiddenIndices: nextValue }); } } @@ -142,9 +141,9 @@ export class IndexTable extends Component { const { pathname, search } = this.props.location; const params = qs.parse(search); if (hidden) { - params.includeHidden = 'true'; + params.includeHiddenIndices = 'true'; } else { - delete params.includeHidden; + delete params.includeHiddenIndices; } this.props.history.push(pathname + '?' + qs.stringify(params)); } @@ -497,7 +496,7 @@ export class IndexTable extends Component { this.setIncludeHiddenParam(event.target.checked)} label={ { let indexArray = allIds.map((indexName) => indices[indexName]); indexArray = filterByToggles(indexArray, tableState.toggleNameToVisibleMap); - const { includeHidden: includeHiddenParam, dataStreams: dataStreamsParam } = qs.parse( + const { includeHiddenIndices: includeHiddenParam, dataStreams: dataStreamsParam } = qs.parse( tableLocation.search ); const includeHidden = includeHiddenParam === 'true'; diff --git a/x-pack/plugins/index_management/public/application/store/selectors/indices_filter.test.ts b/x-pack/plugins/index_management/public/application/store/selectors/indices_filter.test.ts index d77b1be9f0547..ef32a1ba93ca9 100644 --- a/x-pack/plugins/index_management/public/application/store/selectors/indices_filter.test.ts +++ b/x-pack/plugins/index_management/public/application/store/selectors/indices_filter.test.ts @@ -37,7 +37,9 @@ describe('getFilteredIndices selector', () => { }); it('includes hidden indices', () => { - expect(getFilteredIndices(state, { location: { search: '?includeHidden=true' } })).toEqual([ + expect( + getFilteredIndices(state, { location: { search: '?includeHiddenIndices=true' } }) + ).toEqual([ { name: 'index1', hidden: true, dataStream: '123' }, { name: 'index2', hidden: false }, { name: 'index3', dataStream: 'abc' }, @@ -56,7 +58,7 @@ describe('getFilteredIndices selector', () => { it('filters based on data stream and includes hidden indices', () => { expect( getFilteredIndices(state, { - location: { search: '?includeHidden=true&dataStreams=123,abc,does-not-exist' }, + location: { search: '?includeHiddenIndices=true&dataStreams=123,abc,does-not-exist' }, }) ).toEqual([ { name: 'index1', hidden: true, dataStream: '123' }, From c40ade58bc10996159286c6a4dbd5133c45aa583 Mon Sep 17 00:00:00 2001 From: Jean-Louis Leysens Date: Tue, 2 Jun 2020 16:22:52 +0200 Subject: [PATCH 04/10] Fix types --- x-pack/plugins/index_management/server/lib/fetch_indices.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/x-pack/plugins/index_management/server/lib/fetch_indices.ts b/x-pack/plugins/index_management/server/lib/fetch_indices.ts index 793da238dc55b..1304afc8f981f 100644 --- a/x-pack/plugins/index_management/server/lib/fetch_indices.ts +++ b/x-pack/plugins/index_management/server/lib/fetch_indices.ts @@ -23,6 +23,7 @@ interface Hit { interface IndexInfo { aliases: { [aliasName: string]: unknown }; mappings: unknown; + data_stream?: string; settings: { index: { hidden: 'true' | 'false'; From 0f5f80a05f707a921e47380e24aa35657ef29e3c Mon Sep 17 00:00:00 2001 From: Jean-Louis Leysens Date: Tue, 2 Jun 2020 19:58:48 +0200 Subject: [PATCH 05/10] Fix jest test and remove getting filter param from parent --- .../client_integration/helpers/home.helpers.ts | 2 +- .../sections/home/index_list/index_list.js | 8 ++------ .../home/index_list/index_table/index_table.js | 17 +++++++++-------- 3 files changed, 12 insertions(+), 15 deletions(-) diff --git a/x-pack/plugins/index_management/__jest__/client_integration/helpers/home.helpers.ts b/x-pack/plugins/index_management/__jest__/client_integration/helpers/home.helpers.ts index 57b925b8c6fc1..d8a1f247f70f1 100644 --- a/x-pack/plugins/index_management/__jest__/client_integration/helpers/home.helpers.ts +++ b/x-pack/plugins/index_management/__jest__/client_integration/helpers/home.helpers.ts @@ -22,7 +22,7 @@ import { WithAppDependencies, services } from './setup_environment'; const testBedConfig: TestBedConfig = { store: () => indexManagementStore(services as any), memoryRouter: { - initialEntries: [`${BASE_PATH}indices?includeHidden=true`], + initialEntries: [`${BASE_PATH}indices?includeHiddenIndices=true`], componentRoutePath: `${BASE_PATH}:section(indices|templates)`, }, doMountAsync: true, diff --git a/x-pack/plugins/index_management/public/application/sections/home/index_list/index_list.js b/x-pack/plugins/index_management/public/application/sections/home/index_list/index_list.js index 36999bc40e2a1..df5ca7f837d10 100644 --- a/x-pack/plugins/index_management/public/application/sections/home/index_list/index_list.js +++ b/x-pack/plugins/index_management/public/application/sections/home/index_list/index_list.js @@ -8,14 +8,10 @@ import React from 'react'; import { DetailPanel } from './detail_panel'; import { IndexTable } from './index_table'; -export function IndexList({ - match: { - params: { filter }, - }, -}) { +export function IndexList() { return (
- +
); diff --git a/x-pack/plugins/index_management/public/application/sections/home/index_list/index_table/index_table.js b/x-pack/plugins/index_management/public/application/sections/home/index_list/index_table/index_table.js index 360a39971b535..9ab8ecb6a6e31 100644 --- a/x-pack/plugins/index_management/public/application/sections/home/index_list/index_table/index_table.js +++ b/x-pack/plugins/index_management/public/application/sections/home/index_list/index_table/index_table.js @@ -95,7 +95,6 @@ export class IndexTable extends Component { this.state = { includeHiddenIndices: false, - dataStreamFilter: undefined, selectedIndicesMap: {}, }; } @@ -106,10 +105,10 @@ export class IndexTable extends Component { () => this.props.reloadIndices(this.props.indices.map((i) => i.name)), REFRESH_RATE_INDEX_LIST ); - const { filterChanged, filterFromURI } = this.props; - - if (filterFromURI) { - const decodedFilter = decodeURIComponent(filterFromURI); + const { location, filterChanged } = this.props; + const { filter } = qs.parse((location && location.search) || ''); + if (filter) { + const decodedFilter = decodeURIComponent(filter); try { const filter = EuiSearchBar.Query.parse(decodedFilter); @@ -118,19 +117,21 @@ export class IndexTable extends Component { this.setState({ filterError: e }); } } - this.syncUrlParams(); + + this.syncFromUrlParams(); } componentWillUnmount() { clearInterval(this.interval); } componentDidUpdate() { - this.syncUrlParams(); + this.syncFromUrlParams(); } - syncUrlParams() { + syncFromUrlParams() { const { location } = this.props; const { includeHiddenIndices } = qs.parse((location && location.search) || ''); + const nextValue = includeHiddenIndices === 'true'; if (this.state.includeHiddenIndices !== nextValue) { this.setState({ includeHiddenIndices: nextValue }); From dc5470a9120ed3951cd07f4601f624e5d06f8d79 Mon Sep 17 00:00:00 2001 From: Jean-Louis Leysens Date: Tue, 2 Jun 2020 20:42:15 +0200 Subject: [PATCH 06/10] Small refactor to read url params in render function --- .../index_list/index_table/index_table.js | 21 +++++++------------ 1 file changed, 7 insertions(+), 14 deletions(-) diff --git a/x-pack/plugins/index_management/public/application/sections/home/index_list/index_table/index_table.js b/x-pack/plugins/index_management/public/application/sections/home/index_list/index_table/index_table.js index 9ab8ecb6a6e31..c3c94b7d4ac2b 100644 --- a/x-pack/plugins/index_management/public/application/sections/home/index_list/index_table/index_table.js +++ b/x-pack/plugins/index_management/public/application/sections/home/index_list/index_table/index_table.js @@ -94,7 +94,6 @@ export class IndexTable extends Component { super(props); this.state = { - includeHiddenIndices: false, selectedIndicesMap: {}, }; } @@ -117,25 +116,17 @@ export class IndexTable extends Component { this.setState({ filterError: e }); } } - - this.syncFromUrlParams(); } componentWillUnmount() { clearInterval(this.interval); } - componentDidUpdate() { - this.syncFromUrlParams(); - } - - syncFromUrlParams() { + readURLParams() { const { location } = this.props; const { includeHiddenIndices } = qs.parse((location && location.search) || ''); - - const nextValue = includeHiddenIndices === 'true'; - if (this.state.includeHiddenIndices !== nextValue) { - this.setState({ includeHiddenIndices: nextValue }); - } + return { + includeHiddenIndices: includeHiddenIndices === 'true', + }; } setIncludeHiddenParam(hidden) { @@ -446,6 +437,8 @@ export class IndexTable extends Component { pager, } = this.props; + const { includeHiddenIndices } = this.readURLParams(); + let emptyState; if (indicesLoading) { @@ -497,7 +490,7 @@ export class IndexTable extends Component { this.setIncludeHiddenParam(event.target.checked)} label={ Date: Tue, 2 Jun 2020 20:49:06 +0200 Subject: [PATCH 07/10] Clean up old data streams code --- .../application/store/selectors/index.js | 23 ++++----------- .../store/selectors/indices_filter.test.ts | 29 ++++--------------- 2 files changed, 11 insertions(+), 41 deletions(-) diff --git a/x-pack/plugins/index_management/public/application/store/selectors/index.js b/x-pack/plugins/index_management/public/application/store/selectors/index.js index 24b09dd37e377..c80658581dbee 100644 --- a/x-pack/plugins/index_management/public/application/store/selectors/index.js +++ b/x-pack/plugins/index_management/public/application/store/selectors/index.js @@ -90,24 +90,13 @@ export const getFilteredIndices = createSelector( (indices, allIds, tableState, tableLocation) => { let indexArray = allIds.map((indexName) => indices[indexName]); indexArray = filterByToggles(indexArray, tableState.toggleNameToVisibleMap); - const { includeHiddenIndices: includeHiddenParam, dataStreams: dataStreamsParam } = qs.parse( - tableLocation.search - ); + const { includeHiddenIndices: includeHiddenParam } = qs.parse(tableLocation.search); const includeHidden = includeHiddenParam === 'true'; - const dataStreamsFilter = dataStreamsParam ? dataStreamsParam.split(',') : undefined; - const shouldFilterDataStreams = Boolean(dataStreamsFilter && dataStreamsFilter.length); - const filteredIndices = indexArray.filter((index) => { - let include = true; - if (!includeHidden) { - include = !(index.name + '').startsWith('.') && !index.hidden; - } - - if (include && shouldFilterDataStreams) { - return dataStreamsFilter.includes(index.dataStream); - } - - return include; - }); + const filteredIndices = includeHidden + ? indexArray + : indexArray.filter((index) => { + return !(index.name + '').startsWith('.') && !index.hidden; + }); const filter = tableState.filter || EuiSearchBar.Query.MATCH_ALL; return EuiSearchBar.Query.execute(filter, filteredIndices, { defaultFields: defaultFilterFields, diff --git a/x-pack/plugins/index_management/public/application/store/selectors/indices_filter.test.ts b/x-pack/plugins/index_management/public/application/store/selectors/indices_filter.test.ts index ef32a1ba93ca9..f230ddd18e9eb 100644 --- a/x-pack/plugins/index_management/public/application/store/selectors/indices_filter.test.ts +++ b/x-pack/plugins/index_management/public/application/store/selectors/indices_filter.test.ts @@ -20,9 +20,9 @@ describe('getFilteredIndices selector', () => { tableState: { ...defaultTableState }, indices: { byId: { - test: { name: 'index1', hidden: true, dataStream: '123' }, + test: { name: 'index1', hidden: true }, anotherTest: { name: 'index2', hidden: false }, - aTest: { name: 'index3', dataStream: 'abc' }, + aTest: { name: 'index3' }, aFinalTest: { name: '.index4' }, }, allIds: ['test', 'anotherTest', 'aTest', 'aFinalTest'], @@ -32,7 +32,7 @@ describe('getFilteredIndices selector', () => { it('filters out hidden indices', () => { expect(getFilteredIndices(state, { location: { search: '' } })).toEqual([ { name: 'index2', hidden: false }, - { name: 'index3', dataStream: 'abc' }, + { name: 'index3' }, ]); }); @@ -40,29 +40,10 @@ describe('getFilteredIndices selector', () => { expect( getFilteredIndices(state, { location: { search: '?includeHiddenIndices=true' } }) ).toEqual([ - { name: 'index1', hidden: true, dataStream: '123' }, + { name: 'index1', hidden: true }, { name: 'index2', hidden: false }, - { name: 'index3', dataStream: 'abc' }, + { name: 'index3' }, { name: '.index4' }, ]); }); - - it('filters based on data stream', () => { - expect(getFilteredIndices(state, { location: { search: '?dataStreams=abc' } })).toEqual([ - // We don't expect to see a case like this in the wild because data stream backing indicies - // are always hidden, but our logic can handle it - { name: 'index3', dataStream: 'abc' }, - ]); - }); - - it('filters based on data stream and includes hidden indices', () => { - expect( - getFilteredIndices(state, { - location: { search: '?includeHiddenIndices=true&dataStreams=123,abc,does-not-exist' }, - }) - ).toEqual([ - { name: 'index1', hidden: true, dataStream: '123' }, - { name: 'index3', dataStream: 'abc' }, - ]); - }); }); From 764ab74935827d8d056be19de317d70706b94ea6 Mon Sep 17 00:00:00 2001 From: Jean-Louis Leysens Date: Thu, 4 Jun 2020 10:56:46 +0200 Subject: [PATCH 08/10] Fix sorting on data stream field in table --- .../__jest__/client_integration/home.test.ts | 585 ------------------ .../public/application/services/sort_table.ts | 4 +- 2 files changed, 3 insertions(+), 586 deletions(-) delete mode 100644 x-pack/plugins/index_management/__jest__/client_integration/home.test.ts diff --git a/x-pack/plugins/index_management/__jest__/client_integration/home.test.ts b/x-pack/plugins/index_management/__jest__/client_integration/home.test.ts deleted file mode 100644 index ceff8cb39eb55..0000000000000 --- a/x-pack/plugins/index_management/__jest__/client_integration/home.test.ts +++ /dev/null @@ -1,585 +0,0 @@ -/* - * 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 { act } from 'react-dom/test-utils'; -import * as fixtures from '../../test/fixtures'; -import { setupEnvironment, pageHelpers, nextTick, getRandomString } from './helpers'; -import { IdxMgmtHomeTestBed } from './helpers/home.helpers'; -import { API_BASE_PATH } from '../../common/constants'; - -const { setup } = pageHelpers.home; - -const removeWhiteSpaceOnArrayValues = (array: any[]) => - array.map((value) => { - if (!value.trim) { - return value; - } - return value.trim(); - }); - -jest.mock('ui/new_platform'); - -describe('', () => { - const { server, httpRequestsMockHelpers } = setupEnvironment(); - let testBed: IdxMgmtHomeTestBed; - - afterAll(() => { - server.restore(); - }); - - describe('on component mount', () => { - beforeEach(async () => { - httpRequestsMockHelpers.setLoadIndicesResponse([]); - - testBed = await setup(); - - await act(async () => { - const { component } = testBed; - - await nextTick(); - component.update(); - }); - }); - - test('toggles the include hidden button through URL hash correctly', () => { - const { actions } = testBed; - expect(actions.getIncludeHiddenIndicesToggleStatus()).toBe(true); - actions.clickIncludeHiddenIndicesToggle(); - expect(actions.getIncludeHiddenIndicesToggleStatus()).toBe(false); - // Note: this test modifies the shared location.hash state, we put it back the way it was - actions.clickIncludeHiddenIndicesToggle(); - expect(actions.getIncludeHiddenIndicesToggleStatus()).toBe(true); - }); - - test('should set the correct app title', () => { - const { exists, find } = testBed; - expect(exists('appTitle')).toBe(true); - expect(find('appTitle').text()).toEqual('Index Management'); - }); - - test('should have a link to the documentation', () => { - const { exists, find } = testBed; - expect(exists('documentationLink')).toBe(true); - expect(find('documentationLink').text()).toBe('Index Management docs'); - }); - - describe('tabs', () => { - test('should have 2 tabs', () => { - const { find } = testBed; - const templatesTab = find('templatesTab'); - const indicesTab = find('indicesTab'); - - expect(indicesTab.length).toBe(1); - expect(indicesTab.text()).toEqual('Indices'); - expect(templatesTab.length).toBe(1); - expect(templatesTab.text()).toEqual('Index Templates'); - }); - - test('should navigate to Index Templates tab', async () => { - const { exists, actions, component } = testBed; - - expect(exists('indicesList')).toBe(true); - expect(exists('templateList')).toBe(false); - - httpRequestsMockHelpers.setLoadTemplatesResponse([]); - - actions.selectHomeTab('templatesTab'); - - await act(async () => { - await nextTick(); - component.update(); - }); - - expect(exists('indicesList')).toBe(false); - expect(exists('templateList')).toBe(true); - }); - }); - - describe('index templates', () => { - describe('when there are no index templates', () => { - beforeEach(async () => { - const { actions, component } = testBed; - - httpRequestsMockHelpers.setLoadTemplatesResponse([]); - - actions.selectHomeTab('templatesTab'); - - await act(async () => { - await nextTick(); - component.update(); - }); - }); - - test('should display an empty prompt', async () => { - const { exists } = testBed; - - expect(exists('sectionLoading')).toBe(false); - expect(exists('emptyPrompt')).toBe(true); - }); - }); - - describe('when there are index templates', () => { - const template1 = fixtures.getTemplate({ - name: `a${getRandomString()}`, - indexPatterns: ['template1Pattern1*', 'template1Pattern2'], - template: { - settings: { - index: { - number_of_shards: '1', - lifecycle: { - name: 'my_ilm_policy', - }, - }, - }, - }, - }); - const template2 = fixtures.getTemplate({ - name: `b${getRandomString()}`, - indexPatterns: ['template2Pattern1*'], - }); - const template3 = fixtures.getTemplate({ - name: `.c${getRandomString()}`, // mock system template - indexPatterns: ['template3Pattern1*', 'template3Pattern2', 'template3Pattern3'], - }); - - const templates = [template1, template2, template3]; - - beforeEach(async () => { - const { actions, component } = testBed; - - httpRequestsMockHelpers.setLoadTemplatesResponse(templates); - - actions.selectHomeTab('templatesTab'); - - await act(async () => { - await nextTick(); - component.update(); - }); - }); - - test('should list them in the table', async () => { - const { table } = testBed; - - const { tableCellsValues } = table.getMetaData('templateTable'); - - tableCellsValues.forEach((row, i) => { - const template = templates[i]; - const { name, indexPatterns, order, ilmPolicy } = template; - - const ilmPolicyName = ilmPolicy && ilmPolicy.name ? ilmPolicy.name : ''; - const orderFormatted = order ? order.toString() : order; - - expect(removeWhiteSpaceOnArrayValues(row)).toEqual([ - '', - name, - indexPatterns.join(', '), - ilmPolicyName, - orderFormatted, - '', - '', - '', - '', - ]); - }); - }); - - test('should have a button to reload the index templates', async () => { - const { component, exists, actions } = testBed; - const totalRequests = server.requests.length; - - expect(exists('reloadButton')).toBe(true); - - await act(async () => { - actions.clickReloadButton(); - await nextTick(); - component.update(); - }); - - expect(server.requests.length).toBe(totalRequests + 1); - expect(server.requests[server.requests.length - 1].url).toBe( - `${API_BASE_PATH}/templates` - ); - }); - - test('should have a button to create a new template', () => { - const { exists } = testBed; - expect(exists('createTemplateButton')).toBe(true); - }); - - test('should have a switch to view system templates', async () => { - const { table, exists, component, form } = testBed; - const { rows } = table.getMetaData('templateTable'); - - expect(rows.length).toEqual( - templates.filter((template) => !template.name.startsWith('.')).length - ); - - expect(exists('systemTemplatesSwitch')).toBe(true); - - await act(async () => { - form.toggleEuiSwitch('systemTemplatesSwitch'); - await nextTick(); - component.update(); - }); - - const { rows: updatedRows } = table.getMetaData('templateTable'); - expect(updatedRows.length).toEqual(templates.length); - }); - - test('each row should have a link to the template details panel', async () => { - const { find, exists, actions } = testBed; - - await actions.clickTemplateAt(0); - - expect(exists('templateList')).toBe(true); - expect(exists('templateDetails')).toBe(true); - expect(find('templateDetails.title').text()).toBe(template1.name); - }); - - test('template actions column should have an option to delete', () => { - const { actions, findAction } = testBed; - const { name: templateName } = template1; - - actions.clickActionMenu(templateName); - - const deleteAction = findAction('delete'); - - expect(deleteAction.text()).toEqual('Delete'); - }); - - test('template actions column should have an option to clone', () => { - const { actions, findAction } = testBed; - const { name: templateName } = template1; - - actions.clickActionMenu(templateName); - - const cloneAction = findAction('clone'); - - expect(cloneAction.text()).toEqual('Clone'); - }); - - test('template actions column should have an option to edit', () => { - const { actions, findAction } = testBed; - const { name: templateName } = template1; - - actions.clickActionMenu(templateName); - - const editAction = findAction('edit'); - - expect(editAction.text()).toEqual('Edit'); - }); - - describe('delete index template', () => { - test('should show a confirmation when clicking the delete template button', async () => { - const { actions } = testBed; - const { name: templateName } = template1; - - await actions.clickTemplateAction(templateName, 'delete'); - - // We need to read the document "body" as the modal is added there and not inside - // the component DOM tree. - expect( - document.body.querySelector('[data-test-subj="deleteTemplatesConfirmation"]') - ).not.toBe(null); - - expect( - document.body.querySelector('[data-test-subj="deleteTemplatesConfirmation"]')! - .textContent - ).toContain('Delete template'); - }); - - test('should show a warning message when attempting to delete a system template', async () => { - const { component, form, actions } = testBed; - - await act(async () => { - form.toggleEuiSwitch('systemTemplatesSwitch'); - await nextTick(); - component.update(); - }); - - const { name: systemTemplateName } = template3; - await actions.clickTemplateAction(systemTemplateName, 'delete'); - - expect( - document.body.querySelector('[data-test-subj="deleteSystemTemplateCallOut"]') - ).not.toBe(null); - }); - - test('should send the correct HTTP request to delete an index template', async () => { - const { component, actions, table } = testBed; - const { rows } = table.getMetaData('templateTable'); - - const templateId = rows[0].columns[2].value; - - const { - name: templateName, - _kbnMeta: { formatVersion }, - } = template1; - await actions.clickTemplateAction(templateName, 'delete'); - - const modal = document.body.querySelector( - '[data-test-subj="deleteTemplatesConfirmation"]' - ); - const confirmButton: HTMLButtonElement | null = modal!.querySelector( - '[data-test-subj="confirmModalConfirmButton"]' - ); - - httpRequestsMockHelpers.setDeleteTemplateResponse({ - results: { - successes: [templateId], - errors: [], - }, - }); - - await act(async () => { - confirmButton!.click(); - await nextTick(); - component.update(); - }); - - const latestRequest = server.requests[server.requests.length - 1]; - - expect(latestRequest.method).toBe('POST'); - expect(latestRequest.url).toBe(`${API_BASE_PATH}/delete-templates`); - expect(JSON.parse(JSON.parse(latestRequest.requestBody).body)).toEqual({ - templates: [{ name: template1.name, formatVersion }], - }); - }); - }); - - describe('detail panel', () => { - beforeEach(async () => { - const template = fixtures.getTemplate({ - name: `a${getRandomString()}`, - indexPatterns: ['template1Pattern1*', 'template1Pattern2'], - }); - - httpRequestsMockHelpers.setLoadTemplateResponse(template); - }); - - test('should show details when clicking on a template', async () => { - const { exists, actions } = testBed; - - expect(exists('templateDetails')).toBe(false); - - await actions.clickTemplateAt(0); - - expect(exists('templateDetails')).toBe(true); - }); - - describe('on mount', () => { - beforeEach(async () => { - const { actions } = testBed; - - await actions.clickTemplateAt(0); - }); - - test('should set the correct title', async () => { - const { find } = testBed; - const { name } = template1; - - expect(find('templateDetails.title').text()).toEqual(name); - }); - - it('should have a close button and be able to close flyout', async () => { - const { actions, component, exists } = testBed; - - expect(exists('closeDetailsButton')).toBe(true); - expect(exists('summaryTab')).toBe(true); - - actions.clickCloseDetailsButton(); - - await act(async () => { - await nextTick(); - component.update(); - }); - - expect(exists('summaryTab')).toBe(false); - }); - - it('should have a manage button', async () => { - const { actions, exists } = testBed; - - await actions.clickTemplateAt(0); - - expect(exists('templateDetails.manageTemplateButton')).toBe(true); - }); - }); - - describe('tabs', () => { - test('should have 4 tabs', async () => { - const template = fixtures.getTemplate({ - name: `a${getRandomString()}`, - indexPatterns: ['template1Pattern1*', 'template1Pattern2'], - template: { - settings: { - index: { - number_of_shards: '1', - }, - }, - mappings: { - _source: { - enabled: false, - }, - properties: { - created_at: { - type: 'date', - format: 'EEE MMM dd HH:mm:ss Z yyyy', - }, - }, - }, - aliases: { - alias1: {}, - }, - }, - }); - - const { find, actions, exists } = testBed; - - httpRequestsMockHelpers.setLoadTemplateResponse(template); - - await actions.clickTemplateAt(0); - - expect(find('templateDetails.tab').length).toBe(4); - expect(find('templateDetails.tab').map((t) => t.text())).toEqual([ - 'Summary', - 'Settings', - 'Mappings', - 'Aliases', - ]); - - // Summary tab should be initial active tab - expect(exists('summaryTab')).toBe(true); - - // Navigate and verify all tabs - actions.selectDetailsTab('settings'); - expect(exists('summaryTab')).toBe(false); - expect(exists('settingsTab')).toBe(true); - - actions.selectDetailsTab('aliases'); - expect(exists('summaryTab')).toBe(false); - expect(exists('settingsTab')).toBe(false); - expect(exists('aliasesTab')).toBe(true); - - actions.selectDetailsTab('mappings'); - expect(exists('summaryTab')).toBe(false); - expect(exists('settingsTab')).toBe(false); - expect(exists('aliasesTab')).toBe(false); - expect(exists('mappingsTab')).toBe(true); - }); - - test('should show an info callout if data is not present', async () => { - const templateWithNoOptionalFields = fixtures.getTemplate({ - name: `a${getRandomString()}`, - indexPatterns: ['template1Pattern1*', 'template1Pattern2'], - }); - - const { actions, find, exists, component } = testBed; - - httpRequestsMockHelpers.setLoadTemplateResponse(templateWithNoOptionalFields); - - await actions.clickTemplateAt(0); - - await act(async () => { - await nextTick(); - component.update(); - }); - - expect(find('templateDetails.tab').length).toBe(4); - expect(exists('summaryTab')).toBe(true); - - // Navigate and verify callout message per tab - actions.selectDetailsTab('settings'); - expect(exists('noSettingsCallout')).toBe(true); - - actions.selectDetailsTab('mappings'); - expect(exists('noMappingsCallout')).toBe(true); - - actions.selectDetailsTab('aliases'); - expect(exists('noAliasesCallout')).toBe(true); - }); - }); - - describe('error handling', () => { - it('should render an error message if error fetching template details', async () => { - const { actions, exists } = testBed; - const error = { - status: 404, - error: 'Not found', - message: 'Template not found', - }; - - httpRequestsMockHelpers.setLoadTemplateResponse(undefined, { body: error }); - - await actions.clickTemplateAt(0); - - expect(exists('sectionError')).toBe(true); - // Manage button should not render if error - expect(exists('templateDetails.manageTemplateButton')).toBe(false); - }); - }); - }); - }); - }); - }); - - describe('index detail panel with % character in index name', () => { - const indexName = 'test%'; - beforeEach(async () => { - const index = { - health: 'green', - status: 'open', - primary: 1, - replica: 1, - documents: 10000, - documents_deleted: 100, - size: '156kb', - primary_size: '156kb', - name: indexName, - }; - httpRequestsMockHelpers.setLoadIndicesResponse([index]); - - testBed = await setup(); - const { component, find } = testBed; - - component.update(); - - find('indexTableIndexNameLink').at(0).simulate('click'); - }); - - test('should encode indexName when loading settings in detail panel', async () => { - const { actions } = testBed; - await actions.selectIndexDetailsTab('settings'); - - const latestRequest = server.requests[server.requests.length - 1]; - expect(latestRequest.url).toBe(`${API_BASE_PATH}/settings/${encodeURIComponent(indexName)}`); - }); - - test('should encode indexName when loading mappings in detail panel', async () => { - const { actions } = testBed; - await actions.selectIndexDetailsTab('mappings'); - - const latestRequest = server.requests[server.requests.length - 1]; - expect(latestRequest.url).toBe(`${API_BASE_PATH}/mapping/${encodeURIComponent(indexName)}`); - }); - - test('should encode indexName when loading stats in detail panel', async () => { - const { actions } = testBed; - await actions.selectIndexDetailsTab('stats'); - - const latestRequest = server.requests[server.requests.length - 1]; - expect(latestRequest.url).toBe(`${API_BASE_PATH}/stats/${encodeURIComponent(indexName)}`); - }); - - test('should encode indexName when editing settings in detail panel', async () => { - const { actions } = testBed; - await actions.selectIndexDetailsTab('edit_settings'); - - const latestRequest = server.requests[server.requests.length - 1]; - expect(latestRequest.url).toBe(`${API_BASE_PATH}/settings/${encodeURIComponent(indexName)}`); - }); - }); -}); diff --git a/x-pack/plugins/index_management/public/application/services/sort_table.ts b/x-pack/plugins/index_management/public/application/services/sort_table.ts index ed8c5bb146f53..fc113d30b79a5 100644 --- a/x-pack/plugins/index_management/public/application/services/sort_table.ts +++ b/x-pack/plugins/index_management/public/application/services/sort_table.ts @@ -14,7 +14,8 @@ type SortField = | 'replica' | 'documents' | 'size' - | 'primary_size'; + | 'primary_size' + | 'dataStream'; type Unit = 'kb' | 'mb' | 'gb' | 'tb' | 'pb'; @@ -55,6 +56,7 @@ const sorters = { documents: numericSort('documents'), size: byteSort('size'), primary_size: byteSort('primary_size'), + dataStream: stringSort('dataStream'), }; export const sortTable = (array = [], sortField: SortField, isSortAscending: boolean) => { From ff668a703c3b2015b2cba60811da2ca74e6fa751 Mon Sep 17 00:00:00 2001 From: Jean-Louis Leysens Date: Thu, 4 Jun 2020 11:09:20 +0200 Subject: [PATCH 09/10] dataStream -> data_stream --- .../sections/home/index_list/index_table/index_table.js | 2 +- .../public/application/services/sort_table.ts | 4 ++-- x-pack/plugins/index_management/server/lib/fetch_indices.ts | 2 +- x-pack/plugins/index_management/server/types.ts | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/x-pack/plugins/index_management/public/application/sections/home/index_list/index_table/index_table.js b/x-pack/plugins/index_management/public/application/sections/home/index_list/index_table/index_table.js index c3c94b7d4ac2b..0c3363b017a61 100644 --- a/x-pack/plugins/index_management/public/application/sections/home/index_list/index_table/index_table.js +++ b/x-pack/plugins/index_management/public/application/sections/home/index_list/index_table/index_table.js @@ -66,7 +66,7 @@ const HEADERS = { size: i18n.translate('xpack.idxMgmt.indexTable.headers.storageSizeHeader', { defaultMessage: 'Storage size', }), - dataStream: i18n.translate('xpack.idxMgmt.indexTable.headers.dataStreamHeader', { + data_stream: i18n.translate('xpack.idxMgmt.indexTable.headers.dataStreamHeader', { defaultMessage: 'Data stream', }), }; diff --git a/x-pack/plugins/index_management/public/application/services/sort_table.ts b/x-pack/plugins/index_management/public/application/services/sort_table.ts index fc113d30b79a5..429f2961c4521 100644 --- a/x-pack/plugins/index_management/public/application/services/sort_table.ts +++ b/x-pack/plugins/index_management/public/application/services/sort_table.ts @@ -15,7 +15,7 @@ type SortField = | 'documents' | 'size' | 'primary_size' - | 'dataStream'; + | 'data_stream'; type Unit = 'kb' | 'mb' | 'gb' | 'tb' | 'pb'; @@ -56,7 +56,7 @@ const sorters = { documents: numericSort('documents'), size: byteSort('size'), primary_size: byteSort('primary_size'), - dataStream: stringSort('dataStream'), + data_stream: stringSort('data_stream'), }; export const sortTable = (array = [], sortField: SortField, isSortAscending: boolean) => { diff --git a/x-pack/plugins/index_management/server/lib/fetch_indices.ts b/x-pack/plugins/index_management/server/lib/fetch_indices.ts index 1304afc8f981f..ae10629e069e8 100644 --- a/x-pack/plugins/index_management/server/lib/fetch_indices.ts +++ b/x-pack/plugins/index_management/server/lib/fetch_indices.ts @@ -88,7 +88,7 @@ async function fetchIndicesCall( isFrozen: hit.sth === 'true', // sth value coming back as a string from ES aliases: aliases.length ? aliases : 'none', hidden: index.settings.index.hidden === 'true', - dataStream: index.data_stream, + data_stream: index.data_stream, }; }); } diff --git a/x-pack/plugins/index_management/server/types.ts b/x-pack/plugins/index_management/server/types.ts index 7a3e49c48cdb0..b3fb546281f1e 100644 --- a/x-pack/plugins/index_management/server/types.ts +++ b/x-pack/plugins/index_management/server/types.ts @@ -32,7 +32,7 @@ export interface Index { size: any; isFrozen: boolean; aliases: string | string[]; - dataStream?: string; + data_stream?: string; [key: string]: any; } From fd1cdbfc58dd29884d46d1c361d94c2cd4c26056 Mon Sep 17 00:00:00 2001 From: Jean-Louis Leysens Date: Thu, 4 Jun 2020 11:14:22 +0200 Subject: [PATCH 10/10] qs > * as qs --- .../sections/home/index_list/index_table/index_table.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/x-pack/plugins/index_management/public/application/sections/home/index_list/index_table/index_table.js b/x-pack/plugins/index_management/public/application/sections/home/index_list/index_table/index_table.js index 0c3363b017a61..f33d486520a29 100644 --- a/x-pack/plugins/index_management/public/application/sections/home/index_list/index_table/index_table.js +++ b/x-pack/plugins/index_management/public/application/sections/home/index_list/index_table/index_table.js @@ -8,7 +8,7 @@ import React, { Component, Fragment } from 'react'; import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n/react'; import { Route } from 'react-router-dom'; -import * as qs from 'query-string'; +import qs from 'query-string'; import { EuiButton,