[Stack Monitoring] Convert APM routes to TypeScript (#130097)

Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com>
Co-authored-by: Felix Stürmer <felix.stuermer@elastic.co>
This commit is contained in:
Kerry Gallagher 2022-04-20 16:28:34 +01:00 committed by GitHub
parent 473781ad06
commit 8c991cfaef
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
44 changed files with 286 additions and 109 deletions

View file

@ -0,0 +1,10 @@
/*
* 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.
*/
export * from './post_apm_instance';
export * from './post_apm_instances';
export * from './post_apm_overview';

View file

@ -0,0 +1,31 @@
/*
* 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 * as rt from 'io-ts';
export const postApmInstanceRequestParamsRT = rt.type({
clusterUuid: rt.string,
apmUuid: rt.string,
});
export const postApmInstanceRequestPayloadRT = rt.intersection([
rt.partial({
ccs: rt.string,
}),
rt.type({
timeRange: rt.type({
min: rt.string,
max: rt.string,
}),
}),
]);
export type PostApmInstanceRequestPayload = rt.TypeOf<typeof postApmInstanceRequestPayloadRT>;
export const postApmInstanceResponsePayloadRT = rt.type({
data: rt.string,
});

View file

@ -0,0 +1,30 @@
/*
* 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 * as rt from 'io-ts';
export const postApmInstancesRequestParamsRT = rt.type({
clusterUuid: rt.string,
});
export const postApmInstancesRequestPayloadRT = rt.intersection([
rt.partial({
ccs: rt.string,
}),
rt.type({
timeRange: rt.type({
min: rt.string,
max: rt.string,
}),
}),
]);
export type PostApmInstancesRequestPayload = rt.TypeOf<typeof postApmInstancesRequestPayloadRT>;
export const postApmInstancesResponsePayloadRT = rt.type({
data: rt.string,
});

View file

@ -0,0 +1,30 @@
/*
* 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 * as rt from 'io-ts';
export const postApmOverviewRequestParamsRT = rt.type({
clusterUuid: rt.string,
});
export const postApmOverviewRequestPayloadRT = rt.intersection([
rt.partial({
ccs: rt.string,
}),
rt.type({
timeRange: rt.type({
min: rt.string,
max: rt.string,
}),
}),
]);
export type PostApmOverviewRequestPayload = rt.TypeOf<typeof postApmOverviewRequestPayloadRT>;
export const postApmOverviewResponsePayloadRT = rt.type({
data: rt.string,
});

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
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
import { Context, IntersectionType, UnionType, ValidationError } from 'io-ts';
const getErrorPath = ([first, ...rest]: Context): string[] => {
if (typeof first === 'undefined') {
return [];
} else if (first.type instanceof IntersectionType) {
const [, ...next] = rest;
return getErrorPath(next);
} else if (first.type instanceof UnionType) {
const [, ...next] = rest;
return [first.key, ...getErrorPath(next)];
}
return [first.key, ...getErrorPath(rest)];
};
const getErrorType = ({ context }: ValidationError) =>
context[context.length - 1]?.type?.name ?? 'unknown';
const formatError = (error: ValidationError) =>
error.message ??
`in ${getErrorPath(error.context).join('/')}: ${JSON.stringify(
error.value
)} does not match expected type ${getErrorType(error)}`;
export const formatErrors = (errors: ValidationError[]) =>
`Failed to validate: \n${errors.map((error) => ` ${formatError(error)}`).join('\n')}`;

View file

@ -21,8 +21,8 @@ export async function getTimeOfLastEvent({
req: LegacyRequest;
callWithRequest: (_req: any, endpoint: string, params: any) => Promise<ElasticsearchResponse>;
apmIndexPattern: string;
start: number;
end: number;
start?: number;
end?: number;
clusterUuid: string;
}) {
const params = {

View file

@ -85,8 +85,8 @@ export async function getApmInfo(
}: {
clusterUuid: string;
apmUuid: string;
start: number;
end: number;
start?: number;
end?: number;
}
) {
checkParam(apmIndexPattern, 'apmIndexPattern in beats/getBeatSummary');

View file

@ -8,14 +8,14 @@
// @ts-ignore
import { checkParam } from '../error_missing_required';
import { STANDALONE_CLUSTER_CLUSTER_UUID } from '../../../common/constants';
import { ElasticsearchResponse, ElasticsearchModifiedSource } from '../../../common/types/es';
import { LegacyRequest } from '../../types';
import { ElasticsearchResponse } from '../../../common/types/es';
import { LegacyRequest, Cluster } from '../../types';
import { getNewIndexPatterns } from './get_index_patterns';
import { Globals } from '../../static_globals';
async function findSupportedBasicLicenseCluster(
req: LegacyRequest,
clusters: ElasticsearchModifiedSource[],
clusters: Cluster[],
ccs: string,
kibanaUuid: string,
serverLog: (message: string) => void
@ -90,7 +90,7 @@ async function findSupportedBasicLicenseCluster(
*/
export function flagSupportedClusters(req: LegacyRequest, ccs: string) {
const serverLog = (message: string) => req.getLogger('supported-clusters').debug(message);
const flagAllSupported = (clusters: ElasticsearchModifiedSource[]) => {
const flagAllSupported = (clusters: Cluster[]) => {
clusters.forEach((cluster) => {
if (cluster.license || cluster.elasticsearch?.cluster?.stats?.license) {
cluster.isSupported = true;
@ -99,7 +99,7 @@ export function flagSupportedClusters(req: LegacyRequest, ccs: string) {
return clusters;
};
return async function (clusters: ElasticsearchModifiedSource[]) {
return async function (clusters: Cluster[]) {
// Standalone clusters are automatically supported in the UI so ignore those for
// our calculations here
let linkedClusterCount = 0;

View file

@ -0,0 +1,27 @@
/*
* 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 type { RouteValidationFunction } from '@kbn/core/server';
import { fold } from 'fp-ts/lib/Either';
import { pipe } from 'fp-ts/lib/pipeable';
import { Errors, Type } from 'io-ts';
import { formatErrors } from '../../common/runtime_types';
type ValdidationResult<Value> = ReturnType<RouteValidationFunction<Value>>;
export const createValidationFunction =
<DecodedValue, EncodedValue, InputValue>(
runtimeType: Type<DecodedValue, EncodedValue, InputValue>
): RouteValidationFunction<DecodedValue> =>
(inputValue, { badRequest, ok }) =>
pipe(
runtimeType.decode(inputValue),
fold<Errors, DecodedValue, ValdidationResult<DecodedValue>>(
(errors: Errors) => badRequest(formatErrors(errors)),
(result: DecodedValue) => ok(result)
)
);

View file

@ -13,8 +13,8 @@ import { createQuery } from '../create_query';
// @ts-ignore
import { ElasticsearchMetric } from '../metrics';
import { ML_SUPPORTED_LICENSES } from '../../../common/constants';
import { ElasticsearchResponse, ElasticsearchSource } from '../../../common/types/es';
import { LegacyRequest } from '../../types';
import { ElasticsearchResponse } from '../../../common/types/es';
import { LegacyRequest, Cluster } from '../../types';
import { getNewIndexPatterns } from '../cluster/get_index_patterns';
import { Globals } from '../../static_globals';
@ -100,7 +100,7 @@ export function getMlJobs(req: LegacyRequest) {
* cardinality isn't guaranteed to be accurate is the issue
* but it will be as long as the precision threshold is >= the actual value
*/
export function getMlJobsForCluster(req: LegacyRequest, cluster: ElasticsearchSource, ccs: string) {
export function getMlJobsForCluster(req: LegacyRequest, cluster: Cluster, ccs: string) {
const license = cluster.license ?? cluster.elasticsearch?.cluster?.stats?.license ?? {};
if (license.status === 'active' && includes(ML_SUPPORTED_LICENSES, license.type)) {

View file

@ -22,6 +22,7 @@ import {
} from '@kbn/core/server';
import { get } from 'lodash';
import { DEFAULT_APP_CATEGORIES } from '@kbn/core/server';
import { RouteMethod } from '@kbn/core/server';
import {
KIBANA_MONITORING_LOGGING_TAG,
KIBANA_STATS_TYPE_MONITORING,
@ -50,6 +51,7 @@ import {
PluginsSetup,
PluginsStart,
RequestHandlerContextMonitoringPlugin,
MonitoringRouteConfig,
} from './types';
// This is used to test the version of kibana
@ -314,7 +316,9 @@ export class MonitoringPlugin
return {
config,
log: this.log,
route: (options: any) => {
route: <Params = any, Query = any, Body = any, Method extends RouteMethod = any>(
options: MonitoringRouteConfig<Params, Query, Body, Method>
) => {
const method = options.method;
const handler = async (
context: RequestHandlerContextMonitoringPlugin,
@ -389,18 +393,23 @@ export class MonitoringPlugin
}
};
const validate: any = get(options, 'config.validate', false);
if (validate && validate.payload) {
validate.body = validate.payload;
}
const validate: MonitoringRouteConfig<Params, Query, Body, Method>['validate'] =
// NOTE / TODO: "config.validate" is a legacy convention and should be converted over during the TS conversion work
get(options, 'validate', false) || get(options, 'config.validate', false);
options.validate = validate;
if (method === 'POST') {
router.post(options, handler);
} else if (method === 'GET') {
router.get(options, handler);
} else if (method === 'PUT') {
router.put(options, handler);
const routeConfig = {
path: options.path,
validate: options.validate,
};
if (method.toLowerCase() === 'post') {
router.post(routeConfig, handler);
} else if (method.toLowerCase() === 'get') {
router.get(routeConfig, handler);
} else if (method.toLowerCase() === 'put') {
router.put(routeConfig, handler);
} else {
throw new Error('Unsupported API method: ' + method);
}

View file

@ -6,8 +6,16 @@
*/
import { getApmsForClusters } from '../../../../lib/apm/get_apms_for_clusters';
import { LegacyRequest } from '../../../../types';
export const getApmClusterStatus = (req, { clusterUuid }) => {
export const getApmClusterStatus = (
req: LegacyRequest,
{
clusterUuid,
}: {
clusterUuid: string;
}
) => {
const clusters = [{ cluster_uuid: clusterUuid }];
return getApmsForClusters(req, clusters).then((apms) => {
const [{ stats, config }] = apms;

View file

@ -5,32 +5,26 @@
* 2.0.
*/
import { schema } from '@kbn/config-schema';
import { prefixIndexPatternWithCcs } from '../../../../../common/ccs_utils';
import { getMetrics } from '../../../../lib/details/get_metrics';
import { metricSet } from './metric_set_instance';
import { handleError } from '../../../../lib/errors';
import { getApmInfo } from '../../../../lib/apm';
import { INDEX_PATTERN_BEATS } from '../../../../../common/constants';
import {
postApmInstanceRequestParamsRT,
postApmInstanceRequestPayloadRT,
} from '../../../../../common/http_api/apm';
import { getApmInfo } from '../../../../lib/apm';
import { createValidationFunction } from '../../../../lib/create_route_validation_function';
import { getMetrics } from '../../../../lib/details/get_metrics';
import { handleError } from '../../../../lib/errors';
import { MonitoringCore } from '../../../../types';
import { metricSet } from './metric_set_instance';
export function apmInstanceRoute(server) {
export function apmInstanceRoute(server: MonitoringCore) {
server.route({
method: 'POST',
method: 'post',
path: '/api/monitoring/v1/clusters/{clusterUuid}/apm/{apmUuid}',
config: {
validate: {
params: schema.object({
clusterUuid: schema.string(),
apmUuid: schema.string(),
}),
payload: schema.object({
ccs: schema.maybe(schema.string()),
timeRange: schema.object({
min: schema.string(),
max: schema.string(),
}),
}),
},
validate: {
params: createValidationFunction(postApmInstanceRequestParamsRT),
body: createValidationFunction(postApmInstanceRequestPayloadRT),
},
async handler(req) {
const apmUuid = req.params.apmUuid;
@ -42,7 +36,9 @@ export function apmInstanceRoute(server) {
const showCgroupMetrics = config.ui.container.apm.enabled;
if (showCgroupMetrics) {
const metricCpu = metricSet.find((m) => m.name === 'apm_cpu');
metricCpu.keys = ['apm_cgroup_cpu'];
if (metricCpu) {
metricCpu.keys = ['apm_cgroup_cpu'];
}
}
try {

View file

@ -5,29 +5,24 @@
* 2.0.
*/
import { schema } from '@kbn/config-schema';
import { prefixIndexPatternWithCcs } from '../../../../../common/ccs_utils';
import { getStats, getApms } from '../../../../lib/apm';
import { handleError } from '../../../../lib/errors';
import { INDEX_PATTERN_BEATS } from '../../../../../common/constants';
import {
postApmInstancesRequestParamsRT,
postApmInstancesRequestPayloadRT,
} from '../../../../../common/http_api/apm';
import { getApms, getStats } from '../../../../lib/apm';
import { createValidationFunction } from '../../../../lib/create_route_validation_function';
import { handleError } from '../../../../lib/errors';
import { MonitoringCore } from '../../../../types';
export function apmInstancesRoute(server) {
export function apmInstancesRoute(server: MonitoringCore) {
server.route({
method: 'POST',
method: 'post',
path: '/api/monitoring/v1/clusters/{clusterUuid}/apm/instances',
config: {
validate: {
params: schema.object({
clusterUuid: schema.string(),
}),
payload: schema.object({
ccs: schema.maybe(schema.string()),
timeRange: schema.object({
min: schema.string(),
max: schema.string(),
}),
}),
},
validate: {
params: createValidationFunction(postApmInstancesRequestParamsRT),
body: createValidationFunction(postApmInstancesRequestPayloadRT),
},
async handler(req) {
const config = server.config;

View file

@ -5,29 +5,24 @@
* 2.0.
*/
import { schema } from '@kbn/config-schema';
import {
postApmOverviewRequestParamsRT,
postApmOverviewRequestPayloadRT,
} from '../../../../../common/http_api/apm';
import { createValidationFunction } from '../../../../lib/create_route_validation_function';
import { getMetrics } from '../../../../lib/details/get_metrics';
import { metricSet } from './metric_set_overview';
import { handleError } from '../../../../lib/errors';
import { MonitoringCore } from '../../../../types';
import { metricSet } from './metric_set_overview';
import { getApmClusterStatus } from './_get_apm_cluster_status';
export function apmOverviewRoute(server) {
export function apmOverviewRoute(server: MonitoringCore) {
server.route({
method: 'POST',
method: 'post',
path: '/api/monitoring/v1/clusters/{clusterUuid}/apm',
config: {
validate: {
params: schema.object({
clusterUuid: schema.string(),
}),
payload: schema.object({
ccs: schema.maybe(schema.string()),
timeRange: schema.object({
min: schema.string(),
max: schema.string(),
}),
}),
},
validate: {
params: createValidationFunction(postApmOverviewRequestParamsRT),
body: createValidationFunction(postApmOverviewRequestPayloadRT),
},
async handler(req) {
const config = server.config;
@ -36,7 +31,9 @@ export function apmOverviewRoute(server) {
const showCgroupMetrics = config.ui.container.apm.enabled;
if (showCgroupMetrics) {
const metricCpu = metricSet.find((m) => m.name === 'apm_cpu');
metricCpu.keys = ['apm_cgroup_cpu'];
if (metricCpu) {
metricCpu.keys = ['apm_cgroup_cpu'];
}
}
try {

View file

@ -23,7 +23,7 @@ export function beatsDetailRoute(server) {
clusterUuid: schema.string(),
beatUuid: schema.string(),
}),
payload: schema.object({
body: schema.object({
ccs: schema.maybe(schema.string()),
timeRange: schema.object({
min: schema.string(),

View file

@ -20,7 +20,7 @@ export function beatsListingRoute(server) {
params: schema.object({
clusterUuid: schema.string(),
}),
payload: schema.object({
body: schema.object({
ccs: schema.maybe(schema.string()),
timeRange: schema.object({
min: schema.string(),

View file

@ -22,7 +22,7 @@ export function beatsOverviewRoute(server) {
params: schema.object({
clusterUuid: schema.string(),
}),
payload: schema.object({
body: schema.object({
ccs: schema.maybe(schema.string()),
timeRange: schema.object({
min: schema.string(),

View file

@ -24,7 +24,7 @@ export function clusterRoute(server: LegacyServer) {
params: schema.object({
clusterUuid: schema.string(),
}),
payload: schema.object({
body: schema.object({
ccs: schema.maybe(schema.string()),
timeRange: schema.object({
min: schema.string(),

View file

@ -204,7 +204,7 @@ export function ccrRoute(server: { route: (p: any) => void; config: MonitoringCo
params: schema.object({
clusterUuid: schema.string(),
}),
payload: schema.object({
body: schema.object({
ccs: schema.maybe(schema.string()),
timeRange: schema.object({
min: schema.string(),

View file

@ -89,7 +89,7 @@ export function ccrShardRoute(server: { route: (p: any) => void; config: () => {
index: schema.string(),
shardId: schema.string(),
}),
payload: schema.object({
body: schema.object({
ccs: schema.maybe(schema.string()),
timeRange: schema.object({
min: schema.string(),

View file

@ -29,7 +29,7 @@ export function esIndexRoute(server) {
clusterUuid: schema.string(),
id: schema.string(),
}),
payload: schema.object({
body: schema.object({
ccs: schema.maybe(schema.string()),
timeRange: schema.object({
min: schema.string(),

View file

@ -24,7 +24,7 @@ export function esIndicesRoute(server) {
query: schema.object({
show_system_indices: schema.boolean(),
}),
payload: schema.object({
body: schema.object({
ccs: schema.maybe(schema.string()),
timeRange: schema.object({
min: schema.string(),

View file

@ -21,7 +21,7 @@ export function mlJobRoute(server) {
params: schema.object({
clusterUuid: schema.string(),
}),
payload: schema.object({
body: schema.object({
ccs: schema.maybe(schema.string()),
timeRange: schema.object({
min: schema.string(),

View file

@ -29,7 +29,7 @@ export function esNodeRoute(server) {
clusterUuid: schema.string(),
nodeUuid: schema.string(),
}),
payload: schema.object({
body: schema.object({
ccs: schema.maybe(schema.string()),
showSystemIndices: schema.boolean({ defaultValue: false }), // show/hide system indices in shard allocation table
timeRange: schema.object({

View file

@ -24,7 +24,7 @@ export function esNodesRoute(server) {
params: schema.object({
clusterUuid: schema.string(),
}),
payload: schema.object({
body: schema.object({
ccs: schema.maybe(schema.string()),
timeRange: schema.object({
min: schema.string(),

View file

@ -26,7 +26,7 @@ export function esOverviewRoute(server) {
params: schema.object({
clusterUuid: schema.string(),
}),
payload: schema.object({
body: schema.object({
ccs: schema.maybe(schema.string()),
timeRange: schema.object({
min: schema.string(),

View file

@ -20,7 +20,7 @@ export function entSearchOverviewRoute(server) {
params: schema.object({
clusterUuid: schema.string(),
}),
payload: schema.object({
body: schema.object({
ccs: schema.maybe(schema.string()),
timeRange: schema.object({
min: schema.string(),

View file

@ -31,7 +31,7 @@ export function kibanaInstanceRoute(server: LegacyServer) {
clusterUuid: schema.string(),
kibanaUuid: schema.string(),
}),
payload: schema.object({
body: schema.object({
ccs: schema.maybe(schema.string()),
timeRange: schema.object({
min: schema.string(),

View file

@ -22,7 +22,7 @@ export function kibanaInstancesRoute(server) {
params: schema.object({
clusterUuid: schema.string(),
}),
payload: schema.object({
body: schema.object({
ccs: schema.maybe(schema.string()),
timeRange: schema.object({
min: schema.string(),

View file

@ -23,7 +23,7 @@ export function kibanaOverviewRoute(server) {
params: schema.object({
clusterUuid: schema.string(),
}),
payload: schema.object({
body: schema.object({
ccs: schema.maybe(schema.string()),
timeRange: schema.object({
min: schema.string(),

View file

@ -36,7 +36,7 @@ export function logstashNodeRoute(server) {
clusterUuid: schema.string(),
logstashUuid: schema.string(),
}),
payload: schema.object({
body: schema.object({
ccs: schema.maybe(schema.string()),
timeRange: schema.object({
min: schema.string(),

View file

@ -32,7 +32,7 @@ export function logstashNodesRoute(server) {
params: schema.object({
clusterUuid: schema.string(),
}),
payload: schema.object({
body: schema.object({
ccs: schema.maybe(schema.string()),
timeRange: schema.object({
min: schema.string(),

View file

@ -33,7 +33,7 @@ export function logstashOverviewRoute(server) {
params: schema.object({
clusterUuid: schema.string(),
}),
payload: schema.object({
body: schema.object({
ccs: schema.maybe(schema.string()),
timeRange: schema.object({
min: schema.string(),

View file

@ -38,7 +38,7 @@ export function logstashPipelineRoute(server) {
pipelineId: schema.string(),
pipelineHash: schema.maybe(schema.string()),
}),
payload: schema.object({
body: schema.object({
ccs: schema.maybe(schema.string()),
detailVertexId: schema.maybe(schema.string()),
}),

View file

@ -21,7 +21,7 @@ export function logstashClusterPipelineIdsRoute(server) {
params: schema.object({
clusterUuid: schema.string(),
}),
payload: schema.object({
body: schema.object({
ccs: schema.maybe(schema.string()),
timeRange: schema.object({
min: schema.string(),

View file

@ -22,7 +22,7 @@ export function logstashClusterPipelinesRoute(server) {
params: schema.object({
clusterUuid: schema.string(),
}),
payload: schema.object({
body: schema.object({
ccs: schema.maybe(schema.string()),
timeRange: schema.object({
min: schema.string(),

View file

@ -23,7 +23,7 @@ export function logstashNodePipelinesRoute(server) {
clusterUuid: schema.string(),
logstashUuid: schema.string(),
}),
payload: schema.object({
body: schema.object({
ccs: schema.maybe(schema.string()),
timeRange: schema.object({
min: schema.string(),

View file

@ -35,7 +35,7 @@ export function clusterSetupStatusRoute(server) {
// in our testing environment.
skipLiveData: schema.boolean({ defaultValue: false }),
}),
payload: schema.nullable(
body: schema.nullable(
schema.object({
ccs: schema.maybe(schema.string()),
timeRange: schema.object({

View file

@ -35,7 +35,7 @@ export function nodeSetupStatusRoute(server) {
// in our testing environment.
skipLiveData: schema.boolean({ defaultValue: false }),
}),
payload: schema.nullable(
body: schema.nullable(
schema.object({
ccs: schema.maybe(schema.string()),
timeRange: schema.maybe(

View file

@ -34,6 +34,7 @@ import { LicensingPluginStart } from '@kbn/licensing-plugin/server';
import { PluginSetupContract as FeaturesPluginSetupContract } from '@kbn/features-plugin/server';
import { EncryptedSavedObjectsPluginSetup } from '@kbn/encrypted-saved-objects-plugin/server';
import { CloudSetup } from '@kbn/cloud-plugin/server';
import { RouteConfig, RouteMethod } from '@kbn/core/server';
import { ElasticsearchModifiedSource } from '../common/types/es';
import { RulesByType } from '../common/types/alerts';
import { configSchema, MonitoringConfig } from './config';
@ -79,10 +80,18 @@ export interface RouteDependencies {
logger: Logger;
}
export type MonitoringRouteConfig<Params, Query, Body, Method extends RouteMethod> = {
method: RouteMethod;
} & RouteConfig<Params, Query, Body, Method> & {
handler: (request: LegacyRequest) => any;
};
export interface MonitoringCore {
config: MonitoringConfig;
log: Logger;
route: (options: any) => void;
route: <Params = any, Query = any, Body = any, Method extends RouteMethod = any>(
options: MonitoringRouteConfig<Params, Query, Body, Method>
) => void;
}
export interface LegacyShimDependencies {
@ -144,7 +153,8 @@ export interface LegacyServer {
};
}
export type Cluster = ElasticsearchModifiedSource & {
export type Cluster = Omit<ElasticsearchModifiedSource, 'timestamp'> & {
timestamp?: string;
ml?: { jobs: any };
logs?: any;
alerts?: AlertsOnCluster;