mirror of
https://github.com/elastic/kibana.git
synced 2025-04-23 09:19:04 -04:00
[Infrastructure UI] Add strict payload validation to metrics_explorer_views endpoint (#160982)
closes [#157520](https://github.com/elastic/kibana/issues/157520) ## Summary This PR adds strict payload validation to `metrics_explorer_views` endpoint. This PR depends on this to be merged https://github.com/elastic/kibana/pull/160852 ### How to test - Call the endpoint below trying to use invalid values. see [here](https://github.com/elastic/kibana/pull/160982/files#diff-4573683b3b62cdf5f6426ec345b7ad6c7d6e6328237b213ca7519f686d8fa951R125-R131). ```bash [POST|PUT] kbn:/api/infra/metrics_explorer_views { "attributes": { "name": "Ad-hoc", "options": { "aggregation": "avg", "metrics": [ { "aggregation": "avg", "field": "system.cpu.total.norm.pct", "color": "color0" }, ], "source": "default", "groupBy": [ "host.name" ] }, "chartOptions": { "type": "line", "yAxisMode": "fromZero", "stack": false }, "currentTimerange": { "from": "now-1h", "to": "now", "interval": ">=10s" } } } ``` - Set up a local Kibana instance - Navigate to `Infrastructure > Metrics Explorer` - In the UI, use the Saved View feature and try different field combinations --------- Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com>
This commit is contained in:
parent
a9786dfd6b
commit
a0a83c1d3c
23 changed files with 432 additions and 311 deletions
|
@ -4,9 +4,9 @@
|
|||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
import { nonEmptyStringRt } from '@kbn/io-ts-utils';
|
||||
import * as rt from 'io-ts';
|
||||
import { either } from 'fp-ts/Either';
|
||||
import { metricsExplorerViewRT } from '../../../metrics_explorer_views';
|
||||
|
||||
export const METRICS_EXPLORER_VIEW_URL = '/api/infra/metrics_explorer_views';
|
||||
export const METRICS_EXPLORER_VIEW_URL_ENTITY = `${METRICS_EXPLORER_VIEW_URL}/{metricsExplorerViewId}`;
|
||||
|
@ -35,28 +35,6 @@ export const metricsExplorerViewRequestQueryRT = rt.partial({
|
|||
|
||||
export type MetricsExplorerViewRequestQuery = rt.TypeOf<typeof metricsExplorerViewRequestQueryRT>;
|
||||
|
||||
const metricsExplorerViewAttributesResponseRT = rt.intersection([
|
||||
rt.strict({
|
||||
name: nonEmptyStringRt,
|
||||
isDefault: rt.boolean,
|
||||
isStatic: rt.boolean,
|
||||
}),
|
||||
rt.UnknownRecord,
|
||||
]);
|
||||
|
||||
const metricsExplorerViewResponseRT = rt.exact(
|
||||
rt.intersection([
|
||||
rt.type({
|
||||
id: rt.string,
|
||||
attributes: metricsExplorerViewAttributesResponseRT,
|
||||
}),
|
||||
rt.partial({
|
||||
updatedAt: rt.number,
|
||||
version: rt.string,
|
||||
}),
|
||||
])
|
||||
);
|
||||
|
||||
export const metricsExplorerViewResponsePayloadRT = rt.type({
|
||||
data: metricsExplorerViewResponseRT,
|
||||
data: metricsExplorerViewRT,
|
||||
});
|
||||
|
|
|
@ -5,15 +5,15 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import { nonEmptyStringRt } from '@kbn/io-ts-utils';
|
||||
import * as rt from 'io-ts';
|
||||
import {
|
||||
metricsExplorerViewAttributesRT,
|
||||
metricsExplorerViewRT,
|
||||
} from '../../../metrics_explorer_views';
|
||||
|
||||
export const createMetricsExplorerViewAttributesRequestPayloadRT = rt.intersection([
|
||||
rt.type({
|
||||
name: nonEmptyStringRt,
|
||||
}),
|
||||
rt.UnknownRecord,
|
||||
rt.exact(rt.partial({ isDefault: rt.undefined, isStatic: rt.undefined })),
|
||||
metricsExplorerViewAttributesRT,
|
||||
rt.partial({ isDefault: rt.undefined, isStatic: rt.undefined }),
|
||||
]);
|
||||
|
||||
export type CreateMetricsExplorerViewAttributesRequestPayload = rt.TypeOf<
|
||||
|
@ -23,3 +23,5 @@ export type CreateMetricsExplorerViewAttributesRequestPayload = rt.TypeOf<
|
|||
export const createMetricsExplorerViewRequestPayloadRT = rt.type({
|
||||
attributes: createMetricsExplorerViewAttributesRequestPayloadRT,
|
||||
});
|
||||
|
||||
export type CreateMetricsExplorerViewResponsePayload = rt.TypeOf<typeof metricsExplorerViewRT>;
|
||||
|
|
|
@ -5,28 +5,13 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import { nonEmptyStringRt } from '@kbn/io-ts-utils';
|
||||
import * as rt from 'io-ts';
|
||||
|
||||
export const findMetricsExplorerViewAttributesResponseRT = rt.strict({
|
||||
name: nonEmptyStringRt,
|
||||
isDefault: rt.boolean,
|
||||
isStatic: rt.boolean,
|
||||
});
|
||||
|
||||
const findMetricsExplorerViewResponseRT = rt.exact(
|
||||
rt.intersection([
|
||||
rt.type({
|
||||
id: rt.string,
|
||||
attributes: findMetricsExplorerViewAttributesResponseRT,
|
||||
}),
|
||||
rt.partial({
|
||||
updatedAt: rt.number,
|
||||
version: rt.string,
|
||||
}),
|
||||
])
|
||||
);
|
||||
import { singleMetricsExplorerViewRT } from '../../../metrics_explorer_views';
|
||||
|
||||
export const findMetricsExplorerViewResponsePayloadRT = rt.type({
|
||||
data: rt.array(findMetricsExplorerViewResponseRT),
|
||||
data: rt.array(singleMetricsExplorerViewRT),
|
||||
});
|
||||
|
||||
export type FindMetricsExplorerViewResponsePayload = rt.TypeOf<
|
||||
typeof findMetricsExplorerViewResponsePayloadRT
|
||||
>;
|
||||
|
|
|
@ -6,7 +6,10 @@
|
|||
*/
|
||||
|
||||
import * as rt from 'io-ts';
|
||||
import { metricsExplorerViewRT } from '../../../metrics_explorer_views';
|
||||
|
||||
export const getMetricsExplorerViewRequestParamsRT = rt.type({
|
||||
metricsExplorerViewId: rt.string,
|
||||
});
|
||||
|
||||
export type GetMetricsExplorerViewResponsePayload = rt.TypeOf<typeof metricsExplorerViewRT>;
|
||||
|
|
|
@ -5,15 +5,15 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import { nonEmptyStringRt } from '@kbn/io-ts-utils';
|
||||
import * as rt from 'io-ts';
|
||||
import {
|
||||
metricsExplorerViewAttributesRT,
|
||||
metricsExplorerViewRT,
|
||||
} from '../../../metrics_explorer_views';
|
||||
|
||||
export const updateMetricsExplorerViewAttributesRequestPayloadRT = rt.intersection([
|
||||
rt.type({
|
||||
name: nonEmptyStringRt,
|
||||
}),
|
||||
rt.UnknownRecord,
|
||||
rt.exact(rt.partial({ isDefault: rt.undefined, isStatic: rt.undefined })),
|
||||
metricsExplorerViewAttributesRT,
|
||||
rt.partial({ isDefault: rt.undefined, isStatic: rt.undefined }),
|
||||
]);
|
||||
|
||||
export type UpdateMetricsExplorerViewAttributesRequestPayload = rt.TypeOf<
|
||||
|
@ -23,3 +23,5 @@ export type UpdateMetricsExplorerViewAttributesRequestPayload = rt.TypeOf<
|
|||
export const updateMetricsExplorerViewRequestPayloadRT = rt.type({
|
||||
attributes: updateMetricsExplorerViewAttributesRequestPayloadRT,
|
||||
});
|
||||
|
||||
export type UpdateMetricsExplorerViewResponsePayload = rt.TypeOf<typeof metricsExplorerViewRT>;
|
||||
|
|
|
@ -7,7 +7,12 @@
|
|||
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import type { NonEmptyString } from '@kbn/io-ts-utils';
|
||||
import type { MetricsExplorerViewAttributes } from './types';
|
||||
import { Color } from '../color_palette';
|
||||
import {
|
||||
MetricsExplorerChartType,
|
||||
MetricsExplorerViewAttributes,
|
||||
MetricsExplorerYAxisMode,
|
||||
} from './types';
|
||||
|
||||
export const staticMetricsExplorerViewId = '0';
|
||||
|
||||
|
@ -23,24 +28,24 @@ export const staticMetricsExplorerViewAttributes: MetricsExplorerViewAttributes
|
|||
{
|
||||
aggregation: 'avg',
|
||||
field: 'system.cpu.total.norm.pct',
|
||||
color: 'color0',
|
||||
color: Color.color0,
|
||||
},
|
||||
{
|
||||
aggregation: 'avg',
|
||||
field: 'kubernetes.pod.cpu.usage.node.pct',
|
||||
color: 'color1',
|
||||
color: Color.color1,
|
||||
},
|
||||
{
|
||||
aggregation: 'avg',
|
||||
field: 'docker.cpu.total.pct',
|
||||
color: 'color2',
|
||||
color: Color.color2,
|
||||
},
|
||||
],
|
||||
source: 'default',
|
||||
},
|
||||
chartOptions: {
|
||||
type: 'line',
|
||||
yAxisMode: 'fromZero',
|
||||
type: MetricsExplorerChartType.line,
|
||||
yAxisMode: MetricsExplorerYAxisMode.fromZero,
|
||||
stack: false,
|
||||
},
|
||||
currentTimerange: {
|
||||
|
|
|
@ -5,19 +5,101 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import { nonEmptyStringRt } from '@kbn/io-ts-utils';
|
||||
import { isoToEpochRt, nonEmptyStringRt } from '@kbn/io-ts-utils';
|
||||
import * as rt from 'io-ts';
|
||||
import { Color } from '../color_palette';
|
||||
import {
|
||||
metricsExplorerAggregationRT,
|
||||
metricsExplorerMetricRT,
|
||||
} from '../http_api/metrics_explorer';
|
||||
|
||||
export const metricsExplorerViewAttributesRT = rt.intersection([
|
||||
rt.type({
|
||||
name: nonEmptyStringRt,
|
||||
isDefault: rt.boolean,
|
||||
isStatic: rt.boolean,
|
||||
export const inventorySortOptionRT = rt.type({
|
||||
by: rt.keyof({ name: null, value: null }),
|
||||
direction: rt.keyof({ asc: null, desc: null }),
|
||||
});
|
||||
|
||||
export enum MetricsExplorerChartType {
|
||||
line = 'line',
|
||||
area = 'area',
|
||||
bar = 'bar',
|
||||
}
|
||||
|
||||
export enum MetricsExplorerYAxisMode {
|
||||
fromZero = 'fromZero',
|
||||
auto = 'auto',
|
||||
}
|
||||
|
||||
export const metricsExplorerChartOptionsRT = rt.type({
|
||||
yAxisMode: rt.keyof(
|
||||
Object.fromEntries(Object.values(MetricsExplorerYAxisMode).map((v) => [v, null])) as Record<
|
||||
MetricsExplorerYAxisMode,
|
||||
null
|
||||
>
|
||||
),
|
||||
type: rt.keyof(
|
||||
Object.fromEntries(Object.values(MetricsExplorerChartType).map((v) => [v, null])) as Record<
|
||||
MetricsExplorerChartType,
|
||||
null
|
||||
>
|
||||
),
|
||||
stack: rt.boolean,
|
||||
});
|
||||
|
||||
export const metricsExplorerTimeOptionsRT = rt.type({
|
||||
from: rt.string,
|
||||
to: rt.string,
|
||||
interval: rt.string,
|
||||
});
|
||||
const metricsExplorerOptionsMetricRT = rt.intersection([
|
||||
metricsExplorerMetricRT,
|
||||
rt.partial({
|
||||
rate: rt.boolean,
|
||||
color: rt.keyof(
|
||||
Object.fromEntries(Object.values(Color).map((c) => [c, null])) as Record<Color, null>
|
||||
),
|
||||
label: rt.string,
|
||||
}),
|
||||
rt.UnknownRecord,
|
||||
]);
|
||||
|
||||
export type MetricsExplorerViewAttributes = rt.TypeOf<typeof metricsExplorerViewAttributesRT>;
|
||||
export const metricExplorerOptionsRequiredRT = rt.type({
|
||||
aggregation: metricsExplorerAggregationRT,
|
||||
metrics: rt.array(metricsExplorerOptionsMetricRT),
|
||||
});
|
||||
|
||||
export const metricExplorerOptionsOptionalRT = rt.partial({
|
||||
limit: rt.number,
|
||||
groupBy: rt.union([rt.string, rt.array(rt.string)]),
|
||||
filterQuery: rt.string,
|
||||
source: rt.string,
|
||||
forceInterval: rt.boolean,
|
||||
dropLastBucket: rt.boolean,
|
||||
});
|
||||
export const metricsExplorerOptionsRT = rt.intersection([
|
||||
metricExplorerOptionsRequiredRT,
|
||||
metricExplorerOptionsOptionalRT,
|
||||
]);
|
||||
|
||||
export const metricExplorerViewStateRT = rt.type({
|
||||
chartOptions: metricsExplorerChartOptionsRT,
|
||||
currentTimerange: metricsExplorerTimeOptionsRT,
|
||||
options: metricsExplorerOptionsRT,
|
||||
});
|
||||
|
||||
export const metricsExplorerViewBasicAttributesRT = rt.type({
|
||||
name: nonEmptyStringRt,
|
||||
});
|
||||
|
||||
const metricsExplorerViewFlagsRT = rt.partial({ isDefault: rt.boolean, isStatic: rt.boolean });
|
||||
|
||||
export const metricsExplorerViewAttributesRT = rt.intersection([
|
||||
metricExplorerViewStateRT,
|
||||
metricsExplorerViewBasicAttributesRT,
|
||||
metricsExplorerViewFlagsRT,
|
||||
]);
|
||||
|
||||
const singleMetricsExplorerViewAttributesRT = rt.exact(
|
||||
rt.intersection([metricsExplorerViewBasicAttributesRT, metricsExplorerViewFlagsRT])
|
||||
);
|
||||
|
||||
export const metricsExplorerViewRT = rt.exact(
|
||||
rt.intersection([
|
||||
|
@ -26,10 +108,29 @@ export const metricsExplorerViewRT = rt.exact(
|
|||
attributes: metricsExplorerViewAttributesRT,
|
||||
}),
|
||||
rt.partial({
|
||||
updatedAt: rt.number,
|
||||
updatedAt: isoToEpochRt,
|
||||
version: rt.string,
|
||||
}),
|
||||
])
|
||||
);
|
||||
|
||||
export const singleMetricsExplorerViewRT = rt.exact(
|
||||
rt.intersection([
|
||||
rt.type({
|
||||
id: rt.string,
|
||||
attributes: singleMetricsExplorerViewAttributesRT,
|
||||
}),
|
||||
rt.partial({
|
||||
updatedAt: isoToEpochRt,
|
||||
version: rt.string,
|
||||
}),
|
||||
])
|
||||
);
|
||||
|
||||
export type MetricsExplorerChartOptions = rt.TypeOf<typeof metricsExplorerChartOptionsRT>;
|
||||
export type MetricsExplorerOptions = rt.TypeOf<typeof metricsExplorerOptionsRT>;
|
||||
export type MetricsExplorerOptionsMetric = rt.TypeOf<typeof metricsExplorerOptionsMetricRT>;
|
||||
export type MetricsExplorerViewState = rt.TypeOf<typeof metricExplorerViewStateRT>;
|
||||
export type MetricsExplorerTimeOptions = rt.TypeOf<typeof metricsExplorerTimeOptionsRT>;
|
||||
export type MetricsExplorerViewAttributes = rt.TypeOf<typeof metricsExplorerViewAttributesRT>;
|
||||
export type MetricsExplorerView = rt.TypeOf<typeof metricsExplorerViewRT>;
|
||||
|
|
|
@ -13,7 +13,7 @@ import { MetricsSourceConfiguration } from '../../../../common/metrics_sources';
|
|||
import { MetricExpression, TimeRange } from '../types';
|
||||
import {
|
||||
MetricsExplorerOptions,
|
||||
MetricsExplorerTimestampsRT,
|
||||
MetricsExplorerTimestamp,
|
||||
} from '../../../pages/metrics/metrics_explorer/hooks/use_metrics_explorer_options';
|
||||
import { useMetricsExplorerData } from '../../../pages/metrics/metrics_explorer/hooks/use_metrics_explorer_data';
|
||||
import { MetricExplorerCustomMetricAggregations } from '../../../../common/http_api/metrics_explorer';
|
||||
|
@ -59,7 +59,7 @@ export const useMetricsExplorerChartData = (
|
|||
groupBy,
|
||||
]
|
||||
);
|
||||
const timestamps: MetricsExplorerTimestampsRT = useMemo(() => {
|
||||
const timestamps: MetricsExplorerTimestamp = useMemo(() => {
|
||||
const from = timeRange.from ?? `now-${(timeSize || 1) * 20}${timeUnit}`;
|
||||
const to = timeRange.to ?? 'now';
|
||||
const fromTimestamp = DateMath.parse(from)!.valueOf();
|
||||
|
|
|
@ -10,11 +10,11 @@ import React, { useMemo } from 'react';
|
|||
import { ThrowReporter } from 'io-ts/lib/ThrowReporter';
|
||||
import { UrlStateContainer } from '../../utils/url_state';
|
||||
import {
|
||||
MetricsExplorerOptions,
|
||||
type MetricsExplorerOptions,
|
||||
type MetricsExplorerTimeOptions,
|
||||
type MetricsExplorerChartOptions,
|
||||
useMetricsExplorerOptionsContainerContext,
|
||||
MetricsExplorerTimeOptions,
|
||||
MetricsExplorerChartOptions,
|
||||
metricExplorerOptionsRT,
|
||||
metricsExplorerOptionsRT,
|
||||
metricsExplorerChartOptionsRT,
|
||||
metricsExplorerTimeOptionsRT,
|
||||
} from '../../pages/metrics/metrics_explorer/hooks/use_metrics_explorer_options';
|
||||
|
@ -73,7 +73,7 @@ export const WithMetricsExplorerOptionsUrlState = () => {
|
|||
};
|
||||
|
||||
function isMetricExplorerOptions(subject: any): subject is MetricsExplorerOptions {
|
||||
const result = metricExplorerOptionsRT.decode(subject);
|
||||
const result = metricsExplorerOptionsRT.decode(subject);
|
||||
|
||||
try {
|
||||
ThrowReporter.report(result);
|
||||
|
|
|
@ -19,7 +19,10 @@ import {
|
|||
UpdateViewParams,
|
||||
} from '../../common/saved_views';
|
||||
import { MetricsSourceConfigurationResponse } from '../../common/metrics_sources';
|
||||
import { CreateInventoryViewAttributesRequestPayload } from '../../common/http_api/latest';
|
||||
import {
|
||||
CreateInventoryViewAttributesRequestPayload,
|
||||
UpdateInventoryViewAttributesRequestPayload,
|
||||
} from '../../common/http_api/latest';
|
||||
import type { InventoryView } from '../../common/inventory_views';
|
||||
import { useKibanaContextForPlugin } from './use_kibana';
|
||||
import { useUrlState } from '../utils/use_url_state';
|
||||
|
@ -133,7 +136,7 @@ export const useInventoryViews = (): UseInventoryViewsResult => {
|
|||
const { mutateAsync: updateViewById, isLoading: isUpdatingView } = useMutation<
|
||||
InventoryView,
|
||||
ServerError,
|
||||
UpdateViewParams<CreateInventoryViewAttributesRequestPayload>
|
||||
UpdateViewParams<UpdateInventoryViewAttributesRequestPayload>
|
||||
>({
|
||||
mutationFn: ({ id, attributes }) => inventoryViews.client.updateInventoryView(id, attributes),
|
||||
onError: (error) => {
|
||||
|
|
|
@ -19,7 +19,10 @@ import {
|
|||
UpdateViewParams,
|
||||
} from '../../common/saved_views';
|
||||
import { MetricsSourceConfigurationResponse } from '../../common/metrics_sources';
|
||||
import { CreateMetricsExplorerViewAttributesRequestPayload } from '../../common/http_api/latest';
|
||||
import {
|
||||
CreateMetricsExplorerViewAttributesRequestPayload,
|
||||
UpdateMetricsExplorerViewAttributesRequestPayload,
|
||||
} from '../../common/http_api/latest';
|
||||
import { MetricsExplorerView } from '../../common/metrics_explorer_views';
|
||||
import { useKibanaContextForPlugin } from './use_kibana';
|
||||
import { useUrlState } from '../utils/use_url_state';
|
||||
|
@ -133,7 +136,7 @@ export const useMetricsExplorerViews = (): UseMetricsExplorerViewsResult => {
|
|||
const { mutateAsync: updateViewById, isLoading: isUpdatingView } = useMutation<
|
||||
MetricsExplorerView,
|
||||
ServerError,
|
||||
UpdateViewParams<CreateMetricsExplorerViewAttributesRequestPayload>
|
||||
UpdateViewParams<UpdateMetricsExplorerViewAttributesRequestPayload>
|
||||
>({
|
||||
mutationFn: ({ id, attributes }) =>
|
||||
metricsExplorerViews.client.updateMetricsExplorerView(id, attributes),
|
||||
|
|
|
@ -8,10 +8,10 @@
|
|||
import React from 'react';
|
||||
import { useMetricsExplorerViews } from '../../../../hooks/use_metrics_explorer_views';
|
||||
import { SavedViewsToolbarControls } from '../../../../components/saved_views/toolbar_control';
|
||||
import { MetricExplorerViewState } from '../hooks/use_metric_explorer_state';
|
||||
import { MetricsExplorerViewState } from '../hooks/use_metric_explorer_state';
|
||||
|
||||
interface Props {
|
||||
viewState: MetricExplorerViewState;
|
||||
viewState: MetricsExplorerViewState;
|
||||
}
|
||||
|
||||
export const SavedViews = ({ viewState }: Props) => {
|
||||
|
@ -31,7 +31,7 @@ export const SavedViews = ({ viewState }: Props) => {
|
|||
} = useMetricsExplorerViews();
|
||||
|
||||
return (
|
||||
<SavedViewsToolbarControls<any, MetricExplorerViewState>
|
||||
<SavedViewsToolbarControls
|
||||
currentView={currentView}
|
||||
views={views}
|
||||
isFetchingViews={isFetchingViews}
|
||||
|
|
|
@ -8,26 +8,22 @@
|
|||
import DateMath from '@kbn/datemath';
|
||||
import { useCallback, useEffect } from 'react';
|
||||
import { DataViewBase } from '@kbn/es-query';
|
||||
import { MetricsExplorerView } from '../../../../../common/metrics_explorer_views';
|
||||
import type {
|
||||
MetricsExplorerChartOptions,
|
||||
MetricsExplorerOptions,
|
||||
MetricsExplorerTimeOptions,
|
||||
MetricsExplorerView,
|
||||
MetricsExplorerViewState,
|
||||
} from '../../../../../common/metrics_explorer_views';
|
||||
import { MetricsSourceConfigurationProperties } from '../../../../../common/metrics_sources';
|
||||
import {
|
||||
MetricsExplorerMetric,
|
||||
MetricsExplorerAggregation,
|
||||
} from '../../../../../common/http_api/metrics_explorer';
|
||||
import { useMetricsExplorerData } from './use_metrics_explorer_data';
|
||||
import {
|
||||
useMetricsExplorerOptionsContainerContext,
|
||||
MetricsExplorerChartOptions,
|
||||
MetricsExplorerTimeOptions,
|
||||
MetricsExplorerOptions,
|
||||
} from './use_metrics_explorer_options';
|
||||
import { useMetricsExplorerOptionsContainerContext } from './use_metrics_explorer_options';
|
||||
|
||||
export interface MetricExplorerViewState {
|
||||
chartOptions: MetricsExplorerChartOptions;
|
||||
currentTimerange: MetricsExplorerTimeOptions;
|
||||
options: MetricsExplorerOptions;
|
||||
id?: string;
|
||||
}
|
||||
export type { MetricsExplorerViewState };
|
||||
|
||||
export const useMetricsExplorerState = (
|
||||
source: MetricsSourceConfigurationProperties,
|
||||
|
|
|
@ -20,10 +20,7 @@ import {
|
|||
resp,
|
||||
createSeries,
|
||||
} from '../../../../utils/fixtures/metrics_explorer';
|
||||
import {
|
||||
MetricsExplorerOptions,
|
||||
MetricsExplorerTimestampsRT,
|
||||
} from './use_metrics_explorer_options';
|
||||
import { MetricsExplorerOptions, MetricsExplorerTimestamp } from './use_metrics_explorer_options';
|
||||
import { DataViewBase } from '@kbn/es-query';
|
||||
import { MetricsSourceConfigurationProperties } from '../../../../../common/metrics_sources';
|
||||
|
||||
|
@ -56,7 +53,7 @@ const renderUseMetricsExplorerDataHook = () => {
|
|||
options: MetricsExplorerOptions;
|
||||
source: MetricsSourceConfigurationProperties | undefined;
|
||||
derivedIndexPattern: DataViewBase;
|
||||
timestamps: MetricsExplorerTimestampsRT;
|
||||
timestamps: MetricsExplorerTimestamp;
|
||||
}) =>
|
||||
useMetricsExplorerData(
|
||||
props.options,
|
||||
|
|
|
@ -15,17 +15,14 @@ import {
|
|||
metricsExplorerResponseRT,
|
||||
} from '../../../../../common/http_api/metrics_explorer';
|
||||
import { convertKueryToElasticSearchQuery } from '../../../../utils/kuery';
|
||||
import {
|
||||
MetricsExplorerOptions,
|
||||
MetricsExplorerTimestampsRT,
|
||||
} from './use_metrics_explorer_options';
|
||||
import { MetricsExplorerOptions, MetricsExplorerTimestamp } from './use_metrics_explorer_options';
|
||||
import { decodeOrThrow } from '../../../../../common/runtime_types';
|
||||
|
||||
export function useMetricsExplorerData(
|
||||
options: MetricsExplorerOptions,
|
||||
source: MetricsSourceConfigurationProperties | undefined,
|
||||
derivedIndexPattern: DataViewBase,
|
||||
{ fromTimestamp, toTimestamp, interval }: MetricsExplorerTimestampsRT,
|
||||
{ fromTimestamp, toTimestamp, interval }: MetricsExplorerTimestamp,
|
||||
enabled = true
|
||||
) {
|
||||
const { http } = useKibana().services;
|
||||
|
|
|
@ -7,91 +7,48 @@
|
|||
|
||||
import DateMath from '@kbn/datemath';
|
||||
import * as t from 'io-ts';
|
||||
import { values } from 'lodash';
|
||||
import createContainer from 'constate';
|
||||
import type { TimeRange } from '@kbn/es-query';
|
||||
import { useState, useEffect, useMemo, Dispatch, SetStateAction } from 'react';
|
||||
import {
|
||||
type MetricsExplorerChartOptions,
|
||||
type MetricsExplorerOptions,
|
||||
type MetricsExplorerOptionsMetric,
|
||||
type MetricsExplorerTimeOptions,
|
||||
MetricsExplorerYAxisMode,
|
||||
MetricsExplorerChartType,
|
||||
metricsExplorerOptionsRT,
|
||||
metricsExplorerChartOptionsRT,
|
||||
metricsExplorerTimeOptionsRT,
|
||||
} from '../../../../../common/metrics_explorer_views';
|
||||
import { useAlertPrefillContext } from '../../../../alerting/use_alert_prefill';
|
||||
import { Color } from '../../../../../common/color_palette';
|
||||
import { metricsExplorerMetricRT } from '../../../../../common/http_api/metrics_explorer';
|
||||
import {
|
||||
useKibanaTimefilterTime,
|
||||
useSyncKibanaTimeFilterTime,
|
||||
} from '../../../../hooks/use_kibana_timefilter_time';
|
||||
|
||||
const metricsExplorerOptionsMetricRT = t.intersection([
|
||||
metricsExplorerMetricRT,
|
||||
t.partial({
|
||||
rate: t.boolean,
|
||||
color: t.keyof(Object.fromEntries(values(Color).map((c) => [c, null])) as Record<Color, null>),
|
||||
label: t.string,
|
||||
}),
|
||||
]);
|
||||
|
||||
export type MetricsExplorerOptionsMetric = t.TypeOf<typeof metricsExplorerOptionsMetricRT>;
|
||||
|
||||
export enum MetricsExplorerChartType {
|
||||
line = 'line',
|
||||
area = 'area',
|
||||
bar = 'bar',
|
||||
}
|
||||
|
||||
export enum MetricsExplorerYAxisMode {
|
||||
fromZero = 'fromZero',
|
||||
auto = 'auto',
|
||||
}
|
||||
|
||||
export const metricsExplorerChartOptionsRT = t.type({
|
||||
yAxisMode: t.keyof(
|
||||
Object.fromEntries(values(MetricsExplorerYAxisMode).map((v) => [v, null])) as Record<
|
||||
MetricsExplorerYAxisMode,
|
||||
null
|
||||
>
|
||||
),
|
||||
type: t.keyof(
|
||||
Object.fromEntries(values(MetricsExplorerChartType).map((v) => [v, null])) as Record<
|
||||
MetricsExplorerChartType,
|
||||
null
|
||||
>
|
||||
),
|
||||
stack: t.boolean,
|
||||
});
|
||||
|
||||
export type MetricsExplorerChartOptions = t.TypeOf<typeof metricsExplorerChartOptionsRT>;
|
||||
|
||||
const metricExplorerOptionsRequiredRT = t.type({
|
||||
aggregation: t.string,
|
||||
metrics: t.array(metricsExplorerOptionsMetricRT),
|
||||
});
|
||||
|
||||
const metricExplorerOptionsOptionalRT = t.partial({
|
||||
limit: t.number,
|
||||
groupBy: t.union([t.string, t.array(t.string)]),
|
||||
filterQuery: t.string,
|
||||
source: t.string,
|
||||
forceInterval: t.boolean,
|
||||
dropLastBucket: t.boolean,
|
||||
});
|
||||
export const metricExplorerOptionsRT = t.intersection([
|
||||
metricExplorerOptionsRequiredRT,
|
||||
metricExplorerOptionsOptionalRT,
|
||||
]);
|
||||
|
||||
export type MetricsExplorerOptions = t.TypeOf<typeof metricExplorerOptionsRT>;
|
||||
|
||||
export const metricsExplorerTimestampsRT = t.type({
|
||||
fromTimestamp: t.number,
|
||||
toTimestamp: t.number,
|
||||
interval: t.string,
|
||||
});
|
||||
export type MetricsExplorerTimestampsRT = t.TypeOf<typeof metricsExplorerTimestampsRT>;
|
||||
|
||||
export const metricsExplorerTimeOptionsRT = t.type({
|
||||
from: t.string,
|
||||
to: t.string,
|
||||
interval: t.string,
|
||||
});
|
||||
export type MetricsExplorerTimeOptions = t.TypeOf<typeof metricsExplorerTimeOptionsRT>;
|
||||
export type {
|
||||
MetricsExplorerOptions,
|
||||
MetricsExplorerTimeOptions,
|
||||
MetricsExplorerChartOptions,
|
||||
MetricsExplorerOptionsMetric,
|
||||
};
|
||||
|
||||
export {
|
||||
MetricsExplorerYAxisMode,
|
||||
MetricsExplorerChartType,
|
||||
metricsExplorerOptionsRT,
|
||||
metricsExplorerChartOptionsRT,
|
||||
metricsExplorerTimeOptionsRT,
|
||||
};
|
||||
export type MetricsExplorerTimestamp = t.TypeOf<typeof metricsExplorerTimestampsRT>;
|
||||
|
||||
export const DEFAULT_TIMERANGE: MetricsExplorerTimeOptions = {
|
||||
from: 'now-1h',
|
||||
|
@ -182,7 +139,7 @@ export const useMetricsExplorerOptions = () => {
|
|||
to,
|
||||
interval: DEFAULT_TIMERANGE.interval,
|
||||
});
|
||||
const [timestamps, setTimestamps] = useState<MetricsExplorerTimestampsRT>(
|
||||
const [timestamps, setTimestamps] = useState<MetricsExplorerTimestamp>(
|
||||
getDefaultTimeRange({ from, to })
|
||||
);
|
||||
|
||||
|
|
|
@ -7,17 +7,20 @@
|
|||
|
||||
import { HttpStart } from '@kbn/core/public';
|
||||
import {
|
||||
CreateMetricsExplorerViewAttributesRequestPayload,
|
||||
CreateMetricsExplorerViewResponsePayload,
|
||||
createMetricsExplorerViewRequestPayloadRT,
|
||||
FindMetricsExplorerViewResponsePayload,
|
||||
findMetricsExplorerViewResponsePayloadRT,
|
||||
GetMetricsExplorerViewResponsePayload,
|
||||
getMetricsExplorerViewUrl,
|
||||
metricsExplorerViewResponsePayloadRT,
|
||||
UpdateMetricsExplorerViewResponsePayload,
|
||||
CreateMetricsExplorerViewAttributesRequestPayload,
|
||||
UpdateMetricsExplorerViewAttributesRequestPayload,
|
||||
} from '../../../common/http_api/latest';
|
||||
import {
|
||||
DeleteMetricsExplorerViewError,
|
||||
FetchMetricsExplorerViewError,
|
||||
MetricsExplorerView,
|
||||
UpsertMetricsExplorerViewError,
|
||||
} from '../../../common/metrics_explorer_views';
|
||||
import { decodeOrThrow } from '../../../common/runtime_types';
|
||||
|
@ -26,7 +29,7 @@ import { IMetricsExplorerViewsClient } from './types';
|
|||
export class MetricsExplorerViewsClient implements IMetricsExplorerViewsClient {
|
||||
constructor(private readonly http: HttpStart) {}
|
||||
|
||||
async findMetricsExplorerViews(): Promise<MetricsExplorerView[]> {
|
||||
async findMetricsExplorerViews(): Promise<FindMetricsExplorerViewResponsePayload['data']> {
|
||||
const response = await this.http.get(getMetricsExplorerViewUrl()).catch((error) => {
|
||||
throw new FetchMetricsExplorerViewError(`Failed to fetch metrics explorer views: ${error}`);
|
||||
});
|
||||
|
@ -40,7 +43,9 @@ export class MetricsExplorerViewsClient implements IMetricsExplorerViewsClient {
|
|||
return data;
|
||||
}
|
||||
|
||||
async getMetricsExplorerView(metricsExplorerViewId: string): Promise<MetricsExplorerView> {
|
||||
async getMetricsExplorerView(
|
||||
metricsExplorerViewId: string
|
||||
): Promise<GetMetricsExplorerViewResponsePayload> {
|
||||
const response = await this.http
|
||||
.get(getMetricsExplorerViewUrl(metricsExplorerViewId))
|
||||
.catch((error) => {
|
||||
|
@ -62,7 +67,7 @@ export class MetricsExplorerViewsClient implements IMetricsExplorerViewsClient {
|
|||
|
||||
async createMetricsExplorerView(
|
||||
metricsExplorerViewAttributes: CreateMetricsExplorerViewAttributesRequestPayload
|
||||
): Promise<MetricsExplorerView> {
|
||||
): Promise<CreateMetricsExplorerViewResponsePayload> {
|
||||
const response = await this.http
|
||||
.post(getMetricsExplorerViewUrl(), {
|
||||
body: JSON.stringify(
|
||||
|
@ -91,7 +96,7 @@ export class MetricsExplorerViewsClient implements IMetricsExplorerViewsClient {
|
|||
async updateMetricsExplorerView(
|
||||
metricsExplorerViewId: string,
|
||||
metricsExplorerViewAttributes: UpdateMetricsExplorerViewAttributesRequestPayload
|
||||
): Promise<MetricsExplorerView> {
|
||||
): Promise<UpdateMetricsExplorerViewResponsePayload> {
|
||||
const response = await this.http
|
||||
.put(getMetricsExplorerViewUrl(metricsExplorerViewId), {
|
||||
body: JSON.stringify(
|
||||
|
|
|
@ -7,9 +7,12 @@
|
|||
|
||||
import { HttpStart } from '@kbn/core/public';
|
||||
import {
|
||||
MetricsExplorerView,
|
||||
MetricsExplorerViewAttributes,
|
||||
} from '../../../common/metrics_explorer_views';
|
||||
FindMetricsExplorerViewResponsePayload,
|
||||
CreateMetricsExplorerViewResponsePayload,
|
||||
UpdateMetricsExplorerViewResponsePayload,
|
||||
GetMetricsExplorerViewResponsePayload,
|
||||
} from '../../../common/http_api';
|
||||
import { MetricsExplorerViewAttributes } from '../../../common/metrics_explorer_views';
|
||||
|
||||
export type MetricsExplorerViewsServiceSetup = void;
|
||||
|
||||
|
@ -22,14 +25,16 @@ export interface MetricsExplorerViewsServiceStartDeps {
|
|||
}
|
||||
|
||||
export interface IMetricsExplorerViewsClient {
|
||||
findMetricsExplorerViews(): Promise<MetricsExplorerView[]>;
|
||||
getMetricsExplorerView(metricsExplorerViewId: string): Promise<MetricsExplorerView>;
|
||||
findMetricsExplorerViews(): Promise<FindMetricsExplorerViewResponsePayload['data']>;
|
||||
getMetricsExplorerView(
|
||||
metricsExplorerViewId: string
|
||||
): Promise<GetMetricsExplorerViewResponsePayload>;
|
||||
createMetricsExplorerView(
|
||||
metricsExplorerViewAttributes: Partial<MetricsExplorerViewAttributes>
|
||||
): Promise<MetricsExplorerView>;
|
||||
): Promise<CreateMetricsExplorerViewResponsePayload>;
|
||||
updateMetricsExplorerView(
|
||||
metricsExplorerViewId: string,
|
||||
metricsExplorerViewAttributes: Partial<MetricsExplorerViewAttributes>
|
||||
): Promise<MetricsExplorerView>;
|
||||
): Promise<UpdateMetricsExplorerViewResponsePayload>;
|
||||
deleteMetricsExplorerView(metricsExplorerViewId: string): Promise<null>;
|
||||
}
|
||||
|
|
|
@ -15,7 +15,7 @@ import {
|
|||
MetricsExplorerChartType,
|
||||
MetricsExplorerYAxisMode,
|
||||
MetricsExplorerChartOptions,
|
||||
MetricsExplorerTimestampsRT,
|
||||
MetricsExplorerTimestamp,
|
||||
} from '../../pages/metrics/metrics_explorer/hooks/use_metrics_explorer_options';
|
||||
|
||||
export const options: MetricsExplorerOptions = {
|
||||
|
@ -56,7 +56,7 @@ export const timeRange: MetricsExplorerTimeOptions = {
|
|||
interval: '>=10s',
|
||||
};
|
||||
|
||||
export const timestamps: MetricsExplorerTimestampsRT = {
|
||||
export const timestamps: MetricsExplorerTimestamp = {
|
||||
fromTimestamp: 1678376367166,
|
||||
toTimestamp: 1678379973620,
|
||||
interval: '>=10s',
|
||||
|
|
|
@ -77,38 +77,32 @@ Status code: 200
|
|||
"updatedAt": 1681398305034,
|
||||
"attributes": {
|
||||
"name": "Ad-hoc",
|
||||
"isDefault": true,
|
||||
"isStatic": false,
|
||||
"metric": {
|
||||
"type": "cpu"
|
||||
"options": {
|
||||
"aggregation": "avg",
|
||||
"metrics": [
|
||||
{
|
||||
"aggregation": "avg",
|
||||
"field": "system.cpu.total.norm.pct",
|
||||
"color": "color0"
|
||||
},
|
||||
],
|
||||
"source": "default",
|
||||
"groupBy": [
|
||||
"host.name"
|
||||
]
|
||||
},
|
||||
"sort": {
|
||||
"by": "name",
|
||||
"direction": "desc"
|
||||
"chartOptions": {
|
||||
"type": "line",
|
||||
"yAxisMode": "fromZero",
|
||||
"stack": false
|
||||
},
|
||||
"groupBy": [],
|
||||
"nodeType": "host",
|
||||
"view": "map",
|
||||
"customOptions": [],
|
||||
"customMetrics": [],
|
||||
"boundsOverride": {
|
||||
"max": 1,
|
||||
"min": 0
|
||||
"currentTimerange": {
|
||||
"from": "now-1h",
|
||||
"to": "now",
|
||||
"interval": ">=10s"
|
||||
},
|
||||
"autoBounds": true,
|
||||
"accountId": "",
|
||||
"region": "",
|
||||
"autoReload": false,
|
||||
"filterQuery": {
|
||||
"expression": "",
|
||||
"kind": "kuery"
|
||||
},
|
||||
"legend": {
|
||||
"palette": "cool",
|
||||
"reverseColors": false,
|
||||
"steps": 10
|
||||
},
|
||||
"timelineOpen": false
|
||||
"isDefault": false,
|
||||
"isStatic": false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -130,23 +124,47 @@ Status code: 404
|
|||
|
||||
Creates a new metrics explorer view.
|
||||
|
||||
`aggregation`: `"avg" | "max" | "min" | "cardinality" | "rate" | "count" | "sum" | "p95" | "p99" | "custom"`
|
||||
|
||||
`metrics.aggregation`: `"avg" | "max" | "min" | "cardinality" | "rate" | "count" | "sum" | "p95" | "p99" | "custom"`
|
||||
|
||||
`chartOptions.type`: `"line" | "area" | "bar"`
|
||||
`chartOptions.yAxisMode`: `"fromZero" | "auto" | "bar"`
|
||||
|
||||
### Request
|
||||
|
||||
- **Method**: POST
|
||||
- **Path**: /api/infra/metrics_explorer_views
|
||||
- **Request body**:
|
||||
|
||||
```json
|
||||
{
|
||||
"attributes": {
|
||||
"name": "View name",
|
||||
"metric": {
|
||||
"type": "cpu"
|
||||
"options": {
|
||||
"aggregation": "avg",
|
||||
"metrics": [
|
||||
{
|
||||
"aggregation": "avg",
|
||||
"field": "system.cpu.total.norm.pct",
|
||||
"color": "color0"
|
||||
},
|
||||
],
|
||||
"source": "default",
|
||||
"groupBy": [
|
||||
"host.name"
|
||||
]
|
||||
},
|
||||
"sort": {
|
||||
"by": "name",
|
||||
"direction": "desc"
|
||||
"chartOptions": {
|
||||
"type": "line",
|
||||
"yAxisMode": "fromZero",
|
||||
"stack": false
|
||||
},
|
||||
"currentTimerange": {
|
||||
"from": "now-1h",
|
||||
"to": "now",
|
||||
"interval": ">=10s"
|
||||
},
|
||||
//...
|
||||
}
|
||||
}
|
||||
```
|
||||
|
@ -165,38 +183,32 @@ Status code: 201
|
|||
"updatedAt": 1681398305034,
|
||||
"attributes": {
|
||||
"name": "View name",
|
||||
"options": {
|
||||
"aggregation": "avg",
|
||||
"metrics": [
|
||||
{
|
||||
"aggregation": "avg",
|
||||
"field": "system.cpu.total.norm.pct",
|
||||
"color": "color0"
|
||||
},
|
||||
],
|
||||
"source": "default",
|
||||
"groupBy": [
|
||||
"host.name"
|
||||
]
|
||||
},
|
||||
"chartOptions": {
|
||||
"type": "line",
|
||||
"yAxisMode": "fromZero",
|
||||
"stack": false
|
||||
},
|
||||
"currentTimerange": {
|
||||
"from": "now-1h",
|
||||
"to": "now",
|
||||
"interval": ">=10s"
|
||||
},
|
||||
"isDefault": false,
|
||||
"isStatic": false,
|
||||
"metric": {
|
||||
"type": "cpu"
|
||||
},
|
||||
"sort": {
|
||||
"by": "name",
|
||||
"direction": "desc"
|
||||
},
|
||||
"groupBy": [],
|
||||
"nodeType": "host",
|
||||
"view": "map",
|
||||
"customOptions": [],
|
||||
"customMetrics": [],
|
||||
"boundsOverride": {
|
||||
"max": 1,
|
||||
"min": 0
|
||||
},
|
||||
"autoBounds": true,
|
||||
"accountId": "",
|
||||
"region": "",
|
||||
"autoReload": false,
|
||||
"filterQuery": {
|
||||
"expression": "",
|
||||
"kind": "kuery"
|
||||
},
|
||||
"legend": {
|
||||
"palette": "cool",
|
||||
"reverseColors": false,
|
||||
"steps": 10
|
||||
},
|
||||
"timelineOpen": false
|
||||
"isStatic": false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -234,14 +246,30 @@ Any attempt to update the static view with id `0` will return a `400 The metrics
|
|||
{
|
||||
"attributes": {
|
||||
"name": "View name",
|
||||
"metric": {
|
||||
"type": "cpu"
|
||||
"options": {
|
||||
"aggregation": "avg",
|
||||
"metrics": [
|
||||
{
|
||||
"aggregation": "avg",
|
||||
"field": "system.cpu.total.norm.pct",
|
||||
"color": "color0"
|
||||
},
|
||||
],
|
||||
"source": "default",
|
||||
"groupBy": [
|
||||
"host.name"
|
||||
]
|
||||
},
|
||||
"sort": {
|
||||
"by": "name",
|
||||
"direction": "desc"
|
||||
"chartOptions": {
|
||||
"type": "line",
|
||||
"yAxisMode": "fromZero",
|
||||
"stack": false
|
||||
},
|
||||
//...
|
||||
"currentTimerange": {
|
||||
"from": "now-1h",
|
||||
"to": "now",
|
||||
"interval": ">=10s"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
@ -260,38 +288,32 @@ Status code: 200
|
|||
"updatedAt": 1681398305034,
|
||||
"attributes": {
|
||||
"name": "View name",
|
||||
"options": {
|
||||
"aggregation": "avg",
|
||||
"metrics": [
|
||||
{
|
||||
"aggregation": "avg",
|
||||
"field": "system.cpu.total.norm.pct",
|
||||
"color": "color0"
|
||||
},
|
||||
],
|
||||
"source": "default",
|
||||
"groupBy": [
|
||||
"host.name"
|
||||
]
|
||||
},
|
||||
"chartOptions": {
|
||||
"type": "line",
|
||||
"yAxisMode": "fromZero",
|
||||
"stack": false
|
||||
},
|
||||
"currentTimerange": {
|
||||
"from": "now-1h",
|
||||
"to": "now",
|
||||
"interval": ">=10s"
|
||||
},
|
||||
"isDefault": false,
|
||||
"isStatic": false,
|
||||
"metric": {
|
||||
"type": "cpu"
|
||||
},
|
||||
"sort": {
|
||||
"by": "name",
|
||||
"direction": "desc"
|
||||
},
|
||||
"groupBy": [],
|
||||
"nodeType": "host",
|
||||
"view": "map",
|
||||
"customOptions": [],
|
||||
"customMetrics": [],
|
||||
"boundsOverride": {
|
||||
"max": 1,
|
||||
"min": 0
|
||||
},
|
||||
"autoBounds": true,
|
||||
"accountId": "",
|
||||
"region": "",
|
||||
"autoReload": false,
|
||||
"filterQuery": {
|
||||
"expression": "",
|
||||
"kind": "kuery"
|
||||
},
|
||||
"legend": {
|
||||
"palette": "cool",
|
||||
"reverseColors": false,
|
||||
"steps": 10
|
||||
},
|
||||
"timelineOpen": false
|
||||
"isStatic": false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,11 +8,59 @@
|
|||
import { isoToEpochRt, nonEmptyStringRt } from '@kbn/io-ts-utils';
|
||||
import * as rt from 'io-ts';
|
||||
|
||||
export const metricsExplorerViewSavedObjectAttributesRT = rt.intersection([
|
||||
rt.strict({
|
||||
const metricsExplorerSavedObjectChartTypeRT = rt.keyof({ line: null, area: null, bar: null });
|
||||
const metricsExplorerYAxisModeRT = rt.keyof({ fromZero: null, auto: null });
|
||||
|
||||
const metricsExplorerSavedObjectChartOptionsRT = rt.type({
|
||||
yAxisMode: metricsExplorerYAxisModeRT,
|
||||
type: metricsExplorerSavedObjectChartTypeRT,
|
||||
stack: rt.boolean,
|
||||
});
|
||||
|
||||
export const metricsExplorerSavedObjectTimeOptionsRT = rt.type({
|
||||
from: rt.string,
|
||||
to: rt.string,
|
||||
interval: rt.string,
|
||||
});
|
||||
const metricsExplorerSavedObjectOptionsMetricRT = rt.intersection([
|
||||
rt.UnknownRecord,
|
||||
rt.partial({
|
||||
rate: rt.boolean,
|
||||
color: rt.string,
|
||||
label: rt.string,
|
||||
}),
|
||||
]);
|
||||
|
||||
const metricExplorerSavedObjectOptionsRequiredRT = rt.type({
|
||||
aggregation: rt.string,
|
||||
metrics: rt.array(metricsExplorerSavedObjectOptionsMetricRT),
|
||||
});
|
||||
|
||||
const metricExplorerSavedObjectOptionsOptionalRT = rt.partial({
|
||||
limit: rt.number,
|
||||
groupBy: rt.union([rt.string, rt.array(rt.string)]),
|
||||
filterQuery: rt.string,
|
||||
source: rt.string,
|
||||
forceInterval: rt.boolean,
|
||||
dropLastBucket: rt.boolean,
|
||||
});
|
||||
export const metricsExplorerSavedObjectOptionsRT = rt.intersection([
|
||||
metricExplorerSavedObjectOptionsRequiredRT,
|
||||
metricExplorerSavedObjectOptionsOptionalRT,
|
||||
]);
|
||||
|
||||
const metricExplorerViewsSavedObjectStateRT = rt.type({
|
||||
chartOptions: metricsExplorerSavedObjectChartOptionsRT,
|
||||
currentTimerange: metricsExplorerSavedObjectTimeOptionsRT,
|
||||
options: metricsExplorerSavedObjectOptionsRT,
|
||||
});
|
||||
|
||||
const metricsExplorerViewSavedObjectAttributesRT = rt.intersection([
|
||||
metricExplorerViewsSavedObjectStateRT,
|
||||
rt.type({
|
||||
name: nonEmptyStringRt,
|
||||
}),
|
||||
rt.UnknownRecord,
|
||||
rt.partial({ isDefault: rt.boolean, isStatic: rt.boolean }),
|
||||
]);
|
||||
|
||||
export const metricsExplorerViewSavedObjectRT = rt.intersection([
|
||||
|
|
|
@ -14,12 +14,16 @@ import {
|
|||
} from '@kbn/core/server';
|
||||
import Boom from '@hapi/boom';
|
||||
import {
|
||||
metricsExplorerViewAttributesRT,
|
||||
staticMetricsExplorerViewAttributes,
|
||||
staticMetricsExplorerViewId,
|
||||
} from '../../../common/metrics_explorer_views';
|
||||
import type {
|
||||
CreateMetricsExplorerViewAttributesRequestPayload,
|
||||
FindMetricsExplorerViewResponsePayload,
|
||||
GetMetricsExplorerViewResponsePayload,
|
||||
MetricsExplorerViewRequestQuery,
|
||||
UpdateMetricsExplorerViewResponsePayload,
|
||||
} from '../../../common/http_api/latest';
|
||||
import type {
|
||||
MetricsExplorerView,
|
||||
|
@ -41,7 +45,9 @@ export class MetricsExplorerViewsClient implements IMetricsExplorerViewsClient {
|
|||
static STATIC_VIEW_ID = '0';
|
||||
static DEFAULT_SOURCE_ID = 'default';
|
||||
|
||||
public async find(query: MetricsExplorerViewRequestQuery): Promise<MetricsExplorerView[]> {
|
||||
public async find(
|
||||
query: MetricsExplorerViewRequestQuery
|
||||
): Promise<FindMetricsExplorerViewResponsePayload['data']> {
|
||||
this.logger.debug('Trying to load metrics explorer views ...');
|
||||
|
||||
const sourceId = query.sourceId ?? MetricsExplorerViewsClient.DEFAULT_SOURCE_ID;
|
||||
|
@ -71,7 +77,7 @@ export class MetricsExplorerViewsClient implements IMetricsExplorerViewsClient {
|
|||
public async get(
|
||||
metricsExplorerViewId: string,
|
||||
query: MetricsExplorerViewRequestQuery
|
||||
): Promise<MetricsExplorerView> {
|
||||
): Promise<GetMetricsExplorerViewResponsePayload> {
|
||||
this.logger.debug(`Trying to load metrics explorer view with id ${metricsExplorerViewId} ...`);
|
||||
|
||||
const sourceId = query.sourceId ?? MetricsExplorerViewsClient.DEFAULT_SOURCE_ID;
|
||||
|
@ -103,7 +109,7 @@ export class MetricsExplorerViewsClient implements IMetricsExplorerViewsClient {
|
|||
metricsExplorerViewId: string | null,
|
||||
attributes: CreateMetricsExplorerViewAttributesRequestPayload,
|
||||
query: MetricsExplorerViewRequestQuery
|
||||
): Promise<MetricsExplorerView> {
|
||||
): Promise<UpdateMetricsExplorerViewResponsePayload> {
|
||||
this.logger.debug(
|
||||
`Trying to update metrics explorer view with id "${metricsExplorerViewId}"...`
|
||||
);
|
||||
|
@ -147,10 +153,10 @@ export class MetricsExplorerViewsClient implements IMetricsExplorerViewsClient {
|
|||
});
|
||||
}
|
||||
|
||||
private mapSavedObjectToMetricsExplorerView(
|
||||
savedObject: SavedObject | SavedObjectsUpdateResponse,
|
||||
private mapSavedObjectToMetricsExplorerView<T>(
|
||||
savedObject: SavedObject<T> | SavedObjectsUpdateResponse<T>,
|
||||
defaultViewId?: string
|
||||
) {
|
||||
): MetricsExplorerView {
|
||||
const metricsExplorerViewSavedObject = decodeOrThrow(metricsExplorerViewSavedObjectRT)(
|
||||
savedObject
|
||||
);
|
||||
|
@ -160,7 +166,9 @@ export class MetricsExplorerViewsClient implements IMetricsExplorerViewsClient {
|
|||
version: metricsExplorerViewSavedObject.version,
|
||||
updatedAt: metricsExplorerViewSavedObject.updated_at,
|
||||
attributes: {
|
||||
...metricsExplorerViewSavedObject.attributes,
|
||||
...decodeOrThrow(metricsExplorerViewAttributesRT)(
|
||||
metricsExplorerViewSavedObject.attributes
|
||||
),
|
||||
isDefault: metricsExplorerViewSavedObject.id === defaultViewId,
|
||||
isStatic: false,
|
||||
},
|
||||
|
|
|
@ -11,10 +11,12 @@ import type {
|
|||
SavedObjectsServiceStart,
|
||||
} from '@kbn/core/server';
|
||||
import type {
|
||||
FindMetricsExplorerViewResponsePayload,
|
||||
GetMetricsExplorerViewResponsePayload,
|
||||
MetricsExplorerViewRequestQuery,
|
||||
UpdateMetricsExplorerViewAttributesRequestPayload,
|
||||
UpdateMetricsExplorerViewResponsePayload,
|
||||
} from '../../../common/http_api/latest';
|
||||
import type { MetricsExplorerView } from '../../../common/metrics_explorer_views';
|
||||
import type { InfraSources } from '../../lib/sources';
|
||||
|
||||
export interface MetricsExplorerViewsServiceStartDeps {
|
||||
|
@ -31,14 +33,16 @@ export interface MetricsExplorerViewsServiceStart {
|
|||
|
||||
export interface IMetricsExplorerViewsClient {
|
||||
delete(metricsExplorerViewId: string): Promise<{}>;
|
||||
find(query: MetricsExplorerViewRequestQuery): Promise<MetricsExplorerView[]>;
|
||||
find(
|
||||
query: MetricsExplorerViewRequestQuery
|
||||
): Promise<FindMetricsExplorerViewResponsePayload['data']>;
|
||||
get(
|
||||
metricsExplorerViewId: string,
|
||||
query: MetricsExplorerViewRequestQuery
|
||||
): Promise<MetricsExplorerView>;
|
||||
): Promise<GetMetricsExplorerViewResponsePayload>;
|
||||
update(
|
||||
metricsExplorerViewId: string | null,
|
||||
metricsExplorerViewAttributes: UpdateMetricsExplorerViewAttributesRequestPayload,
|
||||
query: MetricsExplorerViewRequestQuery
|
||||
): Promise<MetricsExplorerView>;
|
||||
): Promise<UpdateMetricsExplorerViewResponsePayload>;
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue