From 1c13f545912f3766eeaf64677a37c503fee4a21e Mon Sep 17 00:00:00 2001 From: Kerry Gallagher Date: Wed, 6 Feb 2019 11:08:49 +0000 Subject: [PATCH] Add a custom and specific InvalidNode Error so that more helpful information can be displayed on the Metrics page Add a custom error for an invalid node check Add a component for displaying help when using an invalid node Throw the specific error from kibana_metrics_adapter Add an enum for specific error codes Look for and handle invalid node error in metrics/index Amend container to pass Error instance Add source configuration flyout to metrics/index to facilitate change source config button --- .../components/metrics/invalid_node.tsx | 79 +++++++++++++++++++ .../containers/metrics/with_metrics.tsx | 6 +- .../infra/public/pages/metrics/index.tsx | 16 +++- .../lib/adapters/metrics/adapter_types.ts | 4 + .../metrics/kibana_metrics_adapter.ts | 3 +- .../server/lib/adapters/metrics/lib/errors.ts | 15 ++++ 6 files changed, 118 insertions(+), 5 deletions(-) create mode 100644 x-pack/plugins/infra/public/components/metrics/invalid_node.tsx create mode 100644 x-pack/plugins/infra/server/lib/adapters/metrics/lib/errors.ts diff --git a/x-pack/plugins/infra/public/components/metrics/invalid_node.tsx b/x-pack/plugins/infra/public/components/metrics/invalid_node.tsx new file mode 100644 index 0000000000000..e5a3c8717bd39 --- /dev/null +++ b/x-pack/plugins/infra/public/components/metrics/invalid_node.tsx @@ -0,0 +1,79 @@ +/* + * 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 { EuiButton, EuiEmptyPrompt, EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; +import { InjectedIntl, injectI18n } from '@kbn/i18n/react'; +import React from 'react'; +import styled from 'styled-components'; +import { WithSourceConfigurationFlyoutState } from '../../components/source_configuration/source_configuration_flyout_state'; +import { WithKibanaChrome } from '../../containers/with_kibana_chrome'; + +interface InvalidNodeErrorProps { + nodeName: string; + intl: InjectedIntl; +} + +const invalidNodeError: React.SFC = ({ nodeName, intl }) => ( + + {({ basePath }) => ( + + {intl.formatMessage( + { + id: 'xpack.infra.metrics.invalidNodeErrorTitle', + defaultMessage: "Looks like {nodeName} isn't collecting any metrics data", + }, + { nodeName } + )} + + } + body={ +

+ {intl.formatMessage({ + id: 'xpack.infra.metrics.invalidNodeErrorDescription', + defaultMessage: 'Double check your configuration', + })} +

+ } + actions={ + + + + {intl.formatMessage({ + id: 'xpack.infra.homePage.noMetricsIndicesInstructionsActionLabel', + defaultMessage: 'View setup instructions', + })} + + + + + {({ enable }) => ( + + {intl.formatMessage({ + id: 'xpack.infra.configureSourceActionLabel', + defaultMessage: 'Change source configuration', + })} + + )} + + + + } + /> + )} +
+); + +export const InvalidNodeError = injectI18n(invalidNodeError); + +const CenteredEmptyPrompt = styled(EuiEmptyPrompt)` + align-self: center; +`; diff --git a/x-pack/plugins/infra/public/containers/metrics/with_metrics.tsx b/x-pack/plugins/infra/public/containers/metrics/with_metrics.tsx index 181133cfc917c..b04a40778dc22 100644 --- a/x-pack/plugins/infra/public/containers/metrics/with_metrics.tsx +++ b/x-pack/plugins/infra/public/containers/metrics/with_metrics.tsx @@ -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 { ApolloError } from 'apollo-client'; import React from 'react'; import { Query } from 'react-apollo'; import { @@ -18,7 +18,7 @@ import { metricsQuery } from './metrics.gql_query'; interface WithMetricsArgs { metrics: InfraMetricData[]; - error?: string | undefined; + error?: ApolloError | undefined; loading: boolean; refetch: () => void; } @@ -63,7 +63,7 @@ export const WithMetrics = ({ {({ data, error, loading, refetch }) => { return children({ metrics: filterOnlyInfraMetricData(data && data.source && data.source.metrics), - error: error && error.message, + error, loading, refetch, }); diff --git a/x-pack/plugins/infra/public/pages/metrics/index.tsx b/x-pack/plugins/infra/public/pages/metrics/index.tsx index d775d5e5d236f..61fb8113b261c 100644 --- a/x-pack/plugins/infra/public/pages/metrics/index.tsx +++ b/x-pack/plugins/infra/public/pages/metrics/index.tsx @@ -17,12 +17,15 @@ import { InjectedIntl, injectI18n } from '@kbn/i18n/react'; import React from 'react'; import styled, { withTheme } from 'styled-components'; +import { InfraMetricsErrorCodes } from '../../../server/lib/adapters/metrics/adapter_types'; import { AutoSizer } from '../../components/auto_sizer'; import { Header } from '../../components/header'; import { Metrics } from '../../components/metrics'; +import { InvalidNodeError } from '../../components/metrics/invalid_node'; import { MetricsSideNav } from '../../components/metrics/side_nav'; import { MetricsTimeControls } from '../../components/metrics/time_controls'; import { ColumnarPage, PageContent } from '../../components/page'; +import { SourceConfigurationFlyout } from '../../components/source_configuration'; import { WithMetadata } from '../../containers/metadata/with_metadata'; import { WithMetrics } from '../../containers/metrics/with_metrics'; import { @@ -113,6 +116,7 @@ export const MetricDetail = withTheme( return (
+ {({ metrics, error, loading, refetch }) => { if (error) { - return ; + if (error.graphQLErrors) { + const invalidNodeError = error.graphQLErrors.find( + (err: any) => err.code === InfraMetricsErrorCodes.invalid_node + ); + + if (invalidNodeError) { + return ; + } + } + + return ; } return ( diff --git a/x-pack/plugins/infra/server/lib/adapters/metrics/adapter_types.ts b/x-pack/plugins/infra/server/lib/adapters/metrics/adapter_types.ts index bce552b7f4fcc..956057a97da49 100644 --- a/x-pack/plugins/infra/server/lib/adapters/metrics/adapter_types.ts +++ b/x-pack/plugins/infra/server/lib/adapters/metrics/adapter_types.ts @@ -111,3 +111,7 @@ export type InfraMetricModelCreator = ( indexPattern: string | string[], interval: string ) => InfraMetricModel; + +export enum InfraMetricsErrorCodes { + invalid_node = 'METRICS_INVALID_NODE', +} diff --git a/x-pack/plugins/infra/server/lib/adapters/metrics/kibana_metrics_adapter.ts b/x-pack/plugins/infra/server/lib/adapters/metrics/kibana_metrics_adapter.ts index 02fe9020edc28..2eb0cdc0c4f5b 100644 --- a/x-pack/plugins/infra/server/lib/adapters/metrics/kibana_metrics_adapter.ts +++ b/x-pack/plugins/infra/server/lib/adapters/metrics/kibana_metrics_adapter.ts @@ -11,6 +11,7 @@ import { InfraMetric, InfraMetricData, InfraNodeType } from '../../../graphql/ty import { InfraBackendFrameworkAdapter, InfraFrameworkRequest } from '../framework'; import { InfraMetricsAdapter, InfraMetricsRequestOptions } from './adapter_types'; import { checkValidNode } from './lib/check_valid_node'; +import { InvalidNodeError } from './lib/errors'; import { metricModels } from './models'; export class KibanaMetricsAdapter implements InfraMetricsAdapter { @@ -45,7 +46,7 @@ export class KibanaMetricsAdapter implements InfraMetricsAdapter { const validNode = await checkValidNode(search, indexPattern, nodeField, options.nodeId); if (!validNode) { - throw new Error( + throw new InvalidNodeError( i18n.translate('xpack.infra.kibanaMetrics.nodeDoesNotExistErrorMessage', { defaultMessage: '{nodeId} does not exist.', values: { diff --git a/x-pack/plugins/infra/server/lib/adapters/metrics/lib/errors.ts b/x-pack/plugins/infra/server/lib/adapters/metrics/lib/errors.ts new file mode 100644 index 0000000000000..6a7dfda8bf05a --- /dev/null +++ b/x-pack/plugins/infra/server/lib/adapters/metrics/lib/errors.ts @@ -0,0 +1,15 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { ApolloError } from 'apollo-server-errors'; +import { InfraMetricsErrorCodes } from '../adapter_types'; + +export class InvalidNodeError extends ApolloError { + constructor(message: string) { + super(message, InfraMetricsErrorCodes.invalid_node); + Object.defineProperty(this, 'name', { value: 'InvalidNodeError' }); + } +}