mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 09:48:58 -04:00
This commit is contained in:
parent
dc982bc2b9
commit
a7fc193b35
3 changed files with 137 additions and 86 deletions
|
@ -0,0 +1,70 @@
|
|||
/*
|
||||
* 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 { omit } from 'lodash';
|
||||
import { mapToUrlState } from './with_metrics_explorer_options_url_state';
|
||||
|
||||
describe('WithMetricsExplorerOptionsUrlState', () => {
|
||||
describe('mapToUrlState', () => {
|
||||
it('loads a valid URL state', () => {
|
||||
expect(mapToUrlState(validState)).toEqual(validState);
|
||||
});
|
||||
it('discards invalid properties and loads valid properties into the URL', () => {
|
||||
expect(mapToUrlState(invalidState)).toEqual(omit(invalidState, 'options'));
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
const validState = {
|
||||
chartOptions: {
|
||||
stack: false,
|
||||
type: 'line',
|
||||
yAxisMode: 'fromZero',
|
||||
},
|
||||
options: {
|
||||
aggregation: 'avg',
|
||||
filterQuery: '',
|
||||
groupBy: ['host.hostname'],
|
||||
metrics: [
|
||||
{
|
||||
aggregation: 'avg',
|
||||
color: 'color0',
|
||||
field: 'system.cpu.user.pct',
|
||||
},
|
||||
{
|
||||
aggregation: 'avg',
|
||||
color: 'color1',
|
||||
field: 'system.load.1',
|
||||
},
|
||||
],
|
||||
source: 'url',
|
||||
},
|
||||
timerange: {
|
||||
from: 'now-1h',
|
||||
interval: '>=10s',
|
||||
to: 'now',
|
||||
},
|
||||
};
|
||||
|
||||
const invalidState = {
|
||||
chartOptions: {
|
||||
stack: false,
|
||||
type: 'line',
|
||||
yAxisMode: 'fromZero',
|
||||
},
|
||||
options: {
|
||||
aggregation: 'avg',
|
||||
filterQuery: '',
|
||||
groupBy: ['host.hostname'],
|
||||
metrics: 'this is the wrong data type',
|
||||
source: 'url',
|
||||
},
|
||||
timerange: {
|
||||
from: 'now-1h',
|
||||
interval: '>=10s',
|
||||
to: 'now',
|
||||
},
|
||||
};
|
|
@ -5,19 +5,17 @@
|
|||
*/
|
||||
|
||||
import { set } from '@elastic/safer-lodash-set';
|
||||
import { values } from 'lodash';
|
||||
import React, { useContext, useMemo } from 'react';
|
||||
import * as t from 'io-ts';
|
||||
import { ThrowReporter } from 'io-ts/lib/ThrowReporter';
|
||||
import { MetricsExplorerColor } from '../../../common/color_palette';
|
||||
import { UrlStateContainer } from '../../utils/url_state';
|
||||
import {
|
||||
MetricsExplorerOptions,
|
||||
MetricsExplorerOptionsContainer,
|
||||
MetricsExplorerTimeOptions,
|
||||
MetricsExplorerYAxisMode,
|
||||
MetricsExplorerChartType,
|
||||
MetricsExplorerChartOptions,
|
||||
metricExplorerOptionsRT,
|
||||
metricsExplorerChartOptionsRT,
|
||||
metricsExplorerTimeOptionsRT,
|
||||
} from '../../pages/metrics/metrics_explorer/hooks/use_metrics_explorer_options';
|
||||
|
||||
interface MetricsExplorerUrlState {
|
||||
|
@ -74,36 +72,7 @@ export const WithMetricsExplorerOptionsUrlState = () => {
|
|||
};
|
||||
|
||||
function isMetricExplorerOptions(subject: any): subject is MetricsExplorerOptions {
|
||||
const MetricRequired = t.type({
|
||||
aggregation: t.string,
|
||||
});
|
||||
|
||||
const MetricOptional = t.partial({
|
||||
field: t.string,
|
||||
rate: t.boolean,
|
||||
color: t.keyof(
|
||||
Object.fromEntries(values(MetricsExplorerColor).map((c) => [c, null])) as Record<string, null>
|
||||
),
|
||||
label: t.string,
|
||||
});
|
||||
|
||||
const Metric = t.intersection([MetricRequired, MetricOptional]);
|
||||
|
||||
const OptionsRequired = t.type({
|
||||
aggregation: t.string,
|
||||
metrics: t.array(Metric),
|
||||
});
|
||||
|
||||
const OptionsOptional = t.partial({
|
||||
limit: t.number,
|
||||
groupBy: t.string,
|
||||
filterQuery: t.string,
|
||||
source: t.string,
|
||||
});
|
||||
|
||||
const Options = t.intersection([OptionsRequired, OptionsOptional]);
|
||||
|
||||
const result = Options.decode(subject);
|
||||
const result = metricExplorerOptionsRT.decode(subject);
|
||||
|
||||
try {
|
||||
ThrowReporter.report(result);
|
||||
|
@ -114,22 +83,7 @@ function isMetricExplorerOptions(subject: any): subject is MetricsExplorerOption
|
|||
}
|
||||
|
||||
function isMetricExplorerChartOptions(subject: any): subject is MetricsExplorerChartOptions {
|
||||
const ChartOptions = t.type({
|
||||
yAxisMode: t.keyof(
|
||||
Object.fromEntries(values(MetricsExplorerYAxisMode).map((v) => [v, null])) as Record<
|
||||
string,
|
||||
null
|
||||
>
|
||||
),
|
||||
type: t.keyof(
|
||||
Object.fromEntries(values(MetricsExplorerChartType).map((v) => [v, null])) as Record<
|
||||
string,
|
||||
null
|
||||
>
|
||||
),
|
||||
stack: t.boolean,
|
||||
});
|
||||
const result = ChartOptions.decode(subject);
|
||||
const result = metricsExplorerChartOptionsRT.decode(subject);
|
||||
|
||||
try {
|
||||
ThrowReporter.report(result);
|
||||
|
@ -140,12 +94,7 @@ function isMetricExplorerChartOptions(subject: any): subject is MetricsExplorerC
|
|||
}
|
||||
|
||||
function isMetricExplorerTimeOption(subject: any): subject is MetricsExplorerTimeOptions {
|
||||
const TimeRange = t.type({
|
||||
from: t.string,
|
||||
to: t.string,
|
||||
interval: t.string,
|
||||
});
|
||||
const result = TimeRange.decode(subject);
|
||||
const result = metricsExplorerTimeOptionsRT.decode(subject);
|
||||
try {
|
||||
ThrowReporter.report(result);
|
||||
return true;
|
||||
|
@ -154,7 +103,7 @@ function isMetricExplorerTimeOption(subject: any): subject is MetricsExplorerTim
|
|||
}
|
||||
}
|
||||
|
||||
const mapToUrlState = (value: any): MetricsExplorerUrlState | undefined => {
|
||||
export const mapToUrlState = (value: any): MetricsExplorerUrlState | undefined => {
|
||||
const finalState = {};
|
||||
if (value) {
|
||||
if (value.options && isMetricExplorerOptions(value.options)) {
|
||||
|
|
|
@ -4,19 +4,29 @@
|
|||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import * as t from 'io-ts';
|
||||
import { values } from 'lodash';
|
||||
import createContainer from 'constate';
|
||||
import { useState, useEffect, useMemo, Dispatch, SetStateAction } from 'react';
|
||||
import { useAlertPrefillContext } from '../../../../alerting/use_alert_prefill';
|
||||
import { MetricsExplorerColor } from '../../../../../common/color_palette';
|
||||
import {
|
||||
MetricsExplorerAggregation,
|
||||
MetricsExplorerMetric,
|
||||
} from '../../../../../common/http_api/metrics_explorer';
|
||||
import { metricsExplorerMetricRT } from '../../../../../common/http_api/metrics_explorer';
|
||||
|
||||
export type MetricsExplorerOptionsMetric = MetricsExplorerMetric & {
|
||||
color?: MetricsExplorerColor;
|
||||
label?: string;
|
||||
};
|
||||
const metricsExplorerOptionsMetricRT = t.intersection([
|
||||
metricsExplorerMetricRT,
|
||||
t.partial({
|
||||
rate: t.boolean,
|
||||
color: t.keyof(
|
||||
Object.fromEntries(values(MetricsExplorerColor).map((c) => [c, null])) as Record<
|
||||
MetricsExplorerColor,
|
||||
null
|
||||
>
|
||||
),
|
||||
label: t.string,
|
||||
}),
|
||||
]);
|
||||
|
||||
export type MetricsExplorerOptionsMetric = t.TypeOf<typeof metricsExplorerOptionsMetricRT>;
|
||||
|
||||
export enum MetricsExplorerChartType {
|
||||
line = 'line',
|
||||
|
@ -29,28 +39,50 @@ export enum MetricsExplorerYAxisMode {
|
|||
auto = 'auto',
|
||||
}
|
||||
|
||||
export interface MetricsExplorerChartOptions {
|
||||
type: MetricsExplorerChartType;
|
||||
yAxisMode: MetricsExplorerYAxisMode;
|
||||
stack: boolean;
|
||||
}
|
||||
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 interface MetricsExplorerOptions {
|
||||
metrics: MetricsExplorerOptionsMetric[];
|
||||
limit?: number;
|
||||
groupBy?: string | string[];
|
||||
filterQuery?: string;
|
||||
aggregation: MetricsExplorerAggregation;
|
||||
forceInterval?: boolean;
|
||||
dropLastBucket?: boolean;
|
||||
source?: string;
|
||||
}
|
||||
export type MetricsExplorerChartOptions = t.TypeOf<typeof metricsExplorerChartOptionsRT>;
|
||||
|
||||
export interface MetricsExplorerTimeOptions {
|
||||
from: string;
|
||||
to: string;
|
||||
interval: string;
|
||||
}
|
||||
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 metricsExplorerTimeOptionsRT = t.type({
|
||||
from: t.string,
|
||||
to: t.string,
|
||||
interval: t.string,
|
||||
});
|
||||
export type MetricsExplorerTimeOptions = t.TypeOf<typeof metricsExplorerTimeOptionsRT>;
|
||||
|
||||
export const DEFAULT_TIMERANGE: MetricsExplorerTimeOptions = {
|
||||
from: 'now-1h',
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue