Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(slo): allow configuration of advanced settings from UI #200822

Merged
merged 28 commits into from
Dec 2, 2024
Merged
Show file tree
Hide file tree
Changes from 26 commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
b807203
cleanup form default values initialization logic
kdelemme Nov 18, 2024
83992f5
Use default gap between inputs
kdelemme Nov 18, 2024
05f3955
Move indicators into own folder
kdelemme Nov 18, 2024
9596596
Move advanced settings in own component
kdelemme Nov 18, 2024
e8b9bfd
Add syncDelay advanced settings
kdelemme Nov 18, 2024
9dcfc55
Add sync delay in overview
kdelemme Nov 18, 2024
01425db
Add frequency settings
kdelemme Nov 18, 2024
04c8423
Use grid
kdelemme Nov 18, 2024
c214e12
Introduce syncField settings and use it in transforms
kdelemme Nov 19, 2024
c15af8c
Use syncField when provided
kdelemme Nov 19, 2024
f9c5f20
Handle sync field value
kdelemme Nov 19, 2024
e3011a8
[CI] Auto-commit changed files from 'node scripts/eslint --no-cache -…
kibanamachine Nov 19, 2024
cebdf80
Ensure dataview dependant fields are cleared on update
kdelemme Nov 22, 2024
199721e
remove console log
kdelemme Nov 22, 2024
8774b98
Append optional text
kdelemme Nov 22, 2024
7485773
Update locator
kdelemme Nov 22, 2024
17823db
Fix default values when no state provided
kdelemme Nov 22, 2024
0bf8b60
Merge branch 'main' into feat/advanced-settings-slo
kdelemme Nov 25, 2024
887fbb0
Merge branch 'main' into feat/advanced-settings-slo
kdelemme Nov 26, 2024
b2c9133
Merge branch 'main' into feat/advanced-settings-slo
kdelemme Nov 26, 2024
389fcc4
Merge branch 'main' into feat/advanced-settings-slo
kdelemme Nov 27, 2024
4c0e028
Fix invalid state
kdelemme Nov 28, 2024
da345b6
Merge branch 'main' into feat/advanced-settings-slo
shahzad31 Nov 29, 2024
1dd3775
Update x-pack/plugins/observability_solution/slo/public/pages/slo_edi…
kdelemme Dec 2, 2024
3eb4929
Merge branch 'main' into feat/advanced-settings-slo
kdelemme Dec 2, 2024
27e00b0
Remove unecessary parseInt
kdelemme Dec 2, 2024
9a4ce7f
Add syncField in OpenAPI spec
kdelemme Dec 2, 2024
ca6df89
[CI] Auto-commit changed files from 'make api-docs'
kibanamachine Dec 2, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 14 additions & 4 deletions x-pack/packages/kbn-slo-schema/src/schema/slo.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,16 +27,26 @@ const objectiveSchema = t.intersection([
t.partial({ timesliceTarget: t.number, timesliceWindow: durationType }),
]);

