diff --git a/x-pack/solutions/observability/plugins/slo/common/parse_kuery.ts b/x-pack/solutions/observability/plugins/slo/common/parse_kuery.ts index de4dd3c238bad..8b4b04e9d7192 100644 --- a/x-pack/solutions/observability/plugins/slo/common/parse_kuery.ts +++ b/x-pack/solutions/observability/plugins/slo/common/parse_kuery.ts @@ -6,23 +6,33 @@ */ import type { QueryDslQueryContainer } from '@elastic/elasticsearch/lib/api/types'; -import { kqlQuerySchema, QuerySchema } from '@kbn/slo-schema'; -import { buildEsQuery, fromKueryExpression, toElasticsearchQuery } from '@kbn/es-query'; +import type { QuerySchema } from '@kbn/slo-schema'; +import { kqlQuerySchema } from '@kbn/slo-schema'; +import { buildEsQuery } from '@kbn/es-query'; +import type { DataViewBase } from '@kbn/es-query'; +import { isEmpty } from 'lodash'; -export function getElasticsearchQueryOrThrow(kuery: QuerySchema = ''): QueryDslQueryContainer { +export function getElasticsearchQueryOrThrow( + kuery: QuerySchema = '', + dataView?: DataViewBase +): QueryDslQueryContainer { try { - if (kqlQuerySchema.is(kuery)) { - return toElasticsearchQuery(fromKueryExpression(kuery)); - } else { - return buildEsQuery( - undefined, - { - query: kuery?.kqlQuery, - language: 'kuery', - }, - kuery?.filters - ); + if (isEmpty(kuery)) { + return { match_all: {} }; } + const kqlQuery = kqlQuerySchema.is(kuery) ? kuery : kuery.kqlQuery; + const filters = kqlQuerySchema.is(kuery) ? [] : kuery.filters; + return buildEsQuery( + dataView, + { + query: kqlQuery, + language: 'kuery', + }, + filters, + { + allowLeadingWildcards: true, + } + ); } catch (err) { return [] as QueryDslQueryContainer; } diff --git a/x-pack/solutions/observability/plugins/slo/public/pages/slo_edit/components/common/data_preview_chart.tsx b/x-pack/solutions/observability/plugins/slo/public/pages/slo_edit/components/common/data_preview_chart.tsx index 2afc80d004545..d8f049fa2b2b8 100644 --- a/x-pack/solutions/observability/plugins/slo/public/pages/slo_edit/components/common/data_preview_chart.tsx +++ b/x-pack/solutions/observability/plugins/slo/public/pages/slo_edit/components/common/data_preview_chart.tsx @@ -299,7 +299,7 @@ export function DataPreviewChart({ yAccessors={['value']} data={(previewData?.results ?? []).map((datum) => ({ date: new Date(datum.date).getTime(), - value: datum.sliValue && datum.sliValue >= 0 ? datum.sliValue : null, + value: datum.sliValue != null && datum.sliValue >= 0 ? datum.sliValue : null, events: datum.events, }))} /> @@ -315,7 +315,7 @@ export function DataPreviewChart({ yAccessors={['value']} data={data.map((datum) => ({ date: new Date(datum.date).getTime(), - value: datum.sliValue && datum.sliValue >= 0 ? datum.sliValue : null, + value: datum.sliValue != null && datum.sliValue >= 0 ? datum.sliValue : null, events: datum.events, }))} /> diff --git a/x-pack/solutions/observability/plugins/slo/public/pages/slo_edit/components/common/use_table_docs.tsx b/x-pack/solutions/observability/plugins/slo/public/pages/slo_edit/components/common/use_table_docs.tsx index dc58571f58685..88bc74d9e900e 100644 --- a/x-pack/solutions/observability/plugins/slo/public/pages/slo_edit/components/common/use_table_docs.tsx +++ b/x-pack/solutions/observability/plugins/slo/public/pages/slo_edit/components/common/use_table_docs.tsx @@ -28,7 +28,7 @@ export const useTableDocs = ({ const errorMessages = getFieldState(name).error?.message; const filter = watch(name) as QuerySchema; - const esFilter = getElasticsearchQueryOrThrow(filter); + const esFilter = getElasticsearchQueryOrThrow(filter, dataView); const { data, loading, error } = useEsSearch( { diff --git a/x-pack/solutions/observability/plugins/slo/server/services/aggregations/get_custom_metric_indicator_aggregation.ts b/x-pack/solutions/observability/plugins/slo/server/services/aggregations/get_custom_metric_indicator_aggregation.ts index 395efb788676a..9cfe694644816 100644 --- a/x-pack/solutions/observability/plugins/slo/server/services/aggregations/get_custom_metric_indicator_aggregation.ts +++ b/x-pack/solutions/observability/plugins/slo/server/services/aggregations/get_custom_metric_indicator_aggregation.ts @@ -5,7 +5,9 @@ * 2.0. */ -import { metricCustomDocCountMetric, MetricCustomIndicator } from '@kbn/slo-schema'; +import type { MetricCustomIndicator } from '@kbn/slo-schema'; +import { metricCustomDocCountMetric } from '@kbn/slo-schema'; +import type { DataView } from '@kbn/data-views-plugin/common'; import { getElasticsearchQueryOrThrow } from '../transform_generators'; type MetricCustomMetricDef = @@ -13,12 +15,12 @@ type MetricCustomMetricDef = | MetricCustomIndicator['params']['total']; export class GetCustomMetricIndicatorAggregation { - constructor(private indicator: MetricCustomIndicator) {} + constructor(private indicator: MetricCustomIndicator, private dataView?: DataView) {} private buildMetricAggregations(type: 'good' | 'total', metricDef: MetricCustomMetricDef) { return metricDef.metrics.reduce((acc, metric) => { const filter = metric.filter - ? getElasticsearchQueryOrThrow(metric.filter) + ? getElasticsearchQueryOrThrow(metric.filter, this.dataView) : { match_all: {} }; if (metricCustomDocCountMetric.is(metric)) { diff --git a/x-pack/solutions/observability/plugins/slo/server/services/aggregations/get_histogram_indicator_aggregation.ts b/x-pack/solutions/observability/plugins/slo/server/services/aggregations/get_histogram_indicator_aggregation.ts index 3415e797d21b6..b3fd969155df3 100644 --- a/x-pack/solutions/observability/plugins/slo/server/services/aggregations/get_histogram_indicator_aggregation.ts +++ b/x-pack/solutions/observability/plugins/slo/server/services/aggregations/get_histogram_indicator_aggregation.ts @@ -4,9 +4,9 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ - -import { HistogramIndicator } from '@kbn/slo-schema'; -import { AggregationsAggregationContainer } from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; +import type { HistogramIndicator } from '@kbn/slo-schema'; +import type { AggregationsAggregationContainer } from '@elastic/elasticsearch/lib/api/types'; +import type { DataView } from '@kbn/data-views-plugin/common'; import { getElasticsearchQueryOrThrow } from '../transform_generators/common'; type HistogramIndicatorDef = @@ -14,11 +14,11 @@ type HistogramIndicatorDef = | HistogramIndicator['params']['total']; export class GetHistogramIndicatorAggregation { - constructor(private indicator: HistogramIndicator) {} + constructor(private indicator: HistogramIndicator, private dataView?: DataView) {} private buildAggregation(indicator: HistogramIndicatorDef): AggregationsAggregationContainer { const filter = indicator.filter - ? getElasticsearchQueryOrThrow(indicator.filter) + ? getElasticsearchQueryOrThrow(indicator.filter, this.dataView) : { match_all: {} }; if (indicator.aggregation === 'value_count') { return { diff --git a/x-pack/solutions/observability/plugins/slo/server/services/aggregations/get_timeslice_metric_indicator_aggregation.ts b/x-pack/solutions/observability/plugins/slo/server/services/aggregations/get_timeslice_metric_indicator_aggregation.ts index 453329c3c966f..23f6e38be0521 100644 --- a/x-pack/solutions/observability/plugins/slo/server/services/aggregations/get_timeslice_metric_indicator_aggregation.ts +++ b/x-pack/solutions/observability/plugins/slo/server/services/aggregations/get_timeslice_metric_indicator_aggregation.ts @@ -8,6 +8,7 @@ import { TimesliceMetricIndicator, timesliceMetricMetricDef } from '@kbn/slo-schema'; import * as t from 'io-ts'; import { assertNever } from '@kbn/std'; +import type { DataView } from '@kbn/data-views-plugin/common'; import { getElasticsearchQueryOrThrow } from '../transform_generators'; @@ -15,7 +16,7 @@ type TimesliceMetricDef = TimesliceMetricIndicator['params']['metric']; type TimesliceMetricMetricDef = t.TypeOf; export class GetTimesliceMetricIndicatorAggregation { - constructor(private indicator: TimesliceMetricIndicator) {} + constructor(private indicator: TimesliceMetricIndicator, private dataView?: DataView) {} private buildAggregation(metric: TimesliceMetricMetricDef) { const { aggregation } = metric; @@ -85,7 +86,7 @@ export class GetTimesliceMetricIndicatorAggregation { private buildMetricAggregations(metricDef: TimesliceMetricDef) { return metricDef.metrics.reduce((acc, metric) => { const filter = metric.filter - ? getElasticsearchQueryOrThrow(metric.filter) + ? getElasticsearchQueryOrThrow(metric.filter, this.dataView) : { match_all: {} }; const aggs = { metric: this.buildAggregation(metric) }; return { diff --git a/x-pack/solutions/observability/plugins/slo/server/services/get_preview_data.test.ts b/x-pack/solutions/observability/plugins/slo/server/services/get_preview_data.test.ts index 3572fc3f9d732..bb6d876497788 100644 --- a/x-pack/solutions/observability/plugins/slo/server/services/get_preview_data.test.ts +++ b/x-pack/solutions/observability/plugins/slo/server/services/get_preview_data.test.ts @@ -10,14 +10,56 @@ import { dataViewsService } from '@kbn/data-views-plugin/server/mocks'; import { GetPreviewDataParams } from '@kbn/slo-schema'; import { GetPreviewData } from './get_preview_data'; import { oneMinute } from './fixtures/duration'; +import { createStubDataView } from '@kbn/data-views-plugin/common/data_views/data_view.stub'; +import type { DataViewsService } from '@kbn/data-views-plugin/common'; describe('GetPreviewData', () => { let esClientMock: ElasticsearchClientMock; let service: GetPreviewData; + let mockDataViewsService: jest.Mocked; beforeEach(() => { esClientMock = elasticsearchServiceMock.createElasticsearchClient(); - service = new GetPreviewData(esClientMock, 'default', dataViewsService); + mockDataViewsService = { + ...dataViewsService, + get: jest.fn().mockImplementation((dataViewId: string) => { + if (dataViewId === 'e7744dbe-a7a4-457b-83aa-539e9c88764c') { + return Promise.resolve( + createStubDataView({ + spec: { + id: dataViewId, + title: 'kbn-data-forge-fake_stack.admin-console-*', + timeFieldName: '@timestamp', + fields: { + 'http.response.status_code': { + name: 'http.response.status_code', + type: 'number', + esTypes: ['long'], + searchable: true, + aggregatable: true, + readFromDocValues: true, + }, + }, + }, + }) + ); + } + if (dataViewId === '593f894a-3378-42cc-bafc-61b4877b64b0') { + return Promise.resolve( + createStubDataView({ + spec: { + id: dataViewId, + title: 'kbn-data-forge-fake_stack.message_processor-*', + timeFieldName: '@timestamp', + fields: {}, + }, + }) + ); + } + return Promise.reject(new Error('Data view not found')); + }), + } as any; + service = new GetPreviewData(esClientMock, 'default', mockDataViewsService); }); describe("for 'Custom KQL' indicator type", () => { diff --git a/x-pack/solutions/observability/plugins/slo/server/services/get_preview_data.ts b/x-pack/solutions/observability/plugins/slo/server/services/get_preview_data.ts index c402391da472b..afff4bb39ad0e 100644 --- a/x-pack/solutions/observability/plugins/slo/server/services/get_preview_data.ts +++ b/x-pack/solutions/observability/plugins/slo/server/services/get_preview_data.ts @@ -55,15 +55,20 @@ export class GetPreviewData { private dataViewService: DataViewsService ) {} - private async buildRuntimeMappings({ dataViewId }: { dataViewId?: string }) { - let dataView: DataView | undefined; - if (dataViewId) { - try { - dataView = await this.dataViewService.get(dataViewId); - } catch (e) { - // If the data view is not found, we will continue without it - } + private async getDataView(dataViewId?: string): Promise { + if (!dataViewId) { + return undefined; + } + try { + return await this.dataViewService.get(dataViewId); + } catch (e) { + // If the data view is not found, we will continue without it + return undefined; } + } + + private async buildRuntimeMappings({ dataViewId }: { dataViewId?: string }) { + const dataView = await this.getDataView(dataViewId); return dataView?.getRuntimeMappings?.() ?? {}; } @@ -101,6 +106,7 @@ export class GetPreviewData { indicator: APMTransactionDurationIndicator, options: Options ): Promise { + const dataView = await this.getDataView(indicator.params.dataViewId); const filter: estypes.QueryDslQueryContainer[] = []; const groupingFilters = this.getGroupingFilters(options); if (groupingFilters) { @@ -123,7 +129,7 @@ export class GetPreviewData { match: { 'transaction.type': indicator.params.transactionType }, }); if (!!indicator.params.filter) - filter.push(getElasticsearchQueryOrThrow(indicator.params.filter)); + filter.push(getElasticsearchQueryOrThrow(indicator.params.filter, dataView)); const truncatedThreshold = Math.trunc(indicator.params.threshold * 1000); @@ -227,6 +233,7 @@ export class GetPreviewData { indicator: APMTransactionErrorRateIndicator, options: Options ): Promise { + const dataView = await this.getDataView(indicator.params.dataViewId); const filter: estypes.QueryDslQueryContainer[] = []; const groupingFilters = this.getGroupingFilters(options); if (groupingFilters) { @@ -249,7 +256,7 @@ export class GetPreviewData { match: { 'transaction.type': indicator.params.transactionType }, }); if (!!indicator.params.filter) - filter.push(getElasticsearchQueryOrThrow(indicator.params.filter)); + filter.push(getElasticsearchQueryOrThrow(indicator.params.filter, dataView)); const index = options.remoteName ? `${options.remoteName}:${indicator.params.index}` @@ -345,8 +352,12 @@ export class GetPreviewData { indicator: HistogramIndicator, options: Options ): Promise { - const getHistogramIndicatorAggregations = new GetHistogramIndicatorAggregation(indicator); - const filterQuery = getElasticsearchQueryOrThrow(indicator.params.filter); + const dataView = await this.getDataView(indicator.params.dataViewId); + const getHistogramIndicatorAggregations = new GetHistogramIndicatorAggregation( + indicator, + dataView + ); + const filterQuery = getElasticsearchQueryOrThrow(indicator.params.filter, dataView); const timestampField = indicator.params.timestampField; const filter: estypes.QueryDslQueryContainer[] = [ @@ -447,9 +458,14 @@ export class GetPreviewData { indicator: MetricCustomIndicator, options: Options ): Promise { + const dataView = await this.getDataView(indicator.params.dataViewId); const timestampField = indicator.params.timestampField; - const filterQuery = getElasticsearchQueryOrThrow(indicator.params.filter); - const getCustomMetricIndicatorAggregation = new GetCustomMetricIndicatorAggregation(indicator); + const filterQuery = getElasticsearchQueryOrThrow(indicator.params.filter, dataView); + + const getCustomMetricIndicatorAggregation = new GetCustomMetricIndicatorAggregation( + indicator, + dataView + ); const filter: estypes.QueryDslQueryContainer[] = [ { range: { [timestampField]: { gte: options.range.start, lte: options.range.end } } }, @@ -549,10 +565,12 @@ export class GetPreviewData { indicator: TimesliceMetricIndicator, options: Options ): Promise { + const dataView = await this.getDataView(indicator.params.dataViewId); const timestampField = indicator.params.timestampField; - const filterQuery = getElasticsearchQueryOrThrow(indicator.params.filter); + const filterQuery = getElasticsearchQueryOrThrow(indicator.params.filter, dataView); const getCustomMetricIndicatorAggregation = new GetTimesliceMetricIndicatorAggregation( - indicator + indicator, + dataView ); const filter: estypes.QueryDslQueryContainer[] = [ @@ -629,9 +647,11 @@ export class GetPreviewData { indicator: KQLCustomIndicator, options: Options ): Promise { - const filterQuery = getElasticsearchQueryOrThrow(indicator.params.filter); - const goodQuery = getElasticsearchQueryOrThrow(indicator.params.good); - const totalQuery = getElasticsearchQueryOrThrow(indicator.params.total); + const dataView = await this.getDataView(indicator.params.dataViewId); + const filterQuery = getElasticsearchQueryOrThrow(indicator.params.filter, dataView); + const goodQuery = getElasticsearchQueryOrThrow(indicator.params.good, dataView); + const totalQuery = getElasticsearchQueryOrThrow(indicator.params.total, dataView); + const timestampField = indicator.params.timestampField; const filter: estypes.QueryDslQueryContainer[] = [ { range: { [timestampField]: { gte: options.range.start, lte: options.range.end } } }, @@ -682,9 +702,10 @@ export class GetPreviewData { response.aggregations?.perInterval.buckets.map((bucket) => { const good = bucket.good?.doc_count ?? 0; const total = bucket.total?.doc_count ?? 0; + const sliValue = computeSLIForPreview(good, total); return { date: bucket.key_as_string, - sliValue: computeSLIForPreview(good, total), + sliValue, events: { good, bad: total - good, diff --git a/x-pack/solutions/observability/plugins/slo/server/services/transform_generators/histogram.ts b/x-pack/solutions/observability/plugins/slo/server/services/transform_generators/histogram.ts index 327cc4be79651..7bc6d81c7072f 100644 --- a/x-pack/solutions/observability/plugins/slo/server/services/transform_generators/histogram.ts +++ b/x-pack/solutions/observability/plugins/slo/server/services/transform_generators/histogram.ts @@ -40,7 +40,7 @@ export class HistogramTransformGenerator extends TransformGenerator { await this.buildSource(slo, slo.indicator), this.buildDestination(slo), this.buildCommonGroupBy(slo, slo.indicator.params.timestampField), - this.buildAggregations(slo, slo.indicator), + await this.buildAggregations(slo, slo.indicator), this.buildSettings(slo, slo.indicator.params.timestampField), slo ); @@ -74,8 +74,12 @@ export class HistogramTransformGenerator extends TransformGenerator { }; } - private buildAggregations(slo: SLODefinition, indicator: HistogramIndicator) { - const getHistogramIndicatorAggregations = new GetHistogramIndicatorAggregation(indicator); + private async buildAggregations(slo: SLODefinition, indicator: HistogramIndicator) { + const dataView = await this.getIndicatorDataView(indicator.params.dataViewId); + const getHistogramIndicatorAggregations = new GetHistogramIndicatorAggregation( + indicator, + dataView + ); return { ...getHistogramIndicatorAggregations.execute({ diff --git a/x-pack/solutions/observability/plugins/slo/server/services/transform_generators/kql_custom.ts b/x-pack/solutions/observability/plugins/slo/server/services/transform_generators/kql_custom.ts index 205a439127cef..ca16920fcc1d5 100644 --- a/x-pack/solutions/observability/plugins/slo/server/services/transform_generators/kql_custom.ts +++ b/x-pack/solutions/observability/plugins/slo/server/services/transform_generators/kql_custom.ts @@ -54,7 +54,7 @@ export class KQLCustomTransformGenerator extends TransformGenerator { bool: { filter: [ getFilterRange(slo, indicator.params.timestampField, this.isServerless), - getElasticsearchQueryOrThrow(indicator.params.filter), + getElasticsearchQueryOrThrow(indicator.params.filter, dataView), ], }, }, diff --git a/x-pack/solutions/observability/plugins/slo/server/services/transform_generators/metric_custom.ts b/x-pack/solutions/observability/plugins/slo/server/services/transform_generators/metric_custom.ts index 9896a17b448cd..f8ed18614bdf8 100644 --- a/x-pack/solutions/observability/plugins/slo/server/services/transform_generators/metric_custom.ts +++ b/x-pack/solutions/observability/plugins/slo/server/services/transform_generators/metric_custom.ts @@ -39,7 +39,7 @@ export class MetricCustomTransformGenerator extends TransformGenerator { await this.buildSource(slo, slo.indicator), this.buildDestination(slo), this.buildCommonGroupBy(slo, slo.indicator.params.timestampField), - this.buildAggregations(slo, slo.indicator), + await this.buildAggregations(slo, slo.indicator), this.buildSettings(slo, slo.indicator.params.timestampField), slo ); @@ -72,7 +72,7 @@ export class MetricCustomTransformGenerator extends TransformGenerator { }; } - private buildAggregations(slo: SLODefinition, indicator: MetricCustomIndicator) { + private async buildAggregations(slo: SLODefinition, indicator: MetricCustomIndicator) { if (indicator.params.good.equation.match(INVALID_EQUATION_REGEX)) { throw new Error(`Invalid equation: ${indicator.params.good.equation}`); } @@ -81,7 +81,11 @@ export class MetricCustomTransformGenerator extends TransformGenerator { throw new Error(`Invalid equation: ${indicator.params.total.equation}`); } - const getCustomMetricIndicatorAggregation = new GetCustomMetricIndicatorAggregation(indicator); + const dataView = await this.getIndicatorDataView(indicator.params.dataViewId); + const getCustomMetricIndicatorAggregation = new GetCustomMetricIndicatorAggregation( + indicator, + dataView + ); return { ...getCustomMetricIndicatorAggregation.execute({ type: 'good', diff --git a/x-pack/solutions/observability/plugins/slo/server/services/transform_generators/synthetics_availability.ts b/x-pack/solutions/observability/plugins/slo/server/services/transform_generators/synthetics_availability.ts index e05fe886421b0..088316253d6c5 100644 --- a/x-pack/solutions/observability/plugins/slo/server/services/transform_generators/synthetics_availability.ts +++ b/x-pack/solutions/observability/plugins/slo/server/services/transform_generators/synthetics_availability.ts @@ -108,6 +108,7 @@ export class SyntheticsAvailabilityTransformGenerator extends TransformGenerator } private async buildSource(slo: SLODefinition, indicator: SyntheticsAvailabilityIndicator) { + const dataView = await this.getIndicatorDataView(indicator.params.dataViewId); const queryFilter: estypes.QueryDslQueryContainer[] = [ { term: { 'summary.final_attempt': true } }, { term: { 'meta.space_id': this.spaceId } }, @@ -144,11 +145,9 @@ export class SyntheticsAvailabilityTransformGenerator extends TransformGenerator } if (!!indicator.params.filter) { - queryFilter.push(getElasticsearchQueryOrThrow(indicator.params.filter)); + queryFilter.push(getElasticsearchQueryOrThrow(indicator.params.filter, dataView)); } - const dataView = await this.getIndicatorDataView(indicator.params.dataViewId); - return { index: SYNTHETICS_INDEX_PATTERN, runtime_mappings: this.buildCommonRuntimeMappings(dataView), diff --git a/x-pack/solutions/observability/plugins/slo/server/services/transform_generators/timeslice_metric.ts b/x-pack/solutions/observability/plugins/slo/server/services/transform_generators/timeslice_metric.ts index f3d8a482a4c74..02307c812a150 100644 --- a/x-pack/solutions/observability/plugins/slo/server/services/transform_generators/timeslice_metric.ts +++ b/x-pack/solutions/observability/plugins/slo/server/services/transform_generators/timeslice_metric.ts @@ -43,7 +43,7 @@ export class TimesliceMetricTransformGenerator extends TransformGenerator { await this.buildSource(slo, slo.indicator), this.buildDestination(slo), this.buildCommonGroupBy(slo, slo.indicator.params.timestampField), - this.buildAggregations(slo, slo.indicator), + await this.buildAggregations(slo, slo.indicator), this.buildSettings(slo, slo.indicator.params.timestampField), slo ); @@ -77,7 +77,7 @@ export class TimesliceMetricTransformGenerator extends TransformGenerator { }; } - private buildAggregations(slo: SLODefinition, indicator: TimesliceMetricIndicator) { + private async buildAggregations(slo: SLODefinition, indicator: TimesliceMetricIndicator) { if (indicator.params.metric.equation.match(INVALID_EQUATION_REGEX)) { throw new Error(`Invalid equation: ${indicator.params.metric.equation}`); } @@ -86,7 +86,8 @@ export class TimesliceMetricTransformGenerator extends TransformGenerator { throw new Error('The sli.metric.timeslice indicator MUST have a timeslice budgeting method.'); } - const getIndicatorAggregation = new GetTimesliceMetricIndicatorAggregation(indicator); + const dataView = await this.getIndicatorDataView(indicator.params.dataViewId); + const getIndicatorAggregation = new GetTimesliceMetricIndicatorAggregation(indicator, dataView); const comparator = timesliceMetricComparatorMapping[indicator.params.metric.comparator]; return { ...getIndicatorAggregation.execute('_metric'),