From eec9cb7a07723fb9c85eb186986632fb2927d7f3 Mon Sep 17 00:00:00 2001 From: Matthew Kime Date: Tue, 13 Nov 2018 21:56:26 -0600 Subject: [PATCH] Fixes index pattern wizard when there are remote clusters but no local indices (#24339) (#25613) * don't hide wizard if clusters exist * catch errors * add toast notifs if unable to load data --- src/core_plugins/kibana/index.js | 2 + .../create_index_pattern_wizard.test.js.snap | 283 +++++++++++------- .../create_index_pattern_wizard.test.js | 23 ++ .../__tests__/render.test.js | 1 + .../__tests__/step_index_pattern.test.js | 3 +- .../__tests__/step_time_field.test.js | 3 + .../create_index_pattern_wizard.js | 86 +++++- .../create_index_pattern_wizard/index.js | 1 + .../lib/get_remote_clusters.js | 26 ++ .../create_index_pattern_wizard/lib/index.js | 2 + .../remote_info/call_with_request_factory.js | 37 +++ .../server/routes/api/remote_info/index.js | 51 ++++ .../routes/api/indices/register_list_route.js | 2 +- 13 files changed, 402 insertions(+), 118 deletions(-) create mode 100644 src/core_plugins/kibana/public/management/sections/indices/create_index_pattern_wizard/lib/get_remote_clusters.js create mode 100644 src/core_plugins/kibana/server/routes/api/remote_info/call_with_request_factory.js create mode 100644 src/core_plugins/kibana/server/routes/api/remote_info/index.js diff --git a/src/core_plugins/kibana/index.js b/src/core_plugins/kibana/index.js index 9c8ed813cd84d..c7a44ac2b04bd 100644 --- a/src/core_plugins/kibana/index.js +++ b/src/core_plugins/kibana/index.js @@ -30,6 +30,7 @@ import { managementApi } from './server/routes/api/management'; import { scriptsApi } from './server/routes/api/scripts'; import { registerSuggestionsApi } from './server/routes/api/suggestions'; import { registerKqlTelemetryApi } from './server/routes/api/kql_telemetry'; +import { registerClustersRoute } from './server/routes/api/remote_info'; import { registerFieldFormats } from './server/field_formats/register'; import { registerTutorials } from './server/tutorials/register'; import * as systemApi from './server/lib/system_api'; @@ -167,6 +168,7 @@ export default function (kibana) { registerFieldFormats(server); registerTutorials(server); makeKQLUsageCollector(server); + registerClustersRoute(server); server.expose('systemApi', systemApi); server.expose('handleEsError', handleEsError); server.injectUiAppVars('kibana', () => injectVars(server)); diff --git a/src/core_plugins/kibana/public/management/sections/indices/create_index_pattern_wizard/__tests__/__snapshots__/create_index_pattern_wizard.test.js.snap b/src/core_plugins/kibana/public/management/sections/indices/create_index_pattern_wizard/__tests__/__snapshots__/create_index_pattern_wizard.test.js.snap index d7378e58d42bf..8c7018bf35f6e 100644 --- a/src/core_plugins/kibana/public/management/sections/indices/create_index_pattern_wizard/__tests__/__snapshots__/create_index_pattern_wizard.test.js.snap +++ b/src/core_plugins/kibana/public/management/sections/indices/create_index_pattern_wizard/__tests__/__snapshots__/create_index_pattern_wizard.test.js.snap @@ -1,132 +1,205 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP exports[`CreateIndexPatternWizard defaults to the loading state 1`] = ` -
-
+
+
+ +
+ - -
+ `; exports[`CreateIndexPatternWizard renders index pattern step when there are indices 1`] = ` -
-
- +
+
+ +
+ -
+ `; exports[`CreateIndexPatternWizard renders the empty state when there are no indices 1`] = ` -
-
- +
+
+ +
+ -
+ `; exports[`CreateIndexPatternWizard renders time field step when step is set to 2 1`] = ` -
-
+
+
+ +
+ - +`; + +exports[`CreateIndexPatternWizard renders when there are no indices but there are remote clusters 1`] = ` + +
+
+ +
+ -
+ `; exports[`CreateIndexPatternWizard shows system indices even if there are no other indices if the include system indices is toggled 1`] = ` -
-
- +
+
+ +
+ -
+ `; diff --git a/src/core_plugins/kibana/public/management/sections/indices/create_index_pattern_wizard/__tests__/create_index_pattern_wizard.test.js b/src/core_plugins/kibana/public/management/sections/indices/create_index_pattern_wizard/__tests__/create_index_pattern_wizard.test.js index d8ca11f04ae80..e95a5c7fa8448 100644 --- a/src/core_plugins/kibana/public/management/sections/indices/create_index_pattern_wizard/__tests__/create_index_pattern_wizard.test.js +++ b/src/core_plugins/kibana/public/management/sections/indices/create_index_pattern_wizard/__tests__/create_index_pattern_wizard.test.js @@ -42,6 +42,9 @@ jest.mock('../lib/get_indices', () => ({ ]; }, })); +jest.mock('ui/chrome', () => ({ + addBasePath: () => { }, +})); const loadingDataDocUrl = ''; const initialQuery = ''; @@ -80,6 +83,26 @@ describe('CreateIndexPatternWizard', () => { component.setState({ isInitiallyLoadingIndices: false, allIndices: [], + remoteClustersExist: false + }); + + await component.update(); + expect(component).toMatchSnapshot(); + }); + + it('renders when there are no indices but there are remote clusters', async () => { + const component = shallow( + + ); + + component.setState({ + isInitiallyLoadingIndices: false, + allIndices: [], + remoteClustersExist: true }); await component.update(); diff --git a/src/core_plugins/kibana/public/management/sections/indices/create_index_pattern_wizard/__tests__/render.test.js b/src/core_plugins/kibana/public/management/sections/indices/create_index_pattern_wizard/__tests__/render.test.js index 7c499401bf2fd..dd9ac07c5199e 100644 --- a/src/core_plugins/kibana/public/management/sections/indices/create_index_pattern_wizard/__tests__/render.test.js +++ b/src/core_plugins/kibana/public/management/sections/indices/create_index_pattern_wizard/__tests__/render.test.js @@ -32,6 +32,7 @@ jest.mock('ui/chrome', () => ({ getUiSettingsClient: () => ({ get: () => '', }), + addBasePath: () => { }, })); const { renderCreateIndexPatternWizard, destroyCreateIndexPatternWizard } = require('../render'); diff --git a/src/core_plugins/kibana/public/management/sections/indices/create_index_pattern_wizard/components/step_index_pattern/__tests__/step_index_pattern.test.js b/src/core_plugins/kibana/public/management/sections/indices/create_index_pattern_wizard/components/step_index_pattern/__tests__/step_index_pattern.test.js index 89abe1121e706..6c7deefcc0a45 100644 --- a/src/core_plugins/kibana/public/management/sections/indices/create_index_pattern_wizard/components/step_index_pattern/__tests__/step_index_pattern.test.js +++ b/src/core_plugins/kibana/public/management/sections/indices/create_index_pattern_wizard/components/step_index_pattern/__tests__/step_index_pattern.test.js @@ -41,6 +41,7 @@ jest.mock('ui/chrome', () => ({ getUiSettingsClient: () => ({ get: () => '', }), + addBasePath: () => { }, })); jest.mock('../../../lib/get_indices', () => ({ @@ -62,7 +63,7 @@ const esService = {}; const savedObjectsClient = { find: () => ({ savedObjects: [] }) }; -const goToNextStep = () => {}; +const goToNextStep = () => { }; const createComponent = props => { return shallowWithIntl( diff --git a/src/core_plugins/kibana/public/management/sections/indices/create_index_pattern_wizard/components/step_time_field/__tests__/step_time_field.test.js b/src/core_plugins/kibana/public/management/sections/indices/create_index_pattern_wizard/components/step_time_field/__tests__/step_time_field.test.js index a2448c2fc11fe..f066a36c24a02 100644 --- a/src/core_plugins/kibana/public/management/sections/indices/create_index_pattern_wizard/components/step_time_field/__tests__/step_time_field.test.js +++ b/src/core_plugins/kibana/public/management/sections/indices/create_index_pattern_wizard/components/step_time_field/__tests__/step_time_field.test.js @@ -29,6 +29,9 @@ jest.mock('../components/action_buttons', () => ({ ActionButtons: 'ActionButtons jest.mock('../../../lib/extract_time_fields', () => ({ extractTimeFields: fields => fields, })); +jest.mock('ui/chrome', () => ({ + addBasePath: () => { }, +})); const mockIndexPatternCreationType = { getIndexPatternType: () => 'default', diff --git a/src/core_plugins/kibana/public/management/sections/indices/create_index_pattern_wizard/create_index_pattern_wizard.js b/src/core_plugins/kibana/public/management/sections/indices/create_index_pattern_wizard/create_index_pattern_wizard.js index dc0568b85e00e..e48551a335aaf 100644 --- a/src/core_plugins/kibana/public/management/sections/indices/create_index_pattern_wizard/create_index_pattern_wizard.js +++ b/src/core_plugins/kibana/public/management/sections/indices/create_index_pattern_wizard/create_index_pattern_wizard.js @@ -20,6 +20,11 @@ import React, { Component } from 'react'; import PropTypes from 'prop-types'; +import { + EuiGlobalToastList +} from '@elastic/eui'; +import { FormattedMessage } from '@kbn/i18n/react'; + import { StepIndexPattern } from './components/step_index_pattern'; import { StepTimeField } from './components/step_time_field'; import { Header } from './components/header'; @@ -30,6 +35,7 @@ import { MAX_SEARCH_SIZE } from './constants'; import { ensureMinimumTime, getIndices, + getRemoteClusters } from './lib'; export class CreateIndexPatternWizard extends Component { @@ -52,20 +58,62 @@ export class CreateIndexPatternWizard extends Component { step: 1, indexPattern: '', allIndices: [], + remoteClustersExist: false, isInitiallyLoadingIndices: true, isIncludingSystemIndices: false, + toasts: [] }; } async componentWillMount() { - this.fetchIndices(); + this.fetchData(); } - fetchIndices = async () => { - this.setState({ allIndices: [], isInitiallyLoadingIndices: true }); + catchAndWarn = async (asyncFn, errorValue, errorMsg) => { + try { + return await asyncFn; + } catch (errors) { + this.setState(prevState => ({ + toasts: prevState.toasts.concat([{ + title: errorMsg, + id: errorMsg, + color: 'warning', + iconType: 'alert', + }]) + })); + return errorValue; + } + }; + + fetchData = async () => { const { services } = this.props; - const allIndices = await ensureMinimumTime(getIndices(services.es, this.indexPatternCreationType, `*`, MAX_SEARCH_SIZE)); // - this.setState({ allIndices, isInitiallyLoadingIndices: false }); + + this.setState({ + allIndices: [], + isInitiallyLoadingIndices: true, + remoteClustersExist: false + }); + + const indicesFailMsg = (); + + const clustersFailMsg = (); + + const [allIndices, remoteClusters] = await ensureMinimumTime([ + this.catchAndWarn(getIndices(services.es, this.indexPatternCreationType, `*`, MAX_SEARCH_SIZE), [], indicesFailMsg), + this.catchAndWarn(getRemoteClusters(services.$http), [], clustersFailMsg) + ]); + + this.setState({ + allIndices, + isInitiallyLoadingIndices: false, + remoteClustersExist: remoteClusters.length !== 0 + }); } createIndexPattern = async (timeFieldName, indexPatternId) => { @@ -127,6 +175,7 @@ export class CreateIndexPatternWizard extends Component { isIncludingSystemIndices, step, indexPattern, + remoteClustersExist } = this.state; if (isInitiallyLoadingIndices) { @@ -134,8 +183,10 @@ export class CreateIndexPatternWizard extends Component { } const hasDataIndices = allIndices.some(({ name }) => !name.startsWith('.')); - if (!hasDataIndices && !isIncludingSystemIndices) { - return ; + if (!hasDataIndices && + !isIncludingSystemIndices && + !remoteClustersExist) { + return ; } if (step === 1) { @@ -169,15 +220,28 @@ export class CreateIndexPatternWizard extends Component { return null; } + removeToast = (removedToast) => { + this.setState(prevState => ({ + toasts: prevState.toasts.filter(toast => toast.id !== removedToast.id), + })); + }; + render() { const header = this.renderHeader(); const content = this.renderContent(); return ( -
- {header} - {content} -
+ +
+ {header} + {content} +
+ +
); } } diff --git a/src/core_plugins/kibana/public/management/sections/indices/create_index_pattern_wizard/index.js b/src/core_plugins/kibana/public/management/sections/indices/create_index_pattern_wizard/index.js index 2db685ba85169..0fc80ee769b52 100644 --- a/src/core_plugins/kibana/public/management/sections/indices/create_index_pattern_wizard/index.js +++ b/src/core_plugins/kibana/public/management/sections/indices/create_index_pattern_wizard/index.js @@ -39,6 +39,7 @@ uiRoutes.when('/management/kibana/index', { config: $injector.get('config'), es: $injector.get('es'), indexPatterns: $injector.get('indexPatterns'), + $http: $injector.get('$http'), savedObjectsClient: Private(SavedObjectsClientProvider), indexPatternCreationType, changeUrl: url => { diff --git a/src/core_plugins/kibana/public/management/sections/indices/create_index_pattern_wizard/lib/get_remote_clusters.js b/src/core_plugins/kibana/public/management/sections/indices/create_index_pattern_wizard/lib/get_remote_clusters.js new file mode 100644 index 0000000000000..f1cbd48ac1116 --- /dev/null +++ b/src/core_plugins/kibana/public/management/sections/indices/create_index_pattern_wizard/lib/get_remote_clusters.js @@ -0,0 +1,26 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import chrome from 'ui/chrome'; +const apiPrefix = chrome.addBasePath('/api/kibana'); + +export async function getRemoteClusters($http) { + const response = await $http.get(`${apiPrefix}/clusters`); + return response.data; +} diff --git a/src/core_plugins/kibana/public/management/sections/indices/create_index_pattern_wizard/lib/index.js b/src/core_plugins/kibana/public/management/sections/indices/create_index_pattern_wizard/lib/index.js index 0930eb82514e1..edbab7360b433 100644 --- a/src/core_plugins/kibana/public/management/sections/indices/create_index_pattern_wizard/lib/index.js +++ b/src/core_plugins/kibana/public/management/sections/indices/create_index_pattern_wizard/lib/index.js @@ -28,3 +28,5 @@ export { getMatchedIndices } from './get_matched_indices'; export { containsIllegalCharacters } from './contains_illegal_characters'; export { extractTimeFields } from './extract_time_fields'; + +export { getRemoteClusters } from './get_remote_clusters'; diff --git a/src/core_plugins/kibana/server/routes/api/remote_info/call_with_request_factory.js b/src/core_plugins/kibana/server/routes/api/remote_info/call_with_request_factory.js new file mode 100644 index 0000000000000..dc70072ffd285 --- /dev/null +++ b/src/core_plugins/kibana/server/routes/api/remote_info/call_with_request_factory.js @@ -0,0 +1,37 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +/* + * 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 { once } from 'lodash'; + +const callWithRequest = once(server => { + const cluster = server.plugins.elasticsearch.getCluster('data'); + return cluster.callWithRequest; +}); + +export const callWithRequestFactory = (server, request) => { + return (...args) => { + return callWithRequest(server)(request, ...args); + }; +}; diff --git a/src/core_plugins/kibana/server/routes/api/remote_info/index.js b/src/core_plugins/kibana/server/routes/api/remote_info/index.js new file mode 100644 index 0000000000000..2d1e57d432550 --- /dev/null +++ b/src/core_plugins/kibana/server/routes/api/remote_info/index.js @@ -0,0 +1,51 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +/* + * 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 { callWithRequestFactory } from './call_with_request_factory'; +import handleEsError from '../../../lib/handle_es_error'; + +async function fetchRemoteClusters(callWithRequest) { + const options = { + method: 'GET', + path: '_remote/info' + }; + const remoteInfo = await callWithRequest('transport.request', options); + return Object.keys(remoteInfo); +} + +export function registerClustersRoute(server) { + server.route({ + path: '/api/kibana/clusters', + method: 'GET', + handler: async request => { + const callWithRequest = callWithRequestFactory(server, request); + try { + return await fetchRemoteClusters(callWithRequest); + } catch (error) { + throw handleEsError(error); + } + } + }); +} diff --git a/x-pack/plugins/index_management/server/routes/api/indices/register_list_route.js b/x-pack/plugins/index_management/server/routes/api/indices/register_list_route.js index af2a03f5bc4aa..bb27426044fbe 100644 --- a/x-pack/plugins/index_management/server/routes/api/indices/register_list_route.js +++ b/x-pack/plugins/index_management/server/routes/api/indices/register_list_route.js @@ -57,7 +57,7 @@ export function registerListRoute(server) { } }, config: { - pre: [ licensePreRouting ] + pre: [licensePreRouting] } }); }