chore(slo): refactor transform generators and managers dependency injection (#201031)

This commit is contained in:
Kevin Delemme 2024-11-22 08:52:18 -05:00 committed by GitHub
parent 455a075495
commit 204666310f
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
23 changed files with 642 additions and 677 deletions

View file

@ -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<IndicatorTypes, TransformGenerator> = {
'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,

View file

@ -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,

View file

@ -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 {

View file

@ -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);

View file

@ -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<TransformPutTransformRequest> {
constructor(spaceId: string, dataViewService: DataViewsService) {
super(spaceId, dataViewService);
}
public async getTransformParams(slo: SLODefinition): Promise<TransformPutTransformRequest> {
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: [

View file

@ -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);

View file

@ -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<TransformPutTransformRequest> {
constructor(spaceId: string, dataViewService: DataViewsService) {
super(spaceId, dataViewService);
}
public async getTransformParams(slo: SLODefinition): Promise<TransformPutTransformRequest> {
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: [

View file

@ -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);

View file

@ -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<TransformPutTransformRequest> {
constructor(spaceId: string, dataViewService: DataViewsService) {
super(spaceId, dataViewService);
}
public async getTransformParams(slo: SLODefinition): Promise<TransformPutTransformRequest> {
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: [

View file

@ -14,3 +14,4 @@ export * from './metric_custom';
export * from './histogram';
export * from './timeslice_metric';
export * from './common';
export * from './transform_generators_factory';

View file

@ -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);

View file

@ -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<TransformPutTransformRequest> {
constructor(spaceId: string, dataViewService: DataViewsService) {
super(spaceId, dataViewService);
}
public async getTransformParams(slo: SLODefinition): Promise<TransformPutTransformRequest> {
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: [

View file

@ -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);

View file

@ -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<TransformPutTransformRequest> {
constructor(spaceId: string, dataViewService: DataViewsService) {
super(spaceId, dataViewService);
}
public async getTransformParams(slo: SLODefinition): Promise<TransformPutTransformRequest> {
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: [

View file

@ -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',
},
},
})
);
});
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',
},
},
})
describe('when serverless is disabled', () => {
const generator = new SyntheticsAvailabilityTransformGenerator(
SPACE_ID,
dataViewsService,
false
);
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) => {
it('returns the expected transform params', 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).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);
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,
expect(transform.pivot?.group_by).toEqual(
expect.objectContaining({
'slo.groupings.host.name': {
terms: {
field: 'host.name',
},
},
})
);
});
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',
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.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.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('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,
},
});
});
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);
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 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);
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 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',
},
});
});
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("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(rangeFilter).toEqual({
range: {
'@timestamp': {
gte: 'now-300s/m', // 2m + 2m + 60s
},
},
})
});
});
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');
});
});
describe('when serverless is enabled', () => {
const generator = new SyntheticsAvailabilityTransformGenerator(
SPACE_ID,
dataViewsService,
true
);
});
it('filters by summary.final_attempt', async () => {
const slo = createSLO({ id: 'irrelevant', indicator: createSyntheticsAvailabilityIndicator() });
const transform = await generator.getTransformParams(slo, spaceId, dataViewsService, false);
it("overrides the syncField with '@timestamp'", async () => {
const slo = createSLO({
indicator: createSyntheticsAvailabilityIndicator(),
});
const transform = await generator.getTransformParams(slo);
expect(transform.source.query?.bool?.filter).toContainEqual({
term: {
'summary.final_attempt': true,
},
});
});
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, 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 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, 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 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, spaceId, dataViewsService, false);
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',
},
});
});
it('filters by space', async () => {
const slo = createSLO({ id: 'irrelevant', indicator: createSyntheticsAvailabilityIndicator() });
const transform = await generator.getTransformParams(slo, spaceId, dataViewsService, false);
expect(transform.source.query?.bool?.filter).toContainEqual({
term: {
'meta.space_id': spaceId,
},
});
});
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, 'default', dataViewsService, false);
// @ts-ignore
const rangeFilter = transform.source.query.bool.filter.find((f) => 'range' in f);
expect(rangeFilter).toEqual({
range: {
'@timestamp': {
gte: 'now-300s/m', // 2m + 2m + 60s
},
},
expect(transform.sync?.time?.field).toEqual('@timestamp');
});
});
});

View file

@ -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<TransformPutTransformRequest> {
constructor(spaceId: string, dataViewService: DataViewsService, isServerless: boolean) {
super(spaceId, dataViewService, isServerless);
}
public async getTransformParams(slo: SLODefinition): Promise<TransformPutTransformRequest> {
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,

View file

@ -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);

View file

@ -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<TransformPutTransformRequest> {
constructor(spaceId: string, dataViewService: DataViewsService) {
super(spaceId, dataViewService);
}
public async getTransformParams(slo: SLODefinition): Promise<TransformPutTransformRequest> {
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: [

View file

@ -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({});
});
});
});

View file

@ -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<TransformPutTransformRequest>;
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<TransformPutTransformRequest>;
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<DataView | undefined> {
public async getIndicatorDataView(dataViewId?: string): Promise<DataView | undefined> {
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
}

View file

@ -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<IndicatorTypes, TransformGenerator> {
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),
};
}

View file

@ -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<IndicatorTypes, TransformGenerator> = {
'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<IndicatorTypes, TransformGenerator> = {
'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<IndicatorTypes, TransformGenerator> = {
'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<IndicatorTypes, TransformGenerator> = {
'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<IndicatorTypes, TransformGenerator> = {
'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<IndicatorTypes, TransformGenerator> = {
'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<IndicatorTypes, TransformGenerator> = {
'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<IndicatorTypes, TransformGenerator> = {
'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<TransformPutTransformRequest> {
constructor(spaceId: string, dataViewService: DataViewsService) {
super(spaceId, dataViewService);
}
async getTransformParams(slo: SLODefinition): Promise<TransformPutTransformRequest> {
return {} as TransformPutTransformRequest;
}
}
class FailTransformGenerator extends TransformGenerator {
getTransformParams(
slo: SLODefinition,
spaceId: string,
dataViewService: DataViewsService
): Promise<TransformPutTransformRequest> {
constructor(spaceId: string, dataViewService: DataViewsService) {
super(spaceId, dataViewService);
}
getTransformParams(slo: SLODefinition): Promise<TransformPutTransformRequest> {
throw new Error('Some error');
}
}

View file

@ -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<IndicatorTypes, TransformGenerator>,
private scopedClusterClient: IScopedClusterClient,
private logger: Logger,
private spaceId: string,
private dataViewService: DataViewsService,
private isServerless: boolean
private logger: Logger
) {}
async install(slo: SLODefinition): Promise<TransformId> {
@ -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<void> {