[Logs UI / Metrics UI] New Platform migration server followups (#51615)

This commit is contained in:
Kerry Gallagher 2019-12-17 18:52:57 +00:00 committed by GitHub
parent fb1670c54b
commit 18b5cf9adb
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
36 changed files with 279 additions and 282 deletions

View file

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

View file

@ -6,3 +6,4 @@
export * from './log_analysis';
export * from './metadata_api';
export * from './metrics_explorer';

View file

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

View file

@ -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)

View file

@ -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,
}))}

View file

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

View file

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

View file

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

View file

@ -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()');
});
});

View file

@ -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(

View file

@ -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),

View file

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

View file

@ -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') {

View file

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

View file

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

View file

@ -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,

View file

@ -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,

View file

@ -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();

View file

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

View file

@ -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' },
]);
});
});

View file

@ -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,

View file

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

View file

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

View file

@ -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,

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -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,

View file

@ -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,

View file

@ -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}`,

View file

@ -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,
],

View file

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

View file

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

View file

@ -1,5 +1,6 @@
{
"id": "infra",
"version": "8.0.0",
"kibanaVersion": "kibana",
"server": true
}