const settingsSchema = t.type({
const settingsSchema = t.intersection([
t.type({
syncDelay: durationType,
frequency: durationType,
preventInitialBackfill: t.boolean,
}),
t.partial({ syncField: t.union([t.string, t.null]) }),
]);

const groupBySchema = allOrAnyStringOrArray;

const optionalSettingsSchema = t.partial({
syncDelay: durationType,
frequency: durationType,
preventInitialBackfill: t.boolean,
syncField: t.union([t.string, t.null]),
});

const groupBySchema = allOrAnyStringOrArray;

const optionalSettingsSchema = t.partial({ ...settingsSchema.props });
const tagsSchema = t.array(t.string);

// id cannot contain special characters and spaces
const sloIdSchema = new t.Type<string, string, unknown>(
'sloIdSchema',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ const baseSlo: Omit<SLOWithSummaryResponse, 'id'> = {
good: 'http_status: 2xx',
total: 'a query',
timestampField: 'custom_timestamp',
dataViewId: 'some-data-view-id',
},
},
timeWindow: {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,16 +11,21 @@ import { SloEditLocatorDefinition } from './slo_edit';
describe('SloEditLocator', () => {
const locator = new SloEditLocatorDefinition();

it('should return correct url when empty params are provided', async () => {
it('returns the correct url when empty params are provided', async () => {
const location = await locator.getLocation({});
expect(location.app).toEqual('slo');
expect(location.path).toEqual('/create?_a=()');
});

it('should return correct url when slo is provided', async () => {
const location = await locator.getLocation(buildSlo({ id: 'foo' }));
it('returns the correct url when slo id is provided', async () => {
const location = await locator.getLocation({ id: 'existing-slo-id' });
expect(location.path).toEqual('/edit/existing-slo-id');
});

it('returns the correct url when partial slo input is provided', async () => {
const location = await locator.getLocation(buildSlo({ id: undefined }));
expect(location.path).toEqual(
"/edit/foo?_a=(budgetingMethod:occurrences,createdAt:'2022-12-29T10:11:12.000Z',description:'some%20description%20useful',enabled:!t,groupBy:'*',groupings:(),id:foo,indicator:(params:(filter:'baz:%20foo%20and%20bar%20%3E%202',good:'http_status:%202xx',index:some-index,timestampField:custom_timestamp,total:'a%20query'),type:sli.kql.custom),instanceId:'*',meta:(),name:'super%20important%20level%20service',objective:(target:0.98),revision:1,settings:(frequency:'1m',preventInitialBackfill:!f,syncDelay:'1m'),summary:(errorBudget:(consumed:0.064,initial:0.02,isEstimated:!f,remaining:0.936),fiveMinuteBurnRate:0,oneDayBurnRate:0,oneHourBurnRate:0,sliValue:0.99872,status:HEALTHY),tags:!(k8s,production,critical),timeWindow:(duration:'30d',type:rolling),updatedAt:'2022-12-29T10:11:12.000Z',version:2)"
"/create?_a=(budgetingMethod:occurrences,createdAt:'2022-12-29T10:11:12.000Z',description:'some%20description%20useful',enabled:!t,groupBy:'*',groupings:(),indicator:(params:(dataViewId:some-data-view-id,filter:'baz:%20foo%20and%20bar%20%3E%202',good:'http_status:%202xx',index:some-index,timestampField:custom_timestamp,total:'a%20query'),type:sli.kql.custom),instanceId:'*',meta:(),name:'super%20important%20level%20service',objective:(target:0.98),revision:1,settings:(frequency:'1m',preventInitialBackfill:!f,syncDelay:'1m'),summary:(errorBudget:(consumed:0.064,initial:0.02,isEstimated:!f,remaining:0.936),fiveMinuteBurnRate:0,oneDayBurnRate:0,oneHourBurnRate:0,sliValue:0.99872,status:HEALTHY),tags:!(k8s,production,critical),timeWindow:(duration:'30d',type:rolling),updatedAt:'2022-12-29T10:11:12.000Z',version:2)"
);
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -5,31 +5,34 @@
* 2.0.
*/

import { setStateToKbnUrl } from '@kbn/kibana-utils-plugin/public';
import type { RecursivePartial } from '@elastic/charts';
import type { SerializableRecord } from '@kbn/utility-types';
import type { LocatorDefinition } from '@kbn/share-plugin/public';
import { setStateToKbnUrl } from '@kbn/kibana-utils-plugin/public';
import { sloEditLocatorID } from '@kbn/observability-plugin/common';
import type { CreateSLOForm } from '../pages/slo_edit/types';
import type { LocatorDefinition } from '@kbn/share-plugin/public';
import { CreateSLOInput } from '@kbn/slo-schema';
import { SLO_CREATE_PATH } from '../../common/locators/paths';

export type SloEditParams = RecursivePartial<CreateSLOForm>;

export interface SloEditLocatorParams extends SloEditParams, SerializableRecord {}
export type SloEditLocatorParams = RecursivePartial<CreateSLOInput>;

export class SloEditLocatorDefinition implements LocatorDefinition<SloEditLocatorParams> {
public readonly id = sloEditLocatorID;

public readonly getLocation = async (slo: SloEditLocatorParams) => {
if (!!slo.id) {
return {
app: 'slo',
path: `/edit/${encodeURIComponent(slo.id)}`,
state: {},
};
}

return {
app: 'slo',
path: setStateToKbnUrl<SloEditParams>(
path: setStateToKbnUrl<RecursivePartial<CreateSLOInput>>(
'_a',
{
...slo,
},
slo,
{ useHash: false, storeInHashQuery: false },
slo.id ? `/edit/${encodeURIComponent(String(slo.id))}` : `${SLO_CREATE_PATH}`
SLO_CREATE_PATH
),
state: {},
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,15 +8,14 @@
import { EuiFlexGrid, EuiPanel, EuiText, useIsWithinBreakpoints } from '@elastic/eui';
import numeral from '@elastic/numeral';
import { i18n } from '@kbn/i18n';
import { TagsList } from '@kbn/observability-shared-plugin/public';
import {
SLOWithSummaryResponse,
occurrencesBudgetingMethodSchema,
querySchema,
rollingTimeWindowTypeSchema,
SLOWithSummaryResponse,
} from '@kbn/slo-schema';
import React from 'react';
import { TagsList } from '@kbn/observability-shared-plugin/public';
import { DisplayQuery } from './display_query';
import { useKibana } from '../../../../hooks/use_kibana';
import {
BUDGETING_METHOD_OCCURRENCES,
Expand All @@ -26,9 +25,9 @@ import {
toIndicatorTypeLabel,
} from '../../../../utils/slo/labels';
import { ApmIndicatorOverview } from './apm_indicator_overview';
import { SyntheticsIndicatorOverview } from './synthetics_indicator_overview';

import { DisplayQuery } from './display_query';
import { OverviewItem } from './overview_item';
import { SyntheticsIndicatorOverview } from './synthetics_indicator_overview';

export interface Props {
slo: SLOWithSummaryResponse;
Expand Down Expand Up @@ -170,6 +169,19 @@ export function Overview({ slo }: Props) {
}
/>
)}

<OverviewItem
title={i18n.translate('xpack.slo.sloDetails.overview.settings.syncDelay', {
defaultMessage: 'Sync delay',
})}
subtitle={slo.settings.syncDelay}
/>
<OverviewItem
title={i18n.translate('xpack.slo.sloDetails.overview.settings.frequency', {
defaultMessage: 'Frequency',
})}
subtitle={slo.settings.frequency}
/>
Comment on lines +173 to +184
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not spending too much time here since Maciej has some plan for the new overview section

</EuiFlexGrid>
</EuiPanel>
);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,174 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/

import {
EuiAccordion,
EuiCheckbox,
EuiFieldNumber,
EuiFlexGrid,
EuiFlexGroup,
EuiFlexItem,
EuiFormRow,
EuiIcon,
EuiIconTip,
EuiTitle,
useGeneratedHtmlId,
} from '@elastic/eui';
import { i18n } from '@kbn/i18n';
import React from 'react';
import { Controller, useFormContext } from 'react-hook-form';
import { CreateSLOForm } from '../../../types';
import { SyncFieldSelector } from './sync_field_selector';

export function AdvancedSettings() {
const { control, getFieldState } = useFormContext<CreateSLOForm>();
const preventBackfillCheckbox = useGeneratedHtmlId({ prefix: 'preventBackfill' });
const advancedSettingsAccordion = useGeneratedHtmlId({ prefix: 'advancedSettingsAccordion' });

return (
<EuiAccordion
paddingSize="s"
id={advancedSettingsAccordion}
buttonContent={
<EuiFlexGroup gutterSize="s" alignItems="center" responsive={false}>
<EuiFlexItem grow={false}>
<EuiIcon type="controlsVertical" size="m" />
</EuiFlexItem>

<EuiFlexItem>
<EuiTitle size="xxs">
<h3>
{i18n.translate('xpack.slo.sloEdit.settings.advancedSettingsLabel', {
defaultMessage: 'Advanced settings',
})}
</h3>
</EuiTitle>
</EuiFlexItem>
</EuiFlexGroup>
}
>
<EuiFlexGroup direction="column" gutterSize="m">
<EuiFlexGrid columns={3} gutterSize="m">
<EuiFlexItem>
<SyncFieldSelector />
</EuiFlexItem>

<EuiFlexItem>
<EuiFormRow
isInvalid={getFieldState('settings.syncDelay').invalid}
label={
<span>
{i18n.translate('xpack.slo.sloEdit.settings.syncDelay.label', {
defaultMessage: 'Sync delay (in minutes)',
})}{' '}
<EuiIconTip
content={i18n.translate('xpack.slo.sloEdit.settings.syncDelay.tooltip', {
defaultMessage:
'The time delay in minutes between the current time and the latest source data time. Increasing the value will delay any alerting. The default value is 1 minute. The minimum value is 1m and the maximum is 359m. It should always be greater then source index refresh interval.',
})}
position="top"
/>
</span>
}
>
<Controller
name="settings.syncDelay"
defaultValue={1}
control={control}
rules={{ required: true, min: 1, max: 359 }}
render={({ field: { ref, onChange, ...field }, fieldState }) => (
<EuiFieldNumber
{...field}
data-test-subj="sloAdvancedSettingsSyncDelay"
isInvalid={fieldState.invalid}
required
value={field.value}
min={1}
max={359}
step={1}
onChange={(event) => onChange(event.target.value)}
/>
)}
/>
</EuiFormRow>
</EuiFlexItem>

<EuiFlexItem>
<EuiFormRow
isInvalid={getFieldState('settings.frequency').invalid}
label={
<span>
{i18n.translate('xpack.slo.sloEdit.settings.frequency.label', {
defaultMessage: 'Frequency (in minutes)',
})}{' '}
<EuiIconTip
content={i18n.translate('xpack.slo.sloEdit.settings.frequency.tooltip', {
defaultMessage:
'The interval between checks for changes in the source data. The minimum value is 1m and the maximum is 59m. The default value is 1 minute.',
})}
position="top"
/>
</span>
}
>
<Controller
name="settings.frequency"
defaultValue={1}
control={control}
rules={{ required: true, min: 1, max: 59 }}
render={({ field: { ref, onChange, ...field }, fieldState }) => (
<EuiFieldNumber
{...field}
data-test-subj="sloAdvancedSettingsFrequency"
isInvalid={fieldState.invalid}
required
value={field.value}
min={1}
max={59}
step={1}
onChange={(event) => onChange(event.target.value)}
/>
)}
/>
</EuiFormRow>
</EuiFlexItem>
</EuiFlexGrid>

<EuiFormRow isInvalid={getFieldState('settings.preventInitialBackfill').invalid}>
<Controller
name="settings.preventInitialBackfill"
control={control}
render={({ field: { ref, onChange, ...field } }) => (
<EuiCheckbox
id={preventBackfillCheckbox}
label={
<span>
{i18n.translate('xpack.slo.sloEdit.settings.preventInitialBackfill.label', {
defaultMessage: 'Prevent initial backfill of data',
})}{' '}
<EuiIconTip
content={i18n.translate(
'xpack.slo.sloEdit.settings.preventInitialBackfill.tooltip',
{
defaultMessage:
'Start aggregating data from the time the SLO is created, instead of backfilling data from the beginning of the time window.',
}
)}
position="top"
/>
</span>
}
checked={Boolean(field.value)}
onChange={(event: any) => onChange(event.target.checked)}
/>
)}
/>
</EuiFormRow>
</EuiFlexGroup>
</EuiAccordion>
);
}
Loading