mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 01:38:56 -04:00
[Logs UI / Metrics UI] New Platform migration server followups (#51615)
This commit is contained in:
parent
fb1670c54b
commit
18b5cf9adb
36 changed files with 279 additions and 282 deletions
|
@ -54,10 +54,6 @@ export function getApmIndicesConfig(config: APMConfig): ApmIndicesConfig {
|
|||
};
|
||||
}
|
||||
|
||||
// export async function getApmIndices(context: APMRequestHandlerContext) {
|
||||
// return _getApmIndices(context.core, context.config);
|
||||
// }
|
||||
|
||||
export async function getApmIndices({
|
||||
config,
|
||||
savedObjectsClient
|
||||
|
|
|
@ -6,3 +6,4 @@
|
|||
|
||||
export * from './log_analysis';
|
||||
export * from './metadata_api';
|
||||
export * from './metrics_explorer';
|
||||
|
|
|
@ -0,0 +1,96 @@
|
|||
/*
|
||||
* 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 * as rt from 'io-ts';
|
||||
|
||||
export const METRIC_EXPLORER_AGGREGATIONS = [
|
||||
'avg',
|
||||
'max',
|
||||
'min',
|
||||
'cardinality',
|
||||
'rate',
|
||||
'count',
|
||||
] as const;
|
||||
|
||||
type MetricExplorerAggregations = typeof METRIC_EXPLORER_AGGREGATIONS[number];
|
||||
|
||||
const metricsExplorerAggregationKeys = METRIC_EXPLORER_AGGREGATIONS.reduce<
|
||||
Record<MetricExplorerAggregations, null>
|
||||
>((acc, agg) => ({ ...acc, [agg]: null }), {} as Record<MetricExplorerAggregations, null>);
|
||||
|
||||
export const metricsExplorerAggregationRT = rt.keyof(metricsExplorerAggregationKeys);
|
||||
|
||||
export const metricsExplorerMetricRequiredFieldsRT = rt.type({
|
||||
aggregation: metricsExplorerAggregationRT,
|
||||
});
|
||||
|
||||
export const metricsExplorerMetricOptionalFieldsRT = rt.partial({
|
||||
field: rt.union([rt.string, rt.undefined]),
|
||||
});
|
||||
|
||||
export const metricsExplorerMetricRT = rt.intersection([
|
||||
metricsExplorerMetricRequiredFieldsRT,
|
||||
metricsExplorerMetricOptionalFieldsRT,
|
||||
]);
|
||||
|
||||
export const timeRangeRT = rt.type({
|
||||
field: rt.string,
|
||||
from: rt.number,
|
||||
to: rt.number,
|
||||
interval: rt.string,
|
||||
});
|
||||
|
||||
export const metricsExplorerRequestBodyRequiredFieldsRT = rt.type({
|
||||
timerange: timeRangeRT,
|
||||
indexPattern: rt.string,
|
||||
metrics: rt.array(metricsExplorerMetricRT),
|
||||
});
|
||||
|
||||
export const metricsExplorerRequestBodyOptionalFieldsRT = rt.partial({
|
||||
groupBy: rt.union([rt.string, rt.null, rt.undefined]),
|
||||
afterKey: rt.union([rt.string, rt.null, rt.undefined]),
|
||||
limit: rt.union([rt.number, rt.null, rt.undefined]),
|
||||
filterQuery: rt.union([rt.string, rt.null, rt.undefined]),
|
||||
});
|
||||
|
||||
export const metricsExplorerRequestBodyRT = rt.intersection([
|
||||
metricsExplorerRequestBodyRequiredFieldsRT,
|
||||
metricsExplorerRequestBodyOptionalFieldsRT,
|
||||
]);
|
||||
|
||||
export const metricsExplorerPageInfoRT = rt.type({
|
||||
total: rt.number,
|
||||
afterKey: rt.union([rt.string, rt.null]),
|
||||
});
|
||||
|
||||
export const metricsExplorerColumnTypeRT = rt.keyof({
|
||||
date: null,
|
||||
number: null,
|
||||
string: null,
|
||||
});
|
||||
|
||||
export const metricsExplorerColumnRT = rt.type({
|
||||
name: rt.string,
|
||||
type: metricsExplorerColumnTypeRT,
|
||||
});
|
||||
|
||||
export const metricsExplorerRowRT = rt.intersection([
|
||||
rt.type({
|
||||
timestamp: rt.number,
|
||||
}),
|
||||
rt.record(rt.string, rt.union([rt.string, rt.number, rt.null, rt.undefined])),
|
||||
]);
|
||||
|
||||
export const metricsExplorerSeriesRT = rt.type({
|
||||
id: rt.string,
|
||||
columns: rt.array(metricsExplorerColumnRT),
|
||||
rows: rt.array(metricsExplorerRowRT),
|
||||
});
|
||||
|
||||
export const metricsExplorerResponseRT = rt.type({
|
||||
series: rt.array(metricsExplorerSeriesRT),
|
||||
pageInfo: metricsExplorerPageInfoRT,
|
||||
});
|
|
@ -14,6 +14,8 @@ import { getConfigSchema } from './server/kibana.index';
|
|||
import { savedObjectMappings } from './server/saved_objects';
|
||||
import { plugin, InfraServerPluginDeps } from './server/new_platform_index';
|
||||
import { InfraSetup } from '../../../plugins/infra/server';
|
||||
import { PluginSetupContract as FeaturesPluginSetup } from '../../../plugins/features/server';
|
||||
import { SpacesPluginSetup } from '../../../plugins/spaces/server';
|
||||
import { APMPluginContract } from '../../../plugins/apm/server';
|
||||
|
||||
const APP_ID = 'infra';
|
||||
|
@ -91,8 +93,8 @@ export function infra(kibana: any) {
|
|||
indexPatternsServiceFactory: legacyServer.indexPatternsServiceFactory,
|
||||
},
|
||||
metrics: legacyServer.plugins.metrics,
|
||||
spaces: plugins.spaces,
|
||||
features: plugins.features,
|
||||
spaces: plugins.spaces as SpacesPluginSetup,
|
||||
features: plugins.features as FeaturesPluginSetup,
|
||||
// NP_NOTE: [TSVB_GROUP] Huge hack to make TSVB (getVisData()) work with raw requests that
|
||||
// originate from the New Platform router (and are very different to the old request object).
|
||||
// Once TSVB has migrated over to NP, and can work with the new raw requests, or ideally just
|
||||
|
@ -113,7 +115,7 @@ export function infra(kibana: any) {
|
|||
|
||||
const libs = infraPluginInstance.getLibs();
|
||||
|
||||
// NP_TODO how do we replace this? Answer: return from setup function.
|
||||
// NP_NOTE: Left here for now for legacy plugins to consume
|
||||
legacyServer.expose(
|
||||
'defineInternalSourceConfiguration',
|
||||
libs.sources.defineInternalSourceConfiguration.bind(libs.sources)
|
||||
|
|
|
@ -10,6 +10,10 @@ import { i18n } from '@kbn/i18n';
|
|||
import React, { useCallback } from 'react';
|
||||
import { MetricsExplorerAggregation } from '../../../server/routes/metrics_explorer/types';
|
||||
import { MetricsExplorerOptions } from '../../containers/metrics_explorer/use_metrics_explorer_options';
|
||||
import {
|
||||
metricsExplorerAggregationRT,
|
||||
METRIC_EXPLORER_AGGREGATIONS,
|
||||
} from '../../../common/http_api/metrics_explorer';
|
||||
|
||||
interface Props {
|
||||
options: MetricsExplorerOptions;
|
||||
|
@ -17,43 +21,32 @@ interface Props {
|
|||
onChange: (aggregation: MetricsExplorerAggregation) => void;
|
||||
}
|
||||
|
||||
const isMetricsExplorerAggregation = (subject: any): subject is MetricsExplorerAggregation => {
|
||||
return Object.keys(MetricsExplorerAggregation).includes(subject);
|
||||
};
|
||||
|
||||
export const MetricsExplorerAggregationPicker = ({ options, onChange }: Props) => {
|
||||
const AGGREGATION_LABELS = {
|
||||
[MetricsExplorerAggregation.avg]: i18n.translate(
|
||||
'xpack.infra.metricsExplorer.aggregationLables.avg',
|
||||
{ defaultMessage: 'Average' }
|
||||
),
|
||||
[MetricsExplorerAggregation.max]: i18n.translate(
|
||||
'xpack.infra.metricsExplorer.aggregationLables.max',
|
||||
{ defaultMessage: 'Max' }
|
||||
),
|
||||
[MetricsExplorerAggregation.min]: i18n.translate(
|
||||
'xpack.infra.metricsExplorer.aggregationLables.min',
|
||||
{ defaultMessage: 'Min' }
|
||||
),
|
||||
[MetricsExplorerAggregation.cardinality]: i18n.translate(
|
||||
'xpack.infra.metricsExplorer.aggregationLables.cardinality',
|
||||
{ defaultMessage: 'Cardinality' }
|
||||
),
|
||||
[MetricsExplorerAggregation.rate]: i18n.translate(
|
||||
'xpack.infra.metricsExplorer.aggregationLables.rate',
|
||||
{ defaultMessage: 'Rate' }
|
||||
),
|
||||
[MetricsExplorerAggregation.count]: i18n.translate(
|
||||
'xpack.infra.metricsExplorer.aggregationLables.count',
|
||||
{ defaultMessage: 'Document count' }
|
||||
),
|
||||
['avg']: i18n.translate('xpack.infra.metricsExplorer.aggregationLables.avg', {
|
||||
defaultMessage: 'Average',
|
||||
}),
|
||||
['max']: i18n.translate('xpack.infra.metricsExplorer.aggregationLables.max', {
|
||||
defaultMessage: 'Max',
|
||||
}),
|
||||
['min']: i18n.translate('xpack.infra.metricsExplorer.aggregationLables.min', {
|
||||
defaultMessage: 'Min',
|
||||
}),
|
||||
['cardinality']: i18n.translate('xpack.infra.metricsExplorer.aggregationLables.cardinality', {
|
||||
defaultMessage: 'Cardinality',
|
||||
}),
|
||||
['rate']: i18n.translate('xpack.infra.metricsExplorer.aggregationLables.rate', {
|
||||
defaultMessage: 'Rate',
|
||||
}),
|
||||
['count']: i18n.translate('xpack.infra.metricsExplorer.aggregationLables.count', {
|
||||
defaultMessage: 'Document count',
|
||||
}),
|
||||
};
|
||||
|
||||
const handleChange = useCallback(
|
||||
e => {
|
||||
const aggregation =
|
||||
(isMetricsExplorerAggregation(e.target.value) && e.target.value) ||
|
||||
MetricsExplorerAggregation.avg;
|
||||
(metricsExplorerAggregationRT.is(e.target.value) && e.target.value) || 'avg';
|
||||
onChange(aggregation);
|
||||
},
|
||||
[onChange]
|
||||
|
@ -66,7 +59,7 @@ export const MetricsExplorerAggregationPicker = ({ options, onChange }: Props) =
|
|||
})}
|
||||
fullWidth
|
||||
value={options.aggregation}
|
||||
options={Object.keys(MetricsExplorerAggregation).map(k => ({
|
||||
options={METRIC_EXPLORER_AGGREGATIONS.map(k => ({
|
||||
text: AGGREGATION_LABELS[k as MetricsExplorerAggregation],
|
||||
value: k,
|
||||
}))}
|
||||
|
|
|
@ -5,21 +5,17 @@
|
|||
*/
|
||||
|
||||
import { calculateDomain } from './calculate_domain';
|
||||
import {
|
||||
MetricsExplorerSeries,
|
||||
MetricsExplorerAggregation,
|
||||
MetricsExplorerColumnType,
|
||||
} from '../../../../server/routes/metrics_explorer/types';
|
||||
import { MetricsExplorerSeries } from '../../../../server/routes/metrics_explorer/types';
|
||||
import { MetricsExplorerOptionsMetric } from '../../../containers/metrics_explorer/use_metrics_explorer_options';
|
||||
import { MetricsExplorerColor } from '../../../../common/color_palette';
|
||||
describe('calculateDomain()', () => {
|
||||
const series: MetricsExplorerSeries = {
|
||||
id: 'test-01',
|
||||
columns: [
|
||||
{ type: MetricsExplorerColumnType.date, name: 'timestamp' },
|
||||
{ type: MetricsExplorerColumnType.number, name: 'metric_0' },
|
||||
{ type: MetricsExplorerColumnType.number, name: 'metric_1' },
|
||||
{ type: MetricsExplorerColumnType.string, name: 'groupBy' },
|
||||
{ type: 'date', name: 'timestamp' },
|
||||
{ type: 'number', name: 'metric_0' },
|
||||
{ type: 'number', name: 'metric_1' },
|
||||
{ type: 'string', name: 'groupBy' },
|
||||
],
|
||||
rows: [
|
||||
{ timestamp: 1562860500000, metric_0: null, metric_1: null },
|
||||
|
@ -31,12 +27,12 @@ describe('calculateDomain()', () => {
|
|||
};
|
||||
const metrics: MetricsExplorerOptionsMetric[] = [
|
||||
{
|
||||
aggregation: MetricsExplorerAggregation.avg,
|
||||
aggregation: 'avg',
|
||||
field: 'system.memory.free',
|
||||
color: MetricsExplorerColor.color0,
|
||||
},
|
||||
{
|
||||
aggregation: MetricsExplorerAggregation.avg,
|
||||
aggregation: 'avg',
|
||||
field: 'system.memory.used.bytes',
|
||||
color: MetricsExplorerColor.color1,
|
||||
},
|
||||
|
|
|
@ -4,20 +4,14 @@
|
|||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import {
|
||||
MetricsExplorerAggregation,
|
||||
MetricsExplorerMetric,
|
||||
} from '../../../../server/routes/metrics_explorer/types';
|
||||
import { MetricsExplorerMetric } from '../../../../server/routes/metrics_explorer/types';
|
||||
import { createFormatter } from '../../../utils/formatters';
|
||||
import { InfraFormatterType } from '../../../lib/lib';
|
||||
import { metricToFormat } from './metric_to_format';
|
||||
export const createFormatterForMetric = (metric?: MetricsExplorerMetric) => {
|
||||
if (metric && metric.field) {
|
||||
const format = metricToFormat(metric);
|
||||
if (
|
||||
format === InfraFormatterType.bits &&
|
||||
metric.aggregation === MetricsExplorerAggregation.rate
|
||||
) {
|
||||
if (format === InfraFormatterType.bits && metric.aggregation === 'rate') {
|
||||
return createFormatter(InfraFormatterType.bits, '{{value}}/s');
|
||||
}
|
||||
return createFormatter(format);
|
||||
|
|
|
@ -5,34 +5,35 @@
|
|||
*/
|
||||
|
||||
import { createFormatterForMetric } from './create_formatter_for_metric';
|
||||
import { MetricsExplorerAggregation } from '../../../../server/routes/metrics_explorer/types';
|
||||
import { MetricsExplorerMetric } from '../../../../server/routes/metrics_explorer/types';
|
||||
|
||||
describe('createFormatterForMetric()', () => {
|
||||
it('should just work for count', () => {
|
||||
const metric = { aggregation: MetricsExplorerAggregation.count };
|
||||
const metric: MetricsExplorerMetric = { aggregation: 'count' };
|
||||
const format = createFormatterForMetric(metric);
|
||||
expect(format(1291929)).toBe('1,291,929');
|
||||
});
|
||||
it('should just work for numerics', () => {
|
||||
const metric = { aggregation: MetricsExplorerAggregation.avg, field: 'system.load.1' };
|
||||
const metric: MetricsExplorerMetric = { aggregation: 'avg', field: 'system.load.1' };
|
||||
const format = createFormatterForMetric(metric);
|
||||
expect(format(1000.2)).toBe('1,000.2');
|
||||
});
|
||||
it('should just work for percents', () => {
|
||||
const metric = { aggregation: MetricsExplorerAggregation.avg, field: 'system.cpu.total.pct' };
|
||||
const metric: MetricsExplorerMetric = { aggregation: 'avg', field: 'system.cpu.total.pct' };
|
||||
const format = createFormatterForMetric(metric);
|
||||
expect(format(0.349)).toBe('34.9%');
|
||||
});
|
||||
it('should just work for rates', () => {
|
||||
const metric = {
|
||||
aggregation: MetricsExplorerAggregation.rate,
|
||||
const metric: MetricsExplorerMetric = {
|
||||
aggregation: 'rate',
|
||||
field: 'system.network.out.bytes',
|
||||
};
|
||||
const format = createFormatterForMetric(metric);
|
||||
expect(format(103929292)).toBe('831.4Mbit/s');
|
||||
});
|
||||
it('should just work for bytes', () => {
|
||||
const metric = {
|
||||
aggregation: MetricsExplorerAggregation.avg,
|
||||
const metric: MetricsExplorerMetric = {
|
||||
aggregation: 'avg',
|
||||
field: 'system.network.out.bytes',
|
||||
};
|
||||
const format = createFormatterForMetric(metric);
|
||||
|
|
|
@ -5,15 +5,15 @@
|
|||
*/
|
||||
|
||||
import { createMetricLabel } from './create_metric_label';
|
||||
import { MetricsExplorerAggregation } from '../../../../server/routes/metrics_explorer/types';
|
||||
import { MetricsExplorerMetric } from '../../../../server/routes/metrics_explorer/types';
|
||||
|
||||
describe('createMetricLabel()', () => {
|
||||
it('should work with metrics with fields', () => {
|
||||
const metric = { aggregation: MetricsExplorerAggregation.avg, field: 'system.load.1' };
|
||||
const metric: MetricsExplorerMetric = { aggregation: 'avg', field: 'system.load.1' };
|
||||
expect(createMetricLabel(metric)).toBe('avg(system.load.1)');
|
||||
});
|
||||
it('should work with document count', () => {
|
||||
const metric = { aggregation: MetricsExplorerAggregation.count };
|
||||
const metric: MetricsExplorerMetric = { aggregation: 'count' };
|
||||
expect(createMetricLabel(metric)).toBe('count()');
|
||||
});
|
||||
});
|
||||
|
|
|
@ -8,11 +8,11 @@ import { createTSVBLink, createFilterFromOptions } from './create_tsvb_link';
|
|||
import { source, options, timeRange, chartOptions } from '../../../utils/fixtures/metrics_explorer';
|
||||
import uuid from 'uuid';
|
||||
import { OutputBuffer } from 'uuid/interfaces';
|
||||
import { MetricsExplorerAggregation } from '../../../../server/routes/metrics_explorer/types';
|
||||
import {
|
||||
MetricsExplorerYAxisMode,
|
||||
MetricsExplorerChartType,
|
||||
} from '../../../containers/metrics_explorer/use_metrics_explorer_options';
|
||||
import { MetricsExplorerOptions } from '../../../containers/metrics_explorer/use_metrics_explorer_options';
|
||||
|
||||
jest.mock('uuid');
|
||||
const mockedUuid = uuid as jest.Mocked<typeof uuid>;
|
||||
|
@ -28,11 +28,9 @@ describe('createTSVBLink()', () => {
|
|||
});
|
||||
|
||||
it('should work with rates', () => {
|
||||
const customOptions = {
|
||||
const customOptions: MetricsExplorerOptions = {
|
||||
...options,
|
||||
metrics: [
|
||||
{ aggregation: MetricsExplorerAggregation.rate, field: 'system.network.out.bytes' },
|
||||
],
|
||||
metrics: [{ aggregation: 'rate', field: 'system.network.out.bytes' }],
|
||||
};
|
||||
const link = createTSVBLink(source, customOptions, series, timeRange, chartOptions);
|
||||
expect(link).toBe(
|
||||
|
|
|
@ -8,10 +8,7 @@ import { encode } from 'rison-node';
|
|||
import uuid from 'uuid';
|
||||
import { set } from 'lodash';
|
||||
import { colorTransformer, MetricsExplorerColor } from '../../../../common/color_palette';
|
||||
import {
|
||||
MetricsExplorerSeries,
|
||||
MetricsExplorerAggregation,
|
||||
} from '../../../../server/routes/metrics_explorer/types';
|
||||
import { MetricsExplorerSeries } from '../../../../server/routes/metrics_explorer/types';
|
||||
import {
|
||||
MetricsExplorerOptions,
|
||||
MetricsExplorerOptionsMetric,
|
||||
|
@ -26,7 +23,7 @@ import { SourceQuery } from '../../../graphql/types';
|
|||
import { createMetricLabel } from './create_metric_label';
|
||||
|
||||
export const metricsExplorerMetricToTSVBMetric = (metric: MetricsExplorerOptionsMetric) => {
|
||||
if (metric.aggregation === MetricsExplorerAggregation.rate) {
|
||||
if (metric.aggregation === 'rate') {
|
||||
const metricId = uuid.v1();
|
||||
const positiveOnlyId = uuid.v1();
|
||||
const derivativeId = uuid.v1();
|
||||
|
@ -73,8 +70,7 @@ const mapMetricToSeries = (chartOptions: MetricsExplorerChartOptions) => (
|
|||
),
|
||||
fill: chartOptions.type === MetricsExplorerChartType.area ? 0.5 : 0,
|
||||
formatter: format === InfraFormatterType.bits ? InfraFormatterType.bytes : format,
|
||||
value_template:
|
||||
MetricsExplorerAggregation.rate === metric.aggregation ? '{{value}}/s' : '{{value}}',
|
||||
value_template: 'rate' === metric.aggregation ? '{{value}}/s' : '{{value}}',
|
||||
id: uuid.v1(),
|
||||
line_width: 2,
|
||||
metrics: metricsExplorerMetricToTSVBMetric(metric),
|
||||
|
|
|
@ -5,37 +5,37 @@
|
|||
*/
|
||||
|
||||
import { metricToFormat } from './metric_to_format';
|
||||
import { MetricsExplorerAggregation } from '../../../../server/routes/metrics_explorer/types';
|
||||
import { MetricsExplorerMetric } from '../../../../server/routes/metrics_explorer/types';
|
||||
import { InfraFormatterType } from '../../../lib/lib';
|
||||
describe('metricToFormat()', () => {
|
||||
it('should just work for numeric metrics', () => {
|
||||
const metric = { aggregation: MetricsExplorerAggregation.avg, field: 'system.load.1' };
|
||||
const metric: MetricsExplorerMetric = { aggregation: 'avg', field: 'system.load.1' };
|
||||
expect(metricToFormat(metric)).toBe(InfraFormatterType.number);
|
||||
});
|
||||
it('should just work for byte metrics', () => {
|
||||
const metric = {
|
||||
aggregation: MetricsExplorerAggregation.avg,
|
||||
const metric: MetricsExplorerMetric = {
|
||||
aggregation: 'avg',
|
||||
field: 'system.network.out.bytes',
|
||||
};
|
||||
expect(metricToFormat(metric)).toBe(InfraFormatterType.bytes);
|
||||
});
|
||||
it('should just work for rate bytes metrics', () => {
|
||||
const metric = {
|
||||
aggregation: MetricsExplorerAggregation.rate,
|
||||
const metric: MetricsExplorerMetric = {
|
||||
aggregation: 'rate',
|
||||
field: 'system.network.out.bytes',
|
||||
};
|
||||
expect(metricToFormat(metric)).toBe(InfraFormatterType.bits);
|
||||
});
|
||||
it('should just work for rate metrics', () => {
|
||||
const metric = {
|
||||
aggregation: MetricsExplorerAggregation.rate,
|
||||
const metric: MetricsExplorerMetric = {
|
||||
aggregation: 'rate',
|
||||
field: 'system.cpu.user.ticks',
|
||||
};
|
||||
expect(metricToFormat(metric)).toBe(InfraFormatterType.number);
|
||||
});
|
||||
it('should just work for percent metrics', () => {
|
||||
const metric = {
|
||||
aggregation: MetricsExplorerAggregation.avg,
|
||||
const metric: MetricsExplorerMetric = {
|
||||
aggregation: 'avg',
|
||||
field: 'system.cpu.user.pct',
|
||||
};
|
||||
expect(metricToFormat(metric)).toBe(InfraFormatterType.percent);
|
||||
|
|
|
@ -5,10 +5,7 @@
|
|||
*/
|
||||
|
||||
import { last } from 'lodash';
|
||||
import {
|
||||
MetricsExplorerAggregation,
|
||||
MetricsExplorerMetric,
|
||||
} from '../../../../server/routes/metrics_explorer/types';
|
||||
import { MetricsExplorerMetric } from '../../../../server/routes/metrics_explorer/types';
|
||||
import { InfraFormatterType } from '../../../lib/lib';
|
||||
export const metricToFormat = (metric?: MetricsExplorerMetric) => {
|
||||
if (metric && metric.field) {
|
||||
|
@ -16,7 +13,7 @@ export const metricToFormat = (metric?: MetricsExplorerMetric) => {
|
|||
if (suffix === 'pct') {
|
||||
return InfraFormatterType.percent;
|
||||
}
|
||||
if (suffix === 'bytes' && metric.aggregation === MetricsExplorerAggregation.rate) {
|
||||
if (suffix === 'bytes' && metric.aggregation === 'rate') {
|
||||
return InfraFormatterType.bits;
|
||||
}
|
||||
if (suffix === 'bytes') {
|
||||
|
|
|
@ -10,10 +10,7 @@ import { i18n } from '@kbn/i18n';
|
|||
import React, { useCallback, useState } from 'react';
|
||||
import { FieldType } from 'ui/index_patterns';
|
||||
import { colorTransformer, MetricsExplorerColor } from '../../../common/color_palette';
|
||||
import {
|
||||
MetricsExplorerMetric,
|
||||
MetricsExplorerAggregation,
|
||||
} from '../../../server/routes/metrics_explorer/types';
|
||||
import { MetricsExplorerMetric } from '../../../server/routes/metrics_explorer/types';
|
||||
import { MetricsExplorerOptions } from '../../containers/metrics_explorer/use_metrics_explorer_options';
|
||||
import { isDisplayable } from '../../utils/is_displayable';
|
||||
|
||||
|
@ -61,7 +58,7 @@ export const MetricsExplorerMetrics = ({ options, onChange, fields, autoFocus =
|
|||
.filter(field => isDisplayable(field))
|
||||
.map(field => ({ label: field.name, value: field.name }));
|
||||
const selectedOptions = options.metrics
|
||||
.filter(m => m.aggregation !== MetricsExplorerAggregation.count)
|
||||
.filter(m => m.aggregation !== 'count')
|
||||
.map(metric => ({
|
||||
label: metric.field || '',
|
||||
value: metric.field || '',
|
||||
|
@ -74,7 +71,7 @@ export const MetricsExplorerMetrics = ({ options, onChange, fields, autoFocus =
|
|||
|
||||
return (
|
||||
<EuiComboBox
|
||||
isDisabled={options.aggregation === MetricsExplorerAggregation.count}
|
||||
isDisabled={options.aggregation === 'count'}
|
||||
placeholder={placeholderText}
|
||||
fullWidth
|
||||
options={comboOptions}
|
||||
|
|
|
@ -58,19 +58,18 @@ export const MetricsExplorerToolbar = ({
|
|||
defaultViewState,
|
||||
onViewStateChange,
|
||||
}: Props) => {
|
||||
const isDefaultOptions =
|
||||
options.aggregation === MetricsExplorerAggregation.avg && options.metrics.length === 0;
|
||||
const isDefaultOptions = options.aggregation === 'avg' && options.metrics.length === 0;
|
||||
return (
|
||||
<Toolbar>
|
||||
<EuiFlexGroup alignItems="center">
|
||||
<EuiFlexItem grow={options.aggregation === MetricsExplorerAggregation.count ? 2 : false}>
|
||||
<EuiFlexItem grow={options.aggregation === 'count' ? 2 : false}>
|
||||
<MetricsExplorerAggregationPicker
|
||||
fullWidth
|
||||
options={options}
|
||||
onChange={onAggregationChange}
|
||||
/>
|
||||
</EuiFlexItem>
|
||||
{options.aggregation !== MetricsExplorerAggregation.count && (
|
||||
{options.aggregation !== 'count' && (
|
||||
<EuiText size="s" color="subdued">
|
||||
<FormattedMessage
|
||||
id="xpack.infra.metricsExplorer.aggregationLabel"
|
||||
|
@ -78,7 +77,7 @@ export const MetricsExplorerToolbar = ({
|
|||
/>
|
||||
</EuiText>
|
||||
)}
|
||||
{options.aggregation !== MetricsExplorerAggregation.count && (
|
||||
{options.aggregation !== 'count' && (
|
||||
<EuiFlexItem grow={2}>
|
||||
<MetricsExplorerMetrics
|
||||
autoFocus={isDefaultOptions}
|
||||
|
|
|
@ -6,7 +6,6 @@
|
|||
|
||||
import { fetch } from '../../utils/fetch';
|
||||
import { useMetricsExplorerData } from './use_metrics_explorer_data';
|
||||
import { MetricsExplorerAggregation } from '../../../server/routes/metrics_explorer/types';
|
||||
|
||||
import { renderHook } from '@testing-library/react-hooks';
|
||||
|
||||
|
@ -132,8 +131,8 @@ describe('useMetricsExplorerData Hook', () => {
|
|||
rerender({
|
||||
options: {
|
||||
...options,
|
||||
aggregation: MetricsExplorerAggregation.count,
|
||||
metrics: [{ aggregation: MetricsExplorerAggregation.count }],
|
||||
aggregation: 'count',
|
||||
metrics: [{ aggregation: 'count' }],
|
||||
},
|
||||
source,
|
||||
derivedIndexPattern,
|
||||
|
|
|
@ -9,10 +9,7 @@ import { isEqual } from 'lodash';
|
|||
import { useEffect, useState } from 'react';
|
||||
import { IIndexPattern } from 'src/plugins/data/public';
|
||||
import { SourceQuery } from '../../../common/graphql/types';
|
||||
import {
|
||||
MetricsExplorerAggregation,
|
||||
MetricsExplorerResponse,
|
||||
} from '../../../server/routes/metrics_explorer/types';
|
||||
import { MetricsExplorerResponse } from '../../../server/routes/metrics_explorer/types';
|
||||
import { fetch } from '../../utils/fetch';
|
||||
import { convertKueryToElasticSearchQuery } from '../../utils/kuery';
|
||||
import { MetricsExplorerOptions, MetricsExplorerTimeOptions } from './use_metrics_explorer_options';
|
||||
|
@ -48,8 +45,8 @@ export function useMetricsExplorerData(
|
|||
'../api/infra/metrics_explorer',
|
||||
{
|
||||
metrics:
|
||||
options.aggregation === MetricsExplorerAggregation.count
|
||||
? [{ aggregation: MetricsExplorerAggregation.count }]
|
||||
options.aggregation === 'count'
|
||||
? [{ aggregation: 'count' }]
|
||||
: options.metrics.map(metric => ({
|
||||
aggregation: metric.aggregation,
|
||||
field: metric.field,
|
||||
|
|
|
@ -14,7 +14,6 @@ import {
|
|||
DEFAULT_OPTIONS,
|
||||
DEFAULT_TIMERANGE,
|
||||
} from './use_metrics_explorer_options';
|
||||
import { MetricsExplorerAggregation } from '../../../server/routes/metrics_explorer/types';
|
||||
|
||||
const renderUseMetricsExplorerOptionsHook = () =>
|
||||
renderHook(() => useMetricsExplorerOptions(), {
|
||||
|
@ -68,7 +67,7 @@ describe('useMetricExplorerOptions', () => {
|
|||
const { result, rerender } = renderUseMetricsExplorerOptionsHook();
|
||||
const newOptions: MetricsExplorerOptions = {
|
||||
...DEFAULT_OPTIONS,
|
||||
metrics: [{ aggregation: MetricsExplorerAggregation.count }],
|
||||
metrics: [{ aggregation: 'count' }],
|
||||
};
|
||||
act(() => {
|
||||
result.current.setOptions(newOptions);
|
||||
|
@ -95,7 +94,7 @@ describe('useMetricExplorerOptions', () => {
|
|||
it('should load from store when available', () => {
|
||||
const newOptions: MetricsExplorerOptions = {
|
||||
...DEFAULT_OPTIONS,
|
||||
metrics: [{ aggregation: MetricsExplorerAggregation.avg, field: 'system.load.1' }],
|
||||
metrics: [{ aggregation: 'avg', field: 'system.load.1' }],
|
||||
};
|
||||
STORE.MetricsExplorerOptions = JSON.stringify(newOptions);
|
||||
const { result } = renderUseMetricsExplorerOptionsHook();
|
||||
|
|
|
@ -62,24 +62,24 @@ export const DEFAULT_CHART_OPTIONS: MetricsExplorerChartOptions = {
|
|||
|
||||
export const DEFAULT_METRICS: MetricsExplorerOptionsMetric[] = [
|
||||
{
|
||||
aggregation: MetricsExplorerAggregation.avg,
|
||||
aggregation: 'avg',
|
||||
field: 'system.cpu.user.pct',
|
||||
color: MetricsExplorerColor.color0,
|
||||
},
|
||||
{
|
||||
aggregation: MetricsExplorerAggregation.avg,
|
||||
aggregation: 'avg',
|
||||
field: 'kubernetes.pod.cpu.usage.node.pct',
|
||||
color: MetricsExplorerColor.color1,
|
||||
},
|
||||
{
|
||||
aggregation: MetricsExplorerAggregation.avg,
|
||||
aggregation: 'avg',
|
||||
field: 'docker.cpu.total.pct',
|
||||
color: MetricsExplorerColor.color2,
|
||||
},
|
||||
];
|
||||
|
||||
export const DEFAULT_OPTIONS: MetricsExplorerOptions = {
|
||||
aggregation: MetricsExplorerAggregation.avg,
|
||||
aggregation: 'avg',
|
||||
metrics: DEFAULT_METRICS,
|
||||
};
|
||||
|
||||
|
|
|
@ -15,7 +15,6 @@ import {
|
|||
resp,
|
||||
createSeries,
|
||||
} from '../../../utils/fixtures/metrics_explorer';
|
||||
import { MetricsExplorerAggregation } from '../../../../server/routes/metrics_explorer/types';
|
||||
|
||||
const renderUseMetricsExplorerStateHook = () =>
|
||||
renderHook(props => useMetricsExplorerState(props.source, props.derivedIndexPattern), {
|
||||
|
@ -89,11 +88,9 @@ describe('useMetricsExplorerState', () => {
|
|||
it('should change the metric', async () => {
|
||||
const { result } = renderUseMetricsExplorerStateHook();
|
||||
const { handleMetricsChange } = result.current;
|
||||
handleMetricsChange([
|
||||
{ aggregation: MetricsExplorerAggregation.max, field: 'system.load.1' },
|
||||
]);
|
||||
handleMetricsChange([{ aggregation: 'max', field: 'system.load.1' }]);
|
||||
expect(result.current.options.metrics).toEqual([
|
||||
{ aggregation: MetricsExplorerAggregation.max, field: 'system.load.1' },
|
||||
{ aggregation: 'max', field: 'system.load.1' },
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
@ -134,38 +131,32 @@ describe('useMetricsExplorerState', () => {
|
|||
it('should set the metrics to only count when selecting count', async () => {
|
||||
const { result, waitForNextUpdate } = renderUseMetricsExplorerStateHook();
|
||||
const { handleMetricsChange } = result.current;
|
||||
handleMetricsChange([
|
||||
{ aggregation: MetricsExplorerAggregation.avg, field: 'system.load.1' },
|
||||
]);
|
||||
handleMetricsChange([{ aggregation: 'avg', field: 'system.load.1' }]);
|
||||
expect(result.current.options.metrics).toEqual([
|
||||
{ aggregation: MetricsExplorerAggregation.avg, field: 'system.load.1' },
|
||||
{ aggregation: 'avg', field: 'system.load.1' },
|
||||
]);
|
||||
await waitForNextUpdate();
|
||||
const { handleAggregationChange } = result.current;
|
||||
handleAggregationChange(MetricsExplorerAggregation.count);
|
||||
handleAggregationChange('count');
|
||||
await waitForNextUpdate();
|
||||
expect(result.current.options.aggregation).toBe(MetricsExplorerAggregation.count);
|
||||
expect(result.current.options.metrics).toEqual([
|
||||
{ aggregation: MetricsExplorerAggregation.count },
|
||||
]);
|
||||
expect(result.current.options.aggregation).toBe('count');
|
||||
expect(result.current.options.metrics).toEqual([{ aggregation: 'count' }]);
|
||||
});
|
||||
|
||||
it('should change aggregation for metrics', async () => {
|
||||
const { result, waitForNextUpdate } = renderUseMetricsExplorerStateHook();
|
||||
const { handleMetricsChange } = result.current;
|
||||
handleMetricsChange([
|
||||
{ aggregation: MetricsExplorerAggregation.avg, field: 'system.load.1' },
|
||||
]);
|
||||
handleMetricsChange([{ aggregation: 'avg', field: 'system.load.1' }]);
|
||||
expect(result.current.options.metrics).toEqual([
|
||||
{ aggregation: MetricsExplorerAggregation.avg, field: 'system.load.1' },
|
||||
{ aggregation: 'avg', field: 'system.load.1' },
|
||||
]);
|
||||
await waitForNextUpdate();
|
||||
const { handleAggregationChange } = result.current;
|
||||
handleAggregationChange(MetricsExplorerAggregation.max);
|
||||
handleAggregationChange('max');
|
||||
await waitForNextUpdate();
|
||||
expect(result.current.options.aggregation).toBe(MetricsExplorerAggregation.max);
|
||||
expect(result.current.options.aggregation).toBe('max');
|
||||
expect(result.current.options.metrics).toEqual([
|
||||
{ aggregation: MetricsExplorerAggregation.max, field: 'system.load.1' },
|
||||
{ aggregation: 'max', field: 'system.load.1' },
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
|
|
@ -99,10 +99,10 @@ export const useMetricsExplorerState = (
|
|||
(aggregation: MetricsExplorerAggregation) => {
|
||||
setAfterKey(null);
|
||||
const metrics =
|
||||
aggregation === MetricsExplorerAggregation.count
|
||||
aggregation === 'count'
|
||||
? [{ aggregation }]
|
||||
: options.metrics
|
||||
.filter(metric => metric.aggregation !== MetricsExplorerAggregation.count)
|
||||
.filter(metric => metric.aggregation !== 'count')
|
||||
.map(metric => ({
|
||||
...metric,
|
||||
aggregation,
|
||||
|
|
|
@ -5,10 +5,8 @@
|
|||
*/
|
||||
|
||||
import {
|
||||
MetricsExplorerAggregation,
|
||||
MetricsExplorerResponse,
|
||||
MetricsExplorerSeries,
|
||||
MetricsExplorerColumnType,
|
||||
} from '../../../server/routes/metrics_explorer/types';
|
||||
import {
|
||||
MetricsExplorerOptions,
|
||||
|
@ -21,8 +19,8 @@ import {
|
|||
export const options: MetricsExplorerOptions = {
|
||||
limit: 3,
|
||||
groupBy: 'host.name',
|
||||
aggregation: MetricsExplorerAggregation.avg,
|
||||
metrics: [{ aggregation: MetricsExplorerAggregation.avg, field: 'system.cpu.user.pct' }],
|
||||
aggregation: 'avg',
|
||||
metrics: [{ aggregation: 'avg', field: 'system.cpu.user.pct' }],
|
||||
};
|
||||
|
||||
export const source = {
|
||||
|
@ -58,9 +56,9 @@ export const timeRange: MetricsExplorerTimeOptions = {
|
|||
export const createSeries = (id: string): MetricsExplorerSeries => ({
|
||||
id,
|
||||
columns: [
|
||||
{ name: 'timestamp', type: MetricsExplorerColumnType.date },
|
||||
{ name: 'metric_0', type: MetricsExplorerColumnType.number },
|
||||
{ name: 'groupBy', type: MetricsExplorerColumnType.string },
|
||||
{ name: 'timestamp', type: 'date' },
|
||||
{ name: 'metric_0', type: 'number' },
|
||||
{ name: 'groupBy', type: 'string' },
|
||||
],
|
||||
rows: [
|
||||
{ timestamp: 1, metric_0: 0.5, groupBy: id },
|
||||
|
|
|
@ -9,19 +9,21 @@ import { Lifecycle } from 'hapi';
|
|||
import { ObjectType } from '@kbn/config-schema';
|
||||
import { UsageCollectionSetup } from 'src/plugins/usage_collection/server';
|
||||
import { RouteMethod, RouteConfig } from '../../../../../../../../src/core/server';
|
||||
import { PluginSetupContract as FeaturesPluginSetup } from '../../../../../../../plugins/features/server';
|
||||
import { SpacesPluginSetup } from '../../../../../../../plugins/spaces/server';
|
||||
import { APMPluginContract } from '../../../../../../../plugins/apm/server';
|
||||
|
||||
// NP_TODO: Compose real types from plugins we depend on, no "any"
|
||||
export interface InfraServerPluginDeps {
|
||||
spaces: SpacesPluginSetup;
|
||||
usageCollection: UsageCollectionSetup;
|
||||
spaces: any;
|
||||
metrics: {
|
||||
getVisData: any;
|
||||
};
|
||||
indexPatterns: {
|
||||
indexPatternsServiceFactory: any;
|
||||
};
|
||||
features: any;
|
||||
features: FeaturesPluginSetup;
|
||||
apm: APMPluginContract;
|
||||
___legacy: any;
|
||||
}
|
||||
|
|
|
@ -147,10 +147,6 @@ export class KibanaFramework {
|
|||
headers,
|
||||
body: errorBody,
|
||||
});
|
||||
|
||||
// NP_TODO: Do we still need to re-throw this error in this case? if we do, can we
|
||||
// still call the response.customError method to control the HTTP response?
|
||||
// throw error;
|
||||
}
|
||||
}
|
||||
this.router.post(routeOptions, handler);
|
||||
|
@ -250,7 +246,7 @@ export class KibanaFramework {
|
|||
}
|
||||
}
|
||||
|
||||
// NP_TODO: This method needs to no longer require full KibanaRequest
|
||||
// NP_TODO: [TSVB_GROUP] This method needs fixing when the metrics plugin has migrated to the New Platform
|
||||
public async makeTSVBRequest(
|
||||
request: KibanaRequest,
|
||||
model: TSVBMetricModel,
|
||||
|
|
|
@ -28,7 +28,7 @@ export interface InfraMetricsAdapter {
|
|||
getMetrics(
|
||||
requestContext: RequestHandlerContext,
|
||||
options: InfraMetricsRequestOptions,
|
||||
request: KibanaRequest // NP_TODO: temporarily needed until metrics getVisData no longer needs full request
|
||||
request: KibanaRequest
|
||||
): Promise<InfraMetricData[]>;
|
||||
}
|
||||
|
||||
|
|
|
@ -25,7 +25,7 @@ export class KibanaMetricsAdapter implements InfraMetricsAdapter {
|
|||
public async getMetrics(
|
||||
requestContext: RequestHandlerContext,
|
||||
options: InfraMetricsRequestOptions,
|
||||
rawRequest: KibanaRequest // NP_TODO: Temporarily needed until metrics getVisData no longer needs full request
|
||||
rawRequest: KibanaRequest
|
||||
): Promise<InfraMetricData[]> {
|
||||
const indexPattern = `${options.sourceConfiguration.metricAlias},${options.sourceConfiguration.logAlias}`;
|
||||
const fields = findInventoryFields(options.nodeType, options.sourceConfiguration.fields);
|
||||
|
|
|
@ -18,7 +18,7 @@ export class InfraMetricsDomain {
|
|||
public async getMetrics(
|
||||
requestContext: RequestHandlerContext,
|
||||
options: InfraMetricsRequestOptions,
|
||||
rawRequest: KibanaRequest // NP_TODO: temporarily needed until metrics getVisData no longer needs full request
|
||||
rawRequest: KibanaRequest
|
||||
): Promise<InfraMetricData[]> {
|
||||
return await this.adapter.getMetrics(requestContext, options, rawRequest);
|
||||
}
|
||||
|
|
|
@ -5,11 +5,10 @@
|
|||
*/
|
||||
|
||||
import { PluginInitializerContext } from 'src/core/server';
|
||||
import { InfraServerPlugin } from './new_platform_plugin';
|
||||
import { InfraServerPlugin, InfraPluginSetup } from './new_platform_plugin';
|
||||
import { config, InfraConfig } from '../../../../plugins/infra/server';
|
||||
import { InfraServerPluginDeps } from './lib/adapters/framework';
|
||||
|
||||
export { config, InfraConfig, InfraServerPluginDeps };
|
||||
export { config, InfraConfig, InfraServerPluginDeps, InfraPluginSetup };
|
||||
|
||||
export function plugin(context: PluginInitializerContext) {
|
||||
return new InfraServerPlugin(context);
|
||||
|
|
|
@ -23,11 +23,19 @@ import { InfraSources } from './lib/sources';
|
|||
import { InfraServerPluginDeps } from './lib/adapters/framework';
|
||||
import { METRICS_FEATURE, LOGS_FEATURE } from './features';
|
||||
import { UsageCollector } from './usage/usage_collector';
|
||||
import { InfraStaticSourceConfiguration } from './lib/sources/types';
|
||||
|
||||
export interface KbnServer extends Server {
|
||||
usage: any;
|
||||
}
|
||||
|
||||
export interface InfraPluginSetup {
|
||||
defineInternalSourceConfiguration: (
|
||||
sourceId: string,
|
||||
sourceProperties: InfraStaticSourceConfiguration
|
||||
) => void;
|
||||
}
|
||||
|
||||
const DEFAULT_CONFIG: InfraConfig = {
|
||||
enabled: true,
|
||||
query: {
|
||||
|
@ -103,5 +111,11 @@ export class InfraServerPlugin {
|
|||
|
||||
// Telemetry
|
||||
UsageCollector.registerUsageCollector(plugins.usageCollection);
|
||||
|
||||
return {
|
||||
defineInternalSourceConfiguration(sourceId, sourceProperties) {
|
||||
sources.defineInternalSourceConfiguration(sourceId, sourceProperties);
|
||||
},
|
||||
} as InfraPluginSetup;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -33,12 +33,12 @@ export const initGetLogEntryRateRoute = ({ framework, logAnalysis }: InfraBacken
|
|||
},
|
||||
},
|
||||
async (requestContext, request, response) => {
|
||||
const payload = pipe(
|
||||
getLogEntryRateRequestPayloadRT.decode(request.body),
|
||||
fold(throwErrors(Boom.badRequest), identity)
|
||||
);
|
||||
|
||||
try {
|
||||
const payload = pipe(
|
||||
getLogEntryRateRequestPayloadRT.decode(request.body),
|
||||
fold(throwErrors(Boom.badRequest), identity)
|
||||
);
|
||||
|
||||
const logEntryRateBuckets = await logAnalysis.getLogEntryRateBuckets(
|
||||
requestContext,
|
||||
payload.data.sourceId,
|
||||
|
|
|
@ -4,15 +4,17 @@
|
|||
* 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 { getGroupings } from './lib/get_groupings';
|
||||
import { populateSeriesWithTSVBData } from './lib/populate_series_with_tsvb_data';
|
||||
import { MetricsExplorerRequestBody } from './types';
|
||||
// import { metricsExplorerSchema } from './schema';
|
||||
// import { MetricsExplorerResponse, MetricsExplorerRequestBody } from './types';
|
||||
import { metricsExplorerRequestBodyRT, metricsExplorerResponseRT } from '../../../common/http_api';
|
||||
import { throwErrors } from '../../../common/runtime_types';
|
||||
|
||||
// NP_TODO: need to replace all of this with real types or io-ts or something?
|
||||
const escapeHatch = schema.object({}, { allowUnknowns: true });
|
||||
|
||||
export const initMetricExplorerRoute = (libs: InfraBackendLibs) => {
|
||||
|
@ -29,20 +31,27 @@ export const initMetricExplorerRoute = (libs: InfraBackendLibs) => {
|
|||
},
|
||||
async (requestContext, request, response) => {
|
||||
try {
|
||||
const payload = pipe(
|
||||
metricsExplorerRequestBodyRT.decode(request.body),
|
||||
fold(throwErrors(Boom.badRequest), identity)
|
||||
);
|
||||
|
||||
const search = <Aggregation>(searchOptions: object) =>
|
||||
callWithRequest<{}, Aggregation>(requestContext, 'search', searchOptions);
|
||||
const options = request.body as MetricsExplorerRequestBody; // Need to remove this casting and swap in config-schema demands :(
|
||||
|
||||
// First we get the groupings from a composite aggregation
|
||||
const groupings = await getGroupings(search, options);
|
||||
const groupings = await getGroupings(search, payload);
|
||||
|
||||
// Then we take the results and fill in the data from TSVB with the
|
||||
// user's custom metrics
|
||||
const seriesWithMetrics = await Promise.all(
|
||||
groupings.series.map(
|
||||
populateSeriesWithTSVBData(request, options, framework, requestContext)
|
||||
populateSeriesWithTSVBData(request, payload, framework, requestContext)
|
||||
)
|
||||
);
|
||||
return response.ok({ body: { ...groupings, series: seriesWithMetrics } });
|
||||
return response.ok({
|
||||
body: metricsExplorerResponseRT.encode({ ...groupings, series: seriesWithMetrics }),
|
||||
});
|
||||
} catch (error) {
|
||||
return response.internalError({
|
||||
body: error.message,
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
*/
|
||||
|
||||
import { InfraMetricModelMetricType } from '../../../lib/adapters/metrics';
|
||||
import { MetricsExplorerAggregation, MetricsExplorerRequestBody } from '../types';
|
||||
import { MetricsExplorerRequestBody } from '../types';
|
||||
import { InfraMetric } from '../../../graphql/types';
|
||||
import { TSVBMetricModel } from '../../../../common/inventory_models/types';
|
||||
export const createMetricModel = (options: MetricsExplorerRequestBody): TSVBMetricModel => {
|
||||
|
@ -20,7 +20,7 @@ export const createMetricModel = (options: MetricsExplorerRequestBody): TSVBMetr
|
|||
// when the responses are processed and combined with the grouping request.
|
||||
series: options.metrics.map((metric, index) => {
|
||||
// If the metric is a rate then we need to add TSVB metrics for calculating the derivative
|
||||
if (metric.aggregation === MetricsExplorerAggregation.rate) {
|
||||
if (metric.aggregation === 'rate') {
|
||||
const aggType = 'max';
|
||||
return {
|
||||
id: `metric_${index}`,
|
||||
|
@ -49,8 +49,7 @@ export const createMetricModel = (options: MetricsExplorerRequestBody): TSVBMetr
|
|||
};
|
||||
}
|
||||
// Create a basic TSVB series with a single metric
|
||||
const aggregation =
|
||||
MetricsExplorerAggregation[metric.aggregation] || MetricsExplorerAggregation.avg;
|
||||
const aggregation = metric.aggregation || 'avg';
|
||||
|
||||
return {
|
||||
id: `metric_${index}`,
|
||||
|
|
|
@ -8,10 +8,10 @@ import { union } from 'lodash';
|
|||
import { KibanaRequest, RequestHandlerContext } from 'src/core/server';
|
||||
import { KibanaFramework } from '../../../lib/adapters/framework/kibana_framework_adapter';
|
||||
import {
|
||||
MetricsExplorerColumnType,
|
||||
MetricsExplorerRow,
|
||||
MetricsExplorerSeries,
|
||||
MetricsExplorerRequestBody,
|
||||
MetricsExplorerColumn,
|
||||
} from '../types';
|
||||
import { createMetricModel } from './create_metrics_model';
|
||||
import { JsonObject } from '../../../../common/typed_json';
|
||||
|
@ -95,11 +95,11 @@ export const populateSeriesWithTSVBData = (
|
|||
|
||||
// Setup the dynamic columns and row attributes depending on if the user is doing a group by
|
||||
// and multiple metrics
|
||||
const attributeColumns =
|
||||
options.groupBy != null ? [{ name: 'groupBy', type: MetricsExplorerColumnType.string }] : [];
|
||||
const metricColumns = options.metrics.map((m, i) => ({
|
||||
const attributeColumns: MetricsExplorerColumn[] =
|
||||
options.groupBy != null ? [{ name: 'groupBy', type: 'string' }] : [];
|
||||
const metricColumns: MetricsExplorerColumn[] = options.metrics.map((m, i) => ({
|
||||
name: `metric_${i}`,
|
||||
type: MetricsExplorerColumnType.number,
|
||||
type: 'number',
|
||||
}));
|
||||
const rowAttributes = options.groupBy != null ? { groupBy: series.id } : {};
|
||||
|
||||
|
@ -132,7 +132,7 @@ export const populateSeriesWithTSVBData = (
|
|||
...series,
|
||||
rows,
|
||||
columns: [
|
||||
{ name: 'timestamp', type: MetricsExplorerColumnType.date },
|
||||
{ name: 'timestamp', type: 'date' } as MetricsExplorerColumn,
|
||||
...metricColumns,
|
||||
...attributeColumns,
|
||||
],
|
||||
|
|
|
@ -1,41 +0,0 @@
|
|||
/*
|
||||
* 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 * as Joi from 'joi';
|
||||
import { values } from 'lodash';
|
||||
import { MetricsExplorerColor } from '../../../common/color_palette';
|
||||
import { MetricsExplorerAggregation } from './types';
|
||||
|
||||
export const metricsExplorerSchema = Joi.object({
|
||||
limit: Joi.number()
|
||||
.min(1)
|
||||
.default(9),
|
||||
afterKey: Joi.string().allow(null),
|
||||
groupBy: Joi.string().allow(null),
|
||||
indexPattern: Joi.string().required(),
|
||||
metrics: Joi.array()
|
||||
.items(
|
||||
Joi.object().keys({
|
||||
aggregation: Joi.string()
|
||||
.valid(values(MetricsExplorerAggregation))
|
||||
.required(),
|
||||
field: Joi.string(),
|
||||
rate: Joi.bool().default(false),
|
||||
color: Joi.string().valid(values(MetricsExplorerColor)),
|
||||
label: Joi.string(),
|
||||
})
|
||||
)
|
||||
.required(),
|
||||
filterQuery: Joi.string(),
|
||||
timerange: Joi.object()
|
||||
.keys({
|
||||
field: Joi.string().required(),
|
||||
from: Joi.number().required(),
|
||||
to: Joi.number().required(),
|
||||
interval: Joi.string().required(),
|
||||
})
|
||||
.required(),
|
||||
});
|
|
@ -4,65 +4,33 @@
|
|||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
export interface InfraTimerange {
|
||||
field: string;
|
||||
from: number;
|
||||
to: number;
|
||||
interval: string;
|
||||
}
|
||||
import * as rt from 'io-ts';
|
||||
import {
|
||||
metricsExplorerMetricRT,
|
||||
metricsExplorerPageInfoRT,
|
||||
metricsExplorerColumnRT,
|
||||
metricsExplorerRowRT,
|
||||
metricsExplorerSeriesRT,
|
||||
metricsExplorerRequestBodyRT,
|
||||
metricsExplorerResponseRT,
|
||||
metricsExplorerAggregationRT,
|
||||
metricsExplorerColumnTypeRT,
|
||||
} from '../../../common/http_api';
|
||||
|
||||
export enum MetricsExplorerAggregation {
|
||||
avg = 'avg',
|
||||
max = 'max',
|
||||
min = 'min',
|
||||
cardinality = 'cardinality',
|
||||
rate = 'rate',
|
||||
count = 'count',
|
||||
}
|
||||
export type MetricsExplorerAggregation = rt.TypeOf<typeof metricsExplorerAggregationRT>;
|
||||
|
||||
export interface MetricsExplorerMetric {
|
||||
aggregation: MetricsExplorerAggregation;
|
||||
field?: string | undefined;
|
||||
}
|
||||
export type MetricsExplorerColumnType = rt.TypeOf<typeof metricsExplorerColumnTypeRT>;
|
||||
|
||||
export interface MetricsExplorerRequestBody {
|
||||
timerange: InfraTimerange;
|
||||
indexPattern: string;
|
||||
metrics: MetricsExplorerMetric[];
|
||||
groupBy?: string;
|
||||
afterKey?: string;
|
||||
limit?: number;
|
||||
filterQuery?: string;
|
||||
}
|
||||
export type MetricsExplorerMetric = rt.TypeOf<typeof metricsExplorerMetricRT>;
|
||||
|
||||
export interface MetricsExplorerPageInfo {
|
||||
total: number;
|
||||
afterKey?: string | null;
|
||||
}
|
||||
export type MetricsExplorerPageInfo = rt.TypeOf<typeof metricsExplorerPageInfoRT>;
|
||||
|
||||
export enum MetricsExplorerColumnType {
|
||||
date = 'date',
|
||||
number = 'number',
|
||||
string = 'string',
|
||||
}
|
||||
export type MetricsExplorerColumn = rt.TypeOf<typeof metricsExplorerColumnRT>;
|
||||
|
||||
export interface MetricsExplorerColumn {
|
||||
name: string;
|
||||
type: MetricsExplorerColumnType;
|
||||
}
|
||||
export type MetricsExplorerRow = rt.TypeOf<typeof metricsExplorerRowRT>;
|
||||
|
||||
export interface MetricsExplorerRow {
|
||||
timestamp: number;
|
||||
[key: string]: string | number | null | undefined;
|
||||
}
|
||||
export type MetricsExplorerSeries = rt.TypeOf<typeof metricsExplorerSeriesRT>;
|
||||
|
||||
export interface MetricsExplorerSeries {
|
||||
id: string;
|
||||
columns: MetricsExplorerColumn[];
|
||||
rows: MetricsExplorerRow[];
|
||||
}
|
||||
export type MetricsExplorerRequestBody = rt.TypeOf<typeof metricsExplorerRequestBodyRT>;
|
||||
|
||||
export interface MetricsExplorerResponse {
|
||||
series: MetricsExplorerSeries[];
|
||||
pageInfo: MetricsExplorerPageInfo;
|
||||
}
|
||||
export type MetricsExplorerResponse = rt.TypeOf<typeof metricsExplorerResponseRT>;
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
{
|
||||
"id": "infra",
|
||||
"version": "8.0.0",
|
||||
"kibanaVersion": "kibana",
|
||||
"server": true
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue