[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:
Chris Cowan 2020-10-29 13:55:50 -07:00 committed by GitHub
parent 2a38461817
commit eedf0673d3
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 99 additions and 2 deletions

View file

@ -30,6 +30,7 @@ export const MetricsAPIRequestRT = rt.intersection([
}),
rt.partial({
groupBy: rt.array(groupByRT),
modules: rt.array(rt.string),
afterKey: rt.union([rt.null, afterKeyObjectRT]),
limit: rt.union([rt.number, rt.null, rt.undefined]),
filters: rt.array(rt.object),

View file

@ -25,6 +25,7 @@ import {
import { initGetK8sAnomaliesRoute } from './routes/infra_ml';
import { initGetHostsAnomaliesRoute } from './routes/infra_ml';
import { initMetricExplorerRoute } from './routes/metrics_explorer';
import { initMetricsAPIRoute } from './routes/metrics_api';
import { initMetadataRoute } from './routes/metadata';
import { initSnapshotRoute } from './routes/snapshot';
import { initNodeDetailsRoute } from './routes/node_details';
@ -74,6 +75,7 @@ export const initInfraServer = (libs: InfraBackendLibs) => {
initLogEntriesSummaryHighlightsRoute(libs);
initLogEntriesItemRoute(libs);
initMetricExplorerRoute(libs);
initMetricsAPIRoute(libs);
initMetadataRoute(libs);
initInventoryMetaRoute(libs);
initLogSourceConfigurationRoutes(libs);

View file

@ -17,11 +17,20 @@ import { EMPTY_RESPONSE } from './constants';
import { createAggregations } from './lib/create_aggregations';
import { convertHistogramBucketsToTimeseries } from './lib/convert_histogram_buckets_to_timeseries';
import { calculateBucketSize } from './lib/calculate_bucket_size';
import { calculatedInterval } from './lib/calculate_interval';
export const query = async (
search: ESSearchClient,
options: MetricsAPIRequest
rawOptions: MetricsAPIRequest
): 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 filter: Array<Record<string, any>> = [
{
@ -35,6 +44,7 @@ export const query = async (
},
...(options.groupBy?.map((field) => ({ exists: { field } })) ?? []),
];
const params = {
allowNoIndices: true,
ignoreUnavailable: true,
@ -70,7 +80,7 @@ export const query = async (
throw new Error('Aggregations should be present.');
}
const { bucketSize } = calculateBucketSize(options.timerange);
const { bucketSize } = calculateBucketSize({ ...options.timerange, interval });
if (hasGroupBy && GroupingResponseRT.is(response.aggregations)) {
const { groupings } = response.aggregations;

View file

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

View 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,
});
}
}
);
};