diff --git a/x-pack/plugins/observability_solution/slo/server/routes/slo/route.ts b/x-pack/plugins/observability_solution/slo/server/routes/slo/route.ts index 7f3b395c7adba..2081f38df552f 100644 --- a/x-pack/plugins/observability_solution/slo/server/routes/slo/route.ts +++ b/x-pack/plugins/observability_solution/slo/server/routes/slo/route.ts @@ -29,7 +29,6 @@ import { updateSLOParamsSchema, } from '@kbn/slo-schema'; import { getOverviewParamsSchema } from '@kbn/slo-schema/src/rest_specs/routes/get_overview'; -import type { IndicatorTypes } from '../../domain/models'; import { executeWithErrorHandler } from '../../errors'; import { CreateSLO, @@ -60,29 +59,10 @@ import { SloDefinitionClient } from '../../services/slo_definition_client'; import { getSloSettings, storeSloSettings } from '../../services/slo_settings'; import { DefaultSummarySearchClient } from '../../services/summary_search_client'; import { DefaultSummaryTransformGenerator } from '../../services/summary_transform_generator/summary_transform_generator'; -import { - ApmTransactionDurationTransformGenerator, - ApmTransactionErrorRateTransformGenerator, - HistogramTransformGenerator, - KQLCustomTransformGenerator, - MetricCustomTransformGenerator, - SyntheticsAvailabilityTransformGenerator, - TimesliceMetricTransformGenerator, - TransformGenerator, -} from '../../services/transform_generators'; +import { createTransformGenerators } from '../../services/transform_generators'; import { createSloServerRoute } from '../create_slo_server_route'; import { SLORoutesDependencies } from '../types'; -const transformGenerators: Record = { - 'sli.apm.transactionDuration': new ApmTransactionDurationTransformGenerator(), - 'sli.apm.transactionErrorRate': new ApmTransactionErrorRateTransformGenerator(), - 'sli.synthetics.availability': new SyntheticsAvailabilityTransformGenerator(), - 'sli.kql.custom': new KQLCustomTransformGenerator(), - 'sli.metric.custom': new MetricCustomTransformGenerator(), - 'sli.histogram.custom': new HistogramTransformGenerator(), - 'sli.metric.timeslice': new TimesliceMetricTransformGenerator(), -}; - const assertPlatinumLicense = async (plugins: SLORoutesDependencies['plugins']) => { const licensing = await plugins.licensing.start(); const hasCorrectLicense = (await licensing.getLicense()).hasAtLeast('platinum'); @@ -120,14 +100,18 @@ const createSLORoute = createSloServerRoute({ getSpaceId(plugins, request), dataViews.dataViewsServiceFactory(soClient, esClient), ]); - const transformManager = new DefaultTransformManager( - transformGenerators, - scopedClusterClient, - logger, + + const transformGenerators = createTransformGenerators( spaceId, dataViewsService, sloContext.isServerless ); + + const transformManager = new DefaultTransformManager( + transformGenerators, + scopedClusterClient, + logger + ); const summaryTransformManager = new DefaultSummaryTransformManager( new DefaultSummaryTransformGenerator(), scopedClusterClient, @@ -168,14 +152,17 @@ const inspectSLORoute = createSloServerRoute({ const soClient = core.savedObjects.client; const repository = new KibanaSavedObjectsSLORepository(soClient, logger); const dataViewsService = await dataViews.dataViewsServiceFactory(soClient, esClient); - const transformManager = new DefaultTransformManager( - transformGenerators, - scopedClusterClient, - logger, + + const transformGenerators = createTransformGenerators( spaceId, dataViewsService, sloContext.isServerless ); + const transformManager = new DefaultTransformManager( + transformGenerators, + scopedClusterClient, + logger + ); const summaryTransformManager = new DefaultSummaryTransformManager( new DefaultSummaryTransformGenerator(), scopedClusterClient, @@ -218,14 +205,17 @@ const updateSLORoute = createSloServerRoute({ const soClient = core.savedObjects.client; const dataViewsService = await dataViews.dataViewsServiceFactory(soClient, esClient); const repository = new KibanaSavedObjectsSLORepository(soClient, logger); - const transformManager = new DefaultTransformManager( - transformGenerators, - scopedClusterClient, - logger, + + const transformGenerators = createTransformGenerators( spaceId, dataViewsService, sloContext.isServerless ); + const transformManager = new DefaultTransformManager( + transformGenerators, + scopedClusterClient, + logger + ); const summaryTransformManager = new DefaultSummaryTransformManager( new DefaultSummaryTransformGenerator(), scopedClusterClient, @@ -271,14 +261,16 @@ const deleteSLORoute = createSloServerRoute({ const dataViewsService = await dataViews.dataViewsServiceFactory(soClient, esClient); + const transformGenerators = createTransformGenerators( + spaceId, + dataViewsService, + sloContext.isServerless + ); const repository = new KibanaSavedObjectsSLORepository(soClient, logger); const transformManager = new DefaultTransformManager( transformGenerators, scopedClusterClient, - logger, - spaceId, - dataViewsService, - sloContext.isServerless + logger ); const summaryTransformManager = new DefaultSummaryTransformManager( @@ -346,14 +338,18 @@ const enableSLORoute = createSloServerRoute({ const esClient = core.elasticsearch.client.asCurrentUser; const dataViewsService = await dataViews.dataViewsServiceFactory(soClient, esClient); const repository = new KibanaSavedObjectsSLORepository(soClient, logger); - const transformManager = new DefaultTransformManager( - transformGenerators, - scopedClusterClient, - logger, + + const transformGenerators = createTransformGenerators( spaceId, dataViewsService, sloContext.isServerless ); + + const transformManager = new DefaultTransformManager( + transformGenerators, + scopedClusterClient, + logger + ); const summaryTransformManager = new DefaultSummaryTransformManager( new DefaultSummaryTransformGenerator(), scopedClusterClient, @@ -388,14 +384,17 @@ const disableSLORoute = createSloServerRoute({ const esClient = core.elasticsearch.client.asCurrentUser; const dataViewsService = await dataViews.dataViewsServiceFactory(soClient, esClient); const repository = new KibanaSavedObjectsSLORepository(soClient, logger); - const transformManager = new DefaultTransformManager( - transformGenerators, - scopedClusterClient, - logger, + + const transformGenerators = createTransformGenerators( spaceId, dataViewsService, sloContext.isServerless ); + const transformManager = new DefaultTransformManager( + transformGenerators, + scopedClusterClient, + logger + ); const summaryTransformManager = new DefaultSummaryTransformManager( new DefaultSummaryTransformGenerator(), scopedClusterClient, @@ -430,14 +429,17 @@ const resetSLORoute = createSloServerRoute({ const dataViewsService = await dataViews.dataViewsServiceFactory(soClient, esClient); const repository = new KibanaSavedObjectsSLORepository(soClient, logger); - const transformManager = new DefaultTransformManager( - transformGenerators, - scopedClusterClient, - logger, + + const transformGenerators = createTransformGenerators( spaceId, dataViewsService, sloContext.isServerless ); + const transformManager = new DefaultTransformManager( + transformGenerators, + scopedClusterClient, + logger + ); const summaryTransformManager = new DefaultSummaryTransformManager( new DefaultSummaryTransformGenerator(), scopedClusterClient, diff --git a/x-pack/plugins/observability_solution/slo/server/services/transform_generators/__snapshots__/synthetics_availability.test.ts.snap b/x-pack/plugins/observability_solution/slo/server/services/transform_generators/__snapshots__/synthetics_availability.test.ts.snap index 3c71844678885..309fdc7ffffb3 100644 --- a/x-pack/plugins/observability_solution/slo/server/services/transform_generators/__snapshots__/synthetics_availability.test.ts.snap +++ b/x-pack/plugins/observability_solution/slo/server/services/transform_generators/__snapshots__/synthetics_availability.test.ts.snap @@ -1,6 +1,6 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`Synthetics Availability Transform Generator returns the expected transform params 1`] = ` +exports[`Synthetics Availability Transform Generator when serverless is disabled returns the expected transform params 1`] = ` Object { "_meta": Object { "managed": true, diff --git a/x-pack/plugins/observability_solution/slo/server/services/transform_generators/__snapshots__/transform_generator.test.ts.snap b/x-pack/plugins/observability_solution/slo/server/services/transform_generators/__snapshots__/transform_generator.test.ts.snap index 144a4fa35eda5..7d8e989c1140d 100644 --- a/x-pack/plugins/observability_solution/slo/server/services/transform_generators/__snapshots__/transform_generator.test.ts.snap +++ b/x-pack/plugins/observability_solution/slo/server/services/transform_generators/__snapshots__/transform_generator.test.ts.snap @@ -1,8 +1,6 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`Transform Generator builds common runtime mappings and group by with single group by 1`] = `Object {}`; - -exports[`Transform Generator builds common runtime mappings and group by with single group by 2`] = ` +exports[`Transform Generator buildCommonGroupBy builds common groupBy with single group by 1`] = ` Object { "@timestamp": Object { "date_histogram": Object { @@ -18,9 +16,7 @@ Object { } `; -exports[`Transform Generator builds common runtime mappings and group by with single group by 3`] = `Object {}`; - -exports[`Transform Generator builds common runtime mappings and group by with single group by 4`] = ` +exports[`Transform Generator buildCommonGroupBy builds common groupBy with single group by 2`] = ` Object { "@timestamp": Object { "date_histogram": Object { @@ -36,9 +32,7 @@ Object { } `; -exports[`Transform Generator builds common runtime mappings without multi group by 1`] = `Object {}`; - -exports[`Transform Generator builds common runtime mappings without multi group by 2`] = ` +exports[`Transform Generator buildCommonGroupBy builds common groupBy with single group by 3`] = ` Object { "@timestamp": Object { "date_histogram": Object { @@ -59,9 +53,7 @@ Object { } `; -exports[`Transform Generator builds empty runtime mappings without group by 1`] = `Object {}`; - -exports[`Transform Generator builds empty runtime mappings without group by 2`] = ` +exports[`Transform Generator buildCommonGroupBy builds empty runtime mappings without group by 1`] = ` Object { "@timestamp": Object { "date_histogram": Object { diff --git a/x-pack/plugins/observability_solution/slo/server/services/transform_generators/apm_transaction_duration.test.ts b/x-pack/plugins/observability_solution/slo/server/services/transform_generators/apm_transaction_duration.test.ts index b764b83ea9349..c928c121e3e77 100644 --- a/x-pack/plugins/observability_solution/slo/server/services/transform_generators/apm_transaction_duration.test.ts +++ b/x-pack/plugins/observability_solution/slo/server/services/transform_generators/apm_transaction_duration.test.ts @@ -5,6 +5,7 @@ * 2.0. */ +import { dataViewsService } from '@kbn/data-views-plugin/server/mocks'; import { ALL_VALUE } from '@kbn/slo-schema'; import { twoMinute } from '../fixtures/duration'; import { @@ -13,15 +14,14 @@ import { createSLOWithTimeslicesBudgetingMethod, } from '../fixtures/slo'; import { ApmTransactionDurationTransformGenerator } from './apm_transaction_duration'; -import { dataViewsService } from '@kbn/data-views-plugin/server/mocks'; -const generator = new ApmTransactionDurationTransformGenerator(); -const spaceId = 'custom-space'; +const SPACE_ID = 'custom-space'; +const generator = new ApmTransactionDurationTransformGenerator(SPACE_ID, dataViewsService); describe('APM Transaction Duration Transform Generator', () => { it('returns the expected transform params with every specified indicator params', async () => { const slo = createSLO({ id: 'irrelevant', indicator: createAPMTransactionDurationIndicator() }); - const transform = await generator.getTransformParams(slo, spaceId, dataViewsService); + const transform = await generator.getTransformParams(slo); expect(transform).toMatchSnapshot(); }); @@ -31,7 +31,7 @@ describe('APM Transaction Duration Transform Generator', () => { id: 'irrelevant', indicator: createAPMTransactionDurationIndicator(), }); - const transform = await generator.getTransformParams(slo, spaceId, dataViewsService); + const transform = await generator.getTransformParams(slo); expect(transform).toMatchSnapshot(); }); @@ -46,7 +46,7 @@ describe('APM Transaction Duration Transform Generator', () => { timesliceWindow: twoMinute(), }, }); - const transform = await generator.getTransformParams(slo, spaceId, dataViewsService); + const transform = await generator.getTransformParams(slo); expect(transform).toMatchSnapshot(); }); @@ -60,7 +60,7 @@ describe('APM Transaction Duration Transform Generator', () => { transactionType: ALL_VALUE, }), }); - const transform = await generator.getTransformParams(slo, spaceId, dataViewsService); + const transform = await generator.getTransformParams(slo); expect(transform.source.query).toMatchSnapshot(); }); @@ -72,7 +72,7 @@ describe('APM Transaction Duration Transform Generator', () => { index, }), }); - const transform = await generator.getTransformParams(slo, spaceId, dataViewsService); + const transform = await generator.getTransformParams(slo); expect(transform.source.index).toEqual(index); }); @@ -84,7 +84,7 @@ describe('APM Transaction Duration Transform Generator', () => { filter, }), }); - const transform = await generator.getTransformParams(slo, spaceId, dataViewsService); + const transform = await generator.getTransformParams(slo); expect(transform.source.query).toMatchSnapshot(); }); @@ -99,7 +99,7 @@ describe('APM Transaction Duration Transform Generator', () => { }), }); - const transform = await generator.getTransformParams(slo, spaceId, dataViewsService); + const transform = await generator.getTransformParams(slo); expect(transform.source.query).toMatchSnapshot(); expect(transform.pivot?.group_by).toMatchSnapshot(); @@ -115,7 +115,7 @@ describe('APM Transaction Duration Transform Generator', () => { }), }); - const transform = await generator.getTransformParams(slo, spaceId, dataViewsService); + const transform = await generator.getTransformParams(slo); expect(transform.source.query).toMatchSnapshot(); expect(transform.pivot?.group_by).toMatchSnapshot(); @@ -131,7 +131,7 @@ describe('APM Transaction Duration Transform Generator', () => { }), }); - const transform = await generator.getTransformParams(slo, spaceId, dataViewsService); + const transform = await generator.getTransformParams(slo); expect(transform.source.query).toMatchSnapshot(); expect(transform.pivot?.group_by).toMatchSnapshot(); @@ -147,7 +147,7 @@ describe('APM Transaction Duration Transform Generator', () => { }), }); - const transform = await generator.getTransformParams(slo, spaceId, dataViewsService); + const transform = await generator.getTransformParams(slo); expect(transform.source.query).toMatchSnapshot(); expect(transform.pivot?.group_by).toMatchSnapshot(); @@ -163,7 +163,7 @@ describe('APM Transaction Duration Transform Generator', () => { }, }); - const transform = await generator.getTransformParams(slo, spaceId, dataViewsService); + const transform = await generator.getTransformParams(slo); // @ts-ignore const rangeFilter = transform.source.query.bool.filter.find((f) => 'range' in f); diff --git a/x-pack/plugins/observability_solution/slo/server/services/transform_generators/apm_transaction_duration.ts b/x-pack/plugins/observability_solution/slo/server/services/transform_generators/apm_transaction_duration.ts index b349a5affeceb..d1f05605dab36 100644 --- a/x-pack/plugins/observability_solution/slo/server/services/transform_generators/apm_transaction_duration.ts +++ b/x-pack/plugins/observability_solution/slo/server/services/transform_generators/apm_transaction_duration.ts @@ -8,30 +8,29 @@ import { estypes } from '@elastic/elasticsearch'; import { TransformPutTransformRequest } from '@elastic/elasticsearch/lib/api/types'; import { AggregationsAggregationContainer } from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; +import { DataViewsService } from '@kbn/data-views-plugin/common'; import { ALL_VALUE, apmTransactionDurationIndicatorSchema, timeslicesBudgetingMethodSchema, } from '@kbn/slo-schema'; -import { DataViewsService } from '@kbn/data-views-plugin/common'; -import { getElasticsearchQueryOrThrow, TransformGenerator } from '.'; +import { TransformGenerator, getElasticsearchQueryOrThrow } from '.'; import { + SLO_DESTINATION_INDEX_NAME, getSLOPipelineId, getSLOTransformId, - SLO_DESTINATION_INDEX_NAME, } from '../../../common/constants'; import { getSLOTransformTemplate } from '../../assets/transform_templates/slo_transform_template'; import { APMTransactionDurationIndicator, SLODefinition } from '../../domain/models'; import { InvalidTransformError } from '../../errors'; -import { parseIndex } from './common'; -import { getTimesliceTargetComparator, getFilterRange } from './common'; +import { getFilterRange, getTimesliceTargetComparator, parseIndex } from './common'; export class ApmTransactionDurationTransformGenerator extends TransformGenerator { - public async getTransformParams( - slo: SLODefinition, - spaceId: string, - dataViewService: DataViewsService - ): Promise { + constructor(spaceId: string, dataViewService: DataViewsService) { + super(spaceId, dataViewService); + } + + public async getTransformParams(slo: SLODefinition): Promise { if (!apmTransactionDurationIndicatorSchema.is(slo.indicator)) { throw new InvalidTransformError(`Cannot handle SLO of indicator type: ${slo.indicator.type}`); } @@ -39,7 +38,7 @@ export class ApmTransactionDurationTransformGenerator extends TransformGenerator return getSLOTransformTemplate( this.buildTransformId(slo), this.buildDescription(slo), - await this.buildSource(slo, slo.indicator, dataViewService), + await this.buildSource(slo, slo.indicator), this.buildDestination(slo), this.buildGroupBy(slo, slo.indicator), this.buildAggregations(slo, slo.indicator), @@ -75,11 +74,7 @@ export class ApmTransactionDurationTransformGenerator extends TransformGenerator return this.buildCommonGroupBy(slo, '@timestamp', extraGroupByFields); } - private async buildSource( - slo: SLODefinition, - indicator: APMTransactionDurationIndicator, - dataViewService: DataViewsService - ) { + private async buildSource(slo: SLODefinition, indicator: APMTransactionDurationIndicator) { const queryFilter: estypes.QueryDslQueryContainer[] = [getFilterRange(slo, '@timestamp')]; if (indicator.params.service !== ALL_VALUE) { @@ -113,10 +108,7 @@ export class ApmTransactionDurationTransformGenerator extends TransformGenerator }, }); } - const dataView = await this.getIndicatorDataView({ - dataViewService, - dataViewId: indicator.params.dataViewId, - }); + const dataView = await this.getIndicatorDataView(indicator.params.dataViewId); if (!!indicator.params.filter) { queryFilter.push(getElasticsearchQueryOrThrow(indicator.params.filter, dataView)); @@ -124,7 +116,7 @@ export class ApmTransactionDurationTransformGenerator extends TransformGenerator return { index: parseIndex(indicator.params.index), - runtime_mappings: this.buildCommonRuntimeMappings(slo, dataView), + runtime_mappings: this.buildCommonRuntimeMappings(dataView), query: { bool: { filter: [ diff --git a/x-pack/plugins/observability_solution/slo/server/services/transform_generators/apm_transaction_error_rate.test.ts b/x-pack/plugins/observability_solution/slo/server/services/transform_generators/apm_transaction_error_rate.test.ts index 13c73443960af..6b71e37ec4b93 100644 --- a/x-pack/plugins/observability_solution/slo/server/services/transform_generators/apm_transaction_error_rate.test.ts +++ b/x-pack/plugins/observability_solution/slo/server/services/transform_generators/apm_transaction_error_rate.test.ts @@ -5,6 +5,7 @@ * 2.0. */ +import { dataViewsService } from '@kbn/data-views-plugin/server/mocks'; import { ALL_VALUE } from '@kbn/slo-schema'; import { oneMinute, twoMinute } from '../fixtures/duration'; import { @@ -13,10 +14,9 @@ import { createSLOWithTimeslicesBudgetingMethod, } from '../fixtures/slo'; import { ApmTransactionErrorRateTransformGenerator } from './apm_transaction_error_rate'; -import { dataViewsService } from '@kbn/data-views-plugin/server/mocks'; -const generator = new ApmTransactionErrorRateTransformGenerator(); -const spaceId = 'custom-space'; +const SPACE_ID = 'custom-space'; +const generator = new ApmTransactionErrorRateTransformGenerator(SPACE_ID, dataViewsService); describe('APM Transaction Error Rate Transform Generator', () => { it('returns the expected transform params with every specified indicator params', async () => { @@ -24,7 +24,7 @@ describe('APM Transaction Error Rate Transform Generator', () => { id: 'irrelevant', indicator: createAPMTransactionErrorRateIndicator(), }); - const transform = await generator.getTransformParams(slo, spaceId, dataViewsService); + const transform = await generator.getTransformParams(slo); expect(transform).toMatchSnapshot(); }); @@ -34,7 +34,7 @@ describe('APM Transaction Error Rate Transform Generator', () => { id: 'irrelevant', indicator: createAPMTransactionErrorRateIndicator(), }); - const transform = await generator.getTransformParams(slo, spaceId, dataViewsService); + const transform = await generator.getTransformParams(slo); expect(transform).toMatchSnapshot(); }); @@ -49,7 +49,7 @@ describe('APM Transaction Error Rate Transform Generator', () => { timesliceWindow: twoMinute(), }, }); - const transform = await generator.getTransformParams(slo, spaceId, dataViewsService); + const transform = await generator.getTransformParams(slo); expect(transform).toMatchSnapshot(); }); @@ -63,7 +63,7 @@ describe('APM Transaction Error Rate Transform Generator', () => { transactionType: ALL_VALUE, }), }); - const transform = await generator.getTransformParams(slo, spaceId, dataViewsService); + const transform = await generator.getTransformParams(slo); expect(transform.source.query).toMatchSnapshot(); }); @@ -75,7 +75,7 @@ describe('APM Transaction Error Rate Transform Generator', () => { index, }), }); - const transform = await generator.getTransformParams(slo, spaceId, dataViewsService); + const transform = await generator.getTransformParams(slo); expect(transform.source.index).toEqual(index); }); @@ -87,7 +87,7 @@ describe('APM Transaction Error Rate Transform Generator', () => { filter, }), }); - const transform = await generator.getTransformParams(slo, spaceId, dataViewsService); + const transform = await generator.getTransformParams(slo); expect(transform.source.query).toMatchSnapshot(); }); @@ -102,7 +102,7 @@ describe('APM Transaction Error Rate Transform Generator', () => { }), }); - const transform = await generator.getTransformParams(slo, spaceId, dataViewsService); + const transform = await generator.getTransformParams(slo); expect(transform.source.query).toMatchSnapshot(); expect(transform.pivot?.group_by).toMatchSnapshot(); @@ -118,7 +118,7 @@ describe('APM Transaction Error Rate Transform Generator', () => { }), }); - const transform = await generator.getTransformParams(slo, spaceId, dataViewsService); + const transform = await generator.getTransformParams(slo); expect(transform.source.query).toMatchSnapshot(); expect(transform.pivot?.group_by).toMatchSnapshot(); @@ -134,7 +134,7 @@ describe('APM Transaction Error Rate Transform Generator', () => { }), }); - const transform = await generator.getTransformParams(slo, spaceId, dataViewsService); + const transform = await generator.getTransformParams(slo); expect(transform.source.query).toMatchSnapshot(); expect(transform.pivot?.group_by).toMatchSnapshot(); @@ -150,7 +150,7 @@ describe('APM Transaction Error Rate Transform Generator', () => { }), }); - const transform = await generator.getTransformParams(slo, spaceId, dataViewsService); + const transform = await generator.getTransformParams(slo); expect(transform.source.query).toMatchSnapshot(); expect(transform.pivot?.group_by).toMatchSnapshot(); @@ -166,7 +166,7 @@ describe('APM Transaction Error Rate Transform Generator', () => { }, }); - const transform = await generator.getTransformParams(slo, spaceId, dataViewsService); + const transform = await generator.getTransformParams(slo); // @ts-ignore const rangeFilter = transform.source.query.bool.filter.find((f) => 'range' in f); diff --git a/x-pack/plugins/observability_solution/slo/server/services/transform_generators/apm_transaction_error_rate.ts b/x-pack/plugins/observability_solution/slo/server/services/transform_generators/apm_transaction_error_rate.ts index 3aa0d4507e8a4..6adbd1d3eae9f 100644 --- a/x-pack/plugins/observability_solution/slo/server/services/transform_generators/apm_transaction_error_rate.ts +++ b/x-pack/plugins/observability_solution/slo/server/services/transform_generators/apm_transaction_error_rate.ts @@ -13,11 +13,11 @@ import { apmTransactionErrorRateIndicatorSchema, timeslicesBudgetingMethodSchema, } from '@kbn/slo-schema'; -import { getElasticsearchQueryOrThrow, TransformGenerator } from '.'; +import { TransformGenerator, getElasticsearchQueryOrThrow } from '.'; import { + SLO_DESTINATION_INDEX_NAME, getSLOPipelineId, getSLOTransformId, - SLO_DESTINATION_INDEX_NAME, } from '../../../common/constants'; import { getSLOTransformTemplate } from '../../assets/transform_templates/slo_transform_template'; import { APMTransactionErrorRateIndicator, SLODefinition } from '../../domain/models'; @@ -25,11 +25,11 @@ import { InvalidTransformError } from '../../errors'; import { getFilterRange, getTimesliceTargetComparator, parseIndex } from './common'; export class ApmTransactionErrorRateTransformGenerator extends TransformGenerator { - public async getTransformParams( - slo: SLODefinition, - spaceId: string, - dataViewService: DataViewsService - ): Promise { + constructor(spaceId: string, dataViewService: DataViewsService) { + super(spaceId, dataViewService); + } + + public async getTransformParams(slo: SLODefinition): Promise { if (!apmTransactionErrorRateIndicatorSchema.is(slo.indicator)) { throw new InvalidTransformError(`Cannot handle SLO of indicator type: ${slo.indicator.type}`); } @@ -37,7 +37,7 @@ export class ApmTransactionErrorRateTransformGenerator extends TransformGenerato return getSLOTransformTemplate( this.buildTransformId(slo), this.buildDescription(slo), - await this.buildSource(slo, slo.indicator, dataViewService), + await this.buildSource(slo, slo.indicator), this.buildDestination(slo), this.buildGroupBy(slo, slo.indicator), this.buildAggregations(slo), @@ -73,11 +73,7 @@ export class ApmTransactionErrorRateTransformGenerator extends TransformGenerato return this.buildCommonGroupBy(slo, '@timestamp', extraGroupByFields); } - private async buildSource( - slo: SLODefinition, - indicator: APMTransactionErrorRateIndicator, - dataViewService: DataViewsService - ) { + private async buildSource(slo: SLODefinition, indicator: APMTransactionErrorRateIndicator) { const queryFilter: estypes.QueryDslQueryContainer[] = [getFilterRange(slo, '@timestamp')]; if (indicator.params.service !== ALL_VALUE) { @@ -112,10 +108,7 @@ export class ApmTransactionErrorRateTransformGenerator extends TransformGenerato }); } - const dataView = await this.getIndicatorDataView({ - dataViewService, - dataViewId: indicator.params.dataViewId, - }); + const dataView = await this.getIndicatorDataView(indicator.params.dataViewId); if (indicator.params.filter) { queryFilter.push(getElasticsearchQueryOrThrow(indicator.params.filter, dataView)); @@ -123,7 +116,7 @@ export class ApmTransactionErrorRateTransformGenerator extends TransformGenerato return { index: parseIndex(indicator.params.index), - runtime_mappings: this.buildCommonRuntimeMappings(slo, dataView), + runtime_mappings: this.buildCommonRuntimeMappings(dataView), query: { bool: { filter: [ diff --git a/x-pack/plugins/observability_solution/slo/server/services/transform_generators/histogram.test.ts b/x-pack/plugins/observability_solution/slo/server/services/transform_generators/histogram.test.ts index 2de75b8f7d86c..5410efb048dcd 100644 --- a/x-pack/plugins/observability_solution/slo/server/services/transform_generators/histogram.test.ts +++ b/x-pack/plugins/observability_solution/slo/server/services/transform_generators/histogram.test.ts @@ -14,8 +14,8 @@ import { import { HistogramTransformGenerator } from './histogram'; import { dataViewsService } from '@kbn/data-views-plugin/server/mocks'; -const generator = new HistogramTransformGenerator(); -const spaceId = 'custom-space'; +const SPACE_ID = 'custom-space'; +const generator = new HistogramTransformGenerator(SPACE_ID, dataViewsService); describe('Histogram Transform Generator', () => { describe('validation', () => { @@ -32,9 +32,7 @@ describe('Histogram Transform Generator', () => { }), }); - await expect(generator.getTransformParams(anSLO, spaceId, dataViewsService)).rejects.toThrow( - /Invalid KQL: foo:/ - ); + await expect(generator.getTransformParams(anSLO)).rejects.toThrow(/Invalid KQL: foo:/); }); it('throws when the total filter is invalid', async () => { @@ -48,24 +46,20 @@ describe('Histogram Transform Generator', () => { }), }); - await expect(generator.getTransformParams(anSLO, spaceId, dataViewsService)).rejects.toThrow( - /Invalid KQL: foo:/ - ); + await expect(generator.getTransformParams(anSLO)).rejects.toThrow(/Invalid KQL: foo:/); }); it('throws when the query_filter is invalid', async () => { const anSLO = createSLO({ indicator: createHistogramIndicator({ filter: '{ kql.query: invalid' }), }); - await expect(generator.getTransformParams(anSLO, spaceId, dataViewsService)).rejects.toThrow( - /Invalid KQL/ - ); + await expect(generator.getTransformParams(anSLO)).rejects.toThrow(/Invalid KQL/); }); }); it('returns the expected transform params with every specified indicator params', async () => { const anSLO = createSLO({ id: 'irrelevant', indicator: createHistogramIndicator() }); - const transform = await generator.getTransformParams(anSLO, spaceId, dataViewsService); + const transform = await generator.getTransformParams(anSLO); expect(transform).toMatchSnapshot(); }); @@ -75,7 +69,7 @@ describe('Histogram Transform Generator', () => { id: 'irrelevant', indicator: createHistogramIndicator(), }); - const transform = await generator.getTransformParams(anSLO, spaceId, dataViewsService); + const transform = await generator.getTransformParams(anSLO); expect(transform).toMatchSnapshot(); }); @@ -90,7 +84,7 @@ describe('Histogram Transform Generator', () => { timesliceWindow: twoMinute(), }, }); - const transform = await generator.getTransformParams(anSLO, spaceId, dataViewsService); + const transform = await generator.getTransformParams(anSLO); expect(transform).toMatchSnapshot(); }); @@ -99,7 +93,7 @@ describe('Histogram Transform Generator', () => { const anSLO = createSLO({ indicator: createHistogramIndicator({ filter: 'labels.groupId: group-4' }), }); - const transform = await generator.getTransformParams(anSLO, spaceId, dataViewsService); + const transform = await generator.getTransformParams(anSLO); expect(transform.source.query).toMatchSnapshot(); }); @@ -108,7 +102,7 @@ describe('Histogram Transform Generator', () => { const anSLO = createSLO({ indicator: createHistogramIndicator({ index: 'my-own-index*' }), }); - const transform = await generator.getTransformParams(anSLO, spaceId, dataViewsService); + const transform = await generator.getTransformParams(anSLO); expect(transform.source.index).toBe('my-own-index*'); }); @@ -119,7 +113,7 @@ describe('Histogram Transform Generator', () => { timestampField: 'my-date-field', }), }); - const transform = await generator.getTransformParams(anSLO, spaceId, dataViewsService); + const transform = await generator.getTransformParams(anSLO); expect(transform.sync?.time?.field).toBe('my-date-field'); // @ts-ignore @@ -130,7 +124,7 @@ describe('Histogram Transform Generator', () => { const anSLO = createSLO({ indicator: createHistogramIndicator(), }); - const transform = await generator.getTransformParams(anSLO, spaceId, dataViewsService); + const transform = await generator.getTransformParams(anSLO); expect(transform.pivot!.aggregations!['slo.numerator']).toMatchSnapshot(); }); @@ -147,7 +141,7 @@ describe('Histogram Transform Generator', () => { }, }), }); - const transform = await generator.getTransformParams(anSLO, spaceId, dataViewsService); + const transform = await generator.getTransformParams(anSLO); expect(transform.pivot!.aggregations!['slo.numerator']).toMatchSnapshot(); }); @@ -156,7 +150,7 @@ describe('Histogram Transform Generator', () => { const anSLO = createSLO({ indicator: createHistogramIndicator(), }); - const transform = await generator.getTransformParams(anSLO, spaceId, dataViewsService); + const transform = await generator.getTransformParams(anSLO); expect(transform.pivot!.aggregations!['slo.denominator']).toMatchSnapshot(); }); @@ -171,7 +165,7 @@ describe('Histogram Transform Generator', () => { }, }), }); - const transform = await generator.getTransformParams(anSLO, spaceId, dataViewsService); + const transform = await generator.getTransformParams(anSLO); expect(transform.pivot!.aggregations!['slo.denominator']).toMatchSnapshot(); }); @@ -186,7 +180,7 @@ describe('Histogram Transform Generator', () => { }, }); - const transform = await generator.getTransformParams(slo, spaceId, dataViewsService); + const transform = await generator.getTransformParams(slo); // @ts-ignore const rangeFilter = transform.source.query.bool.filter.find((f) => 'range' in f); diff --git a/x-pack/plugins/observability_solution/slo/server/services/transform_generators/histogram.ts b/x-pack/plugins/observability_solution/slo/server/services/transform_generators/histogram.ts index b19f9a48e70f0..805e18c9c31db 100644 --- a/x-pack/plugins/observability_solution/slo/server/services/transform_generators/histogram.ts +++ b/x-pack/plugins/observability_solution/slo/server/services/transform_generators/histogram.ts @@ -6,18 +6,17 @@ */ import { TransformPutTransformRequest } from '@elastic/elasticsearch/lib/api/types'; +import { DataViewsService } from '@kbn/data-views-plugin/common'; import { HistogramIndicator, histogramIndicatorSchema, timeslicesBudgetingMethodSchema, } from '@kbn/slo-schema'; - -import { DataViewsService } from '@kbn/data-views-plugin/common'; -import { getElasticsearchQueryOrThrow, parseIndex, TransformGenerator } from '.'; +import { TransformGenerator, getElasticsearchQueryOrThrow, parseIndex } from '.'; import { + SLO_DESTINATION_INDEX_NAME, getSLOPipelineId, getSLOTransformId, - SLO_DESTINATION_INDEX_NAME, } from '../../../common/constants'; import { getSLOTransformTemplate } from '../../assets/transform_templates/slo_transform_template'; import { SLODefinition } from '../../domain/models'; @@ -26,11 +25,11 @@ import { GetHistogramIndicatorAggregation } from '../aggregations'; import { getFilterRange, getTimesliceTargetComparator } from './common'; export class HistogramTransformGenerator extends TransformGenerator { - public async getTransformParams( - slo: SLODefinition, - spaceId: string, - dataViewService: DataViewsService - ): Promise { + constructor(spaceId: string, dataViewService: DataViewsService) { + super(spaceId, dataViewService); + } + + public async getTransformParams(slo: SLODefinition): Promise { if (!histogramIndicatorSchema.is(slo.indicator)) { throw new InvalidTransformError(`Cannot handle SLO of indicator type: ${slo.indicator.type}`); } @@ -38,7 +37,7 @@ export class HistogramTransformGenerator extends TransformGenerator { return getSLOTransformTemplate( this.buildTransformId(slo), this.buildDescription(slo), - await this.buildSource(slo, slo.indicator, dataViewService), + await this.buildSource(slo, slo.indicator), this.buildDestination(slo), this.buildCommonGroupBy(slo, slo.indicator.params.timestampField), this.buildAggregations(slo, slo.indicator), @@ -51,19 +50,12 @@ export class HistogramTransformGenerator extends TransformGenerator { return getSLOTransformId(slo.id, slo.revision); } - private async buildSource( - slo: SLODefinition, - indicator: HistogramIndicator, - dataViewService: DataViewsService - ) { - const dataView = await this.getIndicatorDataView({ - dataViewService, - dataViewId: indicator.params.index, - }); + private async buildSource(slo: SLODefinition, indicator: HistogramIndicator) { + const dataView = await this.getIndicatorDataView(indicator.params.dataViewId); return { index: parseIndex(indicator.params.index), - runtime_mappings: this.buildCommonRuntimeMappings(slo, dataView), + runtime_mappings: this.buildCommonRuntimeMappings(dataView), query: { bool: { filter: [ diff --git a/x-pack/plugins/observability_solution/slo/server/services/transform_generators/index.ts b/x-pack/plugins/observability_solution/slo/server/services/transform_generators/index.ts index c58de27e9b98e..9b68c19692ee4 100644 --- a/x-pack/plugins/observability_solution/slo/server/services/transform_generators/index.ts +++ b/x-pack/plugins/observability_solution/slo/server/services/transform_generators/index.ts @@ -14,3 +14,4 @@ export * from './metric_custom'; export * from './histogram'; export * from './timeslice_metric'; export * from './common'; +export * from './transform_generators_factory'; diff --git a/x-pack/plugins/observability_solution/slo/server/services/transform_generators/kql_custom.test.ts b/x-pack/plugins/observability_solution/slo/server/services/transform_generators/kql_custom.test.ts index c41e0d4b3df53..e25aefdb3be1a 100644 --- a/x-pack/plugins/observability_solution/slo/server/services/transform_generators/kql_custom.test.ts +++ b/x-pack/plugins/observability_solution/slo/server/services/transform_generators/kql_custom.test.ts @@ -14,8 +14,8 @@ import { import { KQLCustomTransformGenerator } from './kql_custom'; import { dataViewsService } from '@kbn/data-views-plugin/server/mocks'; -const generator = new KQLCustomTransformGenerator(); -const spaceId = 'custom-space'; +const SPACE_ID = 'custom-space'; +const generator = new KQLCustomTransformGenerator(SPACE_ID, dataViewsService); describe('KQL Custom Transform Generator', () => { describe('validation', () => { @@ -23,31 +23,25 @@ describe('KQL Custom Transform Generator', () => { const anSLO = createSLO({ indicator: createKQLCustomIndicator({ good: '{ kql.query: invalid' }), }); - await expect(generator.getTransformParams(anSLO, spaceId, dataViewsService)).rejects.toThrow( - /Invalid KQL/ - ); + await expect(generator.getTransformParams(anSLO)).rejects.toThrow(/Invalid KQL/); }); it('throws when the KQL denominator is invalid', async () => { const anSLO = createSLO({ indicator: createKQLCustomIndicator({ total: '{ kql.query: invalid' }), }); - await expect(generator.getTransformParams(anSLO, spaceId, dataViewsService)).rejects.toThrow( - /Invalid KQL/ - ); + await expect(generator.getTransformParams(anSLO)).rejects.toThrow(/Invalid KQL/); }); it('throws when the KQL query_filter is invalid', async () => { const anSLO = createSLO({ indicator: createKQLCustomIndicator({ filter: '{ kql.query: invalid' }), }); - await expect(generator.getTransformParams(anSLO, spaceId, dataViewsService)).rejects.toThrow( - /Invalid KQL/ - ); + await expect(generator.getTransformParams(anSLO)).rejects.toThrow(/Invalid KQL/); }); }); it('returns the expected transform params with every specified indicator params', async () => { const anSLO = createSLO({ id: 'irrelevant', indicator: createKQLCustomIndicator() }); - const transform = await generator.getTransformParams(anSLO, spaceId, dataViewsService); + const transform = await generator.getTransformParams(anSLO); expect(transform).toMatchSnapshot(); }); @@ -57,7 +51,7 @@ describe('KQL Custom Transform Generator', () => { id: 'irrelevant', indicator: createKQLCustomIndicator(), }); - const transform = await generator.getTransformParams(anSLO, spaceId, dataViewsService); + const transform = await generator.getTransformParams(anSLO); expect(transform).toMatchSnapshot(); }); @@ -72,7 +66,7 @@ describe('KQL Custom Transform Generator', () => { timesliceWindow: twoMinute(), }, }); - const transform = await generator.getTransformParams(anSLO, spaceId, dataViewsService); + const transform = await generator.getTransformParams(anSLO); expect(transform).toMatchSnapshot(); }); @@ -81,7 +75,7 @@ describe('KQL Custom Transform Generator', () => { const anSLO = createSLO({ indicator: createKQLCustomIndicator({ filter: 'labels.groupId: group-4' }), }); - const transform = await generator.getTransformParams(anSLO, spaceId, dataViewsService); + const transform = await generator.getTransformParams(anSLO); expect(transform.source.query).toMatchSnapshot(); }); @@ -90,7 +84,7 @@ describe('KQL Custom Transform Generator', () => { const anSLO = createSLO({ indicator: createKQLCustomIndicator({ index: 'my-own-index*' }), }); - const transform = await generator.getTransformParams(anSLO, spaceId, dataViewsService); + const transform = await generator.getTransformParams(anSLO); expect(transform.source.index).toBe('my-own-index*'); }); @@ -101,7 +95,7 @@ describe('KQL Custom Transform Generator', () => { timestampField: 'my-date-field', }), }); - const transform = await generator.getTransformParams(anSLO, spaceId, dataViewsService); + const transform = await generator.getTransformParams(anSLO); expect(transform.sync?.time?.field).toBe('my-date-field'); // @ts-ignore @@ -114,7 +108,7 @@ describe('KQL Custom Transform Generator', () => { good: 'latency < 400 and (http.status_code: 2xx or http.status_code: 3xx or http.status_code: 4xx)', }), }); - const transform = await generator.getTransformParams(anSLO, spaceId, dataViewsService); + const transform = await generator.getTransformParams(anSLO); expect(transform.pivot!.aggregations!['slo.numerator']).toMatchSnapshot(); }); @@ -125,7 +119,7 @@ describe('KQL Custom Transform Generator', () => { total: 'http.status_code: *', }), }); - const transform = await generator.getTransformParams(anSLO, spaceId, dataViewsService); + const transform = await generator.getTransformParams(anSLO); expect(transform.pivot!.aggregations!['slo.denominator']).toMatchSnapshot(); }); @@ -140,7 +134,7 @@ describe('KQL Custom Transform Generator', () => { }, }); - const transform = await generator.getTransformParams(slo, spaceId, dataViewsService); + const transform = await generator.getTransformParams(slo); // @ts-ignore const rangeFilter = transform.source.query.bool.filter.find((f) => 'range' in f); diff --git a/x-pack/plugins/observability_solution/slo/server/services/transform_generators/kql_custom.ts b/x-pack/plugins/observability_solution/slo/server/services/transform_generators/kql_custom.ts index 0082e13968c80..61238c82ab600 100644 --- a/x-pack/plugins/observability_solution/slo/server/services/transform_generators/kql_custom.ts +++ b/x-pack/plugins/observability_solution/slo/server/services/transform_generators/kql_custom.ts @@ -6,14 +6,13 @@ */ import { TransformPutTransformRequest } from '@elastic/elasticsearch/lib/api/types'; -import { kqlCustomIndicatorSchema, timeslicesBudgetingMethodSchema } from '@kbn/slo-schema'; - import { DataViewsService } from '@kbn/data-views-plugin/common'; -import { getElasticsearchQueryOrThrow, parseIndex, TransformGenerator } from '.'; +import { kqlCustomIndicatorSchema, timeslicesBudgetingMethodSchema } from '@kbn/slo-schema'; +import { TransformGenerator, getElasticsearchQueryOrThrow, parseIndex } from '.'; import { + SLO_DESTINATION_INDEX_NAME, getSLOPipelineId, getSLOTransformId, - SLO_DESTINATION_INDEX_NAME, } from '../../../common/constants'; import { getSLOTransformTemplate } from '../../assets/transform_templates/slo_transform_template'; import { KQLCustomIndicator, SLODefinition } from '../../domain/models'; @@ -21,11 +20,11 @@ import { InvalidTransformError } from '../../errors'; import { getFilterRange, getTimesliceTargetComparator } from './common'; export class KQLCustomTransformGenerator extends TransformGenerator { - public async getTransformParams( - slo: SLODefinition, - spaceId: string, - dataViewService: DataViewsService - ): Promise { + constructor(spaceId: string, dataViewService: DataViewsService) { + super(spaceId, dataViewService); + } + + public async getTransformParams(slo: SLODefinition): Promise { if (!kqlCustomIndicatorSchema.is(slo.indicator)) { throw new InvalidTransformError(`Cannot handle SLO of indicator type: ${slo.indicator.type}`); } @@ -33,7 +32,7 @@ export class KQLCustomTransformGenerator extends TransformGenerator { return getSLOTransformTemplate( this.buildTransformId(slo), this.buildDescription(slo), - await this.buildSource(slo, slo.indicator, dataViewService), + await this.buildSource(slo, slo.indicator), this.buildDestination(slo), this.buildCommonGroupBy(slo, slo.indicator.params.timestampField), this.buildAggregations(slo, slo.indicator), @@ -46,18 +45,11 @@ export class KQLCustomTransformGenerator extends TransformGenerator { return getSLOTransformId(slo.id, slo.revision); } - private async buildSource( - slo: SLODefinition, - indicator: KQLCustomIndicator, - dataViewService: DataViewsService - ) { - const dataView = await this.getIndicatorDataView({ - dataViewService, - dataViewId: indicator.params.dataViewId, - }); + private async buildSource(slo: SLODefinition, indicator: KQLCustomIndicator) { + const dataView = await this.getIndicatorDataView(indicator.params.dataViewId); return { index: parseIndex(indicator.params.index), - runtime_mappings: this.buildCommonRuntimeMappings(slo, dataView), + runtime_mappings: this.buildCommonRuntimeMappings(dataView), query: { bool: { filter: [ diff --git a/x-pack/plugins/observability_solution/slo/server/services/transform_generators/metric_custom.test.ts b/x-pack/plugins/observability_solution/slo/server/services/transform_generators/metric_custom.test.ts index 85da6de832c98..b725d77af63dc 100644 --- a/x-pack/plugins/observability_solution/slo/server/services/transform_generators/metric_custom.test.ts +++ b/x-pack/plugins/observability_solution/slo/server/services/transform_generators/metric_custom.test.ts @@ -14,8 +14,8 @@ import { import { MetricCustomTransformGenerator } from './metric_custom'; import { dataViewsService } from '@kbn/data-views-plugin/server/mocks'; -const generator = new MetricCustomTransformGenerator(); -const spaceId = 'custom-space'; +const SPACE_ID = 'custom-space'; +const generator = new MetricCustomTransformGenerator(SPACE_ID, dataViewsService); describe('Metric Custom Transform Generator', () => { describe('validation', () => { @@ -28,9 +28,7 @@ describe('Metric Custom Transform Generator', () => { }, }), }); - await expect(generator.getTransformParams(anSLO, spaceId, dataViewsService)).rejects.toThrow( - /Invalid equation/ - ); + await expect(generator.getTransformParams(anSLO)).rejects.toThrow(/Invalid equation/); }); it('throws when the good filter is invalid', async () => { const anSLO = createSLO({ @@ -41,9 +39,7 @@ describe('Metric Custom Transform Generator', () => { }, }), }); - await expect(generator.getTransformParams(anSLO, spaceId, dataViewsService)).rejects.toThrow( - /Invalid KQL: foo:/ - ); + await expect(generator.getTransformParams(anSLO)).rejects.toThrow(/Invalid KQL: foo:/); }); it('throws when the total equation is invalid', async () => { const anSLO = createSLO({ @@ -54,9 +50,7 @@ describe('Metric Custom Transform Generator', () => { }, }), }); - await expect(generator.getTransformParams(anSLO, spaceId, dataViewsService)).rejects.toThrow( - /Invalid equation/ - ); + await expect(generator.getTransformParams(anSLO)).rejects.toThrow(/Invalid equation/); }); it('throws when the total filter is invalid', async () => { const anSLO = createSLO({ @@ -67,23 +61,19 @@ describe('Metric Custom Transform Generator', () => { }, }), }); - await expect(() => - generator.getTransformParams(anSLO, spaceId, dataViewsService) - ).rejects.toThrow(/Invalid KQL: foo:/); + await expect(() => generator.getTransformParams(anSLO)).rejects.toThrow(/Invalid KQL: foo:/); }); it('throws when the query_filter is invalid', async () => { const anSLO = createSLO({ indicator: createMetricCustomIndicator({ filter: '{ kql.query: invalid' }), }); - await expect(() => - generator.getTransformParams(anSLO, spaceId, dataViewsService) - ).rejects.toThrow(/Invalid KQL/); + await expect(() => generator.getTransformParams(anSLO)).rejects.toThrow(/Invalid KQL/); }); }); it('returns the expected transform params with every specified indicator params', async () => { const anSLO = createSLO({ id: 'irrelevant', indicator: createMetricCustomIndicator() }); - const transform = await generator.getTransformParams(anSLO, spaceId, dataViewsService); + const transform = await generator.getTransformParams(anSLO); expect(transform).toMatchSnapshot(); }); @@ -93,7 +83,7 @@ describe('Metric Custom Transform Generator', () => { id: 'irrelevant', indicator: createMetricCustomIndicator(), }); - const transform = await generator.getTransformParams(anSLO, spaceId, dataViewsService); + const transform = await generator.getTransformParams(anSLO); expect(transform).toMatchSnapshot(); }); @@ -102,7 +92,7 @@ describe('Metric Custom Transform Generator', () => { const anSLO = createSLO({ indicator: createMetricCustomIndicator({ filter: 'labels.groupId: group-4' }), }); - const transform = await generator.getTransformParams(anSLO, spaceId, dataViewsService); + const transform = await generator.getTransformParams(anSLO); expect(transform.source.query).toMatchSnapshot(); }); @@ -111,7 +101,7 @@ describe('Metric Custom Transform Generator', () => { const anSLO = createSLO({ indicator: createMetricCustomIndicator({ index: 'my-own-index*' }), }); - const transform = await generator.getTransformParams(anSLO, spaceId, dataViewsService); + const transform = await generator.getTransformParams(anSLO); expect(transform.source.index).toBe('my-own-index*'); }); @@ -122,7 +112,7 @@ describe('Metric Custom Transform Generator', () => { timestampField: 'my-date-field', }), }); - const transform = await generator.getTransformParams(anSLO, spaceId, dataViewsService); + const transform = await generator.getTransformParams(anSLO); expect(transform.sync?.time?.field).toBe('my-date-field'); // @ts-ignore @@ -138,7 +128,7 @@ describe('Metric Custom Transform Generator', () => { }, }), }); - const transform = await generator.getTransformParams(anSLO, spaceId, dataViewsService); + const transform = await generator.getTransformParams(anSLO); expect(transform.pivot!.aggregations!['slo.numerator']).toMatchSnapshot(); }); @@ -152,7 +142,7 @@ describe('Metric Custom Transform Generator', () => { }, }), }); - const transform = await generator.getTransformParams(anSLO, spaceId, dataViewsService); + const transform = await generator.getTransformParams(anSLO); expect(transform.pivot!.aggregations!['slo.numerator']).toMatchSnapshot(); }); @@ -168,7 +158,7 @@ describe('Metric Custom Transform Generator', () => { }, }), }); - const transform = await generator.getTransformParams(anSLO, spaceId, dataViewsService); + const transform = await generator.getTransformParams(anSLO); expect(transform.pivot!.aggregations!['slo.numerator']).toMatchSnapshot(); }); @@ -182,7 +172,7 @@ describe('Metric Custom Transform Generator', () => { }, }), }); - const transform = await generator.getTransformParams(anSLO, spaceId, dataViewsService); + const transform = await generator.getTransformParams(anSLO); expect(transform.pivot!.aggregations!['slo.numerator']).toMatchSnapshot(); }); @@ -196,7 +186,7 @@ describe('Metric Custom Transform Generator', () => { }, }), }); - const transform = await generator.getTransformParams(anSLO, spaceId, dataViewsService); + const transform = await generator.getTransformParams(anSLO); expect(transform.pivot!.aggregations!['slo.denominator']).toMatchSnapshot(); }); @@ -210,7 +200,7 @@ describe('Metric Custom Transform Generator', () => { }, }), }); - const transform = await generator.getTransformParams(anSLO, spaceId, dataViewsService); + const transform = await generator.getTransformParams(anSLO); expect(transform.pivot!.aggregations!['slo.denominator']).toMatchSnapshot(); }); @@ -224,7 +214,7 @@ describe('Metric Custom Transform Generator', () => { }, }), }); - const transform = await generator.getTransformParams(anSLO, spaceId, dataViewsService); + const transform = await generator.getTransformParams(anSLO); expect(transform.pivot!.aggregations!['slo.denominator']).toMatchSnapshot(); }); @@ -239,7 +229,7 @@ describe('Metric Custom Transform Generator', () => { }, }); - const transform = await generator.getTransformParams(slo, spaceId, dataViewsService); + const transform = await generator.getTransformParams(slo); // @ts-ignore const rangeFilter = transform.source.query.bool.filter.find((f) => 'range' in f); diff --git a/x-pack/plugins/observability_solution/slo/server/services/transform_generators/metric_custom.ts b/x-pack/plugins/observability_solution/slo/server/services/transform_generators/metric_custom.ts index e96f252d5ed84..f2259955bdfb0 100644 --- a/x-pack/plugins/observability_solution/slo/server/services/transform_generators/metric_custom.ts +++ b/x-pack/plugins/observability_solution/slo/server/services/transform_generators/metric_custom.ts @@ -24,11 +24,11 @@ import { getFilterRange, getTimesliceTargetComparator } from './common'; export const INVALID_EQUATION_REGEX = /[^A-Z|+|\-|\s|\d+|\.|\(|\)|\/|\*|>|<|=|\?|\:|&|\!|\|]+/g; export class MetricCustomTransformGenerator extends TransformGenerator { - public async getTransformParams( - slo: SLODefinition, - spaceId: string, - dataViewService: DataViewsService - ): Promise { + constructor(spaceId: string, dataViewService: DataViewsService) { + super(spaceId, dataViewService); + } + + public async getTransformParams(slo: SLODefinition): Promise { if (!metricCustomIndicatorSchema.is(slo.indicator)) { throw new InvalidTransformError(`Cannot handle SLO of indicator type: ${slo.indicator.type}`); } @@ -36,7 +36,7 @@ export class MetricCustomTransformGenerator extends TransformGenerator { return getSLOTransformTemplate( this.buildTransformId(slo), this.buildDescription(slo), - await this.buildSource(slo, slo.indicator, dataViewService), + await this.buildSource(slo, slo.indicator), this.buildDestination(slo), this.buildCommonGroupBy(slo, slo.indicator.params.timestampField), this.buildAggregations(slo, slo.indicator), @@ -49,18 +49,11 @@ export class MetricCustomTransformGenerator extends TransformGenerator { return getSLOTransformId(slo.id, slo.revision); } - private async buildSource( - slo: SLODefinition, - indicator: MetricCustomIndicator, - dataViewService: DataViewsService - ) { - const dataView = await this.getIndicatorDataView({ - dataViewService, - dataViewId: indicator.params.dataViewId, - }); + private async buildSource(slo: SLODefinition, indicator: MetricCustomIndicator) { + const dataView = await this.getIndicatorDataView(indicator.params.dataViewId); return { index: parseIndex(indicator.params.index), - runtime_mappings: this.buildCommonRuntimeMappings(slo, dataView), + runtime_mappings: this.buildCommonRuntimeMappings(dataView), query: { bool: { filter: [ diff --git a/x-pack/plugins/observability_solution/slo/server/services/transform_generators/synthetics_availability.test.ts b/x-pack/plugins/observability_solution/slo/server/services/transform_generators/synthetics_availability.test.ts index fa40ab9cc1e8d..cccb1c9eba3e3 100644 --- a/x-pack/plugins/observability_solution/slo/server/services/transform_generators/synthetics_availability.test.ts +++ b/x-pack/plugins/observability_solution/slo/server/services/transform_generators/synthetics_availability.test.ts @@ -12,286 +12,330 @@ import { twoMinute } from '../fixtures/duration'; import { createSLO, createSyntheticsAvailabilityIndicator } from '../fixtures/slo'; import { SyntheticsAvailabilityTransformGenerator } from './synthetics_availability'; -const generator = new SyntheticsAvailabilityTransformGenerator(); +const SPACE_ID = 'custom-space'; describe('Synthetics Availability Transform Generator', () => { - const spaceId = 'custom-space'; - - it('returns the expected transform params', async () => { - const slo = createSLO({ id: 'irrelevant', indicator: createSyntheticsAvailabilityIndicator() }); - const transform = await generator.getTransformParams(slo, spaceId, dataViewsService, false); - - expect(transform).toMatchSnapshot(); - expect(transform.source.query?.bool?.filter).toContainEqual({ - term: { - 'summary.final_attempt': true, - }, - }); - }); - - it('groups by config id and observer.name when using default groupings', async () => { - const slo = createSLO({ - id: 'irrelevant', - indicator: createSyntheticsAvailabilityIndicator(), - }); - const transform = await generator.getTransformParams(slo, spaceId, dataViewsService, false); - - expect(transform.pivot?.group_by).toEqual( - expect.objectContaining({ - 'monitor.config_id': { - terms: { - field: 'config_id', - }, - }, - 'observer.name': { - terms: { - field: 'observer.name', - }, - }, - }) + describe('when serverless is disabled', () => { + const generator = new SyntheticsAvailabilityTransformGenerator( + SPACE_ID, + dataViewsService, + false ); - }); - it('does not include config id and observer.name when using non default groupings', async () => { - const slo = createSLO({ - id: 'irrelevant', - indicator: createSyntheticsAvailabilityIndicator(), - groupBy: ['host.name'], - }); - const transform = await generator.getTransformParams(slo, spaceId, dataViewsService, false); - - expect(transform.pivot?.group_by).not.toEqual( - expect.objectContaining({ - 'monitor.config_id': { - terms: { - field: 'config_id', - }, - }, - 'observer.name': { - terms: { - field: 'observer.name', - }, - }, - }) - ); + it('returns the expected transform params', async () => { + const slo = createSLO({ + id: 'irrelevant', + indicator: createSyntheticsAvailabilityIndicator(), + }); + const transform = await generator.getTransformParams(slo); - expect(transform.pivot?.group_by).toEqual( - expect.objectContaining({ - 'slo.groupings.host.name': { - terms: { - field: 'host.name', - }, + expect(transform).toMatchSnapshot(); + expect(transform.source.query?.bool?.filter).toContainEqual({ + term: { + 'summary.final_attempt': true, }, - }) - ); - }); + }); + }); - it.each([[[]], [[ALL_VALUE]]])( - 'adds observer.geo.name and monitor.name to groupings key by default, multi group by', - async (groupBy) => { + it('groups by config id and observer.name when using default groupings', async () => { const slo = createSLO({ id: 'irrelevant', indicator: createSyntheticsAvailabilityIndicator(), - groupBy, }); - const transform = await generator.getTransformParams(slo, spaceId, dataViewsService, false); + const transform = await generator.getTransformParams(slo); expect(transform.pivot?.group_by).toEqual( expect.objectContaining({ - 'slo.groupings.monitor.name': { + 'monitor.config_id': { terms: { - field: 'monitor.name', + field: 'config_id', }, }, - 'slo.groupings.observer.geo.name': { + 'observer.name': { terms: { - field: 'observer.geo.name', + field: 'observer.name', }, }, }) ); - } - ); + }); - it.each([[''], [ALL_VALUE]])( - 'adds observer.geo.name and monitor.name to groupings key by default, single group by', - async (groupBy) => { + it('does not include config id and observer.name when using non default groupings', async () => { const slo = createSLO({ id: 'irrelevant', indicator: createSyntheticsAvailabilityIndicator(), - groupBy, + groupBy: ['host.name'], }); - const transform = await generator.getTransformParams(slo, spaceId, dataViewsService, false); + const transform = await generator.getTransformParams(slo); - expect(transform.pivot?.group_by).toEqual( + expect(transform.pivot?.group_by).not.toEqual( expect.objectContaining({ - 'slo.groupings.monitor.name': { + 'monitor.config_id': { terms: { - field: 'monitor.name', + field: 'config_id', }, }, - 'slo.groupings.observer.geo.name': { + 'observer.name': { terms: { - field: 'observer.geo.name', + field: 'observer.name', }, }, }) ); - } - ); - - it.each([['host.name'], [['host.name']]])('handles custom groupBy', async (groupBy) => { - const slo = createSLO({ - id: 'irrelevant', - indicator: createSyntheticsAvailabilityIndicator(), - groupBy, - }); - const transform = await generator.getTransformParams(slo, spaceId, dataViewsService, false); - expect(transform.pivot?.group_by).toEqual( - expect.objectContaining({ - 'slo.groupings.host.name': { - terms: { - field: 'host.name', + expect(transform.pivot?.group_by).toEqual( + expect.objectContaining({ + 'slo.groupings.host.name': { + terms: { + field: 'host.name', + }, }, - }, - }) + }) + ); + }); + + it.each([[[]], [[ALL_VALUE]]])( + 'adds observer.geo.name and monitor.name to groupings key by default, multi group by', + async (groupBy) => { + const slo = createSLO({ + id: 'irrelevant', + indicator: createSyntheticsAvailabilityIndicator(), + groupBy, + }); + const transform = await generator.getTransformParams(slo); + + expect(transform.pivot?.group_by).toEqual( + expect.objectContaining({ + 'slo.groupings.monitor.name': { + terms: { + field: 'monitor.name', + }, + }, + 'slo.groupings.observer.geo.name': { + terms: { + field: 'observer.geo.name', + }, + }, + }) + ); + } ); - }); - it('filters by summary.final_attempt', async () => { - const slo = createSLO({ id: 'irrelevant', indicator: createSyntheticsAvailabilityIndicator() }); - const transform = await generator.getTransformParams(slo, spaceId, dataViewsService, false); + it.each([[''], [ALL_VALUE]])( + 'adds observer.geo.name and monitor.name to groupings key by default, single group by', + async (groupBy) => { + const slo = createSLO({ + id: 'irrelevant', + indicator: createSyntheticsAvailabilityIndicator(), + groupBy, + }); + const transform = await generator.getTransformParams(slo); - expect(transform.source.query?.bool?.filter).toContainEqual({ - term: { - 'summary.final_attempt': true, - }, - }); - }); + expect(transform.pivot?.group_by).toEqual( + expect.objectContaining({ + 'slo.groupings.monitor.name': { + terms: { + field: 'monitor.name', + }, + }, + 'slo.groupings.observer.geo.name': { + terms: { + field: 'observer.geo.name', + }, + }, + }) + ); + } + ); + + it.each([[['host.name']], [['host.name', 'host.region']]])( + 'handles custom groupBy', + async (groupBy) => { + const slo = createSLO({ + id: 'irrelevant', + indicator: createSyntheticsAvailabilityIndicator(), + groupBy, + }); + const transform = await generator.getTransformParams(slo); + + expect(transform.pivot?.group_by).toEqual( + expect.objectContaining({ + 'slo.groupings.host.name': { + terms: { + field: 'host.name', + }, + }, + }) + ); + } + ); - it('adds tag filters', async () => { - const tags = [ - { value: 'tag-1', label: 'tag1' }, - { value: 'tag-2', label: 'tag2' }, - ]; - const indicator = createSyntheticsAvailabilityIndicator(); - const slo = createSLO({ - id: 'irrelevant', - indicator: { - ...indicator, - params: { - ...indicator.params, - tags, + it('filters by summary.final_attempt', async () => { + const slo = createSLO({ + id: 'irrelevant', + indicator: createSyntheticsAvailabilityIndicator(), + }); + const transform = await generator.getTransformParams(slo); + + expect(transform.source.query?.bool?.filter).toContainEqual({ + term: { + 'summary.final_attempt': true, }, - } as SLODefinition['indicator'], + }); }); - const transform = await generator.getTransformParams(slo, spaceId, dataViewsService, false); - expect(transform.source.query?.bool?.filter).toContainEqual({ - terms: { - tags: ['tag-1', 'tag-2'], - }, - }); - expect(transform.pivot?.group_by?.tags).toEqual({ - terms: { - field: 'tags', - }, - }); - }); + it('adds tag filters', async () => { + const tags = [ + { value: 'tag-1', label: 'tag1' }, + { value: 'tag-2', label: 'tag2' }, + ]; + const indicator = createSyntheticsAvailabilityIndicator(); + const slo = createSLO({ + id: 'irrelevant', + indicator: { + ...indicator, + params: { + ...indicator.params, + tags, + }, + } as SLODefinition['indicator'], + }); + const transform = await generator.getTransformParams(slo); - it('adds monitorId filter', async () => { - const monitorIds = [ - { value: 'id-1', label: 'Monitor name 1' }, - { value: 'id-2', label: 'Monitor name 2' }, - ]; - const indicator = createSyntheticsAvailabilityIndicator(); - const slo = createSLO({ - id: 'irrelevant', - indicator: { - ...indicator, - params: { - ...indicator.params, - monitorIds, + expect(transform.source.query?.bool?.filter).toContainEqual({ + terms: { + tags: ['tag-1', 'tag-2'], }, - } as SLODefinition['indicator'], + }); + expect(transform.pivot?.group_by?.tags).toEqual({ + terms: { + field: 'tags', + }, + }); }); - const transform = await generator.getTransformParams(slo, spaceId, dataViewsService, false); - expect(transform.source.query?.bool?.filter).toContainEqual({ - terms: { - 'monitor.id': ['id-1', 'id-2'], - }, - }); - expect(transform.pivot?.group_by?.['monitor.id']).toEqual({ - terms: { - field: 'monitor.id', - }, - }); - }); + it('adds monitorId filter', async () => { + const monitorIds = [ + { value: 'id-1', label: 'Monitor name 1' }, + { value: 'id-2', label: 'Monitor name 2' }, + ]; + const indicator = createSyntheticsAvailabilityIndicator(); + const slo = createSLO({ + id: 'irrelevant', + indicator: { + ...indicator, + params: { + ...indicator.params, + monitorIds, + }, + } as SLODefinition['indicator'], + }); + const transform = await generator.getTransformParams(slo); - it('adds project id filter', async () => { - const projects = [ - { value: 'id-1', label: 'Project name 1' }, - { value: 'id-2', label: 'Project name 2' }, - ]; - const indicator = createSyntheticsAvailabilityIndicator(); - const slo = createSLO({ - id: 'irrelevant', - indicator: { - ...indicator, - params: { - ...indicator.params, - projects, + expect(transform.source.query?.bool?.filter).toContainEqual({ + terms: { + 'monitor.id': ['id-1', 'id-2'], + }, + }); + expect(transform.pivot?.group_by?.['monitor.id']).toEqual({ + terms: { + field: 'monitor.id', }, - } as SLODefinition['indicator'], + }); }); - const transform = await generator.getTransformParams(slo, spaceId, dataViewsService, false); - expect(transform.source.query?.bool?.filter).toContainEqual({ - terms: { - 'monitor.project.id': ['id-1', 'id-2'], - }, + it('adds project id filter', async () => { + const projects = [ + { value: 'id-1', label: 'Project name 1' }, + { value: 'id-2', label: 'Project name 2' }, + ]; + const indicator = createSyntheticsAvailabilityIndicator(); + const slo = createSLO({ + id: 'irrelevant', + indicator: { + ...indicator, + params: { + ...indicator.params, + projects, + }, + } as SLODefinition['indicator'], + }); + const transform = await generator.getTransformParams(slo); + + expect(transform.source.query?.bool?.filter).toContainEqual({ + terms: { + 'monitor.project.id': ['id-1', 'id-2'], + }, + }); + expect(transform.pivot?.group_by?.['monitor.project.id']).toEqual({ + terms: { + field: 'monitor.project.id', + }, + }); }); - expect(transform.pivot?.group_by?.['monitor.project.id']).toEqual({ - terms: { - field: 'monitor.project.id', - }, + + it('filters by space', async () => { + const slo = createSLO({ + id: 'irrelevant', + indicator: createSyntheticsAvailabilityIndicator(), + }); + const transform = await generator.getTransformParams(slo); + + expect(transform.source.query?.bool?.filter).toContainEqual({ + term: { + 'meta.space_id': SPACE_ID, + }, + }); }); - }); - it('filters by space', async () => { - const slo = createSLO({ id: 'irrelevant', indicator: createSyntheticsAvailabilityIndicator() }); - const transform = await generator.getTransformParams(slo, spaceId, dataViewsService, false); + it("overrides the range filter when 'preventInitialBackfill' is true", async () => { + const slo = createSLO({ + indicator: createSyntheticsAvailabilityIndicator(), + settings: { + frequency: twoMinute(), + syncDelay: twoMinute(), + preventInitialBackfill: true, + }, + }); + + const transform = await generator.getTransformParams(slo); + + // @ts-ignore + const rangeFilter = transform.source.query.bool.filter.find((f) => 'range' in f); - expect(transform.source.query?.bool?.filter).toContainEqual({ - term: { - 'meta.space_id': spaceId, - }, + expect(rangeFilter).toEqual({ + range: { + '@timestamp': { + gte: 'now-300s/m', // 2m + 2m + 60s + }, + }, + }); }); - }); - it("overrides the range filter when 'preventInitialBackfill' is true", async () => { - const slo = createSLO({ - indicator: createSyntheticsAvailabilityIndicator(), - settings: { - frequency: twoMinute(), - syncDelay: twoMinute(), - preventInitialBackfill: true, - }, + it("uses the 'event.ingested' as syncField", async () => { + const slo = createSLO({ + indicator: createSyntheticsAvailabilityIndicator(), + }); + const transform = await generator.getTransformParams(slo); + + expect(transform.sync?.time?.field).toEqual('event.ingested'); }); + }); - const transform = await generator.getTransformParams(slo, 'default', dataViewsService, false); + describe('when serverless is enabled', () => { + const generator = new SyntheticsAvailabilityTransformGenerator( + SPACE_ID, + dataViewsService, + true + ); - // @ts-ignore - const rangeFilter = transform.source.query.bool.filter.find((f) => 'range' in f); + it("overrides the syncField with '@timestamp'", async () => { + const slo = createSLO({ + indicator: createSyntheticsAvailabilityIndicator(), + }); + const transform = await generator.getTransformParams(slo); - expect(rangeFilter).toEqual({ - range: { - '@timestamp': { - gte: 'now-300s/m', // 2m + 2m + 60s - }, - }, + expect(transform.sync?.time?.field).toEqual('@timestamp'); }); }); }); diff --git a/x-pack/plugins/observability_solution/slo/server/services/transform_generators/synthetics_availability.ts b/x-pack/plugins/observability_solution/slo/server/services/transform_generators/synthetics_availability.ts index 285820f908182..65fda9c3fc222 100644 --- a/x-pack/plugins/observability_solution/slo/server/services/transform_generators/synthetics_availability.ts +++ b/x-pack/plugins/observability_solution/slo/server/services/transform_generators/synthetics_availability.ts @@ -28,12 +28,11 @@ import { InvalidTransformError } from '../../errors'; import { getFilterRange } from './common'; export class SyntheticsAvailabilityTransformGenerator extends TransformGenerator { - public async getTransformParams( - slo: SLODefinition, - spaceId: string, - dataViewService: DataViewsService, - isServerless: boolean - ): Promise { + constructor(spaceId: string, dataViewService: DataViewsService, isServerless: boolean) { + super(spaceId, dataViewService, isServerless); + } + + public async getTransformParams(slo: SLODefinition): Promise { if (!syntheticsAvailabilityIndicatorSchema.is(slo.indicator)) { throw new InvalidTransformError(`Cannot handle SLO of indicator type: ${slo.indicator.type}`); } @@ -41,11 +40,11 @@ export class SyntheticsAvailabilityTransformGenerator extends TransformGenerator return getSLOTransformTemplate( this.buildTransformId(slo), this.buildDescription(slo), - await this.buildSource(slo, slo.indicator, spaceId, dataViewService), + await this.buildSource(slo, slo.indicator), this.buildDestination(slo), this.buildGroupBy(slo, slo.indicator), this.buildAggregations(slo), - this.buildSettings(slo, isServerless ? '@timestamp' : 'event.ingested'), + this.buildSettings(slo, this.isServerless ? '@timestamp' : 'event.ingested'), slo ); } @@ -108,15 +107,10 @@ export class SyntheticsAvailabilityTransformGenerator extends TransformGenerator ); } - private async buildSource( - slo: SLODefinition, - indicator: SyntheticsAvailabilityIndicator, - spaceId: string, - dataViewService: DataViewsService - ) { + private async buildSource(slo: SLODefinition, indicator: SyntheticsAvailabilityIndicator) { const queryFilter: estypes.QueryDslQueryContainer[] = [ { term: { 'summary.final_attempt': true } }, - { term: { 'meta.space_id': spaceId } }, + { term: { 'meta.space_id': this.spaceId } }, getFilterRange(slo, '@timestamp'), ]; const { monitorIds, tags, projects } = buildParamValues({ @@ -153,14 +147,11 @@ export class SyntheticsAvailabilityTransformGenerator extends TransformGenerator queryFilter.push(getElasticsearchQueryOrThrow(indicator.params.filter)); } - const dataView = await this.getIndicatorDataView({ - dataViewService, - dataViewId: indicator.params.dataViewId, - }); + const dataView = await this.getIndicatorDataView(indicator.params.dataViewId); return { index: SYNTHETICS_INDEX_PATTERN, - runtime_mappings: this.buildCommonRuntimeMappings(slo, dataView), + runtime_mappings: this.buildCommonRuntimeMappings(dataView), query: { bool: { filter: queryFilter, diff --git a/x-pack/plugins/observability_solution/slo/server/services/transform_generators/timeslice_metric.test.ts b/x-pack/plugins/observability_solution/slo/server/services/transform_generators/timeslice_metric.test.ts index 02c69f38d6705..c8fd371479c29 100644 --- a/x-pack/plugins/observability_solution/slo/server/services/transform_generators/timeslice_metric.test.ts +++ b/x-pack/plugins/observability_solution/slo/server/services/transform_generators/timeslice_metric.test.ts @@ -5,18 +5,17 @@ * 2.0. */ +import { dataViewsService } from '@kbn/data-views-plugin/server/mocks'; import { twoMinute } from '../fixtures/duration'; import { - createTimesliceMetricIndicator, - createSLOWithTimeslicesBudgetingMethod, createSLO, + createSLOWithTimeslicesBudgetingMethod, + createTimesliceMetricIndicator, } from '../fixtures/slo'; import { TimesliceMetricTransformGenerator } from './timeslice_metric'; -import { dataViewsService } from '@kbn/data-views-plugin/server/mocks'; - -const generator = new TimesliceMetricTransformGenerator(); -const spaceId = 'custom-space'; +const SPACE_ID = 'custom-space'; +const generator = new TimesliceMetricTransformGenerator(SPACE_ID, dataViewsService); const everythingIndicator = createTimesliceMetricIndicator( [ { name: 'A', aggregation: 'avg', field: 'test.field', filter: 'test.category: "test"' }, @@ -38,7 +37,7 @@ describe('Timeslice Metric Transform Generator', () => { '(A / 200) + A' ), }); - await expect(generator.getTransformParams(anSLO, spaceId, dataViewsService)).rejects.toThrow( + await expect(generator.getTransformParams(anSLO)).rejects.toThrow( 'The sli.metric.timeslice indicator MUST have a timeslice budgeting method.' ); }); @@ -49,9 +48,7 @@ describe('Timeslice Metric Transform Generator', () => { '(a / 200) + A' ), }); - await expect(generator.getTransformParams(anSLO, spaceId, dataViewsService)).rejects.toThrow( - /Invalid equation/ - ); + await expect(generator.getTransformParams(anSLO)).rejects.toThrow(/Invalid equation/); }); it('throws when the metric filter is invalid', async () => { const anSLO = createSLOWithTimeslicesBudgetingMethod({ @@ -60,9 +57,7 @@ describe('Timeslice Metric Transform Generator', () => { '(A / 200) + A' ), }); - await expect(generator.getTransformParams(anSLO, spaceId, dataViewsService)).rejects.toThrow( - /Invalid KQL: test:/ - ); + await expect(generator.getTransformParams(anSLO)).rejects.toThrow(/Invalid KQL: test:/); }); it('throws when the query_filter is invalid', async () => { const anSLO = createSLOWithTimeslicesBudgetingMethod({ @@ -72,9 +67,7 @@ describe('Timeslice Metric Transform Generator', () => { 'test:' ), }); - await expect(generator.getTransformParams(anSLO, spaceId, dataViewsService)).rejects.toThrow( - /Invalid KQL/ - ); + await expect(generator.getTransformParams(anSLO)).rejects.toThrow(/Invalid KQL/); }); }); @@ -83,7 +76,7 @@ describe('Timeslice Metric Transform Generator', () => { id: 'irrelevant', indicator: everythingIndicator, }); - const transform = await generator.getTransformParams(anSLO, spaceId, dataViewsService); + const transform = await generator.getTransformParams(anSLO); expect(transform).toMatchSnapshot(); }); @@ -93,7 +86,7 @@ describe('Timeslice Metric Transform Generator', () => { id: 'irrelevant', indicator: everythingIndicator, }); - const transform = await generator.getTransformParams(anSLO, spaceId, dataViewsService); + const transform = await generator.getTransformParams(anSLO); expect(transform).toMatchSnapshot(); }); @@ -102,7 +95,7 @@ describe('Timeslice Metric Transform Generator', () => { const anSLO = createSLOWithTimeslicesBudgetingMethod({ indicator: everythingIndicator, }); - const transform = await generator.getTransformParams(anSLO, spaceId, dataViewsService); + const transform = await generator.getTransformParams(anSLO); expect(transform.source.query).toMatchSnapshot(); }); @@ -114,7 +107,7 @@ describe('Timeslice Metric Transform Generator', () => { params: { ...everythingIndicator.params, index: 'my-own-index*' }, }, }); - const transform = await generator.getTransformParams(anSLO, spaceId, dataViewsService); + const transform = await generator.getTransformParams(anSLO); expect(transform.source.index).toBe('my-own-index*'); }); @@ -126,7 +119,7 @@ describe('Timeslice Metric Transform Generator', () => { params: { ...everythingIndicator.params, timestampField: 'my-date-field' }, }, }); - const transform = await generator.getTransformParams(anSLO, spaceId, dataViewsService); + const transform = await generator.getTransformParams(anSLO); expect(transform.sync?.time?.field).toBe('my-date-field'); // @ts-ignore @@ -137,7 +130,7 @@ describe('Timeslice Metric Transform Generator', () => { const anSLO = createSLOWithTimeslicesBudgetingMethod({ indicator: everythingIndicator, }); - const transform = await generator.getTransformParams(anSLO, spaceId, dataViewsService); + const transform = await generator.getTransformParams(anSLO); expect(transform.pivot!.aggregations!._metric).toEqual({ bucket_script: { @@ -185,7 +178,7 @@ describe('Timeslice Metric Transform Generator', () => { }, }); - const transform = await generator.getTransformParams(slo, spaceId, dataViewsService); + const transform = await generator.getTransformParams(slo); // @ts-ignore const rangeFilter = transform.source.query.bool.filter.find((f) => 'range' in f); diff --git a/x-pack/plugins/observability_solution/slo/server/services/transform_generators/timeslice_metric.ts b/x-pack/plugins/observability_solution/slo/server/services/transform_generators/timeslice_metric.ts index b0e32f7094a2f..e2f305e68fee8 100644 --- a/x-pack/plugins/observability_solution/slo/server/services/transform_generators/timeslice_metric.ts +++ b/x-pack/plugins/observability_solution/slo/server/services/transform_generators/timeslice_metric.ts @@ -28,11 +28,11 @@ import { getFilterRange } from './common'; const INVALID_EQUATION_REGEX = /[^A-Z|+|\-|\s|\d+|\.|\(|\)|\/|\*|>|<|=|\?|\:|&|\!|\|]+/g; export class TimesliceMetricTransformGenerator extends TransformGenerator { - public async getTransformParams( - slo: SLODefinition, - spaceId: string, - dataViewService: DataViewsService - ): Promise { + constructor(spaceId: string, dataViewService: DataViewsService) { + super(spaceId, dataViewService); + } + + public async getTransformParams(slo: SLODefinition): Promise { if (!timesliceMetricIndicatorSchema.is(slo.indicator)) { throw new InvalidTransformError(`Cannot handle SLO of indicator type: ${slo.indicator.type}`); } @@ -40,7 +40,7 @@ export class TimesliceMetricTransformGenerator extends TransformGenerator { return getSLOTransformTemplate( this.buildTransformId(slo), this.buildDescription(slo), - await this.buildSource(slo, slo.indicator, dataViewService), + await this.buildSource(slo, slo.indicator), this.buildDestination(slo), this.buildCommonGroupBy(slo, slo.indicator.params.timestampField), this.buildAggregations(slo, slo.indicator), @@ -53,18 +53,12 @@ export class TimesliceMetricTransformGenerator extends TransformGenerator { return getSLOTransformId(slo.id, slo.revision); } - private async buildSource( - slo: SLODefinition, - indicator: TimesliceMetricIndicator, - dataViewService: DataViewsService - ) { - const dataView = await this.getIndicatorDataView({ - dataViewService, - dataViewId: indicator.params.index, - }); + private async buildSource(slo: SLODefinition, indicator: TimesliceMetricIndicator) { + const dataView = await this.getIndicatorDataView(indicator.params.dataViewId); + return { index: parseIndex(indicator.params.index), - runtime_mappings: this.buildCommonRuntimeMappings(slo, dataView), + runtime_mappings: this.buildCommonRuntimeMappings(dataView), query: { bool: { filter: [ diff --git a/x-pack/plugins/observability_solution/slo/server/services/transform_generators/transform_generator.test.ts b/x-pack/plugins/observability_solution/slo/server/services/transform_generators/transform_generator.test.ts index 9f07c6cfb5afa..e70d406d75396 100644 --- a/x-pack/plugins/observability_solution/slo/server/services/transform_generators/transform_generator.test.ts +++ b/x-pack/plugins/observability_solution/slo/server/services/transform_generators/transform_generator.test.ts @@ -7,50 +7,42 @@ import { createAPMTransactionErrorRateIndicator, createSLO } from '../fixtures/slo'; import { ApmTransactionErrorRateTransformGenerator } from './apm_transaction_error_rate'; +import { dataViewsService } from '@kbn/data-views-plugin/server/mocks'; -const generator = new ApmTransactionErrorRateTransformGenerator(); +const generator = new ApmTransactionErrorRateTransformGenerator('my-space-id', dataViewsService); describe('Transform Generator', () => { - it('builds empty runtime mappings without group by', async () => { - const slo = createSLO({ - id: 'irrelevant', - indicator: createAPMTransactionErrorRateIndicator(), - }); - const commonRuntime = generator.buildCommonRuntimeMappings(slo); - expect(commonRuntime).toMatchSnapshot(); - - const commonGroupBy = generator.buildCommonGroupBy(slo); - expect(commonGroupBy).toMatchSnapshot(); - }); - - it.each(['example', ['example']])( - 'builds common runtime mappings and group by with single group by', - async (groupBy) => { - const indicator = createAPMTransactionErrorRateIndicator(); + describe('buildCommonGroupBy', () => { + it('builds empty runtime mappings without group by', async () => { const slo = createSLO({ id: 'irrelevant', - groupBy, - indicator, + indicator: createAPMTransactionErrorRateIndicator(), }); - const commonRuntime = generator.buildCommonRuntimeMappings(slo); - expect(commonRuntime).toMatchSnapshot(); const commonGroupBy = generator.buildCommonGroupBy(slo); expect(commonGroupBy).toMatchSnapshot(); - } - ); - - it('builds common runtime mappings without multi group by', async () => { - const indicator = createAPMTransactionErrorRateIndicator(); - const slo = createSLO({ - id: 'irrelevant', - groupBy: ['example1', 'example2'], - indicator, }); - const commonRuntime = generator.buildCommonRuntimeMappings(slo); - expect(commonRuntime).toMatchSnapshot(); - const commonGroupBy = generator.buildCommonGroupBy(slo); - expect(commonGroupBy).toMatchSnapshot(); + it.each(['example', ['example'], ['example1', 'example2']])( + 'builds common groupBy with single group by', + async (groupBy) => { + const indicator = createAPMTransactionErrorRateIndicator(); + const slo = createSLO({ + id: 'irrelevant', + groupBy, + indicator, + }); + + const commonGroupBy = generator.buildCommonGroupBy(slo); + expect(commonGroupBy).toMatchSnapshot(); + } + ); + }); + + describe('buildCommonRuntimeMappings', () => { + it('builds empty runtime mappings without data view', async () => { + const runtimeMappings = generator.buildCommonRuntimeMappings(); + expect(runtimeMappings).toEqual({}); + }); }); }); diff --git a/x-pack/plugins/observability_solution/slo/server/services/transform_generators/transform_generator.ts b/x-pack/plugins/observability_solution/slo/server/services/transform_generators/transform_generator.ts index 25b0dc161661c..6c44471fd6566 100644 --- a/x-pack/plugins/observability_solution/slo/server/services/transform_generators/transform_generator.ts +++ b/x-pack/plugins/observability_solution/slo/server/services/transform_generators/transform_generator.ts @@ -9,23 +9,22 @@ import { MappingRuntimeFields, TransformPutTransformRequest, } from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; -import { ALL_VALUE, timeslicesBudgetingMethodSchema } from '@kbn/slo-schema'; import { DataView, DataViewsService } from '@kbn/data-views-plugin/common'; +import { ALL_VALUE, timeslicesBudgetingMethodSchema } from '@kbn/slo-schema'; import { TransformSettings } from '../../assets/transform_templates/slo_transform_template'; import { SLODefinition } from '../../domain/models'; export abstract class TransformGenerator { - public abstract getTransformParams( - slo: SLODefinition, - spaceId: string, - dataViewService: DataViewsService, - isServerless: boolean - ): Promise; + constructor( + protected spaceId: string, + protected dataViewService: DataViewsService, + protected isServerless: boolean = false + ) {} - public buildCommonRuntimeMappings(slo: SLODefinition, dataView?: DataView): MappingRuntimeFields { - return { - ...(dataView?.getRuntimeMappings?.() ?? {}), - }; + public abstract getTransformParams(slo: SLODefinition): Promise; + + public buildCommonRuntimeMappings(dataView?: DataView): MappingRuntimeFields { + return dataView?.getRuntimeMappings?.() ?? {}; } public buildDescription(slo: SLODefinition): string { @@ -71,17 +70,11 @@ export abstract class TransformGenerator { }; } - public async getIndicatorDataView({ - dataViewService, - dataViewId, - }: { - dataViewService: DataViewsService; - dataViewId?: string; - }): Promise { + public async getIndicatorDataView(dataViewId?: string): Promise { let dataView: DataView | undefined; if (dataViewId) { try { - dataView = await dataViewService.get(dataViewId); + dataView = await this.dataViewService.get(dataViewId); } catch (e) { // If the data view is not found, we will continue without it } diff --git a/x-pack/plugins/observability_solution/slo/server/services/transform_generators/transform_generators_factory.ts b/x-pack/plugins/observability_solution/slo/server/services/transform_generators/transform_generators_factory.ts new file mode 100644 index 0000000000000..1da2ce1eca4e5 --- /dev/null +++ b/x-pack/plugins/observability_solution/slo/server/services/transform_generators/transform_generators_factory.ts @@ -0,0 +1,45 @@ +/* + * 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 { DataViewsService } from '@kbn/data-views-plugin/server'; +import { + ApmTransactionDurationTransformGenerator, + ApmTransactionErrorRateTransformGenerator, + HistogramTransformGenerator, + KQLCustomTransformGenerator, + MetricCustomTransformGenerator, + SyntheticsAvailabilityTransformGenerator, + TimesliceMetricTransformGenerator, + TransformGenerator, +} from '.'; +import { IndicatorTypes } from '../../domain/models'; + +export function createTransformGenerators( + spaceId: string, + dataViewsService: DataViewsService, + isServerless: boolean +): Record { + return { + 'sli.apm.transactionDuration': new ApmTransactionDurationTransformGenerator( + spaceId, + dataViewsService + ), + 'sli.apm.transactionErrorRate': new ApmTransactionErrorRateTransformGenerator( + spaceId, + dataViewsService + ), + 'sli.synthetics.availability': new SyntheticsAvailabilityTransformGenerator( + spaceId, + dataViewsService, + isServerless + ), + 'sli.kql.custom': new KQLCustomTransformGenerator(spaceId, dataViewsService), + 'sli.metric.custom': new MetricCustomTransformGenerator(spaceId, dataViewsService), + 'sli.histogram.custom': new HistogramTransformGenerator(spaceId, dataViewsService), + 'sli.metric.timeslice': new TimesliceMetricTransformGenerator(spaceId, dataViewsService), + }; +} diff --git a/x-pack/plugins/observability_solution/slo/server/services/transform_manager.test.ts b/x-pack/plugins/observability_solution/slo/server/services/transform_manager.test.ts index e837db4e88dc2..aa0884860e8e0 100644 --- a/x-pack/plugins/observability_solution/slo/server/services/transform_manager.test.ts +++ b/x-pack/plugins/observability_solution/slo/server/services/transform_manager.test.ts @@ -44,15 +44,12 @@ describe('TransformManager', () => { it('throws when no generator exists for the slo indicator type', async () => { // @ts-ignore defining only a subset of the possible SLI const generators: Record = { - 'sli.apm.transactionDuration': new DummyTransformGenerator(), + 'sli.apm.transactionDuration': new DummyTransformGenerator(spaceId, dataViewsService), }; const service = new DefaultTransformManager( generators, scopedClusterClientMock, - loggerMock, - spaceId, - dataViewsService, - false + loggerMock ); await expect( @@ -63,15 +60,12 @@ describe('TransformManager', () => { it('throws when transform generator fails', async () => { // @ts-ignore defining only a subset of the possible SLI const generators: Record = { - 'sli.apm.transactionDuration': new FailTransformGenerator(), + 'sli.apm.transactionDuration': new FailTransformGenerator(spaceId, dataViewsService), }; const transformManager = new DefaultTransformManager( generators, scopedClusterClientMock, - loggerMock, - spaceId, - dataViewsService, - false + loggerMock ); await expect( @@ -85,15 +79,15 @@ describe('TransformManager', () => { it('installs the transform', async () => { // @ts-ignore defining only a subset of the possible SLI const generators: Record = { - 'sli.apm.transactionErrorRate': new ApmTransactionErrorRateTransformGenerator(), + 'sli.apm.transactionErrorRate': new ApmTransactionErrorRateTransformGenerator( + spaceId, + dataViewsService + ), }; const transformManager = new DefaultTransformManager( generators, scopedClusterClientMock, - loggerMock, - spaceId, - dataViewsService, - false + loggerMock ); const slo = createSLO({ indicator: createAPMTransactionErrorRateIndicator() }); @@ -110,15 +104,15 @@ describe('TransformManager', () => { it('previews the transform', async () => { // @ts-ignore defining only a subset of the possible SLI const generators: Record = { - 'sli.apm.transactionErrorRate': new ApmTransactionErrorRateTransformGenerator(), + 'sli.apm.transactionErrorRate': new ApmTransactionErrorRateTransformGenerator( + spaceId, + dataViewsService + ), }; const transformManager = new DefaultTransformManager( generators, scopedClusterClientMock, - loggerMock, - spaceId, - dataViewsService, - false + loggerMock ); await transformManager.preview('slo-transform-id'); @@ -133,15 +127,15 @@ describe('TransformManager', () => { it('starts the transform', async () => { // @ts-ignore defining only a subset of the possible SLI const generators: Record = { - 'sli.apm.transactionErrorRate': new ApmTransactionErrorRateTransformGenerator(), + 'sli.apm.transactionErrorRate': new ApmTransactionErrorRateTransformGenerator( + spaceId, + dataViewsService + ), }; const transformManager = new DefaultTransformManager( generators, scopedClusterClientMock, - loggerMock, - spaceId, - dataViewsService, - false + loggerMock ); await transformManager.start('slo-transform-id'); @@ -156,15 +150,15 @@ describe('TransformManager', () => { it('stops the transform', async () => { // @ts-ignore defining only a subset of the possible SLI const generators: Record = { - 'sli.apm.transactionErrorRate': new ApmTransactionErrorRateTransformGenerator(), + 'sli.apm.transactionErrorRate': new ApmTransactionErrorRateTransformGenerator( + spaceId, + dataViewsService + ), }; const transformManager = new DefaultTransformManager( generators, scopedClusterClientMock, - loggerMock, - spaceId, - dataViewsService, - false + loggerMock ); await transformManager.stop('slo-transform-id'); @@ -179,15 +173,15 @@ describe('TransformManager', () => { it('uninstalls the transform', async () => { // @ts-ignore defining only a subset of the possible SLI const generators: Record = { - 'sli.apm.transactionErrorRate': new ApmTransactionErrorRateTransformGenerator(), + 'sli.apm.transactionErrorRate': new ApmTransactionErrorRateTransformGenerator( + spaceId, + dataViewsService + ), }; const transformManager = new DefaultTransformManager( generators, scopedClusterClientMock, - loggerMock, - spaceId, - dataViewsService, - false + loggerMock ); await transformManager.uninstall('slo-transform-id'); @@ -203,15 +197,15 @@ describe('TransformManager', () => { ); // @ts-ignore defining only a subset of the possible SLI const generators: Record = { - 'sli.apm.transactionErrorRate': new ApmTransactionErrorRateTransformGenerator(), + 'sli.apm.transactionErrorRate': new ApmTransactionErrorRateTransformGenerator( + spaceId, + dataViewsService + ), }; const transformManager = new DefaultTransformManager( generators, scopedClusterClientMock, - loggerMock, - spaceId, - dataViewsService, - false + loggerMock ); await transformManager.uninstall('slo-transform-id'); @@ -224,21 +218,20 @@ describe('TransformManager', () => { }); class DummyTransformGenerator extends TransformGenerator { - async getTransformParams( - slo: SLODefinition, - spaceId: string, - dataViewService: DataViewsService - ): Promise { + constructor(spaceId: string, dataViewService: DataViewsService) { + super(spaceId, dataViewService); + } + async getTransformParams(slo: SLODefinition): Promise { return {} as TransformPutTransformRequest; } } class FailTransformGenerator extends TransformGenerator { - getTransformParams( - slo: SLODefinition, - spaceId: string, - dataViewService: DataViewsService - ): Promise { + constructor(spaceId: string, dataViewService: DataViewsService) { + super(spaceId, dataViewService); + } + + getTransformParams(slo: SLODefinition): Promise { throw new Error('Some error'); } } diff --git a/x-pack/plugins/observability_solution/slo/server/services/transform_manager.ts b/x-pack/plugins/observability_solution/slo/server/services/transform_manager.ts index aed9931822bdc..464d1f1aeaa59 100644 --- a/x-pack/plugins/observability_solution/slo/server/services/transform_manager.ts +++ b/x-pack/plugins/observability_solution/slo/server/services/transform_manager.ts @@ -5,11 +5,9 @@ * 2.0. */ -import { IScopedClusterClient, Logger } from '@kbn/core/server'; - import { TransformPutTransformRequest } from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; -import { DataViewsService } from '@kbn/data-views-plugin/server'; -import { SLODefinition, IndicatorTypes } from '../domain/models'; +import { IScopedClusterClient, Logger } from '@kbn/core/server'; +import { IndicatorTypes, SLODefinition } from '../domain/models'; import { SecurityException } from '../errors'; import { retryTransientEsErrors } from '../utils/retry'; import { TransformGenerator } from './transform_generators'; @@ -29,10 +27,7 @@ export class DefaultTransformManager implements TransformManager { constructor( private generators: Record, private scopedClusterClient: IScopedClusterClient, - private logger: Logger, - private spaceId: string, - private dataViewService: DataViewsService, - private isServerless: boolean + private logger: Logger ) {} async install(slo: SLODefinition): Promise { @@ -42,12 +37,7 @@ export class DefaultTransformManager implements TransformManager { throw new Error(`Unsupported indicator type [${slo.indicator.type}]`); } - const transformParams = await generator.getTransformParams( - slo, - this.spaceId, - this.dataViewService, - this.isServerless - ); + const transformParams = await generator.getTransformParams(slo); try { await retryTransientEsErrors( () => this.scopedClusterClient.asSecondaryAuthUser.transform.putTransform(transformParams), @@ -74,12 +64,7 @@ export class DefaultTransformManager implements TransformManager { throw new Error(`Unsupported indicator type [${slo.indicator.type}]`); } - return await generator.getTransformParams( - slo, - this.spaceId, - this.dataViewService, - this.isServerless - ); + return await generator.getTransformParams(slo); } async preview(transformId: string): Promise {