From 04c05f807727f401a187b13e473597e273d96397 Mon Sep 17 00:00:00 2001 From: Shahzad Date: Thu, 8 Jul 2021 18:19:09 +0200 Subject: [PATCH] [Exploratory view] Fix core web vital breakdown (#104630) Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- .../configurations/lens_attributes.ts | 20 ++- .../rum/core_web_vitals_config.test.ts | 39 +++++ .../rum/core_web_vitals_config.ts | 7 +- .../test_data/sample_attribute_cwv.ts | 149 ++++++++++++++++++ .../columns/report_types_col.test.tsx | 1 - .../columns/report_types_col.tsx | 1 + .../utils/observability_index_patterns.ts | 13 +- 7 files changed, 216 insertions(+), 14 deletions(-) create mode 100644 x-pack/plugins/observability/public/components/shared/exploratory_view/configurations/rum/core_web_vitals_config.test.ts create mode 100644 x-pack/plugins/observability/public/components/shared/exploratory_view/configurations/test_data/sample_attribute_cwv.ts diff --git a/x-pack/plugins/observability/public/components/shared/exploratory_view/configurations/lens_attributes.ts b/x-pack/plugins/observability/public/components/shared/exploratory_view/configurations/lens_attributes.ts index 058d3f4751f11..dfb17ee470d35 100644 --- a/x-pack/plugins/observability/public/components/shared/exploratory_view/configurations/lens_attributes.ts +++ b/x-pack/plugins/observability/public/components/shared/exploratory_view/configurations/lens_attributes.ts @@ -346,7 +346,7 @@ export class LensAttributes { if (fieldName === RECORDS_FIELD || columnType === FILTER_RECORDS) { return this.getRecordsColumn( - columnLabel || label, + label || columnLabel, colIndex !== undefined ? columnFilters?.[colIndex] : undefined, timeScale ); @@ -435,6 +435,8 @@ export class LensAttributes { if (yAxisColumns.length === 1) { return lensColumns; } + + // starting from 1 index since 0 column is used as main column for (let i = 1; i < yAxisColumns.length; i++) { const { sourceField, operationType, label } = yAxisColumns[i]; @@ -557,16 +559,21 @@ export class LensAttributes { const layerConfigs = this.layerConfigs; layerConfigs.forEach((layerConfig, index) => { - const { breakdown } = layerConfig; + const { breakdown, seriesConfig } = layerConfig; const layerId = `layer${index}`; const columnFilter = this.getLayerFilters(layerConfig, layerConfigs.length); const timeShift = this.getTimeShift(this.layerConfigs[0], layerConfig, index); const mainYAxis = this.getMainYAxis(layerConfig, layerId, columnFilter); + + const { sourceField } = seriesConfig.xAxisColumn; + layers[layerId] = { columnOrder: [ `x-axis-column-${layerId}`, - ...(breakdown ? [`breakdown-column-${layerId}`] : []), + ...(breakdown && sourceField !== USE_BREAK_DOWN_COLUMN + ? [`breakdown-column-${layerId}`] + : []), `y-axis-column-${layerId}`, ...Object.keys(this.getChildYAxises(layerConfig, layerId, columnFilter)), ], @@ -578,7 +585,7 @@ export class LensAttributes { filter: { query: columnFilter, language: 'kuery' }, ...(timeShift ? { timeShift } : {}), }, - ...(breakdown && breakdown !== USE_BREAK_DOWN_COLUMN + ...(breakdown && sourceField !== USE_BREAK_DOWN_COLUMN ? // do nothing since this will be used a x axis source { [`breakdown-column-${layerId}`]: this.getBreakdownColumn({ @@ -620,7 +627,10 @@ export class LensAttributes { { forAccessor: `y-axis-column-layer${index}` }, ], xAccessor: `x-axis-column-layer${index}`, - ...(layerConfig.breakdown ? { splitAccessor: `breakdown-column-layer${index}` } : {}), + ...(layerConfig.breakdown && + layerConfig.seriesConfig.xAxisColumn.sourceField !== USE_BREAK_DOWN_COLUMN + ? { splitAccessor: `breakdown-column-layer${index}` } + : {}), })), ...(this.layerConfigs[0].seriesConfig.yTitle ? { yTitle: this.layerConfigs[0].seriesConfig.yTitle } diff --git a/x-pack/plugins/observability/public/components/shared/exploratory_view/configurations/rum/core_web_vitals_config.test.ts b/x-pack/plugins/observability/public/components/shared/exploratory_view/configurations/rum/core_web_vitals_config.test.ts new file mode 100644 index 0000000000000..07bb13f957e45 --- /dev/null +++ b/x-pack/plugins/observability/public/components/shared/exploratory_view/configurations/rum/core_web_vitals_config.test.ts @@ -0,0 +1,39 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { mockAppIndexPattern, mockIndexPattern } from '../../rtl_helpers'; +import { getDefaultConfigs } from '../default_configs'; +import { LayerConfig, LensAttributes } from '../lens_attributes'; +import { sampleAttributeCoreWebVital } from '../test_data/sample_attribute_cwv'; +import { SERVICE_NAME, USER_AGENT_OS } from '../constants/elasticsearch_fieldnames'; + +describe('Core web vital config test', function () { + mockAppIndexPattern(); + + const seriesConfig = getDefaultConfigs({ + reportType: 'core-web-vitals', + dataType: 'ux', + indexPattern: mockIndexPattern, + }); + + let lnsAttr: LensAttributes; + + const layerConfig: LayerConfig = { + seriesConfig, + indexPattern: mockIndexPattern, + reportDefinitions: { [SERVICE_NAME]: ['elastic-co'] }, + time: { from: 'now-15m', to: 'now' }, + breakdown: USER_AGENT_OS, + }; + + beforeEach(() => { + lnsAttr = new LensAttributes([layerConfig]); + }); + it('should return expected json', function () { + expect(lnsAttr.getJSON()).toEqual(sampleAttributeCoreWebVital); + }); +}); diff --git a/x-pack/plugins/observability/public/components/shared/exploratory_view/configurations/rum/core_web_vitals_config.ts b/x-pack/plugins/observability/public/components/shared/exploratory_view/configurations/rum/core_web_vitals_config.ts index 1d04a9b389503..62455df248085 100644 --- a/x-pack/plugins/observability/public/components/shared/exploratory_view/configurations/rum/core_web_vitals_config.ts +++ b/x-pack/plugins/observability/public/components/shared/exploratory_view/configurations/rum/core_web_vitals_config.ts @@ -31,6 +31,7 @@ import { URL_FULL, SERVICE_ENVIRONMENT, } from '../constants/elasticsearch_fieldnames'; +import { CLS_LABEL, FID_LABEL, LCP_LABEL } from '../constants/labels'; export function getCoreWebVitalsConfig({ indexPattern }: ConfigProps): SeriesConfig { const statusPallete = euiPaletteForStatus(3); @@ -91,7 +92,7 @@ export function getCoreWebVitalsConfig({ indexPattern }: ConfigProps): SeriesCon metricOptions: [ { id: LCP_FIELD, - label: 'Largest contentful paint', + label: LCP_LABEL, columnType: FILTER_RECORDS, columnFilters: [ { @@ -109,7 +110,7 @@ export function getCoreWebVitalsConfig({ indexPattern }: ConfigProps): SeriesCon ], }, { - label: 'First input delay', + label: FID_LABEL, id: FID_FIELD, columnType: FILTER_RECORDS, columnFilters: [ @@ -128,7 +129,7 @@ export function getCoreWebVitalsConfig({ indexPattern }: ConfigProps): SeriesCon ], }, { - label: 'Cumulative layout shift', + label: CLS_LABEL, id: CLS_FIELD, columnType: FILTER_RECORDS, columnFilters: [ diff --git a/x-pack/plugins/observability/public/components/shared/exploratory_view/configurations/test_data/sample_attribute_cwv.ts b/x-pack/plugins/observability/public/components/shared/exploratory_view/configurations/test_data/sample_attribute_cwv.ts new file mode 100644 index 0000000000000..2087b85b81886 --- /dev/null +++ b/x-pack/plugins/observability/public/components/shared/exploratory_view/configurations/test_data/sample_attribute_cwv.ts @@ -0,0 +1,149 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ +export const sampleAttributeCoreWebVital = { + description: '', + references: [ + { + id: 'apm-*', + name: 'indexpattern-datasource-current-indexpattern', + type: 'index-pattern', + }, + { + id: 'apm-*', + name: 'indexpattern-datasource-layer-layer0', + type: 'index-pattern', + }, + ], + state: { + datasourceStates: { + indexpattern: { + layers: { + layer0: { + columnOrder: [ + 'x-axis-column-layer0', + 'y-axis-column-layer0', + 'y-axis-column-1', + 'y-axis-column-2', + ], + columns: { + 'x-axis-column-layer0': { + dataType: 'string', + isBucketed: true, + label: 'Top values of Operating system', + operationType: 'terms', + params: { + missingBucket: false, + orderBy: { + columnId: 'y-axis-column-layer0', + type: 'column', + }, + orderDirection: 'desc', + otherBucket: true, + size: 10, + }, + scale: 'ordinal', + sourceField: 'user_agent.os.name', + }, + 'y-axis-column-1': { + dataType: 'number', + filter: { + language: 'kuery', + query: + 'transaction.marks.agent.largestContentfulPaint > 2500 and transaction.marks.agent.largestContentfulPaint < 4000', + }, + isBucketed: false, + label: 'Average', + operationType: 'count', + scale: 'ratio', + sourceField: 'Records', + }, + 'y-axis-column-2': { + dataType: 'number', + filter: { + language: 'kuery', + query: 'transaction.marks.agent.largestContentfulPaint > 4000', + }, + isBucketed: false, + label: 'Poor', + operationType: 'count', + scale: 'ratio', + sourceField: 'Records', + }, + 'y-axis-column-layer0': { + dataType: 'number', + filter: { + language: 'kuery', + query: 'transaction.type: page-load and processor.event: transaction', + }, + isBucketed: false, + label: 'Good', + operationType: 'count', + scale: 'ratio', + sourceField: 'Records', + }, + }, + incompleteColumns: {}, + }, + }, + }, + }, + filters: [], + query: { + language: 'kuery', + query: '', + }, + visualization: { + axisTitlesVisibilitySettings: { + x: true, + yLeft: true, + yRight: true, + }, + curveType: 'CURVE_MONOTONE_X', + fittingFunction: 'Linear', + gridlinesVisibilitySettings: { + x: true, + yLeft: true, + yRight: true, + }, + layers: [ + { + accessors: ['y-axis-column-layer0', 'y-axis-column-1', 'y-axis-column-2'], + layerId: 'layer0', + seriesType: 'bar_horizontal_percentage_stacked', + xAccessor: 'x-axis-column-layer0', + yConfig: [ + { + color: '#209280', + forAccessor: 'y-axis-column', + }, + { + color: '#d6bf57', + forAccessor: 'y-axis-column-1', + }, + { + color: '#cc5642', + forAccessor: 'y-axis-column-2', + }, + ], + }, + ], + legend: { + isVisible: true, + position: 'right', + }, + preferredSeriesType: 'line', + tickLabelsVisibilitySettings: { + x: true, + yLeft: true, + yRight: true, + }, + valueLabels: 'hide', + }, + }, + title: 'Prefilled from exploratory view app', + visualizationType: 'lnsXY', +}; diff --git a/x-pack/plugins/observability/public/components/shared/exploratory_view/series_builder/columns/report_types_col.test.tsx b/x-pack/plugins/observability/public/components/shared/exploratory_view/series_builder/columns/report_types_col.test.tsx index 07048d47b2bc3..12ae8560453c9 100644 --- a/x-pack/plugins/observability/public/components/shared/exploratory_view/series_builder/columns/report_types_col.test.tsx +++ b/x-pack/plugins/observability/public/components/shared/exploratory_view/series_builder/columns/report_types_col.test.tsx @@ -36,7 +36,6 @@ describe('ReportTypesCol', function () { fireEvent.click(screen.getByText(/KPI over time/i)); expect(setSeries).toHaveBeenCalledWith(seriesId, { - breakdown: 'user_agent.name', dataType: 'ux', selectedMetricField: undefined, reportType: 'kpi-over-time', diff --git a/x-pack/plugins/observability/public/components/shared/exploratory_view/series_builder/columns/report_types_col.tsx b/x-pack/plugins/observability/public/components/shared/exploratory_view/series_builder/columns/report_types_col.tsx index 396f8c4f1deb3..c4eebbfaca3eb 100644 --- a/x-pack/plugins/observability/public/components/shared/exploratory_view/series_builder/columns/report_types_col.tsx +++ b/x-pack/plugins/observability/public/components/shared/exploratory_view/series_builder/columns/report_types_col.tsx @@ -78,6 +78,7 @@ export function ReportTypesCol({ seriesId, reportTypes }: Props) { ...restSeries, reportType, selectedMetricField: undefined, + breakdown: undefined, time: restSeries?.time ?? DEFAULT_TIME, }); } diff --git a/x-pack/plugins/observability/public/components/shared/exploratory_view/utils/observability_index_patterns.ts b/x-pack/plugins/observability/public/components/shared/exploratory_view/utils/observability_index_patterns.ts index 634408dd614da..964de86ddf377 100644 --- a/x-pack/plugins/observability/public/components/shared/exploratory_view/utils/observability_index_patterns.ts +++ b/x-pack/plugins/observability/public/components/shared/exploratory_view/utils/observability_index_patterns.ts @@ -100,11 +100,14 @@ export class ObservabilityIndexPatterns { if (defaultFieldFormats && defaultFieldFormats.length > 0) { let isParamsDifferent = false; defaultFieldFormats.forEach(({ field, format }) => { - const fieldFormat = indexPattern.getFormatterForField(indexPattern.getFieldByName(field)!); - const params = fieldFormat.params(); - if (!isParamsSame(params, format.params)) { - indexPattern.setFieldFormat(field, format); - isParamsDifferent = true; + const fieldByName = indexPattern.getFieldByName(field); + if (fieldByName) { + const fieldFormat = indexPattern.getFormatterForField(fieldByName); + const params = fieldFormat.params(); + if (!isParamsSame(params, format.params)) { + indexPattern.setFieldFormat(field, format); + isParamsDifferent = true; + } } }); if (isParamsDifferent) {