[Metrics UI] Add endpoint for Metrics API (#81693)
* [Metrics UI] Add endpoint for Metrics API * Adding the ability to caculate the interval based on a module * fixing types Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com>
This commit is contained in:
parent
2a38461817
commit
eedf0673d3
|
@ -30,6 +30,7 @@ export const MetricsAPIRequestRT = rt.intersection([
|
||||||
}),
|
}),
|
||||||
rt.partial({
|
rt.partial({
|
||||||
groupBy: rt.array(groupByRT),
|
groupBy: rt.array(groupByRT),
|
||||||
|
modules: rt.array(rt.string),
|
||||||
afterKey: rt.union([rt.null, afterKeyObjectRT]),
|
afterKey: rt.union([rt.null, afterKeyObjectRT]),
|
||||||
limit: rt.union([rt.number, rt.null, rt.undefined]),
|
limit: rt.union([rt.number, rt.null, rt.undefined]),
|
||||||
filters: rt.array(rt.object),
|
filters: rt.array(rt.object),
|
||||||
|
|
|
@ -25,6 +25,7 @@ import {
|
||||||
import { initGetK8sAnomaliesRoute } from './routes/infra_ml';
|
import { initGetK8sAnomaliesRoute } from './routes/infra_ml';
|
||||||
import { initGetHostsAnomaliesRoute } from './routes/infra_ml';
|
import { initGetHostsAnomaliesRoute } from './routes/infra_ml';
|
||||||
import { initMetricExplorerRoute } from './routes/metrics_explorer';
|
import { initMetricExplorerRoute } from './routes/metrics_explorer';
|
||||||
|
import { initMetricsAPIRoute } from './routes/metrics_api';
|
||||||
import { initMetadataRoute } from './routes/metadata';
|
import { initMetadataRoute } from './routes/metadata';
|
||||||
import { initSnapshotRoute } from './routes/snapshot';
|
import { initSnapshotRoute } from './routes/snapshot';
|
||||||
import { initNodeDetailsRoute } from './routes/node_details';
|
import { initNodeDetailsRoute } from './routes/node_details';
|
||||||
|
@ -74,6 +75,7 @@ export const initInfraServer = (libs: InfraBackendLibs) => {
|
||||||
initLogEntriesSummaryHighlightsRoute(libs);
|
initLogEntriesSummaryHighlightsRoute(libs);
|
||||||
initLogEntriesItemRoute(libs);
|
initLogEntriesItemRoute(libs);
|
||||||
initMetricExplorerRoute(libs);
|
initMetricExplorerRoute(libs);
|
||||||
|
initMetricsAPIRoute(libs);
|
||||||
initMetadataRoute(libs);
|
initMetadataRoute(libs);
|
||||||
initInventoryMetaRoute(libs);
|
initInventoryMetaRoute(libs);
|
||||||
initLogSourceConfigurationRoutes(libs);
|
initLogSourceConfigurationRoutes(libs);
|
||||||
|
|
|
@ -17,11 +17,20 @@ import { EMPTY_RESPONSE } from './constants';
|
||||||
import { createAggregations } from './lib/create_aggregations';
|
import { createAggregations } from './lib/create_aggregations';
|
||||||
import { convertHistogramBucketsToTimeseries } from './lib/convert_histogram_buckets_to_timeseries';
|
import { convertHistogramBucketsToTimeseries } from './lib/convert_histogram_buckets_to_timeseries';
|
||||||
import { calculateBucketSize } from './lib/calculate_bucket_size';
|
import { calculateBucketSize } from './lib/calculate_bucket_size';
|
||||||
|
import { calculatedInterval } from './lib/calculate_interval';
|
||||||
|
|
||||||
export const query = async (
|
export const query = async (
|
||||||
search: ESSearchClient,
|
search: ESSearchClient,
|
||||||
options: MetricsAPIRequest
|
rawOptions: MetricsAPIRequest
|
||||||
): Promise<MetricsAPIResponse> => {
|
): Promise<MetricsAPIResponse> => {
|
||||||
|
const interval = await calculatedInterval(search, rawOptions);
|
||||||
|
const options = {
|
||||||
|
...rawOptions,
|
||||||
|
timerange: {
|
||||||
|
...rawOptions.timerange,
|
||||||
|
interval,
|
||||||
|
},
|
||||||
|
};
|
||||||
const hasGroupBy = Array.isArray(options.groupBy) && options.groupBy.length > 0;
|
const hasGroupBy = Array.isArray(options.groupBy) && options.groupBy.length > 0;
|
||||||
const filter: Array<Record<string, any>> = [
|
const filter: Array<Record<string, any>> = [
|
||||||
{
|
{
|
||||||
|
@ -35,6 +44,7 @@ export const query = async (
|
||||||
},
|
},
|
||||||
...(options.groupBy?.map((field) => ({ exists: { field } })) ?? []),
|
...(options.groupBy?.map((field) => ({ exists: { field } })) ?? []),
|
||||||
];
|
];
|
||||||
|
|
||||||
const params = {
|
const params = {
|
||||||
allowNoIndices: true,
|
allowNoIndices: true,
|
||||||
ignoreUnavailable: true,
|
ignoreUnavailable: true,
|
||||||
|
@ -70,7 +80,7 @@ export const query = async (
|
||||||
throw new Error('Aggregations should be present.');
|
throw new Error('Aggregations should be present.');
|
||||||
}
|
}
|
||||||
|
|
||||||
const { bucketSize } = calculateBucketSize(options.timerange);
|
const { bucketSize } = calculateBucketSize({ ...options.timerange, interval });
|
||||||
|
|
||||||
if (hasGroupBy && GroupingResponseRT.is(response.aggregations)) {
|
if (hasGroupBy && GroupingResponseRT.is(response.aggregations)) {
|
||||||
const { groupings } = response.aggregations;
|
const { groupings } = response.aggregations;
|
||||||
|
|
|
@ -0,0 +1,34 @@
|
||||||
|
/*
|
||||||
|
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||||
|
* or more contributor license agreements. Licensed under the Elastic License;
|
||||||
|
* you may not use this file except in compliance with the Elastic License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { isArray, isNumber } from 'lodash';
|
||||||
|
import { MetricsAPIRequest } from '../../../../common/http_api';
|
||||||
|
import { ESSearchClient } from '../types';
|
||||||
|
import { calculateMetricInterval } from '../../../utils/calculate_metric_interval';
|
||||||
|
|
||||||
|
export const calculatedInterval = async (search: ESSearchClient, options: MetricsAPIRequest) => {
|
||||||
|
const useModuleInterval =
|
||||||
|
options.timerange.interval === 'modules' &&
|
||||||
|
isArray(options.modules) &&
|
||||||
|
options.modules.length > 0;
|
||||||
|
|
||||||
|
const calcualatedInterval = useModuleInterval
|
||||||
|
? await calculateMetricInterval(
|
||||||
|
search,
|
||||||
|
{
|
||||||
|
indexPattern: options.indexPattern,
|
||||||
|
timestampField: options.timerange.field,
|
||||||
|
timerange: { from: options.timerange.from, to: options.timerange.to },
|
||||||
|
},
|
||||||
|
options.modules
|
||||||
|
)
|
||||||
|
: false;
|
||||||
|
|
||||||
|
const defaultInterval =
|
||||||
|
options.timerange.interval === 'modules' ? 'auto' : options.timerange.interval;
|
||||||
|
|
||||||
|
return isNumber(calcualatedInterval) ? `>=${calcualatedInterval}s` : defaultInterval;
|
||||||
|
};
|
50
x-pack/plugins/infra/server/routes/metrics_api/index.ts
Normal file
50
x-pack/plugins/infra/server/routes/metrics_api/index.ts
Normal file
|
@ -0,0 +1,50 @@
|
||||||
|
/*
|
||||||
|
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||||
|
* or more contributor license agreements. Licensed under the Elastic License;
|
||||||
|
* you may not use this file except in compliance with the Elastic License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import Boom from 'boom';
|
||||||
|
import { pipe } from 'fp-ts/lib/pipeable';
|
||||||
|
import { fold } from 'fp-ts/lib/Either';
|
||||||
|
import { identity } from 'fp-ts/lib/function';
|
||||||
|
import { schema } from '@kbn/config-schema';
|
||||||
|
import { InfraBackendLibs } from '../../lib/infra_types';
|
||||||
|
import { throwErrors } from '../../../common/runtime_types';
|
||||||
|
import { createSearchClient } from '../../lib/create_search_client';
|
||||||
|
import { query } from '../../lib/metrics';
|
||||||
|
import { MetricsAPIRequestRT, MetricsAPIResponseRT } from '../../../common/http_api';
|
||||||
|
|
||||||
|
const escapeHatch = schema.object({}, { unknowns: 'allow' });
|
||||||
|
|
||||||
|
export const initMetricsAPIRoute = (libs: InfraBackendLibs) => {
|
||||||
|
const { framework } = libs;
|
||||||
|
framework.registerRoute(
|
||||||
|
{
|
||||||
|
method: 'post',
|
||||||
|
path: '/api/infra/metrics_api',
|
||||||
|
validate: {
|
||||||
|
body: escapeHatch,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
async (requestContext, request, response) => {
|
||||||
|
try {
|
||||||
|
const options = pipe(
|
||||||
|
MetricsAPIRequestRT.decode(request.body),
|
||||||
|
fold(throwErrors(Boom.badRequest), identity)
|
||||||
|
);
|
||||||
|
|
||||||
|
const client = createSearchClient(requestContext, framework);
|
||||||
|
const metricsApiResponse = await query(client, options);
|
||||||
|
|
||||||
|
return response.ok({
|
||||||
|
body: MetricsAPIResponseRT.encode(metricsApiResponse),
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
return response.internalError({
|
||||||
|
body: error.message,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
};
|
Loading…
Reference in a new issue