From d207d555ed2265734a9e0d3a017dc1f05e2779b4 Mon Sep 17 00:00:00 2001 From: cauemarcondes Date: Mon, 15 Jun 2020 11:07:45 +0200 Subject: [PATCH 01/10] Adding apm data fetcher --- x-pack/plugins/apm/public/plugin.ts | 9 +++ .../services/rest/observability_dashboard.ts | 54 +++++++++++++++++ .../observability_dashboard/get_error_rate.ts | 59 +++++++++++++++++++ .../get_service_count.ts | 30 ++++++++++ .../get_transaction_distribution.ts | 51 ++++++++++++++++ .../lib/observability_dashboard/has_data.ts | 26 ++++++++ .../apm/server/routes/create_apm_api.ts | 7 ++- .../server/routes/observability_dashboard.ts | 38 ++++++++++++ .../public/application/index.tsx | 16 +++++ 9 files changed, 289 insertions(+), 1 deletion(-) create mode 100644 x-pack/plugins/apm/public/services/rest/observability_dashboard.ts create mode 100644 x-pack/plugins/apm/server/lib/observability_dashboard/get_error_rate.ts create mode 100644 x-pack/plugins/apm/server/lib/observability_dashboard/get_service_count.ts create mode 100644 x-pack/plugins/apm/server/lib/observability_dashboard/get_transaction_distribution.ts create mode 100644 x-pack/plugins/apm/server/lib/observability_dashboard/has_data.ts create mode 100644 x-pack/plugins/apm/server/routes/observability_dashboard.ts diff --git a/x-pack/plugins/apm/public/plugin.ts b/x-pack/plugins/apm/public/plugin.ts index e9de8fcd890d0..ec6b947c5b1cb 100644 --- a/x-pack/plugins/apm/public/plugin.ts +++ b/x-pack/plugins/apm/public/plugin.ts @@ -38,6 +38,7 @@ import { setHelpExtension } from './setHelpExtension'; import { toggleAppLinkInNav } from './toggleAppLinkInNav'; import { setReadonlyBadge } from './updateBadge'; import { createStaticIndexPattern } from './services/rest/index_pattern'; +import { fetchData, hasData } from './services/rest/observability_dashboard'; export type ApmPluginSetup = void; export type ApmPluginStart = void; @@ -73,6 +74,14 @@ export class ApmPlugin implements Plugin { pluginSetupDeps.home.environment.update({ apmUi: true }); pluginSetupDeps.home.featureCatalogue.register(featureCatalogueEntry); + if (plugins.observability) { + plugins.observability.dashboard.register({ + appName: 'apm', + fetchData, + hasData, + }); + } + core.application.register({ id: 'apm', title: 'APM', diff --git a/x-pack/plugins/apm/public/services/rest/observability_dashboard.ts b/x-pack/plugins/apm/public/services/rest/observability_dashboard.ts new file mode 100644 index 0000000000000..96a68d59b4ffd --- /dev/null +++ b/x-pack/plugins/apm/public/services/rest/observability_dashboard.ts @@ -0,0 +1,54 @@ +/* + * 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 { i18n } from '@kbn/i18n'; +import { FetchData } from '../../../../observability/public/typings/data_handler'; +import { callApmApi } from './createCallApmApi'; + +export const fetchData: FetchData = async ({ + startTime, + endTime, + bucketSize, +}) => { + const { + serviceCount, + transactionCoordinates, + errorCoordinates, + } = await callApmApi({ + pathname: '/api/apm/observability-dashboard', + params: { query: { start: startTime, end: endTime, bucketSize } }, + }); + + return { + title: i18n.translate('apm.observabilityDashboard.title', { + defaultMessage: 'APM', + }), + appLink: '/app/apm', + stats: [{ label: 'Services', value: serviceCount }], + series: [ + { + key: 'transactions', + label: i18n.translate('apm.observabilityDashboard.chart.transactions', { + defaultMessage: 'Transactions', + }), + coordinates: transactionCoordinates, + }, + { + key: 'errors', + label: i18n.translate('apm.observabilityDashboard.chart.errors', { + defaultMessage: 'Errors', + }), + coordinates: errorCoordinates, + }, + ], + }; +}; + +export async function hasData() { + return await callApmApi({ + pathname: '/api/apm/observability-dashboard/hasData', + }); +} diff --git a/x-pack/plugins/apm/server/lib/observability_dashboard/get_error_rate.ts b/x-pack/plugins/apm/server/lib/observability_dashboard/get_error_rate.ts new file mode 100644 index 0000000000000..771bf18d420b1 --- /dev/null +++ b/x-pack/plugins/apm/server/lib/observability_dashboard/get_error_rate.ts @@ -0,0 +1,59 @@ +/* + * 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 { PROCESSOR_EVENT } from '../../../common/elasticsearch_fieldnames'; +import { Coordinates } from '../../../../observability/public/typings/data_handler'; +import { Setup, SetupTimeRange } from '../helpers/setup_request'; +import { rangeFilter } from '../helpers/range_filter'; +import { getMetricsDateHistogramParams } from '../helpers/metrics'; +import { ProcessorEvent } from '../../../common/processor_event'; + +export async function getErrorRate({ + setup, + transactionCoordinates, +}: { + setup: Setup & SetupTimeRange; + transactionCoordinates: Coordinates[]; +}): Promise { + const { client, indices, start, end } = setup; + + const { aggregations } = await client.search({ + index: indices['apm_oss.errorIndices'], + body: { + size: 0, + query: { + bool: { + filter: [ + { term: { [PROCESSOR_EVENT]: ProcessorEvent.error } }, + { range: rangeFilter(start, end) }, + ], + }, + }, + aggs: { + distribution: { + date_histogram: getMetricsDateHistogramParams(start, end), + }, + }, + }, + }); + + const transactionCountByTimestamp: Record = {}; + transactionCoordinates.forEach(({ x, y }) => { + transactionCountByTimestamp[x] = y; + }); + + const errorRates = aggregations?.distribution.buckets.map((bucket) => { + const { key, doc_count: errorCount } = bucket; + const transactionCount = transactionCountByTimestamp[key] || 1; + const relativeRate = errorCount / transactionCount; + + return { + x: key, + y: transactionCoordinates.length ? relativeRate : undefined, + }; + }); + + return errorRates || []; +} diff --git a/x-pack/plugins/apm/server/lib/observability_dashboard/get_service_count.ts b/x-pack/plugins/apm/server/lib/observability_dashboard/get_service_count.ts new file mode 100644 index 0000000000000..0467df50395c1 --- /dev/null +++ b/x-pack/plugins/apm/server/lib/observability_dashboard/get_service_count.ts @@ -0,0 +1,30 @@ +/* + * 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 { SERVICE_NAME } from '../../../common/elasticsearch_fieldnames'; +import { Setup, SetupTimeRange } from '../helpers/setup_request'; +import { rangeFilter } from '../helpers/range_filter'; + +export async function getServiceCount({ + setup, +}: { + setup: Setup & SetupTimeRange; +}) { + const { client, indices, start, end } = setup; + + const params = { + // TODO: should I use only the transaction index here? + index: [indices['apm_oss.transactionIndices']], + body: { + size: 0, + query: { bool: { filter: [{ range: rangeFilter(start, end) }] } }, + aggs: { serviceCount: { cardinality: { field: SERVICE_NAME } } }, + }, + }; + + const { aggregations } = await client.search(params); + return aggregations?.serviceCount.value || 0; +} diff --git a/x-pack/plugins/apm/server/lib/observability_dashboard/get_transaction_distribution.ts b/x-pack/plugins/apm/server/lib/observability_dashboard/get_transaction_distribution.ts new file mode 100644 index 0000000000000..e5b8b5a48a4f6 --- /dev/null +++ b/x-pack/plugins/apm/server/lib/observability_dashboard/get_transaction_distribution.ts @@ -0,0 +1,51 @@ +/* + * 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. + */ +/* + * 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 { Coordinates } from '../../../../observability/public/typings/data_handler'; +import { PROCESSOR_EVENT } from '../../../common/elasticsearch_fieldnames'; +import { Setup, SetupTimeRange } from '../helpers/setup_request'; +import { rangeFilter } from '../helpers/range_filter'; +import { getMetricsDateHistogramParams } from '../helpers/metrics'; +import { ProcessorEvent } from '../../../common/processor_event'; + +export async function getTransactionDistribution({ + setup, +}: { + setup: Setup & SetupTimeRange; +}): Promise { + const { client, indices, start, end } = setup; + + const { aggregations } = await client.search({ + index: indices['apm_oss.transactionIndices'], + body: { + size: 0, + query: { + bool: { + filter: [ + { term: { [PROCESSOR_EVENT]: ProcessorEvent.transaction } }, + { range: rangeFilter(start, end) }, + ], + }, + }, + aggs: { + distribution: { + date_histogram: getMetricsDateHistogramParams(start, end), + }, + }, + }, + }); + + return ( + aggregations?.distribution.buckets.map((bucket) => ({ + x: bucket.key, + y: bucket.doc_count, + })) || [] + ); +} diff --git a/x-pack/plugins/apm/server/lib/observability_dashboard/has_data.ts b/x-pack/plugins/apm/server/lib/observability_dashboard/has_data.ts new file mode 100644 index 0000000000000..57d0dcb19cadb --- /dev/null +++ b/x-pack/plugins/apm/server/lib/observability_dashboard/has_data.ts @@ -0,0 +1,26 @@ +/* + * 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 { Setup } from '../helpers/setup_request'; + +export async function hasData({ setup }: { setup: Setup }) { + const { client, indices } = setup; + try { + const params = { + index: [ + indices['apm_oss.errorIndices'], + indices['apm_oss.transactionIndices'], + ], + terminateAfter: 1, + size: 0, + body: { query: {} }, + }; + + const response = await client.search(params); + return response.hits.total.value > 0; + } catch (e) { + return false; + } +} diff --git a/x-pack/plugins/apm/server/routes/create_apm_api.ts b/x-pack/plugins/apm/server/routes/create_apm_api.ts index a34690aff43b4..fb7026197c069 100644 --- a/x-pack/plugins/apm/server/routes/create_apm_api.ts +++ b/x-pack/plugins/apm/server/routes/create_apm_api.ts @@ -76,6 +76,7 @@ import { rumPageViewsTrendRoute, rumPageLoadDistributionRoute, } from './rum_client'; +import { fetchDataRoute, hasDataRoute } from './observability_dashboard'; const createApmApi = () => { const api = createApi() @@ -160,7 +161,11 @@ const createApmApi = () => { .add(rumOverviewLocalFiltersRoute) .add(rumPageViewsTrendRoute) .add(rumPageLoadDistributionRoute) - .add(rumClientMetricsRoute); + .add(rumClientMetricsRoute) + + // Observability dashboard + .add(fetchDataRoute) + .add(hasDataRoute); return api; }; diff --git a/x-pack/plugins/apm/server/routes/observability_dashboard.ts b/x-pack/plugins/apm/server/routes/observability_dashboard.ts new file mode 100644 index 0000000000000..75aae8e20563f --- /dev/null +++ b/x-pack/plugins/apm/server/routes/observability_dashboard.ts @@ -0,0 +1,38 @@ +/* + * 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 t from 'io-ts'; +import { setupRequest } from '../lib/helpers/setup_request'; +import { hasData } from '../lib/observability_dashboard/has_data'; +import { createRoute } from './create_route'; +import { rangeRt } from './default_api_types'; +import { getServiceCount } from '../lib/observability_dashboard/get_service_count'; +import { getTransactionDistribution } from '../lib/observability_dashboard/get_transaction_distribution'; +import { getErrorRate } from '../lib/observability_dashboard/get_error_rate'; + +export const hasDataRoute = createRoute(() => ({ + path: '/api/apm/observability-dashboard/hasData', + handler: async ({ context, request }) => { + const setup = await setupRequest(context, request); + return await hasData({ setup }); + }, +})); + +export const fetchDataRoute = createRoute(() => ({ + path: '/api/apm/observability-dashboard', + params: { + query: t.intersection([rangeRt, t.type({ bucketSize: t.string })]), + }, + handler: async ({ context, request }) => { + const setup = await setupRequest(context, request); + const serviceCount = await getServiceCount({ setup }); + const transactionCoordinates = await getTransactionDistribution({ setup }); + const errorCoordinates = await getErrorRate({ + setup, + transactionCoordinates, + }); + return { serviceCount, transactionCoordinates, errorCoordinates }; + }, +})); diff --git a/x-pack/plugins/observability/public/application/index.tsx b/x-pack/plugins/observability/public/application/index.tsx index 21a9fabf445f1..6ec9e412a931f 100644 --- a/x-pack/plugins/observability/public/application/index.tsx +++ b/x-pack/plugins/observability/public/application/index.tsx @@ -9,10 +9,26 @@ import { EuiThemeProvider } from '../../../../legacy/common/eui_styled_component import { AppMountParameters, CoreStart } from '../../../../../src/core/public'; import { Home } from '../pages/home'; import { PluginContext } from '../context/plugin_context'; +import { getDataHandler } from '../data_handler'; export const renderApp = (core: CoreStart, { element }: AppMountParameters) => { const i18nCore = core.i18n; const isDarkMode = core.uiSettings.get('theme:darkMode'); + const apmDataHandler = getDataHandler('apm'); + if (apmDataHandler) { + apmDataHandler + .fetchData({ + startTime: '2020-06-11T11:34:09.925Z', + endTime: '2020-06-12T11:34:09.926Z', + bucketSize: '1', + }) + .then((value) => { + console.log('#####', value); + }); + apmDataHandler.hasData().then((value) => { + console.log('#####', value); + }); + } ReactDOM.render( From b4ba6ff98f903dcb25cb551dbb3d818a2de61617 Mon Sep 17 00:00:00 2001 From: cauemarcondes Date: Mon, 15 Jun 2020 17:07:52 +0200 Subject: [PATCH 02/10] removing error rate --- .../services/rest/observability_dashboard.ts | 33 +++++++++---------- .../get_service_count.ts | 7 ++-- .../get_transaction_distribution.ts | 10 ++++-- .../lib/observability_dashboard/has_data.ts | 5 +-- .../apm/server/routes/create_apm_api.ts | 9 +++-- .../server/routes/observability_dashboard.ts | 13 ++++---- .../public/application/index.tsx | 16 --------- 7 files changed, 44 insertions(+), 49 deletions(-) diff --git a/x-pack/plugins/apm/public/services/rest/observability_dashboard.ts b/x-pack/plugins/apm/public/services/rest/observability_dashboard.ts index 96a68d59b4ffd..c1d1c58c32d4b 100644 --- a/x-pack/plugins/apm/public/services/rest/observability_dashboard.ts +++ b/x-pack/plugins/apm/public/services/rest/observability_dashboard.ts @@ -13,36 +13,35 @@ export const fetchData: FetchData = async ({ endTime, bucketSize, }) => { - const { - serviceCount, - transactionCoordinates, - errorCoordinates, - } = await callApmApi({ + const { serviceCount, transactionCoordinates } = await callApmApi({ pathname: '/api/apm/observability-dashboard', params: { query: { start: startTime, end: endTime, bucketSize } }, }); return { - title: i18n.translate('apm.observabilityDashboard.title', { + title: i18n.translate('xpack.apm.observabilityDashboard.title', { defaultMessage: 'APM', }), appLink: '/app/apm', - stats: [{ label: 'Services', value: serviceCount }], + stats: [ + { + label: i18n.translate( + 'xpack.apm.observabilityDashboard.stats.services', + { defaultMessage: 'Services' } + ), + value: serviceCount, + }, + ], series: [ { key: 'transactions', - label: i18n.translate('apm.observabilityDashboard.chart.transactions', { - defaultMessage: 'Transactions', - }), + label: i18n.translate( + 'xpack.apm.observabilityDashboard.chart.transactions', + { defaultMessage: 'Transactions' } + ), + color: 'euiColorVis1', coordinates: transactionCoordinates, }, - { - key: 'errors', - label: i18n.translate('apm.observabilityDashboard.chart.errors', { - defaultMessage: 'Errors', - }), - coordinates: errorCoordinates, - }, ], }; }; diff --git a/x-pack/plugins/apm/server/lib/observability_dashboard/get_service_count.ts b/x-pack/plugins/apm/server/lib/observability_dashboard/get_service_count.ts index 0467df50395c1..10b126e623857 100644 --- a/x-pack/plugins/apm/server/lib/observability_dashboard/get_service_count.ts +++ b/x-pack/plugins/apm/server/lib/observability_dashboard/get_service_count.ts @@ -16,8 +16,11 @@ export async function getServiceCount({ const { client, indices, start, end } = setup; const params = { - // TODO: should I use only the transaction index here? - index: [indices['apm_oss.transactionIndices']], + index: [ + indices['apm_oss.transactionIndices'], + indices['apm_oss.errorIndices'], + indices['apm_oss.metricsIndices'], + ], body: { size: 0, query: { bool: { filter: [{ range: rangeFilter(start, end) }] } }, diff --git a/x-pack/plugins/apm/server/lib/observability_dashboard/get_transaction_distribution.ts b/x-pack/plugins/apm/server/lib/observability_dashboard/get_transaction_distribution.ts index e5b8b5a48a4f6..86c1920032616 100644 --- a/x-pack/plugins/apm/server/lib/observability_dashboard/get_transaction_distribution.ts +++ b/x-pack/plugins/apm/server/lib/observability_dashboard/get_transaction_distribution.ts @@ -12,13 +12,14 @@ import { Coordinates } from '../../../../observability/public/typings/data_handl import { PROCESSOR_EVENT } from '../../../common/elasticsearch_fieldnames'; import { Setup, SetupTimeRange } from '../helpers/setup_request'; import { rangeFilter } from '../helpers/range_filter'; -import { getMetricsDateHistogramParams } from '../helpers/metrics'; import { ProcessorEvent } from '../../../common/processor_event'; export async function getTransactionDistribution({ setup, + bucketSize, }: { setup: Setup & SetupTimeRange; + bucketSize: string; }): Promise { const { client, indices, start, end } = setup; @@ -36,7 +37,12 @@ export async function getTransactionDistribution({ }, aggs: { distribution: { - date_histogram: getMetricsDateHistogramParams(start, end), + date_histogram: { + field: '@timestamp', + fixed_interval: bucketSize, + min_doc_count: 0, + extended_bounds: { min: start, max: end }, + }, }, }, }, diff --git a/x-pack/plugins/apm/server/lib/observability_dashboard/has_data.ts b/x-pack/plugins/apm/server/lib/observability_dashboard/has_data.ts index 57d0dcb19cadb..98935898bba57 100644 --- a/x-pack/plugins/apm/server/lib/observability_dashboard/has_data.ts +++ b/x-pack/plugins/apm/server/lib/observability_dashboard/has_data.ts @@ -10,12 +10,13 @@ export async function hasData({ setup }: { setup: Setup }) { try { const params = { index: [ - indices['apm_oss.errorIndices'], indices['apm_oss.transactionIndices'], + indices['apm_oss.errorIndices'], + indices['apm_oss.metricsIndices'], + indices['apm_oss.spanIndices'], ], terminateAfter: 1, size: 0, - body: { query: {} }, }; const response = await client.search(params); diff --git a/x-pack/plugins/apm/server/routes/create_apm_api.ts b/x-pack/plugins/apm/server/routes/create_apm_api.ts index fb7026197c069..b4276bf27d0f3 100644 --- a/x-pack/plugins/apm/server/routes/create_apm_api.ts +++ b/x-pack/plugins/apm/server/routes/create_apm_api.ts @@ -76,7 +76,10 @@ import { rumPageViewsTrendRoute, rumPageLoadDistributionRoute, } from './rum_client'; -import { fetchDataRoute, hasDataRoute } from './observability_dashboard'; +import { + fetchObservabilityDashboardDataRoute, + hasApmDataRoute, +} from './observability_dashboard'; const createApmApi = () => { const api = createApi() @@ -164,8 +167,8 @@ const createApmApi = () => { .add(rumClientMetricsRoute) // Observability dashboard - .add(fetchDataRoute) - .add(hasDataRoute); + .add(fetchObservabilityDashboardDataRoute) + .add(hasApmDataRoute); return api; }; diff --git a/x-pack/plugins/apm/server/routes/observability_dashboard.ts b/x-pack/plugins/apm/server/routes/observability_dashboard.ts index 75aae8e20563f..1b3449f788f80 100644 --- a/x-pack/plugins/apm/server/routes/observability_dashboard.ts +++ b/x-pack/plugins/apm/server/routes/observability_dashboard.ts @@ -10,9 +10,8 @@ import { createRoute } from './create_route'; import { rangeRt } from './default_api_types'; import { getServiceCount } from '../lib/observability_dashboard/get_service_count'; import { getTransactionDistribution } from '../lib/observability_dashboard/get_transaction_distribution'; -import { getErrorRate } from '../lib/observability_dashboard/get_error_rate'; -export const hasDataRoute = createRoute(() => ({ +export const hasApmDataRoute = createRoute(() => ({ path: '/api/apm/observability-dashboard/hasData', handler: async ({ context, request }) => { const setup = await setupRequest(context, request); @@ -20,19 +19,19 @@ export const hasDataRoute = createRoute(() => ({ }, })); -export const fetchDataRoute = createRoute(() => ({ +export const fetchObservabilityDashboardDataRoute = createRoute(() => ({ path: '/api/apm/observability-dashboard', params: { query: t.intersection([rangeRt, t.type({ bucketSize: t.string })]), }, handler: async ({ context, request }) => { const setup = await setupRequest(context, request); + const { bucketSize } = context.params.query; const serviceCount = await getServiceCount({ setup }); - const transactionCoordinates = await getTransactionDistribution({ setup }); - const errorCoordinates = await getErrorRate({ + const transactionCoordinates = await getTransactionDistribution({ setup, - transactionCoordinates, + bucketSize, }); - return { serviceCount, transactionCoordinates, errorCoordinates }; + return { serviceCount, transactionCoordinates }; }, })); diff --git a/x-pack/plugins/observability/public/application/index.tsx b/x-pack/plugins/observability/public/application/index.tsx index 6ec9e412a931f..21a9fabf445f1 100644 --- a/x-pack/plugins/observability/public/application/index.tsx +++ b/x-pack/plugins/observability/public/application/index.tsx @@ -9,26 +9,10 @@ import { EuiThemeProvider } from '../../../../legacy/common/eui_styled_component import { AppMountParameters, CoreStart } from '../../../../../src/core/public'; import { Home } from '../pages/home'; import { PluginContext } from '../context/plugin_context'; -import { getDataHandler } from '../data_handler'; export const renderApp = (core: CoreStart, { element }: AppMountParameters) => { const i18nCore = core.i18n; const isDarkMode = core.uiSettings.get('theme:darkMode'); - const apmDataHandler = getDataHandler('apm'); - if (apmDataHandler) { - apmDataHandler - .fetchData({ - startTime: '2020-06-11T11:34:09.925Z', - endTime: '2020-06-12T11:34:09.926Z', - bucketSize: '1', - }) - .then((value) => { - console.log('#####', value); - }); - apmDataHandler.hasData().then((value) => { - console.log('#####', value); - }); - } ReactDOM.render( From be59683d1eacce4ea911528ccc89b0200f8e6d53 Mon Sep 17 00:00:00 2001 From: cauemarcondes Date: Tue, 23 Jun 2020 11:21:58 +0200 Subject: [PATCH 03/10] chaging observability dashboard routes --- .../services/rest/observability_dashboard.ts | 14 ++++- .../observability_dashboard/get_error_rate.ts | 59 ------------------- ...tion.ts => get_transaction_coordinates.ts} | 4 +- .../apm/server/routes/create_apm_api.ts | 10 ++-- .../server/routes/observability_dashboard.ts | 22 ++++--- 5 files changed, 35 insertions(+), 74 deletions(-) delete mode 100644 x-pack/plugins/apm/server/lib/observability_dashboard/get_error_rate.ts rename x-pack/plugins/apm/server/lib/observability_dashboard/{get_transaction_distribution.ts => get_transaction_coordinates.ts} (93%) diff --git a/x-pack/plugins/apm/public/services/rest/observability_dashboard.ts b/x-pack/plugins/apm/public/services/rest/observability_dashboard.ts index c1d1c58c32d4b..8b42b17c14a4a 100644 --- a/x-pack/plugins/apm/public/services/rest/observability_dashboard.ts +++ b/x-pack/plugins/apm/public/services/rest/observability_dashboard.ts @@ -13,11 +13,21 @@ export const fetchData: FetchData = async ({ endTime, bucketSize, }) => { - const { serviceCount, transactionCoordinates } = await callApmApi({ - pathname: '/api/apm/observability-dashboard', + const serviceCountPromise = callApmApi({ + pathname: '/api/apm/observability-dashboard/service-count', params: { query: { start: startTime, end: endTime, bucketSize } }, }); + const transactionCoordinatesPromise = callApmApi({ + pathname: '/api/apm/observability-dashboard/transactions', + params: { query: { start: startTime, end: endTime, bucketSize } }, + }); + + const [serviceCount, transactionCoordinates] = await Promise.all([ + serviceCountPromise, + transactionCoordinatesPromise, + ]); + return { title: i18n.translate('xpack.apm.observabilityDashboard.title', { defaultMessage: 'APM', diff --git a/x-pack/plugins/apm/server/lib/observability_dashboard/get_error_rate.ts b/x-pack/plugins/apm/server/lib/observability_dashboard/get_error_rate.ts deleted file mode 100644 index 771bf18d420b1..0000000000000 --- a/x-pack/plugins/apm/server/lib/observability_dashboard/get_error_rate.ts +++ /dev/null @@ -1,59 +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 { PROCESSOR_EVENT } from '../../../common/elasticsearch_fieldnames'; -import { Coordinates } from '../../../../observability/public/typings/data_handler'; -import { Setup, SetupTimeRange } from '../helpers/setup_request'; -import { rangeFilter } from '../helpers/range_filter'; -import { getMetricsDateHistogramParams } from '../helpers/metrics'; -import { ProcessorEvent } from '../../../common/processor_event'; - -export async function getErrorRate({ - setup, - transactionCoordinates, -}: { - setup: Setup & SetupTimeRange; - transactionCoordinates: Coordinates[]; -}): Promise { - const { client, indices, start, end } = setup; - - const { aggregations } = await client.search({ - index: indices['apm_oss.errorIndices'], - body: { - size: 0, - query: { - bool: { - filter: [ - { term: { [PROCESSOR_EVENT]: ProcessorEvent.error } }, - { range: rangeFilter(start, end) }, - ], - }, - }, - aggs: { - distribution: { - date_histogram: getMetricsDateHistogramParams(start, end), - }, - }, - }, - }); - - const transactionCountByTimestamp: Record = {}; - transactionCoordinates.forEach(({ x, y }) => { - transactionCountByTimestamp[x] = y; - }); - - const errorRates = aggregations?.distribution.buckets.map((bucket) => { - const { key, doc_count: errorCount } = bucket; - const transactionCount = transactionCountByTimestamp[key] || 1; - const relativeRate = errorCount / transactionCount; - - return { - x: key, - y: transactionCoordinates.length ? relativeRate : undefined, - }; - }); - - return errorRates || []; -} diff --git a/x-pack/plugins/apm/server/lib/observability_dashboard/get_transaction_distribution.ts b/x-pack/plugins/apm/server/lib/observability_dashboard/get_transaction_coordinates.ts similarity index 93% rename from x-pack/plugins/apm/server/lib/observability_dashboard/get_transaction_distribution.ts rename to x-pack/plugins/apm/server/lib/observability_dashboard/get_transaction_coordinates.ts index 86c1920032616..5dc0c82b4424c 100644 --- a/x-pack/plugins/apm/server/lib/observability_dashboard/get_transaction_distribution.ts +++ b/x-pack/plugins/apm/server/lib/observability_dashboard/get_transaction_coordinates.ts @@ -8,13 +8,13 @@ * or more contributor license agreements. Licensed under the Elastic License; * you may not use this file except in compliance with the Elastic License. */ +import { rangeFilter } from '../../../common/utils/range_filter'; import { Coordinates } from '../../../../observability/public/typings/data_handler'; import { PROCESSOR_EVENT } from '../../../common/elasticsearch_fieldnames'; import { Setup, SetupTimeRange } from '../helpers/setup_request'; -import { rangeFilter } from '../helpers/range_filter'; import { ProcessorEvent } from '../../../common/processor_event'; -export async function getTransactionDistribution({ +export async function getTransactionCoordinates({ setup, bucketSize, }: { diff --git a/x-pack/plugins/apm/server/routes/create_apm_api.ts b/x-pack/plugins/apm/server/routes/create_apm_api.ts index b4276bf27d0f3..abc674832f3e4 100644 --- a/x-pack/plugins/apm/server/routes/create_apm_api.ts +++ b/x-pack/plugins/apm/server/routes/create_apm_api.ts @@ -77,8 +77,9 @@ import { rumPageLoadDistributionRoute, } from './rum_client'; import { - fetchObservabilityDashboardDataRoute, - hasApmDataRoute, + observabilityDashboardHasDataRoute, + observabilityDashboardServiceCountRoute, + observabilityDashboardTransactionsRoute, } from './observability_dashboard'; const createApmApi = () => { @@ -167,8 +168,9 @@ const createApmApi = () => { .add(rumClientMetricsRoute) // Observability dashboard - .add(fetchObservabilityDashboardDataRoute) - .add(hasApmDataRoute); + .add(observabilityDashboardHasDataRoute) + .add(observabilityDashboardServiceCountRoute) + .add(observabilityDashboardTransactionsRoute); return api; }; diff --git a/x-pack/plugins/apm/server/routes/observability_dashboard.ts b/x-pack/plugins/apm/server/routes/observability_dashboard.ts index 1b3449f788f80..3f33a9a3b79eb 100644 --- a/x-pack/plugins/apm/server/routes/observability_dashboard.ts +++ b/x-pack/plugins/apm/server/routes/observability_dashboard.ts @@ -9,9 +9,9 @@ import { hasData } from '../lib/observability_dashboard/has_data'; import { createRoute } from './create_route'; import { rangeRt } from './default_api_types'; import { getServiceCount } from '../lib/observability_dashboard/get_service_count'; -import { getTransactionDistribution } from '../lib/observability_dashboard/get_transaction_distribution'; +import { getTransactionCoordinates } from '../lib/observability_dashboard/get_transaction_coordinates'; -export const hasApmDataRoute = createRoute(() => ({ +export const observabilityDashboardHasDataRoute = createRoute(() => ({ path: '/api/apm/observability-dashboard/hasData', handler: async ({ context, request }) => { const setup = await setupRequest(context, request); @@ -19,19 +19,27 @@ export const hasApmDataRoute = createRoute(() => ({ }, })); -export const fetchObservabilityDashboardDataRoute = createRoute(() => ({ - path: '/api/apm/observability-dashboard', +export const observabilityDashboardServiceCountRoute = createRoute(() => ({ + path: '/api/apm/observability-dashboard/service-count', + params: { + query: t.intersection([rangeRt, t.type({ bucketSize: t.string })]), + }, + handler: async ({ context, request }) => { + const setup = await setupRequest(context, request); + return await getServiceCount({ setup }); + }, +})); +export const observabilityDashboardTransactionsRoute = createRoute(() => ({ + path: '/api/apm/observability-dashboard/transactions', params: { query: t.intersection([rangeRt, t.type({ bucketSize: t.string })]), }, handler: async ({ context, request }) => { const setup = await setupRequest(context, request); const { bucketSize } = context.params.query; - const serviceCount = await getServiceCount({ setup }); - const transactionCoordinates = await getTransactionDistribution({ + return await getTransactionCoordinates({ setup, bucketSize, }); - return { serviceCount, transactionCoordinates }; }, })); From 679ccd745d6c5eb6a706cee19977e14d76fdf22d Mon Sep 17 00:00:00 2001 From: cauemarcondes Date: Tue, 23 Jun 2020 13:18:11 +0200 Subject: [PATCH 04/10] APM observability fetch data --- .../services/rest/observability_dashboard.ts | 42 ++++++++++--------- .../get_service_count.ts | 2 +- .../apm/server/routes/create_apm_api.ts | 6 +-- .../server/routes/observability_dashboard.ts | 19 +++------ 4 files changed, 31 insertions(+), 38 deletions(-) diff --git a/x-pack/plugins/apm/public/services/rest/observability_dashboard.ts b/x-pack/plugins/apm/public/services/rest/observability_dashboard.ts index 8b42b17c14a4a..b6d8a81079d86 100644 --- a/x-pack/plugins/apm/public/services/rest/observability_dashboard.ts +++ b/x-pack/plugins/apm/public/services/rest/observability_dashboard.ts @@ -5,46 +5,48 @@ */ import { i18n } from '@kbn/i18n'; -import { FetchData } from '../../../../observability/public/typings/data_handler'; +import { sum } from 'lodash'; +import { + ApmFetchDataResponse, + FetchData, +} from '../../../../observability/public/typings/data_handler'; import { callApmApi } from './createCallApmApi'; -export const fetchData: FetchData = async ({ +export const fetchData: FetchData = async ({ startTime, endTime, bucketSize, }) => { - const serviceCountPromise = callApmApi({ - pathname: '/api/apm/observability-dashboard/service-count', + const data = await callApmApi({ + pathname: '/api/apm/observability-dashboard', params: { query: { start: startTime, end: endTime, bucketSize } }, }); - const transactionCoordinatesPromise = callApmApi({ - pathname: '/api/apm/observability-dashboard/transactions', - params: { query: { start: startTime, end: endTime, bucketSize } }, - }); - - const [serviceCount, transactionCoordinates] = await Promise.all([ - serviceCountPromise, - transactionCoordinatesPromise, - ]); + const { serviceCount, transactionCoordinates } = data; return { title: i18n.translate('xpack.apm.observabilityDashboard.title', { defaultMessage: 'APM', }), appLink: '/app/apm', - stats: [ - { + stats: { + services: { label: i18n.translate( 'xpack.apm.observabilityDashboard.stats.services', { defaultMessage: 'Services' } ), value: serviceCount, }, - ], - series: [ - { - key: 'transactions', + transactions: { + label: i18n.translate( + 'xpack.apm.observabilityDashboard.stats.transactions', + { defaultMessage: 'Transactions' } + ), + value: sum(transactionCoordinates.map((coordinates) => coordinates.y)), + }, + }, + series: { + transactions: { label: i18n.translate( 'xpack.apm.observabilityDashboard.chart.transactions', { defaultMessage: 'Transactions' } @@ -52,7 +54,7 @@ export const fetchData: FetchData = async ({ color: 'euiColorVis1', coordinates: transactionCoordinates, }, - ], + }, }; }; diff --git a/x-pack/plugins/apm/server/lib/observability_dashboard/get_service_count.ts b/x-pack/plugins/apm/server/lib/observability_dashboard/get_service_count.ts index 10b126e623857..b33802b74789e 100644 --- a/x-pack/plugins/apm/server/lib/observability_dashboard/get_service_count.ts +++ b/x-pack/plugins/apm/server/lib/observability_dashboard/get_service_count.ts @@ -4,9 +4,9 @@ * you may not use this file except in compliance with the Elastic License. */ +import { rangeFilter } from '../../../common/utils/range_filter'; import { SERVICE_NAME } from '../../../common/elasticsearch_fieldnames'; import { Setup, SetupTimeRange } from '../helpers/setup_request'; -import { rangeFilter } from '../helpers/range_filter'; export async function getServiceCount({ setup, diff --git a/x-pack/plugins/apm/server/routes/create_apm_api.ts b/x-pack/plugins/apm/server/routes/create_apm_api.ts index abc674832f3e4..02be2e7e4dcdf 100644 --- a/x-pack/plugins/apm/server/routes/create_apm_api.ts +++ b/x-pack/plugins/apm/server/routes/create_apm_api.ts @@ -78,8 +78,7 @@ import { } from './rum_client'; import { observabilityDashboardHasDataRoute, - observabilityDashboardServiceCountRoute, - observabilityDashboardTransactionsRoute, + observabilityDashboardDataRoute, } from './observability_dashboard'; const createApmApi = () => { @@ -169,8 +168,7 @@ const createApmApi = () => { // Observability dashboard .add(observabilityDashboardHasDataRoute) - .add(observabilityDashboardServiceCountRoute) - .add(observabilityDashboardTransactionsRoute); + .add(observabilityDashboardDataRoute); return api; }; diff --git a/x-pack/plugins/apm/server/routes/observability_dashboard.ts b/x-pack/plugins/apm/server/routes/observability_dashboard.ts index 3f33a9a3b79eb..f09e25955b448 100644 --- a/x-pack/plugins/apm/server/routes/observability_dashboard.ts +++ b/x-pack/plugins/apm/server/routes/observability_dashboard.ts @@ -19,27 +19,20 @@ export const observabilityDashboardHasDataRoute = createRoute(() => ({ }, })); -export const observabilityDashboardServiceCountRoute = createRoute(() => ({ - path: '/api/apm/observability-dashboard/service-count', - params: { - query: t.intersection([rangeRt, t.type({ bucketSize: t.string })]), - }, - handler: async ({ context, request }) => { - const setup = await setupRequest(context, request); - return await getServiceCount({ setup }); - }, -})); -export const observabilityDashboardTransactionsRoute = createRoute(() => ({ - path: '/api/apm/observability-dashboard/transactions', +export const observabilityDashboardDataRoute = createRoute(() => ({ + path: '/api/apm/observability-dashboard', params: { query: t.intersection([rangeRt, t.type({ bucketSize: t.string })]), }, handler: async ({ context, request }) => { const setup = await setupRequest(context, request); const { bucketSize } = context.params.query; - return await getTransactionCoordinates({ + const serviceCount = await getServiceCount({ setup }); + const transactionCoordinates = await getTransactionCoordinates({ setup, bucketSize, }); + + return { serviceCount, transactionCoordinates }; }, })); From 1188e7f61eb273e1b79737d3d612bca2a954b63c Mon Sep 17 00:00:00 2001 From: cauemarcondes Date: Tue, 23 Jun 2020 15:33:51 +0200 Subject: [PATCH 05/10] fixing imports --- .../apm/public/services/rest/observability_dashboard.ts | 7 +++---- .../observability_dashboard/get_transaction_coordinates.ts | 2 +- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/x-pack/plugins/apm/public/services/rest/observability_dashboard.ts b/x-pack/plugins/apm/public/services/rest/observability_dashboard.ts index b6d8a81079d86..7570ecd1e45ad 100644 --- a/x-pack/plugins/apm/public/services/rest/observability_dashboard.ts +++ b/x-pack/plugins/apm/public/services/rest/observability_dashboard.ts @@ -6,10 +6,9 @@ import { i18n } from '@kbn/i18n'; import { sum } from 'lodash'; -import { - ApmFetchDataResponse, - FetchData, -} from '../../../../observability/public/typings/data_handler'; +// eslint-disable-next-line @kbn/eslint/no-restricted-paths +import { FetchData } from '../../../../observability/public/data_handler'; +import { ApmFetchDataResponse } from '../../../../observability/public/typings/fetch_data_response'; import { callApmApi } from './createCallApmApi'; export const fetchData: FetchData = async ({ diff --git a/x-pack/plugins/apm/server/lib/observability_dashboard/get_transaction_coordinates.ts b/x-pack/plugins/apm/server/lib/observability_dashboard/get_transaction_coordinates.ts index 5dc0c82b4424c..78ed11d839ad2 100644 --- a/x-pack/plugins/apm/server/lib/observability_dashboard/get_transaction_coordinates.ts +++ b/x-pack/plugins/apm/server/lib/observability_dashboard/get_transaction_coordinates.ts @@ -9,7 +9,7 @@ * you may not use this file except in compliance with the Elastic License. */ import { rangeFilter } from '../../../common/utils/range_filter'; -import { Coordinates } from '../../../../observability/public/typings/data_handler'; +import { Coordinates } from '../../../../observability/public/typings/fetch_data_response'; import { PROCESSOR_EVENT } from '../../../common/elasticsearch_fieldnames'; import { Setup, SetupTimeRange } from '../helpers/setup_request'; import { ProcessorEvent } from '../../../common/processor_event'; From cca31e9ccf29516242c70daefe80b73547752ae4 Mon Sep 17 00:00:00 2001 From: cauemarcondes Date: Tue, 23 Jun 2020 16:23:29 +0200 Subject: [PATCH 06/10] adding unit test --- .../rest/observability.dashboard.test.ts | 106 ++++++++++++++++++ 1 file changed, 106 insertions(+) create mode 100644 x-pack/plugins/apm/public/services/rest/observability.dashboard.test.ts diff --git a/x-pack/plugins/apm/public/services/rest/observability.dashboard.test.ts b/x-pack/plugins/apm/public/services/rest/observability.dashboard.test.ts new file mode 100644 index 0000000000000..ff9dca41ab36e --- /dev/null +++ b/x-pack/plugins/apm/public/services/rest/observability.dashboard.test.ts @@ -0,0 +1,106 @@ +/* + * 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 { fetchData, hasData } from './observability_dashboard'; +import * as createCallApmApi from './createCallApmApi'; + +describe('Observability dashboard data', () => { + const callApmApiMock = jest.spyOn(createCallApmApi, 'callApmApi'); + afterEach(() => { + callApmApiMock.mockClear(); + }); + describe('hasData', () => { + it('returns false when no data is available', async () => { + callApmApiMock.mockImplementation(() => Promise.resolve(false)); + const response = await hasData(); + expect(response).toBeFalsy(); + }); + it('returns true when data is available', async () => { + callApmApiMock.mockImplementation(() => Promise.resolve(true)); + const response = await hasData(); + expect(response).toBeTruthy(); + }); + }); + + describe('fetchData', () => { + it('returns APM data with series and stats', async () => { + callApmApiMock.mockImplementation(() => + Promise.resolve({ + serviceCount: 10, + transactionCoordinates: [ + { x: 1, y: 1 }, + { x: 2, y: 2 }, + { x: 3, y: 3 }, + ], + }) + ); + const response = await fetchData({ + startTime: '1', + endTime: '2', + bucketSize: '3', + }); + expect(response).toEqual({ + title: 'APM', + appLink: '/app/apm', + stats: { + services: { + label: 'Services', + value: 10, + }, + transactions: { + label: 'Transactions', + value: 6, + }, + }, + series: { + transactions: { + label: 'Transactions', + coordinates: [ + { x: 1, y: 1 }, + { x: 2, y: 2 }, + { x: 3, y: 3 }, + ], + color: 'euiColorVis1', + }, + }, + }); + }); + it('returns empty transaction coordinates', async () => { + callApmApiMock.mockImplementation(() => + Promise.resolve({ + serviceCount: 0, + transactionCoordinates: [], + }) + ); + const response = await fetchData({ + startTime: '1', + endTime: '2', + bucketSize: '3', + }); + expect(response).toEqual({ + title: 'APM', + appLink: '/app/apm', + stats: { + services: { + label: 'Services', + value: 0, + }, + transactions: { + label: 'Transactions', + value: 0, + }, + }, + series: { + transactions: { + label: 'Transactions', + coordinates: [], + color: 'euiColorVis1', + }, + }, + }); + }); + }); +}); From 9a92fae470e0af2e6f5fceb1196a02888902b65d Mon Sep 17 00:00:00 2001 From: cauemarcondes Date: Wed, 24 Jun 2020 13:19:36 +0200 Subject: [PATCH 07/10] addressing PR comments --- x-pack/plugins/apm/public/plugin.ts | 10 ++++-- .../rest/observability.dashboard.test.ts | 36 +++++++++++-------- .../services/rest/observability_dashboard.ts | 26 +++++++++----- .../lib/observability_dashboard/has_data.ts | 1 - .../server/routes/observability_dashboard.ts | 13 ++++--- .../observability/public/data_handler.ts | 2 +- 6 files changed, 56 insertions(+), 32 deletions(-) diff --git a/x-pack/plugins/apm/public/plugin.ts b/x-pack/plugins/apm/public/plugin.ts index ec6b947c5b1cb..78fe2ac9c50a0 100644 --- a/x-pack/plugins/apm/public/plugin.ts +++ b/x-pack/plugins/apm/public/plugin.ts @@ -38,7 +38,10 @@ import { setHelpExtension } from './setHelpExtension'; import { toggleAppLinkInNav } from './toggleAppLinkInNav'; import { setReadonlyBadge } from './updateBadge'; import { createStaticIndexPattern } from './services/rest/index_pattern'; -import { fetchData, hasData } from './services/rest/observability_dashboard'; +import { + fetchLandingPageData, + hasData, +} from './services/rest/observability_dashboard'; export type ApmPluginSetup = void; export type ApmPluginStart = void; @@ -75,9 +78,12 @@ export class ApmPlugin implements Plugin { pluginSetupDeps.home.featureCatalogue.register(featureCatalogueEntry); if (plugins.observability) { + const isDarkMode = core.uiSettings.get('theme:darkMode'); plugins.observability.dashboard.register({ appName: 'apm', - fetchData, + fetchData: async (params) => { + return fetchLandingPageData(params, { isDarkMode }); + }, hasData, }); } diff --git a/x-pack/plugins/apm/public/services/rest/observability.dashboard.test.ts b/x-pack/plugins/apm/public/services/rest/observability.dashboard.test.ts index ff9dca41ab36e..5e6dc111c29c1 100644 --- a/x-pack/plugins/apm/public/services/rest/observability.dashboard.test.ts +++ b/x-pack/plugins/apm/public/services/rest/observability.dashboard.test.ts @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import { fetchData, hasData } from './observability_dashboard'; +import { fetchLandingPageData, hasData } from './observability_dashboard'; import * as createCallApmApi from './createCallApmApi'; describe('Observability dashboard data', () => { @@ -25,7 +25,7 @@ describe('Observability dashboard data', () => { }); }); - describe('fetchData', () => { + describe('fetchLandingPageData', () => { it('returns APM data with series and stats', async () => { callApmApiMock.mockImplementation(() => Promise.resolve({ @@ -37,11 +37,14 @@ describe('Observability dashboard data', () => { ], }) ); - const response = await fetchData({ - startTime: '1', - endTime: '2', - bucketSize: '3', - }); + const response = await fetchLandingPageData( + { + startTime: '1', + endTime: '2', + bucketSize: '3', + }, + { isDarkMode: false } + ); expect(response).toEqual({ title: 'APM', appLink: '/app/apm', @@ -53,6 +56,7 @@ describe('Observability dashboard data', () => { transactions: { label: 'Transactions', value: 6, + color: '#6092c0', }, }, series: { @@ -63,7 +67,7 @@ describe('Observability dashboard data', () => { { x: 2, y: 2 }, { x: 3, y: 3 }, ], - color: 'euiColorVis1', + color: '#6092c0', }, }, }); @@ -75,11 +79,14 @@ describe('Observability dashboard data', () => { transactionCoordinates: [], }) ); - const response = await fetchData({ - startTime: '1', - endTime: '2', - bucketSize: '3', - }); + const response = await fetchLandingPageData( + { + startTime: '1', + endTime: '2', + bucketSize: '3', + }, + { isDarkMode: false } + ); expect(response).toEqual({ title: 'APM', appLink: '/app/apm', @@ -91,13 +98,14 @@ describe('Observability dashboard data', () => { transactions: { label: 'Transactions', value: 0, + color: '#6092c0', }, }, series: { transactions: { label: 'Transactions', coordinates: [], - color: 'euiColorVis1', + color: '#6092c0', }, }, }); diff --git a/x-pack/plugins/apm/public/services/rest/observability_dashboard.ts b/x-pack/plugins/apm/public/services/rest/observability_dashboard.ts index 7570ecd1e45ad..eb374b085e15b 100644 --- a/x-pack/plugins/apm/public/services/rest/observability_dashboard.ts +++ b/x-pack/plugins/apm/public/services/rest/observability_dashboard.ts @@ -6,18 +6,25 @@ import { i18n } from '@kbn/i18n'; import { sum } from 'lodash'; +import lightTheme from '@elastic/eui/dist/eui_theme_light.json'; +import darkTheme from '@elastic/eui/dist/eui_theme_dark.json'; // eslint-disable-next-line @kbn/eslint/no-restricted-paths -import { FetchData } from '../../../../observability/public/data_handler'; +import { FetchDataParams } from '../../../../observability/public/data_handler'; import { ApmFetchDataResponse } from '../../../../observability/public/typings/fetch_data_response'; import { callApmApi } from './createCallApmApi'; -export const fetchData: FetchData = async ({ - startTime, - endTime, - bucketSize, -}) => { +interface Options { + isDarkMode: boolean; +} + +export const fetchLandingPageData = async ( + { startTime, endTime, bucketSize }: FetchDataParams, + { isDarkMode }: Options +): Promise => { + const theme = isDarkMode ? darkTheme : lightTheme; + const data = await callApmApi({ - pathname: '/api/apm/observability-dashboard', + pathname: '/api/apm/observability_dashboard', params: { query: { start: startTime, end: endTime, bucketSize } }, }); @@ -42,6 +49,7 @@ export const fetchData: FetchData = async ({ { defaultMessage: 'Transactions' } ), value: sum(transactionCoordinates.map((coordinates) => coordinates.y)), + color: theme.euiColorVis1, }, }, series: { @@ -50,7 +58,7 @@ export const fetchData: FetchData = async ({ 'xpack.apm.observabilityDashboard.chart.transactions', { defaultMessage: 'Transactions' } ), - color: 'euiColorVis1', + color: theme.euiColorVis1, coordinates: transactionCoordinates, }, }, @@ -59,6 +67,6 @@ export const fetchData: FetchData = async ({ export async function hasData() { return await callApmApi({ - pathname: '/api/apm/observability-dashboard/hasData', + pathname: '/api/apm/observability_dashboard/has_data', }); } diff --git a/x-pack/plugins/apm/server/lib/observability_dashboard/has_data.ts b/x-pack/plugins/apm/server/lib/observability_dashboard/has_data.ts index 98935898bba57..73cc2d273ec69 100644 --- a/x-pack/plugins/apm/server/lib/observability_dashboard/has_data.ts +++ b/x-pack/plugins/apm/server/lib/observability_dashboard/has_data.ts @@ -13,7 +13,6 @@ export async function hasData({ setup }: { setup: Setup }) { indices['apm_oss.transactionIndices'], indices['apm_oss.errorIndices'], indices['apm_oss.metricsIndices'], - indices['apm_oss.spanIndices'], ], terminateAfter: 1, size: 0, diff --git a/x-pack/plugins/apm/server/routes/observability_dashboard.ts b/x-pack/plugins/apm/server/routes/observability_dashboard.ts index f09e25955b448..10c74295fe3e4 100644 --- a/x-pack/plugins/apm/server/routes/observability_dashboard.ts +++ b/x-pack/plugins/apm/server/routes/observability_dashboard.ts @@ -12,7 +12,7 @@ import { getServiceCount } from '../lib/observability_dashboard/get_service_coun import { getTransactionCoordinates } from '../lib/observability_dashboard/get_transaction_coordinates'; export const observabilityDashboardHasDataRoute = createRoute(() => ({ - path: '/api/apm/observability-dashboard/hasData', + path: '/api/apm/observability_dashboard/has_data', handler: async ({ context, request }) => { const setup = await setupRequest(context, request); return await hasData({ setup }); @@ -20,19 +20,22 @@ export const observabilityDashboardHasDataRoute = createRoute(() => ({ })); export const observabilityDashboardDataRoute = createRoute(() => ({ - path: '/api/apm/observability-dashboard', + path: '/api/apm/observability_dashboard', params: { query: t.intersection([rangeRt, t.type({ bucketSize: t.string })]), }, handler: async ({ context, request }) => { const setup = await setupRequest(context, request); const { bucketSize } = context.params.query; - const serviceCount = await getServiceCount({ setup }); - const transactionCoordinates = await getTransactionCoordinates({ + const serviceCountPromise = getServiceCount({ setup }); + const transactionCoordinatesPromise = getTransactionCoordinates({ setup, bucketSize, }); - + const [serviceCount, transactionCoordinates] = await Promise.all([ + serviceCountPromise, + transactionCoordinatesPromise, + ]); return { serviceCount, transactionCoordinates }; }, })); diff --git a/x-pack/plugins/observability/public/data_handler.ts b/x-pack/plugins/observability/public/data_handler.ts index 8f80f79b2e829..10589f92fd747 100644 --- a/x-pack/plugins/observability/public/data_handler.ts +++ b/x-pack/plugins/observability/public/data_handler.ts @@ -7,7 +7,7 @@ import { ObservabilityFetchDataResponse, FetchDataResponse } from './typings/fetch_data_response'; import { ObservabilityApp } from '../typings/common'; -interface FetchDataParams { +export interface FetchDataParams { // The start timestamp in milliseconds of the queried time interval startTime: string; // The end timestamp in milliseconds of the queried time interval From 8baaa5ea3e65c83fe43dba9092f20ea9ee36c5ca Mon Sep 17 00:00:00 2001 From: cauemarcondes Date: Thu, 25 Jun 2020 11:37:36 +0200 Subject: [PATCH 08/10] adding processor event in the query, and refactoring theme --- x-pack/plugins/apm/public/plugin.ts | 7 ++++-- .../rest/observability.dashboard.test.ts | 7 ++++-- .../services/rest/observability_dashboard.ts | 9 +++----- x-pack/plugins/apm/public/utils/get_theme.ts | 13 +++++++++++ .../get_service_count.ts | 23 +++++++++++++++++-- 5 files changed, 47 insertions(+), 12 deletions(-) create mode 100644 x-pack/plugins/apm/public/utils/get_theme.ts diff --git a/x-pack/plugins/apm/public/plugin.ts b/x-pack/plugins/apm/public/plugin.ts index 78fe2ac9c50a0..0e495391c94f2 100644 --- a/x-pack/plugins/apm/public/plugin.ts +++ b/x-pack/plugins/apm/public/plugin.ts @@ -42,6 +42,7 @@ import { fetchLandingPageData, hasData, } from './services/rest/observability_dashboard'; +import { getTheme } from './utils/get_theme'; export type ApmPluginSetup = void; export type ApmPluginStart = void; @@ -78,11 +79,13 @@ export class ApmPlugin implements Plugin { pluginSetupDeps.home.featureCatalogue.register(featureCatalogueEntry); if (plugins.observability) { - const isDarkMode = core.uiSettings.get('theme:darkMode'); + const theme = getTheme({ + isDarkMode: core.uiSettings.get('theme:darkMode'), + }); plugins.observability.dashboard.register({ appName: 'apm', fetchData: async (params) => { - return fetchLandingPageData(params, { isDarkMode }); + return fetchLandingPageData(params, { theme }); }, hasData, }); diff --git a/x-pack/plugins/apm/public/services/rest/observability.dashboard.test.ts b/x-pack/plugins/apm/public/services/rest/observability.dashboard.test.ts index 5e6dc111c29c1..ce79ed8fe2c47 100644 --- a/x-pack/plugins/apm/public/services/rest/observability.dashboard.test.ts +++ b/x-pack/plugins/apm/public/services/rest/observability.dashboard.test.ts @@ -6,6 +6,9 @@ import { fetchLandingPageData, hasData } from './observability_dashboard'; import * as createCallApmApi from './createCallApmApi'; +import { getTheme } from '../../utils/get_theme'; + +const theme = getTheme({ isDarkMode: false }); describe('Observability dashboard data', () => { const callApmApiMock = jest.spyOn(createCallApmApi, 'callApmApi'); @@ -43,7 +46,7 @@ describe('Observability dashboard data', () => { endTime: '2', bucketSize: '3', }, - { isDarkMode: false } + { theme } ); expect(response).toEqual({ title: 'APM', @@ -85,7 +88,7 @@ describe('Observability dashboard data', () => { endTime: '2', bucketSize: '3', }, - { isDarkMode: false } + { theme } ); expect(response).toEqual({ title: 'APM', diff --git a/x-pack/plugins/apm/public/services/rest/observability_dashboard.ts b/x-pack/plugins/apm/public/services/rest/observability_dashboard.ts index eb374b085e15b..050b2d0365859 100644 --- a/x-pack/plugins/apm/public/services/rest/observability_dashboard.ts +++ b/x-pack/plugins/apm/public/services/rest/observability_dashboard.ts @@ -6,23 +6,20 @@ import { i18n } from '@kbn/i18n'; import { sum } from 'lodash'; -import lightTheme from '@elastic/eui/dist/eui_theme_light.json'; -import darkTheme from '@elastic/eui/dist/eui_theme_dark.json'; // eslint-disable-next-line @kbn/eslint/no-restricted-paths import { FetchDataParams } from '../../../../observability/public/data_handler'; import { ApmFetchDataResponse } from '../../../../observability/public/typings/fetch_data_response'; import { callApmApi } from './createCallApmApi'; +import { Theme } from '../../utils/get_theme'; interface Options { - isDarkMode: boolean; + theme: Theme; } export const fetchLandingPageData = async ( { startTime, endTime, bucketSize }: FetchDataParams, - { isDarkMode }: Options + { theme }: Options ): Promise => { - const theme = isDarkMode ? darkTheme : lightTheme; - const data = await callApmApi({ pathname: '/api/apm/observability_dashboard', params: { query: { start: startTime, end: endTime, bucketSize } }, diff --git a/x-pack/plugins/apm/public/utils/get_theme.ts b/x-pack/plugins/apm/public/utils/get_theme.ts new file mode 100644 index 0000000000000..e5020202b7721 --- /dev/null +++ b/x-pack/plugins/apm/public/utils/get_theme.ts @@ -0,0 +1,13 @@ +/* + * 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 lightTheme from '@elastic/eui/dist/eui_theme_light.json'; +import darkTheme from '@elastic/eui/dist/eui_theme_dark.json'; + +export type Theme = ReturnType; + +export function getTheme({ isDarkMode }: { isDarkMode: boolean }) { + return isDarkMode ? darkTheme : lightTheme; +} diff --git a/x-pack/plugins/apm/server/lib/observability_dashboard/get_service_count.ts b/x-pack/plugins/apm/server/lib/observability_dashboard/get_service_count.ts index b33802b74789e..4c4d058c7139d 100644 --- a/x-pack/plugins/apm/server/lib/observability_dashboard/get_service_count.ts +++ b/x-pack/plugins/apm/server/lib/observability_dashboard/get_service_count.ts @@ -4,8 +4,12 @@ * you may not use this file except in compliance with the Elastic License. */ +import { ProcessorEvent } from '../../../common/processor_event'; import { rangeFilter } from '../../../common/utils/range_filter'; -import { SERVICE_NAME } from '../../../common/elasticsearch_fieldnames'; +import { + SERVICE_NAME, + PROCESSOR_EVENT, +} from '../../../common/elasticsearch_fieldnames'; import { Setup, SetupTimeRange } from '../helpers/setup_request'; export async function getServiceCount({ @@ -23,7 +27,22 @@ export async function getServiceCount({ ], body: { size: 0, - query: { bool: { filter: [{ range: rangeFilter(start, end) }] } }, + query: { + bool: { + filter: [ + { range: rangeFilter(start, end) }, + { + terms: { + [PROCESSOR_EVENT]: [ + ProcessorEvent.error, + ProcessorEvent.transaction, + ProcessorEvent.metric, + ], + }, + }, + ], + }, + }, aggs: { serviceCount: { cardinality: { field: SERVICE_NAME } } }, }, }; From ceff040cdcdf1ce5e248547294a6aec790b7e953 Mon Sep 17 00:00:00 2001 From: cauemarcondes Date: Thu, 25 Jun 2020 17:05:01 +0200 Subject: [PATCH 09/10] fixing ts issues --- .../plugins/apm/public/services/rest/observability_dashboard.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/x-pack/plugins/apm/public/services/rest/observability_dashboard.ts b/x-pack/plugins/apm/public/services/rest/observability_dashboard.ts index 050b2d0365859..2221904932b63 100644 --- a/x-pack/plugins/apm/public/services/rest/observability_dashboard.ts +++ b/x-pack/plugins/apm/public/services/rest/observability_dashboard.ts @@ -34,6 +34,7 @@ export const fetchLandingPageData = async ( appLink: '/app/apm', stats: { services: { + type: 'number', label: i18n.translate( 'xpack.apm.observabilityDashboard.stats.services', { defaultMessage: 'Services' } @@ -41,6 +42,7 @@ export const fetchLandingPageData = async ( value: serviceCount, }, transactions: { + type: 'number', label: i18n.translate( 'xpack.apm.observabilityDashboard.stats.transactions', { defaultMessage: 'Transactions' } From 1d8d58f0fe57e2afc36aa984078d1c9fcc80c45c Mon Sep 17 00:00:00 2001 From: cauemarcondes Date: Fri, 26 Jun 2020 07:48:51 +0200 Subject: [PATCH 10/10] fixing unit tests --- .../apm/public/services/rest/observability.dashboard.test.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/x-pack/plugins/apm/public/services/rest/observability.dashboard.test.ts b/x-pack/plugins/apm/public/services/rest/observability.dashboard.test.ts index ce79ed8fe2c47..1ee8d79ee99a5 100644 --- a/x-pack/plugins/apm/public/services/rest/observability.dashboard.test.ts +++ b/x-pack/plugins/apm/public/services/rest/observability.dashboard.test.ts @@ -53,10 +53,12 @@ describe('Observability dashboard data', () => { appLink: '/app/apm', stats: { services: { + type: 'number', label: 'Services', value: 10, }, transactions: { + type: 'number', label: 'Transactions', value: 6, color: '#6092c0', @@ -95,10 +97,12 @@ describe('Observability dashboard data', () => { appLink: '/app/apm', stats: { services: { + type: 'number', label: 'Services', value: 0, }, transactions: { + type: 'number', label: 'Transactions', value: 0, color: '#6092c0',