"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.GetPreviewData = void 0; var _esQuery = require("@kbn/es-query"); var _sloSchema = require("@kbn/slo-schema"); var _std = require("@kbn/std"); var _services = require("../../domain/services"); var _errors = require("../../errors"); var _aggregations = require("./aggregations"); /* * 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. */ class GetPreviewData { constructor(esClient) { this.esClient = esClient; } async getAPMTransactionDurationPreviewData(indicator) { var _result$aggregations; const filter = []; if (indicator.params.service !== _sloSchema.ALL_VALUE) filter.push({ match: { 'service.name': indicator.params.service } }); if (indicator.params.environment !== _sloSchema.ALL_VALUE) filter.push({ match: { 'service.environment': indicator.params.environment } }); if (indicator.params.transactionName !== _sloSchema.ALL_VALUE) filter.push({ match: { 'transaction.name': indicator.params.transactionName } }); if (indicator.params.transactionType !== _sloSchema.ALL_VALUE) filter.push({ match: { 'transaction.type': indicator.params.transactionType } }); if (!!indicator.params.filter) filter.push(getElastichsearchQueryOrThrow(indicator.params.filter)); const truncatedThreshold = Math.trunc(indicator.params.threshold * 1000); const result = await this.esClient.search({ index: indicator.params.index, query: { bool: { filter: [{ range: { '@timestamp': { gte: 'now-60m' } } }, { terms: { 'processor.event': ['metric'] } }, { term: { 'metricset.name': 'transaction' } }, { exists: { field: 'transaction.duration.histogram' } }, ...filter] } }, aggs: { perMinute: { date_histogram: { field: '@timestamp', fixed_interval: '1m' }, aggs: { _good: { range: { field: 'transaction.duration.histogram', ranges: [{ to: truncatedThreshold }] } }, good: { bucket_script: { buckets_path: { _good: `_good['*-${truncatedThreshold}.0']>_count` }, script: 'params._good' } }, total: { value_count: { field: 'transaction.duration.histogram' } } } } } }); // @ts-ignore buckets is not improperly typed return (_result$aggregations = result.aggregations) === null || _result$aggregations === void 0 ? void 0 : _result$aggregations.perMinute.buckets.map(bucket => ({ date: bucket.key_as_string, sliValue: !!bucket.good && !!bucket.total ? (0, _services.computeSLI)(bucket.good.value, bucket.total.value) : null })); } async getAPMTransactionErrorPreviewData(indicator) { var _result$aggregations2; const filter = []; if (indicator.params.service !== _sloSchema.ALL_VALUE) filter.push({ match: { 'service.name': indicator.params.service } }); if (indicator.params.environment !== _sloSchema.ALL_VALUE) filter.push({ match: { 'service.environment': indicator.params.environment } }); if (indicator.params.transactionName !== _sloSchema.ALL_VALUE) filter.push({ match: { 'transaction.name': indicator.params.transactionName } }); if (indicator.params.transactionType !== _sloSchema.ALL_VALUE) filter.push({ match: { 'transaction.type': indicator.params.transactionType } }); if (!!indicator.params.filter) filter.push(getElastichsearchQueryOrThrow(indicator.params.filter)); const result = await this.esClient.search({ index: indicator.params.index, query: { bool: { filter: [{ range: { '@timestamp': { gte: 'now-60m' } } }, { term: { 'metricset.name': 'transaction' } }, { terms: { 'event.outcome': ['success', 'failure'] } }, ...filter] } }, aggs: { perMinute: { date_histogram: { field: '@timestamp', fixed_interval: '1m' }, aggs: { good: { filter: { bool: { should: { match: { 'event.outcome': 'success' } } } } }, total: { filter: { match_all: {} } } } } } }); // @ts-ignore buckets is not improperly typed return (_result$aggregations2 = result.aggregations) === null || _result$aggregations2 === void 0 ? void 0 : _result$aggregations2.perMinute.buckets.map(bucket => ({ date: bucket.key_as_string, sliValue: !!bucket.good && !!bucket.total ? (0, _services.computeSLI)(bucket.good.doc_count, bucket.total.doc_count) : null })); } async getHistogramPreviewData(indicator) { var _result$aggregations3; const getHistogramIndicatorAggregations = new _aggregations.GetHistogramIndicatorAggregation(indicator); const filterQuery = getElastichsearchQueryOrThrow(indicator.params.filter); const timestampField = indicator.params.timestampField; const options = { index: indicator.params.index, query: { bool: { filter: [{ range: { [timestampField]: { gte: 'now-60m' } } }, filterQuery] } }, aggs: { perMinute: { date_histogram: { field: timestampField, fixed_interval: '1m' }, aggs: { ...getHistogramIndicatorAggregations.execute({ type: 'good', aggregationKey: 'good' }), ...getHistogramIndicatorAggregations.execute({ type: 'total', aggregationKey: 'total' }) } } } }; const result = await this.esClient.search(options); // @ts-ignore buckets is not improperly typed return (_result$aggregations3 = result.aggregations) === null || _result$aggregations3 === void 0 ? void 0 : _result$aggregations3.perMinute.buckets.map(bucket => ({ date: bucket.key_as_string, sliValue: !!bucket.good && !!bucket.total ? (0, _services.computeSLI)(bucket.good.value, bucket.total.value) : null })); } async getCustomMetricPreviewData(indicator) { var _result$aggregations4; const timestampField = indicator.params.timestampField; const filterQuery = getElastichsearchQueryOrThrow(indicator.params.filter); const getCustomMetricIndicatorAggregation = new _aggregations.GetCustomMetricIndicatorAggregation(indicator); const result = await this.esClient.search({ index: indicator.params.index, query: { bool: { filter: [{ range: { [timestampField]: { gte: 'now-60m' } } }, filterQuery] } }, aggs: { perMinute: { date_histogram: { field: timestampField, fixed_interval: '1m' }, aggs: { ...getCustomMetricIndicatorAggregation.execute({ type: 'good', aggregationKey: 'good' }), ...getCustomMetricIndicatorAggregation.execute({ type: 'total', aggregationKey: 'total' }) } } } }); // @ts-ignore buckets is not improperly typed return (_result$aggregations4 = result.aggregations) === null || _result$aggregations4 === void 0 ? void 0 : _result$aggregations4.perMinute.buckets.map(bucket => ({ date: bucket.key_as_string, sliValue: !!bucket.good && !!bucket.total ? (0, _services.computeSLI)(bucket.good.value, bucket.total.value) : null })); } async getCustomKQLPreviewData(indicator) { var _result$aggregations5; const filterQuery = getElastichsearchQueryOrThrow(indicator.params.filter); const goodQuery = getElastichsearchQueryOrThrow(indicator.params.good); const totalQuery = getElastichsearchQueryOrThrow(indicator.params.total); const timestampField = indicator.params.timestampField; const result = await this.esClient.search({ index: indicator.params.index, query: { bool: { filter: [{ range: { [timestampField]: { gte: 'now-60m' } } }, filterQuery] } }, aggs: { perMinute: { date_histogram: { field: timestampField, fixed_interval: '1m' }, aggs: { good: { filter: goodQuery }, total: { filter: totalQuery } } } } }); // @ts-ignore buckets is not improperly typed return (_result$aggregations5 = result.aggregations) === null || _result$aggregations5 === void 0 ? void 0 : _result$aggregations5.perMinute.buckets.map(bucket => ({ date: bucket.key_as_string, sliValue: !!bucket.good && !!bucket.total ? (0, _services.computeSLI)(bucket.good.doc_count, bucket.total.doc_count) : null })); } async execute(params) { try { const type = params.indicator.type; switch (type) { case 'sli.apm.transactionDuration': return this.getAPMTransactionDurationPreviewData(params.indicator); case 'sli.apm.transactionErrorRate': return this.getAPMTransactionErrorPreviewData(params.indicator); case 'sli.kql.custom': return this.getCustomKQLPreviewData(params.indicator); case 'sli.histogram.custom': return this.getHistogramPreviewData(params.indicator); case 'sli.metric.custom': return this.getCustomMetricPreviewData(params.indicator); default: (0, _std.assertNever)(type); } } catch (err) { return []; } } } exports.GetPreviewData = GetPreviewData; function getElastichsearchQueryOrThrow(kuery = '') { try { return (0, _esQuery.toElasticsearchQuery)((0, _esQuery.fromKueryExpression)(kuery)); } catch (err) { throw new _errors.InvalidQueryError(`Invalid kuery: ${kuery}`); } }