Skip to content

Commit 19fe16e

Browse files
committed
Merge branch 'main' of github.com:elastic/kibana into 181286-obs-registry
2 parents 5e3225c + d887763 commit 19fe16e

39 files changed

Lines changed: 832 additions & 289 deletions

File tree

packages/core/http/core-http-server/src/versioning/types.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ export type VersionedRouteConfig<Method extends RouteMethod> = Omit<
3232
RouteConfig<unknown, unknown, unknown, Method>,
3333
'validate' | 'options'
3434
> & {
35-
options?: Omit<RouteConfigOptions<Method>, 'access'>;
35+
options?: Omit<RouteConfigOptions<Method>, 'access' | 'description'>;
3636
/** See {@link RouteConfigOptions<RouteMethod>['access']} */
3737
access: Exclude<RouteConfigOptions<Method>['access'], undefined>;
3838
/**

x-pack/packages/kbn-slo-schema/src/rest_specs/routes/fetch_historical_summary.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,13 @@ const fetchHistoricalSummaryParamsSchema = t.type({
3131
groupBy: allOrAnyStringOrArray,
3232
revision: t.number,
3333
}),
34-
t.partial({ remoteName: t.string }),
34+
t.partial({
35+
remoteName: t.string,
36+
range: t.type({
37+
from: t.string,
38+
to: t.string,
39+
}),
40+
}),
3541
])
3642
),
3743
}),

x-pack/packages/kbn-slo-schema/src/schema/common.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,10 @@ const groupSummarySchema = t.type({
8181
noData: t.number,
8282
});
8383

84-
const dateRangeSchema = t.type({ from: dateType, to: dateType });
84+
const dateRangeSchema = t.type({
85+
from: t.union([dateType, t.string]),
86+
to: t.union([dateType, t.string]),
87+
});
8588

8689
export {
8790
ALL_VALUE,

x-pack/plugins/observability_solution/slo/common/locators/paths.ts

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,12 @@
44
* 2.0; you may not use this file except in compliance with the Elastic License
55
* 2.0.
66
*/
7+
78
export const SLOS_BASE_PATH = '/app/slos';
89
export const SLO_PREFIX = '/slos';
910
export const SLOS_PATH = '/' as const;
1011
export const SLOS_WELCOME_PATH = '/welcome' as const;
11-
export const SLO_DETAIL_PATH = '/:sloId' as const;
12+
export const SLO_DETAIL_PATH = '/:sloId/:tabId?' as const;
1213
export const SLO_CREATE_PATH = '/create' as const;
1314
export const SLO_EDIT_PATH = '/edit/:sloId' as const;
1415
export const SLOS_OUTDATED_DEFINITIONS_PATH = '/outdated-definitions' as const;
@@ -25,10 +26,13 @@ export const paths = {
2526
sloEdit: (sloId: string) => `${SLOS_BASE_PATH}/edit/${encodeURIComponent(sloId)}`,
2627
sloEditWithEncodedForm: (sloId: string, encodedParams: string) =>
2728
`${SLOS_BASE_PATH}/edit/${encodeURIComponent(sloId)}?_a=${encodedParams}`,
28-
sloDetails: (sloId: string, instanceId?: string, remoteName?: string) => {
29+
sloDetails: (sloId: string, instanceId?: string, remoteName?: string, tabId?: string) => {
2930
const qs = new URLSearchParams();
30-
if (!!instanceId) qs.append('instanceId', instanceId);
31+
if (!!instanceId && instanceId !== '*') qs.append('instanceId', instanceId);
3132
if (!!remoteName) qs.append('remoteName', remoteName);
33+
if (tabId) {
34+
return `${SLOS_BASE_PATH}/${encodeURIComponent(sloId)}/${tabId}?${qs.toString()}`;
35+
}
3236
return `${SLOS_BASE_PATH}/${encodeURIComponent(sloId)}?${qs.toString()}`;
3337
},
3438
};
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
/*
2+
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
3+
* or more contributor license agreements. Licensed under the Elastic License
4+
* 2.0; you may not use this file except in compliance with the Elastic License
5+
* 2.0.
6+
*/
7+
8+
import { EuiButtonGroup, EuiFlexGroup, EuiFlexItem, EuiTitle } from '@elastic/eui';
9+
import { i18n } from '@kbn/i18n';
10+
import React from 'react';
11+
import { SloTabId } from '../../../pages/slo_details/components/slo_details';
12+
import { BurnRateOption } from './burn_rates';
13+
interface Props {
14+
burnRateOption: BurnRateOption;
15+
setBurnRateOption: (option: BurnRateOption) => void;
16+
burnRateOptions: BurnRateOption[];
17+
selectedTabId: SloTabId;
18+
}
19+
export function BurnRateHeader({
20+
burnRateOption,
21+
burnRateOptions,
22+
setBurnRateOption,
23+
selectedTabId,
24+
}: Props) {
25+
const onBurnRateOptionChange = (optionId: string) => {
26+
const selected = burnRateOptions.find((opt) => opt.id === optionId) ?? burnRateOptions[0];
27+
setBurnRateOption(selected);
28+
};
29+
return (
30+
<EuiFlexGroup justifyContent="spaceBetween">
31+
<EuiFlexItem grow={false}>
32+
<EuiTitle size="xs">
33+
<h2>
34+
{i18n.translate('xpack.slo.burnRate.title', {
35+
defaultMessage: 'Burn rate',
36+
})}
37+
</h2>
38+
</EuiTitle>
39+
</EuiFlexItem>
40+
{selectedTabId !== 'history' && (
41+
<EuiFlexItem grow={false}>
42+
<EuiButtonGroup
43+
legend={i18n.translate('xpack.slo.burnRate.timeRangeBtnLegend', {
44+
defaultMessage: 'Select the time range',
45+
})}
46+
options={burnRateOptions.map((opt) => ({ id: opt.id, label: opt.label }))}
47+
idSelected={burnRateOption.id}
48+
onChange={onBurnRateOptionChange}
49+
buttonSize="compressed"
50+
/>
51+
</EuiFlexItem>
52+
)}
53+
</EuiFlexGroup>
54+
);
55+
}

x-pack/plugins/observability_solution/slo/public/components/slo/burn_rate/burn_rates.tsx

Lines changed: 35 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,14 @@
55
* 2.0.
66
*/
77

8-
import { EuiButtonGroup, EuiFlexGroup, EuiFlexItem, EuiPanel, EuiTitle } from '@elastic/eui';
9-
import { i18n } from '@kbn/i18n';
8+
import { EuiFlexGroup, EuiFlexItem, EuiPanel } from '@elastic/eui';
109
import { SLOWithSummaryResponse } from '@kbn/slo-schema';
1110
import moment from 'moment';
1211
import React, { useEffect, useState } from 'react';
12+
import { TimeBounds } from '../../../pages/slo_details/types';
13+
import { TimeRange } from '../error_rate_chart/use_lens_definition';
14+
import { SloTabId } from '../../../pages/slo_details/components/slo_details';
15+
import { BurnRateHeader } from './burn_rate_header';
1316
import { useFetchSloBurnRates } from '../../../hooks/use_fetch_slo_burn_rates';
1417
import { ErrorRateChart } from '../error_rate_chart';
1518
import { BurnRate } from './burn_rate';
@@ -18,6 +21,9 @@ interface Props {
1821
slo: SLOWithSummaryResponse;
1922
isAutoRefreshing?: boolean;
2023
burnRateOptions: BurnRateOption[];
24+
selectedTabId: SloTabId;
25+
range?: TimeRange;
26+
onBrushed?: (timeBounds: TimeBounds) => void;
2127
}
2228

2329
export interface BurnRateOption {
@@ -32,7 +38,14 @@ function getWindowsFromOptions(opts: BurnRateOption[]): Array<{ name: string; du
3238
return opts.map((opt) => ({ name: opt.windowName, duration: `${opt.duration}h` }));
3339
}
3440

35-
export function BurnRates({ slo, isAutoRefreshing, burnRateOptions }: Props) {
41+
export function BurnRates({
42+
slo,
43+
isAutoRefreshing,
44+
burnRateOptions,
45+
selectedTabId,
46+
range,
47+
onBrushed,
48+
}: Props) {
3649
const [burnRateOption, setBurnRateOption] = useState(burnRateOptions[0]);
3750
const { isLoading, data } = useFetchSloBurnRates({
3851
slo,
@@ -46,12 +59,7 @@ export function BurnRates({ slo, isAutoRefreshing, burnRateOptions }: Props) {
4659
}
4760
}, [burnRateOptions]);
4861

49-
const onBurnRateOptionChange = (optionId: string) => {
50-
const selected = burnRateOptions.find((opt) => opt.id === optionId) ?? burnRateOptions[0];
51-
setBurnRateOption(selected);
52-
};
53-
54-
const dataTimeRange = {
62+
const dataTimeRange = range ?? {
5563
from: moment().subtract(burnRateOption.duration, 'hour').toDate(),
5664
to: new Date(),
5765
};
@@ -64,34 +72,26 @@ export function BurnRates({ slo, isAutoRefreshing, burnRateOptions }: Props) {
6472
return (
6573
<EuiPanel paddingSize="m" color="transparent" hasBorder data-test-subj="burnRatePanel">
6674
<EuiFlexGroup direction="column" gutterSize="m">
67-
<EuiFlexGroup justifyContent="spaceBetween">
68-
<EuiFlexItem grow={false}>
69-
<EuiTitle size="xs">
70-
<h2>
71-
{i18n.translate('xpack.slo.burnRate.title', {
72-
defaultMessage: 'Burn rate',
73-
})}
74-
</h2>
75-
</EuiTitle>
76-
</EuiFlexItem>
77-
<EuiFlexItem grow={false}>
78-
<EuiButtonGroup
79-
legend={i18n.translate('xpack.slo.burnRate.timeRangeBtnLegend', {
80-
defaultMessage: 'Select the time range',
81-
})}
82-
options={burnRateOptions.map((opt) => ({ id: opt.id, label: opt.label }))}
83-
idSelected={burnRateOption.id}
84-
onChange={onBurnRateOptionChange}
85-
buttonSize="compressed"
86-
/>
87-
</EuiFlexItem>
88-
</EuiFlexGroup>
75+
<BurnRateHeader
76+
burnRateOption={burnRateOption}
77+
burnRateOptions={burnRateOptions}
78+
setBurnRateOption={setBurnRateOption}
79+
selectedTabId={selectedTabId}
80+
/>
8981
<EuiFlexGroup direction="row" gutterSize="m">
90-
<EuiFlexItem grow={1}>
91-
<BurnRate threshold={threshold} burnRate={burnRate} slo={slo} isLoading={isLoading} />
92-
</EuiFlexItem>
82+
{selectedTabId !== 'history' && (
83+
<EuiFlexItem grow={1}>
84+
<BurnRate threshold={threshold} burnRate={burnRate} slo={slo} isLoading={isLoading} />
85+
</EuiFlexItem>
86+
)}
9387
<EuiFlexItem grow={3}>
94-
<ErrorRateChart slo={slo} dataTimeRange={dataTimeRange} threshold={threshold} />
88+
<ErrorRateChart
89+
slo={slo}
90+
dataTimeRange={dataTimeRange}
91+
threshold={threshold}
92+
onBrushed={onBrushed}
93+
selectedTabId={selectedTabId}
94+
/>
9595
</EuiFlexItem>
9696
</EuiFlexGroup>
9797
</EuiFlexGroup>

x-pack/plugins/observability_solution/slo/public/components/slo/error_rate_chart/error_rate_chart.tsx

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@ import { ViewMode } from '@kbn/embeddable-plugin/public';
99
import { SLOWithSummaryResponse } from '@kbn/slo-schema';
1010
import moment from 'moment';
1111
import React from 'react';
12+
import { SloTabId } from '../../../pages/slo_details/components/slo_details';
13+
import { TimeBounds } from '../../../pages/slo_details/types';
1214
import { useKibana } from '../../../utils/kibana_react';
1315
import { getDelayInSecondsFromSLO } from '../../../utils/slo/get_delay_in_seconds_from_slo';
1416
import { AlertAnnotation, TimeRange, useLensDefinition } from './use_lens_definition';
@@ -20,6 +22,8 @@ interface Props {
2022
alertTimeRange?: TimeRange;
2123
showErrorRateAsLine?: boolean;
2224
annotations?: AlertAnnotation[];
25+
selectedTabId?: SloTabId;
26+
onBrushed?: (timeBounds: TimeBounds) => void;
2327
}
2428

2529
export function ErrorRateChart({
@@ -29,17 +33,20 @@ export function ErrorRateChart({
2933
alertTimeRange,
3034
showErrorRateAsLine,
3135
annotations,
36+
onBrushed,
37+
selectedTabId,
3238
}: Props) {
3339
const {
3440
lens: { EmbeddableComponent },
3541
} = useKibana().services;
36-
const lensDef = useLensDefinition(
42+
const lensDef = useLensDefinition({
3743
slo,
3844
threshold,
3945
alertTimeRange,
4046
annotations,
41-
showErrorRateAsLine
42-
);
47+
showErrorRateAsLine,
48+
selectedTabId,
49+
});
4350
const delayInSeconds = getDelayInSecondsFromSLO(slo);
4451

4552
const from = moment(dataTimeRange.from).subtract(delayInSeconds, 'seconds').toISOString();
@@ -55,6 +62,14 @@ export function ErrorRateChart({
5562
}}
5663
attributes={lensDef}
5764
viewMode={ViewMode.VIEW}
65+
onBrushEnd={({ range }) => {
66+
onBrushed?.({
67+
from: range[0],
68+
to: range[1],
69+
fromUtc: moment(range[0]).format(),
70+
toUtc: moment(range[1]).format(),
71+
});
72+
}}
5873
noPadding
5974
/>
6075
);

x-pack/plugins/observability_solution/slo/public/components/slo/error_rate_chart/use_lens_definition.ts

Lines changed: 34 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import { TypedLensByValueInput } from '@kbn/lens-plugin/public';
1212
import { ALL_VALUE, SLOWithSummaryResponse } from '@kbn/slo-schema';
1313
import moment from 'moment';
1414
import { v4 as uuidv4 } from 'uuid';
15+
import { SloTabId } from '../../../pages/slo_details/components/slo_details';
1516
import { SLO_DESTINATION_INDEX_PATTERN } from '../../../../common/constants';
1617

1718
export interface TimeRange {
@@ -24,13 +25,21 @@ export interface AlertAnnotation {
2425
total: number;
2526
}
2627

27-
export function useLensDefinition(
28-
slo: SLOWithSummaryResponse,
29-
threshold: number,
30-
alertTimeRange?: TimeRange,
31-
annotations?: AlertAnnotation[],
32-
showErrorRateAsLine?: boolean
33-
): TypedLensByValueInput['attributes'] {
28+
export function useLensDefinition({
29+
slo,
30+
threshold,
31+
alertTimeRange,
32+
annotations,
33+
showErrorRateAsLine,
34+
selectedTabId,
35+
}: {
36+
slo: SLOWithSummaryResponse;
37+
threshold: number;
38+
alertTimeRange?: TimeRange;
39+
annotations?: AlertAnnotation[];
40+
showErrorRateAsLine?: boolean;
41+
selectedTabId?: SloTabId;
42+
}): TypedLensByValueInput['attributes'] {
3443
const { euiTheme } = useEuiTheme();
3544

3645
const interval = 'auto';
@@ -87,20 +96,24 @@ export function useLensDefinition(
8796
},
8897
],
8998
},
90-
{
91-
layerId: '34298f84-681e-4fa3-8107-d6facb32ed92',
92-
layerType: 'referenceLine',
93-
accessors: ['0a42b72b-cd5a-4d59-81ec-847d97c268e6'],
94-
yConfig: [
95-
{
96-
forAccessor: '0a42b72b-cd5a-4d59-81ec-847d97c268e6',
97-
axisMode: 'left',
98-
textVisibility: true,
99-
color: euiTheme.colors.danger,
100-
iconPosition: 'right',
101-
},
102-
],
103-
},
99+
...(selectedTabId !== 'history'
100+
? [
101+
{
102+
layerId: '34298f84-681e-4fa3-8107-d6facb32ed92',
103+
layerType: 'referenceLine',
104+
accessors: ['0a42b72b-cd5a-4d59-81ec-847d97c268e6'],
105+
yConfig: [
106+
{
107+
forAccessor: '0a42b72b-cd5a-4d59-81ec-847d97c268e6',
108+
axisMode: 'left',
109+
textVisibility: true,
110+
color: euiTheme.colors.danger,
111+
iconPosition: 'right',
112+
},
113+
],
114+
},
115+
]
116+
: []),
104117
...(!!alertTimeRange
105118
? [
106119
{

x-pack/plugins/observability_solution/slo/public/embeddable/slo/common/slo_overview_details.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -79,9 +79,9 @@ export function SloOverviewDetails({
7979
{tabs.map((tab, index) => (
8080
<EuiTab
8181
key={index}
82-
onClick={tab.onClick}
82+
onClick={'onClick' in tab ? tab.onClick : undefined}
8383
isSelected={tab.id === selectedTabId}
84-
append={tab.append}
84+
append={'append' in tab ? tab.append : null}
8585
>
8686
{tab.label}
8787
</EuiTab>

0 commit comments

Comments
 (0)