From 1eb6426670c32cae6366b3dd957a7a76c00a8d09 Mon Sep 17 00:00:00 2001
From: Kevin Delemme
Date: Mon, 2 Dec 2024 16:19:17 -0500
Subject: [PATCH] feat(slo): allow configuration of advanced settings from UI
(#200822)
---
oas_docs/output/kibana.serverless.yaml | 10 +-
oas_docs/output/kibana.yaml | 10 +-
.../packages/kbn-slo-schema/src/schema/slo.ts | 18 +-
.../slo/docs/openapi/slo/bundled.json | 11 +-
.../slo/docs/openapi/slo/bundled.yaml | 10 +-
.../slo/components/schemas/settings.yaml | 10 +-
.../slo/docs/openapi/slo/entrypoint.yaml | 14 -
.../slo/public/data/slo/slo.ts | 1 +
.../slo/public/locators/slo_edit.test.ts | 13 +-
.../slo/public/locators/slo_edit.ts | 27 +-
.../components/overview/overview.tsx | 22 +-
.../advanced_settings/advanced_settings.tsx | 174 ++++++++
.../advanced_settings/sync_field_selector.tsx | 84 ++++
...ailability_indicator_type_form.stories.tsx | 4 +-
.../apm_availability_indicator_type_form.tsx | 22 +-
.../apm_common/field_selector.stories.tsx | 4 +-
.../apm_common/field_selector.tsx | 4 +-
.../get_group_by_cardinality_filters.test.ts | 0
.../get_group_by_cardinality_filters.ts | 0
.../apm_common/use_apm_default_values.ts | 4 +-
...pm_latency_indicator_type_form.stories.tsx | 4 +-
.../apm_latency_indicator_type_form.tsx | 20 +-
.../index_and_timestamp_field.tsx | 13 +-
.../custom_common/index_selection.stories.tsx | 4 +-
.../custom_common/index_selection.tsx | 85 ++--
.../custom_common/use_adhoc_data_views.ts | 24 +-
...custom_kql_indicator_type_form.stories.tsx | 4 +-
.../custom_kql_indicator_type_form.tsx | 12 +-
.../custom_metric_type_form.stories.tsx | 4 +-
.../custom_metric/custom_metric_type_form.tsx | 12 +-
.../custom_metric/metric_indicator.tsx | 257 +++++------
.../histogram/histogram_indicator.tsx | 6 +-
.../histogram_indicator_type_form.tsx | 12 +-
..._availability_indicator_type_form.test.tsx | 0
...etics_availability_indicator_type_form.tsx | 18 +-
.../synthetics_common/field_selector.tsx | 6 +-
.../timeslice_metric/metric_indicator.tsx | 100 ++---
.../timeslice_metric/metric_input.tsx | 6 +-
.../timeslice_metric_indicator.tsx | 16 +-
.../slo_edit/components/slo_edit_form.tsx | 105 +++--
.../slo_edit_form_description_section.tsx | 218 +++++-----
.../slo_edit_form_indicator_section.tsx | 23 +-
.../slo_edit_form_objective_section.tsx | 409 ++++++++----------
.../slo/public/pages/slo_edit/constants.ts | 21 +-
.../process_slo_form_values.test.ts.snap | 129 ++++++
.../slo_edit/helpers/format_filters.test.ts | 2 +-
.../helpers/process_slo_form_values.test.ts | 18 +-
.../helpers/process_slo_form_values.ts | 41 +-
.../slo_edit/hooks/use_parse_url_state.ts | 4 +-
.../hooks/use_section_form_validation.ts | 4 +-
.../slo_edit/hooks/use_unregister_fields.ts | 2 +-
.../shared_flyout/slo_add_form_flyout.tsx | 29 +-
.../public/pages/slo_edit/slo_edit.test.tsx | 7 +-
.../slo/public/pages/slo_edit/slo_edit.tsx | 2 +-
.../slo/public/pages/slo_edit/types.ts | 3 +
.../public/utils/slo/remote_slo_urls.test.ts | 4 +-
.../slo/server/services/create_slo.ts | 20 +-
.../slo/server/services/slo_repository.ts | 9 +-
.../transform_generator.test.ts.snap | 8 +
.../apm_transaction_duration.ts | 2 +-
.../apm_transaction_error_rate.ts | 2 +-
.../transform_generator.test.ts | 43 ++
.../transform_generator.ts | 3 +-
.../slo/server/services/update_slo.ts | 15 +-
.../monitors_page/hooks/use_create_slo.ts | 4 -
65 files changed, 1326 insertions(+), 846 deletions(-)
create mode 100644 x-pack/plugins/observability_solution/slo/public/pages/slo_edit/components/indicator_section/advanced_settings/advanced_settings.tsx
create mode 100644 x-pack/plugins/observability_solution/slo/public/pages/slo_edit/components/indicator_section/advanced_settings/sync_field_selector.tsx
rename x-pack/plugins/observability_solution/slo/public/pages/slo_edit/components/{ => indicator_section}/apm_availability/apm_availability_indicator_type_form.stories.tsx (84%)
rename x-pack/plugins/observability_solution/slo/public/pages/slo_edit/components/{ => indicator_section}/apm_availability/apm_availability_indicator_type_form.tsx (88%)
rename x-pack/plugins/observability_solution/slo/public/pages/slo_edit/components/{ => indicator_section}/apm_common/field_selector.stories.tsx (87%)
rename x-pack/plugins/observability_solution/slo/public/pages/slo_edit/components/{ => indicator_section}/apm_common/field_selector.tsx (97%)
rename x-pack/plugins/observability_solution/slo/public/pages/slo_edit/components/{ => indicator_section}/apm_common/get_group_by_cardinality_filters.test.ts (100%)
rename x-pack/plugins/observability_solution/slo/public/pages/slo_edit/components/{ => indicator_section}/apm_common/get_group_by_cardinality_filters.ts (100%)
rename x-pack/plugins/observability_solution/slo/public/pages/slo_edit/components/{ => indicator_section}/apm_common/use_apm_default_values.ts (91%)
rename x-pack/plugins/observability_solution/slo/public/pages/slo_edit/components/{ => indicator_section}/apm_latency/apm_latency_indicator_type_form.stories.tsx (84%)
rename x-pack/plugins/observability_solution/slo/public/pages/slo_edit/components/{ => indicator_section}/apm_latency/apm_latency_indicator_type_form.tsx (91%)
rename x-pack/plugins/observability_solution/slo/public/pages/slo_edit/components/{ => indicator_section}/custom_common/index_and_timestamp_field.tsx (83%)
rename x-pack/plugins/observability_solution/slo/public/pages/slo_edit/components/{ => indicator_section}/custom_common/index_selection.stories.tsx (84%)
rename x-pack/plugins/observability_solution/slo/public/pages/slo_edit/components/{ => indicator_section}/custom_common/index_selection.tsx (63%)
rename x-pack/plugins/observability_solution/slo/public/pages/slo_edit/components/{ => indicator_section}/custom_common/use_adhoc_data_views.ts (79%)
rename x-pack/plugins/observability_solution/slo/public/pages/slo_edit/components/{ => indicator_section}/custom_kql/custom_kql_indicator_type_form.stories.tsx (84%)
rename x-pack/plugins/observability_solution/slo/public/pages/slo_edit/components/{ => indicator_section}/custom_kql/custom_kql_indicator_type_form.tsx (91%)
rename x-pack/plugins/observability_solution/slo/public/pages/slo_edit/components/{ => indicator_section}/custom_metric/custom_metric_type_form.stories.tsx (89%)
rename x-pack/plugins/observability_solution/slo/public/pages/slo_edit/components/{ => indicator_section}/custom_metric/custom_metric_type_form.tsx (91%)
rename x-pack/plugins/observability_solution/slo/public/pages/slo_edit/components/{ => indicator_section}/custom_metric/metric_indicator.tsx (60%)
rename x-pack/plugins/observability_solution/slo/public/pages/slo_edit/components/{ => indicator_section}/histogram/histogram_indicator.tsx (98%)
rename x-pack/plugins/observability_solution/slo/public/pages/slo_edit/components/{ => indicator_section}/histogram/histogram_indicator_type_form.tsx (91%)
rename x-pack/plugins/observability_solution/slo/public/pages/slo_edit/components/{ => indicator_section}/synthetics_availability/synthetics_availability_indicator_type_form.test.tsx (100%)
rename x-pack/plugins/observability_solution/slo/public/pages/slo_edit/components/{ => indicator_section}/synthetics_availability/synthetics_availability_indicator_type_form.tsx (93%)
rename x-pack/plugins/observability_solution/slo/public/pages/slo_edit/components/{ => indicator_section}/synthetics_common/field_selector.tsx (96%)
rename x-pack/plugins/observability_solution/slo/public/pages/slo_edit/components/{ => indicator_section}/timeslice_metric/metric_indicator.tsx (80%)
rename x-pack/plugins/observability_solution/slo/public/pages/slo_edit/components/{ => indicator_section}/timeslice_metric/metric_input.tsx (97%)
rename x-pack/plugins/observability_solution/slo/public/pages/slo_edit/components/{ => indicator_section}/timeslice_metric/timeslice_metric_indicator.tsx (88%)
diff --git a/oas_docs/output/kibana.serverless.yaml b/oas_docs/output/kibana.serverless.yaml
index 8766d61ec97f5..b605ab09de62f 100644
--- a/oas_docs/output/kibana.serverless.yaml
+++ b/oas_docs/output/kibana.serverless.yaml
@@ -48663,19 +48663,23 @@ components:
properties:
frequency:
default: 1m
- description: Configure how often the transform runs, default 1m
+ description: 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.
example: 5m
type: string
preventInitialBackfill:
default: false
- description: Prevents the transform from backfilling data when it starts.
+ description: Start aggregating data from the time the SLO is created, instead of backfilling data from the beginning of the time window.
example: true
type: boolean
syncDelay:
default: 1m
- description: The synch delay to apply to the transform. Default 1m
+ description: 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.
example: 5m
type: string
+ syncField:
+ description: The date field that is used to identify new documents in the source. It is strongly recommended to use a field that contains the ingest timestamp. If you use a different field, you might need to set the delay such that it accounts for data transmission delays. When unspecified, we use the indicator timestamp field.
+ example: event.ingested
+ type: string
title: Settings
type: object
SLOs_slo_definition_response:
diff --git a/oas_docs/output/kibana.yaml b/oas_docs/output/kibana.yaml
index 745ddad433106..b1f2186262365 100644
--- a/oas_docs/output/kibana.yaml
+++ b/oas_docs/output/kibana.yaml
@@ -56371,19 +56371,23 @@ components:
properties:
frequency:
default: 1m
- description: Configure how often the transform runs, default 1m
+ description: 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.
example: 5m
type: string
preventInitialBackfill:
default: false
- description: Prevents the transform from backfilling data when it starts.
+ description: Start aggregating data from the time the SLO is created, instead of backfilling data from the beginning of the time window.
example: true
type: boolean
syncDelay:
default: 1m
- description: The synch delay to apply to the transform. Default 1m
+ description: 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.
example: 5m
type: string
+ syncField:
+ description: The date field that is used to identify new documents in the source. It is strongly recommended to use a field that contains the ingest timestamp. If you use a different field, you might need to set the delay such that it accounts for data transmission delays. When unspecified, we use the indicator timestamp field.
+ example: event.ingested
+ type: string
title: Settings
type: object
SLOs_slo_definition_response:
diff --git a/x-pack/packages/kbn-slo-schema/src/schema/slo.ts b/x-pack/packages/kbn-slo-schema/src/schema/slo.ts
index 0576f1cf328eb..c292d355b4867 100644
--- a/x-pack/packages/kbn-slo-schema/src/schema/slo.ts
+++ b/x-pack/packages/kbn-slo-schema/src/schema/slo.ts
@@ -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(
'sloIdSchema',
diff --git a/x-pack/plugins/observability_solution/slo/docs/openapi/slo/bundled.json b/x-pack/plugins/observability_solution/slo/docs/openapi/slo/bundled.json
index 4018a4957b9ab..915fa9e108d4a 100644
--- a/x-pack/plugins/observability_solution/slo/docs/openapi/slo/bundled.json
+++ b/x-pack/plugins/observability_solution/slo/docs/openapi/slo/bundled.json
@@ -1647,20 +1647,25 @@
"description": "Defines properties for SLO settings.",
"type": "object",
"properties": {
+ "syncField": {
+ "description": "The date field that is used to identify new documents in the source. It is strongly recommended to use a field that contains the ingest timestamp. If you use a different field, you might need to set the delay such that it accounts for data transmission delays. When unspecified, we use the indicator timestamp field.",
+ "type": "string",
+ "example": "event.ingested"
+ },
"syncDelay": {
- "description": "The synch delay to apply to the transform. Default 1m",
+ "description": "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.",
"type": "string",
"default": "1m",
"example": "5m"
},
"frequency": {
- "description": "Configure how often the transform runs, default 1m",
+ "description": "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.",
"type": "string",
"default": "1m",
"example": "5m"
},
"preventInitialBackfill": {
- "description": "Prevents the transform from backfilling data when it starts.",
+ "description": "Start aggregating data from the time the SLO is created, instead of backfilling data from the beginning of the time window.",
"type": "boolean",
"default": false,
"example": true
diff --git a/x-pack/plugins/observability_solution/slo/docs/openapi/slo/bundled.yaml b/x-pack/plugins/observability_solution/slo/docs/openapi/slo/bundled.yaml
index a1780acc3e008..96d63163b1d51 100644
--- a/x-pack/plugins/observability_solution/slo/docs/openapi/slo/bundled.yaml
+++ b/x-pack/plugins/observability_solution/slo/docs/openapi/slo/bundled.yaml
@@ -1137,18 +1137,22 @@ components:
description: Defines properties for SLO settings.
type: object
properties:
+ syncField:
+ description: The date field that is used to identify new documents in the source. It is strongly recommended to use a field that contains the ingest timestamp. If you use a different field, you might need to set the delay such that it accounts for data transmission delays. When unspecified, we use the indicator timestamp field.
+ type: string
+ example: event.ingested
syncDelay:
- description: The synch delay to apply to the transform. Default 1m
+ description: 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.
type: string
default: 1m
example: 5m
frequency:
- description: Configure how often the transform runs, default 1m
+ description: 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.
type: string
default: 1m
example: 5m
preventInitialBackfill:
- description: Prevents the transform from backfilling data when it starts.
+ description: Start aggregating data from the time the SLO is created, instead of backfilling data from the beginning of the time window.
type: boolean
default: false
example: true
diff --git a/x-pack/plugins/observability_solution/slo/docs/openapi/slo/components/schemas/settings.yaml b/x-pack/plugins/observability_solution/slo/docs/openapi/slo/components/schemas/settings.yaml
index a50ce0c28c136..e811e18734d51 100644
--- a/x-pack/plugins/observability_solution/slo/docs/openapi/slo/components/schemas/settings.yaml
+++ b/x-pack/plugins/observability_solution/slo/docs/openapi/slo/components/schemas/settings.yaml
@@ -2,18 +2,22 @@ title: Settings
description: Defines properties for SLO settings.
type: object
properties:
+ syncField:
+ description: The date field that is used to identify new documents in the source. It is strongly recommended to use a field that contains the ingest timestamp. If you use a different field, you might need to set the delay such that it accounts for data transmission delays. When unspecified, we use the indicator timestamp field.
+ type: string
+ example: 'event.ingested'
syncDelay:
- description: The synch delay to apply to the transform. Default 1m
+ description: 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.
type: string
default: 1m
example: 5m
frequency:
- description: Configure how often the transform runs, default 1m
+ description: 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.
type: string
default: 1m
example: 5m
preventInitialBackfill:
- description: Prevents the transform from backfilling data when it starts.
+ description: Start aggregating data from the time the SLO is created, instead of backfilling data from the beginning of the time window.
type: boolean
default: false
example: true
diff --git a/x-pack/plugins/observability_solution/slo/docs/openapi/slo/entrypoint.yaml b/x-pack/plugins/observability_solution/slo/docs/openapi/slo/entrypoint.yaml
index 008d063919815..413540ecb96c6 100644
--- a/x-pack/plugins/observability_solution/slo/docs/openapi/slo/entrypoint.yaml
+++ b/x-pack/plugins/observability_solution/slo/docs/openapi/slo/entrypoint.yaml
@@ -33,17 +33,3 @@ paths:
# $ref: "paths/s@{spaceid}@api@slos@_definitions.yaml"
"/s/{spaceId}/api/observability/slos/_delete_instances":
$ref: "paths/s@{spaceid}@api@slos@_delete_instances.yaml"
-# Security is defined when files are joined in oas_docs
-# components:
-# securitySchemes:
-# basicAuth:
-# type: http
-# scheme: basic
-# apiKeyAuth:
-# type: apiKey
-# in: header
-# name: Authorization
-# description: 'e.g. Authorization: ApiKey base64AccessApiKey'
-# security:
-# - basicAuth: []
-# - apiKeyAuth: []
diff --git a/x-pack/plugins/observability_solution/slo/public/data/slo/slo.ts b/x-pack/plugins/observability_solution/slo/public/data/slo/slo.ts
index ce50190eb7adf..0fccc4deb0f8b 100644
--- a/x-pack/plugins/observability_solution/slo/public/data/slo/slo.ts
+++ b/x-pack/plugins/observability_solution/slo/public/data/slo/slo.ts
@@ -39,6 +39,7 @@ const baseSlo: Omit = {
good: 'http_status: 2xx',
total: 'a query',
timestampField: 'custom_timestamp',
+ dataViewId: 'some-data-view-id',
},
},
timeWindow: {
diff --git a/x-pack/plugins/observability_solution/slo/public/locators/slo_edit.test.ts b/x-pack/plugins/observability_solution/slo/public/locators/slo_edit.test.ts
index 40fcae8c840ee..55305a4a3719b 100644
--- a/x-pack/plugins/observability_solution/slo/public/locators/slo_edit.test.ts
+++ b/x-pack/plugins/observability_solution/slo/public/locators/slo_edit.test.ts
@@ -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)"
);
});
});
diff --git a/x-pack/plugins/observability_solution/slo/public/locators/slo_edit.ts b/x-pack/plugins/observability_solution/slo/public/locators/slo_edit.ts
index 120bc533e9eea..2233ea9c5718b 100644
--- a/x-pack/plugins/observability_solution/slo/public/locators/slo_edit.ts
+++ b/x-pack/plugins/observability_solution/slo/public/locators/slo_edit.ts
@@ -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;
-
-export interface SloEditLocatorParams extends SloEditParams, SerializableRecord {}
+export type SloEditLocatorParams = RecursivePartial;
export class SloEditLocatorDefinition implements LocatorDefinition {
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(
+ path: setStateToKbnUrl>(
'_a',
- {
- ...slo,
- },
+ slo,
{ useHash: false, storeInHashQuery: false },
- slo.id ? `/edit/${encodeURIComponent(String(slo.id))}` : `${SLO_CREATE_PATH}`
+ SLO_CREATE_PATH
),
state: {},
};
diff --git a/x-pack/plugins/observability_solution/slo/public/pages/slo_details/components/overview/overview.tsx b/x-pack/plugins/observability_solution/slo/public/pages/slo_details/components/overview/overview.tsx
index 34f3b0132dc8a..9a2f798ab628e 100644
--- a/x-pack/plugins/observability_solution/slo/public/pages/slo_details/components/overview/overview.tsx
+++ b/x-pack/plugins/observability_solution/slo/public/pages/slo_details/components/overview/overview.tsx
@@ -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,
@@ -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;
@@ -170,6 +169,19 @@ export function Overview({ slo }: Props) {
}
/>
)}
+
+
+
);
diff --git a/x-pack/plugins/observability_solution/slo/public/pages/slo_edit/components/indicator_section/advanced_settings/advanced_settings.tsx b/x-pack/plugins/observability_solution/slo/public/pages/slo_edit/components/indicator_section/advanced_settings/advanced_settings.tsx
new file mode 100644
index 0000000000000..81a630990a256
--- /dev/null
+++ b/x-pack/plugins/observability_solution/slo/public/pages/slo_edit/components/indicator_section/advanced_settings/advanced_settings.tsx
@@ -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();
+ const preventBackfillCheckbox = useGeneratedHtmlId({ prefix: 'preventBackfill' });
+ const advancedSettingsAccordion = useGeneratedHtmlId({ prefix: 'advancedSettingsAccordion' });
+
+ return (
+
+
+
+
+
+
+
+
+ {i18n.translate('xpack.slo.sloEdit.settings.advancedSettingsLabel', {
+ defaultMessage: 'Advanced settings',
+ })}
+
+
+
+
+ }
+ >
+
+
+
+
+
+
+
+
+ {i18n.translate('xpack.slo.sloEdit.settings.syncDelay.label', {
+ defaultMessage: 'Sync delay (in minutes)',
+ })}{' '}
+
+
+ }
+ >
+ (
+ onChange(event.target.value)}
+ />
+ )}
+ />
+
+
+
+
+
+ {i18n.translate('xpack.slo.sloEdit.settings.frequency.label', {
+ defaultMessage: 'Frequency (in minutes)',
+ })}{' '}
+
+
+ }
+ >
+ (
+ onChange(event.target.value)}
+ />
+ )}
+ />
+
+
+
+
+
+ (
+
+ {i18n.translate('xpack.slo.sloEdit.settings.preventInitialBackfill.label', {
+ defaultMessage: 'Prevent initial backfill of data',
+ })}{' '}
+
+
+ }
+ checked={Boolean(field.value)}
+ onChange={(event: any) => onChange(event.target.checked)}
+ />
+ )}
+ />
+
+
+
+ );
+}
diff --git a/x-pack/plugins/observability_solution/slo/public/pages/slo_edit/components/indicator_section/advanced_settings/sync_field_selector.tsx b/x-pack/plugins/observability_solution/slo/public/pages/slo_edit/components/indicator_section/advanced_settings/sync_field_selector.tsx
new file mode 100644
index 0000000000000..ddfb51bb28977
--- /dev/null
+++ b/x-pack/plugins/observability_solution/slo/public/pages/slo_edit/components/indicator_section/advanced_settings/sync_field_selector.tsx
@@ -0,0 +1,84 @@
+/*
+ * 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 { EuiComboBox, EuiComboBoxOptionOption, EuiFormRow, EuiIconTip } from '@elastic/eui';
+import { i18n } from '@kbn/i18n';
+import React from 'react';
+import { Controller, useFormContext } from 'react-hook-form';
+import { useCreateDataView } from '../../../../../hooks/use_create_data_view';
+import { createOptionsFromFields } from '../../../helpers/create_options';
+import { CreateSLOForm } from '../../../types';
+import { OptionalText } from '../../common/optional_text';
+
+const placeholder = i18n.translate('xpack.slo.sloEdit.settings.syncField.placeholder', {
+ defaultMessage: 'Select a timestamp field',
+});
+
+export function SyncFieldSelector() {
+ const { control, watch, getFieldState } = useFormContext();
+ const [index, dataViewId] = watch(['indicator.params.index', 'indicator.params.dataViewId']);
+ const { dataView, loading: isIndexFieldsLoading } = useCreateDataView({
+ indexPatternString: index,
+ dataViewId,
+ });
+ const timestampFields = dataView?.fields?.filter((field) => field.type === 'date') ?? [];
+
+ return (
+
+ {i18n.translate('xpack.slo.sloEdit.settings.syncField.label', {
+ defaultMessage: 'Sync field',
+ })}{' '}
+
+
+ }
+ isInvalid={getFieldState('settings.syncField').invalid}
+ labelAppend={ }
+ >
+ {
+ return (
+
+ {...field}
+ placeholder={placeholder}
+ aria-label={placeholder}
+ isClearable
+ isDisabled={isIndexFieldsLoading}
+ isInvalid={fieldState.invalid}
+ isLoading={isIndexFieldsLoading}
+ onChange={(selected: EuiComboBoxOptionOption[]) => {
+ if (selected.length) {
+ return field.onChange(selected[0].value);
+ }
+
+ field.onChange(null);
+ }}
+ singleSelection={{ asPlainText: true }}
+ options={createOptionsFromFields(timestampFields)}
+ selectedOptions={
+ !!timestampFields && !!field.value
+ ? [{ value: field.value, label: field.value }]
+ : []
+ }
+ />
+ );
+ }}
+ />
+
+ );
+}
diff --git a/x-pack/plugins/observability_solution/slo/public/pages/slo_edit/components/apm_availability/apm_availability_indicator_type_form.stories.tsx b/x-pack/plugins/observability_solution/slo/public/pages/slo_edit/components/indicator_section/apm_availability/apm_availability_indicator_type_form.stories.tsx
similarity index 84%
rename from x-pack/plugins/observability_solution/slo/public/pages/slo_edit/components/apm_availability/apm_availability_indicator_type_form.stories.tsx
rename to x-pack/plugins/observability_solution/slo/public/pages/slo_edit/components/indicator_section/apm_availability/apm_availability_indicator_type_form.stories.tsx
index c3c506eb484eb..d40d56941ccfe 100644
--- a/x-pack/plugins/observability_solution/slo/public/pages/slo_edit/components/apm_availability/apm_availability_indicator_type_form.stories.tsx
+++ b/x-pack/plugins/observability_solution/slo/public/pages/slo_edit/components/indicator_section/apm_availability/apm_availability_indicator_type_form.stories.tsx
@@ -9,9 +9,9 @@ import React from 'react';
import { ComponentStory } from '@storybook/react';
import { FormProvider, useForm } from 'react-hook-form';
-import { KibanaReactStorybookDecorator } from '../../../../utils/kibana_react.storybook_decorator';
+import { KibanaReactStorybookDecorator } from '../../../../../utils/kibana_react.storybook_decorator';
import { ApmAvailabilityIndicatorTypeForm as Component } from './apm_availability_indicator_type_form';
-import { SLO_EDIT_FORM_DEFAULT_VALUES } from '../../constants';
+import { SLO_EDIT_FORM_DEFAULT_VALUES } from '../../../constants';
export default {
component: Component,
diff --git a/x-pack/plugins/observability_solution/slo/public/pages/slo_edit/components/apm_availability/apm_availability_indicator_type_form.tsx b/x-pack/plugins/observability_solution/slo/public/pages/slo_edit/components/indicator_section/apm_availability/apm_availability_indicator_type_form.tsx
similarity index 88%
rename from x-pack/plugins/observability_solution/slo/public/pages/slo_edit/components/apm_availability/apm_availability_indicator_type_form.tsx
rename to x-pack/plugins/observability_solution/slo/public/pages/slo_edit/components/indicator_section/apm_availability/apm_availability_indicator_type_form.tsx
index 0dcddcdb232b5..fd00e3d359530 100644
--- a/x-pack/plugins/observability_solution/slo/public/pages/slo_edit/components/apm_availability/apm_availability_indicator_type_form.tsx
+++ b/x-pack/plugins/observability_solution/slo/public/pages/slo_edit/components/indicator_section/apm_availability/apm_availability_indicator_type_form.tsx
@@ -12,14 +12,14 @@ import React from 'react';
import { useFormContext } from 'react-hook-form';
import { useApmDefaultValues } from '../apm_common/use_apm_default_values';
import { DATA_VIEW_FIELD } from '../custom_common/index_selection';
-import { useCreateDataView } from '../../../../hooks/use_create_data_view';
-import { GroupByField } from '../common/group_by_field';
-import { useFetchApmIndex } from '../../../../hooks/use_fetch_apm_indices';
-import { CreateSLOForm } from '../../types';
+import { useCreateDataView } from '../../../../../hooks/use_create_data_view';
+import { GroupByField } from '../../common/group_by_field';
+import { useFetchApmIndex } from '../../../../../hooks/use_fetch_apm_indices';
+import { CreateSLOForm } from '../../../types';
import { FieldSelector } from '../apm_common/field_selector';
-import { DataPreviewChart } from '../common/data_preview_chart';
-import { QueryBuilder } from '../common/query_builder';
-import { formatAllFilters } from '../../helpers/format_filters';
+import { DataPreviewChart } from '../../common/data_preview_chart';
+import { QueryBuilder } from '../../common/query_builder';
+import { formatAllFilters } from '../../../helpers/format_filters';
import { getGroupByCardinalityFilters } from '../apm_common/get_group_by_cardinality_filters';
export function ApmAvailabilityIndicatorTypeForm() {
@@ -56,8 +56,8 @@ export function ApmAvailabilityIndicatorTypeForm() {
});
return (
-
-
+
+
-
+
-
+
{
const { watch, setValue } = useFormContext>();
diff --git a/x-pack/plugins/observability_solution/slo/public/pages/slo_edit/components/apm_latency/apm_latency_indicator_type_form.stories.tsx b/x-pack/plugins/observability_solution/slo/public/pages/slo_edit/components/indicator_section/apm_latency/apm_latency_indicator_type_form.stories.tsx
similarity index 84%
rename from x-pack/plugins/observability_solution/slo/public/pages/slo_edit/components/apm_latency/apm_latency_indicator_type_form.stories.tsx
rename to x-pack/plugins/observability_solution/slo/public/pages/slo_edit/components/indicator_section/apm_latency/apm_latency_indicator_type_form.stories.tsx
index 3ca02641f9bfa..9b346c94dea9a 100644
--- a/x-pack/plugins/observability_solution/slo/public/pages/slo_edit/components/apm_latency/apm_latency_indicator_type_form.stories.tsx
+++ b/x-pack/plugins/observability_solution/slo/public/pages/slo_edit/components/indicator_section/apm_latency/apm_latency_indicator_type_form.stories.tsx
@@ -9,9 +9,9 @@ import React from 'react';
import { ComponentStory } from '@storybook/react';
import { FormProvider, useForm } from 'react-hook-form';
-import { KibanaReactStorybookDecorator } from '../../../../utils/kibana_react.storybook_decorator';
+import { KibanaReactStorybookDecorator } from '../../../../../utils/kibana_react.storybook_decorator';
import { ApmLatencyIndicatorTypeForm as Component } from './apm_latency_indicator_type_form';
-import { SLO_EDIT_FORM_DEFAULT_VALUES } from '../../constants';
+import { SLO_EDIT_FORM_DEFAULT_VALUES } from '../../../constants';
export default {
component: Component,
diff --git a/x-pack/plugins/observability_solution/slo/public/pages/slo_edit/components/apm_latency/apm_latency_indicator_type_form.tsx b/x-pack/plugins/observability_solution/slo/public/pages/slo_edit/components/indicator_section/apm_latency/apm_latency_indicator_type_form.tsx
similarity index 91%
rename from x-pack/plugins/observability_solution/slo/public/pages/slo_edit/components/apm_latency/apm_latency_indicator_type_form.tsx
rename to x-pack/plugins/observability_solution/slo/public/pages/slo_edit/components/indicator_section/apm_latency/apm_latency_indicator_type_form.tsx
index 03b47aafe4150..0d7b86d0b88d3 100644
--- a/x-pack/plugins/observability_solution/slo/public/pages/slo_edit/components/apm_latency/apm_latency_indicator_type_form.tsx
+++ b/x-pack/plugins/observability_solution/slo/public/pages/slo_edit/components/indicator_section/apm_latency/apm_latency_indicator_type_form.tsx
@@ -12,14 +12,14 @@ import React from 'react';
import { Controller, useFormContext } from 'react-hook-form';
import { useApmDefaultValues } from '../apm_common/use_apm_default_values';
import { DATA_VIEW_FIELD } from '../custom_common/index_selection';
-import { GroupByField } from '../common/group_by_field';
-import { useCreateDataView } from '../../../../hooks/use_create_data_view';
-import { useFetchApmIndex } from '../../../../hooks/use_fetch_apm_indices';
-import { CreateSLOForm } from '../../types';
+import { GroupByField } from '../../common/group_by_field';
+import { useCreateDataView } from '../../../../../hooks/use_create_data_view';
+import { useFetchApmIndex } from '../../../../../hooks/use_fetch_apm_indices';
+import { CreateSLOForm } from '../../../types';
import { FieldSelector } from '../apm_common/field_selector';
-import { DataPreviewChart } from '../common/data_preview_chart';
-import { QueryBuilder } from '../common/query_builder';
-import { formatAllFilters } from '../../helpers/format_filters';
+import { DataPreviewChart } from '../../common/data_preview_chart';
+import { QueryBuilder } from '../../common/query_builder';
+import { formatAllFilters } from '../../../helpers/format_filters';
import { getGroupByCardinalityFilters } from '../apm_common/get_group_by_cardinality_filters';
export function ApmLatencyIndicatorTypeForm() {
@@ -58,8 +58,8 @@ export function ApmLatencyIndicatorTypeForm() {
});
return (
-
-
+
+
-
+
();
const index = watch('indicator.params.index');
diff --git a/x-pack/plugins/observability_solution/slo/public/pages/slo_edit/components/custom_common/index_selection.stories.tsx b/x-pack/plugins/observability_solution/slo/public/pages/slo_edit/components/indicator_section/custom_common/index_selection.stories.tsx
similarity index 84%
rename from x-pack/plugins/observability_solution/slo/public/pages/slo_edit/components/custom_common/index_selection.stories.tsx
rename to x-pack/plugins/observability_solution/slo/public/pages/slo_edit/components/indicator_section/custom_common/index_selection.stories.tsx
index 4b8dce62f43bb..b1739a63881f5 100644
--- a/x-pack/plugins/observability_solution/slo/public/pages/slo_edit/components/custom_common/index_selection.stories.tsx
+++ b/x-pack/plugins/observability_solution/slo/public/pages/slo_edit/components/indicator_section/custom_common/index_selection.stories.tsx
@@ -9,9 +9,9 @@ import React from 'react';
import { ComponentStory } from '@storybook/react';
import { FormProvider, useForm } from 'react-hook-form';
-import { KibanaReactStorybookDecorator } from '../../../../utils/kibana_react.storybook_decorator';
+import { KibanaReactStorybookDecorator } from '../../../../../utils/kibana_react.storybook_decorator';
import { IndexSelection as Component } from './index_selection';
-import { SLO_EDIT_FORM_DEFAULT_VALUES } from '../../constants';
+import { SLO_EDIT_FORM_DEFAULT_VALUES } from '../../../constants';
export default {
component: Component,
diff --git a/x-pack/plugins/observability_solution/slo/public/pages/slo_edit/components/custom_common/index_selection.tsx b/x-pack/plugins/observability_solution/slo/public/pages/slo_edit/components/indicator_section/custom_common/index_selection.tsx
similarity index 63%
rename from x-pack/plugins/observability_solution/slo/public/pages/slo_edit/components/custom_common/index_selection.tsx
rename to x-pack/plugins/observability_solution/slo/public/pages/slo_edit/components/indicator_section/custom_common/index_selection.tsx
index 146d11be84ac8..9d5489ddd283f 100644
--- a/x-pack/plugins/observability_solution/slo/public/pages/slo_edit/components/custom_common/index_selection.tsx
+++ b/x-pack/plugins/observability_solution/slo/public/pages/slo_edit/components/indicator_section/custom_common/index_selection.tsx
@@ -8,37 +8,47 @@
import { EuiFormRow } from '@elastic/eui';
import { DataView } from '@kbn/data-views-plugin/public';
import { i18n } from '@kbn/i18n';
+import { ALL_VALUE } from '@kbn/slo-schema';
+import { DataViewPicker } from '@kbn/unified-search-plugin/public';
import React, { useEffect } from 'react';
import { Controller, useFormContext } from 'react-hook-form';
-import { DataViewPicker } from '@kbn/unified-search-plugin/public';
-import { getDataViewPattern, useAdhocDataViews } from './use_adhoc_data_views';
-import { SLOPublicPluginsStart } from '../../../..';
-import { useKibana } from '../../../../hooks/use_kibana';
-import { CreateSLOForm } from '../../types';
+import { SLOPublicPluginsStart } from '../../../../..';
+import { useKibana } from '../../../../../hooks/use_kibana';
+import { CreateSLOForm } from '../../../types';
+import { getDataViewPatternOrId, useAdhocDataViews } from './use_adhoc_data_views';
const BTN_MAX_WIDTH = 515;
export const DATA_VIEW_FIELD = 'indicator.params.dataViewId';
const INDEX_FIELD = 'indicator.params.index';
-const TIMESTAMP_FIELD = 'indicator.params.timestampField';
+const INDICATOR_TIMESTAMP_FIELD = 'indicator.params.timestampField';
+const GROUP_BY_FIELD = 'groupBy';
+const SETTINGS_SYNC_FIELD = 'settings.syncField';
export function IndexSelection({ selectedDataView }: { selectedDataView?: DataView }) {
const { control, getFieldState, setValue, watch } = useFormContext();
- const { dataViews: dataViewsService, dataViewFieldEditor } = useKibana().services;
-
- const { dataViewEditor } = useKibana().services;
+ const {
+ dataViews: dataViewsService,
+ dataViewFieldEditor,
+ dataViewEditor,
+ } = useKibana().services;
const currentIndexPattern = watch(INDEX_FIELD);
const currentDataViewId = watch(DATA_VIEW_FIELD);
- const { dataViewsList, isDataViewsLoading, adHocDataViews, setAdHocDataViews, refetch } =
- useAdhocDataViews({
- currentIndexPattern,
- });
+ const {
+ dataViewsList,
+ isDataViewsLoading,
+ adHocDataViews,
+ setAdHocDataViews,
+ refetchDataViewsList,
+ } = useAdhocDataViews({
+ currentIndexPattern,
+ });
useEffect(() => {
- const indPatternId = getDataViewPattern({
- byPatten: currentIndexPattern,
+ const indPatternId = getDataViewPatternOrId({
+ byPattern: currentIndexPattern,
dataViewsList,
adHocDataViews,
});
@@ -54,13 +64,24 @@ export function IndexSelection({ selectedDataView }: { selectedDataView?: DataVi
setValue,
]);
+ const updateDataViewDependantFields = (indexPattern?: string, timestampField?: string) => {
+ setValue(INDEX_FIELD, indexPattern ?? '');
+ setValue(INDICATOR_TIMESTAMP_FIELD, timestampField ?? '');
+ setValue(GROUP_BY_FIELD, ALL_VALUE);
+ setValue(SETTINGS_SYNC_FIELD, null);
+ };
+
return (
-
+
(
{
- setValue(
- INDEX_FIELD,
- getDataViewPattern({ byId: newId, adHocDataViews, dataViewsList })!
- );
field.onChange(newId);
+
dataViewsService.get(newId).then((dataView) => {
- if (dataView.timeFieldName) {
- setValue(TIMESTAMP_FIELD, dataView.timeFieldName);
- }
+ updateDataViewDependantFields(
+ getDataViewPatternOrId({ byId: newId, adHocDataViews, dataViewsList })!,
+ dataView.timeFieldName
+ );
});
}}
onAddField={
@@ -97,8 +116,8 @@ export function IndexSelection({ selectedDataView }: { selectedDataView?: DataVi
}
currentDataViewId={
field.value ??
- getDataViewPattern({
- byPatten: currentIndexPattern,
+ getDataViewPatternOrId({
+ byPattern: currentIndexPattern,
dataViewsList,
adHocDataViews,
})
@@ -108,17 +127,13 @@ export function IndexSelection({ selectedDataView }: { selectedDataView?: DataVi
allowAdHocDataView: true,
onSave: (dataView: DataView) => {
if (!dataView.isPersisted()) {
- setAdHocDataViews([...adHocDataViews, dataView]);
- field.onChange(dataView.id);
- setValue(INDEX_FIELD, dataView.getIndexPattern());
+ setAdHocDataViews((prev) => [...prev, dataView]);
} else {
- refetch();
- field.onChange(dataView.id);
- setValue(INDEX_FIELD, dataView.getIndexPattern());
- }
- if (dataView.timeFieldName) {
- setValue(TIMESTAMP_FIELD, dataView.timeFieldName);
+ refetchDataViewsList();
}
+
+ field.onChange(dataView.id);
+ updateDataViewDependantFields(dataView.getIndexPattern(), dataView.timeFieldName);
},
});
}}
diff --git a/x-pack/plugins/observability_solution/slo/public/pages/slo_edit/components/custom_common/use_adhoc_data_views.ts b/x-pack/plugins/observability_solution/slo/public/pages/slo_edit/components/indicator_section/custom_common/use_adhoc_data_views.ts
similarity index 79%
rename from x-pack/plugins/observability_solution/slo/public/pages/slo_edit/components/custom_common/use_adhoc_data_views.ts
rename to x-pack/plugins/observability_solution/slo/public/pages/slo_edit/components/indicator_section/custom_common/use_adhoc_data_views.ts
index 67792b056408d..986b681c9bca9 100644
--- a/x-pack/plugins/observability_solution/slo/public/pages/slo_edit/components/custom_common/use_adhoc_data_views.ts
+++ b/x-pack/plugins/observability_solution/slo/public/pages/slo_edit/components/indicator_section/custom_common/use_adhoc_data_views.ts
@@ -8,16 +8,16 @@
import { useEffect, useState } from 'react';
import { DataView, DataViewListItem } from '@kbn/data-views-plugin/common';
import { useFetchDataViews } from '@kbn/observability-plugin/public';
-import { useKibana } from '../../../../hooks/use_kibana';
+import { useKibana } from '../../../../../hooks/use_kibana';
-export const getDataViewPattern = ({
+export const getDataViewPatternOrId = ({
byId,
- byPatten,
+ byPattern,
dataViewsList,
adHocDataViews,
}: {
byId?: string;
- byPatten?: string;
+ byPattern?: string;
dataViewsList: DataViewListItem[];
adHocDataViews: DataView[];
}) => {
@@ -28,20 +28,24 @@ export const getDataViewPattern = ({
if (byId) {
return allDataViews.find((dv) => dv.id === byId)?.title;
}
- if (byPatten) {
- return allDataViews.find((dv) => dv.title === byPatten)?.id;
+ if (byPattern) {
+ return allDataViews.find((dv) => dv.title === byPattern)?.id;
}
};
export const useAdhocDataViews = ({ currentIndexPattern }: { currentIndexPattern: string }) => {
- const { isLoading: isDataViewsLoading, data: dataViewsList = [], refetch } = useFetchDataViews();
+ const {
+ isLoading: isDataViewsLoading,
+ data: dataViewsList = [],
+ refetch: refetchDataViewsList,
+ } = useFetchDataViews();
const { dataViews: dataViewsService } = useKibana().services;
const [adHocDataViews, setAdHocDataViews] = useState([]);
useEffect(() => {
if (!isDataViewsLoading) {
- const missingDataView = getDataViewPattern({
- byPatten: currentIndexPattern,
+ const missingDataView = getDataViewPatternOrId({
+ byPattern: currentIndexPattern,
dataViewsList,
adHocDataViews,
});
@@ -70,6 +74,6 @@ export const useAdhocDataViews = ({ currentIndexPattern }: { currentIndexPattern
setAdHocDataViews,
dataViewsList,
isDataViewsLoading,
- refetch,
+ refetchDataViewsList,
};
};
diff --git a/x-pack/plugins/observability_solution/slo/public/pages/slo_edit/components/custom_kql/custom_kql_indicator_type_form.stories.tsx b/x-pack/plugins/observability_solution/slo/public/pages/slo_edit/components/indicator_section/custom_kql/custom_kql_indicator_type_form.stories.tsx
similarity index 84%
rename from x-pack/plugins/observability_solution/slo/public/pages/slo_edit/components/custom_kql/custom_kql_indicator_type_form.stories.tsx
rename to x-pack/plugins/observability_solution/slo/public/pages/slo_edit/components/indicator_section/custom_kql/custom_kql_indicator_type_form.stories.tsx
index 5eb0b68070789..1ecf3f57c1496 100644
--- a/x-pack/plugins/observability_solution/slo/public/pages/slo_edit/components/custom_kql/custom_kql_indicator_type_form.stories.tsx
+++ b/x-pack/plugins/observability_solution/slo/public/pages/slo_edit/components/indicator_section/custom_kql/custom_kql_indicator_type_form.stories.tsx
@@ -9,9 +9,9 @@ import React from 'react';
import { ComponentStory } from '@storybook/react';
import { FormProvider, useForm } from 'react-hook-form';
-import { KibanaReactStorybookDecorator } from '../../../../utils/kibana_react.storybook_decorator';
+import { KibanaReactStorybookDecorator } from '../../../../../utils/kibana_react.storybook_decorator';
import { CustomKqlIndicatorTypeForm as Component } from './custom_kql_indicator_type_form';
-import { SLO_EDIT_FORM_DEFAULT_VALUES } from '../../constants';
+import { SLO_EDIT_FORM_DEFAULT_VALUES } from '../../../constants';
export default {
component: Component,
diff --git a/x-pack/plugins/observability_solution/slo/public/pages/slo_edit/components/custom_kql/custom_kql_indicator_type_form.tsx b/x-pack/plugins/observability_solution/slo/public/pages/slo_edit/components/indicator_section/custom_kql/custom_kql_indicator_type_form.tsx
similarity index 91%
rename from x-pack/plugins/observability_solution/slo/public/pages/slo_edit/components/custom_kql/custom_kql_indicator_type_form.tsx
rename to x-pack/plugins/observability_solution/slo/public/pages/slo_edit/components/indicator_section/custom_kql/custom_kql_indicator_type_form.tsx
index 92ba2cac50e7f..ccebca1fbb36f 100644
--- a/x-pack/plugins/observability_solution/slo/public/pages/slo_edit/components/custom_kql/custom_kql_indicator_type_form.tsx
+++ b/x-pack/plugins/observability_solution/slo/public/pages/slo_edit/components/indicator_section/custom_kql/custom_kql_indicator_type_form.tsx
@@ -9,12 +9,12 @@ import { EuiFlexGroup, EuiFlexItem, EuiIconTip } from '@elastic/eui';
import { i18n } from '@kbn/i18n';
import React from 'react';
import { useFormContext } from 'react-hook-form';
+import { useCreateDataView } from '../../../../../hooks/use_create_data_view';
+import { CreateSLOForm } from '../../../types';
+import { DataPreviewChart } from '../../common/data_preview_chart';
+import { GroupByField } from '../../common/group_by_field';
+import { QueryBuilder } from '../../common/query_builder';
import { IndexAndTimestampField } from '../custom_common/index_and_timestamp_field';
-import { GroupByField } from '../common/group_by_field';
-import { useCreateDataView } from '../../../../hooks/use_create_data_view';
-import { CreateSLOForm } from '../../types';
-import { DataPreviewChart } from '../common/data_preview_chart';
-import { QueryBuilder } from '../common/query_builder';
import { DATA_VIEW_FIELD } from '../custom_common/index_selection';
export function CustomKqlIndicatorTypeForm() {
@@ -28,7 +28,7 @@ export function CustomKqlIndicatorTypeForm() {
});
return (
-
+
diff --git a/x-pack/plugins/observability_solution/slo/public/pages/slo_edit/components/custom_metric/custom_metric_type_form.stories.tsx b/x-pack/plugins/observability_solution/slo/public/pages/slo_edit/components/indicator_section/custom_metric/custom_metric_type_form.stories.tsx
similarity index 89%
rename from x-pack/plugins/observability_solution/slo/public/pages/slo_edit/components/custom_metric/custom_metric_type_form.stories.tsx
rename to x-pack/plugins/observability_solution/slo/public/pages/slo_edit/components/indicator_section/custom_metric/custom_metric_type_form.stories.tsx
index 1abbff61a0dc8..771405a539f1b 100644
--- a/x-pack/plugins/observability_solution/slo/public/pages/slo_edit/components/custom_metric/custom_metric_type_form.stories.tsx
+++ b/x-pack/plugins/observability_solution/slo/public/pages/slo_edit/components/indicator_section/custom_metric/custom_metric_type_form.stories.tsx
@@ -9,9 +9,9 @@ import React from 'react';
import { ComponentStory } from '@storybook/react';
import { FormProvider, useForm } from 'react-hook-form';
-import { KibanaReactStorybookDecorator } from '../../../../utils/kibana_react.storybook_decorator';
+import { KibanaReactStorybookDecorator } from '../../../../../utils/kibana_react.storybook_decorator';
import { CustomMetricIndicatorTypeForm as Component } from './custom_metric_type_form';
-import { SLO_EDIT_FORM_DEFAULT_VALUES_CUSTOM_METRIC } from '../../constants';
+import { SLO_EDIT_FORM_DEFAULT_VALUES_CUSTOM_METRIC } from '../../../constants';
export default {
component: Component,
diff --git a/x-pack/plugins/observability_solution/slo/public/pages/slo_edit/components/custom_metric/custom_metric_type_form.tsx b/x-pack/plugins/observability_solution/slo/public/pages/slo_edit/components/indicator_section/custom_metric/custom_metric_type_form.tsx
similarity index 91%
rename from x-pack/plugins/observability_solution/slo/public/pages/slo_edit/components/custom_metric/custom_metric_type_form.tsx
rename to x-pack/plugins/observability_solution/slo/public/pages/slo_edit/components/indicator_section/custom_metric/custom_metric_type_form.tsx
index ee9bcf8d99649..365205ed6b4bf 100644
--- a/x-pack/plugins/observability_solution/slo/public/pages/slo_edit/components/custom_metric/custom_metric_type_form.tsx
+++ b/x-pack/plugins/observability_solution/slo/public/pages/slo_edit/components/indicator_section/custom_metric/custom_metric_type_form.tsx
@@ -18,11 +18,11 @@ import { FormattedMessage } from '@kbn/i18n-react';
import React from 'react';
import { useFormContext } from 'react-hook-form';
import { IndexAndTimestampField } from '../custom_common/index_and_timestamp_field';
-import { GroupByField } from '../common/group_by_field';
-import { useCreateDataView } from '../../../../hooks/use_create_data_view';
-import { CreateSLOForm } from '../../types';
-import { DataPreviewChart } from '../common/data_preview_chart';
-import { QueryBuilder } from '../common/query_builder';
+import { GroupByField } from '../../common/group_by_field';
+import { useCreateDataView } from '../../../../../hooks/use_create_data_view';
+import { CreateSLOForm } from '../../../types';
+import { DataPreviewChart } from '../../common/data_preview_chart';
+import { QueryBuilder } from '../../common/query_builder';
import { DATA_VIEW_FIELD } from '../custom_common/index_selection';
import { MetricIndicator } from './metric_indicator';
@@ -55,7 +55,7 @@ export function CustomMetricIndicatorTypeForm() {
-
+
diff --git a/x-pack/plugins/observability_solution/slo/public/pages/slo_edit/components/custom_metric/metric_indicator.tsx b/x-pack/plugins/observability_solution/slo/public/pages/slo_edit/components/indicator_section/custom_metric/metric_indicator.tsx
similarity index 60%
rename from x-pack/plugins/observability_solution/slo/public/pages/slo_edit/components/custom_metric/metric_indicator.tsx
rename to x-pack/plugins/observability_solution/slo/public/pages/slo_edit/components/indicator_section/custom_metric/metric_indicator.tsx
index 03939dce314b6..519167be5db27 100644
--- a/x-pack/plugins/observability_solution/slo/public/pages/slo_edit/components/custom_metric/metric_indicator.tsx
+++ b/x-pack/plugins/observability_solution/slo/public/pages/slo_edit/components/indicator_section/custom_metric/metric_indicator.tsx
@@ -26,10 +26,10 @@ import { Controller, useFieldArray, useFormContext } from 'react-hook-form';
import {
aggValueToLabel,
CUSTOM_METRIC_AGGREGATION_OPTIONS,
-} from '../../helpers/aggregation_options';
-import { createOptionsFromFields, Option } from '../../helpers/create_options';
-import { CreateSLOForm } from '../../types';
-import { QueryBuilder } from '../common/query_builder';
+} from '../../../helpers/aggregation_options';
+import { createOptionsFromFields, Option } from '../../../helpers/create_options';
+import { CreateSLOForm } from '../../../types';
+import { QueryBuilder } from '../../common/query_builder';
interface MetricIndicatorProps {
type: 'good' | 'total';
@@ -134,95 +134,28 @@ export function MetricIndicator({
{fields?.map((metric, index, arr) => (
-
-
-
-
- {i18n.translate('xpack.slo.sloEdit.customMetric.aggregationLabel', {
- defaultMessage: 'Aggregation',
- })}{' '}
- {metric.name}
-
- }
- >
- (
- {
- if (selected.length) {
- return field.onChange(selected[0].value);
- }
- field.onChange('');
- }}
- selectedOptions={
- !!indexPattern &&
- !!field.value &&
- CUSTOM_METRIC_AGGREGATION_OPTIONS.some((agg) => agg.value === field.value)
- ? [
- {
- value: field.value,
- label: aggValueToLabel(field.value),
- },
- ]
- : []
- }
- onSearchChange={(searchValue: string) => {
- setAggregationOptions(
- CUSTOM_METRIC_AGGREGATION_OPTIONS.filter(({ value }) =>
- value.includes(searchValue)
- )
- );
- }}
- options={aggregationOptions}
- />
- )}
- />
-
-
- {watch(`indicator.params.${type}.metrics.${index}.aggregation`) !== 'doc_count' && (
+
+
+
- {metricLabel} {metric.name} {metricTooltip}
+ {i18n.translate('xpack.slo.sloEdit.customMetric.aggregationLabel', {
+ defaultMessage: 'Aggregation',
+ })}{' '}
+ {metric.name}
}
>
(
metricField.name === field.value)
+ CUSTOM_METRIC_AGGREGATION_OPTIONS.some(
+ (agg) => agg.value === field.value
+ )
? [
{
value: field.value,
- label: field.value,
+ label: aggValueToLabel(field.value),
},
]
: []
}
onSearchChange={(searchValue: string) => {
- setOptions(
- createOptionsFromFields(metricFields, ({ value }) =>
+ setAggregationOptions(
+ CUSTOM_METRIC_AGGREGATION_OPTIONS.filter(({ value }) =>
value.includes(searchValue)
)
);
}}
- options={options}
+ options={aggregationOptions}
/>
)}
/>
- )}
-
-
-
+ {watch(`indicator.params.${type}.metrics.${index}.aggregation`) !== 'doc_count' && (
+
+
+ {metricLabel} {metric.name} {metricTooltip}
+
+ }
+ >
+ (
+ {
+ if (selected.length) {
+ return field.onChange(selected[0].value);
+ }
+ field.onChange('');
+ }}
+ selectedOptions={
+ !!indexPattern &&
+ !!field.value &&
+ metricFields.some((metricField) => metricField.name === field.value)
+ ? [
+ {
+ value: field.value,
+ label: field.value,
+ },
+ ]
+ : []
+ }
+ onSearchChange={(searchValue: string) => {
+ setOptions(
+ createOptionsFromFields(metricFields, ({ value }) =>
+ value.includes(searchValue)
+ )
+ );
+ }}
+ options={options}
+ />
+ )}
+ />
+
+
+ )}
+
+
+
+
+
+ }
+ />
-
- }
- />
{index !== arr.length - 1 && }
))}
diff --git a/x-pack/plugins/observability_solution/slo/public/pages/slo_edit/components/histogram/histogram_indicator.tsx b/x-pack/plugins/observability_solution/slo/public/pages/slo_edit/components/indicator_section/histogram/histogram_indicator.tsx
similarity index 98%
rename from x-pack/plugins/observability_solution/slo/public/pages/slo_edit/components/histogram/histogram_indicator.tsx
rename to x-pack/plugins/observability_solution/slo/public/pages/slo_edit/components/indicator_section/histogram/histogram_indicator.tsx
index 009504e5e6979..3b435fa52494b 100644
--- a/x-pack/plugins/observability_solution/slo/public/pages/slo_edit/components/histogram/histogram_indicator.tsx
+++ b/x-pack/plugins/observability_solution/slo/public/pages/slo_edit/components/indicator_section/histogram/histogram_indicator.tsx
@@ -19,9 +19,9 @@ import { DataView, FieldSpec } from '@kbn/data-views-plugin/common';
import { i18n } from '@kbn/i18n';
import React, { Fragment, useEffect, useState } from 'react';
import { Controller, useFormContext } from 'react-hook-form';
-import { createOptionsFromFields, Option } from '../../helpers/create_options';
-import { CreateSLOForm } from '../../types';
-import { QueryBuilder } from '../common/query_builder';
+import { createOptionsFromFields, Option } from '../../../helpers/create_options';
+import { CreateSLOForm } from '../../../types';
+import { QueryBuilder } from '../../common/query_builder';
interface HistogramIndicatorProps {
type: 'good' | 'total';
diff --git a/x-pack/plugins/observability_solution/slo/public/pages/slo_edit/components/histogram/histogram_indicator_type_form.tsx b/x-pack/plugins/observability_solution/slo/public/pages/slo_edit/components/indicator_section/histogram/histogram_indicator_type_form.tsx
similarity index 91%
rename from x-pack/plugins/observability_solution/slo/public/pages/slo_edit/components/histogram/histogram_indicator_type_form.tsx
rename to x-pack/plugins/observability_solution/slo/public/pages/slo_edit/components/indicator_section/histogram/histogram_indicator_type_form.tsx
index 6bb1918dba3c2..2e934c74d9d0e 100644
--- a/x-pack/plugins/observability_solution/slo/public/pages/slo_edit/components/histogram/histogram_indicator_type_form.tsx
+++ b/x-pack/plugins/observability_solution/slo/public/pages/slo_edit/components/indicator_section/histogram/histogram_indicator_type_form.tsx
@@ -18,11 +18,11 @@ import { FormattedMessage } from '@kbn/i18n-react';
import React from 'react';
import { useFormContext } from 'react-hook-form';
import { IndexAndTimestampField } from '../custom_common/index_and_timestamp_field';
-import { useCreateDataView } from '../../../../hooks/use_create_data_view';
-import { GroupByField } from '../common/group_by_field';
-import { CreateSLOForm } from '../../types';
-import { DataPreviewChart } from '../common/data_preview_chart';
-import { QueryBuilder } from '../common/query_builder';
+import { useCreateDataView } from '../../../../../hooks/use_create_data_view';
+import { GroupByField } from '../../common/group_by_field';
+import { CreateSLOForm } from '../../../types';
+import { DataPreviewChart } from '../../common/data_preview_chart';
+import { QueryBuilder } from '../../common/query_builder';
import { DATA_VIEW_FIELD } from '../custom_common/index_selection';
import { HistogramIndicator } from './histogram_indicator';
@@ -49,7 +49,7 @@ export function HistogramIndicatorTypeForm() {
-
+
diff --git a/x-pack/plugins/observability_solution/slo/public/pages/slo_edit/components/synthetics_availability/synthetics_availability_indicator_type_form.test.tsx b/x-pack/plugins/observability_solution/slo/public/pages/slo_edit/components/indicator_section/synthetics_availability/synthetics_availability_indicator_type_form.test.tsx
similarity index 100%
rename from x-pack/plugins/observability_solution/slo/public/pages/slo_edit/components/synthetics_availability/synthetics_availability_indicator_type_form.test.tsx
rename to x-pack/plugins/observability_solution/slo/public/pages/slo_edit/components/indicator_section/synthetics_availability/synthetics_availability_indicator_type_form.test.tsx
diff --git a/x-pack/plugins/observability_solution/slo/public/pages/slo_edit/components/synthetics_availability/synthetics_availability_indicator_type_form.tsx b/x-pack/plugins/observability_solution/slo/public/pages/slo_edit/components/indicator_section/synthetics_availability/synthetics_availability_indicator_type_form.tsx
similarity index 93%
rename from x-pack/plugins/observability_solution/slo/public/pages/slo_edit/components/synthetics_availability/synthetics_availability_indicator_type_form.tsx
rename to x-pack/plugins/observability_solution/slo/public/pages/slo_edit/components/indicator_section/synthetics_availability/synthetics_availability_indicator_type_form.tsx
index 07f2f86663292..88dbb16d667b6 100644
--- a/x-pack/plugins/observability_solution/slo/public/pages/slo_edit/components/synthetics_availability/synthetics_availability_indicator_type_form.tsx
+++ b/x-pack/plugins/observability_solution/slo/public/pages/slo_edit/components/indicator_section/synthetics_availability/synthetics_availability_indicator_type_form.tsx
@@ -17,12 +17,12 @@ import moment from 'moment';
import React, { useEffect, useState } from 'react';
import { useFormContext } from 'react-hook-form';
import { DATA_VIEW_FIELD } from '../custom_common/index_selection';
-import { useCreateDataView } from '../../../../hooks/use_create_data_view';
-import { formatAllFilters } from '../../helpers/format_filters';
-import { CreateSLOForm } from '../../types';
-import { DataPreviewChart } from '../common/data_preview_chart';
-import { GroupByCardinality } from '../common/group_by_cardinality';
-import { QueryBuilder } from '../common/query_builder';
+import { useCreateDataView } from '../../../../../hooks/use_create_data_view';
+import { formatAllFilters } from '../../../helpers/format_filters';
+import { CreateSLOForm } from '../../../types';
+import { DataPreviewChart } from '../../common/data_preview_chart';
+import { GroupByCardinality } from '../../common/group_by_cardinality';
+import { QueryBuilder } from '../../common/query_builder';
import { FieldSelector } from '../synthetics_common/field_selector';
export function SyntheticsAvailabilityIndicatorTypeForm() {
@@ -74,8 +74,8 @@ export function SyntheticsAvailabilityIndicatorTypeForm() {
}, [currentMonitors, setValue]);
return (
-
-
+
+
-
+
{fields?.map((metric, index, arr) => (
-
-
-
-
-
+
+
+
-
+
+
+
+
+
+ }
+ />
-
- }
- />
{index !== arr.length - 1 && }
))}
diff --git a/x-pack/plugins/observability_solution/slo/public/pages/slo_edit/components/timeslice_metric/metric_input.tsx b/x-pack/plugins/observability_solution/slo/public/pages/slo_edit/components/indicator_section/timeslice_metric/metric_input.tsx
similarity index 97%
rename from x-pack/plugins/observability_solution/slo/public/pages/slo_edit/components/timeslice_metric/metric_input.tsx
rename to x-pack/plugins/observability_solution/slo/public/pages/slo_edit/components/indicator_section/timeslice_metric/metric_input.tsx
index ebb539b97dab2..ef798305b20d6 100644
--- a/x-pack/plugins/observability_solution/slo/public/pages/slo_edit/components/timeslice_metric/metric_input.tsx
+++ b/x-pack/plugins/observability_solution/slo/public/pages/slo_edit/components/indicator_section/timeslice_metric/metric_input.tsx
@@ -16,9 +16,9 @@ import { FieldSpec } from '@kbn/data-views-plugin/common';
import { i18n } from '@kbn/i18n';
import React, { useEffect, useState } from 'react';
import { Controller, useFormContext } from 'react-hook-form';
-import { AGGREGATION_OPTIONS, aggValueToLabel } from '../../helpers/aggregation_options';
-import { createOptionsFromFields, Option } from '../../helpers/create_options';
-import { CreateSLOForm } from '../../types';
+import { AGGREGATION_OPTIONS, aggValueToLabel } from '../../../helpers/aggregation_options';
+import { createOptionsFromFields, Option } from '../../../helpers/create_options';
+import { CreateSLOForm } from '../../../types';
const fieldLabel = i18n.translate('xpack.slo.sloEdit.sliType.timesliceMetric.fieldLabel', {
defaultMessage: 'Field',
diff --git a/x-pack/plugins/observability_solution/slo/public/pages/slo_edit/components/timeslice_metric/timeslice_metric_indicator.tsx b/x-pack/plugins/observability_solution/slo/public/pages/slo_edit/components/indicator_section/timeslice_metric/timeslice_metric_indicator.tsx
similarity index 88%
rename from x-pack/plugins/observability_solution/slo/public/pages/slo_edit/components/timeslice_metric/timeslice_metric_indicator.tsx
rename to x-pack/plugins/observability_solution/slo/public/pages/slo_edit/components/indicator_section/timeslice_metric/timeslice_metric_indicator.tsx
index 86eede0ba65e2..73bc3135d91ac 100644
--- a/x-pack/plugins/observability_solution/slo/public/pages/slo_edit/components/timeslice_metric/timeslice_metric_indicator.tsx
+++ b/x-pack/plugins/observability_solution/slo/public/pages/slo_edit/components/indicator_section/timeslice_metric/timeslice_metric_indicator.tsx
@@ -19,15 +19,15 @@ import { FormattedMessage } from '@kbn/i18n-react';
import React from 'react';
import { useFormContext } from 'react-hook-form';
import { IndexAndTimestampField } from '../custom_common/index_and_timestamp_field';
-import { useKibana } from '../../../../hooks/use_kibana';
-import { GroupByField } from '../common/group_by_field';
-import { CreateSLOForm } from '../../types';
-import { DataPreviewChart } from '../common/data_preview_chart';
-import { QueryBuilder } from '../common/query_builder';
+import { useKibana } from '../../../../../hooks/use_kibana';
+import { GroupByField } from '../../common/group_by_field';
+import { CreateSLOForm } from '../../../types';
+import { DataPreviewChart } from '../../common/data_preview_chart';
+import { QueryBuilder } from '../../common/query_builder';
import { DATA_VIEW_FIELD } from '../custom_common/index_selection';
import { MetricIndicator } from './metric_indicator';
-import { COMPARATOR_MAPPING } from '../../constants';
-import { useCreateDataView } from '../../../../hooks/use_create_data_view';
+import { COMPARATOR_MAPPING } from '../../../constants';
+import { useCreateDataView } from '../../../../../hooks/use_create_data_view';
export { NEW_TIMESLICE_METRIC } from './metric_indicator';
@@ -54,7 +54,7 @@ export function TimesliceMetricIndicatorTypeForm() {
-
+
diff --git a/x-pack/plugins/observability_solution/slo/public/pages/slo_edit/components/slo_edit_form.tsx b/x-pack/plugins/observability_solution/slo/public/pages/slo_edit/components/slo_edit_form.tsx
index 7ffc274ffce12..9082d5367670e 100644
--- a/x-pack/plugins/observability_solution/slo/public/pages/slo_edit/components/slo_edit_form.tsx
+++ b/x-pack/plugins/observability_solution/slo/public/pages/slo_edit/components/slo_edit_form.tsx
@@ -5,43 +5,56 @@
* 2.0.
*/
-import { EuiFlexGroup, EuiSpacer, EuiSteps } from '@elastic/eui';
+import { EuiFlexGroup, EuiSteps } from '@elastic/eui';
import { i18n } from '@kbn/i18n';
-import type { GetSLOResponse } from '@kbn/slo-schema';
+import type { CreateSLOInput, GetSLOResponse } from '@kbn/slo-schema';
+import { RecursivePartial } from '@kbn/utility-types';
import React from 'react';
import { FormProvider, useForm } from 'react-hook-form';
-import { RecursivePartial } from '@kbn/utility-types';
-import { SloEditFormFooter } from './slo_edit_form_footer';
import { SLO_EDIT_FORM_DEFAULT_VALUES } from '../constants';
-import { transformSloResponseToCreateSloForm } from '../helpers/process_slo_form_values';
+import {
+ transformPartialSLOStateToFormState,
+ transformSloResponseToCreateSloForm,
+} from '../helpers/process_slo_form_values';
import { useParseUrlState } from '../hooks/use_parse_url_state';
import { useSectionFormValidation } from '../hooks/use_section_form_validation';
import { useShowSections } from '../hooks/use_show_sections';
import { CreateSLOForm } from '../types';
import { SloEditFormDescriptionSection } from './slo_edit_form_description_section';
+import { SloEditFormFooter } from './slo_edit_form_footer';
import { SloEditFormIndicatorSection } from './slo_edit_form_indicator_section';
import { SloEditFormObjectiveSection } from './slo_edit_form_objective_section';
export interface Props {
slo?: GetSLOResponse;
- initialValues?: RecursivePartial;
+ initialValues?: RecursivePartial;
onSave?: () => void;
}
-export const maxWidth = 900;
-
export function SloEditForm({ slo, initialValues, onSave }: Props) {
const isEditMode = slo !== undefined;
+ const isFlyoutMode = initialValues !== undefined && onSave !== undefined;
- const sloFormValuesFromUrlState = useParseUrlState() ?? (initialValues as CreateSLOForm);
+ const sloFormValuesFromFlyoutState = isFlyoutMode
+ ? transformPartialSLOStateToFormState(initialValues)
+ : undefined;
+ const sloFormValuesFromUrlState = useParseUrlState();
const sloFormValuesFromSloResponse = transformSloResponseToCreateSloForm(slo);
- const methods = useForm({
- defaultValues: sloFormValuesFromUrlState ?? SLO_EDIT_FORM_DEFAULT_VALUES,
- values: sloFormValuesFromUrlState ? sloFormValuesFromUrlState : sloFormValuesFromSloResponse,
+ const form = useForm({
+ defaultValues: isFlyoutMode
+ ? sloFormValuesFromFlyoutState
+ : sloFormValuesFromUrlState
+ ? sloFormValuesFromUrlState
+ : sloFormValuesFromSloResponse ?? SLO_EDIT_FORM_DEFAULT_VALUES,
+ values: isFlyoutMode
+ ? sloFormValuesFromFlyoutState
+ : sloFormValuesFromUrlState
+ ? sloFormValuesFromUrlState
+ : sloFormValuesFromSloResponse,
mode: 'all',
});
- const { watch, getFieldState, getValues, formState } = methods;
+ const { watch, getFieldState, getValues, formState } = form;
const { isIndicatorSectionValid, isObjectiveSectionValid, isDescriptionSectionValid } =
useSectionFormValidation({
@@ -59,41 +72,37 @@ export function SloEditForm({ slo, initialValues, onSave }: Props) {
);
return (
- <>
-
-
- ,
- status: isIndicatorSectionValid ? 'complete' : 'incomplete',
- },
- {
- title: i18n.translate('xpack.slo.sloEdit.objectives.title', {
- defaultMessage: 'Set objectives',
- }),
- children: showObjectiveSection ? : null,
- status: showObjectiveSection && isObjectiveSectionValid ? 'complete' : 'incomplete',
- },
- {
- title: i18n.translate('xpack.slo.sloEdit.description.title', {
- defaultMessage: 'Describe SLO',
- }),
- children: showDescriptionSection ? : null,
- status:
- showDescriptionSection && isDescriptionSectionValid ? 'complete' : 'incomplete',
- },
- ]}
- />
-
-
+
+
+ ,
+ status: isIndicatorSectionValid ? 'complete' : 'incomplete',
+ },
+ {
+ title: i18n.translate('xpack.slo.sloEdit.objectives.title', {
+ defaultMessage: 'Set objectives',
+ }),
+ children: showObjectiveSection ? : null,
+ status: showObjectiveSection && isObjectiveSectionValid ? 'complete' : 'incomplete',
+ },
+ {
+ title: i18n.translate('xpack.slo.sloEdit.description.title', {
+ defaultMessage: 'Describe SLO',
+ }),
+ children: showDescriptionSection ? : null,
+ status:
+ showDescriptionSection && isDescriptionSectionValid ? 'complete' : 'incomplete',
+ },
+ ]}
+ />
-
-
-
- >
+
+
+
);
}
diff --git a/x-pack/plugins/observability_solution/slo/public/pages/slo_edit/components/slo_edit_form_description_section.tsx b/x-pack/plugins/observability_solution/slo/public/pages/slo_edit/components/slo_edit_form_description_section.tsx
index a210021674f6b..f242669e566d2 100644
--- a/x-pack/plugins/observability_solution/slo/public/pages/slo_edit/components/slo_edit_form_description_section.tsx
+++ b/x-pack/plugins/observability_solution/slo/public/pages/slo_edit/components/slo_edit_form_description_section.tsx
@@ -9,8 +9,6 @@ import {
EuiComboBox,
EuiComboBoxOptionOption,
EuiFieldText,
- EuiFlexGroup,
- EuiFlexItem,
EuiFormRow,
EuiPanel,
EuiTextArea,
@@ -20,9 +18,9 @@ import { i18n } from '@kbn/i18n';
import React from 'react';
import { Controller, useFormContext } from 'react-hook-form';
import { useFetchSLOSuggestions } from '../hooks/use_fetch_suggestions';
-import { OptionalText } from './common/optional_text';
import { CreateSLOForm } from '../types';
-import { maxWidth } from './slo_edit_form';
+import { OptionalText } from './common/optional_text';
+import { MAX_WIDTH } from '../constants';
export function SloEditFormDescriptionSection() {
const { control, getFieldState } = useFormContext();
@@ -37,129 +35,117 @@ export function SloEditFormDescriptionSection() {
hasBorder={false}
hasShadow={false}
paddingSize="none"
- style={{ maxWidth }}
+ style={{ maxWidth: MAX_WIDTH }}
data-test-subj="sloEditFormDescriptionSection"
>
-
-
-
- (
-
- )}
+
+ (
+
-
-
+ )}
+ />
+
-
- }
- >
- (
-
+ }
+ >
+ (
+
-
-
+ )}
+ />
+
-
-
- (
- {
- if (selected.length) {
- return field.onChange(selected.map((opts) => opts.value));
- }
+
+ (
+ {
+ if (selected.length) {
+ return field.onChange(selected.map((opts) => opts.value));
+ }
- field.onChange([]);
- }}
- onCreateOption={(
- searchValue: string,
- options: EuiComboBoxOptionOption[] = []
- ) => {
- const normalizedSearchValue = searchValue.trim().toLowerCase();
+ field.onChange([]);
+ }}
+ onCreateOption={(searchValue: string, options: EuiComboBoxOptionOption[] = []) => {
+ const normalizedSearchValue = searchValue.trim().toLowerCase();
- if (!normalizedSearchValue) {
- return;
- }
- const values = field.value ?? [];
+ if (!normalizedSearchValue) {
+ return;
+ }
+ const values = field.value ?? [];
- if (
- values.findIndex(
- (tag) => tag.trim().toLowerCase() === normalizedSearchValue
- ) === -1
- ) {
- field.onChange([...values, searchValue]);
- }
- }}
- isClearable
- data-test-subj="sloEditTagsSelector"
- />
- )}
+ if (
+ values.findIndex((tag) => tag.trim().toLowerCase() === normalizedSearchValue) ===
+ -1
+ ) {
+ field.onChange([...values, searchValue]);
+ }
+ }}
+ isClearable
+ data-test-subj="sloEditTagsSelector"
/>
-
-
-
+ )}
+ />
+
);
}
diff --git a/x-pack/plugins/observability_solution/slo/public/pages/slo_edit/components/slo_edit_form_indicator_section.tsx b/x-pack/plugins/observability_solution/slo/public/pages/slo_edit/components/slo_edit_form_indicator_section.tsx
index 156f45c2c982c..4d30bef7ac692 100644
--- a/x-pack/plugins/observability_solution/slo/public/pages/slo_edit/components/slo_edit_form_indicator_section.tsx
+++ b/x-pack/plugins/observability_solution/slo/public/pages/slo_edit/components/slo_edit_form_indicator_section.tsx
@@ -7,19 +7,20 @@
import { EuiFormRow, EuiPanel, EuiSelect, EuiSpacer } from '@elastic/eui';
import { i18n } from '@kbn/i18n';
+import { assertNever } from '@kbn/std';
import React, { useMemo } from 'react';
import { Controller, useFormContext } from 'react-hook-form';
import { SLI_OPTIONS } from '../constants';
import { useUnregisterFields } from '../hooks/use_unregister_fields';
import { CreateSLOForm } from '../types';
-import { ApmAvailabilityIndicatorTypeForm } from './apm_availability/apm_availability_indicator_type_form';
-import { ApmLatencyIndicatorTypeForm } from './apm_latency/apm_latency_indicator_type_form';
-import { SyntheticsAvailabilityIndicatorTypeForm } from './synthetics_availability/synthetics_availability_indicator_type_form';
-import { CustomKqlIndicatorTypeForm } from './custom_kql/custom_kql_indicator_type_form';
-import { CustomMetricIndicatorTypeForm } from './custom_metric/custom_metric_type_form';
-import { HistogramIndicatorTypeForm } from './histogram/histogram_indicator_type_form';
-import { maxWidth } from './slo_edit_form';
-import { TimesliceMetricIndicatorTypeForm } from './timeslice_metric/timeslice_metric_indicator';
+import { MAX_WIDTH } from '../constants';
+import { ApmAvailabilityIndicatorTypeForm } from './indicator_section/apm_availability/apm_availability_indicator_type_form';
+import { ApmLatencyIndicatorTypeForm } from './indicator_section/apm_latency/apm_latency_indicator_type_form';
+import { CustomKqlIndicatorTypeForm } from './indicator_section/custom_kql/custom_kql_indicator_type_form';
+import { CustomMetricIndicatorTypeForm } from './indicator_section/custom_metric/custom_metric_type_form';
+import { HistogramIndicatorTypeForm } from './indicator_section/histogram/histogram_indicator_type_form';
+import { SyntheticsAvailabilityIndicatorTypeForm } from './indicator_section/synthetics_availability/synthetics_availability_indicator_type_form';
+import { TimesliceMetricIndicatorTypeForm } from './indicator_section/timeslice_metric/timeslice_metric_indicator';
interface SloEditFormIndicatorSectionProps {
isEditMode: boolean;
@@ -48,7 +49,7 @@ export function SloEditFormIndicatorSection({ isEditMode }: SloEditFormIndicator
case 'sli.metric.timeslice':
return ;
default:
- return null;
+ assertNever(indicatorType);
}
}, [indicatorType]);
@@ -57,7 +58,7 @@ export function SloEditFormIndicatorSection({ isEditMode }: SloEditFormIndicator
hasBorder={false}
hasShadow={false}
paddingSize="none"
- style={{ maxWidth }}
+ style={{ maxWidth: MAX_WIDTH }}
data-test-subj="sloEditFormIndicatorSection"
>
{!isEditMode && (
@@ -78,7 +79,7 @@ export function SloEditFormIndicatorSection({ isEditMode }: SloEditFormIndicator
)}
/>
-
+
>
)}
{indicatorTypeForm}
diff --git a/x-pack/plugins/observability_solution/slo/public/pages/slo_edit/components/slo_edit_form_objective_section.tsx b/x-pack/plugins/observability_solution/slo/public/pages/slo_edit/components/slo_edit_form_objective_section.tsx
index 15c51b1b86ce4..65e4a25a86c39 100644
--- a/x-pack/plugins/observability_solution/slo/public/pages/slo_edit/components/slo_edit_form_objective_section.tsx
+++ b/x-pack/plugins/observability_solution/slo/public/pages/slo_edit/components/slo_edit_form_objective_section.tsx
@@ -7,15 +7,14 @@
import {
EuiCallOut,
- EuiCheckbox,
EuiFieldNumber,
EuiFlexGrid,
+ EuiFlexGroup,
EuiFlexItem,
EuiFormRow,
EuiIconTip,
EuiPanel,
EuiSelect,
- EuiSpacer,
useGeneratedHtmlId,
} from '@elastic/eui';
import { i18n } from '@kbn/i18n';
@@ -30,7 +29,8 @@ import {
TIMEWINDOW_TYPE_OPTIONS,
} from '../constants';
import { CreateSLOForm } from '../types';
-import { maxWidth } from './slo_edit_form';
+import { MAX_WIDTH } from '../constants';
+import { AdvancedSettings } from './indicator_section/advanced_settings/advanced_settings';
import { SloEditFormObjectiveSectionTimeslices } from './slo_edit_form_objective_section_timeslices';
export function SloEditFormObjectiveSection() {
@@ -44,7 +44,6 @@ export function SloEditFormObjectiveSection() {
const budgetingSelect = useGeneratedHtmlId({ prefix: 'budgetingSelect' });
const timeWindowTypeSelect = useGeneratedHtmlId({ prefix: 'timeWindowTypeSelect' });
const timeWindowSelect = useGeneratedHtmlId({ prefix: 'timeWindowSelect' });
- const preventBackfillCheckbox = useGeneratedHtmlId({ prefix: 'preventBackfill' });
const timeWindowType = watch('timeWindow.type');
const indicator = watch('indicator.type');
@@ -91,237 +90,199 @@ export function SloEditFormObjectiveSection() {
hasBorder={false}
hasShadow={false}
paddingSize="none"
- style={{ maxWidth }}
+ style={{ maxWidth: MAX_WIDTH }}
data-test-subj="sloEditFormObjectiveSection"
>
-
-
-
- {i18n.translate('xpack.slo.sloEdit.timeWindowType.label', {
- defaultMessage: 'Time window',
- })}{' '}
-
-
- }
- >
- (
-
- )}
- />
-
-
-
-
- {i18n.translate('xpack.slo.sloEdit.timeWindowDuration.label', {
- defaultMessage: 'Duration',
- })}{' '}
-
-
- }
- >
- (
-
- )}
- />
-
-
-
-
-
- {indicator === 'sli.metric.timeslice' && (
-
-
-
-
+
+
+
+ {i18n.translate('xpack.slo.sloEdit.timeWindowType.label', {
+ defaultMessage: 'Time window',
+ })}{' '}
+
+
+ }
+ >
+ (
+
+ )}
/>
-
-
-
-
- )}
-
- {indicator === 'sli.synthetics.availability' && (
-
-
-
-
+
+
+
+ {i18n.translate('xpack.slo.sloEdit.timeWindowDuration.label', {
+ defaultMessage: 'Duration',
+ })}{' '}
+
+
+ }
+ >
+ (
+
+ )}
/>
-
-
-
-
- )}
+
+
+
-
-
-
- {i18n.translate('xpack.slo.sloEdit.budgetingMethod.label', {
- defaultMessage: 'Budgeting method',
- })}{' '}
-
-
- }
- >
- (
-
+
+
+
- )}
- />
-
-
+
+
+
+ )}
- {watch('budgetingMethod') === 'timeslices' ? (
-
- ) : null}
-
+ {indicator === 'sli.synthetics.availability' && (
+
+
+
+
+
+
+
+ )}
-
+
+
+
+ {i18n.translate('xpack.slo.sloEdit.budgetingMethod.label', {
+ defaultMessage: 'Budgeting method',
+ })}{' '}
+
+
+ }
+ >
+ (
+
+ )}
+ />
+
+
-
-
-
- {i18n.translate('xpack.slo.sloEdit.targetSlo.label', {
- defaultMessage: 'Target / SLO (%)',
- })}{' '}
-
-
- }
- >
- (
- onChange(event.target.value)}
- />
- )}
- />
-
-
-
+ {watch('budgetingMethod') === 'timeslices' ? (
+
+ ) : null}
+
-
+
+
+
+ {i18n.translate('xpack.slo.sloEdit.targetSlo.label', {
+ defaultMessage: 'Target / SLO (%)',
+ })}{' '}
+
+
+ }
+ >
+ (
+ onChange(event.target.value)}
+ />
+ )}
+ />
+
+
+
-
-
-
- (
-
- {i18n.translate('xpack.slo.sloEdit.settings.preventInitialBackfill.label', {
- defaultMessage: 'Prevent initial backfill of data',
- })}
-
-
- }
- checked={Boolean(field.value)}
- onChange={(event: any) => onChange(event.target.checked)}
- />
- )}
- />
-
-
-
+
+
);
}
diff --git a/x-pack/plugins/observability_solution/slo/public/pages/slo_edit/constants.ts b/x-pack/plugins/observability_solution/slo/public/pages/slo_edit/constants.ts
index 123ebdc660947..55dfec93f8a33 100644
--- a/x-pack/plugins/observability_solution/slo/public/pages/slo_edit/constants.ts
+++ b/x-pack/plugins/observability_solution/slo/public/pages/slo_edit/constants.ts
@@ -33,6 +33,8 @@ import {
import { SYNTHETICS_DEFAULT_GROUPINGS, SYNTHETICS_INDEX_PATTERN } from '../../../common/constants';
import { CreateSLOForm } from './types';
+export const MAX_WIDTH = 900;
+
export const SLI_OPTIONS: Array<{
value: IndicatorType;
text: string;
@@ -205,6 +207,13 @@ export const SYNTHETICS_AVAILABILITY_DEFAULT_VALUES: SyntheticsAvailabilityIndic
},
};
+export const SETTINGS_DEFAULT_VALUES = {
+ frequency: 1,
+ preventInitialBackfill: false,
+ syncDelay: 1,
+ syncField: null,
+};
+
export const SLO_EDIT_FORM_DEFAULT_VALUES: CreateSLOForm = {
name: '',
description: '',
@@ -219,9 +228,7 @@ export const SLO_EDIT_FORM_DEFAULT_VALUES: CreateSLOForm = {
target: 99,
},
groupBy: ALL_VALUE,
- settings: {
- preventInitialBackfill: false,
- },
+ settings: SETTINGS_DEFAULT_VALUES,
};
export const SLO_EDIT_FORM_DEFAULT_VALUES_CUSTOM_METRIC: CreateSLOForm = {
@@ -238,9 +245,7 @@ export const SLO_EDIT_FORM_DEFAULT_VALUES_CUSTOM_METRIC: CreateSLOForm = {
target: 99,
},
groupBy: ALL_VALUE,
- settings: {
- preventInitialBackfill: false,
- },
+ settings: SETTINGS_DEFAULT_VALUES,
};
export const SLO_EDIT_FORM_DEFAULT_VALUES_SYNTHETICS_AVAILABILITY: CreateSLOForm = {
@@ -257,9 +262,7 @@ export const SLO_EDIT_FORM_DEFAULT_VALUES_SYNTHETICS_AVAILABILITY: CreateSLOForm
target: 99,
},
groupBy: SYNTHETICS_DEFAULT_GROUPINGS,
- settings: {
- preventInitialBackfill: false,
- },
+ settings: SETTINGS_DEFAULT_VALUES,
};
export const COMPARATOR_GT = i18n.translate('xpack.slo.sloEdit.sliType.timesliceMetric.gtLabel', {
diff --git a/x-pack/plugins/observability_solution/slo/public/pages/slo_edit/helpers/__snapshots__/process_slo_form_values.test.ts.snap b/x-pack/plugins/observability_solution/slo/public/pages/slo_edit/helpers/__snapshots__/process_slo_form_values.test.ts.snap
index 3f7ac0ce83beb..78f63a4b8f7bc 100644
--- a/x-pack/plugins/observability_solution/slo/public/pages/slo_edit/helpers/__snapshots__/process_slo_form_values.test.ts.snap
+++ b/x-pack/plugins/observability_solution/slo/public/pages/slo_edit/helpers/__snapshots__/process_slo_form_values.test.ts.snap
@@ -26,7 +26,10 @@ Object {
"target": 99,
},
"settings": Object {
+ "frequency": 1,
"preventInitialBackfill": false,
+ "syncDelay": 1,
+ "syncField": null,
},
"tags": Array [],
"timeWindow": Object {
@@ -74,7 +77,10 @@ Object {
"target": 99,
},
"settings": Object {
+ "frequency": 1,
"preventInitialBackfill": false,
+ "syncDelay": 1,
+ "syncField": null,
},
"tags": Array [],
"timeWindow": Object {
@@ -104,7 +110,10 @@ Object {
"target": 99,
},
"settings": Object {
+ "frequency": 1,
"preventInitialBackfill": false,
+ "syncDelay": 1,
+ "syncField": null,
},
"tags": Array [],
"timeWindow": Object {
@@ -146,7 +155,10 @@ Object {
"target": 99,
},
"settings": Object {
+ "frequency": 1,
"preventInitialBackfill": false,
+ "syncDelay": 1,
+ "syncField": null,
},
"tags": Array [],
"timeWindow": Object {
@@ -178,7 +190,10 @@ Object {
"timesliceWindow": "2",
},
"settings": Object {
+ "frequency": 1,
"preventInitialBackfill": false,
+ "syncDelay": 1,
+ "syncField": null,
},
"tags": Array [],
"timeWindow": Object {
@@ -208,7 +223,10 @@ Object {
"target": 99,
},
"settings": Object {
+ "frequency": 1,
"preventInitialBackfill": false,
+ "syncDelay": 1,
+ "syncField": null,
},
"tags": Array [],
"timeWindow": Object {
@@ -218,6 +236,105 @@ Object {
}
`;
+exports[`Transform partial URL state into form state settings handles optional 'syncField' URL state 1`] = `
+Object {
+ "budgetingMethod": "occurrences",
+ "description": "",
+ "groupBy": "*",
+ "indicator": Object {
+ "params": Object {
+ "filter": "",
+ "good": "",
+ "index": "",
+ "timestampField": "",
+ "total": "",
+ },
+ "type": "sli.kql.custom",
+ },
+ "name": "",
+ "objective": Object {
+ "target": 99,
+ },
+ "settings": Object {
+ "frequency": 1,
+ "preventInitialBackfill": false,
+ "syncDelay": 1,
+ "syncField": "override-field",
+ },
+ "tags": Array [],
+ "timeWindow": Object {
+ "duration": "30d",
+ "type": "rolling",
+ },
+}
+`;
+
+exports[`Transform partial URL state into form state settings handles partial 'settings' URL state 1`] = `
+Object {
+ "budgetingMethod": "occurrences",
+ "description": "",
+ "groupBy": "*",
+ "indicator": Object {
+ "params": Object {
+ "filter": "",
+ "good": "",
+ "index": "",
+ "timestampField": "",
+ "total": "",
+ },
+ "type": "sli.kql.custom",
+ },
+ "name": "",
+ "objective": Object {
+ "target": 99,
+ },
+ "settings": Object {
+ "frequency": 1,
+ "preventInitialBackfill": false,
+ "syncDelay": 12,
+ "syncField": null,
+ },
+ "tags": Array [],
+ "timeWindow": Object {
+ "duration": "30d",
+ "type": "rolling",
+ },
+}
+`;
+
+exports[`Transform partial URL state into form state settings handles the 'settings' URL state 1`] = `
+Object {
+ "budgetingMethod": "occurrences",
+ "description": "",
+ "groupBy": "*",
+ "indicator": Object {
+ "params": Object {
+ "filter": "",
+ "good": "",
+ "index": "",
+ "timestampField": "",
+ "total": "",
+ },
+ "type": "sli.kql.custom",
+ },
+ "name": "",
+ "objective": Object {
+ "target": 99,
+ },
+ "settings": Object {
+ "frequency": 1,
+ "preventInitialBackfill": true,
+ "syncDelay": 180,
+ "syncField": null,
+ },
+ "tags": Array [],
+ "timeWindow": Object {
+ "duration": "30d",
+ "type": "rolling",
+ },
+}
+`;
+
exports[`Transform partial URL state into form state with 'indicator' in URL state handles partial APM Availability state 1`] = `
Object {
"budgetingMethod": "occurrences",
@@ -239,7 +356,10 @@ Object {
"target": 99,
},
"settings": Object {
+ "frequency": 1,
"preventInitialBackfill": false,
+ "syncDelay": 1,
+ "syncField": null,
},
"tags": Array [],
"timeWindow": Object {
@@ -271,7 +391,10 @@ Object {
"target": 99,
},
"settings": Object {
+ "frequency": 1,
"preventInitialBackfill": false,
+ "syncDelay": 1,
+ "syncField": null,
},
"tags": Array [],
"timeWindow": Object {
@@ -301,7 +424,10 @@ Object {
"target": 99,
},
"settings": Object {
+ "frequency": 1,
"preventInitialBackfill": false,
+ "syncDelay": 1,
+ "syncField": null,
},
"tags": Array [],
"timeWindow": Object {
@@ -331,7 +457,10 @@ Object {
"target": 99,
},
"settings": Object {
+ "frequency": 1,
"preventInitialBackfill": false,
+ "syncDelay": 1,
+ "syncField": null,
},
"tags": Array [],
"timeWindow": Object {
diff --git a/x-pack/plugins/observability_solution/slo/public/pages/slo_edit/helpers/format_filters.test.ts b/x-pack/plugins/observability_solution/slo/public/pages/slo_edit/helpers/format_filters.test.ts
index 16ad733619e65..c79571d4ab77b 100644
--- a/x-pack/plugins/observability_solution/slo/public/pages/slo_edit/helpers/format_filters.test.ts
+++ b/x-pack/plugins/observability_solution/slo/public/pages/slo_edit/helpers/format_filters.test.ts
@@ -4,7 +4,7 @@
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
-import { getGroupByCardinalityFilters } from '../components/synthetics_availability/synthetics_availability_indicator_type_form';
+import { getGroupByCardinalityFilters } from '../components/indicator_section/synthetics_availability/synthetics_availability_indicator_type_form';
import { formatAllFilters } from './format_filters';
describe('formatAllFilters', () => {
diff --git a/x-pack/plugins/observability_solution/slo/public/pages/slo_edit/helpers/process_slo_form_values.test.ts b/x-pack/plugins/observability_solution/slo/public/pages/slo_edit/helpers/process_slo_form_values.test.ts
index a69cd1152985c..7518e1c679c87 100644
--- a/x-pack/plugins/observability_solution/slo/public/pages/slo_edit/helpers/process_slo_form_values.test.ts
+++ b/x-pack/plugins/observability_solution/slo/public/pages/slo_edit/helpers/process_slo_form_values.test.ts
@@ -5,7 +5,7 @@
* 2.0.
*/
-import { transformPartialUrlStateToFormState as transform } from './process_slo_form_values';
+import { transformPartialSLOStateToFormState as transform } from './process_slo_form_values';
describe('Transform partial URL state into form state', () => {
describe("with 'indicator' in URL state", () => {
@@ -121,4 +121,20 @@ describe('Transform partial URL state into form state', () => {
})
).toMatchSnapshot();
});
+
+ describe('settings', () => {
+ it("handles the 'settings' URL state", () => {
+ expect(
+ transform({ settings: { preventInitialBackfill: true, syncDelay: '3h' } })
+ ).toMatchSnapshot();
+ });
+
+ it("handles partial 'settings' URL state", () => {
+ expect(transform({ settings: { syncDelay: '12m' } })).toMatchSnapshot();
+ });
+
+ it("handles optional 'syncField' URL state", () => {
+ expect(transform({ settings: { syncField: 'override-field' } })).toMatchSnapshot();
+ });
+ });
});
diff --git a/x-pack/plugins/observability_solution/slo/public/pages/slo_edit/helpers/process_slo_form_values.ts b/x-pack/plugins/observability_solution/slo/public/pages/slo_edit/helpers/process_slo_form_values.ts
index 8bbbcf9d2fee9..81d6714dac2e5 100644
--- a/x-pack/plugins/observability_solution/slo/public/pages/slo_edit/helpers/process_slo_form_values.ts
+++ b/x-pack/plugins/observability_solution/slo/public/pages/slo_edit/helpers/process_slo_form_values.ts
@@ -9,13 +9,14 @@ import { CreateSLOInput, GetSLOResponse, Indicator, UpdateSLOInput } from '@kbn/
import { assertNever } from '@kbn/std';
import { RecursivePartial } from '@kbn/utility-types';
import { cloneDeep } from 'lodash';
-import { toDuration } from '../../../utils/slo/duration';
+import { toDuration, toMinutes } from '../../../utils/slo/duration';
import {
APM_AVAILABILITY_DEFAULT_VALUES,
APM_LATENCY_DEFAULT_VALUES,
CUSTOM_KQL_DEFAULT_VALUES,
CUSTOM_METRIC_DEFAULT_VALUES,
HISTOGRAM_DEFAULT_VALUES,
+ SETTINGS_DEFAULT_VALUES,
SLO_EDIT_FORM_DEFAULT_VALUES,
SLO_EDIT_FORM_DEFAULT_VALUES_SYNTHETICS_AVAILABILITY,
SYNTHETICS_AVAILABILITY_DEFAULT_VALUES,
@@ -52,6 +53,13 @@ export function transformSloResponseToCreateSloForm(
tags: values.tags,
settings: {
preventInitialBackfill: values.settings?.preventInitialBackfill ?? false,
+ syncDelay: values.settings?.syncDelay
+ ? toMinutes(toDuration(values.settings.syncDelay))
+ : SETTINGS_DEFAULT_VALUES.syncDelay,
+ frequency: values.settings?.frequency
+ ? toMinutes(toDuration(values.settings.frequency))
+ : SETTINGS_DEFAULT_VALUES.frequency,
+ syncField: values.settings?.syncField ?? null,
},
};
}
@@ -80,7 +88,10 @@ export function transformCreateSLOFormToCreateSLOInput(values: CreateSLOForm): C
tags: values.tags,
groupBy: [values.groupBy].flat(),
settings: {
- preventInitialBackfill: values.settings?.preventInitialBackfill ?? false,
+ preventInitialBackfill: values.settings.preventInitialBackfill,
+ syncDelay: `${values.settings.syncDelay ?? SETTINGS_DEFAULT_VALUES.syncDelay}m`,
+ frequency: `${values.settings.frequency ?? SETTINGS_DEFAULT_VALUES.frequency}m`,
+ syncField: values.settings.syncField,
},
};
}
@@ -109,7 +120,10 @@ export function transformValuesToUpdateSLOInput(values: CreateSLOForm): UpdateSL
tags: values.tags,
groupBy: [values.groupBy].flat(),
settings: {
- preventInitialBackfill: values.settings?.preventInitialBackfill ?? false,
+ preventInitialBackfill: values.settings.preventInitialBackfill,
+ syncDelay: `${values.settings.syncDelay ?? SETTINGS_DEFAULT_VALUES.syncDelay}m`,
+ frequency: `${values.settings.frequency ?? SETTINGS_DEFAULT_VALUES.frequency}m`,
+ syncField: values.settings.syncField,
},
};
}
@@ -165,7 +179,7 @@ function transformPartialIndicatorState(
}
}
-export function transformPartialUrlStateToFormState(
+export function transformPartialSLOStateToFormState(
values: RecursivePartial
): CreateSLOForm {
let state: CreateSLOForm;
@@ -189,8 +203,8 @@ export function transformPartialUrlStateToFormState(
if (values.description) {
state.description = values.description;
}
- if (!!values.tags) {
- state.tags = values.tags as string[];
+ if (values.tags) {
+ state.tags = [values.tags].flat().filter((tag) => !!tag) as string[];
}
if (values.objective) {
@@ -220,8 +234,19 @@ export function transformPartialUrlStateToFormState(
state.timeWindow = { duration: values.timeWindow.duration, type: values.timeWindow.type };
}
- if (!!values.settings?.preventInitialBackfill) {
- state.settings = { preventInitialBackfill: values.settings.preventInitialBackfill };
+ if (!!values.settings) {
+ if (values.settings.preventInitialBackfill) {
+ state.settings.preventInitialBackfill = values.settings.preventInitialBackfill;
+ }
+ if (values.settings.syncDelay) {
+ state.settings.syncDelay = toMinutes(toDuration(values.settings.syncDelay));
+ }
+ if (values.settings.frequency) {
+ state.settings.frequency = toMinutes(toDuration(values.settings.frequency));
+ }
+ if (values.settings.syncField) {
+ state.settings.syncField = values.settings.syncField;
+ }
}
return state;
diff --git a/x-pack/plugins/observability_solution/slo/public/pages/slo_edit/hooks/use_parse_url_state.ts b/x-pack/plugins/observability_solution/slo/public/pages/slo_edit/hooks/use_parse_url_state.ts
index 2c305feda3c06..9ada81ea84387 100644
--- a/x-pack/plugins/observability_solution/slo/public/pages/slo_edit/hooks/use_parse_url_state.ts
+++ b/x-pack/plugins/observability_solution/slo/public/pages/slo_edit/hooks/use_parse_url_state.ts
@@ -10,7 +10,7 @@ import { CreateSLOInput } from '@kbn/slo-schema';
import { RecursivePartial } from '@kbn/utility-types';
import { useHistory } from 'react-router-dom';
import { useMemo } from 'react';
-import { transformPartialUrlStateToFormState } from '../helpers/process_slo_form_values';
+import { transformPartialSLOStateToFormState } from '../helpers/process_slo_form_values';
import { CreateSLOForm } from '../types';
export function useParseUrlState(): CreateSLOForm | undefined {
@@ -25,6 +25,6 @@ export function useParseUrlState(): CreateSLOForm | undefined {
const urlState = urlStateStorage.get>('_a');
- return !!urlState ? transformPartialUrlStateToFormState(urlState) : undefined;
+ return !!urlState ? transformPartialSLOStateToFormState(urlState) : undefined;
}, [history]);
}
diff --git a/x-pack/plugins/observability_solution/slo/public/pages/slo_edit/hooks/use_section_form_validation.ts b/x-pack/plugins/observability_solution/slo/public/pages/slo_edit/hooks/use_section_form_validation.ts
index 7d75359f4cd40..94ffc92adedb4 100644
--- a/x-pack/plugins/observability_solution/slo/public/pages/slo_edit/hooks/use_section_form_validation.ts
+++ b/x-pack/plugins/observability_solution/slo/public/pages/slo_edit/hooks/use_section_form_validation.ts
@@ -220,8 +220,10 @@ export function useSectionFormValidation({ getFieldState, getValues, formState,
'objective.target',
'objective.timesliceTarget',
'objective.timesliceWindow',
+ 'settings.syncDelay',
+ 'settings.frequency',
] as const
- ).every((field) => getFieldState(field).error === undefined);
+ ).every((field) => !getFieldState(field).invalid);
const isDescriptionSectionValid =
!getFieldState('name').invalid &&
diff --git a/x-pack/plugins/observability_solution/slo/public/pages/slo_edit/hooks/use_unregister_fields.ts b/x-pack/plugins/observability_solution/slo/public/pages/slo_edit/hooks/use_unregister_fields.ts
index 9d7752f190344..eb7a77f822660 100644
--- a/x-pack/plugins/observability_solution/slo/public/pages/slo_edit/hooks/use_unregister_fields.ts
+++ b/x-pack/plugins/observability_solution/slo/public/pages/slo_edit/hooks/use_unregister_fields.ts
@@ -19,8 +19,8 @@ import {
CUSTOM_METRIC_DEFAULT_VALUES,
HISTOGRAM_DEFAULT_VALUES,
SLO_EDIT_FORM_DEFAULT_VALUES,
- TIMESLICE_METRIC_DEFAULT_VALUES,
SLO_EDIT_FORM_DEFAULT_VALUES_SYNTHETICS_AVAILABILITY,
+ TIMESLICE_METRIC_DEFAULT_VALUES,
} from '../constants';
import { CreateSLOForm } from '../types';
diff --git a/x-pack/plugins/observability_solution/slo/public/pages/slo_edit/shared_flyout/slo_add_form_flyout.tsx b/x-pack/plugins/observability_solution/slo/public/pages/slo_edit/shared_flyout/slo_add_form_flyout.tsx
index 98c76b190aa1a..f71d7caa80d17 100644
--- a/x-pack/plugins/observability_solution/slo/public/pages/slo_edit/shared_flyout/slo_add_form_flyout.tsx
+++ b/x-pack/plugins/observability_solution/slo/public/pages/slo_edit/shared_flyout/slo_add_form_flyout.tsx
@@ -7,12 +7,11 @@
import { EuiFlyout, EuiFlyoutBody, EuiFlyoutFooter, EuiFlyoutHeader, EuiTitle } from '@elastic/eui';
import { FormattedMessage } from '@kbn/i18n-react';
+import { CreateSLOInput } from '@kbn/slo-schema';
import { RecursivePartial } from '@kbn/utility-types';
-import { merge } from 'lodash';
import React from 'react';
import { OutPortal, createHtmlPortalNode } from 'react-reverse-portal';
import { SloEditForm } from '../components/slo_edit_form';
-import { CreateSLOForm } from '../types';
export const sloEditFormFooterPortal = createHtmlPortalNode();
@@ -22,7 +21,7 @@ export default function SloAddFormFlyout({
initialValues,
}: {
onClose: () => void;
- initialValues?: RecursivePartial;
+ initialValues?: RecursivePartial;
}) {
return (
-
+
diff --git a/x-pack/plugins/observability_solution/slo/public/pages/slo_edit/slo_edit.test.tsx b/x-pack/plugins/observability_solution/slo/public/pages/slo_edit/slo_edit.test.tsx
index abc60d6a00352..8d52ed914302c 100644
--- a/x-pack/plugins/observability_solution/slo/public/pages/slo_edit/slo_edit.test.tsx
+++ b/x-pack/plugins/observability_solution/slo/public/pages/slo_edit/slo_edit.test.tsx
@@ -423,11 +423,11 @@ describe('SLO Edit Page', () => {
jest.spyOn(Router, 'useParams').mockReturnValue({ sloId: '123' });
history.push(
- '/slos/123/edit?_a=(name:%27updated-name%27,indicator:(params:(environment:prod,service:cartService),type:sli.apm.transactionDuration),objective:(target:0.92))'
+ '/slos/edit/123?_a=(name:%27updated-name%27,indicator:(params:(environment:prod,service:cartService),type:sli.apm.transactionDuration),objective:(target:0.92))'
);
jest
.spyOn(Router, 'useLocation')
- .mockReturnValue({ pathname: '/slos/123/edit', search: '', state: '', hash: '' });
+ .mockReturnValue({ pathname: '/slos/edit/123', search: '', state: '', hash: '' });
useFetchSloMock.mockReturnValue({ isLoading: false, data: slo });
@@ -463,8 +463,7 @@ describe('SLO Edit Page', () => {
jest.spyOn(Router, 'useParams').mockReturnValue({ sloId: '123' });
jest
.spyOn(Router, 'useLocation')
- .mockReturnValue({ pathname: '/slos/123/edit', search: '', state: '', hash: '' });
-
+ .mockReturnValue({ pathname: '/slos/edit/123', search: '', state: '', hash: '' });
useFetchSloMock.mockReturnValue({ isLoading: false, data: slo });
const { getByTestId } = render( );
diff --git a/x-pack/plugins/observability_solution/slo/public/pages/slo_edit/slo_edit.tsx b/x-pack/plugins/observability_solution/slo/public/pages/slo_edit/slo_edit.tsx
index b014bdb1d6dec..0a563bbe75b44 100644
--- a/x-pack/plugins/observability_solution/slo/public/pages/slo_edit/slo_edit.tsx
+++ b/x-pack/plugins/observability_solution/slo/public/pages/slo_edit/slo_edit.tsx
@@ -12,10 +12,10 @@ import { useParams } from 'react-router-dom';
import { paths } from '../../../common/locators/paths';
import { HeaderMenu } from '../../components/header_menu/header_menu';
import { useFetchSloDetails } from '../../hooks/use_fetch_slo_details';
+import { useKibana } from '../../hooks/use_kibana';
import { useLicense } from '../../hooks/use_license';
import { usePermissions } from '../../hooks/use_permissions';
import { usePluginContext } from '../../hooks/use_plugin_context';
-import { useKibana } from '../../hooks/use_kibana';
import { SloEditForm } from './components/slo_edit_form';
export function SloEditPage() {
diff --git a/x-pack/plugins/observability_solution/slo/public/pages/slo_edit/types.ts b/x-pack/plugins/observability_solution/slo/public/pages/slo_edit/types.ts
index 5eef9a2d0e5ba..6584e52404bc5 100644
--- a/x-pack/plugins/observability_solution/slo/public/pages/slo_edit/types.ts
+++ b/x-pack/plugins/observability_solution/slo/public/pages/slo_edit/types.ts
@@ -25,5 +25,8 @@ export interface CreateSLOForm {
groupBy: string[] | string;
settings: {
preventInitialBackfill: boolean;
+ syncDelay: number; // in minutes
+ frequency: number; // in minutes
+ syncField: string | null;
};
}
diff --git a/x-pack/plugins/observability_solution/slo/public/utils/slo/remote_slo_urls.test.ts b/x-pack/plugins/observability_solution/slo/public/utils/slo/remote_slo_urls.test.ts
index 3c0495fd1bc9b..6f44d13150819 100644
--- a/x-pack/plugins/observability_solution/slo/public/utils/slo/remote_slo_urls.test.ts
+++ b/x-pack/plugins/observability_solution/slo/public/utils/slo/remote_slo_urls.test.ts
@@ -51,7 +51,7 @@ describe('remote SLO URLs Utils', () => {
`"https://cloud.elast.co/app/slos/edit/fixed-id"`
);
expect(createRemoteSloCloneUrl(remoteSlo)).toMatchInlineSnapshot(
- `"https://cloud.elast.co/app/slos/create?_a=(budgetingMethod:occurrences,createdAt:%272022-12-29T10:11:12.000Z%27,description:%27some%20description%20useful%27,enabled:!t,groupBy:%27*%27,groupings:(),indicator:(params:(filter:%27baz:%20foo%20and%20bar%20%3E%202%27,good:%27http_status:%202xx%27,index:some-index,timestampField:custom_timestamp,total:%27a%20query%27),type:sli.kql.custom),instanceId:%27*%27,meta:(),name:%27[Copy]%20super%20important%20level%20service%27,objective:(target:0.98),remote:(kibanaUrl:%27https:/cloud.elast.co/kibana%27,remoteName:remote_cluster),revision:1,settings:(frequency:%271m%27,preventInitialBackfill:!f,syncDelay:%271m%27),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:%2730d%27,type:rolling),updatedAt:%272022-12-29T10:11:12.000Z%27,version:2)"`
+ `"https://cloud.elast.co/app/slos/create?_a=(budgetingMethod:occurrences,createdAt:%272022-12-29T10:11:12.000Z%27,description:%27some%20description%20useful%27,enabled:!t,groupBy:%27*%27,groupings:(),indicator:(params:(dataViewId:some-data-view-id,filter:%27baz:%20foo%20and%20bar%20%3E%202%27,good:%27http_status:%202xx%27,index:some-index,timestampField:custom_timestamp,total:%27a%20query%27),type:sli.kql.custom),instanceId:%27*%27,meta:(),name:%27[Copy]%20super%20important%20level%20service%27,objective:(target:0.98),remote:(kibanaUrl:%27https:/cloud.elast.co/kibana%27,remoteName:remote_cluster),revision:1,settings:(frequency:%271m%27,preventInitialBackfill:!f,syncDelay:%271m%27),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:%2730d%27,type:rolling),updatedAt:%272022-12-29T10:11:12.000Z%27,version:2)"`
);
});
@@ -71,7 +71,7 @@ describe('remote SLO URLs Utils', () => {
`"https://cloud.elast.co/s/my-custom-space/app/slos/edit/fixed-id"`
);
expect(createRemoteSloCloneUrl(remoteSlo, 'my-custom-space')).toMatchInlineSnapshot(
- `"https://cloud.elast.co/s/my-custom-space/app/slos/create?_a=(budgetingMethod:occurrences,createdAt:%272022-12-29T10:11:12.000Z%27,description:%27some%20description%20useful%27,enabled:!t,groupBy:%27*%27,groupings:(),indicator:(params:(filter:%27baz:%20foo%20and%20bar%20%3E%202%27,good:%27http_status:%202xx%27,index:some-index,timestampField:custom_timestamp,total:%27a%20query%27),type:sli.kql.custom),instanceId:%27*%27,meta:(),name:%27[Copy]%20super%20important%20level%20service%27,objective:(target:0.98),remote:(kibanaUrl:%27https:/cloud.elast.co/kibana%27,remoteName:remote_cluster),revision:1,settings:(frequency:%271m%27,preventInitialBackfill:!f,syncDelay:%271m%27),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:%2730d%27,type:rolling),updatedAt:%272022-12-29T10:11:12.000Z%27,version:2)"`
+ `"https://cloud.elast.co/s/my-custom-space/app/slos/create?_a=(budgetingMethod:occurrences,createdAt:%272022-12-29T10:11:12.000Z%27,description:%27some%20description%20useful%27,enabled:!t,groupBy:%27*%27,groupings:(),indicator:(params:(dataViewId:some-data-view-id,filter:%27baz:%20foo%20and%20bar%20%3E%202%27,good:%27http_status:%202xx%27,index:some-index,timestampField:custom_timestamp,total:%27a%20query%27),type:sli.kql.custom),instanceId:%27*%27,meta:(),name:%27[Copy]%20super%20important%20level%20service%27,objective:(target:0.98),remote:(kibanaUrl:%27https:/cloud.elast.co/kibana%27,remoteName:remote_cluster),revision:1,settings:(frequency:%271m%27,preventInitialBackfill:!f,syncDelay:%271m%27),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:%2730d%27,type:rolling),updatedAt:%272022-12-29T10:11:12.000Z%27,version:2)"`
);
});
});
diff --git a/x-pack/plugins/observability_solution/slo/server/services/create_slo.ts b/x-pack/plugins/observability_solution/slo/server/services/create_slo.ts
index e7c09c352bd66..a8e01fb4681f4 100644
--- a/x-pack/plugins/observability_solution/slo/server/services/create_slo.ts
+++ b/x-pack/plugins/observability_solution/slo/server/services/create_slo.ts
@@ -9,6 +9,7 @@ import { TransformPutTransformRequest } from '@elastic/elasticsearch/lib/api/typ
import { ElasticsearchClient, IBasePath, IScopedClusterClient, Logger } from '@kbn/core/server';
import { ALL_VALUE, CreateSLOParams, CreateSLOResponse } from '@kbn/slo-schema';
import { asyncForEach } from '@kbn/std';
+import { merge } from 'lodash';
import { v4 as uuidv4 } from 'uuid';
import {
SLO_MODEL_VERSION,
@@ -46,8 +47,10 @@ export class CreateSLO {
const slo = this.toSLO(params);
validateSLO(slo);
- await this.assertSLOInexistant(slo);
- await assertExpectedIndicatorSourceIndexPrivileges(slo, this.esClient);
+ await Promise.all([
+ this.assertSLOInexistant(slo),
+ assertExpectedIndicatorSourceIndexPrivileges(slo, this.esClient),
+ ]);
const rollbackOperations = [];
const createPromise = this.repository.create(slo);
@@ -201,11 +204,14 @@ export class CreateSLO {
return {
...params,
id: params.id ?? uuidv4(),
- settings: {
- syncDelay: params.settings?.syncDelay ?? new Duration(1, DurationUnit.Minute),
- frequency: params.settings?.frequency ?? new Duration(1, DurationUnit.Minute),
- preventInitialBackfill: params.settings?.preventInitialBackfill ?? false,
- },
+ settings: merge(
+ {
+ syncDelay: new Duration(1, DurationUnit.Minute),
+ frequency: new Duration(1, DurationUnit.Minute),
+ preventInitialBackfill: false,
+ },
+ params.settings
+ ),
revision: params.revision ?? 1,
enabled: true,
tags: params.tags ?? [],
diff --git a/x-pack/plugins/observability_solution/slo/server/services/slo_repository.ts b/x-pack/plugins/observability_solution/slo/server/services/slo_repository.ts
index 4f9cf439e8ed1..afbdb999fc064 100644
--- a/x-pack/plugins/observability_solution/slo/server/services/slo_repository.ts
+++ b/x-pack/plugins/observability_solution/slo/server/services/slo_repository.ts
@@ -9,6 +9,7 @@ import { SavedObjectsClientContract } from '@kbn/core-saved-objects-api-server';
import { Logger } from '@kbn/core/server';
import { ALL_VALUE, Paginated, Pagination, sloDefinitionSchema } from '@kbn/slo-schema';
import { isLeft } from 'fp-ts/lib/Either';
+import { merge } from 'lodash';
import { SLO_MODEL_VERSION } from '../../common/constants';
import { SLODefinition, StoredSLODefinition } from '../domain/models';
import { SLONotFound } from '../errors';
@@ -155,10 +156,10 @@ export class KibanaSavedObjectsSLORepository implements SLORepository {
// We would need to call the _reset api on this SLO.
version: storedSLO.version ?? 1,
// settings.preventInitialBackfill was added in 8.15.0
- settings: {
- ...storedSLO.settings,
- preventInitialBackfill: storedSLO.settings?.preventInitialBackfill ?? false,
- },
+ settings: merge(
+ { preventInitialBackfill: false, syncDelay: '1m', frequency: '1m' },
+ storedSLO.settings
+ ),
});
if (isLeft(result)) {
diff --git a/x-pack/plugins/observability_solution/slo/server/services/transform_generators/__snapshots__/transform_generator.test.ts.snap b/x-pack/plugins/observability_solution/slo/server/services/transform_generators/__snapshots__/transform_generator.test.ts.snap
index 7d8e989c1140d..f49785cf936c5 100644
--- a/x-pack/plugins/observability_solution/slo/server/services/transform_generators/__snapshots__/transform_generator.test.ts.snap
+++ b/x-pack/plugins/observability_solution/slo/server/services/transform_generators/__snapshots__/transform_generator.test.ts.snap
@@ -63,3 +63,11 @@ Object {
},
}
`;
+
+exports[`Transform Generator settings builds the transform settings 1`] = `
+Object {
+ "frequency": "2m",
+ "sync_delay": "10m",
+ "sync_field": "my_timestamp_sync_field",
+}
+`;
diff --git a/x-pack/plugins/observability_solution/slo/server/services/transform_generators/apm_transaction_duration.ts b/x-pack/plugins/observability_solution/slo/server/services/transform_generators/apm_transaction_duration.ts
index d1f05605dab36..99361fa776789 100644
--- a/x-pack/plugins/observability_solution/slo/server/services/transform_generators/apm_transaction_duration.ts
+++ b/x-pack/plugins/observability_solution/slo/server/services/transform_generators/apm_transaction_duration.ts
@@ -42,7 +42,7 @@ export class ApmTransactionDurationTransformGenerator extends TransformGenerator
this.buildDestination(slo),
this.buildGroupBy(slo, slo.indicator),
this.buildAggregations(slo, slo.indicator),
- this.buildSettings(slo),
+ this.buildSettings(slo, '@timestamp'),
slo
);
}
diff --git a/x-pack/plugins/observability_solution/slo/server/services/transform_generators/apm_transaction_error_rate.ts b/x-pack/plugins/observability_solution/slo/server/services/transform_generators/apm_transaction_error_rate.ts
index 6adbd1d3eae9f..a65e4ae1d50dd 100644
--- a/x-pack/plugins/observability_solution/slo/server/services/transform_generators/apm_transaction_error_rate.ts
+++ b/x-pack/plugins/observability_solution/slo/server/services/transform_generators/apm_transaction_error_rate.ts
@@ -41,7 +41,7 @@ export class ApmTransactionErrorRateTransformGenerator extends TransformGenerato
this.buildDestination(slo),
this.buildGroupBy(slo, slo.indicator),
this.buildAggregations(slo),
- this.buildSettings(slo),
+ this.buildSettings(slo, '@timestamp'),
slo
);
}
diff --git a/x-pack/plugins/observability_solution/slo/server/services/transform_generators/transform_generator.test.ts b/x-pack/plugins/observability_solution/slo/server/services/transform_generators/transform_generator.test.ts
index e70d406d75396..2df8a1e40eebb 100644
--- a/x-pack/plugins/observability_solution/slo/server/services/transform_generators/transform_generator.test.ts
+++ b/x-pack/plugins/observability_solution/slo/server/services/transform_generators/transform_generator.test.ts
@@ -5,6 +5,7 @@
* 2.0.
*/
+import { Duration, DurationUnit } from '../../domain/models';
import { createAPMTransactionErrorRateIndicator, createSLO } from '../fixtures/slo';
import { ApmTransactionErrorRateTransformGenerator } from './apm_transaction_error_rate';
import { dataViewsService } from '@kbn/data-views-plugin/server/mocks';
@@ -45,4 +46,46 @@ describe('Transform Generator', () => {
expect(runtimeMappings).toEqual({});
});
});
+
+ describe('settings', () => {
+ const defaultSettings = {
+ syncDelay: new Duration(10, DurationUnit.Minute),
+ frequency: new Duration(2, DurationUnit.Minute),
+ preventInitialBackfill: true,
+ };
+
+ it('builds the transform settings', async () => {
+ const slo = createSLO({
+ settings: {
+ ...defaultSettings,
+ syncField: 'my_timestamp_sync_field',
+ },
+ });
+ const settings = generator.buildSettings(slo);
+ expect(settings).toMatchSnapshot();
+ });
+
+ it('builds the transform settings using the provided settings.syncField', async () => {
+ const slo = createSLO({
+ settings: {
+ ...defaultSettings,
+ syncField: 'my_timestamp_sync_field',
+ },
+ });
+ const settings = generator.buildSettings(slo, '@timestamp');
+ expect(settings.sync_field).toEqual('my_timestamp_sync_field');
+ });
+
+ it('builds the transform settings using provided fallback when no settings.syncField is configured', async () => {
+ const slo = createSLO({ settings: defaultSettings });
+ const settings = generator.buildSettings(slo, '@timestamp2');
+ expect(settings.sync_field).toEqual('@timestamp2');
+ });
+
+ it("builds the transform settings using '@timestamp' default fallback when no settings.syncField is configured", async () => {
+ const slo = createSLO({ settings: defaultSettings });
+ const settings = generator.buildSettings(slo);
+ expect(settings.sync_field).toEqual('@timestamp');
+ });
+ });
});
diff --git a/x-pack/plugins/observability_solution/slo/server/services/transform_generators/transform_generator.ts b/x-pack/plugins/observability_solution/slo/server/services/transform_generators/transform_generator.ts
index 6c44471fd6566..ea27ebbc7aa38 100644
--- a/x-pack/plugins/observability_solution/slo/server/services/transform_generators/transform_generator.ts
+++ b/x-pack/plugins/observability_solution/slo/server/services/transform_generators/transform_generator.ts
@@ -88,8 +88,9 @@ export abstract class TransformGenerator {
): TransformSettings {
return {
frequency: slo.settings.frequency.format(),
- sync_field: sourceIndexTimestampField, // timestamp field defined in the source index
sync_delay: slo.settings.syncDelay.format(),
+ // 8.17: use settings.syncField if truthy or default to sourceIndexTimestampField which is the indicator timestampField
+ sync_field: !!slo.settings.syncField ? slo.settings.syncField : sourceIndexTimestampField,
};
}
}
diff --git a/x-pack/plugins/observability_solution/slo/server/services/update_slo.ts b/x-pack/plugins/observability_solution/slo/server/services/update_slo.ts
index d1dfb2e70e00c..402ca82acecd4 100644
--- a/x-pack/plugins/observability_solution/slo/server/services/update_slo.ts
+++ b/x-pack/plugins/observability_solution/slo/server/services/update_slo.ts
@@ -43,9 +43,10 @@ export class UpdateSLO {
public async execute(sloId: string, params: UpdateSLOParams): Promise {
const originalSlo = await this.repository.findById(sloId);
- let updatedSlo: SLODefinition = Object.assign({}, originalSlo, params, {
+ let updatedSlo: SLODefinition = Object.assign({}, originalSlo, {
+ ...params,
groupBy: !!params.groupBy ? params.groupBy : originalSlo.groupBy,
- settings: mergePartialSettings(originalSlo.settings, params.settings),
+ settings: Object.assign({}, originalSlo.settings, params.settings),
});
if (isEqual(originalSlo, updatedSlo)) {
@@ -263,13 +264,3 @@ export class UpdateSLO {
return updateSLOResponseSchema.encode(slo);
}
}
-
-/**
- * Settings are merged by overwriting the original settings with the optional new partial settings.
- */
-function mergePartialSettings(
- originalSettings: SLODefinition['settings'],
- newPartialSettings: UpdateSLOParams['settings']
-) {
- return Object.assign({}, originalSettings, newPartialSettings);
-}
diff --git a/x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/components/monitors_page/hooks/use_create_slo.ts b/x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/components/monitors_page/hooks/use_create_slo.ts
index c75bc5e489208..03c2c2ace9210 100644
--- a/x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/components/monitors_page/hooks/use_create_slo.ts
+++ b/x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/components/monitors_page/hooks/use_create_slo.ts
@@ -41,10 +41,6 @@ export function useCreateSLO({
tags: [],
},
},
- budgetingMethod: 'occurrences',
- objective: {
- target: 0.99,
- },
tags: tags || [],
groupBy: ['monitor.name', 'observer.geo.name', 'monitor.id'],
},