diff --git a/.buildkite/pipelines/on_merge.yml b/.buildkite/pipelines/on_merge.yml index 98524f37c8bfe..2af74cbda6b9f 100644 --- a/.buildkite/pipelines/on_merge.yml +++ b/.buildkite/pipelines/on_merge.yml @@ -78,6 +78,12 @@ steps: - exit_status: '*' limit: 1 + - command: '.buildkite/scripts/steps/functional/on_merge_unsupported_ftrs.sh' + label: Trigger unsupported ftr tests + timeout_in_minutes: 10 + agents: + queue: 'kibana-default' + - command: .buildkite/scripts/steps/lint.sh label: 'Linting' agents: diff --git a/.buildkite/pipelines/on_merge_unsupported_ftrs.yml b/.buildkite/pipelines/on_merge_unsupported_ftrs.yml new file mode 100644 index 0000000000000..3244823c6c2e3 --- /dev/null +++ b/.buildkite/pipelines/on_merge_unsupported_ftrs.yml @@ -0,0 +1,66 @@ +steps: + - command: .buildkite/scripts/lifecycle/pre_build.sh + label: Pre-Build + timeout_in_minutes: 10 + agents: + queue: kibana-default + retry: + automatic: + - exit_status: '*' + limit: 1 + + - command: .buildkite/scripts/steps/build_kibana.sh + label: Build Kibana Distribution and Plugins + agents: + queue: n2-16-spot + key: build + if: "build.env('KIBANA_BUILD_ID') == null || build.env('KIBANA_BUILD_ID') == ''" + timeout_in_minutes: 60 + retry: + automatic: + - exit_status: '-1' + limit: 3 + + - command: .buildkite/scripts/steps/functional/apm_cypress.sh + label: 'APM Cypress Tests' + agents: + queue: n2-4-spot + depends_on: build + timeout_in_minutes: 120 + parallelism: 4 + retry: + automatic: + - exit_status: '-1' + limit: 3 + - exit_status: '*' + limit: 1 + + + - command: .buildkite/scripts/steps/functional/security_solution.sh + label: 'Security Solution Tests' + agents: + queue: n2-4-spot + depends_on: build + timeout_in_minutes: 120 + parallelism: 4 + retry: + automatic: + - exit_status: '-1' + limit: 3 + - exit_status: '*' + limit: 1 + + - command: .buildkite/scripts/steps/functional/synthetics_plugin.sh + label: 'Synthetics @elastic/synthetics Tests' + agents: + queue: n2-4-spot + depends_on: build + timeout_in_minutes: 120 + artifact_paths: + - 'x-pack/plugins/synthetics/e2e/.journeys/**/*' + retry: + automatic: + - exit_status: '-1' + limit: 3 + - exit_status: '*' + limit: 1 diff --git a/.buildkite/scripts/steps/functional/on_merge_unsupported_ftrs.sh b/.buildkite/scripts/steps/functional/on_merge_unsupported_ftrs.sh new file mode 100755 index 0000000000000..8c715da15726c --- /dev/null +++ b/.buildkite/scripts/steps/functional/on_merge_unsupported_ftrs.sh @@ -0,0 +1,6 @@ +#!/bin/bash + +set -euo pipefail + +echo "--- Trigger unsupported ftr tests" +ts-node .buildkite/scripts/steps/trigger_pipeline.ts kibana-on-merge-unsupported-ftrs "$BUILDKITE_BRANCH" "$BUILDKITE_COMMIT" diff --git a/api_docs/actions.devdocs.json b/api_docs/actions.devdocs.json index 2e090312b382b..0cdbf07af71d5 100644 --- a/api_docs/actions.devdocs.json +++ b/api_docs/actions.devdocs.json @@ -938,6 +938,42 @@ "returnComment": [], "initialIsOpen": false }, + { + "parentPluginId": "actions", + "id": "def-server.asNotificationExecutionSource", + "type": "Function", + "tags": [], + "label": "asNotificationExecutionSource", + "description": [], + "signature": [ + "(source: ", + "NotificationSource", + ") => ", + "NotificationExecutionSource" + ], + "path": "x-pack/plugins/actions/server/lib/action_execution_source.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "actions", + "id": "def-server.asNotificationExecutionSource.$1", + "type": "Object", + "tags": [], + "label": "source", + "description": [], + "signature": [ + "NotificationSource" + ], + "path": "x-pack/plugins/actions/server/lib/action_execution_source.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + } + ], + "returnComment": [], + "initialIsOpen": false + }, { "parentPluginId": "actions", "id": "def-server.asSavedObjectExecutionSource", @@ -3590,6 +3626,17 @@ "path": "x-pack/plugins/actions/common/execution_log_types.ts", "deprecated": false, "trackAdoption": false + }, + { + "parentPluginId": "actions", + "id": "def-common.IExecutionLog.source", + "type": "string", + "tags": [], + "label": "source", + "description": [], + "path": "x-pack/plugins/actions/common/execution_log_types.ts", + "deprecated": false, + "trackAdoption": false } ], "initialIsOpen": false diff --git a/api_docs/actions.mdx b/api_docs/actions.mdx index 827c76ac7b308..1cff4313a5a2c 100644 --- a/api_docs/actions.mdx +++ b/api_docs/actions.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/actions title: "actions" image: https://source.unsplash.com/400x175/?github description: API docs for the actions plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'actions'] --- import actionsObj from './actions.devdocs.json'; @@ -21,7 +21,7 @@ Contact [@elastic/response-ops](https://github.com/orgs/elastic/teams/response-o | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 256 | 8 | 251 | 24 | +| 259 | 8 | 254 | 26 | ## Client diff --git a/api_docs/advanced_settings.mdx b/api_docs/advanced_settings.mdx index 573cf95db5bfb..cd4c0dc722546 100644 --- a/api_docs/advanced_settings.mdx +++ b/api_docs/advanced_settings.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/advancedSettings title: "advancedSettings" image: https://source.unsplash.com/400x175/?github description: API docs for the advancedSettings plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'advancedSettings'] --- import advancedSettingsObj from './advanced_settings.devdocs.json'; diff --git a/api_docs/aiops.mdx b/api_docs/aiops.mdx index 2e2d97ca82039..e1b23a80e89fb 100644 --- a/api_docs/aiops.mdx +++ b/api_docs/aiops.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/aiops title: "aiops" image: https://source.unsplash.com/400x175/?github description: API docs for the aiops plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'aiops'] --- import aiopsObj from './aiops.devdocs.json'; diff --git a/api_docs/alerting.devdocs.json b/api_docs/alerting.devdocs.json index 016e965013dfe..4cfb55b465603 100644 --- a/api_docs/alerting.devdocs.json +++ b/api_docs/alerting.devdocs.json @@ -875,56 +875,27 @@ "label": "getComponentTemplate", "description": [], "signature": [ - "(fieldMap: ", - { - "pluginId": "@kbn/alerts-as-data-utils", - "scope": "common", - "docId": "kibKbnAlertsAsDataUtilsPluginApi", - "section": "def-common.FieldMap", - "text": "FieldMap" - }, - ", context?: string | undefined) => ", + "({ fieldMap, context, name, dynamic, includeSettings, }: GetComponentTemplateOpts) => ", "ClusterPutComponentTemplateRequest" ], - "path": "x-pack/plugins/alerting/server/alerts_service/types.ts", + "path": "x-pack/plugins/alerting/server/alerts_service/resource_installer_utils.ts", "deprecated": false, "trackAdoption": false, "children": [ { "parentPluginId": "alerting", "id": "def-server.getComponentTemplate.$1", - "type": "Object", + "type": "CompoundType", "tags": [], - "label": "fieldMap", + "label": "{\n fieldMap,\n context,\n name,\n dynamic,\n includeSettings,\n}", "description": [], "signature": [ - { - "pluginId": "@kbn/alerts-as-data-utils", - "scope": "common", - "docId": "kibKbnAlertsAsDataUtilsPluginApi", - "section": "def-common.FieldMap", - "text": "FieldMap" - } + "GetComponentTemplateOpts" ], - "path": "x-pack/plugins/alerting/server/alerts_service/types.ts", + "path": "x-pack/plugins/alerting/server/alerts_service/resource_installer_utils.ts", "deprecated": false, "trackAdoption": false, "isRequired": true - }, - { - "parentPluginId": "alerting", - "id": "def-server.getComponentTemplate.$2", - "type": "string", - "tags": [], - "label": "context", - "description": [], - "signature": [ - "string | undefined" - ], - "path": "x-pack/plugins/alerting/server/alerts_service/types.ts", - "deprecated": false, - "trackAdoption": false, - "isRequired": false } ], "returnComment": [], @@ -1627,6 +1598,97 @@ ], "initialIsOpen": false }, + { + "parentPluginId": "alerting", + "id": "def-server.IRuleTypeAlerts", + "type": "Interface", + "tags": [], + "label": "IRuleTypeAlerts", + "description": [], + "path": "x-pack/plugins/alerting/server/types.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "alerting", + "id": "def-server.IRuleTypeAlerts.context", + "type": "string", + "tags": [], + "label": "context", + "description": [ + "\nSpecifies the target alerts-as-data resource\nfor this rule type. All alerts created with the same\ncontext are written to the same alerts-as-data index.\n\nAll custom mappings defined for a context must be the same!" + ], + "path": "x-pack/plugins/alerting/server/types.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "alerting", + "id": "def-server.IRuleTypeAlerts.mappings", + "type": "Object", + "tags": [], + "label": "mappings", + "description": [ + "\nSpecifies custom mappings for the target alerts-as-data\nindex. These mappings will be translated into a component template\nand used in the index template for the index." + ], + "signature": [ + "ComponentTemplateSpec" + ], + "path": "x-pack/plugins/alerting/server/types.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "alerting", + "id": "def-server.IRuleTypeAlerts.useEcs", + "type": "CompoundType", + "tags": [], + "label": "useEcs", + "description": [ + "\nOptional flag to include a reference to the ECS component template." + ], + "signature": [ + "boolean | undefined" + ], + "path": "x-pack/plugins/alerting/server/types.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "alerting", + "id": "def-server.IRuleTypeAlerts.useLegacyAlerts", + "type": "CompoundType", + "tags": [], + "label": "useLegacyAlerts", + "description": [ + "\nOptional flag to include a reference to the legacy alert component template.\nAny rule type that is migrating from the rule registry should set this\nflag to true to ensure their alerts-as-data indices are backwards compatible." + ], + "signature": [ + "boolean | undefined" + ], + "path": "x-pack/plugins/alerting/server/types.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "alerting", + "id": "def-server.IRuleTypeAlerts.secondaryAlias", + "type": "string", + "tags": [], + "label": "secondaryAlias", + "description": [ + "\nOptional secondary alias to use. This alias should not include the namespace." + ], + "signature": [ + "string | undefined" + ], + "path": "x-pack/plugins/alerting/server/types.ts", + "deprecated": false, + "trackAdoption": false + } + ], + "initialIsOpen": false + }, { "parentPluginId": "alerting", "id": "def-server.PluginSetupContract", @@ -1776,19 +1838,18 @@ }, { "parentPluginId": "alerting", - "id": "def-server.PluginSetupContract.getFrameworkAlertsEnabled", - "type": "Function", + "id": "def-server.PluginSetupContract.frameworkAlerts", + "type": "CompoundType", "tags": [], - "label": "getFrameworkAlertsEnabled", + "label": "frameworkAlerts", "description": [], "signature": [ - "() => boolean" + "PublicAlertsService", + " & { enabled: () => boolean; }" ], "path": "x-pack/plugins/alerting/server/plugin.ts", "deprecated": false, - "trackAdoption": false, - "children": [], - "returnComment": [] + "trackAdoption": false } ], "initialIsOpen": false @@ -3026,7 +3087,13 @@ "label": "alerts", "description": [], "signature": [ - "IRuleTypeAlerts", + { + "pluginId": "alerting", + "scope": "server", + "docId": "kibAlertingPluginApi", + "section": "def-server.IRuleTypeAlerts", + "text": "IRuleTypeAlerts" + }, " | undefined" ], "path": "x-pack/plugins/alerting/server/types.ts", @@ -3428,6 +3495,22 @@ "trackAdoption": false, "initialIsOpen": false }, + { + "parentPluginId": "alerting", + "id": "def-server.PublicFrameworkAlertsService", + "type": "Type", + "tags": [], + "label": "PublicFrameworkAlertsService", + "description": [], + "signature": [ + "PublicAlertsService", + " & { enabled: () => boolean; }" + ], + "path": "x-pack/plugins/alerting/server/alerts_service/alerts_service.ts", + "deprecated": false, + "trackAdoption": false, + "initialIsOpen": false + }, { "parentPluginId": "alerting", "id": "def-server.RuleActionParams", @@ -3543,11 +3626,9 @@ "section": "def-server.FindResult", "text": "FindResult" }, - ">; muteAll: (options: { id: string; }) => Promise; aggregate: (params?: { options?: ", - "AggregateOptions", - " | undefined; } | undefined) => Promise<", - "AggregateResult", - ">; clone: >; muteAll: (options: { id: string; }) => Promise; aggregate: >(params: ", + "AggregateParams", + ") => Promise; clone: ", + { + "pluginId": "alerting", + "scope": "common", + "docId": "kibAlertingPluginApi", + "section": "def-common.RuleAggregationFormattedResult", + "text": "RuleAggregationFormattedResult" + } + ], + "path": "x-pack/plugins/alerting/common/default_rule_aggregation.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "alerting", + "id": "def-common.formatDefaultAggregationResult.$1", + "type": "Object", + "tags": [], + "label": "aggregations", + "description": [], + "signature": [ + { + "pluginId": "alerting", + "scope": "common", + "docId": "kibAlertingPluginApi", + "section": "def-common.DefaultRuleAggregationResult", + "text": "DefaultRuleAggregationResult" + } + ], + "path": "x-pack/plugins/alerting/common/default_rule_aggregation.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + } + ], + "returnComment": [], + "initialIsOpen": false + }, { "parentPluginId": "alerting", "id": "def-common.formatDuration", @@ -3964,6 +4099,60 @@ "returnComment": [], "initialIsOpen": false }, + { + "parentPluginId": "alerting", + "id": "def-common.formatRuleTagsAggregationResult", + "type": "Function", + "tags": [], + "label": "formatRuleTagsAggregationResult", + "description": [], + "signature": [ + "(aggregations: ", + { + "pluginId": "alerting", + "scope": "common", + "docId": "kibAlertingPluginApi", + "section": "def-common.RuleTagsAggregationResult", + "text": "RuleTagsAggregationResult" + }, + ") => ", + { + "pluginId": "alerting", + "scope": "common", + "docId": "kibAlertingPluginApi", + "section": "def-common.RuleTagsAggregationFormattedResult", + "text": "RuleTagsAggregationFormattedResult" + } + ], + "path": "x-pack/plugins/alerting/common/rule_tags_aggregation.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "alerting", + "id": "def-common.formatRuleTagsAggregationResult.$1", + "type": "Object", + "tags": [], + "label": "aggregations", + "description": [], + "signature": [ + { + "pluginId": "alerting", + "scope": "common", + "docId": "kibAlertingPluginApi", + "section": "def-common.RuleTagsAggregationResult", + "text": "RuleTagsAggregationResult" + } + ], + "path": "x-pack/plugins/alerting/common/rule_tags_aggregation.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + } + ], + "returnComment": [], + "initialIsOpen": false + }, { "parentPluginId": "alerting", "id": "def-common.getBuiltinActionGroups", @@ -4036,7 +4225,7 @@ "label": "getComponentTemplateFromFieldMap", "description": [], "signature": [ - "({ name, fieldMap, fieldLimit, }: ", + "({ name, fieldMap, dynamic, includeSettings, }: ", "GetComponentTemplateFromFieldMapOpts", ") => ", "ClusterPutComponentTemplateRequest" @@ -4050,7 +4239,7 @@ "id": "def-common.getComponentTemplateFromFieldMap.$1", "type": "Object", "tags": [], - "label": "{\n name,\n fieldMap,\n fieldLimit,\n}", + "label": "{\n name,\n fieldMap,\n dynamic,\n includeSettings,\n}", "description": [], "signature": [ "GetComponentTemplateFromFieldMapOpts" @@ -4064,6 +4253,41 @@ "returnComment": [], "initialIsOpen": false }, + { + "parentPluginId": "alerting", + "id": "def-common.getDefaultRuleAggregation", + "type": "Function", + "tags": [], + "label": "getDefaultRuleAggregation", + "description": [], + "signature": [ + "(params?: GetDefaultRuleAggregationParams | undefined) => Record" + ], + "path": "x-pack/plugins/alerting/common/default_rule_aggregation.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "alerting", + "id": "def-common.getDefaultRuleAggregation.$1", + "type": "Object", + "tags": [], + "label": "params", + "description": [], + "signature": [ + "GetDefaultRuleAggregationParams | undefined" + ], + "path": "x-pack/plugins/alerting/common/default_rule_aggregation.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": false + } + ], + "returnComment": [], + "initialIsOpen": false + }, { "parentPluginId": "alerting", "id": "def-common.getDurationNumberInItsUnit", @@ -4130,6 +4354,41 @@ "returnComment": [], "initialIsOpen": false }, + { + "parentPluginId": "alerting", + "id": "def-common.getRuleTagsAggregation", + "type": "Function", + "tags": [], + "label": "getRuleTagsAggregation", + "description": [], + "signature": [ + "(params?: GetRuleTagsAggregationParams | undefined) => Record" + ], + "path": "x-pack/plugins/alerting/common/rule_tags_aggregation.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "alerting", + "id": "def-common.getRuleTagsAggregation.$1", + "type": "Object", + "tags": [], + "label": "params", + "description": [], + "signature": [ + "GetRuleTagsAggregationParams | undefined" + ], + "path": "x-pack/plugins/alerting/common/rule_tags_aggregation.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": false + } + ], + "returnComment": [], + "initialIsOpen": false + }, { "parentPluginId": "alerting", "id": "def-common.isActionGroupDisabledForActionTypeId", @@ -4469,6 +4728,126 @@ ], "initialIsOpen": false }, + { + "parentPluginId": "alerting", + "id": "def-common.AggregateOptions", + "type": "Interface", + "tags": [], + "label": "AggregateOptions", + "description": [], + "path": "x-pack/plugins/alerting/common/rule.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "alerting", + "id": "def-common.AggregateOptions.search", + "type": "string", + "tags": [], + "label": "search", + "description": [], + "signature": [ + "string | undefined" + ], + "path": "x-pack/plugins/alerting/common/rule.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "alerting", + "id": "def-common.AggregateOptions.defaultSearchOperator", + "type": "CompoundType", + "tags": [], + "label": "defaultSearchOperator", + "description": [], + "signature": [ + "\"AND\" | \"OR\" | undefined" + ], + "path": "x-pack/plugins/alerting/common/rule.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "alerting", + "id": "def-common.AggregateOptions.searchFields", + "type": "Array", + "tags": [], + "label": "searchFields", + "description": [], + "signature": [ + "string[] | undefined" + ], + "path": "x-pack/plugins/alerting/common/rule.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "alerting", + "id": "def-common.AggregateOptions.hasReference", + "type": "Object", + "tags": [], + "label": "hasReference", + "description": [], + "signature": [ + "{ type: string; id: string; } | undefined" + ], + "path": "x-pack/plugins/alerting/common/rule.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "alerting", + "id": "def-common.AggregateOptions.filter", + "type": "CompoundType", + "tags": [], + "label": "filter", + "description": [], + "signature": [ + "string | ", + { + "pluginId": "@kbn/es-query", + "scope": "common", + "docId": "kibKbnEsQueryPluginApi", + "section": "def-common.KueryNode", + "text": "KueryNode" + }, + " | undefined" + ], + "path": "x-pack/plugins/alerting/common/rule.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "alerting", + "id": "def-common.AggregateOptions.page", + "type": "number", + "tags": [], + "label": "page", + "description": [], + "signature": [ + "number | undefined" + ], + "path": "x-pack/plugins/alerting/common/rule.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "alerting", + "id": "def-common.AggregateOptions.perPage", + "type": "number", + "tags": [], + "label": "perPage", + "description": [], + "signature": [ + "number | undefined" + ], + "path": "x-pack/plugins/alerting/common/rule.ts", + "deprecated": false, + "trackAdoption": false + } + ], + "initialIsOpen": false + }, { "parentPluginId": "alerting", "id": "def-common.AlertingFrameworkHealth", @@ -4950,6 +5329,104 @@ ], "initialIsOpen": false }, + { + "parentPluginId": "alerting", + "id": "def-common.DefaultRuleAggregationResult", + "type": "Interface", + "tags": [], + "label": "DefaultRuleAggregationResult", + "description": [], + "path": "x-pack/plugins/alerting/common/default_rule_aggregation.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "alerting", + "id": "def-common.DefaultRuleAggregationResult.status", + "type": "Object", + "tags": [], + "label": "status", + "description": [], + "signature": [ + "{ buckets: { key: string; doc_count: number; }[]; }" + ], + "path": "x-pack/plugins/alerting/common/default_rule_aggregation.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "alerting", + "id": "def-common.DefaultRuleAggregationResult.outcome", + "type": "Object", + "tags": [], + "label": "outcome", + "description": [], + "signature": [ + "{ buckets: { key: string; doc_count: number; }[]; }" + ], + "path": "x-pack/plugins/alerting/common/default_rule_aggregation.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "alerting", + "id": "def-common.DefaultRuleAggregationResult.muted", + "type": "Object", + "tags": [], + "label": "muted", + "description": [], + "signature": [ + "{ buckets: { key: number; key_as_string: string; doc_count: number; }[]; }" + ], + "path": "x-pack/plugins/alerting/common/default_rule_aggregation.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "alerting", + "id": "def-common.DefaultRuleAggregationResult.enabled", + "type": "Object", + "tags": [], + "label": "enabled", + "description": [], + "signature": [ + "{ buckets: { key: number; key_as_string: string; doc_count: number; }[]; }" + ], + "path": "x-pack/plugins/alerting/common/default_rule_aggregation.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "alerting", + "id": "def-common.DefaultRuleAggregationResult.snoozed", + "type": "Object", + "tags": [], + "label": "snoozed", + "description": [], + "signature": [ + "{ count: { doc_count: number; }; }" + ], + "path": "x-pack/plugins/alerting/common/default_rule_aggregation.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "alerting", + "id": "def-common.DefaultRuleAggregationResult.tags", + "type": "Object", + "tags": [], + "label": "tags", + "description": [], + "signature": [ + "{ buckets: { key: string; doc_count: number; }[]; }" + ], + "path": "x-pack/plugins/alerting/common/default_rule_aggregation.ts", + "deprecated": false, + "trackAdoption": false + } + ], + "initialIsOpen": false + }, { "parentPluginId": "alerting", "id": "def-common.ExecutionDuration", @@ -6263,10 +6740,10 @@ }, { "parentPluginId": "alerting", - "id": "def-common.RuleAggregations", + "id": "def-common.RuleAggregationFormattedResult", "type": "Interface", "tags": [], - "label": "RuleAggregations", + "label": "RuleAggregationFormattedResult", "description": [], "path": "x-pack/plugins/alerting/common/rule.ts", "deprecated": false, @@ -6274,10 +6751,10 @@ "children": [ { "parentPluginId": "alerting", - "id": "def-common.RuleAggregations.alertExecutionStatus", + "id": "def-common.RuleAggregationFormattedResult.ruleExecutionStatus", "type": "Object", "tags": [], - "label": "alertExecutionStatus", + "label": "ruleExecutionStatus", "description": [], "signature": [ "{ [status: string]: number; }" @@ -6288,7 +6765,7 @@ }, { "parentPluginId": "alerting", - "id": "def-common.RuleAggregations.ruleLastRunOutcome", + "id": "def-common.RuleAggregationFormattedResult.ruleLastRunOutcome", "type": "Object", "tags": [], "label": "ruleLastRunOutcome", @@ -6302,7 +6779,7 @@ }, { "parentPluginId": "alerting", - "id": "def-common.RuleAggregations.ruleEnabledStatus", + "id": "def-common.RuleAggregationFormattedResult.ruleEnabledStatus", "type": "Object", "tags": [], "label": "ruleEnabledStatus", @@ -6316,7 +6793,7 @@ }, { "parentPluginId": "alerting", - "id": "def-common.RuleAggregations.ruleMutedStatus", + "id": "def-common.RuleAggregationFormattedResult.ruleMutedStatus", "type": "Object", "tags": [], "label": "ruleMutedStatus", @@ -6330,7 +6807,7 @@ }, { "parentPluginId": "alerting", - "id": "def-common.RuleAggregations.ruleSnoozedStatus", + "id": "def-common.RuleAggregationFormattedResult.ruleSnoozedStatus", "type": "Object", "tags": [], "label": "ruleSnoozedStatus", @@ -6344,7 +6821,7 @@ }, { "parentPluginId": "alerting", - "id": "def-common.RuleAggregations.ruleTags", + "id": "def-common.RuleAggregationFormattedResult.ruleTags", "type": "Array", "tags": [], "label": "ruleTags", @@ -7188,6 +7665,62 @@ ], "initialIsOpen": false }, + { + "parentPluginId": "alerting", + "id": "def-common.RuleTagsAggregationFormattedResult", + "type": "Interface", + "tags": [], + "label": "RuleTagsAggregationFormattedResult", + "description": [], + "path": "x-pack/plugins/alerting/common/rule_tags_aggregation.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "alerting", + "id": "def-common.RuleTagsAggregationFormattedResult.ruleTags", + "type": "Array", + "tags": [], + "label": "ruleTags", + "description": [], + "signature": [ + "string[]" + ], + "path": "x-pack/plugins/alerting/common/rule_tags_aggregation.ts", + "deprecated": false, + "trackAdoption": false + } + ], + "initialIsOpen": false + }, + { + "parentPluginId": "alerting", + "id": "def-common.RuleTagsAggregationResult", + "type": "Interface", + "tags": [], + "label": "RuleTagsAggregationResult", + "description": [], + "path": "x-pack/plugins/alerting/common/rule_tags_aggregation.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "alerting", + "id": "def-common.RuleTagsAggregationResult.tags", + "type": "Object", + "tags": [], + "label": "tags", + "description": [], + "signature": [ + "{ buckets: { key: { tags: string; }; doc_count: number; }[]; }" + ], + "path": "x-pack/plugins/alerting/common/rule_tags_aggregation.ts", + "deprecated": false, + "trackAdoption": false + } + ], + "initialIsOpen": false + }, { "parentPluginId": "alerting", "id": "def-common.RuleType", @@ -8181,6 +8714,29 @@ "trackAdoption": false, "initialIsOpen": false }, + { + "parentPluginId": "alerting", + "id": "def-common.RuleTagsAggregationOptions", + "type": "Type", + "tags": [], + "label": "RuleTagsAggregationOptions", + "description": [], + "signature": [ + "Pick<", + { + "pluginId": "alerting", + "scope": "common", + "docId": "kibAlertingPluginApi", + "section": "def-common.AggregateOptions", + "text": "AggregateOptions" + }, + ", \"filter\" | \"search\"> & { after?: Record | undefined; maxTags?: number | undefined; }" + ], + "path": "x-pack/plugins/alerting/common/rule_tags_aggregation.ts", + "deprecated": false, + "trackAdoption": false, + "initialIsOpen": false + }, { "parentPluginId": "alerting", "id": "def-common.RuleTaskParams", diff --git a/api_docs/alerting.mdx b/api_docs/alerting.mdx index 6a2c94b66601a..a598a47caeb40 100644 --- a/api_docs/alerting.mdx +++ b/api_docs/alerting.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/alerting title: "alerting" image: https://source.unsplash.com/400x175/?github description: API docs for the alerting plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'alerting'] --- import alertingObj from './alerting.devdocs.json'; @@ -21,7 +21,7 @@ Contact [@elastic/response-ops](https://github.com/orgs/elastic/teams/response-o | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 497 | 1 | 486 | 41 | +| 531 | 1 | 515 | 40 | ## Client diff --git a/api_docs/apm.devdocs.json b/api_docs/apm.devdocs.json index 401f2a681fa88..82c3d641ea6d6 100644 --- a/api_docs/apm.devdocs.json +++ b/api_docs/apm.devdocs.json @@ -810,7 +810,7 @@ "label": "APIEndpoint", "description": [], "signature": [ - "\"POST /internal/apm/data_view/static\" | \"GET /internal/apm/data_view/title\" | \"GET /internal/apm/environments\" | \"GET /internal/apm/services/{serviceName}/errors/groups/main_statistics\" | \"GET /internal/apm/services/{serviceName}/errors/groups/main_statistics_by_transaction_name\" | \"POST /internal/apm/services/{serviceName}/errors/groups/detailed_statistics\" | \"GET /internal/apm/services/{serviceName}/errors/{groupId}/samples\" | \"GET /internal/apm/services/{serviceName}/errors/{groupId}/error/{errorId}\" | \"GET /internal/apm/services/{serviceName}/errors/distribution\" | \"GET /internal/apm/services/{serviceName}/errors/{groupId}/top_erroneous_transactions\" | \"POST /internal/apm/latency/overall_distribution/transactions\" | \"GET /internal/apm/services/{serviceName}/metrics/charts\" | \"GET /internal/apm/services/{serviceName}/metrics/nodes\" | \"GET /internal/apm/services/{serviceName}/metrics/serverless/charts\" | \"GET /internal/apm/services/{serviceName}/metrics/serverless/summary\" | \"GET /internal/apm/services/{serviceName}/metrics/serverless/functions_overview\" | \"GET /internal/apm/services/{serviceName}/metrics/serverless/active_instances\" | \"GET /internal/apm/observability_overview\" | \"GET /internal/apm/observability_overview/has_data\" | \"GET /internal/apm/service-map\" | \"GET /internal/apm/service-map/service/{serviceName}\" | \"GET /internal/apm/service-map/dependency\" | \"GET /internal/apm/services\" | \"POST /internal/apm/services/detailed_statistics\" | \"GET /internal/apm/services/{serviceName}/metadata/details\" | \"GET /internal/apm/services/{serviceName}/metadata/icons\" | \"GET /internal/apm/services/{serviceName}/agent\" | \"GET /internal/apm/services/{serviceName}/transaction_types\" | \"GET /internal/apm/services/{serviceName}/node/{serviceNodeName}/metadata\" | \"GET /api/apm/services/{serviceName}/annotation/search\" | \"POST /api/apm/services/{serviceName}/annotation\" | \"GET /internal/apm/services/{serviceName}/service_overview_instances/details/{serviceNodeName}\" | \"GET /internal/apm/services/{serviceName}/throughput\" | \"GET /internal/apm/services/{serviceName}/service_overview_instances/main_statistics\" | \"GET /internal/apm/services/{serviceName}/service_overview_instances/detailed_statistics\" | \"GET /internal/apm/services/{serviceName}/dependencies\" | \"GET /internal/apm/services/{serviceName}/dependencies/breakdown\" | \"GET /internal/apm/services/{serviceName}/anomaly_charts\" | \"GET /internal/apm/services/{serviceName}/alerts_count\" | \"GET /internal/apm/service-groups\" | \"GET /internal/apm/service-group\" | \"POST /internal/apm/service-group\" | \"DELETE /internal/apm/service-group\" | \"GET /internal/apm/service-group/services\" | \"GET /internal/apm/service-group/counts\" | \"GET /internal/apm/suggestions\" | \"GET /internal/apm/traces/{traceId}\" | \"GET /internal/apm/traces\" | \"GET /internal/apm/traces/{traceId}/root_transaction\" | \"GET /internal/apm/transactions/{transactionId}\" | \"GET /internal/apm/traces/find\" | \"POST /internal/apm/traces/aggregated_critical_path\" | \"GET /internal/apm/traces/{traceId}/transactions/{transactionId}\" | \"GET /internal/apm/traces/{traceId}/spans/{spanId}\" | \"GET /internal/apm/services/{serviceName}/transactions/groups/main_statistics\" | \"GET /internal/apm/services/{serviceName}/transactions/groups/detailed_statistics\" | \"GET /internal/apm/services/{serviceName}/transactions/charts/latency\" | \"GET /internal/apm/services/{serviceName}/transactions/traces/samples\" | \"GET /internal/apm/services/{serviceName}/transaction/charts/breakdown\" | \"GET /internal/apm/services/{serviceName}/transactions/charts/error_rate\" | \"GET /internal/apm/services/{serviceName}/transactions/charts/coldstart_rate\" | \"GET /internal/apm/services/{serviceName}/transactions/charts/coldstart_rate_by_transaction_name\" | \"GET /internal/apm/rule_types/transaction_error_rate/chart_preview\" | \"GET /internal/apm/rule_types/transaction_duration/chart_preview\" | \"GET /internal/apm/rule_types/error_count/chart_preview\" | \"GET /api/apm/settings/agent-configuration\" | \"GET /api/apm/settings/agent-configuration/view\" | \"DELETE /api/apm/settings/agent-configuration\" | \"PUT /api/apm/settings/agent-configuration\" | \"POST /api/apm/settings/agent-configuration/search\" | \"GET /api/apm/settings/agent-configuration/environments\" | \"GET /api/apm/settings/agent-configuration/agent_name\" | \"GET /internal/apm/settings/anomaly-detection/jobs\" | \"POST /internal/apm/settings/anomaly-detection/jobs\" | \"GET /internal/apm/settings/anomaly-detection/environments\" | \"POST /internal/apm/settings/anomaly-detection/update_to_v3\" | \"GET /internal/apm/settings/apm-index-settings\" | \"GET /internal/apm/settings/apm-indices\" | \"POST /internal/apm/settings/apm-indices/save\" | \"GET /internal/apm/settings/custom_links/transaction\" | \"GET /internal/apm/settings/custom_links\" | \"POST /internal/apm/settings/custom_links\" | \"PUT /internal/apm/settings/custom_links/{id}\" | \"DELETE /internal/apm/settings/custom_links/{id}\" | \"GET /api/apm/sourcemaps\" | \"POST /api/apm/sourcemaps\" | \"DELETE /api/apm/sourcemaps/{id}\" | \"POST /internal/apm/sourcemaps/migrate_fleet_artifacts\" | \"GET /internal/apm/fleet/has_apm_policies\" | \"GET /internal/apm/fleet/agents\" | \"POST /api/apm/fleet/apm_server_schema\" | \"GET /internal/apm/fleet/apm_server_schema/unsupported\" | \"GET /internal/apm/fleet/migration_check\" | \"POST /internal/apm/fleet/cloud_apm_package_policy\" | \"GET /internal/apm/fleet/java_agent_versions\" | \"GET /internal/apm/dependencies/top_dependencies\" | \"GET /internal/apm/dependencies/upstream_services\" | \"GET /internal/apm/dependencies/metadata\" | \"GET /internal/apm/dependencies/charts/latency\" | \"GET /internal/apm/dependencies/charts/throughput\" | \"GET /internal/apm/dependencies/charts/error_rate\" | \"GET /internal/apm/dependencies/operations\" | \"GET /internal/apm/dependencies/charts/distribution\" | \"GET /internal/apm/dependencies/operations/spans\" | \"GET /internal/apm/correlations/field_candidates/transactions\" | \"GET /internal/apm/correlations/field_value_stats/transactions\" | \"POST /internal/apm/correlations/field_value_pairs/transactions\" | \"POST /internal/apm/correlations/significant_correlations/transactions\" | \"POST /internal/apm/correlations/p_values/transactions\" | \"GET /internal/apm/fallback_to_transactions\" | \"GET /internal/apm/has_data\" | \"GET /internal/apm/event_metadata/{processorEvent}/{id}\" | \"GET /internal/apm/agent_keys\" | \"GET /internal/apm/agent_keys/privileges\" | \"POST /internal/apm/api_key/invalidate\" | \"POST /api/apm/agent_keys\" | \"GET /internal/apm/storage_explorer\" | \"GET /internal/apm/services/{serviceName}/storage_details\" | \"GET /internal/apm/storage_chart\" | \"GET /internal/apm/storage_explorer/privileges\" | \"GET /internal/apm/storage_explorer_summary_stats\" | \"GET /internal/apm/storage_explorer/is_cross_cluster_search\" | \"GET /internal/apm/storage_explorer/get_services\" | \"GET /internal/apm/traces/{traceId}/span_links/{spanId}/parents\" | \"GET /internal/apm/traces/{traceId}/span_links/{spanId}/children\" | \"GET /internal/apm/services/{serviceName}/infrastructure_attributes\" | \"GET /internal/apm/debug-telemetry\" | \"GET /internal/apm/time_range_metadata\" | \"GET /internal/apm/settings/labs\" | \"GET /internal/apm/get_agents_per_service\" | \"GET /internal/apm/services/{serviceName}/agent_instances\" | \"GET /internal/apm/services/{serviceName}/mobile/filters\" | \"GET /internal/apm/mobile-services/{serviceName}/transactions/charts/sessions\" | \"GET /internal/apm/mobile-services/{serviceName}/transactions/charts/http_requests\" | \"GET /internal/apm/mobile-services/{serviceName}/stats\" | \"GET /internal/apm/mobile-services/{serviceName}/location/stats\"" + "\"POST /internal/apm/data_view/static\" | \"GET /internal/apm/data_view/title\" | \"GET /internal/apm/environments\" | \"GET /internal/apm/services/{serviceName}/errors/groups/main_statistics\" | \"GET /internal/apm/services/{serviceName}/errors/groups/main_statistics_by_transaction_name\" | \"POST /internal/apm/services/{serviceName}/errors/groups/detailed_statistics\" | \"GET /internal/apm/services/{serviceName}/errors/{groupId}/samples\" | \"GET /internal/apm/services/{serviceName}/errors/{groupId}/error/{errorId}\" | \"GET /internal/apm/services/{serviceName}/errors/distribution\" | \"GET /internal/apm/services/{serviceName}/errors/{groupId}/top_erroneous_transactions\" | \"POST /internal/apm/latency/overall_distribution/transactions\" | \"GET /internal/apm/services/{serviceName}/metrics/charts\" | \"GET /internal/apm/services/{serviceName}/metrics/nodes\" | \"GET /internal/apm/services/{serviceName}/metrics/serverless/charts\" | \"GET /internal/apm/services/{serviceName}/metrics/serverless/summary\" | \"GET /internal/apm/services/{serviceName}/metrics/serverless/functions_overview\" | \"GET /internal/apm/services/{serviceName}/metrics/serverless/active_instances\" | \"GET /internal/apm/observability_overview\" | \"GET /internal/apm/observability_overview/has_data\" | \"GET /internal/apm/service-map\" | \"GET /internal/apm/service-map/service/{serviceName}\" | \"GET /internal/apm/service-map/dependency\" | \"GET /internal/apm/services\" | \"POST /internal/apm/services/detailed_statistics\" | \"GET /internal/apm/services/{serviceName}/metadata/details\" | \"GET /internal/apm/services/{serviceName}/metadata/icons\" | \"GET /internal/apm/services/{serviceName}/agent\" | \"GET /internal/apm/services/{serviceName}/transaction_types\" | \"GET /internal/apm/services/{serviceName}/node/{serviceNodeName}/metadata\" | \"GET /api/apm/services/{serviceName}/annotation/search\" | \"POST /api/apm/services/{serviceName}/annotation\" | \"GET /internal/apm/services/{serviceName}/service_overview_instances/details/{serviceNodeName}\" | \"GET /internal/apm/services/{serviceName}/throughput\" | \"GET /internal/apm/services/{serviceName}/service_overview_instances/main_statistics\" | \"GET /internal/apm/services/{serviceName}/service_overview_instances/detailed_statistics\" | \"GET /internal/apm/services/{serviceName}/dependencies\" | \"GET /internal/apm/services/{serviceName}/dependencies/breakdown\" | \"GET /internal/apm/services/{serviceName}/anomaly_charts\" | \"GET /internal/apm/services/{serviceName}/alerts_count\" | \"GET /internal/apm/service-groups\" | \"GET /internal/apm/service-group\" | \"POST /internal/apm/service-group\" | \"DELETE /internal/apm/service-group\" | \"GET /internal/apm/service-group/services\" | \"GET /internal/apm/service-group/counts\" | \"GET /internal/apm/suggestions\" | \"GET /internal/apm/traces/{traceId}\" | \"GET /internal/apm/traces\" | \"GET /internal/apm/traces/{traceId}/root_transaction\" | \"GET /internal/apm/transactions/{transactionId}\" | \"GET /internal/apm/traces/find\" | \"POST /internal/apm/traces/aggregated_critical_path\" | \"GET /internal/apm/traces/{traceId}/transactions/{transactionId}\" | \"GET /internal/apm/traces/{traceId}/spans/{spanId}\" | \"GET /internal/apm/services/{serviceName}/transactions/groups/main_statistics\" | \"GET /internal/apm/services/{serviceName}/transactions/groups/detailed_statistics\" | \"GET /internal/apm/services/{serviceName}/transactions/charts/latency\" | \"GET /internal/apm/services/{serviceName}/transactions/traces/samples\" | \"GET /internal/apm/services/{serviceName}/transaction/charts/breakdown\" | \"GET /internal/apm/services/{serviceName}/transactions/charts/error_rate\" | \"GET /internal/apm/services/{serviceName}/transactions/charts/coldstart_rate\" | \"GET /internal/apm/services/{serviceName}/transactions/charts/coldstart_rate_by_transaction_name\" | \"GET /internal/apm/rule_types/transaction_error_rate/chart_preview\" | \"GET /internal/apm/rule_types/transaction_duration/chart_preview\" | \"GET /internal/apm/rule_types/error_count/chart_preview\" | \"GET /api/apm/settings/agent-configuration\" | \"GET /api/apm/settings/agent-configuration/view\" | \"DELETE /api/apm/settings/agent-configuration\" | \"PUT /api/apm/settings/agent-configuration\" | \"POST /api/apm/settings/agent-configuration/search\" | \"GET /api/apm/settings/agent-configuration/environments\" | \"GET /api/apm/settings/agent-configuration/agent_name\" | \"GET /internal/apm/settings/anomaly-detection/jobs\" | \"POST /internal/apm/settings/anomaly-detection/jobs\" | \"GET /internal/apm/settings/anomaly-detection/environments\" | \"POST /internal/apm/settings/anomaly-detection/update_to_v3\" | \"GET /internal/apm/settings/apm-index-settings\" | \"GET /internal/apm/settings/apm-indices\" | \"POST /internal/apm/settings/apm-indices/save\" | \"GET /internal/apm/settings/custom_links/transaction\" | \"GET /internal/apm/settings/custom_links\" | \"POST /internal/apm/settings/custom_links\" | \"PUT /internal/apm/settings/custom_links/{id}\" | \"DELETE /internal/apm/settings/custom_links/{id}\" | \"GET /api/apm/sourcemaps\" | \"POST /api/apm/sourcemaps\" | \"DELETE /api/apm/sourcemaps/{id}\" | \"POST /internal/apm/sourcemaps/migrate_fleet_artifacts\" | \"GET /internal/apm/fleet/has_apm_policies\" | \"GET /internal/apm/fleet/agents\" | \"POST /api/apm/fleet/apm_server_schema\" | \"GET /internal/apm/fleet/apm_server_schema/unsupported\" | \"GET /internal/apm/fleet/migration_check\" | \"POST /internal/apm/fleet/cloud_apm_package_policy\" | \"GET /internal/apm/fleet/java_agent_versions\" | \"GET /internal/apm/dependencies/top_dependencies\" | \"GET /internal/apm/dependencies/upstream_services\" | \"GET /internal/apm/dependencies/metadata\" | \"GET /internal/apm/dependencies/charts/latency\" | \"GET /internal/apm/dependencies/charts/throughput\" | \"GET /internal/apm/dependencies/charts/error_rate\" | \"GET /internal/apm/dependencies/operations\" | \"GET /internal/apm/dependencies/charts/distribution\" | \"GET /internal/apm/dependencies/operations/spans\" | \"GET /internal/apm/correlations/field_candidates/transactions\" | \"GET /internal/apm/correlations/field_value_stats/transactions\" | \"POST /internal/apm/correlations/field_value_pairs/transactions\" | \"POST /internal/apm/correlations/significant_correlations/transactions\" | \"POST /internal/apm/correlations/p_values/transactions\" | \"GET /internal/apm/fallback_to_transactions\" | \"GET /internal/apm/has_data\" | \"GET /internal/apm/event_metadata/{processorEvent}/{id}\" | \"GET /internal/apm/agent_keys\" | \"GET /internal/apm/agent_keys/privileges\" | \"POST /internal/apm/api_key/invalidate\" | \"POST /api/apm/agent_keys\" | \"GET /internal/apm/storage_explorer\" | \"GET /internal/apm/services/{serviceName}/storage_details\" | \"GET /internal/apm/storage_chart\" | \"GET /internal/apm/storage_explorer/privileges\" | \"GET /internal/apm/storage_explorer_summary_stats\" | \"GET /internal/apm/storage_explorer/is_cross_cluster_search\" | \"GET /internal/apm/storage_explorer/get_services\" | \"GET /internal/apm/traces/{traceId}/span_links/{spanId}/parents\" | \"GET /internal/apm/traces/{traceId}/span_links/{spanId}/children\" | \"GET /internal/apm/services/{serviceName}/infrastructure_attributes\" | \"GET /internal/apm/debug-telemetry\" | \"GET /internal/apm/time_range_metadata\" | \"GET /internal/apm/settings/labs\" | \"GET /internal/apm/get_agents_per_service\" | \"GET /internal/apm/services/{serviceName}/agent_instances\" | \"GET /internal/apm/services/{serviceName}/mobile/filters\" | \"GET /internal/apm/mobile-services/{serviceName}/transactions/charts/sessions\" | \"GET /internal/apm/mobile-services/{serviceName}/transactions/charts/http_requests\" | \"GET /internal/apm/mobile-services/{serviceName}/stats\" | \"GET /internal/apm/mobile-services/{serviceName}/location/stats\" | \"GET /internal/apm/mobile-services/{serviceName}/terms\"" ], "path": "x-pack/plugins/apm/server/routes/apm_routes/get_global_apm_server_route_repository.ts", "deprecated": false, @@ -872,7 +872,69 @@ "label": "APMServerRouteRepository", "description": [], "signature": [ - "{ \"GET /internal/apm/mobile-services/{serviceName}/location/stats\": ", + "{ \"GET /internal/apm/mobile-services/{serviceName}/terms\": ", + { + "pluginId": "@kbn/server-route-repository", + "scope": "common", + "docId": "kibKbnServerRouteRepositoryPluginApi", + "section": "def-common.ServerRoute", + "text": "ServerRoute" + }, + "<\"GET /internal/apm/mobile-services/{serviceName}/terms\", ", + "TypeC", + "<{ path: ", + "TypeC", + "<{ serviceName: ", + "StringC", + "; }>; query: ", + "IntersectionC", + "<[", + "TypeC", + "<{ kuery: ", + "StringC", + "; }>, ", + "TypeC", + "<{ start: ", + "Type", + "; end: ", + "Type", + "; }>, ", + "TypeC", + "<{ environment: ", + "UnionC", + "<[", + "LiteralC", + "<\"ENVIRONMENT_NOT_DEFINED\">, ", + "LiteralC", + "<\"ENVIRONMENT_ALL\">, ", + "BrandC", + "<", + "StringC", + ", ", + { + "pluginId": "@kbn/io-ts-utils", + "scope": "common", + "docId": "kibKbnIoTsUtilsPluginApi", + "section": "def-common.NonEmptyStringBrand", + "text": "NonEmptyStringBrand" + }, + ">]>; }>, ", + "TypeC", + "<{ size: ", + "Type", + "; fieldName: ", + "StringC", + "; }>]>; }>, ", + { + "pluginId": "apm", + "scope": "server", + "docId": "kibApmPluginApi", + "section": "def-server.APMRouteHandlerResources", + "text": "APMRouteHandlerResources" + }, + ", { terms: { label: string; count: number; }[]; }, ", + "APMRouteCreateOptions", + ">; \"GET /internal/apm/mobile-services/{serviceName}/location/stats\": ", { "pluginId": "@kbn/server-route-repository", "scope": "common", diff --git a/api_docs/apm.mdx b/api_docs/apm.mdx index d98d3414ab950..3d679c1e77a61 100644 --- a/api_docs/apm.mdx +++ b/api_docs/apm.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/apm title: "apm" image: https://source.unsplash.com/400x175/?github description: API docs for the apm plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'apm'] --- import apmObj from './apm.devdocs.json'; diff --git a/api_docs/banners.mdx b/api_docs/banners.mdx index cb722d8877102..ea20772d893e5 100644 --- a/api_docs/banners.mdx +++ b/api_docs/banners.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/banners title: "banners" image: https://source.unsplash.com/400x175/?github description: API docs for the banners plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'banners'] --- import bannersObj from './banners.devdocs.json'; diff --git a/api_docs/bfetch.mdx b/api_docs/bfetch.mdx index 73ec3af038359..a6180cdb09994 100644 --- a/api_docs/bfetch.mdx +++ b/api_docs/bfetch.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/bfetch title: "bfetch" image: https://source.unsplash.com/400x175/?github description: API docs for the bfetch plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'bfetch'] --- import bfetchObj from './bfetch.devdocs.json'; diff --git a/api_docs/canvas.mdx b/api_docs/canvas.mdx index 0652a6a43194b..c5b23cb2b4369 100644 --- a/api_docs/canvas.mdx +++ b/api_docs/canvas.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/canvas title: "canvas" image: https://source.unsplash.com/400x175/?github description: API docs for the canvas plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'canvas'] --- import canvasObj from './canvas.devdocs.json'; diff --git a/api_docs/cases.mdx b/api_docs/cases.mdx index f0da51b8aa459..d0d9c3d89997a 100644 --- a/api_docs/cases.mdx +++ b/api_docs/cases.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/cases title: "cases" image: https://source.unsplash.com/400x175/?github description: API docs for the cases plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'cases'] --- import casesObj from './cases.devdocs.json'; diff --git a/api_docs/charts.mdx b/api_docs/charts.mdx index c1bbf9d1ff418..4a7b606909632 100644 --- a/api_docs/charts.mdx +++ b/api_docs/charts.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/charts title: "charts" image: https://source.unsplash.com/400x175/?github description: API docs for the charts plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'charts'] --- import chartsObj from './charts.devdocs.json'; diff --git a/api_docs/cloud.mdx b/api_docs/cloud.mdx index 119899af3dc25..7a1a8d535d5a9 100644 --- a/api_docs/cloud.mdx +++ b/api_docs/cloud.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/cloud title: "cloud" image: https://source.unsplash.com/400x175/?github description: API docs for the cloud plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'cloud'] --- import cloudObj from './cloud.devdocs.json'; diff --git a/api_docs/cloud_chat.mdx b/api_docs/cloud_chat.mdx index c68c43b7160bd..40ed9af1aebb3 100644 --- a/api_docs/cloud_chat.mdx +++ b/api_docs/cloud_chat.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/cloudChat title: "cloudChat" image: https://source.unsplash.com/400x175/?github description: API docs for the cloudChat plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'cloudChat'] --- import cloudChatObj from './cloud_chat.devdocs.json'; diff --git a/api_docs/cloud_data_migration.mdx b/api_docs/cloud_data_migration.mdx index 4bf8e1178483b..6ecd4d9dcefb8 100644 --- a/api_docs/cloud_data_migration.mdx +++ b/api_docs/cloud_data_migration.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/cloudDataMigration title: "cloudDataMigration" image: https://source.unsplash.com/400x175/?github description: API docs for the cloudDataMigration plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'cloudDataMigration'] --- import cloudDataMigrationObj from './cloud_data_migration.devdocs.json'; diff --git a/api_docs/cloud_defend.mdx b/api_docs/cloud_defend.mdx index d38772e250213..ffa49010fb809 100644 --- a/api_docs/cloud_defend.mdx +++ b/api_docs/cloud_defend.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/cloudDefend title: "cloudDefend" image: https://source.unsplash.com/400x175/?github description: API docs for the cloudDefend plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'cloudDefend'] --- import cloudDefendObj from './cloud_defend.devdocs.json'; diff --git a/api_docs/cloud_experiments.mdx b/api_docs/cloud_experiments.mdx index b07fc2e1873e7..f5ea2844dc50c 100644 --- a/api_docs/cloud_experiments.mdx +++ b/api_docs/cloud_experiments.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/cloudExperiments title: "cloudExperiments" image: https://source.unsplash.com/400x175/?github description: API docs for the cloudExperiments plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'cloudExperiments'] --- import cloudExperimentsObj from './cloud_experiments.devdocs.json'; diff --git a/api_docs/cloud_security_posture.mdx b/api_docs/cloud_security_posture.mdx index be5e9bacb0232..cea2a7fb7f484 100644 --- a/api_docs/cloud_security_posture.mdx +++ b/api_docs/cloud_security_posture.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/cloudSecurityPosture title: "cloudSecurityPosture" image: https://source.unsplash.com/400x175/?github description: API docs for the cloudSecurityPosture plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'cloudSecurityPosture'] --- import cloudSecurityPostureObj from './cloud_security_posture.devdocs.json'; diff --git a/api_docs/console.mdx b/api_docs/console.mdx index e0373208759f5..e009d3efb2de4 100644 --- a/api_docs/console.mdx +++ b/api_docs/console.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/console title: "console" image: https://source.unsplash.com/400x175/?github description: API docs for the console plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'console'] --- import consoleObj from './console.devdocs.json'; diff --git a/api_docs/content_management.mdx b/api_docs/content_management.mdx index 6b79f6be7bd90..722b4625a4320 100644 --- a/api_docs/content_management.mdx +++ b/api_docs/content_management.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/contentManagement title: "contentManagement" image: https://source.unsplash.com/400x175/?github description: API docs for the contentManagement plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'contentManagement'] --- import contentManagementObj from './content_management.devdocs.json'; diff --git a/api_docs/controls.mdx b/api_docs/controls.mdx index c7b0485b79787..642b19462026a 100644 --- a/api_docs/controls.mdx +++ b/api_docs/controls.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/controls title: "controls" image: https://source.unsplash.com/400x175/?github description: API docs for the controls plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'controls'] --- import controlsObj from './controls.devdocs.json'; diff --git a/api_docs/custom_integrations.mdx b/api_docs/custom_integrations.mdx index 2c2fb6da8ae4f..05ca008c417b7 100644 --- a/api_docs/custom_integrations.mdx +++ b/api_docs/custom_integrations.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/customIntegrations title: "customIntegrations" image: https://source.unsplash.com/400x175/?github description: API docs for the customIntegrations plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'customIntegrations'] --- import customIntegrationsObj from './custom_integrations.devdocs.json'; diff --git a/api_docs/dashboard.mdx b/api_docs/dashboard.mdx index e831ed7a0e709..8564485c9e694 100644 --- a/api_docs/dashboard.mdx +++ b/api_docs/dashboard.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/dashboard title: "dashboard" image: https://source.unsplash.com/400x175/?github description: API docs for the dashboard plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'dashboard'] --- import dashboardObj from './dashboard.devdocs.json'; diff --git a/api_docs/dashboard_enhanced.mdx b/api_docs/dashboard_enhanced.mdx index a66670abeac01..2907dceee7b86 100644 --- a/api_docs/dashboard_enhanced.mdx +++ b/api_docs/dashboard_enhanced.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/dashboardEnhanced title: "dashboardEnhanced" image: https://source.unsplash.com/400x175/?github description: API docs for the dashboardEnhanced plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'dashboardEnhanced'] --- import dashboardEnhancedObj from './dashboard_enhanced.devdocs.json'; diff --git a/api_docs/data.devdocs.json b/api_docs/data.devdocs.json index 5bbe9cf8796d9..7cf44eb94b574 100644 --- a/api_docs/data.devdocs.json +++ b/api_docs/data.devdocs.json @@ -7444,6 +7444,59 @@ "path": "src/plugins/data/common/search/aggs/types.ts", "deprecated": false, "trackAdoption": false + }, + { + "parentPluginId": "data", + "id": "def-public.AggFunctionsMapping.aggRate", + "type": "Object", + "tags": [], + "label": "aggRate", + "description": [], + "signature": [ + { + "pluginId": "expressions", + "scope": "common", + "docId": "kibExpressionsPluginApi", + "section": "def-common.ExpressionFunctionDefinition", + "text": "ExpressionFunctionDefinition" + }, + "<\"aggRate\", any, AggArgs, ", + { + "pluginId": "data", + "scope": "common", + "docId": "kibDataSearchPluginApi", + "section": "def-common.AggExpressionType", + "text": "AggExpressionType" + }, + ", ", + { + "pluginId": "expressions", + "scope": "common", + "docId": "kibExpressionsPluginApi", + "section": "def-common.ExecutionContext", + "text": "ExecutionContext" + }, + "<", + { + "pluginId": "inspector", + "scope": "common", + "docId": "kibInspectorPluginApi", + "section": "def-common.Adapters", + "text": "Adapters" + }, + ", ", + { + "pluginId": "@kbn/utility-types", + "scope": "common", + "docId": "kibKbnUtilityTypesPluginApi", + "section": "def-common.SerializableRecord", + "text": "SerializableRecord" + }, + ">>" + ], + "path": "src/plugins/data/common/search/aggs/types.ts", + "deprecated": false, + "trackAdoption": false } ], "initialIsOpen": false diff --git a/api_docs/data.mdx b/api_docs/data.mdx index e72e75c332d7b..5592b5a27ee89 100644 --- a/api_docs/data.mdx +++ b/api_docs/data.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/data title: "data" image: https://source.unsplash.com/400x175/?github description: API docs for the data plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'data'] --- import dataObj from './data.devdocs.json'; @@ -21,7 +21,7 @@ Contact [@elastic/kibana-visualizations](https://github.com/orgs/elastic/teams/k | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 3280 | 119 | 2584 | 27 | +| 3282 | 119 | 2586 | 27 | ## Client diff --git a/api_docs/data_query.mdx b/api_docs/data_query.mdx index 52fe81682b111..a59d0bbc695a8 100644 --- a/api_docs/data_query.mdx +++ b/api_docs/data_query.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/data-query title: "data.query" image: https://source.unsplash.com/400x175/?github description: API docs for the data.query plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'data.query'] --- import dataQueryObj from './data_query.devdocs.json'; @@ -21,7 +21,7 @@ Contact [@elastic/kibana-visualizations](https://github.com/orgs/elastic/teams/k | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 3280 | 119 | 2584 | 27 | +| 3282 | 119 | 2586 | 27 | ## Client diff --git a/api_docs/data_search.devdocs.json b/api_docs/data_search.devdocs.json index b18c013a92ade..e2a5d49653fa9 100644 --- a/api_docs/data_search.devdocs.json +++ b/api_docs/data_search.devdocs.json @@ -19865,6 +19865,59 @@ "path": "src/plugins/data/common/search/aggs/types.ts", "deprecated": false, "trackAdoption": false + }, + { + "parentPluginId": "data", + "id": "def-common.AggFunctionsMapping.aggRate", + "type": "Object", + "tags": [], + "label": "aggRate", + "description": [], + "signature": [ + { + "pluginId": "expressions", + "scope": "common", + "docId": "kibExpressionsPluginApi", + "section": "def-common.ExpressionFunctionDefinition", + "text": "ExpressionFunctionDefinition" + }, + "<\"aggRate\", any, AggArgs, ", + { + "pluginId": "data", + "scope": "common", + "docId": "kibDataSearchPluginApi", + "section": "def-common.AggExpressionType", + "text": "AggExpressionType" + }, + ", ", + { + "pluginId": "expressions", + "scope": "common", + "docId": "kibExpressionsPluginApi", + "section": "def-common.ExecutionContext", + "text": "ExecutionContext" + }, + "<", + { + "pluginId": "inspector", + "scope": "common", + "docId": "kibInspectorPluginApi", + "section": "def-common.Adapters", + "text": "Adapters" + }, + ", ", + { + "pluginId": "@kbn/utility-types", + "scope": "common", + "docId": "kibKbnUtilityTypesPluginApi", + "section": "def-common.SerializableRecord", + "text": "SerializableRecord" + }, + ">>" + ], + "path": "src/plugins/data/common/search/aggs/types.ts", + "deprecated": false, + "trackAdoption": false } ], "initialIsOpen": false diff --git a/api_docs/data_search.mdx b/api_docs/data_search.mdx index 97b4aa591a3e4..acbc4eaf24b00 100644 --- a/api_docs/data_search.mdx +++ b/api_docs/data_search.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/data-search title: "data.search" image: https://source.unsplash.com/400x175/?github description: API docs for the data.search plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'data.search'] --- import dataSearchObj from './data_search.devdocs.json'; @@ -21,7 +21,7 @@ Contact [@elastic/kibana-visualizations](https://github.com/orgs/elastic/teams/k | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 3280 | 119 | 2584 | 27 | +| 3282 | 119 | 2586 | 27 | ## Client diff --git a/api_docs/data_view_editor.mdx b/api_docs/data_view_editor.mdx index 6169273cfdce0..06f34fec2d4d6 100644 --- a/api_docs/data_view_editor.mdx +++ b/api_docs/data_view_editor.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/dataViewEditor title: "dataViewEditor" image: https://source.unsplash.com/400x175/?github description: API docs for the dataViewEditor plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'dataViewEditor'] --- import dataViewEditorObj from './data_view_editor.devdocs.json'; diff --git a/api_docs/data_view_field_editor.mdx b/api_docs/data_view_field_editor.mdx index 1029d4aa9ca4c..408de7dc65e91 100644 --- a/api_docs/data_view_field_editor.mdx +++ b/api_docs/data_view_field_editor.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/dataViewFieldEditor title: "dataViewFieldEditor" image: https://source.unsplash.com/400x175/?github description: API docs for the dataViewFieldEditor plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'dataViewFieldEditor'] --- import dataViewFieldEditorObj from './data_view_field_editor.devdocs.json'; diff --git a/api_docs/data_view_management.mdx b/api_docs/data_view_management.mdx index 87e596fd6f5ed..f71b6494df4a0 100644 --- a/api_docs/data_view_management.mdx +++ b/api_docs/data_view_management.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/dataViewManagement title: "dataViewManagement" image: https://source.unsplash.com/400x175/?github description: API docs for the dataViewManagement plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'dataViewManagement'] --- import dataViewManagementObj from './data_view_management.devdocs.json'; diff --git a/api_docs/data_views.mdx b/api_docs/data_views.mdx index 01569a94362bf..6618540818f41 100644 --- a/api_docs/data_views.mdx +++ b/api_docs/data_views.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/dataViews title: "dataViews" image: https://source.unsplash.com/400x175/?github description: API docs for the dataViews plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'dataViews'] --- import dataViewsObj from './data_views.devdocs.json'; diff --git a/api_docs/data_visualizer.mdx b/api_docs/data_visualizer.mdx index be5204fd01915..2844235eab4ce 100644 --- a/api_docs/data_visualizer.mdx +++ b/api_docs/data_visualizer.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/dataVisualizer title: "dataVisualizer" image: https://source.unsplash.com/400x175/?github description: API docs for the dataVisualizer plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'dataVisualizer'] --- import dataVisualizerObj from './data_visualizer.devdocs.json'; diff --git a/api_docs/deprecations_by_api.mdx b/api_docs/deprecations_by_api.mdx index 17e8abae492b9..d2aa4e5ef559a 100644 --- a/api_docs/deprecations_by_api.mdx +++ b/api_docs/deprecations_by_api.mdx @@ -7,7 +7,7 @@ id: kibDevDocsDeprecationsByApi slug: /kibana-dev-docs/api-meta/deprecated-api-list-by-api title: Deprecated API usage by API description: A list of deprecated APIs, which plugins are still referencing them, and when they need to be removed by. -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana'] --- diff --git a/api_docs/deprecations_by_plugin.mdx b/api_docs/deprecations_by_plugin.mdx index b3ec5e2c4c912..bc57e2dfed98b 100644 --- a/api_docs/deprecations_by_plugin.mdx +++ b/api_docs/deprecations_by_plugin.mdx @@ -7,7 +7,7 @@ id: kibDevDocsDeprecationsByPlugin slug: /kibana-dev-docs/api-meta/deprecated-api-list-by-plugin title: Deprecated API usage by plugin description: A list of deprecated APIs, which plugins are still referencing them, and when they need to be removed by. -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana'] --- @@ -1367,14 +1367,14 @@ migrates to using the Kibana Privilege model: https://github.com/elastic/kibana/ | | [find_object_by_title.ts](https://github.com/elastic/kibana/tree/main/src/plugins/visualizations/public/utils/saved_objects_utils/find_object_by_title.ts#:~:text=find), [saved_visualize_utils.ts](https://github.com/elastic/kibana/tree/main/src/plugins/visualizations/public/utils/saved_visualize_utils.ts#:~:text=find), [find_object_by_title.test.ts](https://github.com/elastic/kibana/tree/main/src/plugins/visualizations/public/utils/saved_objects_utils/find_object_by_title.test.ts#:~:text=find), [find_object_by_title.test.ts](https://github.com/elastic/kibana/tree/main/src/plugins/visualizations/public/utils/saved_objects_utils/find_object_by_title.test.ts#:~:text=find), [saved_visualize_utils.test.ts](https://github.com/elastic/kibana/tree/main/src/plugins/visualizations/public/utils/saved_visualize_utils.test.ts#:~:text=find), [saved_visualize_utils.test.ts](https://github.com/elastic/kibana/tree/main/src/plugins/visualizations/public/utils/saved_visualize_utils.test.ts#:~:text=find), [saved_visualize_utils.test.ts](https://github.com/elastic/kibana/tree/main/src/plugins/visualizations/public/utils/saved_visualize_utils.test.ts#:~:text=find), [saved_visualize_utils.test.ts](https://github.com/elastic/kibana/tree/main/src/plugins/visualizations/public/utils/saved_visualize_utils.test.ts#:~:text=find), [saved_visualize_utils.test.ts](https://github.com/elastic/kibana/tree/main/src/plugins/visualizations/public/utils/saved_visualize_utils.test.ts#:~:text=find), [saved_visualize_utils.test.ts](https://github.com/elastic/kibana/tree/main/src/plugins/visualizations/public/utils/saved_visualize_utils.test.ts#:~:text=find)+ 2 more | - | | | [update_basic_attributes.ts](https://github.com/elastic/kibana/tree/main/src/plugins/visualizations/public/utils/saved_objects_utils/update_basic_attributes.ts#:~:text=get) | - | | | [saved_visualize_utils.ts](https://github.com/elastic/kibana/tree/main/src/plugins/visualizations/public/utils/saved_visualize_utils.ts#:~:text=resolve), [saved_visualize_utils.test.ts](https://github.com/elastic/kibana/tree/main/src/plugins/visualizations/public/utils/saved_visualize_utils.test.ts#:~:text=resolve) | - | -| | [vis_type_alias_registry.ts](https://github.com/elastic/kibana/tree/main/src/plugins/visualizations/public/vis_types/vis_type_alias_registry.ts#:~:text=SimpleSavedObject), [vis_type_alias_registry.ts](https://github.com/elastic/kibana/tree/main/src/plugins/visualizations/public/vis_types/vis_type_alias_registry.ts#:~:text=SimpleSavedObject), [find_object_by_title.ts](https://github.com/elastic/kibana/tree/main/src/plugins/visualizations/public/utils/saved_objects_utils/find_object_by_title.ts#:~:text=SimpleSavedObject), [find_object_by_title.ts](https://github.com/elastic/kibana/tree/main/src/plugins/visualizations/public/utils/saved_objects_utils/find_object_by_title.ts#:~:text=SimpleSavedObject), [show_saved_object.ts](https://github.com/elastic/kibana/tree/main/src/plugins/visualizations/public/wizard/search_selection/show_saved_object.ts#:~:text=SimpleSavedObject), [show_saved_object.ts](https://github.com/elastic/kibana/tree/main/src/plugins/visualizations/public/wizard/search_selection/show_saved_object.ts#:~:text=SimpleSavedObject), [show_saved_object.ts](https://github.com/elastic/kibana/tree/main/src/plugins/visualizations/public/wizard/search_selection/show_saved_object.ts#:~:text=SimpleSavedObject), [find_object_by_title.test.ts](https://github.com/elastic/kibana/tree/main/src/plugins/visualizations/public/utils/saved_objects_utils/find_object_by_title.test.ts#:~:text=SimpleSavedObject), [find_object_by_title.test.ts](https://github.com/elastic/kibana/tree/main/src/plugins/visualizations/public/utils/saved_objects_utils/find_object_by_title.test.ts#:~:text=SimpleSavedObject), [show_saved_object.test.ts](https://github.com/elastic/kibana/tree/main/src/plugins/visualizations/public/wizard/search_selection/show_saved_object.test.ts#:~:text=SimpleSavedObject)+ 4 more | - | +| | [vis_type_alias_registry.ts](https://github.com/elastic/kibana/tree/main/src/plugins/visualizations/public/vis_types/vis_type_alias_registry.ts#:~:text=SimpleSavedObject), [vis_type_alias_registry.ts](https://github.com/elastic/kibana/tree/main/src/plugins/visualizations/public/vis_types/vis_type_alias_registry.ts#:~:text=SimpleSavedObject), [find_object_by_title.ts](https://github.com/elastic/kibana/tree/main/src/plugins/visualizations/public/utils/saved_objects_utils/find_object_by_title.ts#:~:text=SimpleSavedObject), [find_object_by_title.ts](https://github.com/elastic/kibana/tree/main/src/plugins/visualizations/public/utils/saved_objects_utils/find_object_by_title.ts#:~:text=SimpleSavedObject), [find_object_by_title.test.ts](https://github.com/elastic/kibana/tree/main/src/plugins/visualizations/public/utils/saved_objects_utils/find_object_by_title.test.ts#:~:text=SimpleSavedObject), [find_object_by_title.test.ts](https://github.com/elastic/kibana/tree/main/src/plugins/visualizations/public/utils/saved_objects_utils/find_object_by_title.test.ts#:~:text=SimpleSavedObject), [show_saved_object.test.ts](https://github.com/elastic/kibana/tree/main/src/plugins/visualizations/public/wizard/search_selection/show_saved_object.test.ts#:~:text=SimpleSavedObject), [show_saved_object.test.ts](https://github.com/elastic/kibana/tree/main/src/plugins/visualizations/public/wizard/search_selection/show_saved_object.test.ts#:~:text=SimpleSavedObject), [show_saved_object.test.ts](https://github.com/elastic/kibana/tree/main/src/plugins/visualizations/public/wizard/search_selection/show_saved_object.test.ts#:~:text=SimpleSavedObject), [show_saved_object.test.ts](https://github.com/elastic/kibana/tree/main/src/plugins/visualizations/public/wizard/search_selection/show_saved_object.test.ts#:~:text=SimpleSavedObject)+ 1 more | - | | | [save_with_confirmation.ts](https://github.com/elastic/kibana/tree/main/src/plugins/visualizations/public/utils/saved_objects_utils/save_with_confirmation.ts#:~:text=SavedObjectsCreateOptions), [save_with_confirmation.ts](https://github.com/elastic/kibana/tree/main/src/plugins/visualizations/public/utils/saved_objects_utils/save_with_confirmation.ts#:~:text=SavedObjectsCreateOptions), [save_with_confirmation.test.ts](https://github.com/elastic/kibana/tree/main/src/plugins/visualizations/public/utils/saved_objects_utils/save_with_confirmation.test.ts#:~:text=SavedObjectsCreateOptions), [save_with_confirmation.test.ts](https://github.com/elastic/kibana/tree/main/src/plugins/visualizations/public/utils/saved_objects_utils/save_with_confirmation.test.ts#:~:text=SavedObjectsCreateOptions), [save_with_confirmation.test.ts](https://github.com/elastic/kibana/tree/main/src/plugins/visualizations/public/utils/saved_objects_utils/save_with_confirmation.test.ts#:~:text=SavedObjectsCreateOptions) | - | | | [saved_visualize_utils.ts](https://github.com/elastic/kibana/tree/main/src/plugins/visualizations/public/utils/saved_visualize_utils.ts#:~:text=SavedObjectsFindOptions), [saved_visualize_utils.ts](https://github.com/elastic/kibana/tree/main/src/plugins/visualizations/public/utils/saved_visualize_utils.ts#:~:text=SavedObjectsFindOptions) | - | | | [types.ts](https://github.com/elastic/kibana/tree/main/src/plugins/visualizations/public/types.ts#:~:text=ResolvedSimpleSavedObject), [types.ts](https://github.com/elastic/kibana/tree/main/src/plugins/visualizations/public/types.ts#:~:text=ResolvedSimpleSavedObject), [types.ts](https://github.com/elastic/kibana/tree/main/src/plugins/visualizations/public/types.ts#:~:text=ResolvedSimpleSavedObject), [types.ts](https://github.com/elastic/kibana/tree/main/src/plugins/visualizations/public/types.ts#:~:text=ResolvedSimpleSavedObject) | - | | | [services.ts](https://github.com/elastic/kibana/tree/main/src/plugins/visualizations/public/services.ts#:~:text=SavedObjectsStart), [services.ts](https://github.com/elastic/kibana/tree/main/src/plugins/visualizations/public/services.ts#:~:text=SavedObjectsStart) | - | | | [find_object_by_title.test.ts](https://github.com/elastic/kibana/tree/main/src/plugins/visualizations/public/utils/saved_objects_utils/find_object_by_title.test.ts#:~:text=simpleSavedObjectMock), [find_object_by_title.test.ts](https://github.com/elastic/kibana/tree/main/src/plugins/visualizations/public/utils/saved_objects_utils/find_object_by_title.test.ts#:~:text=simpleSavedObjectMock) | - | | | [saved_visualization_references.ts](https://github.com/elastic/kibana/tree/main/src/plugins/visualizations/public/utils/saved_visualization_references/saved_visualization_references.ts#:~:text=SavedObjectAttribute), [saved_visualization_references.ts](https://github.com/elastic/kibana/tree/main/src/plugins/visualizations/public/utils/saved_visualization_references/saved_visualization_references.ts#:~:text=SavedObjectAttribute) | - | -| | [save_with_confirmation.ts](https://github.com/elastic/kibana/tree/main/src/plugins/visualizations/public/utils/saved_objects_utils/save_with_confirmation.ts#:~:text=SavedObjectAttributes), [save_with_confirmation.ts](https://github.com/elastic/kibana/tree/main/src/plugins/visualizations/public/utils/saved_objects_utils/save_with_confirmation.ts#:~:text=SavedObjectAttributes), [find_object_by_title.ts](https://github.com/elastic/kibana/tree/main/src/plugins/visualizations/public/utils/saved_objects_utils/find_object_by_title.ts#:~:text=SavedObjectAttributes), [find_object_by_title.ts](https://github.com/elastic/kibana/tree/main/src/plugins/visualizations/public/utils/saved_objects_utils/find_object_by_title.ts#:~:text=SavedObjectAttributes), [saved_visualize_utils.ts](https://github.com/elastic/kibana/tree/main/src/plugins/visualizations/public/utils/saved_visualize_utils.ts#:~:text=SavedObjectAttributes), [saved_visualize_utils.ts](https://github.com/elastic/kibana/tree/main/src/plugins/visualizations/public/utils/saved_visualize_utils.ts#:~:text=SavedObjectAttributes), [saved_visualize_utils.ts](https://github.com/elastic/kibana/tree/main/src/plugins/visualizations/public/utils/saved_visualize_utils.ts#:~:text=SavedObjectAttributes), [saved_visualize_utils.ts](https://github.com/elastic/kibana/tree/main/src/plugins/visualizations/public/utils/saved_visualize_utils.ts#:~:text=SavedObjectAttributes), [saved_visualize_utils.ts](https://github.com/elastic/kibana/tree/main/src/plugins/visualizations/public/utils/saved_visualize_utils.ts#:~:text=SavedObjectAttributes), [saved_visualize_utils.ts](https://github.com/elastic/kibana/tree/main/src/plugins/visualizations/public/utils/saved_visualize_utils.ts#:~:text=SavedObjectAttributes)+ 11 more | - | +| | [save_with_confirmation.ts](https://github.com/elastic/kibana/tree/main/src/plugins/visualizations/public/utils/saved_objects_utils/save_with_confirmation.ts#:~:text=SavedObjectAttributes), [save_with_confirmation.ts](https://github.com/elastic/kibana/tree/main/src/plugins/visualizations/public/utils/saved_objects_utils/save_with_confirmation.ts#:~:text=SavedObjectAttributes), [find_object_by_title.ts](https://github.com/elastic/kibana/tree/main/src/plugins/visualizations/public/utils/saved_objects_utils/find_object_by_title.ts#:~:text=SavedObjectAttributes), [find_object_by_title.ts](https://github.com/elastic/kibana/tree/main/src/plugins/visualizations/public/utils/saved_objects_utils/find_object_by_title.ts#:~:text=SavedObjectAttributes), [saved_visualize_utils.ts](https://github.com/elastic/kibana/tree/main/src/plugins/visualizations/public/utils/saved_visualize_utils.ts#:~:text=SavedObjectAttributes), [saved_visualize_utils.ts](https://github.com/elastic/kibana/tree/main/src/plugins/visualizations/public/utils/saved_visualize_utils.ts#:~:text=SavedObjectAttributes), [saved_visualize_utils.ts](https://github.com/elastic/kibana/tree/main/src/plugins/visualizations/public/utils/saved_visualize_utils.ts#:~:text=SavedObjectAttributes), [saved_visualize_utils.ts](https://github.com/elastic/kibana/tree/main/src/plugins/visualizations/public/utils/saved_visualize_utils.ts#:~:text=SavedObjectAttributes), [saved_visualize_utils.ts](https://github.com/elastic/kibana/tree/main/src/plugins/visualizations/public/utils/saved_visualize_utils.ts#:~:text=SavedObjectAttributes), [saved_visualize_utils.ts](https://github.com/elastic/kibana/tree/main/src/plugins/visualizations/public/utils/saved_visualize_utils.ts#:~:text=SavedObjectAttributes)+ 9 more | - | | | [saved_visualization_references.ts](https://github.com/elastic/kibana/tree/main/src/plugins/visualizations/public/utils/saved_visualization_references/saved_visualization_references.ts#:~:text=SavedObjectReference), [saved_visualization_references.ts](https://github.com/elastic/kibana/tree/main/src/plugins/visualizations/public/utils/saved_visualization_references/saved_visualization_references.ts#:~:text=SavedObjectReference), [saved_visualization_references.ts](https://github.com/elastic/kibana/tree/main/src/plugins/visualizations/public/utils/saved_visualization_references/saved_visualization_references.ts#:~:text=SavedObjectReference), [saved_visualize_utils.ts](https://github.com/elastic/kibana/tree/main/src/plugins/visualizations/public/utils/saved_visualize_utils.ts#:~:text=SavedObjectReference), [saved_visualize_utils.ts](https://github.com/elastic/kibana/tree/main/src/plugins/visualizations/public/utils/saved_visualize_utils.ts#:~:text=SavedObjectReference), [saved_visualize_utils.ts](https://github.com/elastic/kibana/tree/main/src/plugins/visualizations/public/utils/saved_visualize_utils.ts#:~:text=SavedObjectReference), [saved_visualize_utils.ts](https://github.com/elastic/kibana/tree/main/src/plugins/visualizations/public/utils/saved_visualize_utils.ts#:~:text=SavedObjectReference), [visualize_embeddable_factory.tsx](https://github.com/elastic/kibana/tree/main/src/plugins/visualizations/public/embeddable/visualize_embeddable_factory.tsx#:~:text=SavedObjectReference), [visualize_embeddable_factory.tsx](https://github.com/elastic/kibana/tree/main/src/plugins/visualizations/public/embeddable/visualize_embeddable_factory.tsx#:~:text=SavedObjectReference), [controls_references.ts](https://github.com/elastic/kibana/tree/main/src/plugins/visualizations/public/utils/saved_visualization_references/controls_references.ts#:~:text=SavedObjectReference)+ 5 more | - | | | [visualization.ts](https://github.com/elastic/kibana/tree/main/src/plugins/visualizations/server/saved_objects/visualization.ts#:~:text=convertToMultiNamespaceTypeVersion) | - | diff --git a/api_docs/deprecations_by_team.mdx b/api_docs/deprecations_by_team.mdx index 6371abc58f782..e9e8c30fa57d4 100644 --- a/api_docs/deprecations_by_team.mdx +++ b/api_docs/deprecations_by_team.mdx @@ -7,7 +7,7 @@ id: kibDevDocsDeprecationsDueByTeam slug: /kibana-dev-docs/api-meta/deprecations-due-by-team title: Deprecated APIs due to be removed, by team description: Lists the teams that are referencing deprecated APIs with a remove by date. -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana'] --- diff --git a/api_docs/dev_tools.mdx b/api_docs/dev_tools.mdx index edf9d441d329b..3ed900e268c8c 100644 --- a/api_docs/dev_tools.mdx +++ b/api_docs/dev_tools.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/devTools title: "devTools" image: https://source.unsplash.com/400x175/?github description: API docs for the devTools plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'devTools'] --- import devToolsObj from './dev_tools.devdocs.json'; diff --git a/api_docs/discover.mdx b/api_docs/discover.mdx index 86e4b08e8e9e9..c7a29c5cbeb50 100644 --- a/api_docs/discover.mdx +++ b/api_docs/discover.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/discover title: "discover" image: https://source.unsplash.com/400x175/?github description: API docs for the discover plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'discover'] --- import discoverObj from './discover.devdocs.json'; diff --git a/api_docs/discover_enhanced.mdx b/api_docs/discover_enhanced.mdx index ddb678bc9016b..665684d77f3fe 100644 --- a/api_docs/discover_enhanced.mdx +++ b/api_docs/discover_enhanced.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/discoverEnhanced title: "discoverEnhanced" image: https://source.unsplash.com/400x175/?github description: API docs for the discoverEnhanced plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'discoverEnhanced'] --- import discoverEnhancedObj from './discover_enhanced.devdocs.json'; diff --git a/api_docs/ecs_data_quality_dashboard.mdx b/api_docs/ecs_data_quality_dashboard.mdx index b03713043b01f..70ad15de40277 100644 --- a/api_docs/ecs_data_quality_dashboard.mdx +++ b/api_docs/ecs_data_quality_dashboard.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/ecsDataQualityDashboard title: "ecsDataQualityDashboard" image: https://source.unsplash.com/400x175/?github description: API docs for the ecsDataQualityDashboard plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'ecsDataQualityDashboard'] --- import ecsDataQualityDashboardObj from './ecs_data_quality_dashboard.devdocs.json'; diff --git a/api_docs/embeddable.mdx b/api_docs/embeddable.mdx index 5a9006e9b0c20..a2f6b3876a311 100644 --- a/api_docs/embeddable.mdx +++ b/api_docs/embeddable.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/embeddable title: "embeddable" image: https://source.unsplash.com/400x175/?github description: API docs for the embeddable plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'embeddable'] --- import embeddableObj from './embeddable.devdocs.json'; diff --git a/api_docs/embeddable_enhanced.mdx b/api_docs/embeddable_enhanced.mdx index 37e553c9bbb38..23b2ed31c361b 100644 --- a/api_docs/embeddable_enhanced.mdx +++ b/api_docs/embeddable_enhanced.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/embeddableEnhanced title: "embeddableEnhanced" image: https://source.unsplash.com/400x175/?github description: API docs for the embeddableEnhanced plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'embeddableEnhanced'] --- import embeddableEnhancedObj from './embeddable_enhanced.devdocs.json'; diff --git a/api_docs/encrypted_saved_objects.mdx b/api_docs/encrypted_saved_objects.mdx index b6bb3858e4d8e..2fe54cdf8739f 100644 --- a/api_docs/encrypted_saved_objects.mdx +++ b/api_docs/encrypted_saved_objects.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/encryptedSavedObjects title: "encryptedSavedObjects" image: https://source.unsplash.com/400x175/?github description: API docs for the encryptedSavedObjects plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'encryptedSavedObjects'] --- import encryptedSavedObjectsObj from './encrypted_saved_objects.devdocs.json'; diff --git a/api_docs/enterprise_search.mdx b/api_docs/enterprise_search.mdx index 0fa4de935dcdb..71f4a7875d21f 100644 --- a/api_docs/enterprise_search.mdx +++ b/api_docs/enterprise_search.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/enterpriseSearch title: "enterpriseSearch" image: https://source.unsplash.com/400x175/?github description: API docs for the enterpriseSearch plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'enterpriseSearch'] --- import enterpriseSearchObj from './enterprise_search.devdocs.json'; diff --git a/api_docs/es_ui_shared.mdx b/api_docs/es_ui_shared.mdx index eb50d05763947..74f69cdeb5eca 100644 --- a/api_docs/es_ui_shared.mdx +++ b/api_docs/es_ui_shared.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/esUiShared title: "esUiShared" image: https://source.unsplash.com/400x175/?github description: API docs for the esUiShared plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'esUiShared'] --- import esUiSharedObj from './es_ui_shared.devdocs.json'; diff --git a/api_docs/event_annotation.mdx b/api_docs/event_annotation.mdx index a5e4e1434bb04..3cb4fc356e98c 100644 --- a/api_docs/event_annotation.mdx +++ b/api_docs/event_annotation.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/eventAnnotation title: "eventAnnotation" image: https://source.unsplash.com/400x175/?github description: API docs for the eventAnnotation plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'eventAnnotation'] --- import eventAnnotationObj from './event_annotation.devdocs.json'; diff --git a/api_docs/event_log.devdocs.json b/api_docs/event_log.devdocs.json index ef90f20d24aa0..e0bdaa48bd824 100644 --- a/api_docs/event_log.devdocs.json +++ b/api_docs/event_log.devdocs.json @@ -1514,7 +1514,7 @@ "label": "data", "description": [], "signature": [ - "(Readonly<{ log?: Readonly<{ logger?: string | undefined; level?: string | undefined; } & {}> | undefined; error?: Readonly<{ type?: string | undefined; id?: string | undefined; message?: string | undefined; code?: string | undefined; stack_trace?: string | undefined; } & {}> | undefined; '@timestamp'?: string | undefined; message?: string | undefined; tags?: string[] | undefined; rule?: Readonly<{ id?: string | undefined; name?: string | undefined; description?: string | undefined; category?: string | undefined; version?: string | undefined; uuid?: string | undefined; license?: string | undefined; reference?: string | undefined; author?: string[] | undefined; ruleset?: string | undefined; } & {}> | undefined; kibana?: Readonly<{ action?: Readonly<{ id?: string | undefined; name?: string | undefined; execution?: Readonly<{ uuid?: string | undefined; } & {}> | undefined; } & {}> | undefined; alerting?: Readonly<{ outcome?: string | undefined; summary?: Readonly<{ recovered?: Readonly<{ count?: string | number | undefined; } & {}> | undefined; new?: Readonly<{ count?: string | number | undefined; } & {}> | undefined; ongoing?: Readonly<{ count?: string | number | undefined; } & {}> | undefined; } & {}> | undefined; status?: string | undefined; instance_id?: string | undefined; action_group_id?: string | undefined; action_subgroup?: string | undefined; } & {}> | undefined; alert?: Readonly<{ rule?: Readonly<{ consumer?: string | undefined; execution?: Readonly<{ uuid?: string | undefined; metrics?: Readonly<{ number_of_triggered_actions?: string | number | undefined; number_of_generated_actions?: string | number | undefined; alert_counts?: Readonly<{ recovered?: string | number | undefined; active?: string | number | undefined; new?: string | number | undefined; } & {}> | undefined; number_of_searches?: string | number | undefined; total_indexing_duration_ms?: string | number | undefined; es_search_duration_ms?: string | number | undefined; total_search_duration_ms?: string | number | undefined; execution_gap_duration_s?: string | number | undefined; rule_type_run_duration_ms?: string | number | undefined; process_alerts_duration_ms?: string | number | undefined; trigger_actions_duration_ms?: string | number | undefined; process_rule_duration_ms?: string | number | undefined; claim_to_start_duration_ms?: string | number | undefined; prepare_rule_duration_ms?: string | number | undefined; total_run_duration_ms?: string | number | undefined; total_enrichment_duration_ms?: string | number | undefined; } & {}> | undefined; status?: string | undefined; status_order?: string | number | undefined; } & {}> | undefined; rule_type_id?: string | undefined; } & {}> | undefined; flapping?: boolean | undefined; } & {}> | undefined; version?: string | undefined; server_uuid?: string | undefined; task?: Readonly<{ id?: string | undefined; schedule_delay?: string | number | undefined; scheduled?: string | undefined; } & {}> | undefined; saved_objects?: Readonly<{ type?: string | undefined; id?: string | undefined; rel?: string | undefined; namespace?: string | undefined; type_id?: string | undefined; space_agnostic?: boolean | undefined; } & {}>[] | undefined; space_ids?: string[] | undefined; } & {}> | undefined; event?: Readonly<{ type?: string[] | undefined; reason?: string | undefined; action?: string | undefined; id?: string | undefined; start?: string | undefined; end?: string | undefined; category?: string[] | undefined; outcome?: string | undefined; code?: string | undefined; url?: string | undefined; created?: string | undefined; severity?: string | number | undefined; duration?: string | number | undefined; original?: string | undefined; dataset?: string | undefined; hash?: string | undefined; ingested?: string | undefined; kind?: string | undefined; module?: string | undefined; provider?: string | undefined; reference?: string | undefined; risk_score?: number | undefined; risk_score_norm?: number | undefined; sequence?: string | number | undefined; timezone?: string | undefined; } & {}> | undefined; ecs?: Readonly<{ version?: string | undefined; } & {}> | undefined; user?: Readonly<{ name?: string | undefined; } & {}> | undefined; } & {}> | undefined)[]" + "(Readonly<{ log?: Readonly<{ logger?: string | undefined; level?: string | undefined; } & {}> | undefined; error?: Readonly<{ type?: string | undefined; id?: string | undefined; message?: string | undefined; code?: string | undefined; stack_trace?: string | undefined; } & {}> | undefined; '@timestamp'?: string | undefined; message?: string | undefined; tags?: string[] | undefined; rule?: Readonly<{ id?: string | undefined; name?: string | undefined; description?: string | undefined; category?: string | undefined; version?: string | undefined; uuid?: string | undefined; license?: string | undefined; reference?: string | undefined; author?: string[] | undefined; ruleset?: string | undefined; } & {}> | undefined; kibana?: Readonly<{ action?: Readonly<{ id?: string | undefined; name?: string | undefined; execution?: Readonly<{ source?: string | undefined; uuid?: string | undefined; } & {}> | undefined; } & {}> | undefined; alerting?: Readonly<{ outcome?: string | undefined; summary?: Readonly<{ recovered?: Readonly<{ count?: string | number | undefined; } & {}> | undefined; new?: Readonly<{ count?: string | number | undefined; } & {}> | undefined; ongoing?: Readonly<{ count?: string | number | undefined; } & {}> | undefined; } & {}> | undefined; status?: string | undefined; instance_id?: string | undefined; action_group_id?: string | undefined; action_subgroup?: string | undefined; } & {}> | undefined; alert?: Readonly<{ rule?: Readonly<{ consumer?: string | undefined; execution?: Readonly<{ uuid?: string | undefined; metrics?: Readonly<{ number_of_triggered_actions?: string | number | undefined; number_of_generated_actions?: string | number | undefined; alert_counts?: Readonly<{ recovered?: string | number | undefined; active?: string | number | undefined; new?: string | number | undefined; } & {}> | undefined; number_of_searches?: string | number | undefined; total_indexing_duration_ms?: string | number | undefined; es_search_duration_ms?: string | number | undefined; total_search_duration_ms?: string | number | undefined; execution_gap_duration_s?: string | number | undefined; rule_type_run_duration_ms?: string | number | undefined; process_alerts_duration_ms?: string | number | undefined; trigger_actions_duration_ms?: string | number | undefined; process_rule_duration_ms?: string | number | undefined; claim_to_start_duration_ms?: string | number | undefined; prepare_rule_duration_ms?: string | number | undefined; total_run_duration_ms?: string | number | undefined; total_enrichment_duration_ms?: string | number | undefined; } & {}> | undefined; status?: string | undefined; status_order?: string | number | undefined; } & {}> | undefined; rule_type_id?: string | undefined; } & {}> | undefined; flapping?: boolean | undefined; } & {}> | undefined; version?: string | undefined; server_uuid?: string | undefined; task?: Readonly<{ id?: string | undefined; schedule_delay?: string | number | undefined; scheduled?: string | undefined; } & {}> | undefined; saved_objects?: Readonly<{ type?: string | undefined; id?: string | undefined; rel?: string | undefined; namespace?: string | undefined; type_id?: string | undefined; space_agnostic?: boolean | undefined; } & {}>[] | undefined; space_ids?: string[] | undefined; } & {}> | undefined; event?: Readonly<{ type?: string[] | undefined; reason?: string | undefined; action?: string | undefined; id?: string | undefined; start?: string | undefined; end?: string | undefined; category?: string[] | undefined; outcome?: string | undefined; code?: string | undefined; url?: string | undefined; created?: string | undefined; severity?: string | number | undefined; duration?: string | number | undefined; original?: string | undefined; dataset?: string | undefined; hash?: string | undefined; ingested?: string | undefined; kind?: string | undefined; module?: string | undefined; provider?: string | undefined; reference?: string | undefined; risk_score?: number | undefined; risk_score_norm?: number | undefined; sequence?: string | number | undefined; timezone?: string | undefined; } & {}> | undefined; ecs?: Readonly<{ version?: string | undefined; } & {}> | undefined; user?: Readonly<{ name?: string | undefined; } & {}> | undefined; } & {}> | undefined)[]" ], "path": "x-pack/plugins/event_log/server/es/cluster_client_adapter.ts", "deprecated": false, @@ -1534,7 +1534,7 @@ "label": "IEvent", "description": [], "signature": [ - "DeepPartial | undefined; error?: Readonly<{ type?: string | undefined; id?: string | undefined; message?: string | undefined; code?: string | undefined; stack_trace?: string | undefined; } & {}> | undefined; '@timestamp'?: string | undefined; message?: string | undefined; tags?: string[] | undefined; rule?: Readonly<{ id?: string | undefined; name?: string | undefined; description?: string | undefined; category?: string | undefined; version?: string | undefined; uuid?: string | undefined; license?: string | undefined; reference?: string | undefined; author?: string[] | undefined; ruleset?: string | undefined; } & {}> | undefined; kibana?: Readonly<{ action?: Readonly<{ id?: string | undefined; name?: string | undefined; execution?: Readonly<{ uuid?: string | undefined; } & {}> | undefined; } & {}> | undefined; alerting?: Readonly<{ outcome?: string | undefined; summary?: Readonly<{ recovered?: Readonly<{ count?: string | number | undefined; } & {}> | undefined; new?: Readonly<{ count?: string | number | undefined; } & {}> | undefined; ongoing?: Readonly<{ count?: string | number | undefined; } & {}> | undefined; } & {}> | undefined; status?: string | undefined; instance_id?: string | undefined; action_group_id?: string | undefined; action_subgroup?: string | undefined; } & {}> | undefined; alert?: Readonly<{ rule?: Readonly<{ consumer?: string | undefined; execution?: Readonly<{ uuid?: string | undefined; metrics?: Readonly<{ number_of_triggered_actions?: string | number | undefined; number_of_generated_actions?: string | number | undefined; alert_counts?: Readonly<{ recovered?: string | number | undefined; active?: string | number | undefined; new?: string | number | undefined; } & {}> | undefined; number_of_searches?: string | number | undefined; total_indexing_duration_ms?: string | number | undefined; es_search_duration_ms?: string | number | undefined; total_search_duration_ms?: string | number | undefined; execution_gap_duration_s?: string | number | undefined; rule_type_run_duration_ms?: string | number | undefined; process_alerts_duration_ms?: string | number | undefined; trigger_actions_duration_ms?: string | number | undefined; process_rule_duration_ms?: string | number | undefined; claim_to_start_duration_ms?: string | number | undefined; prepare_rule_duration_ms?: string | number | undefined; total_run_duration_ms?: string | number | undefined; total_enrichment_duration_ms?: string | number | undefined; } & {}> | undefined; status?: string | undefined; status_order?: string | number | undefined; } & {}> | undefined; rule_type_id?: string | undefined; } & {}> | undefined; flapping?: boolean | undefined; } & {}> | undefined; version?: string | undefined; server_uuid?: string | undefined; task?: Readonly<{ id?: string | undefined; schedule_delay?: string | number | undefined; scheduled?: string | undefined; } & {}> | undefined; saved_objects?: Readonly<{ type?: string | undefined; id?: string | undefined; rel?: string | undefined; namespace?: string | undefined; type_id?: string | undefined; space_agnostic?: boolean | undefined; } & {}>[] | undefined; space_ids?: string[] | undefined; } & {}> | undefined; event?: Readonly<{ type?: string[] | undefined; reason?: string | undefined; action?: string | undefined; id?: string | undefined; start?: string | undefined; end?: string | undefined; category?: string[] | undefined; outcome?: string | undefined; code?: string | undefined; url?: string | undefined; created?: string | undefined; severity?: string | number | undefined; duration?: string | number | undefined; original?: string | undefined; dataset?: string | undefined; hash?: string | undefined; ingested?: string | undefined; kind?: string | undefined; module?: string | undefined; provider?: string | undefined; reference?: string | undefined; risk_score?: number | undefined; risk_score_norm?: number | undefined; sequence?: string | number | undefined; timezone?: string | undefined; } & {}> | undefined; ecs?: Readonly<{ version?: string | undefined; } & {}> | undefined; user?: Readonly<{ name?: string | undefined; } & {}> | undefined; } & {}>>> | undefined" + "DeepPartial | undefined; error?: Readonly<{ type?: string | undefined; id?: string | undefined; message?: string | undefined; code?: string | undefined; stack_trace?: string | undefined; } & {}> | undefined; '@timestamp'?: string | undefined; message?: string | undefined; tags?: string[] | undefined; rule?: Readonly<{ id?: string | undefined; name?: string | undefined; description?: string | undefined; category?: string | undefined; version?: string | undefined; uuid?: string | undefined; license?: string | undefined; reference?: string | undefined; author?: string[] | undefined; ruleset?: string | undefined; } & {}> | undefined; kibana?: Readonly<{ action?: Readonly<{ id?: string | undefined; name?: string | undefined; execution?: Readonly<{ source?: string | undefined; uuid?: string | undefined; } & {}> | undefined; } & {}> | undefined; alerting?: Readonly<{ outcome?: string | undefined; summary?: Readonly<{ recovered?: Readonly<{ count?: string | number | undefined; } & {}> | undefined; new?: Readonly<{ count?: string | number | undefined; } & {}> | undefined; ongoing?: Readonly<{ count?: string | number | undefined; } & {}> | undefined; } & {}> | undefined; status?: string | undefined; instance_id?: string | undefined; action_group_id?: string | undefined; action_subgroup?: string | undefined; } & {}> | undefined; alert?: Readonly<{ rule?: Readonly<{ consumer?: string | undefined; execution?: Readonly<{ uuid?: string | undefined; metrics?: Readonly<{ number_of_triggered_actions?: string | number | undefined; number_of_generated_actions?: string | number | undefined; alert_counts?: Readonly<{ recovered?: string | number | undefined; active?: string | number | undefined; new?: string | number | undefined; } & {}> | undefined; number_of_searches?: string | number | undefined; total_indexing_duration_ms?: string | number | undefined; es_search_duration_ms?: string | number | undefined; total_search_duration_ms?: string | number | undefined; execution_gap_duration_s?: string | number | undefined; rule_type_run_duration_ms?: string | number | undefined; process_alerts_duration_ms?: string | number | undefined; trigger_actions_duration_ms?: string | number | undefined; process_rule_duration_ms?: string | number | undefined; claim_to_start_duration_ms?: string | number | undefined; prepare_rule_duration_ms?: string | number | undefined; total_run_duration_ms?: string | number | undefined; total_enrichment_duration_ms?: string | number | undefined; } & {}> | undefined; status?: string | undefined; status_order?: string | number | undefined; } & {}> | undefined; rule_type_id?: string | undefined; } & {}> | undefined; flapping?: boolean | undefined; } & {}> | undefined; version?: string | undefined; server_uuid?: string | undefined; task?: Readonly<{ id?: string | undefined; schedule_delay?: string | number | undefined; scheduled?: string | undefined; } & {}> | undefined; saved_objects?: Readonly<{ type?: string | undefined; id?: string | undefined; rel?: string | undefined; namespace?: string | undefined; type_id?: string | undefined; space_agnostic?: boolean | undefined; } & {}>[] | undefined; space_ids?: string[] | undefined; } & {}> | undefined; event?: Readonly<{ type?: string[] | undefined; reason?: string | undefined; action?: string | undefined; id?: string | undefined; start?: string | undefined; end?: string | undefined; category?: string[] | undefined; outcome?: string | undefined; code?: string | undefined; url?: string | undefined; created?: string | undefined; severity?: string | number | undefined; duration?: string | number | undefined; original?: string | undefined; dataset?: string | undefined; hash?: string | undefined; ingested?: string | undefined; kind?: string | undefined; module?: string | undefined; provider?: string | undefined; reference?: string | undefined; risk_score?: number | undefined; risk_score_norm?: number | undefined; sequence?: string | number | undefined; timezone?: string | undefined; } & {}> | undefined; ecs?: Readonly<{ version?: string | undefined; } & {}> | undefined; user?: Readonly<{ name?: string | undefined; } & {}> | undefined; } & {}>>> | undefined" ], "path": "x-pack/plugins/event_log/generated/schemas.ts", "deprecated": false, @@ -1549,7 +1549,7 @@ "label": "IValidatedEvent", "description": [], "signature": [ - "Readonly<{ log?: Readonly<{ logger?: string | undefined; level?: string | undefined; } & {}> | undefined; error?: Readonly<{ type?: string | undefined; id?: string | undefined; message?: string | undefined; code?: string | undefined; stack_trace?: string | undefined; } & {}> | undefined; '@timestamp'?: string | undefined; message?: string | undefined; tags?: string[] | undefined; rule?: Readonly<{ id?: string | undefined; name?: string | undefined; description?: string | undefined; category?: string | undefined; version?: string | undefined; uuid?: string | undefined; license?: string | undefined; reference?: string | undefined; author?: string[] | undefined; ruleset?: string | undefined; } & {}> | undefined; kibana?: Readonly<{ action?: Readonly<{ id?: string | undefined; name?: string | undefined; execution?: Readonly<{ uuid?: string | undefined; } & {}> | undefined; } & {}> | undefined; alerting?: Readonly<{ outcome?: string | undefined; summary?: Readonly<{ recovered?: Readonly<{ count?: string | number | undefined; } & {}> | undefined; new?: Readonly<{ count?: string | number | undefined; } & {}> | undefined; ongoing?: Readonly<{ count?: string | number | undefined; } & {}> | undefined; } & {}> | undefined; status?: string | undefined; instance_id?: string | undefined; action_group_id?: string | undefined; action_subgroup?: string | undefined; } & {}> | undefined; alert?: Readonly<{ rule?: Readonly<{ consumer?: string | undefined; execution?: Readonly<{ uuid?: string | undefined; metrics?: Readonly<{ number_of_triggered_actions?: string | number | undefined; number_of_generated_actions?: string | number | undefined; alert_counts?: Readonly<{ recovered?: string | number | undefined; active?: string | number | undefined; new?: string | number | undefined; } & {}> | undefined; number_of_searches?: string | number | undefined; total_indexing_duration_ms?: string | number | undefined; es_search_duration_ms?: string | number | undefined; total_search_duration_ms?: string | number | undefined; execution_gap_duration_s?: string | number | undefined; rule_type_run_duration_ms?: string | number | undefined; process_alerts_duration_ms?: string | number | undefined; trigger_actions_duration_ms?: string | number | undefined; process_rule_duration_ms?: string | number | undefined; claim_to_start_duration_ms?: string | number | undefined; prepare_rule_duration_ms?: string | number | undefined; total_run_duration_ms?: string | number | undefined; total_enrichment_duration_ms?: string | number | undefined; } & {}> | undefined; status?: string | undefined; status_order?: string | number | undefined; } & {}> | undefined; rule_type_id?: string | undefined; } & {}> | undefined; flapping?: boolean | undefined; } & {}> | undefined; version?: string | undefined; server_uuid?: string | undefined; task?: Readonly<{ id?: string | undefined; schedule_delay?: string | number | undefined; scheduled?: string | undefined; } & {}> | undefined; saved_objects?: Readonly<{ type?: string | undefined; id?: string | undefined; rel?: string | undefined; namespace?: string | undefined; type_id?: string | undefined; space_agnostic?: boolean | undefined; } & {}>[] | undefined; space_ids?: string[] | undefined; } & {}> | undefined; event?: Readonly<{ type?: string[] | undefined; reason?: string | undefined; action?: string | undefined; id?: string | undefined; start?: string | undefined; end?: string | undefined; category?: string[] | undefined; outcome?: string | undefined; code?: string | undefined; url?: string | undefined; created?: string | undefined; severity?: string | number | undefined; duration?: string | number | undefined; original?: string | undefined; dataset?: string | undefined; hash?: string | undefined; ingested?: string | undefined; kind?: string | undefined; module?: string | undefined; provider?: string | undefined; reference?: string | undefined; risk_score?: number | undefined; risk_score_norm?: number | undefined; sequence?: string | number | undefined; timezone?: string | undefined; } & {}> | undefined; ecs?: Readonly<{ version?: string | undefined; } & {}> | undefined; user?: Readonly<{ name?: string | undefined; } & {}> | undefined; } & {}> | undefined" + "Readonly<{ log?: Readonly<{ logger?: string | undefined; level?: string | undefined; } & {}> | undefined; error?: Readonly<{ type?: string | undefined; id?: string | undefined; message?: string | undefined; code?: string | undefined; stack_trace?: string | undefined; } & {}> | undefined; '@timestamp'?: string | undefined; message?: string | undefined; tags?: string[] | undefined; rule?: Readonly<{ id?: string | undefined; name?: string | undefined; description?: string | undefined; category?: string | undefined; version?: string | undefined; uuid?: string | undefined; license?: string | undefined; reference?: string | undefined; author?: string[] | undefined; ruleset?: string | undefined; } & {}> | undefined; kibana?: Readonly<{ action?: Readonly<{ id?: string | undefined; name?: string | undefined; execution?: Readonly<{ source?: string | undefined; uuid?: string | undefined; } & {}> | undefined; } & {}> | undefined; alerting?: Readonly<{ outcome?: string | undefined; summary?: Readonly<{ recovered?: Readonly<{ count?: string | number | undefined; } & {}> | undefined; new?: Readonly<{ count?: string | number | undefined; } & {}> | undefined; ongoing?: Readonly<{ count?: string | number | undefined; } & {}> | undefined; } & {}> | undefined; status?: string | undefined; instance_id?: string | undefined; action_group_id?: string | undefined; action_subgroup?: string | undefined; } & {}> | undefined; alert?: Readonly<{ rule?: Readonly<{ consumer?: string | undefined; execution?: Readonly<{ uuid?: string | undefined; metrics?: Readonly<{ number_of_triggered_actions?: string | number | undefined; number_of_generated_actions?: string | number | undefined; alert_counts?: Readonly<{ recovered?: string | number | undefined; active?: string | number | undefined; new?: string | number | undefined; } & {}> | undefined; number_of_searches?: string | number | undefined; total_indexing_duration_ms?: string | number | undefined; es_search_duration_ms?: string | number | undefined; total_search_duration_ms?: string | number | undefined; execution_gap_duration_s?: string | number | undefined; rule_type_run_duration_ms?: string | number | undefined; process_alerts_duration_ms?: string | number | undefined; trigger_actions_duration_ms?: string | number | undefined; process_rule_duration_ms?: string | number | undefined; claim_to_start_duration_ms?: string | number | undefined; prepare_rule_duration_ms?: string | number | undefined; total_run_duration_ms?: string | number | undefined; total_enrichment_duration_ms?: string | number | undefined; } & {}> | undefined; status?: string | undefined; status_order?: string | number | undefined; } & {}> | undefined; rule_type_id?: string | undefined; } & {}> | undefined; flapping?: boolean | undefined; } & {}> | undefined; version?: string | undefined; server_uuid?: string | undefined; task?: Readonly<{ id?: string | undefined; schedule_delay?: string | number | undefined; scheduled?: string | undefined; } & {}> | undefined; saved_objects?: Readonly<{ type?: string | undefined; id?: string | undefined; rel?: string | undefined; namespace?: string | undefined; type_id?: string | undefined; space_agnostic?: boolean | undefined; } & {}>[] | undefined; space_ids?: string[] | undefined; } & {}> | undefined; event?: Readonly<{ type?: string[] | undefined; reason?: string | undefined; action?: string | undefined; id?: string | undefined; start?: string | undefined; end?: string | undefined; category?: string[] | undefined; outcome?: string | undefined; code?: string | undefined; url?: string | undefined; created?: string | undefined; severity?: string | number | undefined; duration?: string | number | undefined; original?: string | undefined; dataset?: string | undefined; hash?: string | undefined; ingested?: string | undefined; kind?: string | undefined; module?: string | undefined; provider?: string | undefined; reference?: string | undefined; risk_score?: number | undefined; risk_score_norm?: number | undefined; sequence?: string | number | undefined; timezone?: string | undefined; } & {}> | undefined; ecs?: Readonly<{ version?: string | undefined; } & {}> | undefined; user?: Readonly<{ name?: string | undefined; } & {}> | undefined; } & {}> | undefined" ], "path": "x-pack/plugins/event_log/generated/schemas.ts", "deprecated": false, diff --git a/api_docs/event_log.mdx b/api_docs/event_log.mdx index e686ae8f97d89..683144bc3b585 100644 --- a/api_docs/event_log.mdx +++ b/api_docs/event_log.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/eventLog title: "eventLog" image: https://source.unsplash.com/400x175/?github description: API docs for the eventLog plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'eventLog'] --- import eventLogObj from './event_log.devdocs.json'; diff --git a/api_docs/expression_error.mdx b/api_docs/expression_error.mdx index fd015dee49ef9..d29975d188924 100644 --- a/api_docs/expression_error.mdx +++ b/api_docs/expression_error.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/expressionError title: "expressionError" image: https://source.unsplash.com/400x175/?github description: API docs for the expressionError plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'expressionError'] --- import expressionErrorObj from './expression_error.devdocs.json'; diff --git a/api_docs/expression_gauge.mdx b/api_docs/expression_gauge.mdx index 5e2d71349af17..4c73941725f98 100644 --- a/api_docs/expression_gauge.mdx +++ b/api_docs/expression_gauge.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/expressionGauge title: "expressionGauge" image: https://source.unsplash.com/400x175/?github description: API docs for the expressionGauge plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'expressionGauge'] --- import expressionGaugeObj from './expression_gauge.devdocs.json'; diff --git a/api_docs/expression_heatmap.mdx b/api_docs/expression_heatmap.mdx index dd22000d4bb3e..ee7ba05a89e8d 100644 --- a/api_docs/expression_heatmap.mdx +++ b/api_docs/expression_heatmap.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/expressionHeatmap title: "expressionHeatmap" image: https://source.unsplash.com/400x175/?github description: API docs for the expressionHeatmap plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'expressionHeatmap'] --- import expressionHeatmapObj from './expression_heatmap.devdocs.json'; diff --git a/api_docs/expression_image.mdx b/api_docs/expression_image.mdx index a32087c19dbfd..0e74b5135d91b 100644 --- a/api_docs/expression_image.mdx +++ b/api_docs/expression_image.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/expressionImage title: "expressionImage" image: https://source.unsplash.com/400x175/?github description: API docs for the expressionImage plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'expressionImage'] --- import expressionImageObj from './expression_image.devdocs.json'; diff --git a/api_docs/expression_legacy_metric_vis.mdx b/api_docs/expression_legacy_metric_vis.mdx index f6756a87e34aa..b1e05b10186db 100644 --- a/api_docs/expression_legacy_metric_vis.mdx +++ b/api_docs/expression_legacy_metric_vis.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/expressionLegacyMetricVis title: "expressionLegacyMetricVis" image: https://source.unsplash.com/400x175/?github description: API docs for the expressionLegacyMetricVis plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'expressionLegacyMetricVis'] --- import expressionLegacyMetricVisObj from './expression_legacy_metric_vis.devdocs.json'; diff --git a/api_docs/expression_metric.mdx b/api_docs/expression_metric.mdx index 60e11927aeff2..9b155f73a9908 100644 --- a/api_docs/expression_metric.mdx +++ b/api_docs/expression_metric.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/expressionMetric title: "expressionMetric" image: https://source.unsplash.com/400x175/?github description: API docs for the expressionMetric plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'expressionMetric'] --- import expressionMetricObj from './expression_metric.devdocs.json'; diff --git a/api_docs/expression_metric_vis.mdx b/api_docs/expression_metric_vis.mdx index e2fbe887be1e1..34b5e2dd9bf27 100644 --- a/api_docs/expression_metric_vis.mdx +++ b/api_docs/expression_metric_vis.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/expressionMetricVis title: "expressionMetricVis" image: https://source.unsplash.com/400x175/?github description: API docs for the expressionMetricVis plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'expressionMetricVis'] --- import expressionMetricVisObj from './expression_metric_vis.devdocs.json'; diff --git a/api_docs/expression_partition_vis.mdx b/api_docs/expression_partition_vis.mdx index 7deadf91f81f0..b79178ba2aaf0 100644 --- a/api_docs/expression_partition_vis.mdx +++ b/api_docs/expression_partition_vis.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/expressionPartitionVis title: "expressionPartitionVis" image: https://source.unsplash.com/400x175/?github description: API docs for the expressionPartitionVis plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'expressionPartitionVis'] --- import expressionPartitionVisObj from './expression_partition_vis.devdocs.json'; diff --git a/api_docs/expression_repeat_image.mdx b/api_docs/expression_repeat_image.mdx index 727ee30af31df..5551698521e02 100644 --- a/api_docs/expression_repeat_image.mdx +++ b/api_docs/expression_repeat_image.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/expressionRepeatImage title: "expressionRepeatImage" image: https://source.unsplash.com/400x175/?github description: API docs for the expressionRepeatImage plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'expressionRepeatImage'] --- import expressionRepeatImageObj from './expression_repeat_image.devdocs.json'; diff --git a/api_docs/expression_reveal_image.mdx b/api_docs/expression_reveal_image.mdx index 17065a0237ad1..cb899cc068bfc 100644 --- a/api_docs/expression_reveal_image.mdx +++ b/api_docs/expression_reveal_image.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/expressionRevealImage title: "expressionRevealImage" image: https://source.unsplash.com/400x175/?github description: API docs for the expressionRevealImage plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'expressionRevealImage'] --- import expressionRevealImageObj from './expression_reveal_image.devdocs.json'; diff --git a/api_docs/expression_shape.mdx b/api_docs/expression_shape.mdx index 7321543084449..f28cc85a8560d 100644 --- a/api_docs/expression_shape.mdx +++ b/api_docs/expression_shape.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/expressionShape title: "expressionShape" image: https://source.unsplash.com/400x175/?github description: API docs for the expressionShape plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'expressionShape'] --- import expressionShapeObj from './expression_shape.devdocs.json'; diff --git a/api_docs/expression_tagcloud.mdx b/api_docs/expression_tagcloud.mdx index a2922a2adbba4..0bdfd96cac0c6 100644 --- a/api_docs/expression_tagcloud.mdx +++ b/api_docs/expression_tagcloud.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/expressionTagcloud title: "expressionTagcloud" image: https://source.unsplash.com/400x175/?github description: API docs for the expressionTagcloud plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'expressionTagcloud'] --- import expressionTagcloudObj from './expression_tagcloud.devdocs.json'; diff --git a/api_docs/expression_x_y.mdx b/api_docs/expression_x_y.mdx index 8bbb60027e363..c6129b13b13be 100644 --- a/api_docs/expression_x_y.mdx +++ b/api_docs/expression_x_y.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/expressionXY title: "expressionXY" image: https://source.unsplash.com/400x175/?github description: API docs for the expressionXY plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'expressionXY'] --- import expressionXYObj from './expression_x_y.devdocs.json'; diff --git a/api_docs/expressions.mdx b/api_docs/expressions.mdx index 8b5f7a53a2758..f8e454f53e241 100644 --- a/api_docs/expressions.mdx +++ b/api_docs/expressions.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/expressions title: "expressions" image: https://source.unsplash.com/400x175/?github description: API docs for the expressions plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'expressions'] --- import expressionsObj from './expressions.devdocs.json'; diff --git a/api_docs/features.mdx b/api_docs/features.mdx index 2bda643c672cb..cdc78347cc120 100644 --- a/api_docs/features.mdx +++ b/api_docs/features.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/features title: "features" image: https://source.unsplash.com/400x175/?github description: API docs for the features plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'features'] --- import featuresObj from './features.devdocs.json'; diff --git a/api_docs/field_formats.mdx b/api_docs/field_formats.mdx index 0fc4c073e8a3a..8063afb72c658 100644 --- a/api_docs/field_formats.mdx +++ b/api_docs/field_formats.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/fieldFormats title: "fieldFormats" image: https://source.unsplash.com/400x175/?github description: API docs for the fieldFormats plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'fieldFormats'] --- import fieldFormatsObj from './field_formats.devdocs.json'; diff --git a/api_docs/file_upload.mdx b/api_docs/file_upload.mdx index 0cca2b13d6204..94e0e77e390c8 100644 --- a/api_docs/file_upload.mdx +++ b/api_docs/file_upload.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/fileUpload title: "fileUpload" image: https://source.unsplash.com/400x175/?github description: API docs for the fileUpload plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'fileUpload'] --- import fileUploadObj from './file_upload.devdocs.json'; diff --git a/api_docs/files.mdx b/api_docs/files.mdx index 84cf800dd99b8..79bd32f09ba56 100644 --- a/api_docs/files.mdx +++ b/api_docs/files.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/files title: "files" image: https://source.unsplash.com/400x175/?github description: API docs for the files plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'files'] --- import filesObj from './files.devdocs.json'; diff --git a/api_docs/files_management.mdx b/api_docs/files_management.mdx index 8edd8c564b069..a787305f636dd 100644 --- a/api_docs/files_management.mdx +++ b/api_docs/files_management.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/filesManagement title: "filesManagement" image: https://source.unsplash.com/400x175/?github description: API docs for the filesManagement plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'filesManagement'] --- import filesManagementObj from './files_management.devdocs.json'; diff --git a/api_docs/fleet.mdx b/api_docs/fleet.mdx index 09b125468a4a8..d6ee2a65cd394 100644 --- a/api_docs/fleet.mdx +++ b/api_docs/fleet.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/fleet title: "fleet" image: https://source.unsplash.com/400x175/?github description: API docs for the fleet plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'fleet'] --- import fleetObj from './fleet.devdocs.json'; diff --git a/api_docs/global_search.mdx b/api_docs/global_search.mdx index 18e5649e03525..5334cb9b7af40 100644 --- a/api_docs/global_search.mdx +++ b/api_docs/global_search.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/globalSearch title: "globalSearch" image: https://source.unsplash.com/400x175/?github description: API docs for the globalSearch plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'globalSearch'] --- import globalSearchObj from './global_search.devdocs.json'; diff --git a/api_docs/guided_onboarding.mdx b/api_docs/guided_onboarding.mdx index 2fc3536239528..a6ddac775ca7a 100644 --- a/api_docs/guided_onboarding.mdx +++ b/api_docs/guided_onboarding.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/guidedOnboarding title: "guidedOnboarding" image: https://source.unsplash.com/400x175/?github description: API docs for the guidedOnboarding plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'guidedOnboarding'] --- import guidedOnboardingObj from './guided_onboarding.devdocs.json'; diff --git a/api_docs/home.mdx b/api_docs/home.mdx index f665a538f5eb2..659f2eafc45ec 100644 --- a/api_docs/home.mdx +++ b/api_docs/home.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/home title: "home" image: https://source.unsplash.com/400x175/?github description: API docs for the home plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'home'] --- import homeObj from './home.devdocs.json'; diff --git a/api_docs/image_embeddable.mdx b/api_docs/image_embeddable.mdx index 9dc3cc136e7cd..c053e8210d559 100644 --- a/api_docs/image_embeddable.mdx +++ b/api_docs/image_embeddable.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/imageEmbeddable title: "imageEmbeddable" image: https://source.unsplash.com/400x175/?github description: API docs for the imageEmbeddable plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'imageEmbeddable'] --- import imageEmbeddableObj from './image_embeddable.devdocs.json'; diff --git a/api_docs/index_lifecycle_management.mdx b/api_docs/index_lifecycle_management.mdx index 720acf8ebe701..daaa6d4a98a46 100644 --- a/api_docs/index_lifecycle_management.mdx +++ b/api_docs/index_lifecycle_management.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/indexLifecycleManagement title: "indexLifecycleManagement" image: https://source.unsplash.com/400x175/?github description: API docs for the indexLifecycleManagement plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'indexLifecycleManagement'] --- import indexLifecycleManagementObj from './index_lifecycle_management.devdocs.json'; diff --git a/api_docs/index_management.mdx b/api_docs/index_management.mdx index a018fd0efe9f8..2186de71c2074 100644 --- a/api_docs/index_management.mdx +++ b/api_docs/index_management.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/indexManagement title: "indexManagement" image: https://source.unsplash.com/400x175/?github description: API docs for the indexManagement plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'indexManagement'] --- import indexManagementObj from './index_management.devdocs.json'; diff --git a/api_docs/infra.mdx b/api_docs/infra.mdx index 8808313066355..8af608feaa9ff 100644 --- a/api_docs/infra.mdx +++ b/api_docs/infra.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/infra title: "infra" image: https://source.unsplash.com/400x175/?github description: API docs for the infra plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'infra'] --- import infraObj from './infra.devdocs.json'; diff --git a/api_docs/inspector.mdx b/api_docs/inspector.mdx index 88542c9842d7d..c6e63a81e4f6a 100644 --- a/api_docs/inspector.mdx +++ b/api_docs/inspector.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/inspector title: "inspector" image: https://source.unsplash.com/400x175/?github description: API docs for the inspector plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'inspector'] --- import inspectorObj from './inspector.devdocs.json'; diff --git a/api_docs/interactive_setup.mdx b/api_docs/interactive_setup.mdx index f826a4049d900..6aa9e23b7b459 100644 --- a/api_docs/interactive_setup.mdx +++ b/api_docs/interactive_setup.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/interactiveSetup title: "interactiveSetup" image: https://source.unsplash.com/400x175/?github description: API docs for the interactiveSetup plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'interactiveSetup'] --- import interactiveSetupObj from './interactive_setup.devdocs.json'; diff --git a/api_docs/kbn_ace.mdx b/api_docs/kbn_ace.mdx index f42ffbe04abc2..73ec62649c485 100644 --- a/api_docs/kbn_ace.mdx +++ b/api_docs/kbn_ace.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ace title: "@kbn/ace" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ace plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ace'] --- import kbnAceObj from './kbn_ace.devdocs.json'; diff --git a/api_docs/kbn_aiops_components.mdx b/api_docs/kbn_aiops_components.mdx index 93459ff6b7311..379dc78a3f03c 100644 --- a/api_docs/kbn_aiops_components.mdx +++ b/api_docs/kbn_aiops_components.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-aiops-components title: "@kbn/aiops-components" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/aiops-components plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/aiops-components'] --- import kbnAiopsComponentsObj from './kbn_aiops_components.devdocs.json'; diff --git a/api_docs/kbn_aiops_utils.devdocs.json b/api_docs/kbn_aiops_utils.devdocs.json index 309acf094ca10..f0ad9531c73b7 100644 --- a/api_docs/kbn_aiops_utils.devdocs.json +++ b/api_docs/kbn_aiops_utils.devdocs.json @@ -103,7 +103,7 @@ "tags": [], "label": "getWindowParameters", "description": [ - "\nGiven a point in time (e.g. where a user clicks), use simple heuristics to compute:\n\n1. The time window around the click to evaluate for changes\n2. The historical time window prior to the click to use as a baseline.\n\nThe philosophy here is that charts are displayed with different granularities according to their\noverall time window. We select the change point and historical time windows inline with the\noverall time window.\n\nThe algorithm for doing this is based on the typical granularities that exist in machine data.\n" + "\nGiven a point in time (e.g. where a user clicks), use simple heuristics to compute:\n\n1. The time window around the click to evaluate for changes\n2. The historical time window prior to the click to use as a baseline.\n\nThe philosophy here is that charts are displayed with different granularities according to their\noverall time window. We select the log spike and historical time windows inline with the\noverall time window.\n\nThe algorithm for doing this is based on the typical granularities that exist in machine data.\n" ], "signature": [ "(clickTime: number, minTime: number, maxTime: number) => ", diff --git a/api_docs/kbn_aiops_utils.mdx b/api_docs/kbn_aiops_utils.mdx index 872f27b277075..fb059f845a018 100644 --- a/api_docs/kbn_aiops_utils.mdx +++ b/api_docs/kbn_aiops_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-aiops-utils title: "@kbn/aiops-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/aiops-utils plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/aiops-utils'] --- import kbnAiopsUtilsObj from './kbn_aiops_utils.devdocs.json'; diff --git a/api_docs/kbn_alerts.mdx b/api_docs/kbn_alerts.mdx index e896ad0c99d33..030e18b65602e 100644 --- a/api_docs/kbn_alerts.mdx +++ b/api_docs/kbn_alerts.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-alerts title: "@kbn/alerts" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/alerts plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/alerts'] --- import kbnAlertsObj from './kbn_alerts.devdocs.json'; diff --git a/api_docs/kbn_alerts_as_data_utils.devdocs.json b/api_docs/kbn_alerts_as_data_utils.devdocs.json index b5390c5044148..1dc2a279ca7e3 100644 --- a/api_docs/kbn_alerts_as_data_utils.devdocs.json +++ b/api_docs/kbn_alerts_as_data_utils.devdocs.json @@ -142,6 +142,21 @@ "trackAdoption": false, "initialIsOpen": false }, + { + "parentPluginId": "@kbn/alerts-as-data-utils", + "id": "def-common.ExperimentalRuleFieldMap", + "type": "Type", + "tags": [], + "label": "ExperimentalRuleFieldMap", + "description": [], + "signature": [ + "{ readonly \"kibana.alert.evaluation.threshold\": { readonly type: \"scaled_float\"; readonly scaling_factor: 100; readonly required: false; }; readonly \"kibana.alert.evaluation.value\": { readonly type: \"scaled_float\"; readonly scaling_factor: 100; readonly required: false; }; }" + ], + "path": "packages/kbn-alerts-as-data-utils/src/field_maps/legacy_experimental_field_map.ts", + "deprecated": false, + "trackAdoption": false, + "initialIsOpen": false + }, { "parentPluginId": "@kbn/alerts-as-data-utils", "id": "def-common.LegacyAlertFieldMap", @@ -209,6 +224,21 @@ "deprecated": false, "trackAdoption": false, "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/alerts-as-data-utils", + "id": "def-common.legacyExperimentalFieldMap", + "type": "Object", + "tags": [], + "label": "legacyExperimentalFieldMap", + "description": [], + "signature": [ + "{ readonly \"kibana.alert.evaluation.threshold\": { readonly type: \"scaled_float\"; readonly scaling_factor: 100; readonly required: false; }; readonly \"kibana.alert.evaluation.value\": { readonly type: \"scaled_float\"; readonly scaling_factor: 100; readonly required: false; }; }" + ], + "path": "packages/kbn-alerts-as-data-utils/src/field_maps/legacy_experimental_field_map.ts", + "deprecated": false, + "trackAdoption": false, + "initialIsOpen": false } ] } diff --git a/api_docs/kbn_alerts_as_data_utils.mdx b/api_docs/kbn_alerts_as_data_utils.mdx index 40ee360d3f666..8551561f8f560 100644 --- a/api_docs/kbn_alerts_as_data_utils.mdx +++ b/api_docs/kbn_alerts_as_data_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-alerts-as-data-utils title: "@kbn/alerts-as-data-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/alerts-as-data-utils plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/alerts-as-data-utils'] --- import kbnAlertsAsDataUtilsObj from './kbn_alerts_as_data_utils.devdocs.json'; @@ -21,7 +21,7 @@ Contact [@elastic/response-ops](https://github.com/orgs/elastic/teams/response-o | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 12 | 0 | 12 | 0 | +| 14 | 0 | 14 | 0 | ## Common diff --git a/api_docs/kbn_alerts_ui_shared.mdx b/api_docs/kbn_alerts_ui_shared.mdx index 9c5653faee847..98c6c959b9303 100644 --- a/api_docs/kbn_alerts_ui_shared.mdx +++ b/api_docs/kbn_alerts_ui_shared.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-alerts-ui-shared title: "@kbn/alerts-ui-shared" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/alerts-ui-shared plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/alerts-ui-shared'] --- import kbnAlertsUiSharedObj from './kbn_alerts_ui_shared.devdocs.json'; diff --git a/api_docs/kbn_analytics.mdx b/api_docs/kbn_analytics.mdx index 4a7bf3c288a9a..fda22f331c776 100644 --- a/api_docs/kbn_analytics.mdx +++ b/api_docs/kbn_analytics.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-analytics title: "@kbn/analytics" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/analytics plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/analytics'] --- import kbnAnalyticsObj from './kbn_analytics.devdocs.json'; diff --git a/api_docs/kbn_analytics_client.mdx b/api_docs/kbn_analytics_client.mdx index 548c20b6aff7e..aa8836b75670c 100644 --- a/api_docs/kbn_analytics_client.mdx +++ b/api_docs/kbn_analytics_client.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-analytics-client title: "@kbn/analytics-client" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/analytics-client plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/analytics-client'] --- import kbnAnalyticsClientObj from './kbn_analytics_client.devdocs.json'; diff --git a/api_docs/kbn_analytics_shippers_elastic_v3_browser.mdx b/api_docs/kbn_analytics_shippers_elastic_v3_browser.mdx index b64976a2401e7..e63a534279466 100644 --- a/api_docs/kbn_analytics_shippers_elastic_v3_browser.mdx +++ b/api_docs/kbn_analytics_shippers_elastic_v3_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-analytics-shippers-elastic-v3-browser title: "@kbn/analytics-shippers-elastic-v3-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/analytics-shippers-elastic-v3-browser plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/analytics-shippers-elastic-v3-browser'] --- import kbnAnalyticsShippersElasticV3BrowserObj from './kbn_analytics_shippers_elastic_v3_browser.devdocs.json'; diff --git a/api_docs/kbn_analytics_shippers_elastic_v3_common.mdx b/api_docs/kbn_analytics_shippers_elastic_v3_common.mdx index c8b63a33fa323..f9317de120286 100644 --- a/api_docs/kbn_analytics_shippers_elastic_v3_common.mdx +++ b/api_docs/kbn_analytics_shippers_elastic_v3_common.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-analytics-shippers-elastic-v3-common title: "@kbn/analytics-shippers-elastic-v3-common" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/analytics-shippers-elastic-v3-common plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/analytics-shippers-elastic-v3-common'] --- import kbnAnalyticsShippersElasticV3CommonObj from './kbn_analytics_shippers_elastic_v3_common.devdocs.json'; diff --git a/api_docs/kbn_analytics_shippers_elastic_v3_server.mdx b/api_docs/kbn_analytics_shippers_elastic_v3_server.mdx index 32b07226bec33..e1fcba5ec5066 100644 --- a/api_docs/kbn_analytics_shippers_elastic_v3_server.mdx +++ b/api_docs/kbn_analytics_shippers_elastic_v3_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-analytics-shippers-elastic-v3-server title: "@kbn/analytics-shippers-elastic-v3-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/analytics-shippers-elastic-v3-server plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/analytics-shippers-elastic-v3-server'] --- import kbnAnalyticsShippersElasticV3ServerObj from './kbn_analytics_shippers_elastic_v3_server.devdocs.json'; diff --git a/api_docs/kbn_analytics_shippers_fullstory.mdx b/api_docs/kbn_analytics_shippers_fullstory.mdx index 79f3ebb32647d..7fcd13fe834b8 100644 --- a/api_docs/kbn_analytics_shippers_fullstory.mdx +++ b/api_docs/kbn_analytics_shippers_fullstory.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-analytics-shippers-fullstory title: "@kbn/analytics-shippers-fullstory" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/analytics-shippers-fullstory plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/analytics-shippers-fullstory'] --- import kbnAnalyticsShippersFullstoryObj from './kbn_analytics_shippers_fullstory.devdocs.json'; diff --git a/api_docs/kbn_analytics_shippers_gainsight.mdx b/api_docs/kbn_analytics_shippers_gainsight.mdx index 96b10654a8a5e..f1f21005bdf0f 100644 --- a/api_docs/kbn_analytics_shippers_gainsight.mdx +++ b/api_docs/kbn_analytics_shippers_gainsight.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-analytics-shippers-gainsight title: "@kbn/analytics-shippers-gainsight" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/analytics-shippers-gainsight plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/analytics-shippers-gainsight'] --- import kbnAnalyticsShippersGainsightObj from './kbn_analytics_shippers_gainsight.devdocs.json'; diff --git a/api_docs/kbn_apm_config_loader.mdx b/api_docs/kbn_apm_config_loader.mdx index c6abdc3eedc6f..fb5323d53f6c1 100644 --- a/api_docs/kbn_apm_config_loader.mdx +++ b/api_docs/kbn_apm_config_loader.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-apm-config-loader title: "@kbn/apm-config-loader" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/apm-config-loader plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/apm-config-loader'] --- import kbnApmConfigLoaderObj from './kbn_apm_config_loader.devdocs.json'; diff --git a/api_docs/kbn_apm_synthtrace.mdx b/api_docs/kbn_apm_synthtrace.mdx index 6a795b51b6e5b..4e9b38045b5dc 100644 --- a/api_docs/kbn_apm_synthtrace.mdx +++ b/api_docs/kbn_apm_synthtrace.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-apm-synthtrace title: "@kbn/apm-synthtrace" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/apm-synthtrace plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/apm-synthtrace'] --- import kbnApmSynthtraceObj from './kbn_apm_synthtrace.devdocs.json'; diff --git a/api_docs/kbn_apm_synthtrace_client.mdx b/api_docs/kbn_apm_synthtrace_client.mdx index 67d5b2c9f03f1..15abd120a1771 100644 --- a/api_docs/kbn_apm_synthtrace_client.mdx +++ b/api_docs/kbn_apm_synthtrace_client.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-apm-synthtrace-client title: "@kbn/apm-synthtrace-client" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/apm-synthtrace-client plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/apm-synthtrace-client'] --- import kbnApmSynthtraceClientObj from './kbn_apm_synthtrace_client.devdocs.json'; diff --git a/api_docs/kbn_apm_utils.mdx b/api_docs/kbn_apm_utils.mdx index 449305a50bef8..a432b3ad5c989 100644 --- a/api_docs/kbn_apm_utils.mdx +++ b/api_docs/kbn_apm_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-apm-utils title: "@kbn/apm-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/apm-utils plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/apm-utils'] --- import kbnApmUtilsObj from './kbn_apm_utils.devdocs.json'; diff --git a/api_docs/kbn_axe_config.mdx b/api_docs/kbn_axe_config.mdx index eb7f3cdefa697..db25d2c35c90a 100644 --- a/api_docs/kbn_axe_config.mdx +++ b/api_docs/kbn_axe_config.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-axe-config title: "@kbn/axe-config" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/axe-config plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/axe-config'] --- import kbnAxeConfigObj from './kbn_axe_config.devdocs.json'; diff --git a/api_docs/kbn_cases_components.mdx b/api_docs/kbn_cases_components.mdx index c3e8e0c6bae1d..224812965a790 100644 --- a/api_docs/kbn_cases_components.mdx +++ b/api_docs/kbn_cases_components.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-cases-components title: "@kbn/cases-components" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/cases-components plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/cases-components'] --- import kbnCasesComponentsObj from './kbn_cases_components.devdocs.json'; diff --git a/api_docs/kbn_cell_actions.mdx b/api_docs/kbn_cell_actions.mdx index 3329f6d83be2d..36df0ce4b8f86 100644 --- a/api_docs/kbn_cell_actions.mdx +++ b/api_docs/kbn_cell_actions.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-cell-actions title: "@kbn/cell-actions" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/cell-actions plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/cell-actions'] --- import kbnCellActionsObj from './kbn_cell_actions.devdocs.json'; diff --git a/api_docs/kbn_chart_expressions_common.mdx b/api_docs/kbn_chart_expressions_common.mdx index c9ed687d548ee..86cc619d08b49 100644 --- a/api_docs/kbn_chart_expressions_common.mdx +++ b/api_docs/kbn_chart_expressions_common.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-chart-expressions-common title: "@kbn/chart-expressions-common" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/chart-expressions-common plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/chart-expressions-common'] --- import kbnChartExpressionsCommonObj from './kbn_chart_expressions_common.devdocs.json'; diff --git a/api_docs/kbn_chart_icons.mdx b/api_docs/kbn_chart_icons.mdx index 6922652a36a4c..644f6b7a032d6 100644 --- a/api_docs/kbn_chart_icons.mdx +++ b/api_docs/kbn_chart_icons.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-chart-icons title: "@kbn/chart-icons" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/chart-icons plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/chart-icons'] --- import kbnChartIconsObj from './kbn_chart_icons.devdocs.json'; diff --git a/api_docs/kbn_ci_stats_core.mdx b/api_docs/kbn_ci_stats_core.mdx index e4625704d479c..5cea780840e23 100644 --- a/api_docs/kbn_ci_stats_core.mdx +++ b/api_docs/kbn_ci_stats_core.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ci-stats-core title: "@kbn/ci-stats-core" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ci-stats-core plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ci-stats-core'] --- import kbnCiStatsCoreObj from './kbn_ci_stats_core.devdocs.json'; diff --git a/api_docs/kbn_ci_stats_performance_metrics.mdx b/api_docs/kbn_ci_stats_performance_metrics.mdx index b5197e159a15e..f67cd274d8d6d 100644 --- a/api_docs/kbn_ci_stats_performance_metrics.mdx +++ b/api_docs/kbn_ci_stats_performance_metrics.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ci-stats-performance-metrics title: "@kbn/ci-stats-performance-metrics" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ci-stats-performance-metrics plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ci-stats-performance-metrics'] --- import kbnCiStatsPerformanceMetricsObj from './kbn_ci_stats_performance_metrics.devdocs.json'; diff --git a/api_docs/kbn_ci_stats_reporter.mdx b/api_docs/kbn_ci_stats_reporter.mdx index b405097409fb0..4660e472804bd 100644 --- a/api_docs/kbn_ci_stats_reporter.mdx +++ b/api_docs/kbn_ci_stats_reporter.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ci-stats-reporter title: "@kbn/ci-stats-reporter" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ci-stats-reporter plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ci-stats-reporter'] --- import kbnCiStatsReporterObj from './kbn_ci_stats_reporter.devdocs.json'; diff --git a/api_docs/kbn_cli_dev_mode.mdx b/api_docs/kbn_cli_dev_mode.mdx index 2f9626b2d634c..82943a329b6d1 100644 --- a/api_docs/kbn_cli_dev_mode.mdx +++ b/api_docs/kbn_cli_dev_mode.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-cli-dev-mode title: "@kbn/cli-dev-mode" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/cli-dev-mode plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/cli-dev-mode'] --- import kbnCliDevModeObj from './kbn_cli_dev_mode.devdocs.json'; diff --git a/api_docs/kbn_code_editor.mdx b/api_docs/kbn_code_editor.mdx index 5d390c5b03666..293bbd826936f 100644 --- a/api_docs/kbn_code_editor.mdx +++ b/api_docs/kbn_code_editor.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-code-editor title: "@kbn/code-editor" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/code-editor plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/code-editor'] --- import kbnCodeEditorObj from './kbn_code_editor.devdocs.json'; diff --git a/api_docs/kbn_code_editor_mocks.mdx b/api_docs/kbn_code_editor_mocks.mdx index fdc82a56996f5..128ed65f76116 100644 --- a/api_docs/kbn_code_editor_mocks.mdx +++ b/api_docs/kbn_code_editor_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-code-editor-mocks title: "@kbn/code-editor-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/code-editor-mocks plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/code-editor-mocks'] --- import kbnCodeEditorMocksObj from './kbn_code_editor_mocks.devdocs.json'; diff --git a/api_docs/kbn_coloring.mdx b/api_docs/kbn_coloring.mdx index a21e8c0eabb52..70db974ae8722 100644 --- a/api_docs/kbn_coloring.mdx +++ b/api_docs/kbn_coloring.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-coloring title: "@kbn/coloring" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/coloring plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/coloring'] --- import kbnColoringObj from './kbn_coloring.devdocs.json'; diff --git a/api_docs/kbn_config.mdx b/api_docs/kbn_config.mdx index cb61e0fd2bb4b..620b15d042d8f 100644 --- a/api_docs/kbn_config.mdx +++ b/api_docs/kbn_config.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-config title: "@kbn/config" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/config plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/config'] --- import kbnConfigObj from './kbn_config.devdocs.json'; diff --git a/api_docs/kbn_config_mocks.mdx b/api_docs/kbn_config_mocks.mdx index 89bca6ce3da4c..ec8f5ecd0e7f5 100644 --- a/api_docs/kbn_config_mocks.mdx +++ b/api_docs/kbn_config_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-config-mocks title: "@kbn/config-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/config-mocks plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/config-mocks'] --- import kbnConfigMocksObj from './kbn_config_mocks.devdocs.json'; diff --git a/api_docs/kbn_config_schema.mdx b/api_docs/kbn_config_schema.mdx index d33d633fdc3b3..b74a8bab5368c 100644 --- a/api_docs/kbn_config_schema.mdx +++ b/api_docs/kbn_config_schema.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-config-schema title: "@kbn/config-schema" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/config-schema plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/config-schema'] --- import kbnConfigSchemaObj from './kbn_config_schema.devdocs.json'; diff --git a/api_docs/kbn_content_management_content_editor.mdx b/api_docs/kbn_content_management_content_editor.mdx index 36273f87cd264..20a8bfc638a53 100644 --- a/api_docs/kbn_content_management_content_editor.mdx +++ b/api_docs/kbn_content_management_content_editor.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-content-management-content-editor title: "@kbn/content-management-content-editor" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/content-management-content-editor plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/content-management-content-editor'] --- import kbnContentManagementContentEditorObj from './kbn_content_management_content_editor.devdocs.json'; diff --git a/api_docs/kbn_content_management_table_list.mdx b/api_docs/kbn_content_management_table_list.mdx index 49688cf3d3650..05cb08207daf5 100644 --- a/api_docs/kbn_content_management_table_list.mdx +++ b/api_docs/kbn_content_management_table_list.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-content-management-table-list title: "@kbn/content-management-table-list" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/content-management-table-list plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/content-management-table-list'] --- import kbnContentManagementTableListObj from './kbn_content_management_table_list.devdocs.json'; diff --git a/api_docs/kbn_core_analytics_browser.mdx b/api_docs/kbn_core_analytics_browser.mdx index 25e257ebede87..9ca1f628b471f 100644 --- a/api_docs/kbn_core_analytics_browser.mdx +++ b/api_docs/kbn_core_analytics_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-analytics-browser title: "@kbn/core-analytics-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-analytics-browser plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-analytics-browser'] --- import kbnCoreAnalyticsBrowserObj from './kbn_core_analytics_browser.devdocs.json'; diff --git a/api_docs/kbn_core_analytics_browser_internal.mdx b/api_docs/kbn_core_analytics_browser_internal.mdx index d78fa4abe279c..235f9c61fe187 100644 --- a/api_docs/kbn_core_analytics_browser_internal.mdx +++ b/api_docs/kbn_core_analytics_browser_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-analytics-browser-internal title: "@kbn/core-analytics-browser-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-analytics-browser-internal plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-analytics-browser-internal'] --- import kbnCoreAnalyticsBrowserInternalObj from './kbn_core_analytics_browser_internal.devdocs.json'; diff --git a/api_docs/kbn_core_analytics_browser_mocks.mdx b/api_docs/kbn_core_analytics_browser_mocks.mdx index 46cef4ea0014f..652c4f9b22ce7 100644 --- a/api_docs/kbn_core_analytics_browser_mocks.mdx +++ b/api_docs/kbn_core_analytics_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-analytics-browser-mocks title: "@kbn/core-analytics-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-analytics-browser-mocks plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-analytics-browser-mocks'] --- import kbnCoreAnalyticsBrowserMocksObj from './kbn_core_analytics_browser_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_analytics_server.mdx b/api_docs/kbn_core_analytics_server.mdx index c9cb04d1d48b0..ad28840e2fc99 100644 --- a/api_docs/kbn_core_analytics_server.mdx +++ b/api_docs/kbn_core_analytics_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-analytics-server title: "@kbn/core-analytics-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-analytics-server plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-analytics-server'] --- import kbnCoreAnalyticsServerObj from './kbn_core_analytics_server.devdocs.json'; diff --git a/api_docs/kbn_core_analytics_server_internal.mdx b/api_docs/kbn_core_analytics_server_internal.mdx index 78a14dfa3d230..2f9e09dad6c08 100644 --- a/api_docs/kbn_core_analytics_server_internal.mdx +++ b/api_docs/kbn_core_analytics_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-analytics-server-internal title: "@kbn/core-analytics-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-analytics-server-internal plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-analytics-server-internal'] --- import kbnCoreAnalyticsServerInternalObj from './kbn_core_analytics_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_analytics_server_mocks.mdx b/api_docs/kbn_core_analytics_server_mocks.mdx index 6a04d49cbdac5..7e2e303aeef50 100644 --- a/api_docs/kbn_core_analytics_server_mocks.mdx +++ b/api_docs/kbn_core_analytics_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-analytics-server-mocks title: "@kbn/core-analytics-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-analytics-server-mocks plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-analytics-server-mocks'] --- import kbnCoreAnalyticsServerMocksObj from './kbn_core_analytics_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_application_browser.mdx b/api_docs/kbn_core_application_browser.mdx index 9635f66f22fcd..a36e62e4b18c7 100644 --- a/api_docs/kbn_core_application_browser.mdx +++ b/api_docs/kbn_core_application_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-application-browser title: "@kbn/core-application-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-application-browser plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-application-browser'] --- import kbnCoreApplicationBrowserObj from './kbn_core_application_browser.devdocs.json'; diff --git a/api_docs/kbn_core_application_browser_internal.mdx b/api_docs/kbn_core_application_browser_internal.mdx index 3c44f014ed28e..577c3c9f760b6 100644 --- a/api_docs/kbn_core_application_browser_internal.mdx +++ b/api_docs/kbn_core_application_browser_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-application-browser-internal title: "@kbn/core-application-browser-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-application-browser-internal plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-application-browser-internal'] --- import kbnCoreApplicationBrowserInternalObj from './kbn_core_application_browser_internal.devdocs.json'; diff --git a/api_docs/kbn_core_application_browser_mocks.mdx b/api_docs/kbn_core_application_browser_mocks.mdx index bc1a4ef63b5e3..01f90019478ac 100644 --- a/api_docs/kbn_core_application_browser_mocks.mdx +++ b/api_docs/kbn_core_application_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-application-browser-mocks title: "@kbn/core-application-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-application-browser-mocks plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-application-browser-mocks'] --- import kbnCoreApplicationBrowserMocksObj from './kbn_core_application_browser_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_application_common.mdx b/api_docs/kbn_core_application_common.mdx index d17e00ed64cad..56dea207d4219 100644 --- a/api_docs/kbn_core_application_common.mdx +++ b/api_docs/kbn_core_application_common.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-application-common title: "@kbn/core-application-common" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-application-common plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-application-common'] --- import kbnCoreApplicationCommonObj from './kbn_core_application_common.devdocs.json'; diff --git a/api_docs/kbn_core_apps_browser_internal.mdx b/api_docs/kbn_core_apps_browser_internal.mdx index df8f80b28f103..08e7111dcc8f1 100644 --- a/api_docs/kbn_core_apps_browser_internal.mdx +++ b/api_docs/kbn_core_apps_browser_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-apps-browser-internal title: "@kbn/core-apps-browser-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-apps-browser-internal plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-apps-browser-internal'] --- import kbnCoreAppsBrowserInternalObj from './kbn_core_apps_browser_internal.devdocs.json'; diff --git a/api_docs/kbn_core_apps_browser_mocks.mdx b/api_docs/kbn_core_apps_browser_mocks.mdx index 92b3277daa85f..e4406ff5d5630 100644 --- a/api_docs/kbn_core_apps_browser_mocks.mdx +++ b/api_docs/kbn_core_apps_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-apps-browser-mocks title: "@kbn/core-apps-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-apps-browser-mocks plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-apps-browser-mocks'] --- import kbnCoreAppsBrowserMocksObj from './kbn_core_apps_browser_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_apps_server_internal.mdx b/api_docs/kbn_core_apps_server_internal.mdx index 999ea13ca0a44..311f1f829329c 100644 --- a/api_docs/kbn_core_apps_server_internal.mdx +++ b/api_docs/kbn_core_apps_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-apps-server-internal title: "@kbn/core-apps-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-apps-server-internal plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-apps-server-internal'] --- import kbnCoreAppsServerInternalObj from './kbn_core_apps_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_base_browser_mocks.mdx b/api_docs/kbn_core_base_browser_mocks.mdx index e43177b92d2a1..a39ba8b1e0612 100644 --- a/api_docs/kbn_core_base_browser_mocks.mdx +++ b/api_docs/kbn_core_base_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-base-browser-mocks title: "@kbn/core-base-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-base-browser-mocks plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-base-browser-mocks'] --- import kbnCoreBaseBrowserMocksObj from './kbn_core_base_browser_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_base_common.mdx b/api_docs/kbn_core_base_common.mdx index b865bec2c4e30..84d39d9a5846e 100644 --- a/api_docs/kbn_core_base_common.mdx +++ b/api_docs/kbn_core_base_common.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-base-common title: "@kbn/core-base-common" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-base-common plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-base-common'] --- import kbnCoreBaseCommonObj from './kbn_core_base_common.devdocs.json'; diff --git a/api_docs/kbn_core_base_server_internal.mdx b/api_docs/kbn_core_base_server_internal.mdx index a897fb4fbad5b..1a79d20cbd929 100644 --- a/api_docs/kbn_core_base_server_internal.mdx +++ b/api_docs/kbn_core_base_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-base-server-internal title: "@kbn/core-base-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-base-server-internal plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-base-server-internal'] --- import kbnCoreBaseServerInternalObj from './kbn_core_base_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_base_server_mocks.mdx b/api_docs/kbn_core_base_server_mocks.mdx index b43dd829c18ba..a7bf9f3180cd9 100644 --- a/api_docs/kbn_core_base_server_mocks.mdx +++ b/api_docs/kbn_core_base_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-base-server-mocks title: "@kbn/core-base-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-base-server-mocks plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-base-server-mocks'] --- import kbnCoreBaseServerMocksObj from './kbn_core_base_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_capabilities_browser_mocks.mdx b/api_docs/kbn_core_capabilities_browser_mocks.mdx index e8895f5c85fa8..e4a2cddc606ed 100644 --- a/api_docs/kbn_core_capabilities_browser_mocks.mdx +++ b/api_docs/kbn_core_capabilities_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-capabilities-browser-mocks title: "@kbn/core-capabilities-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-capabilities-browser-mocks plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-capabilities-browser-mocks'] --- import kbnCoreCapabilitiesBrowserMocksObj from './kbn_core_capabilities_browser_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_capabilities_common.mdx b/api_docs/kbn_core_capabilities_common.mdx index ed5723ec99d01..1f21a729af724 100644 --- a/api_docs/kbn_core_capabilities_common.mdx +++ b/api_docs/kbn_core_capabilities_common.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-capabilities-common title: "@kbn/core-capabilities-common" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-capabilities-common plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-capabilities-common'] --- import kbnCoreCapabilitiesCommonObj from './kbn_core_capabilities_common.devdocs.json'; diff --git a/api_docs/kbn_core_capabilities_server.mdx b/api_docs/kbn_core_capabilities_server.mdx index 73b42e45e12d3..bae71b7ecf453 100644 --- a/api_docs/kbn_core_capabilities_server.mdx +++ b/api_docs/kbn_core_capabilities_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-capabilities-server title: "@kbn/core-capabilities-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-capabilities-server plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-capabilities-server'] --- import kbnCoreCapabilitiesServerObj from './kbn_core_capabilities_server.devdocs.json'; diff --git a/api_docs/kbn_core_capabilities_server_mocks.mdx b/api_docs/kbn_core_capabilities_server_mocks.mdx index 388e2f3a3d88f..1a848eacabe2d 100644 --- a/api_docs/kbn_core_capabilities_server_mocks.mdx +++ b/api_docs/kbn_core_capabilities_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-capabilities-server-mocks title: "@kbn/core-capabilities-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-capabilities-server-mocks plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-capabilities-server-mocks'] --- import kbnCoreCapabilitiesServerMocksObj from './kbn_core_capabilities_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_chrome_browser.mdx b/api_docs/kbn_core_chrome_browser.mdx index f18bfe0f29fab..17f3c6870fcf3 100644 --- a/api_docs/kbn_core_chrome_browser.mdx +++ b/api_docs/kbn_core_chrome_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-chrome-browser title: "@kbn/core-chrome-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-chrome-browser plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-chrome-browser'] --- import kbnCoreChromeBrowserObj from './kbn_core_chrome_browser.devdocs.json'; diff --git a/api_docs/kbn_core_chrome_browser_mocks.mdx b/api_docs/kbn_core_chrome_browser_mocks.mdx index 4afa12a54e2d7..92733f73bb62f 100644 --- a/api_docs/kbn_core_chrome_browser_mocks.mdx +++ b/api_docs/kbn_core_chrome_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-chrome-browser-mocks title: "@kbn/core-chrome-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-chrome-browser-mocks plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-chrome-browser-mocks'] --- import kbnCoreChromeBrowserMocksObj from './kbn_core_chrome_browser_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_config_server_internal.mdx b/api_docs/kbn_core_config_server_internal.mdx index a92e33e2ec9e7..5bbcabee6abb2 100644 --- a/api_docs/kbn_core_config_server_internal.mdx +++ b/api_docs/kbn_core_config_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-config-server-internal title: "@kbn/core-config-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-config-server-internal plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-config-server-internal'] --- import kbnCoreConfigServerInternalObj from './kbn_core_config_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_custom_branding_browser.mdx b/api_docs/kbn_core_custom_branding_browser.mdx index 7b80a3cd824b5..0924b836d3e45 100644 --- a/api_docs/kbn_core_custom_branding_browser.mdx +++ b/api_docs/kbn_core_custom_branding_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-custom-branding-browser title: "@kbn/core-custom-branding-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-custom-branding-browser plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-custom-branding-browser'] --- import kbnCoreCustomBrandingBrowserObj from './kbn_core_custom_branding_browser.devdocs.json'; diff --git a/api_docs/kbn_core_custom_branding_browser_internal.mdx b/api_docs/kbn_core_custom_branding_browser_internal.mdx index 4d0d71d3a873d..15781b0a29cc9 100644 --- a/api_docs/kbn_core_custom_branding_browser_internal.mdx +++ b/api_docs/kbn_core_custom_branding_browser_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-custom-branding-browser-internal title: "@kbn/core-custom-branding-browser-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-custom-branding-browser-internal plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-custom-branding-browser-internal'] --- import kbnCoreCustomBrandingBrowserInternalObj from './kbn_core_custom_branding_browser_internal.devdocs.json'; diff --git a/api_docs/kbn_core_custom_branding_browser_mocks.mdx b/api_docs/kbn_core_custom_branding_browser_mocks.mdx index dbfab8a56081d..8c972df70bc15 100644 --- a/api_docs/kbn_core_custom_branding_browser_mocks.mdx +++ b/api_docs/kbn_core_custom_branding_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-custom-branding-browser-mocks title: "@kbn/core-custom-branding-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-custom-branding-browser-mocks plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-custom-branding-browser-mocks'] --- import kbnCoreCustomBrandingBrowserMocksObj from './kbn_core_custom_branding_browser_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_custom_branding_common.mdx b/api_docs/kbn_core_custom_branding_common.mdx index cb6a5a422cd91..8647dab1c7714 100644 --- a/api_docs/kbn_core_custom_branding_common.mdx +++ b/api_docs/kbn_core_custom_branding_common.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-custom-branding-common title: "@kbn/core-custom-branding-common" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-custom-branding-common plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-custom-branding-common'] --- import kbnCoreCustomBrandingCommonObj from './kbn_core_custom_branding_common.devdocs.json'; diff --git a/api_docs/kbn_core_custom_branding_server.mdx b/api_docs/kbn_core_custom_branding_server.mdx index 559745dee2204..68cba61d1e84e 100644 --- a/api_docs/kbn_core_custom_branding_server.mdx +++ b/api_docs/kbn_core_custom_branding_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-custom-branding-server title: "@kbn/core-custom-branding-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-custom-branding-server plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-custom-branding-server'] --- import kbnCoreCustomBrandingServerObj from './kbn_core_custom_branding_server.devdocs.json'; diff --git a/api_docs/kbn_core_custom_branding_server_internal.mdx b/api_docs/kbn_core_custom_branding_server_internal.mdx index d6da89713d009..cfab7dd850c24 100644 --- a/api_docs/kbn_core_custom_branding_server_internal.mdx +++ b/api_docs/kbn_core_custom_branding_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-custom-branding-server-internal title: "@kbn/core-custom-branding-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-custom-branding-server-internal plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-custom-branding-server-internal'] --- import kbnCoreCustomBrandingServerInternalObj from './kbn_core_custom_branding_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_custom_branding_server_mocks.mdx b/api_docs/kbn_core_custom_branding_server_mocks.mdx index 25dac6bd529ac..1d8b166f7ed08 100644 --- a/api_docs/kbn_core_custom_branding_server_mocks.mdx +++ b/api_docs/kbn_core_custom_branding_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-custom-branding-server-mocks title: "@kbn/core-custom-branding-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-custom-branding-server-mocks plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-custom-branding-server-mocks'] --- import kbnCoreCustomBrandingServerMocksObj from './kbn_core_custom_branding_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_deprecations_browser.mdx b/api_docs/kbn_core_deprecations_browser.mdx index 9fb10153cf758..42631374a0475 100644 --- a/api_docs/kbn_core_deprecations_browser.mdx +++ b/api_docs/kbn_core_deprecations_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-deprecations-browser title: "@kbn/core-deprecations-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-deprecations-browser plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-deprecations-browser'] --- import kbnCoreDeprecationsBrowserObj from './kbn_core_deprecations_browser.devdocs.json'; diff --git a/api_docs/kbn_core_deprecations_browser_internal.mdx b/api_docs/kbn_core_deprecations_browser_internal.mdx index 0adf12f6669ce..5d64a00028055 100644 --- a/api_docs/kbn_core_deprecations_browser_internal.mdx +++ b/api_docs/kbn_core_deprecations_browser_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-deprecations-browser-internal title: "@kbn/core-deprecations-browser-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-deprecations-browser-internal plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-deprecations-browser-internal'] --- import kbnCoreDeprecationsBrowserInternalObj from './kbn_core_deprecations_browser_internal.devdocs.json'; diff --git a/api_docs/kbn_core_deprecations_browser_mocks.mdx b/api_docs/kbn_core_deprecations_browser_mocks.mdx index ef413ec2a18a4..54e08b122da7c 100644 --- a/api_docs/kbn_core_deprecations_browser_mocks.mdx +++ b/api_docs/kbn_core_deprecations_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-deprecations-browser-mocks title: "@kbn/core-deprecations-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-deprecations-browser-mocks plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-deprecations-browser-mocks'] --- import kbnCoreDeprecationsBrowserMocksObj from './kbn_core_deprecations_browser_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_deprecations_common.mdx b/api_docs/kbn_core_deprecations_common.mdx index 5b596f8dd1b81..4e88d025df5c7 100644 --- a/api_docs/kbn_core_deprecations_common.mdx +++ b/api_docs/kbn_core_deprecations_common.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-deprecations-common title: "@kbn/core-deprecations-common" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-deprecations-common plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-deprecations-common'] --- import kbnCoreDeprecationsCommonObj from './kbn_core_deprecations_common.devdocs.json'; diff --git a/api_docs/kbn_core_deprecations_server.mdx b/api_docs/kbn_core_deprecations_server.mdx index 172a72ba2846e..60b80463f5ee6 100644 --- a/api_docs/kbn_core_deprecations_server.mdx +++ b/api_docs/kbn_core_deprecations_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-deprecations-server title: "@kbn/core-deprecations-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-deprecations-server plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-deprecations-server'] --- import kbnCoreDeprecationsServerObj from './kbn_core_deprecations_server.devdocs.json'; diff --git a/api_docs/kbn_core_deprecations_server_internal.mdx b/api_docs/kbn_core_deprecations_server_internal.mdx index 48446688f5c64..452833d3ee1a0 100644 --- a/api_docs/kbn_core_deprecations_server_internal.mdx +++ b/api_docs/kbn_core_deprecations_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-deprecations-server-internal title: "@kbn/core-deprecations-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-deprecations-server-internal plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-deprecations-server-internal'] --- import kbnCoreDeprecationsServerInternalObj from './kbn_core_deprecations_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_deprecations_server_mocks.mdx b/api_docs/kbn_core_deprecations_server_mocks.mdx index 98ae39b05c3f3..d8c4515670f59 100644 --- a/api_docs/kbn_core_deprecations_server_mocks.mdx +++ b/api_docs/kbn_core_deprecations_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-deprecations-server-mocks title: "@kbn/core-deprecations-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-deprecations-server-mocks plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-deprecations-server-mocks'] --- import kbnCoreDeprecationsServerMocksObj from './kbn_core_deprecations_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_doc_links_browser.mdx b/api_docs/kbn_core_doc_links_browser.mdx index fb12929e97f3c..75381ba153baf 100644 --- a/api_docs/kbn_core_doc_links_browser.mdx +++ b/api_docs/kbn_core_doc_links_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-doc-links-browser title: "@kbn/core-doc-links-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-doc-links-browser plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-doc-links-browser'] --- import kbnCoreDocLinksBrowserObj from './kbn_core_doc_links_browser.devdocs.json'; diff --git a/api_docs/kbn_core_doc_links_browser_mocks.mdx b/api_docs/kbn_core_doc_links_browser_mocks.mdx index cef62881232db..5dcd100b6bcff 100644 --- a/api_docs/kbn_core_doc_links_browser_mocks.mdx +++ b/api_docs/kbn_core_doc_links_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-doc-links-browser-mocks title: "@kbn/core-doc-links-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-doc-links-browser-mocks plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-doc-links-browser-mocks'] --- import kbnCoreDocLinksBrowserMocksObj from './kbn_core_doc_links_browser_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_doc_links_server.mdx b/api_docs/kbn_core_doc_links_server.mdx index 47902a1cdb758..8d124fcc286a2 100644 --- a/api_docs/kbn_core_doc_links_server.mdx +++ b/api_docs/kbn_core_doc_links_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-doc-links-server title: "@kbn/core-doc-links-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-doc-links-server plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-doc-links-server'] --- import kbnCoreDocLinksServerObj from './kbn_core_doc_links_server.devdocs.json'; diff --git a/api_docs/kbn_core_doc_links_server_mocks.mdx b/api_docs/kbn_core_doc_links_server_mocks.mdx index 5d322c40ede10..5fc506af57ff0 100644 --- a/api_docs/kbn_core_doc_links_server_mocks.mdx +++ b/api_docs/kbn_core_doc_links_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-doc-links-server-mocks title: "@kbn/core-doc-links-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-doc-links-server-mocks plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-doc-links-server-mocks'] --- import kbnCoreDocLinksServerMocksObj from './kbn_core_doc_links_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_elasticsearch_client_server_internal.mdx b/api_docs/kbn_core_elasticsearch_client_server_internal.mdx index ad8a87f7e3aea..8e7b882d1c6a7 100644 --- a/api_docs/kbn_core_elasticsearch_client_server_internal.mdx +++ b/api_docs/kbn_core_elasticsearch_client_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-elasticsearch-client-server-internal title: "@kbn/core-elasticsearch-client-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-elasticsearch-client-server-internal plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-elasticsearch-client-server-internal'] --- import kbnCoreElasticsearchClientServerInternalObj from './kbn_core_elasticsearch_client_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_elasticsearch_client_server_mocks.mdx b/api_docs/kbn_core_elasticsearch_client_server_mocks.mdx index 89ff82477ba0f..7457d5d227154 100644 --- a/api_docs/kbn_core_elasticsearch_client_server_mocks.mdx +++ b/api_docs/kbn_core_elasticsearch_client_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-elasticsearch-client-server-mocks title: "@kbn/core-elasticsearch-client-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-elasticsearch-client-server-mocks plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-elasticsearch-client-server-mocks'] --- import kbnCoreElasticsearchClientServerMocksObj from './kbn_core_elasticsearch_client_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_elasticsearch_server.mdx b/api_docs/kbn_core_elasticsearch_server.mdx index b1e026be7fb50..0d2c1e7f2059c 100644 --- a/api_docs/kbn_core_elasticsearch_server.mdx +++ b/api_docs/kbn_core_elasticsearch_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-elasticsearch-server title: "@kbn/core-elasticsearch-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-elasticsearch-server plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-elasticsearch-server'] --- import kbnCoreElasticsearchServerObj from './kbn_core_elasticsearch_server.devdocs.json'; diff --git a/api_docs/kbn_core_elasticsearch_server_internal.mdx b/api_docs/kbn_core_elasticsearch_server_internal.mdx index 489d8b8efa2a6..6379de05e289f 100644 --- a/api_docs/kbn_core_elasticsearch_server_internal.mdx +++ b/api_docs/kbn_core_elasticsearch_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-elasticsearch-server-internal title: "@kbn/core-elasticsearch-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-elasticsearch-server-internal plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-elasticsearch-server-internal'] --- import kbnCoreElasticsearchServerInternalObj from './kbn_core_elasticsearch_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_elasticsearch_server_mocks.mdx b/api_docs/kbn_core_elasticsearch_server_mocks.mdx index 4b717d84c0ef8..dcbc695a3b3fa 100644 --- a/api_docs/kbn_core_elasticsearch_server_mocks.mdx +++ b/api_docs/kbn_core_elasticsearch_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-elasticsearch-server-mocks title: "@kbn/core-elasticsearch-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-elasticsearch-server-mocks plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-elasticsearch-server-mocks'] --- import kbnCoreElasticsearchServerMocksObj from './kbn_core_elasticsearch_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_environment_server_internal.mdx b/api_docs/kbn_core_environment_server_internal.mdx index 5f6ed0c26cb27..bcdf8d9bd22a0 100644 --- a/api_docs/kbn_core_environment_server_internal.mdx +++ b/api_docs/kbn_core_environment_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-environment-server-internal title: "@kbn/core-environment-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-environment-server-internal plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-environment-server-internal'] --- import kbnCoreEnvironmentServerInternalObj from './kbn_core_environment_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_environment_server_mocks.mdx b/api_docs/kbn_core_environment_server_mocks.mdx index 51d25ec495b88..7206324114ab9 100644 --- a/api_docs/kbn_core_environment_server_mocks.mdx +++ b/api_docs/kbn_core_environment_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-environment-server-mocks title: "@kbn/core-environment-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-environment-server-mocks plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-environment-server-mocks'] --- import kbnCoreEnvironmentServerMocksObj from './kbn_core_environment_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_execution_context_browser.mdx b/api_docs/kbn_core_execution_context_browser.mdx index b5f9cba27ade4..096402a198b4d 100644 --- a/api_docs/kbn_core_execution_context_browser.mdx +++ b/api_docs/kbn_core_execution_context_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-execution-context-browser title: "@kbn/core-execution-context-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-execution-context-browser plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-execution-context-browser'] --- import kbnCoreExecutionContextBrowserObj from './kbn_core_execution_context_browser.devdocs.json'; diff --git a/api_docs/kbn_core_execution_context_browser_internal.mdx b/api_docs/kbn_core_execution_context_browser_internal.mdx index c17214e6f43c1..271273cfd662e 100644 --- a/api_docs/kbn_core_execution_context_browser_internal.mdx +++ b/api_docs/kbn_core_execution_context_browser_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-execution-context-browser-internal title: "@kbn/core-execution-context-browser-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-execution-context-browser-internal plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-execution-context-browser-internal'] --- import kbnCoreExecutionContextBrowserInternalObj from './kbn_core_execution_context_browser_internal.devdocs.json'; diff --git a/api_docs/kbn_core_execution_context_browser_mocks.mdx b/api_docs/kbn_core_execution_context_browser_mocks.mdx index 220e8f2a6e96f..84a3207c9ff69 100644 --- a/api_docs/kbn_core_execution_context_browser_mocks.mdx +++ b/api_docs/kbn_core_execution_context_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-execution-context-browser-mocks title: "@kbn/core-execution-context-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-execution-context-browser-mocks plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-execution-context-browser-mocks'] --- import kbnCoreExecutionContextBrowserMocksObj from './kbn_core_execution_context_browser_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_execution_context_common.mdx b/api_docs/kbn_core_execution_context_common.mdx index 1faa8e31da383..9b24f22f700df 100644 --- a/api_docs/kbn_core_execution_context_common.mdx +++ b/api_docs/kbn_core_execution_context_common.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-execution-context-common title: "@kbn/core-execution-context-common" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-execution-context-common plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-execution-context-common'] --- import kbnCoreExecutionContextCommonObj from './kbn_core_execution_context_common.devdocs.json'; diff --git a/api_docs/kbn_core_execution_context_server.mdx b/api_docs/kbn_core_execution_context_server.mdx index 689ea3f70c816..29eac7797c723 100644 --- a/api_docs/kbn_core_execution_context_server.mdx +++ b/api_docs/kbn_core_execution_context_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-execution-context-server title: "@kbn/core-execution-context-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-execution-context-server plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-execution-context-server'] --- import kbnCoreExecutionContextServerObj from './kbn_core_execution_context_server.devdocs.json'; diff --git a/api_docs/kbn_core_execution_context_server_internal.mdx b/api_docs/kbn_core_execution_context_server_internal.mdx index c40b5a28b75d2..483133412fccc 100644 --- a/api_docs/kbn_core_execution_context_server_internal.mdx +++ b/api_docs/kbn_core_execution_context_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-execution-context-server-internal title: "@kbn/core-execution-context-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-execution-context-server-internal plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-execution-context-server-internal'] --- import kbnCoreExecutionContextServerInternalObj from './kbn_core_execution_context_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_execution_context_server_mocks.mdx b/api_docs/kbn_core_execution_context_server_mocks.mdx index 42b30a36afb32..2b17bd725e44d 100644 --- a/api_docs/kbn_core_execution_context_server_mocks.mdx +++ b/api_docs/kbn_core_execution_context_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-execution-context-server-mocks title: "@kbn/core-execution-context-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-execution-context-server-mocks plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-execution-context-server-mocks'] --- import kbnCoreExecutionContextServerMocksObj from './kbn_core_execution_context_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_fatal_errors_browser.mdx b/api_docs/kbn_core_fatal_errors_browser.mdx index 1d5fec67f9c5d..6105a55dfe5ab 100644 --- a/api_docs/kbn_core_fatal_errors_browser.mdx +++ b/api_docs/kbn_core_fatal_errors_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-fatal-errors-browser title: "@kbn/core-fatal-errors-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-fatal-errors-browser plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-fatal-errors-browser'] --- import kbnCoreFatalErrorsBrowserObj from './kbn_core_fatal_errors_browser.devdocs.json'; diff --git a/api_docs/kbn_core_fatal_errors_browser_mocks.mdx b/api_docs/kbn_core_fatal_errors_browser_mocks.mdx index c5821c5a33776..b6cace01956e7 100644 --- a/api_docs/kbn_core_fatal_errors_browser_mocks.mdx +++ b/api_docs/kbn_core_fatal_errors_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-fatal-errors-browser-mocks title: "@kbn/core-fatal-errors-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-fatal-errors-browser-mocks plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-fatal-errors-browser-mocks'] --- import kbnCoreFatalErrorsBrowserMocksObj from './kbn_core_fatal_errors_browser_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_http_browser.mdx b/api_docs/kbn_core_http_browser.mdx index 85b722f66acd6..d565af256da31 100644 --- a/api_docs/kbn_core_http_browser.mdx +++ b/api_docs/kbn_core_http_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-http-browser title: "@kbn/core-http-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-http-browser plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-http-browser'] --- import kbnCoreHttpBrowserObj from './kbn_core_http_browser.devdocs.json'; diff --git a/api_docs/kbn_core_http_browser_internal.mdx b/api_docs/kbn_core_http_browser_internal.mdx index 5f7aeb82bad14..ee0109737b0a8 100644 --- a/api_docs/kbn_core_http_browser_internal.mdx +++ b/api_docs/kbn_core_http_browser_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-http-browser-internal title: "@kbn/core-http-browser-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-http-browser-internal plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-http-browser-internal'] --- import kbnCoreHttpBrowserInternalObj from './kbn_core_http_browser_internal.devdocs.json'; diff --git a/api_docs/kbn_core_http_browser_mocks.mdx b/api_docs/kbn_core_http_browser_mocks.mdx index 8d5a1e878e421..e9ccaff15d52d 100644 --- a/api_docs/kbn_core_http_browser_mocks.mdx +++ b/api_docs/kbn_core_http_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-http-browser-mocks title: "@kbn/core-http-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-http-browser-mocks plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-http-browser-mocks'] --- import kbnCoreHttpBrowserMocksObj from './kbn_core_http_browser_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_http_common.mdx b/api_docs/kbn_core_http_common.mdx index e16dfd0ae127b..0e644995d6f83 100644 --- a/api_docs/kbn_core_http_common.mdx +++ b/api_docs/kbn_core_http_common.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-http-common title: "@kbn/core-http-common" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-http-common plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-http-common'] --- import kbnCoreHttpCommonObj from './kbn_core_http_common.devdocs.json'; diff --git a/api_docs/kbn_core_http_context_server_mocks.mdx b/api_docs/kbn_core_http_context_server_mocks.mdx index c13f2ed2810a8..62b9dd47c24c9 100644 --- a/api_docs/kbn_core_http_context_server_mocks.mdx +++ b/api_docs/kbn_core_http_context_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-http-context-server-mocks title: "@kbn/core-http-context-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-http-context-server-mocks plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-http-context-server-mocks'] --- import kbnCoreHttpContextServerMocksObj from './kbn_core_http_context_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_http_request_handler_context_server.mdx b/api_docs/kbn_core_http_request_handler_context_server.mdx index a80e303e1d7df..1971a641f6be5 100644 --- a/api_docs/kbn_core_http_request_handler_context_server.mdx +++ b/api_docs/kbn_core_http_request_handler_context_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-http-request-handler-context-server title: "@kbn/core-http-request-handler-context-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-http-request-handler-context-server plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-http-request-handler-context-server'] --- import kbnCoreHttpRequestHandlerContextServerObj from './kbn_core_http_request_handler_context_server.devdocs.json'; diff --git a/api_docs/kbn_core_http_resources_server.mdx b/api_docs/kbn_core_http_resources_server.mdx index 7c12421639086..69b860357f14b 100644 --- a/api_docs/kbn_core_http_resources_server.mdx +++ b/api_docs/kbn_core_http_resources_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-http-resources-server title: "@kbn/core-http-resources-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-http-resources-server plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-http-resources-server'] --- import kbnCoreHttpResourcesServerObj from './kbn_core_http_resources_server.devdocs.json'; diff --git a/api_docs/kbn_core_http_resources_server_internal.mdx b/api_docs/kbn_core_http_resources_server_internal.mdx index e8fc79ee48463..876e1dea05a28 100644 --- a/api_docs/kbn_core_http_resources_server_internal.mdx +++ b/api_docs/kbn_core_http_resources_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-http-resources-server-internal title: "@kbn/core-http-resources-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-http-resources-server-internal plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-http-resources-server-internal'] --- import kbnCoreHttpResourcesServerInternalObj from './kbn_core_http_resources_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_http_resources_server_mocks.mdx b/api_docs/kbn_core_http_resources_server_mocks.mdx index 4d210d48dc6c0..09ff1e2fd8cdc 100644 --- a/api_docs/kbn_core_http_resources_server_mocks.mdx +++ b/api_docs/kbn_core_http_resources_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-http-resources-server-mocks title: "@kbn/core-http-resources-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-http-resources-server-mocks plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-http-resources-server-mocks'] --- import kbnCoreHttpResourcesServerMocksObj from './kbn_core_http_resources_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_http_router_server_internal.mdx b/api_docs/kbn_core_http_router_server_internal.mdx index 5336a9b9d442a..2c6b9e0ff8e44 100644 --- a/api_docs/kbn_core_http_router_server_internal.mdx +++ b/api_docs/kbn_core_http_router_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-http-router-server-internal title: "@kbn/core-http-router-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-http-router-server-internal plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-http-router-server-internal'] --- import kbnCoreHttpRouterServerInternalObj from './kbn_core_http_router_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_http_router_server_mocks.mdx b/api_docs/kbn_core_http_router_server_mocks.mdx index 05e16bfdfa050..096a789f25489 100644 --- a/api_docs/kbn_core_http_router_server_mocks.mdx +++ b/api_docs/kbn_core_http_router_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-http-router-server-mocks title: "@kbn/core-http-router-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-http-router-server-mocks plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-http-router-server-mocks'] --- import kbnCoreHttpRouterServerMocksObj from './kbn_core_http_router_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_http_server.mdx b/api_docs/kbn_core_http_server.mdx index 3f829ec90ee64..6dad2b3b2c69e 100644 --- a/api_docs/kbn_core_http_server.mdx +++ b/api_docs/kbn_core_http_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-http-server title: "@kbn/core-http-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-http-server plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-http-server'] --- import kbnCoreHttpServerObj from './kbn_core_http_server.devdocs.json'; diff --git a/api_docs/kbn_core_http_server_internal.mdx b/api_docs/kbn_core_http_server_internal.mdx index 8084f9b4f30b9..7d8bf7e023cea 100644 --- a/api_docs/kbn_core_http_server_internal.mdx +++ b/api_docs/kbn_core_http_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-http-server-internal title: "@kbn/core-http-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-http-server-internal plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-http-server-internal'] --- import kbnCoreHttpServerInternalObj from './kbn_core_http_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_http_server_mocks.mdx b/api_docs/kbn_core_http_server_mocks.mdx index abd2681baac31..9f588ffd0224b 100644 --- a/api_docs/kbn_core_http_server_mocks.mdx +++ b/api_docs/kbn_core_http_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-http-server-mocks title: "@kbn/core-http-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-http-server-mocks plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-http-server-mocks'] --- import kbnCoreHttpServerMocksObj from './kbn_core_http_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_i18n_browser.mdx b/api_docs/kbn_core_i18n_browser.mdx index 44e253d88ad99..032fbe0c03de8 100644 --- a/api_docs/kbn_core_i18n_browser.mdx +++ b/api_docs/kbn_core_i18n_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-i18n-browser title: "@kbn/core-i18n-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-i18n-browser plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-i18n-browser'] --- import kbnCoreI18nBrowserObj from './kbn_core_i18n_browser.devdocs.json'; diff --git a/api_docs/kbn_core_i18n_browser_mocks.mdx b/api_docs/kbn_core_i18n_browser_mocks.mdx index 44f17994adaa3..84b487f6c5cc5 100644 --- a/api_docs/kbn_core_i18n_browser_mocks.mdx +++ b/api_docs/kbn_core_i18n_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-i18n-browser-mocks title: "@kbn/core-i18n-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-i18n-browser-mocks plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-i18n-browser-mocks'] --- import kbnCoreI18nBrowserMocksObj from './kbn_core_i18n_browser_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_i18n_server.mdx b/api_docs/kbn_core_i18n_server.mdx index 13e9b6c29312a..d43299bedfde4 100644 --- a/api_docs/kbn_core_i18n_server.mdx +++ b/api_docs/kbn_core_i18n_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-i18n-server title: "@kbn/core-i18n-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-i18n-server plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-i18n-server'] --- import kbnCoreI18nServerObj from './kbn_core_i18n_server.devdocs.json'; diff --git a/api_docs/kbn_core_i18n_server_internal.mdx b/api_docs/kbn_core_i18n_server_internal.mdx index d25bef06f65a0..51d76ed163eeb 100644 --- a/api_docs/kbn_core_i18n_server_internal.mdx +++ b/api_docs/kbn_core_i18n_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-i18n-server-internal title: "@kbn/core-i18n-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-i18n-server-internal plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-i18n-server-internal'] --- import kbnCoreI18nServerInternalObj from './kbn_core_i18n_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_i18n_server_mocks.mdx b/api_docs/kbn_core_i18n_server_mocks.mdx index 46c22e716f96f..676a1e04f9554 100644 --- a/api_docs/kbn_core_i18n_server_mocks.mdx +++ b/api_docs/kbn_core_i18n_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-i18n-server-mocks title: "@kbn/core-i18n-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-i18n-server-mocks plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-i18n-server-mocks'] --- import kbnCoreI18nServerMocksObj from './kbn_core_i18n_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_injected_metadata_browser_mocks.mdx b/api_docs/kbn_core_injected_metadata_browser_mocks.mdx index 097d289d080cd..9ee4e141d1862 100644 --- a/api_docs/kbn_core_injected_metadata_browser_mocks.mdx +++ b/api_docs/kbn_core_injected_metadata_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-injected-metadata-browser-mocks title: "@kbn/core-injected-metadata-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-injected-metadata-browser-mocks plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-injected-metadata-browser-mocks'] --- import kbnCoreInjectedMetadataBrowserMocksObj from './kbn_core_injected_metadata_browser_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_integrations_browser_internal.mdx b/api_docs/kbn_core_integrations_browser_internal.mdx index efe2d3f52e9c9..b48516b422f02 100644 --- a/api_docs/kbn_core_integrations_browser_internal.mdx +++ b/api_docs/kbn_core_integrations_browser_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-integrations-browser-internal title: "@kbn/core-integrations-browser-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-integrations-browser-internal plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-integrations-browser-internal'] --- import kbnCoreIntegrationsBrowserInternalObj from './kbn_core_integrations_browser_internal.devdocs.json'; diff --git a/api_docs/kbn_core_integrations_browser_mocks.mdx b/api_docs/kbn_core_integrations_browser_mocks.mdx index e37fbb1df3a19..f906f4abb9062 100644 --- a/api_docs/kbn_core_integrations_browser_mocks.mdx +++ b/api_docs/kbn_core_integrations_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-integrations-browser-mocks title: "@kbn/core-integrations-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-integrations-browser-mocks plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-integrations-browser-mocks'] --- import kbnCoreIntegrationsBrowserMocksObj from './kbn_core_integrations_browser_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_lifecycle_browser.mdx b/api_docs/kbn_core_lifecycle_browser.mdx index b9697db490fc4..9960b9aa956c5 100644 --- a/api_docs/kbn_core_lifecycle_browser.mdx +++ b/api_docs/kbn_core_lifecycle_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-lifecycle-browser title: "@kbn/core-lifecycle-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-lifecycle-browser plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-lifecycle-browser'] --- import kbnCoreLifecycleBrowserObj from './kbn_core_lifecycle_browser.devdocs.json'; diff --git a/api_docs/kbn_core_lifecycle_browser_mocks.mdx b/api_docs/kbn_core_lifecycle_browser_mocks.mdx index bf7c8bec0dd1c..f5d3cdc39153a 100644 --- a/api_docs/kbn_core_lifecycle_browser_mocks.mdx +++ b/api_docs/kbn_core_lifecycle_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-lifecycle-browser-mocks title: "@kbn/core-lifecycle-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-lifecycle-browser-mocks plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-lifecycle-browser-mocks'] --- import kbnCoreLifecycleBrowserMocksObj from './kbn_core_lifecycle_browser_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_lifecycle_server.mdx b/api_docs/kbn_core_lifecycle_server.mdx index 98fbe044ffb5e..910315776afb0 100644 --- a/api_docs/kbn_core_lifecycle_server.mdx +++ b/api_docs/kbn_core_lifecycle_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-lifecycle-server title: "@kbn/core-lifecycle-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-lifecycle-server plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-lifecycle-server'] --- import kbnCoreLifecycleServerObj from './kbn_core_lifecycle_server.devdocs.json'; diff --git a/api_docs/kbn_core_lifecycle_server_mocks.mdx b/api_docs/kbn_core_lifecycle_server_mocks.mdx index b479c088b870c..0787f48b9426f 100644 --- a/api_docs/kbn_core_lifecycle_server_mocks.mdx +++ b/api_docs/kbn_core_lifecycle_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-lifecycle-server-mocks title: "@kbn/core-lifecycle-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-lifecycle-server-mocks plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-lifecycle-server-mocks'] --- import kbnCoreLifecycleServerMocksObj from './kbn_core_lifecycle_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_logging_browser_mocks.mdx b/api_docs/kbn_core_logging_browser_mocks.mdx index 4a34ce31cb9f3..8d4d6f0b90412 100644 --- a/api_docs/kbn_core_logging_browser_mocks.mdx +++ b/api_docs/kbn_core_logging_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-logging-browser-mocks title: "@kbn/core-logging-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-logging-browser-mocks plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-logging-browser-mocks'] --- import kbnCoreLoggingBrowserMocksObj from './kbn_core_logging_browser_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_logging_common_internal.mdx b/api_docs/kbn_core_logging_common_internal.mdx index 7bcb8e679015c..277cc443001fb 100644 --- a/api_docs/kbn_core_logging_common_internal.mdx +++ b/api_docs/kbn_core_logging_common_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-logging-common-internal title: "@kbn/core-logging-common-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-logging-common-internal plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-logging-common-internal'] --- import kbnCoreLoggingCommonInternalObj from './kbn_core_logging_common_internal.devdocs.json'; diff --git a/api_docs/kbn_core_logging_server.mdx b/api_docs/kbn_core_logging_server.mdx index 05f73906c9355..84defb0441e85 100644 --- a/api_docs/kbn_core_logging_server.mdx +++ b/api_docs/kbn_core_logging_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-logging-server title: "@kbn/core-logging-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-logging-server plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-logging-server'] --- import kbnCoreLoggingServerObj from './kbn_core_logging_server.devdocs.json'; diff --git a/api_docs/kbn_core_logging_server_internal.mdx b/api_docs/kbn_core_logging_server_internal.mdx index 43660ec5faad4..9309f619108e5 100644 --- a/api_docs/kbn_core_logging_server_internal.mdx +++ b/api_docs/kbn_core_logging_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-logging-server-internal title: "@kbn/core-logging-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-logging-server-internal plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-logging-server-internal'] --- import kbnCoreLoggingServerInternalObj from './kbn_core_logging_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_logging_server_mocks.mdx b/api_docs/kbn_core_logging_server_mocks.mdx index 66be47ea5f99a..3b38dac13cff5 100644 --- a/api_docs/kbn_core_logging_server_mocks.mdx +++ b/api_docs/kbn_core_logging_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-logging-server-mocks title: "@kbn/core-logging-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-logging-server-mocks plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-logging-server-mocks'] --- import kbnCoreLoggingServerMocksObj from './kbn_core_logging_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_metrics_collectors_server_internal.mdx b/api_docs/kbn_core_metrics_collectors_server_internal.mdx index 0e09543c50b31..ddbbbdbf9d847 100644 --- a/api_docs/kbn_core_metrics_collectors_server_internal.mdx +++ b/api_docs/kbn_core_metrics_collectors_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-metrics-collectors-server-internal title: "@kbn/core-metrics-collectors-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-metrics-collectors-server-internal plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-metrics-collectors-server-internal'] --- import kbnCoreMetricsCollectorsServerInternalObj from './kbn_core_metrics_collectors_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_metrics_collectors_server_mocks.mdx b/api_docs/kbn_core_metrics_collectors_server_mocks.mdx index 4a13cf123fb93..5d6f41f3454ef 100644 --- a/api_docs/kbn_core_metrics_collectors_server_mocks.mdx +++ b/api_docs/kbn_core_metrics_collectors_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-metrics-collectors-server-mocks title: "@kbn/core-metrics-collectors-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-metrics-collectors-server-mocks plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-metrics-collectors-server-mocks'] --- import kbnCoreMetricsCollectorsServerMocksObj from './kbn_core_metrics_collectors_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_metrics_server.mdx b/api_docs/kbn_core_metrics_server.mdx index e8f06e93c37f2..4981220d86d7c 100644 --- a/api_docs/kbn_core_metrics_server.mdx +++ b/api_docs/kbn_core_metrics_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-metrics-server title: "@kbn/core-metrics-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-metrics-server plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-metrics-server'] --- import kbnCoreMetricsServerObj from './kbn_core_metrics_server.devdocs.json'; diff --git a/api_docs/kbn_core_metrics_server_internal.mdx b/api_docs/kbn_core_metrics_server_internal.mdx index 63ab81459ca7b..c4eafdbd1beb3 100644 --- a/api_docs/kbn_core_metrics_server_internal.mdx +++ b/api_docs/kbn_core_metrics_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-metrics-server-internal title: "@kbn/core-metrics-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-metrics-server-internal plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-metrics-server-internal'] --- import kbnCoreMetricsServerInternalObj from './kbn_core_metrics_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_metrics_server_mocks.mdx b/api_docs/kbn_core_metrics_server_mocks.mdx index 7838597d6d293..d5af2f2e42bdc 100644 --- a/api_docs/kbn_core_metrics_server_mocks.mdx +++ b/api_docs/kbn_core_metrics_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-metrics-server-mocks title: "@kbn/core-metrics-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-metrics-server-mocks plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-metrics-server-mocks'] --- import kbnCoreMetricsServerMocksObj from './kbn_core_metrics_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_mount_utils_browser.mdx b/api_docs/kbn_core_mount_utils_browser.mdx index 615311bc29dc8..d046288c6160f 100644 --- a/api_docs/kbn_core_mount_utils_browser.mdx +++ b/api_docs/kbn_core_mount_utils_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-mount-utils-browser title: "@kbn/core-mount-utils-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-mount-utils-browser plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-mount-utils-browser'] --- import kbnCoreMountUtilsBrowserObj from './kbn_core_mount_utils_browser.devdocs.json'; diff --git a/api_docs/kbn_core_node_server.mdx b/api_docs/kbn_core_node_server.mdx index 5232d8cb78ad4..73562eaf4100d 100644 --- a/api_docs/kbn_core_node_server.mdx +++ b/api_docs/kbn_core_node_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-node-server title: "@kbn/core-node-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-node-server plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-node-server'] --- import kbnCoreNodeServerObj from './kbn_core_node_server.devdocs.json'; diff --git a/api_docs/kbn_core_node_server_internal.mdx b/api_docs/kbn_core_node_server_internal.mdx index 2cacd5d8d120f..64768e0369b80 100644 --- a/api_docs/kbn_core_node_server_internal.mdx +++ b/api_docs/kbn_core_node_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-node-server-internal title: "@kbn/core-node-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-node-server-internal plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-node-server-internal'] --- import kbnCoreNodeServerInternalObj from './kbn_core_node_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_node_server_mocks.mdx b/api_docs/kbn_core_node_server_mocks.mdx index f36039c2d25c6..51f0bf4fa50bb 100644 --- a/api_docs/kbn_core_node_server_mocks.mdx +++ b/api_docs/kbn_core_node_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-node-server-mocks title: "@kbn/core-node-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-node-server-mocks plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-node-server-mocks'] --- import kbnCoreNodeServerMocksObj from './kbn_core_node_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_notifications_browser.mdx b/api_docs/kbn_core_notifications_browser.mdx index 9b1a10ec9cabe..0189a51ebf170 100644 --- a/api_docs/kbn_core_notifications_browser.mdx +++ b/api_docs/kbn_core_notifications_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-notifications-browser title: "@kbn/core-notifications-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-notifications-browser plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-notifications-browser'] --- import kbnCoreNotificationsBrowserObj from './kbn_core_notifications_browser.devdocs.json'; diff --git a/api_docs/kbn_core_notifications_browser_internal.mdx b/api_docs/kbn_core_notifications_browser_internal.mdx index 84109625800d3..b494eaacf1f25 100644 --- a/api_docs/kbn_core_notifications_browser_internal.mdx +++ b/api_docs/kbn_core_notifications_browser_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-notifications-browser-internal title: "@kbn/core-notifications-browser-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-notifications-browser-internal plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-notifications-browser-internal'] --- import kbnCoreNotificationsBrowserInternalObj from './kbn_core_notifications_browser_internal.devdocs.json'; diff --git a/api_docs/kbn_core_notifications_browser_mocks.mdx b/api_docs/kbn_core_notifications_browser_mocks.mdx index 3ece67ecfe23e..17bcc4ae5f263 100644 --- a/api_docs/kbn_core_notifications_browser_mocks.mdx +++ b/api_docs/kbn_core_notifications_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-notifications-browser-mocks title: "@kbn/core-notifications-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-notifications-browser-mocks plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-notifications-browser-mocks'] --- import kbnCoreNotificationsBrowserMocksObj from './kbn_core_notifications_browser_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_overlays_browser.mdx b/api_docs/kbn_core_overlays_browser.mdx index 70e8e32631af0..21983170a2d98 100644 --- a/api_docs/kbn_core_overlays_browser.mdx +++ b/api_docs/kbn_core_overlays_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-overlays-browser title: "@kbn/core-overlays-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-overlays-browser plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-overlays-browser'] --- import kbnCoreOverlaysBrowserObj from './kbn_core_overlays_browser.devdocs.json'; diff --git a/api_docs/kbn_core_overlays_browser_internal.mdx b/api_docs/kbn_core_overlays_browser_internal.mdx index c4ee8f0af23ed..e8708bb109b34 100644 --- a/api_docs/kbn_core_overlays_browser_internal.mdx +++ b/api_docs/kbn_core_overlays_browser_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-overlays-browser-internal title: "@kbn/core-overlays-browser-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-overlays-browser-internal plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-overlays-browser-internal'] --- import kbnCoreOverlaysBrowserInternalObj from './kbn_core_overlays_browser_internal.devdocs.json'; diff --git a/api_docs/kbn_core_overlays_browser_mocks.mdx b/api_docs/kbn_core_overlays_browser_mocks.mdx index 8f62b09f3d70d..a6203b7b48396 100644 --- a/api_docs/kbn_core_overlays_browser_mocks.mdx +++ b/api_docs/kbn_core_overlays_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-overlays-browser-mocks title: "@kbn/core-overlays-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-overlays-browser-mocks plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-overlays-browser-mocks'] --- import kbnCoreOverlaysBrowserMocksObj from './kbn_core_overlays_browser_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_plugins_browser.mdx b/api_docs/kbn_core_plugins_browser.mdx index 19868f7132ca0..d3ad2be011e19 100644 --- a/api_docs/kbn_core_plugins_browser.mdx +++ b/api_docs/kbn_core_plugins_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-plugins-browser title: "@kbn/core-plugins-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-plugins-browser plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-plugins-browser'] --- import kbnCorePluginsBrowserObj from './kbn_core_plugins_browser.devdocs.json'; diff --git a/api_docs/kbn_core_plugins_browser_mocks.mdx b/api_docs/kbn_core_plugins_browser_mocks.mdx index 53a876f7be9cc..76ab6e9cbb3ef 100644 --- a/api_docs/kbn_core_plugins_browser_mocks.mdx +++ b/api_docs/kbn_core_plugins_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-plugins-browser-mocks title: "@kbn/core-plugins-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-plugins-browser-mocks plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-plugins-browser-mocks'] --- import kbnCorePluginsBrowserMocksObj from './kbn_core_plugins_browser_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_plugins_server.mdx b/api_docs/kbn_core_plugins_server.mdx index 332bf82c80a08..ebc8e72e2b5aa 100644 --- a/api_docs/kbn_core_plugins_server.mdx +++ b/api_docs/kbn_core_plugins_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-plugins-server title: "@kbn/core-plugins-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-plugins-server plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-plugins-server'] --- import kbnCorePluginsServerObj from './kbn_core_plugins_server.devdocs.json'; diff --git a/api_docs/kbn_core_plugins_server_mocks.mdx b/api_docs/kbn_core_plugins_server_mocks.mdx index a8e4c60792592..97804d8f88b6f 100644 --- a/api_docs/kbn_core_plugins_server_mocks.mdx +++ b/api_docs/kbn_core_plugins_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-plugins-server-mocks title: "@kbn/core-plugins-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-plugins-server-mocks plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-plugins-server-mocks'] --- import kbnCorePluginsServerMocksObj from './kbn_core_plugins_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_preboot_server.mdx b/api_docs/kbn_core_preboot_server.mdx index b2c3855529fd9..5bbcb2eb43324 100644 --- a/api_docs/kbn_core_preboot_server.mdx +++ b/api_docs/kbn_core_preboot_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-preboot-server title: "@kbn/core-preboot-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-preboot-server plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-preboot-server'] --- import kbnCorePrebootServerObj from './kbn_core_preboot_server.devdocs.json'; diff --git a/api_docs/kbn_core_preboot_server_mocks.mdx b/api_docs/kbn_core_preboot_server_mocks.mdx index b0a38a0bf5dfc..dd69369c9b62a 100644 --- a/api_docs/kbn_core_preboot_server_mocks.mdx +++ b/api_docs/kbn_core_preboot_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-preboot-server-mocks title: "@kbn/core-preboot-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-preboot-server-mocks plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-preboot-server-mocks'] --- import kbnCorePrebootServerMocksObj from './kbn_core_preboot_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_rendering_browser_mocks.mdx b/api_docs/kbn_core_rendering_browser_mocks.mdx index 5982402998dec..d5d2e5e6a5882 100644 --- a/api_docs/kbn_core_rendering_browser_mocks.mdx +++ b/api_docs/kbn_core_rendering_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-rendering-browser-mocks title: "@kbn/core-rendering-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-rendering-browser-mocks plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-rendering-browser-mocks'] --- import kbnCoreRenderingBrowserMocksObj from './kbn_core_rendering_browser_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_rendering_server_internal.mdx b/api_docs/kbn_core_rendering_server_internal.mdx index d2268511570bf..d03ea5d847886 100644 --- a/api_docs/kbn_core_rendering_server_internal.mdx +++ b/api_docs/kbn_core_rendering_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-rendering-server-internal title: "@kbn/core-rendering-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-rendering-server-internal plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-rendering-server-internal'] --- import kbnCoreRenderingServerInternalObj from './kbn_core_rendering_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_rendering_server_mocks.mdx b/api_docs/kbn_core_rendering_server_mocks.mdx index 17d6944eca4b7..925dd5233f046 100644 --- a/api_docs/kbn_core_rendering_server_mocks.mdx +++ b/api_docs/kbn_core_rendering_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-rendering-server-mocks title: "@kbn/core-rendering-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-rendering-server-mocks plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-rendering-server-mocks'] --- import kbnCoreRenderingServerMocksObj from './kbn_core_rendering_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_root_server_internal.mdx b/api_docs/kbn_core_root_server_internal.mdx index 22a0ab6f2c55c..305d67a189190 100644 --- a/api_docs/kbn_core_root_server_internal.mdx +++ b/api_docs/kbn_core_root_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-root-server-internal title: "@kbn/core-root-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-root-server-internal plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-root-server-internal'] --- import kbnCoreRootServerInternalObj from './kbn_core_root_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_saved_objects_api_browser.devdocs.json b/api_docs/kbn_core_saved_objects_api_browser.devdocs.json index 755eca219cfc1..e60c712cdc4b0 100644 --- a/api_docs/kbn_core_saved_objects_api_browser.devdocs.json +++ b/api_docs/kbn_core_saved_objects_api_browser.devdocs.json @@ -3924,18 +3924,6 @@ "plugin": "visualizations", "path": "src/plugins/visualizations/public/utils/saved_objects_utils/find_object_by_title.ts" }, - { - "plugin": "visualizations", - "path": "src/plugins/visualizations/public/wizard/search_selection/show_saved_object.ts" - }, - { - "plugin": "visualizations", - "path": "src/plugins/visualizations/public/wizard/search_selection/show_saved_object.ts" - }, - { - "plugin": "visualizations", - "path": "src/plugins/visualizations/public/wizard/search_selection/show_saved_object.ts" - }, { "plugin": "dashboard", "path": "src/plugins/dashboard/public/services/dashboard_saved_object/lib/find_dashboard_saved_objects.ts" diff --git a/api_docs/kbn_core_saved_objects_api_browser.mdx b/api_docs/kbn_core_saved_objects_api_browser.mdx index 85a73b35a0ac5..32efe8ad69260 100644 --- a/api_docs/kbn_core_saved_objects_api_browser.mdx +++ b/api_docs/kbn_core_saved_objects_api_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-saved-objects-api-browser title: "@kbn/core-saved-objects-api-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-saved-objects-api-browser plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-saved-objects-api-browser'] --- import kbnCoreSavedObjectsApiBrowserObj from './kbn_core_saved_objects_api_browser.devdocs.json'; diff --git a/api_docs/kbn_core_saved_objects_api_server.mdx b/api_docs/kbn_core_saved_objects_api_server.mdx index 2fb2c1582fc11..240959948ca2b 100644 --- a/api_docs/kbn_core_saved_objects_api_server.mdx +++ b/api_docs/kbn_core_saved_objects_api_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-saved-objects-api-server title: "@kbn/core-saved-objects-api-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-saved-objects-api-server plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-saved-objects-api-server'] --- import kbnCoreSavedObjectsApiServerObj from './kbn_core_saved_objects_api_server.devdocs.json'; diff --git a/api_docs/kbn_core_saved_objects_api_server_internal.mdx b/api_docs/kbn_core_saved_objects_api_server_internal.mdx index 1a0d859b5fefd..ee44da841167a 100644 --- a/api_docs/kbn_core_saved_objects_api_server_internal.mdx +++ b/api_docs/kbn_core_saved_objects_api_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-saved-objects-api-server-internal title: "@kbn/core-saved-objects-api-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-saved-objects-api-server-internal plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-saved-objects-api-server-internal'] --- import kbnCoreSavedObjectsApiServerInternalObj from './kbn_core_saved_objects_api_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_saved_objects_api_server_mocks.mdx b/api_docs/kbn_core_saved_objects_api_server_mocks.mdx index 4f04793bf33e0..9592e54773f9c 100644 --- a/api_docs/kbn_core_saved_objects_api_server_mocks.mdx +++ b/api_docs/kbn_core_saved_objects_api_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-saved-objects-api-server-mocks title: "@kbn/core-saved-objects-api-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-saved-objects-api-server-mocks plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-saved-objects-api-server-mocks'] --- import kbnCoreSavedObjectsApiServerMocksObj from './kbn_core_saved_objects_api_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_saved_objects_base_server_internal.devdocs.json b/api_docs/kbn_core_saved_objects_base_server_internal.devdocs.json index 7e50fa4187ed5..6d09966152258 100644 --- a/api_docs/kbn_core_saved_objects_base_server_internal.devdocs.json +++ b/api_docs/kbn_core_saved_objects_base_server_internal.devdocs.json @@ -187,6 +187,60 @@ "returnComment": [], "initialIsOpen": false }, + { + "parentPluginId": "@kbn/core-saved-objects-base-server-internal", + "id": "def-common.compareModelVersions", + "type": "Function", + "tags": [], + "label": "compareModelVersions", + "description": [], + "signature": [ + "({ appVersions, indexVersions, deletedTypes, }: ", + { + "pluginId": "@kbn/core-saved-objects-base-server-internal", + "scope": "common", + "docId": "kibKbnCoreSavedObjectsBaseServerInternalPluginApi", + "section": "def-common.CompareModelVersionMapParams", + "text": "CompareModelVersionMapParams" + }, + ") => ", + { + "pluginId": "@kbn/core-saved-objects-base-server-internal", + "scope": "common", + "docId": "kibKbnCoreSavedObjectsBaseServerInternalPluginApi", + "section": "def-common.CompareModelVersionResult", + "text": "CompareModelVersionResult" + } + ], + "path": "packages/core/saved-objects/core-saved-objects-base-server-internal/src/model_version/version_compare.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/core-saved-objects-base-server-internal", + "id": "def-common.compareModelVersions.$1", + "type": "Object", + "tags": [], + "label": "{\n appVersions,\n indexVersions,\n deletedTypes,\n}", + "description": [], + "signature": [ + { + "pluginId": "@kbn/core-saved-objects-base-server-internal", + "scope": "common", + "docId": "kibKbnCoreSavedObjectsBaseServerInternalPluginApi", + "section": "def-common.CompareModelVersionMapParams", + "text": "CompareModelVersionMapParams" + } + ], + "path": "packages/core/saved-objects/core-saved-objects-base-server-internal/src/model_version/version_compare.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + } + ], + "returnComment": [], + "initialIsOpen": false + }, { "parentPluginId": "@kbn/core-saved-objects-base-server-internal", "id": "def-common.decodeRequestVersion", @@ -401,6 +455,290 @@ "returnComment": [], "initialIsOpen": false }, + { + "parentPluginId": "@kbn/core-saved-objects-base-server-internal", + "id": "def-common.getLatestModelVersion", + "type": "Function", + "tags": [], + "label": "getLatestModelVersion", + "description": [ + "\nReturns the latest registered model version number for the given type." + ], + "signature": [ + "(type: ", + { + "pluginId": "@kbn/core-saved-objects-server", + "scope": "common", + "docId": "kibKbnCoreSavedObjectsServerPluginApi", + "section": "def-common.SavedObjectsType", + "text": "SavedObjectsType" + }, + ") => number" + ], + "path": "packages/core/saved-objects/core-saved-objects-base-server-internal/src/model_version/version_map.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/core-saved-objects-base-server-internal", + "id": "def-common.getLatestModelVersion.$1", + "type": "Object", + "tags": [], + "label": "type", + "description": [], + "signature": [ + { + "pluginId": "@kbn/core-saved-objects-server", + "scope": "common", + "docId": "kibKbnCoreSavedObjectsServerPluginApi", + "section": "def-common.SavedObjectsType", + "text": "SavedObjectsType" + }, + "" + ], + "path": "packages/core/saved-objects/core-saved-objects-base-server-internal/src/model_version/version_map.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + } + ], + "returnComment": [], + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/core-saved-objects-base-server-internal", + "id": "def-common.getModelVersionDelta", + "type": "Function", + "tags": [], + "label": "getModelVersionDelta", + "description": [ + "\nWill generate the difference to go from `currentVersions` to `targetVersions`.\n" + ], + "signature": [ + "({ currentVersions, targetVersions, deletedTypes, }: GetModelVersionDeltaOpts) => ModelVersionDeltaResult" + ], + "path": "packages/core/saved-objects/core-saved-objects-base-server-internal/src/model_version/get_version_delta.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/core-saved-objects-base-server-internal", + "id": "def-common.getModelVersionDelta.$1", + "type": "Object", + "tags": [], + "label": "{\n currentVersions,\n targetVersions,\n deletedTypes,\n}", + "description": [], + "signature": [ + "GetModelVersionDeltaOpts" + ], + "path": "packages/core/saved-objects/core-saved-objects-base-server-internal/src/model_version/get_version_delta.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + } + ], + "returnComment": [], + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/core-saved-objects-base-server-internal", + "id": "def-common.getModelVersionMapForTypes", + "type": "Function", + "tags": [], + "label": "getModelVersionMapForTypes", + "description": [ + "\nBuild a version map for the given types." + ], + "signature": [ + "(types: ", + { + "pluginId": "@kbn/core-saved-objects-server", + "scope": "common", + "docId": "kibKbnCoreSavedObjectsServerPluginApi", + "section": "def-common.SavedObjectsType", + "text": "SavedObjectsType" + }, + "[]) => ", + { + "pluginId": "@kbn/core-saved-objects-base-server-internal", + "scope": "common", + "docId": "kibKbnCoreSavedObjectsBaseServerInternalPluginApi", + "section": "def-common.ModelVersionMap", + "text": "ModelVersionMap" + } + ], + "path": "packages/core/saved-objects/core-saved-objects-base-server-internal/src/model_version/version_map.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/core-saved-objects-base-server-internal", + "id": "def-common.getModelVersionMapForTypes.$1", + "type": "Array", + "tags": [], + "label": "types", + "description": [], + "signature": [ + { + "pluginId": "@kbn/core-saved-objects-server", + "scope": "common", + "docId": "kibKbnCoreSavedObjectsServerPluginApi", + "section": "def-common.SavedObjectsType", + "text": "SavedObjectsType" + }, + "[]" + ], + "path": "packages/core/saved-objects/core-saved-objects-base-server-internal/src/model_version/version_map.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + } + ], + "returnComment": [], + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/core-saved-objects-base-server-internal", + "id": "def-common.getModelVersionsFromMappingMeta", + "type": "Function", + "tags": [], + "label": "getModelVersionsFromMappingMeta", + "description": [ + "\nBuild the version map from the specified source of the provided mappings meta." + ], + "signature": [ + "({ meta, source, }: { meta: ", + "IndexMappingMeta", + "; source: \"mappingVersions\" | \"docVersions\"; }) => ", + { + "pluginId": "@kbn/core-saved-objects-base-server-internal", + "scope": "common", + "docId": "kibKbnCoreSavedObjectsBaseServerInternalPluginApi", + "section": "def-common.ModelVersionMap", + "text": "ModelVersionMap" + }, + " | undefined" + ], + "path": "packages/core/saved-objects/core-saved-objects-base-server-internal/src/model_version/model_version_from_mappings.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/core-saved-objects-base-server-internal", + "id": "def-common.getModelVersionsFromMappingMeta.$1", + "type": "Object", + "tags": [], + "label": "{\n meta,\n source,\n}", + "description": [], + "path": "packages/core/saved-objects/core-saved-objects-base-server-internal/src/model_version/model_version_from_mappings.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/core-saved-objects-base-server-internal", + "id": "def-common.getModelVersionsFromMappingMeta.$1.meta", + "type": "Object", + "tags": [], + "label": "meta", + "description": [], + "signature": [ + "IndexMappingMeta" + ], + "path": "packages/core/saved-objects/core-saved-objects-base-server-internal/src/model_version/model_version_from_mappings.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/core-saved-objects-base-server-internal", + "id": "def-common.getModelVersionsFromMappingMeta.$1.source", + "type": "CompoundType", + "tags": [], + "label": "source", + "description": [], + "signature": [ + "\"mappingVersions\" | \"docVersions\"" + ], + "path": "packages/core/saved-objects/core-saved-objects-base-server-internal/src/model_version/model_version_from_mappings.ts", + "deprecated": false, + "trackAdoption": false + } + ] + } + ], + "returnComment": [], + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/core-saved-objects-base-server-internal", + "id": "def-common.getModelVersionsFromMappings", + "type": "Function", + "tags": [], + "label": "getModelVersionsFromMappings", + "description": [ + "\nBuild the version map from the specified source of the provided mappings." + ], + "signature": [ + "({ mappings, source, }: { mappings: ", + "IndexMapping", + "; source: \"mappingVersions\" | \"docVersions\"; }) => ", + { + "pluginId": "@kbn/core-saved-objects-base-server-internal", + "scope": "common", + "docId": "kibKbnCoreSavedObjectsBaseServerInternalPluginApi", + "section": "def-common.ModelVersionMap", + "text": "ModelVersionMap" + }, + " | undefined" + ], + "path": "packages/core/saved-objects/core-saved-objects-base-server-internal/src/model_version/model_version_from_mappings.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/core-saved-objects-base-server-internal", + "id": "def-common.getModelVersionsFromMappings.$1", + "type": "Object", + "tags": [], + "label": "{\n mappings,\n source,\n}", + "description": [], + "path": "packages/core/saved-objects/core-saved-objects-base-server-internal/src/model_version/model_version_from_mappings.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/core-saved-objects-base-server-internal", + "id": "def-common.getModelVersionsFromMappings.$1.mappings", + "type": "Object", + "tags": [], + "label": "mappings", + "description": [], + "signature": [ + "IndexMapping" + ], + "path": "packages/core/saved-objects/core-saved-objects-base-server-internal/src/model_version/model_version_from_mappings.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/core-saved-objects-base-server-internal", + "id": "def-common.getModelVersionsFromMappings.$1.source", + "type": "CompoundType", + "tags": [], + "label": "source", + "description": [], + "signature": [ + "\"mappingVersions\" | \"docVersions\"" + ], + "path": "packages/core/saved-objects/core-saved-objects-base-server-internal/src/model_version/model_version_from_mappings.ts", + "deprecated": false, + "trackAdoption": false + } + ] + } + ], + "returnComment": [], + "initialIsOpen": false + }, { "parentPluginId": "@kbn/core-saved-objects-base-server-internal", "id": "def-common.getProperty", @@ -704,9 +1042,208 @@ "initialIsOpen": false } ], - "interfaces": [], + "interfaces": [ + { + "parentPluginId": "@kbn/core-saved-objects-base-server-internal", + "id": "def-common.CompareModelVersionDetails", + "type": "Interface", + "tags": [], + "label": "CompareModelVersionDetails", + "description": [], + "path": "packages/core/saved-objects/core-saved-objects-base-server-internal/src/model_version/version_compare.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/core-saved-objects-base-server-internal", + "id": "def-common.CompareModelVersionDetails.greater", + "type": "Array", + "tags": [], + "label": "greater", + "description": [], + "signature": [ + "string[]" + ], + "path": "packages/core/saved-objects/core-saved-objects-base-server-internal/src/model_version/version_compare.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/core-saved-objects-base-server-internal", + "id": "def-common.CompareModelVersionDetails.lesser", + "type": "Array", + "tags": [], + "label": "lesser", + "description": [], + "signature": [ + "string[]" + ], + "path": "packages/core/saved-objects/core-saved-objects-base-server-internal/src/model_version/version_compare.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/core-saved-objects-base-server-internal", + "id": "def-common.CompareModelVersionDetails.equal", + "type": "Array", + "tags": [], + "label": "equal", + "description": [], + "signature": [ + "string[]" + ], + "path": "packages/core/saved-objects/core-saved-objects-base-server-internal/src/model_version/version_compare.ts", + "deprecated": false, + "trackAdoption": false + } + ], + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/core-saved-objects-base-server-internal", + "id": "def-common.CompareModelVersionMapParams", + "type": "Interface", + "tags": [], + "label": "CompareModelVersionMapParams", + "description": [], + "path": "packages/core/saved-objects/core-saved-objects-base-server-internal/src/model_version/version_compare.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/core-saved-objects-base-server-internal", + "id": "def-common.CompareModelVersionMapParams.appVersions", + "type": "Object", + "tags": [], + "label": "appVersions", + "description": [ + "The latest model version of the types registered in the application" + ], + "signature": [ + "{ [x: string]: number; }" + ], + "path": "packages/core/saved-objects/core-saved-objects-base-server-internal/src/model_version/version_compare.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/core-saved-objects-base-server-internal", + "id": "def-common.CompareModelVersionMapParams.indexVersions", + "type": "Object", + "tags": [], + "label": "indexVersions", + "description": [ + "The model version stored in the index" + ], + "signature": [ + "{ [x: string]: number; }" + ], + "path": "packages/core/saved-objects/core-saved-objects-base-server-internal/src/model_version/version_compare.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/core-saved-objects-base-server-internal", + "id": "def-common.CompareModelVersionMapParams.deletedTypes", + "type": "Array", + "tags": [], + "label": "deletedTypes", + "description": [ + "The list of deleted types to exclude during the compare process" + ], + "signature": [ + "string[]" + ], + "path": "packages/core/saved-objects/core-saved-objects-base-server-internal/src/model_version/version_compare.ts", + "deprecated": false, + "trackAdoption": false + } + ], + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/core-saved-objects-base-server-internal", + "id": "def-common.CompareModelVersionResult", + "type": "Interface", + "tags": [], + "label": "CompareModelVersionResult", + "description": [], + "path": "packages/core/saved-objects/core-saved-objects-base-server-internal/src/model_version/version_compare.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/core-saved-objects-base-server-internal", + "id": "def-common.CompareModelVersionResult.status", + "type": "CompoundType", + "tags": [], + "label": "status", + "description": [], + "signature": [ + "\"conflict\" | \"greater\" | \"lesser\" | \"equal\"" + ], + "path": "packages/core/saved-objects/core-saved-objects-base-server-internal/src/model_version/version_compare.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/core-saved-objects-base-server-internal", + "id": "def-common.CompareModelVersionResult.details", + "type": "Object", + "tags": [], + "label": "details", + "description": [], + "signature": [ + { + "pluginId": "@kbn/core-saved-objects-base-server-internal", + "scope": "common", + "docId": "kibKbnCoreSavedObjectsBaseServerInternalPluginApi", + "section": "def-common.CompareModelVersionDetails", + "text": "CompareModelVersionDetails" + } + ], + "path": "packages/core/saved-objects/core-saved-objects-base-server-internal/src/model_version/version_compare.ts", + "deprecated": false, + "trackAdoption": false + } + ], + "initialIsOpen": false + } + ], "enums": [], "misc": [ + { + "parentPluginId": "@kbn/core-saved-objects-base-server-internal", + "id": "def-common.CompareModelVersionStatus", + "type": "Type", + "tags": [], + "label": "CompareModelVersionStatus", + "description": [ + "\nThe overall status of the model version comparison:\n- `greater`: app version is greater than the index version\n- `lesser`: app version is lesser than the index version\n- `equal`: app version is equal to the index version\n- `conflict`: app and index versions are incompatible (versions for some types are higher, and for other types lower)" + ], + "signature": [ + "\"conflict\" | \"greater\" | \"lesser\" | \"equal\"" + ], + "path": "packages/core/saved-objects/core-saved-objects-base-server-internal/src/model_version/version_compare.ts", + "deprecated": false, + "trackAdoption": false, + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/core-saved-objects-base-server-internal", + "id": "def-common.ModelVersionMap", + "type": "Type", + "tags": [], + "label": "ModelVersionMap", + "description": [], + "signature": [ + "{ [x: string]: number; }" + ], + "path": "packages/core/saved-objects/core-saved-objects-base-server-internal/src/model_version/version_map.ts", + "deprecated": false, + "trackAdoption": false, + "initialIsOpen": false + }, { "parentPluginId": "@kbn/core-saved-objects-base-server-internal", "id": "def-common.modelVersionVirtualMajor", diff --git a/api_docs/kbn_core_saved_objects_base_server_internal.mdx b/api_docs/kbn_core_saved_objects_base_server_internal.mdx index 80a5d03bbaa2c..da0d2bfd26e4b 100644 --- a/api_docs/kbn_core_saved_objects_base_server_internal.mdx +++ b/api_docs/kbn_core_saved_objects_base_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-saved-objects-base-server-internal title: "@kbn/core-saved-objects-base-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-saved-objects-base-server-internal plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-saved-objects-base-server-internal'] --- import kbnCoreSavedObjectsBaseServerInternalObj from './kbn_core_saved_objects_base_server_internal.devdocs.json'; @@ -21,7 +21,7 @@ Contact [@elastic/kibana-core](https://github.com/orgs/elastic/teams/kibana-core | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 47 | 0 | 36 | 6 | +| 76 | 0 | 56 | 7 | ## Common @@ -34,6 +34,9 @@ Contact [@elastic/kibana-core](https://github.com/orgs/elastic/teams/kibana-core ### Classes +### Interfaces + + ### Consts, variables and types diff --git a/api_docs/kbn_core_saved_objects_base_server_mocks.mdx b/api_docs/kbn_core_saved_objects_base_server_mocks.mdx index 64faab1dcecfa..83e3c3856a76d 100644 --- a/api_docs/kbn_core_saved_objects_base_server_mocks.mdx +++ b/api_docs/kbn_core_saved_objects_base_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-saved-objects-base-server-mocks title: "@kbn/core-saved-objects-base-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-saved-objects-base-server-mocks plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-saved-objects-base-server-mocks'] --- import kbnCoreSavedObjectsBaseServerMocksObj from './kbn_core_saved_objects_base_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_saved_objects_browser.mdx b/api_docs/kbn_core_saved_objects_browser.mdx index e9ebf8e7f8a0f..4805fe1d1c347 100644 --- a/api_docs/kbn_core_saved_objects_browser.mdx +++ b/api_docs/kbn_core_saved_objects_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-saved-objects-browser title: "@kbn/core-saved-objects-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-saved-objects-browser plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-saved-objects-browser'] --- import kbnCoreSavedObjectsBrowserObj from './kbn_core_saved_objects_browser.devdocs.json'; diff --git a/api_docs/kbn_core_saved_objects_browser_internal.mdx b/api_docs/kbn_core_saved_objects_browser_internal.mdx index 8ff1dcf6be728..b8be2e2f977e1 100644 --- a/api_docs/kbn_core_saved_objects_browser_internal.mdx +++ b/api_docs/kbn_core_saved_objects_browser_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-saved-objects-browser-internal title: "@kbn/core-saved-objects-browser-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-saved-objects-browser-internal plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-saved-objects-browser-internal'] --- import kbnCoreSavedObjectsBrowserInternalObj from './kbn_core_saved_objects_browser_internal.devdocs.json'; diff --git a/api_docs/kbn_core_saved_objects_browser_mocks.mdx b/api_docs/kbn_core_saved_objects_browser_mocks.mdx index b92c10d72dccc..f694fe442d589 100644 --- a/api_docs/kbn_core_saved_objects_browser_mocks.mdx +++ b/api_docs/kbn_core_saved_objects_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-saved-objects-browser-mocks title: "@kbn/core-saved-objects-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-saved-objects-browser-mocks plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-saved-objects-browser-mocks'] --- import kbnCoreSavedObjectsBrowserMocksObj from './kbn_core_saved_objects_browser_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_saved_objects_common.devdocs.json b/api_docs/kbn_core_saved_objects_common.devdocs.json index 62156f3b4dc5a..58fdd0d6a056b 100644 --- a/api_docs/kbn_core_saved_objects_common.devdocs.json +++ b/api_docs/kbn_core_saved_objects_common.devdocs.json @@ -2025,14 +2025,6 @@ "plugin": "visualizations", "path": "src/plugins/visualizations/public/utils/saved_visualize_utils.ts" }, - { - "plugin": "visualizations", - "path": "src/plugins/visualizations/public/wizard/search_selection/show_saved_object.ts" - }, - { - "plugin": "visualizations", - "path": "src/plugins/visualizations/public/wizard/search_selection/show_saved_object.ts" - }, { "plugin": "visualizations", "path": "src/plugins/visualizations/public/embeddable/visualize_embeddable_factory.tsx" diff --git a/api_docs/kbn_core_saved_objects_common.mdx b/api_docs/kbn_core_saved_objects_common.mdx index 6dbdc476177c5..6f11da3455e0b 100644 --- a/api_docs/kbn_core_saved_objects_common.mdx +++ b/api_docs/kbn_core_saved_objects_common.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-saved-objects-common title: "@kbn/core-saved-objects-common" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-saved-objects-common plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-saved-objects-common'] --- import kbnCoreSavedObjectsCommonObj from './kbn_core_saved_objects_common.devdocs.json'; diff --git a/api_docs/kbn_core_saved_objects_import_export_server_internal.mdx b/api_docs/kbn_core_saved_objects_import_export_server_internal.mdx index a5ef1082f2c63..30e717fff16e1 100644 --- a/api_docs/kbn_core_saved_objects_import_export_server_internal.mdx +++ b/api_docs/kbn_core_saved_objects_import_export_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-saved-objects-import-export-server-internal title: "@kbn/core-saved-objects-import-export-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-saved-objects-import-export-server-internal plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-saved-objects-import-export-server-internal'] --- import kbnCoreSavedObjectsImportExportServerInternalObj from './kbn_core_saved_objects_import_export_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_saved_objects_import_export_server_mocks.mdx b/api_docs/kbn_core_saved_objects_import_export_server_mocks.mdx index 074454d4423ac..5e48830159c8c 100644 --- a/api_docs/kbn_core_saved_objects_import_export_server_mocks.mdx +++ b/api_docs/kbn_core_saved_objects_import_export_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-saved-objects-import-export-server-mocks title: "@kbn/core-saved-objects-import-export-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-saved-objects-import-export-server-mocks plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-saved-objects-import-export-server-mocks'] --- import kbnCoreSavedObjectsImportExportServerMocksObj from './kbn_core_saved_objects_import_export_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_saved_objects_migration_server_internal.devdocs.json b/api_docs/kbn_core_saved_objects_migration_server_internal.devdocs.json index 64a0494b9127c..108ffe98f9f8f 100644 --- a/api_docs/kbn_core_saved_objects_migration_server_internal.devdocs.json +++ b/api_docs/kbn_core_saved_objects_migration_server_internal.devdocs.json @@ -1396,7 +1396,7 @@ "\nWait for Elasticsearch to reindex all the changes." ], "signature": [ - "({ client, targetIndex, }: ", + "({ client, index, }: ", "RefreshIndexParams", ") => ", "TaskEither", @@ -1413,7 +1413,7 @@ "id": "def-common.refreshIndex.$1", "type": "Object", "tags": [], - "label": "{\n client,\n targetIndex,\n }", + "label": "{\n client,\n index,\n }", "description": [], "signature": [ "RefreshIndexParams" diff --git a/api_docs/kbn_core_saved_objects_migration_server_internal.mdx b/api_docs/kbn_core_saved_objects_migration_server_internal.mdx index 17974665b6de7..c4f78c6520b05 100644 --- a/api_docs/kbn_core_saved_objects_migration_server_internal.mdx +++ b/api_docs/kbn_core_saved_objects_migration_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-saved-objects-migration-server-internal title: "@kbn/core-saved-objects-migration-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-saved-objects-migration-server-internal plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-saved-objects-migration-server-internal'] --- import kbnCoreSavedObjectsMigrationServerInternalObj from './kbn_core_saved_objects_migration_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_saved_objects_migration_server_mocks.mdx b/api_docs/kbn_core_saved_objects_migration_server_mocks.mdx index 525a4081ec914..0d2a9f736e672 100644 --- a/api_docs/kbn_core_saved_objects_migration_server_mocks.mdx +++ b/api_docs/kbn_core_saved_objects_migration_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-saved-objects-migration-server-mocks title: "@kbn/core-saved-objects-migration-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-saved-objects-migration-server-mocks plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-saved-objects-migration-server-mocks'] --- import kbnCoreSavedObjectsMigrationServerMocksObj from './kbn_core_saved_objects_migration_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_saved_objects_server.mdx b/api_docs/kbn_core_saved_objects_server.mdx index 509b5fb13d55c..f43866ab71b27 100644 --- a/api_docs/kbn_core_saved_objects_server.mdx +++ b/api_docs/kbn_core_saved_objects_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-saved-objects-server title: "@kbn/core-saved-objects-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-saved-objects-server plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-saved-objects-server'] --- import kbnCoreSavedObjectsServerObj from './kbn_core_saved_objects_server.devdocs.json'; diff --git a/api_docs/kbn_core_saved_objects_server_internal.mdx b/api_docs/kbn_core_saved_objects_server_internal.mdx index 80d8f12ad7215..bf4ff00c4abae 100644 --- a/api_docs/kbn_core_saved_objects_server_internal.mdx +++ b/api_docs/kbn_core_saved_objects_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-saved-objects-server-internal title: "@kbn/core-saved-objects-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-saved-objects-server-internal plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-saved-objects-server-internal'] --- import kbnCoreSavedObjectsServerInternalObj from './kbn_core_saved_objects_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_saved_objects_server_mocks.mdx b/api_docs/kbn_core_saved_objects_server_mocks.mdx index e3096bf7bc765..81ee083941f12 100644 --- a/api_docs/kbn_core_saved_objects_server_mocks.mdx +++ b/api_docs/kbn_core_saved_objects_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-saved-objects-server-mocks title: "@kbn/core-saved-objects-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-saved-objects-server-mocks plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-saved-objects-server-mocks'] --- import kbnCoreSavedObjectsServerMocksObj from './kbn_core_saved_objects_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_saved_objects_utils_server.mdx b/api_docs/kbn_core_saved_objects_utils_server.mdx index 77ce037db7b84..7f86eee1a9945 100644 --- a/api_docs/kbn_core_saved_objects_utils_server.mdx +++ b/api_docs/kbn_core_saved_objects_utils_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-saved-objects-utils-server title: "@kbn/core-saved-objects-utils-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-saved-objects-utils-server plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-saved-objects-utils-server'] --- import kbnCoreSavedObjectsUtilsServerObj from './kbn_core_saved_objects_utils_server.devdocs.json'; diff --git a/api_docs/kbn_core_status_common.mdx b/api_docs/kbn_core_status_common.mdx index 7cd2fd7a6b386..977bcb708a7f0 100644 --- a/api_docs/kbn_core_status_common.mdx +++ b/api_docs/kbn_core_status_common.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-status-common title: "@kbn/core-status-common" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-status-common plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-status-common'] --- import kbnCoreStatusCommonObj from './kbn_core_status_common.devdocs.json'; diff --git a/api_docs/kbn_core_status_common_internal.mdx b/api_docs/kbn_core_status_common_internal.mdx index 70e6f24d97ddf..98fa33d379978 100644 --- a/api_docs/kbn_core_status_common_internal.mdx +++ b/api_docs/kbn_core_status_common_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-status-common-internal title: "@kbn/core-status-common-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-status-common-internal plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-status-common-internal'] --- import kbnCoreStatusCommonInternalObj from './kbn_core_status_common_internal.devdocs.json'; diff --git a/api_docs/kbn_core_status_server.mdx b/api_docs/kbn_core_status_server.mdx index 8f41ace88a1ec..e52dfd732b1b0 100644 --- a/api_docs/kbn_core_status_server.mdx +++ b/api_docs/kbn_core_status_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-status-server title: "@kbn/core-status-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-status-server plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-status-server'] --- import kbnCoreStatusServerObj from './kbn_core_status_server.devdocs.json'; diff --git a/api_docs/kbn_core_status_server_internal.mdx b/api_docs/kbn_core_status_server_internal.mdx index 7845f3f7ba8ef..c5256494a2689 100644 --- a/api_docs/kbn_core_status_server_internal.mdx +++ b/api_docs/kbn_core_status_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-status-server-internal title: "@kbn/core-status-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-status-server-internal plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-status-server-internal'] --- import kbnCoreStatusServerInternalObj from './kbn_core_status_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_status_server_mocks.mdx b/api_docs/kbn_core_status_server_mocks.mdx index f1b0fb9379b96..a65e944a7fdc5 100644 --- a/api_docs/kbn_core_status_server_mocks.mdx +++ b/api_docs/kbn_core_status_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-status-server-mocks title: "@kbn/core-status-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-status-server-mocks plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-status-server-mocks'] --- import kbnCoreStatusServerMocksObj from './kbn_core_status_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_test_helpers_deprecations_getters.mdx b/api_docs/kbn_core_test_helpers_deprecations_getters.mdx index ff1d95c673b0a..9a9091b4d0c49 100644 --- a/api_docs/kbn_core_test_helpers_deprecations_getters.mdx +++ b/api_docs/kbn_core_test_helpers_deprecations_getters.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-test-helpers-deprecations-getters title: "@kbn/core-test-helpers-deprecations-getters" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-test-helpers-deprecations-getters plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-test-helpers-deprecations-getters'] --- import kbnCoreTestHelpersDeprecationsGettersObj from './kbn_core_test_helpers_deprecations_getters.devdocs.json'; diff --git a/api_docs/kbn_core_test_helpers_http_setup_browser.mdx b/api_docs/kbn_core_test_helpers_http_setup_browser.mdx index bbffb9df23fa8..b0045b7ebb9ac 100644 --- a/api_docs/kbn_core_test_helpers_http_setup_browser.mdx +++ b/api_docs/kbn_core_test_helpers_http_setup_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-test-helpers-http-setup-browser title: "@kbn/core-test-helpers-http-setup-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-test-helpers-http-setup-browser plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-test-helpers-http-setup-browser'] --- import kbnCoreTestHelpersHttpSetupBrowserObj from './kbn_core_test_helpers_http_setup_browser.devdocs.json'; diff --git a/api_docs/kbn_core_test_helpers_kbn_server.mdx b/api_docs/kbn_core_test_helpers_kbn_server.mdx index bab94a2d606fc..f8d729f20d119 100644 --- a/api_docs/kbn_core_test_helpers_kbn_server.mdx +++ b/api_docs/kbn_core_test_helpers_kbn_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-test-helpers-kbn-server title: "@kbn/core-test-helpers-kbn-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-test-helpers-kbn-server plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-test-helpers-kbn-server'] --- import kbnCoreTestHelpersKbnServerObj from './kbn_core_test_helpers_kbn_server.devdocs.json'; diff --git a/api_docs/kbn_core_test_helpers_so_type_serializer.mdx b/api_docs/kbn_core_test_helpers_so_type_serializer.mdx index 72b1c9dfa6673..9230849caf50a 100644 --- a/api_docs/kbn_core_test_helpers_so_type_serializer.mdx +++ b/api_docs/kbn_core_test_helpers_so_type_serializer.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-test-helpers-so-type-serializer title: "@kbn/core-test-helpers-so-type-serializer" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-test-helpers-so-type-serializer plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-test-helpers-so-type-serializer'] --- import kbnCoreTestHelpersSoTypeSerializerObj from './kbn_core_test_helpers_so_type_serializer.devdocs.json'; diff --git a/api_docs/kbn_core_test_helpers_test_utils.mdx b/api_docs/kbn_core_test_helpers_test_utils.mdx index 3a4c3040ba497..cc6d41c2a8126 100644 --- a/api_docs/kbn_core_test_helpers_test_utils.mdx +++ b/api_docs/kbn_core_test_helpers_test_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-test-helpers-test-utils title: "@kbn/core-test-helpers-test-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-test-helpers-test-utils plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-test-helpers-test-utils'] --- import kbnCoreTestHelpersTestUtilsObj from './kbn_core_test_helpers_test_utils.devdocs.json'; diff --git a/api_docs/kbn_core_theme_browser.mdx b/api_docs/kbn_core_theme_browser.mdx index 0427db028a071..9aa902178dc5c 100644 --- a/api_docs/kbn_core_theme_browser.mdx +++ b/api_docs/kbn_core_theme_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-theme-browser title: "@kbn/core-theme-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-theme-browser plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-theme-browser'] --- import kbnCoreThemeBrowserObj from './kbn_core_theme_browser.devdocs.json'; diff --git a/api_docs/kbn_core_theme_browser_internal.mdx b/api_docs/kbn_core_theme_browser_internal.mdx index 7cbae1548692a..2c0b8fae47cec 100644 --- a/api_docs/kbn_core_theme_browser_internal.mdx +++ b/api_docs/kbn_core_theme_browser_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-theme-browser-internal title: "@kbn/core-theme-browser-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-theme-browser-internal plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-theme-browser-internal'] --- import kbnCoreThemeBrowserInternalObj from './kbn_core_theme_browser_internal.devdocs.json'; diff --git a/api_docs/kbn_core_theme_browser_mocks.mdx b/api_docs/kbn_core_theme_browser_mocks.mdx index af16145c85147..2251e84453f25 100644 --- a/api_docs/kbn_core_theme_browser_mocks.mdx +++ b/api_docs/kbn_core_theme_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-theme-browser-mocks title: "@kbn/core-theme-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-theme-browser-mocks plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-theme-browser-mocks'] --- import kbnCoreThemeBrowserMocksObj from './kbn_core_theme_browser_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_ui_settings_browser.mdx b/api_docs/kbn_core_ui_settings_browser.mdx index 608daa095bc99..9c2b7203691ca 100644 --- a/api_docs/kbn_core_ui_settings_browser.mdx +++ b/api_docs/kbn_core_ui_settings_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-ui-settings-browser title: "@kbn/core-ui-settings-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-ui-settings-browser plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-ui-settings-browser'] --- import kbnCoreUiSettingsBrowserObj from './kbn_core_ui_settings_browser.devdocs.json'; diff --git a/api_docs/kbn_core_ui_settings_browser_internal.mdx b/api_docs/kbn_core_ui_settings_browser_internal.mdx index e49a270f35f31..ba4c38f5b79b1 100644 --- a/api_docs/kbn_core_ui_settings_browser_internal.mdx +++ b/api_docs/kbn_core_ui_settings_browser_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-ui-settings-browser-internal title: "@kbn/core-ui-settings-browser-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-ui-settings-browser-internal plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-ui-settings-browser-internal'] --- import kbnCoreUiSettingsBrowserInternalObj from './kbn_core_ui_settings_browser_internal.devdocs.json'; diff --git a/api_docs/kbn_core_ui_settings_browser_mocks.mdx b/api_docs/kbn_core_ui_settings_browser_mocks.mdx index 9a86f982c6187..03bca26be62b3 100644 --- a/api_docs/kbn_core_ui_settings_browser_mocks.mdx +++ b/api_docs/kbn_core_ui_settings_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-ui-settings-browser-mocks title: "@kbn/core-ui-settings-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-ui-settings-browser-mocks plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-ui-settings-browser-mocks'] --- import kbnCoreUiSettingsBrowserMocksObj from './kbn_core_ui_settings_browser_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_ui_settings_common.mdx b/api_docs/kbn_core_ui_settings_common.mdx index fb4e004a7ae1b..2fea6626cbe16 100644 --- a/api_docs/kbn_core_ui_settings_common.mdx +++ b/api_docs/kbn_core_ui_settings_common.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-ui-settings-common title: "@kbn/core-ui-settings-common" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-ui-settings-common plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-ui-settings-common'] --- import kbnCoreUiSettingsCommonObj from './kbn_core_ui_settings_common.devdocs.json'; diff --git a/api_docs/kbn_core_ui_settings_server.mdx b/api_docs/kbn_core_ui_settings_server.mdx index 588ce97dbda85..4b5260aa63514 100644 --- a/api_docs/kbn_core_ui_settings_server.mdx +++ b/api_docs/kbn_core_ui_settings_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-ui-settings-server title: "@kbn/core-ui-settings-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-ui-settings-server plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-ui-settings-server'] --- import kbnCoreUiSettingsServerObj from './kbn_core_ui_settings_server.devdocs.json'; diff --git a/api_docs/kbn_core_ui_settings_server_internal.mdx b/api_docs/kbn_core_ui_settings_server_internal.mdx index 1a6d0308af22c..4b0d22d61d418 100644 --- a/api_docs/kbn_core_ui_settings_server_internal.mdx +++ b/api_docs/kbn_core_ui_settings_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-ui-settings-server-internal title: "@kbn/core-ui-settings-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-ui-settings-server-internal plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-ui-settings-server-internal'] --- import kbnCoreUiSettingsServerInternalObj from './kbn_core_ui_settings_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_ui_settings_server_mocks.mdx b/api_docs/kbn_core_ui_settings_server_mocks.mdx index 6bcf5453826ef..40d626dfdf8b9 100644 --- a/api_docs/kbn_core_ui_settings_server_mocks.mdx +++ b/api_docs/kbn_core_ui_settings_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-ui-settings-server-mocks title: "@kbn/core-ui-settings-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-ui-settings-server-mocks plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-ui-settings-server-mocks'] --- import kbnCoreUiSettingsServerMocksObj from './kbn_core_ui_settings_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_usage_data_server.mdx b/api_docs/kbn_core_usage_data_server.mdx index 9ceb6bea40e28..acc4c70e26af8 100644 --- a/api_docs/kbn_core_usage_data_server.mdx +++ b/api_docs/kbn_core_usage_data_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-usage-data-server title: "@kbn/core-usage-data-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-usage-data-server plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-usage-data-server'] --- import kbnCoreUsageDataServerObj from './kbn_core_usage_data_server.devdocs.json'; diff --git a/api_docs/kbn_core_usage_data_server_internal.mdx b/api_docs/kbn_core_usage_data_server_internal.mdx index dc32d1702fa8c..b7bf539029799 100644 --- a/api_docs/kbn_core_usage_data_server_internal.mdx +++ b/api_docs/kbn_core_usage_data_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-usage-data-server-internal title: "@kbn/core-usage-data-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-usage-data-server-internal plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-usage-data-server-internal'] --- import kbnCoreUsageDataServerInternalObj from './kbn_core_usage_data_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_usage_data_server_mocks.mdx b/api_docs/kbn_core_usage_data_server_mocks.mdx index 96c91b815c39c..b0ded0ae45d27 100644 --- a/api_docs/kbn_core_usage_data_server_mocks.mdx +++ b/api_docs/kbn_core_usage_data_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-usage-data-server-mocks title: "@kbn/core-usage-data-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-usage-data-server-mocks plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-usage-data-server-mocks'] --- import kbnCoreUsageDataServerMocksObj from './kbn_core_usage_data_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_crypto.mdx b/api_docs/kbn_crypto.mdx index 2f23a2c491f82..c333a0295b8e9 100644 --- a/api_docs/kbn_crypto.mdx +++ b/api_docs/kbn_crypto.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-crypto title: "@kbn/crypto" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/crypto plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/crypto'] --- import kbnCryptoObj from './kbn_crypto.devdocs.json'; diff --git a/api_docs/kbn_crypto_browser.mdx b/api_docs/kbn_crypto_browser.mdx index 3d89a3ec55f34..20852302e88d8 100644 --- a/api_docs/kbn_crypto_browser.mdx +++ b/api_docs/kbn_crypto_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-crypto-browser title: "@kbn/crypto-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/crypto-browser plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/crypto-browser'] --- import kbnCryptoBrowserObj from './kbn_crypto_browser.devdocs.json'; diff --git a/api_docs/kbn_cypress_config.mdx b/api_docs/kbn_cypress_config.mdx index fed22cfee99f8..a72eb6cae6bf9 100644 --- a/api_docs/kbn_cypress_config.mdx +++ b/api_docs/kbn_cypress_config.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-cypress-config title: "@kbn/cypress-config" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/cypress-config plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/cypress-config'] --- import kbnCypressConfigObj from './kbn_cypress_config.devdocs.json'; diff --git a/api_docs/kbn_datemath.mdx b/api_docs/kbn_datemath.mdx index 45fb92bd21992..8df77e19b10f1 100644 --- a/api_docs/kbn_datemath.mdx +++ b/api_docs/kbn_datemath.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-datemath title: "@kbn/datemath" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/datemath plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/datemath'] --- import kbnDatemathObj from './kbn_datemath.devdocs.json'; diff --git a/api_docs/kbn_dev_cli_errors.mdx b/api_docs/kbn_dev_cli_errors.mdx index c5f4d025b125b..08a2711ad0df2 100644 --- a/api_docs/kbn_dev_cli_errors.mdx +++ b/api_docs/kbn_dev_cli_errors.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-dev-cli-errors title: "@kbn/dev-cli-errors" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/dev-cli-errors plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/dev-cli-errors'] --- import kbnDevCliErrorsObj from './kbn_dev_cli_errors.devdocs.json'; diff --git a/api_docs/kbn_dev_cli_runner.mdx b/api_docs/kbn_dev_cli_runner.mdx index 0badcd6a478d2..28614eca38545 100644 --- a/api_docs/kbn_dev_cli_runner.mdx +++ b/api_docs/kbn_dev_cli_runner.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-dev-cli-runner title: "@kbn/dev-cli-runner" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/dev-cli-runner plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/dev-cli-runner'] --- import kbnDevCliRunnerObj from './kbn_dev_cli_runner.devdocs.json'; diff --git a/api_docs/kbn_dev_proc_runner.mdx b/api_docs/kbn_dev_proc_runner.mdx index 7ca84854d2f5b..2e8175a200590 100644 --- a/api_docs/kbn_dev_proc_runner.mdx +++ b/api_docs/kbn_dev_proc_runner.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-dev-proc-runner title: "@kbn/dev-proc-runner" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/dev-proc-runner plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/dev-proc-runner'] --- import kbnDevProcRunnerObj from './kbn_dev_proc_runner.devdocs.json'; diff --git a/api_docs/kbn_dev_utils.mdx b/api_docs/kbn_dev_utils.mdx index 7de2432af1c84..36338cbaea4c9 100644 --- a/api_docs/kbn_dev_utils.mdx +++ b/api_docs/kbn_dev_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-dev-utils title: "@kbn/dev-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/dev-utils plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/dev-utils'] --- import kbnDevUtilsObj from './kbn_dev_utils.devdocs.json'; diff --git a/api_docs/kbn_doc_links.devdocs.json b/api_docs/kbn_doc_links.devdocs.json index 75685baffbc4a..e00902df93b87 100644 --- a/api_docs/kbn_doc_links.devdocs.json +++ b/api_docs/kbn_doc_links.devdocs.json @@ -700,7 +700,7 @@ "label": "maps", "description": [], "signature": [ - "{ readonly guide: string; readonly importGeospatialPrivileges: string; readonly gdalTutorial: string; readonly termJoinsExample: string; }" + "{ readonly connectToEms: string; readonly guide: string; readonly importGeospatialPrivileges: string; readonly gdalTutorial: string; readonly termJoinsExample: string; }" ], "path": "packages/kbn-doc-links/src/types.ts", "deprecated": false, diff --git a/api_docs/kbn_doc_links.mdx b/api_docs/kbn_doc_links.mdx index e95d53afbf334..d1d919ae654c9 100644 --- a/api_docs/kbn_doc_links.mdx +++ b/api_docs/kbn_doc_links.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-doc-links title: "@kbn/doc-links" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/doc-links plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/doc-links'] --- import kbnDocLinksObj from './kbn_doc_links.devdocs.json'; diff --git a/api_docs/kbn_docs_utils.mdx b/api_docs/kbn_docs_utils.mdx index 911e107bc5e02..183ab1fd2b5a8 100644 --- a/api_docs/kbn_docs_utils.mdx +++ b/api_docs/kbn_docs_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-docs-utils title: "@kbn/docs-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/docs-utils plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/docs-utils'] --- import kbnDocsUtilsObj from './kbn_docs_utils.devdocs.json'; diff --git a/api_docs/kbn_ebt_tools.mdx b/api_docs/kbn_ebt_tools.mdx index 3b263802ecb24..443f41099e8e2 100644 --- a/api_docs/kbn_ebt_tools.mdx +++ b/api_docs/kbn_ebt_tools.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ebt-tools title: "@kbn/ebt-tools" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ebt-tools plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ebt-tools'] --- import kbnEbtToolsObj from './kbn_ebt_tools.devdocs.json'; diff --git a/api_docs/kbn_ecs.mdx b/api_docs/kbn_ecs.mdx index 59880df46dd7e..ed97d67fac04c 100644 --- a/api_docs/kbn_ecs.mdx +++ b/api_docs/kbn_ecs.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ecs title: "@kbn/ecs" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ecs plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ecs'] --- import kbnEcsObj from './kbn_ecs.devdocs.json'; diff --git a/api_docs/kbn_ecs_data_quality_dashboard.mdx b/api_docs/kbn_ecs_data_quality_dashboard.mdx index 6b13720734d40..5b6e5e3e9d744 100644 --- a/api_docs/kbn_ecs_data_quality_dashboard.mdx +++ b/api_docs/kbn_ecs_data_quality_dashboard.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ecs-data-quality-dashboard title: "@kbn/ecs-data-quality-dashboard" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ecs-data-quality-dashboard plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ecs-data-quality-dashboard'] --- import kbnEcsDataQualityDashboardObj from './kbn_ecs_data_quality_dashboard.devdocs.json'; diff --git a/api_docs/kbn_es.mdx b/api_docs/kbn_es.mdx index 601aac18068b4..4c229f69031b2 100644 --- a/api_docs/kbn_es.mdx +++ b/api_docs/kbn_es.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-es title: "@kbn/es" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/es plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/es'] --- import kbnEsObj from './kbn_es.devdocs.json'; diff --git a/api_docs/kbn_es_archiver.mdx b/api_docs/kbn_es_archiver.mdx index 2f5d10e84856b..0e07c28700dcc 100644 --- a/api_docs/kbn_es_archiver.mdx +++ b/api_docs/kbn_es_archiver.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-es-archiver title: "@kbn/es-archiver" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/es-archiver plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/es-archiver'] --- import kbnEsArchiverObj from './kbn_es_archiver.devdocs.json'; diff --git a/api_docs/kbn_es_errors.mdx b/api_docs/kbn_es_errors.mdx index 2f6e22208e49f..2d3782b5f9460 100644 --- a/api_docs/kbn_es_errors.mdx +++ b/api_docs/kbn_es_errors.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-es-errors title: "@kbn/es-errors" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/es-errors plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/es-errors'] --- import kbnEsErrorsObj from './kbn_es_errors.devdocs.json'; diff --git a/api_docs/kbn_es_query.mdx b/api_docs/kbn_es_query.mdx index 805ad561888fd..31fff259cfa6f 100644 --- a/api_docs/kbn_es_query.mdx +++ b/api_docs/kbn_es_query.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-es-query title: "@kbn/es-query" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/es-query plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/es-query'] --- import kbnEsQueryObj from './kbn_es_query.devdocs.json'; diff --git a/api_docs/kbn_es_types.mdx b/api_docs/kbn_es_types.mdx index a3c3c33dc33f3..db6380d1adc2e 100644 --- a/api_docs/kbn_es_types.mdx +++ b/api_docs/kbn_es_types.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-es-types title: "@kbn/es-types" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/es-types plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/es-types'] --- import kbnEsTypesObj from './kbn_es_types.devdocs.json'; diff --git a/api_docs/kbn_eslint_plugin_imports.mdx b/api_docs/kbn_eslint_plugin_imports.mdx index 01c421a635cc9..34691767fb1ac 100644 --- a/api_docs/kbn_eslint_plugin_imports.mdx +++ b/api_docs/kbn_eslint_plugin_imports.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-eslint-plugin-imports title: "@kbn/eslint-plugin-imports" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/eslint-plugin-imports plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/eslint-plugin-imports'] --- import kbnEslintPluginImportsObj from './kbn_eslint_plugin_imports.devdocs.json'; diff --git a/api_docs/kbn_expandable_flyout.mdx b/api_docs/kbn_expandable_flyout.mdx index f6685e4647717..c4380f17bc37e 100644 --- a/api_docs/kbn_expandable_flyout.mdx +++ b/api_docs/kbn_expandable_flyout.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-expandable-flyout title: "@kbn/expandable-flyout" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/expandable-flyout plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/expandable-flyout'] --- import kbnExpandableFlyoutObj from './kbn_expandable_flyout.devdocs.json'; diff --git a/api_docs/kbn_field_types.mdx b/api_docs/kbn_field_types.mdx index 96bb2b15e6174..0129006fefa58 100644 --- a/api_docs/kbn_field_types.mdx +++ b/api_docs/kbn_field_types.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-field-types title: "@kbn/field-types" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/field-types plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/field-types'] --- import kbnFieldTypesObj from './kbn_field_types.devdocs.json'; diff --git a/api_docs/kbn_find_used_node_modules.mdx b/api_docs/kbn_find_used_node_modules.mdx index 7afa38b4f962e..807cb42228dd6 100644 --- a/api_docs/kbn_find_used_node_modules.mdx +++ b/api_docs/kbn_find_used_node_modules.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-find-used-node-modules title: "@kbn/find-used-node-modules" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/find-used-node-modules plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/find-used-node-modules'] --- import kbnFindUsedNodeModulesObj from './kbn_find_used_node_modules.devdocs.json'; diff --git a/api_docs/kbn_ftr_common_functional_services.mdx b/api_docs/kbn_ftr_common_functional_services.mdx index ee38bc9239971..97da9eff246d0 100644 --- a/api_docs/kbn_ftr_common_functional_services.mdx +++ b/api_docs/kbn_ftr_common_functional_services.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ftr-common-functional-services title: "@kbn/ftr-common-functional-services" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ftr-common-functional-services plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ftr-common-functional-services'] --- import kbnFtrCommonFunctionalServicesObj from './kbn_ftr_common_functional_services.devdocs.json'; diff --git a/api_docs/kbn_generate.mdx b/api_docs/kbn_generate.mdx index 5eb8f9c9e45d4..08e0b0b284a10 100644 --- a/api_docs/kbn_generate.mdx +++ b/api_docs/kbn_generate.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-generate title: "@kbn/generate" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/generate plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/generate'] --- import kbnGenerateObj from './kbn_generate.devdocs.json'; diff --git a/api_docs/kbn_guided_onboarding.mdx b/api_docs/kbn_guided_onboarding.mdx index 65ff252da824f..f6241782e4d47 100644 --- a/api_docs/kbn_guided_onboarding.mdx +++ b/api_docs/kbn_guided_onboarding.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-guided-onboarding title: "@kbn/guided-onboarding" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/guided-onboarding plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/guided-onboarding'] --- import kbnGuidedOnboardingObj from './kbn_guided_onboarding.devdocs.json'; diff --git a/api_docs/kbn_handlebars.mdx b/api_docs/kbn_handlebars.mdx index 3967e485abc13..ec0993cd6121c 100644 --- a/api_docs/kbn_handlebars.mdx +++ b/api_docs/kbn_handlebars.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-handlebars title: "@kbn/handlebars" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/handlebars plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/handlebars'] --- import kbnHandlebarsObj from './kbn_handlebars.devdocs.json'; diff --git a/api_docs/kbn_hapi_mocks.mdx b/api_docs/kbn_hapi_mocks.mdx index b2dd529e5434e..3c1004cb3ae87 100644 --- a/api_docs/kbn_hapi_mocks.mdx +++ b/api_docs/kbn_hapi_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-hapi-mocks title: "@kbn/hapi-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/hapi-mocks plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/hapi-mocks'] --- import kbnHapiMocksObj from './kbn_hapi_mocks.devdocs.json'; diff --git a/api_docs/kbn_health_gateway_server.mdx b/api_docs/kbn_health_gateway_server.mdx index 3f74803b248e7..b6344957a3810 100644 --- a/api_docs/kbn_health_gateway_server.mdx +++ b/api_docs/kbn_health_gateway_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-health-gateway-server title: "@kbn/health-gateway-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/health-gateway-server plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/health-gateway-server'] --- import kbnHealthGatewayServerObj from './kbn_health_gateway_server.devdocs.json'; diff --git a/api_docs/kbn_home_sample_data_card.mdx b/api_docs/kbn_home_sample_data_card.mdx index af003a70898fa..4c28f12f92227 100644 --- a/api_docs/kbn_home_sample_data_card.mdx +++ b/api_docs/kbn_home_sample_data_card.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-home-sample-data-card title: "@kbn/home-sample-data-card" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/home-sample-data-card plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/home-sample-data-card'] --- import kbnHomeSampleDataCardObj from './kbn_home_sample_data_card.devdocs.json'; diff --git a/api_docs/kbn_home_sample_data_tab.mdx b/api_docs/kbn_home_sample_data_tab.mdx index cea83c79fcfcc..55dda17a9259a 100644 --- a/api_docs/kbn_home_sample_data_tab.mdx +++ b/api_docs/kbn_home_sample_data_tab.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-home-sample-data-tab title: "@kbn/home-sample-data-tab" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/home-sample-data-tab plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/home-sample-data-tab'] --- import kbnHomeSampleDataTabObj from './kbn_home_sample_data_tab.devdocs.json'; diff --git a/api_docs/kbn_i18n.mdx b/api_docs/kbn_i18n.mdx index 8b0aab978b40a..261745025c694 100644 --- a/api_docs/kbn_i18n.mdx +++ b/api_docs/kbn_i18n.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-i18n title: "@kbn/i18n" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/i18n plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/i18n'] --- import kbnI18nObj from './kbn_i18n.devdocs.json'; diff --git a/api_docs/kbn_i18n_react.mdx b/api_docs/kbn_i18n_react.mdx index 088892cc32b26..97b8ed9c2f126 100644 --- a/api_docs/kbn_i18n_react.mdx +++ b/api_docs/kbn_i18n_react.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-i18n-react title: "@kbn/i18n-react" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/i18n-react plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/i18n-react'] --- import kbnI18nReactObj from './kbn_i18n_react.devdocs.json'; diff --git a/api_docs/kbn_import_resolver.mdx b/api_docs/kbn_import_resolver.mdx index 2bbc8879205fd..56150b825a5d3 100644 --- a/api_docs/kbn_import_resolver.mdx +++ b/api_docs/kbn_import_resolver.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-import-resolver title: "@kbn/import-resolver" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/import-resolver plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/import-resolver'] --- import kbnImportResolverObj from './kbn_import_resolver.devdocs.json'; diff --git a/api_docs/kbn_interpreter.mdx b/api_docs/kbn_interpreter.mdx index afcb61ea1c125..68942d1515563 100644 --- a/api_docs/kbn_interpreter.mdx +++ b/api_docs/kbn_interpreter.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-interpreter title: "@kbn/interpreter" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/interpreter plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/interpreter'] --- import kbnInterpreterObj from './kbn_interpreter.devdocs.json'; diff --git a/api_docs/kbn_io_ts_utils.mdx b/api_docs/kbn_io_ts_utils.mdx index f22c38fe790c3..bb5ca935e1d59 100644 --- a/api_docs/kbn_io_ts_utils.mdx +++ b/api_docs/kbn_io_ts_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-io-ts-utils title: "@kbn/io-ts-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/io-ts-utils plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/io-ts-utils'] --- import kbnIoTsUtilsObj from './kbn_io_ts_utils.devdocs.json'; diff --git a/api_docs/kbn_jest_serializers.mdx b/api_docs/kbn_jest_serializers.mdx index eea7d39f4ae74..a6367252c208e 100644 --- a/api_docs/kbn_jest_serializers.mdx +++ b/api_docs/kbn_jest_serializers.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-jest-serializers title: "@kbn/jest-serializers" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/jest-serializers plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/jest-serializers'] --- import kbnJestSerializersObj from './kbn_jest_serializers.devdocs.json'; diff --git a/api_docs/kbn_journeys.mdx b/api_docs/kbn_journeys.mdx index 2e195bd94ac63..0790930d2e41b 100644 --- a/api_docs/kbn_journeys.mdx +++ b/api_docs/kbn_journeys.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-journeys title: "@kbn/journeys" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/journeys plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/journeys'] --- import kbnJourneysObj from './kbn_journeys.devdocs.json'; diff --git a/api_docs/kbn_json_ast.mdx b/api_docs/kbn_json_ast.mdx index 2aaf7417d0c4c..00e09a6989945 100644 --- a/api_docs/kbn_json_ast.mdx +++ b/api_docs/kbn_json_ast.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-json-ast title: "@kbn/json-ast" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/json-ast plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/json-ast'] --- import kbnJsonAstObj from './kbn_json_ast.devdocs.json'; diff --git a/api_docs/kbn_kibana_manifest_schema.mdx b/api_docs/kbn_kibana_manifest_schema.mdx index 1935795608a48..d42540e98bd32 100644 --- a/api_docs/kbn_kibana_manifest_schema.mdx +++ b/api_docs/kbn_kibana_manifest_schema.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-kibana-manifest-schema title: "@kbn/kibana-manifest-schema" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/kibana-manifest-schema plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/kibana-manifest-schema'] --- import kbnKibanaManifestSchemaObj from './kbn_kibana_manifest_schema.devdocs.json'; diff --git a/api_docs/kbn_language_documentation_popover.mdx b/api_docs/kbn_language_documentation_popover.mdx index 35352b24998c9..153b73666905e 100644 --- a/api_docs/kbn_language_documentation_popover.mdx +++ b/api_docs/kbn_language_documentation_popover.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-language-documentation-popover title: "@kbn/language-documentation-popover" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/language-documentation-popover plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/language-documentation-popover'] --- import kbnLanguageDocumentationPopoverObj from './kbn_language_documentation_popover.devdocs.json'; diff --git a/api_docs/kbn_logging.mdx b/api_docs/kbn_logging.mdx index fbd4671d6d654..8fb32c29ab734 100644 --- a/api_docs/kbn_logging.mdx +++ b/api_docs/kbn_logging.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-logging title: "@kbn/logging" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/logging plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/logging'] --- import kbnLoggingObj from './kbn_logging.devdocs.json'; diff --git a/api_docs/kbn_logging_mocks.mdx b/api_docs/kbn_logging_mocks.mdx index 41b2572baffc2..551003b264819 100644 --- a/api_docs/kbn_logging_mocks.mdx +++ b/api_docs/kbn_logging_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-logging-mocks title: "@kbn/logging-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/logging-mocks plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/logging-mocks'] --- import kbnLoggingMocksObj from './kbn_logging_mocks.devdocs.json'; diff --git a/api_docs/kbn_managed_vscode_config.mdx b/api_docs/kbn_managed_vscode_config.mdx index 470f368843e39..628d5d80249c4 100644 --- a/api_docs/kbn_managed_vscode_config.mdx +++ b/api_docs/kbn_managed_vscode_config.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-managed-vscode-config title: "@kbn/managed-vscode-config" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/managed-vscode-config plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/managed-vscode-config'] --- import kbnManagedVscodeConfigObj from './kbn_managed_vscode_config.devdocs.json'; diff --git a/api_docs/kbn_mapbox_gl.mdx b/api_docs/kbn_mapbox_gl.mdx index f1116eb28165c..b21d758b5dbd4 100644 --- a/api_docs/kbn_mapbox_gl.mdx +++ b/api_docs/kbn_mapbox_gl.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-mapbox-gl title: "@kbn/mapbox-gl" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/mapbox-gl plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/mapbox-gl'] --- import kbnMapboxGlObj from './kbn_mapbox_gl.devdocs.json'; diff --git a/api_docs/kbn_ml_agg_utils.devdocs.json b/api_docs/kbn_ml_agg_utils.devdocs.json index b476ff02578e4..022dfb35db093 100644 --- a/api_docs/kbn_ml_agg_utils.devdocs.json +++ b/api_docs/kbn_ml_agg_utils.devdocs.json @@ -631,29 +631,12 @@ }, { "parentPluginId": "@kbn/ml-agg-utils", - "id": "def-common.ChangePoint", + "id": "def-common.FieldValuePair", "type": "Interface", "tags": [], - "label": "ChangePoint", + "label": "FieldValuePair", "description": [ - "\nChange point meta data for a field/value pair." - ], - "signature": [ - { - "pluginId": "@kbn/ml-agg-utils", - "scope": "common", - "docId": "kibKbnMlAggUtilsPluginApi", - "section": "def-common.ChangePoint", - "text": "ChangePoint" - }, - " extends ", - { - "pluginId": "@kbn/ml-agg-utils", - "scope": "common", - "docId": "kibKbnMlAggUtilsPluginApi", - "section": "def-common.FieldValuePair", - "text": "FieldValuePair" - } + "\nField/value pair definition." ], "path": "x-pack/packages/ml/agg_utils/src/types.ts", "deprecated": false, @@ -661,32 +644,10 @@ "children": [ { "parentPluginId": "@kbn/ml-agg-utils", - "id": "def-common.ChangePoint.doc_count", - "type": "number", - "tags": [], - "label": "doc_count", - "description": [], - "path": "x-pack/packages/ml/agg_utils/src/types.ts", - "deprecated": false, - "trackAdoption": false - }, - { - "parentPluginId": "@kbn/ml-agg-utils", - "id": "def-common.ChangePoint.bg_count", - "type": "number", - "tags": [], - "label": "bg_count", - "description": [], - "path": "x-pack/packages/ml/agg_utils/src/types.ts", - "deprecated": false, - "trackAdoption": false - }, - { - "parentPluginId": "@kbn/ml-agg-utils", - "id": "def-common.ChangePoint.total_doc_count", - "type": "number", + "id": "def-common.FieldValuePair.fieldName", + "type": "string", "tags": [], - "label": "total_doc_count", + "label": "fieldName", "description": [], "path": "x-pack/packages/ml/agg_utils/src/types.ts", "deprecated": false, @@ -694,21 +655,40 @@ }, { "parentPluginId": "@kbn/ml-agg-utils", - "id": "def-common.ChangePoint.total_bg_count", - "type": "number", + "id": "def-common.FieldValuePair.fieldValue", + "type": "CompoundType", "tags": [], - "label": "total_bg_count", + "label": "fieldValue", "description": [], + "signature": [ + "string | number" + ], "path": "x-pack/packages/ml/agg_utils/src/types.ts", "deprecated": false, "trackAdoption": false - }, + } + ], + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/ml-agg-utils", + "id": "def-common.HistogramField", + "type": "Interface", + "tags": [], + "label": "HistogramField", + "description": [ + "\nParameters to identify which histogram data needs to be generated for a field." + ], + "path": "x-pack/packages/ml/agg_utils/src/types.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ { "parentPluginId": "@kbn/ml-agg-utils", - "id": "def-common.ChangePoint.score", - "type": "number", + "id": "def-common.HistogramField.fieldName", + "type": "string", "tags": [], - "label": "score", + "label": "fieldName", "description": [], "path": "x-pack/packages/ml/agg_utils/src/types.ts", "deprecated": false, @@ -716,61 +696,68 @@ }, { "parentPluginId": "@kbn/ml-agg-utils", - "id": "def-common.ChangePoint.pValue", - "type": "CompoundType", + "id": "def-common.HistogramField.type", + "type": "Enum", "tags": [], - "label": "pValue", + "label": "type", "description": [], "signature": [ - "number | null" + { + "pluginId": "@kbn/field-types", + "scope": "common", + "docId": "kibKbnFieldTypesPluginApi", + "section": "def-common.KBN_FIELD_TYPES", + "text": "KBN_FIELD_TYPES" + } ], "path": "x-pack/packages/ml/agg_utils/src/types.ts", "deprecated": false, "trackAdoption": false - }, + } + ], + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/ml-agg-utils", + "id": "def-common.NumberValidationResult", + "type": "Interface", + "tags": [], + "label": "NumberValidationResult", + "description": [], + "path": "x-pack/packages/ml/agg_utils/src/validate_number.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ { "parentPluginId": "@kbn/ml-agg-utils", - "id": "def-common.ChangePoint.normalizedScore", - "type": "number", + "id": "def-common.NumberValidationResult.min", + "type": "boolean", "tags": [], - "label": "normalizedScore", + "label": "min", "description": [], - "path": "x-pack/packages/ml/agg_utils/src/types.ts", + "path": "x-pack/packages/ml/agg_utils/src/validate_number.ts", "deprecated": false, "trackAdoption": false }, { "parentPluginId": "@kbn/ml-agg-utils", - "id": "def-common.ChangePoint.histogram", - "type": "Array", + "id": "def-common.NumberValidationResult.max", + "type": "boolean", "tags": [], - "label": "histogram", + "label": "max", "description": [], - "signature": [ - { - "pluginId": "@kbn/ml-agg-utils", - "scope": "common", - "docId": "kibKbnMlAggUtilsPluginApi", - "section": "def-common.ChangePointHistogramItem", - "text": "ChangePointHistogramItem" - }, - "[] | undefined" - ], - "path": "x-pack/packages/ml/agg_utils/src/types.ts", + "path": "x-pack/packages/ml/agg_utils/src/validate_number.ts", "deprecated": false, "trackAdoption": false }, { "parentPluginId": "@kbn/ml-agg-utils", - "id": "def-common.ChangePoint.unique", - "type": "CompoundType", + "id": "def-common.NumberValidationResult.integerOnly", + "type": "boolean", "tags": [], - "label": "unique", + "label": "integerOnly", "description": [], - "signature": [ - "boolean | undefined" - ], - "path": "x-pack/packages/ml/agg_utils/src/types.ts", + "path": "x-pack/packages/ml/agg_utils/src/validate_number.ts", "deprecated": false, "trackAdoption": false } @@ -779,85 +766,78 @@ }, { "parentPluginId": "@kbn/ml-agg-utils", - "id": "def-common.ChangePointGroup", + "id": "def-common.NumericChartData", "type": "Interface", "tags": [], - "label": "ChangePointGroup", + "label": "NumericChartData", "description": [ - "\nTree leaves" + "\nInterface to describe the data structure returned for numeric based charts." ], - "path": "x-pack/packages/ml/agg_utils/src/types.ts", + "path": "x-pack/packages/ml/agg_utils/src/fetch_histograms_for_fields.ts", "deprecated": false, "trackAdoption": false, "children": [ { "parentPluginId": "@kbn/ml-agg-utils", - "id": "def-common.ChangePointGroup.id", - "type": "string", + "id": "def-common.NumericChartData.data", + "type": "Array", "tags": [], - "label": "id", + "label": "data", "description": [], - "path": "x-pack/packages/ml/agg_utils/src/types.ts", + "signature": [ + "NumericDataItem[]" + ], + "path": "x-pack/packages/ml/agg_utils/src/fetch_histograms_for_fields.ts", "deprecated": false, "trackAdoption": false }, { "parentPluginId": "@kbn/ml-agg-utils", - "id": "def-common.ChangePointGroup.group", - "type": "Array", + "id": "def-common.NumericChartData.id", + "type": "string", "tags": [], - "label": "group", + "label": "id", "description": [], - "signature": [ - "ChangePointGroupItem[]" - ], - "path": "x-pack/packages/ml/agg_utils/src/types.ts", + "path": "x-pack/packages/ml/agg_utils/src/fetch_histograms_for_fields.ts", "deprecated": false, "trackAdoption": false }, { "parentPluginId": "@kbn/ml-agg-utils", - "id": "def-common.ChangePointGroup.docCount", + "id": "def-common.NumericChartData.interval", "type": "number", "tags": [], - "label": "docCount", + "label": "interval", "description": [], - "path": "x-pack/packages/ml/agg_utils/src/types.ts", + "path": "x-pack/packages/ml/agg_utils/src/fetch_histograms_for_fields.ts", "deprecated": false, "trackAdoption": false }, { "parentPluginId": "@kbn/ml-agg-utils", - "id": "def-common.ChangePointGroup.pValue", - "type": "CompoundType", + "id": "def-common.NumericChartData.stats", + "type": "Object", "tags": [], - "label": "pValue", + "label": "stats", "description": [], "signature": [ - "number | null" + "[number, number]" ], - "path": "x-pack/packages/ml/agg_utils/src/types.ts", + "path": "x-pack/packages/ml/agg_utils/src/fetch_histograms_for_fields.ts", "deprecated": false, "trackAdoption": false }, { "parentPluginId": "@kbn/ml-agg-utils", - "id": "def-common.ChangePointGroup.histogram", - "type": "Array", + "id": "def-common.NumericChartData.type", + "type": "string", "tags": [], - "label": "histogram", + "label": "type", "description": [], "signature": [ - { - "pluginId": "@kbn/ml-agg-utils", - "scope": "common", - "docId": "kibKbnMlAggUtilsPluginApi", - "section": "def-common.ChangePointHistogramItem", - "text": "ChangePointHistogramItem" - }, - "[] | undefined" + "\"numeric\"" ], - "path": "x-pack/packages/ml/agg_utils/src/types.ts", + "path": "x-pack/packages/ml/agg_utils/src/fetch_histograms_for_fields.ts", "deprecated": false, "trackAdoption": false } @@ -866,12 +846,12 @@ }, { "parentPluginId": "@kbn/ml-agg-utils", - "id": "def-common.ChangePointGroupHistogram", + "id": "def-common.NumericColumnStats", "type": "Interface", "tags": [], - "label": "ChangePointGroupHistogram", + "label": "NumericColumnStats", "description": [ - "\nChange point histogram data for a group of field/value pairs." + "\nInterface to describe attributes used for histograms." ], "path": "x-pack/packages/ml/agg_utils/src/types.ts", "deprecated": false, @@ -879,10 +859,10 @@ "children": [ { "parentPluginId": "@kbn/ml-agg-utils", - "id": "def-common.ChangePointGroupHistogram.id", - "type": "string", + "id": "def-common.NumericColumnStats.interval", + "type": "number", "tags": [], - "label": "id", + "label": "interval", "description": [], "path": "x-pack/packages/ml/agg_utils/src/types.ts", "deprecated": false, @@ -890,21 +870,22 @@ }, { "parentPluginId": "@kbn/ml-agg-utils", - "id": "def-common.ChangePointGroupHistogram.histogram", - "type": "Array", + "id": "def-common.NumericColumnStats.min", + "type": "number", "tags": [], - "label": "histogram", + "label": "min", + "description": [], + "path": "x-pack/packages/ml/agg_utils/src/types.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/ml-agg-utils", + "id": "def-common.NumericColumnStats.max", + "type": "number", + "tags": [], + "label": "max", "description": [], - "signature": [ - { - "pluginId": "@kbn/ml-agg-utils", - "scope": "common", - "docId": "kibKbnMlAggUtilsPluginApi", - "section": "def-common.ChangePointHistogramItem", - "text": "ChangePointHistogramItem" - }, - "[]" - ], "path": "x-pack/packages/ml/agg_utils/src/types.ts", "deprecated": false, "trackAdoption": false @@ -914,52 +895,60 @@ }, { "parentPluginId": "@kbn/ml-agg-utils", - "id": "def-common.ChangePointHistogram", + "id": "def-common.NumericHistogramField", "type": "Interface", "tags": [], - "label": "ChangePointHistogram", + "label": "NumericHistogramField", "description": [ - "\nChange point histogram data for a field/value pair." + "\nNumeric based histogram field interface, limited to `date` and `number`." ], "signature": [ { "pluginId": "@kbn/ml-agg-utils", "scope": "common", "docId": "kibKbnMlAggUtilsPluginApi", - "section": "def-common.ChangePointHistogram", - "text": "ChangePointHistogram" + "section": "def-common.NumericHistogramField", + "text": "NumericHistogramField" }, " extends ", { "pluginId": "@kbn/ml-agg-utils", "scope": "common", "docId": "kibKbnMlAggUtilsPluginApi", - "section": "def-common.FieldValuePair", - "text": "FieldValuePair" + "section": "def-common.HistogramField", + "text": "HistogramField" } ], - "path": "x-pack/packages/ml/agg_utils/src/types.ts", + "path": "x-pack/packages/ml/agg_utils/src/fetch_histograms_for_fields.ts", "deprecated": false, "trackAdoption": false, "children": [ { "parentPluginId": "@kbn/ml-agg-utils", - "id": "def-common.ChangePointHistogram.histogram", - "type": "Array", + "id": "def-common.NumericHistogramField.type", + "type": "CompoundType", "tags": [], - "label": "histogram", + "label": "type", "description": [], "signature": [ { - "pluginId": "@kbn/ml-agg-utils", + "pluginId": "@kbn/field-types", "scope": "common", - "docId": "kibKbnMlAggUtilsPluginApi", - "section": "def-common.ChangePointHistogramItem", - "text": "ChangePointHistogramItem" + "docId": "kibKbnFieldTypesPluginApi", + "section": "def-common.KBN_FIELD_TYPES", + "text": "KBN_FIELD_TYPES" }, - "[]" + ".DATE | ", + { + "pluginId": "@kbn/field-types", + "scope": "common", + "docId": "kibKbnFieldTypesPluginApi", + "section": "def-common.KBN_FIELD_TYPES", + "text": "KBN_FIELD_TYPES" + }, + ".NUMBER" ], - "path": "x-pack/packages/ml/agg_utils/src/types.ts", + "path": "x-pack/packages/ml/agg_utils/src/fetch_histograms_for_fields.ts", "deprecated": false, "trackAdoption": false } @@ -968,23 +957,40 @@ }, { "parentPluginId": "@kbn/ml-agg-utils", - "id": "def-common.ChangePointHistogramItem", + "id": "def-common.SignificantTerm", "type": "Interface", "tags": [], - "label": "ChangePointHistogramItem", + "label": "SignificantTerm", "description": [ - "\nChange point histogram data item." + "\nSignificant term meta data for a field/value pair." ], - "path": "x-pack/packages/ml/agg_utils/src/types.ts", - "deprecated": false, + "signature": [ + { + "pluginId": "@kbn/ml-agg-utils", + "scope": "common", + "docId": "kibKbnMlAggUtilsPluginApi", + "section": "def-common.SignificantTerm", + "text": "SignificantTerm" + }, + " extends ", + { + "pluginId": "@kbn/ml-agg-utils", + "scope": "common", + "docId": "kibKbnMlAggUtilsPluginApi", + "section": "def-common.FieldValuePair", + "text": "FieldValuePair" + } + ], + "path": "x-pack/packages/ml/agg_utils/src/types.ts", + "deprecated": false, "trackAdoption": false, "children": [ { "parentPluginId": "@kbn/ml-agg-utils", - "id": "def-common.ChangePointHistogramItem.doc_count_overall", + "id": "def-common.SignificantTerm.doc_count", "type": "number", "tags": [], - "label": "doc_count_overall", + "label": "doc_count", "description": [], "path": "x-pack/packages/ml/agg_utils/src/types.ts", "deprecated": false, @@ -992,10 +998,10 @@ }, { "parentPluginId": "@kbn/ml-agg-utils", - "id": "def-common.ChangePointHistogramItem.doc_count_change_point", + "id": "def-common.SignificantTerm.bg_count", "type": "number", "tags": [], - "label": "doc_count_change_point", + "label": "bg_count", "description": [], "path": "x-pack/packages/ml/agg_utils/src/types.ts", "deprecated": false, @@ -1003,10 +1009,10 @@ }, { "parentPluginId": "@kbn/ml-agg-utils", - "id": "def-common.ChangePointHistogramItem.key", + "id": "def-common.SignificantTerm.total_doc_count", "type": "number", "tags": [], - "label": "key", + "label": "total_doc_count", "description": [], "path": "x-pack/packages/ml/agg_utils/src/types.ts", "deprecated": false, @@ -1014,37 +1020,21 @@ }, { "parentPluginId": "@kbn/ml-agg-utils", - "id": "def-common.ChangePointHistogramItem.key_as_string", - "type": "string", + "id": "def-common.SignificantTerm.total_bg_count", + "type": "number", "tags": [], - "label": "key_as_string", + "label": "total_bg_count", "description": [], "path": "x-pack/packages/ml/agg_utils/src/types.ts", "deprecated": false, "trackAdoption": false - } - ], - "initialIsOpen": false - }, - { - "parentPluginId": "@kbn/ml-agg-utils", - "id": "def-common.FieldValuePair", - "type": "Interface", - "tags": [], - "label": "FieldValuePair", - "description": [ - "\nField/value pair definition." - ], - "path": "x-pack/packages/ml/agg_utils/src/types.ts", - "deprecated": false, - "trackAdoption": false, - "children": [ + }, { "parentPluginId": "@kbn/ml-agg-utils", - "id": "def-common.FieldValuePair.fieldName", - "type": "string", + "id": "def-common.SignificantTerm.score", + "type": "number", "tags": [], - "label": "fieldName", + "label": "score", "description": [], "path": "x-pack/packages/ml/agg_utils/src/types.ts", "deprecated": false, @@ -1052,40 +1042,24 @@ }, { "parentPluginId": "@kbn/ml-agg-utils", - "id": "def-common.FieldValuePair.fieldValue", + "id": "def-common.SignificantTerm.pValue", "type": "CompoundType", "tags": [], - "label": "fieldValue", + "label": "pValue", "description": [], "signature": [ - "string | number" + "number | null" ], "path": "x-pack/packages/ml/agg_utils/src/types.ts", "deprecated": false, "trackAdoption": false - } - ], - "initialIsOpen": false - }, - { - "parentPluginId": "@kbn/ml-agg-utils", - "id": "def-common.HistogramField", - "type": "Interface", - "tags": [], - "label": "HistogramField", - "description": [ - "\nParameters to identify which histogram data needs to be generated for a field." - ], - "path": "x-pack/packages/ml/agg_utils/src/types.ts", - "deprecated": false, - "trackAdoption": false, - "children": [ + }, { "parentPluginId": "@kbn/ml-agg-utils", - "id": "def-common.HistogramField.fieldName", - "type": "string", + "id": "def-common.SignificantTerm.normalizedScore", + "type": "number", "tags": [], - "label": "fieldName", + "label": "normalizedScore", "description": [], "path": "x-pack/packages/ml/agg_utils/src/types.ts", "deprecated": false, @@ -1093,68 +1067,36 @@ }, { "parentPluginId": "@kbn/ml-agg-utils", - "id": "def-common.HistogramField.type", - "type": "Enum", + "id": "def-common.SignificantTerm.histogram", + "type": "Array", "tags": [], - "label": "type", + "label": "histogram", "description": [], "signature": [ { - "pluginId": "@kbn/field-types", + "pluginId": "@kbn/ml-agg-utils", "scope": "common", - "docId": "kibKbnFieldTypesPluginApi", - "section": "def-common.KBN_FIELD_TYPES", - "text": "KBN_FIELD_TYPES" - } + "docId": "kibKbnMlAggUtilsPluginApi", + "section": "def-common.SignificantTermHistogramItem", + "text": "SignificantTermHistogramItem" + }, + "[] | undefined" ], "path": "x-pack/packages/ml/agg_utils/src/types.ts", "deprecated": false, "trackAdoption": false - } - ], - "initialIsOpen": false - }, - { - "parentPluginId": "@kbn/ml-agg-utils", - "id": "def-common.NumberValidationResult", - "type": "Interface", - "tags": [], - "label": "NumberValidationResult", - "description": [], - "path": "x-pack/packages/ml/agg_utils/src/validate_number.ts", - "deprecated": false, - "trackAdoption": false, - "children": [ - { - "parentPluginId": "@kbn/ml-agg-utils", - "id": "def-common.NumberValidationResult.min", - "type": "boolean", - "tags": [], - "label": "min", - "description": [], - "path": "x-pack/packages/ml/agg_utils/src/validate_number.ts", - "deprecated": false, - "trackAdoption": false }, { "parentPluginId": "@kbn/ml-agg-utils", - "id": "def-common.NumberValidationResult.max", - "type": "boolean", - "tags": [], - "label": "max", - "description": [], - "path": "x-pack/packages/ml/agg_utils/src/validate_number.ts", - "deprecated": false, - "trackAdoption": false - }, - { - "parentPluginId": "@kbn/ml-agg-utils", - "id": "def-common.NumberValidationResult.integerOnly", - "type": "boolean", + "id": "def-common.SignificantTerm.unique", + "type": "CompoundType", "tags": [], - "label": "integerOnly", + "label": "unique", "description": [], - "path": "x-pack/packages/ml/agg_utils/src/validate_number.ts", + "signature": [ + "boolean | undefined" + ], + "path": "x-pack/packages/ml/agg_utils/src/types.ts", "deprecated": false, "trackAdoption": false } @@ -1163,78 +1105,85 @@ }, { "parentPluginId": "@kbn/ml-agg-utils", - "id": "def-common.NumericChartData", + "id": "def-common.SignificantTermGroup", "type": "Interface", "tags": [], - "label": "NumericChartData", + "label": "SignificantTermGroup", "description": [ - "\nInterface to describe the data structure returned for numeric based charts." + "\nTree leaves" ], - "path": "x-pack/packages/ml/agg_utils/src/fetch_histograms_for_fields.ts", + "path": "x-pack/packages/ml/agg_utils/src/types.ts", "deprecated": false, "trackAdoption": false, "children": [ { "parentPluginId": "@kbn/ml-agg-utils", - "id": "def-common.NumericChartData.data", - "type": "Array", + "id": "def-common.SignificantTermGroup.id", + "type": "string", "tags": [], - "label": "data", + "label": "id", "description": [], - "signature": [ - "NumericDataItem[]" - ], - "path": "x-pack/packages/ml/agg_utils/src/fetch_histograms_for_fields.ts", + "path": "x-pack/packages/ml/agg_utils/src/types.ts", "deprecated": false, "trackAdoption": false }, { "parentPluginId": "@kbn/ml-agg-utils", - "id": "def-common.NumericChartData.id", - "type": "string", + "id": "def-common.SignificantTermGroup.group", + "type": "Array", "tags": [], - "label": "id", + "label": "group", "description": [], - "path": "x-pack/packages/ml/agg_utils/src/fetch_histograms_for_fields.ts", + "signature": [ + "SignificantTermGroupItem[]" + ], + "path": "x-pack/packages/ml/agg_utils/src/types.ts", "deprecated": false, "trackAdoption": false }, { "parentPluginId": "@kbn/ml-agg-utils", - "id": "def-common.NumericChartData.interval", + "id": "def-common.SignificantTermGroup.docCount", "type": "number", "tags": [], - "label": "interval", + "label": "docCount", "description": [], - "path": "x-pack/packages/ml/agg_utils/src/fetch_histograms_for_fields.ts", + "path": "x-pack/packages/ml/agg_utils/src/types.ts", "deprecated": false, "trackAdoption": false }, { "parentPluginId": "@kbn/ml-agg-utils", - "id": "def-common.NumericChartData.stats", - "type": "Object", + "id": "def-common.SignificantTermGroup.pValue", + "type": "CompoundType", "tags": [], - "label": "stats", + "label": "pValue", "description": [], "signature": [ - "[number, number]" + "number | null" ], - "path": "x-pack/packages/ml/agg_utils/src/fetch_histograms_for_fields.ts", + "path": "x-pack/packages/ml/agg_utils/src/types.ts", "deprecated": false, "trackAdoption": false }, { "parentPluginId": "@kbn/ml-agg-utils", - "id": "def-common.NumericChartData.type", - "type": "string", + "id": "def-common.SignificantTermGroup.histogram", + "type": "Array", "tags": [], - "label": "type", + "label": "histogram", "description": [], "signature": [ - "\"numeric\"" + { + "pluginId": "@kbn/ml-agg-utils", + "scope": "common", + "docId": "kibKbnMlAggUtilsPluginApi", + "section": "def-common.SignificantTermHistogramItem", + "text": "SignificantTermHistogramItem" + }, + "[] | undefined" ], - "path": "x-pack/packages/ml/agg_utils/src/fetch_histograms_for_fields.ts", + "path": "x-pack/packages/ml/agg_utils/src/types.ts", "deprecated": false, "trackAdoption": false } @@ -1243,12 +1192,12 @@ }, { "parentPluginId": "@kbn/ml-agg-utils", - "id": "def-common.NumericColumnStats", + "id": "def-common.SignificantTermGroupHistogram", "type": "Interface", "tags": [], - "label": "NumericColumnStats", + "label": "SignificantTermGroupHistogram", "description": [ - "\nInterface to describe attributes used for histograms." + "\nHistogram data for a group of field/value pairs." ], "path": "x-pack/packages/ml/agg_utils/src/types.ts", "deprecated": false, @@ -1256,21 +1205,10 @@ "children": [ { "parentPluginId": "@kbn/ml-agg-utils", - "id": "def-common.NumericColumnStats.interval", - "type": "number", - "tags": [], - "label": "interval", - "description": [], - "path": "x-pack/packages/ml/agg_utils/src/types.ts", - "deprecated": false, - "trackAdoption": false - }, - { - "parentPluginId": "@kbn/ml-agg-utils", - "id": "def-common.NumericColumnStats.min", - "type": "number", + "id": "def-common.SignificantTermGroupHistogram.id", + "type": "string", "tags": [], - "label": "min", + "label": "id", "description": [], "path": "x-pack/packages/ml/agg_utils/src/types.ts", "deprecated": false, @@ -1278,11 +1216,21 @@ }, { "parentPluginId": "@kbn/ml-agg-utils", - "id": "def-common.NumericColumnStats.max", - "type": "number", + "id": "def-common.SignificantTermGroupHistogram.histogram", + "type": "Array", "tags": [], - "label": "max", + "label": "histogram", "description": [], + "signature": [ + { + "pluginId": "@kbn/ml-agg-utils", + "scope": "common", + "docId": "kibKbnMlAggUtilsPluginApi", + "section": "def-common.SignificantTermHistogramItem", + "text": "SignificantTermHistogramItem" + }, + "[]" + ], "path": "x-pack/packages/ml/agg_utils/src/types.ts", "deprecated": false, "trackAdoption": false @@ -1292,60 +1240,112 @@ }, { "parentPluginId": "@kbn/ml-agg-utils", - "id": "def-common.NumericHistogramField", + "id": "def-common.SignificantTermHistogram", "type": "Interface", "tags": [], - "label": "NumericHistogramField", + "label": "SignificantTermHistogram", "description": [ - "\nNumeric based histogram field interface, limited to `date` and `number`." + "\nHistogram data for a field/value pair." ], "signature": [ { "pluginId": "@kbn/ml-agg-utils", "scope": "common", "docId": "kibKbnMlAggUtilsPluginApi", - "section": "def-common.NumericHistogramField", - "text": "NumericHistogramField" + "section": "def-common.SignificantTermHistogram", + "text": "SignificantTermHistogram" }, " extends ", { "pluginId": "@kbn/ml-agg-utils", "scope": "common", "docId": "kibKbnMlAggUtilsPluginApi", - "section": "def-common.HistogramField", - "text": "HistogramField" + "section": "def-common.FieldValuePair", + "text": "FieldValuePair" } ], - "path": "x-pack/packages/ml/agg_utils/src/fetch_histograms_for_fields.ts", + "path": "x-pack/packages/ml/agg_utils/src/types.ts", "deprecated": false, "trackAdoption": false, "children": [ { "parentPluginId": "@kbn/ml-agg-utils", - "id": "def-common.NumericHistogramField.type", - "type": "CompoundType", + "id": "def-common.SignificantTermHistogram.histogram", + "type": "Array", "tags": [], - "label": "type", + "label": "histogram", "description": [], "signature": [ { - "pluginId": "@kbn/field-types", - "scope": "common", - "docId": "kibKbnFieldTypesPluginApi", - "section": "def-common.KBN_FIELD_TYPES", - "text": "KBN_FIELD_TYPES" - }, - ".DATE | ", - { - "pluginId": "@kbn/field-types", + "pluginId": "@kbn/ml-agg-utils", "scope": "common", - "docId": "kibKbnFieldTypesPluginApi", - "section": "def-common.KBN_FIELD_TYPES", - "text": "KBN_FIELD_TYPES" + "docId": "kibKbnMlAggUtilsPluginApi", + "section": "def-common.SignificantTermHistogramItem", + "text": "SignificantTermHistogramItem" }, - ".NUMBER" + "[]" ], - "path": "x-pack/packages/ml/agg_utils/src/fetch_histograms_for_fields.ts", + "path": "x-pack/packages/ml/agg_utils/src/types.ts", + "deprecated": false, + "trackAdoption": false + } + ], + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/ml-agg-utils", + "id": "def-common.SignificantTermHistogramItem", + "type": "Interface", + "tags": [], + "label": "SignificantTermHistogramItem", + "description": [ + "\nSignificant term histogram data item." + ], + "path": "x-pack/packages/ml/agg_utils/src/types.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/ml-agg-utils", + "id": "def-common.SignificantTermHistogramItem.doc_count_overall", + "type": "number", + "tags": [], + "label": "doc_count_overall", + "description": [], + "path": "x-pack/packages/ml/agg_utils/src/types.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/ml-agg-utils", + "id": "def-common.SignificantTermHistogramItem.doc_count_significant_term", + "type": "number", + "tags": [], + "label": "doc_count_significant_term", + "description": [], + "path": "x-pack/packages/ml/agg_utils/src/types.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/ml-agg-utils", + "id": "def-common.SignificantTermHistogramItem.key", + "type": "number", + "tags": [], + "label": "key", + "description": [], + "path": "x-pack/packages/ml/agg_utils/src/types.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/ml-agg-utils", + "id": "def-common.SignificantTermHistogramItem.key_as_string", + "type": "string", + "tags": [], + "label": "key_as_string", + "description": [], + "path": "x-pack/packages/ml/agg_utils/src/types.ts", "deprecated": false, "trackAdoption": false } diff --git a/api_docs/kbn_ml_agg_utils.mdx b/api_docs/kbn_ml_agg_utils.mdx index 8c0aa554208c4..63b44ca043221 100644 --- a/api_docs/kbn_ml_agg_utils.mdx +++ b/api_docs/kbn_ml_agg_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-agg-utils title: "@kbn/ml-agg-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-agg-utils plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ml-agg-utils'] --- import kbnMlAggUtilsObj from './kbn_ml_agg_utils.devdocs.json'; diff --git a/api_docs/kbn_ml_date_picker.mdx b/api_docs/kbn_ml_date_picker.mdx index af53cd90c5535..35dbbe668e468 100644 --- a/api_docs/kbn_ml_date_picker.mdx +++ b/api_docs/kbn_ml_date_picker.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-date-picker title: "@kbn/ml-date-picker" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-date-picker plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ml-date-picker'] --- import kbnMlDatePickerObj from './kbn_ml_date_picker.devdocs.json'; diff --git a/api_docs/kbn_ml_is_defined.mdx b/api_docs/kbn_ml_is_defined.mdx index 237859946a4dc..bbad1646d9a3f 100644 --- a/api_docs/kbn_ml_is_defined.mdx +++ b/api_docs/kbn_ml_is_defined.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-is-defined title: "@kbn/ml-is-defined" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-is-defined plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ml-is-defined'] --- import kbnMlIsDefinedObj from './kbn_ml_is_defined.devdocs.json'; diff --git a/api_docs/kbn_ml_is_populated_object.mdx b/api_docs/kbn_ml_is_populated_object.mdx index 45df472a885b8..dc954552d26d4 100644 --- a/api_docs/kbn_ml_is_populated_object.mdx +++ b/api_docs/kbn_ml_is_populated_object.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-is-populated-object title: "@kbn/ml-is-populated-object" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-is-populated-object plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ml-is-populated-object'] --- import kbnMlIsPopulatedObjectObj from './kbn_ml_is_populated_object.devdocs.json'; diff --git a/api_docs/kbn_ml_local_storage.mdx b/api_docs/kbn_ml_local_storage.mdx index c924aac8994d6..98c34c33fd443 100644 --- a/api_docs/kbn_ml_local_storage.mdx +++ b/api_docs/kbn_ml_local_storage.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-local-storage title: "@kbn/ml-local-storage" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-local-storage plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ml-local-storage'] --- import kbnMlLocalStorageObj from './kbn_ml_local_storage.devdocs.json'; diff --git a/api_docs/kbn_ml_nested_property.mdx b/api_docs/kbn_ml_nested_property.mdx index fd2ad2d6254e3..238fbf59820fd 100644 --- a/api_docs/kbn_ml_nested_property.mdx +++ b/api_docs/kbn_ml_nested_property.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-nested-property title: "@kbn/ml-nested-property" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-nested-property plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ml-nested-property'] --- import kbnMlNestedPropertyObj from './kbn_ml_nested_property.devdocs.json'; diff --git a/api_docs/kbn_ml_query_utils.mdx b/api_docs/kbn_ml_query_utils.mdx index 8a36953203209..7e3f24bc19439 100644 --- a/api_docs/kbn_ml_query_utils.mdx +++ b/api_docs/kbn_ml_query_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-query-utils title: "@kbn/ml-query-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-query-utils plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ml-query-utils'] --- import kbnMlQueryUtilsObj from './kbn_ml_query_utils.devdocs.json'; diff --git a/api_docs/kbn_ml_string_hash.mdx b/api_docs/kbn_ml_string_hash.mdx index fbb5e51562cf6..4e723f80a8c6a 100644 --- a/api_docs/kbn_ml_string_hash.mdx +++ b/api_docs/kbn_ml_string_hash.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-string-hash title: "@kbn/ml-string-hash" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-string-hash plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ml-string-hash'] --- import kbnMlStringHashObj from './kbn_ml_string_hash.devdocs.json'; diff --git a/api_docs/kbn_ml_url_state.mdx b/api_docs/kbn_ml_url_state.mdx index 07b2b33524fbc..7ae8c4556e98e 100644 --- a/api_docs/kbn_ml_url_state.mdx +++ b/api_docs/kbn_ml_url_state.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-url-state title: "@kbn/ml-url-state" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-url-state plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ml-url-state'] --- import kbnMlUrlStateObj from './kbn_ml_url_state.devdocs.json'; diff --git a/api_docs/kbn_monaco.mdx b/api_docs/kbn_monaco.mdx index 7f256b27e9c78..26d48c92bffca 100644 --- a/api_docs/kbn_monaco.mdx +++ b/api_docs/kbn_monaco.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-monaco title: "@kbn/monaco" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/monaco plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/monaco'] --- import kbnMonacoObj from './kbn_monaco.devdocs.json'; diff --git a/api_docs/kbn_optimizer.mdx b/api_docs/kbn_optimizer.mdx index 231925f9bdcd9..1798193215023 100644 --- a/api_docs/kbn_optimizer.mdx +++ b/api_docs/kbn_optimizer.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-optimizer title: "@kbn/optimizer" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/optimizer plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/optimizer'] --- import kbnOptimizerObj from './kbn_optimizer.devdocs.json'; diff --git a/api_docs/kbn_optimizer_webpack_helpers.mdx b/api_docs/kbn_optimizer_webpack_helpers.mdx index de078b695f5ab..9a5c5f552c424 100644 --- a/api_docs/kbn_optimizer_webpack_helpers.mdx +++ b/api_docs/kbn_optimizer_webpack_helpers.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-optimizer-webpack-helpers title: "@kbn/optimizer-webpack-helpers" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/optimizer-webpack-helpers plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/optimizer-webpack-helpers'] --- import kbnOptimizerWebpackHelpersObj from './kbn_optimizer_webpack_helpers.devdocs.json'; diff --git a/api_docs/kbn_osquery_io_ts_types.mdx b/api_docs/kbn_osquery_io_ts_types.mdx index c0b4c6e4d1d45..b6c2b470a5ae4 100644 --- a/api_docs/kbn_osquery_io_ts_types.mdx +++ b/api_docs/kbn_osquery_io_ts_types.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-osquery-io-ts-types title: "@kbn/osquery-io-ts-types" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/osquery-io-ts-types plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/osquery-io-ts-types'] --- import kbnOsqueryIoTsTypesObj from './kbn_osquery_io_ts_types.devdocs.json'; diff --git a/api_docs/kbn_performance_testing_dataset_extractor.mdx b/api_docs/kbn_performance_testing_dataset_extractor.mdx index a022ac1faf9db..e067ea380bae6 100644 --- a/api_docs/kbn_performance_testing_dataset_extractor.mdx +++ b/api_docs/kbn_performance_testing_dataset_extractor.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-performance-testing-dataset-extractor title: "@kbn/performance-testing-dataset-extractor" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/performance-testing-dataset-extractor plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/performance-testing-dataset-extractor'] --- import kbnPerformanceTestingDatasetExtractorObj from './kbn_performance_testing_dataset_extractor.devdocs.json'; diff --git a/api_docs/kbn_plugin_generator.mdx b/api_docs/kbn_plugin_generator.mdx index 743dbbb875f18..de247317c8ac3 100644 --- a/api_docs/kbn_plugin_generator.mdx +++ b/api_docs/kbn_plugin_generator.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-plugin-generator title: "@kbn/plugin-generator" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/plugin-generator plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/plugin-generator'] --- import kbnPluginGeneratorObj from './kbn_plugin_generator.devdocs.json'; diff --git a/api_docs/kbn_plugin_helpers.mdx b/api_docs/kbn_plugin_helpers.mdx index 2cc3657b93d73..e1f8da4b25797 100644 --- a/api_docs/kbn_plugin_helpers.mdx +++ b/api_docs/kbn_plugin_helpers.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-plugin-helpers title: "@kbn/plugin-helpers" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/plugin-helpers plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/plugin-helpers'] --- import kbnPluginHelpersObj from './kbn_plugin_helpers.devdocs.json'; diff --git a/api_docs/kbn_react_field.mdx b/api_docs/kbn_react_field.mdx index 8e598ab296467..adea46e770b69 100644 --- a/api_docs/kbn_react_field.mdx +++ b/api_docs/kbn_react_field.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-react-field title: "@kbn/react-field" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/react-field plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/react-field'] --- import kbnReactFieldObj from './kbn_react_field.devdocs.json'; diff --git a/api_docs/kbn_repo_file_maps.mdx b/api_docs/kbn_repo_file_maps.mdx index bfbbf608a79fb..5b3929f19ff8f 100644 --- a/api_docs/kbn_repo_file_maps.mdx +++ b/api_docs/kbn_repo_file_maps.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-repo-file-maps title: "@kbn/repo-file-maps" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/repo-file-maps plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/repo-file-maps'] --- import kbnRepoFileMapsObj from './kbn_repo_file_maps.devdocs.json'; diff --git a/api_docs/kbn_repo_linter.mdx b/api_docs/kbn_repo_linter.mdx index 6ad34e29b820b..2f9ebadf30c09 100644 --- a/api_docs/kbn_repo_linter.mdx +++ b/api_docs/kbn_repo_linter.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-repo-linter title: "@kbn/repo-linter" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/repo-linter plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/repo-linter'] --- import kbnRepoLinterObj from './kbn_repo_linter.devdocs.json'; diff --git a/api_docs/kbn_repo_path.mdx b/api_docs/kbn_repo_path.mdx index 678fc7083beb1..5ed06a52ffab8 100644 --- a/api_docs/kbn_repo_path.mdx +++ b/api_docs/kbn_repo_path.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-repo-path title: "@kbn/repo-path" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/repo-path plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/repo-path'] --- import kbnRepoPathObj from './kbn_repo_path.devdocs.json'; diff --git a/api_docs/kbn_repo_source_classifier.mdx b/api_docs/kbn_repo_source_classifier.mdx index 6dcd6455c7423..ce9b3fdcbf9e7 100644 --- a/api_docs/kbn_repo_source_classifier.mdx +++ b/api_docs/kbn_repo_source_classifier.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-repo-source-classifier title: "@kbn/repo-source-classifier" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/repo-source-classifier plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/repo-source-classifier'] --- import kbnRepoSourceClassifierObj from './kbn_repo_source_classifier.devdocs.json'; diff --git a/api_docs/kbn_rison.mdx b/api_docs/kbn_rison.mdx index 083aa29ee2bee..e8689a3a783a3 100644 --- a/api_docs/kbn_rison.mdx +++ b/api_docs/kbn_rison.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-rison title: "@kbn/rison" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/rison plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/rison'] --- import kbnRisonObj from './kbn_rison.devdocs.json'; diff --git a/api_docs/kbn_rule_data_utils.mdx b/api_docs/kbn_rule_data_utils.mdx index 9dc7abd90fd5d..78fea4f67d6fa 100644 --- a/api_docs/kbn_rule_data_utils.mdx +++ b/api_docs/kbn_rule_data_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-rule-data-utils title: "@kbn/rule-data-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/rule-data-utils plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/rule-data-utils'] --- import kbnRuleDataUtilsObj from './kbn_rule_data_utils.devdocs.json'; diff --git a/api_docs/kbn_securitysolution_autocomplete.mdx b/api_docs/kbn_securitysolution_autocomplete.mdx index 734af94e1ba36..2a282cb7bd3ad 100644 --- a/api_docs/kbn_securitysolution_autocomplete.mdx +++ b/api_docs/kbn_securitysolution_autocomplete.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-autocomplete title: "@kbn/securitysolution-autocomplete" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/securitysolution-autocomplete plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/securitysolution-autocomplete'] --- import kbnSecuritysolutionAutocompleteObj from './kbn_securitysolution_autocomplete.devdocs.json'; diff --git a/api_docs/kbn_securitysolution_ecs.mdx b/api_docs/kbn_securitysolution_ecs.mdx index abb2afd0dd027..f754cded0292d 100644 --- a/api_docs/kbn_securitysolution_ecs.mdx +++ b/api_docs/kbn_securitysolution_ecs.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-ecs title: "@kbn/securitysolution-ecs" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/securitysolution-ecs plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/securitysolution-ecs'] --- import kbnSecuritysolutionEcsObj from './kbn_securitysolution_ecs.devdocs.json'; diff --git a/api_docs/kbn_securitysolution_es_utils.mdx b/api_docs/kbn_securitysolution_es_utils.mdx index ebba4f8efd378..f2b821f9485f7 100644 --- a/api_docs/kbn_securitysolution_es_utils.mdx +++ b/api_docs/kbn_securitysolution_es_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-es-utils title: "@kbn/securitysolution-es-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/securitysolution-es-utils plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/securitysolution-es-utils'] --- import kbnSecuritysolutionEsUtilsObj from './kbn_securitysolution_es_utils.devdocs.json'; diff --git a/api_docs/kbn_securitysolution_exception_list_components.mdx b/api_docs/kbn_securitysolution_exception_list_components.mdx index cafd91cc1f46d..e260940ff4f66 100644 --- a/api_docs/kbn_securitysolution_exception_list_components.mdx +++ b/api_docs/kbn_securitysolution_exception_list_components.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-exception-list-components title: "@kbn/securitysolution-exception-list-components" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/securitysolution-exception-list-components plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/securitysolution-exception-list-components'] --- import kbnSecuritysolutionExceptionListComponentsObj from './kbn_securitysolution_exception_list_components.devdocs.json'; diff --git a/api_docs/kbn_securitysolution_grouping.devdocs.json b/api_docs/kbn_securitysolution_grouping.devdocs.json index 696b2545dd92e..b85a58ac7e7e1 100644 --- a/api_docs/kbn_securitysolution_grouping.devdocs.json +++ b/api_docs/kbn_securitysolution_grouping.devdocs.json @@ -207,10 +207,24 @@ }, { "parentPluginId": "@kbn/securitysolution-grouping", - "id": "def-common.GroupingAggregation.groupsCount0", + "id": "def-common.GroupingAggregation.groupCount0", "type": "Object", "tags": [], - "label": "groupsCount0", + "label": "groupCount0", + "description": [], + "signature": [ + "{ value?: number | null | undefined; } | undefined" + ], + "path": "packages/kbn-securitysolution-grouping/src/components/types.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/securitysolution-grouping", + "id": "def-common.GroupingAggregation.unitCount0", + "type": "Object", + "tags": [], + "label": "unitCount0", "description": [], "signature": [ "{ value?: number | null | undefined; } | undefined" diff --git a/api_docs/kbn_securitysolution_grouping.mdx b/api_docs/kbn_securitysolution_grouping.mdx index ba755f9169342..b7fa32ace4ad6 100644 --- a/api_docs/kbn_securitysolution_grouping.mdx +++ b/api_docs/kbn_securitysolution_grouping.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-grouping title: "@kbn/securitysolution-grouping" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/securitysolution-grouping plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/securitysolution-grouping'] --- import kbnSecuritysolutionGroupingObj from './kbn_securitysolution_grouping.devdocs.json'; @@ -21,7 +21,7 @@ Contact [@elastic/security-threat-hunting-explore](https://github.com/orgs/elast | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 17 | 0 | 16 | 4 | +| 18 | 0 | 17 | 4 | ## Common diff --git a/api_docs/kbn_securitysolution_hook_utils.mdx b/api_docs/kbn_securitysolution_hook_utils.mdx index d0139de62d180..36a7c93f036eb 100644 --- a/api_docs/kbn_securitysolution_hook_utils.mdx +++ b/api_docs/kbn_securitysolution_hook_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-hook-utils title: "@kbn/securitysolution-hook-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/securitysolution-hook-utils plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/securitysolution-hook-utils'] --- import kbnSecuritysolutionHookUtilsObj from './kbn_securitysolution_hook_utils.devdocs.json'; diff --git a/api_docs/kbn_securitysolution_io_ts_alerting_types.mdx b/api_docs/kbn_securitysolution_io_ts_alerting_types.mdx index 45b39cc92ac8c..6fca3a8e96121 100644 --- a/api_docs/kbn_securitysolution_io_ts_alerting_types.mdx +++ b/api_docs/kbn_securitysolution_io_ts_alerting_types.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-io-ts-alerting-types title: "@kbn/securitysolution-io-ts-alerting-types" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/securitysolution-io-ts-alerting-types plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/securitysolution-io-ts-alerting-types'] --- import kbnSecuritysolutionIoTsAlertingTypesObj from './kbn_securitysolution_io_ts_alerting_types.devdocs.json'; diff --git a/api_docs/kbn_securitysolution_io_ts_list_types.mdx b/api_docs/kbn_securitysolution_io_ts_list_types.mdx index aa6d326c85d59..ff6d487051967 100644 --- a/api_docs/kbn_securitysolution_io_ts_list_types.mdx +++ b/api_docs/kbn_securitysolution_io_ts_list_types.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-io-ts-list-types title: "@kbn/securitysolution-io-ts-list-types" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/securitysolution-io-ts-list-types plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/securitysolution-io-ts-list-types'] --- import kbnSecuritysolutionIoTsListTypesObj from './kbn_securitysolution_io_ts_list_types.devdocs.json'; diff --git a/api_docs/kbn_securitysolution_io_ts_types.mdx b/api_docs/kbn_securitysolution_io_ts_types.mdx index 7cbffe5720c81..934b73b7385c4 100644 --- a/api_docs/kbn_securitysolution_io_ts_types.mdx +++ b/api_docs/kbn_securitysolution_io_ts_types.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-io-ts-types title: "@kbn/securitysolution-io-ts-types" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/securitysolution-io-ts-types plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/securitysolution-io-ts-types'] --- import kbnSecuritysolutionIoTsTypesObj from './kbn_securitysolution_io_ts_types.devdocs.json'; diff --git a/api_docs/kbn_securitysolution_io_ts_utils.mdx b/api_docs/kbn_securitysolution_io_ts_utils.mdx index e5aee3cf5c917..06e2b5dedd786 100644 --- a/api_docs/kbn_securitysolution_io_ts_utils.mdx +++ b/api_docs/kbn_securitysolution_io_ts_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-io-ts-utils title: "@kbn/securitysolution-io-ts-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/securitysolution-io-ts-utils plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/securitysolution-io-ts-utils'] --- import kbnSecuritysolutionIoTsUtilsObj from './kbn_securitysolution_io_ts_utils.devdocs.json'; diff --git a/api_docs/kbn_securitysolution_list_api.mdx b/api_docs/kbn_securitysolution_list_api.mdx index 8d5b897ccf606..c9673ec1ae605 100644 --- a/api_docs/kbn_securitysolution_list_api.mdx +++ b/api_docs/kbn_securitysolution_list_api.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-list-api title: "@kbn/securitysolution-list-api" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/securitysolution-list-api plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/securitysolution-list-api'] --- import kbnSecuritysolutionListApiObj from './kbn_securitysolution_list_api.devdocs.json'; diff --git a/api_docs/kbn_securitysolution_list_constants.mdx b/api_docs/kbn_securitysolution_list_constants.mdx index 97ad6cb4d7230..c3492940b9dd6 100644 --- a/api_docs/kbn_securitysolution_list_constants.mdx +++ b/api_docs/kbn_securitysolution_list_constants.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-list-constants title: "@kbn/securitysolution-list-constants" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/securitysolution-list-constants plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/securitysolution-list-constants'] --- import kbnSecuritysolutionListConstantsObj from './kbn_securitysolution_list_constants.devdocs.json'; diff --git a/api_docs/kbn_securitysolution_list_hooks.mdx b/api_docs/kbn_securitysolution_list_hooks.mdx index 6508fdcbcdd0d..0891b65001cb2 100644 --- a/api_docs/kbn_securitysolution_list_hooks.mdx +++ b/api_docs/kbn_securitysolution_list_hooks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-list-hooks title: "@kbn/securitysolution-list-hooks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/securitysolution-list-hooks plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/securitysolution-list-hooks'] --- import kbnSecuritysolutionListHooksObj from './kbn_securitysolution_list_hooks.devdocs.json'; diff --git a/api_docs/kbn_securitysolution_list_utils.mdx b/api_docs/kbn_securitysolution_list_utils.mdx index d58e0651b217f..3f9d36700e9e5 100644 --- a/api_docs/kbn_securitysolution_list_utils.mdx +++ b/api_docs/kbn_securitysolution_list_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-list-utils title: "@kbn/securitysolution-list-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/securitysolution-list-utils plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/securitysolution-list-utils'] --- import kbnSecuritysolutionListUtilsObj from './kbn_securitysolution_list_utils.devdocs.json'; diff --git a/api_docs/kbn_securitysolution_rules.mdx b/api_docs/kbn_securitysolution_rules.mdx index 46478e9bf8ebc..71ad50bc4eeab 100644 --- a/api_docs/kbn_securitysolution_rules.mdx +++ b/api_docs/kbn_securitysolution_rules.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-rules title: "@kbn/securitysolution-rules" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/securitysolution-rules plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/securitysolution-rules'] --- import kbnSecuritysolutionRulesObj from './kbn_securitysolution_rules.devdocs.json'; diff --git a/api_docs/kbn_securitysolution_t_grid.mdx b/api_docs/kbn_securitysolution_t_grid.mdx index e33446c02d925..1ecfe2d977ee0 100644 --- a/api_docs/kbn_securitysolution_t_grid.mdx +++ b/api_docs/kbn_securitysolution_t_grid.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-t-grid title: "@kbn/securitysolution-t-grid" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/securitysolution-t-grid plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/securitysolution-t-grid'] --- import kbnSecuritysolutionTGridObj from './kbn_securitysolution_t_grid.devdocs.json'; diff --git a/api_docs/kbn_securitysolution_utils.mdx b/api_docs/kbn_securitysolution_utils.mdx index d1ed927898679..0abfe76e01f3f 100644 --- a/api_docs/kbn_securitysolution_utils.mdx +++ b/api_docs/kbn_securitysolution_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-utils title: "@kbn/securitysolution-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/securitysolution-utils plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/securitysolution-utils'] --- import kbnSecuritysolutionUtilsObj from './kbn_securitysolution_utils.devdocs.json'; diff --git a/api_docs/kbn_server_http_tools.mdx b/api_docs/kbn_server_http_tools.mdx index 8fe5578326229..5bb3c6624ef5b 100644 --- a/api_docs/kbn_server_http_tools.mdx +++ b/api_docs/kbn_server_http_tools.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-server-http-tools title: "@kbn/server-http-tools" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/server-http-tools plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/server-http-tools'] --- import kbnServerHttpToolsObj from './kbn_server_http_tools.devdocs.json'; diff --git a/api_docs/kbn_server_route_repository.mdx b/api_docs/kbn_server_route_repository.mdx index 9e6b05b6300eb..8211d849e7c84 100644 --- a/api_docs/kbn_server_route_repository.mdx +++ b/api_docs/kbn_server_route_repository.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-server-route-repository title: "@kbn/server-route-repository" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/server-route-repository plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/server-route-repository'] --- import kbnServerRouteRepositoryObj from './kbn_server_route_repository.devdocs.json'; diff --git a/api_docs/kbn_shared_svg.mdx b/api_docs/kbn_shared_svg.mdx index 683bcebab82a9..de2bcff897bce 100644 --- a/api_docs/kbn_shared_svg.mdx +++ b/api_docs/kbn_shared_svg.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-svg title: "@kbn/shared-svg" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-svg plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-svg'] --- import kbnSharedSvgObj from './kbn_shared_svg.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_avatar_solution.mdx b/api_docs/kbn_shared_ux_avatar_solution.mdx index c378fcfd47217..ee48928ec965d 100644 --- a/api_docs/kbn_shared_ux_avatar_solution.mdx +++ b/api_docs/kbn_shared_ux_avatar_solution.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-avatar-solution title: "@kbn/shared-ux-avatar-solution" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-avatar-solution plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-avatar-solution'] --- import kbnSharedUxAvatarSolutionObj from './kbn_shared_ux_avatar_solution.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_avatar_user_profile_components.mdx b/api_docs/kbn_shared_ux_avatar_user_profile_components.mdx index 79c464530b843..5b8f95496c7c1 100644 --- a/api_docs/kbn_shared_ux_avatar_user_profile_components.mdx +++ b/api_docs/kbn_shared_ux_avatar_user_profile_components.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-avatar-user-profile-components title: "@kbn/shared-ux-avatar-user-profile-components" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-avatar-user-profile-components plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-avatar-user-profile-components'] --- import kbnSharedUxAvatarUserProfileComponentsObj from './kbn_shared_ux_avatar_user_profile_components.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_button_exit_full_screen.mdx b/api_docs/kbn_shared_ux_button_exit_full_screen.mdx index 603e184cee7db..2013dec4e56c5 100644 --- a/api_docs/kbn_shared_ux_button_exit_full_screen.mdx +++ b/api_docs/kbn_shared_ux_button_exit_full_screen.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-button-exit-full-screen title: "@kbn/shared-ux-button-exit-full-screen" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-button-exit-full-screen plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-button-exit-full-screen'] --- import kbnSharedUxButtonExitFullScreenObj from './kbn_shared_ux_button_exit_full_screen.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_button_exit_full_screen_mocks.mdx b/api_docs/kbn_shared_ux_button_exit_full_screen_mocks.mdx index ef4be66aeba19..385234f0e2c09 100644 --- a/api_docs/kbn_shared_ux_button_exit_full_screen_mocks.mdx +++ b/api_docs/kbn_shared_ux_button_exit_full_screen_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-button-exit-full-screen-mocks title: "@kbn/shared-ux-button-exit-full-screen-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-button-exit-full-screen-mocks plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-button-exit-full-screen-mocks'] --- import kbnSharedUxButtonExitFullScreenMocksObj from './kbn_shared_ux_button_exit_full_screen_mocks.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_button_toolbar.mdx b/api_docs/kbn_shared_ux_button_toolbar.mdx index 66187b8f47663..46317725ab924 100644 --- a/api_docs/kbn_shared_ux_button_toolbar.mdx +++ b/api_docs/kbn_shared_ux_button_toolbar.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-button-toolbar title: "@kbn/shared-ux-button-toolbar" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-button-toolbar plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-button-toolbar'] --- import kbnSharedUxButtonToolbarObj from './kbn_shared_ux_button_toolbar.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_card_no_data.mdx b/api_docs/kbn_shared_ux_card_no_data.mdx index 00c03ae7192a4..149be98b02b0b 100644 --- a/api_docs/kbn_shared_ux_card_no_data.mdx +++ b/api_docs/kbn_shared_ux_card_no_data.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-card-no-data title: "@kbn/shared-ux-card-no-data" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-card-no-data plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-card-no-data'] --- import kbnSharedUxCardNoDataObj from './kbn_shared_ux_card_no_data.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_card_no_data_mocks.mdx b/api_docs/kbn_shared_ux_card_no_data_mocks.mdx index e26e65ebb6b16..ecf3f914b81f6 100644 --- a/api_docs/kbn_shared_ux_card_no_data_mocks.mdx +++ b/api_docs/kbn_shared_ux_card_no_data_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-card-no-data-mocks title: "@kbn/shared-ux-card-no-data-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-card-no-data-mocks plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-card-no-data-mocks'] --- import kbnSharedUxCardNoDataMocksObj from './kbn_shared_ux_card_no_data_mocks.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_file_context.mdx b/api_docs/kbn_shared_ux_file_context.mdx index a16115a6a4302..46d29ba17bfe5 100644 --- a/api_docs/kbn_shared_ux_file_context.mdx +++ b/api_docs/kbn_shared_ux_file_context.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-file-context title: "@kbn/shared-ux-file-context" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-file-context plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-file-context'] --- import kbnSharedUxFileContextObj from './kbn_shared_ux_file_context.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_file_image.mdx b/api_docs/kbn_shared_ux_file_image.mdx index 61bd5dc35bb01..876508dc2c880 100644 --- a/api_docs/kbn_shared_ux_file_image.mdx +++ b/api_docs/kbn_shared_ux_file_image.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-file-image title: "@kbn/shared-ux-file-image" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-file-image plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-file-image'] --- import kbnSharedUxFileImageObj from './kbn_shared_ux_file_image.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_file_image_mocks.mdx b/api_docs/kbn_shared_ux_file_image_mocks.mdx index 3d9e570888cee..7d75906fb5531 100644 --- a/api_docs/kbn_shared_ux_file_image_mocks.mdx +++ b/api_docs/kbn_shared_ux_file_image_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-file-image-mocks title: "@kbn/shared-ux-file-image-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-file-image-mocks plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-file-image-mocks'] --- import kbnSharedUxFileImageMocksObj from './kbn_shared_ux_file_image_mocks.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_file_mocks.mdx b/api_docs/kbn_shared_ux_file_mocks.mdx index de293a7270318..f8d5f230986b9 100644 --- a/api_docs/kbn_shared_ux_file_mocks.mdx +++ b/api_docs/kbn_shared_ux_file_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-file-mocks title: "@kbn/shared-ux-file-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-file-mocks plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-file-mocks'] --- import kbnSharedUxFileMocksObj from './kbn_shared_ux_file_mocks.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_file_picker.mdx b/api_docs/kbn_shared_ux_file_picker.mdx index 40ca67e4f06f7..e62d4d4cdd84e 100644 --- a/api_docs/kbn_shared_ux_file_picker.mdx +++ b/api_docs/kbn_shared_ux_file_picker.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-file-picker title: "@kbn/shared-ux-file-picker" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-file-picker plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-file-picker'] --- import kbnSharedUxFilePickerObj from './kbn_shared_ux_file_picker.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_file_types.mdx b/api_docs/kbn_shared_ux_file_types.mdx index edeefe9d46c41..bd980874d0c36 100644 --- a/api_docs/kbn_shared_ux_file_types.mdx +++ b/api_docs/kbn_shared_ux_file_types.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-file-types title: "@kbn/shared-ux-file-types" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-file-types plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-file-types'] --- import kbnSharedUxFileTypesObj from './kbn_shared_ux_file_types.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_file_upload.mdx b/api_docs/kbn_shared_ux_file_upload.mdx index ccd77f26d2132..1422db76d698e 100644 --- a/api_docs/kbn_shared_ux_file_upload.mdx +++ b/api_docs/kbn_shared_ux_file_upload.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-file-upload title: "@kbn/shared-ux-file-upload" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-file-upload plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-file-upload'] --- import kbnSharedUxFileUploadObj from './kbn_shared_ux_file_upload.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_file_util.mdx b/api_docs/kbn_shared_ux_file_util.mdx index fbbe2f420a326..ff76e1aefaa1b 100644 --- a/api_docs/kbn_shared_ux_file_util.mdx +++ b/api_docs/kbn_shared_ux_file_util.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-file-util title: "@kbn/shared-ux-file-util" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-file-util plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-file-util'] --- import kbnSharedUxFileUtilObj from './kbn_shared_ux_file_util.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_link_redirect_app.mdx b/api_docs/kbn_shared_ux_link_redirect_app.mdx index 251b7372b643a..70a521de0921d 100644 --- a/api_docs/kbn_shared_ux_link_redirect_app.mdx +++ b/api_docs/kbn_shared_ux_link_redirect_app.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-link-redirect-app title: "@kbn/shared-ux-link-redirect-app" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-link-redirect-app plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-link-redirect-app'] --- import kbnSharedUxLinkRedirectAppObj from './kbn_shared_ux_link_redirect_app.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_link_redirect_app_mocks.mdx b/api_docs/kbn_shared_ux_link_redirect_app_mocks.mdx index 74f4c2f5bbf82..690799c56469a 100644 --- a/api_docs/kbn_shared_ux_link_redirect_app_mocks.mdx +++ b/api_docs/kbn_shared_ux_link_redirect_app_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-link-redirect-app-mocks title: "@kbn/shared-ux-link-redirect-app-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-link-redirect-app-mocks plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-link-redirect-app-mocks'] --- import kbnSharedUxLinkRedirectAppMocksObj from './kbn_shared_ux_link_redirect_app_mocks.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_markdown.mdx b/api_docs/kbn_shared_ux_markdown.mdx index e2c715f24bb99..9c7326af092d7 100644 --- a/api_docs/kbn_shared_ux_markdown.mdx +++ b/api_docs/kbn_shared_ux_markdown.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-markdown title: "@kbn/shared-ux-markdown" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-markdown plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-markdown'] --- import kbnSharedUxMarkdownObj from './kbn_shared_ux_markdown.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_markdown_mocks.mdx b/api_docs/kbn_shared_ux_markdown_mocks.mdx index 42a6e2cced9bc..b880384e6efa7 100644 --- a/api_docs/kbn_shared_ux_markdown_mocks.mdx +++ b/api_docs/kbn_shared_ux_markdown_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-markdown-mocks title: "@kbn/shared-ux-markdown-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-markdown-mocks plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-markdown-mocks'] --- import kbnSharedUxMarkdownMocksObj from './kbn_shared_ux_markdown_mocks.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_page_analytics_no_data.mdx b/api_docs/kbn_shared_ux_page_analytics_no_data.mdx index 9984e989354c9..cbac73c7352ce 100644 --- a/api_docs/kbn_shared_ux_page_analytics_no_data.mdx +++ b/api_docs/kbn_shared_ux_page_analytics_no_data.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-page-analytics-no-data title: "@kbn/shared-ux-page-analytics-no-data" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-page-analytics-no-data plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-page-analytics-no-data'] --- import kbnSharedUxPageAnalyticsNoDataObj from './kbn_shared_ux_page_analytics_no_data.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_page_analytics_no_data_mocks.mdx b/api_docs/kbn_shared_ux_page_analytics_no_data_mocks.mdx index bb38c3e2415d4..0e355e35b3fba 100644 --- a/api_docs/kbn_shared_ux_page_analytics_no_data_mocks.mdx +++ b/api_docs/kbn_shared_ux_page_analytics_no_data_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-page-analytics-no-data-mocks title: "@kbn/shared-ux-page-analytics-no-data-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-page-analytics-no-data-mocks plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-page-analytics-no-data-mocks'] --- import kbnSharedUxPageAnalyticsNoDataMocksObj from './kbn_shared_ux_page_analytics_no_data_mocks.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_page_kibana_no_data.mdx b/api_docs/kbn_shared_ux_page_kibana_no_data.mdx index f7daa20c2a59d..d4fb4518f3e30 100644 --- a/api_docs/kbn_shared_ux_page_kibana_no_data.mdx +++ b/api_docs/kbn_shared_ux_page_kibana_no_data.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-page-kibana-no-data title: "@kbn/shared-ux-page-kibana-no-data" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-page-kibana-no-data plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-page-kibana-no-data'] --- import kbnSharedUxPageKibanaNoDataObj from './kbn_shared_ux_page_kibana_no_data.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_page_kibana_no_data_mocks.mdx b/api_docs/kbn_shared_ux_page_kibana_no_data_mocks.mdx index 6fd8fe55c21ff..732dc8eb355e4 100644 --- a/api_docs/kbn_shared_ux_page_kibana_no_data_mocks.mdx +++ b/api_docs/kbn_shared_ux_page_kibana_no_data_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-page-kibana-no-data-mocks title: "@kbn/shared-ux-page-kibana-no-data-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-page-kibana-no-data-mocks plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-page-kibana-no-data-mocks'] --- import kbnSharedUxPageKibanaNoDataMocksObj from './kbn_shared_ux_page_kibana_no_data_mocks.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_page_kibana_template.mdx b/api_docs/kbn_shared_ux_page_kibana_template.mdx index 5e4c25c1c780b..d063aff58eabf 100644 --- a/api_docs/kbn_shared_ux_page_kibana_template.mdx +++ b/api_docs/kbn_shared_ux_page_kibana_template.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-page-kibana-template title: "@kbn/shared-ux-page-kibana-template" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-page-kibana-template plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-page-kibana-template'] --- import kbnSharedUxPageKibanaTemplateObj from './kbn_shared_ux_page_kibana_template.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_page_kibana_template_mocks.mdx b/api_docs/kbn_shared_ux_page_kibana_template_mocks.mdx index eaafa6af4308b..f82b37477aca5 100644 --- a/api_docs/kbn_shared_ux_page_kibana_template_mocks.mdx +++ b/api_docs/kbn_shared_ux_page_kibana_template_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-page-kibana-template-mocks title: "@kbn/shared-ux-page-kibana-template-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-page-kibana-template-mocks plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-page-kibana-template-mocks'] --- import kbnSharedUxPageKibanaTemplateMocksObj from './kbn_shared_ux_page_kibana_template_mocks.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_page_no_data.mdx b/api_docs/kbn_shared_ux_page_no_data.mdx index 8805d6a9b29a9..3048252f995f4 100644 --- a/api_docs/kbn_shared_ux_page_no_data.mdx +++ b/api_docs/kbn_shared_ux_page_no_data.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-page-no-data title: "@kbn/shared-ux-page-no-data" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-page-no-data plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-page-no-data'] --- import kbnSharedUxPageNoDataObj from './kbn_shared_ux_page_no_data.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_page_no_data_config.mdx b/api_docs/kbn_shared_ux_page_no_data_config.mdx index 1f413be73d760..5f0e0450e58b7 100644 --- a/api_docs/kbn_shared_ux_page_no_data_config.mdx +++ b/api_docs/kbn_shared_ux_page_no_data_config.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-page-no-data-config title: "@kbn/shared-ux-page-no-data-config" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-page-no-data-config plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-page-no-data-config'] --- import kbnSharedUxPageNoDataConfigObj from './kbn_shared_ux_page_no_data_config.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_page_no_data_config_mocks.mdx b/api_docs/kbn_shared_ux_page_no_data_config_mocks.mdx index 565effdcffb9e..18b3fd74422d4 100644 --- a/api_docs/kbn_shared_ux_page_no_data_config_mocks.mdx +++ b/api_docs/kbn_shared_ux_page_no_data_config_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-page-no-data-config-mocks title: "@kbn/shared-ux-page-no-data-config-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-page-no-data-config-mocks plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-page-no-data-config-mocks'] --- import kbnSharedUxPageNoDataConfigMocksObj from './kbn_shared_ux_page_no_data_config_mocks.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_page_no_data_mocks.mdx b/api_docs/kbn_shared_ux_page_no_data_mocks.mdx index e10aa502c486e..8d99849330b95 100644 --- a/api_docs/kbn_shared_ux_page_no_data_mocks.mdx +++ b/api_docs/kbn_shared_ux_page_no_data_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-page-no-data-mocks title: "@kbn/shared-ux-page-no-data-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-page-no-data-mocks plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-page-no-data-mocks'] --- import kbnSharedUxPageNoDataMocksObj from './kbn_shared_ux_page_no_data_mocks.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_page_solution_nav.mdx b/api_docs/kbn_shared_ux_page_solution_nav.mdx index a187fca6a0040..698d903d12dc1 100644 --- a/api_docs/kbn_shared_ux_page_solution_nav.mdx +++ b/api_docs/kbn_shared_ux_page_solution_nav.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-page-solution-nav title: "@kbn/shared-ux-page-solution-nav" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-page-solution-nav plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-page-solution-nav'] --- import kbnSharedUxPageSolutionNavObj from './kbn_shared_ux_page_solution_nav.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_prompt_no_data_views.mdx b/api_docs/kbn_shared_ux_prompt_no_data_views.mdx index 139e3e4dd8c58..982344bb543e0 100644 --- a/api_docs/kbn_shared_ux_prompt_no_data_views.mdx +++ b/api_docs/kbn_shared_ux_prompt_no_data_views.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-prompt-no-data-views title: "@kbn/shared-ux-prompt-no-data-views" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-prompt-no-data-views plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-prompt-no-data-views'] --- import kbnSharedUxPromptNoDataViewsObj from './kbn_shared_ux_prompt_no_data_views.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_prompt_no_data_views_mocks.mdx b/api_docs/kbn_shared_ux_prompt_no_data_views_mocks.mdx index 9fcfd1db26267..63d66ac6f7d7f 100644 --- a/api_docs/kbn_shared_ux_prompt_no_data_views_mocks.mdx +++ b/api_docs/kbn_shared_ux_prompt_no_data_views_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-prompt-no-data-views-mocks title: "@kbn/shared-ux-prompt-no-data-views-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-prompt-no-data-views-mocks plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-prompt-no-data-views-mocks'] --- import kbnSharedUxPromptNoDataViewsMocksObj from './kbn_shared_ux_prompt_no_data_views_mocks.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_prompt_not_found.mdx b/api_docs/kbn_shared_ux_prompt_not_found.mdx index 68ec96c4ca6bb..c5aefe4a5d102 100644 --- a/api_docs/kbn_shared_ux_prompt_not_found.mdx +++ b/api_docs/kbn_shared_ux_prompt_not_found.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-prompt-not-found title: "@kbn/shared-ux-prompt-not-found" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-prompt-not-found plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-prompt-not-found'] --- import kbnSharedUxPromptNotFoundObj from './kbn_shared_ux_prompt_not_found.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_router.mdx b/api_docs/kbn_shared_ux_router.mdx index 9468a8b9ea493..e4d3b3100602a 100644 --- a/api_docs/kbn_shared_ux_router.mdx +++ b/api_docs/kbn_shared_ux_router.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-router title: "@kbn/shared-ux-router" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-router plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-router'] --- import kbnSharedUxRouterObj from './kbn_shared_ux_router.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_router_mocks.mdx b/api_docs/kbn_shared_ux_router_mocks.mdx index c3379184142a5..47c4883f638b3 100644 --- a/api_docs/kbn_shared_ux_router_mocks.mdx +++ b/api_docs/kbn_shared_ux_router_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-router-mocks title: "@kbn/shared-ux-router-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-router-mocks plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-router-mocks'] --- import kbnSharedUxRouterMocksObj from './kbn_shared_ux_router_mocks.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_storybook_config.mdx b/api_docs/kbn_shared_ux_storybook_config.mdx index b29658711374a..66633f64fb6e0 100644 --- a/api_docs/kbn_shared_ux_storybook_config.mdx +++ b/api_docs/kbn_shared_ux_storybook_config.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-storybook-config title: "@kbn/shared-ux-storybook-config" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-storybook-config plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-storybook-config'] --- import kbnSharedUxStorybookConfigObj from './kbn_shared_ux_storybook_config.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_storybook_mock.mdx b/api_docs/kbn_shared_ux_storybook_mock.mdx index 7232cc3e8fdd9..ccc5787a65b9e 100644 --- a/api_docs/kbn_shared_ux_storybook_mock.mdx +++ b/api_docs/kbn_shared_ux_storybook_mock.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-storybook-mock title: "@kbn/shared-ux-storybook-mock" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-storybook-mock plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-storybook-mock'] --- import kbnSharedUxStorybookMockObj from './kbn_shared_ux_storybook_mock.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_utility.mdx b/api_docs/kbn_shared_ux_utility.mdx index 6f2582b36d6eb..589ebafc470e9 100644 --- a/api_docs/kbn_shared_ux_utility.mdx +++ b/api_docs/kbn_shared_ux_utility.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-utility title: "@kbn/shared-ux-utility" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-utility plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-utility'] --- import kbnSharedUxUtilityObj from './kbn_shared_ux_utility.devdocs.json'; diff --git a/api_docs/kbn_slo_schema.mdx b/api_docs/kbn_slo_schema.mdx index 8101eb2436906..169de61763f05 100644 --- a/api_docs/kbn_slo_schema.mdx +++ b/api_docs/kbn_slo_schema.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-slo-schema title: "@kbn/slo-schema" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/slo-schema plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/slo-schema'] --- import kbnSloSchemaObj from './kbn_slo_schema.devdocs.json'; diff --git a/api_docs/kbn_some_dev_log.mdx b/api_docs/kbn_some_dev_log.mdx index f27e5e08356f8..5e8660f71e403 100644 --- a/api_docs/kbn_some_dev_log.mdx +++ b/api_docs/kbn_some_dev_log.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-some-dev-log title: "@kbn/some-dev-log" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/some-dev-log plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/some-dev-log'] --- import kbnSomeDevLogObj from './kbn_some_dev_log.devdocs.json'; diff --git a/api_docs/kbn_std.mdx b/api_docs/kbn_std.mdx index 276921516d18d..34da57ab78902 100644 --- a/api_docs/kbn_std.mdx +++ b/api_docs/kbn_std.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-std title: "@kbn/std" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/std plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/std'] --- import kbnStdObj from './kbn_std.devdocs.json'; diff --git a/api_docs/kbn_stdio_dev_helpers.mdx b/api_docs/kbn_stdio_dev_helpers.mdx index c1d876b2d4840..3093097093eff 100644 --- a/api_docs/kbn_stdio_dev_helpers.mdx +++ b/api_docs/kbn_stdio_dev_helpers.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-stdio-dev-helpers title: "@kbn/stdio-dev-helpers" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/stdio-dev-helpers plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/stdio-dev-helpers'] --- import kbnStdioDevHelpersObj from './kbn_stdio_dev_helpers.devdocs.json'; diff --git a/api_docs/kbn_storybook.mdx b/api_docs/kbn_storybook.mdx index b59b771d23bb5..548e2f812bbd7 100644 --- a/api_docs/kbn_storybook.mdx +++ b/api_docs/kbn_storybook.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-storybook title: "@kbn/storybook" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/storybook plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/storybook'] --- import kbnStorybookObj from './kbn_storybook.devdocs.json'; diff --git a/api_docs/kbn_telemetry_tools.mdx b/api_docs/kbn_telemetry_tools.mdx index 295eded74f797..add11b7944941 100644 --- a/api_docs/kbn_telemetry_tools.mdx +++ b/api_docs/kbn_telemetry_tools.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-telemetry-tools title: "@kbn/telemetry-tools" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/telemetry-tools plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/telemetry-tools'] --- import kbnTelemetryToolsObj from './kbn_telemetry_tools.devdocs.json'; diff --git a/api_docs/kbn_test.mdx b/api_docs/kbn_test.mdx index 94a8679a10aa4..44ee176445991 100644 --- a/api_docs/kbn_test.mdx +++ b/api_docs/kbn_test.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-test title: "@kbn/test" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/test plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/test'] --- import kbnTestObj from './kbn_test.devdocs.json'; diff --git a/api_docs/kbn_test_jest_helpers.mdx b/api_docs/kbn_test_jest_helpers.mdx index cfd2b509b39c4..8b9273f05b076 100644 --- a/api_docs/kbn_test_jest_helpers.mdx +++ b/api_docs/kbn_test_jest_helpers.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-test-jest-helpers title: "@kbn/test-jest-helpers" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/test-jest-helpers plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/test-jest-helpers'] --- import kbnTestJestHelpersObj from './kbn_test_jest_helpers.devdocs.json'; diff --git a/api_docs/kbn_test_subj_selector.mdx b/api_docs/kbn_test_subj_selector.mdx index 5ebce6825c7cd..f7ec956ce01b6 100644 --- a/api_docs/kbn_test_subj_selector.mdx +++ b/api_docs/kbn_test_subj_selector.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-test-subj-selector title: "@kbn/test-subj-selector" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/test-subj-selector plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/test-subj-selector'] --- import kbnTestSubjSelectorObj from './kbn_test_subj_selector.devdocs.json'; diff --git a/api_docs/kbn_tooling_log.mdx b/api_docs/kbn_tooling_log.mdx index c89f48bc3b4fa..4f617b3f3eba8 100644 --- a/api_docs/kbn_tooling_log.mdx +++ b/api_docs/kbn_tooling_log.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-tooling-log title: "@kbn/tooling-log" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/tooling-log plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/tooling-log'] --- import kbnToolingLogObj from './kbn_tooling_log.devdocs.json'; diff --git a/api_docs/kbn_ts_projects.mdx b/api_docs/kbn_ts_projects.mdx index 40ae880dcfd91..8961538ce4089 100644 --- a/api_docs/kbn_ts_projects.mdx +++ b/api_docs/kbn_ts_projects.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ts-projects title: "@kbn/ts-projects" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ts-projects plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ts-projects'] --- import kbnTsProjectsObj from './kbn_ts_projects.devdocs.json'; diff --git a/api_docs/kbn_typed_react_router_config.mdx b/api_docs/kbn_typed_react_router_config.mdx index 238ef34655126..9ad3a266c8858 100644 --- a/api_docs/kbn_typed_react_router_config.mdx +++ b/api_docs/kbn_typed_react_router_config.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-typed-react-router-config title: "@kbn/typed-react-router-config" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/typed-react-router-config plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/typed-react-router-config'] --- import kbnTypedReactRouterConfigObj from './kbn_typed_react_router_config.devdocs.json'; diff --git a/api_docs/kbn_ui_actions_browser.mdx b/api_docs/kbn_ui_actions_browser.mdx index 2468845ba8250..10ba4f0ef47aa 100644 --- a/api_docs/kbn_ui_actions_browser.mdx +++ b/api_docs/kbn_ui_actions_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ui-actions-browser title: "@kbn/ui-actions-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ui-actions-browser plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ui-actions-browser'] --- import kbnUiActionsBrowserObj from './kbn_ui_actions_browser.devdocs.json'; diff --git a/api_docs/kbn_ui_shared_deps_src.mdx b/api_docs/kbn_ui_shared_deps_src.mdx index 9703d281b4422..19b2d09fedcde 100644 --- a/api_docs/kbn_ui_shared_deps_src.mdx +++ b/api_docs/kbn_ui_shared_deps_src.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ui-shared-deps-src title: "@kbn/ui-shared-deps-src" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ui-shared-deps-src plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ui-shared-deps-src'] --- import kbnUiSharedDepsSrcObj from './kbn_ui_shared_deps_src.devdocs.json'; diff --git a/api_docs/kbn_ui_theme.mdx b/api_docs/kbn_ui_theme.mdx index a7f7584f6f636..f2b662de16071 100644 --- a/api_docs/kbn_ui_theme.mdx +++ b/api_docs/kbn_ui_theme.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ui-theme title: "@kbn/ui-theme" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ui-theme plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ui-theme'] --- import kbnUiThemeObj from './kbn_ui_theme.devdocs.json'; diff --git a/api_docs/kbn_user_profile_components.mdx b/api_docs/kbn_user_profile_components.mdx index 30d20fb1c1546..f78fc9b77fc25 100644 --- a/api_docs/kbn_user_profile_components.mdx +++ b/api_docs/kbn_user_profile_components.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-user-profile-components title: "@kbn/user-profile-components" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/user-profile-components plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/user-profile-components'] --- import kbnUserProfileComponentsObj from './kbn_user_profile_components.devdocs.json'; diff --git a/api_docs/kbn_utility_types.mdx b/api_docs/kbn_utility_types.mdx index 12c20d1266d9a..c6642fb08a6f9 100644 --- a/api_docs/kbn_utility_types.mdx +++ b/api_docs/kbn_utility_types.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-utility-types title: "@kbn/utility-types" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/utility-types plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/utility-types'] --- import kbnUtilityTypesObj from './kbn_utility_types.devdocs.json'; diff --git a/api_docs/kbn_utility_types_jest.mdx b/api_docs/kbn_utility_types_jest.mdx index 1ee5ccb9d9e77..3762ec6203681 100644 --- a/api_docs/kbn_utility_types_jest.mdx +++ b/api_docs/kbn_utility_types_jest.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-utility-types-jest title: "@kbn/utility-types-jest" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/utility-types-jest plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/utility-types-jest'] --- import kbnUtilityTypesJestObj from './kbn_utility_types_jest.devdocs.json'; diff --git a/api_docs/kbn_utils.mdx b/api_docs/kbn_utils.mdx index eeb9c2db067d1..1465063df11b6 100644 --- a/api_docs/kbn_utils.mdx +++ b/api_docs/kbn_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-utils title: "@kbn/utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/utils plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/utils'] --- import kbnUtilsObj from './kbn_utils.devdocs.json'; diff --git a/api_docs/kbn_yarn_lock_validator.mdx b/api_docs/kbn_yarn_lock_validator.mdx index c6e8cead54965..ba16e0f1e3a5b 100644 --- a/api_docs/kbn_yarn_lock_validator.mdx +++ b/api_docs/kbn_yarn_lock_validator.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-yarn-lock-validator title: "@kbn/yarn-lock-validator" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/yarn-lock-validator plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/yarn-lock-validator'] --- import kbnYarnLockValidatorObj from './kbn_yarn_lock_validator.devdocs.json'; diff --git a/api_docs/kibana_overview.mdx b/api_docs/kibana_overview.mdx index 800fb1635a893..d0da9ebd3ca5c 100644 --- a/api_docs/kibana_overview.mdx +++ b/api_docs/kibana_overview.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kibanaOverview title: "kibanaOverview" image: https://source.unsplash.com/400x175/?github description: API docs for the kibanaOverview plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'kibanaOverview'] --- import kibanaOverviewObj from './kibana_overview.devdocs.json'; diff --git a/api_docs/kibana_react.mdx b/api_docs/kibana_react.mdx index 0985c732d2386..b29bc6af7d55a 100644 --- a/api_docs/kibana_react.mdx +++ b/api_docs/kibana_react.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kibanaReact title: "kibanaReact" image: https://source.unsplash.com/400x175/?github description: API docs for the kibanaReact plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'kibanaReact'] --- import kibanaReactObj from './kibana_react.devdocs.json'; diff --git a/api_docs/kibana_utils.mdx b/api_docs/kibana_utils.mdx index 4d97bd9bcc62c..0f8507afe5b7b 100644 --- a/api_docs/kibana_utils.mdx +++ b/api_docs/kibana_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kibanaUtils title: "kibanaUtils" image: https://source.unsplash.com/400x175/?github description: API docs for the kibanaUtils plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'kibanaUtils'] --- import kibanaUtilsObj from './kibana_utils.devdocs.json'; diff --git a/api_docs/kubernetes_security.mdx b/api_docs/kubernetes_security.mdx index 2029abfa7d4df..383df0c92ff87 100644 --- a/api_docs/kubernetes_security.mdx +++ b/api_docs/kubernetes_security.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kubernetesSecurity title: "kubernetesSecurity" image: https://source.unsplash.com/400x175/?github description: API docs for the kubernetesSecurity plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'kubernetesSecurity'] --- import kubernetesSecurityObj from './kubernetes_security.devdocs.json'; diff --git a/api_docs/lens.devdocs.json b/api_docs/lens.devdocs.json index e8b3ba633d26e..def9e6a6371e5 100644 --- a/api_docs/lens.devdocs.json +++ b/api_docs/lens.devdocs.json @@ -334,98 +334,6 @@ ], "returnComment": [] }, - { - "parentPluginId": "lens", - "id": "def-public.Embeddable.onContainerStateChanged", - "type": "Function", - "tags": [], - "label": "onContainerStateChanged", - "description": [], - "signature": [ - "(containerState: ", - { - "pluginId": "lens", - "scope": "public", - "docId": "kibLensPluginApi", - "section": "def-public.LensEmbeddableInput", - "text": "LensEmbeddableInput" - }, - ") => void" - ], - "path": "x-pack/plugins/lens/public/embeddable/embeddable.tsx", - "deprecated": false, - "trackAdoption": false, - "children": [ - { - "parentPluginId": "lens", - "id": "def-public.Embeddable.onContainerStateChanged.$1", - "type": "CompoundType", - "tags": [], - "label": "containerState", - "description": [], - "signature": [ - { - "pluginId": "lens", - "scope": "public", - "docId": "kibLensPluginApi", - "section": "def-public.LensEmbeddableInput", - "text": "LensEmbeddableInput" - } - ], - "path": "x-pack/plugins/lens/public/embeddable/embeddable.tsx", - "deprecated": false, - "trackAdoption": false, - "isRequired": true - } - ], - "returnComment": [] - }, - { - "parentPluginId": "lens", - "id": "def-public.Embeddable.handleContainerStateChanged", - "type": "Function", - "tags": [], - "label": "handleContainerStateChanged", - "description": [], - "signature": [ - "(containerState: ", - { - "pluginId": "lens", - "scope": "public", - "docId": "kibLensPluginApi", - "section": "def-public.LensEmbeddableInput", - "text": "LensEmbeddableInput" - }, - ") => boolean" - ], - "path": "x-pack/plugins/lens/public/embeddable/embeddable.tsx", - "deprecated": false, - "trackAdoption": false, - "children": [ - { - "parentPluginId": "lens", - "id": "def-public.Embeddable.handleContainerStateChanged.$1", - "type": "CompoundType", - "tags": [], - "label": "containerState", - "description": [], - "signature": [ - { - "pluginId": "lens", - "scope": "public", - "docId": "kibLensPluginApi", - "section": "def-public.LensEmbeddableInput", - "text": "LensEmbeddableInput" - } - ], - "path": "x-pack/plugins/lens/public/embeddable/embeddable.tsx", - "deprecated": false, - "trackAdoption": false, - "isRequired": true - } - ], - "returnComment": [] - }, { "parentPluginId": "lens", "id": "def-public.Embeddable.getExecutionContext", diff --git a/api_docs/lens.mdx b/api_docs/lens.mdx index 3216011a0e4bf..23fda70d84730 100644 --- a/api_docs/lens.mdx +++ b/api_docs/lens.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/lens title: "lens" image: https://source.unsplash.com/400x175/?github description: API docs for the lens plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'lens'] --- import lensObj from './lens.devdocs.json'; @@ -21,7 +21,7 @@ Contact [@elastic/kibana-visualizations](https://github.com/orgs/elastic/teams/k | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 690 | 0 | 596 | 52 | +| 686 | 0 | 592 | 52 | ## Client diff --git a/api_docs/license_api_guard.mdx b/api_docs/license_api_guard.mdx index 96908058686c2..2ac302dda4148 100644 --- a/api_docs/license_api_guard.mdx +++ b/api_docs/license_api_guard.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/licenseApiGuard title: "licenseApiGuard" image: https://source.unsplash.com/400x175/?github description: API docs for the licenseApiGuard plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'licenseApiGuard'] --- import licenseApiGuardObj from './license_api_guard.devdocs.json'; diff --git a/api_docs/license_management.mdx b/api_docs/license_management.mdx index 28004894fd7d3..a171be2cae29b 100644 --- a/api_docs/license_management.mdx +++ b/api_docs/license_management.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/licenseManagement title: "licenseManagement" image: https://source.unsplash.com/400x175/?github description: API docs for the licenseManagement plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'licenseManagement'] --- import licenseManagementObj from './license_management.devdocs.json'; diff --git a/api_docs/licensing.mdx b/api_docs/licensing.mdx index c3942bc1e3076..23a0aa37b461b 100644 --- a/api_docs/licensing.mdx +++ b/api_docs/licensing.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/licensing title: "licensing" image: https://source.unsplash.com/400x175/?github description: API docs for the licensing plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'licensing'] --- import licensingObj from './licensing.devdocs.json'; diff --git a/api_docs/lists.mdx b/api_docs/lists.mdx index 13a3e92bae540..12fcd4401096e 100644 --- a/api_docs/lists.mdx +++ b/api_docs/lists.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/lists title: "lists" image: https://source.unsplash.com/400x175/?github description: API docs for the lists plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'lists'] --- import listsObj from './lists.devdocs.json'; diff --git a/api_docs/management.mdx b/api_docs/management.mdx index dfa68adf49360..54218f838eaa3 100644 --- a/api_docs/management.mdx +++ b/api_docs/management.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/management title: "management" image: https://source.unsplash.com/400x175/?github description: API docs for the management plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'management'] --- import managementObj from './management.devdocs.json'; diff --git a/api_docs/maps.mdx b/api_docs/maps.mdx index 9b159382473ab..ec567dcc0adbf 100644 --- a/api_docs/maps.mdx +++ b/api_docs/maps.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/maps title: "maps" image: https://source.unsplash.com/400x175/?github description: API docs for the maps plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'maps'] --- import mapsObj from './maps.devdocs.json'; diff --git a/api_docs/maps_ems.mdx b/api_docs/maps_ems.mdx index c8695c8a61f7e..189a519784cf6 100644 --- a/api_docs/maps_ems.mdx +++ b/api_docs/maps_ems.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/mapsEms title: "mapsEms" image: https://source.unsplash.com/400x175/?github description: API docs for the mapsEms plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'mapsEms'] --- import mapsEmsObj from './maps_ems.devdocs.json'; diff --git a/api_docs/ml.mdx b/api_docs/ml.mdx index 98c9cfeec4f6c..3abe3617d3173 100644 --- a/api_docs/ml.mdx +++ b/api_docs/ml.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/ml title: "ml" image: https://source.unsplash.com/400x175/?github description: API docs for the ml plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'ml'] --- import mlObj from './ml.devdocs.json'; diff --git a/api_docs/monitoring.mdx b/api_docs/monitoring.mdx index a9cf484fb8b01..cd381495f7217 100644 --- a/api_docs/monitoring.mdx +++ b/api_docs/monitoring.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/monitoring title: "monitoring" image: https://source.unsplash.com/400x175/?github description: API docs for the monitoring plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'monitoring'] --- import monitoringObj from './monitoring.devdocs.json'; diff --git a/api_docs/monitoring_collection.mdx b/api_docs/monitoring_collection.mdx index aa0dd052cc59b..3843623aef188 100644 --- a/api_docs/monitoring_collection.mdx +++ b/api_docs/monitoring_collection.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/monitoringCollection title: "monitoringCollection" image: https://source.unsplash.com/400x175/?github description: API docs for the monitoringCollection plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'monitoringCollection'] --- import monitoringCollectionObj from './monitoring_collection.devdocs.json'; diff --git a/api_docs/navigation.mdx b/api_docs/navigation.mdx index 6016f75a8b68a..df35f11fe01ad 100644 --- a/api_docs/navigation.mdx +++ b/api_docs/navigation.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/navigation title: "navigation" image: https://source.unsplash.com/400x175/?github description: API docs for the navigation plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'navigation'] --- import navigationObj from './navigation.devdocs.json'; diff --git a/api_docs/newsfeed.mdx b/api_docs/newsfeed.mdx index c7ba49948c6cd..da637e3534c9f 100644 --- a/api_docs/newsfeed.mdx +++ b/api_docs/newsfeed.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/newsfeed title: "newsfeed" image: https://source.unsplash.com/400x175/?github description: API docs for the newsfeed plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'newsfeed'] --- import newsfeedObj from './newsfeed.devdocs.json'; diff --git a/api_docs/notifications.mdx b/api_docs/notifications.mdx index 8a469c5f2e7b9..d723b866de400 100644 --- a/api_docs/notifications.mdx +++ b/api_docs/notifications.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/notifications title: "notifications" image: https://source.unsplash.com/400x175/?github description: API docs for the notifications plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'notifications'] --- import notificationsObj from './notifications.devdocs.json'; diff --git a/api_docs/observability.devdocs.json b/api_docs/observability.devdocs.json index 300eaaab7770e..6a432e216c8e5 100644 --- a/api_docs/observability.devdocs.json +++ b/api_docs/observability.devdocs.json @@ -80,7 +80,7 @@ ") => ", "BucketSize" ], - "path": "x-pack/plugins/observability/public/pages/overview/containers/overview_page/helpers/calculate_bucket_size.ts", + "path": "x-pack/plugins/observability/public/pages/overview/helpers/calculate_bucket_size.ts", "deprecated": false, "trackAdoption": false, "children": [ @@ -100,7 +100,7 @@ "text": "TimeRange" } ], - "path": "x-pack/plugins/observability/public/pages/overview/containers/overview_page/helpers/calculate_bucket_size.ts", + "path": "x-pack/plugins/observability/public/pages/overview/helpers/calculate_bucket_size.ts", "deprecated": false, "trackAdoption": false, "isRequired": true @@ -121,7 +121,7 @@ "text": "TimeBuckets" } ], - "path": "x-pack/plugins/observability/public/pages/overview/containers/overview_page/helpers/calculate_bucket_size.ts", + "path": "x-pack/plugins/observability/public/pages/overview/helpers/calculate_bucket_size.ts", "deprecated": false, "trackAdoption": false, "isRequired": true @@ -667,6 +667,54 @@ "returnComment": [], "initialIsOpen": false }, + { + "parentPluginId": "observability", + "id": "def-public.formatAlertEvaluationValue", + "type": "Function", + "tags": [], + "label": "formatAlertEvaluationValue", + "description": [], + "signature": [ + "(ruleTypeId?: string | undefined, evaluationValue?: number | undefined) => any" + ], + "path": "x-pack/plugins/observability/public/utils/format_alert_evaluation_value.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "observability", + "id": "def-public.formatAlertEvaluationValue.$1", + "type": "string", + "tags": [], + "label": "ruleTypeId", + "description": [], + "signature": [ + "string | undefined" + ], + "path": "x-pack/plugins/observability/public/utils/format_alert_evaluation_value.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": false + }, + { + "parentPluginId": "observability", + "id": "def-public.formatAlertEvaluationValue.$2", + "type": "number", + "tags": [], + "label": "evaluationValue", + "description": [], + "signature": [ + "number | undefined" + ], + "path": "x-pack/plugins/observability/public/utils/format_alert_evaluation_value.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": false + } + ], + "returnComment": [], + "initialIsOpen": false + }, { "parentPluginId": "observability", "id": "def-public.fromQuery", @@ -10024,7 +10072,7 @@ "label": "ObservabilityAPIReturnType", "description": [], "signature": [ - "{ \"PUT /api/observability/slos/{id}\"?: ", + "{ \"PUT /api/observability/slos/{id}\": ", { "pluginId": "@kbn/server-route-repository", "scope": "common", @@ -10248,7 +10296,7 @@ "section": "def-server.ObservabilityRouteCreateOptions", "text": "ObservabilityRouteCreateOptions" }, - "> | undefined; \"GET /api/observability/slos/{id}\"?: ", + ">; \"GET /api/observability/slos/{id}\": ", { "pluginId": "@kbn/server-route-repository", "scope": "common", @@ -10278,7 +10326,7 @@ "section": "def-server.ObservabilityRouteCreateOptions", "text": "ObservabilityRouteCreateOptions" }, - "> | undefined; \"GET /api/observability/slos\"?: ", + ">; \"GET /api/observability/slos\": ", { "pluginId": "@kbn/server-route-repository", "scope": "common", @@ -10326,7 +10374,7 @@ "section": "def-server.ObservabilityRouteCreateOptions", "text": "ObservabilityRouteCreateOptions" }, - "> | undefined; \"POST /internal/observability/slos/_historical_summary\"?: ", + ">; \"POST /internal/observability/slos/_historical_summary\": ", { "pluginId": "@kbn/server-route-repository", "scope": "common", @@ -10358,7 +10406,7 @@ "section": "def-server.ObservabilityRouteCreateOptions", "text": "ObservabilityRouteCreateOptions" }, - "> | undefined; \"POST /api/observability/slos/{id}/enable\"?: ", + ">; \"POST /api/observability/slos/{id}/enable\": ", { "pluginId": "@kbn/server-route-repository", "scope": "common", @@ -10388,7 +10436,7 @@ "section": "def-server.ObservabilityRouteCreateOptions", "text": "ObservabilityRouteCreateOptions" }, - "> | undefined; \"POST /api/observability/slos/{id}/disable\"?: ", + ">; \"POST /api/observability/slos/{id}/disable\": ", { "pluginId": "@kbn/server-route-repository", "scope": "common", @@ -10418,7 +10466,7 @@ "section": "def-server.ObservabilityRouteCreateOptions", "text": "ObservabilityRouteCreateOptions" }, - "> | undefined; \"DELETE /api/observability/slos/{id}\"?: ", + ">; \"DELETE /api/observability/slos/{id}\": ", { "pluginId": "@kbn/server-route-repository", "scope": "common", @@ -10448,7 +10496,7 @@ "section": "def-server.ObservabilityRouteCreateOptions", "text": "ObservabilityRouteCreateOptions" }, - "> | undefined; \"POST /api/observability/slos\"?: ", + ">; \"POST /api/observability/slos\": ", { "pluginId": "@kbn/server-route-repository", "scope": "common", @@ -10672,7 +10720,7 @@ "section": "def-server.ObservabilityRouteCreateOptions", "text": "ObservabilityRouteCreateOptions" }, - "> | undefined; \"GET /api/observability/rules/alerts/dynamic_index_pattern\": ", + ">; \"GET /api/observability/rules/alerts/dynamic_index_pattern\": ", { "pluginId": "@kbn/server-route-repository", "scope": "common", @@ -10731,7 +10779,7 @@ "label": "ObservabilityConfig", "description": [], "signature": [ - "{ readonly annotations: Readonly<{} & { enabled: boolean; index: string; }>; readonly unsafe: Readonly<{} & { slo: Readonly<{} & { enabled: boolean; }>; alertDetails: Readonly<{} & { uptime: Readonly<{} & { enabled: boolean; }>; apm: Readonly<{} & { enabled: boolean; }>; metrics: Readonly<{} & { enabled: boolean; }>; logs: Readonly<{} & { enabled: boolean; }>; }>; }>; }" + "{ readonly annotations: Readonly<{} & { enabled: boolean; index: string; }>; readonly unsafe: Readonly<{} & { alertDetails: Readonly<{} & { uptime: Readonly<{} & { enabled: boolean; }>; apm: Readonly<{} & { enabled: boolean; }>; metrics: Readonly<{} & { enabled: boolean; }>; logs: Readonly<{} & { enabled: boolean; }>; }>; }>; }" ], "path": "x-pack/plugins/observability/server/index.ts", "deprecated": false, @@ -10746,7 +10794,7 @@ "label": "ObservabilityServerRouteRepository", "description": [], "signature": [ - "{ \"PUT /api/observability/slos/{id}\"?: ", + "{ \"PUT /api/observability/slos/{id}\": ", { "pluginId": "@kbn/server-route-repository", "scope": "common", @@ -10970,7 +11018,7 @@ "section": "def-server.ObservabilityRouteCreateOptions", "text": "ObservabilityRouteCreateOptions" }, - "> | undefined; \"GET /api/observability/slos/{id}\"?: ", + ">; \"GET /api/observability/slos/{id}\": ", { "pluginId": "@kbn/server-route-repository", "scope": "common", @@ -11000,7 +11048,7 @@ "section": "def-server.ObservabilityRouteCreateOptions", "text": "ObservabilityRouteCreateOptions" }, - "> | undefined; \"GET /api/observability/slos\"?: ", + ">; \"GET /api/observability/slos\": ", { "pluginId": "@kbn/server-route-repository", "scope": "common", @@ -11048,7 +11096,7 @@ "section": "def-server.ObservabilityRouteCreateOptions", "text": "ObservabilityRouteCreateOptions" }, - "> | undefined; \"POST /internal/observability/slos/_historical_summary\"?: ", + ">; \"POST /internal/observability/slos/_historical_summary\": ", { "pluginId": "@kbn/server-route-repository", "scope": "common", @@ -11080,7 +11128,7 @@ "section": "def-server.ObservabilityRouteCreateOptions", "text": "ObservabilityRouteCreateOptions" }, - "> | undefined; \"POST /api/observability/slos/{id}/enable\"?: ", + ">; \"POST /api/observability/slos/{id}/enable\": ", { "pluginId": "@kbn/server-route-repository", "scope": "common", @@ -11110,7 +11158,7 @@ "section": "def-server.ObservabilityRouteCreateOptions", "text": "ObservabilityRouteCreateOptions" }, - "> | undefined; \"POST /api/observability/slos/{id}/disable\"?: ", + ">; \"POST /api/observability/slos/{id}/disable\": ", { "pluginId": "@kbn/server-route-repository", "scope": "common", @@ -11140,7 +11188,7 @@ "section": "def-server.ObservabilityRouteCreateOptions", "text": "ObservabilityRouteCreateOptions" }, - "> | undefined; \"DELETE /api/observability/slos/{id}\"?: ", + ">; \"DELETE /api/observability/slos/{id}\": ", { "pluginId": "@kbn/server-route-repository", "scope": "common", @@ -11170,7 +11218,7 @@ "section": "def-server.ObservabilityRouteCreateOptions", "text": "ObservabilityRouteCreateOptions" }, - "> | undefined; \"POST /api/observability/slos\"?: ", + ">; \"POST /api/observability/slos\": ", { "pluginId": "@kbn/server-route-repository", "scope": "common", @@ -11394,7 +11442,7 @@ "section": "def-server.ObservabilityRouteCreateOptions", "text": "ObservabilityRouteCreateOptions" }, - "> | undefined; \"GET /api/observability/rules/alerts/dynamic_index_pattern\": ", + ">; \"GET /api/observability/rules/alerts/dynamic_index_pattern\": ", { "pluginId": "@kbn/server-route-repository", "scope": "common", @@ -13614,6 +13662,72 @@ "common": { "classes": [], "functions": [ + { + "parentPluginId": "observability", + "id": "def-common.asPercent", + "type": "Function", + "tags": [], + "label": "asPercent", + "description": [], + "signature": [ + "(numerator: ", + "Maybe", + ", denominator: number | undefined, fallbackResult: any) => any" + ], + "path": "x-pack/plugins/observability/common/utils/formatters/formatters.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "observability", + "id": "def-common.asPercent.$1", + "type": "CompoundType", + "tags": [], + "label": "numerator", + "description": [], + "signature": [ + "Maybe", + "" + ], + "path": "x-pack/plugins/observability/common/utils/formatters/formatters.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": false + }, + { + "parentPluginId": "observability", + "id": "def-common.asPercent.$2", + "type": "number", + "tags": [], + "label": "denominator", + "description": [], + "signature": [ + "number | undefined" + ], + "path": "x-pack/plugins/observability/common/utils/formatters/formatters.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": false + }, + { + "parentPluginId": "observability", + "id": "def-common.asPercent.$3", + "type": "Any", + "tags": [], + "label": "fallbackResult", + "description": [], + "signature": [ + "any" + ], + "path": "x-pack/plugins/observability/common/utils/formatters/formatters.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + } + ], + "returnComment": [], + "initialIsOpen": false + }, { "parentPluginId": "observability", "id": "def-common.formatDurationFromTimeUnitChar", @@ -13676,6 +13790,42 @@ "returnComment": [], "initialIsOpen": false }, + { + "parentPluginId": "observability", + "id": "def-common.getDurationFormatter", + "type": "Function", + "tags": [], + "label": "getDurationFormatter", + "description": [], + "signature": [ + "(max: number) => ", + { + "pluginId": "observability", + "scope": "common", + "docId": "kibObservabilityPluginApi", + "section": "def-common.TimeFormatter", + "text": "TimeFormatter" + } + ], + "path": "x-pack/plugins/observability/common/utils/formatters/duration.ts", + "deprecated": false, + "trackAdoption": false, + "returnComment": [], + "children": [ + { + "parentPluginId": "observability", + "id": "def-common.getDurationFormatter.$1", + "type": "number", + "tags": [], + "label": "max", + "description": [], + "path": "x-pack/plugins/observability/common/utils/formatters/duration.ts", + "deprecated": false, + "trackAdoption": false + } + ], + "initialIsOpen": false + }, { "parentPluginId": "observability", "id": "def-common.getInspectResponse", @@ -14566,6 +14716,54 @@ "trackAdoption": false, "initialIsOpen": false }, + { + "parentPluginId": "observability", + "id": "def-common.TimeFormatter", + "type": "Type", + "tags": [], + "label": "TimeFormatter", + "description": [], + "signature": [ + "(value: ", + "Maybe", + ", options?: FormatterOptions | undefined) => ConvertedDuration" + ], + "path": "x-pack/plugins/observability/common/utils/formatters/duration.ts", + "deprecated": false, + "trackAdoption": false, + "returnComment": [], + "children": [ + { + "parentPluginId": "observability", + "id": "def-common.TimeFormatter.$1", + "type": "CompoundType", + "tags": [], + "label": "value", + "description": [], + "signature": [ + "number | null | undefined" + ], + "path": "x-pack/plugins/observability/common/utils/formatters/duration.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "observability", + "id": "def-common.TimeFormatter.$2", + "type": "Object", + "tags": [], + "label": "options", + "description": [], + "signature": [ + "FormatterOptions | undefined" + ], + "path": "x-pack/plugins/observability/common/utils/formatters/duration.ts", + "deprecated": false, + "trackAdoption": false + } + ], + "initialIsOpen": false + }, { "parentPluginId": "observability", "id": "def-common.TimeUnitChar", diff --git a/api_docs/observability.mdx b/api_docs/observability.mdx index 300b348cf404d..1e55f29e92627 100644 --- a/api_docs/observability.mdx +++ b/api_docs/observability.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/observability title: "observability" image: https://source.unsplash.com/400x175/?github description: API docs for the observability plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'observability'] --- import observabilityObj from './observability.devdocs.json'; @@ -21,7 +21,7 @@ Contact [@elastic/actionable-observability](https://github.com/orgs/elastic/team | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 628 | 43 | 622 | 34 | +| 640 | 44 | 634 | 34 | ## Client diff --git a/api_docs/osquery.devdocs.json b/api_docs/osquery.devdocs.json index 6232998bcc0ea..8dec14c663274 100644 --- a/api_docs/osquery.devdocs.json +++ b/api_docs/osquery.devdocs.json @@ -43,7 +43,13 @@ "((props: ", "OsqueryActionProps", " & { ecsData?: ", - "Ecs", + { + "pluginId": "@kbn/securitysolution-ecs", + "scope": "common", + "docId": "kibKbnSecuritysolutionEcsPluginApi", + "section": "def-common.EcsSecurityExtension", + "text": "EcsSecurityExtension" + }, " | undefined; }) => JSX.Element) | undefined" ], "path": "x-pack/plugins/osquery/public/types.ts", diff --git a/api_docs/osquery.mdx b/api_docs/osquery.mdx index 3971c6a006f12..cbb0d2e7bb81e 100644 --- a/api_docs/osquery.mdx +++ b/api_docs/osquery.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/osquery title: "osquery" image: https://source.unsplash.com/400x175/?github description: API docs for the osquery plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'osquery'] --- import osqueryObj from './osquery.devdocs.json'; @@ -21,7 +21,7 @@ Contact [@elastic/security-defend-workflows](https://github.com/orgs/elastic/tea | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 24 | 0 | 24 | 7 | +| 24 | 0 | 24 | 6 | ## Client diff --git a/api_docs/plugin_directory.mdx b/api_docs/plugin_directory.mdx index db0c4dd9f6931..9a71f35ab5c81 100644 --- a/api_docs/plugin_directory.mdx +++ b/api_docs/plugin_directory.mdx @@ -7,7 +7,7 @@ id: kibDevDocsPluginDirectory slug: /kibana-dev-docs/api-meta/plugin-api-directory title: Directory description: Directory of public APIs available through plugins or packages. -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana'] --- @@ -21,16 +21,16 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana'] | API Count | Any Count | Missing comments | Missing exports | |--------------|----------|-----------------|--------| -| 67917 | 515 | 58627 | 1239 | +| 68002 | 516 | 58695 | 1239 | ## Plugin Directory | Plugin name           | Maintaining team | Description | API Cnt | Any Cnt | Missing
comments | Missing
exports | |--------------|----------------|-----------|--------------|----------|---------------|--------| -| | [@elastic/response-ops](https://github.com/orgs/elastic/teams/response-ops) | - | 256 | 8 | 251 | 24 | +| | [@elastic/response-ops](https://github.com/orgs/elastic/teams/response-ops) | - | 259 | 8 | 254 | 26 | | | [@elastic/appex-sharedux](https://github.com/orgs/elastic/teams/appex-sharedux) | - | 36 | 1 | 32 | 2 | | | [@elastic/ml-ui](https://github.com/orgs/elastic/teams/ml-ui) | AIOps plugin maintained by ML team. | 12 | 0 | 1 | 2 | -| | [@elastic/response-ops](https://github.com/orgs/elastic/teams/response-ops) | - | 497 | 1 | 486 | 41 | +| | [@elastic/response-ops](https://github.com/orgs/elastic/teams/response-ops) | - | 531 | 1 | 515 | 40 | | | [@elastic/apm-ui](https://github.com/orgs/elastic/teams/apm-ui) | The user interface for Elastic APM | 42 | 0 | 42 | 65 | | | [@elastic/appex-sharedux](https://github.com/orgs/elastic/teams/appex-sharedux) | - | 9 | 0 | 9 | 0 | | | [@elastic/appex-sharedux](https://github.com/orgs/elastic/teams/appex-sharedux) | Considering using bfetch capabilities when fetching large amounts of data. This services supports batching HTTP requests and streaming responses back. | 89 | 1 | 74 | 2 | @@ -54,7 +54,7 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana'] | | [@elastic/fleet](https://github.com/orgs/elastic/teams/fleet) | Add custom data integrations so they can be displayed in the Fleet integrations app | 107 | 0 | 88 | 1 | | | [@elastic/kibana-presentation](https://github.com/orgs/elastic/teams/kibana-presentation) | Adds the Dashboard app to Kibana | 188 | 0 | 179 | 12 | | | [@elastic/kibana-presentation](https://github.com/orgs/elastic/teams/kibana-presentation) | - | 54 | 0 | 51 | 0 | -| | [@elastic/kibana-visualizations](https://github.com/orgs/elastic/teams/kibana-visualizations) | Data services are useful for searching and querying data from Elasticsearch. Helpful utilities include: a re-usable react query bar, KQL autocomplete, async search, Data Views (Index Patterns) and field formatters. | 3280 | 119 | 2584 | 27 | +| | [@elastic/kibana-visualizations](https://github.com/orgs/elastic/teams/kibana-visualizations) | Data services are useful for searching and querying data from Elasticsearch. Helpful utilities include: a re-usable react query bar, KQL autocomplete, async search, Data Views (Index Patterns) and field formatters. | 3282 | 119 | 2586 | 27 | | | [@elastic/kibana-data-discovery](https://github.com/orgs/elastic/teams/kibana-data-discovery) | This plugin provides the ability to create data views via a modal flyout inside Kibana apps | 16 | 0 | 7 | 0 | | | [@elastic/kibana-data-discovery](https://github.com/orgs/elastic/teams/kibana-data-discovery) | Reusable data view field editor across Kibana | 72 | 0 | 33 | 0 | | | [@elastic/kibana-data-discovery](https://github.com/orgs/elastic/teams/kibana-data-discovery) | Data view management app | 2 | 0 | 2 | 0 | @@ -112,7 +112,7 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana'] | kibanaUsageCollection | [@elastic/kibana-core](https://github.com/orgs/elastic/teams/kibana-core) | - | 0 | 0 | 0 | 0 | | | [@elastic/kibana-app-services](https://github.com/orgs/elastic/teams/kibana-app-services) | - | 609 | 3 | 416 | 9 | | | [@elastic/awp-viz](https://github.com/orgs/elastic/teams/awp-viz) | - | 3 | 0 | 3 | 1 | -| | [@elastic/kibana-visualizations](https://github.com/orgs/elastic/teams/kibana-visualizations) | Visualization editor allowing to quickly and easily configure compelling visualizations to use on dashboards and canvas workpads. Exposes components to embed visualizations and link into the Lens editor from within other apps in Kibana. | 690 | 0 | 596 | 52 | +| | [@elastic/kibana-visualizations](https://github.com/orgs/elastic/teams/kibana-visualizations) | Visualization editor allowing to quickly and easily configure compelling visualizations to use on dashboards and canvas workpads. Exposes components to embed visualizations and link into the Lens editor from within other apps in Kibana. | 686 | 0 | 592 | 52 | | | [@elastic/platform-deployment-management](https://github.com/orgs/elastic/teams/platform-deployment-management) | - | 8 | 0 | 8 | 0 | | | [@elastic/platform-deployment-management](https://github.com/orgs/elastic/teams/platform-deployment-management) | - | 3 | 0 | 3 | 0 | | | [@elastic/kibana-core](https://github.com/orgs/elastic/teams/kibana-core) | - | 117 | 0 | 42 | 10 | @@ -127,8 +127,8 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana'] | | [@elastic/appex-sharedux](https://github.com/orgs/elastic/teams/appex-sharedux) | - | 34 | 0 | 34 | 2 | | | [@elastic/kibana-core](https://github.com/orgs/elastic/teams/kibana-core) | - | 17 | 0 | 17 | 0 | | | [@elastic/appex-sharedux](https://github.com/orgs/elastic/teams/appex-sharedux) | - | 2 | 0 | 2 | 1 | -| | [@elastic/actionable-observability](https://github.com/orgs/elastic/teams/actionable-observability) | - | 628 | 43 | 622 | 34 | -| | [@elastic/security-defend-workflows](https://github.com/orgs/elastic/teams/security-defend-workflows) | - | 24 | 0 | 24 | 7 | +| | [@elastic/actionable-observability](https://github.com/orgs/elastic/teams/actionable-observability) | - | 640 | 44 | 634 | 34 | +| | [@elastic/security-defend-workflows](https://github.com/orgs/elastic/teams/security-defend-workflows) | - | 24 | 0 | 24 | 6 | | painlessLab | [@elastic/platform-deployment-management](https://github.com/orgs/elastic/teams/platform-deployment-management) | - | 0 | 0 | 0 | 0 | | | [@elastic/kibana-presentation](https://github.com/orgs/elastic/teams/kibana-presentation) | The Presentation Utility Plugin is a set of common, shared components and toolkits for solutions within the Presentation space, (e.g. Dashboards, Canvas). | 202 | 7 | 146 | 12 | | | [@elastic/profiling-ui](https://github.com/orgs/elastic/teams/profiling-ui) | - | 15 | 2 | 15 | 0 | @@ -146,7 +146,7 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana'] | | [@elastic/appex-sharedux](https://github.com/orgs/elastic/teams/appex-sharedux) | - | 32 | 0 | 13 | 0 | | | [@elastic/kibana-reporting-services](https://github.com/orgs/elastic/teams/kibana-reporting-services) | Kibana Screenshotting Plugin | 27 | 0 | 8 | 4 | | searchprofiler | [@elastic/platform-deployment-management](https://github.com/orgs/elastic/teams/platform-deployment-management) | - | 0 | 0 | 0 | 0 | -| | [@elastic/kibana-security](https://github.com/orgs/elastic/teams/kibana-security) | This plugin provides authentication and authorization features, and exposes functionality to understand the capabilities of the currently authenticated user. | 269 | 0 | 89 | 0 | +| | [@elastic/kibana-security](https://github.com/orgs/elastic/teams/kibana-security) | This plugin provides authentication and authorization features, and exposes functionality to understand the capabilities of the currently authenticated user. | 278 | 0 | 94 | 0 | | | [@elastic/security-solution](https://github.com/orgs/elastic/teams/security-solution) | - | 117 | 0 | 77 | 28 | | | [@elastic/awp-viz](https://github.com/orgs/elastic/teams/awp-viz) | - | 7 | 0 | 7 | 1 | | | [@elastic/appex-sharedux](https://github.com/orgs/elastic/teams/appex-sharedux) | Adds URL Service and sharing capabilities to Kibana | 118 | 0 | 59 | 10 | @@ -155,7 +155,7 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana'] | | [@elastic/response-ops](https://github.com/orgs/elastic/teams/response-ops) | - | 12 | 0 | 12 | 2 | | | [@elastic/response-ops](https://github.com/orgs/elastic/teams/response-ops) | - | 4 | 0 | 4 | 0 | | synthetics | [@elastic/uptime](https://github.com/orgs/elastic/teams/uptime) | This plugin visualizes data from Synthetics and Heartbeat, and integrates with other Observability solutions. | 0 | 0 | 0 | 0 | -| | [@elastic/response-ops](https://github.com/orgs/elastic/teams/response-ops) | - | 83 | 0 | 41 | 7 | +| | [@elastic/response-ops](https://github.com/orgs/elastic/teams/response-ops) | - | 80 | 0 | 39 | 7 | | | [@elastic/kibana-core](https://github.com/orgs/elastic/teams/kibana-core) | - | 44 | 0 | 1 | 0 | | | [@elastic/kibana-core](https://github.com/orgs/elastic/teams/kibana-core) | - | 31 | 0 | 26 | 6 | | | [@elastic/kibana-core](https://github.com/orgs/elastic/teams/kibana-core) | - | 1 | 0 | 1 | 0 | @@ -164,7 +164,7 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana'] | | [@elastic/security-threat-hunting-investigations](https://github.com/orgs/elastic/teams/security-threat-hunting-investigations) | - | 257 | 1 | 214 | 20 | | | [@elastic/ml-ui](https://github.com/orgs/elastic/teams/ml-ui) | This plugin provides access to the transforms features provided by Elastic. Transforms enable you to convert existing Elasticsearch indices into summarized indices, which provide opportunities for new insights and analytics. | 4 | 0 | 4 | 1 | | translations | [@elastic/kibana-localization](https://github.com/orgs/elastic/teams/kibana-localization) | - | 0 | 0 | 0 | 0 | -| | [@elastic/response-ops](https://github.com/orgs/elastic/teams/response-ops) | - | 533 | 10 | 504 | 50 | +| | [@elastic/response-ops](https://github.com/orgs/elastic/teams/response-ops) | - | 533 | 10 | 504 | 49 | | | [@elastic/appex-sharedux](https://github.com/orgs/elastic/teams/appex-sharedux) | Adds UI Actions service to Kibana | 134 | 2 | 92 | 9 | | | [@elastic/appex-sharedux](https://github.com/orgs/elastic/teams/appex-sharedux) | Extends UI Actions plugin with more functionality | 206 | 0 | 140 | 9 | | | [@elastic/kibana-data-discovery](https://github.com/orgs/elastic/teams/kibana-data-discovery) | Contains functionality for the field list which can be integrated into apps | 267 | 0 | 242 | 7 | @@ -199,7 +199,7 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana'] | | [@elastic/ml-ui](https://github.com/orgs/elastic/teams/ml-ui) | - | 6 | 0 | 6 | 0 | | | [@elastic/ml-ui](https://github.com/orgs/elastic/teams/ml-ui) | - | 53 | 0 | 22 | 0 | | | [@elastic/security-solution](https://github.com/orgs/elastic/teams/security-solution) | - | 9 | 1 | 9 | 0 | -| | [@elastic/response-ops](https://github.com/orgs/elastic/teams/response-ops) | - | 12 | 0 | 12 | 0 | +| | [@elastic/response-ops](https://github.com/orgs/elastic/teams/response-ops) | - | 14 | 0 | 14 | 0 | | | [@elastic/response-ops](https://github.com/orgs/elastic/teams/response-ops) | - | 5 | 0 | 4 | 0 | | | [@elastic/kibana-core](https://github.com/orgs/elastic/teams/kibana-core) | - | 73 | 0 | 73 | 2 | | | [@elastic/kibana-core](https://github.com/orgs/elastic/teams/kibana-core) | - | 98 | 0 | 0 | 0 | @@ -347,7 +347,7 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana'] | | [@elastic/kibana-core](https://github.com/orgs/elastic/teams/kibana-core) | - | 334 | 1 | 4 | 1 | | | [@elastic/kibana-core](https://github.com/orgs/elastic/teams/kibana-core) | - | 75 | 0 | 54 | 1 | | | [@elastic/kibana-core](https://github.com/orgs/elastic/teams/kibana-core) | - | 11 | 0 | 11 | 0 | -| | [@elastic/kibana-core](https://github.com/orgs/elastic/teams/kibana-core) | - | 47 | 0 | 36 | 6 | +| | [@elastic/kibana-core](https://github.com/orgs/elastic/teams/kibana-core) | - | 76 | 0 | 56 | 7 | | | [@elastic/kibana-core](https://github.com/orgs/elastic/teams/kibana-core) | - | 4 | 0 | 4 | 0 | | | [@elastic/kibana-core](https://github.com/orgs/elastic/teams/kibana-core) | - | 2 | 0 | 1 | 0 | | | [@elastic/kibana-core](https://github.com/orgs/elastic/teams/kibana-core) | - | 6 | 0 | 6 | 0 | @@ -455,7 +455,7 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana'] | | [@elastic/security-threat-hunting-explore](https://github.com/orgs/elastic/teams/security-threat-hunting-explore) | - | 341 | 1 | 337 | 32 | | | [@elastic/security-solution-platform](https://github.com/orgs/elastic/teams/security-solution-platform) | - | 67 | 0 | 61 | 1 | | | [@elastic/security-solution-platform](https://github.com/orgs/elastic/teams/security-solution-platform) | - | 104 | 0 | 93 | 1 | -| | [@elastic/security-threat-hunting-explore](https://github.com/orgs/elastic/teams/security-threat-hunting-explore) | - | 17 | 0 | 16 | 4 | +| | [@elastic/security-threat-hunting-explore](https://github.com/orgs/elastic/teams/security-threat-hunting-explore) | - | 18 | 0 | 17 | 4 | | | [@elastic/security-solution-platform](https://github.com/orgs/elastic/teams/security-solution-platform) | - | 15 | 0 | 7 | 0 | | | [@elastic/security-solution-platform](https://github.com/orgs/elastic/teams/security-solution-platform) | - | 140 | 0 | 121 | 0 | | | [@elastic/security-solution-platform](https://github.com/orgs/elastic/teams/security-solution-platform) | - | 516 | 1 | 503 | 0 | diff --git a/api_docs/presentation_util.mdx b/api_docs/presentation_util.mdx index b841a8b0efdf9..57711209c35a9 100644 --- a/api_docs/presentation_util.mdx +++ b/api_docs/presentation_util.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/presentationUtil title: "presentationUtil" image: https://source.unsplash.com/400x175/?github description: API docs for the presentationUtil plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'presentationUtil'] --- import presentationUtilObj from './presentation_util.devdocs.json'; diff --git a/api_docs/profiling.mdx b/api_docs/profiling.mdx index d8dd426242b35..87288293bf7d8 100644 --- a/api_docs/profiling.mdx +++ b/api_docs/profiling.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/profiling title: "profiling" image: https://source.unsplash.com/400x175/?github description: API docs for the profiling plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'profiling'] --- import profilingObj from './profiling.devdocs.json'; diff --git a/api_docs/remote_clusters.mdx b/api_docs/remote_clusters.mdx index 028baf97f67ff..2e68530ec647e 100644 --- a/api_docs/remote_clusters.mdx +++ b/api_docs/remote_clusters.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/remoteClusters title: "remoteClusters" image: https://source.unsplash.com/400x175/?github description: API docs for the remoteClusters plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'remoteClusters'] --- import remoteClustersObj from './remote_clusters.devdocs.json'; diff --git a/api_docs/reporting.mdx b/api_docs/reporting.mdx index 9879cfdadbbde..2733cbd876970 100644 --- a/api_docs/reporting.mdx +++ b/api_docs/reporting.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/reporting title: "reporting" image: https://source.unsplash.com/400x175/?github description: API docs for the reporting plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'reporting'] --- import reportingObj from './reporting.devdocs.json'; diff --git a/api_docs/rollup.mdx b/api_docs/rollup.mdx index 33c381981db41..aace62a20534e 100644 --- a/api_docs/rollup.mdx +++ b/api_docs/rollup.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/rollup title: "rollup" image: https://source.unsplash.com/400x175/?github description: API docs for the rollup plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'rollup'] --- import rollupObj from './rollup.devdocs.json'; diff --git a/api_docs/rule_registry.devdocs.json b/api_docs/rule_registry.devdocs.json index e0588585ba405..51430b3f825c1 100644 --- a/api_docs/rule_registry.devdocs.json +++ b/api_docs/rule_registry.devdocs.json @@ -1900,7 +1900,13 @@ ") => Promise<{ new: { count: number; data: { _id: string; _index: string; }[]; }; ongoing: { count: number; data: { _id: string; _index: string; }[]; }; recovered: { count: number; data: { _id: string; _index: string; }[]; }; }>; id: string; name: string; validate?: { params?: ", "RuleTypeParamsValidator", " | undefined; } | undefined; cancelAlertsOnRuleTimeout?: boolean | undefined; alerts?: ", - "IRuleTypeAlerts", + { + "pluginId": "alerting", + "scope": "server", + "docId": "kibAlertingPluginApi", + "section": "def-server.IRuleTypeAlerts", + "text": "IRuleTypeAlerts" + }, " | undefined; producer: string; actionGroups: ", { "pluginId": "alerting", diff --git a/api_docs/rule_registry.mdx b/api_docs/rule_registry.mdx index f02930726161a..7ec99d35dc4d1 100644 --- a/api_docs/rule_registry.mdx +++ b/api_docs/rule_registry.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/ruleRegistry title: "ruleRegistry" image: https://source.unsplash.com/400x175/?github description: API docs for the ruleRegistry plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'ruleRegistry'] --- import ruleRegistryObj from './rule_registry.devdocs.json'; diff --git a/api_docs/runtime_fields.mdx b/api_docs/runtime_fields.mdx index fdbd2cec72b26..2d90a880b13b0 100644 --- a/api_docs/runtime_fields.mdx +++ b/api_docs/runtime_fields.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/runtimeFields title: "runtimeFields" image: https://source.unsplash.com/400x175/?github description: API docs for the runtimeFields plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'runtimeFields'] --- import runtimeFieldsObj from './runtime_fields.devdocs.json'; diff --git a/api_docs/saved_objects.mdx b/api_docs/saved_objects.mdx index ced4234b1f0d8..10fc4017b46f6 100644 --- a/api_docs/saved_objects.mdx +++ b/api_docs/saved_objects.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/savedObjects title: "savedObjects" image: https://source.unsplash.com/400x175/?github description: API docs for the savedObjects plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'savedObjects'] --- import savedObjectsObj from './saved_objects.devdocs.json'; diff --git a/api_docs/saved_objects_finder.mdx b/api_docs/saved_objects_finder.mdx index cf4a7a7fffe3b..a76665b9579ac 100644 --- a/api_docs/saved_objects_finder.mdx +++ b/api_docs/saved_objects_finder.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/savedObjectsFinder title: "savedObjectsFinder" image: https://source.unsplash.com/400x175/?github description: API docs for the savedObjectsFinder plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'savedObjectsFinder'] --- import savedObjectsFinderObj from './saved_objects_finder.devdocs.json'; diff --git a/api_docs/saved_objects_management.mdx b/api_docs/saved_objects_management.mdx index 1fa275428fa84..1970bd4a1ca5c 100644 --- a/api_docs/saved_objects_management.mdx +++ b/api_docs/saved_objects_management.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/savedObjectsManagement title: "savedObjectsManagement" image: https://source.unsplash.com/400x175/?github description: API docs for the savedObjectsManagement plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'savedObjectsManagement'] --- import savedObjectsManagementObj from './saved_objects_management.devdocs.json'; diff --git a/api_docs/saved_objects_tagging.mdx b/api_docs/saved_objects_tagging.mdx index 96e367093e3e3..35d1080f9eea4 100644 --- a/api_docs/saved_objects_tagging.mdx +++ b/api_docs/saved_objects_tagging.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/savedObjectsTagging title: "savedObjectsTagging" image: https://source.unsplash.com/400x175/?github description: API docs for the savedObjectsTagging plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'savedObjectsTagging'] --- import savedObjectsTaggingObj from './saved_objects_tagging.devdocs.json'; diff --git a/api_docs/saved_objects_tagging_oss.mdx b/api_docs/saved_objects_tagging_oss.mdx index 4d67de7297c82..cc8b7ddab7ec0 100644 --- a/api_docs/saved_objects_tagging_oss.mdx +++ b/api_docs/saved_objects_tagging_oss.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/savedObjectsTaggingOss title: "savedObjectsTaggingOss" image: https://source.unsplash.com/400x175/?github description: API docs for the savedObjectsTaggingOss plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'savedObjectsTaggingOss'] --- import savedObjectsTaggingOssObj from './saved_objects_tagging_oss.devdocs.json'; diff --git a/api_docs/saved_search.mdx b/api_docs/saved_search.mdx index 20bd21e28a490..96844dfed02f4 100644 --- a/api_docs/saved_search.mdx +++ b/api_docs/saved_search.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/savedSearch title: "savedSearch" image: https://source.unsplash.com/400x175/?github description: API docs for the savedSearch plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'savedSearch'] --- import savedSearchObj from './saved_search.devdocs.json'; diff --git a/api_docs/screenshot_mode.mdx b/api_docs/screenshot_mode.mdx index a28a4fc588d61..01e3438419254 100644 --- a/api_docs/screenshot_mode.mdx +++ b/api_docs/screenshot_mode.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/screenshotMode title: "screenshotMode" image: https://source.unsplash.com/400x175/?github description: API docs for the screenshotMode plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'screenshotMode'] --- import screenshotModeObj from './screenshot_mode.devdocs.json'; diff --git a/api_docs/screenshotting.mdx b/api_docs/screenshotting.mdx index 19616239b78b1..1f1ccd969d9e8 100644 --- a/api_docs/screenshotting.mdx +++ b/api_docs/screenshotting.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/screenshotting title: "screenshotting" image: https://source.unsplash.com/400x175/?github description: API docs for the screenshotting plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'screenshotting'] --- import screenshottingObj from './screenshotting.devdocs.json'; diff --git a/api_docs/security.devdocs.json b/api_docs/security.devdocs.json index 497208feeac6e..6416e926dce7a 100644 --- a/api_docs/security.devdocs.json +++ b/api_docs/security.devdocs.json @@ -1206,7 +1206,170 @@ } }, "server": { - "classes": [], + "classes": [ + { + "parentPluginId": "security", + "id": "def-server.HTTPAuthorizationHeader", + "type": "Class", + "tags": [], + "label": "HTTPAuthorizationHeader", + "description": [], + "path": "x-pack/plugins/security/server/authentication/http_authentication/http_authorization_header.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "security", + "id": "def-server.HTTPAuthorizationHeader.scheme", + "type": "string", + "tags": [], + "label": "scheme", + "description": [ + "\nThe authentication scheme. Should be consumed in a case-insensitive manner.\nhttps://www.iana.org/assignments/http-authschemes/http-authschemes.xhtml#authschemes" + ], + "path": "x-pack/plugins/security/server/authentication/http_authentication/http_authorization_header.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "security", + "id": "def-server.HTTPAuthorizationHeader.credentials", + "type": "string", + "tags": [], + "label": "credentials", + "description": [ + "\nThe authentication credentials for the scheme." + ], + "path": "x-pack/plugins/security/server/authentication/http_authentication/http_authorization_header.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "security", + "id": "def-server.HTTPAuthorizationHeader.Unnamed", + "type": "Function", + "tags": [], + "label": "Constructor", + "description": [], + "signature": [ + "any" + ], + "path": "x-pack/plugins/security/server/authentication/http_authentication/http_authorization_header.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "security", + "id": "def-server.HTTPAuthorizationHeader.Unnamed.$1", + "type": "string", + "tags": [], + "label": "scheme", + "description": [], + "signature": [ + "string" + ], + "path": "x-pack/plugins/security/server/authentication/http_authentication/http_authorization_header.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + }, + { + "parentPluginId": "security", + "id": "def-server.HTTPAuthorizationHeader.Unnamed.$2", + "type": "string", + "tags": [], + "label": "credentials", + "description": [], + "signature": [ + "string" + ], + "path": "x-pack/plugins/security/server/authentication/http_authentication/http_authorization_header.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + } + ], + "returnComment": [] + }, + { + "parentPluginId": "security", + "id": "def-server.HTTPAuthorizationHeader.parseFromRequest", + "type": "Function", + "tags": [], + "label": "parseFromRequest", + "description": [ + "\nParses request's `Authorization` HTTP header if present." + ], + "signature": [ + "(request: ", + { + "pluginId": "@kbn/core-http-server", + "scope": "common", + "docId": "kibKbnCoreHttpServerPluginApi", + "section": "def-common.KibanaRequest", + "text": "KibanaRequest" + }, + ") => ", + { + "pluginId": "security", + "scope": "server", + "docId": "kibSecurityPluginApi", + "section": "def-server.HTTPAuthorizationHeader", + "text": "HTTPAuthorizationHeader" + }, + " | null" + ], + "path": "x-pack/plugins/security/server/authentication/http_authentication/http_authorization_header.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "security", + "id": "def-server.HTTPAuthorizationHeader.parseFromRequest.$1", + "type": "Object", + "tags": [], + "label": "request", + "description": [ + "Request instance to extract the authorization header from." + ], + "signature": [ + { + "pluginId": "@kbn/core-http-server", + "scope": "common", + "docId": "kibKbnCoreHttpServerPluginApi", + "section": "def-common.KibanaRequest", + "text": "KibanaRequest" + }, + "" + ], + "path": "x-pack/plugins/security/server/authentication/http_authentication/http_authorization_header.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + } + ], + "returnComment": [] + }, + { + "parentPluginId": "security", + "id": "def-server.HTTPAuthorizationHeader.toString", + "type": "Function", + "tags": [], + "label": "toString", + "description": [], + "signature": [ + "() => string" + ], + "path": "x-pack/plugins/security/server/authentication/http_authentication/http_authorization_header.ts", + "deprecated": false, + "trackAdoption": false, + "children": [], + "returnComment": [] + } + ], + "initialIsOpen": false + } + ], "functions": [], "interfaces": [ { diff --git a/api_docs/security.mdx b/api_docs/security.mdx index c3b77e7ae0637..6f57323904bd3 100644 --- a/api_docs/security.mdx +++ b/api_docs/security.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/security title: "security" image: https://source.unsplash.com/400x175/?github description: API docs for the security plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'security'] --- import securityObj from './security.devdocs.json'; @@ -21,7 +21,7 @@ Contact [@elastic/kibana-security](https://github.com/orgs/elastic/teams/kibana- | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 269 | 0 | 89 | 0 | +| 278 | 0 | 94 | 0 | ## Client @@ -45,6 +45,9 @@ Contact [@elastic/kibana-security](https://github.com/orgs/elastic/teams/kibana- ### Start +### Classes + + ### Interfaces diff --git a/api_docs/security_solution.devdocs.json b/api_docs/security_solution.devdocs.json index f8cb3ecf363dd..eea2346c4e160 100644 --- a/api_docs/security_solution.devdocs.json +++ b/api_docs/security_solution.devdocs.json @@ -95,7 +95,7 @@ "label": "experimentalFeatures", "description": [], "signature": [ - "{ readonly tGridEnabled: boolean; readonly tGridEventRenderedViewEnabled: boolean; readonly excludePoliciesInFilterEnabled: boolean; readonly kubernetesEnabled: boolean; readonly disableIsolationUIPendingStatuses: boolean; readonly pendingActionResponsesWithAck: boolean; readonly policyListEnabled: boolean; readonly policyResponseInFleetEnabled: boolean; readonly chartEmbeddablesEnabled: boolean; readonly previewTelemetryUrlEnabled: boolean; readonly responseActionsConsoleEnabled: boolean; readonly insightsRelatedAlertsByProcessAncestry: boolean; readonly extendedRuleExecutionLoggingEnabled: boolean; readonly socTrendsEnabled: boolean; readonly responseActionsEnabled: boolean; readonly endpointRbacEnabled: boolean; readonly endpointRbacV1Enabled: boolean; readonly alertDetailsPageEnabled: boolean; readonly responseActionGetFileEnabled: boolean; readonly responseActionExecuteEnabled: boolean; readonly alertsPageChartsEnabled: boolean; readonly securityFlyoutEnabled: boolean; readonly riskyHostsEnabled: boolean; readonly riskyUsersEnabled: boolean; readonly alertsPageFiltersEnabled: boolean; }" + "{ readonly tGridEnabled: boolean; readonly tGridEventRenderedViewEnabled: boolean; readonly excludePoliciesInFilterEnabled: boolean; readonly kubernetesEnabled: boolean; readonly disableIsolationUIPendingStatuses: boolean; readonly pendingActionResponsesWithAck: boolean; readonly policyListEnabled: boolean; readonly policyResponseInFleetEnabled: boolean; readonly chartEmbeddablesEnabled: boolean; readonly donutChartEmbeddablesEnabled: boolean; readonly alertsPreviewChartEmbeddablesEnabled: boolean; readonly previewTelemetryUrlEnabled: boolean; readonly responseActionsConsoleEnabled: boolean; readonly insightsRelatedAlertsByProcessAncestry: boolean; readonly extendedRuleExecutionLoggingEnabled: boolean; readonly prebuiltRulesNewUpgradeAndInstallationWorkflowsEnabled: boolean; readonly socTrendsEnabled: boolean; readonly responseActionsEnabled: boolean; readonly endpointRbacEnabled: boolean; readonly endpointRbacV1Enabled: boolean; readonly alertDetailsPageEnabled: boolean; readonly responseActionGetFileEnabled: boolean; readonly responseActionExecuteEnabled: boolean; readonly alertsPageChartsEnabled: boolean; readonly alertTypeEnabled: boolean; readonly securityFlyoutEnabled: boolean; readonly riskyHostsEnabled: boolean; readonly riskyUsersEnabled: boolean; readonly alertsPageFiltersEnabled: boolean; }" ], "path": "x-pack/plugins/security_solution/public/plugin.tsx", "deprecated": false, diff --git a/api_docs/security_solution.mdx b/api_docs/security_solution.mdx index b4afc3894940f..867cae72d28a2 100644 --- a/api_docs/security_solution.mdx +++ b/api_docs/security_solution.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/securitySolution title: "securitySolution" image: https://source.unsplash.com/400x175/?github description: API docs for the securitySolution plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'securitySolution'] --- import securitySolutionObj from './security_solution.devdocs.json'; diff --git a/api_docs/session_view.mdx b/api_docs/session_view.mdx index d4e0d1f3b9254..6dfa36814034d 100644 --- a/api_docs/session_view.mdx +++ b/api_docs/session_view.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/sessionView title: "sessionView" image: https://source.unsplash.com/400x175/?github description: API docs for the sessionView plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'sessionView'] --- import sessionViewObj from './session_view.devdocs.json'; diff --git a/api_docs/share.mdx b/api_docs/share.mdx index 4b33651f5fc54..8bbc341f79d5e 100644 --- a/api_docs/share.mdx +++ b/api_docs/share.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/share title: "share" image: https://source.unsplash.com/400x175/?github description: API docs for the share plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'share'] --- import shareObj from './share.devdocs.json'; diff --git a/api_docs/snapshot_restore.mdx b/api_docs/snapshot_restore.mdx index 4dbd17f72b39d..205d50605a5c3 100644 --- a/api_docs/snapshot_restore.mdx +++ b/api_docs/snapshot_restore.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/snapshotRestore title: "snapshotRestore" image: https://source.unsplash.com/400x175/?github description: API docs for the snapshotRestore plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'snapshotRestore'] --- import snapshotRestoreObj from './snapshot_restore.devdocs.json'; diff --git a/api_docs/spaces.mdx b/api_docs/spaces.mdx index 4d07e3121279b..4443f77a61eea 100644 --- a/api_docs/spaces.mdx +++ b/api_docs/spaces.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/spaces title: "spaces" image: https://source.unsplash.com/400x175/?github description: API docs for the spaces plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'spaces'] --- import spacesObj from './spaces.devdocs.json'; diff --git a/api_docs/stack_alerts.mdx b/api_docs/stack_alerts.mdx index 3369b89d03496..5087b3499d02b 100644 --- a/api_docs/stack_alerts.mdx +++ b/api_docs/stack_alerts.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/stackAlerts title: "stackAlerts" image: https://source.unsplash.com/400x175/?github description: API docs for the stackAlerts plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'stackAlerts'] --- import stackAlertsObj from './stack_alerts.devdocs.json'; diff --git a/api_docs/stack_connectors.mdx b/api_docs/stack_connectors.mdx index b48e1ef02dda5..3f0b9d89b931d 100644 --- a/api_docs/stack_connectors.mdx +++ b/api_docs/stack_connectors.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/stackConnectors title: "stackConnectors" image: https://source.unsplash.com/400x175/?github description: API docs for the stackConnectors plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'stackConnectors'] --- import stackConnectorsObj from './stack_connectors.devdocs.json'; diff --git a/api_docs/task_manager.devdocs.json b/api_docs/task_manager.devdocs.json index 915e316b089ae..e6149a28ee203 100644 --- a/api_docs/task_manager.devdocs.json +++ b/api_docs/task_manager.devdocs.json @@ -1074,55 +1074,6 @@ "deprecated": false, "trackAdoption": false }, - { - "parentPluginId": "taskManager", - "id": "def-server.TaskRegisterDefinition.getRetry", - "type": "Function", - "tags": [], - "label": "getRetry", - "description": [ - "\nFunction that customizes how the task should behave when the task fails. This\nfunction can return `true`, `false` or a Date. True will tell task manager\nto retry using default delay logic. False will tell task manager to stop retrying\nthis task. Date will suggest when to the task manager the task should retry.\nThis function isn't used for recurring tasks, those retry as per their configured recurring schedule." - ], - "signature": [ - "((attempts: number, error: object) => boolean | Date) | undefined" - ], - "path": "x-pack/plugins/task_manager/server/task_type_dictionary.ts", - "deprecated": false, - "trackAdoption": false, - "children": [ - { - "parentPluginId": "taskManager", - "id": "def-server.TaskRegisterDefinition.getRetry.$1", - "type": "number", - "tags": [], - "label": "attempts", - "description": [], - "signature": [ - "number" - ], - "path": "x-pack/plugins/task_manager/server/task_type_dictionary.ts", - "deprecated": false, - "trackAdoption": false, - "isRequired": true - }, - { - "parentPluginId": "taskManager", - "id": "def-server.TaskRegisterDefinition.getRetry.$2", - "type": "Uncategorized", - "tags": [], - "label": "error", - "description": [], - "signature": [ - "object" - ], - "path": "x-pack/plugins/task_manager/server/task_type_dictionary.ts", - "deprecated": false, - "trackAdoption": false, - "isRequired": true - } - ], - "returnComment": [] - }, { "parentPluginId": "taskManager", "id": "def-server.TaskRegisterDefinition.createTaskRunner", diff --git a/api_docs/task_manager.mdx b/api_docs/task_manager.mdx index 6ad31dfe4bc9b..8dd6e53abd6e5 100644 --- a/api_docs/task_manager.mdx +++ b/api_docs/task_manager.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/taskManager title: "taskManager" image: https://source.unsplash.com/400x175/?github description: API docs for the taskManager plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'taskManager'] --- import taskManagerObj from './task_manager.devdocs.json'; @@ -21,7 +21,7 @@ Contact [@elastic/response-ops](https://github.com/orgs/elastic/teams/response-o | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 83 | 0 | 41 | 7 | +| 80 | 0 | 39 | 7 | ## Server diff --git a/api_docs/telemetry.mdx b/api_docs/telemetry.mdx index a3a555a6e025b..d36a0ae8995ba 100644 --- a/api_docs/telemetry.mdx +++ b/api_docs/telemetry.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/telemetry title: "telemetry" image: https://source.unsplash.com/400x175/?github description: API docs for the telemetry plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'telemetry'] --- import telemetryObj from './telemetry.devdocs.json'; diff --git a/api_docs/telemetry_collection_manager.mdx b/api_docs/telemetry_collection_manager.mdx index 837039232491a..a24c82df4d2ec 100644 --- a/api_docs/telemetry_collection_manager.mdx +++ b/api_docs/telemetry_collection_manager.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/telemetryCollectionManager title: "telemetryCollectionManager" image: https://source.unsplash.com/400x175/?github description: API docs for the telemetryCollectionManager plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'telemetryCollectionManager'] --- import telemetryCollectionManagerObj from './telemetry_collection_manager.devdocs.json'; diff --git a/api_docs/telemetry_collection_xpack.mdx b/api_docs/telemetry_collection_xpack.mdx index a084cc3faeb37..52c9f7dd21c90 100644 --- a/api_docs/telemetry_collection_xpack.mdx +++ b/api_docs/telemetry_collection_xpack.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/telemetryCollectionXpack title: "telemetryCollectionXpack" image: https://source.unsplash.com/400x175/?github description: API docs for the telemetryCollectionXpack plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'telemetryCollectionXpack'] --- import telemetryCollectionXpackObj from './telemetry_collection_xpack.devdocs.json'; diff --git a/api_docs/telemetry_management_section.mdx b/api_docs/telemetry_management_section.mdx index 96213798a41b9..9e0333ef03f7a 100644 --- a/api_docs/telemetry_management_section.mdx +++ b/api_docs/telemetry_management_section.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/telemetryManagementSection title: "telemetryManagementSection" image: https://source.unsplash.com/400x175/?github description: API docs for the telemetryManagementSection plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'telemetryManagementSection'] --- import telemetryManagementSectionObj from './telemetry_management_section.devdocs.json'; diff --git a/api_docs/threat_intelligence.mdx b/api_docs/threat_intelligence.mdx index ebf6929e9c7f8..edee90eb53682 100644 --- a/api_docs/threat_intelligence.mdx +++ b/api_docs/threat_intelligence.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/threatIntelligence title: "threatIntelligence" image: https://source.unsplash.com/400x175/?github description: API docs for the threatIntelligence plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'threatIntelligence'] --- import threatIntelligenceObj from './threat_intelligence.devdocs.json'; diff --git a/api_docs/timelines.mdx b/api_docs/timelines.mdx index 3ff07dda3d5cd..eb8be4196ad5b 100644 --- a/api_docs/timelines.mdx +++ b/api_docs/timelines.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/timelines title: "timelines" image: https://source.unsplash.com/400x175/?github description: API docs for the timelines plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'timelines'] --- import timelinesObj from './timelines.devdocs.json'; diff --git a/api_docs/transform.mdx b/api_docs/transform.mdx index 8065c252f1740..354c9ba330297 100644 --- a/api_docs/transform.mdx +++ b/api_docs/transform.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/transform title: "transform" image: https://source.unsplash.com/400x175/?github description: API docs for the transform plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'transform'] --- import transformObj from './transform.devdocs.json'; diff --git a/api_docs/triggers_actions_ui.devdocs.json b/api_docs/triggers_actions_ui.devdocs.json index a5736cefd3749..791444d2576da 100644 --- a/api_docs/triggers_actions_ui.devdocs.json +++ b/api_docs/triggers_actions_ui.devdocs.json @@ -1109,7 +1109,13 @@ "({\n http,\n searchText,\n typesFilter,\n actionTypesFilter,\n ruleExecutionStatusesFilter,\n ruleStatusesFilter,\n tagsFilter,\n}: ", "LoadRuleAggregationsProps", ") => Promise<", - "RuleAggregations", + { + "pluginId": "alerting", + "scope": "common", + "docId": "kibAlertingPluginApi", + "section": "def-common.RuleAggregationFormattedResult", + "text": "RuleAggregationFormattedResult" + }, ">" ], "path": "x-pack/plugins/triggers_actions_ui/public/application/lib/rule_api/aggregate.ts", diff --git a/api_docs/triggers_actions_ui.mdx b/api_docs/triggers_actions_ui.mdx index ce0b794f990ae..63c37d77d32c6 100644 --- a/api_docs/triggers_actions_ui.mdx +++ b/api_docs/triggers_actions_ui.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/triggersActionsUi title: "triggersActionsUi" image: https://source.unsplash.com/400x175/?github description: API docs for the triggersActionsUi plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'triggersActionsUi'] --- import triggersActionsUiObj from './triggers_actions_ui.devdocs.json'; @@ -21,7 +21,7 @@ Contact [@elastic/response-ops](https://github.com/orgs/elastic/teams/response-o | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 533 | 10 | 504 | 50 | +| 533 | 10 | 504 | 49 | ## Client diff --git a/api_docs/ui_actions.mdx b/api_docs/ui_actions.mdx index 2ad5e4b0d5b5f..97833531b0352 100644 --- a/api_docs/ui_actions.mdx +++ b/api_docs/ui_actions.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/uiActions title: "uiActions" image: https://source.unsplash.com/400x175/?github description: API docs for the uiActions plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'uiActions'] --- import uiActionsObj from './ui_actions.devdocs.json'; diff --git a/api_docs/ui_actions_enhanced.mdx b/api_docs/ui_actions_enhanced.mdx index 87aae7657834a..c8e94d708e6f7 100644 --- a/api_docs/ui_actions_enhanced.mdx +++ b/api_docs/ui_actions_enhanced.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/uiActionsEnhanced title: "uiActionsEnhanced" image: https://source.unsplash.com/400x175/?github description: API docs for the uiActionsEnhanced plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'uiActionsEnhanced'] --- import uiActionsEnhancedObj from './ui_actions_enhanced.devdocs.json'; diff --git a/api_docs/unified_field_list.mdx b/api_docs/unified_field_list.mdx index e98e598c0c1d0..c0afb2cdc8cda 100644 --- a/api_docs/unified_field_list.mdx +++ b/api_docs/unified_field_list.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/unifiedFieldList title: "unifiedFieldList" image: https://source.unsplash.com/400x175/?github description: API docs for the unifiedFieldList plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'unifiedFieldList'] --- import unifiedFieldListObj from './unified_field_list.devdocs.json'; diff --git a/api_docs/unified_histogram.mdx b/api_docs/unified_histogram.mdx index dba12f0e63b4c..a3b26f715a8b9 100644 --- a/api_docs/unified_histogram.mdx +++ b/api_docs/unified_histogram.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/unifiedHistogram title: "unifiedHistogram" image: https://source.unsplash.com/400x175/?github description: API docs for the unifiedHistogram plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'unifiedHistogram'] --- import unifiedHistogramObj from './unified_histogram.devdocs.json'; diff --git a/api_docs/unified_search.mdx b/api_docs/unified_search.mdx index 50bfbe1a647fc..a55e210f159a5 100644 --- a/api_docs/unified_search.mdx +++ b/api_docs/unified_search.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/unifiedSearch title: "unifiedSearch" image: https://source.unsplash.com/400x175/?github description: API docs for the unifiedSearch plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'unifiedSearch'] --- import unifiedSearchObj from './unified_search.devdocs.json'; diff --git a/api_docs/unified_search_autocomplete.mdx b/api_docs/unified_search_autocomplete.mdx index 43c40f6bb3a78..3d19073fa751d 100644 --- a/api_docs/unified_search_autocomplete.mdx +++ b/api_docs/unified_search_autocomplete.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/unifiedSearch-autocomplete title: "unifiedSearch.autocomplete" image: https://source.unsplash.com/400x175/?github description: API docs for the unifiedSearch.autocomplete plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'unifiedSearch.autocomplete'] --- import unifiedSearchAutocompleteObj from './unified_search_autocomplete.devdocs.json'; diff --git a/api_docs/url_forwarding.mdx b/api_docs/url_forwarding.mdx index effcc2fe1351a..74131e655a950 100644 --- a/api_docs/url_forwarding.mdx +++ b/api_docs/url_forwarding.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/urlForwarding title: "urlForwarding" image: https://source.unsplash.com/400x175/?github description: API docs for the urlForwarding plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'urlForwarding'] --- import urlForwardingObj from './url_forwarding.devdocs.json'; diff --git a/api_docs/usage_collection.mdx b/api_docs/usage_collection.mdx index 6edf129b155ee..c378a7e609215 100644 --- a/api_docs/usage_collection.mdx +++ b/api_docs/usage_collection.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/usageCollection title: "usageCollection" image: https://source.unsplash.com/400x175/?github description: API docs for the usageCollection plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'usageCollection'] --- import usageCollectionObj from './usage_collection.devdocs.json'; diff --git a/api_docs/ux.mdx b/api_docs/ux.mdx index 2837412b7fdac..20cc0dc720bd8 100644 --- a/api_docs/ux.mdx +++ b/api_docs/ux.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/ux title: "ux" image: https://source.unsplash.com/400x175/?github description: API docs for the ux plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'ux'] --- import uxObj from './ux.devdocs.json'; diff --git a/api_docs/vis_default_editor.mdx b/api_docs/vis_default_editor.mdx index 939052e27698a..290dcb5dd6e5f 100644 --- a/api_docs/vis_default_editor.mdx +++ b/api_docs/vis_default_editor.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/visDefaultEditor title: "visDefaultEditor" image: https://source.unsplash.com/400x175/?github description: API docs for the visDefaultEditor plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'visDefaultEditor'] --- import visDefaultEditorObj from './vis_default_editor.devdocs.json'; diff --git a/api_docs/vis_type_gauge.mdx b/api_docs/vis_type_gauge.mdx index 45783d649516d..8642cd3628324 100644 --- a/api_docs/vis_type_gauge.mdx +++ b/api_docs/vis_type_gauge.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/visTypeGauge title: "visTypeGauge" image: https://source.unsplash.com/400x175/?github description: API docs for the visTypeGauge plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'visTypeGauge'] --- import visTypeGaugeObj from './vis_type_gauge.devdocs.json'; diff --git a/api_docs/vis_type_heatmap.mdx b/api_docs/vis_type_heatmap.mdx index ef93ae3109479..7076d73d94b27 100644 --- a/api_docs/vis_type_heatmap.mdx +++ b/api_docs/vis_type_heatmap.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/visTypeHeatmap title: "visTypeHeatmap" image: https://source.unsplash.com/400x175/?github description: API docs for the visTypeHeatmap plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'visTypeHeatmap'] --- import visTypeHeatmapObj from './vis_type_heatmap.devdocs.json'; diff --git a/api_docs/vis_type_pie.mdx b/api_docs/vis_type_pie.mdx index 9308a35c1f9bf..4e144af35077a 100644 --- a/api_docs/vis_type_pie.mdx +++ b/api_docs/vis_type_pie.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/visTypePie title: "visTypePie" image: https://source.unsplash.com/400x175/?github description: API docs for the visTypePie plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'visTypePie'] --- import visTypePieObj from './vis_type_pie.devdocs.json'; diff --git a/api_docs/vis_type_table.mdx b/api_docs/vis_type_table.mdx index 0e25664a82185..f68cd179dc365 100644 --- a/api_docs/vis_type_table.mdx +++ b/api_docs/vis_type_table.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/visTypeTable title: "visTypeTable" image: https://source.unsplash.com/400x175/?github description: API docs for the visTypeTable plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'visTypeTable'] --- import visTypeTableObj from './vis_type_table.devdocs.json'; diff --git a/api_docs/vis_type_timelion.mdx b/api_docs/vis_type_timelion.mdx index aa1276c7d4f9e..03eef9d5bea57 100644 --- a/api_docs/vis_type_timelion.mdx +++ b/api_docs/vis_type_timelion.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/visTypeTimelion title: "visTypeTimelion" image: https://source.unsplash.com/400x175/?github description: API docs for the visTypeTimelion plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'visTypeTimelion'] --- import visTypeTimelionObj from './vis_type_timelion.devdocs.json'; diff --git a/api_docs/vis_type_timeseries.mdx b/api_docs/vis_type_timeseries.mdx index 82e83e7cdd575..fd8d512b5d66b 100644 --- a/api_docs/vis_type_timeseries.mdx +++ b/api_docs/vis_type_timeseries.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/visTypeTimeseries title: "visTypeTimeseries" image: https://source.unsplash.com/400x175/?github description: API docs for the visTypeTimeseries plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'visTypeTimeseries'] --- import visTypeTimeseriesObj from './vis_type_timeseries.devdocs.json'; diff --git a/api_docs/vis_type_vega.mdx b/api_docs/vis_type_vega.mdx index c20709d80e589..13c79d84a5563 100644 --- a/api_docs/vis_type_vega.mdx +++ b/api_docs/vis_type_vega.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/visTypeVega title: "visTypeVega" image: https://source.unsplash.com/400x175/?github description: API docs for the visTypeVega plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'visTypeVega'] --- import visTypeVegaObj from './vis_type_vega.devdocs.json'; diff --git a/api_docs/vis_type_vislib.mdx b/api_docs/vis_type_vislib.mdx index e3d4d95776801..b38bf91be2d4e 100644 --- a/api_docs/vis_type_vislib.mdx +++ b/api_docs/vis_type_vislib.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/visTypeVislib title: "visTypeVislib" image: https://source.unsplash.com/400x175/?github description: API docs for the visTypeVislib plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'visTypeVislib'] --- import visTypeVislibObj from './vis_type_vislib.devdocs.json'; diff --git a/api_docs/vis_type_xy.mdx b/api_docs/vis_type_xy.mdx index d71defc6e57ea..5308bf58d21c7 100644 --- a/api_docs/vis_type_xy.mdx +++ b/api_docs/vis_type_xy.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/visTypeXy title: "visTypeXy" image: https://source.unsplash.com/400x175/?github description: API docs for the visTypeXy plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'visTypeXy'] --- import visTypeXyObj from './vis_type_xy.devdocs.json'; diff --git a/api_docs/visualizations.mdx b/api_docs/visualizations.mdx index 9dee32340c2b4..b887f601043d1 100644 --- a/api_docs/visualizations.mdx +++ b/api_docs/visualizations.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/visualizations title: "visualizations" image: https://source.unsplash.com/400x175/?github description: API docs for the visualizations plugin -date: 2023-03-07 +date: 2023-03-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'visualizations'] --- import visualizationsObj from './visualizations.devdocs.json'; diff --git a/docs/concepts/images/add-filter-popup.png b/docs/concepts/images/add-filter-popup.png index ece20d12f5eaa..38ddfaba69b65 100644 Binary files a/docs/concepts/images/add-filter-popup.png and b/docs/concepts/images/add-filter-popup.png differ diff --git a/docs/concepts/images/keyboard-shortcut-icon.png b/docs/concepts/images/keyboard-shortcut-icon.png new file mode 100644 index 0000000000000..3aa990dcaf40d Binary files /dev/null and b/docs/concepts/images/keyboard-shortcut-icon.png differ diff --git a/docs/developer/getting-started/debugging.asciidoc b/docs/developer/getting-started/debugging.asciidoc index 1254462d2e4ea..0c6821b6bcde4 100644 --- a/docs/developer/getting-started/debugging.asciidoc +++ b/docs/developer/getting-started/debugging.asciidoc @@ -38,15 +38,15 @@ https://www.elastic.co/guide/en/apm/agent/nodejs/current/configuration.html#acti APM config option. All config options can be set by -creating an appropriate config file under `config/apm.dev.js`. +creating an appropriate config file under `config/kibana.dev.yml`. -Example `config/apm.dev.js` file: +Example `config/kibana.dev.yml` file: -[source,js] +[source,yaml] ---- -module.exports = { - active: true, -}; +elastic: + apm: + active: true ---- APM @@ -68,7 +68,7 @@ UI in {kib}. [discrete] === Running Kibana with the APM Agent Locally -The easiest and recommended way of running Kibana with the APM agent locally is to use the solution provided by the https://github.com/elastic/apm-integration-testing[apm-integration-testing] repo. You’ll need https://www.docker.com/community-edition[Docker] and https://docs.docker.com/compose/install/[Docker Compose] to use the tool. +The easiest and recommended way of running Kibana with the APM agent locally is to use the solution provided by the https://github.com/elastic/apm-integration-testing[apm-integration-testing] repo. You’ll need https://www.docker.com/community-edition[Docker], https://docs.docker.com/compose/install/[Docker Compose] and https://www.python.org/downloads[Python (version 3 preferred)] to use the tool. [discrete] ==== Quick start guide @@ -88,6 +88,7 @@ cd apm-integration-testing ./scripts/compose.py start master --no-kibana ---- +. Clone the https://github.com/elastic/kibana[elastic/kibana] repo. . Change into the {kib} repo: + [source,bash] @@ -96,20 +97,20 @@ cd ../kibana ---- . Change the elasticsearch credentials in your `kibana.yml` configuration file to match those needed by elasticsearch and the APM server (see the apm-integration-testing repo's https://github.com/elastic/apm-integration-testing#logging-in[README] for users provided to test different scenarios). -. Make sure that the APM agent is active and points to the local APM server by adding the following configuration settings to a config file under `config/apm.dev.js`: +. Make sure that the APM agent is active and points to the local APM server by adding the following configuration settings to a config file under `config/kibana.dev.yml`: + -Example `config/apm.dev.js` file: +Example `config/kibana.dev.yml` file: + -[source,js] +[source,yaml] ---- -module.exports = { - active: true, - serverUrl: 'http://127.0.0.1:8200', // supports `http://localhost:8200` - centralConfig: false, - breakdownMetrics: false, - transactionSampleRate: 0.1, - metricsInterval: '120s' -}; +elastic: + apm: + active: true + serverUrl: http://localhost:8200 + secretToken: very_secret + centralConfig: true + breakdownMetrics: true + transactionSampleRate: 0.1 ---- . Start Kibana with APM active using: diff --git a/docs/discover/document-explorer.asciidoc b/docs/discover/document-explorer.asciidoc index 79bf640707ebf..2576cd1e3ae6d 100644 --- a/docs/discover/document-explorer.asciidoc +++ b/docs/discover/document-explorer.asciidoc @@ -16,7 +16,7 @@ Hide or resize the chart for a better fit. * To turn off the display of the chart, click image:images/chart-icon.png[icon button for opening Show/Hide chart menu, width=24px] -to open the *Chart options* menu, and then click *Hide*. +to open the *Chart options* menu, and then click *Hide chart*. * To change the chart height, drag the resize handle image:images/resize-icon.png[two-line icon for increasing or decreasing the height of the chart, width=24px] @@ -84,6 +84,7 @@ image::images/document-explorer-multi-field.png[Multi field sort in the document . To change the sort order, select a field in the pop-up, and then drag it to the new location. + [float] [[document-explorer-edit-field]] ==== Edit a field @@ -114,20 +115,11 @@ image::images/document-explorer-compare-data.png[Compare data in the document ta [[document-explorer-configure-table]] ==== Set the number of rows per page -To change the numbers of rows you want to display on each page, use the *Rows per page* menu. By default, the document table displays 100 rows per page. +To change the numbers of rows you want to display on each page, use the *Rows per page* menu. The default is 100 rows per page. [role="screenshot"] image::images/document-table-rows-per-page.png["Menu with options for setting the number of rows in the document table"] -To customize the default number of rows per page, go to *Stack Managaement > Advanced Settings* -and search for *rows per page*. - -[float] -[[document-explorer-full-screen]] -==== View in fullscreen - -To toggle the table in and out of fullscreen mode, click the fullscreen icon -image:images/fullscreen-icon.png[icon to display the document table in fullscreen mode]. [float] [[document-explorer-expand-documents]] diff --git a/docs/discover/field-statistics.asciidoc b/docs/discover/field-statistics.asciidoc index 6c5b5a6767a50..8dccc0d4a5bbd 100644 --- a/docs/discover/field-statistics.asciidoc +++ b/docs/discover/field-statistics.asciidoc @@ -1,8 +1,6 @@ [[show-field-statistics]] == View field statistics -beta::[] - Explore the fields in your data with the *Field statistics* view in *Discover* and answer such questions as: diff --git a/docs/discover/images/customer.png b/docs/discover/images/customer.png index 11504bf10ac20..3163d8458e12c 100644 Binary files a/docs/discover/images/customer.png and b/docs/discover/images/customer.png differ diff --git a/docs/discover/images/discover-add-filter.png b/docs/discover/images/discover-add-filter.png index b022090afcb1f..3ce158fc4fb84 100644 Binary files a/docs/discover/images/discover-add-filter.png and b/docs/discover/images/discover-add-filter.png differ diff --git a/docs/discover/images/discover-add-icon.png b/docs/discover/images/discover-add-icon.png index c8330ef07b07c..aa9f3ac40f440 100644 Binary files a/docs/discover/images/discover-add-icon.png and b/docs/discover/images/discover-add-icon.png differ diff --git a/docs/discover/images/discover-search-for-relevance.png b/docs/discover/images/discover-search-for-relevance.png index ea7c1a8d93436..2157604ddec30 100644 Binary files a/docs/discover/images/discover-search-for-relevance.png and b/docs/discover/images/discover-search-for-relevance.png differ diff --git a/docs/discover/images/discover-sidebar-available-fields.png b/docs/discover/images/discover-sidebar-available-fields.png index 37ae6858337b6..a5c24f27a0ed2 100644 Binary files a/docs/discover/images/discover-sidebar-available-fields.png and b/docs/discover/images/discover-sidebar-available-fields.png differ diff --git a/docs/discover/images/discover-visualize.png b/docs/discover/images/discover-visualize.png index f88bbb7d51a62..15321c3ea9755 100644 Binary files a/docs/discover/images/discover-visualize.png and b/docs/discover/images/discover-visualize.png differ diff --git a/docs/discover/images/document-explorer-row-height.png b/docs/discover/images/document-explorer-row-height.png index 3706f195c84ad..d274d8de338c2 100644 Binary files a/docs/discover/images/document-explorer-row-height.png and b/docs/discover/images/document-explorer-row-height.png differ diff --git a/docs/discover/images/document-table-expanded.png b/docs/discover/images/document-table-expanded.png index d92bf70821126..d0b85010c5b7d 100644 Binary files a/docs/discover/images/document-table-expanded.png and b/docs/discover/images/document-table-expanded.png differ diff --git a/docs/discover/images/document-table.png b/docs/discover/images/document-table.png index 6a27d972655aa..f2a9e57a38cbc 100644 Binary files a/docs/discover/images/document-table.png and b/docs/discover/images/document-table.png differ diff --git a/docs/discover/images/field-statistics-geo.png b/docs/discover/images/field-statistics-geo.png index da720e7429eb6..905747086372b 100644 Binary files a/docs/discover/images/field-statistics-geo.png and b/docs/discover/images/field-statistics-geo.png differ diff --git a/docs/discover/images/field-statistics-numeric.png b/docs/discover/images/field-statistics-numeric.png index cc298e75dcb65..d051dd725eece 100644 Binary files a/docs/discover/images/field-statistics-numeric.png and b/docs/discover/images/field-statistics-numeric.png differ diff --git a/docs/discover/images/field-statistics-view.png b/docs/discover/images/field-statistics-view.png index bf610add8a790..9ee2650f12b9c 100644 Binary files a/docs/discover/images/field-statistics-view.png and b/docs/discover/images/field-statistics-view.png differ diff --git a/docs/discover/images/hello-field.png b/docs/discover/images/hello-field.png index 2471ee494f29d..8d8d5943ba76a 100644 Binary files a/docs/discover/images/hello-field.png and b/docs/discover/images/hello-field.png differ diff --git a/docs/discover/search-for-relevance.asciidoc b/docs/discover/search-for-relevance.asciidoc index b2c69a3cc5479..24cd13c05f7e9 100644 --- a/docs/discover/search-for-relevance.asciidoc +++ b/docs/discover/search-for-relevance.asciidoc @@ -18,7 +18,8 @@ For the sample flights data, set the {data-source} to *Kibana Sample Data Flight Warsaw OR Venice OR Clear ``` . If you don't see any results, expand the <>, for example to *Last 7 days*. -. From the list of *Available fields*, add `_score` and any other fields you want to the document table. +. From the list of *Meta fields* list in the sidebar, add `_score`. +. Add any other fields you want to the document table. + At this point, you're sorting by the`timestamp` field. . To turn off sorting by the `timestamp` field, click the *field sorted* option, and then click *Clear sorting.* diff --git a/docs/index-custom-title-page.html b/docs/index-custom-title-page.html index baaa155a8913b..87405c783a0a7 100644 --- a/docs/index-custom-title-page.html +++ b/docs/index-custom-title-page.html @@ -71,7 +71,7 @@

Bring your data to life

-

Explore by use case

+

Explore by Elastic solution

@@ -79,7 +79,7 @@

Explore by use case

- Search my data + Enterprise Search

Create search experiences for your content, wherever it lives.

@@ -90,9 +90,9 @@

- Observe my data + Observability

-

Follow our guides to monitor logs, metrics, and traces.

+

Learn how to monitor logs, metrics, and traces.

@@ -101,7 +101,7 @@

- Protect my environment + Security

Learn how to defend against threats across your environment.

diff --git a/docs/user/dashboard/dashboard.asciidoc b/docs/user/dashboard/dashboard.asciidoc index a6a540acfc973..cd6a024da8172 100644 --- a/docs/user/dashboard/dashboard.asciidoc +++ b/docs/user/dashboard/dashboard.asciidoc @@ -293,6 +293,8 @@ To make changes to the panel, use the panel menu options. + To make changes without changing the original version, open the panel menu, then click *More > Unlink from library*. +* *Convert to Lens* — Opens *TSVB* and aggregation-based visualizations in *Lens*. + * *Edit panel settings* — Opens the *Panel settings* window to change the *title*, *description*, and *time range*. * *More > Replace panel* — Opens the *Visualize Library* so you can select a new panel to replace the existing panel. @@ -343,10 +345,16 @@ To apply a panel-level time filter: . Open the panel menu, then select *More > Edit panel settings*. -. Toggle the switch labelled *Apply a custom time range*. +. Select *Apply a custom time range*. . Enter the time range you want to view, then click *Save*. +To view and edit panel-level filters: + +. On the panel, click image:images/dashboard_panelFiltersButton_8.7.0.png[Panel filters button on panel header]. + +. To edit, click *Edit filters*. + [float] [[apply-design-options]] == Apply design options diff --git a/docs/user/dashboard/images/dashboard_controlsOptionsList_8.6.0.png b/docs/user/dashboard/images/dashboard_controlsOptionsList_8.6.0.png deleted file mode 100644 index 0002dc2ab784f..0000000000000 Binary files a/docs/user/dashboard/images/dashboard_controlsOptionsList_8.6.0.png and /dev/null differ diff --git a/docs/user/dashboard/images/dashboard_controlsOptionsList_8.7.0.png b/docs/user/dashboard/images/dashboard_controlsOptionsList_8.7.0.png new file mode 100644 index 0000000000000..5ee20ddd44153 Binary files /dev/null and b/docs/user/dashboard/images/dashboard_controlsOptionsList_8.7.0.png differ diff --git a/docs/user/dashboard/images/dashboard_createNewImageButton_8.7.0.png b/docs/user/dashboard/images/dashboard_createNewImageButton_8.7.0.png index c6522f38dbb57..badd98f94e1de 100644 Binary files a/docs/user/dashboard/images/dashboard_createNewImageButton_8.7.0.png and b/docs/user/dashboard/images/dashboard_createNewImageButton_8.7.0.png differ diff --git a/docs/user/dashboard/images/dashboard_panelFiltersButton_8.7.0.png b/docs/user/dashboard/images/dashboard_panelFiltersButton_8.7.0.png new file mode 100644 index 0000000000000..b1188f3781801 Binary files /dev/null and b/docs/user/dashboard/images/dashboard_panelFiltersButton_8.7.0.png differ diff --git a/docs/user/dashboard/images/dashboard_timeSliderControl_8.5.0.gif b/docs/user/dashboard/images/dashboard_timeSliderControl_8.5.0.gif deleted file mode 100644 index 89ca09dccc71e..0000000000000 Binary files a/docs/user/dashboard/images/dashboard_timeSliderControl_8.5.0.gif and /dev/null differ diff --git a/docs/user/dashboard/images/dashboard_timeSliderControl_8.7.0.gif b/docs/user/dashboard/images/dashboard_timeSliderControl_8.7.0.gif new file mode 100644 index 0000000000000..22b0c25310c88 Binary files /dev/null and b/docs/user/dashboard/images/dashboard_timeSliderControl_8.7.0.gif differ diff --git a/docs/user/dashboard/make-dashboards-interactive.asciidoc b/docs/user/dashboard/make-dashboards-interactive.asciidoc index 507f706b9a9cf..e66e7c33b4be4 100644 --- a/docs/user/dashboard/make-dashboards-interactive.asciidoc +++ b/docs/user/dashboard/make-dashboards-interactive.asciidoc @@ -35,7 +35,7 @@ There are three types of controls: For example, if you are using the *[Logs] Web Traffic* dashboard from the sample web logs data, you can add an options list for the `machine.os.keyword` field that allows you to display only the logs generated from `osx` and `ios` operating systems. + [role="screenshot"] -image::images/dashboard_controlsOptionsList_8.6.0.png[Options list control for the `machine.os.keyword` field with the `osx` and `ios` options selected] +image::images/dashboard_controlsOptionsList_8.7.0.png[Options list control for the `machine.os.keyword` field with the `osx` and `ios` options selected] * *Range slider* — Adds a slider that allows you to filter the data within a specified range of values. + @@ -48,7 +48,7 @@ image::images/dashboard_controlsRangeSlider_8.3.0.png[Range slider control for t + For example, you are using the *[Logs] Web Traffic* dashboard from the sample web logs data, and the global time filter is *Last 7 days*. When you add the time slider, you can click the previous and next buttons to advance the time range backward or forward, and click the play button to watch how the data changes over the last 7 days. [role="screenshot"] -image::images/dashboard_timeSliderControl_8.5.0.gif[Time slider control for the the Last 7 days] +image::images/dashboard_timeSliderControl_8.7.0.gif[Time slider control for the the Last 7 days] [float] [[create-and-add-options-list-and-range-slider-controls]] @@ -80,10 +80,6 @@ The *Control type* is automatically applied for the field you selected. * To allow multiple options to be selected in the dropdown, select *Allow multiple selections in dropdown*. -* To allow options to be inlcluded or excluded on the dashboard, select *Allow selections to be excluded*. - -* To allow an exists query to be created, select *Allow exists query*. - * To populate the entire list of options, even when the list takes longer to populate than expected, select *Ignore timeout for results*. . Click *Save and close*. diff --git a/docs/user/discover.asciidoc b/docs/user/discover.asciidoc index 823d9dcf1fe53..17cde2abcba6b 100644 --- a/docs/user/discover.asciidoc +++ b/docs/user/discover.asciidoc @@ -13,7 +13,7 @@ about the structure of the fields, and display your findings in a visualization. You can also customize and save your searches and place them on a dashboard. [role="screenshot"] -image::images/discover.png[A view of the Discover app] +image::images/hello-field.png[A view of the Discover app] [float] @@ -82,13 +82,12 @@ that shows all the documents that match your search. By default, the document table includes a column for the time field and a column that lists all other fields in the document. You’ll modify the document table to display your fields of interest. -. Scan through the list of **Available fields** until you find the `manufacturer` field. -You can also search for the field by name. +. In the sidebar, enter `ma` to in the search field to find the `manufacturer` field. + [role="screenshot"] image:images/discover-sidebar-available-fields.png[Fields list that displays the top five search results, width=50%] -. Click `manufacturer` to view its most popular values. +. In the *Available fields* list, click `manufacturer` to view its most popular values. + **Discover** shows the top 10 values and the number of records used to calculate those values. @@ -104,14 +103,30 @@ them to the document table. Your table should look similar to this: [role="screenshot"] image:images/document-table.png[Document table with fields for manufacturer, customer_first_name, and customer_last_name] -. To rearrange the table columns, click a -column header, and then select *Move left* or *Move right*. -. To view more of the document table, +. Optionally try out these actions: ++ +* To rearrange the table columns, click a +column header, and then select *Move left* or *Move right*. ++ +* To copy the name or values in a column to the clipboard, click the column header and select the desired **Copy** option. ++ +* For keyboard shortcuts on the document table, click +image:images/keyboard-shortcut-icon.png[icon button for opening list of keyboard shortcuts, width=24px]. ++ +* To view more of the document table, click image:images/chart-icon.png[icon button for opening Show/Hide chart menu, width=24px] to open the *Chart options* menu, and then select *Hide chart*. ++ +* To toggle the table in and out of fullscreen mode, click the fullscreen icon +image:images/fullscreen-icon.png[icon to display the document table in fullscreen mode]. + + + + + [float] [[add-field-in-discover]] @@ -137,7 +152,7 @@ emit("Hello World!"); . Click *Save*. -. In the fields list, search for the *hello* field, and then add it to the document table. +. In the sidebar, search for the *hello* field, and then add it to the document table. + [role="screenshot"] image:images/hello-field.png[hello field in the document tables] @@ -178,7 +193,7 @@ you can use to build a structured query. Search the ecommerce data for documents where the country matches US: . Enter `g`, and then select *geoip.country_iso_code*. -. Select *:* for equals some value and *US*, and then click *Refresh*. +. Select *:* for equals some value and *US*, and then click the refresh button or press the Enter key. . For a more complex search, try: + ```ts @@ -197,7 +212,8 @@ and more. Exclude documents where day of week is not Wednesday: . Click image:images/add-icon.png[Add icon] next to the query bar. -. In the *Add filter* pop-up, set *Field* to *day_of_week*, *Operator* to *is not*, and *Value* to *Wednesday*. +. In the *Add filter* pop-up, set the field to *day_of_week*, the operator to *is not*, +and the value to *Wednesday*. + [role="screenshot"] image:images/discover-add-filter.png[Add filter dialog in Discover] @@ -242,19 +258,20 @@ the document table, the sort order, and the {data-source}. . In the toolbar, click **Save**. +. Give your search a title. + . Optionally store <> and the time range with the search. -. Give your search a title, and then click **Save**. +. Click **Save**. [float] === Visualize your findings If a field can be {ref}/search-aggregations.html[aggregated], you can quickly visualize it from **Discover**. -. From the **Available fields** list, click `day_of_week`, and then click **Visualize**. -+ -[role="screenshot"] -image:images/discover-visualize.png[Discover sidebar field popover with visualize button, width=75%] +. In the sidebar, find and then click `day_of_week`. + +. In the popup, click **Visualize**. + {kib} creates a visualization best suited for this field. @@ -315,6 +332,6 @@ include::{kib-repo-dir}/discover/search-for-relevance.asciidoc[] include::{kib-repo-dir}/discover/save-search.asciidoc[] -include::{kib-repo-dir}/discover/search-sessions.asciidoc[] - include::{kib-repo-dir}/discover/field-statistics.asciidoc[] + +include::{kib-repo-dir}/discover/search-sessions.asciidoc[] diff --git a/package.json b/package.json index d0b35a24dd664..c017c702a11d1 100644 --- a/package.json +++ b/package.json @@ -921,7 +921,7 @@ "usng.js": "^0.4.5", "utility-types": "^3.10.0", "uuid": "9.0.0", - "vega": "^5.23.0", + "vega": "5.22.1", "vega-interpreter": "^1.0.4", "vega-lite": "^5.5.0", "vega-schema-url-parser": "^2.2.0", diff --git a/packages/core/capabilities/core-capabilities-server-internal/src/merge_capabilities.ts b/packages/core/capabilities/core-capabilities-server-internal/src/merge_capabilities.ts index 472c11050d87b..be007688154df 100644 --- a/packages/core/capabilities/core-capabilities-server-internal/src/merge_capabilities.ts +++ b/packages/core/capabilities/core-capabilities-server-internal/src/merge_capabilities.ts @@ -10,7 +10,7 @@ import { mergeWith } from 'lodash'; import type { Capabilities } from '@kbn/core-capabilities-common'; export const mergeCapabilities = (...sources: Array>): Capabilities => - mergeWith({}, ...sources, (a: any, b: any) => { + mergeWith({}, ...sources, (a: unknown, b: unknown) => { if ( (typeof a === 'boolean' && typeof b === 'object') || (typeof a === 'object' && typeof b === 'boolean') diff --git a/packages/core/capabilities/core-capabilities-server-internal/src/resolve_capabilities.ts b/packages/core/capabilities/core-capabilities-server-internal/src/resolve_capabilities.ts index 46ec9c9f97eb9..f30392a2213ba 100644 --- a/packages/core/capabilities/core-capabilities-server-internal/src/resolve_capabilities.ts +++ b/packages/core/capabilities/core-capabilities-server-internal/src/resolve_capabilities.ts @@ -46,16 +46,14 @@ export const resolveCapabilities = async ( applications: string[], useDefaultCapabilities: boolean ): Promise => { - const mergedCaps = cloneDeep({ + const mergedCaps: Capabilities = cloneDeep({ ...capabilities, - navLinks: applications.reduce( - (acc, app) => ({ - ...acc, - [app]: true, - }), - capabilities.navLinks - ), + navLinks: applications.reduce((acc, app) => { + acc[app] = true; + return acc; + }, capabilities.navLinks), }); + return switchers.reduce(async (caps, switcher) => { const resolvedCaps = await caps; const changes = await switcher(request, resolvedCaps, useDefaultCapabilities); @@ -79,11 +77,8 @@ function recursiveApplyChanges< } return [key, typeof orig === typeof changed ? changed : orig]; }) - .reduce( - (acc, [key, value]) => ({ - ...acc, - [key]: value, - }), - {} as TDestination - ); + .reduce((acc, [key, value]) => { + acc[key as keyof TDestination] = value; + return acc; + }, {} as TDestination); } diff --git a/packages/core/node/core-node-server-internal/src/node_config.ts b/packages/core/node/core-node-server-internal/src/node_config.ts index f6e8afa694fb7..ecb0cec4fdd57 100644 --- a/packages/core/node/core-node-server-internal/src/node_config.ts +++ b/packages/core/node/core-node-server-internal/src/node_config.ts @@ -41,6 +41,7 @@ export const rolesConfig = schema.arrayOf( ]), { defaultValue: [NODE_WILDCARD_CHAR], + minSize: 1, validate: (value) => { if (value.length > 1) { if (value.includes(NODE_WILDCARD_CHAR)) { @@ -51,7 +52,6 @@ export const rolesConfig = schema.arrayOf( } } }, - minSize: 1, } ); diff --git a/packages/core/root/core-root-server-internal/src/bootstrap.ts b/packages/core/root/core-root-server-internal/src/bootstrap.ts index d5b82a26bf464..3b5a340a7b79c 100644 --- a/packages/core/root/core-root-server-internal/src/bootstrap.ts +++ b/packages/core/root/core-root-server-internal/src/bootstrap.ts @@ -11,6 +11,7 @@ import { getPackages } from '@kbn/repo-packages'; import { CliArgs, Env, RawConfigService } from '@kbn/config'; import { CriticalError } from '@kbn/core-base-server-internal'; import { Root } from './root'; +import { MIGRATION_EXCEPTION_CODE } from './constants'; interface BootstrapArgs { configs: string[]; @@ -114,11 +115,13 @@ export async function bootstrap({ configs, cliArgs, applyConfigOverrides }: Boot function onRootShutdown(reason?: any) { if (reason !== undefined) { - // There is a chance that logger wasn't configured properly and error that - // that forced root to shut down could go unnoticed. To prevent this we always - // mirror such fatal errors in standard output with `console.error`. - // eslint-disable-next-line no-console - console.error(`\n${chalk.white.bgRed(' FATAL ')} ${reason}\n`); + if (reason.code !== MIGRATION_EXCEPTION_CODE) { + // There is a chance that logger wasn't configured properly and error that + // that forced root to shut down could go unnoticed. To prevent this we always + // mirror such fatal errors in standard output with `console.error`. + // eslint-disable-next-line no-console + console.error(`\n${chalk.white.bgRed(' FATAL ')} ${reason}\n`); + } process.exit(reason instanceof CriticalError ? reason.processExitCode : 1); } diff --git a/packages/core/root/core-root-server-internal/src/constants.ts b/packages/core/root/core-root-server-internal/src/constants.ts new file mode 100644 index 0000000000000..7e9796d76ea42 --- /dev/null +++ b/packages/core/root/core-root-server-internal/src/constants.ts @@ -0,0 +1,9 @@ +/* + * 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 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +export const MIGRATION_EXCEPTION_CODE = 'MigrationOnlyNode'; diff --git a/packages/core/root/core-root-server-internal/src/root/index.test.mocks.ts b/packages/core/root/core-root-server-internal/src/root/index.test.mocks.ts index 24de11bef5017..98fc2ec1948ab 100644 --- a/packages/core/root/core-root-server-internal/src/root/index.test.mocks.ts +++ b/packages/core/root/core-root-server-internal/src/root/index.test.mocks.ts @@ -27,6 +27,7 @@ export const mockServer = { setupCoreConfig: jest.fn(), preboot: jest.fn(), setup: jest.fn(), + start: jest.fn(), stop: jest.fn(), configService, }; diff --git a/packages/core/root/core-root-server-internal/src/root/index.test.ts b/packages/core/root/core-root-server-internal/src/root/index.test.ts index 278c56bbe7450..5371c30a03cd6 100644 --- a/packages/core/root/core-root-server-internal/src/root/index.test.ts +++ b/packages/core/root/core-root-server-internal/src/root/index.test.ts @@ -10,6 +10,7 @@ import { rawConfigService, configService, logger, mockServer } from './index.tes import { BehaviorSubject } from 'rxjs'; import { filter, first } from 'rxjs/operators'; +import { CriticalError } from '@kbn/core-base-server-internal'; import { REPO_ROOT } from '@kbn/repo-info'; import { Env } from '@kbn/config'; import { getEnvOptions } from '@kbn/config-mocks'; @@ -239,3 +240,16 @@ test('stops services if consequent logger upgrade fails', async () => { expect(mockConsoleError.mock.calls).toMatchSnapshot(); }); + +test('handles migrator-only node exception', async () => { + const mockOnShutdown = jest.fn(); + const root = new Root(rawConfigService, env, mockOnShutdown); + mockServer.start.mockImplementation(() => { + throw new CriticalError('Test', 'MigratioOnlyNode', 0); + }); + await root.preboot(); + await root.setup(); + await expect(() => root.start()).rejects.toBeInstanceOf(CriticalError); + expect(mockServer.stop).toHaveBeenCalledTimes(1); + expect(mockOnShutdown).toHaveBeenCalledTimes(1); +}); diff --git a/packages/core/root/core-root-server-internal/src/root/index.ts b/packages/core/root/core-root-server-internal/src/root/index.ts index d8c9fde25683d..82c7d8feaec78 100644 --- a/packages/core/root/core-root-server-internal/src/root/index.ts +++ b/packages/core/root/core-root-server-internal/src/root/index.ts @@ -22,6 +22,7 @@ import apm from 'elastic-apm-node'; import { isEqual } from 'lodash'; import type { ElasticConfigType } from './elastic_config'; import { Server } from '../server'; +import { MIGRATION_EXCEPTION_CODE } from '../constants'; /** * Top-level entry point to kick off the app and start the Kibana server. @@ -89,7 +90,9 @@ export class Root { ); } - this.log.fatal(reason); + if (reason.code !== MIGRATION_EXCEPTION_CODE) { + this.log.fatal(reason); + } } await this.server.stop(); diff --git a/packages/core/root/core-root-server-internal/src/server.test.ts b/packages/core/root/core-root-server-internal/src/server.test.ts index a1cb56e66e781..e7b821f006a76 100644 --- a/packages/core/root/core-root-server-internal/src/server.test.ts +++ b/packages/core/root/core-root-server-internal/src/server.test.ts @@ -33,8 +33,11 @@ import { REPO_ROOT } from '@kbn/repo-info'; import { Env } from '@kbn/config'; import { rawConfigServiceMock, getEnvOptions } from '@kbn/config-mocks'; import { Server } from './server'; +import { MIGRATION_EXCEPTION_CODE } from './constants'; import { loggingSystemMock } from '@kbn/core-logging-server-mocks'; +import type { InternalNodeServicePreboot } from '@kbn/core-node-server-internal'; +import { CriticalError } from '@kbn/core-base-server-internal'; const env = Env.createDefault(REPO_ROOT, getEnvOptions()); const logger = loggingSystemMock.create(); @@ -58,6 +61,7 @@ beforeEach(() => { afterEach(() => { jest.clearAllMocks(); + mockEnsureValidConfiguration.mockReset(); }); test('preboot services on "preboot"', async () => { @@ -252,3 +256,33 @@ test(`doesn't preboot core services if config validation fails`, async () => { expect(mockPluginsService.preboot).not.toHaveBeenCalled(); expect(mockPrebootService.preboot).not.toHaveBeenCalled(); }); + +test('migrator-only node throws exception during start', async () => { + rawConfigService.getConfig$.mockReturnValue( + new BehaviorSubject({ node: { roles: ['migrator'] } }) + ); + const nodeServiceContract: InternalNodeServicePreboot = { + roles: { migrator: true, ui: false, backgroundTasks: false }, + }; + mockNodeService.preboot.mockResolvedValue(nodeServiceContract); + mockNodeService.start.mockReturnValue(nodeServiceContract); + + const server = new Server(rawConfigService, env, logger); + + await server.preboot(); + await server.setup(); + + let migrationException: undefined | CriticalError; + expect(mockSavedObjectsService.start).not.toHaveBeenCalled(); + await server.start().catch((e) => (migrationException = e)); + + expect(mockSavedObjectsService.start).toHaveBeenCalledTimes(1); + expect(mockSavedObjectsService.start).toHaveNthReturnedWith(1, expect.anything()); + + expect(migrationException).not.toBeUndefined(); + expect(migrationException).toBeInstanceOf(CriticalError); + expect(migrationException!.message).toBe('Migrations completed, shutting down Kibana'); + expect(migrationException!.code).toBe(MIGRATION_EXCEPTION_CODE); + expect(migrationException!.processExitCode).toBe(0); + expect(migrationException!.cause).toBeUndefined(); +}); diff --git a/packages/core/root/core-root-server-internal/src/server.ts b/packages/core/root/core-root-server-internal/src/server.ts index 8c8d636d795e9..d05cb89e5efc9 100644 --- a/packages/core/root/core-root-server-internal/src/server.ts +++ b/packages/core/root/core-root-server-internal/src/server.ts @@ -9,6 +9,8 @@ import apm from 'elastic-apm-node'; import { reportPerformanceMetricEvent } from '@kbn/ebt-tools'; import type { Logger, LoggerFactory } from '@kbn/logging'; +import type { NodeRoles } from '@kbn/core-node-server'; +import { CriticalError } from '@kbn/core-base-server-internal'; import { ConfigService, Env, RawConfigurationProvider } from '@kbn/config'; import { DocLinksService } from '@kbn/core-doc-links-server-internal'; import { LoggingService, ILoggingSystem } from '@kbn/core-logging-server-internal'; @@ -51,6 +53,7 @@ import type { import { DiscoveredPlugins, PluginsService } from '@kbn/core-plugins-server-internal'; import { CoreAppsService } from '@kbn/core-apps-server-internal'; import { registerServiceConfig } from './register_service_config'; +import { MIGRATION_EXCEPTION_CODE } from './constants'; const coreId = Symbol('core'); const KIBANA_STARTED_EVENT = 'kibana_started'; @@ -103,6 +106,7 @@ export class Server { private coreStart?: InternalCoreStart; private discoveredPlugins?: DiscoveredPlugins; private readonly logger: LoggerFactory; + private nodeRoles?: NodeRoles; private readonly uptimePerStep: Partial = {}; @@ -159,6 +163,8 @@ export class Server { const environmentPreboot = await this.environment.preboot({ analytics: analyticsPreboot }); const nodePreboot = await this.node.preboot({ loggingSystem: this.loggingSystem }); + this.nodeRoles = nodePreboot.roles; + // Discover any plugins before continuing. This allows other systems to utilize the plugin dependency graph. this.discoveredPlugins = await this.plugins.discover({ environment: environmentPreboot, @@ -364,6 +370,17 @@ export class Server { await this.resolveSavedObjectsStartPromise!(savedObjectsStart); soStartSpan?.end(); + + if (this.nodeRoles?.migrator === true) { + startTransaction?.end(); + this.log.info('Detected migrator node role; shutting down Kibana...'); + throw new CriticalError( + 'Migrations completed, shutting down Kibana', + MIGRATION_EXCEPTION_CODE, + 0 + ); + } + const capabilitiesStart = this.capabilities.start(); const uiSettingsStart = await this.uiSettings.start(); const customBrandingStart = this.customBranding.start(); diff --git a/packages/core/root/core-root-server-internal/tsconfig.json b/packages/core/root/core-root-server-internal/tsconfig.json index 06142eda5880f..4eafe9fe15e34 100644 --- a/packages/core/root/core-root-server-internal/tsconfig.json +++ b/packages/core/root/core-root-server-internal/tsconfig.json @@ -68,6 +68,7 @@ "@kbn/core-custom-branding-server-internal", "@kbn/core-custom-branding-server-mocks", "@kbn/repo-packages", + "@kbn/core-node-server", ], "exclude": [ "target/**/*", diff --git a/packages/core/saved-objects/core-saved-objects-api-server-internal/src/lib/aggregations/aggs_types/bucket_aggs.ts b/packages/core/saved-objects/core-saved-objects-api-server-internal/src/lib/aggregations/aggs_types/bucket_aggs.ts index 76abc1b08bd84..af483264e9a9e 100644 --- a/packages/core/saved-objects/core-saved-objects-api-server-internal/src/lib/aggregations/aggs_types/bucket_aggs.ts +++ b/packages/core/saved-objects/core-saved-objects-api-server-internal/src/lib/aggregations/aggs_types/bucket_aggs.ts @@ -162,6 +162,8 @@ const histogramSchema = s.object({ }); const compositeSchema = s.object({ + size: s.maybe(s.number()), + after: s.maybe(s.recordOf(s.string(), s.nullable(s.oneOf([s.string(), s.number()])))), sources: s.arrayOf( s.recordOf( s.string(), diff --git a/packages/core/saved-objects/core-saved-objects-base-server-internal/index.ts b/packages/core/saved-objects/core-saved-objects-base-server-internal/index.ts index 722694a32f6ad..f93602bd99350 100644 --- a/packages/core/saved-objects/core-saved-objects-base-server-internal/index.ts +++ b/packages/core/saved-objects/core-saved-objects-base-server-internal/index.ts @@ -45,4 +45,15 @@ export { isVirtualModelVersion, virtualVersionToModelVersion, modelVersionToVirtualVersion, + getModelVersionMapForTypes, + getLatestModelVersion, + type ModelVersionMap, + compareModelVersions, + type CompareModelVersionMapParams, + type CompareModelVersionStatus, + type CompareModelVersionDetails, + type CompareModelVersionResult, + getModelVersionsFromMappings, + getModelVersionsFromMappingMeta, + getModelVersionDelta, } from './src/model_version'; diff --git a/packages/core/saved-objects/core-saved-objects-base-server-internal/src/mappings/types.ts b/packages/core/saved-objects/core-saved-objects-base-server-internal/src/mappings/types.ts index c93abc2064fb6..0267f2ce27c1a 100644 --- a/packages/core/saved-objects/core-saved-objects-base-server-internal/src/mappings/types.ts +++ b/packages/core/saved-objects/core-saved-objects-base-server-internal/src/mappings/types.ts @@ -57,8 +57,24 @@ export interface IndexMapping { /** @internal */ export interface IndexMappingMeta { - // A dictionary of key -> md5 hash (e.g. 'dashboard': '24234qdfa3aefa3wa') - // with each key being a root-level mapping property, and each value being - // the md5 hash of that mapping's value when the index was created. + /** + * A dictionary of key -> md5 hash (e.g. 'dashboard': '24234qdfa3aefa3wa') + * with each key being a root-level mapping property, and each value being + * the md5 hash of that mapping's value when the index was created. + * + * @remark: Only defined for indices using the v2 migration algorithm. + */ migrationMappingPropertyHashes?: { [k: string]: string }; + /** + * The current model versions of the mapping of the index. + * + * @remark: Only defined for indices using the zdt migration algorithm. + */ + mappingVersions?: { [k: string]: number }; + /** + * The current model versions of the documents of the index. + * + * @remark: Only defined for indices using the zdt migration algorithm. + */ + docVersions?: { [k: string]: number }; } diff --git a/packages/core/saved-objects/core-saved-objects-base-server-internal/src/model_version/get_version_delta.test.ts b/packages/core/saved-objects/core-saved-objects-base-server-internal/src/model_version/get_version_delta.test.ts new file mode 100644 index 0000000000000..521ddd2a0efc3 --- /dev/null +++ b/packages/core/saved-objects/core-saved-objects-base-server-internal/src/model_version/get_version_delta.test.ts @@ -0,0 +1,122 @@ +/* + * 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 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { getModelVersionDelta } from './get_version_delta'; + +describe('getModelVersionDelta', () => { + it('generates an upward delta', () => { + const result = getModelVersionDelta({ + currentVersions: { + a: 1, + b: 1, + }, + targetVersions: { + a: 2, + b: 3, + }, + deletedTypes: [], + }); + + expect(result.status).toEqual('upward'); + expect(result.diff).toEqual([ + { + name: 'a', + current: 1, + target: 2, + }, + { + name: 'b', + current: 1, + target: 3, + }, + ]); + }); + + it('generates a downward delta', () => { + const result = getModelVersionDelta({ + currentVersions: { + a: 4, + b: 2, + }, + targetVersions: { + a: 1, + b: 1, + }, + deletedTypes: [], + }); + + expect(result.status).toEqual('downward'); + expect(result.diff).toEqual([ + { + name: 'a', + current: 4, + target: 1, + }, + { + name: 'b', + current: 2, + target: 1, + }, + ]); + }); + + it('generates a noop delta', () => { + const result = getModelVersionDelta({ + currentVersions: { + a: 4, + b: 2, + }, + targetVersions: { + a: 4, + b: 2, + }, + deletedTypes: [], + }); + + expect(result.status).toEqual('noop'); + expect(result.diff).toEqual([]); + }); + + it('ignores deleted types', () => { + const result = getModelVersionDelta({ + currentVersions: { + a: 1, + b: 3, + }, + targetVersions: { + a: 2, + }, + deletedTypes: ['b'], + }); + + expect(result.status).toEqual('upward'); + expect(result.diff).toEqual([ + { + name: 'a', + current: 1, + target: 2, + }, + ]); + }); + + it('throws if the provided version maps are in conflict', () => { + expect(() => + getModelVersionDelta({ + currentVersions: { + a: 1, + b: 2, + }, + targetVersions: { + a: 2, + b: 1, + }, + deletedTypes: [], + }) + ).toThrow(); + }); +}); diff --git a/packages/core/saved-objects/core-saved-objects-base-server-internal/src/model_version/get_version_delta.ts b/packages/core/saved-objects/core-saved-objects-base-server-internal/src/model_version/get_version_delta.ts new file mode 100644 index 0000000000000..f39c52b47f9f7 --- /dev/null +++ b/packages/core/saved-objects/core-saved-objects-base-server-internal/src/model_version/get_version_delta.ts @@ -0,0 +1,98 @@ +/* + * 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 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import type { ModelVersionMap } from './version_map'; +import { compareModelVersions } from './version_compare'; + +interface GetModelVersionDeltaOpts { + currentVersions: ModelVersionMap; + targetVersions: ModelVersionMap; + deletedTypes: string[]; +} + +type ModelVersionDeltaResultStatus = 'upward' | 'downward' | 'noop'; + +interface ModelVersionDeltaResult { + status: ModelVersionDeltaResultStatus; + diff: ModelVersionDeltaTypeResult[]; +} + +interface ModelVersionDeltaTypeResult { + /** the name of the type */ + name: string; + /** the current version the type is at */ + current: number; + /** the target version the type should go to */ + target: number; +} + +/** + * Will generate the difference to go from `currentVersions` to `targetVersions`. + * + * @remarks: will throw if the version maps are in conflict + */ +export const getModelVersionDelta = ({ + currentVersions, + targetVersions, + deletedTypes, +}: GetModelVersionDeltaOpts): ModelVersionDeltaResult => { + const compared = compareModelVersions({ + indexVersions: currentVersions, + appVersions: targetVersions, + deletedTypes, + }); + + if (compared.status === 'conflict') { + throw new Error('Cannot generate model version difference: conflict between versions'); + } + + const status: ModelVersionDeltaResultStatus = + compared.status === 'lesser' ? 'downward' : compared.status === 'greater' ? 'upward' : 'noop'; + + const result: ModelVersionDeltaResult = { + status, + diff: [], + }; + + if (compared.status === 'greater') { + compared.details.greater.forEach((type) => { + result.diff.push(getTypeDelta({ type, currentVersions, targetVersions })); + }); + } else if (compared.status === 'lesser') { + compared.details.lesser.forEach((type) => { + result.diff.push(getTypeDelta({ type, currentVersions, targetVersions })); + }); + } + + return result; +}; + +const getTypeDelta = ({ + type, + currentVersions, + targetVersions, +}: { + type: string; + currentVersions: ModelVersionMap; + targetVersions: ModelVersionMap; +}): ModelVersionDeltaTypeResult => { + const currentVersion = currentVersions[type]; + const targetVersion = targetVersions[type]; + if (currentVersion === undefined || targetVersion === undefined) { + // should never occur given we've been checking consistency numerous times before getting there + // but better safe than sorry. + throw new Error( + `Consistency error: trying to generate delta with missing entry for type ${type}` + ); + } + return { + name: type, + current: currentVersion, + target: targetVersion, + }; +}; diff --git a/packages/core/saved-objects/core-saved-objects-base-server-internal/src/model_version/index.ts b/packages/core/saved-objects/core-saved-objects-base-server-internal/src/model_version/index.ts index 5301c0a4d219c..2179199921a82 100644 --- a/packages/core/saved-objects/core-saved-objects-base-server-internal/src/model_version/index.ts +++ b/packages/core/saved-objects/core-saved-objects-base-server-internal/src/model_version/index.ts @@ -13,3 +13,20 @@ export { modelVersionToVirtualVersion, virtualVersionToModelVersion, } from './conversion'; +export { + getModelVersionMapForTypes, + getLatestModelVersion, + type ModelVersionMap, +} from './version_map'; +export { + compareModelVersions, + type CompareModelVersionMapParams, + type CompareModelVersionStatus, + type CompareModelVersionDetails, + type CompareModelVersionResult, +} from './version_compare'; +export { + getModelVersionsFromMappings, + getModelVersionsFromMappingMeta, +} from './model_version_from_mappings'; +export { getModelVersionDelta } from './get_version_delta'; diff --git a/packages/core/saved-objects/core-saved-objects-base-server-internal/src/model_version/model_version_from_mappings.test.ts b/packages/core/saved-objects/core-saved-objects-base-server-internal/src/model_version/model_version_from_mappings.test.ts new file mode 100644 index 0000000000000..8fea10f11f6b1 --- /dev/null +++ b/packages/core/saved-objects/core-saved-objects-base-server-internal/src/model_version/model_version_from_mappings.test.ts @@ -0,0 +1,73 @@ +/* + * 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 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import type { IndexMapping, IndexMappingMeta } from '../mappings'; +import { getModelVersionsFromMappings } from './model_version_from_mappings'; + +describe('getModelVersionsFromMappings', () => { + const createIndexMapping = (parts: Partial = {}): IndexMapping => ({ + properties: {}, + _meta: { + ...parts, + }, + }); + + it('retrieves the version map from docVersions', () => { + const mappings = createIndexMapping({ + docVersions: { + foo: 3, + bar: 5, + }, + }); + const versionMap = getModelVersionsFromMappings({ mappings, source: 'docVersions' }); + + expect(versionMap).toEqual({ + foo: 3, + bar: 5, + }); + }); + + it('retrieves the version map from mappingVersions', () => { + const mappings = createIndexMapping({ + mappingVersions: { + foo: 2, + bar: 7, + }, + }); + const versionMap = getModelVersionsFromMappings({ mappings, source: 'mappingVersions' }); + + expect(versionMap).toEqual({ + foo: 2, + bar: 7, + }); + }); + + it('returns undefined for docVersions if meta field is not present', () => { + const mappings = createIndexMapping({ + mappingVersions: { + foo: 3, + bar: 5, + }, + }); + const versionMap = getModelVersionsFromMappings({ mappings, source: 'docVersions' }); + + expect(versionMap).toBeUndefined(); + }); + + it('returns undefined for mappingVersions if meta field is not present', () => { + const mappings = createIndexMapping({ + docVersions: { + foo: 3, + bar: 5, + }, + }); + const versionMap = getModelVersionsFromMappings({ mappings, source: 'mappingVersions' }); + + expect(versionMap).toBeUndefined(); + }); +}); diff --git a/packages/core/saved-objects/core-saved-objects-base-server-internal/src/model_version/model_version_from_mappings.ts b/packages/core/saved-objects/core-saved-objects-base-server-internal/src/model_version/model_version_from_mappings.ts new file mode 100644 index 0000000000000..8e7816a12fb53 --- /dev/null +++ b/packages/core/saved-objects/core-saved-objects-base-server-internal/src/model_version/model_version_from_mappings.ts @@ -0,0 +1,51 @@ +/* + * 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 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import type { IndexMapping, IndexMappingMeta } from '../mappings'; +import type { ModelVersionMap } from './version_map'; +import { assertValidModelVersion } from './conversion'; + +/** + * Build the version map from the specified source of the provided mappings. + */ +export const getModelVersionsFromMappings = ({ + mappings, + source, +}: { + mappings: IndexMapping; + source: 'mappingVersions' | 'docVersions'; +}): ModelVersionMap | undefined => { + if (!mappings._meta) { + return undefined; + } + + return getModelVersionsFromMappingMeta({ + meta: mappings._meta, + source, + }); +}; + +/** + * Build the version map from the specified source of the provided mappings meta. + */ +export const getModelVersionsFromMappingMeta = ({ + meta, + source, +}: { + meta: IndexMappingMeta; + source: 'mappingVersions' | 'docVersions'; +}): ModelVersionMap | undefined => { + const indexVersions = source === 'mappingVersions' ? meta.mappingVersions : meta.docVersions; + if (!indexVersions) { + return undefined; + } + return Object.entries(indexVersions).reduce((map, [type, rawVersion]) => { + map[type] = assertValidModelVersion(rawVersion); + return map; + }, {}); +}; diff --git a/packages/core/saved-objects/core-saved-objects-base-server-internal/src/model_version/version_compare.test.ts b/packages/core/saved-objects/core-saved-objects-base-server-internal/src/model_version/version_compare.test.ts new file mode 100644 index 0000000000000..eba6fe1837cce --- /dev/null +++ b/packages/core/saved-objects/core-saved-objects-base-server-internal/src/model_version/version_compare.test.ts @@ -0,0 +1,142 @@ +/* + * 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 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { compareModelVersions } from './version_compare'; + +describe('compareModelVersions', () => { + it('returns the correct value for greater app version', () => { + const result = compareModelVersions({ + appVersions: { + foo: 3, + bar: 2, + }, + indexVersions: { + foo: 2, + bar: 2, + }, + deletedTypes: [], + }); + + expect(result.status).toEqual('greater'); + }); + + it('returns the correct value for lesser app version', () => { + const result = compareModelVersions({ + appVersions: { + foo: 1, + bar: 2, + }, + indexVersions: { + foo: 2, + bar: 2, + }, + deletedTypes: [], + }); + + expect(result.status).toEqual('lesser'); + }); + + it('returns the correct value for equal versions', () => { + const result = compareModelVersions({ + appVersions: { + foo: 2, + bar: 2, + }, + indexVersions: { + foo: 2, + bar: 2, + }, + deletedTypes: [], + }); + + expect(result.status).toEqual('equal'); + }); + + it('handles new types not being present in the index', () => { + const result = compareModelVersions({ + appVersions: { + foo: 2, + new: 1, + }, + indexVersions: { + foo: 2, + }, + deletedTypes: [], + }); + + expect(result.status).toEqual('greater'); + }); + + it('handles types not being present in the app', () => { + const result = compareModelVersions({ + appVersions: { + foo: 3, + }, + indexVersions: { + foo: 2, + old: 1, + }, + deletedTypes: [], + }); + + expect(result.status).toEqual('conflict'); + }); + + it('returns the correct value for conflicts', () => { + const result = compareModelVersions({ + appVersions: { + a: 3, + b: 3, + c: 3, + }, + indexVersions: { + a: 2, + b: 3, + c: 4, + }, + deletedTypes: [], + }); + + expect(result.status).toEqual('conflict'); + }); + + it('properly lists the details', () => { + const result = compareModelVersions({ + appVersions: { + a: 3, + b: 3, + c: 3, + }, + indexVersions: { + a: 2, + b: 3, + c: 4, + }, + deletedTypes: [], + }); + + expect(result.details.lesser).toEqual(['c']); + expect(result.details.equal).toEqual(['b']); + expect(result.details.greater).toEqual(['a']); + }); + + it('ignores deleted types when comparing', () => { + const result = compareModelVersions({ + appVersions: { + a: 3, + }, + indexVersions: { + a: 2, + b: 3, + }, + deletedTypes: ['b'], + }); + + expect(result.status).toEqual('greater'); + }); +}); diff --git a/packages/core/saved-objects/core-saved-objects-base-server-internal/src/model_version/version_compare.ts b/packages/core/saved-objects/core-saved-objects-base-server-internal/src/model_version/version_compare.ts new file mode 100644 index 0000000000000..9b8d14b7fd862 --- /dev/null +++ b/packages/core/saved-objects/core-saved-objects-base-server-internal/src/model_version/version_compare.ts @@ -0,0 +1,77 @@ +/* + * 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 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import type { ModelVersionMap } from './version_map'; + +export interface CompareModelVersionMapParams { + /** The latest model version of the types registered in the application */ + appVersions: ModelVersionMap; + /** The model version stored in the index */ + indexVersions: ModelVersionMap; + /** The list of deleted types to exclude during the compare process */ + deletedTypes: string[]; +} + +/** + * The overall status of the model version comparison: + * - `greater`: app version is greater than the index version + * - `lesser`: app version is lesser than the index version + * - `equal`: app version is equal to the index version + * - `conflict`: app and index versions are incompatible (versions for some types are higher, and for other types lower) + */ +export type CompareModelVersionStatus = 'greater' | 'lesser' | 'equal' | 'conflict'; + +export interface CompareModelVersionDetails { + greater: string[]; + lesser: string[]; + equal: string[]; +} + +export interface CompareModelVersionResult { + status: CompareModelVersionStatus; + details: CompareModelVersionDetails; +} + +export const compareModelVersions = ({ + appVersions, + indexVersions, + deletedTypes, +}: CompareModelVersionMapParams): CompareModelVersionResult => { + const allTypes = [ + ...new Set([...Object.keys(appVersions), ...Object.keys(indexVersions)]), + ].filter((type) => !deletedTypes.includes(type)); + + const details: CompareModelVersionDetails = { + greater: [], + lesser: [], + equal: [], + }; + + allTypes.forEach((type) => { + const appVersion = appVersions[type] ?? 0; + const indexVersion = indexVersions[type] ?? 0; + + if (appVersion > indexVersion) { + details.greater.push(type); + } else if (appVersion < indexVersion) { + details.lesser.push(type); + } else { + details.equal.push(type); + } + }); + + const hasGreater = details.greater.length > 0; + const hasLesser = details.lesser.length > 0; + const status: CompareModelVersionStatus = + hasGreater && hasLesser ? 'conflict' : hasGreater ? 'greater' : hasLesser ? 'lesser' : 'equal'; + + return { + status, + details, + }; +}; diff --git a/packages/core/saved-objects/core-saved-objects-base-server-internal/src/model_version/version_map.test.ts b/packages/core/saved-objects/core-saved-objects-base-server-internal/src/model_version/version_map.test.ts new file mode 100644 index 0000000000000..aafb83ab96009 --- /dev/null +++ b/packages/core/saved-objects/core-saved-objects-base-server-internal/src/model_version/version_map.test.ts @@ -0,0 +1,119 @@ +/* + * 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 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import type { SavedObjectsType, SavedObjectsModelVersion } from '@kbn/core-saved-objects-server'; +import { getModelVersionMapForTypes, getLatestModelVersion } from './version_map'; + +describe('ModelVersion map utilities', () => { + const buildType = (parts: Partial = {}): SavedObjectsType => ({ + name: 'test-type', + hidden: false, + namespaceType: 'single', + mappings: { properties: {} }, + ...parts, + }); + + const dummyModelVersion = (): SavedObjectsModelVersion => ({ + modelChange: { + type: 'expansion', + }, + }); + + describe('getLatestModelVersion', () => { + it('returns 0 when no model versions are registered', () => { + expect(getLatestModelVersion(buildType({ modelVersions: {} }))).toEqual(0); + expect(getLatestModelVersion(buildType({ modelVersions: undefined }))).toEqual(0); + }); + + it('throws if an invalid version is provided', () => { + expect(() => + getLatestModelVersion( + buildType({ + modelVersions: { + foo: dummyModelVersion(), + }, + }) + ) + ).toThrow(); + }); + + it('returns the latest registered version', () => { + expect( + getLatestModelVersion( + buildType({ + modelVersions: { + '1': dummyModelVersion(), + '2': dummyModelVersion(), + '3': dummyModelVersion(), + }, + }) + ) + ).toEqual(3); + }); + + it('accepts provider functions', () => { + expect( + getLatestModelVersion( + buildType({ + modelVersions: () => ({ + '1': dummyModelVersion(), + '2': dummyModelVersion(), + '3': dummyModelVersion(), + }), + }) + ) + ).toEqual(3); + }); + + it('supports unordered maps', () => { + expect( + getLatestModelVersion( + buildType({ + modelVersions: { + '3': dummyModelVersion(), + '1': dummyModelVersion(), + '2': dummyModelVersion(), + }, + }) + ) + ).toEqual(3); + }); + }); + + describe('getModelVersionMapForTypes', () => { + it('returns a map with the latest version of the provided types', () => { + expect( + getModelVersionMapForTypes([ + buildType({ + name: 'foo', + modelVersions: { + '1': dummyModelVersion(), + '2': dummyModelVersion(), + }, + }), + buildType({ + name: 'bar', + modelVersions: {}, + }), + buildType({ + name: 'dolly', + modelVersions: { + '1': dummyModelVersion(), + '2': dummyModelVersion(), + '3': dummyModelVersion(), + }, + }), + ]) + ).toEqual({ + foo: 2, + bar: 0, + dolly: 3, + }); + }); + }); +}); diff --git a/packages/core/saved-objects/core-saved-objects-base-server-internal/src/model_version/version_map.ts b/packages/core/saved-objects/core-saved-objects-base-server-internal/src/model_version/version_map.ts new file mode 100644 index 0000000000000..dd05e64dbcbef --- /dev/null +++ b/packages/core/saved-objects/core-saved-objects-base-server-internal/src/model_version/version_map.ts @@ -0,0 +1,33 @@ +/* + * 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 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import type { SavedObjectsType } from '@kbn/core-saved-objects-server'; +import { assertValidModelVersion } from './conversion'; + +export type ModelVersionMap = Record; + +/** + * Returns the latest registered model version number for the given type. + */ +export const getLatestModelVersion = (type: SavedObjectsType): number => { + const versionMap = + typeof type.modelVersions === 'function' ? type.modelVersions() : type.modelVersions ?? {}; + return Object.keys(versionMap).reduce((memo, current) => { + return Math.max(memo, assertValidModelVersion(current)); + }, 0); +}; + +/** + * Build a version map for the given types. + */ +export const getModelVersionMapForTypes = (types: SavedObjectsType[]): ModelVersionMap => { + return types.reduce((versionMap, type) => { + versionMap[type.name] = getLatestModelVersion(type); + return versionMap; + }, {}); +}; diff --git a/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/actions/index.ts b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/actions/index.ts index 7e380d3a7ad17..7aec839dca066 100644 --- a/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/actions/index.ts +++ b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/actions/index.ts @@ -37,7 +37,11 @@ export { removeWriteBlock } from './remove_write_block'; export type { CloneIndexResponse, CloneIndexParams } from './clone_index'; export { cloneIndex } from './clone_index'; -export type { WaitForIndexStatusParams, IndexNotYellowTimeout } from './wait_for_index_status'; +export type { + WaitForIndexStatusParams, + IndexNotYellowTimeout, + IndexNotGreenTimeout, +} from './wait_for_index_status'; import type { IndexNotGreenTimeout, IndexNotYellowTimeout } from './wait_for_index_status'; import { waitForIndexStatus } from './wait_for_index_status'; @@ -78,7 +82,7 @@ export { cleanupUnknownAndExcluded } from './cleanup_unknown_and_excluded'; export { waitForDeleteByQueryTask } from './wait_for_delete_by_query_task'; -export type { CreateIndexParams } from './create_index'; +export type { CreateIndexParams, ClusterShardLimitExceeded } from './create_index'; export { createIndex } from './create_index'; export { checkTargetMappings } from './check_target_mappings'; @@ -91,7 +95,7 @@ export type { } from './update_and_pickup_mappings'; export { updateAndPickupMappings } from './update_and_pickup_mappings'; -export { updateMappings } from './update_mappings'; +export { updateMappings, type IncompatibleMappingException } from './update_mappings'; import type { UnknownDocsFound } from './check_for_unknown_docs'; import type { IncompatibleClusterRoutingAllocation } from './initialize_action'; diff --git a/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/common/constants.ts b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/common/constants.ts new file mode 100644 index 0000000000000..5dfdb05a0bca8 --- /dev/null +++ b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/common/constants.ts @@ -0,0 +1,9 @@ +/* + * 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 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +export const CLUSTER_SHARD_LIMIT_EXCEEDED_REASON = `[cluster_shard_limit_exceeded] Upgrading Kibana requires adding a small number of new shards. Ensure that Kibana is able to add 10 more shards by increasing the cluster.max_shards_per_node setting, or removing indices to clear up resources.`; diff --git a/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/common/utils/delay.test.ts b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/common/utils/delay.test.ts new file mode 100644 index 0000000000000..3701621a96a12 --- /dev/null +++ b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/common/utils/delay.test.ts @@ -0,0 +1,41 @@ +/* + * 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 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { createDelayFn } from './delay'; + +const nextTick = () => new Promise((resolve) => resolve()); + +describe('createDelayFn', () => { + beforeAll(() => { + jest.useFakeTimers(); + }); + + afterAll(() => { + jest.useRealTimers(); + }); + + it('adds a delay effect to the provided function', async () => { + const handler = jest.fn(); + + const wrapped = createDelayFn({ retryDelay: 2000, retryCount: 0 })(handler); + + wrapped(); + + expect(handler).not.toHaveBeenCalled(); + + jest.advanceTimersByTime(500); + await nextTick(); + + expect(handler).not.toHaveBeenCalled(); + + jest.advanceTimersByTime(1500); + await nextTick(); + + expect(handler).toHaveBeenCalledTimes(1); + }); +}); diff --git a/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/common/utils/delay.ts b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/common/utils/delay.ts new file mode 100644 index 0000000000000..8a8a87ec65def --- /dev/null +++ b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/common/utils/delay.ts @@ -0,0 +1,25 @@ +/* + * 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 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +export interface RetryableState { + retryCount: number; + retryDelay: number; +} + +/** + * HOC wrapping the function with a delay. + */ +export const createDelayFn = + (state: RetryableState) => + any>(fn: F): (() => ReturnType) => { + return () => { + return state.retryDelay > 0 + ? new Promise((resolve) => setTimeout(resolve, state.retryDelay)).then(fn) + : fn(); + }; + }; diff --git a/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/common/utils/index.ts b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/common/utils/index.ts index 962f40b87db02..3801e180ed024 100644 --- a/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/common/utils/index.ts +++ b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/common/utils/index.ts @@ -7,3 +7,4 @@ */ export { logActionResponse, logStateTransition, type LogAwareState } from './logs'; +export { createDelayFn, type RetryableState } from './delay'; diff --git a/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/common/utils/logs.test.ts b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/common/utils/logs.test.ts new file mode 100644 index 0000000000000..e3bb8451b0ae9 --- /dev/null +++ b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/common/utils/logs.test.ts @@ -0,0 +1,80 @@ +/* + * 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 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { omit } from 'lodash'; +import { loggerMock, type MockedLogger } from '@kbn/logging-mocks'; +import { logStateTransition, type LogAwareState } from './logs'; + +describe('logStateTransition', () => { + let logger: MockedLogger; + + const messagePrefix = '[PREFIX] '; + + beforeEach(() => { + logger = loggerMock.create(); + }); + + it('logs the offset of messages between the old and the new state', () => { + const previous: LogAwareState = { + controlState: 'PREVIOUS', + logs: [], + }; + const next: LogAwareState = { + controlState: 'NEXT', + logs: [ + ...previous.logs, + { level: 'info', message: 'info message' }, + { level: 'warning', message: 'warning message' }, + ], + }; + + logStateTransition(logger, messagePrefix, previous, next, 500); + + expect(omit(loggerMock.collect(logger), 'debug')).toEqual({ + error: [], + fatal: [], + info: [['[PREFIX] info message'], ['[PREFIX] PREVIOUS -> NEXT. took: 500ms.']], + log: [], + trace: [], + warn: [['[PREFIX] warning message']], + }); + }); + + it('logs a debug message with the correct meta', () => { + const previous: LogAwareState = { + controlState: 'PREVIOUS', + logs: [], + }; + const next: LogAwareState = { + controlState: 'NEXT', + logs: [ + ...previous.logs, + { level: 'info', message: 'info message' }, + { level: 'warning', message: 'warning message' }, + ], + }; + + logStateTransition(logger, messagePrefix, previous, next, 500); + + expect(loggerMock.collect(logger).debug).toEqual([ + [ + '[PREFIX] PREVIOUS -> NEXT. took: 500ms.', + { + kibana: { + migrations: { + duration: 500, + state: expect.objectContaining({ + controlState: 'NEXT', + }), + }, + }, + }, + ], + ]); + }); +}); diff --git a/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/core/build_active_mappings.ts b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/core/build_active_mappings.ts index f14de6bc72ee0..3d8648ae33274 100644 --- a/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/core/build_active_mappings.ts +++ b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/core/build_active_mappings.ts @@ -27,7 +27,7 @@ import type { export function buildActiveMappings( typeDefinitions: SavedObjectsTypeMappingDefinitions | SavedObjectsMappingProperties ): IndexMapping { - const mapping = defaultMapping(); + const mapping = getBaseMappings(); const mergedProperties = validateAndMerge(mapping.properties, typeDefinitions); @@ -114,7 +114,7 @@ function findChangedProp(actual: any, expected: any) { * * @returns {IndexMapping} */ -function defaultMapping(): IndexMapping { +export function getBaseMappings(): IndexMapping { return { dynamic: 'strict', properties: { diff --git a/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/core/index.ts b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/core/index.ts index a113e5e5f77bc..1503fdcf19814 100644 --- a/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/core/index.ts +++ b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/core/index.ts @@ -6,7 +6,7 @@ * Side Public License, v 1. */ -export { buildActiveMappings } from './build_active_mappings'; +export { buildActiveMappings, getBaseMappings } from './build_active_mappings'; export type { LogFn } from './migration_logger'; export { excludeUnusedTypesQuery, REMOVED_TYPES } from './unused_types'; export { TransformSavedObjectDocumentError } from './transform_saved_object_document_error'; diff --git a/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/kibana_migrator.ts b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/kibana_migrator.ts index 408b277444995..d63cc69d8f7f4 100644 --- a/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/kibana_migrator.ts +++ b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/kibana_migrator.ts @@ -145,6 +145,7 @@ export class KibanaMigrator implements IKibanaMigrator { private runMigrationZdt(): Promise { return runZeroDowntimeMigration({ + kibanaVersion: this.kibanaVersion, kibanaIndexPrefix: this.kibanaIndex, typeRegistry: this.typeRegistry, logger: this.log, diff --git a/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/model/model.ts b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/model/model.ts index dbaacdbdd6808..45dbcc877bbdb 100644 --- a/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/model/model.ts +++ b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/model/model.ts @@ -44,9 +44,9 @@ import { import { createBatches } from './create_batches'; import type { MigrationLog } from '../types'; import { diffMappings } from '../core/build_active_mappings'; +import { CLUSTER_SHARD_LIMIT_EXCEEDED_REASON } from '../common/constants'; export const FATAL_REASON_REQUEST_ENTITY_TOO_LARGE = `While indexing a batch of saved objects, Elasticsearch returned a 413 Request Entity Too Large exception. Ensure that the Kibana configuration option 'migrations.maxBatchSizeBytes' is set to a value that is lower than or equal to the Elasticsearch 'http.max_content_length' configuration option.`; -const CLUSTER_SHARD_LIMIT_EXCEEDED_REASON = `[cluster_shard_limit_exceeded] Upgrading Kibana requires adding a small number of new shards. Ensure that Kibana is able to add 10 more shards by increasing the cluster.max_shards_per_node setting, or removing indices to clear up resources.`; export const model = (currentState: State, resW: ResponseType): State => { // The action response `resW` is weakly typed, the type includes all action diff --git a/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/next.ts b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/next.ts index 78042acd7a6a4..1109d52039d3e 100644 --- a/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/next.ts +++ b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/next.ts @@ -50,6 +50,7 @@ import type { WaitForMigrationCompletionState, WaitForYellowSourceState, } from './state'; +import { createDelayFn } from './common/utils'; import type { TransformRawDocs } from './types'; import * as Actions from './actions'; import { REMOVED_TYPES } from './core'; @@ -253,13 +254,7 @@ export const nextActionMap = (client: ElasticsearchClient, transformRawDocs: Tra export const next = (client: ElasticsearchClient, transformRawDocs: TransformRawDocs) => { const map = nextActionMap(client, transformRawDocs); return (state: State) => { - const delay = any>(fn: F): (() => ReturnType) => { - return () => { - return state.retryDelay > 0 - ? new Promise((resolve) => setTimeout(resolve, state.retryDelay)).then(fn) - : fn(); - }; - }; + const delay = createDelayFn(state); if (state.controlState === 'DONE' || state.controlState === 'FATAL') { // Return null if we're in one of the terminating states diff --git a/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/zdt/actions/index.ts b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/zdt/actions/index.ts index 92334d396adc0..bb135c115ce92 100644 --- a/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/zdt/actions/index.ts +++ b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/zdt/actions/index.ts @@ -10,11 +10,22 @@ import type { IncompatibleClusterRoutingAllocation, RetryableEsClientError, WaitForTaskCompletionTimeout, + IndexNotYellowTimeout, + IndexNotGreenTimeout, + ClusterShardLimitExceeded, IndexNotFound, + AliasNotFound, + IncompatibleMappingException, } from '../../actions'; export { initAction as init, + waitForIndexStatus, + createIndex, + updateAliases, + updateMappings, + updateAndPickupMappings, + waitForPickupUpdatedMappingsTask, type InitActionParams, type IncompatibleClusterRoutingAllocation, type RetryableEsClientError, @@ -27,6 +38,11 @@ export interface ActionErrorTypeMap { incompatible_cluster_routing_allocation: IncompatibleClusterRoutingAllocation; retryable_es_client_error: RetryableEsClientError; index_not_found_exception: IndexNotFound; + index_not_green_timeout: IndexNotGreenTimeout; + index_not_yellow_timeout: IndexNotYellowTimeout; + cluster_shard_limit_exceeded: ClusterShardLimitExceeded; + alias_not_found_exception: AliasNotFound; + incompatible_mapping_exception: IncompatibleMappingException; } /** Type guard for narrowing the type of a left */ diff --git a/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/zdt/context/create_context.ts b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/zdt/context/create_context.ts index cc4c7b63d3993..7a660ea470443 100644 --- a/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/zdt/context/create_context.ts +++ b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/zdt/context/create_context.ts @@ -6,8 +6,10 @@ * Side Public License, v 1. */ -import type { MigratorContext } from './types'; +import { getModelVersionMapForTypes } from '@kbn/core-saved-objects-base-server-internal'; +import { REMOVED_TYPES } from '../../core'; import type { MigrateIndexOptions } from '../migrate_index'; +import type { MigratorContext } from './types'; export type CreateContextOps = Omit; @@ -15,6 +17,7 @@ export type CreateContextOps = Omit; * Create the context object that will be used for this index migration. */ export const createContext = ({ + kibanaVersion, types, docLinks, migrationConfig, @@ -24,12 +27,15 @@ export const createContext = ({ serializer, }: CreateContextOps): MigratorContext => { return { + kibanaVersion, indexPrefix, types, + typeModelVersions: getModelVersionMapForTypes(types.map((type) => typeRegistry.getType(type)!)), elasticsearchClient, typeRegistry, serializer, maxRetryAttempts: migrationConfig.retryAttempts, migrationDocLinks: docLinks.links.kibanaUpgradeSavedObjects, + deletedTypes: REMOVED_TYPES, }; }; diff --git a/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/zdt/context/types.ts b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/zdt/context/types.ts index 2603a5b69a681..5b6d4b2fe27e9 100644 --- a/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/zdt/context/types.ts +++ b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/zdt/context/types.ts @@ -11,16 +11,21 @@ import type { ISavedObjectTypeRegistry, ISavedObjectsSerializer, } from '@kbn/core-saved-objects-server'; +import type { ModelVersionMap } from '@kbn/core-saved-objects-base-server-internal'; import type { DocLinks } from '@kbn/doc-links'; /** * The set of static, precomputed values and services used by the ZDT migration */ export interface MigratorContext { + /** The current Kibana version */ + readonly kibanaVersion: string; /** The first part of the index name such as `.kibana` or `.kibana_task_manager` */ readonly indexPrefix: string; /** Name of the types that are living in the index */ readonly types: string[]; + /** Model versions for the registered types */ + readonly typeModelVersions: ModelVersionMap; /** The client to use for communications with ES */ readonly elasticsearchClient: ElasticsearchClient; /** The maximum number of retries to attempt for a failing action */ @@ -31,4 +36,6 @@ export interface MigratorContext { readonly serializer: ISavedObjectsSerializer; /** The SO type registry to use for the migration */ readonly typeRegistry: ISavedObjectTypeRegistry; + /** List of types that are no longer registered */ + readonly deletedTypes: string[]; } diff --git a/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/zdt/migrate_index.ts b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/zdt/migrate_index.ts index 72ee369236e16..6c3850d5dc8e9 100644 --- a/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/zdt/migrate_index.ts +++ b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/zdt/migrate_index.ts @@ -25,6 +25,7 @@ import { model } from './model'; import { createInitialState } from './state'; export interface MigrateIndexOptions { + kibanaVersion: string; indexPrefix: string; types: string[]; /** The SO type registry to use for the migration */ diff --git a/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/zdt/model/model.test.mocks.ts b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/zdt/model/model.test.mocks.ts index 70faab1fdc8d5..b59f1ef7b7aa4 100644 --- a/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/zdt/model/model.test.mocks.ts +++ b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/zdt/model/model.test.mocks.ts @@ -11,7 +11,7 @@ const realStages = jest.requireActual('./stages'); export const StageMocks = Object.keys(realStages).reduce((mocks, key) => { mocks[key] = jest.fn().mockImplementation((state: unknown) => state); return mocks; -}, {} as Record); +}, {} as Record>); jest.doMock('./stages', () => { return StageMocks; diff --git a/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/zdt/model/model.test.ts b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/zdt/model/model.test.ts index b6c91e702bd2b..c0316b954e5f3 100644 --- a/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/zdt/model/model.test.ts +++ b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/zdt/model/model.test.ts @@ -10,7 +10,7 @@ import { StageMocks } from './model.test.mocks'; import * as Either from 'fp-ts/lib/Either'; import { createContextMock, MockedMigratorContext } from '../test_helpers'; import type { RetryableEsClientError } from '../../actions'; -import type { State, BaseState, FatalState } from '../state'; +import type { State, BaseState, FatalState, AllActionStates } from '../state'; import type { StateActionResponse } from './types'; import { model } from './model'; @@ -113,22 +113,39 @@ describe('model', () => { }); describe('dispatching to correct stage', () => { - test('dispatching INIT state', () => { - const state: State = { + const createStubState = (controlState: AllActionStates): State => + ({ ...baseState, - controlState: 'INIT', - }; - const res: StateActionResponse<'INIT'> = Either.right({ + controlState, + } as unknown as State); + + const createStubResponse = () => + Either.right({ '.kibana_7.11.0_001': { aliases: {}, mappings: { properties: {} }, settings: {}, }, }); - model(state, res, context); - expect(StageMocks.init).toHaveBeenCalledTimes(1); - expect(StageMocks.init).toHaveBeenCalledWith(state, res, context); + const stageMapping: Record = { + INIT: StageMocks.init, + CREATE_TARGET_INDEX: StageMocks.createTargetIndex, + UPDATE_INDEX_MAPPINGS: StageMocks.updateIndexMappings, + UPDATE_INDEX_MAPPINGS_WAIT_FOR_TASK: StageMocks.updateIndexMappingsWaitForTask, + UPDATE_MAPPING_MODEL_VERSIONS: StageMocks.updateMappingModelVersion, + UPDATE_ALIASES: StageMocks.updateAliases, + }; + + Object.entries(stageMapping).forEach(([stage, handler]) => { + test(`dispatch ${stage} state`, () => { + const state = createStubState(stage as AllActionStates); + const res = createStubResponse(); + model(state, res, context); + + expect(handler).toHaveBeenCalledTimes(1); + expect(handler).toHaveBeenCalledWith(state, res, context); + }); }); }); }); diff --git a/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/zdt/model/model.ts b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/zdt/model/model.ts index 2ea48bd1a58af..62971c3a614aa 100644 --- a/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/zdt/model/model.ts +++ b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/zdt/model/model.ts @@ -32,6 +32,36 @@ export const model = ( switch (current.controlState) { case 'INIT': return Stages.init(current, response as StateActionResponse<'INIT'>, context); + case 'CREATE_TARGET_INDEX': + return Stages.createTargetIndex( + current, + response as StateActionResponse<'CREATE_TARGET_INDEX'>, + context + ); + case 'UPDATE_ALIASES': + return Stages.updateAliases( + current, + response as StateActionResponse<'UPDATE_ALIASES'>, + context + ); + case 'UPDATE_INDEX_MAPPINGS': + return Stages.updateIndexMappings( + current, + response as StateActionResponse<'UPDATE_INDEX_MAPPINGS'>, + context + ); + case 'UPDATE_INDEX_MAPPINGS_WAIT_FOR_TASK': + return Stages.updateIndexMappingsWaitForTask( + current, + response as StateActionResponse<'UPDATE_INDEX_MAPPINGS_WAIT_FOR_TASK'>, + context + ); + case 'UPDATE_MAPPING_MODEL_VERSIONS': + return Stages.updateMappingModelVersion( + current, + response as StateActionResponse<'UPDATE_MAPPING_MODEL_VERSIONS'>, + context + ); case 'DONE': case 'FATAL': // The state-action machine will never call the model in the terminating states diff --git a/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/zdt/model/stages/create_target_index.test.mocks.ts b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/zdt/model/stages/create_target_index.test.mocks.ts new file mode 100644 index 0000000000000..b85e06bde59bc --- /dev/null +++ b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/zdt/model/stages/create_target_index.test.mocks.ts @@ -0,0 +1,17 @@ +/* + * 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 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +export const getAliasActionsMock = jest.fn(); + +jest.doMock('../../utils', () => { + const realModule = jest.requireActual('../../utils'); + return { + ...realModule, + getAliasActions: getAliasActionsMock, + }; +}); diff --git a/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/zdt/model/stages/create_target_index.test.ts b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/zdt/model/stages/create_target_index.test.ts new file mode 100644 index 0000000000000..cd15594d32b29 --- /dev/null +++ b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/zdt/model/stages/create_target_index.test.ts @@ -0,0 +1,107 @@ +/* + * 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 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { getAliasActionsMock } from './create_target_index.test.mocks'; +import * as Either from 'fp-ts/lib/Either'; +import { + createContextMock, + createPostInitState, + type MockedMigratorContext, +} from '../../test_helpers'; +import type { CreateTargetIndexState } from '../../state'; +import type { StateActionResponse } from '../types'; +import { createTargetIndex } from './create_target_index'; + +describe('Stage: createTargetIndex', () => { + let context: MockedMigratorContext; + + const createState = (parts: Partial = {}): CreateTargetIndexState => ({ + ...createPostInitState(), + controlState: 'CREATE_TARGET_INDEX', + indexMappings: { properties: { foo: { type: 'text' } }, _meta: {} }, + ...parts, + }); + + beforeEach(() => { + context = createContextMock(); + getAliasActionsMock.mockReset().mockReturnValue([]); + }); + + describe('In case of left return', () => { + it('CREATE_TARGET_INDEX -> CREATE_TARGET_INDEX in case of index_not_green_timeout exception', () => { + const state = createState(); + const res: StateActionResponse<'CREATE_TARGET_INDEX'> = Either.left({ + type: 'index_not_green_timeout', + message: 'index not green', + }); + + const result = createTargetIndex(state, res, context); + + expect(result).toEqual({ + ...state, + controlState: 'CREATE_TARGET_INDEX', + retryCount: 1, + retryDelay: 2000, + logs: expect.any(Array), + }); + }); + + it('CREATE_TARGET_INDEX -> FATAL in case of cluster_shard_limit_exceeded exception', () => { + const state = createState(); + const res: StateActionResponse<'CREATE_TARGET_INDEX'> = Either.left({ + type: 'cluster_shard_limit_exceeded', + }); + + const result = createTargetIndex(state, res, context); + + expect(result).toEqual({ + ...state, + controlState: 'FATAL', + reason: expect.stringContaining('[cluster_shard_limit_exceeded]'), + }); + }); + }); + + describe('In case of right return', () => { + it('calls getAliasActions with the correct parameters', () => { + const state = createState(); + const res: StateActionResponse<'CREATE_TARGET_INDEX'> = + Either.right('create_index_succeeded'); + + createTargetIndex(state, res, context); + + expect(getAliasActionsMock).toHaveBeenCalledTimes(1); + expect(getAliasActionsMock).toHaveBeenCalledWith({ + currentIndex: state.currentIndex, + existingAliases: [], + indexPrefix: context.indexPrefix, + kibanaVersion: context.kibanaVersion, + }); + }); + + it('CREATE_TARGET_INDEX -> UPDATE_ALIASES when successful', () => { + const state = createState(); + const res: StateActionResponse<'CREATE_TARGET_INDEX'> = + Either.right('create_index_succeeded'); + + const aliasActions = [{ add: { index: '.kibana_1', alias: '.kibana' } }]; + getAliasActionsMock.mockReturnValue(aliasActions); + + const newState = createTargetIndex(state, res, context); + + expect(newState).toEqual({ + ...state, + controlState: 'UPDATE_ALIASES', + previousMappings: state.indexMappings, + currentIndexMeta: state.indexMappings._meta, + aliases: [], + aliasActions, + }); + }); + }); +}); diff --git a/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/zdt/model/stages/create_target_index.ts b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/zdt/model/stages/create_target_index.ts new file mode 100644 index 0000000000000..bb0697a70cf51 --- /dev/null +++ b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/zdt/model/stages/create_target_index.ts @@ -0,0 +1,57 @@ +/* + * 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 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { cloneDeep } from 'lodash'; +import * as Either from 'fp-ts/lib/Either'; +import { delayRetryState } from '../../../model/retry_state'; +import { throwBadResponse } from '../../../model/helpers'; +import { CLUSTER_SHARD_LIMIT_EXCEEDED_REASON } from '../../../common/constants'; +import { isTypeof } from '../../actions'; +import { getAliasActions } from '../../utils'; +import type { ModelStage } from '../types'; + +export const createTargetIndex: ModelStage<'CREATE_TARGET_INDEX', 'UPDATE_ALIASES' | 'FATAL'> = ( + state, + res, + context +) => { + if (Either.isLeft(res)) { + const left = res.left; + if (isTypeof(left, 'index_not_green_timeout')) { + // cluster might just be busy so we retry the action for a set number of times. + const retryErrorMessage = `${left.message} Refer to ${context.migrationDocLinks.repeatedTimeoutRequests} for information on how to resolve the issue.`; + return delayRetryState(state, retryErrorMessage, context.maxRetryAttempts); + } else if (isTypeof(left, 'cluster_shard_limit_exceeded')) { + return { + ...state, + controlState: 'FATAL', + reason: `${CLUSTER_SHARD_LIMIT_EXCEEDED_REASON} See ${context.migrationDocLinks.clusterShardLimitExceeded}`, + }; + } else { + return throwBadResponse(state, left); + } + } + + const aliasActions = getAliasActions({ + currentIndex: state.currentIndex, + existingAliases: [], + indexPrefix: context.indexPrefix, + kibanaVersion: context.kibanaVersion, + }); + + const currentIndexMeta = cloneDeep(state.indexMappings._meta!); + + return { + ...state, + controlState: 'UPDATE_ALIASES', + previousMappings: state.indexMappings, + currentIndexMeta, + aliases: [], + aliasActions, + }; +}; diff --git a/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/zdt/model/stages/index.ts b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/zdt/model/stages/index.ts index aa12c6bed1b22..0322f92eb35aa 100644 --- a/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/zdt/model/stages/index.ts +++ b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/zdt/model/stages/index.ts @@ -7,3 +7,8 @@ */ export { init } from './init'; +export { createTargetIndex } from './create_target_index'; +export { updateAliases } from './update_aliases'; +export { updateIndexMappings } from './update_index_mappings'; +export { updateIndexMappingsWaitForTask } from './update_index_mappings_wait_for_task'; +export { updateMappingModelVersion } from './update_mapping_model_version'; diff --git a/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/zdt/model/stages/init.test.mocks.ts b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/zdt/model/stages/init.test.mocks.ts new file mode 100644 index 0000000000000..8f773fe951171 --- /dev/null +++ b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/zdt/model/stages/init.test.mocks.ts @@ -0,0 +1,23 @@ +/* + * 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 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +export const getCurrentIndexMock = jest.fn(); +export const checkVersionCompatibilityMock = jest.fn(); +export const buildIndexMappingsMock = jest.fn(); +export const generateAdditiveMappingDiffMock = jest.fn(); + +jest.doMock('../../utils', () => { + const realModule = jest.requireActual('../../utils'); + return { + ...realModule, + getCurrentIndex: getCurrentIndexMock, + checkVersionCompatibility: checkVersionCompatibilityMock, + buildIndexMappings: buildIndexMappingsMock, + generateAdditiveMappingDiff: generateAdditiveMappingDiffMock, + }; +}); diff --git a/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/zdt/model/stages/init.test.ts b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/zdt/model/stages/init.test.ts index 8105449c7fce8..ea6f4424404ef 100644 --- a/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/zdt/model/stages/init.test.ts +++ b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/zdt/model/stages/init.test.ts @@ -6,15 +6,24 @@ * Side Public License, v 1. */ +import { + getCurrentIndexMock, + checkVersionCompatibilityMock, + buildIndexMappingsMock, + generateAdditiveMappingDiffMock, +} from './init.test.mocks'; import * as Either from 'fp-ts/lib/Either'; +import { FetchIndexResponse } from '../../../actions'; import { createContextMock, MockedMigratorContext } from '../../test_helpers'; import type { InitState } from '../../state'; import type { StateActionResponse } from '../types'; import { init } from './init'; -describe('Action: init', () => { +describe('Stage: init', () => { let context: MockedMigratorContext; + const currentIndex = '.kibana_1'; + const createState = (parts: Partial = {}): InitState => ({ controlState: 'INIT', retryDelay: 0, @@ -23,29 +32,40 @@ describe('Action: init', () => { ...parts, }); - beforeEach(() => { - context = createContextMock(); + const createResponse = (): FetchIndexResponse => ({ + [currentIndex]: { + aliases: {}, + mappings: { + properties: {}, + _meta: { mappingVersions: { foo: 1, bar: 1 } }, + }, + settings: {}, + }, }); - test('INIT -> DONE because its not implemented yet', () => { - const state = createState(); - const res: StateActionResponse<'INIT'> = Either.right({ - '.kibana_8.7.0_001': { - aliases: { - '.kibana': {}, - '.kibana_8.7.0': {}, - }, - mappings: { properties: {} }, - settings: {}, - }, + beforeEach(() => { + getCurrentIndexMock.mockReset().mockReturnValue(currentIndex); + checkVersionCompatibilityMock.mockReset().mockReturnValue({ + status: 'equal', }); + generateAdditiveMappingDiffMock.mockReset().mockReturnValue({}); - const newState = init(state, res, context); - - expect(newState.controlState).toEqual('DONE'); + context = createContextMock({ indexPrefix: '.kibana', types: ['foo', 'bar'] }); + context.typeRegistry.registerType({ + name: 'foo', + mappings: { properties: {} }, + namespaceType: 'single', + hidden: false, + }); + context.typeRegistry.registerType({ + name: 'bar', + mappings: { properties: {} }, + namespaceType: 'single', + hidden: false, + }); }); - test('INIT -> INIT when cluster routing allocation is incompatible', () => { + it('loops to INIT when cluster routing allocation is incompatible', () => { const state = createState(); const res: StateActionResponse<'INIT'> = Either.left({ type: 'incompatible_cluster_routing_allocation', @@ -58,4 +78,236 @@ describe('Action: init', () => { expect(newState.retryDelay).toEqual(2000); expect(newState.logs).toHaveLength(1); }); + + it('calls getCurrentIndex with the correct parameters', () => { + const state = createState(); + const fetchIndexResponse = createResponse(); + const res: StateActionResponse<'INIT'> = Either.right(fetchIndexResponse); + + init(state, res, context); + + expect(getCurrentIndexMock).toHaveBeenCalledTimes(1); + expect(getCurrentIndexMock).toHaveBeenCalledWith(fetchIndexResponse, context.indexPrefix); + }); + + it('calls checkVersionCompatibility with the correct parameters', () => { + const state = createState(); + const fetchIndexResponse = createResponse(); + const res: StateActionResponse<'INIT'> = Either.right(fetchIndexResponse); + + init(state, res, context); + + expect(checkVersionCompatibilityMock).toHaveBeenCalledTimes(1); + expect(checkVersionCompatibilityMock).toHaveBeenCalledWith({ + mappings: fetchIndexResponse[currentIndex].mappings, + types: ['foo', 'bar'].map((type) => context.typeRegistry.getType(type)), + source: 'mappingVersions', + deletedTypes: context.deletedTypes, + }); + }); + + describe('when getCurrentIndex returns undefined', () => { + beforeEach(() => { + getCurrentIndexMock.mockReturnValue(undefined); + }); + + it('calls buildIndexMappings with the correct parameters', () => { + const state = createState(); + const fetchIndexResponse = createResponse(); + const res: StateActionResponse<'INIT'> = Either.right(fetchIndexResponse); + + init(state, res, context); + + expect(buildIndexMappingsMock).toHaveBeenCalledTimes(1); + expect(buildIndexMappingsMock).toHaveBeenCalledWith({ + types: ['foo', 'bar'].map((type) => context.typeRegistry.getType(type)), + }); + }); + + it('forwards to CREATE_TARGET_INDEX', () => { + const state = createState(); + const fetchIndexResponse = createResponse(); + const res: StateActionResponse<'INIT'> = Either.right(fetchIndexResponse); + + const mockMappings = { properties: { someMappings: 'string' } }; + buildIndexMappingsMock.mockReturnValue(mockMappings); + + const newState = init(state, res, context); + + expect(newState).toEqual( + expect.objectContaining({ + controlState: 'CREATE_TARGET_INDEX', + currentIndex: '.kibana_1', + indexMappings: mockMappings, + }) + ); + }); + }); + + describe('when checkVersionCompatibility returns `greater`', () => { + it('calls generateAdditiveMappingDiff with the correct parameters', () => { + const state = createState(); + const fetchIndexResponse = createResponse(); + const res: StateActionResponse<'INIT'> = Either.right(fetchIndexResponse); + + checkVersionCompatibilityMock.mockReturnValue({ + status: 'greater', + }); + + init(state, res, context); + + expect(generateAdditiveMappingDiffMock).toHaveBeenCalledTimes(1); + expect(generateAdditiveMappingDiffMock).toHaveBeenCalledWith({ + types: ['foo', 'bar'].map((type) => context.typeRegistry.getType(type)), + meta: fetchIndexResponse[currentIndex].mappings._meta, + deletedTypes: context.deletedTypes, + }); + }); + + it('forwards to UPDATE_INDEX_MAPPINGS', () => { + const state = createState(); + const fetchIndexResponse = createResponse(); + const res: StateActionResponse<'INIT'> = Either.right(fetchIndexResponse); + + checkVersionCompatibilityMock.mockReturnValue({ + status: 'greater', + }); + generateAdditiveMappingDiffMock.mockReturnValue({ someToken: {} }); + + const newState = init(state, res, context); + + expect(newState).toEqual( + expect.objectContaining({ + controlState: 'UPDATE_INDEX_MAPPINGS', + currentIndex, + previousMappings: fetchIndexResponse[currentIndex].mappings, + additiveMappingChanges: { someToken: {} }, + }) + ); + }); + + it('adds a log entry about the version check', () => { + const state = createState(); + const res: StateActionResponse<'INIT'> = Either.right(createResponse()); + + checkVersionCompatibilityMock.mockReturnValue({ + status: 'greater', + }); + + const newState = init(state, res, context); + + expect(newState.logs.map((entry) => entry.message)).toEqual([ + `Mappings model version check result: greater`, + ]); + }); + }); + + describe('when checkVersionCompatibility returns `equal`', () => { + it('forwards to UPDATE_ALIASES', () => { + const state = createState(); + const fetchIndexResponse = createResponse(); + const res: StateActionResponse<'INIT'> = Either.right(fetchIndexResponse); + + checkVersionCompatibilityMock.mockReturnValue({ + status: 'equal', + }); + + const newState = init(state, res, context); + + expect(newState).toEqual( + expect.objectContaining({ + controlState: 'UPDATE_ALIASES', + currentIndex, + previousMappings: fetchIndexResponse[currentIndex].mappings, + }) + ); + }); + + it('adds a log entry about the version check', () => { + const state = createState(); + const res: StateActionResponse<'INIT'> = Either.right(createResponse()); + + checkVersionCompatibilityMock.mockReturnValue({ + status: 'equal', + }); + + const newState = init(state, res, context); + + expect(newState.logs.map((entry) => entry.message)).toEqual([ + `Mappings model version check result: equal`, + ]); + }); + }); + + describe('when checkVersionCompatibility returns `lesser`', () => { + it('forwards to FATAL', () => { + const state = createState(); + const fetchIndexResponse = createResponse(); + const res: StateActionResponse<'INIT'> = Either.right(fetchIndexResponse); + + checkVersionCompatibilityMock.mockReturnValue({ + status: 'lesser', + }); + + const newState = init(state, res, context); + + expect(newState).toEqual( + expect.objectContaining({ + controlState: 'FATAL', + reason: 'Downgrading model version is currently unsupported', + }) + ); + }); + + it('adds a log entry about the version check', () => { + const state = createState(); + const res: StateActionResponse<'INIT'> = Either.right(createResponse()); + + checkVersionCompatibilityMock.mockReturnValue({ + status: 'lesser', + }); + + const newState = init(state, res, context); + + expect(newState.logs.map((entry) => entry.message)).toEqual([ + `Mappings model version check result: lesser`, + ]); + }); + }); + + describe('when checkVersionCompatibility returns `conflict`', () => { + it('forwards to FATAL', () => { + const state = createState(); + const fetchIndexResponse = createResponse(); + const res: StateActionResponse<'INIT'> = Either.right(fetchIndexResponse); + + checkVersionCompatibilityMock.mockReturnValue({ + status: 'conflict', + }); + + const newState = init(state, res, context); + + expect(newState).toEqual( + expect.objectContaining({ + controlState: 'FATAL', + reason: 'Model version conflict: inconsistent higher/lower versions', + }) + ); + }); + + it('adds a log entry about the version check', () => { + const state = createState(); + const res: StateActionResponse<'INIT'> = Either.right(createResponse()); + + checkVersionCompatibilityMock.mockReturnValue({ + status: 'conflict', + }); + + const newState = init(state, res, context); + + expect(newState.logs.map((entry) => entry.message)).toEqual([ + `Mappings model version check result: conflict`, + ]); + }); + }); }); diff --git a/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/zdt/model/stages/init.ts b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/zdt/model/stages/init.ts index 78dccf237afca..da138730364f4 100644 --- a/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/zdt/model/stages/init.ts +++ b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/zdt/model/stages/init.ts @@ -6,14 +6,25 @@ * Side Public License, v 1. */ +import { cloneDeep } from 'lodash'; import * as Either from 'fp-ts/lib/Either'; import { delayRetryState } from '../../../model/retry_state'; import { throwBadResponse } from '../../../model/helpers'; +import type { MigrationLog } from '../../../types'; import { isTypeof } from '../../actions'; -import type { State } from '../../state'; +import { + getCurrentIndex, + checkVersionCompatibility, + buildIndexMappings, + getAliasActions, + generateAdditiveMappingDiff, +} from '../../utils'; import type { ModelStage } from '../types'; -export const init: ModelStage<'INIT', 'DONE' | 'FATAL'> = (state, res, context): State => { +export const init: ModelStage< + 'INIT', + 'CREATE_TARGET_INDEX' | 'UPDATE_INDEX_MAPPINGS' | 'UPDATE_ALIASES' | 'FATAL' +> = (state, res, context) => { if (Either.isLeft(res)) { const left = res.left; if (isTypeof(left, 'incompatible_cluster_routing_allocation')) { @@ -24,9 +35,101 @@ export const init: ModelStage<'INIT', 'DONE' | 'FATAL'> = (state, res, context): } } - // nothing implemented yet, just going to 'DONE' - return { - ...state, - controlState: 'DONE', - }; + const types = context.types.map((type) => context.typeRegistry.getType(type)!); + const logs: MigrationLog[] = [...state.logs]; + + const indices = res.right; + const currentIndex = getCurrentIndex(indices, context.indexPrefix); + + // No indices were found, likely because it is the first time Kibana boots. + // In that case, we just create the index. + if (!currentIndex) { + return { + ...state, + logs, + controlState: 'CREATE_TARGET_INDEX', + currentIndex: `${context.indexPrefix}_1`, + indexMappings: buildIndexMappings({ types }), + }; + } + + // Index was found. This is the standard scenario, we check the model versions + // compatibility before going further. + const currentMappings = indices[currentIndex].mappings; + const versionCheck = checkVersionCompatibility({ + mappings: currentMappings, + types, + source: 'mappingVersions', + deletedTypes: context.deletedTypes, + }); + + logs.push({ + level: 'info', + message: `Mappings model version check result: ${versionCheck.status}`, + }); + + const aliases = Object.keys(indices[currentIndex].aliases); + const aliasActions = getAliasActions({ + existingAliases: aliases, + currentIndex, + indexPrefix: context.indexPrefix, + kibanaVersion: context.kibanaVersion, + }); + // cloning as we may be mutating it in later stages. + const currentIndexMeta = cloneDeep(currentMappings._meta!); + + switch (versionCheck.status) { + // app version is greater than the index mapping version. + // scenario of an upgrade: we need to update the mappings + case 'greater': + const additiveMappingChanges = generateAdditiveMappingDiff({ + types, + meta: currentMappings._meta ?? {}, + deletedTypes: context.deletedTypes, + }); + return { + ...state, + controlState: 'UPDATE_INDEX_MAPPINGS', + logs, + currentIndex, + currentIndexMeta, + aliases, + aliasActions, + previousMappings: currentMappings, + additiveMappingChanges, + }; + // app version and index mapping version are the same. + // either application upgrade without model change, or a simple reboot on the same version. + // In that case we jump directly to alias update + case 'equal': + return { + ...state, + controlState: 'UPDATE_ALIASES', + logs, + currentIndex, + currentIndexMeta, + aliases, + aliasActions, + previousMappings: currentMappings, + }; + // app version is lower than the index mapping version. + // likely a rollback scenario - unsupported for the initial implementation + case 'lesser': + return { + ...state, + controlState: 'FATAL', + reason: 'Downgrading model version is currently unsupported', + logs, + }; + // conflicts: version for some types are greater, some are lower + // shouldn't occur in any normal scenario - cannot recover + case 'conflict': + default: + return { + ...state, + controlState: 'FATAL', + reason: 'Model version conflict: inconsistent higher/lower versions', + logs, + }; + } }; diff --git a/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/zdt/model/stages/update_aliases.test.ts b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/zdt/model/stages/update_aliases.test.ts new file mode 100644 index 0000000000000..4fac3d02db044 --- /dev/null +++ b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/zdt/model/stages/update_aliases.test.ts @@ -0,0 +1,74 @@ +/* + * 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 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import * as Either from 'fp-ts/lib/Either'; +import { + createContextMock, + createPostInitState, + type MockedMigratorContext, +} from '../../test_helpers'; +import type { UpdateAliasesState } from '../../state'; +import type { StateActionResponse } from '../types'; +import { updateAliases } from './update_aliases'; + +describe('Stage: updateAliases', () => { + let context: MockedMigratorContext; + + const createState = (parts: Partial = {}): UpdateAliasesState => ({ + ...createPostInitState(), + controlState: 'UPDATE_ALIASES', + ...parts, + }); + + beforeEach(() => { + context = createContextMock(); + }); + + it('UPDATE_ALIASES -> FATAL in case of alias_not_found_exception', () => { + const state = createState(); + const res: StateActionResponse<'UPDATE_ALIASES'> = Either.left({ + type: 'alias_not_found_exception', + }); + + const newState = updateAliases(state, res, context); + + expect(newState).toEqual({ + ...state, + controlState: 'FATAL', + reason: `Alias missing during alias update`, + }); + }); + + it('UPDATE_ALIASES -> FATAL in case of index_not_found_exception', () => { + const state = createState(); + const res: StateActionResponse<'UPDATE_ALIASES'> = Either.left({ + type: 'index_not_found_exception', + index: '.test', + }); + + const newState = updateAliases(state, res, context); + + expect(newState).toEqual({ + ...state, + controlState: 'FATAL', + reason: `Index .test missing during alias update`, + }); + }); + + it('UPDATE_ALIASES -> DONE if successful', () => { + const state = createState(); + const res: StateActionResponse<'UPDATE_ALIASES'> = Either.right('update_aliases_succeeded'); + + const newState = updateAliases(state, res, context); + + expect(newState).toEqual({ + ...state, + controlState: 'DONE', + }); + }); +}); diff --git a/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/zdt/model/stages/update_aliases.ts b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/zdt/model/stages/update_aliases.ts new file mode 100644 index 0000000000000..4d91eb116871b --- /dev/null +++ b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/zdt/model/stages/update_aliases.ts @@ -0,0 +1,46 @@ +/* + * 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 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import * as Either from 'fp-ts/lib/Either'; +import { throwBadResponse } from '../../../model/helpers'; +import { isTypeof } from '../../actions'; +import type { ModelStage } from '../types'; + +export const updateAliases: ModelStage<'UPDATE_ALIASES', 'DONE' | 'FATAL'> = ( + state, + res, + context +) => { + if (Either.isLeft(res)) { + const left = res.left; + if (isTypeof(left, 'alias_not_found_exception')) { + // Should never occur given a single operator is supposed to perform the migration. + // we just terminate in that case + return { + ...state, + controlState: 'FATAL', + reason: `Alias missing during alias update`, + }; + } else if (isTypeof(left, 'index_not_found_exception')) { + // Should never occur given a single operator is supposed to perform the migration. + // we just terminate in that case + return { + ...state, + controlState: 'FATAL', + reason: `Index ${left.index} missing during alias update`, + }; + } else { + throwBadResponse(state, left as never); + } + } + + return { + ...state, + controlState: 'DONE', + }; +}; diff --git a/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/zdt/model/stages/update_index_mappings.test.ts b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/zdt/model/stages/update_index_mappings.test.ts new file mode 100644 index 0000000000000..971482d3262b7 --- /dev/null +++ b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/zdt/model/stages/update_index_mappings.test.ts @@ -0,0 +1,53 @@ +/* + * 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 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import * as Either from 'fp-ts/lib/Either'; +import { + createContextMock, + createPostInitState, + type MockedMigratorContext, +} from '../../test_helpers'; +import type { ResponseType } from '../../next'; +import type { UpdateIndexMappingsState } from '../../state'; +import type { StateActionResponse } from '../types'; +import { updateIndexMappings } from './update_index_mappings'; + +describe('Stage: updateIndexMappings', () => { + let context: MockedMigratorContext; + + const createState = ( + parts: Partial = {} + ): UpdateIndexMappingsState => ({ + ...createPostInitState(), + controlState: 'UPDATE_INDEX_MAPPINGS', + additiveMappingChanges: {}, + ...parts, + }); + + beforeEach(() => { + context = createContextMock(); + }); + + it('UPDATE_INDEX_MAPPINGS -> UPDATE_INDEX_MAPPINGS_WAIT_FOR_TASK when successful', () => { + const state = createState(); + const res: ResponseType<'UPDATE_INDEX_MAPPINGS'> = Either.right({ + taskId: '42', + }); + + const newState = updateIndexMappings( + state, + res as StateActionResponse<'UPDATE_INDEX_MAPPINGS'>, + context + ); + expect(newState).toEqual({ + ...state, + controlState: 'UPDATE_INDEX_MAPPINGS_WAIT_FOR_TASK', + updateTargetMappingsTaskId: '42', + }); + }); +}); diff --git a/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/zdt/model/stages/update_index_mappings.ts b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/zdt/model/stages/update_index_mappings.ts new file mode 100644 index 0000000000000..ffcd67aed2b78 --- /dev/null +++ b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/zdt/model/stages/update_index_mappings.ts @@ -0,0 +1,27 @@ +/* + * 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 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import * as Either from 'fp-ts/lib/Either'; +import { throwBadResponse } from '../../../model/helpers'; +import type { ModelStage } from '../types'; + +export const updateIndexMappings: ModelStage< + 'UPDATE_INDEX_MAPPINGS', + 'UPDATE_INDEX_MAPPINGS_WAIT_FOR_TASK' | 'FATAL' +> = (state, res, context) => { + if (Either.isRight(res)) { + const right = res.right; + return { + ...state, + controlState: 'UPDATE_INDEX_MAPPINGS_WAIT_FOR_TASK', + updateTargetMappingsTaskId: right.taskId, + }; + } + + throwBadResponse(state, res as never); +}; diff --git a/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/zdt/model/stages/update_index_mappings_wait_for_task.test.ts b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/zdt/model/stages/update_index_mappings_wait_for_task.test.ts new file mode 100644 index 0000000000000..971482d3262b7 --- /dev/null +++ b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/zdt/model/stages/update_index_mappings_wait_for_task.test.ts @@ -0,0 +1,53 @@ +/* + * 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 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import * as Either from 'fp-ts/lib/Either'; +import { + createContextMock, + createPostInitState, + type MockedMigratorContext, +} from '../../test_helpers'; +import type { ResponseType } from '../../next'; +import type { UpdateIndexMappingsState } from '../../state'; +import type { StateActionResponse } from '../types'; +import { updateIndexMappings } from './update_index_mappings'; + +describe('Stage: updateIndexMappings', () => { + let context: MockedMigratorContext; + + const createState = ( + parts: Partial = {} + ): UpdateIndexMappingsState => ({ + ...createPostInitState(), + controlState: 'UPDATE_INDEX_MAPPINGS', + additiveMappingChanges: {}, + ...parts, + }); + + beforeEach(() => { + context = createContextMock(); + }); + + it('UPDATE_INDEX_MAPPINGS -> UPDATE_INDEX_MAPPINGS_WAIT_FOR_TASK when successful', () => { + const state = createState(); + const res: ResponseType<'UPDATE_INDEX_MAPPINGS'> = Either.right({ + taskId: '42', + }); + + const newState = updateIndexMappings( + state, + res as StateActionResponse<'UPDATE_INDEX_MAPPINGS'>, + context + ); + expect(newState).toEqual({ + ...state, + controlState: 'UPDATE_INDEX_MAPPINGS_WAIT_FOR_TASK', + updateTargetMappingsTaskId: '42', + }); + }); +}); diff --git a/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/zdt/model/stages/update_index_mappings_wait_for_task.ts b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/zdt/model/stages/update_index_mappings_wait_for_task.ts new file mode 100644 index 0000000000000..9856cb0c5a1e5 --- /dev/null +++ b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/zdt/model/stages/update_index_mappings_wait_for_task.ts @@ -0,0 +1,43 @@ +/* + * 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 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import {} from 'lodash'; +import * as Either from 'fp-ts/lib/Either'; +import { delayRetryState } from '../../../model/retry_state'; +import { throwBadResponse } from '../../../model/helpers'; +import { isTypeof } from '../../actions'; +import type { ModelStage } from '../types'; + +export const updateIndexMappingsWaitForTask: ModelStage< + 'UPDATE_INDEX_MAPPINGS_WAIT_FOR_TASK', + 'UPDATE_MAPPING_MODEL_VERSIONS' | 'FATAL' +> = (state, res, context) => { + if (Either.isLeft(res)) { + const left = res.left; + if (isTypeof(left, 'wait_for_task_completion_timeout')) { + // After waiting for the specified timeout, the task has not yet + // completed. Retry this step to see if the task has completed after an + // exponential delay. We will basically keep polling forever until the + // Elasticsearch task succeeds or fails. + return delayRetryState(state, left.message, Number.MAX_SAFE_INTEGER); + } else { + throwBadResponse(state, left); + } + } + + return { + ...state, + controlState: 'UPDATE_MAPPING_MODEL_VERSIONS', + currentIndexMeta: { + ...state.currentIndexMeta, + mappingVersions: { + ...context.typeModelVersions, + }, + }, + }; +}; diff --git a/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/zdt/model/stages/update_mapping_model_version.test.ts b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/zdt/model/stages/update_mapping_model_version.test.ts new file mode 100644 index 0000000000000..971482d3262b7 --- /dev/null +++ b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/zdt/model/stages/update_mapping_model_version.test.ts @@ -0,0 +1,53 @@ +/* + * 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 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import * as Either from 'fp-ts/lib/Either'; +import { + createContextMock, + createPostInitState, + type MockedMigratorContext, +} from '../../test_helpers'; +import type { ResponseType } from '../../next'; +import type { UpdateIndexMappingsState } from '../../state'; +import type { StateActionResponse } from '../types'; +import { updateIndexMappings } from './update_index_mappings'; + +describe('Stage: updateIndexMappings', () => { + let context: MockedMigratorContext; + + const createState = ( + parts: Partial = {} + ): UpdateIndexMappingsState => ({ + ...createPostInitState(), + controlState: 'UPDATE_INDEX_MAPPINGS', + additiveMappingChanges: {}, + ...parts, + }); + + beforeEach(() => { + context = createContextMock(); + }); + + it('UPDATE_INDEX_MAPPINGS -> UPDATE_INDEX_MAPPINGS_WAIT_FOR_TASK when successful', () => { + const state = createState(); + const res: ResponseType<'UPDATE_INDEX_MAPPINGS'> = Either.right({ + taskId: '42', + }); + + const newState = updateIndexMappings( + state, + res as StateActionResponse<'UPDATE_INDEX_MAPPINGS'>, + context + ); + expect(newState).toEqual({ + ...state, + controlState: 'UPDATE_INDEX_MAPPINGS_WAIT_FOR_TASK', + updateTargetMappingsTaskId: '42', + }); + }); +}); diff --git a/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/zdt/model/stages/update_mapping_model_version.ts b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/zdt/model/stages/update_mapping_model_version.ts new file mode 100644 index 0000000000000..8b4df56fc83a7 --- /dev/null +++ b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/zdt/model/stages/update_mapping_model_version.ts @@ -0,0 +1,25 @@ +/* + * 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 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import * as Either from 'fp-ts/lib/Either'; +import { throwBadResponse } from '../../../model/helpers'; +import type { ModelStage } from '../types'; + +export const updateMappingModelVersion: ModelStage< + 'UPDATE_MAPPING_MODEL_VERSIONS', + 'DONE' | 'FATAL' +> = (state, res, context) => { + if (Either.isLeft(res)) { + throwBadResponse(state, res as never); + } + + return { + ...state, + controlState: 'DONE', + }; +}; diff --git a/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/zdt/next.ts b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/zdt/next.ts index 329ba194b5957..a85e9bfde6b56 100644 --- a/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/zdt/next.ts +++ b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/zdt/next.ts @@ -6,9 +6,19 @@ * Side Public License, v 1. */ -import type { AllActionStates, InitState, State } from './state'; +import type { + AllActionStates, + State, + InitState, + CreateTargetIndexState, + UpdateIndexMappingsState, + UpdateIndexMappingsWaitForTaskState, + UpdateMappingModelVersionState, + UpdateAliasesState, +} from './state'; import type { MigratorContext } from './context'; import * as Actions from './actions'; +import { createDelayFn } from '../common/utils'; export type ActionMap = ReturnType; @@ -23,9 +33,45 @@ export type ResponseType = Awaited< >; export const nextActionMap = (context: MigratorContext) => { + const client = context.elasticsearchClient; return { INIT: (state: InitState) => - Actions.init({ client: context.elasticsearchClient, indices: [context.indexPrefix] }), + Actions.init({ + client, + indices: [`${context.indexPrefix}_*`], + }), + CREATE_TARGET_INDEX: (state: CreateTargetIndexState) => + Actions.createIndex({ + client, + indexName: state.currentIndex, + mappings: state.indexMappings, + }), + UPDATE_INDEX_MAPPINGS: (state: UpdateIndexMappingsState) => + Actions.updateAndPickupMappings({ + client, + index: state.currentIndex, + mappings: { properties: state.additiveMappingChanges }, + }), + UPDATE_INDEX_MAPPINGS_WAIT_FOR_TASK: (state: UpdateIndexMappingsWaitForTaskState) => + Actions.waitForPickupUpdatedMappingsTask({ + client, + taskId: state.updateTargetMappingsTaskId, + timeout: '60s', + }), + UPDATE_MAPPING_MODEL_VERSIONS: (state: UpdateMappingModelVersionState) => + Actions.updateMappings({ + client, + index: state.currentIndex, + mappings: { + properties: {}, + _meta: state.currentIndexMeta, + }, + }), + UPDATE_ALIASES: (state: UpdateAliasesState) => + Actions.updateAliases({ + client, + aliasActions: state.aliasActions, + }), }; }; @@ -33,13 +79,7 @@ export const next = (context: MigratorContext) => { const map = nextActionMap(context); return (state: State) => { - const delay = any>(fn: F): (() => ReturnType) => { - return () => { - return state.retryDelay > 0 - ? new Promise((resolve) => setTimeout(resolve, state.retryDelay)).then(fn) - : fn(); - }; - }; + const delay = createDelayFn(state); if (state.controlState === 'DONE' || state.controlState === 'FATAL') { // Return null if we're in one of the terminating states diff --git a/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/zdt/run_zdt_migration.ts b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/zdt/run_zdt_migration.ts index 8a2686e23764b..44a067944bd05 100644 --- a/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/zdt/run_zdt_migration.ts +++ b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/zdt/run_zdt_migration.ts @@ -22,7 +22,9 @@ import { buildMigratorConfigs } from './utils'; import { migrateIndex } from './migrate_index'; export interface RunZeroDowntimeMigrationOpts { - /** The kibana system index prefix. e.g `.kibana` */ + /** The current Kibana version */ + kibanaVersion: string; + /** The Kibana system index prefix. e.g `.kibana` or `.kibana_task_manager` */ kibanaIndexPrefix: string; /** The SO type registry to use for the migration */ typeRegistry: ISavedObjectTypeRegistry; diff --git a/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/zdt/state/create_initial_state.ts b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/zdt/state/create_initial_state.ts index ceb68b42b4a72..f5132d2ad2739 100644 --- a/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/zdt/state/create_initial_state.ts +++ b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/zdt/state/create_initial_state.ts @@ -9,6 +9,9 @@ import type { InitState, State } from './types'; import type { MigratorContext } from '../context'; +/** + * Create the initial state to be used for the ZDT migrator. + */ export const createInitialState = (context: MigratorContext): State => { const initialState: InitState = { controlState: 'INIT', diff --git a/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/zdt/state/index.ts b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/zdt/state/index.ts index bff3ea15da4c2..0f7d28507bb4a 100644 --- a/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/zdt/state/index.ts +++ b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/zdt/state/index.ts @@ -9,6 +9,11 @@ export type { BaseState, InitState, + CreateTargetIndexState, + UpdateIndexMappingsState, + UpdateIndexMappingsWaitForTaskState, + UpdateMappingModelVersionState, + UpdateAliasesState, DoneState, FatalState, State, diff --git a/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/zdt/state/types.ts b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/zdt/state/types.ts index 90a888a8a947c..d43c6e49dd5e5 100644 --- a/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/zdt/state/types.ts +++ b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/zdt/state/types.ts @@ -6,8 +6,11 @@ * Side Public License, v 1. */ +import type { SavedObjectsMappingProperties } from '@kbn/core-saved-objects-server'; +import type { IndexMapping, IndexMappingMeta } from '@kbn/core-saved-objects-base-server-internal'; import type { MigrationLog } from '../../types'; import type { ControlState } from '../../state_action_machine'; +import type { AliasAction } from '../../actions'; export interface BaseState extends ControlState { readonly retryCount: number; @@ -15,10 +18,60 @@ export interface BaseState extends ControlState { readonly logs: MigrationLog[]; } +/** Initial state before any action is performed */ export interface InitState extends BaseState { readonly controlState: 'INIT'; } +export interface PostInitState extends BaseState { + /** + * The index we're currently migrating. + */ + readonly currentIndex: string; + /** + * The aliases that are already present for the current index. + */ + readonly aliases: string[]; + /** + * The alias actions to perform to update the aliases. + */ + readonly aliasActions: AliasAction[]; + /** + * The *previous* mappings (and _meta), as they were when we resolved the index + * information. This shouldn't be updated once populated. + */ + readonly previousMappings: IndexMapping; + /** + * The *current* _meta field of the index. + * All operations updating this field will update in the state accordingly. + */ + readonly currentIndexMeta: IndexMappingMeta; +} + +export interface CreateTargetIndexState extends BaseState { + readonly controlState: 'CREATE_TARGET_INDEX'; + readonly currentIndex: string; + readonly indexMappings: IndexMapping; +} + +export interface UpdateIndexMappingsState extends PostInitState { + readonly controlState: 'UPDATE_INDEX_MAPPINGS'; + readonly additiveMappingChanges: SavedObjectsMappingProperties; +} + +export interface UpdateIndexMappingsWaitForTaskState extends PostInitState { + readonly controlState: 'UPDATE_INDEX_MAPPINGS_WAIT_FOR_TASK'; + readonly updateTargetMappingsTaskId: string; +} + +export interface UpdateMappingModelVersionState extends PostInitState { + readonly controlState: 'UPDATE_MAPPING_MODEL_VERSIONS'; +} + +export interface UpdateAliasesState extends PostInitState { + readonly controlState: 'UPDATE_ALIASES'; +} + /** Migration completed successfully */ export interface DoneState extends BaseState { readonly controlState: 'DONE'; @@ -31,7 +84,15 @@ export interface FatalState extends BaseState { readonly reason: string; } -export type State = InitState | DoneState | FatalState; +export type State = + | InitState + | DoneState + | FatalState + | CreateTargetIndexState + | UpdateIndexMappingsState + | UpdateIndexMappingsWaitForTaskState + | UpdateMappingModelVersionState + | UpdateAliasesState; export type AllControlStates = State['controlState']; @@ -44,6 +105,11 @@ export interface ControlStateMap { INIT: InitState; FATAL: FatalState; DONE: DoneState; + CREATE_TARGET_INDEX: CreateTargetIndexState; + UPDATE_INDEX_MAPPINGS: UpdateIndexMappingsState; + UPDATE_INDEX_MAPPINGS_WAIT_FOR_TASK: UpdateIndexMappingsWaitForTaskState; + UPDATE_MAPPING_MODEL_VERSIONS: UpdateMappingModelVersionState; + UPDATE_ALIASES: UpdateAliasesState; } /** diff --git a/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/zdt/test_helpers/context.ts b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/zdt/test_helpers/context.ts index ded69aa02e7de..faf9f9c89c9f5 100644 --- a/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/zdt/test_helpers/context.ts +++ b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/zdt/test_helpers/context.ts @@ -17,6 +17,7 @@ import type { MigratorContext } from '../context'; export type MockedMigratorContext = Omit & { elasticsearchClient: ElasticsearchClientMock; + typeRegistry: SavedObjectTypeRegistry; }; export const createContextMock = ( @@ -25,13 +26,19 @@ export const createContextMock = ( const typeRegistry = new SavedObjectTypeRegistry(); return { + kibanaVersion: '8.7.0', indexPrefix: '.kibana', types: ['foo', 'bar'], + typeModelVersions: { + foo: 1, + bar: 2, + }, elasticsearchClient: elasticsearchClientMock.createElasticsearchClient(), maxRetryAttempts: 15, migrationDocLinks: docLinksServiceMock.createSetupContract().links.kibanaUpgradeSavedObjects, typeRegistry, serializer: serializerMock.create(), + deletedTypes: ['deleted-type'], ...parts, }; }; diff --git a/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/zdt/test_helpers/index.ts b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/zdt/test_helpers/index.ts index 8b0c329317c03..5658828fc2e0c 100644 --- a/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/zdt/test_helpers/index.ts +++ b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/zdt/test_helpers/index.ts @@ -7,3 +7,5 @@ */ export { createContextMock, type MockedMigratorContext } from './context'; +export { createPostInitState } from './state'; +export { createType } from './saved_object_type'; diff --git a/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/zdt/test_helpers/saved_object_type.ts b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/zdt/test_helpers/saved_object_type.ts new file mode 100644 index 0000000000000..5f1f2ee8676f1 --- /dev/null +++ b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/zdt/test_helpers/saved_object_type.ts @@ -0,0 +1,17 @@ +/* + * 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 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import type { SavedObjectsType } from '@kbn/core-saved-objects-server'; + +export const createType = (parts: Partial): SavedObjectsType => ({ + name: 'test-type', + hidden: false, + namespaceType: 'single', + mappings: { properties: {} }, + ...parts, +}); diff --git a/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/zdt/test_helpers/state.ts b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/zdt/test_helpers/state.ts new file mode 100644 index 0000000000000..bd95881abbba4 --- /dev/null +++ b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/zdt/test_helpers/state.ts @@ -0,0 +1,21 @@ +/* + * 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 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { PostInitState } from '../state/types'; + +export const createPostInitState = (): PostInitState => ({ + controlState: 'INIT', + retryDelay: 0, + retryCount: 0, + logs: [], + currentIndex: '.kibana_1', + aliases: ['.kibana'], + aliasActions: [], + previousMappings: { properties: {} }, + currentIndexMeta: {}, +}); diff --git a/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/zdt/utils/build_index_mappings.test.ts b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/zdt/utils/build_index_mappings.test.ts new file mode 100644 index 0000000000000..f4536cf1c75b0 --- /dev/null +++ b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/zdt/utils/build_index_mappings.test.ts @@ -0,0 +1,83 @@ +/* + * 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 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { buildIndexMappings, buildIndexMeta } from './build_index_mappings'; +import { createType } from '../test_helpers'; + +const getTestTypes = () => { + const foo = createType({ + name: 'foo', + switchToModelVersionAt: '8.7.0', + modelVersions: { + 1: { modelChange: { type: 'expansion' } }, + 2: { modelChange: { type: 'expansion' } }, + }, + mappings: { properties: { fooField: { type: 'text' } } }, + }); + const bar = createType({ + name: 'bar', + switchToModelVersionAt: '8.7.0', + modelVersions: { + 1: { modelChange: { type: 'expansion' } }, + }, + mappings: { properties: { barField: { type: 'text' } } }, + }); + const dolly = createType({ + name: 'dolly', + switchToModelVersionAt: '8.7.0', + modelVersions: () => ({ + 1: { modelChange: { type: 'expansion' } }, + 2: { modelChange: { type: 'expansion' } }, + 3: { modelChange: { type: 'expansion' } }, + }), + mappings: { properties: { dollyField: { type: 'text' } } }, + }); + + return { foo, bar, dolly }; +}; + +describe('buildIndexMappings', () => { + it('builds the mappings used when creating a new index', () => { + const { foo, bar, dolly } = getTestTypes(); + const mappings = buildIndexMappings({ + types: [foo, bar, dolly], + }); + + expect(mappings).toEqual({ + dynamic: 'strict', + properties: expect.objectContaining({ + foo: foo.mappings, + bar: bar.mappings, + dolly: dolly.mappings, + }), + _meta: buildIndexMeta({ types: [foo, bar, dolly] }), + }); + }); +}); + +describe('buildIndexMeta', () => { + it('builds the _meta field value of the mapping', () => { + const { foo, bar, dolly } = getTestTypes(); + const meta = buildIndexMeta({ + types: [foo, bar, dolly], + }); + + expect(meta).toEqual({ + mappingVersions: { + foo: 2, + bar: 1, + dolly: 3, + }, + docVersions: { + foo: 2, + bar: 1, + dolly: 3, + }, + }); + }); +}); diff --git a/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/zdt/utils/build_index_mappings.ts b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/zdt/utils/build_index_mappings.ts new file mode 100644 index 0000000000000..6221221ab993c --- /dev/null +++ b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/zdt/utils/build_index_mappings.ts @@ -0,0 +1,57 @@ +/* + * 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 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { cloneDeep } from 'lodash'; +import type { SavedObjectsType } from '@kbn/core-saved-objects-server'; +import { + type IndexMapping, + type IndexMappingMeta, + getModelVersionMapForTypes, +} from '@kbn/core-saved-objects-base-server-internal'; +import { getBaseMappings, buildTypesMappings } from '../../core'; + +interface BuildIndexMappingsOpts { + types: SavedObjectsType[]; +} + +/** + * Build the mappings to use when creating a new index. + * + * @param types The list of all registered SO types. + */ +export const buildIndexMappings = ({ types }: BuildIndexMappingsOpts): IndexMapping => { + const mappings: IndexMapping = cloneDeep(getBaseMappings()); + const typeMappings = buildTypesMappings(types); + + mappings.properties = { + ...mappings.properties, + ...typeMappings, + }; + + mappings._meta = buildIndexMeta({ types }); + + return mappings; +}; + +interface BuildIndexMetaOpts { + types: SavedObjectsType[]; +} + +/** + * Build the mapping _meta field to use when creating a new index. + * + * @param types The list of all registered SO types. + */ +export const buildIndexMeta = ({ types }: BuildIndexMetaOpts): IndexMappingMeta => { + const modelVersions = getModelVersionMapForTypes(types); + + return { + mappingVersions: modelVersions, + docVersions: modelVersions, + }; +}; diff --git a/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/zdt/utils/check_version_compatibility.test.mocks.ts b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/zdt/utils/check_version_compatibility.test.mocks.ts new file mode 100644 index 0000000000000..a0e03552ed946 --- /dev/null +++ b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/zdt/utils/check_version_compatibility.test.mocks.ts @@ -0,0 +1,21 @@ +/* + * 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 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +export const getModelVersionsFromMappingsMock = jest.fn(); +export const compareModelVersionsMock = jest.fn(); +export const getModelVersionMapForTypesMock = jest.fn(); + +jest.doMock('@kbn/core-saved-objects-base-server-internal', () => { + const actual = jest.requireActual('@kbn/core-saved-objects-base-server-internal'); + return { + ...actual, + getModelVersionsFromMappings: getModelVersionsFromMappingsMock, + compareModelVersions: compareModelVersionsMock, + getModelVersionMapForTypes: getModelVersionMapForTypesMock, + }; +}); diff --git a/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/zdt/utils/check_version_compatibility.test.ts b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/zdt/utils/check_version_compatibility.test.ts new file mode 100644 index 0000000000000..6ad12656229fc --- /dev/null +++ b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/zdt/utils/check_version_compatibility.test.ts @@ -0,0 +1,110 @@ +/* + * 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 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { + compareModelVersionsMock, + getModelVersionsFromMappingsMock, + getModelVersionMapForTypesMock, +} from './check_version_compatibility.test.mocks'; +import type { SavedObjectsType } from '@kbn/core-saved-objects-server'; +import type { + IndexMapping, + ModelVersionMap, + CompareModelVersionResult, +} from '@kbn/core-saved-objects-base-server-internal'; +import { checkVersionCompatibility } from './check_version_compatibility'; +import { createType } from '../test_helpers'; + +describe('checkVersionCompatibility', () => { + const deletedTypes = ['some-deleted-type']; + + let types: SavedObjectsType[]; + let mappings: IndexMapping; + + beforeEach(() => { + compareModelVersionsMock.mockReset().mockReturnValue({}); + getModelVersionsFromMappingsMock.mockReset().mockReturnValue({}); + getModelVersionMapForTypesMock.mockReset().mockReturnValue({ status: 'equal' }); + + types = [createType({ name: 'foo' }), createType({ name: 'bar' })]; + + mappings = { + properties: { foo: { type: 'boolean' } }, + }; + }); + + it('calls getModelVersionMapForTypes with the correct parameters', () => { + checkVersionCompatibility({ + types, + mappings, + source: 'mappingVersions', + deletedTypes, + }); + + expect(getModelVersionMapForTypesMock).toHaveBeenCalledTimes(1); + expect(getModelVersionMapForTypesMock).toHaveBeenCalledWith(types); + }); + + it('calls getModelVersionsFromMappings with the correct parameters', () => { + checkVersionCompatibility({ + types, + mappings, + source: 'mappingVersions', + deletedTypes, + }); + + expect(getModelVersionsFromMappingsMock).toHaveBeenCalledTimes(1); + expect(getModelVersionsFromMappingsMock).toHaveBeenCalledWith({ + mappings, + source: 'mappingVersions', + }); + }); + + it('calls compareModelVersions with the correct parameters', () => { + const appVersions: ModelVersionMap = { foo: 2, bar: 2 }; + const indexVersions: ModelVersionMap = { foo: 1, bar: 1 }; + + getModelVersionMapForTypesMock.mockReturnValue(appVersions); + getModelVersionsFromMappingsMock.mockReturnValue(indexVersions); + + checkVersionCompatibility({ + types, + mappings, + source: 'mappingVersions', + deletedTypes, + }); + + expect(compareModelVersionsMock).toHaveBeenCalledTimes(1); + expect(compareModelVersionsMock).toHaveBeenCalledWith({ + appVersions, + indexVersions, + deletedTypes, + }); + }); + + it('returns the result of the compareModelVersions call', () => { + const expected: CompareModelVersionResult = { + status: 'lesser', + details: { + greater: [], + lesser: [], + equal: [], + }, + }; + compareModelVersionsMock.mockReturnValue(expected); + + const result = checkVersionCompatibility({ + types, + mappings, + source: 'mappingVersions', + deletedTypes, + }); + + expect(result).toEqual(expected); + }); +}); diff --git a/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/zdt/utils/check_version_compatibility.ts b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/zdt/utils/check_version_compatibility.ts new file mode 100644 index 0000000000000..4499ce419d34a --- /dev/null +++ b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/zdt/utils/check_version_compatibility.ts @@ -0,0 +1,37 @@ +/* + * 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 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import type { SavedObjectsType } from '@kbn/core-saved-objects-server'; +import { + getModelVersionsFromMappings, + compareModelVersions, + getModelVersionMapForTypes, + type IndexMapping, + type CompareModelVersionResult, +} from '@kbn/core-saved-objects-base-server-internal'; + +interface CheckVersionCompatibilityOpts { + mappings: IndexMapping; + types: SavedObjectsType[]; + source: 'docVersions' | 'mappingVersions'; + deletedTypes: string[]; +} + +export const checkVersionCompatibility = ({ + mappings, + types, + source, + deletedTypes, +}: CheckVersionCompatibilityOpts): CompareModelVersionResult => { + const appVersions = getModelVersionMapForTypes(types); + const indexVersions = getModelVersionsFromMappings({ mappings, source }); + if (!indexVersions) { + throw new Error(`Cannot check version: ${source} not present in the mapping meta`); + } + return compareModelVersions({ appVersions, indexVersions, deletedTypes }); +}; diff --git a/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/zdt/utils/generate_additive_mapping_diff.test.ts b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/zdt/utils/generate_additive_mapping_diff.test.ts new file mode 100644 index 0000000000000..ce0cba20f427c --- /dev/null +++ b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/zdt/utils/generate_additive_mapping_diff.test.ts @@ -0,0 +1,139 @@ +/* + * 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 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import type { IndexMappingMeta } from '@kbn/core-saved-objects-base-server-internal'; +import { generateAdditiveMappingDiff } from './generate_additive_mapping_diff'; +import { createType } from '../test_helpers'; + +describe('generateAdditiveMappingDiff', () => { + const deletedTypes = ['deletedType']; + + const getTypes = () => { + const foo = createType({ + name: 'foo', + modelVersions: { + 1: { modelChange: { type: 'expansion' } }, + 2: { modelChange: { type: 'expansion' } }, + }, + mappings: { properties: { fooProp: { type: 'text' } } }, + }); + const bar = createType({ + name: 'bar', + modelVersions: { + 1: { modelChange: { type: 'expansion' } }, + 2: { modelChange: { type: 'expansion' } }, + }, + mappings: { properties: { barProp: { type: 'text' } } }, + }); + + return { foo, bar }; + }; + + it('aggregates the mappings of the types with versions higher than in the index', () => { + const { foo, bar } = getTypes(); + const types = [foo, bar]; + const meta: IndexMappingMeta = { + mappingVersions: { + foo: 1, + bar: 1, + }, + }; + + const addedMappings = generateAdditiveMappingDiff({ + types, + meta, + deletedTypes, + }); + + expect(addedMappings).toEqual({ + foo: foo.mappings, + bar: bar.mappings, + }); + }); + + it('ignores mapping from types already up to date', () => { + const { foo, bar } = getTypes(); + const types = [foo, bar]; + const meta: IndexMappingMeta = { + mappingVersions: { + foo: 1, + bar: 2, + }, + }; + + const addedMappings = generateAdditiveMappingDiff({ + types, + meta, + deletedTypes, + }); + + expect(addedMappings).toEqual({ + foo: foo.mappings, + }); + }); + + it('ignores deleted types', () => { + const { foo, bar } = getTypes(); + const types = [foo, bar]; + const meta: IndexMappingMeta = { + mappingVersions: { + foo: 1, + bar: 1, + deletedType: 42, + }, + }; + + const addedMappings = generateAdditiveMappingDiff({ + types, + meta, + deletedTypes, + }); + + expect(addedMappings).toEqual({ + foo: foo.mappings, + bar: bar.mappings, + }); + }); + + it('throws an error in case of version conflict', () => { + const { foo, bar } = getTypes(); + const types = [foo, bar]; + const meta: IndexMappingMeta = { + mappingVersions: { + foo: 1, + bar: 3, + }, + }; + + expect(() => + generateAdditiveMappingDiff({ + types, + meta, + deletedTypes, + }) + ).toThrowErrorMatchingInlineSnapshot( + `"Cannot generate model version difference: conflict between versions"` + ); + }); + + it('throws an error if mappingVersions is not present on the index meta', () => { + const { foo, bar } = getTypes(); + const types = [foo, bar]; + const meta: IndexMappingMeta = {}; + + expect(() => + generateAdditiveMappingDiff({ + types, + meta, + deletedTypes, + }) + ).toThrowErrorMatchingInlineSnapshot( + `"Cannot generate additive mapping diff: mappingVersions not present on index meta"` + ); + }); +}); diff --git a/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/zdt/utils/generate_additive_mapping_diff.ts b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/zdt/utils/generate_additive_mapping_diff.ts new file mode 100644 index 0000000000000..f23b1e84a87ea --- /dev/null +++ b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/zdt/utils/generate_additive_mapping_diff.ts @@ -0,0 +1,69 @@ +/* + * 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 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import type { + SavedObjectsType, + SavedObjectsMappingProperties, +} from '@kbn/core-saved-objects-server'; +import { + IndexMappingMeta, + getModelVersionsFromMappingMeta, + getModelVersionMapForTypes, + getModelVersionDelta, +} from '@kbn/core-saved-objects-base-server-internal'; + +interface GenerateAdditiveMappingsDiffOpts { + types: SavedObjectsType[]; + meta: IndexMappingMeta; + deletedTypes: string[]; +} + +/** + * Generates the additive mapping diff we will need to update the index mapping with. + * + * @param types The types to generate the diff for + * @param meta The meta field of the index we're migrating + * @param deletedTypes The list of deleted types to ignore during diff/comparison + */ +export const generateAdditiveMappingDiff = ({ + types, + meta, + deletedTypes, +}: GenerateAdditiveMappingsDiffOpts): SavedObjectsMappingProperties => { + const typeVersions = getModelVersionMapForTypes(types); + const mappingVersion = getModelVersionsFromMappingMeta({ meta, source: 'mappingVersions' }); + if (!mappingVersion) { + // should never occur given we checked previously in the flow but better safe than sorry. + throw new Error( + 'Cannot generate additive mapping diff: mappingVersions not present on index meta' + ); + } + + const delta = getModelVersionDelta({ + currentVersions: mappingVersion, + targetVersions: typeVersions, + deletedTypes, + }); + const typeMap = types.reduce>((map, type) => { + map[type.name] = type; + return map; + }, {}); + + // TODO: later we will want to generate the proper diff from `SavedObjectsModelExpansionChange.addedMappings` + // for this first implementation this is acceptable given we only allow compatible mapping changes anyway. + // we may want to implement the proper logic before this get used by real (non-test) type owners. + + const changedTypes = delta.diff.map((diff) => diff.name); + + const addedMappings: SavedObjectsMappingProperties = {}; + changedTypes.forEach((type) => { + addedMappings[type] = typeMap[type].mappings; + }); + + return addedMappings; +}; diff --git a/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/zdt/utils/get_alias_actions.test.ts b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/zdt/utils/get_alias_actions.test.ts new file mode 100644 index 0000000000000..42d7bcf27d11a --- /dev/null +++ b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/zdt/utils/get_alias_actions.test.ts @@ -0,0 +1,86 @@ +/* + * 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 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { getAliasActions } from './get_alias_actions'; + +describe('getAliasActions', () => { + it('creates the global and version aliases', () => { + const actions = getAliasActions({ + indexPrefix: '.kibana', + currentIndex: '.kibana_1', + existingAliases: [], + kibanaVersion: '8.7.0', + }); + + expect(actions).toEqual([ + { add: { alias: '.kibana', index: '.kibana_1' } }, + { add: { alias: '.kibana_8.7.0', index: '.kibana_1' } }, + ]); + }); + + it('does not create the version alias when already present', () => { + const actions = getAliasActions({ + indexPrefix: '.kibana', + currentIndex: '.kibana_1', + existingAliases: ['.kibana_8.7.0'], + kibanaVersion: '8.7.0', + }); + + expect(actions).toEqual([{ add: { alias: '.kibana', index: '.kibana_1' } }]); + }); + + it('does not create the global alias when already present', () => { + const actions = getAliasActions({ + indexPrefix: '.kibana', + currentIndex: '.kibana_1', + existingAliases: ['.kibana'], + kibanaVersion: '8.7.0', + }); + + expect(actions).toEqual([{ add: { alias: '.kibana_8.7.0', index: '.kibana_1' } }]); + }); + + it('creates nothing when both aliases are present', () => { + const actions = getAliasActions({ + indexPrefix: '.kibana', + currentIndex: '.kibana_1', + existingAliases: ['.kibana', '.kibana_8.7.0'], + kibanaVersion: '8.7.0', + }); + + expect(actions).toEqual([]); + }); + + it('ignores other aliases', () => { + const actions = getAliasActions({ + indexPrefix: '.kibana', + currentIndex: '.kibana_1', + existingAliases: ['.kibana_8.6.0', '.kibana_old'], + kibanaVersion: '8.7.0', + }); + + expect(actions).toEqual([ + { add: { alias: '.kibana', index: '.kibana_1' } }, + { add: { alias: '.kibana_8.7.0', index: '.kibana_1' } }, + ]); + }); + + it('accepts other prefixes', () => { + const actions = getAliasActions({ + indexPrefix: '.kibana_task_manager', + currentIndex: '.kibana_task_manager_2', + existingAliases: [], + kibanaVersion: '8.7.0', + }); + + expect(actions).toEqual([ + { add: { alias: '.kibana_task_manager', index: '.kibana_task_manager_2' } }, + { add: { alias: '.kibana_task_manager_8.7.0', index: '.kibana_task_manager_2' } }, + ]); + }); +}); diff --git a/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/zdt/utils/get_alias_actions.ts b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/zdt/utils/get_alias_actions.ts new file mode 100644 index 0000000000000..185b454987b45 --- /dev/null +++ b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/zdt/utils/get_alias_actions.ts @@ -0,0 +1,41 @@ +/* + * 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 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import type { AliasAction } from '../../actions'; + +interface GetAliasActionOpts { + indexPrefix: string; + currentIndex: string; + existingAliases: string[]; + kibanaVersion: string; +} + +/** + * Build the list of alias actions to perform, depending on the current state of the cluster. + */ +export const getAliasActions = ({ + indexPrefix, + currentIndex, + existingAliases, + kibanaVersion, +}: GetAliasActionOpts): AliasAction[] => { + const actions: AliasAction[] = []; + + const globalAlias = indexPrefix; + const versionAlias = `${indexPrefix}_${kibanaVersion}`; + const allAliases = [globalAlias, versionAlias]; + allAliases.forEach((alias) => { + if (!existingAliases.includes(alias)) { + actions.push({ + add: { index: currentIndex, alias }, + }); + } + }); + + return actions; +}; diff --git a/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/zdt/utils/get_current_index.test.ts b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/zdt/utils/get_current_index.test.ts new file mode 100644 index 0000000000000..d6db500dca633 --- /dev/null +++ b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/zdt/utils/get_current_index.test.ts @@ -0,0 +1,48 @@ +/* + * 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 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { getCurrentIndex } from './get_current_index'; +import type { FetchIndexResponse } from '../../actions'; + +describe('getCurrentIndex', () => { + const createIndexResponse = (...indexNames: string[]): FetchIndexResponse => { + return indexNames.reduce((resp, indexName) => { + resp[indexName] = { aliases: {}, mappings: { properties: {} }, settings: {} }; + return resp; + }, {}); + }; + + it('returns the highest numbered index matching the index prefix', () => { + const resp = createIndexResponse('.kibana_1', '.kibana_2'); + expect(getCurrentIndex(resp, '.kibana')).toEqual('.kibana_2'); + }); + + it('ignores other indices', () => { + const resp = createIndexResponse('.kibana_1', '.kibana_2', '.foo_3'); + expect(getCurrentIndex(resp, '.kibana')).toEqual('.kibana_2'); + }); + + it('ignores other indices including the prefix', () => { + const resp = createIndexResponse('.kibana_1', '.kibana_2', '.kibana_task_manager_3'); + expect(getCurrentIndex(resp, '.kibana')).toEqual('.kibana_2'); + }); + + it('ignores other indices including a subpart of the prefix', () => { + const resp = createIndexResponse( + '.kibana_3', + '.kibana_task_manager_1', + '.kibana_task_manager_2' + ); + expect(getCurrentIndex(resp, '.kibana_task_manager')).toEqual('.kibana_task_manager_2'); + }); + + it('returns undefined if no indices match', () => { + const resp = createIndexResponse('.kibana_task_manager_1', '.kibana_task_manager_2'); + expect(getCurrentIndex(resp, '.kibana')).toBeUndefined(); + }); +}); diff --git a/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/zdt/utils/get_current_index.ts b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/zdt/utils/get_current_index.ts new file mode 100644 index 0000000000000..59eea5c550804 --- /dev/null +++ b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/zdt/utils/get_current_index.ts @@ -0,0 +1,28 @@ +/* + * 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 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { escapeRegExp } from 'lodash'; +import type { FetchIndexResponse } from '../../actions'; + +export const getCurrentIndex = ( + indices: FetchIndexResponse, + indexPrefix: string +): string | undefined => { + const matcher = new RegExp(`^${escapeRegExp(indexPrefix)}[_](?\\d+)$`); + + let lastCount = -1; + Object.keys(indices).forEach((indexName) => { + const match = matcher.exec(indexName); + if (match && match.groups?.counter) { + const suffix = parseInt(match.groups.counter, 10); + lastCount = Math.max(lastCount, suffix); + } + }); + + return lastCount === -1 ? undefined : `${indexPrefix}_${lastCount}`; +}; diff --git a/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/zdt/utils/index.ts b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/zdt/utils/index.ts index f96ea531e460d..ebc22e623f600 100644 --- a/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/zdt/utils/index.ts +++ b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/zdt/utils/index.ts @@ -7,3 +7,8 @@ */ export { buildMigratorConfigs, type MigratorConfig } from './get_migrator_configs'; +export { getCurrentIndex } from './get_current_index'; +export { checkVersionCompatibility } from './check_version_compatibility'; +export { buildIndexMappings, buildIndexMeta } from './build_index_mappings'; +export { getAliasActions } from './get_alias_actions'; +export { generateAdditiveMappingDiff } from './generate_additive_mapping_diff'; diff --git a/packages/core/saved-objects/core-saved-objects-server-internal/src/routes/bulk_create.ts b/packages/core/saved-objects/core-saved-objects-server-internal/src/routes/bulk_create.ts index c212dae936aed..d66090fe0ebdd 100644 --- a/packages/core/saved-objects/core-saved-objects-server-internal/src/routes/bulk_create.ts +++ b/packages/core/saved-objects/core-saved-objects-server-internal/src/routes/bulk_create.ts @@ -11,7 +11,11 @@ import { SavedObjectConfig } from '@kbn/core-saved-objects-base-server-internal' import type { InternalCoreUsageDataSetup } from '@kbn/core-usage-data-base-server-internal'; import type { Logger } from '@kbn/logging'; import type { InternalSavedObjectRouter } from '../internal_types'; -import { catchAndReturnBoomErrors, throwIfAnyTypeNotVisibleByAPI } from './utils'; +import { + catchAndReturnBoomErrors, + logWarnOnExternalRequest, + throwIfAnyTypeNotVisibleByAPI, +} from './utils'; interface RouteDependencies { config: SavedObjectConfig; @@ -54,9 +58,12 @@ export const registerBulkCreateRoute = ( }, }, catchAndReturnBoomErrors(async (context, req, res) => { - logger.warn( - "The bulk create saved object API '/api/saved_objects/_bulk_create' is deprecated." - ); + logWarnOnExternalRequest({ + method: 'post', + path: '/api/saved_objects/_bulk_create', + req, + logger, + }); const { overwrite } = req.query; const usageStatsClient = coreUsageData.getClient(); diff --git a/packages/core/saved-objects/core-saved-objects-server-internal/src/routes/bulk_delete.ts b/packages/core/saved-objects/core-saved-objects-server-internal/src/routes/bulk_delete.ts index d0e80f75dc906..1d3c52a581da5 100644 --- a/packages/core/saved-objects/core-saved-objects-server-internal/src/routes/bulk_delete.ts +++ b/packages/core/saved-objects/core-saved-objects-server-internal/src/routes/bulk_delete.ts @@ -11,7 +11,11 @@ import { SavedObjectConfig } from '@kbn/core-saved-objects-base-server-internal' import type { InternalCoreUsageDataSetup } from '@kbn/core-usage-data-base-server-internal'; import type { Logger } from '@kbn/logging'; import type { InternalSavedObjectRouter } from '../internal_types'; -import { catchAndReturnBoomErrors, throwIfAnyTypeNotVisibleByAPI } from './utils'; +import { + catchAndReturnBoomErrors, + logWarnOnExternalRequest, + throwIfAnyTypeNotVisibleByAPI, +} from './utils'; interface RouteDependencies { config: SavedObjectConfig; @@ -40,9 +44,12 @@ export const registerBulkDeleteRoute = ( }, }, catchAndReturnBoomErrors(async (context, req, res) => { - logger.warn( - "The bulk update saved object API '/api/saved_objects/_bulk_update' is deprecated." - ); + logWarnOnExternalRequest({ + method: 'post', + path: '/api/saved_objects/_bulk_delete', + req, + logger, + }); const { force } = req.query; const usageStatsClient = coreUsageData.getClient(); usageStatsClient.incrementSavedObjectsBulkDelete({ request: req }).catch(() => {}); diff --git a/packages/core/saved-objects/core-saved-objects-server-internal/src/routes/bulk_get.ts b/packages/core/saved-objects/core-saved-objects-server-internal/src/routes/bulk_get.ts index c2e2bb6c6ec1f..b9bc66cc2f693 100644 --- a/packages/core/saved-objects/core-saved-objects-server-internal/src/routes/bulk_get.ts +++ b/packages/core/saved-objects/core-saved-objects-server-internal/src/routes/bulk_get.ts @@ -11,7 +11,11 @@ import { SavedObjectConfig } from '@kbn/core-saved-objects-base-server-internal' import type { InternalCoreUsageDataSetup } from '@kbn/core-usage-data-base-server-internal'; import type { Logger } from '@kbn/logging'; import type { InternalSavedObjectRouter } from '../internal_types'; -import { catchAndReturnBoomErrors, throwIfAnyTypeNotVisibleByAPI } from './utils'; +import { + catchAndReturnBoomErrors, + logWarnOnExternalRequest, + throwIfAnyTypeNotVisibleByAPI, +} from './utils'; interface RouteDependencies { config: SavedObjectConfig; @@ -39,7 +43,12 @@ export const registerBulkGetRoute = ( }, }, catchAndReturnBoomErrors(async (context, req, res) => { - logger.warn("The bulk get saved object API '/api/saved_objects/_bulk_get' is deprecated."); + logWarnOnExternalRequest({ + method: 'post', + path: '/api/saved_objects/_bulk_get', + req, + logger, + }); const usageStatsClient = coreUsageData.getClient(); usageStatsClient.incrementSavedObjectsBulkGet({ request: req }).catch(() => {}); diff --git a/packages/core/saved-objects/core-saved-objects-server-internal/src/routes/bulk_resolve.ts b/packages/core/saved-objects/core-saved-objects-server-internal/src/routes/bulk_resolve.ts index 231eb108b50e1..c5b409b2b771f 100644 --- a/packages/core/saved-objects/core-saved-objects-server-internal/src/routes/bulk_resolve.ts +++ b/packages/core/saved-objects/core-saved-objects-server-internal/src/routes/bulk_resolve.ts @@ -11,7 +11,11 @@ import { SavedObjectConfig } from '@kbn/core-saved-objects-base-server-internal' import type { InternalCoreUsageDataSetup } from '@kbn/core-usage-data-base-server-internal'; import type { Logger } from '@kbn/logging'; import type { InternalSavedObjectRouter } from '../internal_types'; -import { catchAndReturnBoomErrors, throwIfAnyTypeNotVisibleByAPI } from './utils'; +import { + catchAndReturnBoomErrors, + logWarnOnExternalRequest, + throwIfAnyTypeNotVisibleByAPI, +} from './utils'; interface RouteDependencies { config: SavedObjectConfig; @@ -37,9 +41,12 @@ export const registerBulkResolveRoute = ( }, }, catchAndReturnBoomErrors(async (context, req, res) => { - logger.warn( - "The bulk resolve saved object API '/api/saved_objects/_bulk_resolve' is deprecated." - ); + logWarnOnExternalRequest({ + method: 'post', + path: '/api/saved_objects/_bulk_resolve', + req, + logger, + }); const usageStatsClient = coreUsageData.getClient(); usageStatsClient.incrementSavedObjectsBulkResolve({ request: req }).catch(() => {}); diff --git a/packages/core/saved-objects/core-saved-objects-server-internal/src/routes/bulk_update.ts b/packages/core/saved-objects/core-saved-objects-server-internal/src/routes/bulk_update.ts index 699a997074bc7..4aacc320e9ee6 100644 --- a/packages/core/saved-objects/core-saved-objects-server-internal/src/routes/bulk_update.ts +++ b/packages/core/saved-objects/core-saved-objects-server-internal/src/routes/bulk_update.ts @@ -11,7 +11,11 @@ import { SavedObjectConfig } from '@kbn/core-saved-objects-base-server-internal' import type { InternalCoreUsageDataSetup } from '@kbn/core-usage-data-base-server-internal'; import type { Logger } from '@kbn/logging'; import type { InternalSavedObjectRouter } from '../internal_types'; -import { catchAndReturnBoomErrors, throwIfAnyTypeNotVisibleByAPI } from './utils'; +import { + catchAndReturnBoomErrors, + logWarnOnExternalRequest, + throwIfAnyTypeNotVisibleByAPI, +} from './utils'; interface RouteDependencies { config: SavedObjectConfig; @@ -49,9 +53,12 @@ export const registerBulkUpdateRoute = ( }, }, catchAndReturnBoomErrors(async (context, req, res) => { - logger.warn( - "The bulk update saved object API '/api/saved_objects/_bulk_update' is deprecated." - ); + logWarnOnExternalRequest({ + method: 'put', + path: '/api/saved_objects/_bulk_update', + req, + logger, + }); const usageStatsClient = coreUsageData.getClient(); usageStatsClient.incrementSavedObjectsBulkUpdate({ request: req }).catch(() => {}); diff --git a/packages/core/saved-objects/core-saved-objects-server-internal/src/routes/create.ts b/packages/core/saved-objects/core-saved-objects-server-internal/src/routes/create.ts index 82533c8979636..6366105067cb1 100644 --- a/packages/core/saved-objects/core-saved-objects-server-internal/src/routes/create.ts +++ b/packages/core/saved-objects/core-saved-objects-server-internal/src/routes/create.ts @@ -11,7 +11,11 @@ import { SavedObjectConfig } from '@kbn/core-saved-objects-base-server-internal' import type { InternalCoreUsageDataSetup } from '@kbn/core-usage-data-base-server-internal'; import type { Logger } from '@kbn/logging'; import type { InternalSavedObjectRouter } from '../internal_types'; -import { catchAndReturnBoomErrors, throwIfTypeNotVisibleByAPI } from './utils'; +import { + catchAndReturnBoomErrors, + logWarnOnExternalRequest, + throwIfTypeNotVisibleByAPI, +} from './utils'; interface RouteDependencies { config: SavedObjectConfig; @@ -53,7 +57,12 @@ export const registerCreateRoute = ( }, }, catchAndReturnBoomErrors(async (context, req, res) => { - logger.warn("The create saved object API '/api/saved_objects/{type}/{id}' is deprecated."); + logWarnOnExternalRequest({ + method: 'post', + path: '/api/saved_objects/{type}/{id?}', + req, + logger, + }); const { type, id } = req.params; const { overwrite } = req.query; const { attributes, migrationVersion, coreMigrationVersion, references, initialNamespaces } = diff --git a/packages/core/saved-objects/core-saved-objects-server-internal/src/routes/delete.ts b/packages/core/saved-objects/core-saved-objects-server-internal/src/routes/delete.ts index 4f341e027e155..a36c14b7c1672 100644 --- a/packages/core/saved-objects/core-saved-objects-server-internal/src/routes/delete.ts +++ b/packages/core/saved-objects/core-saved-objects-server-internal/src/routes/delete.ts @@ -11,7 +11,11 @@ import { SavedObjectConfig } from '@kbn/core-saved-objects-base-server-internal' import type { InternalCoreUsageDataSetup } from '@kbn/core-usage-data-base-server-internal'; import type { Logger } from '@kbn/logging'; import type { InternalSavedObjectRouter } from '../internal_types'; -import { catchAndReturnBoomErrors, throwIfTypeNotVisibleByAPI } from './utils'; +import { + catchAndReturnBoomErrors, + logWarnOnExternalRequest, + throwIfTypeNotVisibleByAPI, +} from './utils'; interface RouteDependencies { config: SavedObjectConfig; @@ -38,7 +42,12 @@ export const registerDeleteRoute = ( }, }, catchAndReturnBoomErrors(async (context, req, res) => { - logger.warn("The delete saved object API '/api/saved_objects/{type}/{id}' is deprecated."); + logWarnOnExternalRequest({ + method: 'delete', + path: '/api/saved_objects/{type}/{id}', + req, + logger, + }); const { type, id } = req.params; const { force } = req.query; const { getClient, typeRegistry } = (await context.core).savedObjects; diff --git a/packages/core/saved-objects/core-saved-objects-server-internal/src/routes/find.ts b/packages/core/saved-objects/core-saved-objects-server-internal/src/routes/find.ts index 5768c04e40929..9e8def13dbae1 100644 --- a/packages/core/saved-objects/core-saved-objects-server-internal/src/routes/find.ts +++ b/packages/core/saved-objects/core-saved-objects-server-internal/src/routes/find.ts @@ -12,7 +12,7 @@ import type { InternalCoreUsageDataSetup } from '@kbn/core-usage-data-base-serve import type { Logger } from '@kbn/logging'; import type { InternalSavedObjectRouter } from '../internal_types'; import { catchAndReturnBoomErrors, throwOnHttpHiddenTypes } from './utils'; - +import { logWarnOnExternalRequest } from './utils'; interface RouteDependencies { config: SavedObjectConfig; coreUsageData: InternalCoreUsageDataSetup; @@ -63,7 +63,12 @@ export const registerFindRoute = ( }, }, catchAndReturnBoomErrors(async (context, req, res) => { - logger.warn("The find saved object API '/api/saved_objects/_find' is deprecated."); + logWarnOnExternalRequest({ + method: 'get', + path: '/api/saved_objects/_find', + req, + logger, + }); const query = req.query; const namespaces = diff --git a/packages/core/saved-objects/core-saved-objects-server-internal/src/routes/get.ts b/packages/core/saved-objects/core-saved-objects-server-internal/src/routes/get.ts index 62f7c88a12b98..ed24f730c05ef 100644 --- a/packages/core/saved-objects/core-saved-objects-server-internal/src/routes/get.ts +++ b/packages/core/saved-objects/core-saved-objects-server-internal/src/routes/get.ts @@ -11,7 +11,11 @@ import { SavedObjectConfig } from '@kbn/core-saved-objects-base-server-internal' import type { InternalCoreUsageDataSetup } from '@kbn/core-usage-data-base-server-internal'; import type { Logger } from '@kbn/logging'; import type { InternalSavedObjectRouter } from '../internal_types'; -import { catchAndReturnBoomErrors, throwIfTypeNotVisibleByAPI } from './utils'; +import { + catchAndReturnBoomErrors, + logWarnOnExternalRequest, + throwIfTypeNotVisibleByAPI, +} from './utils'; interface RouteDependencies { config: SavedObjectConfig; @@ -35,7 +39,12 @@ export const registerGetRoute = ( }, }, catchAndReturnBoomErrors(async (context, req, res) => { - logger.warn("The get saved object API '/api/saved_objects/{type}/{id}' is deprecated."); + logWarnOnExternalRequest({ + method: 'get', + path: '/api/saved_objects/{type}/{id}', + req, + logger, + }); const { type, id } = req.params; const usageStatsClient = coreUsageData.getClient(); diff --git a/packages/core/saved-objects/core-saved-objects-server-internal/src/routes/index.ts b/packages/core/saved-objects/core-saved-objects-server-internal/src/routes/index.ts index 3fc2ef2afd51c..158ac0e0a4d41 100644 --- a/packages/core/saved-objects/core-saved-objects-server-internal/src/routes/index.ts +++ b/packages/core/saved-objects/core-saved-objects-server-internal/src/routes/index.ts @@ -52,7 +52,6 @@ export function registerRoutes({ }) { const router = http.createRouter('/api/saved_objects/'); - registerGetRoute(router, { config, coreUsageData, logger }); registerResolveRoute(router, { config, coreUsageData, logger }); registerCreateRoute(router, { config, coreUsageData, logger }); diff --git a/packages/core/saved-objects/core-saved-objects-server-internal/src/routes/resolve.ts b/packages/core/saved-objects/core-saved-objects-server-internal/src/routes/resolve.ts index 527c7f13e5500..5115bd8987829 100644 --- a/packages/core/saved-objects/core-saved-objects-server-internal/src/routes/resolve.ts +++ b/packages/core/saved-objects/core-saved-objects-server-internal/src/routes/resolve.ts @@ -11,7 +11,7 @@ import { SavedObjectConfig } from '@kbn/core-saved-objects-base-server-internal' import type { InternalCoreUsageDataSetup } from '@kbn/core-usage-data-base-server-internal'; import type { Logger } from '@kbn/logging'; import type { InternalSavedObjectRouter } from '../internal_types'; -import { throwIfTypeNotVisibleByAPI } from './utils'; +import { throwIfTypeNotVisibleByAPI, logWarnOnExternalRequest } from './utils'; interface RouteDependencies { config: SavedObjectConfig; @@ -35,9 +35,12 @@ export const registerResolveRoute = ( }, }, router.handleLegacyErrors(async (context, req, res) => { - logger.warn( - "The resolve saved object API '/api/saved_objects/resolve/{type}/{id}' is deprecated." - ); + logWarnOnExternalRequest({ + method: 'get', + path: '/api/saved_objects/resolve/{type}/{id}', + req, + logger, + }); const { type, id } = req.params; const { savedObjects } = await context.core; diff --git a/packages/core/saved-objects/core-saved-objects-server-internal/src/routes/update.ts b/packages/core/saved-objects/core-saved-objects-server-internal/src/routes/update.ts index 327f856d580fe..11a6545bfd612 100644 --- a/packages/core/saved-objects/core-saved-objects-server-internal/src/routes/update.ts +++ b/packages/core/saved-objects/core-saved-objects-server-internal/src/routes/update.ts @@ -12,7 +12,11 @@ import type { Logger } from '@kbn/logging'; import { SavedObjectConfig } from '@kbn/core-saved-objects-base-server-internal'; import type { InternalCoreUsageDataSetup } from '@kbn/core-usage-data-base-server-internal'; import type { InternalSavedObjectRouter } from '../internal_types'; -import { catchAndReturnBoomErrors, throwIfTypeNotVisibleByAPI } from './utils'; +import { + catchAndReturnBoomErrors, + logWarnOnExternalRequest, + throwIfTypeNotVisibleByAPI, +} from './utils'; interface RouteDependencies { config: SavedObjectConfig; @@ -50,7 +54,12 @@ export const registerUpdateRoute = ( }, }, catchAndReturnBoomErrors(async (context, req, res) => { - logger.warn("The update saved object API '/api/saved_objects/{type}/{id}' is deprecated."); + logWarnOnExternalRequest({ + method: 'get', + path: '/api/saved_objects/{type}/{id}', + req, + logger, + }); const { type, id } = req.params; const { attributes, version, references, upsert } = req.body; const options: SavedObjectsUpdateOptions = { version, references, upsert }; diff --git a/packages/core/saved-objects/core-saved-objects-server-internal/src/routes/utils.test.ts b/packages/core/saved-objects/core-saved-objects-server-internal/src/routes/utils.test.ts index 6d0b78c58837e..43c633290efcf 100644 --- a/packages/core/saved-objects/core-saved-objects-server-internal/src/routes/utils.test.ts +++ b/packages/core/saved-objects/core-saved-objects-server-internal/src/routes/utils.test.ts @@ -14,6 +14,7 @@ import { throwOnGloballyHiddenTypes, throwIfTypeNotVisibleByAPI, throwIfAnyTypeNotVisibleByAPI, + logWarnOnExternalRequest, } from './utils'; import { Readable } from 'stream'; import { createPromiseFromStreams, createConcatStream } from '@kbn/utils'; @@ -27,6 +28,8 @@ import type { } from '@kbn/core-http-server'; import { kibanaResponseFactory } from '@kbn/core-http-router-server-internal'; import { typeRegistryInstanceMock } from '../saved_objects_service.test.mocks'; +import { httpServerMock } from '@kbn/core-http-server-mocks'; +import { loggerMock, type MockedLogger } from '@kbn/logging-mocks'; async function readStreamToCompletion(stream: Readable) { return createPromiseFromStreams([stream, createConcatStream([])]); @@ -341,3 +344,60 @@ describe('throwIfAnyTypeNotVisibleByAPI', () => { expect(() => throwIfAnyTypeNotVisibleByAPI(['config'], registry)).not.toThrowError(); }); }); + +describe('logWarnOnExternalRequest', () => { + let logger: MockedLogger; + const firstPartyRequestHeaders = { 'kbn-version': 'a', referer: 'b' }; + const kibRequest = httpServerMock.createKibanaRequest({ headers: firstPartyRequestHeaders }); + const extRequest = httpServerMock.createKibanaRequest(); + + beforeEach(() => { + logger = loggerMock.create(); + }); + afterEach(() => { + jest.clearAllMocks(); + }); + + it('logs on external requests to non-bulk apis', () => { + logWarnOnExternalRequest({ + method: 'get', + path: '/resolve/{type}/{id}', + req: extRequest, + logger, + }); + expect(logger.warn).toHaveBeenCalledTimes(1); + expect(logger.warn).toHaveBeenCalledWith( + 'The get saved object API /resolve/{type}/{id} is deprecated.' + ); + }); + + it('logs on external requests to bulk apis', () => { + logWarnOnExternalRequest({ + method: 'post', + path: '/_bulk_resolve', + req: extRequest, + logger, + }); + expect(logger.warn).toHaveBeenCalledTimes(1); + expect(logger.warn).toHaveBeenCalledWith( + 'The post saved object API /_bulk_resolve is deprecated.' + ); + }); + + it('does not log a warning on internal requests', () => { + logWarnOnExternalRequest({ + method: 'get', + path: '/resolve/{type}/{id}', + req: kibRequest, + logger, + }); + expect(logger.warn).toHaveBeenCalledTimes(0); + logWarnOnExternalRequest({ + method: 'post', + path: '/_bulk_resolve', + req: kibRequest, + logger, + }); + expect(logger.warn).toHaveBeenCalledTimes(0); + }); +}); diff --git a/packages/core/saved-objects/core-saved-objects-server-internal/src/routes/utils.ts b/packages/core/saved-objects/core-saved-objects-server-internal/src/routes/utils.ts index 3d86f9247c2d5..03023d167a38f 100644 --- a/packages/core/saved-objects/core-saved-objects-server-internal/src/routes/utils.ts +++ b/packages/core/saved-objects/core-saved-objects-server-internal/src/routes/utils.ts @@ -16,13 +16,14 @@ import { createConcatStream, } from '@kbn/utils'; import Boom from '@hapi/boom'; -import type { RequestHandlerWrapper } from '@kbn/core-http-server'; +import type { KibanaRequest, RequestHandlerWrapper } from '@kbn/core-http-server'; import { SavedObject, ISavedObjectTypeRegistry, SavedObjectsExportResultDetails, SavedObjectsErrorHelpers, } from '@kbn/core-saved-objects-server'; +import { Logger } from '@kbn/logging'; export async function createSavedObjectsStreamFromNdJson(ndJsonStream: Readable) { const savedObjects = await createPromiseFromStreams([ @@ -152,3 +153,27 @@ export interface BulkGetItem { fields?: string[]; namespaces?: string[]; } + +export function isKibanaRequest({ headers }: KibanaRequest) { + // The presence of these two request headers gives us a good indication that this is a first-party request from the Kibana client. + // We can't be 100% certain, but this is a reasonable attempt. + return headers && headers['kbn-version'] && headers.referer; +} + +export interface LogWarnOnExternalRequest { + method: string; + path: string; + req: KibanaRequest; + logger: Logger; +} +/** + * Only log a warning when the request is internal + * Allows us to silence the logs for development + * @internal + */ +export function logWarnOnExternalRequest(params: LogWarnOnExternalRequest) { + const { method, path, req, logger } = params; + if (!isKibanaRequest(req)) { + logger.warn(`The ${method} saved object API ${path} is deprecated.`); + } +} diff --git a/packages/core/saved-objects/core-saved-objects-server-internal/tsconfig.json b/packages/core/saved-objects/core-saved-objects-server-internal/tsconfig.json index cfe782040bf6d..7f6d935a488b7 100644 --- a/packages/core/saved-objects/core-saved-objects-server-internal/tsconfig.json +++ b/packages/core/saved-objects/core-saved-objects-server-internal/tsconfig.json @@ -45,6 +45,7 @@ "@kbn/core-elasticsearch-server-mocks", "@kbn/utils", "@kbn/core-http-router-server-internal", + "@kbn/logging-mocks", ], "exclude": [ "target/**/*", diff --git a/packages/kbn-alerts-as-data-utils/src/field_maps/index.ts b/packages/kbn-alerts-as-data-utils/src/field_maps/index.ts index 9aef7690b343c..04928dbd46191 100644 --- a/packages/kbn-alerts-as-data-utils/src/field_maps/index.ts +++ b/packages/kbn-alerts-as-data-utils/src/field_maps/index.ts @@ -9,4 +9,5 @@ export * from './alert_field_map'; export * from './ecs_field_map'; export * from './legacy_alert_field_map'; +export * from './legacy_experimental_field_map'; export type { FieldMap, MultiField } from './types'; diff --git a/packages/kbn-alerts-as-data-utils/src/field_maps/legacy_experimental_field_map.ts b/packages/kbn-alerts-as-data-utils/src/field_maps/legacy_experimental_field_map.ts new file mode 100644 index 0000000000000..8c8445ad6761a --- /dev/null +++ b/packages/kbn-alerts-as-data-utils/src/field_maps/legacy_experimental_field_map.ts @@ -0,0 +1,20 @@ +/* + * 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 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { ALERT_EVALUATION_THRESHOLD, ALERT_EVALUATION_VALUE } from '@kbn/rule-data-utils'; + +export const legacyExperimentalFieldMap = { + [ALERT_EVALUATION_THRESHOLD]: { + type: 'scaled_float', + scaling_factor: 100, + required: false, + }, + [ALERT_EVALUATION_VALUE]: { type: 'scaled_float', scaling_factor: 100, required: false }, +} as const; + +export type ExperimentalRuleFieldMap = typeof legacyExperimentalFieldMap; diff --git a/packages/kbn-apm-synthtrace-client/src/lib/apm/mobile_app.ts b/packages/kbn-apm-synthtrace-client/src/lib/apm/mobile_app.ts index 07753a0944f36..d6d1375dd50a4 100644 --- a/packages/kbn-apm-synthtrace-client/src/lib/apm/mobile_app.ts +++ b/packages/kbn-apm-synthtrace-client/src/lib/apm/mobile_app.ts @@ -14,12 +14,12 @@ import { generateLongId } from '../utils/generate_id'; type MobileAgentName = 'android/java' | 'iOS/swift'; export class MobileApp extends Entity { - mobileDevice(deviceId?: string) { + mobileDevice({ deviceId, serviceVersion }: { deviceId?: string; serviceVersion?: string }) { return new MobileDevice({ ...this.fields, 'device.id': deviceId ? deviceId : generateLongId(), 'service.language.name': this.fields['agent.name'] === 'iOS' ? 'swift' : 'java', - 'service.version': this.fields['service.version'] ?? '1.0', + 'service.version': serviceVersion ?? this.fields['service.version'] ?? '1.0', }).startNewSession(); } } diff --git a/packages/kbn-apm-synthtrace/src/scenarios/mobile.ts b/packages/kbn-apm-synthtrace/src/scenarios/mobile.ts index 0d57b99d9230e..6db2d17b624f9 100644 --- a/packages/kbn-apm-synthtrace/src/scenarios/mobile.ts +++ b/packages/kbn-apm-synthtrace/src/scenarios/mobile.ts @@ -309,6 +309,11 @@ function randomInt(max: number) { return Math.floor(Math.random() * max); } +function randomRange(min: number, max: number) { + const cal = Math.random() * (max - min) + min; + return cal.toPrecision(2); +} + const scenario: Scenario = async ({ scenarioOpts, logger }) => { const { numDevices = 10 } = scenarioOpts || {}; @@ -318,8 +323,12 @@ const scenario: Scenario = async ({ scenarioOpts, logger }) => { const deviceMetadata = ANDROID_DEVICES[randomInt(ANDROID_DEVICES.length)]; const geoNetwork = GEO_AND_NETWORK[randomInt(GEO_AND_NETWORK.length)]; return apm - .mobileApp({ name: 'synth-android', environment: ENVIRONMENT, agentName: 'android/java' }) - .mobileDevice() + .mobileApp({ + name: 'synth-android', + environment: ENVIRONMENT, + agentName: 'android/java', + }) + .mobileDevice({ serviceVersion: randomRange(1, 2) }) .deviceInfo(deviceMetadata) .osInfo(deviceMetadata) .setGeoInfo(geoNetwork) @@ -331,7 +340,7 @@ const scenario: Scenario = async ({ scenarioOpts, logger }) => { const geoNetwork = GEO_AND_NETWORK[randomInt(GEO_AND_NETWORK.length)]; return apm .mobileApp({ name: 'synth-ios', environment: ENVIRONMENT, agentName: 'iOS/swift' }) - .mobileDevice() + .mobileDevice({ serviceVersion: randomRange(1, 1.5) }) .deviceInfo(deviceMetadata) .osInfo(deviceMetadata) .setGeoInfo(geoNetwork) diff --git a/packages/kbn-optimizer/src/worker/webpack.config.ts b/packages/kbn-optimizer/src/worker/webpack.config.ts index 986ec051637e4..83b9882118b1c 100644 --- a/packages/kbn-optimizer/src/worker/webpack.config.ts +++ b/packages/kbn-optimizer/src/worker/webpack.config.ts @@ -91,7 +91,7 @@ export function getWebpackConfig( // already bundled with all its necessary dependencies noParse: [ /[\/\\]node_modules[\/\\]lodash[\/\\]index\.js$/, - /[\/\\]node_modules[\/\\]vega[\/\\]build-es5[\/\\]vega\.js$/, + /[\/\\]node_modules[\/\\]vega[\/\\]build[\/\\]vega\.js$/, ], rules: [ diff --git a/packages/kbn-securitysolution-grouping/src/components/grouping.test.tsx b/packages/kbn-securitysolution-grouping/src/components/grouping.test.tsx index 64cd6adfed98c..6b1d360e50468 100644 --- a/packages/kbn-securitysolution-grouping/src/components/grouping.test.tsx +++ b/packages/kbn-securitysolution-grouping/src/components/grouping.test.tsx @@ -21,7 +21,7 @@ const rule2Desc = 'Rule 2 description'; const testProps = { data: { - groupsNumber: { + groupCount0: { value: 2, }, stackByMultipleFields0: { @@ -72,7 +72,7 @@ const testProps = { sum_other_doc_count: 0, buckets: [], }, - alertsCount: { + unitCount0: { value: 1, }, severitiesSubAggregation: { @@ -94,7 +94,7 @@ const testProps = { }, ], }, - alertsCount: { + unitCount0: { value: 2, }, }, @@ -115,21 +115,21 @@ describe('grouping container', () => { beforeEach(() => { jest.clearAllMocks(); }); - it('Renders group counts when groupsNumber > 0', () => { + it('Renders group counts when groupCount0 > 0', () => { const { getByTestId, getAllByTestId, queryByTestId } = render( ); - expect(getByTestId('alert-count').textContent).toBe('2 alerts'); - expect(getByTestId('groups-count').textContent).toBe('2 groups'); + expect(getByTestId('unit-count').textContent).toBe('2 events'); + expect(getByTestId('group-count').textContent).toBe('2 groups'); expect(getAllByTestId('grouping-accordion').length).toBe(2); expect(queryByTestId('empty-results-panel')).not.toBeInTheDocument(); }); - it('Does not render group counts when groupsNumber = 0', () => { + it('Does not render group counts when groupCount0 = 0', () => { const data = { - groupsNumber: { + groupCount0: { value: 0, }, stackByMultipleFields0: { @@ -137,7 +137,7 @@ describe('grouping container', () => { sum_other_doc_count: 0, buckets: [], }, - alertsCount: { + unitCount0: { value: 0, }, }; @@ -146,8 +146,8 @@ describe('grouping container', () => { ); - expect(queryByTestId('alert-count')).not.toBeInTheDocument(); - expect(queryByTestId('groups-count')).not.toBeInTheDocument(); + expect(queryByTestId('unit-count')).not.toBeInTheDocument(); + expect(queryByTestId('group-count')).not.toBeInTheDocument(); expect(queryByTestId('grouping-accordion')).not.toBeInTheDocument(); expect(getByTestId('empty-results-panel')).toBeInTheDocument(); }); diff --git a/packages/kbn-securitysolution-grouping/src/components/grouping.tsx b/packages/kbn-securitysolution-grouping/src/components/grouping.tsx index 210d75fcf4e66..420656c69db41 100644 --- a/packages/kbn-securitysolution-grouping/src/components/grouping.tsx +++ b/packages/kbn-securitysolution-grouping/src/components/grouping.tsx @@ -21,7 +21,7 @@ import type { BadgeMetric, CustomMetric } from './accordion_panel'; import { GroupPanel } from './accordion_panel'; import { GroupStats } from './accordion_panel/group_stats'; import { EmptyGroupingComponent } from './empty_results_panel'; -import { groupingContainerCss, groupsUnitCountCss } from './styles'; +import { groupingContainerCss, countCss } from './styles'; import { GROUPS_UNIT } from './translations'; import type { GroupingAggregation, GroupingFieldTotalAggregation, RawBucket } from './types'; @@ -64,15 +64,15 @@ const GroupingComponent = ({ Record }> >({}); - const groupsNumber = data?.groupsNumber?.value ?? 0; + const unitCount = data?.unitCount0?.value ?? 0; const unitCountText = useMemo(() => { - const count = data?.alertsCount?.value ?? 0; - return `${count.toLocaleString()} ${unit && unit(count)}`; - }, [data?.alertsCount?.value, unit]); + return `${unitCount.toLocaleString()} ${unit && unit(unitCount)}`; + }, [unitCount, unit]); - const unitGroupsCountText = useMemo( - () => `${groupsNumber.toLocaleString()} ${GROUPS_UNIT(groupsNumber)}`, - [groupsNumber] + const groupCount = data?.groupCount0?.value ?? 0; + const groupCountText = useMemo( + () => `${groupCount.toLocaleString()} ${GROUPS_UNIT(groupCount)}`, + [groupCount] ); const groupPanels = useMemo( @@ -129,8 +129,8 @@ const GroupingComponent = ({ ] ); const pageCount = useMemo( - () => (groupsNumber && pagination.pageSize ? Math.ceil(groupsNumber / pagination.pageSize) : 1), - [groupsNumber, pagination.pageSize] + () => (groupCount && pagination.pageSize ? Math.ceil(groupCount / pagination.pageSize) : 1), + [groupCount, pagination.pageSize] ); return ( <> @@ -141,20 +141,16 @@ const GroupingComponent = ({ style={{ paddingBottom: 20, paddingTop: 20 }} > - {groupsNumber > 0 ? ( + {groupCount > 0 && unitCount > 0 ? ( - + {unitCountText} - - {unitGroupsCountText} + + {groupCountText} @@ -168,7 +164,7 @@ const GroupingComponent = ({
- {groupsNumber > 0 ? ( + {groupCount > 0 ? ( <> {groupPanels} diff --git a/packages/kbn-securitysolution-grouping/src/components/styles.tsx b/packages/kbn-securitysolution-grouping/src/components/styles.tsx index ede1b4eae164c..abf3d83db508e 100644 --- a/packages/kbn-securitysolution-grouping/src/components/styles.tsx +++ b/packages/kbn-securitysolution-grouping/src/components/styles.tsx @@ -11,7 +11,7 @@ import { euiStyled } from '@kbn/kibana-react-plugin/common'; import { css } from '@emotion/react'; import { euiThemeVars } from '@kbn/ui-theme'; -export const groupsUnitCountCss = css` +export const countCss = css` font-size: ${euiThemeVars.euiFontSizeXS}; font-weight: ${euiThemeVars.euiFontWeightSemiBold}; border-right: ${euiThemeVars.euiBorderThin}; diff --git a/packages/kbn-securitysolution-grouping/src/components/translations.ts b/packages/kbn-securitysolution-grouping/src/components/translations.ts index 0ce0e118e7a43..e3896d25b910f 100644 --- a/packages/kbn-securitysolution-grouping/src/components/translations.ts +++ b/packages/kbn-securitysolution-grouping/src/components/translations.ts @@ -47,8 +47,8 @@ export const CUSTOM_FIELD = i18n.translate('grouping.customGroupByOptionName', { defaultMessage: 'Custom field', }); -export const ALERTS_UNIT = (totalCount: number) => +export const DEFAULT_UNIT = (totalCount: number) => i18n.translate('grouping.eventsTab.unit', { values: { totalCount }, - defaultMessage: `{totalCount, plural, =1 {alert} other {alerts}}`, + defaultMessage: `{totalCount, plural, =1 {event} other {events}}`, }); diff --git a/packages/kbn-securitysolution-grouping/src/components/types.ts b/packages/kbn-securitysolution-grouping/src/components/types.ts index 6885ab2f07e12..df978d99eba59 100644 --- a/packages/kbn-securitysolution-grouping/src/components/types.ts +++ b/packages/kbn-securitysolution-grouping/src/components/types.ts @@ -18,11 +18,15 @@ export const NONE_GROUP_KEY = 'none'; export type RawBucket = GenericBuckets & T; /** Defines the shape of the aggregation returned by Elasticsearch */ +// TODO: write developer docs for these fields export interface GroupingAggregation { stackByMultipleFields0?: { buckets?: Array>; }; - groupsCount0?: { + groupCount0?: { + value?: number | null; + }; + unitCount0?: { value?: number | null; }; } diff --git a/packages/kbn-securitysolution-grouping/src/helpers.ts b/packages/kbn-securitysolution-grouping/src/helpers.ts index 4c1b7142b85e5..948da59dcb22a 100644 --- a/packages/kbn-securitysolution-grouping/src/helpers.ts +++ b/packages/kbn-securitysolution-grouping/src/helpers.ts @@ -30,9 +30,9 @@ export function firstNonNullValue(valueOrCollection: ECSField): T | undefi } } -export const defaultUnit = (n: number) => i18n.ALERTS_UNIT(n); +export const defaultUnit = (n: number) => i18n.DEFAULT_UNIT(n); -const LOCAL_STORAGE_GROUPING_KEY = 'groups'; +export const LOCAL_STORAGE_GROUPING_KEY = 'groups'; export const getAllGroupsInStorage = (storage: Storage): GroupsById => { const allGroups = storage.getItem(LOCAL_STORAGE_GROUPING_KEY); if (!allGroups) { diff --git a/packages/kbn-securitysolution-grouping/src/hooks/state/reducer.test.ts b/packages/kbn-securitysolution-grouping/src/hooks/state/reducer.test.ts new file mode 100644 index 0000000000000..5348731d39128 --- /dev/null +++ b/packages/kbn-securitysolution-grouping/src/hooks/state/reducer.test.ts @@ -0,0 +1,101 @@ +/* + * 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 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { act, renderHook } from '@testing-library/react-hooks'; +import { useReducer } from 'react'; +import { groupActions, groupsReducerWithStorage, initialState } from '.'; +import { defaultGroup, LOCAL_STORAGE_GROUPING_KEY } from '../..'; + +const groupingOptions = [ + { label: 'ruleName', key: 'kibana.alert.rule.name' }, + { label: 'userName', key: 'user.name' }, + { label: 'hostName', key: 'host.name' }, + { label: 'sourceIP', key: 'source.ip' }, +]; + +const groupingId = 'test-table'; + +const groupById = { + [groupingId]: { + ...defaultGroup, + options: groupingOptions, + activeGroup: 'host.name', + }, +}; + +const setItem = jest.spyOn(window.localStorage.__proto__, 'setItem'); +const getItem = jest.spyOn(window.localStorage.__proto__, 'getItem'); + +describe('grouping reducer', () => { + beforeEach(() => { + jest.clearAllMocks(); + }); + it('updateGroupOptions, initializes group with defaults and provided newOptionList', () => { + const { result } = renderHook(() => useReducer(groupsReducerWithStorage, initialState)); + let [groupingState, dispatch] = result.current; + expect(groupingState.groupById).toEqual({}); + act(() => { + dispatch(groupActions.updateGroupOptions({ id: groupingId, newOptionList: groupingOptions })); + }); + [groupingState, dispatch] = result.current; + expect(groupingState.groupById[groupingId]).toEqual({ + ...defaultGroup, + options: groupingOptions, + }); + + expect(getItem).toHaveBeenCalledTimes(2); + expect(setItem).toHaveBeenCalledWith( + LOCAL_STORAGE_GROUPING_KEY, + JSON.stringify(groupingState.groupById) + ); + }); + it('updateActiveGroup', () => { + const { result } = renderHook(() => + useReducer(groupsReducerWithStorage, { + ...initialState, + groupById, + }) + ); + let [groupingState, dispatch] = result.current; + expect(groupingState.groupById[groupingId].activeGroup).toEqual('host.name'); + act(() => { + dispatch(groupActions.updateActiveGroup({ id: groupingId, activeGroup: 'user.name' })); + }); + [groupingState, dispatch] = result.current; + expect(groupingState.groupById[groupingId].activeGroup).toEqual('user.name'); + }); + it('updateGroupActivePage', () => { + const { result } = renderHook(() => + useReducer(groupsReducerWithStorage, { + ...initialState, + groupById, + }) + ); + let [groupingState, dispatch] = result.current; + expect(groupingState.groupById[groupingId].activePage).toEqual(0); + act(() => { + dispatch(groupActions.updateGroupActivePage({ id: groupingId, activePage: 12 })); + }); + [groupingState, dispatch] = result.current; + expect(groupingState.groupById[groupingId].activePage).toEqual(12); + }); + it('updateGroupItemsPerPage', () => { + const { result } = renderHook(() => useReducer(groupsReducerWithStorage, initialState)); + let [groupingState, dispatch] = result.current; + act(() => { + dispatch(groupActions.updateGroupOptions({ id: groupingId, newOptionList: groupingOptions })); + }); + [groupingState, dispatch] = result.current; + expect(groupingState.groupById[groupingId].itemsPerPage).toEqual(25); + act(() => { + dispatch(groupActions.updateGroupItemsPerPage({ id: groupingId, itemsPerPage: 12 })); + }); + [groupingState, dispatch] = result.current; + expect(groupingState.groupById[groupingId].itemsPerPage).toEqual(12); + }); +}); diff --git a/packages/kbn-securitysolution-grouping/src/hooks/state/reducer.ts b/packages/kbn-securitysolution-grouping/src/hooks/state/reducer.ts index 1784737bc924d..287227b5763b3 100644 --- a/packages/kbn-securitysolution-grouping/src/hooks/state/reducer.ts +++ b/packages/kbn-securitysolution-grouping/src/hooks/state/reducer.ts @@ -32,6 +32,7 @@ const groupsReducer = (state: GroupMap, action: Action, groupsById: GroupsById) groupById: { ...groupsById, [id]: { + ...defaultGroup, ...groupsById[id], activeGroup, }, @@ -45,6 +46,7 @@ const groupsReducer = (state: GroupMap, action: Action, groupsById: GroupsById) groupById: { ...groupsById, [id]: { + ...defaultGroup, ...groupsById[id], activePage, }, @@ -58,6 +60,7 @@ const groupsReducer = (state: GroupMap, action: Action, groupsById: GroupsById) groupById: { ...groupsById, [id]: { + ...defaultGroup, ...groupsById[id], itemsPerPage, }, @@ -82,14 +85,26 @@ const groupsReducer = (state: GroupMap, action: Action, groupsById: GroupsById) throw Error(`Unknown grouping action`); }; export const groupsReducerWithStorage = (state: GroupMap, action: Action) => { - let groupsInStorage = {}; + let groupsInStorage: GroupsById = {}; if (storage) { groupsInStorage = getAllGroupsInStorage(storage); } + const trackedGroupIds = Object.keys(state.groupById); + + const adjustedStorageGroups = Object.entries(groupsInStorage).reduce( + (acc: GroupsById, [key, group]) => ({ + ...acc, + [key]: { + // reset page to 0 if is initial state + ...(trackedGroupIds.includes(key) ? group : { ...group, activePage: 0 }), + }, + }), + {} as GroupsById + ); const groupsById: GroupsById = { ...state.groupById, - ...groupsInStorage, + ...adjustedStorageGroups, }; const newState = groupsReducer(state, action, groupsById); diff --git a/packages/kbn-securitysolution-grouping/src/hooks/use_get_group_selector.test.tsx b/packages/kbn-securitysolution-grouping/src/hooks/use_get_group_selector.test.tsx new file mode 100644 index 0000000000000..2a3e6d39d7a41 --- /dev/null +++ b/packages/kbn-securitysolution-grouping/src/hooks/use_get_group_selector.test.tsx @@ -0,0 +1,158 @@ +/* + * 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 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ +import { act, renderHook } from '@testing-library/react-hooks'; + +import { useGetGroupSelector } from './use_get_group_selector'; +import { initialState } from './state'; +import { ActionType, defaultGroup } from '..'; + +const defaultGroupingOptions = [ + { label: 'ruleName', key: 'kibana.alert.rule.name' }, + { label: 'userName', key: 'user.name' }, + { label: 'hostName', key: 'host.name' }, + { label: 'sourceIP', key: 'source.ip' }, +]; +const groupingId = 'test-table'; +const dispatch = jest.fn(); +const defaultArgs = { + defaultGroupingOptions, + dispatch, + fields: [], + groupingId, + groupingState: initialState, +}; +const customField = 'custom.field'; +describe('useGetGroupSelector', () => { + beforeEach(() => { + jest.clearAllMocks(); + }); + + it('Initializes a group with options', () => { + renderHook(() => useGetGroupSelector(defaultArgs)); + + expect(dispatch).toHaveBeenCalledWith({ + payload: { + id: groupingId, + newOptionList: defaultGroupingOptions, + }, + type: ActionType.updateGroupOptions, + }); + }); + + it('Initializes a group with custom selected group', () => { + renderHook(() => + useGetGroupSelector({ + ...defaultArgs, + groupingState: { + groupById: { [groupingId]: { ...defaultGroup, activeGroup: customField } }, + }, + }) + ); + + expect(dispatch).toHaveBeenCalledWith({ + payload: { + id: groupingId, + newOptionList: [ + ...defaultGroupingOptions, + { + key: customField, + label: customField, + }, + ], + }, + type: ActionType.updateGroupOptions, + }); + }); + + it('On group change, does nothing when set to prev selected group', () => { + const testGroup = { + [groupingId]: { + ...defaultGroup, + options: defaultGroupingOptions, + activeGroup: 'host.name', + }, + }; + const { result } = renderHook((props) => useGetGroupSelector(props), { + initialProps: { + ...defaultArgs, + groupingState: { + groupById: testGroup, + }, + }, + }); + act(() => result.current.props.onGroupChange('host.name')); + expect(dispatch).toHaveBeenCalledTimes(0); + }); + + it('On group change, resets active page, sets active group, and leaves options alone', () => { + const testGroup = { + [groupingId]: { + ...defaultGroup, + options: defaultGroupingOptions, + activeGroup: 'host.name', + }, + }; + const { result } = renderHook((props) => useGetGroupSelector(props), { + initialProps: { + ...defaultArgs, + groupingState: { + groupById: testGroup, + }, + }, + }); + act(() => result.current.props.onGroupChange('user.name')); + expect(dispatch).toHaveBeenNthCalledWith(1, { + payload: { + id: groupingId, + activePage: 0, + }, + type: ActionType.updateGroupActivePage, + }); + expect(dispatch).toHaveBeenNthCalledWith(2, { + payload: { + id: groupingId, + activeGroup: 'user.name', + }, + type: ActionType.updateActiveGroup, + }); + expect(dispatch).toHaveBeenCalledTimes(2); + }); + + it('On group change to custom field, updates options', () => { + const testGroup = { + [groupingId]: { + ...defaultGroup, + options: defaultGroupingOptions, + activeGroup: 'host.name', + }, + }; + const { result } = renderHook((props) => useGetGroupSelector(props), { + initialProps: { + ...defaultArgs, + groupingState: { + groupById: testGroup, + }, + }, + }); + act(() => result.current.props.onGroupChange(customField)); + expect(dispatch).toHaveBeenCalledTimes(3); + expect(dispatch).toHaveBeenNthCalledWith(3, { + payload: { + id: groupingId, + newOptionList: [ + ...defaultGroupingOptions, + { + label: customField, + key: customField, + }, + ], + }, + type: ActionType.updateGroupOptions, + }); + }); +}); diff --git a/packages/kbn-securitysolution-grouping/src/hooks/use_get_group_selector.tsx b/packages/kbn-securitysolution-grouping/src/hooks/use_get_group_selector.tsx index 34229c96cb09a..3509e095e6cc8 100644 --- a/packages/kbn-securitysolution-grouping/src/hooks/use_get_group_selector.tsx +++ b/packages/kbn-securitysolution-grouping/src/hooks/use_get_group_selector.tsx @@ -53,7 +53,40 @@ export const useGetGroupSelector = ({ [dispatch, groupingId] ); + const onGroupChange = useCallback( + (groupSelection: string) => { + if (groupSelection === selectedGroup) { + return; + } + setGroupsActivePage(0); + setSelectedGroup(groupSelection); + + // only update options if the new selection is a custom field + if ( + !isNoneGroup(groupSelection) && + !options.find((o: GroupOption) => o.key === groupSelection) + ) { + setOptions([ + ...defaultGroupingOptions, + { + label: groupSelection, + key: groupSelection, + }, + ]); + } + }, + [ + defaultGroupingOptions, + options, + selectedGroup, + setGroupsActivePage, + setOptions, + setSelectedGroup, + ] + ); + useEffect(() => { + // only set options the first time, all other updates will be taken care of by onGroupChange if (options.length > 0) return; setOptions( defaultGroupingOptions.find((o) => o.key === selectedGroup) @@ -70,33 +103,12 @@ export const useGetGroupSelector = ({ : []), ] ); - }, [defaultGroupingOptions, selectedGroup, setOptions, options]); + }, [defaultGroupingOptions, options.length, selectedGroup, setOptions]); return getGroupSelector({ groupSelected: selectedGroup, 'data-test-subj': 'alerts-table-group-selector', - onGroupChange: (groupSelection: string) => { - if (groupSelection === selectedGroup) { - return; - } - setGroupsActivePage(0); - setSelectedGroup(groupSelection); - - if ( - !isNoneGroup(groupSelection) && - !options.find((o: GroupOption) => o.key === groupSelection) - ) { - setOptions([ - ...defaultGroupingOptions, - { - label: groupSelection, - key: groupSelection, - }, - ]); - } else { - setOptions(defaultGroupingOptions); - } - }, + onGroupChange, fields, options, }); diff --git a/packages/kbn-securitysolution-grouping/src/hooks/use_grouping.test.tsx b/packages/kbn-securitysolution-grouping/src/hooks/use_grouping.test.tsx index f1e3dc73dd617..9d585ce4fc454 100644 --- a/packages/kbn-securitysolution-grouping/src/hooks/use_grouping.test.tsx +++ b/packages/kbn-securitysolution-grouping/src/hooks/use_grouping.test.tsx @@ -41,12 +41,13 @@ const groupingArgs = { to: '2020-07-08T08:20:18.966Z', }; -describe('use_grouping', () => { +describe('useGrouping', () => { it('Returns the expected default results on initial mount', () => { const { result } = renderHook(() => useGrouping(defaultArgs)); expect(result.current.selectedGroup).toEqual('none'); expect(result.current.getGrouping(groupingArgs).props.selectedGroup).toEqual('none'); expect(result.current.groupSelector.props.options).toEqual(defaultGroupingOptions); - expect(result.current.pagination).toEqual({ pageIndex: 0, pageSize: 25 }); + const { reset, ...withoutReset } = result.current.pagination; + expect(withoutReset).toEqual({ pageIndex: 0, pageSize: 25 }); }); }); diff --git a/packages/kbn-securitysolution-grouping/src/hooks/use_grouping.tsx b/packages/kbn-securitysolution-grouping/src/hooks/use_grouping.tsx index 1ffd485213d5c..d81c19cafda91 100644 --- a/packages/kbn-securitysolution-grouping/src/hooks/use_grouping.tsx +++ b/packages/kbn-securitysolution-grouping/src/hooks/use_grouping.tsx @@ -11,7 +11,7 @@ import React, { useCallback, useMemo, useReducer } from 'react'; import { groupsReducerWithStorage, initialState } from './state/reducer'; import { GroupingProps, GroupSelectorProps } from '..'; import { useGroupingPagination } from './use_grouping_pagination'; -import { groupByIdSelector } from './state'; +import { groupActions, groupByIdSelector } from './state'; import { useGetGroupSelector } from './use_get_group_selector'; import { defaultGroup, GroupOption } from './types'; import { Grouping as GroupingComponent } from '../components/grouping'; @@ -22,6 +22,7 @@ interface Grouping { ) => React.ReactElement>; groupSelector: React.ReactElement; pagination: { + reset: () => void; pageIndex: number; pageSize: number; }; @@ -70,16 +71,28 @@ export const useGrouping = ({ [groupSelector, pagination, selectedGroup] ); + const resetPagination = useCallback(() => { + dispatch(groupActions.updateGroupActivePage({ id: groupingId, activePage: 0 })); + }, [groupingId]); + return useMemo( () => ({ getGrouping, groupSelector, selectedGroup, pagination: { + reset: resetPagination, pageIndex: pagination.pageIndex, pageSize: pagination.pageSize, }, }), - [getGrouping, groupSelector, pagination.pageIndex, pagination.pageSize, selectedGroup] + [ + getGrouping, + groupSelector, + pagination.pageIndex, + pagination.pageSize, + resetPagination, + selectedGroup, + ] ); }; diff --git a/src/core/server/integration_tests/node/migrator.test.ts b/src/core/server/integration_tests/node/migrator.test.ts new file mode 100644 index 0000000000000..c93e64a65c79d --- /dev/null +++ b/src/core/server/integration_tests/node/migrator.test.ts @@ -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 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import ChildProcess from 'child_process'; +import Path from 'path'; + +import * as Rx from 'rxjs'; + +import { ToolingLog } from '@kbn/tooling-log'; +import { createTestEsCluster, kibanaServerTestUser } from '@kbn/test'; +import { observeLines } from '@kbn/stdio-dev-helpers'; +import { REPO_ROOT } from '@kbn/repo-info'; + +describe('migrator-only node', () => { + const log = new ToolingLog({ writeTo: process.stdout, level: 'debug' }); + log.indent(4); + const es = createTestEsCluster({ log }); + jest.setTimeout(100_000 + es.getStartTimeout()); + + it('starts Kibana, runs migrations and then exits with a "0" status code', async () => { + const expectedLog = /Detected migrator node role/; + + let proc: undefined | ChildProcess.ChildProcess; + let logsSub: undefined | Rx.Subscription; + try { + await es.start(); + + proc = ChildProcess.spawn( + process.execPath, + [ + Path.resolve(REPO_ROOT, './scripts/kibana.js'), + `--elasticsearch.username=${kibanaServerTestUser.username}`, + `--elasticsearch.password=${kibanaServerTestUser.password}`, + '--elasticsearch.hosts=http://localhost:9220', + '--node.roles=["migrator"]', + '--no-optimizer', + '--no-base-path', + '--no-watch', + '--oss', + ], + { stdio: ['pipe', 'pipe', 'pipe'] } + ); + + let sawExpectedLog = false; + + logsSub = Rx.merge( + observeLines(proc.stdout!).pipe( + Rx.tap((line) => { + log.debug(line); + if (!sawExpectedLog) sawExpectedLog = expectedLog.test(line); + }) + ), + observeLines(proc.stderr!).pipe( + Rx.tap((line) => { + log.error(line); + }) + ) + ).subscribe(); + + const [exitCode] = await Rx.firstValueFrom( + Rx.race( + Rx.fromEvent<[number, unknown]>(proc, 'exit'), + Rx.fromEvent(proc, 'error').pipe( + Rx.map((error) => { + throw error; + }) + ) + ) + ); + + expect(sawExpectedLog).toBe(true); + expect(exitCode).toBe(0); + } finally { + logsSub?.unsubscribe(); + await es.stop(); + if (proc?.exitCode == null) proc?.kill(1); + } + }); +}); diff --git a/src/core/server/integration_tests/saved_objects/migrations/group2/check_registered_types.test.ts b/src/core/server/integration_tests/saved_objects/migrations/group2/check_registered_types.test.ts index 8aa1eb22fccbe..fbc82719bd381 100644 --- a/src/core/server/integration_tests/saved_objects/migrations/group2/check_registered_types.test.ts +++ b/src/core/server/integration_tests/saved_objects/migrations/group2/check_registered_types.test.ts @@ -56,8 +56,8 @@ describe('checking migration metadata changes on all registered SO types', () => expect(hashMap).toMatchInlineSnapshot(` Object { "action": "6cfc277ed3211639e37546ac625f4a68f2494215", - "action_task_params": "db2afea7d78e00e725486b791554d0d4e81956ef", - "alert": "2568bf6d8ba0876441c61c9e58e08016c1dc1617", + "action_task_params": "5f419caba96dd8c77d0f94013e71d43890e3d5d6", + "alert": "785240e3137f5eb1a0f8986e5b8eff99780fc04f", "api_key_pending_invalidation": "16e7bcf8e78764102d7f525542d5b616809a21ee", "apm-indices": "d19dd7fb51f2d2cbc1f8769481721e0953f9a6d2", "apm-server-schema": "1d42f17eff9ec6c16d3a9324d9539e2d123d0a9a", @@ -134,6 +134,7 @@ describe('checking migration metadata changes on all registered SO types', () => "siem-ui-timeline": "e9d6b3a9fd7af6dc502293c21cbdb309409f3996", "siem-ui-timeline-note": "13c9d4c142f96624a93a623c6d7cba7e1ae9b5a6", "siem-ui-timeline-pinned-event": "96a43d59b9e2fc11f12255a0cb47ef0a3d83af4c", + "slo": "9a138b459c7efef7fecfda91f22db8b7655d0e61", "space": "9542afcd6fd71558623c09151e453c5e84b4e5e1", "spaces-usage-stats": "084bd0f080f94fb5735d7f3cf12f13ec92f36bad", "synthetics-monitor": "96cc312bfa597022f83dfb3b5d1501e27a73e8d5", diff --git a/src/core/server/integration_tests/saved_objects/migrations/group3/type_registrations.test.ts b/src/core/server/integration_tests/saved_objects/migrations/group3/type_registrations.test.ts index 7f4ca3fb70dc2..914c825597774 100644 --- a/src/core/server/integration_tests/saved_objects/migrations/group3/type_registrations.test.ts +++ b/src/core/server/integration_tests/saved_objects/migrations/group3/type_registrations.test.ts @@ -107,6 +107,7 @@ const previouslyRegisteredTypes = [ 'siem-ui-timeline', 'siem-ui-timeline-note', 'siem-ui-timeline-pinned-event', + 'slo', 'space', 'spaces-usage-stats', 'synthetics-monitor', diff --git a/src/core/server/integration_tests/saved_objects/migrations/test_utils.ts b/src/core/server/integration_tests/saved_objects/migrations/test_utils.ts index aa78d8dbc5d90..0a84b9bc4b7e8 100644 --- a/src/core/server/integration_tests/saved_objects/migrations/test_utils.ts +++ b/src/core/server/integration_tests/saved_objects/migrations/test_utils.ts @@ -10,6 +10,7 @@ import { Env } from '@kbn/config'; import { getDocLinksMeta, getDocLinks } from '@kbn/doc-links'; import { REPO_ROOT } from '@kbn/repo-info'; import { getEnvOptions } from '@kbn/config-mocks'; +import type { SavedObjectsType } from '@kbn/core-saved-objects-server'; export const getDocVersion = () => { const env = Env.createDefault(REPO_ROOT, getEnvOptions()); @@ -24,3 +25,11 @@ export const getMigrationDocLink = () => { export const delay = (seconds: number) => new Promise((resolve) => setTimeout(resolve, seconds * 1000)); + +export const createType = (parts: Partial): SavedObjectsType => ({ + name: 'test-type', + hidden: false, + namespaceType: 'single', + mappings: { properties: {} }, + ...parts, +}); diff --git a/src/core/server/integration_tests/saved_objects/migrations/zero_downtime/base_types.fixtures.ts b/src/core/server/integration_tests/saved_objects/migrations/zero_downtime/base_types.fixtures.ts new file mode 100644 index 0000000000000..9bfac3ac5fd49 --- /dev/null +++ b/src/core/server/integration_tests/saved_objects/migrations/zero_downtime/base_types.fixtures.ts @@ -0,0 +1,47 @@ +/* + * 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 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { SavedObjectsModelVersion } from '@kbn/core-saved-objects-server'; +import { createType } from '../test_utils'; + +export const dummyModelVersion: SavedObjectsModelVersion = { + modelChange: { + type: 'expansion', + }, +}; + +export const getFooType = () => { + return createType({ + name: 'foo', + mappings: { + properties: { + someField: { type: 'text' }, + }, + }, + switchToModelVersionAt: '8.7.0', + modelVersions: { + '1': dummyModelVersion, + '2': dummyModelVersion, + }, + }); +}; + +export const getBarType = () => { + return createType({ + name: 'bar', + mappings: { + properties: { + aKeyword: { type: 'keyword' }, + }, + }, + switchToModelVersionAt: '8.7.0', + modelVersions: { + '1': dummyModelVersion, + }, + }); +}; diff --git a/src/core/server/integration_tests/saved_objects/migrations/zero_downtime/create_index.test.ts b/src/core/server/integration_tests/saved_objects/migrations/zero_downtime/create_index.test.ts new file mode 100644 index 0000000000000..e3f45bb561cf6 --- /dev/null +++ b/src/core/server/integration_tests/saved_objects/migrations/zero_downtime/create_index.test.ts @@ -0,0 +1,115 @@ +/* + * 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 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import Path from 'path'; +import fs from 'fs/promises'; +import JSON5 from 'json5'; +import { LogRecord } from '@kbn/logging'; +import { createTestServers, type TestElasticsearchUtils } from '@kbn/core-test-helpers-kbn-server'; +import { getKibanaMigratorTestKit } from '../kibana_migrator_test_kit'; +import { delay } from '../test_utils'; +import { getFooType, getBarType } from './base_types.fixtures'; + +export const logFilePath = Path.join(__dirname, 'create_index.test.log'); + +describe('ZDT upgrades - running on a fresh cluster', () => { + let esServer: TestElasticsearchUtils['es']; + + const startElasticsearch = async () => { + const { startES } = createTestServers({ + adjustTimeout: (t: number) => jest.setTimeout(t), + settings: { + es: { + license: 'basic', + }, + }, + }); + return await startES(); + }; + + beforeAll(async () => { + await fs.unlink(logFilePath).catch(() => {}); + esServer = await startElasticsearch(); + }); + + afterAll(async () => { + await esServer?.stop(); + await delay(10); + }); + + it('create the index with the correct mappings and meta', async () => { + const fooType = getFooType(); + const barType = getBarType(); + + const { runMigrations, client } = await getKibanaMigratorTestKit({ + kibanaIndex: '.kibana', + kibanaVersion: '8.7.0', + logFilePath, + types: [fooType, barType], + settings: { + migrations: { + algorithm: 'zdt', + }, + }, + }); + + const result = await runMigrations(); + + expect(result).toEqual([ + { + destIndex: '.kibana', + elapsedMs: expect.any(Number), + status: 'patched', + }, + ]); + + const indices = await client.indices.get({ index: '.kibana*' }); + + expect(Object.keys(indices)).toEqual(['.kibana_1']); + + const index = indices['.kibana_1']; + const aliases = Object.keys(index.aliases ?? {}).sort(); + const mappings = index.mappings ?? {}; + const mappingMeta = mappings._meta ?? {}; + + expect(aliases).toEqual(['.kibana', '.kibana_8.7.0']); + + expect(mappings.properties).toEqual( + expect.objectContaining({ + foo: fooType.mappings, + bar: barType.mappings, + }) + ); + + expect(mappingMeta).toEqual({ + docVersions: { + foo: 2, + bar: 1, + }, + mappingVersions: { + foo: 2, + bar: 1, + }, + }); + + const logFileContent = await fs.readFile(logFilePath, 'utf-8'); + const records = logFileContent + .split('\n') + .filter(Boolean) + .map((str) => JSON5.parse(str)) as LogRecord[]; + + const expectLogsContains = (messagePrefix: string) => { + expect(records.find((entry) => entry.message.includes(messagePrefix))).toBeDefined(); + }; + + expectLogsContains('INIT -> CREATE_TARGET_INDEX'); + expectLogsContains('CREATE_TARGET_INDEX -> UPDATE_ALIASES'); + expectLogsContains('UPDATE_ALIASES -> DONE'); + expectLogsContains('Migration completed'); + }); +}); diff --git a/src/core/server/integration_tests/saved_objects/migrations/zero_downtime/jest.integration.config.js b/src/core/server/integration_tests/saved_objects/migrations/zero_downtime/jest.integration.config.js new file mode 100644 index 0000000000000..4772e43faa148 --- /dev/null +++ b/src/core/server/integration_tests/saved_objects/migrations/zero_downtime/jest.integration.config.js @@ -0,0 +1,19 @@ +/* + * 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 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +module.exports = { + // TODO replace the line below with + // preset: '@kbn/test/jest_integration_node + // to do so, we must fix all integration tests first + // see https://github.com/elastic/kibana/pull/130255/ + preset: '@kbn/test/jest_integration', + rootDir: '../../../../../../..', + roots: ['/src/core/server/integration_tests/saved_objects/migrations/zero_downtime'], + // must override to match all test given there is no `integration_tests` subfolder + testMatch: ['**/*.test.{js,mjs,ts,tsx}'], +}; diff --git a/src/core/server/integration_tests/saved_objects/migrations/zero_downtime/mapping_version_conflict.test.ts b/src/core/server/integration_tests/saved_objects/migrations/zero_downtime/mapping_version_conflict.test.ts new file mode 100644 index 0000000000000..eadd24bf447ac --- /dev/null +++ b/src/core/server/integration_tests/saved_objects/migrations/zero_downtime/mapping_version_conflict.test.ts @@ -0,0 +1,139 @@ +/* + * 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 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import Path from 'path'; +import fs from 'fs/promises'; +import JSON5 from 'json5'; +import { LogRecord } from '@kbn/logging'; +import { createTestServers, type TestElasticsearchUtils } from '@kbn/core-test-helpers-kbn-server'; +import { + getKibanaMigratorTestKit, + type KibanaMigratorTestKitParams, +} from '../kibana_migrator_test_kit'; +import { delay } from '../test_utils'; +import { getFooType, getBarType, dummyModelVersion } from './base_types.fixtures'; + +export const logFilePath = Path.join(__dirname, 'mapping_version_conflict.test.log'); + +describe('ZDT upgrades - mapping model version conflict', () => { + let esServer: TestElasticsearchUtils['es']; + + const startElasticsearch = async () => { + const { startES } = createTestServers({ + adjustTimeout: (t: number) => jest.setTimeout(t), + settings: { + es: { + license: 'basic', + }, + }, + }); + return await startES(); + }; + + const baseMigratorParams: KibanaMigratorTestKitParams = { + kibanaIndex: '.kibana', + kibanaVersion: '8.7.0', + settings: { + migrations: { + algorithm: 'zdt', + }, + }, + }; + + beforeAll(async () => { + await fs.unlink(logFilePath).catch(() => {}); + esServer = await startElasticsearch(); + }); + + afterAll(async () => { + await esServer?.stop(); + await delay(10); + }); + + const createBaseline = async () => { + const fooType = getFooType(); + const barType = getBarType(); + + // increasing bar model version on the baseline + barType.modelVersions = { + ...barType.modelVersions, + '2': dummyModelVersion, + }; + barType.mappings.properties = { + ...barType.mappings.properties, + anotherAddedField: { type: 'boolean' }, + }; + const { runMigrations } = await getKibanaMigratorTestKit({ + ...baseMigratorParams, + types: [fooType, barType], + }); + await runMigrations(); + }; + + it('fails the migration with an error', async () => { + await createBaseline(); + + const fooType = getFooType(); + const barType = getBarType(); + + // increasing foo model version + fooType.modelVersions = { + ...fooType.modelVersions, + '3': dummyModelVersion, + }; + fooType.mappings.properties = { + ...fooType.mappings.properties, + someAddedField: { type: 'keyword' }, + }; + + // we have the following versions: + // baseline : bar:2, foo: 2 + // upgrade: bar:3, foo:1 + // which should cause a migration failure. + + const { runMigrations, client } = await getKibanaMigratorTestKit({ + ...baseMigratorParams, + logFilePath, + types: [fooType, barType], + }); + + await expect(runMigrations()).rejects.toThrowErrorMatchingInlineSnapshot( + `"Unable to complete saved object migrations for the [.kibana] index: Model version conflict: inconsistent higher/lower versions"` + ); + + const indices = await client.indices.get({ index: '.kibana*' }); + + expect(Object.keys(indices)).toEqual(['.kibana_1']); + + const index = indices['.kibana_1']; + const aliases = Object.keys(index.aliases ?? {}).sort(); + const mappings = index.mappings ?? {}; + const mappingMeta = mappings._meta ?? {}; + + expect(aliases).toEqual(['.kibana', '.kibana_8.7.0']); + + expect(mappingMeta.mappingVersions).toEqual({ + foo: 2, + bar: 2, + }); + + const logFileContent = await fs.readFile(logFilePath, 'utf-8'); + const records = logFileContent + .split('\n') + .filter(Boolean) + .map((str) => JSON5.parse(str)) as LogRecord[]; + + const expectLogsContains = (messagePrefix: string) => { + expect(records.find((entry) => entry.message.includes(messagePrefix))).toBeDefined(); + }; + + // + expectLogsContains('Mappings model version check result: conflict'); + expectLogsContains('INIT -> FATAL'); + }); +}); diff --git a/src/core/server/integration_tests/saved_objects/migrations/zero_downtime/update_mappings.test.ts b/src/core/server/integration_tests/saved_objects/migrations/zero_downtime/update_mappings.test.ts new file mode 100644 index 0000000000000..5d27c32c1ee6a --- /dev/null +++ b/src/core/server/integration_tests/saved_objects/migrations/zero_downtime/update_mappings.test.ts @@ -0,0 +1,155 @@ +/* + * 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 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import Path from 'path'; +import fs from 'fs/promises'; +import JSON5 from 'json5'; +import { LogRecord } from '@kbn/logging'; +import { createTestServers, type TestElasticsearchUtils } from '@kbn/core-test-helpers-kbn-server'; +import { + getKibanaMigratorTestKit, + type KibanaMigratorTestKitParams, +} from '../kibana_migrator_test_kit'; +import { delay } from '../test_utils'; +import { getFooType, getBarType, dummyModelVersion } from './base_types.fixtures'; + +export const logFilePath = Path.join(__dirname, 'update_mappings.test.log'); + +describe('ZDT upgrades - basic mapping update', () => { + let esServer: TestElasticsearchUtils['es']; + + const startElasticsearch = async () => { + const { startES } = createTestServers({ + adjustTimeout: (t: number) => jest.setTimeout(t), + settings: { + es: { + license: 'basic', + }, + }, + }); + return await startES(); + }; + + const baseMigratorParams: KibanaMigratorTestKitParams = { + kibanaIndex: '.kibana', + kibanaVersion: '8.7.0', + settings: { + migrations: { + algorithm: 'zdt', + }, + }, + }; + + beforeAll(async () => { + await fs.unlink(logFilePath).catch(() => {}); + esServer = await startElasticsearch(); + }); + + afterAll(async () => { + await esServer?.stop(); + await delay(10); + }); + + const createBaseline = async () => { + const fooType = getFooType(); + const barType = getBarType(); + const { runMigrations } = await getKibanaMigratorTestKit({ + ...baseMigratorParams, + types: [fooType, barType], + }); + await runMigrations(); + }; + + it('updates the mappings and the meta', async () => { + await createBaseline(); + + const fooType = getFooType(); + const barType = getBarType(); + + // increasing the model version of the types + fooType.modelVersions = { + ...fooType.modelVersions, + '3': dummyModelVersion, + }; + fooType.mappings.properties = { + ...fooType.mappings.properties, + someAddedField: { type: 'keyword' }, + }; + + barType.modelVersions = { + ...barType.modelVersions, + '2': dummyModelVersion, + }; + barType.mappings.properties = { + ...barType.mappings.properties, + anotherAddedField: { type: 'boolean' }, + }; + + const { runMigrations, client } = await getKibanaMigratorTestKit({ + ...baseMigratorParams, + logFilePath, + types: [fooType, barType], + }); + + const result = await runMigrations(); + + expect(result).toEqual([ + { + destIndex: '.kibana', + elapsedMs: expect.any(Number), + status: 'patched', + }, + ]); + + const indices = await client.indices.get({ index: '.kibana*' }); + + expect(Object.keys(indices)).toEqual(['.kibana_1']); + + const index = indices['.kibana_1']; + const aliases = Object.keys(index.aliases ?? {}).sort(); + const mappings = index.mappings ?? {}; + const mappingMeta = mappings._meta ?? {}; + + expect(aliases).toEqual(['.kibana', '.kibana_8.7.0']); + + expect(mappings.properties).toEqual( + expect.objectContaining({ + foo: fooType.mappings, + bar: barType.mappings, + }) + ); + + expect(mappingMeta).toEqual({ + // doc migration not implemented yet - docVersions are not bumped. + docVersions: { + foo: 2, + bar: 1, + }, + mappingVersions: { + foo: 3, + bar: 2, + }, + }); + + const logFileContent = await fs.readFile(logFilePath, 'utf-8'); + const records = logFileContent + .split('\n') + .filter(Boolean) + .map((str) => JSON5.parse(str)) as LogRecord[]; + + const expectLogsContains = (messagePrefix: string) => { + expect(records.find((entry) => entry.message.includes(messagePrefix))).toBeDefined(); + }; + + expectLogsContains('INIT -> UPDATE_INDEX_MAPPINGS'); + expectLogsContains('UPDATE_INDEX_MAPPINGS -> UPDATE_INDEX_MAPPINGS_WAIT_FOR_TASK'); + expectLogsContains('UPDATE_INDEX_MAPPINGS_WAIT_FOR_TASK -> UPDATE_MAPPING_MODEL_VERSIONS'); + expectLogsContains('UPDATE_MAPPING_MODEL_VERSIONS -> DONE'); + expectLogsContains('Migration completed'); + }); +}); diff --git a/src/core/tsconfig.json b/src/core/tsconfig.json index d1c69ad2d1e49..20b7911e4f782 100644 --- a/src/core/tsconfig.json +++ b/src/core/tsconfig.json @@ -149,6 +149,8 @@ "@kbn/core-custom-branding-browser", "@kbn/core-custom-branding-server", "@kbn/core-elasticsearch-client-server-internal", + "@kbn/tooling-log", + "@kbn/stdio-dev-helpers", ], "exclude": [ "target/**/*", diff --git a/src/dev/build/tasks/os_packages/docker_generator/resources/base/bin/kibana-docker b/src/dev/build/tasks/os_packages/docker_generator/resources/base/bin/kibana-docker index f23da578d2921..5dc79eefe2229 100755 --- a/src/dev/build/tasks/os_packages/docker_generator/resources/base/bin/kibana-docker +++ b/src/dev/build/tasks/os_packages/docker_generator/resources/base/bin/kibana-docker @@ -300,7 +300,6 @@ kibana_vars=( xpack.ingestManager.fleet.tlsCheckDisabled xpack.ingestManager.registryUrl xpack.observability.annotations.index - xpack.observability.unsafe.slo.enabled xpack.observability.unsafe.alertDetails.apm.enabled xpack.observability.unsafe.alertDetails.metrics.enabled xpack.observability.unsafe.alertDetails.logs.enabled diff --git a/src/plugins/advanced_settings/server/capabilities_provider.ts b/src/plugins/advanced_settings/server/capabilities_provider.ts index 5d22df3ad6a80..760d6f418201b 100644 --- a/src/plugins/advanced_settings/server/capabilities_provider.ts +++ b/src/plugins/advanced_settings/server/capabilities_provider.ts @@ -7,6 +7,10 @@ */ export const capabilitiesProvider = () => ({ + globalSettings: { + show: true, + save: true, + }, advancedSettings: { show: true, save: true, diff --git a/src/plugins/dashboard/public/dashboard_container/embeddable/integrations/diff_state/dashboard_diffing_functions.ts b/src/plugins/dashboard/public/dashboard_container/embeddable/integrations/diff_state/dashboard_diffing_functions.ts index 74f852df484e4..36fd494bd0816 100644 --- a/src/plugins/dashboard/public/dashboard_container/embeddable/integrations/diff_state/dashboard_diffing_functions.ts +++ b/src/plugins/dashboard/public/dashboard_container/embeddable/integrations/diff_state/dashboard_diffing_functions.ts @@ -119,7 +119,11 @@ export const unsavedChangesDiffingFunctions: DashboardDiffFunctions = { }; export const shouldRefreshDiffingFunctions: DashboardDiffFunctions = { - ...unsavedChangesDiffingFunctions, filters: ({ currentValue, lastValue }) => onlyDisabledFiltersChanged(lastValue, currentValue, shouldRefreshFilterCompareOptions), + + // fire on all time range changes, regardless of timeRestore + timeRange: ({ currentValue, lastValue }) => + areTimesEqual(currentValue?.from, lastValue?.from) && + areTimesEqual(currentValue?.to, lastValue?.to), }; diff --git a/src/plugins/dashboard/public/dashboard_container/embeddable/integrations/diff_state/dashboard_diffing_integration.test.ts b/src/plugins/dashboard/public/dashboard_container/embeddable/integrations/diff_state/dashboard_diffing_integration.test.ts index 6c4e9c3989cd7..011fd833b6d55 100644 --- a/src/plugins/dashboard/public/dashboard_container/embeddable/integrations/diff_state/dashboard_diffing_integration.test.ts +++ b/src/plugins/dashboard/public/dashboard_container/embeddable/integrations/diff_state/dashboard_diffing_integration.test.ts @@ -26,38 +26,77 @@ describe('getShouldRefresh', () => { } as DataViewBase ); - test('should return true when pinned filters change', async () => { - const pinnedFilter = pinFilter(existsFilter); - const lastInput = { - filters: [pinnedFilter], - } as unknown as DashboardContainerByValueInput; - const input = { - filters: [toggleFilterNegated(pinnedFilter)], - } as unknown as DashboardContainerByValueInput; - expect(await getShouldRefresh.bind(dashboardContainerMock)(lastInput, { ...lastInput })).toBe( - false - ); - expect(await getShouldRefresh.bind(dashboardContainerMock)(lastInput, input)).toBe(true); - }); + describe('filter changes', () => { + test('should return false when filters do not change', async () => { + const lastInput = { + filters: [existsFilter], + } as unknown as DashboardContainerByValueInput; + expect(await getShouldRefresh.bind(dashboardContainerMock)(lastInput, lastInput)).toBe(false); + }); + + test('should return true when pinned filters change', async () => { + const pinnedFilter = pinFilter(existsFilter); + const lastInput = { + filters: [pinnedFilter], + } as unknown as DashboardContainerByValueInput; + const input = { + filters: [toggleFilterNegated(pinnedFilter)], + } as unknown as DashboardContainerByValueInput; + expect(await getShouldRefresh.bind(dashboardContainerMock)(lastInput, input)).toBe(true); + }); + + test('should return false when disabled filters change', async () => { + const disabledFilter = disableFilter(existsFilter); + const lastInput = { + filters: [disabledFilter], + } as unknown as DashboardContainerByValueInput; + const input = { + filters: [toggleFilterNegated(disabledFilter)], + } as unknown as DashboardContainerByValueInput; + expect(await getShouldRefresh.bind(dashboardContainerMock)(lastInput, input)).toBe(false); + }); - test('should return false when disabled filters change', async () => { - const disabledFilter = disableFilter(existsFilter); - const lastInput = { - filters: [disabledFilter], - } as unknown as DashboardContainerByValueInput; - const input = { - filters: [toggleFilterNegated(disabledFilter)], - } as unknown as DashboardContainerByValueInput; - expect(await getShouldRefresh.bind(dashboardContainerMock)(lastInput, input)).toBe(false); + test('should return false when pinned filter changes to unpinned', async () => { + const lastInput = { + filters: [existsFilter], + } as unknown as DashboardContainerByValueInput; + const input = { + filters: [pinFilter(existsFilter)], + } as unknown as DashboardContainerByValueInput; + expect(await getShouldRefresh.bind(dashboardContainerMock)(lastInput, input)).toBe(false); + }); }); - test('should return false when pinned filter changes to unpinned', async () => { - const lastInput = { - filters: [existsFilter], - } as unknown as DashboardContainerByValueInput; - const input = { - filters: [pinFilter(existsFilter)], - } as unknown as DashboardContainerByValueInput; - expect(await getShouldRefresh.bind(dashboardContainerMock)(lastInput, input)).toBe(false); + describe('timeRange changes', () => { + test('should return false when timeRange does not change', async () => { + const lastInput = { + timeRange: { from: 'now-15m', to: 'now' }, + } as unknown as DashboardContainerByValueInput; + expect(await getShouldRefresh.bind(dashboardContainerMock)(lastInput, lastInput)).toBe(false); + }); + + test('should return true when timeRange changes (timeRestore is true)', async () => { + const lastInput = { + timeRange: { from: 'now-15m', to: 'now' }, + timeRestore: true, + } as unknown as DashboardContainerByValueInput; + const input = { + timeRange: { from: 'now-30m', to: 'now' }, + timeRestore: true, + } as unknown as DashboardContainerByValueInput; + expect(await getShouldRefresh.bind(dashboardContainerMock)(lastInput, input)).toBe(true); + }); + + test('should return true when timeRange changes (timeRestore is false)', async () => { + const lastInput = { + timeRange: { from: 'now-15m', to: 'now' }, + timeRestore: false, + } as unknown as DashboardContainerByValueInput; + const input = { + timeRange: { from: 'now-30m', to: 'now' }, + timeRestore: false, + } as unknown as DashboardContainerByValueInput; + expect(await getShouldRefresh.bind(dashboardContainerMock)(lastInput, input)).toBe(true); + }); }); }); diff --git a/src/plugins/dashboard/public/dashboard_container/embeddable/integrations/diff_state/dashboard_diffing_integration.ts b/src/plugins/dashboard/public/dashboard_container/embeddable/integrations/diff_state/dashboard_diffing_integration.ts index 2062ac3a4b620..3f5f137d829fa 100644 --- a/src/plugins/dashboard/public/dashboard_container/embeddable/integrations/diff_state/dashboard_diffing_integration.ts +++ b/src/plugins/dashboard/public/dashboard_container/embeddable/integrations/diff_state/dashboard_diffing_integration.ts @@ -200,7 +200,11 @@ async function getInputChanges( await container.untilInitialized(); const keyComparePromises = keys.map( (key) => - new Promise<{ key: keyof DashboardContainerByValueInput; isEqual: boolean }>((resolve) => + new Promise<{ key: keyof DashboardContainerByValueInput; isEqual: boolean }>((resolve) => { + if (input[key] === undefined && lastInput[key] === undefined) { + resolve({ key, isEqual: true }); + } + isKeyEqual( key, { @@ -213,8 +217,8 @@ async function getInputChanges( lastInput, }, diffingFunctions - ).then((isEqual) => resolve({ key, isEqual })) - ) + ).then((isEqual) => resolve({ key, isEqual })); + }) ); const inputChanges = (await Promise.allSettled(keyComparePromises)).reduce((changes, current) => { if (current.status === 'fulfilled') { diff --git a/src/plugins/data/common/search/aggs/types.ts b/src/plugins/data/common/search/aggs/types.ts index b7105baa2bd99..4bd20b63945a2 100644 --- a/src/plugins/data/common/search/aggs/types.ts +++ b/src/plugins/data/common/search/aggs/types.ts @@ -36,6 +36,7 @@ import { aggMedian, aggMin, aggMovingAvg, + aggRate, AggParamsAvg, AggParamsBucketAvg, AggParamsBucketAvgSerialized, @@ -309,4 +310,5 @@ export interface AggFunctionsMapping { aggSum: ReturnType; aggTopHit: ReturnType; aggTopMetrics: ReturnType; + aggRate: ReturnType; } diff --git a/src/plugins/embeddable/public/lib/filterable_embeddable/should_fetch.tsx b/src/plugins/embeddable/public/lib/filterable_embeddable/should_fetch.tsx index cf9cba103e011..b8b9fb1f4795f 100644 --- a/src/plugins/embeddable/public/lib/filterable_embeddable/should_fetch.tsx +++ b/src/plugins/embeddable/public/lib/filterable_embeddable/should_fetch.tsx @@ -28,12 +28,13 @@ export function shouldFetch$< // wrapping distinctUntilChanged with startWith and skip to prime distinctUntilChanged with an initial input value. startWith(getInput()), distinctUntilChanged((a: TFilterableEmbeddableInput, b: TFilterableEmbeddableInput) => { - if ( - !fastIsEqual( - [a.searchSessionId, a.query, a.timeRange, a.timeslice], - [b.searchSessionId, b.query, b.timeRange, b.timeslice] - ) - ) { + // Only need to diff searchSessionId when container uses search sessions because + // searchSessionId changes with any filter, query, or time changes + if (a.searchSessionId !== undefined || b.searchSessionId !== undefined) { + return a.searchSessionId === b.searchSessionId; + } + + if (!fastIsEqual([a.query, a.timeRange, a.timeslice], [b.query, b.timeRange, b.timeslice])) { return false; } diff --git a/src/plugins/embeddable/public/lib/filterable_embeddable/types.ts b/src/plugins/embeddable/public/lib/filterable_embeddable/types.ts index 2b6182b1b95db..2aa65b8c332d2 100644 --- a/src/plugins/embeddable/public/lib/filterable_embeddable/types.ts +++ b/src/plugins/embeddable/public/lib/filterable_embeddable/types.ts @@ -12,6 +12,7 @@ import { EmbeddableInput } from '../embeddables'; export type FilterableEmbeddableInput = EmbeddableInput & { filters?: Filter[]; query?: Query; + searchSessionId?: string; timeRange?: TimeRange; timeslice?: [number, number]; }; diff --git a/src/plugins/es_ui_shared/static/forms/hook_form_lib/hooks/use_form_is_modified.ts b/src/plugins/es_ui_shared/static/forms/hook_form_lib/hooks/use_form_is_modified.ts index e5e0fd6d61472..ea8b1602711c1 100644 --- a/src/plugins/es_ui_shared/static/forms/hook_form_lib/hooks/use_form_is_modified.ts +++ b/src/plugins/es_ui_shared/static/forms/hook_form_lib/hooks/use_form_is_modified.ts @@ -5,7 +5,7 @@ * in compliance with, at your election, the Elastic License 2.0 or the Server * Side Public License, v 1. */ -import { useMemo, useState } from 'react'; +import { useEffect, useMemo, useState } from 'react'; import { get } from 'lodash'; import { FieldHook, FormHook } from '../types'; @@ -53,7 +53,7 @@ export const useFormIsModified = ({ const { getFields, __getFieldsRemoved, __getFormDefaultValue } = form; - const discardArrayToString = JSON.stringify(fieldPathsToDiscard); + const discardArrayToString = useMemo(() => JSON.stringify(fieldPathsToDiscard), []); // eslint-disable-line react-hooks/exhaustive-deps // Create a map of the fields to discard to optimize look up const fieldsToDiscard = useMemo(() => { @@ -72,40 +72,48 @@ export const useFormIsModified = ({ // We listen to all the form data change to trigger a re-render // and update our derived "isModified" state - useFormData({ form }); - - const isFieldIncluded = fieldsToDiscard - ? ([path]: [string, FieldHook]) => fieldsToDiscard[path] !== true - : () => true; - - // Calculate next state value - // 1. Check if any field has been modified - let nextIsModified = Object.entries(getFields()) - .filter(isFieldIncluded) - .some(([_, field]) => field.isModified); - - if (!nextIsModified) { - // 2. Check if any field has been removed. - // If somme field has been removed **and** they were originaly present on the - // form "defaultValue" then the form has been modified. - const formDefaultValue = __getFormDefaultValue(); - const fieldOnFormDefaultValue = (path: string) => Boolean(get(formDefaultValue, path)); - - const fieldsRemovedFromDOM: string[] = fieldsToDiscard - ? Object.keys(__getFieldsRemoved()) - .filter((path) => fieldsToDiscard[path] !== true) - .filter(fieldOnFormDefaultValue) - : Object.keys(__getFieldsRemoved()).filter(fieldOnFormDefaultValue); - - nextIsModified = fieldsRemovedFromDOM.length > 0; - } + const formData = useFormData({ form }); + + const isFieldIncluded = useMemo( + () => + fieldsToDiscard + ? ([path]: [string, FieldHook]) => fieldsToDiscard[path] !== true + : () => true, + [fieldsToDiscard] + ); + + useEffect(() => { + // Calculate next state value + // 1. Check if any field has been modified + let nextIsModified = Object.entries(getFields()) + .filter(isFieldIncluded) + .some(([_, field]) => field.isModified); + + if (!nextIsModified) { + // 2. Check if any field has been removed. + // If somme field has been removed **and** they were originaly present on the + // form "defaultValue" then the form has been modified. + const formDefaultValue = __getFormDefaultValue(); + const fieldOnFormDefaultValue = (path: string) => Boolean(get(formDefaultValue, path)); + + const fieldsRemovedFromDOM: string[] = fieldsToDiscard + ? Object.keys(__getFieldsRemoved()) + .filter((path) => fieldsToDiscard[path] !== true) + .filter(fieldOnFormDefaultValue) + : Object.keys(__getFieldsRemoved()).filter(fieldOnFormDefaultValue); + + nextIsModified = fieldsRemovedFromDOM.length > 0; + } - // Update the state **only** if it has changed to avoid creating an infinite re-render - if (nextIsModified && !isFormModified) { - setIsFormModified(true); - } else if (!nextIsModified && isFormModified) { - setIsFormModified(false); - } + setIsFormModified(nextIsModified); + }, [ + __getFieldsRemoved, + __getFormDefaultValue, + fieldsToDiscard, + formData, + getFields, + isFieldIncluded, + ]); return isFormModified; }; diff --git a/src/plugins/guided_onboarding/public/components/guide_panel.styles.ts b/src/plugins/guided_onboarding/public/components/guide_panel.styles.ts index 121bafb23e8ee..fb636b706d93c 100644 --- a/src/plugins/guided_onboarding/public/components/guide_panel.styles.ts +++ b/src/plugins/guided_onboarding/public/components/guide_panel.styles.ts @@ -6,8 +6,15 @@ * Side Public License, v 1. */ -import { EuiThemeComputed } from '@elastic/eui'; +import { + euiCanAnimate, + euiFlyoutSlideInRight, + euiYScrollWithShadows, + logicalCSS, + logicalCSSWithFallback, +} from '@elastic/eui'; import { css } from '@emotion/react'; +import { UseEuiTheme } from '@elastic/eui/src/services/theme/hooks'; import panelBgTop from '../../assets/panel_bg_top.svg'; import panelBgTopDark from '../../assets/panel_bg_top_dark.svg'; import panelBgBottom from '../../assets/panel_bg_bottom.svg'; @@ -21,54 +28,98 @@ import panelBgBottomDark from '../../assets/panel_bg_bottom_dark.svg'; * See https://github.com/elastic/eui/issues/6241 for more details */ export const getGuidePanelStyles = ({ - euiTheme, + euiThemeContext, isDarkTheme, }: { - euiTheme: EuiThemeComputed; + euiThemeContext: UseEuiTheme; isDarkTheme: boolean; -}) => ({ - setupButton: css` - margin-right: ${euiTheme.size.m}; - `, - wellDoneAnimatedPrompt: css` - text-align: left; - `, - flyoutOverrides: { - flyoutHeader: css` - background: url(${isDarkTheme ? panelBgTopDark : panelBgTop}) top right no-repeat; - `, - flyoutContainer: css` - top: 55px !important; - // Unsetting bottom and height default values to create auto height - bottom: unset !important; +}) => { + const euiTheme = euiThemeContext.euiTheme; + const flyoutContainerBase = css` + position: fixed; + height: 100%; + max-height: 76vh; + max-inline-size: 480px; + max-block-size: auto; + inset-inline-end: 0; + inset-block-start: ${euiTheme.size.xxxl}; + ${euiCanAnimate} { + animation: ${euiFlyoutSlideInRight} ${euiTheme.animation.normal} + ${euiTheme.animation.resistance}; + } + @media (min-width: ${euiTheme.breakpoint.m}px) { right: calc(${euiTheme.size.s} + 128px); // Accounting for margin on button - border-radius: 6px; - animation: euiModal 350ms cubic-bezier(0.34, 1.61, 0.7, 1); - box-shadow: none; - max-height: 76vh; - @media (max-width: ${euiTheme.breakpoint.s}px) { - right: 25px !important; - } - `, - flyoutBody: css` - overflow: auto; - .euiFlyoutBody__overflowContent { - width: 480px; - padding-top: 10px; - @media (max-width: ${euiTheme.breakpoint.s}px) { - width: 100%; - } - } - `, - flyoutFooter: css` - border-radius: 0 0 6px 6px; - background: url(${isDarkTheme ? panelBgBottomDark : panelBgBottom}) 0 7px no-repeat; - padding: 24px 30px; - height: 125px; + }) + `; + + return { + setupButton: css` + margin-right: ${euiTheme.size.m}; `, - flyoutFooterLink: css` - color: ${euiTheme.colors.darkShade}; - font-weight: 400; + wellDoneAnimatedPrompt: css` + text-align: left; `, - }, -}); + flyoutOverrides: { + flyoutContainer: css` + ${flyoutContainerBase}; + background: ${euiTheme.colors.emptyShade} url(${isDarkTheme ? panelBgTopDark : panelBgTop}) + top right no-repeat; + padding: 0; + `, + flyoutContainerError: css` + ${flyoutContainerBase}; + padding: 24px; + `, + flyoutHeader: css` + flex-grow: 0; + padding: 16px 16px 0; + `, + flyoutHeaderError: css` + flex-grow: 0; + padding: 8px 0 0; + `, + flyoutContentWrapper: css` + display: flex; + block-size: 100%; + justify-content: space-between; + flex-direction: column; + `, + flyoutCloseButtonIcon: css` + position: absolute; + inset-block-start: ${euiTheme.size.base}; + inset-inline-end: ${euiTheme.size.base}; + `, + flyoutBodyWrapper: css` + ${logicalCSS('height', '100%')} + ${logicalCSSWithFallback('overflow-y', 'hidden')} + flex-grow: 1; + `, + flyoutBody: css` + ${euiYScrollWithShadows(euiThemeContext, { + side: 'end', + })} + padding: 16px 10px 0 16px; + `, + flyoutBodyError: css` + height: 600px; + `, + flyoutStepsWrapper: css` + > li { + list-style-type: none; + } + margin-inline-start: 0 !important; + `, + flyoutFooter: css` + border-radius: 0 0 6px 6px; + background: url(${isDarkTheme ? panelBgBottomDark : panelBgBottom}) 0 36px no-repeat; + padding: 24px 30px; + height: 125px; + flex-grow: 0; + `, + flyoutFooterLink: css` + color: ${euiTheme.colors.darkShade}; + font-weight: 400; + `, + }, + }; +}; diff --git a/src/plugins/guided_onboarding/public/components/guide_panel.tsx b/src/plugins/guided_onboarding/public/components/guide_panel.tsx index e450eaedd85cd..2d3ef51489081 100644 --- a/src/plugins/guided_onboarding/public/components/guide_panel.tsx +++ b/src/plugins/guided_onboarding/public/components/guide_panel.tsx @@ -7,26 +7,7 @@ */ import React, { useState, useEffect, useCallback } from 'react'; -import { - EuiFlyout, - EuiFlyoutBody, - EuiFlyoutHeader, - EuiFlyoutFooter, - EuiButton, - EuiText, - EuiProgress, - EuiHorizontalRule, - EuiSpacer, - htmlIdGenerator, - EuiButtonEmpty, - EuiTitle, - EuiLink, - EuiFlexGroup, - EuiFlexItem, - useEuiTheme, - EuiEmptyPrompt, - EuiImage, -} from '@elastic/eui'; +import { useEuiTheme } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; @@ -38,13 +19,11 @@ import type { GuidedOnboardingApi } from '../types'; import type { PluginState } from '../../common'; -import { GuideStep } from './guide_panel_step'; import { QuitGuideModal } from './quit_guide_modal'; import { getGuidePanelStyles } from './guide_panel.styles'; import { GuideButton } from './guide_button'; -import wellDoneAnimatedGif from '../../assets/well_done_animated.gif'; -import wellDoneAnimatedDarkGif from '../../assets/well_done_animated_dark.gif'; +import { GuidePanelFlyout } from './guide_panel_flyout'; interface GuidePanelProps { api: GuidedOnboardingApi; @@ -65,43 +44,9 @@ const getProgress = (state?: GuideState): number => { return 0; }; -const errorSection = ( - - {i18n.translate('guidedOnboarding.dropdownPanel.errorSectionTitle', { - defaultMessage: 'Unable to load the guide', - })} -

- } - body={ - <> - - {i18n.translate('guidedOnboarding.dropdownPanel.errorSectionDescription', { - defaultMessage: `Wait a moment and try again. If the problem persists, contact your administrator.`, - })} - - - window.location.reload()} - iconType="refresh" - color="danger" - > - {i18n.translate('guidedOnboarding.dropdownPanel.errorSectionReloadButton', { - defaultMessage: 'Reload', - })} - - - } - /> -); - export const GuidePanel = ({ api, application, notifications, uiSettings }: GuidePanelProps) => { - const { euiTheme } = useEuiTheme(); + const euiThemeContext = useEuiTheme(); + const euiTheme = euiThemeContext.euiTheme; const [isGuideOpen, setIsGuideOpen] = useState(false); const [isQuitGuideModalOpen, setIsQuitGuideModalOpen] = useState(false); const [pluginState, setPluginState] = useState(undefined); @@ -109,7 +54,7 @@ export const GuidePanel = ({ api, application, notifications, uiSettings }: Guid const [isLoading, setIsLoading] = useState(false); const isDarkTheme = uiSettings.get('theme:darkMode'); - const styles = getGuidePanelStyles({ euiTheme, isDarkTheme }); + const styles = getGuidePanelStyles({ euiThemeContext, isDarkTheme }); const toggleGuide = () => { setIsGuideOpen((prevIsGuideOpen) => !prevIsGuideOpen); @@ -224,23 +169,7 @@ export const GuidePanel = ({ api, application, notifications, uiSettings }: Guid const stepsCompleted = getProgress(pluginState?.activeGuide); const isGuideReadyToComplete = pluginState?.activeGuide?.status === 'ready_to_complete'; - const getImageUrl = () => { - return isDarkTheme ? wellDoneAnimatedDarkGif : wellDoneAnimatedGif; - }; - const backToGuidesButton = ( - - {i18n.translate('guidedOnboarding.dropdownPanel.backToGuidesLink', { - defaultMessage: 'Back to guides', - })} - - ); return ( <>
@@ -254,228 +183,22 @@ export const GuidePanel = ({ api, application, notifications, uiSettings }: Guid />
- {isGuideOpen && ( - - {guideConfig && pluginState && pluginState.status !== 'error' ? ( - <> - - {backToGuidesButton} - -

- {isGuideReadyToComplete - ? i18n.translate('guidedOnboarding.dropdownPanel.completeGuideFlyoutTitle', { - defaultMessage: 'Well done!', - }) - : guideConfig.title} -

-
- - - -
- - -
- {isGuideReadyToComplete && ( - <> - - - - - )} - - -

- {isGuideReadyToComplete - ? i18n.translate( - 'guidedOnboarding.dropdownPanel.completeGuideFlyoutDescription', - { - defaultMessage: `You've completed the Elastic {guideName} guide. Feel free to come back to the Guides for more onboarding help or a refresher.`, - values: { - guideName: guideConfig.guideName, - }, - } - ) - : guideConfig.description} -

-
- - {guideConfig.docs && ( - <> - - - - {guideConfig.docs.text} - - - - )} - - {/* Progress bar should only show after the first step has been complete */} - {stepsCompleted > 0 && ( - <> - - - - - - )} - - - - {guideConfig?.steps.map((step, index) => { - const accordionId = htmlIdGenerator(`accordion${index}`)(); - const stepState = pluginState?.activeGuide?.steps[index]; - - if (stepState) { - return ( - handleStepButtonClick(stepState, step)} - key={accordionId} - telemetryGuideId={guideConfig!.telemetryId} - /> - ); - } - })} - - {isGuideReadyToComplete && ( - - - completeGuide(guideConfig.completedGuideRedirectLocation)} - fill - // data-test-subj used for FS tracking and testing - data-test-subj={`onboarding--completeGuideButton--${ - guideConfig!.telemetryId - }`} - > - {i18n.translate('guidedOnboarding.dropdownPanel.elasticButtonLabel', { - defaultMessage: 'Continue using Elastic', - })} - - - - )} -
-
- - - - - - {i18n.translate('guidedOnboarding.dropdownPanel.footer.support', { - defaultMessage: 'Need help?', - })} - - - - - | - - - - - {i18n.translate('guidedOnboarding.dropdownPanel.footer.feedback', { - defaultMessage: 'Give feedback', - })} - - - - - | - - - - - {i18n.translate( - 'guidedOnboarding.dropdownPanel.footer.exitGuideButtonLabel', - { - defaultMessage: 'Quit guide', - } - )} - - - - - - ) : ( - - {backToGuidesButton} - {errorSection} - - )} -
- )} + {isQuitGuideModalOpen && ( ; + guideConfig?: GuideConfig; + isDarkTheme: boolean; + stepsCompleted: number; + isGuideReadyToComplete: boolean; + pluginState?: PluginState; + handleStepButtonClick: ( + stepState: GuideStepType, + step: StepConfig + ) => Promise<{ pluginState: PluginState } | undefined>; + isLoading: boolean; + completeGuide: ( + completedGuideRedirectLocation: GuideConfig['completedGuideRedirectLocation'] + ) => Promise; +}) => { + const docsLink = () => { + if (!guideConfig || !guideConfig.docs) { + return null; + } + + return ( + <> + + + + {guideConfig.docs.text} + + + + ); + }; + + if (!guideConfig || !pluginState || (pluginState && pluginState.status === 'error')) { + return ( + + {i18n.translate('guidedOnboarding.dropdownPanel.errorSectionTitle', { + defaultMessage: 'Unable to load the guide', + })} + + } + body={ + <> + + {i18n.translate('guidedOnboarding.dropdownPanel.errorSectionDescription', { + defaultMessage: `Wait a moment and try again. If the problem persists, contact your administrator.`, + })} + + + window.location.reload()} + iconType="refresh" + color="danger" + > + {i18n.translate('guidedOnboarding.dropdownPanel.errorSectionReloadButton', { + defaultMessage: 'Reload', + })} + + + } + /> + ); + } + + if (isGuideReadyToComplete) { + return ( + <> + + + + + +

+ {i18n.translate('guidedOnboarding.dropdownPanel.completeGuideFlyoutDescription', { + defaultMessage: `You've completed the Elastic {guideName} guide. Feel free to come back to the Guides for more onboarding help or a refresher.`, + values: { + guideName: guideConfig.guideName, + }, + })} +

+
+ + {docsLink()} + + + + + + completeGuide(guideConfig.completedGuideRedirectLocation)} + fill + // data-test-subj used for FS tracking and testing + data-test-subj={`onboarding--completeGuideButton--${guideConfig!.telemetryId}`} + > + {i18n.translate('guidedOnboarding.dropdownPanel.elasticButtonLabel', { + defaultMessage: 'Continue using Elastic', + })} + + + + + ); + } + + return ( + <> +
+ +

{guideConfig.description}

+
+ + {docsLink()} + + +
+ + ); +}; diff --git a/src/plugins/guided_onboarding/public/components/guide_panel_flyout/guide_panel_flyout_footer.tsx b/src/plugins/guided_onboarding/public/components/guide_panel_flyout/guide_panel_flyout_footer.tsx new file mode 100644 index 0000000000000..badae4517e989 --- /dev/null +++ b/src/plugins/guided_onboarding/public/components/guide_panel_flyout/guide_panel_flyout_footer.tsx @@ -0,0 +1,87 @@ +/* + * 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 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { EuiButtonEmpty, EuiFlexGroup, EuiFlexItem, EuiText, EuiThemeComputed } from '@elastic/eui'; +import React from 'react'; +import { i18n } from '@kbn/i18n'; +import { getGuidePanelStyles } from '../guide_panel.styles'; + +export const GuidePanelFlyoutFooter = ({ + styles, + euiTheme, + openQuitGuideModal, +}: { + styles: ReturnType; + euiTheme: EuiThemeComputed; + openQuitGuideModal: () => void; +}) => { + return ( +
+ + + + {i18n.translate('guidedOnboarding.dropdownPanel.footer.support', { + defaultMessage: 'Need help?', + })} + + + + + | + + + + + {i18n.translate('guidedOnboarding.dropdownPanel.footer.feedback', { + defaultMessage: 'Give feedback', + })} + + + + + | + + + + + {i18n.translate('guidedOnboarding.dropdownPanel.footer.exitGuideButtonLabel', { + defaultMessage: 'Quit guide', + })} + + + +
+ ); +}; diff --git a/src/plugins/guided_onboarding/public/components/guide_panel_flyout/guide_panel_flyout_header.tsx b/src/plugins/guided_onboarding/public/components/guide_panel_flyout/guide_panel_flyout_header.tsx new file mode 100644 index 0000000000000..b57b305e3331e --- /dev/null +++ b/src/plugins/guided_onboarding/public/components/guide_panel_flyout/guide_panel_flyout_header.tsx @@ -0,0 +1,94 @@ +/* + * 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 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import React, { ReactElement } from 'react'; +import { EuiButtonIcon, EuiHorizontalRule, EuiSpacer, EuiTitle, keys } from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; +import type { GuideConfig } from '@kbn/guided-onboarding'; +import { getGuidePanelStyles } from '../guide_panel.styles'; + +export const GuidePanelFlyoutHeader = ({ + styles, + titleId, + toggleGuide, + hasError, + isGuideReadyToComplete, + guideConfig, + backButton, +}: { + styles: ReturnType; + titleId: string; + toggleGuide: () => void; + hasError: boolean; + isGuideReadyToComplete: boolean; + guideConfig?: GuideConfig; + backButton: ReactElement; +}) => { + /** + * ESC key closes CustomFlyout + */ + const onKeyDown = (event: any) => { + if (event.key === keys.ESCAPE) { + event.preventDefault(); + event.stopPropagation(); + toggleGuide(); + } + }; + + const getTitle = () => { + if (isGuideReadyToComplete) { + return i18n.translate('guidedOnboarding.dropdownPanel.completeGuideFlyoutTitle', { + defaultMessage: 'Well done!', + }); + } + + return guideConfig ? guideConfig.title : ''; + }; + + const closeIcon = ( + + ); + + if (hasError) { + return ( +
+ {backButton} + {closeIcon} +
+ ); + } + + return ( +
+ + + {backButton} + + + + +

+ {getTitle()} +

+
+ + {closeIcon} + + +
+ ); +}; diff --git a/src/plugins/guided_onboarding/public/components/guide_panel_flyout/guide_progress.tsx b/src/plugins/guided_onboarding/public/components/guide_panel_flyout/guide_progress.tsx new file mode 100644 index 0000000000000..c139a6a66dbed --- /dev/null +++ b/src/plugins/guided_onboarding/public/components/guide_panel_flyout/guide_progress.tsx @@ -0,0 +1,94 @@ +/* + * 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 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import React from 'react'; +import { EuiHorizontalRule, EuiProgress, EuiSpacer, htmlIdGenerator } from '@elastic/eui'; +import type { GuideConfig, GuideStep as GuideStepType, StepConfig } from '@kbn/guided-onboarding'; +import { i18n } from '@kbn/i18n'; +import { GuideStep } from '../guide_panel_step'; +import type { PluginState } from '../../../common'; +import { getGuidePanelStyles } from '../guide_panel.styles'; + +export const GuideProgress = ({ + guideConfig, + styles, + pluginState, + isLoading, + stepsCompleted, + isGuideReadyToComplete, + handleStepButtonClick, +}: { + guideConfig: GuideConfig; + styles: ReturnType; + pluginState: PluginState; + isLoading: boolean; + stepsCompleted: number; + isGuideReadyToComplete: boolean; + handleStepButtonClick: (stepState: GuideStepType, step: StepConfig) => void; +}) => { + const { flyoutStepsWrapper } = styles.flyoutOverrides; + + return ( + <> + {/* Progress bar should only show after the first step has been complete */} + {stepsCompleted > 0 && ( + <> + + + + + + )} + + + +
    + {guideConfig?.steps.map((step, index) => { + const accordionId = htmlIdGenerator(`accordion${index}`)(); + const stepState = pluginState?.activeGuide?.steps[index]; + + if (stepState) { + return ( +
  1. + handleStepButtonClick(stepState, step)} + telemetryGuideId={guideConfig!.telemetryId} + /> +
  2. + ); + } + })} +
+ + ); +}; diff --git a/src/plugins/guided_onboarding/public/components/guide_panel_flyout/index.tsx b/src/plugins/guided_onboarding/public/components/guide_panel_flyout/index.tsx new file mode 100644 index 0000000000000..05ff95a035a47 --- /dev/null +++ b/src/plugins/guided_onboarding/public/components/guide_panel_flyout/index.tsx @@ -0,0 +1,140 @@ +/* + * 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 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import React from 'react'; +import { + EuiButtonEmpty, + EuiPanel, + EuiPortal, + EuiOverlayMask, + EuiFocusTrap, + EuiThemeComputed, +} from '@elastic/eui'; +import { GuideConfig, GuideStep as GuideStepType, StepConfig } from '@kbn/guided-onboarding'; +import { i18n } from '@kbn/i18n'; +import { GuidePanelFlyoutHeader } from './guide_panel_flyout_header'; +import { GuidePanelFlyoutBody } from './guide_panel_flyout_body'; +import type { PluginState } from '../../../common'; +import { GuidePanelFlyoutFooter } from './guide_panel_flyout_footer'; +import { getGuidePanelStyles } from '../guide_panel.styles'; + +export const GuidePanelFlyout = ({ + isOpen, + isDarkTheme, + toggleGuide, + isGuideReadyToComplete, + guideConfig, + styles, + navigateToLandingPage, + stepsCompleted, + pluginState, + handleStepButtonClick, + isLoading, + euiTheme, + openQuitGuideModal, + completeGuide, +}: { + isOpen: boolean; + isDarkTheme: boolean; + toggleGuide: () => void; + isGuideReadyToComplete: boolean; + guideConfig?: GuideConfig; + styles: ReturnType; + navigateToLandingPage: () => void; + stepsCompleted: number; + pluginState?: PluginState; + handleStepButtonClick: ( + stepState: GuideStepType, + step: StepConfig + ) => Promise<{ pluginState: PluginState } | undefined>; + isLoading: boolean; + euiTheme: EuiThemeComputed; + openQuitGuideModal: () => void; + completeGuide: ( + completedGuideRedirectLocation: GuideConfig['completedGuideRedirectLocation'] + ) => Promise; +}) => { + if (!isOpen) { + return null; + } + + const guidePanelFlyoutTitleId = 'onboarding-guide'; + const backToGuidesButton = ( + + {i18n.translate('guidedOnboarding.dropdownPanel.backToGuidesLink', { + defaultMessage: 'Back to guides', + })} + + ); + + const hasError = !guideConfig || !pluginState || (pluginState && pluginState.status === 'error'); + const { + flyoutContentWrapper, + flyoutBody, + flyoutBodyWrapper, + flyoutContainerError, + flyoutContainer, + } = styles.flyoutOverrides; + + return ( + + + + +
+ + +
+
+ +
+
+ + {!hasError && ( + + )} +
+
+
+
+
+ ); +}; diff --git a/src/plugins/kibana_utils/public/state_sync/state_sync_state_storage/create_kbn_url_state_storage.test.ts b/src/plugins/kibana_utils/public/state_sync/state_sync_state_storage/create_kbn_url_state_storage.test.ts index ca39d49af3cb1..f52ed8ee9c525 100644 --- a/src/plugins/kibana_utils/public/state_sync/state_sync_state_storage/create_kbn_url_state_storage.test.ts +++ b/src/plugins/kibana_utils/public/state_sync/state_sync_state_storage/create_kbn_url_state_storage.test.ts @@ -102,6 +102,16 @@ describe('KbnUrlStateStorage', () => { expect(cb).toBeCalledWith(expect.any(Error)); }); + it('should notify about errors throttled', () => { + const cb = jest.fn(); + urlStateStorage = createKbnUrlStateStorage({ useHash: false, history, onGetError: cb }); + const key = '_s'; + history.replace(`/#?${key}=(ok:2,test:`); // malformed rison + urlStateStorage.get(key); + urlStateStorage.get(key); + expect(cb).toBeCalledTimes(1); + }); + describe('withNotifyOnErrors integration', () => { test('toast is shown', () => { const toasts = coreMock.createStart().notifications.toasts; diff --git a/src/plugins/kibana_utils/public/state_sync/state_sync_state_storage/create_kbn_url_state_storage.ts b/src/plugins/kibana_utils/public/state_sync/state_sync_state_storage/create_kbn_url_state_storage.ts index 97f048ea0d916..6ad69238608c6 100644 --- a/src/plugins/kibana_utils/public/state_sync/state_sync_state_storage/create_kbn_url_state_storage.ts +++ b/src/plugins/kibana_utils/public/state_sync/state_sync_state_storage/create_kbn_url_state_storage.ts @@ -9,6 +9,7 @@ import { Observable, of } from 'rxjs'; import { catchError, map, share } from 'rxjs/operators'; import { History } from 'history'; +import { throttle } from 'lodash'; import { IStateStorage } from './types'; import { createKbnUrlControls, @@ -74,6 +75,7 @@ export const createKbnUrlStateStorage = ( } ): IKbnUrlStateStorage => { const url = createKbnUrlControls(history); + const onGetErrorThrottled = onGetError ? throttle((e) => onGetError(e), 100) : undefined; return { set: ( key: string, @@ -100,7 +102,7 @@ export const createKbnUrlStateStorage = ( try { return getStateFromKbnUrl(key, url.getPendingUrl(), { getFromHashQuery: useHashQuery }); } catch (e) { - if (onGetError) onGetError(e); + if (onGetErrorThrottled) onGetErrorThrottled(e); return null; } }, @@ -116,7 +118,7 @@ export const createKbnUrlStateStorage = ( }).pipe( map(() => getStateFromKbnUrl(key, undefined, { getFromHashQuery: useHashQuery })), catchError((error) => { - if (onGetError) onGetError(error); + if (onGetErrorThrottled) onGetErrorThrottled(error); return of(null); }), share() diff --git a/src/plugins/usage_collection/common/types/index.ts b/src/plugins/usage_collection/common/types/index.ts new file mode 100644 index 0000000000000..285785a7ad952 --- /dev/null +++ b/src/plugins/usage_collection/common/types/index.ts @@ -0,0 +1,11 @@ +/* + * 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 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +export * as Stats from './stats'; +export * as UiCounters from './ui_counters'; +export * as UsageCounters from './usage_counters'; diff --git a/src/plugins/usage_collection/common/types/stats/core_metrics.ts b/src/plugins/usage_collection/common/types/stats/core_metrics.ts new file mode 100644 index 0000000000000..64e5ef34155d9 --- /dev/null +++ b/src/plugins/usage_collection/common/types/stats/core_metrics.ts @@ -0,0 +1,219 @@ +/* + * 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 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +/** + * The types are exact duplicates of the ops metrics types declared in core needed for reporting in the stats api. + * We use a copy of these to detect changes in the ops metrics reported from core + * See packages/core/metrics/core-metrics-server/index.ts + */ + +/** + * an IntervalHistogram object that samples and reports the event loop delay over time. + * The delays will be reported in milliseconds. + * See {@link IntervalHistogram} + * @public + */ +export interface IntervalHistogram { + // The first timestamp the interval timer kicked in for collecting data points. + fromTimestamp: string; + // Last timestamp the interval timer kicked in for collecting data points. + lastUpdatedAt: string; + // The minimum recorded event loop delay. + min: number; + // The maximum recorded event loop delay. + max: number; + // The mean of the recorded event loop delays. + mean: number; + // The number of times the event loop delay exceeded the maximum 1 hour event loop delay threshold. + exceeds: number; + // The standard deviation of the recorded event loop delays. + stddev: number; + // An object detailing the accumulated percentile distribution. + percentiles: { + // 50th percentile of delays of the collected data points. + 50: number; + // 75th percentile of delays of the collected data points. + 75: number; + // 95th percentile of delays of the collected data points. + 95: number; + // 99th percentile of delays of the collected data points. + 99: number; + }; +} + +/** + * See {@link ElasticsearchClientProtocol} + * Protocol(s) used by the Elasticsearch Client + * @public + */ + +export type ElasticsearchClientProtocol = 'none' | 'http' | 'https' | 'mixed'; + +/** + * See {@link ElasticsearchClientsMetrics} + * Metrics related to the elasticsearch clients + * @public + */ +export interface ElasticsearchClientsMetrics { + /** Total number of active sockets (all nodes, all connections) */ + totalActiveSockets: number; + /** Total number of available sockets (alive but idle, all nodes, all connections) */ + totalIdleSockets: number; + /** Total number of queued requests (all nodes, all connections) */ + totalQueuedRequests: number; +} + +/** + * See {@link OpsProcessMetrics} + * Process related metrics + * @public + */ +export interface OpsProcessMetrics { + /** pid of the kibana process */ + pid: number; + /** process memory usage */ + memory: { + /** heap memory usage */ + heap: { + /** total heap available */ + total_in_bytes: number; + /** used heap */ + used_in_bytes: number; + /** v8 heap size limit */ + size_limit: number; + }; + /** node rss */ + resident_set_size_in_bytes: number; + }; + /** mean event loop delay since last collection*/ + event_loop_delay: number; + /** node event loop delay histogram since last collection */ + event_loop_delay_histogram: IntervalHistogram; + /** uptime of the kibana process */ + uptime_in_millis: number; +} + +/** + * See {@link OpsOsMetrics} + * OS related metrics + * @public + */ +export interface OpsOsMetrics { + /** The os platform */ + platform: NodeJS.Platform; + /** The os platform release, prefixed by the platform name */ + platformRelease: string; + /** The os distrib. Only present for linux platforms */ + distro?: string; + /** The os distrib release, prefixed by the os distrib. Only present for linux platforms */ + distroRelease?: string; + /** cpu load metrics */ + load: { + /** load for last minute */ + '1m': number; + /** load for last 5 minutes */ + '5m': number; + /** load for last 15 minutes */ + '15m': number; + }; + /** system memory usage metrics */ + memory: { + /** total memory available */ + total_in_bytes: number; + /** current free memory */ + free_in_bytes: number; + /** current used memory */ + used_in_bytes: number; + }; + /** the OS uptime */ + uptime_in_millis: number; + + /** cpu accounting metrics, undefined when not running in a cgroup */ + cpuacct?: { + /** name of this process's cgroup */ + control_group: string; + /** cpu time used by this process's cgroup */ + usage_nanos: number; + }; + + /** cpu cgroup metrics, undefined when not running in a cgroup */ + cpu?: { + /** name of this process's cgroup */ + control_group: string; + /** the length of the cfs period */ + cfs_period_micros: number; + /** total available run-time within a cfs period */ + cfs_quota_micros: number; + /** current stats on the cfs periods */ + stat: { + /** number of cfs periods that elapsed */ + number_of_elapsed_periods: number; + /** number of times the cgroup has been throttled */ + number_of_times_throttled: number; + /** total amount of time the cgroup has been throttled for */ + time_throttled_nanos: number; + }; + }; +} + +/** + * {@link OpsServerMetrics} + * server related metrics + * @public + */ +export interface OpsServerMetrics { + /** server response time stats */ + response_times: { + /** average response time */ + avg_in_millis: number; + /** maximum response time */ + max_in_millis: number; + }; + /** server requests stats */ + requests: { + /** number of disconnected requests since startup */ + disconnects: number; + /** total number of requests handled since startup */ + total: number; + /** number of request handled per response status code */ + statusCodes: Record; + }; + /** number of current concurrent connections to the server */ + concurrent_connections: number; +} + +/** + * {@link OpsMetrics} + * Regroups metrics gathered by all the collectors. + * This contains metrics about the os/runtime, the kibana process and the http server. + + * @public + */ +export interface OpsMetrics { + /** Time metrics were recorded at. */ + collected_at: Date; + /** + * Metrics related to the elasticsearch client + */ + elasticsearch_client: ElasticsearchClientsMetrics; + /** + * Process related metrics. + * @remarks processes field preferred + */ + process: OpsProcessMetrics; + /** Process related metrics. Reports an array of objects for each kibana pid.*/ + processes: OpsProcessMetrics[]; + /** OS related metrics */ + os: OpsOsMetrics; + /** server response time stats */ + response_times: OpsServerMetrics['response_times']; + /** server requests stats */ + requests: OpsServerMetrics['requests']; + /** number of current concurrent connections to the server */ + concurrent_connections: OpsServerMetrics['concurrent_connections']; +} diff --git a/src/plugins/usage_collection/common/types/stats/index.ts b/src/plugins/usage_collection/common/types/stats/index.ts new file mode 100644 index 0000000000000..f14c7c0580841 --- /dev/null +++ b/src/plugins/usage_collection/common/types/stats/index.ts @@ -0,0 +1,10 @@ +/* + * 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 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +export * as v1 from './v1'; +export * from './latest'; diff --git a/src/plugins/usage_collection/common/types/stats/latest.ts b/src/plugins/usage_collection/common/types/stats/latest.ts new file mode 100644 index 0000000000000..5d3faf872d5d6 --- /dev/null +++ b/src/plugins/usage_collection/common/types/stats/latest.ts @@ -0,0 +1,9 @@ +/* + * 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 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ +export * as OpsMetricsCopy from './core_metrics'; +export * from './v1'; diff --git a/src/plugins/usage_collection/common/types/stats/v1.ts b/src/plugins/usage_collection/common/types/stats/v1.ts new file mode 100644 index 0000000000000..97581a84cb813 --- /dev/null +++ b/src/plugins/usage_collection/common/types/stats/v1.ts @@ -0,0 +1,70 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import * as OpsMetricsCopy from './core_metrics'; + +/** v1 types Start */ +/** + * stats query v1 + * @remarks exclude_usage is always interpreted as true. query param retained to prevent breaking changes to existing consumers. + */ +export interface StatsHTTPQuery { + extended?: boolean | ''; + legacy?: boolean | ''; + exclude_usage?: boolean | ''; +} + +export interface UsageObject { + kibana?: UsageObject; + xpack?: UsageObject; + [key: string]: unknown | UsageObject; +} + +export interface ClusterUuidLegacy { + clusterUuid?: string; +} +export interface ClusterUuid { + cluster_uuid?: string; +} +/** + * Extended usage stats. + * @remarks + * Legacy implementation used to conditionally include kibana usage metrics + * as of https://github.com/elastic/kibana/pull/151082, usage is no longer reported + * and set to an empty object to prevent breaking changes to existing consumers. + */ +export interface ExtendedStats { + [key: string]: unknown | UsageObject; + clusterUuid?: string; // camel case if legacy === true + cluster_uuid?: string; // snake_case if legacy === false +} +/** + * OpsMetrics: aliased from a duplicate of core's OpsMetrics types + * @remarks the alternative to creating a local copy of the OpsMetrics types is to declare them as `unknown` and assume validation happens elsewhere. + * The disadvantage is that any changes made to the original OpsMetrics will be passed through without needing to update the API types. + */ +export type LastOpsMetrics = OpsMetricsCopy.OpsMetrics; + +export type KibanaServiceStatus = Record; +/** explicitly typed stats for kibana */ +export interface KibanaStats { + // kibana + kibana: { + uuid: string; + name: string; + index: string; + host: string; + locale: string; + transport_address: string; + version: string; + snapshot: boolean; + status: string; + }; +} +/** Stats response body */ +export type StatsHTTPBodyTyped = LastOpsMetrics | KibanaStats | ExtendedStats; diff --git a/src/plugins/usage_collection/common/types/ui_counters/index.ts b/src/plugins/usage_collection/common/types/ui_counters/index.ts new file mode 100644 index 0000000000000..f14c7c0580841 --- /dev/null +++ b/src/plugins/usage_collection/common/types/ui_counters/index.ts @@ -0,0 +1,10 @@ +/* + * 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 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +export * as v1 from './v1'; +export * from './latest'; diff --git a/src/plugins/usage_collection/common/types/ui_counters/latest.ts b/src/plugins/usage_collection/common/types/ui_counters/latest.ts new file mode 100644 index 0000000000000..f966e698fb59e --- /dev/null +++ b/src/plugins/usage_collection/common/types/ui_counters/latest.ts @@ -0,0 +1,9 @@ +/* + * 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 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +export * as v1 from './v1'; diff --git a/src/plugins/usage_collection/common/types/ui_counters/v1.ts b/src/plugins/usage_collection/common/types/ui_counters/v1.ts new file mode 100644 index 0000000000000..fba90a4956666 --- /dev/null +++ b/src/plugins/usage_collection/common/types/ui_counters/v1.ts @@ -0,0 +1,58 @@ +/* + * 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 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +/** + * ui_counters query v1 + * @remarks + */ +export interface UiCountersRequestBody { + report: { + reportVersion?: 3; + userAgent?: Record< + string, + Readonly< + {} & { + key: string; + type: string; + appName: string; + userAgent: string; + } + > + >; + uiCounter?: Record< + string, + Readonly< + {} & { + key: string; + type: string; + appName: string; + eventName: string; + total: number; + } + > + >; + application_usage?: Record< + string, + Readonly<{ + minutesOnScreen: number; + numberOfClicks: number; + appId: string; + viewId: string; + }> + >; + }; +} +/** explicit response type for store report success. The status value is hardcoded. */ +export interface UiCountersResponseOk { + status: 'ok'; +} + +/** explicit response type for store report fail. The status value is hardcoded. */ +export interface UiCountersResponseFail { + status: 'fail'; +} diff --git a/src/plugins/usage_collection/common/types/usage_counters/index.ts b/src/plugins/usage_collection/common/types/usage_counters/index.ts new file mode 100644 index 0000000000000..f14c7c0580841 --- /dev/null +++ b/src/plugins/usage_collection/common/types/usage_counters/index.ts @@ -0,0 +1,10 @@ +/* + * 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 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +export * as v1 from './v1'; +export * from './latest'; diff --git a/src/plugins/usage_collection/common/types/usage_counters/latest.ts b/src/plugins/usage_collection/common/types/usage_counters/latest.ts new file mode 100644 index 0000000000000..e9c79f0f50f93 --- /dev/null +++ b/src/plugins/usage_collection/common/types/usage_counters/latest.ts @@ -0,0 +1,9 @@ +/* + * 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 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +export * from './v1'; diff --git a/src/plugins/usage_collection/common/types/usage_counters/v1.ts b/src/plugins/usage_collection/common/types/usage_counters/v1.ts new file mode 100644 index 0000000000000..fa53a9d9e93b0 --- /dev/null +++ b/src/plugins/usage_collection/common/types/usage_counters/v1.ts @@ -0,0 +1,26 @@ +/* + * 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 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +export interface CounterMetric { + domainId: string; + counterName: string; + counterType: string; + incrementBy: number; +} + +/** + * Details about the counter to be incremented + */ +export interface IncrementCounterParams { + /** The name of the counter **/ + counterName: string; + /** The counter type ("count" by default) **/ + counterType?: string; + /** Increment the counter by this number (1 if not specified) **/ + incrementBy?: number; +} diff --git a/src/plugins/usage_collection/public/services/create_reporter.ts b/src/plugins/usage_collection/public/services/create_reporter.ts index 95d5fff9cc943..1fbf17fe2e49a 100644 --- a/src/plugins/usage_collection/public/services/create_reporter.ts +++ b/src/plugins/usage_collection/public/services/create_reporter.ts @@ -8,6 +8,7 @@ import { Reporter, Storage } from '@kbn/analytics'; import { HttpSetup } from '@kbn/core/public'; +import { UiCounters } from '../../common/types'; interface AnalyicsReporterConfig { localStorage: Storage; @@ -27,8 +28,8 @@ export function createReporter(config: AnalyicsReporterConfig): Reporter { body: JSON.stringify({ report }), asSystemRequest: true, }); - - if (response.status !== 'ok') { + const okStatus: UiCounters.v1.UiCountersResponseOk = response.status; + if (response.status !== okStatus) { throw Error('Unable to store report.'); } return response; diff --git a/src/plugins/usage_collection/server/routes/stats/stats.ts b/src/plugins/usage_collection/server/routes/stats/stats.ts index 242e93a7554e3..d98457d498244 100644 --- a/src/plugins/usage_collection/server/routes/stats/stats.ts +++ b/src/plugins/usage_collection/server/routes/stats/stats.ts @@ -18,14 +18,9 @@ import { ServiceStatusLevels, } from '@kbn/core/server'; import { CollectorSet } from '../../collector'; +import { Stats } from '../../../common/types'; const SNAPSHOT_REGEX = /-snapshot/i; -interface UsageObject { - kibana?: UsageObject; - xpack?: UsageObject; - [key: string]: unknown | UsageObject; -} - export function registerStatsRoute({ router, config, @@ -73,28 +68,22 @@ export function registerStatsRoute({ }, }, async (context, req, res) => { - const isExtended = req.query.extended === '' || req.query.extended; - const isLegacy = req.query.legacy === '' || req.query.legacy; + const requestQuery: Stats.v1.StatsHTTPQuery = req.query; + const isExtended = requestQuery.extended === '' || requestQuery.extended; + const isLegacy = requestQuery.legacy === '' || requestQuery.legacy; let extended; if (isExtended) { const core = await context.core; const { asCurrentUser } = core.elasticsearch.client; + // as of https://github.com/elastic/kibana/pull/151082, usage will always be an empty object. - const usage = {} as UsageObject; const clusterUuid = await getClusterUuid(asCurrentUser); - - // In an effort to make telemetry more easily augmented, we need to ensure - // we can passthrough the data without every part of the process needing - // to know about the change; however, to support legacy use cases where this - // wasn't true, we need to be backwards compatible with how the legacy data - // looked and support those use cases here. - extended = isLegacy - ? { usage, clusterUuid } - : collectorSet.toApiFieldNames({ - usage, - clusterUuid, - }); + const extendedClusterUuid = isLegacy ? { clusterUuid } : { cluster_uuid: clusterUuid }; + extended = { + usage: {}, + ...extendedClusterUuid, + }; } // Guaranteed to resolve immediately due to replay effect on getOpsMetrics$ @@ -120,17 +109,19 @@ export function registerStatsRoute({ collection_interval_in_millis: metrics.collectionInterval, }); + const body: Stats.v1.StatsHTTPBodyTyped = { + ...kibanaStats, + ...extended, + }; + return res.ok({ - body: { - ...kibanaStats, - ...extended, - }, + body, }); } ); } -const ServiceStatusToLegacyState: Record = { +const ServiceStatusToLegacyState: Stats.v1.KibanaServiceStatus = { [ServiceStatusLevels.critical.toString()]: 'red', [ServiceStatusLevels.unavailable.toString()]: 'red', [ServiceStatusLevels.degraded.toString()]: 'yellow', diff --git a/src/plugins/usage_collection/server/routes/ui_counters.ts b/src/plugins/usage_collection/server/routes/ui_counters.ts index f4d7385195012..9752afeae3ef9 100644 --- a/src/plugins/usage_collection/server/routes/ui_counters.ts +++ b/src/plugins/usage_collection/server/routes/ui_counters.ts @@ -10,6 +10,7 @@ import { schema } from '@kbn/config-schema'; import { IRouter, ISavedObjectsRepository } from '@kbn/core/server'; import { storeReport, reportSchema } from '../report'; import { UsageCounter } from '../usage_counters'; +import { UiCounters } from '../../common/types'; export function registerUiCountersRoute( router: IRouter, @@ -26,16 +27,22 @@ export function registerUiCountersRoute( }, }, async (context, req, res) => { - const { report } = req.body; + const requestBody: UiCounters.v1.UiCountersRequestBody = req.body; try { const internalRepository = getSavedObjects(); if (!internalRepository) { throw Error(`The saved objects client hasn't been initialised yet`); } - await storeReport(internalRepository, uiCountersUsageCounter, report); - return res.ok({ body: { status: 'ok' } }); + await storeReport(internalRepository, uiCountersUsageCounter, requestBody.report); + const bodyOk: UiCounters.v1.UiCountersResponseOk = { + status: 'ok', + }; + return res.ok({ body: bodyOk }); } catch (error) { - return res.ok({ body: { status: 'fail' } }); + const bodyFail: UiCounters.v1.UiCountersResponseFail = { + status: 'fail', + }; + return res.ok({ body: bodyFail }); } } ); diff --git a/src/plugins/usage_collection/server/usage_counters/index.ts b/src/plugins/usage_collection/server/usage_counters/index.ts index cc1ee77ef3ea4..05d36aaef502f 100644 --- a/src/plugins/usage_collection/server/usage_counters/index.ts +++ b/src/plugins/usage_collection/server/usage_counters/index.ts @@ -5,10 +5,12 @@ * in compliance with, at your election, the Elastic License 2.0 or the Server * Side Public License, v 1. */ +import { UsageCounters } from '../../common/types'; +export type IncrementCounterParams = UsageCounters.v1.IncrementCounterParams; export type { UsageCountersServiceSetup } from './usage_counters_service'; export type { UsageCountersSavedObjectAttributes, UsageCountersSavedObject } from './saved_objects'; -export type { IUsageCounter as UsageCounter, IncrementCounterParams } from './usage_counter'; +export type { IUsageCounter as UsageCounter } from './usage_counter'; export { UsageCountersService } from './usage_counters_service'; export type { SerializeCounterParams } from './saved_objects'; diff --git a/src/plugins/usage_collection/server/usage_counters/saved_objects.test.ts b/src/plugins/usage_collection/server/usage_counters/saved_objects.test.ts index cb3c5e7683b16..fa40fd5a8d2be 100644 --- a/src/plugins/usage_collection/server/usage_counters/saved_objects.test.ts +++ b/src/plugins/usage_collection/server/usage_counters/saved_objects.test.ts @@ -8,7 +8,9 @@ import { serializeCounterKey, storeCounter } from './saved_objects'; import { savedObjectsRepositoryMock } from '@kbn/core/server/mocks'; -import { CounterMetric } from './usage_counter'; + +import { UsageCounters } from '../../common/types'; + import moment from 'moment'; describe('counterKey', () => { @@ -38,7 +40,7 @@ describe('storeCounter', () => { }); it('stores counter in a saved object', async () => { - const counterMetric: CounterMetric = { + const counterMetric: UsageCounters.v1.CounterMetric = { domainId: 'a', counterName: 'b', counterType: 'c', diff --git a/src/plugins/usage_collection/server/usage_counters/saved_objects.ts b/src/plugins/usage_collection/server/usage_counters/saved_objects.ts index 701bbba404c49..402dabb62b96b 100644 --- a/src/plugins/usage_collection/server/usage_counters/saved_objects.ts +++ b/src/plugins/usage_collection/server/usage_counters/saved_objects.ts @@ -12,7 +12,7 @@ import type { SavedObjectsServiceSetup, } from '@kbn/core/server'; import moment from 'moment'; -import type { CounterMetric } from './usage_counter'; +import { UsageCounters } from '../../common/types'; /** * The attributes stored in the UsageCounters' SavedObjects @@ -83,7 +83,7 @@ export const serializeCounterKey = ({ }; export const storeCounter = async ( - counterMetric: CounterMetric, + counterMetric: UsageCounters.v1.CounterMetric, internalRepository: Pick ) => { const { counterName, counterType, domainId, incrementBy } = counterMetric; diff --git a/src/plugins/usage_collection/server/usage_counters/usage_counter.test.ts b/src/plugins/usage_collection/server/usage_counters/usage_counter.test.ts index 3602ff1a29376..e22423b2283cf 100644 --- a/src/plugins/usage_collection/server/usage_counters/usage_counter.test.ts +++ b/src/plugins/usage_collection/server/usage_counters/usage_counter.test.ts @@ -5,13 +5,14 @@ * in compliance with, at your election, the Elastic License 2.0 or the Server * Side Public License, v 1. */ -import { UsageCounter, CounterMetric } from './usage_counter'; +import { UsageCounter } from './usage_counter'; +import type { UsageCounters } from '../../common/types'; import * as Rx from 'rxjs'; import * as rxOp from 'rxjs/operators'; describe('UsageCounter', () => { const domainId = 'test-domain-id'; - const counter$ = new Rx.Subject(); + const counter$ = new Rx.Subject(); const usageCounter = new UsageCounter({ domainId, counter$ }); afterAll(() => { diff --git a/src/plugins/usage_collection/server/usage_counters/usage_counter.ts b/src/plugins/usage_collection/server/usage_counters/usage_counter.ts index b8057fdda8eb6..80bd32ae4d1db 100644 --- a/src/plugins/usage_collection/server/usage_counters/usage_counter.ts +++ b/src/plugins/usage_collection/server/usage_counters/usage_counter.ts @@ -7,29 +7,11 @@ */ import * as Rx from 'rxjs'; - -export interface CounterMetric { - domainId: string; - counterName: string; - counterType: string; - incrementBy: number; -} +import { UsageCounters } from '../../common/types'; export interface UsageCounterDeps { domainId: string; - counter$: Rx.Subject; -} - -/** - * Details about the counter to be incremented - */ -export interface IncrementCounterParams { - /** The name of the counter **/ - counterName: string; - /** The counter type ("count" by default) **/ - counterType?: string; - /** Increment the counter by this number (1 if not specified) **/ - incrementBy?: number; + counter$: Rx.Subject; } /** @@ -42,19 +24,19 @@ export interface IUsageCounter { * Notifies the counter about a new event happening so it can increase the count internally. * @param params {@link IncrementCounterParams} */ - incrementCounter: (params: IncrementCounterParams) => void; + incrementCounter: (params: UsageCounters.v1.IncrementCounterParams) => void; } export class UsageCounter implements IUsageCounter { private domainId: string; - private counter$: Rx.Subject; + private counter$: Rx.Subject; constructor({ domainId, counter$ }: UsageCounterDeps) { this.domainId = domainId; this.counter$ = counter$; } - public incrementCounter = (params: IncrementCounterParams) => { + public incrementCounter = (params: UsageCounters.v1.IncrementCounterParams) => { const { counterName, counterType = 'count', incrementBy = 1 } = params; this.counter$.next({ diff --git a/src/plugins/usage_collection/server/usage_counters/usage_counters_service.ts b/src/plugins/usage_collection/server/usage_counters/usage_counters_service.ts index 095f314a8e80b..0218334b7e153 100644 --- a/src/plugins/usage_collection/server/usage_counters/usage_counters_service.ts +++ b/src/plugins/usage_collection/server/usage_counters/usage_counters_service.ts @@ -16,7 +16,8 @@ import { import type { Logger, LogMeta } from '@kbn/core/server'; import moment from 'moment'; -import { CounterMetric, UsageCounter } from './usage_counter'; +import { UsageCounter } from './usage_counter'; +import { UsageCounters } from '../../common/types'; import { registerUsageCountersSavedObjectType, storeCounter, @@ -54,7 +55,7 @@ export class UsageCountersService { private readonly bufferDurationMs: number; private readonly counterSets = new Map(); - private readonly source$ = new Rx.Subject(); + private readonly source$ = new Rx.Subject(); private readonly counter$ = this.source$.pipe(rxOp.multicast(new Rx.Subject()), rxOp.refCount()); private readonly flushCache$ = new Rx.Subject(); @@ -69,7 +70,7 @@ export class UsageCountersService { } public setup = (core: UsageCountersServiceSetupDeps): UsageCountersServiceSetup => { - const cache$ = new Rx.ReplaySubject(); + const cache$ = new Rx.ReplaySubject(); const storingCache$ = new Rx.BehaviorSubject(false); // flush cache data from cache -> source this.flushCache$ @@ -135,7 +136,7 @@ export class UsageCountersService { }; private storeDate$( - counters: CounterMetric[], + counters: UsageCounters.v1.CounterMetric[], internalRepository: Pick ) { return Rx.forkJoin( @@ -170,7 +171,9 @@ export class UsageCountersService { return this.counterSets.get(type); }; - private mergeCounters = (counters: CounterMetric[]): Record => { + private mergeCounters = ( + counters: UsageCounters.v1.CounterMetric[] + ): Record => { const date = moment.now(); return counters.reduce((acc, counter) => { const { counterName, domainId, counterType } = counter; @@ -188,6 +191,6 @@ export class UsageCountersService { incrementBy: existingCounter.incrementBy + counter.incrementBy, }, }; - }, {} as Record); + }, {} as Record); }; } diff --git a/src/plugins/usage_collection/tsconfig.json b/src/plugins/usage_collection/tsconfig.json index f6e360d80b3ad..becc176c0451e 100644 --- a/src/plugins/usage_collection/tsconfig.json +++ b/src/plugins/usage_collection/tsconfig.json @@ -7,7 +7,7 @@ "include": [ "public/**/*", "server/**/*", - "common/*", + "common/**/*", "../../../typings/**/*" ], "kbn_references": [ diff --git a/src/plugins/vis_types/vega/public/__snapshots__/vega_visualization.test.js.snap b/src/plugins/vis_types/vega/public/__snapshots__/vega_visualization.test.js.snap index a81418c79bb0b..f49b0bb75951f 100644 --- a/src/plugins/vis_types/vega/public/__snapshots__/vega_visualization.test.js.snap +++ b/src/plugins/vis_types/vega/public/__snapshots__/vega_visualization.test.js.snap @@ -1,7 +1,7 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`VegaVisualizations VegaVisualization - basics should show vega graph (may fail in dev env) 1`] = `"
"`; +exports[`VegaVisualizations VegaVisualization - basics should show vega graph (may fail in dev env) 1`] = `"
"`; exports[`VegaVisualizations VegaVisualization - basics should show vegalite graph and update on resize (may fail in dev env) 1`] = `"
  • \\"width\\" and \\"height\\" params are ignored because \\"autosize\\" is enabled. Set \\"autosize\\": \\"none\\" to disable
"`; -exports[`VegaVisualizations VegaVisualization - basics should show vegalite graph and update on resize (may fail in dev env) 2`] = `"
  • \\"width\\" and \\"height\\" params are ignored because \\"autosize\\" is enabled. Set \\"autosize\\": \\"none\\" to disable
"`; +exports[`VegaVisualizations VegaVisualization - basics should show vegalite graph and update on resize (may fail in dev env) 2`] = `"
  • \\"width\\" and \\"height\\" params are ignored because \\"autosize\\" is enabled. Set \\"autosize\\": \\"none\\" to disable
"`; diff --git a/src/plugins/visualizations/kibana.jsonc b/src/plugins/visualizations/kibana.jsonc index 7b79606d083d3..d67c2efb7e40c 100644 --- a/src/plugins/visualizations/kibana.jsonc +++ b/src/plugins/visualizations/kibana.jsonc @@ -23,7 +23,9 @@ "dataViews", "dataViewEditor", "unifiedSearch", - "usageCollection" + "usageCollection", + "savedObjectsFinder", + "savedObjectsManagement", ], "optionalPlugins": [ "home", diff --git a/src/plugins/visualizations/public/mocks.ts b/src/plugins/visualizations/public/mocks.ts index d739c17383cef..179e0cbfa53fb 100644 --- a/src/plugins/visualizations/public/mocks.ts +++ b/src/plugins/visualizations/public/mocks.ts @@ -24,6 +24,7 @@ import { savedObjectTaggingOssPluginMock } from '@kbn/saved-objects-tagging-oss- import { screenshotModePluginMock } from '@kbn/screenshot-mode-plugin/public/mocks'; import { fieldFormatsServiceMock } from '@kbn/field-formats-plugin/public/mocks'; import { unifiedSearchPluginMock } from '@kbn/unified-search-plugin/public/mocks'; +import { savedObjectsManagementPluginMock } from '@kbn/saved-objects-management-plugin/public/mocks'; import { VisualizationsPlugin } from './plugin'; import { Schemas } from './vis_types'; import { Schema, VisualizationsSetup, VisualizationsStart } from '.'; @@ -80,6 +81,7 @@ const createInstance = async () => { usageCollection: { reportUiCounter: jest.fn(), }, + savedObjectsManagement: savedObjectsManagementPluginMock.createStartContract(), }); return { diff --git a/src/plugins/visualizations/public/plugin.ts b/src/plugins/visualizations/public/plugin.ts index 291b20028917b..a5b0b6baa24e7 100644 --- a/src/plugins/visualizations/public/plugin.ts +++ b/src/plugins/visualizations/public/plugin.ts @@ -57,6 +57,7 @@ import type { ScreenshotModePluginStart } from '@kbn/screenshot-mode-plugin/publ import type { HomePublicPluginSetup } from '@kbn/home-plugin/public'; import type { SpacesPluginStart } from '@kbn/spaces-plugin/public'; import type { DataViewEditorStart } from '@kbn/data-view-editor-plugin/public'; +import { SavedObjectsManagementPluginStart } from '@kbn/saved-objects-management-plugin/public'; import type { TypesSetup, TypesStart } from './vis_types'; import type { VisualizeServices } from './visualize_app/types'; import { @@ -98,6 +99,7 @@ import { setFieldFormats, setSavedObjectTagging, setUsageCollection, + setSavedObjectsManagement, } from './services'; import { VisualizeConstants } from '../common/constants'; import { EditInLensAction } from './actions/edit_in_lens_action'; @@ -147,6 +149,7 @@ export interface VisualizationsStartDeps { fieldFormats: FieldFormatsStart; unifiedSearch: UnifiedSearchPublicPluginStart; usageCollection: UsageCollectionStart; + savedObjectsManagement: SavedObjectsManagementPluginStart; } /** @@ -379,6 +382,7 @@ export class VisualizationsPlugin savedObjectsTaggingOss, fieldFormats, usageCollection, + savedObjectsManagement, }: VisualizationsStartDeps ): VisualizationsStart { const types = this.types.start(); @@ -399,6 +403,7 @@ export class VisualizationsPlugin setChrome(core.chrome); setFieldFormats(fieldFormats); setUsageCollection(usageCollection); + setSavedObjectsManagement(savedObjectsManagement); if (spaces) { setSpaces(spaces); diff --git a/src/plugins/visualizations/public/services.ts b/src/plugins/visualizations/public/services.ts index 8227df9a52b20..df86e5a335645 100644 --- a/src/plugins/visualizations/public/services.ts +++ b/src/plugins/visualizations/public/services.ts @@ -27,6 +27,7 @@ import type { EmbeddableStart } from '@kbn/embeddable-plugin/public'; import type { SpacesPluginStart } from '@kbn/spaces-plugin/public'; import type { SavedObjectTaggingOssPluginStart } from '@kbn/saved-objects-tagging-oss-plugin/public'; import type { UsageCollectionStart } from '@kbn/usage-collection-plugin/public'; +import { SavedObjectsManagementPluginStart } from '@kbn/saved-objects-management-plugin/public'; import type { TypesStart } from './vis_types'; export const [getUISettings, setUISettings] = createGetterSetter('UISettings'); @@ -76,3 +77,6 @@ export const [getSavedObjectTagging, setSavedObjectTagging] = export const [getUsageCollection, setUsageCollection] = createGetterSetter('UsageCollection'); + +export const [getSavedObjectsManagement, setSavedObjectsManagement] = + createGetterSetter('SavedObjectsManagement'); diff --git a/src/plugins/visualizations/public/wizard/new_vis_modal.test.tsx b/src/plugins/visualizations/public/wizard/new_vis_modal.test.tsx index edce87cebe495..0e48386a97be3 100644 --- a/src/plugins/visualizations/public/wizard/new_vis_modal.test.tsx +++ b/src/plugins/visualizations/public/wizard/new_vis_modal.test.tsx @@ -13,6 +13,7 @@ import NewVisModal from './new_vis_modal'; import { ApplicationStart, DocLinksStart } from '@kbn/core/public'; import { embeddablePluginMock } from '@kbn/embeddable-plugin/public/mocks'; import { httpServiceMock } from '@kbn/core-http-browser-mocks'; +import { savedObjectsManagementPluginMock } from '@kbn/saved-objects-management-plugin/public/mocks'; describe('NewVisModal', () => { const defaultVisTypeParams = { @@ -77,6 +78,7 @@ describe('NewVisModal', () => { }, }; const http = httpServiceMock.createStartContract({ basePath: '' }); + const savedObjectsManagement = savedObjectsManagementPluginMock.createStartContract(); beforeAll(() => { Object.defineProperty(window, 'location', { @@ -101,6 +103,7 @@ describe('NewVisModal', () => { application={{} as ApplicationStart} docLinks={docLinks as DocLinksStart} http={http} + savedObjectsManagement={savedObjectsManagement} /> ); expect(wrapper.find('[data-test-subj="visGroup-aggbased"]').exists()).toBe(true); @@ -118,6 +121,7 @@ describe('NewVisModal', () => { application={{} as ApplicationStart} docLinks={docLinks as DocLinksStart} http={http} + savedObjectsManagement={savedObjectsManagement} /> ); expect(wrapper.find('[data-test-subj="visGroup-tools"]').exists()).toBe(true); @@ -134,6 +138,7 @@ describe('NewVisModal', () => { application={{} as ApplicationStart} docLinks={docLinks as DocLinksStart} http={http} + savedObjectsManagement={savedObjectsManagement} /> ); expect(wrapper.find('[data-test-subj="visType-vis2"]').exists()).toBe(true); @@ -151,6 +156,7 @@ describe('NewVisModal', () => { application={{} as ApplicationStart} docLinks={docLinks as DocLinksStart} http={http} + savedObjectsManagement={savedObjectsManagement} /> ); const visCard = wrapper.find('[data-test-subj="visType-vis"]').last(); @@ -170,6 +176,7 @@ describe('NewVisModal', () => { application={{} as ApplicationStart} docLinks={docLinks as DocLinksStart} http={http} + savedObjectsManagement={savedObjectsManagement} /> ); const visCard = wrapper.find('[data-test-subj="visType-vis"]').last(); @@ -196,6 +203,7 @@ describe('NewVisModal', () => { docLinks={docLinks as DocLinksStart} stateTransfer={stateTransfer} http={http} + savedObjectsManagement={savedObjectsManagement} /> ); const visCard = wrapper.find('[data-test-subj="visType-visWithAliasUrl"]').last(); @@ -221,6 +229,7 @@ describe('NewVisModal', () => { application={{ navigateToApp } as unknown as ApplicationStart} docLinks={docLinks as DocLinksStart} http={http} + savedObjectsManagement={savedObjectsManagement} /> ); const visCard = wrapper.find('[data-test-subj="visType-visWithAliasUrl"]').last(); @@ -242,6 +251,7 @@ describe('NewVisModal', () => { application={{} as ApplicationStart} docLinks={docLinks as DocLinksStart} http={http} + savedObjectsManagement={savedObjectsManagement} /> ); const aggBasedGroupCard = wrapper diff --git a/src/plugins/visualizations/public/wizard/new_vis_modal.tsx b/src/plugins/visualizations/public/wizard/new_vis_modal.tsx index ed682ca54e680..4a736c26a6f8e 100644 --- a/src/plugins/visualizations/public/wizard/new_vis_modal.tsx +++ b/src/plugins/visualizations/public/wizard/new_vis_modal.tsx @@ -14,6 +14,7 @@ import { i18n } from '@kbn/i18n'; import { METRIC_TYPE, UiCounterMetricType } from '@kbn/analytics'; import { ApplicationStart, IUiSettingsClient, DocLinksStart, HttpStart } from '@kbn/core/public'; import { EmbeddableStateTransfer } from '@kbn/embeddable-plugin/public'; +import { SavedObjectsManagementPluginStart } from '@kbn/saved-objects-management-plugin/public'; import { SearchSelection } from './search_selection'; import { GroupSelection } from './group_selection'; import { AggBasedSelection } from './agg_based_selection'; @@ -36,6 +37,7 @@ interface TypeSelectionProps { originatingApp?: string; showAggsSelection?: boolean; selectedVisType?: BaseVisType; + savedObjectsManagement: SavedObjectsManagementPluginStart; } interface TypeSelectionState { @@ -93,6 +95,7 @@ class NewVisModal extends React.Component this.setState({ showSearchVisModal: false })} /> diff --git a/src/plugins/visualizations/public/wizard/search_selection/search_selection.tsx b/src/plugins/visualizations/public/wizard/search_selection/search_selection.tsx index 02e5864da9ce0..341123b780445 100644 --- a/src/plugins/visualizations/public/wizard/search_selection/search_selection.tsx +++ b/src/plugins/visualizations/public/wizard/search_selection/search_selection.tsx @@ -12,7 +12,8 @@ import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n-react'; import { IUiSettingsClient, HttpStart } from '@kbn/core/public'; -import { SavedObjectFinderUi } from '@kbn/saved-objects-plugin/public'; +import { SavedObjectsManagementPluginStart } from '@kbn/saved-objects-management-plugin/public'; +import { SavedObjectFinder } from '@kbn/saved-objects-finder-plugin/public'; import type { BaseVisType } from '../../vis_types'; import { DialogNavigation } from '../dialog_navigation'; import { showSavedObject } from './show_saved_object'; @@ -22,12 +23,12 @@ interface SearchSelectionProps { visType: BaseVisType; uiSettings: IUiSettingsClient; http: HttpStart; + savedObjectsManagement: SavedObjectsManagementPluginStart; goBack: () => void; } export class SearchSelection extends React.Component { private fixedPageSize: number = 8; - public render() { return ( @@ -47,7 +48,7 @@ export class SearchSelection extends React.Component { - { }, ]} fixedPageSize={this.fixedPageSize} - uiSettings={this.props.uiSettings} - http={this.props.http} + services={{ + uiSettings: this.props.uiSettings, + http: this.props.http, + savedObjectsManagement: this.props.savedObjectsManagement, + }} /> diff --git a/src/plugins/visualizations/public/wizard/search_selection/show_saved_object.ts b/src/plugins/visualizations/public/wizard/search_selection/show_saved_object.ts index d3d2c15b72004..02085d5acc2ba 100644 --- a/src/plugins/visualizations/public/wizard/search_selection/show_saved_object.ts +++ b/src/plugins/visualizations/public/wizard/search_selection/show_saved_object.ts @@ -6,15 +6,14 @@ * Side Public License, v 1. */ -import type { SimpleSavedObject, SavedObjectAttributes } from '@kbn/core/public'; -import type { FinderAttributes } from '@kbn/saved-objects-plugin/public'; +import type { SavedObjectCommon, FinderAttributes } from '@kbn/saved-objects-finder-plugin/common'; -export interface SavedSearchesAttributes extends SavedObjectAttributes { +export interface SavedSearchesAttributes extends SavedObjectCommon { isTextBasedQuery: boolean; usesAdHocDataView?: boolean; } -export const showSavedObject = (savedObject: SimpleSavedObject) => { - const so = savedObject as unknown as SimpleSavedObject; +export const showSavedObject = (savedObject: SavedObjectCommon) => { + const so = savedObject as unknown as SavedObjectCommon; return !so.attributes.isTextBasedQuery && !so.attributes.usesAdHocDataView; }; diff --git a/src/plugins/visualizations/public/wizard/show_new_vis.tsx b/src/plugins/visualizations/public/wizard/show_new_vis.tsx index 6e298bb036122..3252a92ae8a4c 100644 --- a/src/plugins/visualizations/public/wizard/show_new_vis.tsx +++ b/src/plugins/visualizations/public/wizard/show_new_vis.tsx @@ -19,6 +19,7 @@ import { getEmbeddable, getDocLinks, getTheme, + getSavedObjectsManagement, } from '../services'; import type { BaseVisType } from '../vis_types'; @@ -81,6 +82,7 @@ export function showNewVisModal({ addBasePath={getHttp().basePath.prepend} uiSettings={getUISettings()} http={getHttp()} + savedObjectsManagement={getSavedObjectsManagement()} application={getApplication()} docLinks={getDocLinks()} showAggsSelection={showAggsSelection} diff --git a/src/plugins/visualizations/tsconfig.json b/src/plugins/visualizations/tsconfig.json index 01fdf03133b73..a15ec048cb784 100644 --- a/src/plugins/visualizations/tsconfig.json +++ b/src/plugins/visualizations/tsconfig.json @@ -52,6 +52,8 @@ "@kbn/usage-collection-plugin", "@kbn/core-http-browser-mocks", "@kbn/shared-ux-router", + "@kbn/saved-objects-management-plugin", + "@kbn/saved-objects-finder-plugin", ], "exclude": [ "target/**/*", diff --git a/test/functional/apps/discover/group1/_discover_accessibility.ts b/test/functional/apps/discover/group1/_discover_accessibility.ts index c9213fbe2c4f0..c5cc1df3e4c4c 100644 --- a/test/functional/apps/discover/group1/_discover_accessibility.ts +++ b/test/functional/apps/discover/group1/_discover_accessibility.ts @@ -31,7 +31,8 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { return (await targetElement._webElement.getId()) === (await activeElement._webElement.getId()); }; - describe('discover accessibility', () => { + // Failing: See https://github.com/elastic/kibana/issues/152938 + describe.skip('discover accessibility', () => { before(async () => { log.debug('load kibana index with default index pattern'); await kibanaServer.importExport.load('test/functional/fixtures/kbn_archiver/discover'); diff --git a/test/functional/page_objects/dashboard_page.ts b/test/functional/page_objects/dashboard_page.ts index 1bfcf354b1086..41a99e046aa42 100644 --- a/test/functional/page_objects/dashboard_page.ts +++ b/test/functional/page_objects/dashboard_page.ts @@ -179,10 +179,14 @@ export class DashboardPageObject extends FtrService { await this.testSubjects.click('breadcrumb dashboardListingBreadcrumb first'); } - public async expectOnDashboard(dashboardTitle: string) { + public async expectOnDashboard(expectedTitle: string) { await this.retry.waitFor( - 'last breadcrumb to have dashboard title', - async () => (await this.globalNav.getLastBreadcrumb()) === dashboardTitle + `last breadcrumb to have dashboard title: ${expectedTitle}`, + async () => { + const actualTitle = await this.globalNav.getLastBreadcrumb(); + this.log.debug(`Expected dashboard title ${expectedTitle}, actual: ${actualTitle}`); + return actualTitle === expectedTitle; + } ); } diff --git a/test/functional/services/dashboard/add_panel.ts b/test/functional/services/dashboard/add_panel.ts index 7af97ef7ff32f..c68b6deb46953 100644 --- a/test/functional/services/dashboard/add_panel.ts +++ b/test/functional/services/dashboard/add_panel.ts @@ -15,6 +15,7 @@ export class DashboardAddPanelService extends FtrService { private readonly common = this.ctx.getPageObject('common'); private readonly header = this.ctx.getPageObject('header'); private readonly savedObjectsFinder = this.ctx.getService('savedObjectsFinder'); + private readonly browser = this.ctx.getService('browser'); async clickOpenAddPanel() { this.log.debug('DashboardAddPanel.clickOpenAddPanel'); @@ -26,6 +27,8 @@ export class DashboardAddPanelService extends FtrService { async clickCreateNewLink() { this.log.debug('DashboardAddPanel.clickAddNewPanelButton'); await this.retry.try(async () => { + // prevent query bar auto suggest from blocking button + await this.browser.pressKeys(this.browser.keys.ESCAPE); await this.testSubjects.click('dashboardAddNewPanelButton'); await this.testSubjects.waitForDeleted('dashboardAddNewPanelButton'); await this.header.waitUntilLoadingHasFinished(); diff --git a/test/plugin_functional/test_suites/core_plugins/rendering.ts b/test/plugin_functional/test_suites/core_plugins/rendering.ts index 88cb0f969a776..b0807d0997c06 100644 --- a/test/plugin_functional/test_suites/core_plugins/rendering.ts +++ b/test/plugin_functional/test_suites/core_plugins/rendering.ts @@ -232,7 +232,6 @@ export default function ({ getService }: PluginFunctionalProviderContext) { 'xpack.observability.unsafe.alertDetails.metrics.enabled (boolean)', 'xpack.observability.unsafe.alertDetails.logs.enabled (boolean)', 'xpack.observability.unsafe.alertDetails.uptime.enabled (boolean)', - 'xpack.observability.unsafe.slo.enabled (boolean)', ]; // We don't assert that actualExposedConfigKeys and expectedExposedConfigKeys are equal, because test failure messages with large // arrays are hard to grok. Instead, we take the difference between the two arrays and assert them separately, that way it's diff --git a/x-pack/packages/ml/agg_utils/index.ts b/x-pack/packages/ml/agg_utils/index.ts index c20a31f703ff4..0dc06d6f7df29 100644 --- a/x-pack/packages/ml/agg_utils/index.ts +++ b/x-pack/packages/ml/agg_utils/index.ts @@ -20,11 +20,11 @@ export type { } from './src/fetch_histograms_for_fields'; export type { AggCardinality, - ChangePoint, - ChangePointGroup, - ChangePointGroupHistogram, - ChangePointHistogram, - ChangePointHistogramItem, + SignificantTerm, + SignificantTermGroup, + SignificantTermGroupHistogram, + SignificantTermHistogram, + SignificantTermHistogramItem, HistogramField, NumericColumnStats, NumericColumnStatsMap, diff --git a/x-pack/packages/ml/agg_utils/src/types.ts b/x-pack/packages/ml/agg_utils/src/types.ts index 5cebd7613765b..09368db8f1cd3 100644 --- a/x-pack/packages/ml/agg_utils/src/types.ts +++ b/x-pack/packages/ml/agg_utils/src/types.ts @@ -57,9 +57,9 @@ export interface HistogramField { } /** - * Change point meta data for a field/value pair. + * Significant term meta data for a field/value pair. */ -export interface ChangePoint extends FieldValuePair { +export interface SignificantTerm extends FieldValuePair { doc_count: number; bg_count: number; total_doc_count: number; @@ -67,46 +67,46 @@ export interface ChangePoint extends FieldValuePair { score: number; pValue: number | null; normalizedScore: number; - histogram?: ChangePointHistogramItem[]; + histogram?: SignificantTermHistogramItem[]; unique?: boolean; } /** - * Change point histogram data item. + * Significant term histogram data item. */ -export interface ChangePointHistogramItem { +export interface SignificantTermHistogramItem { doc_count_overall: number; - doc_count_change_point: number; + doc_count_significant_term: number; key: number; key_as_string: string; } /** - * Change point histogram data for a field/value pair. + * Histogram data for a field/value pair. */ -export interface ChangePointHistogram extends FieldValuePair { - histogram: ChangePointHistogramItem[]; +export interface SignificantTermHistogram extends FieldValuePair { + histogram: SignificantTermHistogramItem[]; } /** - * Change point histogram data for a group of field/value pairs. + * Histogram data for a group of field/value pairs. */ -export interface ChangePointGroupHistogram { +export interface SignificantTermGroupHistogram { id: string; - histogram: ChangePointHistogramItem[]; + histogram: SignificantTermHistogramItem[]; } -interface ChangePointGroupItem extends FieldValuePair { +interface SignificantTermGroupItem extends FieldValuePair { duplicate?: boolean; } /** * Tree leaves */ -export interface ChangePointGroup { +export interface SignificantTermGroup { id: string; - group: ChangePointGroupItem[]; + group: SignificantTermGroupItem[]; docCount: number; pValue: number | null; - histogram?: ChangePointHistogramItem[]; + histogram?: SignificantTermHistogramItem[]; } diff --git a/x-pack/packages/ml/aiops_utils/src/get_window_parameters.ts b/x-pack/packages/ml/aiops_utils/src/get_window_parameters.ts index 9408e360ba5eb..7402161db64a1 100644 --- a/x-pack/packages/ml/aiops_utils/src/get_window_parameters.ts +++ b/x-pack/packages/ml/aiops_utils/src/get_window_parameters.ts @@ -22,7 +22,7 @@ export interface WindowParameters { * 2. The historical time window prior to the click to use as a baseline. * * The philosophy here is that charts are displayed with different granularities according to their - * overall time window. We select the change point and historical time windows inline with the + * overall time window. We select the log spike and historical time windows inline with the * overall time window. * * The algorithm for doing this is based on the typical granularities that exist in machine data. diff --git a/x-pack/plugins/actions/common/execution_log_types.ts b/x-pack/plugins/actions/common/execution_log_types.ts index 7fed846f081ca..d28ee5cf50481 100644 --- a/x-pack/plugins/actions/common/execution_log_types.ts +++ b/x-pack/plugins/actions/common/execution_log_types.ts @@ -19,6 +19,7 @@ export interface IExecutionLog { connector_name: string; connector_id: string; timed_out: boolean; + source: string; } export interface IExecutionLogResult { diff --git a/x-pack/plugins/actions/server/action_type_registry.test.ts b/x-pack/plugins/actions/server/action_type_registry.test.ts index 3a7c04da54924..de31b3cff7f7c 100644 --- a/x-pack/plugins/actions/server/action_type_registry.test.ts +++ b/x-pack/plugins/actions/server/action_type_registry.test.ts @@ -8,7 +8,7 @@ import { taskManagerMock } from '@kbn/task-manager-plugin/server/mocks'; import { ActionTypeRegistry, ActionTypeRegistryOpts } from './action_type_registry'; import { ActionType, ExecutorType } from './types'; -import { ActionExecutor, ExecutorError, ILicenseState, TaskRunnerFactory } from './lib'; +import { ActionExecutor, ILicenseState, TaskRunnerFactory } from './lib'; import { actionsConfigMock } from './actions_config.mock'; import { licenseStateMock } from './lib/license_state.mock'; import { ActionsConfigurationUtilities } from './actions_config'; @@ -70,7 +70,6 @@ describe('actionTypeRegistry', () => { Object { "actions:my-action-type": Object { "createTaskRunner": [Function], - "getRetry": [Function], "maxAttempts": 3, "title": "My action type", }, @@ -149,45 +148,6 @@ describe('actionTypeRegistry', () => { ); }); - test('provides a getRetry function that handles ExecutorError', () => { - const actionTypeRegistry = new ActionTypeRegistry(actionTypeRegistryParams); - actionTypeRegistry.register({ - id: 'my-action-type', - name: 'My action type', - minimumLicenseRequired: 'basic', - supportedFeatureIds: ['alerting'], - executor, - }); - expect(mockTaskManager.registerTaskDefinitions).toHaveBeenCalledTimes(1); - const registerTaskDefinitionsCall = mockTaskManager.registerTaskDefinitions.mock.calls[0][0]; - const getRetry = registerTaskDefinitionsCall['actions:my-action-type'].getRetry!; - - const retryTime = new Date(); - expect(getRetry(0, new Error())).toEqual(true); - expect(getRetry(0, new ExecutorError('my message', {}, true))).toEqual(true); - expect(getRetry(0, new ExecutorError('my message', {}, false))).toEqual(false); - expect(getRetry(0, new ExecutorError('my message', {}, undefined))).toEqual(false); - expect(getRetry(0, new ExecutorError('my message', {}, retryTime))).toEqual(retryTime); - }); - - test('provides a getRetry function that handles errors based on maxAttempts', () => { - const actionTypeRegistry = new ActionTypeRegistry(actionTypeRegistryParams); - actionTypeRegistry.register({ - id: 'my-action-type', - name: 'My action type', - minimumLicenseRequired: 'basic', - supportedFeatureIds: ['alerting'], - executor, - maxAttempts: 2, - }); - expect(mockTaskManager.registerTaskDefinitions).toHaveBeenCalledTimes(1); - const registerTaskDefinitionsCall = mockTaskManager.registerTaskDefinitions.mock.calls[0][0]; - const getRetry = registerTaskDefinitionsCall['actions:my-action-type'].getRetry!; - - expect(getRetry(1, new Error())).toEqual(true); - expect(getRetry(3, new Error())).toEqual(false); - }); - test('registers gold+ action types to the licensing feature usage API', () => { const actionTypeRegistry = new ActionTypeRegistry(actionTypeRegistryParams); actionTypeRegistry.register({ diff --git a/x-pack/plugins/actions/server/action_type_registry.ts b/x-pack/plugins/actions/server/action_type_registry.ts index 31203a4c7efab..d274118650b61 100644 --- a/x-pack/plugins/actions/server/action_type_registry.ts +++ b/x-pack/plugins/actions/server/action_type_registry.ts @@ -11,12 +11,7 @@ import { RunContext, TaskManagerSetupContract } from '@kbn/task-manager-plugin/s import { LicensingPluginSetup } from '@kbn/licensing-plugin/server'; import { ActionType as CommonActionType, areValidFeatures } from '../common'; import { ActionsConfigurationUtilities } from './actions_config'; -import { - ExecutorError, - getActionTypeFeatureUsageName, - TaskRunnerFactory, - ILicenseState, -} from './lib'; +import { getActionTypeFeatureUsageName, TaskRunnerFactory, ILicenseState } from './lib'; import { ActionType, PreConfiguredAction, @@ -157,13 +152,6 @@ export class ActionTypeRegistry { [`actions:${actionType.id}`]: { title: actionType.name, maxAttempts, - getRetry(attempts: number, error: unknown) { - if (error instanceof ExecutorError) { - return error.retry == null ? false : error.retry; - } - // Only retry other kinds of errors based on attempts - return attempts < maxAttempts; - }, createTaskRunner: (context: RunContext) => this.taskRunnerFactory.create(context, maxAttempts), }, diff --git a/x-pack/plugins/actions/server/actions_client.test.ts b/x-pack/plugins/actions/server/actions_client.test.ts index a164058d3b87d..3c2d91939e688 100644 --- a/x-pack/plugins/actions/server/actions_client.test.ts +++ b/x-pack/plugins/actions/server/actions_client.test.ts @@ -12,7 +12,12 @@ import { ByteSizeValue } from '@kbn/config-schema'; import { ActionTypeRegistry, ActionTypeRegistryOpts } from './action_type_registry'; import { ActionsClient } from './actions_client'; import { ExecutorType, ActionType } from './types'; -import { ActionExecutor, TaskRunnerFactory, ILicenseState } from './lib'; +import { + ActionExecutor, + TaskRunnerFactory, + ILicenseState, + asHttpRequestExecutionSource, +} from './lib'; import { taskManagerMock } from '@kbn/task-manager-plugin/server/mocks'; import { actionsConfigMock } from './actions_config.mock'; import { getActionsConfigurationUtilities } from './actions_config'; @@ -2171,6 +2176,7 @@ describe('execute()', () => { params: { name: 'my name', }, + source: asHttpRequestExecutionSource(request), }); expect(authorization.ensureAuthorized).toHaveBeenCalledWith('execute'); }); @@ -2189,6 +2195,7 @@ describe('execute()', () => { params: { name: 'my name', }, + source: asHttpRequestExecutionSource(request), }) ).rejects.toMatchInlineSnapshot(`[Error: Unauthorized to execute all actions]`); @@ -2205,6 +2212,7 @@ describe('execute()', () => { params: { name: 'my name', }, + source: asHttpRequestExecutionSource(request), }); expect(trackLegacyRBACExemption as jest.Mock).toBeCalledWith('execute', mockUsageCounter); @@ -2221,6 +2229,7 @@ describe('execute()', () => { params: { name: 'my name', }, + source: asHttpRequestExecutionSource(request), }) ).resolves.toMatchObject({ status: 'ok', actionId }); @@ -2231,6 +2240,7 @@ describe('execute()', () => { name: 'my name', }, actionExecutionId, + source: asHttpRequestExecutionSource(request), }); await expect( @@ -2239,6 +2249,7 @@ describe('execute()', () => { params: { name: 'my name', }, + source: asHttpRequestExecutionSource(request), relatedSavedObjects: [ { id: 'some-id', @@ -2263,6 +2274,7 @@ describe('execute()', () => { }, ], actionExecutionId, + source: asHttpRequestExecutionSource(request), }); await expect( @@ -2271,6 +2283,7 @@ describe('execute()', () => { params: { name: 'my name', }, + source: asHttpRequestExecutionSource(request), relatedSavedObjects: [ { id: 'some-id', @@ -2288,6 +2301,7 @@ describe('execute()', () => { params: { name: 'my name', }, + source: asHttpRequestExecutionSource(request), relatedSavedObjects: [ { id: 'some-id', @@ -2313,6 +2327,7 @@ describe('enqueueExecution()', () => { spaceId: 'default', executionId: '123abc', apiKey: null, + source: asHttpRequestExecutionSource(request), }); expect(authorization.ensureAuthorized).toHaveBeenCalledWith('execute'); }); @@ -2332,6 +2347,7 @@ describe('enqueueExecution()', () => { spaceId: 'default', executionId: '123abc', apiKey: null, + source: asHttpRequestExecutionSource(request), }) ).rejects.toMatchInlineSnapshot(`[Error: Unauthorized to execute all actions]`); @@ -2349,6 +2365,7 @@ describe('enqueueExecution()', () => { spaceId: 'default', executionId: '123abc', apiKey: null, + source: asHttpRequestExecutionSource(request), }); expect(trackLegacyRBACExemption as jest.Mock).toBeCalledWith( @@ -2365,6 +2382,7 @@ describe('enqueueExecution()', () => { spaceId: 'default', executionId: '123abc', apiKey: Buffer.from('123:abc').toString('base64'), + source: asHttpRequestExecutionSource(request), }; await expect(actionsClient.enqueueExecution(opts)).resolves.toMatchInlineSnapshot(`undefined`); @@ -2385,6 +2403,7 @@ describe('bulkEnqueueExecution()', () => { spaceId: 'default', executionId: '123abc', apiKey: null, + source: asHttpRequestExecutionSource(request), }, { id: uuidv4(), @@ -2392,6 +2411,7 @@ describe('bulkEnqueueExecution()', () => { spaceId: 'default', executionId: '456def', apiKey: null, + source: asHttpRequestExecutionSource(request), }, ]); expect(authorization.ensureAuthorized).toHaveBeenCalledWith('execute'); @@ -2413,6 +2433,7 @@ describe('bulkEnqueueExecution()', () => { spaceId: 'default', executionId: '123abc', apiKey: null, + source: asHttpRequestExecutionSource(request), }, { id: uuidv4(), @@ -2420,6 +2441,7 @@ describe('bulkEnqueueExecution()', () => { spaceId: 'default', executionId: '456def', apiKey: null, + source: asHttpRequestExecutionSource(request), }, ]) ).rejects.toMatchInlineSnapshot(`[Error: Unauthorized to execute all actions]`); @@ -2439,6 +2461,7 @@ describe('bulkEnqueueExecution()', () => { spaceId: 'default', executionId: '123abc', apiKey: null, + source: asHttpRequestExecutionSource(request), }, { id: uuidv4(), @@ -2446,6 +2469,7 @@ describe('bulkEnqueueExecution()', () => { spaceId: 'default', executionId: '456def', apiKey: null, + source: asHttpRequestExecutionSource(request), }, ]); @@ -2468,6 +2492,7 @@ describe('bulkEnqueueExecution()', () => { spaceId: 'default', executionId: '123abc', apiKey: null, + source: asHttpRequestExecutionSource(request), }, { id: uuidv4(), @@ -2475,6 +2500,7 @@ describe('bulkEnqueueExecution()', () => { spaceId: 'default', executionId: '456def', apiKey: null, + source: asHttpRequestExecutionSource(request), }, ]; await expect(actionsClient.bulkEnqueueExecution(opts)).resolves.toMatchInlineSnapshot( diff --git a/x-pack/plugins/actions/server/constants/event_log.ts b/x-pack/plugins/actions/server/constants/event_log.ts index 9dba72462f317..e3ae4e8c5b855 100644 --- a/x-pack/plugins/actions/server/constants/event_log.ts +++ b/x-pack/plugins/actions/server/constants/event_log.ts @@ -9,6 +9,5 @@ export const EVENT_LOG_PROVIDER = 'actions'; export const EVENT_LOG_ACTIONS = { execute: 'execute', executeStart: 'execute-start', - executeViaHttp: 'execute-via-http', executeTimeout: 'execute-timeout', }; diff --git a/x-pack/plugins/actions/server/create_execute_function.test.ts b/x-pack/plugins/actions/server/create_execute_function.test.ts index 8147e14d2a943..e4787101b1cde 100644 --- a/x-pack/plugins/actions/server/create_execute_function.test.ts +++ b/x-pack/plugins/actions/server/create_execute_function.test.ts @@ -79,6 +79,7 @@ describe('execute()', () => { actionId: '123', params: { baz: false }, executionId: '123abc', + source: 'HTTP_REQUEST', apiKey: Buffer.from('123:abc').toString('base64'), }, { @@ -151,6 +152,7 @@ describe('execute()', () => { params: { baz: false }, executionId: '123abc', consumer: 'test-consumer', + source: 'HTTP_REQUEST', apiKey: Buffer.from('123:abc').toString('base64'), }, { @@ -213,6 +215,7 @@ describe('execute()', () => { params: { baz: false }, apiKey: Buffer.from('123:abc').toString('base64'), executionId: '123abc', + source: 'HTTP_REQUEST', relatedSavedObjects: [ { id: 'related_some-type_0', @@ -303,6 +306,7 @@ describe('execute()', () => { actionId: '123', params: { baz: false }, executionId: '123abc', + source: 'SAVED_OBJECT', apiKey: Buffer.from('123:abc').toString('base64'), }, { @@ -390,6 +394,7 @@ describe('execute()', () => { params: { baz: false }, apiKey: Buffer.from('123:abc').toString('base64'), executionId: '123abc', + source: 'SAVED_OBJECT', relatedSavedObjects: [ { id: 'related_some-type_0', @@ -430,6 +435,7 @@ describe('execute()', () => { spaceId: 'default', executionId: '123abc', apiKey: null, + source: asHttpRequestExecutionSource(request), }) ).rejects.toThrowErrorMatchingInlineSnapshot( `"Unable to execute action because the Encrypted Saved Objects plugin is missing encryption key. Please set xpack.encryptedSavedObjects.encryptionKey in the kibana.yml or use the bin/kibana-encryption-keys command."` @@ -460,6 +466,7 @@ describe('execute()', () => { spaceId: 'default', executionId: '123abc', apiKey: null, + source: asHttpRequestExecutionSource(request), }) ).rejects.toThrowErrorMatchingInlineSnapshot( `"Unable to execute action because no secrets are defined for the \\"mock-action\\" connector."` @@ -493,6 +500,7 @@ describe('execute()', () => { spaceId: 'default', executionId: '123abc', apiKey: null, + source: asHttpRequestExecutionSource(request), }) ).rejects.toThrowErrorMatchingInlineSnapshot(`"Fail"`); }); @@ -537,6 +545,7 @@ describe('execute()', () => { spaceId: 'default', executionId: '123abc', apiKey: null, + source: asHttpRequestExecutionSource(request), }); expect(mockedActionTypeRegistry.ensureActionTypeEnabled).not.toHaveBeenCalled(); @@ -613,6 +622,7 @@ describe('bulkExecute()', () => { actionId: '123', params: { baz: false }, executionId: '123abc', + source: 'HTTP_REQUEST', apiKey: Buffer.from('123:abc').toString('base64'), }, references: [ @@ -703,6 +713,7 @@ describe('bulkExecute()', () => { params: { baz: false }, executionId: '123abc', consumer: 'test-consumer', + source: 'HTTP_REQUEST', apiKey: Buffer.from('123:abc').toString('base64'), }, references: [ @@ -778,6 +789,7 @@ describe('bulkExecute()', () => { params: { baz: false }, apiKey: Buffer.from('123:abc').toString('base64'), executionId: '123abc', + source: 'HTTP_REQUEST', relatedSavedObjects: [ { id: 'related_some-type_0', @@ -885,6 +897,7 @@ describe('bulkExecute()', () => { actionId: '123', params: { baz: false }, executionId: '123abc', + source: 'SAVED_OBJECT', apiKey: Buffer.from('123:abc').toString('base64'), }, references: [ @@ -989,6 +1002,7 @@ describe('bulkExecute()', () => { params: { baz: false }, apiKey: Buffer.from('123:abc').toString('base64'), executionId: '123abc', + source: 'SAVED_OBJECT', relatedSavedObjects: [ { id: 'related_some-type_0', @@ -1031,6 +1045,7 @@ describe('bulkExecute()', () => { spaceId: 'default', executionId: '123abc', apiKey: null, + source: asHttpRequestExecutionSource(request), }, ]) ).rejects.toThrowErrorMatchingInlineSnapshot( @@ -1067,6 +1082,7 @@ describe('bulkExecute()', () => { spaceId: 'default', executionId: '123abc', apiKey: null, + source: asHttpRequestExecutionSource(request), }, ]) ).rejects.toThrowErrorMatchingInlineSnapshot( @@ -1106,6 +1122,7 @@ describe('bulkExecute()', () => { spaceId: 'default', executionId: '123abc', apiKey: null, + source: asHttpRequestExecutionSource(request), }, ]) ).rejects.toThrowErrorMatchingInlineSnapshot(`"Fail"`); @@ -1162,6 +1179,7 @@ describe('bulkExecute()', () => { spaceId: 'default', executionId: '123abc', apiKey: null, + source: asHttpRequestExecutionSource(request), }, ]); diff --git a/x-pack/plugins/actions/server/create_execute_function.ts b/x-pack/plugins/actions/server/create_execute_function.ts index c050cf34c9fca..c33bcc6923d8a 100644 --- a/x-pack/plugins/actions/server/create_execute_function.ts +++ b/x-pack/plugins/actions/server/create_execute_function.ts @@ -116,6 +116,7 @@ export function createExecutionEnqueuerFunction({ executionId, consumer, relatedSavedObjects: relatedSavedObjectWithRefs, + ...(source ? { source: source.type } : {}), }, { references: taskReferences, @@ -202,6 +203,7 @@ export function createBulkExecutionEnqueuerFunction({ executionId: actionToExecute.executionId, consumer: actionToExecute.consumer, relatedSavedObjects: relatedSavedObjectWithRefs, + ...(actionToExecute.source ? { source: actionToExecute.source.type } : {}), }, references: taskReferences, }; diff --git a/x-pack/plugins/actions/server/create_unsecured_execute_function.test.ts b/x-pack/plugins/actions/server/create_unsecured_execute_function.test.ts index a61d6193a9ab5..cd50be7558fea 100644 --- a/x-pack/plugins/actions/server/create_unsecured_execute_function.test.ts +++ b/x-pack/plugins/actions/server/create_unsecured_execute_function.test.ts @@ -10,7 +10,10 @@ import { savedObjectsRepositoryMock } from '@kbn/core/server/mocks'; import { taskManagerMock } from '@kbn/task-manager-plugin/server/mocks'; import { createBulkUnsecuredExecutionEnqueuerFunction } from './create_unsecured_execute_function'; import { actionTypeRegistryMock } from './action_type_registry.mock'; -import { asSavedObjectExecutionSource } from './lib/action_execution_source'; +import { + asNotificationExecutionSource, + asSavedObjectExecutionSource, +} from './lib/action_execution_source'; const mockTaskManager = taskManagerMock.createStart(); const internalSavedObjectsRepository = savedObjectsRepositoryMock.create(); @@ -59,10 +62,12 @@ describe('bulkExecute()', () => { { id: '123', params: { baz: false }, + source: asNotificationExecutionSource({ connectorId: 'abc', requesterId: 'foo' }), }, { id: '123', params: { baz: true }, + source: asNotificationExecutionSource({ connectorId: 'abc', requesterId: 'foo' }), }, ]); expect(mockTaskManager.bulkSchedule).toHaveBeenCalledTimes(1); @@ -102,6 +107,7 @@ describe('bulkExecute()', () => { actionId: '123', params: { baz: false }, apiKey: null, + source: 'NOTIFICATION', }, references: [], }, @@ -111,6 +117,7 @@ describe('bulkExecute()', () => { actionId: '123', params: { baz: true }, apiKey: null, + source: 'NOTIFICATION', }, references: [], }, @@ -171,6 +178,7 @@ describe('bulkExecute()', () => { { id: '123', params: { baz: true }, + source: asNotificationExecutionSource({ connectorId: 'abc', requesterId: 'foo' }), }, ]); expect(mockTaskManager.bulkSchedule).toHaveBeenCalledTimes(1); @@ -210,6 +218,7 @@ describe('bulkExecute()', () => { actionId: '123', params: { baz: false }, apiKey: null, + source: 'SAVED_OBJECT', }, references: [ { @@ -225,6 +234,7 @@ describe('bulkExecute()', () => { actionId: '123', params: { baz: true }, apiKey: null, + source: 'NOTIFICATION', }, references: [], }, @@ -291,6 +301,7 @@ describe('bulkExecute()', () => { { id: '123', params: { baz: true }, + source: asNotificationExecutionSource({ connectorId: 'abc', requesterId: 'foo' }), relatedSavedObjects: [ { id: 'some-id', @@ -337,6 +348,7 @@ describe('bulkExecute()', () => { actionId: '123', params: { baz: false }, apiKey: null, + source: 'SAVED_OBJECT', }, references: [ { @@ -352,6 +364,7 @@ describe('bulkExecute()', () => { actionId: '123', params: { baz: true }, apiKey: null, + source: 'NOTIFICATION', relatedSavedObjects: [ { id: 'related_some-type_0', @@ -392,10 +405,12 @@ describe('bulkExecute()', () => { { id: '123', params: { baz: false }, + source: asNotificationExecutionSource({ connectorId: 'abc', requesterId: 'foo' }), }, { id: 'not-preconfigured', params: { baz: true }, + source: asNotificationExecutionSource({ connectorId: 'abc', requesterId: 'foo' }), }, ]) ).rejects.toThrowErrorMatchingInlineSnapshot( @@ -429,10 +444,12 @@ describe('bulkExecute()', () => { { id: '123', params: { baz: false }, + source: asNotificationExecutionSource({ connectorId: 'abc', requesterId: 'foo' }), }, { id: '123', params: { baz: true }, + source: asNotificationExecutionSource({ connectorId: 'abc', requesterId: 'foo' }), }, ]) ).rejects.toThrowErrorMatchingInlineSnapshot(`"Fail"`); @@ -468,10 +485,12 @@ describe('bulkExecute()', () => { { id: '123', params: { baz: false }, + source: asNotificationExecutionSource({ connectorId: 'abc', requesterId: 'foo' }), }, { id: '456', params: { baz: true }, + source: asNotificationExecutionSource({ connectorId: 'abc', requesterId: 'foo' }), }, ]) ).rejects.toThrowErrorMatchingInlineSnapshot( diff --git a/x-pack/plugins/actions/server/create_unsecured_execute_function.ts b/x-pack/plugins/actions/server/create_unsecured_execute_function.ts index 4670601ecff83..b1ad90d9093eb 100644 --- a/x-pack/plugins/actions/server/create_unsecured_execute_function.ts +++ b/x-pack/plugins/actions/server/create_unsecured_execute_function.ts @@ -111,6 +111,7 @@ export function createBulkUnsecuredExecutionEnqueuerFunction({ params: actionToExecute.params, apiKey: null, relatedSavedObjects: relatedSavedObjectWithRefs, + ...(actionToExecute.source ? { source: actionToExecute.source.type } : {}), }, references: taskReferences, }; diff --git a/x-pack/plugins/actions/server/index.ts b/x-pack/plugins/actions/server/index.ts index 2713ee17463e4..15f3c2fa7e20a 100644 --- a/x-pack/plugins/actions/server/index.ts +++ b/x-pack/plugins/actions/server/index.ts @@ -29,7 +29,11 @@ export type { export type { PluginSetupContract, PluginStartContract } from './plugin'; -export { asSavedObjectExecutionSource, asHttpRequestExecutionSource } from './lib'; +export { + asSavedObjectExecutionSource, + asHttpRequestExecutionSource, + asNotificationExecutionSource, +} from './lib'; export { ACTION_SAVED_OBJECT_TYPE } from './constants/saved_objects'; export const plugin = (initContext: PluginInitializerContext) => new ActionsPlugin(initContext); diff --git a/x-pack/plugins/actions/server/lib/action_execution_source.ts b/x-pack/plugins/actions/server/lib/action_execution_source.ts index f5e1570dfa4c5..9701b759f981e 100644 --- a/x-pack/plugins/actions/server/lib/action_execution_source.ts +++ b/x-pack/plugins/actions/server/lib/action_execution_source.ts @@ -10,6 +10,7 @@ import { KibanaRequest, SavedObjectReference } from '@kbn/core/server'; export enum ActionExecutionSourceType { SAVED_OBJECT = 'SAVED_OBJECT', HTTP_REQUEST = 'HTTP_REQUEST', + NOTIFICATION = 'NOTIFICATION', } export interface ActionExecutionSource { @@ -19,6 +20,12 @@ export interface ActionExecutionSource { export type HttpRequestExecutionSource = ActionExecutionSource; export type SavedObjectExecutionSource = ActionExecutionSource>; +export interface NotificationSource { + requesterId: string; + connectorId: string; +} +export type NotificationExecutionSource = ActionExecutionSource; + export function asHttpRequestExecutionSource(source: KibanaRequest): HttpRequestExecutionSource { return { type: ActionExecutionSourceType.HTTP_REQUEST, @@ -26,6 +33,13 @@ export function asHttpRequestExecutionSource(source: KibanaRequest): HttpRequest }; } +export function asEmptySource(type: ActionExecutionSourceType): ActionExecutionSource<{}> { + return { + type, + source: {}, + }; +} + export function asSavedObjectExecutionSource( source: Omit ): SavedObjectExecutionSource { @@ -35,6 +49,15 @@ export function asSavedObjectExecutionSource( }; } +export function asNotificationExecutionSource( + source: NotificationSource +): NotificationExecutionSource { + return { + type: ActionExecutionSourceType.NOTIFICATION, + source, + }; +} + export function isHttpRequestExecutionSource( executionSource?: ActionExecutionSource ): executionSource is HttpRequestExecutionSource { @@ -46,3 +69,9 @@ export function isSavedObjectExecutionSource( ): executionSource is SavedObjectExecutionSource { return executionSource?.type === ActionExecutionSourceType.SAVED_OBJECT; } + +export function isNotificationExecutionSource( + executionSource?: ActionExecutionSource +): executionSource is NotificationExecutionSource { + return executionSource?.type === ActionExecutionSourceType.NOTIFICATION; +} diff --git a/x-pack/plugins/actions/server/lib/action_executor.test.ts b/x-pack/plugins/actions/server/lib/action_executor.test.ts index dd7cdbcb45124..a6acfc25a923a 100644 --- a/x-pack/plugins/actions/server/lib/action_executor.test.ts +++ b/x-pack/plugins/actions/server/lib/action_executor.test.ts @@ -10,11 +10,15 @@ import { schema } from '@kbn/config-schema'; import { ActionExecutor } from './action_executor'; import { actionTypeRegistryMock } from '../action_type_registry.mock'; import { encryptedSavedObjectsMock } from '@kbn/encrypted-saved-objects-plugin/server/mocks'; -import { loggingSystemMock } from '@kbn/core/server/mocks'; +import { httpServerMock, loggingSystemMock } from '@kbn/core/server/mocks'; import { eventLoggerMock } from '@kbn/event-log-plugin/server/mocks'; import { spacesServiceMock } from '@kbn/spaces-plugin/server/spaces_service/spaces_service.mock'; import { ActionType } from '../types'; import { actionsMock } from '../mocks'; +import { + asHttpRequestExecutionSource, + asSavedObjectExecutionSource, +} from './action_execution_source'; const actionExecutor = new ActionExecutor({ isESOCanEncrypt: true }); const services = actionsMock.createServices(); @@ -200,6 +204,285 @@ test('successfully executes', async () => { `); }); +test('successfully executes when http_request source is specified', async () => { + const actionType: jest.Mocked = { + id: 'test', + name: 'Test', + minimumLicenseRequired: 'basic', + supportedFeatureIds: ['alerting'], + executor: jest.fn(), + }; + const actionSavedObject = { + id: '1', + type: 'action', + attributes: { + name: '1', + actionTypeId: 'test', + config: { + bar: true, + }, + secrets: { + baz: true, + }, + }, + references: [], + }; + encryptedSavedObjectsClient.getDecryptedAsInternalUser.mockResolvedValueOnce(actionSavedObject); + actionTypeRegistry.get.mockReturnValueOnce(actionType); + await actionExecutor.execute({ + ...executeParams, + source: asHttpRequestExecutionSource(httpServerMock.createKibanaRequest()), + }); + + expect(encryptedSavedObjectsClient.getDecryptedAsInternalUser).toHaveBeenCalledWith( + 'action', + '1', + { namespace: 'some-namespace' } + ); + + expect(actionTypeRegistry.get).toHaveBeenCalledWith('test'); + expect(actionTypeRegistry.isActionExecutable).toHaveBeenCalledWith('1', 'test', { + notifyUsage: true, + }); + + expect(actionType.executor).toHaveBeenCalledWith({ + actionId: '1', + services: expect.anything(), + config: { + bar: true, + }, + secrets: { + baz: true, + }, + params: { foo: true }, + logger: loggerMock, + }); + + expect(loggerMock.debug).toBeCalledWith('executing action test:1: 1'); + expect(eventLogger.logEvent.mock.calls).toMatchInlineSnapshot(` + Array [ + Array [ + Object { + "event": Object { + "action": "execute-start", + "kind": "action", + }, + "kibana": Object { + "action": Object { + "execution": Object { + "source": "http_request", + "uuid": "2", + }, + "id": "1", + "name": "1", + }, + "alert": Object { + "rule": Object { + "execution": Object { + "uuid": "123abc", + }, + }, + }, + "saved_objects": Array [ + Object { + "id": "1", + "namespace": "some-namespace", + "rel": "primary", + "type": "action", + "type_id": "test", + }, + ], + "space_ids": Array [ + "some-namespace", + ], + }, + "message": "action started: test:1: 1", + }, + ], + Array [ + Object { + "event": Object { + "action": "execute", + "kind": "action", + "outcome": "success", + }, + "kibana": Object { + "action": Object { + "execution": Object { + "source": "http_request", + "uuid": "2", + }, + "id": "1", + "name": "1", + }, + "alert": Object { + "rule": Object { + "execution": Object { + "uuid": "123abc", + }, + }, + }, + "saved_objects": Array [ + Object { + "id": "1", + "namespace": "some-namespace", + "rel": "primary", + "type": "action", + "type_id": "test", + }, + ], + "space_ids": Array [ + "some-namespace", + ], + }, + "message": "action executed: test:1: 1", + }, + ], + ] + `); +}); + +test('successfully executes when saved_object source is specified', async () => { + const actionType: jest.Mocked = { + id: 'test', + name: 'Test', + minimumLicenseRequired: 'basic', + supportedFeatureIds: ['alerting'], + executor: jest.fn(), + }; + const actionSavedObject = { + id: '1', + type: 'action', + attributes: { + name: '1', + actionTypeId: 'test', + config: { + bar: true, + }, + secrets: { + baz: true, + }, + }, + references: [], + }; + encryptedSavedObjectsClient.getDecryptedAsInternalUser.mockResolvedValueOnce(actionSavedObject); + actionTypeRegistry.get.mockReturnValueOnce(actionType); + await actionExecutor.execute({ + ...executeParams, + source: asSavedObjectExecutionSource({ + id: '573891ae-8c48-49cb-a197-0cd5ec34a88b', + type: 'alert', + }), + }); + + expect(encryptedSavedObjectsClient.getDecryptedAsInternalUser).toHaveBeenCalledWith( + 'action', + '1', + { namespace: 'some-namespace' } + ); + + expect(actionTypeRegistry.get).toHaveBeenCalledWith('test'); + expect(actionTypeRegistry.isActionExecutable).toHaveBeenCalledWith('1', 'test', { + notifyUsage: true, + }); + + expect(actionType.executor).toHaveBeenCalledWith({ + actionId: '1', + services: expect.anything(), + config: { + bar: true, + }, + secrets: { + baz: true, + }, + params: { foo: true }, + logger: loggerMock, + }); + + expect(loggerMock.debug).toBeCalledWith('executing action test:1: 1'); + expect(eventLogger.logEvent.mock.calls).toMatchInlineSnapshot(` + Array [ + Array [ + Object { + "event": Object { + "action": "execute-start", + "kind": "action", + }, + "kibana": Object { + "action": Object { + "execution": Object { + "source": "alert", + "uuid": "2", + }, + "id": "1", + "name": "1", + }, + "alert": Object { + "rule": Object { + "execution": Object { + "uuid": "123abc", + }, + }, + }, + "saved_objects": Array [ + Object { + "id": "1", + "namespace": "some-namespace", + "rel": "primary", + "type": "action", + "type_id": "test", + }, + ], + "space_ids": Array [ + "some-namespace", + ], + }, + "message": "action started: test:1: 1", + }, + ], + Array [ + Object { + "event": Object { + "action": "execute", + "kind": "action", + "outcome": "success", + }, + "kibana": Object { + "action": Object { + "execution": Object { + "source": "alert", + "uuid": "2", + }, + "id": "1", + "name": "1", + }, + "alert": Object { + "rule": Object { + "execution": Object { + "uuid": "123abc", + }, + }, + }, + "saved_objects": Array [ + Object { + "id": "1", + "namespace": "some-namespace", + "rel": "primary", + "type": "action", + "type_id": "test", + }, + ], + "space_ids": Array [ + "some-namespace", + ], + }, + "message": "action executed: test:1: 1", + }, + ], + ] + `); +}); + test('successfully executes with preconfigured connector', async () => { const actionType: jest.Mocked = { id: 'test', diff --git a/x-pack/plugins/actions/server/lib/action_executor.ts b/x-pack/plugins/actions/server/lib/action_executor.ts index d42951947f93d..d9b2f7abc73cb 100644 --- a/x-pack/plugins/actions/server/lib/action_executor.ts +++ b/x-pack/plugins/actions/server/lib/action_executor.ts @@ -90,6 +90,7 @@ export class ActionExecutor { actionId, params, request, + source, isEphemeral, taskInfo, executionId, @@ -184,6 +185,7 @@ export class ActionExecutor { name, actionExecutionId, isPreconfigured: this.actionInfo.isPreconfigured, + ...(source ? { source } : {}), }); eventLogger.startTiming(event); @@ -348,6 +350,7 @@ export class ActionExecutor { relatedSavedObjects, actionExecutionId, isPreconfigured: this.actionInfo.isPreconfigured, + ...(source ? { source } : {}), }); eventLogger.logEvent(event); @@ -363,7 +366,7 @@ interface ActionInfo { isPreconfigured?: boolean; } -async function getActionInfoInternal( +async function getActionInfoInternal( isESOCanEncrypt: boolean, encryptedSavedObjectsClient: EncryptedSavedObjectsClient, preconfiguredActions: PreConfiguredAction[], diff --git a/x-pack/plugins/actions/server/lib/create_action_event_log_record_object.test.ts b/x-pack/plugins/actions/server/lib/create_action_event_log_record_object.test.ts index 58a21c911cf34..28c3a96e14507 100644 --- a/x-pack/plugins/actions/server/lib/create_action_event_log_record_object.test.ts +++ b/x-pack/plugins/actions/server/lib/create_action_event_log_record_object.test.ts @@ -5,6 +5,8 @@ * 2.0. */ +import { httpServerMock } from '@kbn/core-http-server-mocks'; +import { asHttpRequestExecutionSource } from './action_execution_source'; import { createActionEventLogRecordObject } from './create_action_event_log_record_object'; describe('createActionEventLogRecordObject', () => { @@ -297,6 +299,120 @@ describe('createActionEventLogRecordObject', () => { }); }); + test('created action event "execute" with http_request source', async () => { + expect( + createActionEventLogRecordObject({ + actionId: '1', + name: 'test name', + action: 'execute', + message: 'action execution start', + namespace: 'default', + executionId: '123abc', + consumer: 'test-consumer', + savedObjects: [ + { + id: '2', + type: 'action', + typeId: '.email', + relation: 'primary', + }, + ], + actionExecutionId: '123abc', + source: asHttpRequestExecutionSource(httpServerMock.createKibanaRequest()), + }) + ).toStrictEqual({ + event: { + action: 'execute', + kind: 'action', + }, + kibana: { + alert: { + rule: { + consumer: 'test-consumer', + execution: { + uuid: '123abc', + }, + }, + }, + saved_objects: [ + { + id: '2', + namespace: 'default', + rel: 'primary', + type: 'action', + type_id: '.email', + }, + ], + action: { + name: 'test name', + id: '1', + execution: { + source: 'http_request', + uuid: '123abc', + }, + }, + }, + message: 'action execution start', + }); + }); + + test('created action event "execute" with saved_object source', async () => { + expect( + createActionEventLogRecordObject({ + actionId: '1', + name: 'test name', + action: 'execute', + message: 'action execution start', + namespace: 'default', + executionId: '123abc', + consumer: 'test-consumer', + savedObjects: [ + { + id: '2', + type: 'action', + typeId: '.email', + relation: 'primary', + }, + ], + actionExecutionId: '123abc', + source: asHttpRequestExecutionSource(httpServerMock.createKibanaRequest()), + }) + ).toStrictEqual({ + event: { + action: 'execute', + kind: 'action', + }, + kibana: { + alert: { + rule: { + consumer: 'test-consumer', + execution: { + uuid: '123abc', + }, + }, + }, + saved_objects: [ + { + id: '2', + namespace: 'default', + rel: 'primary', + type: 'action', + type_id: '.email', + }, + ], + action: { + name: 'test name', + id: '1', + execution: { + source: 'http_request', + uuid: '123abc', + }, + }, + }, + message: 'action execution start', + }); + }); + test('created action event "execute" for preconfigured connector with space_agnostic true', async () => { expect( createActionEventLogRecordObject({ diff --git a/x-pack/plugins/actions/server/lib/create_action_event_log_record_object.ts b/x-pack/plugins/actions/server/lib/create_action_event_log_record_object.ts index 95a6101d684f5..46dcddd9b55c5 100644 --- a/x-pack/plugins/actions/server/lib/create_action_event_log_record_object.ts +++ b/x-pack/plugins/actions/server/lib/create_action_event_log_record_object.ts @@ -9,6 +9,7 @@ import { set } from '@kbn/safer-lodash-set'; import { isEmpty } from 'lodash'; import { IEvent, SAVED_OBJECT_REL_PRIMARY } from '@kbn/event-log-plugin/server'; import { RelatedSavedObjects } from './related_saved_objects'; +import { ActionExecutionSource, isSavedObjectExecutionSource } from './action_execution_source'; export type Event = Exclude; @@ -35,6 +36,7 @@ interface CreateActionEventLogRecordParams { }>; relatedSavedObjects?: RelatedSavedObjects; isPreconfigured?: boolean; + source?: ActionExecutionSource; } export function createActionEventLogRecordObject(params: CreateActionEventLogRecordParams): Event { @@ -51,6 +53,7 @@ export function createActionEventLogRecordObject(params: CreateActionEventLogRec actionExecutionId, isPreconfigured, actionId, + source, } = params; const kibanaAlertRule = { @@ -94,6 +97,14 @@ export function createActionEventLogRecordObject(params: CreateActionEventLogRec ...(message ? { message } : {}), }; + if (source) { + if (isSavedObjectExecutionSource(source)) { + set(event, 'kibana.action.execution.source', source.source.type); + } else { + set(event, 'kibana.action.execution.source', source.type?.toLowerCase()); + } + } + for (const relatedSavedObject of relatedSavedObjects || []) { const ruleTypeId = relatedSavedObject.type === 'alert' ? relatedSavedObject.typeId : null; if (ruleTypeId) { diff --git a/x-pack/plugins/actions/server/lib/executor_error.ts b/x-pack/plugins/actions/server/lib/executor_error.ts deleted file mode 100644 index 6e0652eae43d3..0000000000000 --- a/x-pack/plugins/actions/server/lib/executor_error.ts +++ /dev/null @@ -1,16 +0,0 @@ -/* - * 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. - */ - -export class ExecutorError extends Error { - readonly data?: unknown; - readonly retry: boolean | Date; - constructor(message?: string, data?: unknown, retry: boolean | Date = false) { - super(message); - this.data = data; - this.retry = retry; - } -} diff --git a/x-pack/plugins/actions/server/lib/get_execution_log_aggregation.test.ts b/x-pack/plugins/actions/server/lib/get_execution_log_aggregation.test.ts index c92f4be981795..f85d51b5ae2c3 100644 --- a/x-pack/plugins/actions/server/lib/get_execution_log_aggregation.test.ts +++ b/x-pack/plugins/actions/server/lib/get_execution_log_aggregation.test.ts @@ -96,6 +96,7 @@ describe('getExecutionLogAggregation', () => { 'kibana.space_ids', 'kibana.action.name', 'kibana.action.id', + 'kibana.action.execution.source', ], }, }, @@ -225,6 +226,7 @@ describe('getExecutionLogAggregation', () => { 'kibana.space_ids', 'kibana.action.name', 'kibana.action.id', + 'kibana.action.execution.source', ], }, }, @@ -370,6 +372,7 @@ describe('getExecutionLogAggregation', () => { 'kibana.space_ids', 'kibana.action.name', 'kibana.action.id', + 'kibana.action.execution.source', ], }, }, @@ -526,7 +529,11 @@ describe('formatExecutionLogResult', () => { kibana: { space_ids: ['default'], version: '8.7.0', - action: { name: 'test connector', id: '1' }, + action: { + name: 'test connector', + id: '1', + execution: { source: 'SAVED_OBJECT' }, + }, }, message: 'action executed: .server-log:6709f660-8d11-11ed-bae5-bd32cbc9eaaa: test connector', @@ -563,6 +570,7 @@ describe('formatExecutionLogResult', () => { timestamp: '2023-01-05T15:55:50.495Z', version: '8.7.0', timed_out: false, + source: 'SAVED_OBJECT', }, ], total: 1, @@ -598,7 +606,11 @@ describe('formatExecutionLogResult', () => { kibana: { space_ids: ['default'], version: '8.7.0', - action: { name: 'test', id: '1' }, + action: { + name: 'test', + id: '1', + execution: { source: 'SAVED_OBJECT' }, + }, }, message: 'action execution failure: .email:e020c620-8d14-11ed-bae5-bd32cbc9eaaa: test', @@ -638,7 +650,11 @@ describe('formatExecutionLogResult', () => { kibana: { space_ids: ['default'], version: '8.7.0', - action: { name: 'test connector', id: '1' }, + action: { + name: 'test connector', + id: '1', + execution: { source: 'SAVED_OBJECT' }, + }, }, message: 'action executed: .server-log:6709f660-8d11-11ed-bae5-bd32cbc9eaaa: test connector', @@ -675,6 +691,7 @@ describe('formatExecutionLogResult', () => { timestamp: '2023-01-05T16:23:53.813Z', version: '8.7.0', timed_out: false, + source: 'SAVED_OBJECT', }, { connector_name: 'test connector', @@ -689,6 +706,7 @@ describe('formatExecutionLogResult', () => { timestamp: '2023-01-05T15:55:50.495Z', version: '8.7.0', timed_out: false, + source: 'SAVED_OBJECT', }, ], total: 2, diff --git a/x-pack/plugins/actions/server/lib/get_execution_log_aggregation.ts b/x-pack/plugins/actions/server/lib/get_execution_log_aggregation.ts index e2f3940716317..ddbb864d4c06a 100644 --- a/x-pack/plugins/actions/server/lib/get_execution_log_aggregation.ts +++ b/x-pack/plugins/actions/server/lib/get_execution_log_aggregation.ts @@ -28,6 +28,7 @@ const VERSION_FIELD = 'kibana.version'; const ERROR_MESSAGE_FIELD = 'error.message'; const SCHEDULE_DELAY_FIELD = 'kibana.task.schedule_delay'; const EXECUTION_UUID_FIELD = 'kibana.action.execution.uuid'; +const EXECUTION_SOURCE_FIELD = 'kibana.action.execution.source'; const Millis2Nanos = 1000 * 1000; @@ -257,6 +258,7 @@ export function getExecutionLogAggregation({ SPACE_ID_FIELD, ACTION_NAME_FIELD, ACTION_ID_FIELD, + EXECUTION_SOURCE_FIELD, ], }, }, @@ -312,6 +314,7 @@ function formatExecutionLogAggBucket(bucket: IExecutionUuidAggBucket): IExecutio const message = status === 'failure' ? `${outcomeMessage} - ${outcomeErrorMessage}` : outcomeMessage; const version = outcomeAndMessage.kibana?.version ?? ''; + const source = outcomeAndMessage.kibana?.action?.execution?.source ?? ''; const spaceIds = outcomeAndMessage?.kibana?.space_ids ?? []; const connectorName = outcomeAndMessage?.kibana?.action?.name ?? ''; @@ -324,6 +327,7 @@ function formatExecutionLogAggBucket(bucket: IExecutionUuidAggBucket): IExecutio status, message, version, + source, schedule_delay_ms: scheduleDelayUs / Millis2Nanos, space_ids: spaceIds, connector_name: connectorName, diff --git a/x-pack/plugins/actions/server/lib/index.ts b/x-pack/plugins/actions/server/lib/index.ts index 9ac123babecef..6d2c3b8c109c5 100644 --- a/x-pack/plugins/actions/server/lib/index.ts +++ b/x-pack/plugins/actions/server/lib/index.ts @@ -5,7 +5,6 @@ * 2.0. */ -export { ExecutorError } from './executor_error'; export { validateParams, validateConfig, @@ -32,6 +31,8 @@ export { isSavedObjectExecutionSource, asHttpRequestExecutionSource, isHttpRequestExecutionSource, + asNotificationExecutionSource, + isNotificationExecutionSource, } from './action_execution_source'; export { validateEmptyStrings } from './validate_empty_strings'; export { parseDate } from './parse_date'; diff --git a/x-pack/plugins/actions/server/lib/task_runner_factory.test.ts b/x-pack/plugins/actions/server/lib/task_runner_factory.test.ts index 49aadcbefe933..404c3ba452086 100644 --- a/x-pack/plugins/actions/server/lib/task_runner_factory.test.ts +++ b/x-pack/plugins/actions/server/lib/task_runner_factory.test.ts @@ -6,7 +6,6 @@ */ import sinon from 'sinon'; -import { ExecutorError } from './executor_error'; import { ActionExecutor } from './action_executor'; import { ConcreteTaskInstance, TaskStatus } from '@kbn/task-manager-plugin/server'; import { TaskRunnerFactory } from './task_runner_factory'; @@ -20,6 +19,7 @@ import { actionsClientMock } from '../mocks'; import { inMemoryMetricsMock } from '../monitoring/in_memory_metrics.mock'; import { IN_MEMORY_METRICS } from '../monitoring'; import { pick } from 'lodash'; +import { isRetryableError } from '@kbn/task-manager-plugin/server/task_running'; const executeParamsFields = [ 'actionId', @@ -29,6 +29,7 @@ const executeParamsFields = [ 'executionId', 'request.headers', 'taskInfo', + 'source', ]; const spaceIdToNamespace = jest.fn(); const actionTypeRegistry = actionTypeRegistryMock.create(); @@ -287,6 +288,128 @@ test('executes the task by calling the executor with proper parameters when cons ); }); +test('executes the task by calling the executor with proper parameters when saved_object source is provided', async () => { + const taskRunner = taskRunnerFactory.create({ + taskInstance: mockedTaskInstance, + }); + + mockedActionExecutor.execute.mockResolvedValueOnce({ status: 'ok', actionId: '2' }); + spaceIdToNamespace.mockReturnValueOnce('namespace-test'); + mockedEncryptedSavedObjectsClient.getDecryptedAsInternalUser.mockResolvedValueOnce({ + id: '3', + type: 'action_task_params', + attributes: { + actionId: '2', + consumer: 'test-consumer', + params: { baz: true }, + executionId: '123abc', + source: 'SAVED_OBJECT', + apiKey: Buffer.from('123:abc').toString('base64'), + }, + references: [{ name: 'source', id: 'abc', type: 'alert' }], + }); + + const runnerResult = await taskRunner.run(); + + expect(runnerResult).toBeUndefined(); + expect(spaceIdToNamespace).toHaveBeenCalledWith('test'); + expect(mockedEncryptedSavedObjectsClient.getDecryptedAsInternalUser).toHaveBeenCalledWith( + 'action_task_params', + '3', + { namespace: 'namespace-test' } + ); + + const [executeParams] = mockedActionExecutor.execute.mock.calls[0]; + expect(pick(executeParams, [...executeParamsFields, 'consumer'])).toEqual({ + actionId: '2', + consumer: 'test-consumer', + isEphemeral: false, + params: { baz: true }, + relatedSavedObjects: [], + executionId: '123abc', + request: { + headers: { + // base64 encoded "123:abc" + authorization: 'ApiKey MTIzOmFiYw==', + }, + }, + source: { + type: 'SAVED_OBJECT', + source: { id: 'abc', type: 'alert' }, + }, + taskInfo: { + scheduled: new Date(), + attempts: 0, + }, + }); + + expect(taskRunnerFactoryInitializerParams.basePathService.set).toHaveBeenCalledWith( + executeParams.request, + '/s/test' + ); +}); + +test('executes the task by calling the executor with proper parameters when notification source is provided', async () => { + const taskRunner = taskRunnerFactory.create({ + taskInstance: mockedTaskInstance, + }); + + mockedActionExecutor.execute.mockResolvedValueOnce({ status: 'ok', actionId: '2' }); + spaceIdToNamespace.mockReturnValueOnce('namespace-test'); + mockedEncryptedSavedObjectsClient.getDecryptedAsInternalUser.mockResolvedValueOnce({ + id: '3', + type: 'action_task_params', + attributes: { + actionId: '2', + consumer: 'test-consumer', + params: { baz: true }, + executionId: '123abc', + source: 'NOTIFICATION', + apiKey: Buffer.from('123:abc').toString('base64'), + }, + references: [], + }); + + const runnerResult = await taskRunner.run(); + + expect(runnerResult).toBeUndefined(); + expect(spaceIdToNamespace).toHaveBeenCalledWith('test'); + expect(mockedEncryptedSavedObjectsClient.getDecryptedAsInternalUser).toHaveBeenCalledWith( + 'action_task_params', + '3', + { namespace: 'namespace-test' } + ); + + const [executeParams] = mockedActionExecutor.execute.mock.calls[0]; + expect(pick(executeParams, [...executeParamsFields, 'consumer'])).toEqual({ + actionId: '2', + consumer: 'test-consumer', + isEphemeral: false, + params: { baz: true }, + relatedSavedObjects: [], + executionId: '123abc', + request: { + headers: { + // base64 encoded "123:abc" + authorization: 'ApiKey MTIzOmFiYw==', + }, + }, + source: { + type: 'NOTIFICATION', + source: {}, + }, + taskInfo: { + scheduled: new Date(), + attempts: 0, + }, + }); + + expect(taskRunnerFactoryInitializerParams.basePathService.set).toHaveBeenCalledWith( + executeParams.request, + '/s/test' + ); +}); + test('cleans up action_task_params object', async () => { const taskRunner = taskRunnerFactory.create({ taskInstance: mockedTaskInstance, @@ -421,9 +544,7 @@ test('throws an error with suggested retry logic when return status is error', a await taskRunner.run(); throw new Error('Should have thrown'); } catch (e) { - expect(e instanceof ExecutorError).toEqual(true); - expect(e.data).toEqual({ foo: true }); - expect(e.retry).toEqual(false); + expect(isRetryableError(e)).toEqual(false); } }); @@ -728,13 +849,11 @@ test(`throws an error when license doesn't support the action type`, async () => await taskRunner.run(); throw new Error('Should have thrown'); } catch (e) { - expect(e instanceof ExecutorError).toEqual(true); - expect(e.data).toEqual({}); - expect(e.retry).toEqual(true); + expect(isRetryableError(e)).toEqual(true); } }); -test(`treats errors as errors if the task is retryable`, async () => { +test(`will throw an error with retry: false if the task is not retryable`, async () => { const taskRunner = taskRunnerFactory.create({ taskInstance: { ...mockedTaskInstance, @@ -774,9 +893,7 @@ test(`treats errors as errors if the task is retryable`, async () => { err = e; } expect(err).toBeDefined(); - expect(err instanceof ExecutorError).toEqual(true); - expect(err.data).toEqual({ foo: true }); - expect(err.retry).toEqual(false); + expect(isRetryableError(err)).toEqual(false); expect(taskRunnerFactoryInitializerParams.logger.error as jest.Mock).toHaveBeenCalledWith( `Action '2' failed and will not retry: Error message` ); @@ -827,7 +944,7 @@ test(`treats errors as successes if the task is not retryable`, async () => { ); }); -test('treats errors as errors if the error is thrown instead of returned', async () => { +test('will throw a retry error if the error is thrown instead of returned', async () => { const taskRunner = taskRunnerFactory.create({ taskInstance: { ...mockedTaskInstance, @@ -861,9 +978,7 @@ test('treats errors as errors if the error is thrown instead of returned', async err = e; } expect(err).toBeDefined(); - expect(err instanceof ExecutorError).toEqual(true); - expect(err.data).toEqual({}); - expect(err.retry).toEqual(true); + expect(isRetryableError(err)).toEqual(true); expect(taskRunnerFactoryInitializerParams.logger.error as jest.Mock).toHaveBeenCalledWith( `Action '2' failed and will retry: undefined` ); diff --git a/x-pack/plugins/actions/server/lib/task_runner_factory.ts b/x-pack/plugins/actions/server/lib/task_runner_factory.ts index d5b82e8fc6f6e..de347637f5cf4 100644 --- a/x-pack/plugins/actions/server/lib/task_runner_factory.ts +++ b/x-pack/plugins/actions/server/lib/task_runner_factory.ts @@ -7,24 +7,22 @@ import { v4 as uuidv4 } from 'uuid'; import { pick } from 'lodash'; -import { pipe } from 'fp-ts/lib/pipeable'; -import { map, fromNullable, getOrElse } from 'fp-ts/lib/Option'; import { addSpaceIdToPath } from '@kbn/spaces-plugin/server'; import { Logger, SavedObjectsClientContract, KibanaRequest, CoreKibanaRequest, - SavedObjectReference, IBasePath, SavedObject, Headers, FakeRawRequest, + SavedObjectReference, } from '@kbn/core/server'; import { RunContext } from '@kbn/task-manager-plugin/server'; import { EncryptedSavedObjectsClient } from '@kbn/encrypted-saved-objects-plugin/server'; +import { throwRetryableError } from '@kbn/task-manager-plugin/server/task_running'; import { ActionExecutorContract } from './action_executor'; -import { ExecutorError } from './executor_error'; import { ActionTaskParams, ActionTypeRegistryContract, @@ -34,7 +32,11 @@ import { isPersistedActionTask, } from '../types'; import { ACTION_TASK_PARAMS_SAVED_OBJECT_TYPE } from '../constants/saved_objects'; -import { asSavedObjectExecutionSource } from './action_execution_source'; +import { + ActionExecutionSourceType, + asEmptySource, + asSavedObjectExecutionSource, +} from './action_execution_source'; import { RelatedSavedObjects, validatedRelatedSavedObjects } from './related_saved_objects'; import { injectSavedObjectReferences } from './action_task_params_utils'; import { InMemoryMetrics, IN_MEMORY_METRICS } from '../monitoring'; @@ -93,7 +95,15 @@ export class TaskRunnerFactory { const { spaceId } = actionTaskExecutorParams; const { - attributes: { actionId, params, apiKey, executionId, consumer, relatedSavedObjects }, + attributes: { + actionId, + params, + apiKey, + executionId, + consumer, + source, + relatedSavedObjects, + }, references, } = await getActionTaskParams( actionTaskExecutorParams, @@ -105,7 +115,6 @@ export class TaskRunnerFactory { const request = getFakeRequest(apiKey); basePathService.set(request, path); - // Throwing an executor error means we will attempt to retry the task // TM will treat a task as a failure if `attempts >= maxAttempts` // so we need to handle that here to avoid TM persisting the failed task const isRetryableBasedOnAttempts = taskInfo.attempts < maxAttempts; @@ -119,12 +128,12 @@ export class TaskRunnerFactory { actionId: actionId as string, isEphemeral: !isPersistedActionTask(actionTaskExecutorParams), request, - ...getSourceFromReferences(references), taskInfo, executionId, consumer, relatedSavedObjects: validatedRelatedSavedObjects(logger, relatedSavedObjects), actionExecutionId, + ...getSource(references, source), }); } catch (e) { logger.error( @@ -133,9 +142,8 @@ export class TaskRunnerFactory { }: ${e.message}` ); if (isRetryableBasedOnAttempts) { - // In order for retry to work, we need to indicate to task manager this task - // failed - throw new ExecutorError(e.message, {}, true); + // To retry, we will throw a Task Manager RetryableError + throw throwRetryableError(new Error(e.message), true); } } @@ -152,11 +160,9 @@ export class TaskRunnerFactory { !!executorResult.retry ? willRetryMessage : willNotRetryMessage }: ${executorResult.message}` ); - // Task manager error handler only kicks in when an error thrown (at this time) - // So what we have to do is throw when the return status is `error`. - throw new ExecutorError( - executorResult.message, - executorResult.data, + // When the return status is `error`, we will throw a Task Manager RetryableError + throw throwRetryableError( + new Error(executorResult.message), executorResult.retry as boolean | Date ); } else if (executorResult && executorResult?.status === 'error') { @@ -192,7 +198,7 @@ export class TaskRunnerFactory { const { spaceId } = actionTaskExecutorParams; const { - attributes: { actionId, apiKey, executionId, consumer, relatedSavedObjects }, + attributes: { actionId, apiKey, executionId, consumer, source, relatedSavedObjects }, references, } = await getActionTaskParams( actionTaskExecutorParams, @@ -210,8 +216,8 @@ export class TaskRunnerFactory { consumer, executionId, relatedSavedObjects: (relatedSavedObjects || []) as RelatedSavedObjects, - ...getSourceFromReferences(references), actionExecutionId, + ...getSource(references, source), }); inMemoryMetrics.increment(IN_MEMORY_METRICS.ACTION_TIMEOUTS); @@ -279,12 +285,11 @@ async function getActionTaskParams( } } -function getSourceFromReferences(references: SavedObjectReference[]) { - return pipe( - fromNullable(references.find((ref) => ref.name === 'source')), - map((source) => ({ - source: asSavedObjectExecutionSource(pick(source, 'id', 'type')), - })), - getOrElse(() => ({})) - ); +function getSource(references: SavedObjectReference[], sourceType?: string) { + const sourceInReferences = references.find((ref) => ref.name === 'source'); + if (sourceInReferences) { + return { source: asSavedObjectExecutionSource(pick(sourceInReferences, 'id', 'type')) }; + } + + return sourceType ? { source: asEmptySource(sourceType as ActionExecutionSourceType) } : {}; } diff --git a/x-pack/plugins/actions/server/routes/get_global_execution_logs.test.ts b/x-pack/plugins/actions/server/routes/get_global_execution_logs.test.ts index e91ab9268bac5..1c309e14dae09 100644 --- a/x-pack/plugins/actions/server/routes/get_global_execution_logs.test.ts +++ b/x-pack/plugins/actions/server/routes/get_global_execution_logs.test.ts @@ -39,6 +39,7 @@ describe('getRuleExecutionLogRoute', () => { timestamp: '2023-01-05T15:55:50.495Z', version: '8.7.0', timed_out: false, + source: 'SAVED_OBJECT', }, ], total: 1, diff --git a/x-pack/plugins/actions/server/saved_objects/mappings.json b/x-pack/plugins/actions/server/saved_objects/mappings.json index 80646579f86db..be0a691f852b2 100644 --- a/x-pack/plugins/actions/server/saved_objects/mappings.json +++ b/x-pack/plugins/actions/server/saved_objects/mappings.json @@ -25,6 +25,7 @@ } }, "action_task_params": { + "dynamic": false, "properties": { "actionId": { "type": "keyword" diff --git a/x-pack/plugins/actions/server/types.ts b/x-pack/plugins/actions/server/types.ts index 147a71da0c960..72e7eeddce1c1 100644 --- a/x-pack/plugins/actions/server/types.ts +++ b/x-pack/plugins/actions/server/types.ts @@ -155,6 +155,7 @@ export interface ActionTaskParams extends SavedObjectAttributes { apiKey?: string; executionId?: string; consumer?: string; + source?: string; } interface PersistedActionTaskExecutorParams { diff --git a/x-pack/plugins/actions/server/unsecured_actions_client/unsecured_actions_client.test.ts b/x-pack/plugins/actions/server/unsecured_actions_client/unsecured_actions_client.test.ts index c863e943b8dc0..7d0d4d687a71a 100644 --- a/x-pack/plugins/actions/server/unsecured_actions_client/unsecured_actions_client.test.ts +++ b/x-pack/plugins/actions/server/unsecured_actions_client/unsecured_actions_client.test.ts @@ -7,6 +7,7 @@ import { UnsecuredActionsClient } from './unsecured_actions_client'; import { savedObjectsRepositoryMock } from '@kbn/core/server/mocks'; +import { asNotificationExecutionSource } from '../lib'; const internalSavedObjectsRepository = savedObjectsRepositoryMock.create(); const executionEnqueuer = jest.fn(); @@ -56,9 +57,33 @@ describe('bulkEnqueueExecution()', () => { }, ]; await expect( - unsecuredActionsClient.bulkEnqueueExecution('notifications', opts) + unsecuredActionsClient.bulkEnqueueExecution('functional_tester', opts) ).resolves.toMatchInlineSnapshot(`undefined`); expect(executionEnqueuer).toHaveBeenCalledWith(internalSavedObjectsRepository, opts); }); + + test('injects source and calls the executionEnqueuer with the appropriate parameters when requester is "notifications"', async () => { + const opts = [ + { + id: 'preconfigured1', + params: {}, + executionId: '123abc', + }, + { + id: 'preconfigured2', + params: {}, + executionId: '456def', + }, + ]; + await expect( + unsecuredActionsClient.bulkEnqueueExecution('notifications', opts) + ).resolves.toMatchInlineSnapshot(`undefined`); + + const optsWithSource = opts.map((opt) => ({ + ...opt, + source: asNotificationExecutionSource({ connectorId: opt.id, requesterId: 'notifications' }), + })); + expect(executionEnqueuer).toHaveBeenCalledWith(internalSavedObjectsRepository, optsWithSource); + }); }); diff --git a/x-pack/plugins/actions/server/unsecured_actions_client/unsecured_actions_client.ts b/x-pack/plugins/actions/server/unsecured_actions_client/unsecured_actions_client.ts index 333490389013a..a2d87c8a5db4a 100644 --- a/x-pack/plugins/actions/server/unsecured_actions_client/unsecured_actions_client.ts +++ b/x-pack/plugins/actions/server/unsecured_actions_client/unsecured_actions_client.ts @@ -10,11 +10,14 @@ import { BulkUnsecuredExecutionEnqueuer, ExecuteOptions, } from '../create_unsecured_execute_function'; +import { asNotificationExecutionSource } from '../lib'; + +const NOTIFICATION_REQUESTER_ID = 'notifications'; // allowlist for features wanting access to the unsecured actions client // which allows actions to be enqueued for execution without a user request const ALLOWED_REQUESTER_IDS = [ - 'notifications', + NOTIFICATION_REQUESTER_ID, // For functional testing 'functional_tester', ]; @@ -47,6 +50,25 @@ export class UnsecuredActionsClient { `"${requesterId}" feature is not allow-listed for UnsecuredActionsClient access.` ); } - return this.executionEnqueuer(this.internalSavedObjectsRepository, actionsToExecute); + // Inject source based on requesterId + return this.executionEnqueuer( + this.internalSavedObjectsRepository, + this.injectSource(requesterId, actionsToExecute) + ); + } + + private injectSource(requesterId: string, actionsToExecute: ExecuteOptions[]): ExecuteOptions[] { + switch (requesterId) { + case NOTIFICATION_REQUESTER_ID: + return actionsToExecute.map((actionToExecute) => ({ + ...actionToExecute, + source: asNotificationExecutionSource({ + requesterId, + connectorId: actionToExecute.id, + }), + })); + default: + return actionsToExecute; + } } } diff --git a/x-pack/plugins/actions/tsconfig.json b/x-pack/plugins/actions/tsconfig.json index 72ba9c7c92e09..77ef11e88bfe3 100644 --- a/x-pack/plugins/actions/tsconfig.json +++ b/x-pack/plugins/actions/tsconfig.json @@ -32,7 +32,8 @@ "@kbn/logging", "@kbn/logging-mocks", "@kbn/core-elasticsearch-client-server-mocks", - "@kbn/safer-lodash-set" + "@kbn/safer-lodash-set", + "@kbn/core-http-server-mocks" ], "exclude": [ "target/**/*", diff --git a/x-pack/plugins/aiops/common/__mocks__/artificial_logs/final_change_point_groups.ts b/x-pack/plugins/aiops/common/__mocks__/artificial_logs/final_significant_term_groups.ts similarity index 84% rename from x-pack/plugins/aiops/common/__mocks__/artificial_logs/final_change_point_groups.ts rename to x-pack/plugins/aiops/common/__mocks__/artificial_logs/final_significant_term_groups.ts index dce10c1280985..219f7cd8bf126 100644 --- a/x-pack/plugins/aiops/common/__mocks__/artificial_logs/final_change_point_groups.ts +++ b/x-pack/plugins/aiops/common/__mocks__/artificial_logs/final_significant_term_groups.ts @@ -5,9 +5,9 @@ * 2.0. */ -import type { ChangePointGroup } from '@kbn/ml-agg-utils'; +import type { SignificantTermGroup } from '@kbn/ml-agg-utils'; -export const finalChangePointGroups: ChangePointGroup[] = [ +export const finalSignificantTermGroups: SignificantTermGroup[] = [ { id: '2038579476', group: [ diff --git a/x-pack/plugins/aiops/common/__mocks__/artificial_logs/change_point_groups.ts b/x-pack/plugins/aiops/common/__mocks__/artificial_logs/significant_term_groups.ts similarity index 78% rename from x-pack/plugins/aiops/common/__mocks__/artificial_logs/change_point_groups.ts rename to x-pack/plugins/aiops/common/__mocks__/artificial_logs/significant_term_groups.ts index 9b580ac36ce26..68acb81e35f4f 100644 --- a/x-pack/plugins/aiops/common/__mocks__/artificial_logs/change_point_groups.ts +++ b/x-pack/plugins/aiops/common/__mocks__/artificial_logs/significant_term_groups.ts @@ -5,9 +5,9 @@ * 2.0. */ -import type { ChangePointGroup } from '@kbn/ml-agg-utils'; +import type { SignificantTermGroup } from '@kbn/ml-agg-utils'; -export const changePointGroups: ChangePointGroup[] = [ +export const significantTermGroups: SignificantTermGroup[] = [ { id: '2038579476', group: [ diff --git a/x-pack/plugins/aiops/common/__mocks__/artificial_logs/change_points.ts b/x-pack/plugins/aiops/common/__mocks__/artificial_logs/significant_terms.ts similarity index 97% rename from x-pack/plugins/aiops/common/__mocks__/artificial_logs/change_points.ts rename to x-pack/plugins/aiops/common/__mocks__/artificial_logs/significant_terms.ts index 5b4597a2ecd8a..f1bf9720aed3c 100644 --- a/x-pack/plugins/aiops/common/__mocks__/artificial_logs/change_points.ts +++ b/x-pack/plugins/aiops/common/__mocks__/artificial_logs/significant_terms.ts @@ -5,7 +5,7 @@ * 2.0. */ -export const changePoints = [ +export const significantTerms = [ { fieldName: 'response_code', fieldValue: '500', diff --git a/x-pack/plugins/aiops/common/__mocks__/farequote/change_point_groups.ts b/x-pack/plugins/aiops/common/__mocks__/farequote/significant_term_groups.ts similarity index 85% rename from x-pack/plugins/aiops/common/__mocks__/farequote/change_point_groups.ts rename to x-pack/plugins/aiops/common/__mocks__/farequote/significant_term_groups.ts index b30303e384f84..c8d20384a8895 100644 --- a/x-pack/plugins/aiops/common/__mocks__/farequote/change_point_groups.ts +++ b/x-pack/plugins/aiops/common/__mocks__/farequote/significant_term_groups.ts @@ -5,9 +5,9 @@ * 2.0. */ -import type { ChangePointGroup } from '@kbn/ml-agg-utils'; +import type { SignificantTermGroup } from '@kbn/ml-agg-utils'; -export const changePointGroups: ChangePointGroup[] = [ +export const significantTermGroups: SignificantTermGroup[] = [ { id: 'group-1', group: [ diff --git a/x-pack/plugins/aiops/common/api/explain_log_rate_spikes/actions.ts b/x-pack/plugins/aiops/common/api/explain_log_rate_spikes/actions.ts index 52c8a802ca4ca..cbd2050a5ba8f 100644 --- a/x-pack/plugins/aiops/common/api/explain_log_rate_spikes/actions.ts +++ b/x-pack/plugins/aiops/common/api/explain_log_rate_spikes/actions.ts @@ -6,17 +6,17 @@ */ import type { - ChangePoint, - ChangePointHistogram, - ChangePointGroup, - ChangePointGroupHistogram, + SignificantTerm, + SignificantTermHistogram, + SignificantTermGroup, + SignificantTermGroupHistogram, } from '@kbn/ml-agg-utils'; export const API_ACTION_NAME = { - ADD_CHANGE_POINTS: 'add_change_points', - ADD_CHANGE_POINTS_HISTOGRAM: 'add_change_points_histogram', - ADD_CHANGE_POINTS_GROUP: 'add_change_point_group', - ADD_CHANGE_POINTS_GROUP_HISTOGRAM: 'add_change_point_group_histogram', + ADD_SIGNIFICANT_TERMS: 'add_significant_terms', + ADD_SIGNIFICANT_TERMS_HISTOGRAM: 'add_significant_terms_histogram', + ADD_SIGNIFICANT_TERMS_GROUP: 'add_significant_terms_group', + ADD_SIGNIFICANT_TERMS_GROUP_HISTOGRAM: 'add_significant_terms_group_histogram', ADD_ERROR: 'add_error', PING: 'ping', RESET_ALL: 'reset_all', @@ -25,56 +25,58 @@ export const API_ACTION_NAME = { } as const; export type ApiActionName = typeof API_ACTION_NAME[keyof typeof API_ACTION_NAME]; -interface ApiActionAddChangePoints { - type: typeof API_ACTION_NAME.ADD_CHANGE_POINTS; - payload: ChangePoint[]; +interface ApiActionAddSignificantTerms { + type: typeof API_ACTION_NAME.ADD_SIGNIFICANT_TERMS; + payload: SignificantTerm[]; } -export function addChangePointsAction( - payload: ApiActionAddChangePoints['payload'] -): ApiActionAddChangePoints { +export function addSignificantTermsAction( + payload: ApiActionAddSignificantTerms['payload'] +): ApiActionAddSignificantTerms { return { - type: API_ACTION_NAME.ADD_CHANGE_POINTS, + type: API_ACTION_NAME.ADD_SIGNIFICANT_TERMS, payload, }; } -interface ApiActionAddChangePointsHistogram { - type: typeof API_ACTION_NAME.ADD_CHANGE_POINTS_HISTOGRAM; - payload: ChangePointHistogram[]; +interface ApiActionAddSignificantTermsHistogram { + type: typeof API_ACTION_NAME.ADD_SIGNIFICANT_TERMS_HISTOGRAM; + payload: SignificantTermHistogram[]; } -export function addChangePointsHistogramAction( - payload: ApiActionAddChangePointsHistogram['payload'] -): ApiActionAddChangePointsHistogram { +export function addSignificantTermsHistogramAction( + payload: ApiActionAddSignificantTermsHistogram['payload'] +): ApiActionAddSignificantTermsHistogram { return { - type: API_ACTION_NAME.ADD_CHANGE_POINTS_HISTOGRAM, + type: API_ACTION_NAME.ADD_SIGNIFICANT_TERMS_HISTOGRAM, payload, }; } -interface ApiActionAddChangePointsGroup { - type: typeof API_ACTION_NAME.ADD_CHANGE_POINTS_GROUP; - payload: ChangePointGroup[]; +interface ApiActionAddSignificantTermsGroup { + type: typeof API_ACTION_NAME.ADD_SIGNIFICANT_TERMS_GROUP; + payload: SignificantTermGroup[]; } -export function addChangePointsGroupAction(payload: ApiActionAddChangePointsGroup['payload']) { +export function addSignificantTermsGroupAction( + payload: ApiActionAddSignificantTermsGroup['payload'] +) { return { - type: API_ACTION_NAME.ADD_CHANGE_POINTS_GROUP, + type: API_ACTION_NAME.ADD_SIGNIFICANT_TERMS_GROUP, payload, }; } -interface ApiActionAddChangePointsGroupHistogram { - type: typeof API_ACTION_NAME.ADD_CHANGE_POINTS_GROUP_HISTOGRAM; - payload: ChangePointGroupHistogram[]; +interface ApiActionAddSignificantTermsGroupHistogram { + type: typeof API_ACTION_NAME.ADD_SIGNIFICANT_TERMS_GROUP_HISTOGRAM; + payload: SignificantTermGroupHistogram[]; } -export function addChangePointsGroupHistogramAction( - payload: ApiActionAddChangePointsGroupHistogram['payload'] -): ApiActionAddChangePointsGroupHistogram { +export function addSignificantTermsGroupHistogramAction( + payload: ApiActionAddSignificantTermsGroupHistogram['payload'] +): ApiActionAddSignificantTermsGroupHistogram { return { - type: API_ACTION_NAME.ADD_CHANGE_POINTS_GROUP_HISTOGRAM, + type: API_ACTION_NAME.ADD_SIGNIFICANT_TERMS_GROUP_HISTOGRAM, payload, }; } @@ -138,10 +140,10 @@ export function updateLoadingStateAction( } export type AiopsExplainLogRateSpikesApiAction = - | ApiActionAddChangePoints - | ApiActionAddChangePointsGroup - | ApiActionAddChangePointsHistogram - | ApiActionAddChangePointsGroupHistogram + | ApiActionAddSignificantTerms + | ApiActionAddSignificantTermsGroup + | ApiActionAddSignificantTermsHistogram + | ApiActionAddSignificantTermsGroupHistogram | ApiActionAddError | ApiActionPing | ApiActionResetAll diff --git a/x-pack/plugins/aiops/common/api/explain_log_rate_spikes/index.ts b/x-pack/plugins/aiops/common/api/explain_log_rate_spikes/index.ts index e49b3d62f86ec..87bcea25810dc 100644 --- a/x-pack/plugins/aiops/common/api/explain_log_rate_spikes/index.ts +++ b/x-pack/plugins/aiops/common/api/explain_log_rate_spikes/index.ts @@ -6,10 +6,10 @@ */ export { - addChangePointsAction, - addChangePointsGroupAction, - addChangePointsGroupHistogramAction, - addChangePointsHistogramAction, + addSignificantTermsAction, + addSignificantTermsGroupAction, + addSignificantTermsGroupHistogramAction, + addSignificantTermsHistogramAction, addErrorAction, pingAction, resetAllAction, diff --git a/x-pack/plugins/aiops/common/api/explain_log_rate_spikes/schema.ts b/x-pack/plugins/aiops/common/api/explain_log_rate_spikes/schema.ts index 5d7993f881ec6..5114070711929 100644 --- a/x-pack/plugins/aiops/common/api/explain_log_rate_spikes/schema.ts +++ b/x-pack/plugins/aiops/common/api/explain_log_rate_spikes/schema.ts @@ -30,7 +30,7 @@ export const aiopsExplainLogRateSpikesSchema = schema.object({ loaded: schema.maybe(schema.number()), remainingFieldCandidates: schema.maybe(schema.arrayOf(schema.string())), // TODO Improve schema - changePoints: schema.maybe(schema.arrayOf(schema.any())), + significantTerms: schema.maybe(schema.arrayOf(schema.any())), }) ), }); diff --git a/x-pack/plugins/aiops/common/api/stream_reducer.test.ts b/x-pack/plugins/aiops/common/api/stream_reducer.test.ts index 8d1bae8c9f45f..3ea239b79ee2e 100644 --- a/x-pack/plugins/aiops/common/api/stream_reducer.test.ts +++ b/x-pack/plugins/aiops/common/api/stream_reducer.test.ts @@ -6,7 +6,7 @@ */ import { - addChangePointsAction, + addSignificantTermsAction, resetAllAction, updateLoadingStateAction, } from './explain_log_rate_spikes'; @@ -23,16 +23,16 @@ describe('streamReducer', () => { ccsWarning: true, loaded: 50, loadingState: 'Loaded 50%', - changePoints: [], - changePointsGroups: [], + significantTerms: [], + significantTermsGroups: [], errors: [], }); }); - it('adds change point, then resets state again', () => { + it('adds significant term, then resets state again', () => { const state1 = streamReducer( initialState, - addChangePointsAction([ + addSignificantTermsAction([ { fieldName: 'the-field-name', fieldValue: 'the-field-value', @@ -47,10 +47,10 @@ describe('streamReducer', () => { ]) ); - expect(state1.changePoints).toHaveLength(1); + expect(state1.significantTerms).toHaveLength(1); const state2 = streamReducer(state1, resetAllAction()); - expect(state2.changePoints).toHaveLength(0); + expect(state2.significantTerms).toHaveLength(0); }); }); diff --git a/x-pack/plugins/aiops/common/api/stream_reducer.ts b/x-pack/plugins/aiops/common/api/stream_reducer.ts index 9374cc5461bf6..7043752abd8f1 100644 --- a/x-pack/plugins/aiops/common/api/stream_reducer.ts +++ b/x-pack/plugins/aiops/common/api/stream_reducer.ts @@ -5,14 +5,14 @@ * 2.0. */ -import type { ChangePoint, ChangePointGroup } from '@kbn/ml-agg-utils'; +import type { SignificantTerm, SignificantTermGroup } from '@kbn/ml-agg-utils'; import { API_ACTION_NAME, AiopsExplainLogRateSpikesApiAction } from './explain_log_rate_spikes'; interface StreamState { ccsWarning: boolean; - changePoints: ChangePoint[]; - changePointsGroups: ChangePointGroup[]; + significantTerms: SignificantTerm[]; + significantTermsGroups: SignificantTermGroup[]; errors: string[]; loaded: number; loadingState: string; @@ -22,8 +22,8 @@ interface StreamState { export const initialState: StreamState = { ccsWarning: false, - changePoints: [], - changePointsGroups: [], + significantTerms: [], + significantTermsGroups: [], errors: [], loaded: 0, loadingState: '', @@ -38,10 +38,10 @@ export function streamReducer( } switch (action.type) { - case API_ACTION_NAME.ADD_CHANGE_POINTS: - return { ...state, changePoints: [...state.changePoints, ...action.payload] }; - case API_ACTION_NAME.ADD_CHANGE_POINTS_HISTOGRAM: - const changePoints = state.changePoints.map((cp) => { + case API_ACTION_NAME.ADD_SIGNIFICANT_TERMS: + return { ...state, significantTerms: [...state.significantTerms, ...action.payload] }; + case API_ACTION_NAME.ADD_SIGNIFICANT_TERMS_HISTOGRAM: + const significantTerms = state.significantTerms.map((cp) => { const cpHistogram = action.payload.find( (h) => h.fieldName === cp.fieldName && h.fieldValue === cp.fieldValue ); @@ -50,18 +50,18 @@ export function streamReducer( } return cp; }); - return { ...state, changePoints }; - case API_ACTION_NAME.ADD_CHANGE_POINTS_GROUP: - return { ...state, changePointsGroups: action.payload }; - case API_ACTION_NAME.ADD_CHANGE_POINTS_GROUP_HISTOGRAM: - const changePointsGroups = state.changePointsGroups.map((cpg) => { + return { ...state, significantTerms }; + case API_ACTION_NAME.ADD_SIGNIFICANT_TERMS_GROUP: + return { ...state, significantTermsGroups: action.payload }; + case API_ACTION_NAME.ADD_SIGNIFICANT_TERMS_GROUP_HISTOGRAM: + const significantTermsGroups = state.significantTermsGroups.map((cpg) => { const cpHistogram = action.payload.find((h) => h.id === cpg.id); if (cpHistogram) { cpg.histogram = cpHistogram.histogram; } return cpg; }); - return { ...state, changePointsGroups }; + return { ...state, significantTermsGroups }; case API_ACTION_NAME.ADD_ERROR: return { ...state, errors: [...state.errors, action.payload] }; case API_ACTION_NAME.RESET_ERRORS: diff --git a/x-pack/plugins/aiops/common/types.ts b/x-pack/plugins/aiops/common/types.ts index 0acb3d07883ad..cb6b81dcece6d 100644 --- a/x-pack/plugins/aiops/common/types.ts +++ b/x-pack/plugins/aiops/common/types.ts @@ -5,11 +5,11 @@ * 2.0. */ -import type { ChangePoint, FieldValuePair } from '@kbn/ml-agg-utils'; +import type { SignificantTerm, FieldValuePair } from '@kbn/ml-agg-utils'; -export interface ChangePointDuplicateGroup { - keys: Pick; - group: ChangePoint[]; +export interface SignificantTermDuplicateGroup { + keys: Pick; + group: SignificantTerm[]; } export type FieldValuePairCounts = Record>; diff --git a/x-pack/plugins/aiops/public/application/utils/build_extended_base_filter_criteria.test.ts b/x-pack/plugins/aiops/public/application/utils/build_extended_base_filter_criteria.test.ts index c19dbbdb203c9..c007054801438 100644 --- a/x-pack/plugins/aiops/public/application/utils/build_extended_base_filter_criteria.test.ts +++ b/x-pack/plugins/aiops/public/application/utils/build_extended_base_filter_criteria.test.ts @@ -5,13 +5,13 @@ * 2.0. */ -import type { ChangePoint } from '@kbn/ml-agg-utils'; +import type { SignificantTerm } from '@kbn/ml-agg-utils'; import type { GroupTableItem } from '../../components/spike_analysis_table/types'; import { buildExtendedBaseFilterCriteria } from './build_extended_base_filter_criteria'; -const selectedChangePointMock: ChangePoint = { +const selectedSignificantTermMock: SignificantTerm = { doc_count: 53408, bg_count: 1154, fieldName: 'meta.cloud.instance_id.keyword', @@ -80,13 +80,13 @@ describe('query_utils', () => { ]); }); - it('includes a term filter when including a selectedChangePoint', () => { + it('includes a term filter when including a selectedSignificantTerm', () => { const baseFilterCriteria = buildExtendedBaseFilterCriteria( '@timestamp', 1640082000012, 1640103600906, { match_all: {} }, - selectedChangePointMock + selectedSignificantTermMock ); expect(baseFilterCriteria).toEqual([ @@ -104,13 +104,13 @@ describe('query_utils', () => { ]); }); - it('includes a term filter with must_not when excluding a selectedChangePoint', () => { + it('includes a term filter with must_not when excluding a selectedSignificantTerm', () => { const baseFilterCriteria = buildExtendedBaseFilterCriteria( '@timestamp', 1640082000012, 1640103600906, { match_all: {} }, - selectedChangePointMock, + selectedSignificantTermMock, false ); diff --git a/x-pack/plugins/aiops/public/application/utils/build_extended_base_filter_criteria.ts b/x-pack/plugins/aiops/public/application/utils/build_extended_base_filter_criteria.ts index b8a2ad55f468c..c61196272e58a 100644 --- a/x-pack/plugins/aiops/public/application/utils/build_extended_base_filter_criteria.ts +++ b/x-pack/plugins/aiops/public/application/utils/build_extended_base_filter_criteria.ts @@ -11,7 +11,7 @@ import type * as estypes from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; import type { Query } from '@kbn/es-query'; -import type { ChangePoint, FieldValuePair } from '@kbn/ml-agg-utils'; +import type { SignificantTerm, FieldValuePair } from '@kbn/ml-agg-utils'; import { buildBaseFilterCriteria } from '@kbn/ml-query-utils'; @@ -28,8 +28,8 @@ export function buildExtendedBaseFilterCriteria( earliestMs?: number, latestMs?: number, query?: Query['query'], - selectedChangePoint?: ChangePoint, - includeSelectedChangePoint = true, + selectedSignificantTerm?: SignificantTerm, + includeSelectedSignificantTerm = true, selectedGroup?: GroupTableItem | null ): estypes.QueryDslQueryContainer[] { const filterCriteria = buildBaseFilterCriteria(timeFieldName, earliestMs, latestMs, query); @@ -43,25 +43,25 @@ export function buildExtendedBaseFilterCriteria( } } - if (includeSelectedChangePoint) { - if (selectedChangePoint) { + if (includeSelectedSignificantTerm) { + if (selectedSignificantTerm) { filterCriteria.push({ - term: { [selectedChangePoint.fieldName]: selectedChangePoint.fieldValue }, + term: { [selectedSignificantTerm.fieldName]: selectedSignificantTerm.fieldValue }, }); } else if (selectedGroup) { filterCriteria.push(...groupFilter); } - } else if (selectedChangePoint && !includeSelectedChangePoint) { + } else if (selectedSignificantTerm && !includeSelectedSignificantTerm) { filterCriteria.push({ bool: { must_not: [ { - term: { [selectedChangePoint.fieldName]: selectedChangePoint.fieldValue }, + term: { [selectedSignificantTerm.fieldName]: selectedSignificantTerm.fieldValue }, }, ], }, }); - } else if (selectedGroup && !includeSelectedChangePoint) { + } else if (selectedGroup && !includeSelectedSignificantTerm) { filterCriteria.push({ bool: { must_not: [ diff --git a/x-pack/plugins/aiops/public/components/change_point_detection/change_point_detetion_root.tsx b/x-pack/plugins/aiops/public/components/change_point_detection/change_point_detection_root.tsx similarity index 100% rename from x-pack/plugins/aiops/public/components/change_point_detection/change_point_detetion_root.tsx rename to x-pack/plugins/aiops/public/components/change_point_detection/change_point_detection_root.tsx diff --git a/x-pack/plugins/aiops/public/components/change_point_detection/index.ts b/x-pack/plugins/aiops/public/components/change_point_detection/index.ts index 5083f19cfe39f..af7288a0262d6 100644 --- a/x-pack/plugins/aiops/public/components/change_point_detection/index.ts +++ b/x-pack/plugins/aiops/public/components/change_point_detection/index.ts @@ -5,9 +5,9 @@ * 2.0. */ -export { type ChangePointDetectionAppStateProps } from './change_point_detetion_root'; +export { type ChangePointDetectionAppStateProps } from './change_point_detection_root'; -import { ChangePointDetectionAppState } from './change_point_detetion_root'; +import { ChangePointDetectionAppState } from './change_point_detection_root'; // required for dynamic import using React.lazy() // eslint-disable-next-line import/no-default-export diff --git a/x-pack/plugins/aiops/public/components/explain_log_rate_spikes/explain_log_rate_spikes_analysis.tsx b/x-pack/plugins/aiops/public/components/explain_log_rate_spikes/explain_log_rate_spikes_analysis.tsx index 649ab73098a87..64f1ef92aae7d 100644 --- a/x-pack/plugins/aiops/public/components/explain_log_rate_spikes/explain_log_rate_spikes_analysis.tsx +++ b/x-pack/plugins/aiops/public/components/explain_log_rate_spikes/explain_log_rate_spikes_analysis.tsx @@ -129,7 +129,7 @@ export const ExplainLogRateSpikesAnalysis: FC ((Array.isArray(remainingFieldCandidates) && remainingFieldCandidates.length > 0) || groupsMissing) ) { - setOverrides({ loaded, remainingFieldCandidates, changePoints: data.changePoints }); + setOverrides({ loaded, remainingFieldCandidates, significantTerms: data.significantTerms }); } else { setOverrides(undefined); } @@ -140,7 +140,7 @@ export const ExplainLogRateSpikesAnalysis: FC const errors = useMemo(() => [...streamErrors, ...data.errors], [streamErrors, data.errors]); // Start handler clears possibly hovered or pinned - // change points on analysis refresh. + // significant terms on analysis refresh. function startHandler(continueAnalysis = false) { if (!continueAnalysis) { setOverrides(undefined); @@ -172,8 +172,8 @@ export const ExplainLogRateSpikesAnalysis: FC }, []); const groupTableItems = useMemo( - () => getGroupTableItems(data.changePointsGroups), - [data.changePointsGroups] + () => getGroupTableItems(data.significantTermsGroups), + [data.significantTermsGroups] ); const shouldRerunAnalysis = useMemo( @@ -183,7 +183,7 @@ export const ExplainLogRateSpikesAnalysis: FC [currentAnalysisWindowParameters, windowParameters] ); - const showSpikeAnalysisTable = data?.changePoints.length > 0; + const showSpikeAnalysisTable = data?.significantTerms.length > 0; const groupItemCount = groupTableItems.reduce((p, c) => { return p + c.group.length; }, 0); @@ -288,7 +288,7 @@ export const ExplainLogRateSpikesAnalysis: FC )} {showSpikeAnalysisTable && groupResults && foundGroups ? ( ) : null} {showSpikeAnalysisTable && (!groupResults || !foundGroups) ? ( diff --git a/x-pack/plugins/aiops/public/components/explain_log_rate_spikes/explain_log_rate_spikes_page.tsx b/x-pack/plugins/aiops/public/components/explain_log_rate_spikes/explain_log_rate_spikes_page.tsx index 6c277456d2ec3..6d3528b500f57 100644 --- a/x-pack/plugins/aiops/public/components/explain_log_rate_spikes/explain_log_rate_spikes_page.tsx +++ b/x-pack/plugins/aiops/public/components/explain_log_rate_spikes/explain_log_rate_spikes_page.tsx @@ -19,7 +19,7 @@ import { import { i18n } from '@kbn/i18n'; import type { WindowParameters } from '@kbn/aiops-utils'; -import type { ChangePoint } from '@kbn/ml-agg-utils'; +import type { SignificantTerm } from '@kbn/ml-agg-utils'; import { Filter, FilterStateStore, Query } from '@kbn/es-query'; import { FormattedMessage } from '@kbn/i18n-react'; import { useUrlState, usePageUrlState } from '@kbn/ml-url-state'; @@ -38,9 +38,12 @@ import { PageHeader } from '../page_header'; import { restorableDefaults, type AiOpsPageUrlState } from './explain_log_rate_spikes_app_state'; import { ExplainLogRateSpikesAnalysis } from './explain_log_rate_spikes_analysis'; -function getDocumentCountStatsSplitLabel(changePoint?: ChangePoint, group?: GroupTableItem) { - if (changePoint) { - return `${changePoint?.fieldName}:${changePoint?.fieldValue}`; +function getDocumentCountStatsSplitLabel( + significantTerm?: SignificantTerm, + group?: GroupTableItem +) { + if (significantTerm) { + return `${significantTerm?.fieldName}:${significantTerm?.fieldValue}`; } else if (group) { return i18n.translate('xpack.aiops.spikeAnalysisPage.documentCountStatsSplitGroupLabel', { defaultMessage: 'Selected group', @@ -53,11 +56,11 @@ export const ExplainLogRateSpikesPage: FC = () => { const { dataView, savedSearch } = useDataSource(); const { - currentSelectedChangePoint, + currentSelectedSignificantTerm, currentSelectedGroup, - setPinnedChangePoint, + setPinnedSignificantTerm, setPinnedGroup, - setSelectedChangePoint, + setSelectedSignificantTerm, setSelectedGroup, } = useSpikeAnalysisTableRowContext(); @@ -111,7 +114,7 @@ export const ExplainLogRateSpikesPage: FC = () => { { selectedDataView: dataView, selectedSavedSearch }, aiopsListState, setGlobalState, - currentSelectedChangePoint, + currentSelectedSignificantTerm, currentSelectedGroup ); @@ -161,9 +164,9 @@ export const ExplainLogRateSpikesPage: FC = () => { function clearSelection() { setWindowParameters(undefined); - setPinnedChangePoint(null); + setPinnedSignificantTerm(null); setPinnedGroup(null); - setSelectedChangePoint(null); + setSelectedSignificantTerm(null); setSelectedGroup(null); } @@ -191,7 +194,7 @@ export const ExplainLogRateSpikesPage: FC = () => { documentCountStats={documentCountStats} documentCountStatsSplit={documentCountStatsCompare} documentCountStatsSplitLabel={getDocumentCountStatsSplitLabel( - currentSelectedChangePoint, + currentSelectedSignificantTerm, currentSelectedGroup )} totalCount={totalCount} diff --git a/x-pack/plugins/aiops/public/components/log_categorization/category_table/category_table.tsx b/x-pack/plugins/aiops/public/components/log_categorization/category_table/category_table.tsx index 9c5862d34dbde..c888694a7b0c3 100644 --- a/x-pack/plugins/aiops/public/components/log_categorization/category_table/category_table.tsx +++ b/x-pack/plugins/aiops/public/components/log_categorization/category_table/category_table.tsx @@ -108,7 +108,7 @@ export const CategoryTable: FC = ({ } const histogram = eventRate.map((e) => ({ doc_count_overall: e.docCount, - doc_count_change_point: sparkLine[e.key], + doc_count_significant_term: sparkLine[e.key], key: e.key, key_as_string: `${e.key}`, })); diff --git a/x-pack/plugins/aiops/public/components/mini_histogram/mini_histogram.tsx b/x-pack/plugins/aiops/public/components/mini_histogram/mini_histogram.tsx index ba1e54550ccb0..ce50c08c5c21a 100644 --- a/x-pack/plugins/aiops/public/components/mini_histogram/mini_histogram.tsx +++ b/x-pack/plugins/aiops/public/components/mini_histogram/mini_histogram.tsx @@ -12,13 +12,13 @@ import { Chart, BarSeries, PartialTheme, ScaleType, Settings } from '@elastic/ch import { EuiLoadingChart, EuiTextColor } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n-react'; -import type { ChangePointHistogramItem } from '@kbn/ml-agg-utils'; +import type { SignificantTermHistogramItem } from '@kbn/ml-agg-utils'; import { useAiopsAppContext } from '../../hooks/use_aiops_app_context'; import { useEuiTheme } from '../../hooks/use_eui_theme'; interface MiniHistogramProps { - chartData?: ChangePointHistogramItem[]; + chartData?: SignificantTermHistogramItem[]; isLoading: boolean; label: string; } @@ -102,7 +102,7 @@ export const MiniHistogram: FC = ({ chartData, isLoading, la xScaleType={ScaleType.Time} yScaleType={ScaleType.Linear} xAccessor={'key'} - yAccessors={['doc_count_change_point']} + yAccessors={['doc_count_significant_term']} data={chartData} stackAccessors={[0]} color={['orange']} diff --git a/x-pack/plugins/aiops/public/components/spike_analysis_table/get_group_table_items.test.ts b/x-pack/plugins/aiops/public/components/spike_analysis_table/get_group_table_items.test.ts index 8807479da1242..79c24088aca21 100644 --- a/x-pack/plugins/aiops/public/components/spike_analysis_table/get_group_table_items.test.ts +++ b/x-pack/plugins/aiops/public/components/spike_analysis_table/get_group_table_items.test.ts @@ -5,13 +5,13 @@ * 2.0. */ -import { finalChangePointGroups } from '../../../common/__mocks__/artificial_logs/final_change_point_groups'; +import { finalSignificantTermGroups } from '../../../common/__mocks__/artificial_logs/final_significant_term_groups'; import { getGroupTableItems } from './get_group_table_items'; describe('getGroupTableItems', () => { - it('transforms change point groups into table items', () => { - const groupTableItems = getGroupTableItems(finalChangePointGroups); + it('transforms groups into table items', () => { + const groupTableItems = getGroupTableItems(finalSignificantTermGroups); expect(groupTableItems).toEqual([ { diff --git a/x-pack/plugins/aiops/public/components/spike_analysis_table/get_group_table_items.ts b/x-pack/plugins/aiops/public/components/spike_analysis_table/get_group_table_items.ts index 9135a5449e504..6690311bbca18 100644 --- a/x-pack/plugins/aiops/public/components/spike_analysis_table/get_group_table_items.ts +++ b/x-pack/plugins/aiops/public/components/spike_analysis_table/get_group_table_items.ts @@ -5,12 +5,14 @@ * 2.0. */ -import type { ChangePointGroup, FieldValuePair } from '@kbn/ml-agg-utils'; +import type { SignificantTermGroup, FieldValuePair } from '@kbn/ml-agg-utils'; import type { GroupTableItem } from './types'; -export function getGroupTableItems(changePointsGroups: ChangePointGroup[]): GroupTableItem[] { - const tableItems = changePointsGroups.map(({ id, group, docCount, histogram, pValue }) => { +export function getGroupTableItems( + significantTermsGroups: SignificantTermGroup[] +): GroupTableItem[] { + const tableItems = significantTermsGroups.map(({ id, group, docCount, histogram, pValue }) => { const sortedGroup = group.sort((a, b) => a.fieldName > b.fieldName ? 1 : b.fieldName > a.fieldName ? -1 : 0 ); diff --git a/x-pack/plugins/aiops/public/components/spike_analysis_table/spike_analysis_table.tsx b/x-pack/plugins/aiops/public/components/spike_analysis_table/spike_analysis_table.tsx index 9307d9eced93b..619cd11ea757f 100644 --- a/x-pack/plugins/aiops/public/components/spike_analysis_table/spike_analysis_table.tsx +++ b/x-pack/plugins/aiops/public/components/spike_analysis_table/spike_analysis_table.tsx @@ -22,7 +22,7 @@ import { import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n-react'; import { escapeKuery } from '@kbn/es-query'; -import type { ChangePoint } from '@kbn/ml-agg-utils'; +import type { SignificantTerm } from '@kbn/ml-agg-utils'; import { SEARCH_QUERY_LANGUAGE } from '../../application/utils/search_utils'; import { useEuiTheme } from '../../hooks/use_eui_theme'; @@ -49,14 +49,14 @@ const viewInDiscoverMessage = i18n.translate( ); interface SpikeAnalysisTableProps { - changePoints: ChangePoint[]; + significantTerms: SignificantTerm[]; dataViewId?: string; loading: boolean; isExpandedRow?: boolean; } export const SpikeAnalysisTable: FC = ({ - changePoints, + significantTerms, dataViewId, loading, isExpandedRow, @@ -64,12 +64,16 @@ export const SpikeAnalysisTable: FC = ({ const euiTheme = useEuiTheme(); const primaryBackgroundColor = useEuiBackgroundColor('primary'); - const { pinnedChangePoint, selectedChangePoint, setPinnedChangePoint, setSelectedChangePoint } = - useSpikeAnalysisTableRowContext(); + const { + pinnedSignificantTerm, + selectedSignificantTerm, + setPinnedSignificantTerm, + setSelectedSignificantTerm, + } = useSpikeAnalysisTableRowContext(); const [pageIndex, setPageIndex] = useState(0); const [pageSize, setPageSize] = useState(10); - const [sortField, setSortField] = useState(DEFAULT_SORT_FIELD); + const [sortField, setSortField] = useState(DEFAULT_SORT_FIELD); const [sortDirection, setSortDirection] = useState<'asc' | 'desc'>(DEFAULT_SORT_DIRECTION); const { application, share, data } = useAiopsAppContext(); @@ -112,7 +116,7 @@ export const SpikeAnalysisTable: FC = ({ } }, [application.capabilities.discover?.show, dataViewId, discoverLocator]); - const generateDiscoverUrl = async (changePoint: ChangePoint) => { + const generateDiscoverUrl = async (significantTerm: SignificantTerm) => { if (discoverLocator !== undefined) { const url = await discoverLocator.getRedirectUrl({ indexPatternId: dataViewId, @@ -120,8 +124,8 @@ export const SpikeAnalysisTable: FC = ({ filters: data.query.filterManager.getFilters(), query: { language: SEARCH_QUERY_LANGUAGE.KUERY, - query: `${escapeKuery(changePoint.fieldName)}:${escapeKuery( - String(changePoint.fieldValue) + query: `${escapeKuery(significantTerm.fieldName)}:${escapeKuery( + String(significantTerm.fieldValue) )}`, }, }); @@ -130,7 +134,7 @@ export const SpikeAnalysisTable: FC = ({ } }; - const columns: Array> = [ + const columns: Array> = [ { 'data-test-subj': 'aiopsSpikeAnalysisTableColumnFieldName', field: 'fieldName', @@ -268,8 +272,8 @@ export const SpikeAnalysisTable: FC = ({ ), description: viewInDiscoverMessage, type: 'button', - onClick: async (changePoint) => { - const openInDiscoverUrl = await generateDiscoverUrl(changePoint); + onClick: async (significantTerm) => { + const openInDiscoverUrl = await generateDiscoverUrl(significantTerm); if (typeof openInDiscoverUrl === 'string') { await application.navigateToUrl(openInDiscoverUrl); } @@ -322,10 +326,10 @@ export const SpikeAnalysisTable: FC = ({ const { pagination, pageOfItems, sorting } = useMemo(() => { const pageStart = pageIndex * pageSize; - const itemCount = changePoints?.length ?? 0; + const itemCount = significantTerms?.length ?? 0; - let items: ChangePoint[] = changePoints ?? []; - items = sortBy(changePoints, (item) => { + let items: SignificantTerm[] = significantTerms ?? []; + items = sortBy(significantTerms, (item) => { if (item && typeof item[sortField] === 'string') { // @ts-ignore Object is possibly null or undefined return item[sortField].toLowerCase(); @@ -349,13 +353,13 @@ export const SpikeAnalysisTable: FC = ({ }, }, }; - }, [pageIndex, pageSize, sortField, sortDirection, changePoints]); + }, [pageIndex, pageSize, sortField, sortDirection, significantTerms]); - const getRowStyle = (changePoint: ChangePoint) => { + const getRowStyle = (significantTerm: SignificantTerm) => { if ( - pinnedChangePoint && - pinnedChangePoint.fieldName === changePoint.fieldName && - pinnedChangePoint.fieldValue === changePoint.fieldValue + pinnedSignificantTerm && + pinnedSignificantTerm.fieldName === significantTerm.fieldName && + pinnedSignificantTerm.fieldValue === significantTerm.fieldValue ) { return { backgroundColor: primaryBackgroundColor, @@ -363,9 +367,9 @@ export const SpikeAnalysisTable: FC = ({ } if ( - selectedChangePoint && - selectedChangePoint.fieldName === changePoint.fieldName && - selectedChangePoint.fieldValue === changePoint.fieldValue + selectedSignificantTerm && + selectedSignificantTerm.fieldName === significantTerm.fieldName && + selectedSignificantTerm.fieldValue === significantTerm.fieldValue ) { return { backgroundColor: euiTheme.euiColorLightestShade, @@ -393,27 +397,27 @@ export const SpikeAnalysisTable: FC = ({ onChange={onChange} pagination={pagination} loading={false} - sorting={sorting as EuiTableSortingType} - rowProps={(changePoint) => { + sorting={sorting as EuiTableSortingType} + rowProps={(significantTerm) => { return { - 'data-test-subj': `aiopsSpikeAnalysisTableRow row-${changePoint.fieldName}-${changePoint.fieldValue}`, + 'data-test-subj': `aiopsSpikeAnalysisTableRow row-${significantTerm.fieldName}-${significantTerm.fieldValue}`, onClick: () => { if ( - changePoint.fieldName === pinnedChangePoint?.fieldName && - changePoint.fieldValue === pinnedChangePoint?.fieldValue + significantTerm.fieldName === pinnedSignificantTerm?.fieldName && + significantTerm.fieldValue === pinnedSignificantTerm?.fieldValue ) { - setPinnedChangePoint(null); + setPinnedSignificantTerm(null); } else { - setPinnedChangePoint(changePoint); + setPinnedSignificantTerm(significantTerm); } }, onMouseEnter: () => { - setSelectedChangePoint(changePoint); + setSelectedSignificantTerm(significantTerm); }, onMouseLeave: () => { - setSelectedChangePoint(null); + setSelectedSignificantTerm(null); }, - style: getRowStyle(changePoint), + style: getRowStyle(significantTerm), }; }} /> diff --git a/x-pack/plugins/aiops/public/components/spike_analysis_table/spike_analysis_table_groups.tsx b/x-pack/plugins/aiops/public/components/spike_analysis_table/spike_analysis_table_groups.tsx index 363aff283c4cc..176acdd52ecd3 100644 --- a/x-pack/plugins/aiops/public/components/spike_analysis_table/spike_analysis_table_groups.tsx +++ b/x-pack/plugins/aiops/public/components/spike_analysis_table/spike_analysis_table_groups.tsx @@ -27,7 +27,7 @@ import { import { i18n } from '@kbn/i18n'; import { escapeKuery } from '@kbn/es-query'; import { FormattedMessage } from '@kbn/i18n-react'; -import type { ChangePoint, FieldValuePair } from '@kbn/ml-agg-utils'; +import type { SignificantTerm, FieldValuePair } from '@kbn/ml-agg-utils'; import { SEARCH_QUERY_LANGUAGE } from '../../application/utils/search_utils'; import { useAiopsAppContext } from '../../hooks/use_aiops_app_context'; @@ -56,14 +56,14 @@ const viewInDiscoverMessage = i18n.translate( ); interface SpikeAnalysisTableProps { - changePoints: ChangePoint[]; + significantTerms: SignificantTerm[]; groupTableItems: GroupTableItem[]; dataViewId?: string; loading: boolean; } export const SpikeAnalysisGroupsTable: FC = ({ - changePoints, + significantTerms, groupTableItems, dataViewId, loading, @@ -84,24 +84,24 @@ export const SpikeAnalysisGroupsTable: FC = ({ useSpikeAnalysisTableRowContext(); const pushExpandedTableItem = ( - expandedTableItems: ChangePoint[], + expandedTableItems: SignificantTerm[], items: FieldValuePair[], unique = false ) => { for (const groupItem of items) { const { fieldName, fieldValue } = groupItem; const itemToPush = { - ...(changePoints.find( - (changePoint) => - (changePoint.fieldName === fieldName || - changePoint.fieldName === `${fieldName}.keyword`) && - (changePoint.fieldValue === fieldValue || - changePoint.fieldValue === `${fieldValue}.keyword`) + ...(significantTerms.find( + (significantTerm) => + (significantTerm.fieldName === fieldName || + significantTerm.fieldName === `${fieldName}.keyword`) && + (significantTerm.fieldValue === fieldValue || + significantTerm.fieldValue === `${fieldValue}.keyword`) ) ?? {}), fieldName: `${fieldName}`, fieldValue: `${fieldValue}`, unique, - } as ChangePoint; + } as SignificantTerm; expandedTableItems.push(itemToPush); } @@ -114,14 +114,14 @@ export const SpikeAnalysisGroupsTable: FC = ({ delete itemIdToExpandedRowMapValues[item.id]; } else { const { group, repeatedValues } = item; - const expandedTableItems: ChangePoint[] = []; + const expandedTableItems: SignificantTerm[] = []; pushExpandedTableItem(expandedTableItems, group, true); pushExpandedTableItem(expandedTableItems, repeatedValues); itemIdToExpandedRowMapValues[item.id] = ( >; + pinnedSignificantTerm: SignificantTermOrNull; + setPinnedSignificantTerm: Dispatch>; pinnedGroup: GroupOrNull; setPinnedGroup: Dispatch>; - selectedChangePoint: ChangePointOrNull; - setSelectedChangePoint: Dispatch>; + selectedSignificantTerm: SignificantTermOrNull; + setSelectedSignificantTerm: Dispatch>; selectedGroup: GroupOrNull; setSelectedGroup: Dispatch>; - currentSelectedChangePoint?: ChangePoint; + currentSelectedSignificantTerm?: SignificantTerm; currentSelectedGroup?: GroupTableItem; clearAllRowState: () => void; } @@ -42,19 +42,20 @@ export const spikeAnalysisTableRowContext = createContext { // State that will be shared with all components - const [pinnedChangePoint, setPinnedChangePoint] = useState(null); + const [pinnedSignificantTerm, setPinnedSignificantTerm] = useState(null); const [pinnedGroup, setPinnedGroup] = useState(null); - const [selectedChangePoint, setSelectedChangePoint] = useState(null); + const [selectedSignificantTerm, setSelectedSignificantTerm] = + useState(null); const [selectedGroup, setSelectedGroup] = useState(null); // If a row is pinned, still overrule with a potentially hovered row. - const currentSelectedChangePoint = useMemo(() => { - if (selectedChangePoint) { - return selectedChangePoint; - } else if (pinnedChangePoint) { - return pinnedChangePoint; + const currentSelectedSignificantTerm = useMemo(() => { + if (selectedSignificantTerm) { + return selectedSignificantTerm; + } else if (pinnedSignificantTerm) { + return pinnedSignificantTerm; } - }, [pinnedChangePoint, selectedChangePoint]); + }, [pinnedSignificantTerm, selectedSignificantTerm]); // If a group is pinned, still overrule with a potentially hovered group. const currentSelectedGroup = useMemo(() => { @@ -67,33 +68,33 @@ export const SpikeAnalysisTableRowStateProvider: FC = ({ children }) => { const contextValue: SpikeAnalysisTableRow = useMemo( () => ({ - pinnedChangePoint, - setPinnedChangePoint, + pinnedSignificantTerm, + setPinnedSignificantTerm, pinnedGroup, setPinnedGroup, - selectedChangePoint, - setSelectedChangePoint, + selectedSignificantTerm, + setSelectedSignificantTerm, selectedGroup, setSelectedGroup, - currentSelectedChangePoint, + currentSelectedSignificantTerm, currentSelectedGroup, clearAllRowState: () => { - setPinnedChangePoint(null); + setPinnedSignificantTerm(null); setPinnedGroup(null); - setSelectedChangePoint(null); + setSelectedSignificantTerm(null); setSelectedGroup(null); }, }), [ - pinnedChangePoint, - setPinnedChangePoint, + pinnedSignificantTerm, + setPinnedSignificantTerm, pinnedGroup, setPinnedGroup, - selectedChangePoint, - setSelectedChangePoint, + selectedSignificantTerm, + setSelectedSignificantTerm, selectedGroup, setSelectedGroup, - currentSelectedChangePoint, + currentSelectedSignificantTerm, currentSelectedGroup, ] ); diff --git a/x-pack/plugins/aiops/public/components/spike_analysis_table/types.ts b/x-pack/plugins/aiops/public/components/spike_analysis_table/types.ts index 842816f5095a6..880ad3508b9b7 100644 --- a/x-pack/plugins/aiops/public/components/spike_analysis_table/types.ts +++ b/x-pack/plugins/aiops/public/components/spike_analysis_table/types.ts @@ -5,7 +5,7 @@ * 2.0. */ -import type { ChangePoint, FieldValuePair } from '@kbn/ml-agg-utils'; +import type { SignificantTerm, FieldValuePair } from '@kbn/ml-agg-utils'; export interface GroupTableItem { id: string; @@ -13,5 +13,5 @@ export interface GroupTableItem { pValue: number | null; group: FieldValuePair[]; repeatedValues: FieldValuePair[]; - histogram: ChangePoint['histogram']; + histogram: SignificantTerm['histogram']; } diff --git a/x-pack/plugins/aiops/public/get_document_stats.ts b/x-pack/plugins/aiops/public/get_document_stats.ts index 4f76cb8244e68..cbfefea4d4370 100644 --- a/x-pack/plugins/aiops/public/get_document_stats.ts +++ b/x-pack/plugins/aiops/public/get_document_stats.ts @@ -10,7 +10,7 @@ import { each, get } from 'lodash'; import type * as estypes from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; import { isPopulatedObject } from '@kbn/ml-is-populated-object'; -import type { ChangePoint } from '@kbn/ml-agg-utils'; +import type { SignificantTerm } from '@kbn/ml-agg-utils'; import type { Query } from '@kbn/es-query'; import { buildExtendedBaseFilterCriteria } from './application/utils/build_extended_base_filter_criteria'; @@ -33,8 +33,8 @@ export interface DocumentStatsSearchStrategyParams { timeFieldName?: string; runtimeFieldMap?: estypes.MappingRuntimeFields; fieldsToFetch?: string[]; - selectedChangePoint?: ChangePoint; - includeSelectedChangePoint?: boolean; + selectedSignificantTerm?: SignificantTerm; + includeSelectedSignificantTerm?: boolean; selectedGroup?: GroupTableItem | null; } @@ -48,8 +48,8 @@ export const getDocumentCountStatsRequest = (params: DocumentStatsSearchStrategy searchQuery, intervalMs, fieldsToFetch, - selectedChangePoint, - includeSelectedChangePoint, + selectedSignificantTerm, + includeSelectedSignificantTerm, selectedGroup, } = params; @@ -59,8 +59,8 @@ export const getDocumentCountStatsRequest = (params: DocumentStatsSearchStrategy earliestMs, latestMs, searchQuery, - selectedChangePoint, - includeSelectedChangePoint, + selectedSignificantTerm, + includeSelectedSignificantTerm, selectedGroup ); diff --git a/x-pack/plugins/aiops/public/hooks/use_data.ts b/x-pack/plugins/aiops/public/hooks/use_data.ts index ff04b27b44e3c..3546cead3edab 100644 --- a/x-pack/plugins/aiops/public/hooks/use_data.ts +++ b/x-pack/plugins/aiops/public/hooks/use_data.ts @@ -9,7 +9,7 @@ import { useEffect, useMemo, useState } from 'react'; import { merge } from 'rxjs'; import type { DataView } from '@kbn/data-views-plugin/public'; -import type { ChangePoint } from '@kbn/ml-agg-utils'; +import type { SignificantTerm } from '@kbn/ml-agg-utils'; import type { SavedSearch } from '@kbn/discover-plugin/public'; import type { Dictionary } from '@kbn/ml-url-state'; import { mlTimefilterRefresh$, useTimefilter } from '@kbn/ml-date-picker'; @@ -33,7 +33,7 @@ export const useData = ( }: { selectedDataView: DataView; selectedSavedSearch: SavedSearch | null }, aiopsListState: AiOpsIndexBasedAppState, onUpdate: (params: Dictionary) => void, - selectedChangePoint?: ChangePoint, + selectedSignificantTerm?: SignificantTerm, selectedGroup?: GroupTableItem | null, barTarget: number = DEFAULT_BAR_TARGET ) => { @@ -101,27 +101,27 @@ export const useData = ( return fieldStatsRequest ? { ...fieldStatsRequest, - selectedChangePoint, + selectedSignificantTerm, selectedGroup, - includeSelectedChangePoint: false, + includeSelectedSignificantTerm: false, } : undefined; - }, [fieldStatsRequest, selectedChangePoint, selectedGroup]); + }, [fieldStatsRequest, selectedSignificantTerm, selectedGroup]); - const selectedChangePointStatsRequest = useMemo(() => { - return fieldStatsRequest && (selectedChangePoint || selectedGroup) + const selectedSignificantTermStatsRequest = useMemo(() => { + return fieldStatsRequest && (selectedSignificantTerm || selectedGroup) ? { ...fieldStatsRequest, - selectedChangePoint, + selectedSignificantTerm, selectedGroup, - includeSelectedChangePoint: true, + includeSelectedSignificantTerm: true, } : undefined; - }, [fieldStatsRequest, selectedChangePoint, selectedGroup]); + }, [fieldStatsRequest, selectedSignificantTerm, selectedGroup]); const documentStats = useDocumentCountStats( overallStatsRequest, - selectedChangePointStatsRequest, + selectedSignificantTermStatsRequest, lastRefresh ); diff --git a/x-pack/plugins/aiops/server/routes/explain_log_rate_spikes.ts b/x-pack/plugins/aiops/server/routes/explain_log_rate_spikes.ts index f8fac77a63cd8..298673827acb6 100644 --- a/x-pack/plugins/aiops/server/routes/explain_log_rate_spikes.ts +++ b/x-pack/plugins/aiops/server/routes/explain_log_rate_spikes.ts @@ -16,18 +16,18 @@ import type { Logger } from '@kbn/logging'; import type { DataRequestHandlerContext } from '@kbn/data-plugin/server'; import { streamFactory } from '@kbn/aiops-utils'; import type { - ChangePoint, - ChangePointGroup, + SignificantTerm, + SignificantTermGroup, NumericChartData, NumericHistogramField, } from '@kbn/ml-agg-utils'; import { fetchHistogramsForFields } from '@kbn/ml-agg-utils'; import { - addChangePointsAction, - addChangePointsGroupAction, - addChangePointsGroupHistogramAction, - addChangePointsHistogramAction, + addSignificantTermsAction, + addSignificantTermsGroupAction, + addSignificantTermsGroupHistogramAction, + addSignificantTermsHistogramAction, aiopsExplainLogRateSpikesSchema, addErrorAction, pingAction, @@ -42,12 +42,12 @@ import { isRequestAbortedError } from '../lib/is_request_aborted_error'; import type { AiopsLicense } from '../types'; import { duplicateIdentifier } from './queries/duplicate_identifier'; -import { fetchChangePointPValues } from './queries/fetch_change_point_p_values'; +import { fetchSignificantTermPValues } from './queries/fetch_significant_term_p_values'; import { fetchIndexInfo } from './queries/fetch_index_info'; import { dropDuplicates, fetchFrequentItemSets } from './queries/fetch_frequent_item_sets'; import { getHistogramQuery } from './queries/get_histogram_query'; import { getGroupFilter } from './queries/get_group_filter'; -import { getChangePointGroups } from './queries/get_change_point_groups'; +import { getSignificantTermGroups } from './queries/get_significant_term_groups'; // 10s ping frequency to keep the stream alive. const PING_FREQUENCY = 10000; @@ -251,8 +251,8 @@ export const defineExplainLogRateSpikesRoute = ( // Step 2: Significant Terms - const changePoints: ChangePoint[] = request.body.overrides?.changePoints - ? request.body.overrides?.changePoints + const significantTerms: SignificantTerm[] = request.body.overrides?.significantTerms + ? request.body.overrides?.significantTerms : []; const fieldsToSample = new Set(); @@ -280,10 +280,10 @@ export const defineExplainLogRateSpikesRoute = ( const pValuesQueue = queue(async function (fieldCandidate: string) { loaded += (1 / fieldCandidatesCount) * loadingStepSizePValues; - let pValues: Awaited>; + let pValues: Awaited>; try { - pValues = await fetchChangePointPValues( + pValues = await fetchSignificantTermPValues( client, request.body, [fieldCandidate], @@ -308,9 +308,9 @@ export const defineExplainLogRateSpikesRoute = ( pValues.forEach((d) => { fieldsToSample.add(d.fieldName); }); - changePoints.push(...pValues); + significantTerms.push(...pValues); - push(addChangePointsAction(pValues)); + push(addSignificantTermsAction(pValues)); } push( @@ -323,7 +323,7 @@ export const defineExplainLogRateSpikesRoute = ( defaultMessage: 'Identified {fieldValuePairsCount, plural, one {# significant field/value pair} other {# significant field/value pairs}}.', values: { - fieldValuePairsCount: changePoints.length, + fieldValuePairsCount: significantTerms.length, }, } ), @@ -346,8 +346,8 @@ export const defineExplainLogRateSpikesRoute = ( }); await pValuesQueue.drain(); - if (changePoints.length === 0) { - logDebugMessage('Stopping analysis, did not find change points.'); + if (significantTerms.length === 0) { + logDebugMessage('Stopping analysis, did not find significant terms.'); endWithUpdatedLoadingState(); return; } @@ -423,15 +423,18 @@ export const defineExplainLogRateSpikesRoute = ( }) ); - // Deduplicated change points we pass to the `frequent_item_sets` aggregation. - const deduplicatedChangePoints = dropDuplicates(changePoints, duplicateIdentifier); + // Deduplicated significant terms we pass to the `frequent_item_sets` aggregation. + const deduplicatedSignificantTerms = dropDuplicates( + significantTerms, + duplicateIdentifier + ); try { const { fields, df } = await fetchFrequentItemSets( client, request.body.index, JSON.parse(request.body.searchQuery) as estypes.QueryDslQueryContainer, - deduplicatedChangePoints, + deduplicatedSignificantTerms, request.body.timeFieldName, request.body.deviationMin, request.body.deviationMax, @@ -448,14 +451,18 @@ export const defineExplainLogRateSpikesRoute = ( } if (fields.length > 0 && df.length > 0) { - const changePointGroups = getChangePointGroups(df, changePoints, fields); + const significantTermGroups = getSignificantTermGroups( + df, + significantTerms, + fields + ); // We'll find out if there's at least one group with at least two items, // only then will we return the groups to the clients and make the grouping option available. - const maxItems = Math.max(...changePointGroups.map((g) => g.group.length)); + const maxItems = Math.max(...significantTermGroups.map((g) => g.group.length)); if (maxItems > 1) { - push(addChangePointsGroupAction(changePointGroups)); + push(addSignificantTermsGroupAction(significantTermGroups)); } loaded += PROGRESS_STEP_GROUPING; @@ -468,9 +475,9 @@ export const defineExplainLogRateSpikesRoute = ( return; } - logDebugMessage(`Fetch ${changePointGroups.length} group histograms.`); + logDebugMessage(`Fetch ${significantTermGroups.length} group histograms.`); - const groupHistogramQueue = queue(async function (cpg: ChangePointGroup) { + const groupHistogramQueue = queue(async function (cpg: SignificantTermGroup) { if (shouldStop) { logDebugMessage('shouldStop abort fetching group histograms.'); groupHistogramQueue.kill(); @@ -526,13 +533,13 @@ export const defineExplainLogRateSpikesRoute = ( return { key: o.key, key_as_string: o.key_as_string ?? '', - doc_count_change_point: current.doc_count, + doc_count_significant_term: current.doc_count, doc_count_overall: Math.max(0, o.doc_count - current.doc_count), }; }) ?? []; push( - addChangePointsGroupHistogramAction([ + addSignificantTermsGroupHistogramAction([ { id: cpg.id, histogram, @@ -542,7 +549,7 @@ export const defineExplainLogRateSpikesRoute = ( } }, MAX_CONCURRENT_QUERIES); - groupHistogramQueue.push(changePointGroups); + groupHistogramQueue.push(significantTermGroups); await groupHistogramQueue.drain(); } } catch (e) { @@ -557,11 +564,11 @@ export const defineExplainLogRateSpikesRoute = ( loaded += PROGRESS_STEP_HISTOGRAMS_GROUPS; - logDebugMessage(`Fetch ${changePoints.length} field/value histograms.`); + logDebugMessage(`Fetch ${significantTerms.length} field/value histograms.`); // time series filtered by fields - if (changePoints.length > 0 && overallTimeSeries !== undefined) { - const fieldValueHistogramQueue = queue(async function (cp: ChangePoint) { + if (significantTerms.length > 0 && overallTimeSeries !== undefined) { + const fieldValueHistogramQueue = queue(async function (cp: SignificantTerm) { if (shouldStop) { logDebugMessage('shouldStop abort fetching field/value histograms.'); fieldValueHistogramQueue.kill(); @@ -623,17 +630,17 @@ export const defineExplainLogRateSpikesRoute = ( return { key: o.key, key_as_string: o.key_as_string ?? '', - doc_count_change_point: current.doc_count, + doc_count_significant_term: current.doc_count, doc_count_overall: Math.max(0, o.doc_count - current.doc_count), }; }) ?? []; const { fieldName, fieldValue } = cp; - loaded += (1 / changePoints.length) * PROGRESS_STEP_HISTOGRAMS; + loaded += (1 / significantTerms.length) * PROGRESS_STEP_HISTOGRAMS; pushHistogramDataLoadingState(); push( - addChangePointsHistogramAction([ + addSignificantTermsHistogramAction([ { fieldName, fieldValue, @@ -644,7 +651,7 @@ export const defineExplainLogRateSpikesRoute = ( } }, MAX_CONCURRENT_QUERIES); - fieldValueHistogramQueue.push(changePoints); + fieldValueHistogramQueue.push(significantTerms); await fieldValueHistogramQueue.drain(); } diff --git a/x-pack/plugins/aiops/server/routes/queries/duplicate_identifier.ts b/x-pack/plugins/aiops/server/routes/queries/duplicate_identifier.ts index b8e24946672dc..82513ab2b4e2f 100644 --- a/x-pack/plugins/aiops/server/routes/queries/duplicate_identifier.ts +++ b/x-pack/plugins/aiops/server/routes/queries/duplicate_identifier.ts @@ -5,12 +5,12 @@ * 2.0. */ -import type { ChangePoint } from '@kbn/ml-agg-utils'; +import type { SignificantTerm } from '@kbn/ml-agg-utils'; -// To optimize the `frequent_item_sets` query, we identify duplicate change points by count attributes. -// Note this is a compromise and not 100% accurate because there could be change points that +// To optimize the `frequent_item_sets` query, we identify duplicate significant terms by count attributes. +// Note this is a compromise and not 100% accurate because there could be significant terms that // have the exact same counts but still don't co-occur. -export const duplicateIdentifier: Array = [ +export const duplicateIdentifier: Array = [ 'doc_count', 'bg_count', 'total_doc_count', diff --git a/x-pack/plugins/aiops/server/routes/queries/fetch_frequent_item_sets.ts b/x-pack/plugins/aiops/server/routes/queries/fetch_frequent_item_sets.ts index 2f28a55ceeb2d..dc0b37aee9ea3 100644 --- a/x-pack/plugins/aiops/server/routes/queries/fetch_frequent_item_sets.ts +++ b/x-pack/plugins/aiops/server/routes/queries/fetch_frequent_item_sets.ts @@ -11,10 +11,10 @@ import type * as estypes from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; import type { ElasticsearchClient } from '@kbn/core-elasticsearch-server'; import type { Logger } from '@kbn/logging'; -import { type ChangePoint, RANDOM_SAMPLER_SEED } from '@kbn/ml-agg-utils'; +import { type SignificantTerm, RANDOM_SAMPLER_SEED } from '@kbn/ml-agg-utils'; import { isPopulatedObject } from '@kbn/ml-is-populated-object'; -import type { ChangePointDuplicateGroup, ItemsetResult } from '../../../common/types'; +import type { SignificantTermDuplicateGroup, ItemsetResult } from '../../../common/types'; const FREQUENT_ITEM_SETS_FIELDS_LIMIT = 15; @@ -32,12 +32,15 @@ function isRandomSamplerAggregation(arg: unknown): arg is RandomSamplerAggregati return isPopulatedObject(arg, ['sample']); } -export function dropDuplicates(cps: ChangePoint[], uniqueFields: Array) { +export function dropDuplicates(cps: SignificantTerm[], uniqueFields: Array) { return uniqWith(cps, (a, b) => isEqual(pick(a, uniqueFields), pick(b, uniqueFields))); } -export function groupDuplicates(cps: ChangePoint[], uniqueFields: Array) { - const groups: ChangePointDuplicateGroup[] = []; +export function groupDuplicates( + cps: SignificantTerm[], + uniqueFields: Array +) { + const groups: SignificantTermDuplicateGroup[] = []; for (const cp of cps) { const compareAttributes = pick(cp, uniqueFields); @@ -60,7 +63,7 @@ export async function fetchFrequentItemSets( client: ElasticsearchClient, index: string, searchQuery: estypes.QueryDslQueryContainer, - changePoints: ChangePoint[], + significantTerms: SignificantTerm[], timeFieldName: string, deviationMin: number, deviationMax: number, @@ -70,13 +73,13 @@ export async function fetchFrequentItemSets( emitError: (m: string) => void, abortSignal?: AbortSignal ) { - // Sort change points by ascending p-value, necessary to apply the field limit correctly. - const sortedChangePoints = changePoints.slice().sort((a, b) => { + // Sort significant terms by ascending p-value, necessary to apply the field limit correctly. + const sortedSignificantTerms = significantTerms.slice().sort((a, b) => { return (a.pValue ?? 0) - (b.pValue ?? 0); }); - // Get up to 15 unique fields from change points with retained order - const fields = sortedChangePoints.reduce((p, c) => { + // Get up to 15 unique fields from significant terms with retained order + const fields = sortedSignificantTerms.reduce((p, c) => { if (p.length < FREQUENT_ITEM_SETS_FIELDS_LIMIT && !p.some((d) => d === c.fieldName)) { p.push(c.fieldName); } @@ -97,7 +100,7 @@ export async function fetchFrequentItemSets( }, }, ], - should: sortedChangePoints.map((t) => { + should: sortedSignificantTerms.map((t) => { return { term: { [t.fieldName]: t.fieldValue } }; }), }, @@ -190,7 +193,7 @@ export async function fetchFrequentItemSets( Object.entries(fis.key).forEach(([key, value]) => { result.set[key] = value[0]; - const pValue = sortedChangePoints.find( + const pValue = sortedSignificantTerms.find( (t) => t.fieldName === key && t.fieldValue === value[0] )?.pValue; diff --git a/x-pack/plugins/aiops/server/routes/queries/fetch_change_point_p_values.ts b/x-pack/plugins/aiops/server/routes/queries/fetch_significant_term_p_values.ts similarity index 95% rename from x-pack/plugins/aiops/server/routes/queries/fetch_change_point_p_values.ts rename to x-pack/plugins/aiops/server/routes/queries/fetch_significant_term_p_values.ts index 65ac7e648eec2..60761837c3f6b 100644 --- a/x-pack/plugins/aiops/server/routes/queries/fetch_change_point_p_values.ts +++ b/x-pack/plugins/aiops/server/routes/queries/fetch_significant_term_p_values.ts @@ -9,7 +9,7 @@ import type * as estypes from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; import { ElasticsearchClient } from '@kbn/core/server'; import type { Logger } from '@kbn/logging'; -import { type ChangePoint, RANDOM_SAMPLER_SEED } from '@kbn/ml-agg-utils'; +import { type SignificantTerm, RANDOM_SAMPLER_SEED } from '@kbn/ml-agg-utils'; import { isPopulatedObject } from '@kbn/ml-is-populated-object'; import { SPIKE_ANALYSIS_THRESHOLD } from '../../../common/constants'; import type { AiopsExplainLogRateSpikesSchema } from '../../../common/api/explain_log_rate_spikes'; @@ -22,7 +22,7 @@ import { getRequestBase } from './get_request_base'; // TODO Consolidate with duplicate `fetchDurationFieldCandidates` in // `x-pack/plugins/apm/server/routes/correlations/queries/fetch_failed_events_correlation_p_values.ts` -export const getChangePointRequest = ( +export const getSignificantTermRequest = ( params: AiopsExplainLogRateSpikesSchema, fieldName: string, // The default value of 1 means no sampling will be used @@ -121,7 +121,7 @@ function isRandomSamplerAggregation(arg: unknown): arg is RandomSamplerAggregati return isPopulatedObject(arg, ['sample']); } -export const fetchChangePointPValues = async ( +export const fetchSignificantTermPValues = async ( esClient: ElasticsearchClient, params: AiopsExplainLogRateSpikesSchema, fieldNames: string[], @@ -130,13 +130,13 @@ export const fetchChangePointPValues = async ( sampleProbability: number = 1, emitError: (m: string) => void, abortSignal?: AbortSignal -): Promise => { - const result: ChangePoint[] = []; +): Promise => { + const result: SignificantTerm[] = []; const settledPromises = await Promise.allSettled( fieldNames.map((fieldName) => esClient.search( - getChangePointRequest(params, fieldName, sampleProbability), + getSignificantTermRequest(params, fieldName, sampleProbability), { signal: abortSignal, maxRetries: 0 } ) ) diff --git a/x-pack/plugins/aiops/server/routes/queries/get_change_point_groups.test.ts b/x-pack/plugins/aiops/server/routes/queries/get_change_point_groups.test.ts deleted file mode 100644 index 7340d8e834d10..0000000000000 --- a/x-pack/plugins/aiops/server/routes/queries/get_change_point_groups.test.ts +++ /dev/null @@ -1,21 +0,0 @@ -/* - * 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 { fields } from '../../../common/__mocks__/artificial_logs/fields'; -import { frequentItemSets } from '../../../common/__mocks__/artificial_logs/frequent_item_sets'; -import { changePoints } from '../../../common/__mocks__/artificial_logs/change_points'; -import { finalChangePointGroups } from '../../../common/__mocks__/artificial_logs/final_change_point_groups'; - -import { getChangePointGroups } from './get_change_point_groups'; - -describe('getChangePointGroups', () => { - it('gets change point groups', () => { - const changePointGroups = getChangePointGroups(frequentItemSets, changePoints, fields); - - expect(changePointGroups).toEqual(finalChangePointGroups); - }); -}); diff --git a/x-pack/plugins/aiops/server/routes/queries/get_field_value_pair_counts.test.ts b/x-pack/plugins/aiops/server/routes/queries/get_field_value_pair_counts.test.ts index 7c104e2ecb586..1fc58314b5a2f 100644 --- a/x-pack/plugins/aiops/server/routes/queries/get_field_value_pair_counts.test.ts +++ b/x-pack/plugins/aiops/server/routes/queries/get_field_value_pair_counts.test.ts @@ -5,7 +5,7 @@ * 2.0. */ -import { changePointGroups } from '../../../common/__mocks__/farequote/change_point_groups'; +import { significantTermGroups } from '../../../common/__mocks__/farequote/significant_term_groups'; import { fields } from '../../../common/__mocks__/artificial_logs/fields'; import { filteredFrequentItemSets } from '../../../common/__mocks__/artificial_logs/filtered_frequent_item_sets'; @@ -15,7 +15,7 @@ import { getSimpleHierarchicalTreeLeaves } from './get_simple_hierarchical_tree_ describe('getFieldValuePairCounts', () => { it('returns a nested record with field/value pair counts for farequote', () => { - const fieldValuePairCounts = getFieldValuePairCounts(changePointGroups); + const fieldValuePairCounts = getFieldValuePairCounts(significantTermGroups); expect(fieldValuePairCounts).toEqual({ airline: { diff --git a/x-pack/plugins/aiops/server/routes/queries/get_field_value_pair_counts.ts b/x-pack/plugins/aiops/server/routes/queries/get_field_value_pair_counts.ts index 1c5f5ca33d718..7637bf27919ab 100644 --- a/x-pack/plugins/aiops/server/routes/queries/get_field_value_pair_counts.ts +++ b/x-pack/plugins/aiops/server/routes/queries/get_field_value_pair_counts.ts @@ -5,14 +5,14 @@ * 2.0. */ -import type { ChangePointGroup } from '@kbn/ml-agg-utils'; +import type { SignificantTermGroup } from '@kbn/ml-agg-utils'; import type { FieldValuePairCounts } from '../../../common/types'; /** * Get a nested record of field/value pairs with counts */ -export function getFieldValuePairCounts(cpgs: ChangePointGroup[]): FieldValuePairCounts { +export function getFieldValuePairCounts(cpgs: SignificantTermGroup[]): FieldValuePairCounts { return cpgs.reduce((p, { group }) => { group.forEach(({ fieldName, fieldValue }) => { if (p[fieldName] === undefined) { diff --git a/x-pack/plugins/aiops/server/routes/queries/get_filtered_frequent_item_sets.test.ts b/x-pack/plugins/aiops/server/routes/queries/get_filtered_frequent_item_sets.test.ts index ed907770d3484..e51f91dc1d3d1 100644 --- a/x-pack/plugins/aiops/server/routes/queries/get_filtered_frequent_item_sets.test.ts +++ b/x-pack/plugins/aiops/server/routes/queries/get_filtered_frequent_item_sets.test.ts @@ -5,15 +5,15 @@ * 2.0. */ -import { changePoints } from '../../../common/__mocks__/artificial_logs/change_points'; +import { significantTerms } from '../../../common/__mocks__/artificial_logs/significant_terms'; import { frequentItemSets } from '../../../common/__mocks__/artificial_logs/frequent_item_sets'; import { filteredFrequentItemSets } from '../../../common/__mocks__/artificial_logs/filtered_frequent_item_sets'; import { getFilteredFrequentItemSets } from './get_filtered_frequent_item_sets'; describe('getFilteredFrequentItemSets', () => { - it('filter frequent item set based on provided change points', () => { - expect(getFilteredFrequentItemSets(frequentItemSets, changePoints)).toStrictEqual( + it('filter frequent item set based on provided significant terms', () => { + expect(getFilteredFrequentItemSets(frequentItemSets, significantTerms)).toStrictEqual( filteredFrequentItemSets ); }); diff --git a/x-pack/plugins/aiops/server/routes/queries/get_filtered_frequent_item_sets.ts b/x-pack/plugins/aiops/server/routes/queries/get_filtered_frequent_item_sets.ts index 15ba268fa733c..bd987e0e3c960 100644 --- a/x-pack/plugins/aiops/server/routes/queries/get_filtered_frequent_item_sets.ts +++ b/x-pack/plugins/aiops/server/routes/queries/get_filtered_frequent_item_sets.ts @@ -7,22 +7,22 @@ import { isEqual } from 'lodash'; -import type { ChangePoint } from '@kbn/ml-agg-utils'; +import type { SignificantTerm } from '@kbn/ml-agg-utils'; import type { ItemsetResult } from '../../../common/types'; // The way the `frequent_item_sets` aggregation works could return item sets that include -// field/value pairs that are not part of the original list of significant change points. +// field/value pairs that are not part of the original list of significant terms. // This cleans up groups and removes those unrelated field/value pairs. export function getFilteredFrequentItemSets( itemsets: ItemsetResult[], - changePoints: ChangePoint[] + significantTerms: SignificantTerm[] ): ItemsetResult[] { return itemsets.reduce((p, itemset, itemsetIndex) => { - // Remove field/value pairs not part of the provided change points + // Remove field/value pairs not part of the provided significant terms itemset.set = Object.entries(itemset.set).reduce( (set, [field, value]) => { - if (changePoints.some((cp) => cp.fieldName === field && cp.fieldValue === value)) { + if (significantTerms.some((cp) => cp.fieldName === field && cp.fieldValue === value)) { set[field] = value; } return set; diff --git a/x-pack/plugins/aiops/server/routes/queries/get_group_filter.test.ts b/x-pack/plugins/aiops/server/routes/queries/get_group_filter.test.ts index b2c15d70e83f9..3da9c5f43a0f3 100644 --- a/x-pack/plugins/aiops/server/routes/queries/get_group_filter.test.ts +++ b/x-pack/plugins/aiops/server/routes/queries/get_group_filter.test.ts @@ -5,13 +5,13 @@ * 2.0. */ -import { finalChangePointGroups } from '../../../common/__mocks__/artificial_logs/final_change_point_groups'; +import { finalSignificantTermGroups } from '../../../common/__mocks__/artificial_logs/final_significant_term_groups'; import { getGroupFilter } from './get_group_filter'; describe('getGroupFilter', () => { - it('gets a query filter for the change points of a group with multiple values per field', () => { - expect(getGroupFilter(finalChangePointGroups[0])).toStrictEqual([ + it('gets a query filter for the significant terms of a group with multiple values per field', () => { + expect(getGroupFilter(finalSignificantTermGroups[0])).toStrictEqual([ { term: { response_code: '500', @@ -25,8 +25,8 @@ describe('getGroupFilter', () => { ]); }); - it('gets a query filter for the change points of a group with just a single field/value', () => { - expect(getGroupFilter(finalChangePointGroups[1])).toStrictEqual([ + it('gets a query filter for the significant terms of a group with just a single field/value', () => { + expect(getGroupFilter(finalSignificantTermGroups[1])).toStrictEqual([ { term: { user: 'Peter', diff --git a/x-pack/plugins/aiops/server/routes/queries/get_group_filter.ts b/x-pack/plugins/aiops/server/routes/queries/get_group_filter.ts index fdd97eb101072..b6d780310df83 100644 --- a/x-pack/plugins/aiops/server/routes/queries/get_group_filter.ts +++ b/x-pack/plugins/aiops/server/routes/queries/get_group_filter.ts @@ -7,18 +7,18 @@ import * as estypes from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; -import type { ChangePointGroup } from '@kbn/ml-agg-utils'; +import type { SignificantTermGroup } from '@kbn/ml-agg-utils'; -// Transforms a list of change point items from a group in a query filter. +// Transforms a list of significant terms from a group in a query filter. // Uses a `term` filter for single field value combinations. // For fields with multiple values it creates a single `terms` filter that includes // all values. This avoids queries not returning any results otherwise because // separate `term` filter for multiple values for the same field would rule each other out. export function getGroupFilter( - changePointGroup: ChangePointGroup + significantTermGroup: SignificantTermGroup ): estypes.QueryDslQueryContainer[] { return Object.entries( - changePointGroup.group.reduce>>((p, c) => { + significantTermGroup.group.reduce>>((p, c) => { if (p[c.fieldName]) { p[c.fieldName].push(c.fieldValue); } else { diff --git a/x-pack/plugins/aiops/server/routes/queries/get_groups_with_readded_duplicates.test.ts b/x-pack/plugins/aiops/server/routes/queries/get_groups_with_readded_duplicates.test.ts index 257acff9793b5..4c5e75f1ff0c1 100644 --- a/x-pack/plugins/aiops/server/routes/queries/get_groups_with_readded_duplicates.test.ts +++ b/x-pack/plugins/aiops/server/routes/queries/get_groups_with_readded_duplicates.test.ts @@ -5,8 +5,8 @@ * 2.0. */ -import { changePointGroups } from '../../../common/__mocks__/artificial_logs/change_point_groups'; -import { changePoints } from '../../../common/__mocks__/artificial_logs/change_points'; +import { significantTermGroups } from '../../../common/__mocks__/artificial_logs/significant_term_groups'; +import { significantTerms } from '../../../common/__mocks__/artificial_logs/significant_terms'; import { duplicateIdentifier } from './duplicate_identifier'; import { getGroupsWithReaddedDuplicates } from './get_groups_with_readded_duplicates'; @@ -16,15 +16,15 @@ import { getMarkedDuplicates } from './get_marked_duplicates'; describe('getGroupsWithReaddedDuplicates', () => { it('gets groups with readded duplicates', () => { - const groupedChangePoints = groupDuplicates(changePoints, duplicateIdentifier).filter( + const groupedSignificantTerms = groupDuplicates(significantTerms, duplicateIdentifier).filter( (g) => g.group.length > 1 ); - const fieldValuePairCounts = getFieldValuePairCounts(changePointGroups); - const markedDuplicates = getMarkedDuplicates(changePointGroups, fieldValuePairCounts); + const fieldValuePairCounts = getFieldValuePairCounts(significantTermGroups); + const markedDuplicates = getMarkedDuplicates(significantTermGroups, fieldValuePairCounts); const groupsWithReaddedDuplicates = getGroupsWithReaddedDuplicates( markedDuplicates, - groupedChangePoints + groupedSignificantTerms ); expect(groupsWithReaddedDuplicates).toEqual([ diff --git a/x-pack/plugins/aiops/server/routes/queries/get_groups_with_readded_duplicates.ts b/x-pack/plugins/aiops/server/routes/queries/get_groups_with_readded_duplicates.ts index 91622c2b15419..c2e1c9ec1c5da 100644 --- a/x-pack/plugins/aiops/server/routes/queries/get_groups_with_readded_duplicates.ts +++ b/x-pack/plugins/aiops/server/routes/queries/get_groups_with_readded_duplicates.ts @@ -7,20 +7,20 @@ import { uniqWith, isEqual } from 'lodash'; -import type { ChangePointGroup } from '@kbn/ml-agg-utils'; +import type { SignificantTermGroup } from '@kbn/ml-agg-utils'; -import type { ChangePointDuplicateGroup } from '../../../common/types'; +import type { SignificantTermDuplicateGroup } from '../../../common/types'; export function getGroupsWithReaddedDuplicates( - groups: ChangePointGroup[], - groupedChangePoints: ChangePointDuplicateGroup[] -): ChangePointGroup[] { + groups: SignificantTermGroup[], + groupedSignificantTerms: SignificantTermDuplicateGroup[] +): SignificantTermGroup[] { return groups.map((g) => { const group = [...g.group]; for (const groupItem of g.group) { const { duplicate } = groupItem; - const duplicates = groupedChangePoints.find((d) => + const duplicates = groupedSignificantTerms.find((d) => d.group.some( (dg) => dg.fieldName === groupItem.fieldName && dg.fieldValue === groupItem.fieldValue ) diff --git a/x-pack/plugins/aiops/server/routes/queries/get_marked_duplicates.test.ts b/x-pack/plugins/aiops/server/routes/queries/get_marked_duplicates.test.ts index f84f08e235b6a..91be036fa21bd 100644 --- a/x-pack/plugins/aiops/server/routes/queries/get_marked_duplicates.test.ts +++ b/x-pack/plugins/aiops/server/routes/queries/get_marked_duplicates.test.ts @@ -5,7 +5,7 @@ * 2.0. */ -import { changePointGroups } from '../../../common/__mocks__/farequote/change_point_groups'; +import { significantTermGroups } from '../../../common/__mocks__/farequote/significant_term_groups'; import { fields } from '../../../common/__mocks__/artificial_logs/fields'; import { filteredFrequentItemSets } from '../../../common/__mocks__/artificial_logs/filtered_frequent_item_sets'; @@ -15,9 +15,9 @@ import { getSimpleHierarchicalTree } from './get_simple_hierarchical_tree'; import { getSimpleHierarchicalTreeLeaves } from './get_simple_hierarchical_tree_leaves'; describe('markDuplicates', () => { - it('marks duplicates based on change point groups for farequote', () => { - const fieldValuePairCounts = getFieldValuePairCounts(changePointGroups); - const markedDuplicates = getMarkedDuplicates(changePointGroups, fieldValuePairCounts); + it('marks duplicates based on significant terms groups for farequote', () => { + const fieldValuePairCounts = getFieldValuePairCounts(significantTermGroups); + const markedDuplicates = getMarkedDuplicates(significantTermGroups, fieldValuePairCounts); expect(markedDuplicates).toEqual([ { @@ -57,7 +57,7 @@ describe('markDuplicates', () => { ]); }); - it('marks duplicates based on change point groups for artificial logs', () => { + it('marks duplicates based on significant terms groups for artificial logs', () => { const simpleHierarchicalTree = getSimpleHierarchicalTree( filteredFrequentItemSets, true, diff --git a/x-pack/plugins/aiops/server/routes/queries/get_marked_duplicates.ts b/x-pack/plugins/aiops/server/routes/queries/get_marked_duplicates.ts index a4ed85e8e94b6..374c6c8085d19 100644 --- a/x-pack/plugins/aiops/server/routes/queries/get_marked_duplicates.ts +++ b/x-pack/plugins/aiops/server/routes/queries/get_marked_duplicates.ts @@ -5,17 +5,17 @@ * 2.0. */ -import type { ChangePointGroup } from '@kbn/ml-agg-utils'; +import type { SignificantTermGroup } from '@kbn/ml-agg-utils'; import type { FieldValuePairCounts } from '../../../common/types'; /** - * Analyse duplicate field/value pairs in change point groups. + * Analyse duplicate field/value pairs in groups. */ export function getMarkedDuplicates( - cpgs: ChangePointGroup[], + cpgs: SignificantTermGroup[], fieldValuePairCounts: FieldValuePairCounts -): ChangePointGroup[] { +): SignificantTermGroup[] { return cpgs.map((cpg) => { return { ...cpg, diff --git a/x-pack/plugins/aiops/server/routes/queries/get_missing_change_points.test.ts b/x-pack/plugins/aiops/server/routes/queries/get_missing_significant_terms.test.ts similarity index 54% rename from x-pack/plugins/aiops/server/routes/queries/get_missing_change_points.test.ts rename to x-pack/plugins/aiops/server/routes/queries/get_missing_significant_terms.test.ts index 757097c84e0c8..55a220f9d6d17 100644 --- a/x-pack/plugins/aiops/server/routes/queries/get_missing_change_points.test.ts +++ b/x-pack/plugins/aiops/server/routes/queries/get_missing_significant_terms.test.ts @@ -5,37 +5,37 @@ * 2.0. */ -import { changePointGroups } from '../../../common/__mocks__/artificial_logs/change_point_groups'; -import { changePoints } from '../../../common/__mocks__/artificial_logs/change_points'; +import { significantTermGroups } from '../../../common/__mocks__/artificial_logs/significant_term_groups'; +import { significantTerms } from '../../../common/__mocks__/artificial_logs/significant_terms'; import { duplicateIdentifier } from './duplicate_identifier'; import { getGroupsWithReaddedDuplicates } from './get_groups_with_readded_duplicates'; import { dropDuplicates, groupDuplicates } from './fetch_frequent_item_sets'; import { getFieldValuePairCounts } from './get_field_value_pair_counts'; import { getMarkedDuplicates } from './get_marked_duplicates'; -import { getMissingChangePoints } from './get_missing_change_points'; +import { getMissingSignificantTerms } from './get_missing_significant_terms'; -describe('getMissingChangePoints', () => { - it('get missing change points', () => { - const deduplicatedChangePoints = dropDuplicates(changePoints, duplicateIdentifier); +describe('getMissingSignificantTerms', () => { + it('get missing significant terms', () => { + const deduplicatedSignificantTerms = dropDuplicates(significantTerms, duplicateIdentifier); - const groupedChangePoints = groupDuplicates(changePoints, duplicateIdentifier).filter( + const groupedSignificantTerms = groupDuplicates(significantTerms, duplicateIdentifier).filter( (g) => g.group.length > 1 ); - const fieldValuePairCounts = getFieldValuePairCounts(changePointGroups); - const markedDuplicates = getMarkedDuplicates(changePointGroups, fieldValuePairCounts); + const fieldValuePairCounts = getFieldValuePairCounts(significantTermGroups); + const markedDuplicates = getMarkedDuplicates(significantTermGroups, fieldValuePairCounts); const groupsWithReaddedDuplicates = getGroupsWithReaddedDuplicates( markedDuplicates, - groupedChangePoints + groupedSignificantTerms ); - const missingChangePoints = getMissingChangePoints( - deduplicatedChangePoints, + const missingSignificantTerms = getMissingSignificantTerms( + deduplicatedSignificantTerms, groupsWithReaddedDuplicates ); - expect(missingChangePoints).toEqual([ + expect(missingSignificantTerms).toEqual([ { bg_count: 553, doc_count: 1981, diff --git a/x-pack/plugins/aiops/server/routes/queries/get_missing_change_points.ts b/x-pack/plugins/aiops/server/routes/queries/get_missing_significant_terms.ts similarity index 53% rename from x-pack/plugins/aiops/server/routes/queries/get_missing_change_points.ts rename to x-pack/plugins/aiops/server/routes/queries/get_missing_significant_terms.ts index 57422ad16213f..c29d6e6614bb9 100644 --- a/x-pack/plugins/aiops/server/routes/queries/get_missing_change_points.ts +++ b/x-pack/plugins/aiops/server/routes/queries/get_missing_significant_terms.ts @@ -5,14 +5,14 @@ * 2.0. */ -import type { ChangePoint, ChangePointGroup } from '@kbn/ml-agg-utils'; +import type { SignificantTerm, SignificantTermGroup } from '@kbn/ml-agg-utils'; -export function getMissingChangePoints( - deduplicatedChangePoints: ChangePoint[], - changePointGroups: ChangePointGroup[] +export function getMissingSignificantTerms( + deduplicatedSignificantTerms: SignificantTerm[], + significantTermGroups: SignificantTermGroup[] ) { - return deduplicatedChangePoints.filter((cp) => { - return !changePointGroups.some((cpg) => { + return deduplicatedSignificantTerms.filter((cp) => { + return !significantTermGroups.some((cpg) => { return cpg.group.some((d) => d.fieldName === cp.fieldName && d.fieldValue === cp.fieldValue); }); }); diff --git a/x-pack/plugins/aiops/server/routes/queries/get_significant_term_groups.test.ts b/x-pack/plugins/aiops/server/routes/queries/get_significant_term_groups.test.ts new file mode 100644 index 0000000000000..e3ae80a435931 --- /dev/null +++ b/x-pack/plugins/aiops/server/routes/queries/get_significant_term_groups.test.ts @@ -0,0 +1,25 @@ +/* + * 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 { fields } from '../../../common/__mocks__/artificial_logs/fields'; +import { frequentItemSets } from '../../../common/__mocks__/artificial_logs/frequent_item_sets'; +import { significantTerms } from '../../../common/__mocks__/artificial_logs/significant_terms'; +import { finalSignificantTermGroups } from '../../../common/__mocks__/artificial_logs/final_significant_term_groups'; + +import { getSignificantTermGroups } from './get_significant_term_groups'; + +describe('getSignificantTermGroups', () => { + it('gets significant terms groups', () => { + const significantTermGroups = getSignificantTermGroups( + frequentItemSets, + significantTerms, + fields + ); + + expect(significantTermGroups).toEqual(finalSignificantTermGroups); + }); +}); diff --git a/x-pack/plugins/aiops/server/routes/queries/get_change_point_groups.ts b/x-pack/plugins/aiops/server/routes/queries/get_significant_term_groups.ts similarity index 64% rename from x-pack/plugins/aiops/server/routes/queries/get_change_point_groups.ts rename to x-pack/plugins/aiops/server/routes/queries/get_significant_term_groups.ts index 5b9920c180d45..f7fcde07a5cfd 100644 --- a/x-pack/plugins/aiops/server/routes/queries/get_change_point_groups.ts +++ b/x-pack/plugins/aiops/server/routes/queries/get_significant_term_groups.ts @@ -5,7 +5,7 @@ * 2.0. */ -import type { ChangePoint, ChangePointGroup } from '@kbn/ml-agg-utils'; +import type { SignificantTerm, SignificantTermGroup } from '@kbn/ml-agg-utils'; import { duplicateIdentifier } from './duplicate_identifier'; import { dropDuplicates, groupDuplicates } from './fetch_frequent_item_sets'; @@ -15,25 +15,25 @@ import { getSimpleHierarchicalTree } from './get_simple_hierarchical_tree'; import { getSimpleHierarchicalTreeLeaves } from './get_simple_hierarchical_tree_leaves'; import { getFilteredFrequentItemSets } from './get_filtered_frequent_item_sets'; import { getGroupsWithReaddedDuplicates } from './get_groups_with_readded_duplicates'; -import { getMissingChangePoints } from './get_missing_change_points'; -import { transformChangePointToGroup } from './transform_change_point_to_group'; +import { getMissingSignificantTerms } from './get_missing_significant_terms'; +import { transformSignificantTermToGroup } from './transform_significant_term_to_group'; import type { ItemsetResult } from '../../../common/types'; -export function getChangePointGroups( +export function getSignificantTermGroups( itemsets: ItemsetResult[], - changePoints: ChangePoint[], + significantTerms: SignificantTerm[], fields: string[] -): ChangePointGroup[] { - // These are the deduplicated change points we pass to the `frequent_item_sets` aggregation. - const deduplicatedChangePoints = dropDuplicates(changePoints, duplicateIdentifier); +): SignificantTermGroup[] { + // These are the deduplicated significant terms we pass to the `frequent_item_sets` aggregation. + const deduplicatedSignificantTerms = dropDuplicates(significantTerms, duplicateIdentifier); - // We use the grouped change points to later repopulate + // We use the grouped significant terms to later repopulate // the `frequent_item_sets` result with the missing duplicates. - const groupedChangePoints = groupDuplicates(changePoints, duplicateIdentifier).filter( + const groupedSignificantTerms = groupDuplicates(significantTerms, duplicateIdentifier).filter( (g) => g.group.length > 1 ); - const filteredDf = getFilteredFrequentItemSets(itemsets, changePoints); + const filteredDf = getFilteredFrequentItemSets(itemsets, significantTerms); // `frequent_item_sets` returns lot of different small groups of field/value pairs that co-occur. // The following steps analyse these small groups, identify overlap between these groups, @@ -50,25 +50,28 @@ export function getChangePointGroups( // unique to a group in a better way. This step will also re-add duplicates we identified in the // beginning and didn't pass on to the `frequent_item_sets` agg. const fieldValuePairCounts = getFieldValuePairCounts(treeLeaves); - const changePointGroupsWithMarkedDuplicates = getMarkedDuplicates( + const significantTermGroupsWithMarkedDuplicates = getMarkedDuplicates( treeLeaves, fieldValuePairCounts ); - const changePointGroups = getGroupsWithReaddedDuplicates( - changePointGroupsWithMarkedDuplicates, - groupedChangePoints + const significantTermGroups = getGroupsWithReaddedDuplicates( + significantTermGroupsWithMarkedDuplicates, + groupedSignificantTerms ); // Some field/value pairs might not be part of the `frequent_item_sets` result set, for example // because they don't co-occur with other field/value pairs or because of the limits we set on the query. // In this next part we identify those missing pairs and add them as individual groups. - const missingChangePoints = getMissingChangePoints(deduplicatedChangePoints, changePointGroups); + const missingSignificantTerms = getMissingSignificantTerms( + deduplicatedSignificantTerms, + significantTermGroups + ); - changePointGroups.push( - ...missingChangePoints.map((changePoint) => - transformChangePointToGroup(changePoint, groupedChangePoints) + significantTermGroups.push( + ...missingSignificantTerms.map((significantTerm) => + transformSignificantTermToGroup(significantTerm, groupedSignificantTerms) ) ); - return changePointGroups; + return significantTermGroups; } diff --git a/x-pack/plugins/aiops/server/routes/queries/get_simple_hierarchical_tree_leaves.ts b/x-pack/plugins/aiops/server/routes/queries/get_simple_hierarchical_tree_leaves.ts index 699c6e447c4de..27054ccec839e 100644 --- a/x-pack/plugins/aiops/server/routes/queries/get_simple_hierarchical_tree_leaves.ts +++ b/x-pack/plugins/aiops/server/routes/queries/get_simple_hierarchical_tree_leaves.ts @@ -5,7 +5,7 @@ * 2.0. */ -import type { ChangePointGroup } from '@kbn/ml-agg-utils'; +import type { SignificantTermGroup } from '@kbn/ml-agg-utils'; import { stringHash } from '@kbn/ml-string-hash'; import type { SimpleHierarchicalTreeNode } from '../../../common/types'; @@ -15,7 +15,7 @@ import type { SimpleHierarchicalTreeNode } from '../../../common/types'; */ export function getSimpleHierarchicalTreeLeaves( tree: SimpleHierarchicalTreeNode, - leaves: ChangePointGroup[], + leaves: SignificantTermGroup[], level = 1 ) { if (tree.children.length === 0) { diff --git a/x-pack/plugins/aiops/server/routes/queries/transform_change_point_to_group.test.ts b/x-pack/plugins/aiops/server/routes/queries/transform_change_point_to_group.test.ts deleted file mode 100644 index c1ed0bcbcb6ab..0000000000000 --- a/x-pack/plugins/aiops/server/routes/queries/transform_change_point_to_group.test.ts +++ /dev/null @@ -1,48 +0,0 @@ -/* - * 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 { changePointGroups } from '../../../common/__mocks__/artificial_logs/change_point_groups'; -import { changePoints } from '../../../common/__mocks__/artificial_logs/change_points'; - -import { duplicateIdentifier } from './duplicate_identifier'; -import { getGroupsWithReaddedDuplicates } from './get_groups_with_readded_duplicates'; -import { dropDuplicates, groupDuplicates } from './fetch_frequent_item_sets'; -import { getFieldValuePairCounts } from './get_field_value_pair_counts'; -import { getMarkedDuplicates } from './get_marked_duplicates'; -import { getMissingChangePoints } from './get_missing_change_points'; -import { transformChangePointToGroup } from './transform_change_point_to_group'; - -describe('getMissingChangePoints', () => { - it('get missing change points', () => { - const deduplicatedChangePoints = dropDuplicates(changePoints, duplicateIdentifier); - - const groupedChangePoints = groupDuplicates(changePoints, duplicateIdentifier).filter( - (g) => g.group.length > 1 - ); - - const fieldValuePairCounts = getFieldValuePairCounts(changePointGroups); - const markedDuplicates = getMarkedDuplicates(changePointGroups, fieldValuePairCounts); - const groupsWithReaddedDuplicates = getGroupsWithReaddedDuplicates( - markedDuplicates, - groupedChangePoints - ); - - const missingChangePoints = getMissingChangePoints( - deduplicatedChangePoints, - groupsWithReaddedDuplicates - ); - - const transformed = transformChangePointToGroup(missingChangePoints[0], groupedChangePoints); - - expect(transformed).toEqual({ - docCount: 1981, - group: [{ duplicate: false, fieldName: 'user', fieldValue: 'Peter' }], - id: '817080373', - pValue: 2.7454255728359757e-21, - }); - }); -}); diff --git a/x-pack/plugins/aiops/server/routes/queries/transform_significant_term_to_group.test.ts b/x-pack/plugins/aiops/server/routes/queries/transform_significant_term_to_group.test.ts new file mode 100644 index 0000000000000..4d52930b73c77 --- /dev/null +++ b/x-pack/plugins/aiops/server/routes/queries/transform_significant_term_to_group.test.ts @@ -0,0 +1,51 @@ +/* + * 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 { significantTermGroups } from '../../../common/__mocks__/artificial_logs/significant_term_groups'; +import { significantTerms } from '../../../common/__mocks__/artificial_logs/significant_terms'; + +import { duplicateIdentifier } from './duplicate_identifier'; +import { getGroupsWithReaddedDuplicates } from './get_groups_with_readded_duplicates'; +import { dropDuplicates, groupDuplicates } from './fetch_frequent_item_sets'; +import { getFieldValuePairCounts } from './get_field_value_pair_counts'; +import { getMarkedDuplicates } from './get_marked_duplicates'; +import { getMissingSignificantTerms } from './get_missing_significant_terms'; +import { transformSignificantTermToGroup } from './transform_significant_term_to_group'; + +describe('getMissingSignificantTerms', () => { + it('get missing significant terms', () => { + const deduplicatedSignificantTerms = dropDuplicates(significantTerms, duplicateIdentifier); + + const groupedSignificantTerms = groupDuplicates(significantTerms, duplicateIdentifier).filter( + (g) => g.group.length > 1 + ); + + const fieldValuePairCounts = getFieldValuePairCounts(significantTermGroups); + const markedDuplicates = getMarkedDuplicates(significantTermGroups, fieldValuePairCounts); + const groupsWithReaddedDuplicates = getGroupsWithReaddedDuplicates( + markedDuplicates, + groupedSignificantTerms + ); + + const missingSignificantTerms = getMissingSignificantTerms( + deduplicatedSignificantTerms, + groupsWithReaddedDuplicates + ); + + const transformed = transformSignificantTermToGroup( + missingSignificantTerms[0], + groupedSignificantTerms + ); + + expect(transformed).toEqual({ + docCount: 1981, + group: [{ duplicate: false, fieldName: 'user', fieldValue: 'Peter' }], + id: '817080373', + pValue: 2.7454255728359757e-21, + }); + }); +}); diff --git a/x-pack/plugins/aiops/server/routes/queries/transform_change_point_to_group.ts b/x-pack/plugins/aiops/server/routes/queries/transform_significant_term_to_group.ts similarity index 76% rename from x-pack/plugins/aiops/server/routes/queries/transform_change_point_to_group.ts rename to x-pack/plugins/aiops/server/routes/queries/transform_significant_term_to_group.ts index 8e6c77971dcee..03de8130b7a33 100644 --- a/x-pack/plugins/aiops/server/routes/queries/transform_change_point_to_group.ts +++ b/x-pack/plugins/aiops/server/routes/queries/transform_significant_term_to_group.ts @@ -6,17 +6,17 @@ */ import { stringHash } from '@kbn/ml-string-hash'; -import type { ChangePoint } from '@kbn/ml-agg-utils'; +import type { SignificantTerm } from '@kbn/ml-agg-utils'; -import type { ChangePointDuplicateGroup } from '../../../common/types'; +import type { SignificantTermDuplicateGroup } from '../../../common/types'; -export function transformChangePointToGroup( - changePoint: ChangePoint, - groupedChangePoints: ChangePointDuplicateGroup[] +export function transformSignificantTermToGroup( + significantTerm: SignificantTerm, + groupedSignificantTerms: SignificantTermDuplicateGroup[] ) { - const { fieldName, fieldValue, doc_count: docCount, pValue } = changePoint; + const { fieldName, fieldValue, doc_count: docCount, pValue } = significantTerm; - const duplicates = groupedChangePoints.find((d) => + const duplicates = groupedSignificantTerms.find((d) => d.group.some((dg) => dg.fieldName === fieldName && dg.fieldValue === fieldValue) ); diff --git a/x-pack/plugins/alerting/common/alert_schema/field_maps/component_template_from_field_map.test.ts b/x-pack/plugins/alerting/common/alert_schema/field_maps/component_template_from_field_map.test.ts new file mode 100644 index 0000000000000..17bde15bd01a5 --- /dev/null +++ b/x-pack/plugins/alerting/common/alert_schema/field_maps/component_template_from_field_map.test.ts @@ -0,0 +1,79 @@ +/* + * 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 { getComponentTemplateFromFieldMap } from './component_template_from_field_map'; +import { testFieldMap, expectedTestMapping } from './mapping_from_field_map.test'; + +describe('getComponentTemplateFromFieldMap', () => { + it('correctly creates component template from field map', () => { + expect( + getComponentTemplateFromFieldMap({ name: 'test-mappings', fieldMap: testFieldMap }) + ).toEqual({ + name: 'test-mappings', + _meta: { + managed: true, + }, + template: { + settings: {}, + mappings: { + dynamic: 'strict', + ...expectedTestMapping, + }, + }, + }); + }); + + it('correctly creates component template with settings when includeSettings = true', () => { + expect( + getComponentTemplateFromFieldMap({ + name: 'test-mappings', + fieldMap: testFieldMap, + includeSettings: true, + }) + ).toEqual({ + name: 'test-mappings', + _meta: { + managed: true, + }, + template: { + settings: { + number_of_shards: 1, + 'index.mapping.total_fields.limit': 1500, + }, + mappings: { + dynamic: 'strict', + ...expectedTestMapping, + }, + }, + }); + }); + + it('correctly creates component template with dynamic setting when defined', () => { + expect( + getComponentTemplateFromFieldMap({ + name: 'test-mappings', + fieldMap: testFieldMap, + includeSettings: true, + dynamic: false, + }) + ).toEqual({ + name: 'test-mappings', + _meta: { + managed: true, + }, + template: { + settings: { + number_of_shards: 1, + 'index.mapping.total_fields.limit': 1500, + }, + mappings: { + dynamic: false, + ...expectedTestMapping, + }, + }, + }); + }); +}); diff --git a/x-pack/plugins/alerting/common/alert_schema/field_maps/component_template_from_field_map.ts b/x-pack/plugins/alerting/common/alert_schema/field_maps/component_template_from_field_map.ts index 4fc36193a15d9..9d5c651e92cda 100644 --- a/x-pack/plugins/alerting/common/alert_schema/field_maps/component_template_from_field_map.ts +++ b/x-pack/plugins/alerting/common/alert_schema/field_maps/component_template_from_field_map.ts @@ -11,13 +11,15 @@ import { mappingFromFieldMap } from './mapping_from_field_map'; export interface GetComponentTemplateFromFieldMapOpts { name: string; - fieldLimit?: number; fieldMap: FieldMap; + includeSettings?: boolean; + dynamic?: 'strict' | false; } export const getComponentTemplateFromFieldMap = ({ name, fieldMap, - fieldLimit, + dynamic, + includeSettings, }: GetComponentTemplateFromFieldMapOpts): ClusterPutComponentTemplateRequest => { return { name, @@ -26,10 +28,16 @@ export const getComponentTemplateFromFieldMap = ({ }, template: { settings: { - number_of_shards: 1, - 'index.mapping.total_fields.limit': fieldLimit ?? 1000, + ...(includeSettings + ? { + number_of_shards: 1, + 'index.mapping.total_fields.limit': + Math.ceil(Object.keys(fieldMap).length / 1000) * 1000 + 500, + } + : {}), }, - mappings: mappingFromFieldMap(fieldMap, 'strict'), + + mappings: mappingFromFieldMap(fieldMap, dynamic ?? 'strict'), }, }; }; diff --git a/x-pack/plugins/alerting/common/alert_schema/field_maps/mapping_from_field_map.test.ts b/x-pack/plugins/alerting/common/alert_schema/field_maps/mapping_from_field_map.test.ts index f5eeeb8ba6c35..ff3d9aeecd79f 100644 --- a/x-pack/plugins/alerting/common/alert_schema/field_maps/mapping_from_field_map.test.ts +++ b/x-pack/plugins/alerting/common/alert_schema/field_maps/mapping_from_field_map.test.ts @@ -7,179 +7,183 @@ import { alertFieldMap, legacyAlertFieldMap, type FieldMap } from '@kbn/alerts-as-data-utils'; import { mappingFromFieldMap } from './mapping_from_field_map'; -describe('mappingFromFieldMap', () => { - const fieldMap: FieldMap = { - date_field: { - type: 'date', - array: false, - required: true, - }, - keyword_field: { - type: 'keyword', - array: false, - required: false, +export const testFieldMap: FieldMap = { + date_field: { + type: 'date', + array: false, + required: true, + }, + keyword_field: { + type: 'keyword', + array: false, + required: false, + ignore_above: 1024, + }, + long_field: { + type: 'long', + array: false, + required: false, + }, + multifield_field: { + type: 'keyword', + array: false, + required: false, + ignore_above: 1024, + multi_fields: [ + { + flat_name: 'multifield_field.text', + name: 'text', + type: 'match_only_text', + }, + ], + }, + geopoint_field: { + type: 'geo_point', + array: false, + required: false, + }, + ip_field: { + type: 'ip', + array: false, + required: false, + }, + array_field: { + type: 'keyword', + array: true, + required: false, + ignore_above: 1024, + }, + nested_array_field: { + type: 'nested', + array: false, + required: false, + }, + 'nested_array_field.field1': { + type: 'keyword', + array: false, + required: false, + ignore_above: 1024, + }, + 'nested_array_field.field2': { + type: 'keyword', + array: false, + required: false, + ignore_above: 1024, + }, + scaled_float_field: { + type: 'scaled_float', + array: false, + required: false, + scaling_factor: 1000, + }, + constant_keyword_field: { + type: 'constant_keyword', + array: false, + required: false, + }, + 'parent_field.child1': { + type: 'keyword', + array: false, + required: false, + ignore_above: 1024, + }, + 'parent_field.child2': { + type: 'keyword', + array: false, + required: false, + ignore_above: 1024, + }, + unmapped_object: { + type: 'object', + required: false, + enabled: false, + }, + formatted_field: { + type: 'date_range', + required: false, + format: 'epoch_millis||strict_date_optional_time', + }, +}; +export const expectedTestMapping = { + properties: { + array_field: { ignore_above: 1024, + type: 'keyword', }, - long_field: { - type: 'long', - array: false, - required: false, + constant_keyword_field: { + type: 'constant_keyword', + }, + date_field: { + type: 'date', }, multifield_field: { - type: 'keyword', - array: false, - required: false, - ignore_above: 1024, - multi_fields: [ - { - flat_name: 'multifield_field.text', - name: 'text', + fields: { + text: { type: 'match_only_text', }, - ], + }, + ignore_above: 1024, + type: 'keyword', }, geopoint_field: { type: 'geo_point', - array: false, - required: false, }, ip_field: { type: 'ip', - array: false, - required: false, }, - array_field: { - type: 'keyword', - array: true, - required: false, + keyword_field: { ignore_above: 1024, + type: 'keyword', + }, + long_field: { + type: 'long', }, nested_array_field: { + properties: { + field1: { + ignore_above: 1024, + type: 'keyword', + }, + field2: { + ignore_above: 1024, + type: 'keyword', + }, + }, type: 'nested', - array: false, - required: false, }, - 'nested_array_field.field1': { - type: 'keyword', - array: false, - required: false, - ignore_above: 1024, - }, - 'nested_array_field.field2': { - type: 'keyword', - array: false, - required: false, - ignore_above: 1024, + parent_field: { + properties: { + child1: { + ignore_above: 1024, + type: 'keyword', + }, + child2: { + ignore_above: 1024, + type: 'keyword', + }, + }, }, scaled_float_field: { - type: 'scaled_float', - array: false, - required: false, scaling_factor: 1000, - }, - constant_keyword_field: { - type: 'constant_keyword', - array: false, - required: false, - }, - 'parent_field.child1': { - type: 'keyword', - array: false, - required: false, - ignore_above: 1024, - }, - 'parent_field.child2': { - type: 'keyword', - array: false, - required: false, - ignore_above: 1024, + type: 'scaled_float', }, unmapped_object: { - type: 'object', - required: false, enabled: false, + type: 'object', }, formatted_field: { type: 'date_range', - required: false, format: 'epoch_millis||strict_date_optional_time', }, - }; - const expectedMapping = { - properties: { - array_field: { - ignore_above: 1024, - type: 'keyword', - }, - constant_keyword_field: { - type: 'constant_keyword', - }, - date_field: { - type: 'date', - }, - multifield_field: { - fields: { - text: { - type: 'match_only_text', - }, - }, - ignore_above: 1024, - type: 'keyword', - }, - geopoint_field: { - type: 'geo_point', - }, - ip_field: { - type: 'ip', - }, - keyword_field: { - ignore_above: 1024, - type: 'keyword', - }, - long_field: { - type: 'long', - }, - nested_array_field: { - properties: { - field1: { - ignore_above: 1024, - type: 'keyword', - }, - field2: { - ignore_above: 1024, - type: 'keyword', - }, - }, - type: 'nested', - }, - parent_field: { - properties: { - child1: { - ignore_above: 1024, - type: 'keyword', - }, - child2: { - ignore_above: 1024, - type: 'keyword', - }, - }, - }, - scaled_float_field: { - scaling_factor: 1000, - type: 'scaled_float', - }, - unmapped_object: { - enabled: false, - type: 'object', - }, - formatted_field: { - type: 'date_range', - format: 'epoch_millis||strict_date_optional_time', - }, - }, - }; + }, +}; + +describe('mappingFromFieldMap', () => { it('correctly creates mapping from field map', () => { - expect(mappingFromFieldMap(fieldMap)).toEqual({ dynamic: 'strict', ...expectedMapping }); + expect(mappingFromFieldMap(testFieldMap)).toEqual({ + dynamic: 'strict', + ...expectedTestMapping, + }); expect(mappingFromFieldMap(alertFieldMap)).toEqual({ dynamic: 'strict', properties: { @@ -344,6 +348,9 @@ describe('mappingFromFieldMap', () => { }); it('uses dynamic setting if specified', () => { - expect(mappingFromFieldMap(fieldMap, true)).toEqual({ dynamic: true, ...expectedMapping }); + expect(mappingFromFieldMap(testFieldMap, true)).toEqual({ + dynamic: true, + ...expectedTestMapping, + }); }); }); diff --git a/x-pack/plugins/alerting/common/default_rule_aggregation.test.ts b/x-pack/plugins/alerting/common/default_rule_aggregation.test.ts new file mode 100644 index 0000000000000..e1c38587d2dd0 --- /dev/null +++ b/x-pack/plugins/alerting/common/default_rule_aggregation.test.ts @@ -0,0 +1,114 @@ +/* + * 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 { + getDefaultRuleAggregation, + formatDefaultAggregationResult, +} from './default_rule_aggregation'; + +describe('getDefaultRuleAggregation', () => { + it('should return aggregation with default maxTags', () => { + const result = getDefaultRuleAggregation(); + expect(result.tags).toEqual({ + terms: { field: 'alert.attributes.tags', order: { _key: 'asc' }, size: 50 }, + }); + }); + + it('should return aggregation with custom maxTags', () => { + const result = getDefaultRuleAggregation({ maxTags: 100 }); + expect(result.tags).toEqual({ + terms: { field: 'alert.attributes.tags', order: { _key: 'asc' }, size: 100 }, + }); + }); +}); + +describe('formatDefaultAggregationResult', () => { + it('should format aggregation result', () => { + const result = formatDefaultAggregationResult({ + status: { + buckets: [ + { key: 'active', doc_count: 8 }, + { key: 'error', doc_count: 6 }, + { key: 'ok', doc_count: 10 }, + { key: 'pending', doc_count: 4 }, + { key: 'unknown', doc_count: 2 }, + { key: 'warning', doc_count: 1 }, + ], + }, + outcome: { + buckets: [ + { key: 'succeeded', doc_count: 2 }, + { key: 'failed', doc_count: 4 }, + { key: 'warning', doc_count: 6 }, + ], + }, + enabled: { + buckets: [ + { key: 0, key_as_string: '0', doc_count: 2 }, + { key: 1, key_as_string: '1', doc_count: 28 }, + ], + }, + muted: { + buckets: [ + { key: 0, key_as_string: '0', doc_count: 27 }, + { key: 1, key_as_string: '1', doc_count: 3 }, + ], + }, + snoozed: { + count: { + doc_count: 5, + }, + }, + tags: { + buckets: [ + { + key: 'a', + doc_count: 10, + }, + { + key: 'b', + doc_count: 20, + }, + { + key: 'c', + doc_count: 30, + }, + ], + }, + }); + + expect(result).toEqual( + expect.objectContaining({ + ruleExecutionStatus: { + active: 8, + error: 6, + ok: 10, + pending: 4, + unknown: 2, + warning: 1, + }, + ruleLastRunOutcome: { + succeeded: 2, + failed: 4, + warning: 6, + }, + ruleEnabledStatus: { + enabled: 28, + disabled: 2, + }, + ruleMutedStatus: { + muted: 3, + unmuted: 27, + }, + ruleSnoozedStatus: { + snoozed: 5, + }, + ruleTags: ['a', 'b', 'c'], + }) + ); + }); +}); diff --git a/x-pack/plugins/alerting/common/default_rule_aggregation.ts b/x-pack/plugins/alerting/common/default_rule_aggregation.ts new file mode 100644 index 0000000000000..1b72657ce2dbe --- /dev/null +++ b/x-pack/plugins/alerting/common/default_rule_aggregation.ts @@ -0,0 +1,172 @@ +/* + * 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 type { AggregationsAggregationContainer } from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; +import { + RuleExecutionStatusValues, + RuleLastRunOutcomeValues, + RuleAggregationFormattedResult, +} from './rule'; + +export interface DefaultRuleAggregationResult { + status: { + buckets: Array<{ + key: string; + doc_count: number; + }>; + }; + outcome: { + buckets: Array<{ + key: string; + doc_count: number; + }>; + }; + muted: { + buckets: Array<{ + key: number; + key_as_string: string; + doc_count: number; + }>; + }; + enabled: { + buckets: Array<{ + key: number; + key_as_string: string; + doc_count: number; + }>; + }; + snoozed: { + count: { + doc_count: number; + }; + }; + tags: { + buckets: Array<{ + key: string; + doc_count: number; + }>; + }; +} + +interface GetDefaultRuleAggregationParams { + maxTags?: number; +} + +export const getDefaultRuleAggregation = ( + params?: GetDefaultRuleAggregationParams +): Record => { + const { maxTags = 50 } = params || {}; + return { + status: { + terms: { field: 'alert.attributes.executionStatus.status' }, + }, + outcome: { + terms: { field: 'alert.attributes.lastRun.outcome' }, + }, + enabled: { + terms: { field: 'alert.attributes.enabled' }, + }, + muted: { + terms: { field: 'alert.attributes.muteAll' }, + }, + tags: { + terms: { field: 'alert.attributes.tags', order: { _key: 'asc' }, size: maxTags }, + }, + snoozed: { + nested: { + path: 'alert.attributes.snoozeSchedule', + }, + aggs: { + count: { + filter: { + exists: { + field: 'alert.attributes.snoozeSchedule.duration', + }, + }, + }, + }, + }, + }; +}; + +export const formatDefaultAggregationResult = ( + aggregations: DefaultRuleAggregationResult +): RuleAggregationFormattedResult => { + if (!aggregations) { + // Return a placeholder with all zeroes + const placeholder: RuleAggregationFormattedResult = { + ruleExecutionStatus: {}, + ruleLastRunOutcome: {}, + ruleEnabledStatus: { + enabled: 0, + disabled: 0, + }, + ruleMutedStatus: { + muted: 0, + unmuted: 0, + }, + ruleSnoozedStatus: { snoozed: 0 }, + ruleTags: [], + }; + + for (const key of RuleExecutionStatusValues) { + placeholder.ruleExecutionStatus[key] = 0; + } + + return placeholder; + } + + const ruleExecutionStatus = aggregations.status.buckets.map(({ key, doc_count: docCount }) => ({ + [key]: docCount, + })); + + const ruleLastRunOutcome = aggregations.outcome.buckets.map(({ key, doc_count: docCount }) => ({ + [key]: docCount, + })); + + const enabledBuckets = aggregations.enabled.buckets; + const mutedBuckets = aggregations.muted.buckets; + + const result: RuleAggregationFormattedResult = { + ruleExecutionStatus: ruleExecutionStatus.reduce( + (acc, curr: { [status: string]: number }) => Object.assign(acc, curr), + {} + ), + ruleLastRunOutcome: ruleLastRunOutcome.reduce( + (acc, curr: { [status: string]: number }) => Object.assign(acc, curr), + {} + ), + ruleEnabledStatus: { + enabled: enabledBuckets.find((bucket) => bucket.key === 1)?.doc_count ?? 0, + disabled: enabledBuckets.find((bucket) => bucket.key === 0)?.doc_count ?? 0, + }, + ruleMutedStatus: { + muted: mutedBuckets.find((bucket) => bucket.key === 1)?.doc_count ?? 0, + unmuted: mutedBuckets.find((bucket) => bucket.key === 0)?.doc_count ?? 0, + }, + ruleSnoozedStatus: { + snoozed: aggregations.snoozed?.count?.doc_count ?? 0, + }, + ruleTags: [], + }; + + // Fill missing keys with zeroes + for (const key of RuleExecutionStatusValues) { + if (!result.ruleExecutionStatus.hasOwnProperty(key)) { + result.ruleExecutionStatus[key] = 0; + } + } + for (const key of RuleLastRunOutcomeValues) { + if (!result.ruleLastRunOutcome.hasOwnProperty(key)) { + result.ruleLastRunOutcome[key] = 0; + } + } + + const tagsBuckets = aggregations.tags?.buckets || []; + result.ruleTags = tagsBuckets.map((bucket) => bucket.key); + + return result; +}; diff --git a/x-pack/plugins/alerting/common/index.ts b/x-pack/plugins/alerting/common/index.ts index 64e5a78d21e8d..52d1e4f221db0 100644 --- a/x-pack/plugins/alerting/common/index.ts +++ b/x-pack/plugins/alerting/common/index.ts @@ -23,6 +23,8 @@ export * from './rule_notify_when_type'; export * from './parse_duration'; export * from './execution_log_types'; export * from './rule_snooze_type'; +export * from './default_rule_aggregation'; +export * from './rule_tags_aggregation'; export { mappingFromFieldMap, getComponentTemplateFromFieldMap } from './alert_schema'; diff --git a/x-pack/plugins/alerting/common/rule.ts b/x-pack/plugins/alerting/common/rule.ts index cc5d7fda4a8b6..3ff66f68c5417 100644 --- a/x-pack/plugins/alerting/common/rule.ts +++ b/x-pack/plugins/alerting/common/rule.ts @@ -10,6 +10,7 @@ import type { SavedObjectAttributes, SavedObjectsResolveResponse, } from '@kbn/core/server'; +import type { KueryNode } from '@kbn/es-query'; import { RuleNotifyWhenType } from './rule_notify_when_type'; import { RuleSnooze } from './rule_snooze_type'; @@ -89,8 +90,21 @@ export interface RuleAction { }; } -export interface RuleAggregations { - alertExecutionStatus: { [status: string]: number }; +export interface AggregateOptions { + search?: string; + defaultSearchOperator?: 'AND' | 'OR'; + searchFields?: string[]; + hasReference?: { + type: string; + id: string; + }; + filter?: string | KueryNode; + page?: number; + perPage?: number; +} + +export interface RuleAggregationFormattedResult { + ruleExecutionStatus: { [status: string]: number }; ruleLastRunOutcome: { [status: string]: number }; ruleEnabledStatus: { enabled: number; disabled: number }; ruleMutedStatus: { muted: number; unmuted: number }; diff --git a/x-pack/plugins/alerting/common/rule_tags_aggregation.test.ts b/x-pack/plugins/alerting/common/rule_tags_aggregation.test.ts new file mode 100644 index 0000000000000..7a492049dcfe9 --- /dev/null +++ b/x-pack/plugins/alerting/common/rule_tags_aggregation.test.ts @@ -0,0 +1,93 @@ +/* + * 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 { getRuleTagsAggregation, formatRuleTagsAggregationResult } from './rule_tags_aggregation'; + +describe('getRuleTagsAggregation', () => { + it('should return aggregation with default params', () => { + const result = getRuleTagsAggregation(); + expect(result.tags).toEqual({ + composite: { + size: 50, + sources: [ + { + tags: { + terms: { + field: 'alert.attributes.tags', + order: 'asc', + }, + }, + }, + ], + }, + }); + }); + + it('should return aggregation with custom maxTags', () => { + const result = getRuleTagsAggregation({ + maxTags: 100, + after: { + tags: 'e', + }, + }); + expect(result.tags).toEqual({ + composite: { + size: 100, + after: { + tags: 'e', + }, + sources: [ + { + tags: { + terms: { + field: 'alert.attributes.tags', + order: 'asc', + }, + }, + }, + ], + }, + }); + }); +}); + +describe('formatRuleTagsAggregationResult', () => { + it('should format aggregation result', () => { + const result = formatRuleTagsAggregationResult({ + tags: { + buckets: [ + { + key: { + tags: 'a', + }, + doc_count: 1, + }, + { + key: { + tags: 'b', + }, + doc_count: 2, + }, + { + key: { + tags: 'c', + }, + doc_count: 3, + }, + { + key: { + tags: 'd', + }, + doc_count: 4, + }, + ], + }, + }); + + expect(result.ruleTags).toEqual(['a', 'b', 'c', 'd']); + }); +}); diff --git a/x-pack/plugins/alerting/common/rule_tags_aggregation.ts b/x-pack/plugins/alerting/common/rule_tags_aggregation.ts new file mode 100644 index 0000000000000..d7b8fc4089c27 --- /dev/null +++ b/x-pack/plugins/alerting/common/rule_tags_aggregation.ts @@ -0,0 +1,76 @@ +/* + * 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 type { + AggregationsAggregationContainer, + AggregationsCompositeAggregation, + AggregationsAggregateOrder, +} from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; +import type { AggregateOptions } from './rule'; + +export type RuleTagsAggregationOptions = Pick & { + after?: AggregationsCompositeAggregation['after']; + maxTags?: number; +}; + +export interface RuleTagsAggregationFormattedResult { + ruleTags: string[]; +} + +export interface RuleTagsAggregationResult { + tags: { + buckets: Array<{ + key: { + tags: string; + }; + doc_count: number; + }>; + }; +} + +interface GetRuleTagsAggregationParams { + maxTags?: number; + after?: AggregationsCompositeAggregation['after']; +} + +export const getRuleTagsAggregation = ( + params?: GetRuleTagsAggregationParams +): Record => { + const { maxTags = 50, after } = params || {}; + return { + tags: { + composite: { + ...(after ? { after } : {}), + size: maxTags, + sources: [ + { + tags: { + terms: { + field: 'alert.attributes.tags', + order: 'asc' as unknown as AggregationsAggregateOrder, + }, + }, + }, + ], + }, + }, + }; +}; + +export const formatRuleTagsAggregationResult = ( + aggregations: RuleTagsAggregationResult +): RuleTagsAggregationFormattedResult => { + if (!aggregations) { + return { + ruleTags: [], + }; + } + const tagsBuckets = aggregations.tags.buckets || []; + return { + ruleTags: tagsBuckets.map((bucket) => bucket.key.tags), + }; +}; diff --git a/x-pack/plugins/alerting/server/alert/alert.test.ts b/x-pack/plugins/alerting/server/alert/alert.test.ts index 479c5a4a55412..8a97a3b8b337a 100644 --- a/x-pack/plugins/alerting/server/alert/alert.test.ts +++ b/x-pack/plugins/alerting/server/alert/alert.test.ts @@ -52,6 +52,25 @@ describe('isThrottled', () => { expect(alert.isThrottled({ throttle: '1m' })).toEqual(true); }); + test(`should use actionHash if it was used in a legacy action`, () => { + const alert = new Alert('1', { + meta: { + lastScheduledActions: { + date: new Date(), + group: 'default', + actions: { + 'slack:alert:1h': { date: new Date() }, + }, + }, + }, + }); + clock.tick(30000); + alert.scheduleActions('default'); + expect( + alert.isThrottled({ throttle: '1m', actionHash: 'slack:alert:1h', uuid: '111-222' }) + ).toEqual(true); + }); + test(`shouldn't throttle when group didn't change and throttle period expired`, () => { const alert = new Alert('1', { meta: { @@ -87,14 +106,14 @@ describe('isThrottled', () => { date: new Date(), group: 'default', actions: { - 'slack:1h': { date: new Date() }, + '111-111': { date: new Date() }, }, }, }, }); clock.tick(5000); alert.scheduleActions('other-group'); - expect(alert.isThrottled({ throttle: '1m', actionHash: 'slack:1h' })).toEqual(false); + expect(alert.isThrottled({ throttle: '1m', uuid: '111-111' })).toEqual(false); }); test(`shouldn't throttle a specific action when group didn't change and throttle period expired`, () => { @@ -104,14 +123,16 @@ describe('isThrottled', () => { date: new Date('2020-01-01'), group: 'default', actions: { - 'slack:1h': { date: new Date() }, + '111-111': { date: new Date() }, }, }, }, }); clock.tick(30000); alert.scheduleActions('default'); - expect(alert.isThrottled({ throttle: '15s', actionHash: 'slack:1h' })).toEqual(false); + expect(alert.isThrottled({ throttle: '15s', uuid: '111-111', actionHash: 'slack:1h' })).toEqual( + false + ); }); test(`shouldn't throttle a specific action when group changes`, () => { @@ -121,14 +142,14 @@ describe('isThrottled', () => { date: new Date(), group: 'default', actions: { - 'slack:1h': { date: new Date() }, + '111-111': { date: new Date() }, }, }, }, }); clock.tick(5000); alert.scheduleActions('other-group'); - expect(alert.isThrottled({ throttle: '1m', actionHash: 'slack:1h' })).toEqual(false); + expect(alert.isThrottled({ throttle: '1m', uuid: '111-111' })).toEqual(false); }); }); @@ -312,7 +333,36 @@ describe('updateLastScheduledActions()', () => { const alert = new Alert('1', { meta: {}, }); - alert.updateLastScheduledActions('default', 'actionId1'); + alert.updateLastScheduledActions('default', 'actionId1', '111-111'); + expect(alert.toJSON()).toEqual({ + state: {}, + meta: { + flappingHistory: [], + lastScheduledActions: { + date: new Date().toISOString(), + group: 'default', + actions: { + '111-111': { date: new Date().toISOString() }, + }, + }, + }, + }); + }); + + test('removes the objects with an old actionHash', () => { + const alert = new Alert('1', { + meta: { + flappingHistory: [], + lastScheduledActions: { + date: new Date(), + group: 'default', + actions: { + 'slack:alert:1h': { date: new Date() }, + }, + }, + }, + }); + alert.updateLastScheduledActions('default', 'slack:alert:1h', '111-111'); expect(alert.toJSON()).toEqual({ state: {}, meta: { @@ -321,7 +371,7 @@ describe('updateLastScheduledActions()', () => { date: new Date().toISOString(), group: 'default', actions: { - actionId1: { date: new Date().toISOString() }, + '111-111': { date: new Date().toISOString() }, }, }, }, diff --git a/x-pack/plugins/alerting/server/alert/alert.ts b/x-pack/plugins/alerting/server/alert/alert.ts index 361d3d0c52258..d6aa320633c5a 100644 --- a/x-pack/plugins/alerting/server/alert/alert.ts +++ b/x-pack/plugins/alerting/server/alert/alert.ts @@ -67,7 +67,15 @@ export class Alert< return this.scheduledExecutionOptions !== undefined; } - isThrottled({ throttle, actionHash }: { throttle: string | null; actionHash?: string }) { + isThrottled({ + throttle, + actionHash, + uuid, + }: { + throttle: string | null; + actionHash?: string; + uuid?: string; + }) { if (this.scheduledExecutionOptions === undefined) { return false; } @@ -79,9 +87,12 @@ export class Alert< this.scheduledExecutionOptions ) ) { - if (actionHash) { + if (uuid && actionHash) { if (this.meta.lastScheduledActions.actions) { - const lastTriggerDate = this.meta.lastScheduledActions.actions[actionHash]?.date; + const actionInState = + this.meta.lastScheduledActions.actions[uuid] || + this.meta.lastScheduledActions.actions[actionHash]; // actionHash must be removed once all the hash identifiers removed from the task state + const lastTriggerDate = actionInState?.date; return !!(lastTriggerDate && lastTriggerDate.getTime() + throttleMills > Date.now()); } return false; @@ -169,7 +180,7 @@ export class Alert< return this; } - updateLastScheduledActions(group: ActionGroupIds, actionHash?: string | null) { + updateLastScheduledActions(group: ActionGroupIds, actionHash?: string | null, uuid?: string) { if (!this.meta.lastScheduledActions) { this.meta.lastScheduledActions = {} as LastScheduledActions; } @@ -179,11 +190,15 @@ export class Alert< if (this.meta.lastScheduledActions.group !== group) { this.meta.lastScheduledActions.actions = {}; - } else if (actionHash) { + } else if (uuid) { if (!this.meta.lastScheduledActions.actions) { this.meta.lastScheduledActions.actions = {}; } - this.meta.lastScheduledActions.actions[actionHash] = { date }; + // remove deprecated actionHash + if (!!actionHash && this.meta.lastScheduledActions.actions[actionHash]) { + delete this.meta.lastScheduledActions.actions[actionHash]; + } + this.meta.lastScheduledActions.actions[uuid] = { date }; } } diff --git a/x-pack/plugins/alerting/server/alerts_service/alerts_service.mock.ts b/x-pack/plugins/alerting/server/alerts_service/alerts_service.mock.ts index d11e95f909c19..a3754d66e1cad 100644 --- a/x-pack/plugins/alerting/server/alerts_service/alerts_service.mock.ts +++ b/x-pack/plugins/alerting/server/alerts_service/alerts_service.mock.ts @@ -8,10 +8,9 @@ const creatAlertsServiceMock = () => { return jest.fn().mockImplementation(() => { return { - initialize: jest.fn(), register: jest.fn(), isInitialized: jest.fn(), - isContextInitialized: jest.fn(), + getContextInitializationPromise: jest.fn(), }; }); }; diff --git a/x-pack/plugins/alerting/server/alerts_service/alerts_service.test.ts b/x-pack/plugins/alerting/server/alerts_service/alerts_service.test.ts index ca64faa7c51ea..1360e5f7924a5 100644 --- a/x-pack/plugins/alerting/server/alerts_service/alerts_service.test.ts +++ b/x-pack/plugins/alerting/server/alerts_service/alerts_service.test.ts @@ -11,6 +11,7 @@ import { errors as EsErrors } from '@elastic/elasticsearch'; import { ReplaySubject, Subject } from 'rxjs'; import { AlertsService } from './alerts_service'; import { IRuleTypeAlerts } from '../types'; +import { retryUntil } from './test_utils'; let logger: ReturnType; const clusterClient = elasticsearchServiceMock.createClusterClient().asInternalUser; @@ -81,19 +82,21 @@ interface GetIndexTemplatePutBodyOpts { context?: string; useLegacyAlerts?: boolean; useEcs?: boolean; + secondaryAlias?: string; } const getIndexTemplatePutBody = (opts?: GetIndexTemplatePutBodyOpts) => { const context = opts ? opts.context : undefined; const useLegacyAlerts = opts ? opts.useLegacyAlerts : undefined; const useEcs = opts ? opts.useEcs : undefined; + const secondaryAlias = opts ? opts.secondaryAlias : undefined; return { - name: `.alerts-${context ? context : 'test'}-default-template`, + name: `.alerts-${context ? context : 'test'}.alerts-default-index-template`, body: { - index_patterns: [`.alerts-${context ? context : 'test'}-default-*`], + index_patterns: [`.internal.alerts-${context ? context : 'test'}.alerts-default-*`], composed_of: [ - `.alerts-${context ? context : 'test'}-mappings`, - ...(useLegacyAlerts ? ['.alerts-legacy-alert-mappings'] : []), ...(useEcs ? ['.alerts-ecs-mappings'] : []), + `.alerts-${context ? `${context}.alerts` : 'test.alerts'}-mappings`, + ...(useLegacyAlerts ? ['.alerts-legacy-alert-mappings'] : []), '.alerts-framework-mappings', ], template: { @@ -102,16 +105,32 @@ const getIndexTemplatePutBody = (opts?: GetIndexTemplatePutBodyOpts) => { hidden: true, 'index.lifecycle': { name: '.alerts-ilm-policy', - rollover_alias: `.alerts-${context ? context : 'test'}-default`, + rollover_alias: `.alerts-${context ? context : 'test'}.alerts-default`, }, 'index.mapping.total_fields.limit': 2500, }, mappings: { dynamic: false, + _meta: { + kibana: { version: '8.8.0' }, + managed: true, + namespace: 'default', + }, }, + ...(secondaryAlias + ? { + aliases: { + [`${secondaryAlias}-default`]: { + is_write_index: false, + }, + }, + } + : {}), }, _meta: { + kibana: { version: '8.8.0' }, managed: true, + namespace: 'default', }, }, }; @@ -119,12 +138,15 @@ const getIndexTemplatePutBody = (opts?: GetIndexTemplatePutBodyOpts) => { const TestRegistrationContext: IRuleTypeAlerts = { context: 'test', - fieldMap: { field: { type: 'keyword', required: false } }, + mappings: { fieldMap: { field: { type: 'keyword', required: false } } }, }; -const AnotherRegistrationContext: IRuleTypeAlerts = { - context: 'another', - fieldMap: { field: { type: 'keyword', required: false } }, +const getContextInitialized = async ( + alertsService: AlertsService, + context: string = TestRegistrationContext.context +) => { + const { result } = await alertsService.getContextInitializationPromise(context); + return result; }; describe('Alerts Service', () => { @@ -146,16 +168,19 @@ describe('Alerts Service', () => { pluginStop$.next(); pluginStop$.complete(); }); - describe('initialize()', () => { + describe('AlertsService()', () => { test('should correctly initialize common resources', async () => { const alertsService = new AlertsService({ logger, elasticsearchClientPromise: Promise.resolve(clusterClient), pluginStop$, + kibanaVersion: '8.8.0', }); - alertsService.initialize(); - await new Promise((r) => setTimeout(r, 50)); + await retryUntil( + 'alert service initialized', + async () => alertsService.isInitialized() === true + ); expect(alertsService.isInitialized()).toEqual(true); expect(clusterClient.ilm.putLifecycle).toHaveBeenCalledWith(IlmPutBody); @@ -175,10 +200,10 @@ describe('Alerts Service', () => { logger, elasticsearchClientPromise: Promise.resolve(clusterClient), pluginStop$, + kibanaVersion: '8.8.0', }); - alertsService.initialize(); - await new Promise((r) => setTimeout(r, 50)); + await retryUntil('error log called', async () => logger.error.mock.calls.length > 0); expect(alertsService.isInitialized()).toEqual(false); @@ -196,10 +221,10 @@ describe('Alerts Service', () => { logger, elasticsearchClientPromise: Promise.resolve(clusterClient), pluginStop$, + kibanaVersion: '8.8.0', }); - alertsService.initialize(); - await new Promise((r) => setTimeout(r, 50)); + await retryUntil('error log called', async () => logger.error.mock.calls.length > 0); expect(alertsService.isInitialized()).toEqual(false); expect(logger.error).toHaveBeenCalledWith( @@ -274,12 +299,14 @@ describe('Alerts Service', () => { logger, elasticsearchClientPromise: Promise.resolve(clusterClient), pluginStop$, + kibanaVersion: '8.8.0', }); - alertsService.initialize(); - await new Promise((r) => setTimeout(r, 50)); + await retryUntil( + 'alert service initialized', + async () => alertsService.isInitialized() === true + ); - expect(alertsService.isInitialized()).toEqual(true); expect(clusterClient.indices.getIndexTemplate).toHaveBeenCalledTimes(1); expect(clusterClient.indices.putIndexTemplate).toHaveBeenCalledTimes(1); expect(clusterClient.indices.putIndexTemplate).toHaveBeenCalledWith({ @@ -301,32 +328,34 @@ describe('Alerts Service', () => { // after updating index template field limit expect(clusterClient.cluster.putComponentTemplate).toHaveBeenCalledTimes(4); }); + }); - test('should install resources for contexts awaiting initialization when common resources are initialized', async () => { - const alertsService = new AlertsService({ + describe('register()', () => { + let alertsService: AlertsService; + beforeEach(async () => { + alertsService = new AlertsService({ logger, elasticsearchClientPromise: Promise.resolve(clusterClient), pluginStop$, + kibanaVersion: '8.8.0', }); - // pre-register contexts so they get installed right after initialization - alertsService.register(TestRegistrationContext); - alertsService.register(AnotherRegistrationContext); - alertsService.initialize(); - await new Promise((r) => setTimeout(r, 50)); - - expect(alertsService.isInitialized()).toEqual(true); - expect(await alertsService.isContextInitialized(TestRegistrationContext.context)).toEqual( - true + await retryUntil( + 'alert service initialized', + async () => alertsService.isInitialized() === true ); - expect(await alertsService.isContextInitialized(AnotherRegistrationContext.context)).toEqual( - true + }); + + test('should correctly install resources for context when common initialization is complete', async () => { + alertsService.register(TestRegistrationContext); + await retryUntil( + 'context initialized', + async () => (await getContextInitialized(alertsService)) === true ); expect(clusterClient.ilm.putLifecycle).toHaveBeenCalledWith(IlmPutBody); - // 1x for framework component template, 1x for legacy alert, 1x for ecs, 2x for context specific - expect(clusterClient.cluster.putComponentTemplate).toHaveBeenCalledTimes(5); + expect(clusterClient.cluster.putComponentTemplate).toHaveBeenCalledTimes(4); const componentTemplate1 = clusterClient.cluster.putComponentTemplate.mock.calls[0][0]; expect(componentTemplate1.name).toEqual('.alerts-framework-mappings'); const componentTemplate2 = clusterClient.cluster.putComponentTemplate.mock.calls[1][0]; @@ -334,73 +363,35 @@ describe('Alerts Service', () => { const componentTemplate3 = clusterClient.cluster.putComponentTemplate.mock.calls[2][0]; expect(componentTemplate3.name).toEqual('.alerts-ecs-mappings'); const componentTemplate4 = clusterClient.cluster.putComponentTemplate.mock.calls[3][0]; - expect(componentTemplate4.name).toEqual('.alerts-another-mappings'); - const componentTemplate5 = clusterClient.cluster.putComponentTemplate.mock.calls[4][0]; - expect(componentTemplate5.name).toEqual('.alerts-test-mappings'); - - expect(clusterClient.indices.putIndexTemplate).toHaveBeenCalledTimes(2); - expect(clusterClient.indices.putIndexTemplate).toHaveBeenNthCalledWith( - 1, - getIndexTemplatePutBody({ context: 'another' }) - ); - expect(clusterClient.indices.putIndexTemplate).toHaveBeenNthCalledWith( - 2, + expect(componentTemplate4.name).toEqual('.alerts-test.alerts-mappings'); + + expect(clusterClient.indices.putIndexTemplate).toHaveBeenCalledWith( getIndexTemplatePutBody() ); - - expect(clusterClient.indices.getAlias).toHaveBeenCalledTimes(2); - expect(clusterClient.indices.getAlias).toHaveBeenNthCalledWith(1, { - index: '.alerts-another-default-*', - }); - expect(clusterClient.indices.getAlias).toHaveBeenNthCalledWith(2, { - index: '.alerts-test-default-*', - }); - expect(clusterClient.indices.putSettings).toHaveBeenCalledTimes(4); - expect(clusterClient.indices.simulateIndexTemplate).toHaveBeenCalledTimes(4); - expect(clusterClient.indices.putMapping).toHaveBeenCalledTimes(4); - expect(clusterClient.indices.create).toHaveBeenCalledTimes(2); - expect(clusterClient.indices.create).toHaveBeenNthCalledWith(1, { - index: '.alerts-another-default-000001', - body: { - aliases: { - '.alerts-another-default': { - is_write_index: true, - }, - }, - }, + expect(clusterClient.indices.getAlias).toHaveBeenCalledWith({ + index: '.internal.alerts-test.alerts-default-*', + name: '.alerts-test.alerts-*', }); - expect(clusterClient.indices.create).toHaveBeenNthCalledWith(2, { - index: '.alerts-test-default-000001', + expect(clusterClient.indices.putSettings).toHaveBeenCalledTimes(2); + expect(clusterClient.indices.simulateIndexTemplate).toHaveBeenCalledTimes(2); + expect(clusterClient.indices.putMapping).toHaveBeenCalledTimes(2); + expect(clusterClient.indices.create).toHaveBeenCalledWith({ + index: '.internal.alerts-test.alerts-default-000001', body: { aliases: { - '.alerts-test-default': { + '.alerts-test.alerts-default': { is_write_index: true, }, }, }, }); }); - }); - - describe('register()', () => { - let alertsService: AlertsService; - beforeEach(async () => { - alertsService = new AlertsService({ - logger, - elasticsearchClientPromise: Promise.resolve(clusterClient), - pluginStop$, - }); - alertsService.initialize(); - await new Promise((r) => setTimeout(r, 50)); - expect(alertsService.isInitialized()).toEqual(true); - }); - - test('should correctly install resources for context when common initialization is complete', async () => { - alertsService.register(TestRegistrationContext); - await new Promise((r) => setTimeout(r, 50)); - expect(await alertsService.isContextInitialized(TestRegistrationContext.context)).toEqual( - true + test('should correctly install resources for context when useLegacyAlerts is true', async () => { + alertsService.register({ ...TestRegistrationContext, useLegacyAlerts: true }); + await retryUntil( + 'context initialized', + async () => (await getContextInitialized(alertsService)) === true ); expect(clusterClient.ilm.putLifecycle).toHaveBeenCalledWith(IlmPutBody); @@ -413,22 +404,23 @@ describe('Alerts Service', () => { const componentTemplate3 = clusterClient.cluster.putComponentTemplate.mock.calls[2][0]; expect(componentTemplate3.name).toEqual('.alerts-ecs-mappings'); const componentTemplate4 = clusterClient.cluster.putComponentTemplate.mock.calls[3][0]; - expect(componentTemplate4.name).toEqual('.alerts-test-mappings'); + expect(componentTemplate4.name).toEqual('.alerts-test.alerts-mappings'); expect(clusterClient.indices.putIndexTemplate).toHaveBeenCalledWith( - getIndexTemplatePutBody() + getIndexTemplatePutBody({ useLegacyAlerts: true }) ); expect(clusterClient.indices.getAlias).toHaveBeenCalledWith({ - index: '.alerts-test-default-*', + index: '.internal.alerts-test.alerts-default-*', + name: '.alerts-test.alerts-*', }); expect(clusterClient.indices.putSettings).toHaveBeenCalledTimes(2); expect(clusterClient.indices.simulateIndexTemplate).toHaveBeenCalledTimes(2); expect(clusterClient.indices.putMapping).toHaveBeenCalledTimes(2); expect(clusterClient.indices.create).toHaveBeenCalledWith({ - index: '.alerts-test-default-000001', + index: '.internal.alerts-test.alerts-default-000001', body: { aliases: { - '.alerts-test-default': { + '.alerts-test.alerts-default': { is_write_index: true, }, }, @@ -436,11 +428,11 @@ describe('Alerts Service', () => { }); }); - test('should correctly install resources for context when useLegacyAlerts is true', async () => { - alertsService.register({ ...TestRegistrationContext, useLegacyAlerts: true }); - await new Promise((r) => setTimeout(r, 50)); - expect(await alertsService.isContextInitialized(TestRegistrationContext.context)).toEqual( - true + test('should correctly install resources for context when useEcs is true', async () => { + alertsService.register({ ...TestRegistrationContext, useEcs: true }); + await retryUntil( + 'context initialized', + async () => (await getContextInitialized(alertsService)) === true ); expect(clusterClient.ilm.putLifecycle).toHaveBeenCalledWith(IlmPutBody); @@ -453,22 +445,23 @@ describe('Alerts Service', () => { const componentTemplate3 = clusterClient.cluster.putComponentTemplate.mock.calls[2][0]; expect(componentTemplate3.name).toEqual('.alerts-ecs-mappings'); const componentTemplate4 = clusterClient.cluster.putComponentTemplate.mock.calls[3][0]; - expect(componentTemplate4.name).toEqual('.alerts-test-mappings'); + expect(componentTemplate4.name).toEqual('.alerts-test.alerts-mappings'); expect(clusterClient.indices.putIndexTemplate).toHaveBeenCalledWith( - getIndexTemplatePutBody({ useLegacyAlerts: true }) + getIndexTemplatePutBody({ useEcs: true }) ); expect(clusterClient.indices.getAlias).toHaveBeenCalledWith({ - index: '.alerts-test-default-*', + index: '.internal.alerts-test.alerts-default-*', + name: '.alerts-test.alerts-*', }); expect(clusterClient.indices.putSettings).toHaveBeenCalledTimes(2); expect(clusterClient.indices.simulateIndexTemplate).toHaveBeenCalledTimes(2); expect(clusterClient.indices.putMapping).toHaveBeenCalledTimes(2); expect(clusterClient.indices.create).toHaveBeenCalledWith({ - index: '.alerts-test-default-000001', + index: '.internal.alerts-test.alerts-default-000001', body: { aliases: { - '.alerts-test-default': { + '.alerts-test.alerts-default': { is_write_index: true, }, }, @@ -476,11 +469,11 @@ describe('Alerts Service', () => { }); }); - test('should correctly install resources for context when useEcs is true', async () => { - alertsService.register({ ...TestRegistrationContext, useEcs: true }); - await new Promise((r) => setTimeout(r, 50)); - expect(await alertsService.isContextInitialized(TestRegistrationContext.context)).toEqual( - true + test('should correctly install resources for context when secondaryAlias is defined', async () => { + alertsService.register({ ...TestRegistrationContext, secondaryAlias: 'another.alias' }); + await retryUntil( + 'context initialized', + async () => (await getContextInitialized(alertsService)) === true ); expect(clusterClient.ilm.putLifecycle).toHaveBeenCalledWith(IlmPutBody); @@ -493,22 +486,23 @@ describe('Alerts Service', () => { const componentTemplate3 = clusterClient.cluster.putComponentTemplate.mock.calls[2][0]; expect(componentTemplate3.name).toEqual('.alerts-ecs-mappings'); const componentTemplate4 = clusterClient.cluster.putComponentTemplate.mock.calls[3][0]; - expect(componentTemplate4.name).toEqual('.alerts-test-mappings'); + expect(componentTemplate4.name).toEqual('.alerts-test.alerts-mappings'); expect(clusterClient.indices.putIndexTemplate).toHaveBeenCalledWith( - getIndexTemplatePutBody({ useEcs: true }) + getIndexTemplatePutBody({ secondaryAlias: 'another.alias' }) ); expect(clusterClient.indices.getAlias).toHaveBeenCalledWith({ - index: '.alerts-test-default-*', + index: '.internal.alerts-test.alerts-default-*', + name: '.alerts-test.alerts-*', }); expect(clusterClient.indices.putSettings).toHaveBeenCalledTimes(2); expect(clusterClient.indices.simulateIndexTemplate).toHaveBeenCalledTimes(2); expect(clusterClient.indices.putMapping).toHaveBeenCalledTimes(2); expect(clusterClient.indices.create).toHaveBeenCalledWith({ - index: '.alerts-test-default-000001', + index: '.internal.alerts-test.alerts-default-000001', body: { aliases: { - '.alerts-test-default': { + '.alerts-test.alerts-default': { is_write_index: true, }, }, @@ -519,10 +513,12 @@ describe('Alerts Service', () => { test('should not install component template for context if fieldMap is empty', async () => { alertsService.register({ context: 'empty', - fieldMap: {}, + mappings: { fieldMap: {} }, }); - await new Promise((r) => setTimeout(r, 50)); - expect(await alertsService.isContextInitialized('empty')).toEqual(true); + await retryUntil( + 'context initialized', + async () => (await getContextInitialized(alertsService, 'empty')) === true + ); expect(clusterClient.ilm.putLifecycle).toHaveBeenCalledWith(IlmPutBody); @@ -535,9 +531,9 @@ describe('Alerts Service', () => { expect(componentTemplate3.name).toEqual('.alerts-ecs-mappings'); expect(clusterClient.indices.putIndexTemplate).toHaveBeenCalledWith({ - name: `.alerts-empty-default-template`, + name: `.alerts-empty.alerts-default-index-template`, body: { - index_patterns: [`.alerts-empty-default-*`], + index_patterns: [`.internal.alerts-empty.alerts-default-*`], composed_of: ['.alerts-framework-mappings'], template: { settings: { @@ -545,30 +541,38 @@ describe('Alerts Service', () => { hidden: true, 'index.lifecycle': { name: '.alerts-ilm-policy', - rollover_alias: `.alerts-empty-default`, + rollover_alias: `.alerts-empty.alerts-default`, }, 'index.mapping.total_fields.limit': 2500, }, mappings: { + _meta: { + kibana: { version: '8.8.0' }, + managed: true, + namespace: 'default', + }, dynamic: false, }, }, _meta: { + kibana: { version: '8.8.0' }, managed: true, + namespace: 'default', }, }, }); expect(clusterClient.indices.getAlias).toHaveBeenCalledWith({ - index: '.alerts-empty-default-*', + index: '.internal.alerts-empty.alerts-default-*', + name: '.alerts-empty.alerts-*', }); expect(clusterClient.indices.putSettings).toHaveBeenCalledTimes(2); expect(clusterClient.indices.simulateIndexTemplate).toHaveBeenCalledTimes(2); expect(clusterClient.indices.putMapping).toHaveBeenCalledTimes(2); expect(clusterClient.indices.create).toHaveBeenCalledWith({ - index: '.alerts-empty-default-000001', + index: '.internal.alerts-empty.alerts-default-000001', body: { aliases: { - '.alerts-empty-default': { + '.alerts-empty.alerts-default': { is_write_index: true, }, }, @@ -587,14 +591,25 @@ describe('Alerts Service', () => { test('should throw error if context already exists and has been registered with a different field map', async () => { alertsService.register(TestRegistrationContext); - await new Promise((r) => setTimeout(r, 50)); expect(() => { alertsService.register({ ...TestRegistrationContext, - fieldMap: { anotherField: { type: 'keyword', required: false } }, + mappings: { fieldMap: { anotherField: { type: 'keyword', required: false } } }, + }); + }).toThrowErrorMatchingInlineSnapshot( + `"test has already been registered with different options"` + ); + }); + + test('should throw error if context already exists and has been registered with a different options', async () => { + alertsService.register(TestRegistrationContext); + expect(() => { + alertsService.register({ + ...TestRegistrationContext, + useEcs: true, }); }).toThrowErrorMatchingInlineSnapshot( - `"test has already been registered with a different mapping"` + `"test has already been registered with different options"` ); }); @@ -602,13 +617,13 @@ describe('Alerts Service', () => { clusterClient.indices.simulateTemplate.mockRejectedValueOnce(new Error('fail')); alertsService.register(TestRegistrationContext); - await new Promise((r) => setTimeout(r, 50)); - expect(await alertsService.isContextInitialized(TestRegistrationContext.context)).toEqual( - true + await retryUntil( + 'context initialized', + async () => (await getContextInitialized(alertsService)) === true ); expect(logger.error).toHaveBeenCalledWith( - `Failed to simulate index template mappings for .alerts-test-default-template; not applying mappings - fail` + `Failed to simulate index template mappings for .alerts-test.alerts-default-index-template; not applying mappings - fail` ); expect(clusterClient.ilm.putLifecycle).toHaveBeenCalled(); @@ -633,14 +648,18 @@ describe('Alerts Service', () => { })); alertsService.register(TestRegistrationContext); - await new Promise((r) => setTimeout(r, 50)); - expect(await alertsService.isContextInitialized(TestRegistrationContext.context)).toEqual( - false - ); + await retryUntil('error logger called', async () => logger.error.mock.calls.length > 0); + expect( + await alertsService.getContextInitializationPromise(TestRegistrationContext.context) + ).toEqual({ + result: false, + error: + 'Failure during installation. No mappings would be generated for .alerts-test.alerts-default-index-template, possibly due to failed/misconfigured bootstrapping', + }); expect(logger.error).toHaveBeenCalledWith( new Error( - `No mappings would be generated for .alerts-test-default-template, possibly due to failed/misconfigured bootstrapping` + `No mappings would be generated for .alerts-test.alerts-default-index-template, possibly due to failed/misconfigured bootstrapping` ) ); @@ -659,13 +678,14 @@ describe('Alerts Service', () => { clusterClient.indices.putIndexTemplate.mockRejectedValueOnce(new Error('fail')); alertsService.register(TestRegistrationContext); - await new Promise((r) => setTimeout(r, 50)); - expect(await alertsService.isContextInitialized(TestRegistrationContext.context)).toEqual( - false - ); + await retryUntil('error logger called', async () => logger.error.mock.calls.length > 0); + + expect( + await alertsService.getContextInitializationPromise(TestRegistrationContext.context) + ).toEqual({ error: 'Failure during installation. fail', result: false }); expect(logger.error).toHaveBeenCalledWith( - `Error installing index template .alerts-test-default-template - fail` + `Error installing index template .alerts-test.alerts-default-index-template - fail` ); expect(clusterClient.ilm.putLifecycle).toHaveBeenCalled(); @@ -683,13 +703,13 @@ describe('Alerts Service', () => { clusterClient.indices.getAlias.mockRejectedValueOnce(new Error('fail')); alertsService.register(TestRegistrationContext); - await new Promise((r) => setTimeout(r, 50)); - expect(await alertsService.isContextInitialized(TestRegistrationContext.context)).toEqual( - false - ); + await retryUntil('error logger called', async () => logger.error.mock.calls.length > 0); + expect( + await alertsService.getContextInitializationPromise(TestRegistrationContext.context) + ).toEqual({ error: 'Failure during installation. fail', result: false }); expect(logger.error).toHaveBeenCalledWith( - `Error fetching concrete indices for .alerts-test-default-* pattern - fail` + `Error fetching concrete indices for .internal.alerts-test.alerts-default-* pattern - fail` ); expect(clusterClient.ilm.putLifecycle).toHaveBeenCalled(); @@ -708,9 +728,9 @@ describe('Alerts Service', () => { clusterClient.indices.getAlias.mockRejectedValueOnce(error); alertsService.register(TestRegistrationContext); - await new Promise((r) => setTimeout(r, 50)); - expect(await alertsService.isContextInitialized(TestRegistrationContext.context)).toEqual( - true + await retryUntil( + 'context initialized', + async () => (await getContextInitialized(alertsService)) === true ); expect(clusterClient.ilm.putLifecycle).toHaveBeenCalled(); @@ -727,10 +747,11 @@ describe('Alerts Service', () => { clusterClient.indices.putSettings.mockRejectedValueOnce(new Error('fail')); alertsService.register(TestRegistrationContext); - await new Promise((r) => setTimeout(r, 50)); - expect(await alertsService.isContextInitialized(TestRegistrationContext.context)).toEqual( - false - ); + await retryUntil('error logger called', async () => logger.error.mock.calls.length > 0); + + expect( + await alertsService.getContextInitializationPromise(TestRegistrationContext.context) + ).toEqual({ error: 'Failure during installation. fail', result: false }); expect(logger.error).toHaveBeenCalledWith( `Failed to PUT index.mapping.total_fields.limit settings for alias alias_1: fail` @@ -751,9 +772,9 @@ describe('Alerts Service', () => { clusterClient.indices.simulateIndexTemplate.mockRejectedValueOnce(new Error('fail')); alertsService.register(TestRegistrationContext); - await new Promise((r) => setTimeout(r, 50)); - expect(await alertsService.isContextInitialized(TestRegistrationContext.context)).toEqual( - true + await retryUntil( + 'context initialized', + async () => (await getContextInitialized(alertsService)) === true ); expect(logger.error).toHaveBeenCalledWith( @@ -775,10 +796,10 @@ describe('Alerts Service', () => { clusterClient.indices.putMapping.mockRejectedValueOnce(new Error('fail')); alertsService.register(TestRegistrationContext); - await new Promise((r) => setTimeout(r, 50)); - expect(await alertsService.isContextInitialized(TestRegistrationContext.context)).toEqual( - false - ); + await retryUntil('error logger called', async () => logger.error.mock.calls.length > 0); + expect( + await alertsService.getContextInitializationPromise(TestRegistrationContext.context) + ).toEqual({ error: 'Failure during installation. fail', result: false }); expect(logger.error).toHaveBeenCalledWith(`Failed to PUT mapping for alias alias_1: fail`); @@ -797,9 +818,9 @@ describe('Alerts Service', () => { clusterClient.indices.getAlias.mockImplementationOnce(async () => ({})); alertsService.register(TestRegistrationContext); - await new Promise((r) => setTimeout(r, 50)); - expect(await alertsService.isContextInitialized(TestRegistrationContext.context)).toEqual( - true + await retryUntil( + 'context initialized', + async () => (await getContextInitialized(alertsService)) === true ); expect(clusterClient.ilm.putLifecycle).toHaveBeenCalled(); @@ -815,9 +836,9 @@ describe('Alerts Service', () => { test('should log error and set initialized to false if concrete indices exist but none are write index', async () => { clusterClient.indices.getAlias.mockImplementationOnce(async () => ({ - '.alerts-test-default-0001': { + '.internal.alerts-test.alerts-default-0001': { aliases: { - '.alerts-test-default': { + '.alerts-test.alerts-default': { is_write_index: false, is_hidden: true, }, @@ -830,14 +851,18 @@ describe('Alerts Service', () => { })); alertsService.register(TestRegistrationContext); - await new Promise((r) => setTimeout(r, 50)); - expect(await alertsService.isContextInitialized(TestRegistrationContext.context)).toEqual( - false - ); + await retryUntil('error logger called', async () => logger.error.mock.calls.length > 0); + expect( + await alertsService.getContextInitializationPromise(TestRegistrationContext.context) + ).toEqual({ + error: + 'Failure during installation. Indices matching pattern .internal.alerts-test.alerts-default-* exist but none are set as the write index for alias .alerts-test.alerts-default', + result: false, + }); expect(logger.error).toHaveBeenCalledWith( new Error( - `Indices matching pattern .alerts-test-default-* exist but none are set as the write index for alias .alerts-test-default` + `Indices matching pattern .internal.alerts-test.alerts-default-* exist but none are set as the write index for alias .alerts-test.alerts-default` ) ); @@ -854,9 +879,9 @@ describe('Alerts Service', () => { test('does not create new index if concrete write index exists', async () => { clusterClient.indices.getAlias.mockImplementationOnce(async () => ({ - '.alerts-test-default-0001': { + '.internal.alerts-test.alerts-default-0001': { aliases: { - '.alerts-test-default': { + '.alerts-test.alerts-default': { is_write_index: true, is_hidden: true, }, @@ -869,9 +894,9 @@ describe('Alerts Service', () => { })); alertsService.register(TestRegistrationContext); - await new Promise((r) => setTimeout(r, 50)); - expect(await alertsService.isContextInitialized(TestRegistrationContext.context)).toEqual( - true + await retryUntil( + 'context initialized', + async () => (await getContextInitialized(alertsService)) === true ); expect(clusterClient.ilm.putLifecycle).toHaveBeenCalled(); @@ -889,10 +914,11 @@ describe('Alerts Service', () => { clusterClient.indices.create.mockRejectedValueOnce(new Error('fail')); alertsService.register(TestRegistrationContext); - await new Promise((r) => setTimeout(r, 50)); - expect(await alertsService.isContextInitialized(TestRegistrationContext.context)).toEqual( - false - ); + await retryUntil('error logger called', async () => logger.error.mock.calls.length > 0); + + expect( + await alertsService.getContextInitializationPromise(TestRegistrationContext.context) + ).toEqual({ error: 'Failure during installation. fail', result: false }); expect(logger.error).toHaveBeenCalledWith(`Error creating concrete write index - fail`); @@ -918,15 +944,15 @@ describe('Alerts Service', () => { }; clusterClient.indices.create.mockRejectedValueOnce(error); clusterClient.indices.get.mockImplementationOnce(async () => ({ - '.alerts-test-default-000001': { - aliases: { '.alerts-test-default': { is_write_index: true } }, + '.internal.alerts-test.alerts-default-000001': { + aliases: { '.alerts-test.alerts-default': { is_write_index: true } }, }, })); alertsService.register(TestRegistrationContext); - await new Promise((r) => setTimeout(r, 50)); - expect(await alertsService.isContextInitialized(TestRegistrationContext.context)).toEqual( - true + await retryUntil( + 'context initialized', + async () => (await getContextInitialized(alertsService)) === true ); expect(logger.error).toHaveBeenCalledWith(`Error creating concrete write index - fail`); @@ -954,16 +980,20 @@ describe('Alerts Service', () => { }; clusterClient.indices.create.mockRejectedValueOnce(error); clusterClient.indices.get.mockImplementationOnce(async () => ({ - '.alerts-test-default-000001': { - aliases: { '.alerts-test-default': { is_write_index: false } }, + '.internal.alerts-test.alerts-default-000001': { + aliases: { '.alerts-test.alerts-default': { is_write_index: false } }, }, })); alertsService.register(TestRegistrationContext); - await new Promise((r) => setTimeout(r, 50)); - expect(await alertsService.isContextInitialized(TestRegistrationContext.context)).toEqual( - false - ); + await retryUntil('error logger called', async () => logger.error.mock.calls.length > 0); + expect( + await alertsService.getContextInitializationPromise(TestRegistrationContext.context) + ).toEqual({ + error: + 'Failure during installation. Attempted to create index: .internal.alerts-test.alerts-default-000001 as the write index for alias: .alerts-test.alerts-default, but the index already exists and is not the write index for the alias', + result: false, + }); expect(logger.error).toHaveBeenCalledWith(`Error creating concrete write index - fail`); @@ -990,11 +1020,13 @@ describe('Alerts Service', () => { logger, elasticsearchClientPromise: Promise.resolve(clusterClient), pluginStop$, + kibanaVersion: '8.8.0', }); - alertsService.initialize(); - await new Promise((r) => setTimeout(r, 150)); - expect(alertsService.isInitialized()).toEqual(true); + await retryUntil( + 'alert service initialized', + async () => alertsService.isInitialized() === true + ); expect(clusterClient.ilm.putLifecycle).toHaveBeenCalledTimes(3); }); @@ -1007,11 +1039,13 @@ describe('Alerts Service', () => { logger, elasticsearchClientPromise: Promise.resolve(clusterClient), pluginStop$, + kibanaVersion: '8.8.0', }); - alertsService.initialize(); - await new Promise((r) => setTimeout(r, 150)); - expect(alertsService.isInitialized()).toEqual(true); + await retryUntil( + 'alert service initialized', + async () => alertsService.isInitialized() === true + ); expect(clusterClient.cluster.putComponentTemplate).toHaveBeenCalledTimes(5); }); @@ -1024,17 +1058,21 @@ describe('Alerts Service', () => { logger, elasticsearchClientPromise: Promise.resolve(clusterClient), pluginStop$, + kibanaVersion: '8.8.0', }); - alertsService.initialize(); - await new Promise((r) => setTimeout(r, 150)); + await retryUntil( + 'alert service initialized', + async () => alertsService.isInitialized() === true + ); expect(alertsService.isInitialized()).toEqual(true); alertsService.register(TestRegistrationContext); - await new Promise((r) => setTimeout(r, 150)); - expect(await alertsService.isContextInitialized(TestRegistrationContext.context)).toEqual( - true + await retryUntil( + 'context initialized', + async () => (await getContextInitialized(alertsService)) === true ); + expect(clusterClient.indices.putIndexTemplate).toHaveBeenCalledTimes(3); }); @@ -1047,17 +1085,20 @@ describe('Alerts Service', () => { logger, elasticsearchClientPromise: Promise.resolve(clusterClient), pluginStop$, + kibanaVersion: '8.8.0', }); - alertsService.initialize(); - await new Promise((r) => setTimeout(r, 150)); - expect(alertsService.isInitialized()).toEqual(true); + await retryUntil( + 'alert service initialized', + async () => alertsService.isInitialized() === true + ); alertsService.register(TestRegistrationContext); - await new Promise((r) => setTimeout(r, 150)); - expect(await alertsService.isContextInitialized(TestRegistrationContext.context)).toEqual( - true + await retryUntil( + 'context initialized', + async () => (await getContextInitialized(alertsService)) === true ); + expect(clusterClient.indices.putSettings).toHaveBeenCalledTimes(4); }); @@ -1070,17 +1111,20 @@ describe('Alerts Service', () => { logger, elasticsearchClientPromise: Promise.resolve(clusterClient), pluginStop$, + kibanaVersion: '8.8.0', }); - alertsService.initialize(); - await new Promise((r) => setTimeout(r, 150)); - expect(alertsService.isInitialized()).toEqual(true); + await retryUntil( + 'alert service initialized', + async () => alertsService.isInitialized() === true + ); alertsService.register(TestRegistrationContext); - await new Promise((r) => setTimeout(r, 150)); - expect(await alertsService.isContextInitialized(TestRegistrationContext.context)).toEqual( - true + await retryUntil( + 'context initialized', + async () => (await getContextInitialized(alertsService)) === true ); + expect(clusterClient.indices.putMapping).toHaveBeenCalledTimes(4); }); @@ -1093,17 +1137,20 @@ describe('Alerts Service', () => { logger, elasticsearchClientPromise: Promise.resolve(clusterClient), pluginStop$, + kibanaVersion: '8.8.0', }); - alertsService.initialize(); - await new Promise((r) => setTimeout(r, 150)); - expect(alertsService.isInitialized()).toEqual(true); + await retryUntil( + 'alert service initialized', + async () => alertsService.isInitialized() === true + ); alertsService.register(TestRegistrationContext); - await new Promise((r) => setTimeout(r, 150)); - expect(await alertsService.isContextInitialized(TestRegistrationContext.context)).toEqual( - true + await retryUntil( + 'context initialized', + async () => (await getContextInitialized(alertsService)) === true ); + expect(clusterClient.indices.create).toHaveBeenCalledTimes(3); }); }); @@ -1114,29 +1161,30 @@ describe('Alerts Service', () => { await new Promise((resolve) => setTimeout(resolve, 20)); return { acknowledged: true }; }); - const alertsService = new AlertsService({ + new AlertsService({ logger, elasticsearchClientPromise: Promise.resolve(clusterClient), pluginStop$, + kibanaVersion: '8.8.0', + timeoutMs: 10, }); - alertsService.initialize(10); - await new Promise((r) => setTimeout(r, 150)); - expect(alertsService.isInitialized()).toEqual(false); + await retryUntil('error logger called', async () => logger.error.mock.calls.length > 0); expect(logger.error).toHaveBeenCalledWith(new Error(`Timeout: it took more than 10ms`)); }); test('should short circuit initialization if pluginStop$ signal received but not throw error', async () => { pluginStop$.next(); - const alertsService = new AlertsService({ + new AlertsService({ logger, elasticsearchClientPromise: Promise.resolve(clusterClient), pluginStop$, + kibanaVersion: '8.8.0', + timeoutMs: 10, }); - alertsService.initialize(); - await new Promise((r) => setTimeout(r, 50)); + await retryUntil('error logger called', async () => logger.error.mock.calls.length > 0); expect(logger.error).toHaveBeenCalledWith( new Error(`Server is stopping; must stop all async operations`) diff --git a/x-pack/plugins/alerting/server/alerts_service/alerts_service.ts b/x-pack/plugins/alerting/server/alerts_service/alerts_service.ts index 22cb9f4df1884..5e32a599de744 100644 --- a/x-pack/plugins/alerting/server/alerts_service/alerts_service.ts +++ b/x-pack/plugins/alerting/server/alerts_service/alerts_service.ts @@ -13,13 +13,11 @@ import { import { get, isEmpty, isEqual } from 'lodash'; import { Logger, ElasticsearchClient } from '@kbn/core/server'; import { firstValueFrom, Observable } from 'rxjs'; +import { alertFieldMap, ecsFieldMap, legacyAlertFieldMap } from '@kbn/alerts-as-data-utils'; import { - alertFieldMap, - ecsFieldMap, - legacyAlertFieldMap, - type FieldMap, -} from '@kbn/alerts-as-data-utils'; -import { IndicesGetIndexTemplateIndexTemplateItem } from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; + IndicesGetIndexTemplateIndexTemplateItem, + Metadata, +} from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; import { asyncForEach } from '@kbn/std'; import { DEFAULT_ALERTS_ILM_POLICY_NAME, @@ -30,23 +28,28 @@ import { getComponentTemplateName, getIndexTemplateAndPattern, IIndexPatternString, -} from './types'; +} from './resource_installer_utils'; import { retryTransientEsErrors } from './retry_transient_es_errors'; import { IRuleTypeAlerts } from '../types'; import { createResourceInstallationHelper, + errorResult, + InitializationPromise, ResourceInstallationHelper, + successResult, } from './create_resource_installation_helper'; const TOTAL_FIELDS_LIMIT = 2500; const INSTALLATION_TIMEOUT = 20 * 60 * 1000; // 20 minutes const LEGACY_ALERT_CONTEXT = 'legacy-alert'; export const ECS_CONTEXT = `ecs`; -export const ECS_COMPONENT_TEMPLATE_NAME = getComponentTemplateName(ECS_CONTEXT); +export const ECS_COMPONENT_TEMPLATE_NAME = getComponentTemplateName({ name: ECS_CONTEXT }); interface AlertsServiceParams { logger: Logger; pluginStop$: Observable; + kibanaVersion: string; elasticsearchClientPromise: Promise; + timeoutMs?: number; } interface ConcreteIndexInfo { @@ -54,17 +57,8 @@ interface ConcreteIndexInfo { alias: string; isWriteIndex: boolean; } -interface IAlertsService { - /** - * Initializes the common ES resources needed for framework alerts as data - * - ILM policy - common policy shared by all AAD indices - * - Component template - common mappings for fields populated and used by the framework - * - * Once common resource initialization is complete, look for any solution-specific - * resources that have been registered and are awaiting initialization. - */ - initialize(timeoutMs?: number): void; +interface IAlertsService { /** * Register solution specific resources. If common resource initialization is * complete, go ahead and install those resources, otherwise add to queue to @@ -78,16 +72,37 @@ interface IAlertsService { register(opts: IRuleTypeAlerts, timeoutMs?: number): void; isInitialized(): boolean; + + /** + * Returns promise that resolves when the resources for the given + * context are installed. These include the context specific component template, + * the index template for the default namespace and the concrete write index + * for the default namespace. + */ + getContextInitializationPromise(context: string): Promise; } +export type PublicAlertsService = Pick; +export type PublicFrameworkAlertsService = PublicAlertsService & { + enabled: () => boolean; +}; + export class AlertsService implements IAlertsService { private initialized: boolean; private resourceInitializationHelper: ResourceInstallationHelper; - private registeredContexts: Map = new Map(); + private registeredContexts: Map = new Map(); + private commonInitPromise: Promise; constructor(private readonly options: AlertsServiceParams) { this.initialized = false; + + // Kick off initialization of common assets and save the promise + this.commonInitPromise = this.initializeCommon(this.options.timeoutMs); + + // Create helper for initializing context-specific resources this.resourceInitializationHelper = createResourceInstallationHelper( + this.options.logger, + this.commonInitPromise, this.initializeContext.bind(this) ); } @@ -96,88 +111,118 @@ export class AlertsService implements IAlertsService { return this.initialized; } - public async isContextInitialized(context: string) { - return (await this.resourceInitializationHelper.getInitializedContexts().get(context)) ?? false; - } - - public initialize(timeoutMs?: number) { - // Only initialize once - if (this.initialized) return; - - this.options.logger.debug(`Initializing resources for AlertsService`); - - // Use setImmediate to execute async fns as soon as possible - setImmediate(async () => { - try { - const esClient = await this.options.elasticsearchClientPromise; - - // Common initialization installs ILM policy and shared component template - const initFns = [ - () => this.createOrUpdateIlmPolicy(esClient), - () => this.createOrUpdateComponentTemplate(esClient, getComponentTemplate(alertFieldMap)), - () => - this.createOrUpdateComponentTemplate( - esClient, - getComponentTemplate(legacyAlertFieldMap, LEGACY_ALERT_CONTEXT) - ), - () => - this.createOrUpdateComponentTemplate( - esClient, - getComponentTemplate(ecsFieldMap, ECS_CONTEXT) - ), - ]; - - for (const fn of initFns) { - await this.installWithTimeout(async () => await fn(), timeoutMs); - } - - this.initialized = true; - } catch (err) { - this.options.logger.error( - `Error installing common resources for AlertsService. No additional resources will be installed and rule execution may be impacted.` - ); - this.initialized = false; - } - - if (this.initialized) { - this.resourceInitializationHelper.setReadyToInitialize(timeoutMs); - } - }); + public async getContextInitializationPromise( + context: string, + timeoutMs?: number + ): Promise { + if (!this.registeredContexts.has(context)) { + const errMsg = `Error getting initialized status for context ${context} - context has not been registered.`; + this.options.logger.error(errMsg); + return Promise.resolve(errorResult(errMsg)); + } + return this.resourceInitializationHelper.getInitializedContext(context, timeoutMs); } public register(opts: IRuleTypeAlerts, timeoutMs?: number) { - const { context, fieldMap } = opts; + const { context } = opts; // check whether this context has been registered before if (this.registeredContexts.has(context)) { - const registeredFieldMap = this.registeredContexts.get(context); - if (!isEqual(fieldMap, registeredFieldMap)) { - throw new Error(`${context} has already been registered with a different mapping`); + const registeredOptions = this.registeredContexts.get(context); + if (!isEqual(opts, registeredOptions)) { + throw new Error(`${context} has already been registered with different options`); } this.options.logger.debug(`Resources for context "${context}" have already been registered.`); return; } this.options.logger.info(`Registering resources for context "${context}".`); - this.registeredContexts.set(context, fieldMap); + this.registeredContexts.set(context, opts); this.resourceInitializationHelper.add(opts, timeoutMs); } + /** + * Initializes the common ES resources needed for framework alerts as data + * - ILM policy - common policy shared by all AAD indices + * - Component template - common mappings for fields populated and used by the framework + */ + private async initializeCommon(timeoutMs?: number): Promise { + try { + this.options.logger.debug(`Initializing resources for AlertsService`); + const esClient = await this.options.elasticsearchClientPromise; + + // Common initialization installs ILM policy and shared component template + const initFns = [ + () => this.createOrUpdateIlmPolicy(esClient), + () => + this.createOrUpdateComponentTemplate( + esClient, + getComponentTemplate({ fieldMap: alertFieldMap, includeSettings: true }) + ), + () => + this.createOrUpdateComponentTemplate( + esClient, + getComponentTemplate({ + fieldMap: legacyAlertFieldMap, + name: LEGACY_ALERT_CONTEXT, + includeSettings: true, + }) + ), + () => + this.createOrUpdateComponentTemplate( + esClient, + getComponentTemplate({ + fieldMap: ecsFieldMap, + name: ECS_CONTEXT, + includeSettings: true, + }) + ), + ]; + + for (const fn of initFns) { + await this.installWithTimeout(async () => await fn(), timeoutMs); + } + + this.initialized = true; + return successResult(); + } catch (err) { + this.options.logger.error( + `Error installing common resources for AlertsService. No additional resources will be installed and rule execution may be impacted. - ${err.message}` + ); + this.initialized = false; + return errorResult(err.message); + } + } + private async initializeContext( - { context, fieldMap, useEcs, useLegacyAlerts }: IRuleTypeAlerts, + { context, mappings, useEcs, useLegacyAlerts, secondaryAlias }: IRuleTypeAlerts, timeoutMs?: number ) { const esClient = await this.options.elasticsearchClientPromise; - const indexTemplateAndPattern = getIndexTemplateAndPattern(context); + const indexTemplateAndPattern = getIndexTemplateAndPattern({ context, secondaryAlias }); let initFns: Array<() => Promise> = []; // List of component templates to reference + // Order matters in this list - templates specified last take precedence over those specified first + // 1. ECS component template, if using + // 2. Context specific component template, if defined during registration + // 3. Legacy alert component template, if using + // 4. Framework common component template, always included const componentTemplateRefs: string[] = []; - // If fieldMap is not empty, create a context specific component template - if (!isEmpty(fieldMap)) { - const componentTemplate = getComponentTemplate(fieldMap, context); + // If useEcs is set to true, add the ECS component template to the references + if (useEcs) { + componentTemplateRefs.push(getComponentTemplateName({ name: ECS_CONTEXT })); + } + + // If fieldMap is not empty, create a context specific component template and add to the references + if (!isEmpty(mappings.fieldMap)) { + const componentTemplate = getComponentTemplate({ + fieldMap: mappings.fieldMap, + dynamic: mappings.dynamic, + context, + }); initFns.push( async () => await this.createOrUpdateComponentTemplate(esClient, componentTemplate) ); @@ -186,12 +231,7 @@ export class AlertsService implements IAlertsService { // If useLegacy is set to true, add the legacy alert component template to the references if (useLegacyAlerts) { - componentTemplateRefs.push(getComponentTemplateName(LEGACY_ALERT_CONTEXT)); - } - - // If useEcs is set to true, add the ECS component template to the references - if (useEcs) { - componentTemplateRefs.push(getComponentTemplateName(ECS_CONTEXT)); + componentTemplateRefs.push(getComponentTemplateName({ name: LEGACY_ALERT_CONTEXT })); } // Add framework component template to the references @@ -326,6 +366,14 @@ export class AlertsService implements IAlertsService { ) { this.options.logger.info(`Installing index template ${indexPatterns.template}`); + const indexMetadata: Metadata = { + kibana: { + version: this.options.kibanaVersion, + }, + managed: true, + namespace: 'default', // hard-coded to default here until we start supporting space IDs + }; + const indexTemplate = { name: indexPatterns.template, body: { @@ -343,12 +391,21 @@ export class AlertsService implements IAlertsService { }, mappings: { dynamic: false, + _meta: indexMetadata, }, + ...(indexPatterns.secondaryAlias + ? { + aliases: { + [indexPatterns.secondaryAlias]: { + is_write_index: false, + }, + }, + } + : {}), }, - _meta: { - managed: true, - }, - // do we need metadata? like kibana version? doesn't that get updated every version? or just the first version its installed + _meta: indexMetadata, + + // TODO - set priority of this template when we start supporting spaces }, }; @@ -484,8 +541,11 @@ export class AlertsService implements IAlertsService { // check if a concrete write index already exists let concreteIndices: ConcreteIndexInfo[] = []; try { + // Specify both the index pattern for the backing indices and their aliases + // The alias prevents the request from finding other namespaces that could match the -* pattern const response = await esClient.indices.getAlias({ index: indexPatterns.pattern, + name: indexPatterns.basePattern, }); concreteIndices = Object.entries(response).flatMap(([index, { aliases }]) => diff --git a/x-pack/plugins/alerting/server/alerts_service/create_resource_installation_helper.test.ts b/x-pack/plugins/alerting/server/alerts_service/create_resource_installation_helper.test.ts index f9ce460d04093..ba128d46a2887 100644 --- a/x-pack/plugins/alerting/server/alerts_service/create_resource_installation_helper.test.ts +++ b/x-pack/plugins/alerting/server/alerts_service/create_resource_installation_helper.test.ts @@ -7,7 +7,14 @@ import { loggingSystemMock } from '@kbn/core/server/mocks'; import { IRuleTypeAlerts } from '../types'; -import { createResourceInstallationHelper } from './create_resource_installation_helper'; +import { + createResourceInstallationHelper, + errorResult, + InitializationPromise, + ResourceInstallationHelper, + successResult, +} from './create_resource_installation_helper'; +import { retryUntil } from './test_utils'; const logger: ReturnType = loggingSystemMock.createLogger(); @@ -16,13 +23,29 @@ const initFn = async (context: IRuleTypeAlerts, timeoutMs?: number) => { logger.info(context.context); }; -const initFnWithDelay = async (context: IRuleTypeAlerts, timeoutMs?: number) => { - logger.info(context.context); - await new Promise((r) => setTimeout(r, 50)); +const initFnWithError = async (context: IRuleTypeAlerts, timeoutMs?: number) => { + throw new Error('no go'); }; -const initFnWithError = async (context: IRuleTypeAlerts, timeoutMs?: number) => { - throw new Error('fail'); +const getCommonInitPromise = async ( + resolution: boolean, + timeoutMs: number = 1 +): Promise => { + if (timeoutMs < 0) { + throw new Error('fail'); + } + // delay resolution of promise by timeout value + await new Promise((r) => setTimeout(r, timeoutMs)); + logger.info(`commonInitPromise resolved`); + return Promise.resolve(resolution ? successResult() : errorResult(`error initializing`)); +}; + +const getContextInitialized = async ( + helper: ResourceInstallationHelper, + context: string = 'test1' +) => { + const { result } = await helper.getInitializedContext(context); + return result; }; describe('createResourceInstallationHelper', () => { @@ -30,108 +53,117 @@ describe('createResourceInstallationHelper', () => { jest.clearAllMocks(); }); - test(`should not call init function if readyToInitialize is false`, () => { - const helper = createResourceInstallationHelper(initFn); - - // Add two contexts that need to be initialized but don't call helper.setReadyToInitialize() - helper.add({ context: 'test1', fieldMap: { field: { type: 'keyword', required: false } } }); - helper.add({ context: 'test2', fieldMap: { field: { type: 'keyword', required: false } } }); + test(`should wait for commonInitFunction to resolve before calling initFns for registered contexts`, async () => { + const helper = createResourceInstallationHelper( + logger, + getCommonInitPromise(true, 100), + initFn + ); - expect(logger.info).not.toHaveBeenCalled(); - const initializedContexts = helper.getInitializedContexts(); - expect([...initializedContexts.keys()].length).toEqual(0); + // Add two contexts that need to be initialized + helper.add({ + context: 'test1', + mappings: { fieldMap: { field: { type: 'keyword', required: false } } }, + }); + helper.add({ + context: 'test2', + mappings: { fieldMap: { field: { type: 'keyword', required: false } } }, + }); + + await retryUntil('init fns run', async () => logger.info.mock.calls.length === 3); + + expect(logger.info).toHaveBeenNthCalledWith(1, `commonInitPromise resolved`); + expect(logger.info).toHaveBeenNthCalledWith(2, 'test1'); + expect(logger.info).toHaveBeenNthCalledWith(3, 'test2'); + expect(await helper.getInitializedContext('test1')).toEqual({ result: true }); + expect(await helper.getInitializedContext('test2')).toEqual({ result: true }); }); - test(`should call init function if readyToInitialize is set to true`, async () => { - const helper = createResourceInstallationHelper(initFn); - - // Add two contexts that need to be initialized and then call helper.setReadyToInitialize() - helper.add({ context: 'test1', fieldMap: { field: { type: 'keyword', required: false } } }); - helper.add({ context: 'test2', fieldMap: { field: { type: 'keyword', required: false } } }); - - helper.setReadyToInitialize(); - - // for the setImmediate - await new Promise((r) => setTimeout(r, 10)); - - expect(logger.info).toHaveBeenCalledTimes(2); - const initializedContexts = helper.getInitializedContexts(); - expect([...initializedContexts.keys()].length).toEqual(2); - - expect(await initializedContexts.get('test1')).toEqual(true); - expect(await initializedContexts.get('test2')).toEqual(true); + test(`should return false if context is unrecognized`, async () => { + const helper = createResourceInstallationHelper( + logger, + getCommonInitPromise(true, 100), + initFn + ); + + helper.add({ + context: 'test1', + mappings: { fieldMap: { field: { type: 'keyword', required: false } } }, + }); + + await retryUntil('init fns run', async () => logger.info.mock.calls.length === 2); + + expect(await helper.getInitializedContext('test1')).toEqual({ result: true }); + expect(await helper.getInitializedContext('test2')).toEqual({ + result: false, + error: `Unrecognized context test2`, + }); }); - test(`should install resources for contexts added after readyToInitialize is called`, async () => { - const helper = createResourceInstallationHelper(initFnWithDelay); - - // Add two contexts that need to be initialized - helper.add({ context: 'test1', fieldMap: { field: { type: 'keyword', required: false } } }); - helper.add({ context: 'test2', fieldMap: { field: { type: 'keyword', required: false } } }); - - // Start processing the queued contexts - helper.setReadyToInitialize(); - - // for the setImmediate - await new Promise((r) => setTimeout(r, 10)); - - // Add another context to process - helper.add({ context: 'test3', fieldMap: { field: { type: 'keyword', required: false } } }); - - // 3 contexts with delay will take 150 - await new Promise((r) => setTimeout(r, 10)); - - expect(logger.info).toHaveBeenCalledTimes(3); - const initializedContexts = helper.getInitializedContexts(); - expect([...initializedContexts.keys()].length).toEqual(3); - - expect(await initializedContexts.get('test1')).toEqual(true); - expect(await initializedContexts.get('test2')).toEqual(true); - expect(await initializedContexts.get('test3')).toEqual(true); + test(`should log and return false if common init function returns false`, async () => { + const helper = createResourceInstallationHelper( + logger, + getCommonInitPromise(false, 100), + initFn + ); + + helper.add({ + context: 'test1', + mappings: { fieldMap: { field: { type: 'keyword', required: false } } }, + }); + + await retryUntil('common init fns run', async () => logger.info.mock.calls.length === 1); + + expect(logger.warn).toHaveBeenCalledWith( + `Common resources were not initialized, cannot initialize context for test1` + ); + expect(await helper.getInitializedContext('test1')).toEqual({ + result: false, + error: `error initializing`, + }); }); - test(`should install resources for contexts added after initial processing loop has run`, async () => { - const helper = createResourceInstallationHelper(initFn); - - // No contexts queued so this should finish quickly - helper.setReadyToInitialize(); - - // for the setImmediate - await new Promise((r) => setTimeout(r, 10)); + test(`should log and return false if common init function throws error`, async () => { + const helper = createResourceInstallationHelper(logger, getCommonInitPromise(true, -1), initFn); - expect(logger.info).not.toHaveBeenCalled(); - let initializedContexts = helper.getInitializedContexts(); - expect([...initializedContexts.keys()].length).toEqual(0); + helper.add({ + context: 'test1', + mappings: { fieldMap: { field: { type: 'keyword', required: false } } }, + }); - // Add a context to process - helper.add({ context: 'test1', fieldMap: { field: { type: 'keyword', required: false } } }); + await retryUntil( + 'common init fns run', + async () => (await getContextInitialized(helper)) === false + ); - // for the setImmediate - await new Promise((r) => setTimeout(r, 10)); - - expect(logger.info).toHaveBeenCalledTimes(1); - initializedContexts = helper.getInitializedContexts(); - expect([...initializedContexts.keys()].length).toEqual(1); - - expect(await initializedContexts.get('test1')).toEqual(true); + expect(logger.error).toHaveBeenCalledWith(`Error initializing context test1 - fail`); + expect(await helper.getInitializedContext('test1')).toEqual({ + result: false, + error: `fail`, + }); }); - test(`should gracefully handle errors during initialization and set initialized flag to false`, async () => { - const helper = createResourceInstallationHelper(initFnWithError); - - helper.setReadyToInitialize(); - - // for the setImmediate - await new Promise((r) => setTimeout(r, 10)); - - // Add a context to process - helper.add({ context: 'test1', fieldMap: { field: { type: 'keyword', required: false } } }); - - // for the setImmediate - await new Promise((r) => setTimeout(r, 10)); - - const initializedContexts = helper.getInitializedContexts(); - expect([...initializedContexts.keys()].length).toEqual(1); - expect(await initializedContexts.get('test1')).toEqual(false); + test(`should log and return false if context init function throws error`, async () => { + const helper = createResourceInstallationHelper( + logger, + getCommonInitPromise(true, 100), + initFnWithError + ); + + helper.add({ + context: 'test1', + mappings: { fieldMap: { field: { type: 'keyword', required: false } } }, + }); + + await retryUntil( + 'context init fns run', + async () => (await getContextInitialized(helper)) === false + ); + + expect(logger.error).toHaveBeenCalledWith(`Error initializing context test1 - no go`); + expect(await helper.getInitializedContext('test1')).toEqual({ + result: false, + error: `no go`, + }); }); }); diff --git a/x-pack/plugins/alerting/server/alerts_service/create_resource_installation_helper.ts b/x-pack/plugins/alerting/server/alerts_service/create_resource_installation_helper.ts index 0e3cbe0f87a9a..36dddcbf214d6 100644 --- a/x-pack/plugins/alerting/server/alerts_service/create_resource_installation_helper.ts +++ b/x-pack/plugins/alerting/server/alerts_service/create_resource_installation_helper.ts @@ -5,12 +5,16 @@ * 2.0. */ +import { Logger } from '@kbn/core/server'; import { IRuleTypeAlerts } from '../types'; +export interface InitializationPromise { + result: boolean; + error?: string; +} export interface ResourceInstallationHelper { add: (context: IRuleTypeAlerts, timeoutMs?: number) => void; - setReadyToInitialize: (timeoutMs?: number) => void; - getInitializedContexts: () => Map>; + getInitializedContext: (context: string, delayMs?: number) => Promise; } /** @@ -25,57 +29,49 @@ export interface ResourceInstallationHelper { * running, kick off the processing loop */ export function createResourceInstallationHelper( - initFn: (context: IRuleTypeAlerts, timeoutMs?: number) => Promise + logger: Logger, + commonResourcesInitPromise: Promise, + installFn: (context: IRuleTypeAlerts, timeoutMs?: number) => Promise ): ResourceInstallationHelper { - let readyToInitialize = false; - let isInitializing: boolean = false; - const contextsToInitialize: IRuleTypeAlerts[] = []; - const initializedContexts: Map> = new Map(); + const initializedContexts: Map> = new Map(); const waitUntilContextResourcesInstalled = async ( context: IRuleTypeAlerts, timeoutMs?: number - ): Promise => { + ): Promise => { try { - await initFn(context, timeoutMs); - return true; + const { result: commonInitResult, error: commonInitError } = await commonResourcesInitPromise; + if (commonInitResult) { + await installFn(context, timeoutMs); + return successResult(); + } else { + logger.warn( + `Common resources were not initialized, cannot initialize context for ${context.context}` + ); + return errorResult(commonInitError); + } } catch (err) { - return false; + logger.error(`Error initializing context ${context.context} - ${err.message}`); + return errorResult(err.message); } }; - const startInitialization = (timeoutMs?: number) => { - if (!readyToInitialize) { - return; - } - - setImmediate(async () => { - isInitializing = true; - while (contextsToInitialize.length > 0) { - const context = contextsToInitialize.pop()!; - initializedContexts.set( - context.context, - - // Return a promise than can be checked when needed - waitUntilContextResourcesInstalled(context, timeoutMs) - ); - } - isInitializing = false; - }); - }; return { add: (context: IRuleTypeAlerts, timeoutMs?: number) => { - contextsToInitialize.push(context); - if (!isInitializing) { - startInitialization(timeoutMs); - } - }, - setReadyToInitialize: (timeoutMs?: number) => { - readyToInitialize = true; - startInitialization(timeoutMs); + initializedContexts.set( + context.context, + + // Return a promise than can be checked when needed + waitUntilContextResourcesInstalled(context, timeoutMs) + ); }, - getInitializedContexts: () => { - return initializedContexts; + getInitializedContext: async (context: string): Promise => { + return initializedContexts.has(context) + ? initializedContexts.get(context)! + : errorResult(`Unrecognized context ${context}`); }, }; } + +export const successResult = () => ({ result: true }); +export const errorResult = (error?: string) => ({ result: false, error }); diff --git a/x-pack/plugins/alerting/server/alerts_service/index.ts b/x-pack/plugins/alerting/server/alerts_service/index.ts index 49247f3baa243..5c3dd2a17d086 100644 --- a/x-pack/plugins/alerting/server/alerts_service/index.ts +++ b/x-pack/plugins/alerting/server/alerts_service/index.ts @@ -10,4 +10,10 @@ export { DEFAULT_ALERTS_ILM_POLICY_NAME, } from './default_lifecycle_policy'; export { ECS_COMPONENT_TEMPLATE_NAME, ECS_CONTEXT } from './alerts_service'; -export { getComponentTemplate } from './types'; +export { getComponentTemplate } from './resource_installer_utils'; +export { + type InitializationPromise, + successResult, + errorResult, +} from './create_resource_installation_helper'; +export { AlertsService, type PublicFrameworkAlertsService } from './alerts_service'; diff --git a/x-pack/plugins/alerting/server/alerts_service/resource_installer_utils.test.ts b/x-pack/plugins/alerting/server/alerts_service/resource_installer_utils.test.ts new file mode 100644 index 0000000000000..0f5f482cf3c84 --- /dev/null +++ b/x-pack/plugins/alerting/server/alerts_service/resource_installer_utils.test.ts @@ -0,0 +1,71 @@ +/* + * 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 { getComponentTemplateName, getIndexTemplateAndPattern } from './resource_installer_utils'; + +describe('getComponentTemplateName', () => { + test('should use default when name and context are undefined', () => { + expect(getComponentTemplateName()).toEqual(`.alerts-framework-mappings`); + }); + + test('should use name as is when name is defined', () => { + expect(getComponentTemplateName({ name: 'test-my-mappings' })).toEqual( + `.alerts-test-my-mappings-mappings` + ); + }); + + test('should use append .alerts to context when context is defined', () => { + expect(getComponentTemplateName({ context: 'test-my-mappings' })).toEqual( + `.alerts-test-my-mappings.alerts-mappings` + ); + }); + + test('should prioritize context when context and name are both defined', () => { + expect( + getComponentTemplateName({ context: 'test-my-mappings', name: 'dont-name-me-this' }) + ).toEqual(`.alerts-test-my-mappings.alerts-mappings`); + }); +}); + +describe('getIndexTemplateAndPattern', () => { + test('should use default namespace when namespace is undefined', () => { + expect(getIndexTemplateAndPattern({ context: 'test' })).toEqual({ + template: '.alerts-test.alerts-default-index-template', + pattern: '.internal.alerts-test.alerts-default-*', + basePattern: '.alerts-test.alerts-*', + alias: '.alerts-test.alerts-default', + name: '.internal.alerts-test.alerts-default-000001', + }); + }); + + test('should use namespace when namespace is defined', () => { + expect(getIndexTemplateAndPattern({ context: 'test', namespace: 'special' })).toEqual({ + template: '.alerts-test.alerts-special-index-template', + pattern: '.internal.alerts-test.alerts-special-*', + basePattern: '.alerts-test.alerts-*', + alias: '.alerts-test.alerts-special', + name: '.internal.alerts-test.alerts-special-000001', + }); + }); + + test('should return secondaryAlias when secondaryAlias is defined', () => { + expect( + getIndexTemplateAndPattern({ + context: 'test', + namespace: 'special', + secondaryAlias: 'siem.signals', + }) + ).toEqual({ + template: '.alerts-test.alerts-special-index-template', + pattern: '.internal.alerts-test.alerts-special-*', + basePattern: '.alerts-test.alerts-*', + alias: '.alerts-test.alerts-special', + name: '.internal.alerts-test.alerts-special-000001', + secondaryAlias: `siem.signals-special`, + }); + }); +}); diff --git a/x-pack/plugins/alerting/server/alerts_service/resource_installer_utils.ts b/x-pack/plugins/alerting/server/alerts_service/resource_installer_utils.ts new file mode 100644 index 0000000000000..eba614e655617 --- /dev/null +++ b/x-pack/plugins/alerting/server/alerts_service/resource_installer_utils.ts @@ -0,0 +1,70 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { ClusterPutComponentTemplateRequest } from '@elastic/elasticsearch/lib/api/types'; +import type { FieldMap } from '@kbn/alerts-as-data-utils'; +import { getComponentTemplateFromFieldMap } from '../../common'; + +interface GetComponentTemplateNameOpts { + context?: string; + name?: string; +} +export const getComponentTemplateName = ({ context, name }: GetComponentTemplateNameOpts = {}) => + `.alerts-${context ? `${context}.alerts` : name ? name : 'framework'}-mappings`; + +export interface IIndexPatternString { + template: string; + pattern: string; + alias: string; + name: string; + basePattern: string; + secondaryAlias?: string; +} + +interface GetIndexTemplateAndPatternOpts { + context: string; + secondaryAlias?: string; + namespace?: string; +} + +export const getIndexTemplateAndPattern = ({ + context, + namespace, + secondaryAlias, +}: GetIndexTemplateAndPatternOpts): IIndexPatternString => { + const concreteNamespace = namespace ? namespace : 'default'; + const pattern = `${context}.alerts`; + const patternWithNamespace = `${pattern}-${concreteNamespace}`; + return { + template: `.alerts-${patternWithNamespace}-index-template`, + pattern: `.internal.alerts-${patternWithNamespace}-*`, + basePattern: `.alerts-${pattern}-*`, + name: `.internal.alerts-${patternWithNamespace}-000001`, + alias: `.alerts-${patternWithNamespace}`, + ...(secondaryAlias ? { secondaryAlias: `${secondaryAlias}-${concreteNamespace}` } : {}), + }; +}; + +type GetComponentTemplateOpts = GetComponentTemplateNameOpts & { + fieldMap: FieldMap; + dynamic?: 'strict' | false; + includeSettings?: boolean; +}; + +export const getComponentTemplate = ({ + fieldMap, + context, + name, + dynamic, + includeSettings, +}: GetComponentTemplateOpts): ClusterPutComponentTemplateRequest => + getComponentTemplateFromFieldMap({ + name: getComponentTemplateName({ context, name }), + fieldMap, + dynamic, + includeSettings, + }); diff --git a/x-pack/plugins/alerting/server/alerts_service/test_utils.ts b/x-pack/plugins/alerting/server/alerts_service/test_utils.ts new file mode 100644 index 0000000000000..1d710caf57670 --- /dev/null +++ b/x-pack/plugins/alerting/server/alerts_service/test_utils.ts @@ -0,0 +1,34 @@ +/* + * 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. + */ + +const RETRY_UNTIL_DEFAULT_COUNT = 20; +const RETRY_UNTIL_DEFAULT_WAIT = 100; // milliseconds +type RetryableFunction = () => Promise; + +export const retryUntil = async ( + label: string, + fn: RetryableFunction, + count: number = RETRY_UNTIL_DEFAULT_COUNT, + wait: number = RETRY_UNTIL_DEFAULT_WAIT +): Promise => { + await delay(wait); + while (count > 0) { + count--; + + if (await fn()) return true; + + // eslint-disable-next-line no-console + console.log(`attempt failed waiting for "${label}", attempts left: ${count}`); + + if (count === 0) return false; + await delay(wait); + } + + return false; +}; + +const delay = async (millis: number) => await new Promise((resolve) => setTimeout(resolve, millis)); diff --git a/x-pack/plugins/alerting/server/alerts_service/types.ts b/x-pack/plugins/alerting/server/alerts_service/types.ts deleted file mode 100644 index aeb73cab6ffd2..0000000000000 --- a/x-pack/plugins/alerting/server/alerts_service/types.ts +++ /dev/null @@ -1,44 +0,0 @@ -/* - * 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 { ClusterPutComponentTemplateRequest } from '@elastic/elasticsearch/lib/api/types'; -import type { FieldMap } from '@kbn/alerts-as-data-utils'; -import { getComponentTemplateFromFieldMap } from '../../common'; - -export const getComponentTemplateName = (context?: string) => - `.alerts-${context || 'framework'}-mappings`; - -export interface IIndexPatternString { - template: string; - pattern: string; - alias: string; - name: string; -} - -export const getIndexTemplateAndPattern = ( - context: string, - namespace?: string -): IIndexPatternString => { - const pattern = `${context}-${namespace ? namespace : 'default'}`; - return { - template: `.alerts-${pattern}-template`, - pattern: `.alerts-${pattern}-*`, - alias: `.alerts-${pattern}`, - name: `.alerts-${pattern}-000001`, - }; -}; - -export const getComponentTemplate = ( - fieldMap: FieldMap, - context?: string -): ClusterPutComponentTemplateRequest => - getComponentTemplateFromFieldMap({ - name: getComponentTemplateName(context), - fieldMap, - // set field limit slightly higher than actual number of fields - fieldLimit: Math.ceil(Object.keys(fieldMap).length / 1000) * 1000 + 500, - }); diff --git a/x-pack/plugins/alerting/server/index.ts b/x-pack/plugins/alerting/server/index.ts index 6b5ad3012d8a9..d7546d8fd73a6 100644 --- a/x-pack/plugins/alerting/server/index.ts +++ b/x-pack/plugins/alerting/server/index.ts @@ -30,6 +30,7 @@ export type { RuleParamsAndRefs, GetSummarizedAlertsFnOpts, ExecutorType, + IRuleTypeAlerts, } from './types'; export { RuleNotifyWhen } from '../common'; export { DEFAULT_MAX_EPHEMERAL_ACTIONS_PER_ALERT } from './config'; @@ -59,6 +60,7 @@ export { ECS_COMPONENT_TEMPLATE_NAME, ECS_CONTEXT, getComponentTemplate, + type PublicFrameworkAlertsService, } from './alerts_service'; export const plugin = (initContext: PluginInitializerContext) => new AlertingPlugin(initContext); diff --git a/x-pack/plugins/alerting/server/mocks.ts b/x-pack/plugins/alerting/server/mocks.ts index 787ab3e9c856f..a5cadcd9bafa1 100644 --- a/x-pack/plugins/alerting/server/mocks.ts +++ b/x-pack/plugins/alerting/server/mocks.ts @@ -30,7 +30,10 @@ const createSetupMock = () => { registerType: jest.fn(), getSecurityHealth: jest.fn(), getConfig: jest.fn(), - getFrameworkAlertsEnabled: jest.fn(), + frameworkAlerts: { + enabled: jest.fn(), + getContextInitializationPromise: jest.fn(), + }, }; return mock; }; diff --git a/x-pack/plugins/alerting/server/plugin.test.ts b/x-pack/plugins/alerting/server/plugin.test.ts index f0dd5ce64315f..ed4b8575b9810 100644 --- a/x-pack/plugins/alerting/server/plugin.test.ts +++ b/x-pack/plugins/alerting/server/plugin.test.ts @@ -135,9 +135,8 @@ describe('Alerting Plugin', () => { const setupContract = await plugin.setup(setupMocks, mockPlugins); expect(AlertsService).toHaveBeenCalled(); - expect(mockAlertService.initialize).toHaveBeenCalled(); - expect(setupContract.getFrameworkAlertsEnabled()).toEqual(true); + expect(setupContract.frameworkAlerts.enabled()).toEqual(true); }); it(`exposes configured minimumScheduleInterval()`, async () => { @@ -153,7 +152,7 @@ describe('Alerting Plugin', () => { minimumScheduleInterval: { value: '1m', enforce: false }, }); - expect(setupContract.getFrameworkAlertsEnabled()).toEqual(false); + expect(setupContract.frameworkAlerts.enabled()).toEqual(false); }); describe('registerType()', () => { diff --git a/x-pack/plugins/alerting/server/plugin.ts b/x-pack/plugins/alerting/server/plugin.ts index 414d4f4e01b91..8840a541c10c7 100644 --- a/x-pack/plugins/alerting/server/plugin.ts +++ b/x-pack/plugins/alerting/server/plugin.ts @@ -87,7 +87,12 @@ import { getSecurityHealth, SecurityHealth } from './lib/get_security_health'; import { registerNodeCollector, registerClusterCollector, InMemoryMetrics } from './monitoring'; import { getRuleTaskTimeout } from './lib/get_rule_task_timeout'; import { getActionsConfigMap } from './lib/get_actions_config_map'; -import { AlertsService } from './alerts_service/alerts_service'; +import { + AlertsService, + type PublicFrameworkAlertsService, + type InitializationPromise, + errorResult, +} from './alerts_service'; import { rulesSettingsFeature } from './rules_settings_feature'; export const EVENT_LOG_PROVIDER = 'alerting'; @@ -126,7 +131,7 @@ export interface PluginSetupContract { ): void; getSecurityHealth: () => Promise; getConfig: () => AlertingRulesConfig; - getFrameworkAlertsEnabled: () => boolean; + frameworkAlerts: PublicFrameworkAlertsService; } export interface PluginStartContract { @@ -245,11 +250,11 @@ export class AlertingPlugin { this.alertsService = new AlertsService({ logger: this.logger, pluginStop$: this.pluginStop$, + kibanaVersion: this.kibanaVersion, elasticsearchClientPromise: core .getStartServices() .then(([{ elasticsearch }]) => elasticsearch.client.asInternalUser), }); - this.alertsService!.initialize(); } const ruleTypeRegistry = new RuleTypeRegistry({ @@ -386,7 +391,16 @@ export class AlertingPlugin { isUsingSecurity: this.licenseState ? !!this.licenseState.getIsSecurityEnabled() : false, }; }, - getFrameworkAlertsEnabled: () => this.config.enableFrameworkAlerts, + frameworkAlerts: { + enabled: () => this.config.enableFrameworkAlerts, + getContextInitializationPromise: (context: string): Promise => { + if (this.alertsService) { + return this.alertsService.getContextInitializationPromise(context); + } + + return Promise.resolve(errorResult(`Framework alerts service not available`)); + }, + }, }; } diff --git a/x-pack/plugins/alerting/server/routes/aggregate_rules.test.ts b/x-pack/plugins/alerting/server/routes/aggregate_rules.test.ts index 26210e9bed285..c9bfbdca4aa0b 100644 --- a/x-pack/plugins/alerting/server/routes/aggregate_rules.test.ts +++ b/x-pack/plugins/alerting/server/routes/aggregate_rules.test.ts @@ -30,6 +30,103 @@ beforeEach(() => { jest.resetAllMocks(); }); +const aggregateResult = { + status: { + buckets: [ + { + key: 'ok', + doc_count: 15, + }, + { + key: 'error', + doc_count: 2, + }, + { + key: 'active', + doc_count: 23, + }, + { + key: 'pending', + doc_count: 1, + }, + { + key: 'unknown', + doc_count: 0, + }, + { + key: 'warning', + doc_count: 10, + }, + ], + }, + outcome: { + buckets: [ + { + key: 'succeeded', + doc_count: 2, + }, + { + key: 'failed', + doc_count: 4, + }, + { + key: 'warning', + doc_count: 6, + }, + ], + }, + enabled: { + buckets: [ + { + key: 0, + key_as_string: '0', + doc_count: 2, + }, + { + key: 1, + key_as_string: '1', + doc_count: 28, + }, + ], + }, + muted: { + buckets: [ + { + key: 0, + key_as_string: '0', + doc_count: 27, + }, + { + key: 1, + key_as_string: '1', + doc_count: 3, + }, + ], + }, + snoozed: { + doc_count: 0, + count: { + doc_count: 0, + }, + }, + tags: { + buckets: [ + { + key: 'a', + doc_count: 10, + }, + { + key: 'b', + doc_count: 20, + }, + { + key: 'c', + doc_count: 30, + }, + ], + }, +}; + describe('aggregateRulesRoute', () => { it('aggregate rules with proper parameters', async () => { const licenseState = licenseStateMock.create(); @@ -41,32 +138,6 @@ describe('aggregateRulesRoute', () => { expect(config.path).toMatchInlineSnapshot(`"/internal/alerting/rules/_aggregate"`); - const aggregateResult = { - alertExecutionStatus: { - ok: 15, - error: 2, - active: 23, - pending: 1, - unknown: 0, - }, - ruleLastRunOutcome: { - succeeded: 1, - failed: 2, - warning: 3, - }, - ruleEnabledStatus: { - disabled: 1, - enabled: 40, - }, - ruleMutedStatus: { - muted: 2, - unmuted: 39, - }, - ruleSnoozedStatus: { - snoozed: 4, - }, - ruleTags: ['a', 'b', 'c'], - }; rulesClient.aggregate.mockResolvedValueOnce(aggregateResult); const [context, req, res] = mockHandlerArguments( @@ -83,8 +154,8 @@ describe('aggregateRulesRoute', () => { Object { "body": Object { "rule_enabled_status": Object { - "disabled": 1, - "enabled": 40, + "disabled": 2, + "enabled": 28, }, "rule_execution_status": Object { "active": 23, @@ -92,18 +163,19 @@ describe('aggregateRulesRoute', () => { "ok": 15, "pending": 1, "unknown": 0, + "warning": 10, }, "rule_last_run_outcome": Object { - "failed": 2, - "succeeded": 1, - "warning": 3, + "failed": 4, + "succeeded": 2, + "warning": 6, }, "rule_muted_status": Object { - "muted": 2, - "unmuted": 39, + "muted": 3, + "unmuted": 27, }, "rule_snoozed_status": Object { - "snoozed": 4, + "snoozed": 0, }, "rule_tags": Array [ "a", @@ -118,6 +190,51 @@ describe('aggregateRulesRoute', () => { expect(rulesClient.aggregate.mock.calls[0]).toMatchInlineSnapshot(` Array [ Object { + "aggs": Object { + "enabled": Object { + "terms": Object { + "field": "alert.attributes.enabled", + }, + }, + "muted": Object { + "terms": Object { + "field": "alert.attributes.muteAll", + }, + }, + "outcome": Object { + "terms": Object { + "field": "alert.attributes.lastRun.outcome", + }, + }, + "snoozed": Object { + "aggs": Object { + "count": Object { + "filter": Object { + "exists": Object { + "field": "alert.attributes.snoozeSchedule.duration", + }, + }, + }, + }, + "nested": Object { + "path": "alert.attributes.snoozeSchedule", + }, + }, + "status": Object { + "terms": Object { + "field": "alert.attributes.executionStatus.status", + }, + }, + "tags": Object { + "terms": Object { + "field": "alert.attributes.tags", + "order": Object { + "_key": "asc", + }, + "size": 50, + }, + }, + }, "options": Object { "defaultSearchOperator": "AND", }, @@ -128,8 +245,8 @@ describe('aggregateRulesRoute', () => { expect(res.ok).toHaveBeenCalledWith({ body: { rule_enabled_status: { - disabled: 1, - enabled: 40, + disabled: 2, + enabled: 28, }, rule_execution_status: { ok: 15, @@ -137,18 +254,19 @@ describe('aggregateRulesRoute', () => { active: 23, pending: 1, unknown: 0, + warning: 10, }, rule_last_run_outcome: { - succeeded: 1, - failed: 2, - warning: 3, + failed: 4, + succeeded: 2, + warning: 6, }, rule_muted_status: { - muted: 2, - unmuted: 39, + muted: 3, + unmuted: 27, }, rule_snoozed_status: { - snoozed: 4, + snoozed: 0, }, rule_tags: ['a', 'b', 'c'], }, @@ -163,20 +281,7 @@ describe('aggregateRulesRoute', () => { const [, handler] = router.get.mock.calls[0]; - rulesClient.aggregate.mockResolvedValueOnce({ - alertExecutionStatus: { - ok: 15, - error: 2, - active: 23, - pending: 1, - unknown: 0, - }, - ruleLastRunOutcome: { - succeeded: 2, - failed: 4, - warning: 6, - }, - }); + rulesClient.aggregate.mockResolvedValueOnce(aggregateResult); const [context, req, res] = mockHandlerArguments( { rulesClient }, @@ -221,22 +326,10 @@ describe('aggregateRulesRoute', () => { const router = httpServiceMock.createRouter(); aggregateRulesRoute(router, licenseState, mockUsageCounter); - const aggregateResult = { - alertExecutionStatus: { - ok: 15, - error: 2, - active: 23, - pending: 1, - unknown: 0, - }, - ruleLastRunOutcome: { - succeeded: 2, - failed: 4, - warning: 6, - }, - }; - rulesClient.aggregate.mockResolvedValueOnce(aggregateResult); const [, handler] = router.get.mock.calls[0]; + + rulesClient.aggregate.mockResolvedValueOnce(aggregateResult); + const [context, req, res] = mockHandlerArguments( { rulesClient }, { diff --git a/x-pack/plugins/alerting/server/routes/aggregate_rules.ts b/x-pack/plugins/alerting/server/routes/aggregate_rules.ts index b927464b67e38..ea3bb22bd0d17 100644 --- a/x-pack/plugins/alerting/server/routes/aggregate_rules.ts +++ b/x-pack/plugins/alerting/server/routes/aggregate_rules.ts @@ -8,8 +8,14 @@ import { IRouter } from '@kbn/core/server'; import { schema } from '@kbn/config-schema'; import { UsageCounter } from '@kbn/usage-collection-plugin/server'; +import { + AggregateOptions, + DefaultRuleAggregationResult, + formatDefaultAggregationResult, + getDefaultRuleAggregation, + RuleAggregationFormattedResult, +} from '../../common'; import { ILicenseState } from '../lib'; -import { AggregateResult, AggregateOptions } from '../rules_client'; import { RewriteResponseCase, RewriteRequestCase, verifyAccessAndContext } from './lib'; import { AlertingRequestHandlerContext, INTERNAL_BASE_ALERTING_API_PATH } from '../types'; import { trackLegacyTerminology } from './lib/track_legacy_terminology'; @@ -45,8 +51,8 @@ const rewriteQueryReq: RewriteRequestCase = ({ ...(hasReference ? { hasReference } : {}), ...(searchFields ? { searchFields } : {}), }); -const rewriteBodyRes: RewriteResponseCase = ({ - alertExecutionStatus, +const rewriteBodyRes: RewriteResponseCase = ({ + ruleExecutionStatus, ruleLastRunOutcome, ruleEnabledStatus, ruleMutedStatus, @@ -55,7 +61,7 @@ const rewriteBodyRes: RewriteResponseCase = ({ ...rest }) => ({ ...rest, - rule_execution_status: alertExecutionStatus, + rule_execution_status: ruleExecutionStatus, rule_last_run_outcome: ruleLastRunOutcome, rule_enabled_status: ruleEnabledStatus, rule_muted_status: ruleMutedStatus, @@ -86,9 +92,12 @@ export const aggregateRulesRoute = ( [req.query.search, req.query.search_fields].filter(Boolean) as string[], usageCounter ); - const aggregateResult = await rulesClient.aggregate({ options }); + const aggregateResult = await rulesClient.aggregate({ + aggs: getDefaultRuleAggregation(), + options, + }); return res.ok({ - body: rewriteBodyRes(aggregateResult), + body: rewriteBodyRes(formatDefaultAggregationResult(aggregateResult)), }); }) ) @@ -111,9 +120,12 @@ export const aggregateRulesRoute = ( [req.body.search, req.body.search_fields].filter(Boolean) as string[], usageCounter ); - const aggregateResult = await rulesClient.aggregate({ options }); + const aggregateResult = await rulesClient.aggregate({ + aggs: getDefaultRuleAggregation(), + options, + }); return res.ok({ - body: rewriteBodyRes(aggregateResult), + body: rewriteBodyRes(formatDefaultAggregationResult(aggregateResult)), }); }) ) diff --git a/x-pack/plugins/alerting/server/routes/get_rule_tags.test.ts b/x-pack/plugins/alerting/server/routes/get_rule_tags.test.ts new file mode 100644 index 0000000000000..746501aa83b58 --- /dev/null +++ b/x-pack/plugins/alerting/server/routes/get_rule_tags.test.ts @@ -0,0 +1,170 @@ +/* + * 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 { httpServiceMock } from '@kbn/core/server/mocks'; +import { licenseStateMock } from '../lib/license_state.mock'; +import { verifyApiAccess } from '../lib/license_api_access'; +import { mockHandlerArguments } from './_mock_handler_arguments'; +import { rulesClientMock } from '../rules_client.mock'; +import { getRuleTagsRoute } from './get_rule_tags'; + +import {} from '../../common/rule_tags_aggregation'; + +const rulesClient = rulesClientMock.create(); + +jest.mock('../lib/license_api_access', () => ({ + verifyApiAccess: jest.fn(), +})); + +jest.mock('../../common/rule_tags_aggregation', () => ({ + ...jest.requireActual('../../common/rule_tags_aggregation'), + formatRuleTagsAggregationResult: jest.fn(), +})); + +beforeEach(() => { + jest.resetAllMocks(); +}); + +const { formatRuleTagsAggregationResult } = jest.requireMock('../../common/rule_tags_aggregation'); + +describe('getRuleTagsRoute', () => { + it('aggregates rule tags with proper parameters', async () => { + const licenseState = licenseStateMock.create(); + const router = httpServiceMock.createRouter(); + + getRuleTagsRoute(router, licenseState); + + const [config, handler] = router.get.mock.calls[0]; + + expect(config.path).toMatchInlineSnapshot(`"/internal/alerting/rules/_tags"`); + + const aggregateResult = { ruleTags: ['a', 'b', 'c'] }; + + formatRuleTagsAggregationResult.mockReturnValueOnce(aggregateResult); + + const [context, req, res] = mockHandlerArguments( + { rulesClient }, + { + query: { + filter: 'test', + search: 'search text', + after: { + tags: 'c', + }, + }, + }, + ['ok'] + ); + + expect(await handler(context, req, res)).toMatchInlineSnapshot(` + Object { + "body": Object { + "rule_tags": Array [ + "a", + "b", + "c", + ], + }, + } + `); + expect(rulesClient.aggregate).toHaveBeenCalledTimes(1); + expect(rulesClient.aggregate.mock.calls[0]).toMatchInlineSnapshot(` + Array [ + Object { + "aggs": Object { + "tags": Object { + "composite": Object { + "after": Object { + "tags": "c", + }, + "size": 50, + "sources": Array [ + Object { + "tags": Object { + "terms": Object { + "field": "alert.attributes.tags", + "order": "asc", + }, + }, + }, + ], + }, + }, + }, + "options": Object { + "after": Object { + "tags": "c", + }, + "defaultSearchOperator": "AND", + "filter": "test", + "search": "search text", + "searchFields": Array [ + "tags", + ], + }, + }, + ] + `); + expect(res.ok).toHaveBeenCalledWith({ + body: { + rule_tags: ['a', 'b', 'c'], + }, + }); + }); + + it('ensures the license allows aggregating rule tags', async () => { + const licenseState = licenseStateMock.create(); + const router = httpServiceMock.createRouter(); + + getRuleTagsRoute(router, licenseState); + + const [, handler] = router.get.mock.calls[0]; + + formatRuleTagsAggregationResult.mockReturnValueOnce({ ruleTags: ['a', 'b', 'c', 'd'] }); + + const [context, req, res] = mockHandlerArguments( + { rulesClient }, + { + query: { + filter: 'test', + search: 'search text', + after: { + tags: 'c', + }, + }, + } + ); + + await handler(context, req, res); + + expect(verifyApiAccess).toHaveBeenCalledWith(licenseState); + }); + + it('ensures the license check prevents aggregating rule tags', async () => { + const licenseState = licenseStateMock.create(); + const router = httpServiceMock.createRouter(); + + (verifyApiAccess as jest.Mock).mockImplementation(() => { + throw new Error('OMG'); + }); + + getRuleTagsRoute(router, licenseState); + + const [, handler] = router.get.mock.calls[0]; + + const [context, req, res] = mockHandlerArguments( + {}, + { + query: {}, + }, + ['ok'] + ); + expect(handler(context, req, res)).rejects.toMatchInlineSnapshot(`[Error: OMG]`); + + expect(verifyApiAccess).toHaveBeenCalledWith(licenseState); + }); +}); diff --git a/x-pack/plugins/alerting/server/routes/get_rule_tags.ts b/x-pack/plugins/alerting/server/routes/get_rule_tags.ts new file mode 100644 index 0000000000000..f952247fa2fc4 --- /dev/null +++ b/x-pack/plugins/alerting/server/routes/get_rule_tags.ts @@ -0,0 +1,83 @@ +/* + * 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 { IRouter } from '@kbn/core/server'; +import { schema } from '@kbn/config-schema'; +import { + RuleTagsAggregationResult, + RuleTagsAggregationFormattedResult, + RuleTagsAggregationOptions, + getRuleTagsAggregation, + formatRuleTagsAggregationResult, +} from '../../common'; +import { AlertingRequestHandlerContext, INTERNAL_BASE_ALERTING_API_PATH } from '../types'; +import { ILicenseState } from '../lib'; +import { RewriteResponseCase, RewriteRequestCase, verifyAccessAndContext } from './lib'; + +const querySchema = schema.object({ + filter: schema.maybe(schema.string()), + search: schema.maybe(schema.string()), + after: schema.maybe( + schema.recordOf( + schema.string(), + schema.nullable(schema.oneOf([schema.string(), schema.number()])) + ) + ), + max_tags: schema.maybe(schema.number()), +}); + +const rewriteQueryReq: RewriteRequestCase = ({ + max_tags: maxTags, + ...rest +}) => ({ + ...rest, + ...(maxTags ? { maxTags } : {}), +}); + +const rewriteBodyRes: RewriteResponseCase = ({ + ruleTags, + ...rest +}) => ({ + ...rest, + rule_tags: ruleTags, +}); + +export const getRuleTagsRoute = ( + router: IRouter, + licenseState: ILicenseState +) => { + router.get( + { + path: `${INTERNAL_BASE_ALERTING_API_PATH}/rules/_tags`, + validate: { + query: querySchema, + }, + }, + router.handleLegacyErrors( + verifyAccessAndContext(licenseState, async function (context, req, res) { + const rulesClient = (await context.alerting).getRulesClient(); + const options = rewriteQueryReq(req.query); + + const aggregateResult = await rulesClient.aggregate({ + options: { + ...options, + defaultSearchOperator: 'AND', + searchFields: ['tags'], + }, + aggs: getRuleTagsAggregation({ + maxTags: options.maxTags, + after: options.after, + }), + }); + + return res.ok({ + body: rewriteBodyRes(formatRuleTagsAggregationResult(aggregateResult)), + }); + }) + ) + ); +}; diff --git a/x-pack/plugins/alerting/server/routes/index.ts b/x-pack/plugins/alerting/server/routes/index.ts index 32deff30edd7c..364159d66a25a 100644 --- a/x-pack/plugins/alerting/server/routes/index.ts +++ b/x-pack/plugins/alerting/server/routes/index.ts @@ -44,6 +44,7 @@ import { bulkDisableRulesRoute } from './bulk_disable_rules'; import { cloneRuleRoute } from './clone_rule'; import { getFlappingSettingsRoute } from './get_flapping_settings'; import { updateFlappingSettingsRoute } from './update_flapping_settings'; +import { getRuleTagsRoute } from './get_rule_tags'; export interface RouteOptions { router: IRouter; @@ -91,4 +92,5 @@ export function defineRoutes(opts: RouteOptions) { cloneRuleRoute(router, licenseState); getFlappingSettingsRoute(router, licenseState); updateFlappingSettingsRoute(router, licenseState); + getRuleTagsRoute(router, licenseState); } diff --git a/x-pack/plugins/alerting/server/routes/legacy/aggregate.test.ts b/x-pack/plugins/alerting/server/routes/legacy/aggregate.test.ts index 1fc3ee17ab51e..9ba0bbd86da3f 100644 --- a/x-pack/plugins/alerting/server/routes/legacy/aggregate.test.ts +++ b/x-pack/plugins/alerting/server/routes/legacy/aggregate.test.ts @@ -31,6 +31,13 @@ jest.mock('../lib/track_legacy_terminology', () => ({ trackLegacyTerminology: jest.fn(), })); +jest.mock('../../../common', () => ({ + ...jest.requireActual('../../../common'), + formatDefaultAggregationResult: jest.fn(), +})); + +const { formatDefaultAggregationResult } = jest.requireMock('../../../common'); + beforeEach(() => { jest.resetAllMocks(); }); @@ -47,7 +54,7 @@ describe('aggregateAlertRoute', () => { expect(config.path).toMatchInlineSnapshot(`"/api/alerts/_aggregate"`); const aggregateResult = { - alertExecutionStatus: { + ruleExecutionStatus: { ok: 15, error: 2, active: 23, @@ -60,7 +67,7 @@ describe('aggregateAlertRoute', () => { warning: 3, }, }; - rulesClient.aggregate.mockResolvedValueOnce(aggregateResult); + formatDefaultAggregationResult.mockReturnValueOnce(aggregateResult); const [context, req, res] = mockHandlerArguments( { rulesClient }, @@ -95,6 +102,51 @@ describe('aggregateAlertRoute', () => { expect(rulesClient.aggregate.mock.calls[0]).toMatchInlineSnapshot(` Array [ Object { + "aggs": Object { + "enabled": Object { + "terms": Object { + "field": "alert.attributes.enabled", + }, + }, + "muted": Object { + "terms": Object { + "field": "alert.attributes.muteAll", + }, + }, + "outcome": Object { + "terms": Object { + "field": "alert.attributes.lastRun.outcome", + }, + }, + "snoozed": Object { + "aggs": Object { + "count": Object { + "filter": Object { + "exists": Object { + "field": "alert.attributes.snoozeSchedule.duration", + }, + }, + }, + }, + "nested": Object { + "path": "alert.attributes.snoozeSchedule", + }, + }, + "status": Object { + "terms": Object { + "field": "alert.attributes.executionStatus.status", + }, + }, + "tags": Object { + "terms": Object { + "field": "alert.attributes.tags", + "order": Object { + "_key": "asc", + }, + "size": 50, + }, + }, + }, "options": Object { "defaultSearchOperator": "AND", }, @@ -103,7 +155,10 @@ describe('aggregateAlertRoute', () => { `); expect(res.ok).toHaveBeenCalledWith({ - body: aggregateResult, + body: { + ruleLastRunOutcome: aggregateResult.ruleLastRunOutcome, + alertExecutionStatus: aggregateResult.ruleExecutionStatus, + }, }); }); @@ -115,8 +170,8 @@ describe('aggregateAlertRoute', () => { const [, handler] = router.get.mock.calls[0]; - rulesClient.aggregate.mockResolvedValueOnce({ - alertExecutionStatus: { + formatDefaultAggregationResult.mockReturnValueOnce({ + ruleExecutionStatus: { ok: 15, error: 2, active: 23, @@ -173,6 +228,22 @@ describe('aggregateAlertRoute', () => { const router = httpServiceMock.createRouter(); aggregateAlertRoute(router, licenseState, mockUsageCounter); + + formatDefaultAggregationResult.mockReturnValueOnce({ + ruleExecutionStatus: { + ok: 15, + error: 2, + active: 23, + pending: 1, + unknown: 0, + }, + ruleLastRunOutcome: { + succeeded: 1, + failed: 2, + warning: 3, + }, + }); + const [, handler] = router.get.mock.calls[0]; const [context, req, res] = mockHandlerArguments( { rulesClient }, @@ -192,6 +263,22 @@ describe('aggregateAlertRoute', () => { const router = httpServiceMock.createRouter(); aggregateAlertRoute(router, licenseState, mockUsageCounter); + + formatDefaultAggregationResult.mockReturnValueOnce({ + ruleExecutionStatus: { + ok: 15, + error: 2, + active: 23, + pending: 1, + unknown: 0, + }, + ruleLastRunOutcome: { + succeeded: 1, + failed: 2, + warning: 3, + }, + }); + const [, handler] = router.get.mock.calls[0]; const [context, req, res] = mockHandlerArguments( { rulesClient }, diff --git a/x-pack/plugins/alerting/server/routes/legacy/aggregate.ts b/x-pack/plugins/alerting/server/routes/legacy/aggregate.ts index ba520cc401262..44559830a24e9 100644 --- a/x-pack/plugins/alerting/server/routes/legacy/aggregate.ts +++ b/x-pack/plugins/alerting/server/routes/legacy/aggregate.ts @@ -10,7 +10,12 @@ import { UsageCounter } from '@kbn/usage-collection-plugin/server'; import type { AlertingRouter } from '../../types'; import { ILicenseState } from '../../lib/license_state'; import { verifyApiAccess } from '../../lib/license_api_access'; -import { LEGACY_BASE_ALERT_API_PATH } from '../../../common'; +import { + LEGACY_BASE_ALERT_API_PATH, + DefaultRuleAggregationResult, + getDefaultRuleAggregation, + formatDefaultAggregationResult, +} from '../../../common'; import { renameKeys } from '../lib/rename_keys'; import { FindOptions } from '../../rules_client'; import { trackLegacyRouteUsage } from '../../lib/track_legacy_route_usage'; @@ -77,9 +82,16 @@ export const aggregateAlertRoute = ( : [query.search_fields]; } - const aggregateResult = await rulesClient.aggregate({ options }); + const aggregateResult = await rulesClient.aggregate({ + options, + aggs: getDefaultRuleAggregation(), + }); + const { ruleExecutionStatus, ...rest } = formatDefaultAggregationResult(aggregateResult); return res.ok({ - body: aggregateResult, + body: { + ...rest, + alertExecutionStatus: ruleExecutionStatus, + }, }); }) ); diff --git a/x-pack/plugins/alerting/server/routes/lib/rewrite_request_case.ts b/x-pack/plugins/alerting/server/routes/lib/rewrite_request_case.ts index a95d78915e9ae..bd11ab9771ad2 100644 --- a/x-pack/plugins/alerting/server/routes/lib/rewrite_request_case.ts +++ b/x-pack/plugins/alerting/server/routes/lib/rewrite_request_case.ts @@ -10,8 +10,6 @@ type RenameAlertToRule = K extends `alertTypeId` ? `ruleTypeId` : K extends `alertId` ? `ruleId` - : K extends `alertExecutionStatus` - ? `ruleExecutionStatus` : K extends `actionTypeId` ? `connectorTypeId` : K extends `alertInstanceId` diff --git a/x-pack/plugins/alerting/server/rule_type_registry.test.ts b/x-pack/plugins/alerting/server/rule_type_registry.test.ts index 7ce33e2649d71..b6f6d6237fd63 100644 --- a/x-pack/plugins/alerting/server/rule_type_registry.test.ts +++ b/x-pack/plugins/alerting/server/rule_type_registry.test.ts @@ -472,13 +472,13 @@ describe('Create Lifecycle', () => { producer: 'alerts', alerts: { context: 'test', - fieldMap: { field: { type: 'keyword', required: false } }, + mappings: { fieldMap: { field: { type: 'keyword', required: false } } }, }, }); expect(alertsService.register).toHaveBeenCalledWith({ context: 'test', - fieldMap: { field: { type: 'keyword', required: false } }, + mappings: { fieldMap: { field: { type: 'keyword', required: false } } }, }); }); diff --git a/x-pack/plugins/alerting/server/rules_client.mock.ts b/x-pack/plugins/alerting/server/rules_client.mock.ts index 585d3b1c6aa05..29d0a8ba9c1e2 100644 --- a/x-pack/plugins/alerting/server/rules_client.mock.ts +++ b/x-pack/plugins/alerting/server/rules_client.mock.ts @@ -12,7 +12,7 @@ export type RulesClientMock = jest.Mocked; const createRulesClientMock = () => { const mocked: RulesClientMock = { - aggregate: jest.fn().mockReturnValue({ alertExecutionStatus: {}, ruleLastRunOutcome: {} }), + aggregate: jest.fn().mockReturnValue({ ruleExecutionStatus: {}, ruleLastRunOutcome: {} }), create: jest.fn(), get: jest.fn(), resolve: jest.fn(), diff --git a/x-pack/plugins/alerting/server/rules_client/lib/validate_rule_aggregation_fields.test.ts b/x-pack/plugins/alerting/server/rules_client/lib/validate_rule_aggregation_fields.test.ts new file mode 100644 index 0000000000000..599938de4b1fc --- /dev/null +++ b/x-pack/plugins/alerting/server/rules_client/lib/validate_rule_aggregation_fields.test.ts @@ -0,0 +1,304 @@ +/* + * 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 { getRuleTagsAggregation, getDefaultRuleAggregation } from '../../../common'; +import type { AggregationsAggregateOrder } from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; +import { validateRuleAggregationFields } from './validate_rule_aggregation_fields'; + +describe('validateAggregationTerms', () => { + it('should allow for simple valid aggregations', () => { + expect(() => { + validateRuleAggregationFields({ + name1: { + terms: { + field: 'alert.attributes.lastRun.outcome', + }, + }, + name2: { + terms: { + field: 'alert.attributes.tags', + }, + }, + }); + }).not.toThrowError(); + }); + + it('should allow for nested valid aggregations', () => { + expect(() => { + validateRuleAggregationFields({ + name1: { + terms: { + field: 'alert.attributes.executionStatus.status', + }, + aggs: { + nestedAggs: { + terms: { + field: 'alert.attributes.snoozeSchedule', + }, + aggs: { + anotherNestedAgg: { + terms: { + field: 'alert.attributes.alertTypeId', + }, + }, + }, + }, + }, + }, + }); + }).not.toThrowError(); + }); + + it('should allow for nested valid aggregations with root level aggs', () => { + expect(() => { + validateRuleAggregationFields({ + name1: { + composite: { + sources: [ + { + tags: { + terms: { + field: 'alert.attributes.tags', + order: 'asc' as unknown as AggregationsAggregateOrder, + }, + }, + }, + ], + }, + terms: { + field: 'alert.attributes.muteAll', + }, + aggs: { + nestedAggs: { + nested: { + path: 'alert.attributes.snoozeSchedule', + }, + terms: { + field: 'alert.attributes.enabled', + }, + aggs: { + nestedAggsAgain: { + terms: { + field: 'alert.attributes.executionStatus.status', + }, + }, + }, + }, + }, + }, + }); + }).not.toThrowError(); + }); + + it('should allow for default and tags aggregations', () => { + expect(() => validateRuleAggregationFields(getDefaultRuleAggregation())).not.toThrowError(); + expect(() => validateRuleAggregationFields(getRuleTagsAggregation())).not.toThrowError(); + }); + + it('should throw for simple aggregation with invalid fields', () => { + expect(() => { + validateRuleAggregationFields({ + name1: { + terms: { + field: 'alert.attributes.apiKey', + }, + }, + name2: { + terms: { + field: 'foo.attributes.bar1', + }, + }, + }); + }).toThrowErrorMatchingInlineSnapshot(`"Invalid aggregation term: alert.attributes.apiKey"`); + + expect(() => { + validateRuleAggregationFields({ + name1: { + terms: { + field: 'alert.attributes.bar', + }, + }, + name2: { + terms: { + field: 'alert.attributes.consumer', + }, + }, + }); + }).toThrowErrorMatchingInlineSnapshot(`"Invalid aggregation term: alert.attributes.bar"`); + }); + + it('should throw for nested aggregations with invalid fields', () => { + expect(() => { + validateRuleAggregationFields({ + name1: { + terms: { + field: 'alert.attributes.apiKey', + }, + aggs: { + nestedAggs: { + terms: { + field: 'foo.attributes.bar1', + }, + aggs: { + anotherNestedAgg: { + terms: { + field: 'foo.attributes.bar2', + }, + }, + }, + }, + }, + }, + }); + }).toThrowErrorMatchingInlineSnapshot(`"Invalid aggregation term: alert.attributes.apiKey"`); + + expect(() => { + validateRuleAggregationFields({ + name1: { + terms: { + field: 'alert.attributes.snoozeSchedule', + }, + aggs: { + nestedAggs: { + terms: { + field: 'alert.attributes.enabled', + }, + aggs: { + anotherNestedAgg: { + terms: { + field: 'alert.attributes.consumer', + }, + }, + }, + }, + }, + }, + }); + }).toThrowErrorMatchingInlineSnapshot(`"Invalid aggregation term: alert.attributes.consumer"`); + }); + + it('should throw for both aggs and aggregations at the same nesting level with invalid fields', () => { + expect(() => { + validateRuleAggregationFields({ + name1: { + terms: { + field: 'alert.attributes.snoozeSchedule', + }, + aggs: { + nestedAggs1: { + terms: { + field: 'alert.attributes.enabled', + }, + }, + }, + aggregations: { + nestedAggs2: { + terms: { + field: 'alert.attributes.consumer', + }, + }, + }, + }, + }); + }).toThrowErrorMatchingInlineSnapshot(`"Invalid aggregation term: alert.attributes.consumer"`); + + expect(() => { + validateRuleAggregationFields({ + name1: { + terms: { + field: 'alert.attributes.snoozeSchedule', + }, + aggs: { + nestedAggs1: { + terms: { + field: 'alert.attributes.consumer', + }, + }, + }, + aggregations: { + nestedAggs2: { + terms: { + field: 'alert.attributes.enabled', + }, + }, + }, + }, + }); + }).toThrowErrorMatchingInlineSnapshot(`"Invalid aggregation term: alert.attributes.consumer"`); + }); + + it('should throw for nested aggregations with invalid root level aggs types', () => { + expect(() => { + validateRuleAggregationFields({ + name1: { + cardinality: { + field: 'alert.attributes.muteAll', + }, + }, + }); + }).toThrowErrorMatchingInlineSnapshot(`"Invalid aggregation type: cardinality"`); + + expect(() => { + validateRuleAggregationFields({ + name1: { + aggs: { + nestedAggs: { + terms: { + field: 'alert.attributes.executionStatus.status', + }, + avg: { + field: 'alert.attributes.executionStatus.status', + }, + }, + }, + max: { + field: 'alert.attributes.executionStatus.status', + }, + cardinality: { + field: 'alert.attributes.executionStatus.status', + }, + }, + }); + }).toThrowErrorMatchingInlineSnapshot(`"Invalid aggregation type: max"`); + }); + + it('should throw for invalid multi_terms aggregations', () => { + expect(() => { + validateRuleAggregationFields({ + name1: { + multi_terms: { + terms: [{ field: 'foo.attributes.bar' }, { field: 'alert.attributes.apiKey' }], + }, + aggs: { + nestedAggs: { + multi_terms: { + terms: [{ field: 'foo.attributes.bar2' }, { field: 'foo.attributes.bar3' }], + }, + }, + }, + }, + }); + }).toThrowErrorMatchingInlineSnapshot(`"Invalid aggregation type: multi_terms"`); + + expect(() => { + validateRuleAggregationFields({ + name1: { + multi_terms: { + terms: [{ field: 'foo.attributes.bar' }, { field: 'foo.attributes.bar1' }], + }, + aggs: { + nestedAggs: { + multi_terms: { + terms: [{ field: 'alert.attributes.consumer' }, { field: 'foo.attributes.bar3' }], + }, + }, + }, + }, + }); + }).toThrowErrorMatchingInlineSnapshot(`"Invalid aggregation type: multi_terms"`); + }); +}); diff --git a/x-pack/plugins/alerting/server/rules_client/lib/validate_rule_aggregation_fields.ts b/x-pack/plugins/alerting/server/rules_client/lib/validate_rule_aggregation_fields.ts new file mode 100644 index 0000000000000..14fd204ddc009 --- /dev/null +++ b/x-pack/plugins/alerting/server/rules_client/lib/validate_rule_aggregation_fields.ts @@ -0,0 +1,73 @@ +/* + * 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 Boom from '@hapi/boom'; +import type { AggregationsAggregationContainer } from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; + +const ALLOW_FIELDS = [ + 'alert.attributes.executionStatus.status', + 'alert.attributes.lastRun.outcome', + 'alert.attributes.muteAll', + 'alert.attributes.tags', + 'alert.attributes.snoozeSchedule', + 'alert.attributes.snoozeSchedule.duration', + 'alert.attributes.alertTypeId', + 'alert.attributes.enabled', + 'alert.attributes.params.*', +]; + +const ALLOW_AGG_TYPES = ['terms', 'composite', 'nested', 'filter']; + +const AGG_TYPES_TO_VERIFY = ['field', 'path']; + +const AGG_KEYS = ['aggs', 'aggregations']; + +export const validateRuleAggregationFields = ( + aggs: Record +) => { + Object.values(aggs).forEach((aggContainer) => { + // validate root level aggregation types (non aggs/aggregations) + validateTypes(aggContainer); + + // Recursively go through aggs to validate terms + if (aggContainer.aggs) { + validateRuleAggregationFields(aggContainer.aggs); + } + if (aggContainer.aggregations) { + validateRuleAggregationFields(aggContainer.aggregations); + } + }); +}; + +const validateTypes = (container: AggregationsAggregationContainer) => { + Object.entries(container).forEach(([aggType, aggContainer]) => { + // Do not try to validate aggs/aggregations, as the above function is already doing that + if (AGG_KEYS.includes(aggType)) { + return; + } + + if (!ALLOW_AGG_TYPES.includes(aggType)) { + throw Boom.badRequest(`Invalid aggregation type: ${aggType}`); + } + + validateFields(aggContainer); + }); +}; + +const validateFields = (container: AggregationsAggregationContainer) => { + Object.entries(container).forEach(([aggType, aggContainer]) => { + // Found field, check field against blocklist + if (AGG_TYPES_TO_VERIFY.includes(aggType) && !ALLOW_FIELDS.includes(aggContainer)) { + throw Boom.badRequest(`Invalid aggregation term: ${aggContainer}`); + } + + // Did not find anything, keep recursing if possible + if (typeof aggContainer === 'object' && aggContainer !== null && !Array.isArray(aggContainer)) { + validateFields(aggContainer); + } + }); +}; diff --git a/x-pack/plugins/alerting/server/rules_client/methods/aggregate.ts b/x-pack/plugins/alerting/server/rules_client/methods/aggregate.ts index 2c9b991618aa1..6dd26c1a7e197 100644 --- a/x-pack/plugins/alerting/server/rules_client/methods/aggregate.ts +++ b/x-pack/plugins/alerting/server/rules_client/methods/aggregate.ts @@ -6,90 +6,34 @@ */ import { KueryNode, nodeBuilder } from '@kbn/es-query'; -import { RawRule, RuleExecutionStatusValues, RuleLastRunOutcomeValues } from '../../types'; +import type { AggregationsAggregationContainer } from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; import { AlertingAuthorizationEntity } from '../../authorization'; import { ruleAuditEvent, RuleAuditAction } from '../common/audit_events'; import { buildKueryNodeFilter } from '../common'; import { alertingAuthorizationFilterOpts } from '../common/constants'; import { RulesClientContext } from '../types'; +import { RawRule, AggregateOptions } from '../../types'; +import { validateRuleAggregationFields } from '../lib/validate_rule_aggregation_fields'; -export interface AggregateOptions extends IndexType { - search?: string; - defaultSearchOperator?: 'AND' | 'OR'; - searchFields?: string[]; - hasReference?: { - type: string; - id: string; - }; - filter?: string | KueryNode; - maxTags?: number; +export interface AggregateParams { + options?: AggregateOptions; + aggs: Record; } -interface IndexType { - [key: string]: unknown; -} - -export interface AggregateResult { - alertExecutionStatus: { [status: string]: number }; - ruleLastRunOutcome: { [status: string]: number }; - ruleEnabledStatus?: { enabled: number; disabled: number }; - ruleMutedStatus?: { muted: number; unmuted: number }; - ruleSnoozedStatus?: { snoozed: number }; - ruleTags?: string[]; -} - -export interface RuleAggregation { - status: { - buckets: Array<{ - key: string; - doc_count: number; - }>; - }; - outcome: { - buckets: Array<{ - key: string; - doc_count: number; - }>; - }; - muted: { - buckets: Array<{ - key: number; - key_as_string: string; - doc_count: number; - }>; - }; - enabled: { - buckets: Array<{ - key: number; - key_as_string: string; - doc_count: number; - }>; - }; - snoozed: { - count: { - doc_count: number; - }; - }; - tags: { - buckets: Array<{ - key: string; - doc_count: number; - }>; - }; -} - -export async function aggregate( +export async function aggregate>( context: RulesClientContext, - { - options: { fields, filter, maxTags = 50, ...options } = {}, - }: { options?: AggregateOptions } = {} -): Promise { + params: AggregateParams +): Promise { + const { options = {}, aggs } = params; + const { filter, page = 1, perPage = 0, ...restOptions } = options; + let authorizationTuple; try { authorizationTuple = await context.authorization.getFindAuthorizationFilter( AlertingAuthorizationEntity.Rule, alertingAuthorizationFilterOpts ); + validateRuleAggregationFields(aggs); } catch (error) { context.auditLogger?.log( ruleAuditEvent({ @@ -103,124 +47,18 @@ export async function aggregate( const { filter: authorizationFilter } = authorizationTuple; const filterKueryNode = buildKueryNodeFilter(filter); - const resp = await context.unsecuredSavedObjectsClient.find({ - ...options, + const result = await context.unsecuredSavedObjectsClient.find({ + ...restOptions, filter: authorizationFilter && filterKueryNode ? nodeBuilder.and([filterKueryNode, authorizationFilter as KueryNode]) : authorizationFilter, - page: 1, - perPage: 0, + page, + perPage, type: 'alert', - aggs: { - status: { - terms: { field: 'alert.attributes.executionStatus.status' }, - }, - outcome: { - terms: { field: 'alert.attributes.lastRun.outcome' }, - }, - enabled: { - terms: { field: 'alert.attributes.enabled' }, - }, - muted: { - terms: { field: 'alert.attributes.muteAll' }, - }, - tags: { - terms: { field: 'alert.attributes.tags', order: { _key: 'asc' }, size: maxTags }, - }, - snoozed: { - nested: { - path: 'alert.attributes.snoozeSchedule', - }, - aggs: { - count: { - filter: { - exists: { - field: 'alert.attributes.snoozeSchedule.duration', - }, - }, - }, - }, - }, - }, + aggs, }); - if (!resp.aggregations) { - // Return a placeholder with all zeroes - const placeholder: AggregateResult = { - alertExecutionStatus: {}, - ruleLastRunOutcome: {}, - ruleEnabledStatus: { - enabled: 0, - disabled: 0, - }, - ruleMutedStatus: { - muted: 0, - unmuted: 0, - }, - ruleSnoozedStatus: { snoozed: 0 }, - }; - - for (const key of RuleExecutionStatusValues) { - placeholder.alertExecutionStatus[key] = 0; - } - - return placeholder; - } - - const alertExecutionStatus = resp.aggregations.status.buckets.map( - ({ key, doc_count: docCount }) => ({ - [key]: docCount, - }) - ); - - const ruleLastRunOutcome = resp.aggregations.outcome.buckets.map( - ({ key, doc_count: docCount }) => ({ - [key]: docCount, - }) - ); - - const ret: AggregateResult = { - alertExecutionStatus: alertExecutionStatus.reduce( - (acc, curr: { [status: string]: number }) => Object.assign(acc, curr), - {} - ), - ruleLastRunOutcome: ruleLastRunOutcome.reduce( - (acc, curr: { [status: string]: number }) => Object.assign(acc, curr), - {} - ), - }; - - // Fill missing keys with zeroes - for (const key of RuleExecutionStatusValues) { - if (!ret.alertExecutionStatus.hasOwnProperty(key)) { - ret.alertExecutionStatus[key] = 0; - } - } - for (const key of RuleLastRunOutcomeValues) { - if (!ret.ruleLastRunOutcome.hasOwnProperty(key)) { - ret.ruleLastRunOutcome[key] = 0; - } - } - - const enabledBuckets = resp.aggregations.enabled.buckets; - ret.ruleEnabledStatus = { - enabled: enabledBuckets.find((bucket) => bucket.key === 1)?.doc_count ?? 0, - disabled: enabledBuckets.find((bucket) => bucket.key === 0)?.doc_count ?? 0, - }; - - const mutedBuckets = resp.aggregations.muted.buckets; - ret.ruleMutedStatus = { - muted: mutedBuckets.find((bucket) => bucket.key === 1)?.doc_count ?? 0, - unmuted: mutedBuckets.find((bucket) => bucket.key === 0)?.doc_count ?? 0, - }; - - ret.ruleSnoozedStatus = { - snoozed: resp.aggregations.snoozed?.count?.doc_count ?? 0, - }; - - const tagsBuckets = resp.aggregations.tags?.buckets || []; - ret.ruleTags = tagsBuckets.map((bucket) => bucket.key); - - return ret; + // params. + return result.aggregations!; } diff --git a/x-pack/plugins/alerting/server/rules_client/rules_client.ts b/x-pack/plugins/alerting/server/rules_client/rules_client.ts index 35cb052ac6381..75e563d2011da 100644 --- a/x-pack/plugins/alerting/server/rules_client/rules_client.ts +++ b/x-pack/plugins/alerting/server/rules_client/rules_client.ts @@ -33,7 +33,7 @@ import { GetRuleExecutionKPIParams, } from './methods/get_execution_kpi'; import { find, FindParams } from './methods/find'; -import { aggregate, AggregateOptions } from './methods/aggregate'; +import { aggregate, AggregateParams } from './methods/aggregate'; import { deleteRule } from './methods/delete'; import { update, UpdateOptions } from './methods/update'; import { bulkDeleteRules } from './methods/bulk_delete'; @@ -77,7 +77,8 @@ export class RulesClient { }; } - public aggregate = (params?: { options?: AggregateOptions }) => aggregate(this.context, params); + public aggregate = >(params: AggregateParams): Promise => + aggregate(this.context, params); public clone = (...args: CloneArguments) => clone(this.context, ...args); public create = (params: CreateOptions) => diff --git a/x-pack/plugins/alerting/server/rules_client/tests/aggregate.test.ts b/x-pack/plugins/alerting/server/rules_client/tests/aggregate.test.ts index 7a9e7db434818..3253d9d71ac03 100644 --- a/x-pack/plugins/alerting/server/rules_client/tests/aggregate.test.ts +++ b/x-pack/plugins/alerting/server/rules_client/tests/aggregate.test.ts @@ -16,7 +16,11 @@ import { AlertingAuthorization } from '../../authorization/alerting_authorizatio import { ActionsAuthorization } from '@kbn/actions-plugin/server'; import { auditLoggerMock } from '@kbn/security-plugin/server/audit/mocks'; import { getBeforeSetup, setGlobalDate } from './lib'; -import { RecoveredActionGroup } from '../../../common'; +import { + RecoveredActionGroup, + getDefaultRuleAggregation, + DefaultRuleAggregationResult, +} from '../../../common'; import { RegistryRuleType } from '../../rule_type_registry'; import { fromKueryExpression, nodeTypes } from '@kbn/es-query'; @@ -157,38 +161,107 @@ describe('aggregate()', () => { test('calls saved objects client with given params to perform aggregation', async () => { const rulesClient = new RulesClient(rulesClientParams); - const result = await rulesClient.aggregate({ options: {} }); + const result = await rulesClient.aggregate({ + options: {}, + aggs: getDefaultRuleAggregation(), + }); + expect(result).toMatchInlineSnapshot(` Object { - "alertExecutionStatus": Object { - "active": 8, - "error": 6, - "ok": 10, - "pending": 4, - "unknown": 2, - "warning": 1, + "enabled": Object { + "buckets": Array [ + Object { + "doc_count": 2, + "key": 0, + "key_as_string": "0", + }, + Object { + "doc_count": 28, + "key": 1, + "key_as_string": "1", + }, + ], }, - "ruleEnabledStatus": Object { - "disabled": 2, - "enabled": 28, + "muted": Object { + "buckets": Array [ + Object { + "doc_count": 27, + "key": 0, + "key_as_string": "0", + }, + Object { + "doc_count": 3, + "key": 1, + "key_as_string": "1", + }, + ], + }, + "outcome": Object { + "buckets": Array [ + Object { + "doc_count": 2, + "key": "succeeded", + }, + Object { + "doc_count": 4, + "key": "failed", + }, + Object { + "doc_count": 6, + "key": "warning", + }, + ], }, - "ruleLastRunOutcome": Object { - "failed": 4, - "succeeded": 2, - "warning": 6, + "snoozed": Object { + "count": Object { + "doc_count": 0, + }, + "doc_count": 0, }, - "ruleMutedStatus": Object { - "muted": 3, - "unmuted": 27, + "status": Object { + "buckets": Array [ + Object { + "doc_count": 8, + "key": "active", + }, + Object { + "doc_count": 6, + "key": "error", + }, + Object { + "doc_count": 10, + "key": "ok", + }, + Object { + "doc_count": 4, + "key": "pending", + }, + Object { + "doc_count": 2, + "key": "unknown", + }, + Object { + "doc_count": 1, + "key": "warning", + }, + ], }, - "ruleSnoozedStatus": Object { - "snoozed": 0, + "tags": Object { + "buckets": Array [ + Object { + "doc_count": 10, + "key": "a", + }, + Object { + "doc_count": 20, + "key": "b", + }, + Object { + "doc_count": 30, + "key": "c", + }, + ], }, - "ruleTags": Array [ - "a", - "b", - "c", - ], } `); expect(unsecuredSavedObjectsClient.find).toHaveBeenCalledTimes(1); @@ -244,7 +317,10 @@ describe('aggregate()', () => { }); const rulesClient = new RulesClient(rulesClientParams); - await rulesClient.aggregate({ options: { filter: 'foo: someTerm' } }); + await rulesClient.aggregate({ + options: { filter: 'foo: someTerm' }, + aggs: getDefaultRuleAggregation(), + }); expect(unsecuredSavedObjectsClient.find).toHaveBeenCalledTimes(1); expect(unsecuredSavedObjectsClient.find.mock.calls[0]).toEqual([ @@ -296,7 +372,7 @@ describe('aggregate()', () => { const rulesClient = new RulesClient({ ...rulesClientParams, auditLogger }); authorization.getFindAuthorizationFilter.mockRejectedValue(new Error('Unauthorized')); - await expect(rulesClient.aggregate()).rejects.toThrow(); + await expect(rulesClient.aggregate({ aggs: getDefaultRuleAggregation() })).rejects.toThrow(); expect(auditLogger.log).toHaveBeenCalledWith( expect.objectContaining({ event: expect.objectContaining({ @@ -315,7 +391,7 @@ describe('aggregate()', () => { test('sets to default (50) if it is not provided', async () => { const rulesClient = new RulesClient(rulesClientParams); - await rulesClient.aggregate(); + await rulesClient.aggregate({ aggs: getDefaultRuleAggregation() }); expect(unsecuredSavedObjectsClient.find.mock.calls[0]).toMatchObject([ { @@ -331,7 +407,9 @@ describe('aggregate()', () => { test('sets to the provided value', async () => { const rulesClient = new RulesClient(rulesClientParams); - await rulesClient.aggregate({ options: { maxTags: 1000 } }); + await rulesClient.aggregate({ + aggs: getDefaultRuleAggregation({ maxTags: 1000 }), + }); expect(unsecuredSavedObjectsClient.find.mock.calls[0]).toMatchObject([ { diff --git a/x-pack/plugins/alerting/server/rules_client/types.ts b/x-pack/plugins/alerting/server/rules_client/types.ts index c6243d6fad5e2..ed745328efd7f 100644 --- a/x-pack/plugins/alerting/server/rules_client/types.ts +++ b/x-pack/plugins/alerting/server/rules_client/types.ts @@ -37,7 +37,6 @@ export type { export type { CreateOptions } from './methods/create'; export type { FindOptions, FindResult } from './methods/find'; export type { UpdateOptions } from './methods/update'; -export type { AggregateOptions, AggregateResult } from './methods/aggregate'; export type { GetAlertSummaryParams } from './methods/get_alert_summary'; export type { GetExecutionLogByIdParams, diff --git a/x-pack/plugins/alerting/server/saved_objects/migrations/8.7/index.ts b/x-pack/plugins/alerting/server/saved_objects/migrations/8.7/index.ts index 831e4eff10d43..1b49f606cb6b5 100644 --- a/x-pack/plugins/alerting/server/saved_objects/migrations/8.7/index.ts +++ b/x-pack/plugins/alerting/server/saved_objects/migrations/8.7/index.ts @@ -7,7 +7,6 @@ import { SavedObjectUnsanitizedDoc } from '@kbn/core-saved-objects-server'; import { EncryptedSavedObjectsPluginSetup } from '@kbn/encrypted-saved-objects-plugin/server'; -import { v4 as uuidv4 } from 'uuid'; import { extractedSavedObjectParamReferenceNamePrefix } from '../../../rules_client/common/constants'; import { createEsoMigration, @@ -38,27 +37,6 @@ function addGroupByToEsQueryRule( return doc; } -function addActionUuid( - doc: SavedObjectUnsanitizedDoc -): SavedObjectUnsanitizedDoc { - const { - attributes: { actions }, - } = doc; - - return { - ...doc, - attributes: { - ...doc.attributes, - actions: actions - ? actions.map((action) => ({ - ...action, - uuid: uuidv4(), - })) - : [], - }, - }; -} - function addLogViewRefToLogThresholdRule( doc: SavedObjectUnsanitizedDoc ): SavedObjectUnsanitizedDoc { @@ -116,10 +94,5 @@ export const getMigrations870 = (encryptedSavedObjects: EncryptedSavedObjectsPlu createEsoMigration( encryptedSavedObjects, (doc: SavedObjectUnsanitizedDoc): doc is SavedObjectUnsanitizedDoc => true, - pipeMigrations( - addGroupByToEsQueryRule, - addLogViewRefToLogThresholdRule, - addOutcomeOrder, - addActionUuid - ) + pipeMigrations(addGroupByToEsQueryRule, addLogViewRefToLogThresholdRule, addOutcomeOrder) ); diff --git a/x-pack/plugins/alerting/server/saved_objects/migrations/8.8/index.ts b/x-pack/plugins/alerting/server/saved_objects/migrations/8.8/index.ts new file mode 100644 index 0000000000000..48c58fad0c2c6 --- /dev/null +++ b/x-pack/plugins/alerting/server/saved_objects/migrations/8.8/index.ts @@ -0,0 +1,40 @@ +/* + * 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 { SavedObjectUnsanitizedDoc } from '@kbn/core-saved-objects-server'; +import { EncryptedSavedObjectsPluginSetup } from '@kbn/encrypted-saved-objects-plugin/server'; +import { v4 as uuidv4 } from 'uuid'; +import { createEsoMigration, pipeMigrations } from '../utils'; +import { RawRule } from '../../../types'; + +function addActionUuid( + doc: SavedObjectUnsanitizedDoc +): SavedObjectUnsanitizedDoc { + const { + attributes: { actions }, + } = doc; + + return { + ...doc, + attributes: { + ...doc.attributes, + actions: actions + ? actions.map((action) => ({ + ...action, + uuid: uuidv4(), + })) + : [], + }, + }; +} + +export const getMigrations880 = (encryptedSavedObjects: EncryptedSavedObjectsPluginSetup) => + createEsoMigration( + encryptedSavedObjects, + (doc: SavedObjectUnsanitizedDoc): doc is SavedObjectUnsanitizedDoc => true, + pipeMigrations(addActionUuid) + ); diff --git a/x-pack/plugins/alerting/server/saved_objects/migrations/index.test.ts b/x-pack/plugins/alerting/server/saved_objects/migrations/index.test.ts index 940b926f5e326..de03eec71d4e1 100644 --- a/x-pack/plugins/alerting/server/saved_objects/migrations/index.test.ts +++ b/x-pack/plugins/alerting/server/saved_objects/migrations/index.test.ts @@ -2628,9 +2628,11 @@ describe('successful migrations', () => { outcomeOrder: 0, }); }); + }); + describe('8.8.0', () => { test('adds uuid to rule actions', () => { - const migration870 = getMigrations(encryptedSavedObjectsSetup, {}, isPreconfigured)['8.7.0']; + const migration880 = getMigrations(encryptedSavedObjectsSetup, {}, isPreconfigured)['8.8.0']; const rule = getMockData( { params: { foo: true }, @@ -2638,9 +2640,9 @@ describe('successful migrations', () => { }, true ); - const migratedAlert870 = migration870(rule, migrationContext); + const migratedAlert880 = migration880(rule, migrationContext); - expect(migratedAlert870.attributes.actions).toEqual([ + expect(migratedAlert880.attributes.actions).toEqual([ { group: 'default', actionRef: '1', diff --git a/x-pack/plugins/alerting/server/saved_objects/migrations/index.ts b/x-pack/plugins/alerting/server/saved_objects/migrations/index.ts index 5a88af4062b19..b94fb907d8275 100644 --- a/x-pack/plugins/alerting/server/saved_objects/migrations/index.ts +++ b/x-pack/plugins/alerting/server/saved_objects/migrations/index.ts @@ -30,6 +30,7 @@ import { getMigrations841 } from './8.4'; import { getMigrations850 } from './8.5'; import { getMigrations860 } from './8.6'; import { getMigrations870 } from './8.7'; +import { getMigrations880 } from './8.8'; import { AlertLogMeta, AlertMigration } from './types'; import { MINIMUM_SS_MIGRATION_VERSION } from './constants'; import { createEsoMigration, isEsQueryRuleType, pipeMigrations } from './utils'; @@ -79,6 +80,7 @@ export function getMigrations( '8.5.0': executeMigrationWithErrorHandling(getMigrations850(encryptedSavedObjects), '8.5.0'), '8.6.0': executeMigrationWithErrorHandling(getMigrations860(encryptedSavedObjects), '8.6.0'), '8.7.0': executeMigrationWithErrorHandling(getMigrations870(encryptedSavedObjects), '8.7.0'), + '8.8.0': executeMigrationWithErrorHandling(getMigrations880(encryptedSavedObjects), '8.8.0'), }, getSearchSourceMigrations(encryptedSavedObjects, searchSourceMigrations) ); diff --git a/x-pack/plugins/alerting/server/task_runner/execution_handler.test.ts b/x-pack/plugins/alerting/server/task_runner/execution_handler.test.ts index cc1741fe722fd..0cefafab3a225 100644 --- a/x-pack/plugins/alerting/server/task_runner/execution_handler.test.ts +++ b/x-pack/plugins/alerting/server/task_runner/execution_handler.test.ts @@ -92,6 +92,7 @@ const rule = { stateVal: 'My {{state.value}} goes here', alertVal: 'My {{alertId}} {{alertName}} {{spaceId}} {{tags}} {{alertInstanceId}} goes here', }, + uuid: '111-111', }, ], } as unknown as SanitizedRule; @@ -774,7 +775,7 @@ describe('Execution Handler', () => { await executionHandler.run( generateAlert({ id: 1, - throttledActions: { 'test:default:1h': { date: new Date(DATE_1970) } }, + throttledActions: { '111-111': { date: new Date(DATE_1970) } }, }) ); @@ -982,6 +983,7 @@ describe('Execution Handler', () => { message: 'New: {{alerts.new.count}} Ongoing: {{alerts.ongoing.count}} Recovered: {{alerts.recovered.count}}', }, + uuid: '111-111', }, ], }, @@ -998,8 +1000,8 @@ describe('Execution Handler', () => { excludedAlertInstanceIds: ['foo'], }); expect(result).toEqual({ - throttledActions: { - 'testActionTypeId:summary:1d': { + throttledSummaryActions: { + '111-111': { date: new Date(), }, }, @@ -1067,13 +1069,14 @@ describe('Execution Handler', () => { message: 'New: {{alerts.new.count}} Ongoing: {{alerts.ongoing.count}} Recovered: {{alerts.recovered.count}}', }, + uuid: '111-111', }, ], }, taskInstance: { state: { ...defaultExecutionParams.taskInstance.state, - summaryActions: { 'testActionTypeId:summary:1d': { date: new Date() } }, + summaryActions: { '111-111': { date: new Date() } }, }, } as unknown as ConcreteTaskInstance, }) @@ -1112,6 +1115,7 @@ describe('Execution Handler', () => { params: { message: 'New: {{alerts.new.count}}', }, + uuid: '111-111', }, { id: '2', @@ -1122,6 +1126,7 @@ describe('Execution Handler', () => { notifyWhen: 'onThrottleInterval', throttle: '10d', }, + uuid: '222-222', }, ], }, @@ -1129,9 +1134,9 @@ describe('Execution Handler', () => { state: { ...defaultExecutionParams.taskInstance.state, summaryActions: { - 'testActionTypeId:summary:1d': { date: new Date() }, - 'testActionTypeId:summary:10d': { date: new Date() }, - 'testActionTypeId:summary:10m': { date: new Date() }, // does not exist in the actions list + '111-111': { date: new Date() }, + '222-222': { date: new Date() }, + '333-333': { date: new Date() }, // does not exist in the actions list }, }, } as unknown as ConcreteTaskInstance, @@ -1140,11 +1145,11 @@ describe('Execution Handler', () => { const result = await executionHandler.run({}); expect(result).toEqual({ - throttledActions: { - 'testActionTypeId:summary:1d': { + throttledSummaryActions: { + '111-111': { date: new Date(), }, - 'testActionTypeId:summary:10d': { + '222-222': { date: new Date(), }, }, diff --git a/x-pack/plugins/alerting/server/task_runner/execution_handler.ts b/x-pack/plugins/alerting/server/task_runner/execution_handler.ts index 9975c8f9e6923..30f7083b0019d 100644 --- a/x-pack/plugins/alerting/server/task_runner/execution_handler.ts +++ b/x-pack/plugins/alerting/server/task_runner/execution_handler.ts @@ -34,7 +34,7 @@ import { import { generateActionHash, getSummaryActionsFromTaskState, - isSummaryActionOnInterval, + isActionOnInterval, isSummaryAction, isSummaryActionThrottled, isSummaryActionPerRuleRun, @@ -47,7 +47,7 @@ enum Reasons { } export interface RunResult { - throttledActions: ThrottledActions; + throttledSummaryActions: ThrottledActions; } export class ExecutionHandler< @@ -129,11 +129,11 @@ export class ExecutionHandler< public async run( alerts: Record> ): Promise { - const executables = this.generateExecutables(alerts); - const throttledActions: ThrottledActions = getSummaryActionsFromTaskState({ + const throttledSummaryActions: ThrottledActions = getSummaryActionsFromTaskState({ actions: this.rule.actions, summaryActions: this.taskInstance.state?.summaryActions, }); + const executables = this.generateExecutables(alerts, throttledSummaryActions); if (!!executables.length) { const { @@ -232,8 +232,8 @@ export class ExecutionHandler< bulkActions, }); - if (isSummaryActionOnInterval(action)) { - throttledActions[generateActionHash(action)] = { date: new Date() }; + if (isActionOnInterval(action)) { + throttledSummaryActions[action.uuid!] = { date: new Date() }; } logActions.push({ @@ -289,10 +289,11 @@ export class ExecutionHandler< }); if (!this.isRecoveredAlert(actionGroup)) { - if (isSummaryActionOnInterval(action)) { + if (isActionOnInterval(action)) { executableAlert.updateLastScheduledActions( action.group as ActionGroupIds, - generateActionHash(action) + generateActionHash(action), + action.uuid ); } else { executableAlert.updateLastScheduledActions(action.group as ActionGroupIds); @@ -314,7 +315,7 @@ export class ExecutionHandler< } } } - return { throttledActions }; + return { throttledSummaryActions }; } private hasAlerts( @@ -379,7 +380,8 @@ export class ExecutionHandler< const throttled = action.frequency?.throttle ? alert.isThrottled({ throttle: action.frequency.throttle ?? null, - actionHash: generateActionHash(action), + actionHash: generateActionHash(action), // generateActionHash must be removed once all the hash identifiers removed from the task state + uuid: action.uuid, }) : alert.isThrottled({ throttle: rule.throttle ?? null }); @@ -462,7 +464,8 @@ export class ExecutionHandler< } private generateExecutables( - alerts: Record> + alerts: Record>, + summaryActions: ThrottledActions ) { const executables = []; @@ -472,7 +475,7 @@ export class ExecutionHandler< this.canFetchSummarizedAlerts(action) && !isSummaryActionThrottled({ action, - summaryActions: this.taskInstance.state?.summaryActions, + summaryActions, logger: this.logger, }) ) { @@ -525,7 +528,7 @@ export class ExecutionHandler< }) { let options; - if (isSummaryActionOnInterval(action)) { + if (isActionOnInterval(action)) { const throttleMills = parseDuration(action.frequency!.throttle!); const start = new Date(Date.now() - throttleMills); diff --git a/x-pack/plugins/alerting/server/task_runner/rule_action_helper.test.ts b/x-pack/plugins/alerting/server/task_runner/rule_action_helper.test.ts index 87af43fe58d2e..10b1534f66edc 100644 --- a/x-pack/plugins/alerting/server/task_runner/rule_action_helper.test.ts +++ b/x-pack/plugins/alerting/server/task_runner/rule_action_helper.test.ts @@ -10,7 +10,7 @@ import { RuleAction } from '../types'; import { generateActionHash, getSummaryActionsFromTaskState, - isSummaryActionOnInterval, + isActionOnInterval, isSummaryAction, isSummaryActionThrottled, } from './rule_action_helper'; @@ -46,6 +46,7 @@ const mockSummaryAction: RuleAction = { notifyWhen: 'onThrottleInterval', throttle: '1d', }, + uuid: '111-111', }; describe('rule_action_helper', () => { @@ -63,16 +64,26 @@ describe('rule_action_helper', () => { const result = isSummaryAction(mockSummaryAction); expect(result).toBe(true); }); + + test('should return false if the action is undefined', () => { + const result = isSummaryAction(undefined); + expect(result).toBe(false); + }); + + test('should return false if the action is not a proper RuleAction', () => { + const result = isSummaryAction({} as RuleAction); + expect(result).toBe(false); + }); }); - describe('isSummaryActionOnInterval', () => { + describe('isActionOnInterval', () => { test('should return false if the action does not have frequency field', () => { - const result = isSummaryActionOnInterval(mockOldAction); + const result = isActionOnInterval(mockOldAction); expect(result).toBe(false); }); test('should return false if notifyWhen is not onThrottleInterval', () => { - const result = isSummaryActionOnInterval({ + const result = isActionOnInterval({ ...mockAction, frequency: { ...mockAction.frequency, notifyWhen: 'onActiveAlert' }, } as RuleAction); @@ -80,7 +91,7 @@ describe('rule_action_helper', () => { }); test('should return false if throttle is not a valid interval string', () => { - const result = isSummaryActionOnInterval({ + const result = isActionOnInterval({ ...mockAction, frequency: { ...mockAction.frequency, throttle: null }, } as RuleAction); @@ -88,9 +99,19 @@ describe('rule_action_helper', () => { }); test('should return true if the action is a throttling action', () => { - const result = isSummaryActionOnInterval(mockSummaryAction); + const result = isActionOnInterval(mockSummaryAction); expect(result).toBe(true); }); + + test('should return false if the action undefined', () => { + const result = isActionOnInterval(undefined); + expect(result).toBe(false); + }); + + test('should return false if the action is not a proper RuleAction', () => { + const result = isActionOnInterval({} as RuleAction); + expect(result).toBe(false); + }); }); describe('generateActionHash', () => { @@ -108,18 +129,33 @@ describe('rule_action_helper', () => { const result = generateActionHash(mockSummaryAction); expect(result).toBe('slack:summary:1d'); }); + + test('should return a hash for a broken summary action', () => { + const result = generateActionHash(undefined); + expect(result).toBe('no-action-type-id:no-action-group:no-throttling'); + }); }); describe('getSummaryActionsFromTaskState', () => { test('should remove the obsolete actions from the task instance', () => { + const result = getSummaryActionsFromTaskState({ + actions: [mockSummaryAction], + summaryActions: { + '111-111': { date: new Date('01.01.2020') }, + '222-222': { date: new Date('01.01.2020') }, + }, + }); + expect(result).toEqual({ '111-111': { date: new Date('01.01.2020') } }); + }); + + test('should replace hash with uuid', () => { const result = getSummaryActionsFromTaskState({ actions: [mockSummaryAction], summaryActions: { 'slack:summary:1d': { date: new Date('01.01.2020') }, - 'slack:summary:2d': { date: new Date('01.01.2020') }, }, }); - expect(result).toEqual({ 'slack:summary:1d': { date: new Date('01.01.2020') } }); + expect(result).toEqual({ '111-111': { date: new Date('01.01.2020') } }); }); }); @@ -131,12 +167,15 @@ describe('rule_action_helper', () => { beforeEach(() => { jest.setSystemTime(new Date('2020-01-01T23:00:00.000Z').getTime()); }); + afterEach(() => { + jest.clearAllMocks(); + }); afterAll(() => { jest.useRealTimers(); }); - const logger = { debug: jest.fn } as unknown as Logger; - const summaryActions = { 'slack:summary:1d': { date: new Date('2020-01-01T00:00:00.000Z') } }; + const logger = { debug: jest.fn() } as unknown as Logger; + const summaryActions = { '111-111': { date: new Date('2020-01-01T00:00:00.000Z') } }; test('should return false if the action does not have throttle filed', () => { const result = isSummaryActionThrottled({ @@ -183,7 +222,7 @@ describe('rule_action_helper', () => { test('should return false if the action is not in the task instance', () => { const result = isSummaryActionThrottled({ action: mockSummaryAction, - summaryActions: { 'slack:summary:2d': { date: new Date('2020-01-01T00:00:00.000Z') } }, + summaryActions: { '123-456': { date: new Date('2020-01-01T00:00:00.000Z') } }, logger, }); expect(result).toBe(false); @@ -193,7 +232,7 @@ describe('rule_action_helper', () => { jest.advanceTimersByTime(3600000 * 2); const result = isSummaryActionThrottled({ action: mockSummaryAction, - summaryActions: { 'slack:summary:1d': { date: new Date('2020-01-01T00:00:00.000Z') } }, + summaryActions: { '123-456': { date: new Date('2020-01-01T00:00:00.000Z') } }, logger, }); expect(result).toBe(false); @@ -207,5 +246,42 @@ describe('rule_action_helper', () => { }); expect(result).toBe(true); }); + + test('should return false if the action is broken', () => { + const result = isSummaryActionThrottled({ + action: undefined, + summaryActions, + logger, + }); + expect(result).toBe(false); + }); + + test('should return false if there is no summary action in the state', () => { + const result = isSummaryActionThrottled({ + action: mockSummaryAction, + summaryActions: undefined, + logger, + }); + expect(result).toBe(false); + }); + + test('should return false if the actions throttle interval is not valid', () => { + const result = isSummaryActionThrottled({ + action: { + ...mockSummaryAction, + frequency: { + summary: true, + notifyWhen: 'onThrottleInterval', + throttle: '1', + }, + }, + summaryActions, + logger, + }); + expect(result).toBe(false); + expect(logger.debug).toHaveBeenCalledWith( + "Action'slack:1', has an invalid throttle interval" + ); + }); }); }); diff --git a/x-pack/plugins/alerting/server/task_runner/rule_action_helper.ts b/x-pack/plugins/alerting/server/task_runner/rule_action_helper.ts index 5fa8da9b25d21..a87f71f46358d 100644 --- a/x-pack/plugins/alerting/server/task_runner/rule_action_helper.ts +++ b/x-pack/plugins/alerting/server/task_runner/rule_action_helper.ts @@ -13,12 +13,12 @@ import { ThrottledActions, } from '../../common'; -export const isSummaryAction = (action: RuleAction) => { - return action.frequency?.summary || false; +export const isSummaryAction = (action?: RuleAction) => { + return action?.frequency?.summary || false; }; -export const isSummaryActionOnInterval = (action: RuleAction) => { - if (!action.frequency) { +export const isActionOnInterval = (action?: RuleAction) => { + if (!action?.frequency) { return false; } return ( @@ -42,36 +42,41 @@ export const isSummaryActionThrottled = ({ summaryActions, logger, }: { - action: RuleAction; + action?: RuleAction; summaryActions?: ThrottledActions; logger: Logger; }) => { - if (!isSummaryActionOnInterval(action)) { + if (!isActionOnInterval(action)) { return false; } if (!summaryActions) { return false; } - const hash = generateActionHash(action); - const triggeredSummaryAction = summaryActions[hash]; + const triggeredSummaryAction = summaryActions[action?.uuid!]; if (!triggeredSummaryAction) { return false; } - const throttleMills = parseDuration(action.frequency!.throttle!); + let throttleMills = 0; + try { + throttleMills = parseDuration(action?.frequency!.throttle!); + } catch (e) { + logger.debug(`Action'${action?.actionTypeId}:${action?.id}', has an invalid throttle interval`); + } + const throttled = triggeredSummaryAction.date.getTime() + throttleMills > Date.now(); if (throttled) { logger.debug( - `skipping scheduling the action '${action.actionTypeId}:${action.id}', summary action is still being throttled` + `skipping scheduling the action '${action?.actionTypeId}:${action?.id}', summary action is still being throttled` ); } return throttled; }; -export const generateActionHash = (action: RuleAction) => { - return `${action.actionTypeId}:${action.frequency?.summary ? 'summary' : action.group}:${ - action.frequency?.throttle || 'no-throttling' - }`; +export const generateActionHash = (action?: RuleAction) => { + return `${action?.actionTypeId || 'no-action-type-id'}:${ + action?.frequency?.summary ? 'summary' : action?.group || 'no-action-group' + }:${action?.frequency?.throttle || 'no-throttling'}`; }; export const getSummaryActionsFromTaskState = ({ @@ -82,11 +87,12 @@ export const getSummaryActionsFromTaskState = ({ summaryActions?: ThrottledActions; }) => { return Object.entries(summaryActions).reduce((newObj, [key, val]) => { - const actionExists = actions.some( - (action) => action.frequency?.summary && generateActionHash(action) === key + const actionExists = actions.find( + (action) => + action.frequency?.summary && (action.uuid === key || generateActionHash(action) === key) ); if (actionExists) { - return { ...newObj, [key]: val }; + return { ...newObj, [actionExists.uuid!]: val }; // replace hash with uuid } else { return newObj; } diff --git a/x-pack/plugins/alerting/server/task_runner/task_runner.test.ts b/x-pack/plugins/alerting/server/task_runner/task_runner.test.ts index 7a000815e3959..984b4decdfd39 100644 --- a/x-pack/plugins/alerting/server/task_runner/task_runner.test.ts +++ b/x-pack/plugins/alerting/server/task_runner/task_runner.test.ts @@ -1486,7 +1486,7 @@ describe('Task Runner', () => { generateEnqueueFunctionInput({ isBulk, id: '1', foo: true }) ); expect(result.state.summaryActions).toEqual({ - 'slack:summary:1h': { date: new Date(DATE_1970) }, + '111-111': { date: new Date(DATE_1970) }, }); } ); diff --git a/x-pack/plugins/alerting/server/task_runner/task_runner.ts b/x-pack/plugins/alerting/server/task_runner/task_runner.ts index 82e01a93125e5..147ac3e2c2970 100644 --- a/x-pack/plugins/alerting/server/task_runner/task_runner.ts +++ b/x-pack/plugins/alerting/server/task_runner/task_runner.ts @@ -446,7 +446,7 @@ export class TaskRunner< actionsClient: await this.context.actionsPlugin.getActionsClientWithRequest(fakeRequest), }); - let executionHandlerRunResult: RunResult = { throttledActions: {} }; + let executionHandlerRunResult: RunResult = { throttledSummaryActions: {} }; await this.timer.runWithTimer(TaskRunnerTimerSpan.TriggerActions, async () => { await rulesClient.clearExpiredSnoozes({ id: rule.id }); @@ -484,7 +484,7 @@ export class TaskRunner< alertTypeState: updatedRuleTypeState || undefined, alertInstances: alertsToReturn, alertRecoveredInstances: recoveredAlertsToReturn, - summaryActions: executionHandlerRunResult.throttledActions, + summaryActions: executionHandlerRunResult.throttledSummaryActions, }; } diff --git a/x-pack/plugins/alerting/server/types.ts b/x-pack/plugins/alerting/server/types.ts index 4997d9563d59e..8a463e7ec1aa1 100644 --- a/x-pack/plugins/alerting/server/types.ts +++ b/x-pack/plugins/alerting/server/types.ts @@ -168,12 +168,45 @@ export interface GetViewInAppRelativeUrlFnOpts { export type GetViewInAppRelativeUrlFn = ( opts: GetViewInAppRelativeUrlFnOpts ) => string; + +interface ComponentTemplateSpec { + dynamic?: 'strict' | false; // defaults to 'strict' + fieldMap: FieldMap; +} + export interface IRuleTypeAlerts { + /** + * Specifies the target alerts-as-data resource + * for this rule type. All alerts created with the same + * context are written to the same alerts-as-data index. + * + * All custom mappings defined for a context must be the same! + */ context: string; - namespace?: string; - fieldMap: FieldMap; + + /** + * Specifies custom mappings for the target alerts-as-data + * index. These mappings will be translated into a component template + * and used in the index template for the index. + */ + mappings: ComponentTemplateSpec; + + /** + * Optional flag to include a reference to the ECS component template. + */ useEcs?: boolean; + + /** + * Optional flag to include a reference to the legacy alert component template. + * Any rule type that is migrating from the rule registry should set this + * flag to true to ensure their alerts-as-data indices are backwards compatible. + */ useLegacyAlerts?: boolean; + + /** + * Optional secondary alias to use. This alias should not include the namespace. + */ + secondaryAlias?: string; } export interface RuleType< diff --git a/x-pack/plugins/apm/public/components/alerting/rule_types/register_apm_rule_types.ts b/x-pack/plugins/apm/public/components/alerting/rule_types/register_apm_rule_types.ts index f355ea4c2f6eb..aa7a8041f966f 100644 --- a/x-pack/plugins/apm/public/components/alerting/rule_types/register_apm_rule_types.ts +++ b/x-pack/plugins/apm/public/components/alerting/rule_types/register_apm_rule_types.ts @@ -54,10 +54,7 @@ export function registerApmRuleTypes( errors: [], }), alertDetailsAppSection: lazy( - () => - import( - '../ui_components/alert_details_app_section/alert_details_app_section' - ) + () => import('../ui_components/alert_details_app_section') ), requiresAppContext: false, defaultActionMessage: errorCountMessage, @@ -94,10 +91,7 @@ export function registerApmRuleTypes( errors: [], }), alertDetailsAppSection: lazy( - () => - import( - '../ui_components/alert_details_app_section/alert_details_app_section' - ) + () => import('../ui_components/alert_details_app_section') ), requiresAppContext: false, defaultActionMessage: transactionDurationMessage, @@ -132,10 +126,7 @@ export function registerApmRuleTypes( errors: [], }), alertDetailsAppSection: lazy( - () => - import( - '../ui_components/alert_details_app_section/alert_details_app_section' - ) + () => import('../ui_components/alert_details_app_section') ), requiresAppContext: false, defaultActionMessage: transactionErrorRateMessage, @@ -167,10 +158,7 @@ export function registerApmRuleTypes( errors: [], }), alertDetailsAppSection: lazy( - () => - import( - '../ui_components/alert_details_app_section/alert_details_app_section' - ) + () => import('../ui_components/alert_details_app_section') ), requiresAppContext: false, defaultActionMessage: anomalyMessage, diff --git a/x-pack/plugins/apm/public/components/alerting/ui_components/alert_details_app_section/alert_details_app_section.tsx b/x-pack/plugins/apm/public/components/alerting/ui_components/alert_details_app_section/alert_details_app_section.tsx deleted file mode 100644 index 73690e8b0a00e..0000000000000 --- a/x-pack/plugins/apm/public/components/alerting/ui_components/alert_details_app_section/alert_details_app_section.tsx +++ /dev/null @@ -1,447 +0,0 @@ -/* - * 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 React, { useMemo } from 'react'; -import { EuiFlexGroup } from '@elastic/eui'; -import { EuiFlexItem } from '@elastic/eui'; -import { i18n } from '@kbn/i18n'; -import { EuiPanel } from '@elastic/eui'; -import { EuiTitle } from '@elastic/eui'; -import { EuiIconTip } from '@elastic/eui'; -import { - ALERT_DURATION, - ALERT_END, - ALERT_RULE_UUID, - ALERT_EVALUATION_THRESHOLD, - ALERT_RULE_TYPE_ID, -} from '@kbn/rule-data-utils'; -import moment from 'moment'; -import { asPercent } from '../../../../../common/utils/formatters'; -import { APIReturnType } from '../../../../services/rest/create_call_apm_api'; -import { getDurationFormatter } from '../../../../../common/utils/formatters/duration'; -import { useFetcher } from '../../../../hooks/use_fetcher'; -import { useTimeRange } from '../../../../hooks/use_time_range'; -import { isTimeComparison } from '../../../shared/time_comparison/get_comparison_options'; -import { getComparisonChartTheme } from '../../../shared/time_comparison/get_comparison_chart_theme'; -import { getLatencyChartSelector } from '../../../../selectors/latency_chart_selectors'; -import { TimeseriesChart } from '../../../shared/charts/timeseries_chart'; -import { - getMaxY, - getResponseTimeTickFormatter, -} from '../../../shared/charts/transaction_charts/helper'; -import { ChartPointerEventContextProvider } from '../../../../context/chart_pointer_event/chart_pointer_event_context'; -import { - ChartType, - getTimeSeriesColor, -} from '../../../shared/charts/helper/get_timeseries_color'; -import { - AlertDetailsAppSectionProps, - SERVICE_NAME, - TRANSACTION_TYPE, -} from './types'; -import { getAggsTypeFromRule, isLatencyThresholdRuleType } from './helpers'; -import { filterNil } from '../../../shared/charts/latency_chart'; -import { errorRateI18n } from '../../../shared/charts/failed_transaction_rate_chart'; -import { LatencyAlertsHistoryChart } from './latency_alerts_history_chart'; -import { - AlertActiveRect, - AlertAnnotation, - AlertThresholdRect, - AlertThresholdAnnotation, -} from './latency_chart_components'; -import { SERVICE_ENVIRONMENT } from '../../../../../common/es_fields/apm'; - -export function AlertDetailsAppSection({ - rule, - alert, - timeZone, -}: AlertDetailsAppSectionProps) { - const params = rule.params; - const environment = alert.fields[SERVICE_ENVIRONMENT]; - const latencyAggregationType = getAggsTypeFromRule(params.aggregationType); - - // duration is us, convert it to MS - const alertDurationMS = alert.fields[ALERT_DURATION]! / 1000; - - const serviceName = String(alert.fields[SERVICE_NAME]); - - // Currently, we don't use comparisonEnabled nor offset. - // But providing them as they are required for the chart. - const comparisonEnabled = false; - const offset = '1d'; - const ruleWindowSizeMS = moment - .duration(rule.params.windowSize, rule.params.windowUnit) - .asMilliseconds(); - - const TWENTY_TIMES_RULE_WINDOW_MS = 20 * ruleWindowSizeMS; - /** - * This is part or the requirements (RFC). - * If the alert is less than 20 units of `FOR THE LAST ` then we should draw a time range of 20 units. - * IE. The user set "FOR THE LAST 5 minutes" at a minimum we should show 100 minutes. - */ - const rangeFrom = - alertDurationMS < TWENTY_TIMES_RULE_WINDOW_MS - ? moment(alert.start) - .subtract(TWENTY_TIMES_RULE_WINDOW_MS, 'millisecond') - .toISOString() - : moment(alert.start) - .subtract(ruleWindowSizeMS, 'millisecond') - .toISOString(); - - const rangeTo = alert.active - ? 'now' - : moment(alert.fields[ALERT_END]) - .add(ruleWindowSizeMS, 'millisecond') - .toISOString(); - - const { start, end } = useTimeRange({ rangeFrom, rangeTo }); - const transactionType = alert.fields[TRANSACTION_TYPE]; - const comparisonChartTheme = getComparisonChartTheme(); - const INITIAL_STATE = { - currentPeriod: [], - previousPeriod: [], - }; - - /* Latency Chart */ - const { data, status } = useFetcher( - (callApmApi) => { - if ( - serviceName && - start && - end && - transactionType && - latencyAggregationType - ) { - return callApmApi( - `GET /internal/apm/services/{serviceName}/transactions/charts/latency`, - { - params: { - path: { serviceName }, - query: { - environment, - kuery: '', - start, - end, - transactionType, - transactionName: undefined, - latencyAggregationType, - }, - }, - } - ); - } - }, - [ - end, - environment, - latencyAggregationType, - serviceName, - start, - transactionType, - ] - ); - - const memoizedData = useMemo( - () => - getLatencyChartSelector({ - latencyChart: data, - latencyAggregationType, - previousPeriodLabel: '', - }), - // It should only update when the data has changed - // eslint-disable-next-line react-hooks/exhaustive-deps - [data] - ); - const { currentPeriod, previousPeriod } = memoizedData; - - const timeseriesLatency = [ - currentPeriod, - comparisonEnabled && isTimeComparison(offset) ? previousPeriod : undefined, - ].filter(filterNil); - - const latencyMaxY = getMaxY(timeseriesLatency); - const latencyFormatter = getDurationFormatter(latencyMaxY); - - /* Latency Chart */ - - /* Throughput Chart */ - const { data: dataThroughput = INITIAL_STATE, status: statusThroughput } = - useFetcher( - (callApmApi) => { - if (serviceName && transactionType && start && end) { - return callApmApi( - 'GET /internal/apm/services/{serviceName}/throughput', - { - params: { - path: { - serviceName, - }, - query: { - environment, - kuery: '', - start, - end, - transactionType, - transactionName: undefined, - }, - }, - } - ); - } - }, - [environment, serviceName, start, end, transactionType] - ); - const { currentPeriodColor, previousPeriodColor } = getTimeSeriesColor( - ChartType.THROUGHPUT - ); - const timeseriesThroughput = [ - { - data: dataThroughput.currentPeriod, - type: 'linemark', - color: currentPeriodColor, - title: i18n.translate('xpack.apm.serviceOverview.throughtputChartTitle', { - defaultMessage: 'Throughput', - }), - }, - ...(comparisonEnabled - ? [ - { - data: dataThroughput.previousPeriod, - type: 'area', - color: previousPeriodColor, - title: '', - }, - ] - : []), - ]; - - /* Throughput Chart */ - - /* Error Rate */ - type ErrorRate = - APIReturnType<'GET /internal/apm/services/{serviceName}/transactions/charts/error_rate'>; - - const INITIAL_STATE_ERROR_RATE: ErrorRate = { - currentPeriod: { - timeseries: [], - average: null, - }, - previousPeriod: { - timeseries: [], - average: null, - }, - }; - function yLabelFormat(y?: number | null) { - return asPercent(y || 0, 1); - } - - const { - data: dataErrorRate = INITIAL_STATE_ERROR_RATE, - status: statusErrorRate, - } = useFetcher( - (callApmApi) => { - if (transactionType && serviceName && start && end) { - return callApmApi( - 'GET /internal/apm/services/{serviceName}/transactions/charts/error_rate', - { - params: { - path: { - serviceName, - }, - query: { - environment, - kuery: '', - start, - end, - transactionType, - transactionName: undefined, - }, - }, - } - ); - } - }, - [environment, serviceName, start, end, transactionType] - ); - - const { currentPeriodColor: currentPeriodColorErrorRate } = - getTimeSeriesColor(ChartType.FAILED_TRANSACTION_RATE); - - const timeseriesErrorRate = [ - { - data: dataErrorRate.currentPeriod.timeseries, - type: 'linemark', - color: currentPeriodColorErrorRate, - title: i18n.translate('xpack.apm.errorRate.chart.errorRate', { - defaultMessage: 'Failed transaction rate (avg.)', - }), - }, - ]; - /* Error Rate */ - - const getLatencyChartAdditionalData = () => { - if (isLatencyThresholdRuleType(alert.fields[ALERT_RULE_TYPE_ID])) { - return [ - , - , - , - , - ]; - } - }; - - return ( - - - - - - - -

- {i18n.translate( - 'xpack.apm.dependencyLatencyChart.chartTitle', - { - defaultMessage: 'Latency', - } - )} -

-
-
-
- -
-
- - - - - - - - -

- {i18n.translate( - 'xpack.apm.serviceOverview.throughtputChartTitle', - { defaultMessage: 'Throughput' } - )} -

-
-
- - - - -
- - -
-
- - - - - -

- {i18n.translate('xpack.apm.errorRate', { - defaultMessage: 'Failed transaction rate', - })} -

-
-
- - - - -
- - -
-
-
-
- - - -
-
- ); -} - -// eslint-disable-next-line import/no-default-export -export default AlertDetailsAppSection; diff --git a/x-pack/plugins/apm/public/components/alerting/ui_components/alert_details_app_section/failed_transaction_chart.tsx b/x-pack/plugins/apm/public/components/alerting/ui_components/alert_details_app_section/failed_transaction_chart.tsx new file mode 100644 index 0000000000000..688a3635b942f --- /dev/null +++ b/x-pack/plugins/apm/public/components/alerting/ui_components/alert_details_app_section/failed_transaction_chart.tsx @@ -0,0 +1,134 @@ +/* + * 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. + */ +/* Error Rate */ + +import { + EuiFlexItem, + EuiPanel, + EuiFlexGroup, + EuiTitle, + EuiIconTip, +} from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; +import React from 'react'; +import { RecursivePartial } from '@elastic/eui'; +import { Theme } from '@elastic/charts'; +import { useFetcher } from '../../../../hooks/use_fetcher'; +import { ChartType } from '../../../shared/charts/helper/get_timeseries_color'; +import * as get_timeseries_color from '../../../shared/charts/helper/get_timeseries_color'; +import { APIReturnType } from '../../../../services/rest/create_call_apm_api'; +import { errorRateI18n } from '../../../shared/charts/failed_transaction_rate_chart'; +import { TimeseriesChart } from '../../../shared/charts/timeseries_chart'; +import { yLabelFormat } from './helpers'; + +type ErrorRate = + APIReturnType<'GET /internal/apm/services/{serviceName}/transactions/charts/error_rate'>; + +const INITIAL_STATE_ERROR_RATE: ErrorRate = { + currentPeriod: { + timeseries: [], + average: null, + }, + previousPeriod: { + timeseries: [], + average: null, + }, +}; + +function FailedTransactionChart({ + transactionType, + serviceName, + environment, + start, + end, + comparisonChartTheme, + timeZone, +}: { + transactionType: string; + serviceName: string; + environment: string; + start: string; + end: string; + comparisonChartTheme: RecursivePartial; + timeZone: string; +}) { + const { currentPeriodColor: currentPeriodColorErrorRate } = + get_timeseries_color.getTimeSeriesColor(ChartType.FAILED_TRANSACTION_RATE); + + const { data: dataErrorRate = INITIAL_STATE_ERROR_RATE, status } = useFetcher( + (callApmApi) => { + if (transactionType && serviceName && start && end) { + return callApmApi( + 'GET /internal/apm/services/{serviceName}/transactions/charts/error_rate', + { + params: { + path: { + serviceName, + }, + query: { + environment, + kuery: '', + start, + end, + transactionType, + transactionName: undefined, + }, + }, + } + ); + } + }, + [environment, serviceName, start, end, transactionType] + ); + const timeseriesErrorRate = [ + { + data: dataErrorRate.currentPeriod.timeseries, + type: 'linemark', + color: currentPeriodColorErrorRate, + title: i18n.translate('xpack.apm.errorRate.chart.errorRate', { + defaultMessage: 'Failed transaction rate (avg.)', + }), + }, + ]; + return ( + + + + + +

+ {i18n.translate('xpack.apm.errorRate', { + defaultMessage: 'Failed transaction rate', + })} +

+
+
+ + + + +
+ + +
+
+ ); +} + +// eslint-disable-next-line import/no-default-export +export default FailedTransactionChart; diff --git a/x-pack/plugins/apm/public/components/alerting/ui_components/alert_details_app_section/helpers.ts b/x-pack/plugins/apm/public/components/alerting/ui_components/alert_details_app_section/helpers.ts index b49e84b4b76c1..917fca0956814 100644 --- a/x-pack/plugins/apm/public/components/alerting/ui_components/alert_details_app_section/helpers.ts +++ b/x-pack/plugins/apm/public/components/alerting/ui_components/alert_details_app_section/helpers.ts @@ -5,6 +5,7 @@ * 2.0. */ +import { asPercent } from '@kbn/observability-plugin/common'; import { LatencyAggregationType } from '../../../../../common/latency_aggregation_types'; export const getAggsTypeFromRule = ( @@ -17,3 +18,7 @@ export const getAggsTypeFromRule = ( export const isLatencyThresholdRuleType = (ruleTypeId: string) => ruleTypeId === 'apm.transaction_duration'; + +export const yLabelFormat = (y?: number | null) => { + return asPercent(y || 0, 1); +}; diff --git a/x-pack/plugins/apm/public/components/alerting/ui_components/alert_details_app_section/index.tsx b/x-pack/plugins/apm/public/components/alerting/ui_components/alert_details_app_section/index.tsx new file mode 100644 index 0000000000000..2f6b9f00f6e90 --- /dev/null +++ b/x-pack/plugins/apm/public/components/alerting/ui_components/alert_details_app_section/index.tsx @@ -0,0 +1,196 @@ +/* + * 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 React, { useEffect, useState } from 'react'; +import { EuiFlexGroup } from '@elastic/eui'; +import { EuiFlexItem } from '@elastic/eui'; +import { + ALERT_DURATION, + ALERT_END, + ALERT_RULE_UUID, + ALERT_EVALUATION_THRESHOLD, + ALERT_RULE_TYPE_ID, + ALERT_EVALUATION_VALUE, +} from '@kbn/rule-data-utils'; +import moment from 'moment'; +import { formatAlertEvaluationValue } from '@kbn/observability-plugin/public'; +import { FormattedMessage } from '@kbn/i18n-react'; +import { useTimeRange } from '../../../../hooks/use_time_range'; +import { getComparisonChartTheme } from '../../../shared/time_comparison/get_comparison_chart_theme'; +import { ChartPointerEventContextProvider } from '../../../../context/chart_pointer_event/chart_pointer_event_context'; + +import { + AlertDetailsAppSectionProps, + SERVICE_NAME, + TRANSACTION_TYPE, +} from './types'; +import { getAggsTypeFromRule } from './helpers'; +import { LatencyAlertsHistoryChart } from './latency_alerts_history_chart'; + +import { SERVICE_ENVIRONMENT } from '../../../../../common/es_fields/apm'; +import FailedTransactionChart from './failed_transaction_chart'; +import LatencyChart from './latency_chart/latency_chart'; +import ThroughputChart from './throughput_chart'; + +export function AlertDetailsAppSection({ + rule, + alert, + timeZone, + setAlertSummaryFields, +}: AlertDetailsAppSectionProps) { + useEffect(() => { + const alertSummaryFields = [ + { + label: ( + + ), + value: formatAlertEvaluationValue( + alert?.fields[ALERT_RULE_TYPE_ID], + alert?.fields[ALERT_EVALUATION_VALUE] + ), + }, + { + label: ( + + ), + value: formatAlertEvaluationValue( + alert?.fields[ALERT_RULE_TYPE_ID], + alert?.fields[ALERT_EVALUATION_THRESHOLD] + ), + }, + { + label: ( + + ), + value: alert?.fields[SERVICE_ENVIRONMENT], + }, + { + label: ( + + ), + value: alert?.fields[SERVICE_NAME], + }, + ]; + setAlertSummaryFields(alertSummaryFields); + }, [alert?.fields, setAlertSummaryFields]); + + const params = rule.params; + const environment = alert.fields[SERVICE_ENVIRONMENT]; + const latencyAggregationType = getAggsTypeFromRule(params.aggregationType); + const [latencyMaxY, setLatencyMaxY] = useState(0); + + // duration is us, convert it to MS + const alertDurationMS = alert.fields[ALERT_DURATION]! / 1000; + + const serviceName = String(alert.fields[SERVICE_NAME]); + + // Currently, we don't use comparisonEnabled nor offset. + // But providing them as they are required for the chart. + const comparisonEnabled = false; + const offset = '1d'; + const ruleWindowSizeMS = moment + .duration(rule.params.windowSize, rule.params.windowUnit) + .asMilliseconds(); + + const TWENTY_TIMES_RULE_WINDOW_MS = 20 * ruleWindowSizeMS; + /** + * This is part or the requirements (RFC). + * If the alert is less than 20 units of `FOR THE LAST ` then we should draw a time range of 20 units. + * IE. The user set "FOR THE LAST 5 minutes" at a minimum we should show 100 minutes. + */ + const rangeFrom = + alertDurationMS < TWENTY_TIMES_RULE_WINDOW_MS + ? moment(alert.start) + .subtract(TWENTY_TIMES_RULE_WINDOW_MS, 'millisecond') + .toISOString() + : moment(alert.start) + .subtract(ruleWindowSizeMS, 'millisecond') + .toISOString(); + + const rangeTo = alert.active + ? 'now' + : moment(alert.fields[ALERT_END]) + .add(ruleWindowSizeMS, 'millisecond') + .toISOString(); + + const { start, end } = useTimeRange({ rangeFrom, rangeTo }); + const transactionType = alert.fields[TRANSACTION_TYPE]; + const comparisonChartTheme = getComparisonChartTheme(); + + return ( + + + + + + + + + + + + + + + ); +} + +// eslint-disable-next-line import/no-default-export +export default AlertDetailsAppSection; diff --git a/x-pack/plugins/apm/public/components/alerting/ui_components/alert_details_app_section/latency_chart_components/alert_active_rect.tsx b/x-pack/plugins/apm/public/components/alerting/ui_components/alert_details_app_section/latency_chart/alert_active_rect.tsx similarity index 100% rename from x-pack/plugins/apm/public/components/alerting/ui_components/alert_details_app_section/latency_chart_components/alert_active_rect.tsx rename to x-pack/plugins/apm/public/components/alerting/ui_components/alert_details_app_section/latency_chart/alert_active_rect.tsx diff --git a/x-pack/plugins/apm/public/components/alerting/ui_components/alert_details_app_section/latency_chart_components/alert_annotation.tsx b/x-pack/plugins/apm/public/components/alerting/ui_components/alert_details_app_section/latency_chart/alert_annotation.tsx similarity index 100% rename from x-pack/plugins/apm/public/components/alerting/ui_components/alert_details_app_section/latency_chart_components/alert_annotation.tsx rename to x-pack/plugins/apm/public/components/alerting/ui_components/alert_details_app_section/latency_chart/alert_annotation.tsx diff --git a/x-pack/plugins/apm/public/components/alerting/ui_components/alert_details_app_section/latency_chart_components/alert_threshold_annotation.tsx b/x-pack/plugins/apm/public/components/alerting/ui_components/alert_details_app_section/latency_chart/alert_threshold_annotation.tsx similarity index 100% rename from x-pack/plugins/apm/public/components/alerting/ui_components/alert_details_app_section/latency_chart_components/alert_threshold_annotation.tsx rename to x-pack/plugins/apm/public/components/alerting/ui_components/alert_details_app_section/latency_chart/alert_threshold_annotation.tsx diff --git a/x-pack/plugins/apm/public/components/alerting/ui_components/alert_details_app_section/latency_chart_components/alert_threshold_rect.tsx b/x-pack/plugins/apm/public/components/alerting/ui_components/alert_details_app_section/latency_chart/alert_threshold_rect.tsx similarity index 100% rename from x-pack/plugins/apm/public/components/alerting/ui_components/alert_details_app_section/latency_chart_components/alert_threshold_rect.tsx rename to x-pack/plugins/apm/public/components/alerting/ui_components/alert_details_app_section/latency_chart/alert_threshold_rect.tsx diff --git a/x-pack/plugins/apm/public/components/alerting/ui_components/alert_details_app_section/latency_chart_components/index.ts b/x-pack/plugins/apm/public/components/alerting/ui_components/alert_details_app_section/latency_chart/index.ts similarity index 100% rename from x-pack/plugins/apm/public/components/alerting/ui_components/alert_details_app_section/latency_chart_components/index.ts rename to x-pack/plugins/apm/public/components/alerting/ui_components/alert_details_app_section/latency_chart/index.ts diff --git a/x-pack/plugins/apm/public/components/alerting/ui_components/alert_details_app_section/latency_chart/latency_chart.tsx b/x-pack/plugins/apm/public/components/alerting/ui_components/alert_details_app_section/latency_chart/latency_chart.tsx new file mode 100644 index 0000000000000..79d7d38764b00 --- /dev/null +++ b/x-pack/plugins/apm/public/components/alerting/ui_components/alert_details_app_section/latency_chart/latency_chart.tsx @@ -0,0 +1,172 @@ +/* + * 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 { Theme } from '@elastic/charts'; +import { RecursivePartial } from '@elastic/eui'; +import React, { useMemo } from 'react'; +import { EuiFlexItem, EuiPanel, EuiFlexGroup, EuiTitle } from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; +import { getDurationFormatter } from '@kbn/observability-plugin/common'; +import { + ALERT_RULE_TYPE_ID, + ALERT_EVALUATION_THRESHOLD, +} from '@kbn/rule-data-utils'; +import { TopAlert } from '@kbn/observability-plugin/public/pages/alerts'; +import { filterNil } from '../../../../shared/charts/latency_chart'; +import { TimeseriesChart } from '../../../../shared/charts/timeseries_chart'; +import { + getMaxY, + getResponseTimeTickFormatter, +} from '../../../../shared/charts/transaction_charts/helper'; +import { isTimeComparison } from '../../../../shared/time_comparison/get_comparison_options'; +import { useFetcher } from '../../../../../hooks/use_fetcher'; +import { getLatencyChartSelector } from '../../../../../selectors/latency_chart_selectors'; +import { LatencyAggregationType } from '../../../../../../common/latency_aggregation_types'; +import { isLatencyThresholdRuleType } from '../helpers'; +import { AlertActiveRect } from './alert_active_rect'; +import { AlertAnnotation } from './alert_annotation'; +import { AlertThresholdAnnotation } from './alert_threshold_annotation'; +import { AlertThresholdRect } from './alert_threshold_rect'; + +function LatencyChart({ + alert, + transactionType, + serviceName, + environment, + start, + end, + latencyAggregationType, + comparisonChartTheme, + comparisonEnabled, + offset, + timeZone, + setLatencyMaxY, +}: { + alert: TopAlert; + transactionType: string; + serviceName: string; + environment: string; + start: string; + end: string; + comparisonChartTheme: RecursivePartial; + latencyAggregationType: LatencyAggregationType; + comparisonEnabled: boolean; + offset: string; + timeZone: string; + setLatencyMaxY: React.Dispatch>; +}) { + const { data, status } = useFetcher( + (callApmApi) => { + if ( + serviceName && + start && + end && + transactionType && + latencyAggregationType + ) { + return callApmApi( + `GET /internal/apm/services/{serviceName}/transactions/charts/latency`, + { + params: { + path: { serviceName }, + query: { + environment, + kuery: '', + start, + end, + transactionType, + transactionName: undefined, + latencyAggregationType, + }, + }, + } + ); + } + }, + [ + end, + environment, + latencyAggregationType, + serviceName, + start, + transactionType, + ] + ); + + const getLatencyChartAdditionalData = () => { + if (isLatencyThresholdRuleType(alert.fields[ALERT_RULE_TYPE_ID])) { + return [ + , + , + , + , + ]; + } + }; + const memoizedData = useMemo( + () => + getLatencyChartSelector({ + latencyChart: data, + latencyAggregationType, + previousPeriodLabel: '', + }), + [data, latencyAggregationType] + ); + const { currentPeriod, previousPeriod } = memoizedData; + + const timeseriesLatency = [ + currentPeriod, + comparisonEnabled && isTimeComparison(offset) ? previousPeriod : undefined, + ].filter(filterNil); + const latencyMaxY = getMaxY(timeseriesLatency); + setLatencyMaxY(latencyMaxY); + const latencyFormatter = getDurationFormatter(latencyMaxY); + return ( + + + + + +

+ {i18n.translate('xpack.apm.dependencyLatencyChart.chartTitle', { + defaultMessage: 'Latency', + })} +

+
+
+
+ +
+
+ ); +} + +// eslint-disable-next-line import/no-default-export +export default LatencyChart; diff --git a/x-pack/plugins/apm/public/components/alerting/ui_components/alert_details_app_section/throughput_chart.tsx b/x-pack/plugins/apm/public/components/alerting/ui_components/alert_details_app_section/throughput_chart.tsx new file mode 100644 index 0000000000000..60b6997ad78d0 --- /dev/null +++ b/x-pack/plugins/apm/public/components/alerting/ui_components/alert_details_app_section/throughput_chart.tsx @@ -0,0 +1,152 @@ +/* + * 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 React from 'react'; +import { Theme } from '@elastic/charts'; +import { + RecursivePartial, + EuiFlexItem, + EuiPanel, + EuiFlexGroup, + EuiTitle, + EuiIconTip, +} from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; + +import { getDurationFormatter } from '@kbn/observability-plugin/common'; +import { + ChartType, + getTimeSeriesColor, +} from '../../../shared/charts/helper/get_timeseries_color'; +import { useFetcher } from '../../../../hooks/use_fetcher'; +import { TimeseriesChart } from '../../../shared/charts/timeseries_chart'; +import { getResponseTimeTickFormatter } from '../../../shared/charts/transaction_charts/helper'; + +const INITIAL_STATE = { + currentPeriod: [], + previousPeriod: [], +}; +function ThroughputChart({ + transactionType, + serviceName, + environment, + start, + end, + comparisonChartTheme, + comparisonEnabled, + latencyMaxY, + offset, + timeZone, +}: { + transactionType: string; + serviceName: string; + environment: string; + start: string; + end: string; + comparisonChartTheme: RecursivePartial; + comparisonEnabled: boolean; + latencyMaxY: number; + offset: string; + timeZone: string; +}) { + /* Throughput Chart */ + const { data: dataThroughput = INITIAL_STATE, status: statusThroughput } = + useFetcher( + (callApmApi) => { + if (serviceName && transactionType && start && end) { + return callApmApi( + 'GET /internal/apm/services/{serviceName}/throughput', + { + params: { + path: { + serviceName, + }, + query: { + environment, + kuery: '', + start, + end, + transactionType, + transactionName: undefined, + }, + }, + } + ); + } + }, + [environment, serviceName, start, end, transactionType] + ); + const { currentPeriodColor, previousPeriodColor } = getTimeSeriesColor( + ChartType.THROUGHPUT + ); + const timeseriesThroughput = [ + { + data: dataThroughput.currentPeriod, + type: 'linemark', + color: currentPeriodColor, + title: i18n.translate('xpack.apm.serviceOverview.throughtputChartTitle', { + defaultMessage: 'Throughput', + }), + }, + ...(comparisonEnabled + ? [ + { + data: dataThroughput.previousPeriod, + type: 'area', + color: previousPeriodColor, + title: '', + }, + ] + : []), + ]; + + const latencyFormatter = getDurationFormatter(latencyMaxY); + + return ( + + + + + +

+ {i18n.translate( + 'xpack.apm.serviceOverview.throughtputChartTitle', + { defaultMessage: 'Throughput' } + )} +

+
+
+ + + + +
+ + +
+
+ ); +} + +// eslint-disable-next-line import/no-default-export +export default ThroughputChart; diff --git a/x-pack/plugins/apm/public/components/alerting/ui_components/alert_details_app_section/types.ts b/x-pack/plugins/apm/public/components/alerting/ui_components/alert_details_app_section/types.ts index 99fafb00bcac0..67aabf9ae30a9 100644 --- a/x-pack/plugins/apm/public/components/alerting/ui_components/alert_details_app_section/types.ts +++ b/x-pack/plugins/apm/public/components/alerting/ui_components/alert_details_app_section/types.ts @@ -7,6 +7,7 @@ import { Rule } from '@kbn/alerting-plugin/common'; import { TopAlert } from '@kbn/observability-plugin/public/pages/alerts'; +import { AlertSummaryField } from '@kbn/observability-plugin/public/pages/alert_details/types'; import { TIME_UNITS } from '@kbn/triggers-actions-ui-plugin/public'; import { SERVICE_ENVIRONMENT } from '../../../../../common/es_fields/apm'; @@ -25,4 +26,7 @@ export interface AlertDetailsAppSectionProps { [SERVICE_ENVIRONMENT]: string; }>; timeZone: string; + setAlertSummaryFields: React.Dispatch< + React.SetStateAction + >; } diff --git a/x-pack/plugins/apm/public/components/app/mobile/charts/mobile_treemap/index.tsx b/x-pack/plugins/apm/public/components/app/mobile/charts/mobile_treemap/index.tsx new file mode 100644 index 0000000000000..7691253e1bb80 --- /dev/null +++ b/x-pack/plugins/apm/public/components/app/mobile/charts/mobile_treemap/index.tsx @@ -0,0 +1,81 @@ +/* + * 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 React, { useState } from 'react'; +import { EuiSpacer } from '@elastic/eui'; +import { TreemapSelect, TreemapTypes } from './treemap_select'; +import { TreemapChart } from '../../../../shared/charts/treemap_chart'; +import { useFetcher } from '../../../../../hooks/use_fetcher'; +import { + DEVICE_MODEL_IDENTIFIER, + SERVICE_VERSION, +} from '../../../../../../common/es_fields/apm'; + +const ES_FIELD_MAPPING: Record = { + [TreemapTypes.Devices]: DEVICE_MODEL_IDENTIFIER, + [TreemapTypes.Versions]: SERVICE_VERSION, +}; + +export function MobileTreemap({ + kuery, + serviceName, + start, + end, + environment, +}: { + kuery: string; + serviceName: string; + start: string; + end: string; + environment: string; +}) { + const [selectedTreemap, selectTreemap] = useState(TreemapTypes.Devices); + + const { data, status } = useFetcher( + (callApmApi) => { + const fieldName = ES_FIELD_MAPPING[selectedTreemap]; + + if (fieldName) { + return callApmApi( + 'GET /internal/apm/mobile-services/{serviceName}/terms', + { + params: { + path: { + serviceName, + }, + query: { + environment, + kuery, + start, + end, + fieldName, + size: 500, + }, + }, + } + ); + } + }, + [environment, kuery, serviceName, start, end, selectedTreemap] + ); + + return ( + <> + + + + + ); +} diff --git a/x-pack/plugins/apm/public/components/app/mobile/charts/mobile_treemap/treemap_select.tsx b/x-pack/plugins/apm/public/components/app/mobile/charts/mobile_treemap/treemap_select.tsx new file mode 100644 index 0000000000000..6f86b5a83c30c --- /dev/null +++ b/x-pack/plugins/apm/public/components/app/mobile/charts/mobile_treemap/treemap_select.tsx @@ -0,0 +1,121 @@ +/* + * 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 React from 'react'; +import { i18n } from '@kbn/i18n'; +import { + EuiFlexGroup, + EuiFlexItem, + EuiTitle, + EuiSuperSelect, + EuiText, +} from '@elastic/eui'; +import type { EuiSuperSelectOption } from '@elastic/eui'; + +export enum TreemapTypes { + Devices = 'devices', + Versions = 'versions', +} + +const options: Array> = [ + { + value: TreemapTypes.Devices, + label: i18n.translate( + 'xpack.apm.transactionOverview.treemap.dropdown.devices', + { + defaultMessage: 'Devices treemap', + } + ), + description: i18n.translate( + 'xpack.apm.transactionOverview.treemap.dropdown.devices.subtitle', + { + defaultMessage: + 'This treemap view allows for easy and faster visual way the most used devices', + } + ), + }, + { + value: TreemapTypes.Versions, + label: i18n.translate( + 'xpack.apm.transactionOverview.treemap.versions.devices', + { + defaultMessage: 'Versions treemap', + } + ), + description: i18n.translate( + 'xpack.apm.transactionOverview.treemap.dropdown.versions.subtitle', + { + defaultMessage: + 'This treemap view allows for easy and faster visual way the most used versions.', + } + ), + }, +].map(({ value, label, description }) => ({ + inputDisplay: label, + value, + dropdownDisplay: ( + <> + {label} + +

{description}

+
+ + ), +})); + +export function TreemapSelect({ + selectedTreemap, + onChange, +}: { + selectedTreemap: TreemapTypes; + onChange: (value: TreemapTypes) => void; +}) { + const currentTreemap = + options.find(({ value }) => value === selectedTreemap) ?? options[0]; + + return ( + + + +

+ {i18n.translate('xpack.apm.transactionOverview.treemap.title', { + defaultMessage: 'Most used {currentTreemap}', + values: { currentTreemap: currentTreemap.value }, + })} +

+
+ + {i18n.translate('xpack.apm.transactionOverview.treemap.subtitle', { + defaultMessage: + 'Treemap showing the total and most used {currentTreemap}', + values: { currentTreemap: currentTreemap.value }, + })} + +
+ + + + + {i18n.translate('xpack.apm.transactionOverview.treemap.show', { + defaultMessage: 'Show', + })} + + + + + + +
+ ); +} diff --git a/x-pack/plugins/apm/public/components/app/mobile/service_overview/geo_map/embedded_map_select.tsx b/x-pack/plugins/apm/public/components/app/mobile/service_overview/geo_map/embedded_map_select.tsx new file mode 100644 index 0000000000000..3dfcfa8d1a74f --- /dev/null +++ b/x-pack/plugins/apm/public/components/app/mobile/service_overview/geo_map/embedded_map_select.tsx @@ -0,0 +1,107 @@ +/* + * 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 React from 'react'; +import { i18n } from '@kbn/i18n'; +import { + EuiFlexGroup, + EuiFlexItem, + EuiTitle, + EuiSuperSelect, + EuiText, +} from '@elastic/eui'; +import type { EuiSuperSelectOption } from '@elastic/eui'; +import { MapTypes } from '../../../../../../common/mobile/constants'; + +const options: Array> = [ + { + value: MapTypes.Http, + label: i18n.translate( + 'xpack.apm.serviceOverview.embeddedMap.dropdown.http.requests', + { + defaultMessage: 'HTTP requests', + } + ), + description: i18n.translate( + 'xpack.apm.serviceOverview.embeddedMap.dropdown.http.requests.subtitle', + { + defaultMessage: + 'HTTP defines a set of request methods to indicate the desired action to be performed for a given resource', + } + ), + }, + { + value: MapTypes.Session, + label: i18n.translate( + 'xpack.apm.serviceOverview.embeddedMap.dropdown.sessions', + { + defaultMessage: 'Sessions', + } + ), + description: i18n.translate( + 'xpack.apm.serviceOverview.embeddedMap.dropdown.sessions.subtitle', + { + defaultMessage: + 'An application session begins when a user starts an application and ends when the application exits.', + } + ), + }, +].map(({ value, label, description }) => ({ + inputDisplay: label, + value, + dropdownDisplay: ( + <> + {label} + +

{description}

+
+ + ), +})); + +export function EmbeddedMapSelect({ + selectedMap, + onChange, +}: { + selectedMap: MapTypes; + onChange: (value: MapTypes) => void; +}) { + const currentMap = + options.find(({ value }) => value === selectedMap) ?? options[0]; + + return ( + + + +

+ {i18n.translate('xpack.apm.serviceOverview.embeddedMap.title', { + defaultMessage: 'Geographic regions', + })} +

+
+ +

+ {i18n.translate('xpack.apm.serviceOverview.embeddedMap.subtitle', { + defaultMessage: + 'Map showing the total number of {currentMap} based on country and region', + values: { currentMap: currentMap.inputDisplay as string }, + })} +

+
+
+ + +
+ ); +} diff --git a/x-pack/plugins/apm/public/components/app/mobile/service_overview/geo_map/index.tsx b/x-pack/plugins/apm/public/components/app/mobile/service_overview/geo_map/index.tsx index bf8331b69555b..6b7ddfc46f545 100644 --- a/x-pack/plugins/apm/public/components/app/mobile/service_overview/geo_map/index.tsx +++ b/x-pack/plugins/apm/public/components/app/mobile/service_overview/geo_map/index.tsx @@ -6,51 +6,11 @@ */ import React, { useState } from 'react'; -import { EuiFlexGroup, EuiFlexItem, EuiSpacer, EuiTitle } from '@elastic/eui'; -import { i18n } from '@kbn/i18n'; +import { EuiSpacer } from '@elastic/eui'; import type { Filter } from '@kbn/es-query'; -import { EuiSuperSelect, EuiText } from '@elastic/eui'; import { EmbeddedMap } from './embedded_map'; import { MapTypes } from '../../../../../../common/mobile/constants'; - -const availableMaps: Array<{ - id: MapTypes; - label: string; - description: string; -}> = [ - { - id: MapTypes.Http, - label: i18n.translate( - 'xpack.apm.serviceOverview.embeddedMap.dropdown.http.requests', - { - defaultMessage: 'HTTP requests', - } - ), - description: i18n.translate( - 'xpack.apm.serviceOverview.embeddedMap.dropdown.http.requests.subtitle', - { - defaultMessage: - 'HTTP defines a set of request methods to indicate the desired action to be performed for a given resource', - } - ), - }, - { - id: MapTypes.Session, - label: i18n.translate( - 'xpack.apm.serviceOverview.embeddedMap.dropdown.sessions', - { - defaultMessage: 'Sessions', - } - ), - description: i18n.translate( - 'xpack.apm.serviceOverview.embeddedMap.dropdown.sessions.subtitle', - { - defaultMessage: - 'An application session begins when a user starts an application and ends when the application exits.', - } - ), - }, -]; +import { EmbeddedMapSelect } from './embedded_map_select'; export function GeoMap({ start, @@ -63,57 +23,11 @@ export function GeoMap({ kuery?: string; filters: Filter[]; }) { - const [selectedMap, setMap] = useState(MapTypes.Http); - - const currentMap = - availableMaps.find(({ id }) => id === selectedMap) ?? availableMaps[0]; + const [selectedMap, selectMap] = useState(MapTypes.Http); return ( <> - - - -

- {i18n.translate('xpack.apm.serviceOverview.embeddedMap.title', { - defaultMessage: 'Geographic regions', - })} -

-
- -

- {i18n.translate( - 'xpack.apm.serviceOverview.embeddedMap.subtitle', - { - defaultMessage: - 'Map showing the total number of {currentMap} based on country and region', - values: { currentMap: currentMap.label }, - } - )} -

-
-
- - ({ - inputDisplay: item.label, - value: item.id, - dropdownDisplay: ( - <> - {item.label} - -

{item.description}

-
- - ), - }))} - valueOfSelected={selectedMap} - onChange={(value: MapTypes) => setMap(value)} - itemLayoutAlign="top" - hasDividers - /> -
+ - - - - - - - - - - - + + + + ; +export function TreemapChart({ + data, + height, + fetchStatus, + id, +}: { + data: DataType; + height: number; + fetchStatus: FETCH_STATUS; + id: string; +}) { + const colorPalette = euiPaletteColorBlind(); + + return ( + + + d.count} + valueGetter={percentValueGetter} + layout={PartitionLayout.treemap} + layers={[ + { + groupByRollup: (d: Datum) => d.label, + shape: { + fillColor: ({ sortIndex }: { sortIndex: number }) => + colorPalette[Math.floor(sortIndex % 10)], + }, + fillLabel: { + valueFormatter: () => '', + fontWeight: 500, + minFontSize: 10, + maxFontSize: 14, + }, + nodeLabel: (label: Datum) => label, + }, + ]} + /> + + + ); +} diff --git a/x-pack/plugins/apm/server/plugin.ts b/x-pack/plugins/apm/server/plugin.ts index 5b98c8e437782..43bbe13d9662c 100644 --- a/x-pack/plugins/apm/server/plugin.ts +++ b/x-pack/plugins/apm/server/plugin.ts @@ -15,13 +15,16 @@ import { PluginInitializerContext, } from '@kbn/core/server'; import { isEmpty, mapValues } from 'lodash'; -import { experimentalRuleFieldMap } from '@kbn/rule-registry-plugin/common/assets/field_maps/experimental_rule_field_map'; import { Dataset } from '@kbn/rule-registry-plugin/server'; import { UI_SETTINGS } from '@kbn/data-plugin/common'; import { mappingFromFieldMap } from '@kbn/alerting-plugin/common'; import { APMConfig, APM_SERVER_FEATURE_ID } from '.'; import { APM_FEATURE, registerFeaturesUsage } from './feature'; -import { registerApmRuleTypes } from './routes/alerts/register_apm_rule_types'; +import { + registerApmRuleTypes, + apmRuleTypeAlertFieldMap, + APM_RULE_TYPE_ALERT_CONTEXT, +} from './routes/alerts/register_apm_rule_types'; import { registerFleetPolicyCallbacks } from './routes/fleet/register_fleet_policy_callbacks'; import { createApmTelemetry } from './lib/apm_telemetry'; import { APMEventClient } from './lib/helpers/create_es_client/create_apm_event_client'; @@ -46,14 +49,6 @@ import { } from './types'; import { registerRoutes } from './routes/apm_routes/register_apm_server_routes'; import { getGlobalApmServerRouteRepository } from './routes/apm_routes/get_global_apm_server_route_repository'; -import { - PROCESSOR_EVENT, - SERVICE_ENVIRONMENT, - SERVICE_NAME, - TRANSACTION_TYPE, - AGENT_NAME, - SERVICE_LANGUAGE_NAME, -} from '../common/es_fields/apm'; import { tutorialProvider } from './tutorial'; import { migrateLegacyAPMIndicesToSpaceAware } from './saved_objects/migrations/migrate_legacy_apm_indices_to_space_aware'; import { scheduleSourceMapMigration } from './routes/source_maps/schedule_source_map_migration'; @@ -119,47 +114,13 @@ export class APMPlugin const { ruleDataService } = plugins.ruleRegistry; const ruleDataClient = ruleDataService.initializeIndex({ feature: APM_SERVER_FEATURE_ID, - registrationContext: 'observability.apm', + registrationContext: APM_RULE_TYPE_ALERT_CONTEXT, dataset: Dataset.alerts, componentTemplateRefs: [], componentTemplates: [ { name: 'mappings', - mappings: mappingFromFieldMap( - { - ...experimentalRuleFieldMap, - [SERVICE_NAME]: { - type: 'keyword', - required: false, - }, - [SERVICE_ENVIRONMENT]: { - type: 'keyword', - required: false, - }, - [TRANSACTION_TYPE]: { - type: 'keyword', - required: false, - }, - [PROCESSOR_EVENT]: { - type: 'keyword', - required: false, - }, - [AGENT_NAME]: { - type: 'keyword', - required: false, - }, - [SERVICE_LANGUAGE_NAME]: { - type: 'keyword', - required: false, - }, - labels: { - type: 'object', - dynamic: true, - required: false, - }, - }, - 'strict' - ), + mappings: mappingFromFieldMap(apmRuleTypeAlertFieldMap, 'strict'), }, ], }); diff --git a/x-pack/plugins/apm/server/routes/alerts/register_apm_rule_types.ts b/x-pack/plugins/apm/server/routes/alerts/register_apm_rule_types.ts index b2abf6b7ed126..951d733836528 100644 --- a/x-pack/plugins/apm/server/routes/alerts/register_apm_rule_types.ts +++ b/x-pack/plugins/apm/server/routes/alerts/register_apm_rule_types.ts @@ -7,16 +7,70 @@ import { Observable } from 'rxjs'; import { IBasePath, Logger } from '@kbn/core/server'; -import { PluginSetupContract as AlertingPluginSetupContract } from '@kbn/alerting-plugin/server'; +import { + PluginSetupContract as AlertingPluginSetupContract, + type IRuleTypeAlerts, +} from '@kbn/alerting-plugin/server'; import { ObservabilityPluginSetup } from '@kbn/observability-plugin/server'; import { IRuleDataClient } from '@kbn/rule-registry-plugin/server'; import { MlPluginSetup } from '@kbn/ml-plugin/server'; +import { legacyExperimentalFieldMap } from '@kbn/alerts-as-data-utils'; +import { + AGENT_NAME, + PROCESSOR_EVENT, + SERVICE_ENVIRONMENT, + SERVICE_LANGUAGE_NAME, + SERVICE_NAME, + TRANSACTION_TYPE, +} from '../../../common/es_fields/apm'; import { registerTransactionDurationRuleType } from './rule_types/transaction_duration/register_transaction_duration_rule_type'; import { registerAnomalyRuleType } from './rule_types/anomaly/register_anomaly_rule_type'; import { registerErrorCountRuleType } from './rule_types/error_count/register_error_count_rule_type'; import { APMConfig } from '../..'; import { registerTransactionErrorRateRuleType } from './rule_types/transaction_error_rate/register_transaction_error_rate_rule_type'; +export const APM_RULE_TYPE_ALERT_CONTEXT = 'observability.apm'; + +export const apmRuleTypeAlertFieldMap = { + ...legacyExperimentalFieldMap, + [SERVICE_NAME]: { + type: 'keyword', + required: false, + }, + [SERVICE_ENVIRONMENT]: { + type: 'keyword', + required: false, + }, + [TRANSACTION_TYPE]: { + type: 'keyword', + required: false, + }, + [PROCESSOR_EVENT]: { + type: 'keyword', + required: false, + }, + [AGENT_NAME]: { + type: 'keyword', + required: false, + }, + [SERVICE_LANGUAGE_NAME]: { + type: 'keyword', + required: false, + }, + labels: { + type: 'object', + dynamic: true, + required: false, + }, +}; + +// Defines which alerts-as-data index alerts will use +export const ApmRuleTypeAlertDefinition: IRuleTypeAlerts = { + context: APM_RULE_TYPE_ALERT_CONTEXT, + mappings: { fieldMap: apmRuleTypeAlertFieldMap }, + useLegacyAlerts: true, +}; + export interface RegisterRuleDependencies { alerting: AlertingPluginSetupContract; basePath: IBasePath; diff --git a/x-pack/plugins/apm/server/routes/alerts/rule_types/anomaly/register_anomaly_rule_type.ts b/x-pack/plugins/apm/server/routes/alerts/rule_types/anomaly/register_anomaly_rule_type.ts index a10d803ef6d86..1870ee8778356 100644 --- a/x-pack/plugins/apm/server/routes/alerts/rule_types/anomaly/register_anomaly_rule_type.ts +++ b/x-pack/plugins/apm/server/routes/alerts/rule_types/anomaly/register_anomaly_rule_type.ts @@ -45,7 +45,10 @@ import { asMutableArray } from '../../../../../common/utils/as_mutable_array'; import { getAlertUrlTransaction } from '../../../../../common/utils/formatters'; import { getMLJobs } from '../../../service_map/get_service_anomalies'; import { apmActionVariables } from '../../action_variables'; -import { RegisterRuleDependencies } from '../../register_apm_rule_types'; +import { + ApmRuleTypeAlertDefinition, + RegisterRuleDependencies, +} from '../../register_apm_rule_types'; import { getServiceGroupFieldsForAnomaly } from './get_service_group_fields_for_anomaly'; import { anomalyParamsSchema } from '../../../../../common/rules/schema'; @@ -322,6 +325,7 @@ export function registerAnomalyRuleType({ return { state: {} }; }, + alerts: ApmRuleTypeAlertDefinition, }) ); } diff --git a/x-pack/plugins/apm/server/routes/alerts/rule_types/error_count/register_error_count_rule_type.ts b/x-pack/plugins/apm/server/routes/alerts/rule_types/error_count/register_error_count_rule_type.ts index c811e71fe1f17..dee4c0e3cb950 100644 --- a/x-pack/plugins/apm/server/routes/alerts/rule_types/error_count/register_error_count_rule_type.ts +++ b/x-pack/plugins/apm/server/routes/alerts/rule_types/error_count/register_error_count_rule_type.ts @@ -42,7 +42,10 @@ import { getAlertUrlErrorCount } from '../../../../../common/utils/formatters'; import { getApmIndices } from '../../../settings/apm_indices/get_apm_indices'; import { apmActionVariables } from '../../action_variables'; import { alertingEsClient } from '../../alerting_es_client'; -import { RegisterRuleDependencies } from '../../register_apm_rule_types'; +import { + ApmRuleTypeAlertDefinition, + RegisterRuleDependencies, +} from '../../register_apm_rule_types'; import { getServiceGroupFields, getServiceGroupFieldsAgg, @@ -221,6 +224,7 @@ export function registerErrorCountRuleType({ return { state: {} }; }, + alerts: ApmRuleTypeAlertDefinition, }) ); } diff --git a/x-pack/plugins/apm/server/routes/alerts/rule_types/transaction_duration/register_transaction_duration_rule_type.ts b/x-pack/plugins/apm/server/routes/alerts/rule_types/transaction_duration/register_transaction_duration_rule_type.ts index bc11cee03a506..3df41732c6e11 100644 --- a/x-pack/plugins/apm/server/routes/alerts/rule_types/transaction_duration/register_transaction_duration_rule_type.ts +++ b/x-pack/plugins/apm/server/routes/alerts/rule_types/transaction_duration/register_transaction_duration_rule_type.ts @@ -53,7 +53,10 @@ import { import { getApmIndices } from '../../../settings/apm_indices/get_apm_indices'; import { apmActionVariables } from '../../action_variables'; import { alertingEsClient } from '../../alerting_es_client'; -import { RegisterRuleDependencies } from '../../register_apm_rule_types'; +import { + ApmRuleTypeAlertDefinition, + RegisterRuleDependencies, +} from '../../register_apm_rule_types'; import { getServiceGroupFields, getServiceGroupFieldsAgg, @@ -293,6 +296,7 @@ export function registerTransactionDurationRuleType({ return { state: {} }; }, + alerts: ApmRuleTypeAlertDefinition, }); alerting.registerType(ruleType); diff --git a/x-pack/plugins/apm/server/routes/alerts/rule_types/transaction_error_rate/register_transaction_error_rate_rule_type.ts b/x-pack/plugins/apm/server/routes/alerts/rule_types/transaction_error_rate/register_transaction_error_rate_rule_type.ts index df94a3e8a3c75..305419e9ea362 100644 --- a/x-pack/plugins/apm/server/routes/alerts/rule_types/transaction_error_rate/register_transaction_error_rate_rule_type.ts +++ b/x-pack/plugins/apm/server/routes/alerts/rule_types/transaction_error_rate/register_transaction_error_rate_rule_type.ts @@ -51,7 +51,10 @@ import { getDocumentTypeFilterForTransactions } from '../../../../lib/helpers/tr import { getApmIndices } from '../../../settings/apm_indices/get_apm_indices'; import { apmActionVariables } from '../../action_variables'; import { alertingEsClient } from '../../alerting_es_client'; -import { RegisterRuleDependencies } from '../../register_apm_rule_types'; +import { + ApmRuleTypeAlertDefinition, + RegisterRuleDependencies, +} from '../../register_apm_rule_types'; import { getServiceGroupFields, getServiceGroupFieldsAgg, @@ -295,6 +298,7 @@ export function registerTransactionErrorRateRuleType({ return { state: {} }; }, + alerts: ApmRuleTypeAlertDefinition, }) ); } diff --git a/x-pack/plugins/apm/server/routes/mobile/get_mobile_terms_by_field.ts b/x-pack/plugins/apm/server/routes/mobile/get_mobile_terms_by_field.ts new file mode 100644 index 0000000000000..6f8fcbf040624 --- /dev/null +++ b/x-pack/plugins/apm/server/routes/mobile/get_mobile_terms_by_field.ts @@ -0,0 +1,74 @@ +/* + * 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 { + termQuery, + kqlQuery, + rangeQuery, +} from '@kbn/observability-plugin/server'; +import { ProcessorEvent } from '@kbn/observability-plugin/common'; +import { SERVICE_NAME } from '../../../common/es_fields/apm'; +import { environmentQuery } from '../../../common/utils/environment_query'; +import { APMEventClient } from '../../lib/helpers/create_es_client/create_apm_event_client'; + +export async function getMobileTermsByField({ + kuery, + apmEventClient, + serviceName, + environment, + start, + end, + size, + fieldName, +}: { + kuery: string; + apmEventClient: APMEventClient; + serviceName: string; + environment: string; + start: number; + end: number; + size: number; + fieldName: string; +}): Promise> { + const response = await apmEventClient.search( + `get_mobile_terms_by_${fieldName}`, + { + apm: { + events: [ProcessorEvent.transaction], + }, + body: { + track_total_hits: false, + size: 0, + query: { + bool: { + filter: [ + ...termQuery(SERVICE_NAME, serviceName), + ...rangeQuery(start, end), + ...environmentQuery(environment), + ...kqlQuery(kuery), + ], + }, + }, + aggs: { + terms: { + terms: { + field: fieldName, + size, + }, + }, + }, + }, + } + ); + + return ( + response.aggregations?.terms?.buckets?.map(({ key, doc_count: count }) => ({ + label: key as string, + count, + })) ?? [] + ); +} diff --git a/x-pack/plugins/apm/server/routes/mobile/route.ts b/x-pack/plugins/apm/server/routes/mobile/route.ts index efc562421be9b..b0ead6be216a1 100644 --- a/x-pack/plugins/apm/server/routes/mobile/route.ts +++ b/x-pack/plugins/apm/server/routes/mobile/route.ts @@ -6,6 +6,7 @@ */ import * as t from 'io-ts'; +import { toNumberRt } from '@kbn/io-ts-utils'; import { getApmEventClient } from '../../lib/helpers/get_apm_event_client'; import { createApmServerRoute } from '../apm_routes/create_apm_server_route'; import { environmentRt, kueryRt, rangeRt } from '../default_api_types'; @@ -21,6 +22,7 @@ import { getMobileLocationStatsPeriods, MobileLocationStats, } from './get_mobile_location_stats'; +import { getMobileTermsByField } from './get_mobile_terms_by_field'; const mobileFiltersRoute = createApmServerRoute({ endpoint: 'GET /internal/apm/services/{serviceName}/mobile/filters', @@ -219,10 +221,53 @@ const httpRequestsChartRoute = createApmServerRoute({ }, }); +const mobileTermsByFieldRoute = createApmServerRoute({ + endpoint: 'GET /internal/apm/mobile-services/{serviceName}/terms', + params: t.type({ + path: t.type({ + serviceName: t.string, + }), + query: t.intersection([ + kueryRt, + rangeRt, + environmentRt, + t.type({ + size: toNumberRt, + fieldName: t.string, + }), + ]), + }), + options: { tags: ['access:apm'] }, + handler: async ( + resources + ): Promise<{ + terms: Awaited>; + }> => { + const apmEventClient = await getApmEventClient(resources); + const { params } = resources; + const { serviceName } = params.path; + const { kuery, environment, start, end, size, fieldName } = params.query; + + const terms = await getMobileTermsByField({ + kuery, + environment, + start, + end, + serviceName, + apmEventClient, + fieldName, + size, + }); + + return { terms }; + }, +}); + export const mobileRouteRepository = { ...mobileFiltersRoute, ...sessionsChartRoute, ...httpRequestsChartRoute, ...mobileStatsRoute, ...mobileLocationStatsRoute, + ...mobileTermsByFieldRoute, }; diff --git a/x-pack/plugins/apm/tsconfig.json b/x-pack/plugins/apm/tsconfig.json index 4fc45adafd44d..149b43ebc6483 100644 --- a/x-pack/plugins/apm/tsconfig.json +++ b/x-pack/plugins/apm/tsconfig.json @@ -80,6 +80,7 @@ "@kbn/core-saved-objects-api-server", "@kbn/safer-lodash-set", "@kbn/shared-ux-router", + "@kbn/alerts-as-data-utils", ], "exclude": [ "target/**/*", diff --git a/x-pack/plugins/cases/server/client/attachments/add.ts b/x-pack/plugins/cases/server/client/attachments/add.ts index 03b228333fcfb..920adca9dc1bf 100644 --- a/x-pack/plugins/cases/server/client/attachments/add.ts +++ b/x-pack/plugins/cases/server/client/attachments/add.ts @@ -12,10 +12,6 @@ import { identity } from 'fp-ts/lib/function'; import { SavedObjectsUtils } from '@kbn/core/server'; -import { - isCommentRequestTypeExternalReference, - isCommentRequestTypePersistableState, -} from '../../../common/utils/attachments'; import type { CaseResponse } from '../../../common/api'; import { CommentRequestRt, throwErrors } from '../../../common/api'; @@ -26,6 +22,7 @@ import type { CasesClientArgs } from '..'; import { decodeCommentRequest } from '../utils'; import { Operations } from '../../authorization'; import type { AddArgs } from './types'; +import { validateRegisteredAttachments } from './validators'; /** * Create an attachment to a case. @@ -58,23 +55,11 @@ export const addComment = async ( entities: [{ owner: comment.owner, id: savedObjectID }], }); - if ( - isCommentRequestTypeExternalReference(query) && - !externalReferenceAttachmentTypeRegistry.has(query.externalReferenceAttachmentTypeId) - ) { - throw Boom.badRequest( - `Attachment type ${query.externalReferenceAttachmentTypeId} is not registered.` - ); - } - - if ( - isCommentRequestTypePersistableState(query) && - !persistableStateAttachmentTypeRegistry.has(query.persistableStateAttachmentTypeId) - ) { - throw Boom.badRequest( - `Attachment type ${query.persistableStateAttachmentTypeId} is not registered.` - ); - } + validateRegisteredAttachments({ + query, + persistableStateAttachmentTypeRegistry, + externalReferenceAttachmentTypeRegistry, + }); const createdDate = new Date().toISOString(); diff --git a/x-pack/plugins/cases/server/client/attachments/bulk_create.ts b/x-pack/plugins/cases/server/client/attachments/bulk_create.ts index 4087c2f071605..1213f9a373b7c 100644 --- a/x-pack/plugins/cases/server/client/attachments/bulk_create.ts +++ b/x-pack/plugins/cases/server/client/attachments/bulk_create.ts @@ -23,6 +23,7 @@ import { decodeCommentRequest } from '../utils'; import type { OwnerEntity } from '../../authorization'; import { Operations } from '../../authorization'; import type { BulkCreateArgs } from './types'; +import { validateRegisteredAttachments } from './validators'; /** * Create an attachment to a case. @@ -40,10 +41,20 @@ export const bulkCreate = async ( fold(throwErrors(Boom.badRequest), identity) ); - const { logger, authorization, externalReferenceAttachmentTypeRegistry } = clientArgs; + const { + logger, + authorization, + externalReferenceAttachmentTypeRegistry, + persistableStateAttachmentTypeRegistry, + } = clientArgs; attachments.forEach((attachment) => { decodeCommentRequest(attachment, externalReferenceAttachmentTypeRegistry); + validateRegisteredAttachments({ + query: attachment, + persistableStateAttachmentTypeRegistry, + externalReferenceAttachmentTypeRegistry, + }); }); try { diff --git a/x-pack/plugins/cases/server/client/attachments/validators.ts b/x-pack/plugins/cases/server/client/attachments/validators.ts new file mode 100644 index 0000000000000..4a66d32c31c19 --- /dev/null +++ b/x-pack/plugins/cases/server/client/attachments/validators.ts @@ -0,0 +1,43 @@ +/* + * 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 Boom from '@hapi/boom'; +import { + isCommentRequestTypeExternalReference, + isCommentRequestTypePersistableState, +} from '../../../common/utils/attachments'; +import type { CommentRequest } from '../../../common/api'; +import type { ExternalReferenceAttachmentTypeRegistry } from '../../attachment_framework/external_reference_registry'; +import type { PersistableStateAttachmentTypeRegistry } from '../../attachment_framework/persistable_state_registry'; + +export const validateRegisteredAttachments = ({ + query, + persistableStateAttachmentTypeRegistry, + externalReferenceAttachmentTypeRegistry, +}: { + query: CommentRequest; + persistableStateAttachmentTypeRegistry: PersistableStateAttachmentTypeRegistry; + externalReferenceAttachmentTypeRegistry: ExternalReferenceAttachmentTypeRegistry; +}) => { + if ( + isCommentRequestTypeExternalReference(query) && + !externalReferenceAttachmentTypeRegistry.has(query.externalReferenceAttachmentTypeId) + ) { + throw Boom.badRequest( + `Attachment type ${query.externalReferenceAttachmentTypeId} is not registered.` + ); + } + + if ( + isCommentRequestTypePersistableState(query) && + !persistableStateAttachmentTypeRegistry.has(query.persistableStateAttachmentTypeId) + ) { + throw Boom.badRequest( + `Attachment type ${query.persistableStateAttachmentTypeId} is not registered.` + ); + } +}; diff --git a/x-pack/plugins/cases/server/client/cases/push.ts b/x-pack/plugins/cases/server/client/cases/push.ts index 177f6f2ec6ce6..4f3ab74557278 100644 --- a/x-pack/plugins/cases/server/client/cases/push.ts +++ b/x-pack/plugins/cases/server/client/cases/push.ts @@ -11,6 +11,7 @@ import type { SavedObjectsFindResponse } from '@kbn/core/server'; import type { UserProfile } from '@kbn/security-plugin/common'; import type { SecurityPluginStart } from '@kbn/security-plugin/server'; +import { asSavedObjectExecutionSource } from '@kbn/actions-plugin/server'; import type { ActionConnector, CaseResponse, @@ -26,7 +27,7 @@ import { OWNER_FIELD, CommentType, } from '../../../common/api'; -import { CASE_COMMENT_SAVED_OBJECT } from '../../../common/constants'; +import { CASE_COMMENT_SAVED_OBJECT, CASE_SAVED_OBJECT } from '../../../common/constants'; import { createIncident, getDurationInSeconds, getUserProfiles } from './utils'; import { createCaseError } from '../../common/error'; @@ -164,6 +165,7 @@ export const push = async ( subAction: 'pushToService', subActionParams: externalServiceIncident, }, + source: asSavedObjectExecutionSource({ id: caseId, type: CASE_SAVED_OBJECT }), }); if (pushRes.status === 'error') { diff --git a/x-pack/plugins/cases/server/index.ts b/x-pack/plugins/cases/server/index.ts index ea2adeb1ca6d8..d039d7ebd41a1 100644 --- a/x-pack/plugins/cases/server/index.ts +++ b/x-pack/plugins/cases/server/index.ts @@ -23,5 +23,4 @@ export const config: PluginConfigDescriptor = { export const plugin = (initializerContext: PluginInitializerContext) => new CasePlugin(initializerContext); -export type { PluginSetupContract } from './types'; -export type { PluginStartContract } from './types'; +export type { CasesSetup, CasesStart } from './types'; diff --git a/x-pack/plugins/cases/server/mocks.ts b/x-pack/plugins/cases/server/mocks.ts index c68606bad456e..6a4d76cf035c8 100644 --- a/x-pack/plugins/cases/server/mocks.ts +++ b/x-pack/plugins/cases/server/mocks.ts @@ -10,6 +10,8 @@ import type { CaseSavedObject } from './common/types'; import type { CasePostRequest, CommentAttributes } from '../common/api'; import { CaseSeverity, CaseStatuses, CommentType, ConnectorTypes } from '../common/api'; import { SECURITY_SOLUTION_OWNER } from '../common/constants'; +import type { CasesStart } from './types'; +import { createCasesClientMock } from './client/mocks'; export const mockCases: CaseSavedObject[] = [ { @@ -417,3 +419,15 @@ export const newCase: CasePostRequest = { }, owner: SECURITY_SOLUTION_OWNER, }; + +const casesClientMock = createCasesClientMock(); + +export const mockCasesContract = (): CasesStart => ({ + getCasesClientWithRequest: jest.fn().mockResolvedValue(casesClientMock), + getExternalReferenceAttachmentTypeRegistry: jest.fn(), + getPersistableStateAttachmentTypeRegistry: jest.fn(), +}); + +export const casesPluginMock = { + createStartContract: mockCasesContract, +}; diff --git a/x-pack/plugins/cases/server/plugin.ts b/x-pack/plugins/cases/server/plugin.ts index 4f98d8604cefc..4655748faeed3 100644 --- a/x-pack/plugins/cases/server/plugin.ts +++ b/x-pack/plugins/cases/server/plugin.ts @@ -46,7 +46,7 @@ import { } from './saved_object_types'; import type { CasesClient } from './client'; -import type { CasesRequestHandlerContext, PluginSetupContract, PluginStartContract } from './types'; +import type { CasesRequestHandlerContext, CasesSetup, CasesStart } from './types'; import { CasesClientFactory } from './client/factory'; import { getCasesKibanaFeature } from './features'; import { registerRoutes } from './routes/api/register_routes'; @@ -101,7 +101,7 @@ export class CasePlugin { this.userProfileService = new UserProfileService(this.logger); } - public setup(core: CoreSetup, plugins: PluginsSetup): PluginSetupContract { + public setup(core: CoreSetup, plugins: PluginsSetup): CasesSetup { this.logger.debug( `Setting up Case Workflow with core contract [${Object.keys( core @@ -176,7 +176,7 @@ export class CasePlugin { }; } - public start(core: CoreStart, plugins: PluginsStart): PluginStartContract { + public start(core: CoreStart, plugins: PluginsStart): CasesStart { this.logger.debug(`Starting Case Workflow`); if (plugins.taskManager) { @@ -226,6 +226,9 @@ export class CasePlugin { return { getCasesClientWithRequest, + getExternalReferenceAttachmentTypeRegistry: () => + this.externalReferenceAttachmentTypeRegistry, + getPersistableStateAttachmentTypeRegistry: () => this.persistableStateAttachmentTypeRegistry, }; } diff --git a/x-pack/plugins/cases/server/types.ts b/x-pack/plugins/cases/server/types.ts index ba6d64dc88668..d0dbeef6c1eb4 100644 --- a/x-pack/plugins/cases/server/types.ts +++ b/x-pack/plugins/cases/server/types.ts @@ -14,6 +14,8 @@ import type { } from '@kbn/actions-plugin/server/types'; import type { CasesClient } from './client'; import type { AttachmentFramework } from './attachment_framework/types'; +import type { ExternalReferenceAttachmentTypeRegistry } from './attachment_framework/external_reference_registry'; +import type { PersistableStateAttachmentTypeRegistry } from './attachment_framework/persistable_state_registry'; export interface CaseRequestContext { getCasesClient: () => Promise; @@ -43,7 +45,7 @@ export type RegisterActionType = < /** * Cases server exposed contract for interacting with cases entities. */ -export interface PluginStartContract { +export interface CasesStart { /** * Returns a client which can be used to interact with the cases backend entities. * @@ -51,9 +53,11 @@ export interface PluginStartContract { * @returns a {@link CasesClient} */ getCasesClientWithRequest(request: KibanaRequest): Promise; + getExternalReferenceAttachmentTypeRegistry(): ExternalReferenceAttachmentTypeRegistry; + getPersistableStateAttachmentTypeRegistry(): PersistableStateAttachmentTypeRegistry; } -export interface PluginSetupContract { +export interface CasesSetup { attachmentFramework: AttachmentFramework; } diff --git a/x-pack/plugins/cloud_security_posture/public/assets/illustrations/no_data_illustration.svg b/x-pack/plugins/cloud_security_posture/public/assets/illustrations/no_data_illustration.svg index 025262d2513b0..74a2835d429a7 100644 --- a/x-pack/plugins/cloud_security_posture/public/assets/illustrations/no_data_illustration.svg +++ b/x-pack/plugins/cloud_security_posture/public/assets/illustrations/no_data_illustration.svg @@ -1 +1,49 @@ - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/x-pack/plugins/cloud_security_posture/public/pages/compliance_dashboard/dashboard_sections/benchmarks_section.tsx b/x-pack/plugins/cloud_security_posture/public/pages/compliance_dashboard/dashboard_sections/benchmarks_section.tsx index 036bf86003d2e..668dcfcfae0bc 100644 --- a/x-pack/plugins/cloud_security_posture/public/pages/compliance_dashboard/dashboard_sections/benchmarks_section.tsx +++ b/x-pack/plugins/cloud_security_posture/public/pages/compliance_dashboard/dashboard_sections/benchmarks_section.tsx @@ -39,7 +39,7 @@ const CLUSTER_DEFAULT_SORT_ORDER = 'asc'; export const getClusterIdQuery = (cluster: Cluster): NavFilter => { if (cluster.meta.benchmark.posture_type === CSPM_POLICY_TEMPLATE) { // TODO: remove assertion after typing CspFinding as discriminating union - return { 'cloud.account.name': cluster.meta.cloud!.account.name }; + return { 'cloud.account.id': cluster.meta.cloud!.account.id }; } return { cluster_id: cluster.meta.assetIdentifierId }; }; diff --git a/x-pack/plugins/enterprise_search/common/constants.ts b/x-pack/plugins/enterprise_search/common/constants.ts index 091dc88b5a689..3071e9569d13f 100644 --- a/x-pack/plugins/enterprise_search/common/constants.ts +++ b/x-pack/plugins/enterprise_search/common/constants.ts @@ -154,3 +154,10 @@ export const DEFAULT_PIPELINE_VALUES: IngestPipelineParams = { reduce_whitespace: true, run_ml_inference: false, }; + +export enum INGESTION_METHOD_IDS { + api = 'api', + connector = 'connector', + crawler = 'crawler', + native_connector = 'native_connector', +} diff --git a/x-pack/plugins/enterprise_search/common/guided_onboarding/search_guide_config.ts b/x-pack/plugins/enterprise_search/common/guided_onboarding/search_guide_config.ts index b7926bc01f349..7d0473d252401 100644 --- a/x-pack/plugins/enterprise_search/common/guided_onboarding/search_guide_config.ts +++ b/x-pack/plugins/enterprise_search/common/guided_onboarding/search_guide_config.ts @@ -5,77 +5,93 @@ * 2.0. */ -import type { GuideConfig } from '@kbn/guided-onboarding'; +import type { GuideConfig, StepConfig } from '@kbn/guided-onboarding'; import { i18n } from '@kbn/i18n'; +import { INGESTION_METHOD_IDS } from '../constants'; + export const appSearchGuideId = 'appSearch'; export const websiteSearchGuideId = 'websiteSearch'; export const databaseSearchGuideId = 'databaseSearch'; -const guideConfig: Omit = { - title: i18n.translate('xpack.enterpriseSearch.guideConfig.title', { - defaultMessage: 'Build search experiences with Elasticsearch', - }), - description: i18n.translate('xpack.enterpriseSearch.guideConfig.description', { - defaultMessage: `We'll help you build a search experience with your data using Elastic's web crawler, connectors, and APIs.`, - }), - guideName: 'Enterprise Search', - steps: [ - { - id: 'add_data', - title: i18n.translate('xpack.enterpriseSearch.guideConfig.addDataStep.title', { - defaultMessage: 'Add data', - }), - description: i18n.translate('xpack.enterpriseSearch.guideConfig.addDataStep.description', { - defaultMessage: - 'Ingest your data, create an index, and enrich your data with customizable ingest and inference pipelines.', - }), - location: { - appID: 'enterpriseSearchContent', - path: '/search_indices/new_index', - }, + +const apiMethods = { + [appSearchGuideId]: INGESTION_METHOD_IDS.api, + [databaseSearchGuideId]: INGESTION_METHOD_IDS.native_connector, + [websiteSearchGuideId]: INGESTION_METHOD_IDS.crawler, +}; + +export type EnterpriseSearchGuideIds = + | typeof appSearchGuideId + | typeof websiteSearchGuideId + | typeof databaseSearchGuideId; + +const getAddDataStep: (method?: INGESTION_METHOD_IDS) => StepConfig = (method) => { + return { + id: 'add_data', + title: i18n.translate('xpack.enterpriseSearch.guideConfig.addDataStep.title', { + defaultMessage: 'Add data', + }), + description: i18n.translate('xpack.enterpriseSearch.guideConfig.addDataStep.description', { + defaultMessage: + 'Ingest your data, create an index, and enrich your data with customizable ingest and inference pipelines.', + }), + location: { + appID: 'enterpriseSearchContent', + path: `/search_indices/new_index?${method ? 'method=' + method : ''}`, }, - { - id: 'search_experience', - title: i18n.translate('xpack.enterpriseSearch.guideConfig.searchExperienceStep.title', { - defaultMessage: 'Build a search experience', - }), + }; +}; + +const getSearchExperienceStep: () => StepConfig = () => { + return { + id: 'search_experience', + title: i18n.translate('xpack.enterpriseSearch.guideConfig.searchExperienceStep.title', { + defaultMessage: 'Build a search experience', + }), + description: i18n.translate( + 'xpack.enterpriseSearch.guideConfig.searchExperienceStep.description', + { + defaultMessage: `Learn more about Elastic's Search UI, try our Search UI tutorial for Elasticsearch, and build a search experience.`, + } + ), + location: { + appID: 'searchExperiences', + path: '', + }, + manualCompletion: { + title: i18n.translate( + 'xpack.enterpriseSearch.guideConfig.searchExperienceStep.manualCompletionPopoverTitle', + { + defaultMessage: 'Explore Search UI', + } + ), description: i18n.translate( - 'xpack.enterpriseSearch.guideConfig.searchExperienceStep.description', + 'xpack.enterpriseSearch.guideConfig.searchExperienceStep.manualCompletionPopoverDescription', { - defaultMessage: `Learn more about Elastic's Search UI, try our Search UI tutorial for Elasticsearch, and build a search experience.`, + defaultMessage: `Take your time to explore how to use Search UI to build world-class search experiences. When you're ready, click the Setup guide button to continue.`, } ), - location: { - appID: 'searchExperiences', - path: '', - }, - manualCompletion: { - title: i18n.translate( - 'xpack.enterpriseSearch.guideConfig.searchExperienceStep.manualCompletionPopoverTitle', - { - defaultMessage: 'Explore Search UI', - } - ), - description: i18n.translate( - 'xpack.enterpriseSearch.guideConfig.searchExperienceStep.manualCompletionPopoverDescription', - { - defaultMessage: `Take your time to explore how to use Search UI to build world-class search experiences. When you're ready, click the Setup guide button to continue.`, - } - ), - readyToCompleteOnNavigation: true, - }, + readyToCompleteOnNavigation: true, }, - ], -}; -export const appSearchGuideConfig: GuideConfig = { - ...guideConfig, - telemetryId: 'appSearch', -}; -export const websiteSearchGuideConfig: GuideConfig = { - ...guideConfig, - telemetryId: 'websiteSearch', + }; }; -export const databaseSearchGuideConfig: GuideConfig = { - ...guideConfig, - telemetryId: 'databaseSearch', + +const getGuideConfig: (telemetryId: EnterpriseSearchGuideIds) => GuideConfig = (telemetryId) => { + return { + telemetryId, + title: i18n.translate('xpack.enterpriseSearch.guideConfig.title', { + defaultMessage: 'Build search experiences with Elasticsearch', + }), + description: i18n.translate('xpack.enterpriseSearch.guideConfig.description', { + defaultMessage: `We'll help you build a search experience with your data using Elastic's web crawler, connectors, and APIs.`, + }), + guideName: 'Enterprise Search', + steps: [getAddDataStep(apiMethods[telemetryId]), getSearchExperienceStep()], + }; }; + +export const appSearchGuideConfig: GuideConfig = getGuideConfig(appSearchGuideId); + +export const websiteSearchGuideConfig: GuideConfig = getGuideConfig(websiteSearchGuideId); + +export const databaseSearchGuideConfig: GuideConfig = getGuideConfig(databaseSearchGuideId); diff --git a/x-pack/plugins/enterprise_search/common/types/engines.ts b/x-pack/plugins/enterprise_search/common/types/engines.ts index ae230a2531c0c..2c59f74e42f02 100644 --- a/x-pack/plugins/enterprise_search/common/types/engines.ts +++ b/x-pack/plugins/enterprise_search/common/types/engines.ts @@ -35,7 +35,6 @@ export interface EnterpriseSearchEngineIndex { count: number; health: HealthStatus | 'unknown'; name: string; - source: 'api' | 'connector' | 'crawler'; } export interface EnterpriseSearchEngineFieldCapabilities { diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/engine/engine_indices.tsx b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/engine/engine_indices.tsx index 6c95f73ec12f8..4b7c799659833 100644 --- a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/engine/engine_indices.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/engine/engine_indices.tsx @@ -33,8 +33,6 @@ import { EuiLinkTo } from '../../../shared/react_router_helpers'; import { TelemetryLogic } from '../../../shared/telemetry/telemetry_logic'; import { SEARCH_INDEX_PATH, EngineViewTabs } from '../../routes'; -import { IngestionMethod } from '../../types'; -import { ingestionMethodToText } from '../../utils/indices'; import { EnterpriseSearchEnginesPageTemplate } from '../layout/engines_page_template'; @@ -135,20 +133,6 @@ export const EngineIndices: React.FC = () => { truncateText: true, width: '15%', }, - { - field: 'source', - name: i18n.translate( - 'xpack.enterpriseSearch.content.engine.indices.ingestionMethod.columnTitle', - { - defaultMessage: 'Ingestion method', - } - ), - render: (source: IngestionMethod) => ( - {ingestionMethodToText(source)} - ), - truncateText: true, - width: '15%', - }, { actions: [ { diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/engine/engine_indices_logic.test.ts b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/engine/engine_indices_logic.test.ts index bf088988ed125..eea25db76b676 100644 --- a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/engine/engine_indices_logic.test.ts +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/engine/engine_indices_logic.test.ts @@ -26,13 +26,11 @@ const mockEngineData: EnterpriseSearchEngineDetails = { count: 10, health: 'green', name: 'search-001', - source: 'api', }, { count: 1000, health: 'yellow', name: 'search-002', - source: 'crawler', }, ], name: DEFAULT_VALUES.engineName, diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/engine/engine_overview_logic.test.ts b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/engine/engine_overview_logic.test.ts index f0d4f2a8fac73..4093898baeef4 100644 --- a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/engine/engine_overview_logic.test.ts +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/engine/engine_overview_logic.test.ts @@ -76,13 +76,11 @@ describe('EngineOverviewLogic', () => { count: 10, health: 'green', name: 'index-001', - source: 'api', }, { count: 10, health: 'green', name: 'index-002', - source: 'api', }, ]; const engineData = { @@ -101,11 +99,11 @@ describe('EngineOverviewLogic', () => { it('returns the number of indices', () => { const noIndices: EnterpriseSearchEngineIndex[] = []; const oneIndex = [ - { count: 23, health: 'unknown', name: 'index-001', source: 'api' }, + { count: 23, health: 'unknown', name: 'index-001' }, ] as EnterpriseSearchEngineIndex[]; const twoIndices = [ - { count: 23, health: 'unknown', name: 'index-001', source: 'api' }, - { count: 92, health: 'unknown', name: 'index-002', source: 'api' }, + { count: 23, health: 'unknown', name: 'index-001' }, + { count: 92, health: 'unknown', name: 'index-002' }, ] as EnterpriseSearchEngineIndex[]; expect(selectIndicesCount(noIndices)).toBe(0); @@ -130,19 +128,16 @@ describe('EngineOverviewLogic', () => { count: 12, health: 'unknown', name: 'index-001', - source: 'api', }, { count: 34, health: 'unknown', name: 'index-002', - source: 'crawler', }, { count: 56, health: 'unknown', name: 'index-003', - source: 'api', }, ] as EnterpriseSearchEngineIndex[]; it('returns true', () => { @@ -156,19 +151,16 @@ describe('EngineOverviewLogic', () => { count: 12, health: 'unknown', name: 'index-001', - source: 'api', }, { count: 34, health: 'yellow', name: 'index-002', - source: 'crawler', }, { count: 56, health: 'green', name: 'index-003', - source: 'api', }, ] as EnterpriseSearchEngineIndex[]; it('returns true', () => { @@ -182,19 +174,16 @@ describe('EngineOverviewLogic', () => { count: 12, health: 'unknown', name: 'index-001', - source: 'api', }, { count: 34, health: 'yellow', name: 'index-002', - source: 'crawler', }, { count: 56, health: 'unknown', name: 'index-003', - source: 'api', }, ] as EnterpriseSearchEngineIndex[]; it('returns true', () => { @@ -208,19 +197,16 @@ describe('EngineOverviewLogic', () => { count: 12, health: 'green', name: 'index-001', - source: 'api', }, { count: 34, health: 'yellow', name: 'index-002', - source: 'crawler', }, { count: 56, health: 'green', name: 'index-003', - source: 'api', }, ] as EnterpriseSearchEngineIndex[]; it('returns false', () => { @@ -245,7 +231,6 @@ describe('EngineOverviewLogic', () => { count: 23, health: 'green', name: 'index-001', - source: 'crawler', }, ] as EnterpriseSearchEngineIndex[]) ).toBe(23); @@ -258,13 +243,11 @@ describe('EngineOverviewLogic', () => { count: 23, health: 'green', name: 'index-001', - source: 'crawler', }, { count: 45, health: 'green', name: 'index-002', - source: 'crawler', }, ] as EnterpriseSearchEngineIndex[]) ).toBe(68); @@ -277,19 +260,16 @@ describe('EngineOverviewLogic', () => { count: 23, health: 'green', name: 'index-001', - source: 'crawler', }, { count: null, health: 'unknown', name: 'index-002', - source: 'crawler', }, { count: 45, health: 'green', name: 'index-002', - source: 'crawler', }, ] as EnterpriseSearchEngineIndex[]) ).toBe(68); diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/engines/engines_list_flyout.tsx b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/engines/engines_list_flyout.tsx index db1ebaf088a00..52a5db798e84d 100644 --- a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/engines/engines_list_flyout.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/engines/engines_list_flyout.tsx @@ -33,8 +33,6 @@ import { healthColorsMap } from '../../../shared/constants/health_colors'; import { generateEncodedPath } from '../../../shared/encode_path_params'; import { EuiLinkTo } from '../../../shared/react_router_helpers'; import { SEARCH_INDEX_PATH } from '../../routes'; -import { IngestionMethod } from '../../types'; -import { ingestionMethodToText } from '../../utils/indices'; import { EngineError } from '../engine/engine_error'; @@ -107,20 +105,6 @@ export const EngineListIndicesFlyout: React.FC = () => { truncateText: true, width: '15%', }, - { - field: 'source', - name: i18n.translate( - 'xpack.enterpriseSearch.content.enginesList.indicesFlyout.table.ingestionMethod.columnTitle', - { - defaultMessage: 'Ingestion method', - } - ), - render: (source: IngestionMethod) => ( - {ingestionMethodToText(source)} - ), - truncateText: true, - width: '15%', - }, ]; if (isFetchEngineFlyoutVisible) { diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/engines/engines_list_flyout_logic.test.ts b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/engines/engines_list_flyout_logic.test.ts index a3973565c4306..647e7f2660599 100644 --- a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/engines/engines_list_flyout_logic.test.ts +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/engines/engines_list_flyout_logic.test.ts @@ -30,13 +30,11 @@ const mockEngineData: EnterpriseSearchEngineDetails = { count: 10, health: 'green', name: 'search-001', - source: 'api', }, { count: 1000, health: 'yellow', name: 'search-002', - source: 'crawler', }, ], name: 'my-test-engine', diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/new_index/new_index.tsx b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/new_index/new_index.tsx index 21df7e3ad82eb..6377ba4d2c879 100644 --- a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/new_index/new_index.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/new_index/new_index.tsx @@ -20,6 +20,8 @@ import { } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; +import { INGESTION_METHOD_IDS } from '../../../../../common/constants'; + import { BETA_LABEL } from '../../../shared/constants/labels'; import { parseQueryParams } from '../../../shared/query_params'; import { EuiLinkTo } from '../../../shared/react_router_helpers'; @@ -33,13 +35,6 @@ import { MethodApi } from './method_api/method_api'; import { MethodConnector } from './method_connector/method_connector'; import { MethodCrawler } from './method_crawler/method_crawler'; -export enum IngestionMethodId { - api = 'api', - connector = 'connector', - crawler = 'crawler', - native_connector = 'native_connector', -} - const betaBadge = ( {BETA_LABEL} @@ -58,7 +53,7 @@ const METHOD_BUTTON_GROUP_OPTIONS: ButtonGroupOption[] = [ defaultMessage: 'No development required', }), icon: 'globe', - id: IngestionMethodId.crawler, + id: INGESTION_METHOD_IDS.crawler, label: i18n.translate('xpack.enterpriseSearch.content.newIndex.buttonGroup.crawler.label', { defaultMessage: 'Use the web crawler', }), @@ -79,7 +74,7 @@ const METHOD_BUTTON_GROUP_OPTIONS: ButtonGroupOption[] = [ } ), icon: 'visVega', - id: IngestionMethodId.native_connector, + id: INGESTION_METHOD_IDS.native_connector, label: i18n.translate( 'xpack.enterpriseSearch.content.newIndex.buttonGroup.nativeConnector.label', { @@ -98,7 +93,7 @@ const METHOD_BUTTON_GROUP_OPTIONS: ButtonGroupOption[] = [ defaultMessage: 'Some development required', }), icon: 'visVega', - id: IngestionMethodId.api, + id: INGESTION_METHOD_IDS.api, label: i18n.translate('xpack.enterpriseSearch.content.newIndex.buttonGroup.api.label', { defaultMessage: 'Use the API', }), @@ -116,7 +111,7 @@ const METHOD_BUTTON_GROUP_OPTIONS: ButtonGroupOption[] = [ defaultMessage: 'Development required', }), icon: 'package', - id: IngestionMethodId.connector, + id: INGESTION_METHOD_IDS.connector, label: i18n.translate('xpack.enterpriseSearch.content.newIndex.buttonGroup.connector.label', { defaultMessage: 'Build a connector', }), @@ -188,12 +183,12 @@ export const NewIndex: React.FC = () => { {selectedMethod ? ( <> - {selectedMethod.id === IngestionMethodId.crawler && } - {selectedMethod.id === IngestionMethodId.api && } - {selectedMethod.id === IngestionMethodId.connector && ( + {selectedMethod.id === INGESTION_METHOD_IDS.crawler && } + {selectedMethod.id === INGESTION_METHOD_IDS.api && } + {selectedMethod.id === INGESTION_METHOD_IDS.connector && ( )} - {selectedMethod.id === IngestionMethodId.native_connector && ( + {selectedMethod.id === INGESTION_METHOD_IDS.native_connector && ( )} diff --git a/x-pack/plugins/event_log/generated/mappings.json b/x-pack/plugins/event_log/generated/mappings.json index a5212917393f8..8c61d6385d80b 100644 --- a/x-pack/plugins/event_log/generated/mappings.json +++ b/x-pack/plugins/event_log/generated/mappings.json @@ -445,6 +445,10 @@ }, "execution": { "properties": { + "source": { + "ignore_above": 1024, + "type": "keyword" + }, "uuid": { "ignore_above": 1024, "type": "keyword" diff --git a/x-pack/plugins/event_log/generated/schemas.ts b/x-pack/plugins/event_log/generated/schemas.ts index 93ee9f2977a36..27c3813dae658 100644 --- a/x-pack/plugins/event_log/generated/schemas.ts +++ b/x-pack/plugins/event_log/generated/schemas.ts @@ -201,6 +201,7 @@ export const EventSchema = schema.maybe( id: ecsString(), execution: schema.maybe( schema.object({ + source: ecsString(), uuid: ecsString(), }) ), diff --git a/x-pack/plugins/event_log/scripts/mappings.js b/x-pack/plugins/event_log/scripts/mappings.js index 36838a4208a87..ecc6a5fc30792 100644 --- a/x-pack/plugins/event_log/scripts/mappings.js +++ b/x-pack/plugins/event_log/scripts/mappings.js @@ -227,6 +227,10 @@ exports.EcsCustomPropertyMappings = { }, execution: { properties: { + source: { + ignore_above: 1024, + type: 'keyword', + }, uuid: { ignore_above: 1024, type: 'keyword', diff --git a/x-pack/plugins/file_upload/server/capabilities.test.ts b/x-pack/plugins/file_upload/server/capabilities.test.ts index bece0196f5fea..e5f0a01cd6abe 100644 --- a/x-pack/plugins/file_upload/server/capabilities.test.ts +++ b/x-pack/plugins/file_upload/server/capabilities.test.ts @@ -44,16 +44,9 @@ describe('setupCapabilities', () => { const request = httpServerMock.createKibanaRequest(); - await expect(switcher(request, capabilities, false)).resolves.toMatchInlineSnapshot(` - Object { - "catalogue": Object {}, - "fileUpload": Object { - "show": true, - }, - "management": Object {}, - "navLinks": Object {}, - } - `); + await expect(switcher(request, capabilities, false)).resolves.toMatchInlineSnapshot( + `Object {}` + ); }); it('registers a capabilities switcher that returns unaltered capabilities when default capabilities are requested', async () => { @@ -81,16 +74,7 @@ describe('setupCapabilities', () => { const request = httpServerMock.createKibanaRequest(); - await expect(switcher(request, capabilities, true)).resolves.toMatchInlineSnapshot(` - Object { - "catalogue": Object {}, - "fileUpload": Object { - "show": true, - }, - "management": Object {}, - "navLinks": Object {}, - } - `); + await expect(switcher(request, capabilities, true)).resolves.toMatchInlineSnapshot(`Object {}`); expect(security.authz.mode.useRbacForRequest).not.toHaveBeenCalled(); expect(security.authz.checkPrivilegesDynamicallyWithRequest).not.toHaveBeenCalled(); @@ -166,16 +150,9 @@ describe('setupCapabilities', () => { const request = httpServerMock.createKibanaRequest(); - await expect(switcher(request, capabilities, false)).resolves.toMatchInlineSnapshot(` - Object { - "catalogue": Object {}, - "fileUpload": Object { - "show": true, - }, - "management": Object {}, - "navLinks": Object {}, - } - `); + await expect(switcher(request, capabilities, false)).resolves.toMatchInlineSnapshot( + `Object {}` + ); expect(security.authz.mode.useRbacForRequest).toHaveBeenCalledTimes(1); expect(security.authz.mode.useRbacForRequest).toHaveBeenCalledWith(request); @@ -249,16 +226,9 @@ describe('setupCapabilities', () => { const request = httpServerMock.createKibanaRequest(); - await expect(switcher(request, capabilities, false)).resolves.toMatchInlineSnapshot(` - Object { - "catalogue": Object {}, - "fileUpload": Object { - "show": true, - }, - "management": Object {}, - "navLinks": Object {}, - } - `); + await expect(switcher(request, capabilities, false)).resolves.toMatchInlineSnapshot( + `Object {}` + ); expect(security.authz.mode.useRbacForRequest).toHaveBeenCalledTimes(1); expect(security.authz.mode.useRbacForRequest).toHaveBeenCalledWith(request); diff --git a/x-pack/plugins/file_upload/server/capabilities.ts b/x-pack/plugins/file_upload/server/capabilities.ts index 5e0f338e4ba6c..9217176fb2b25 100644 --- a/x-pack/plugins/file_upload/server/capabilities.ts +++ b/x-pack/plugins/file_upload/server/capabilities.ts @@ -22,7 +22,7 @@ export const setupCapabilities = ( core.capabilities.registerSwitcher(async (request, capabilities, useDefaultCapabilities) => { if (useDefaultCapabilities) { - return capabilities; + return {}; } const [, { security }] = await core.getStartServices(); @@ -42,6 +42,6 @@ export const setupCapabilities = ( }; } - return capabilities; + return {}; }); }; diff --git a/x-pack/plugins/fleet/common/services/validate_package_policy.test.ts b/x-pack/plugins/fleet/common/services/validate_package_policy.test.ts index d318f964ccd11..13410561e497e 100644 --- a/x-pack/plugins/fleet/common/services/validate_package_policy.test.ts +++ b/x-pack/plugins/fleet/common/services/validate_package_policy.test.ts @@ -945,4 +945,69 @@ describe('Fleet - validatePackagePolicyConfig', () => { expect(res).toBeNull(); }); }); + + describe('Select', () => { + it('should return an error message if the value is not an option value', () => { + const res = validatePackagePolicyConfig( + { + type: 'select', + value: 'c', + }, + { + name: 'myvariable', + type: 'select', + options: [ + { value: 'a', text: 'A' }, + { value: 'b', text: 'B' }, + ], + }, + 'myvariable', + safeLoad + ); + + expect(res).toEqual(['Invalid value for select type']); + }); + + it('should accept a select with a valid value', () => { + const res = validatePackagePolicyConfig( + { + type: 'select', + value: 'b', + }, + { + name: 'myvariable', + type: 'select', + options: [ + { value: 'a', text: 'A' }, + { value: 'b', text: 'B' }, + ], + }, + 'myvariable', + safeLoad + ); + + expect(res).toBeNull(); + }); + + it('should accept a select with undefined value', () => { + const res = validatePackagePolicyConfig( + { + type: 'select', + value: undefined, + }, + { + name: 'myvariable', + type: 'select', + options: [ + { value: 'a', text: 'A' }, + { value: 'b', text: 'B' }, + ], + }, + 'myvariable', + safeLoad + ); + + expect(res).toBeNull(); + }); + }); }); diff --git a/x-pack/plugins/fleet/common/services/validate_package_policy.ts b/x-pack/plugins/fleet/common/services/validate_package_policy.ts index 82fb8c82da39c..0af746ad2453e 100644 --- a/x-pack/plugins/fleet/common/services/validate_package_policy.ts +++ b/x-pack/plugins/fleet/common/services/validate_package_policy.ts @@ -337,6 +337,16 @@ export const validatePackagePolicyConfig = ( } } + if (varDef.type === 'select' && parsedValue) { + if (!varDef.options?.map((o) => o.value).includes(parsedValue)) { + errors.push( + i18n.translate('xpack.fleet.packagePolicyValidation.invalidSelectValueErrorMessage', { + defaultMessage: 'Invalid value for select type', + }) + ); + } + } + return errors.length ? errors : null; }; diff --git a/x-pack/plugins/fleet/common/types/models/epm.ts b/x-pack/plugins/fleet/common/types/models/epm.ts index db3497d20c349..96977bc351b12 100644 --- a/x-pack/plugins/fleet/common/types/models/epm.ts +++ b/x-pack/plugins/fleet/common/types/models/epm.ts @@ -378,6 +378,7 @@ export type RegistryVarType = | 'integer' | 'bool' | 'password' + | 'select' | 'text' | 'yaml' | 'string' @@ -390,6 +391,7 @@ export enum RegistryVarsEntryKeys { required = 'required', show_user = 'show_user', multi = 'multi', + options = 'options', default = 'default', os = 'os', } @@ -405,6 +407,7 @@ export interface RegistryVarsEntry { [RegistryVarsEntryKeys.required]?: boolean; [RegistryVarsEntryKeys.show_user]?: boolean; [RegistryVarsEntryKeys.multi]?: boolean; + [RegistryVarsEntryKeys.options]?: Array<{ value: string; text: string }>; [RegistryVarsEntryKeys.default]?: string | string[] | boolean; [RegistryVarsEntryKeys.os]?: { [key: string]: { diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/components/steps/components/package_policy_input_var_field.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/components/steps/components/package_policy_input_var_field.tsx index e8a8bd16ed64e..283a87fdc9e3c 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/components/steps/components/package_policy_input_var_field.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/components/steps/components/package_policy_input_var_field.tsx @@ -17,6 +17,7 @@ import { EuiFieldPassword, EuiCodeBlock, EuiTextArea, + EuiSelect, } from '@elastic/eui'; import styled from 'styled-components'; @@ -58,7 +59,7 @@ export const PackagePolicyInputVarField: React.FunctionComponent<{ isEditPage = false, }) => { const [isDirty, setIsDirty] = useState(false); - const { multi, required, type, title, name, description } = varDef; + const { multi, required, type, title, name, description, options } = varDef; const isInvalid = (isDirty || forceShowErrors) && !!varErrors; const errors = isInvalid ? varErrors : null; const fieldLabel = title || name; @@ -154,6 +155,10 @@ export const PackagePolicyInputVarField: React.FunctionComponent<{ disabled={frozen} /> ); + case 'select': + return ( + onChange(e.target.value)} /> + ); default: return ( { }, }; - const updatedMappings = { ...defaultMappings }; - await act(async () => { testBed = setup({ value: defaultMappings, onChange: onChangeHandler }); }); @@ -58,17 +56,26 @@ describe('Mappings editor: point datatype', () => { const { component, - actions: { startEditField, updateFieldAndCloseFlyout }, + actions: { startEditField, updateFieldName, updateFieldAndCloseFlyout }, } = testBed; // Open the flyout to edit the field await startEditField('myField'); + // Update the name of the field + await updateFieldName('updatedField'); + // Save the field and close the flyout await updateFieldAndCloseFlyout(); - // It should have the default parameters values added - updatedMappings.properties.myField = defaultPointParameters; + // It should have the default parameters values added for fields which are not set + const updatedMappings = { + properties: { + updatedField: { + ...defaultPointParameters, + }, + }, + }; ({ data } = await getMappingsEditorData(component)); expect(data).toEqual(updatedMappings); diff --git a/x-pack/plugins/index_management/public/application/components/mappings_editor/__jest__/client_integration/datatypes/shape_datatype.test.tsx b/x-pack/plugins/index_management/public/application/components/mappings_editor/__jest__/client_integration/datatypes/shape_datatype.test.tsx index 5d501084ef124..d38e6bb5d67cb 100644 --- a/x-pack/plugins/index_management/public/application/components/mappings_editor/__jest__/client_integration/datatypes/shape_datatype.test.tsx +++ b/x-pack/plugins/index_management/public/application/components/mappings_editor/__jest__/client_integration/datatypes/shape_datatype.test.tsx @@ -50,8 +50,6 @@ describe('Mappings editor: shape datatype', () => { }, }; - const updatedMappings = { ...defaultMappings }; - await act(async () => { testBed = setup({ value: defaultMappings, onChange: onChangeHandler }); }); @@ -59,17 +57,26 @@ describe('Mappings editor: shape datatype', () => { const { component, - actions: { startEditField, updateFieldAndCloseFlyout }, + actions: { startEditField, updateFieldName, updateFieldAndCloseFlyout }, } = testBed; // Open the flyout to edit the field await startEditField('myField'); + // Update the name of the field + await updateFieldName('updatedField'); + // Save the field and close the flyout await updateFieldAndCloseFlyout(); - // It should have the default parameters values added - updatedMappings.properties.myField = defaultShapeParameters; + // It should have the default parameters values added for fields which are not set + const updatedMappings = { + properties: { + updatedField: { + ...defaultShapeParameters, + }, + }, + }; ({ data } = await getMappingsEditorData(component)); expect(data).toEqual(updatedMappings); diff --git a/x-pack/plugins/index_management/public/application/components/mappings_editor/__jest__/client_integration/datatypes/text_datatype.test.tsx b/x-pack/plugins/index_management/public/application/components/mappings_editor/__jest__/client_integration/datatypes/text_datatype.test.tsx index 589afca42e73a..798c60a7bc284 100644 --- a/x-pack/plugins/index_management/public/application/components/mappings_editor/__jest__/client_integration/datatypes/text_datatype.test.tsx +++ b/x-pack/plugins/index_management/public/application/components/mappings_editor/__jest__/client_integration/datatypes/text_datatype.test.tsx @@ -55,8 +55,6 @@ describe('Mappings editor: text datatype', () => { }, }; - const updatedMappings = { ...defaultMappings }; - await act(async () => { testBed = setup({ value: defaultMappings, onChange: onChangeHandler }); }); @@ -65,12 +63,15 @@ describe('Mappings editor: text datatype', () => { const { component, exists, - actions: { startEditField, getToggleValue, updateFieldAndCloseFlyout }, + actions: { startEditField, updateFieldName, getToggleValue, updateFieldAndCloseFlyout }, } = testBed; // Open the flyout to edit the field await startEditField('myField'); + // Update the name of the field + await updateFieldName('updatedField'); + // It should have searchable ("index" param) active by default const indexFieldConfig = getFieldConfig('index'); expect(getToggleValue('indexParameter.formRowToggle')).toBe(indexFieldConfig.defaultValue); @@ -86,8 +87,12 @@ describe('Mappings editor: text datatype', () => { await updateFieldAndCloseFlyout(); // It should have the default parameters values added - updatedMappings.properties.myField = { - ...defaultTextParameters, + const updatedMappings = { + properties: { + updatedField: { + ...defaultTextParameters, + }, + }, }; ({ data } = await getMappingsEditorData(component)); @@ -120,16 +125,19 @@ describe('Mappings editor: text datatype', () => { form: { selectCheckBox, setSelectValue }, actions: { startEditField, + updateFieldName, getCheckboxValue, showAdvancedSettings, updateFieldAndCloseFlyout, }, } = testBed; const fieldToEdit = 'myField'; + const newFieldName = 'updatedField'; - // Start edit and immediately save to have all the default values + // Start edit, update the name only, and save to have all the default values await startEditField(fieldToEdit); await showAdvancedSettings(); + await updateFieldName(newFieldName); await updateFieldAndCloseFlyout(); expect(exists('mappingsEditorFieldEdit')).toBe(false); @@ -139,7 +147,7 @@ describe('Mappings editor: text datatype', () => { let updatedMappings: any = { ...defaultMappings, properties: { - myField: { + updatedField: { ...defaultMappings.properties.myField, ...defaultTextParameters, }, @@ -148,7 +156,7 @@ describe('Mappings editor: text datatype', () => { expect(data).toEqual(updatedMappings); // Re-open the edit panel - await startEditField(fieldToEdit); + await startEditField(newFieldName); await showAdvancedSettings(); // When no analyzer is defined, defaults to "Index default" @@ -195,8 +203,8 @@ describe('Mappings editor: text datatype', () => { updatedMappings = { ...updatedMappings, properties: { - myField: { - ...updatedMappings.properties.myField, + updatedField: { + ...updatedMappings.properties.updatedField, analyzer: 'standard', search_analyzer: 'simple', search_quote_analyzer: 'whitespace', @@ -208,7 +216,7 @@ describe('Mappings editor: text datatype', () => { expect(data).toEqual(updatedMappings); // Re-open the flyout and make sure the select have the correct updated value - await startEditField('myField'); + await startEditField(newFieldName); await showAdvancedSettings(); isUseSameAnalyzerForSearchChecked = getCheckboxValue('useSameAnalyzerForSearchCheckBox.input'); diff --git a/x-pack/plugins/index_management/public/application/components/mappings_editor/__jest__/client_integration/edit_field.test.tsx b/x-pack/plugins/index_management/public/application/components/mappings_editor/__jest__/client_integration/edit_field.test.tsx index 7588df1a7f7c2..5277c26f93341 100644 --- a/x-pack/plugins/index_management/public/application/components/mappings_editor/__jest__/client_integration/edit_field.test.tsx +++ b/x-pack/plugins/index_management/public/application/components/mappings_editor/__jest__/client_integration/edit_field.test.tsx @@ -121,4 +121,30 @@ describe('Mappings editor: edit field', () => { expect(data).toEqual(updatedMappings); }); + + test('should have Update button enabled only when changes are made', async () => { + const defaultMappings = { + properties: { + myField: { + type: 'text', + }, + }, + }; + + await act(async () => { + testBed = setup({ value: defaultMappings, onChange: onChangeHandler }); + }); + testBed.component.update(); + + await testBed.actions.expandAllFieldsAndReturnMetadata(); + + const { + actions: { startEditField, isUpdateButtonDisabled, updateFieldName }, + } = testBed; + // Open the flyout to edit the field + await startEditField('myField'); + expect(isUpdateButtonDisabled()).toBe(true); + await updateFieldName('updatedField'); + expect(isUpdateButtonDisabled()).toBe(false); + }); }); diff --git a/x-pack/plugins/index_management/public/application/components/mappings_editor/__jest__/client_integration/helpers/mappings_editor.helpers.tsx b/x-pack/plugins/index_management/public/application/components/mappings_editor/__jest__/client_integration/helpers/mappings_editor.helpers.tsx index 2abcff3f68a41..126afdc909567 100644 --- a/x-pack/plugins/index_management/public/application/components/mappings_editor/__jest__/client_integration/helpers/mappings_editor.helpers.tsx +++ b/x-pack/plugins/index_management/public/application/components/mappings_editor/__jest__/client_integration/helpers/mappings_editor.helpers.tsx @@ -141,6 +141,15 @@ const createActions = (testBed: TestBed) => { component.update(); }; + const updateFieldName = async (name: string) => { + await act(async () => { + form.setInputValue('nameParameterInput', name); + jest.advanceTimersByTime(0); // advance timers to allow the form to validate + }); + + component.update(); + }; + const startEditField = async (path: string) => { const { testSubject } = getFieldAt(path); await act(async () => { @@ -158,6 +167,9 @@ const createActions = (testBed: TestBed) => { component.update(); }; + const isUpdateButtonDisabled = () => + find('mappingsEditorFieldEdit.editFieldUpdateButton').props().disabled; + const showAdvancedSettings = async () => { if (find('mappingsEditorFieldEdit.advancedSettings').props().style.display === 'block') { // Already opened, nothing else to do @@ -323,8 +335,10 @@ const createActions = (testBed: TestBed) => { getFieldAt, addField, expandAllFieldsAndReturnMetadata, + updateFieldName, startEditField, updateFieldAndCloseFlyout, + isUpdateButtonDisabled, showAdvancedSettings, updateJsonEditor, getJsonEditorValue, diff --git a/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/edit_field/edit_field.tsx b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/edit_field/edit_field.tsx index aa6a5e7981cbc..87b7b059164f8 100644 --- a/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/edit_field/edit_field.tsx +++ b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/edit_field/edit_field.tsx @@ -18,9 +18,14 @@ import { EuiFlexItem, EuiSpacer, EuiCallOut, + EuiText, + EuiToolTip, + EuiIcon, + EuiTextColor, } from '@elastic/eui'; import SemVer from 'semver/classes/semver'; +import { useFormIsModified } from '@kbn/es-ui-shared-plugin/static/forms/hook_form_lib'; import { documentationService } from '../../../../../../services/documentation'; import { Form, FormHook, FormDataProvider } from '../../../../shared_imports'; import { TYPE_DEFINITION } from '../../../../constants'; @@ -64,6 +69,8 @@ export const EditField = React.memo( const { isMultiField } = field; + const isFormModified = useFormIsModified({ form }); + return (
@@ -186,7 +193,39 @@ export const EditField = React.memo( )} - + + {isFormModified && ( + <> + + +

+ + + + {i18n.translate( + 'xpack.idxMgmt.mappingsEditor.editFieldFlyout.formCompletionGuide', + { + defaultMessage: 'Review all settings before updating ', + } + )} + + + + +

+
+
+ + )} {i18n.translate('xpack.idxMgmt.mappingsEditor.editFieldCancelButtonLabel', { @@ -199,7 +238,7 @@ export const EditField = React.memo( fill onClick={submitForm} type="submit" - disabled={form.isSubmitted && !form.isValid} + disabled={(form.isSubmitted && !form.isValid) || !isFormModified} data-test-subj="editFieldUpdateButton" > {i18n.translate('xpack.idxMgmt.mappingsEditor.editFieldUpdateButtonLabel', { diff --git a/x-pack/plugins/infra/public/hooks/use_http_request.tsx b/x-pack/plugins/infra/public/hooks/use_http_request.tsx index ab425afdfcb80..041cd1ea0f66e 100644 --- a/x-pack/plugins/infra/public/hooks/use_http_request.tsx +++ b/x-pack/plugins/infra/public/hooks/use_http_request.tsx @@ -5,11 +5,12 @@ * 2.0. */ -import React, { useCallback, useMemo, useState } from 'react'; +import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react'; import { i18n } from '@kbn/i18n'; import { HttpHandler } from '@kbn/core/public'; import { ToastInput } from '@kbn/core/public'; import { useKibana } from '@kbn/kibana-react-plugin/public'; +import { AbortError } from '@kbn/kibana-utils-plugin/common'; import { useTrackedPromise, CanceledPromiseError } from '../utils/use_tracked_promise'; import { InfraHttpError } from '../types'; @@ -19,18 +20,20 @@ export function useHTTPRequest( body?: string | null, decode: (response: any) => Response = (response) => response, fetch?: HttpHandler, - toastDanger?: (input: ToastInput) => void + toastDanger?: (input: ToastInput) => void, + abortable = false ) { const kibana = useKibana(); const fetchService = fetch ? fetch : kibana.services.http?.fetch; const toast = toastDanger ? toastDanger : kibana.notifications.toasts.danger; const [response, setResponse] = useState(null); const [error, setError] = useState(null); + const abortController = useRef(new AbortController()); const onError = useCallback( (e: unknown) => { const err = e as InfraHttpError; - if (e && e instanceof CanceledPromiseError) { + if (e && (e instanceof CanceledPromiseError || (e as Error).name === AbortError.name)) { return; } setError(err); @@ -72,6 +75,14 @@ export function useHTTPRequest( [toast] ); + useEffect(() => { + return () => { + if (abortable) { + abortController.current.abort(); + } + }; + }, [abortable]); + const [request, makeRequest] = useTrackedPromise( { cancelPreviousOn: 'resolution', @@ -79,7 +90,15 @@ export function useHTTPRequest( if (!fetchService) { throw new Error('HTTP service is unavailable'); } + + if (abortable) { + abortController.current.abort(); + } + + abortController.current = new AbortController(); + return fetchService(pathname, { + signal: abortController.current.signal, method, body, }); diff --git a/x-pack/plugins/infra/public/pages/metrics/hosts/components/controls_content.tsx b/x-pack/plugins/infra/public/pages/metrics/hosts/components/controls_content.tsx index 79e95f7482b04..d9cc9801b6cbc 100644 --- a/x-pack/plugins/infra/public/pages/metrics/hosts/components/controls_content.tsx +++ b/x-pack/plugins/infra/public/pages/metrics/hosts/components/controls_content.tsx @@ -5,81 +5,77 @@ * 2.0. */ -import React, { useEffect, useState } from 'react'; -import { ControlGroupContainer, CONTROL_GROUP_TYPE } from '@kbn/controls-plugin/public'; +import React, { useCallback, useEffect, useRef } from 'react'; +import { ControlGroupContainer, type ControlGroupInput } from '@kbn/controls-plugin/public'; import { ViewMode } from '@kbn/embeddable-plugin/public'; -import { Filter, TimeRange } from '@kbn/es-query'; +import type { Filter, Query, TimeRange } from '@kbn/es-query'; import { DataView } from '@kbn/data-views-plugin/public'; +import { Subscription } from 'rxjs'; import { LazyControlsRenderer } from './lazy_controls_renderer'; import { useControlPanels } from '../hooks/use_control_panels_url_state'; interface Props { - timeRange: TimeRange; dataView: DataView; filters: Filter[]; - query: { - language: string; - query: string; - }; - onFilterChange: (filters: Filter[]) => void; + query: Query; + timeRange: TimeRange; + onFiltersChange: (filters: Filter[]) => void; } -// Disable refresh, allow our timerange changes to refresh the embeddable. -const REFRESH_CONFIG = { - pause: true, - value: 0, -}; - export const ControlsContent: React.FC = ({ - timeRange, dataView, - query, filters, - onFilterChange, + query, + timeRange, + onFiltersChange, }) => { - const [controlPanel, setControlPanels] = useControlPanels(dataView); - const [controlGroup, setControlGroup] = useState(); + const [controlPanels, setControlPanels] = useControlPanels(dataView); + const inputSubscription = useRef(); + const filterSubscription = useRef(); - useEffect(() => { - if (!controlGroup) { - return; - } - const filtersSubscription = controlGroup.onFiltersPublished$.subscribe((newFilters) => { - onFilterChange(newFilters); - }); - const inputSubscription = controlGroup.getInput$().subscribe(({ panels }) => { - setControlPanels(panels); - }); + const getInitialInput = useCallback(async () => { + const initialInput: Partial = { + id: dataView.id ?? '', + viewMode: ViewMode.VIEW, + chainingSystem: 'HIERARCHICAL', + controlStyle: 'oneLine', + defaultControlWidth: 'small', + panels: controlPanels, + filters, + query, + timeRange, + }; + return { initialInput }; + }, [controlPanels, dataView.id, filters, query, timeRange]); + + const loadCompleteHandler = useCallback( + (controlGroup: ControlGroupContainer) => { + inputSubscription.current = controlGroup.onFiltersPublished$.subscribe((newFilters) => { + onFiltersChange(newFilters); + }); + + filterSubscription.current = controlGroup + .getInput$() + .subscribe(({ panels }) => setControlPanels(panels)); + }, + [onFiltersChange, setControlPanels] + ); + + useEffect(() => { return () => { - filtersSubscription.unsubscribe(); - inputSubscription.unsubscribe(); + filterSubscription.current?.unsubscribe(); + inputSubscription.current?.unsubscribe(); }; - }, [controlGroup, onFilterChange, setControlPanels]); + }, []); return ( ({ - initialInput: { - id: dataView.id ?? '', - type: CONTROL_GROUP_TYPE, - timeRange, - refreshConfig: REFRESH_CONFIG, - viewMode: ViewMode.VIEW, - filters: [...filters], - query, - chainingSystem: 'HIERARCHICAL', - controlStyle: 'oneLine', - defaultControlWidth: 'small', - panels: controlPanel, - }, - })} - onLoadComplete={(newControlGroup) => { - setControlGroup(newControlGroup); - }} - query={query} + getCreationOptions={getInitialInput} + onLoadComplete={loadCompleteHandler} timeRange={timeRange} + query={query} + filters={filters} /> ); }; diff --git a/x-pack/plugins/infra/public/pages/metrics/hosts/components/hosts_table.tsx b/x-pack/plugins/infra/public/pages/metrics/hosts/components/hosts_table.tsx index 00b86550a2adc..7b576ac9007e9 100644 --- a/x-pack/plugins/infra/public/pages/metrics/hosts/components/hosts_table.tsx +++ b/x-pack/plugins/infra/public/pages/metrics/hosts/components/hosts_table.tsx @@ -18,10 +18,10 @@ import { useUnifiedSearchContext } from '../hooks/use_unified_search'; export const HostsTable = () => { const { hostNodes, loading } = useHostsViewContext(); - const { onSubmit, unifiedSearchDateRange } = useUnifiedSearchContext(); + const { onSubmit, searchCriteria } = useUnifiedSearchContext(); const [properties, setProperties] = useTableProperties(); - const { columns, items } = useHostsTable(hostNodes, { time: unifiedSearchDateRange }); + const { columns, items } = useHostsTable(hostNodes, { time: searchCriteria.dateRange }); const noData = items.length === 0; diff --git a/x-pack/plugins/infra/public/pages/metrics/hosts/components/kpi_charts/tile.tsx b/x-pack/plugins/infra/public/pages/metrics/hosts/components/kpi_charts/tile.tsx index 1b86c5a543d06..3f396ebcd7bf8 100644 --- a/x-pack/plugins/infra/public/pages/metrics/hosts/components/kpi_charts/tile.tsx +++ b/x-pack/plugins/infra/public/pages/metrics/hosts/components/kpi_charts/tile.tsx @@ -17,13 +17,16 @@ interface Props extends Omit { export const Tile = ({ type, ...props }: Props) => { const { baseRequest } = useHostsViewContext(); - const { nodes, loading } = useSnapshot({ - ...baseRequest, - metrics: [{ type }], - groupBy: null, - includeTimeseries: true, - dropPartialBuckets: false, - }); + const { nodes, loading } = useSnapshot( + { + ...baseRequest, + metrics: [{ type }], + groupBy: null, + includeTimeseries: true, + dropPartialBuckets: false, + }, + { abortable: true } + ); return ; }; diff --git a/x-pack/plugins/infra/public/pages/metrics/hosts/components/tabs/alerts/alerts_tab_content.tsx b/x-pack/plugins/infra/public/pages/metrics/hosts/components/tabs/alerts/alerts_tab_content.tsx index 7933d3bdb9f3c..f190b983287e6 100644 --- a/x-pack/plugins/infra/public/pages/metrics/hosts/components/tabs/alerts/alerts_tab_content.tsx +++ b/x-pack/plugins/infra/public/pages/metrics/hosts/components/tabs/alerts/alerts_tab_content.tsx @@ -36,7 +36,7 @@ export const AlertsTabContent = () => { const { alertStatus, setAlertStatus, alertsEsQueryByStatus } = useAlertsQuery(); - const { unifiedSearchDateRange } = useUnifiedSearchContext(); + const { searchCriteria } = useUnifiedSearchContext(); const { application, cases, triggersActionsUi } = services; @@ -58,7 +58,7 @@ export const AlertsTabContent = () => { {alertsEsQueryByStatus && ( diff --git a/x-pack/plugins/infra/public/pages/metrics/hosts/components/tabs/metrics/metric_chart.tsx b/x-pack/plugins/infra/public/pages/metrics/hosts/components/tabs/metrics/metric_chart.tsx index fd83ef7b9f6c8..23dd72cbf023c 100644 --- a/x-pack/plugins/infra/public/pages/metrics/hosts/components/tabs/metrics/metric_chart.tsx +++ b/x-pack/plugins/infra/public/pages/metrics/hosts/components/tabs/metrics/metric_chart.tsx @@ -30,13 +30,7 @@ export interface MetricChartProps { const MIN_HEIGHT = 300; export const MetricChart = ({ title, type, breakdownSize }: MetricChartProps) => { - const { - unifiedSearchDateRange, - unifiedSearchQuery, - unifiedSearchFilters, - controlPanelFilters, - onSubmit, - } = useUnifiedSearchContext(); + const { searchCriteria, onSubmit } = useUnifiedSearchContext(); const { metricsDataView } = useMetricsDataViewContext(); const { baseRequest } = useHostsViewContext(); const { @@ -54,12 +48,12 @@ export const MetricChart = ({ title, type, breakdownSize }: MetricChartProps) => }); const injectedLensAttributes = injectData({ - filters: [...unifiedSearchFilters, ...controlPanelFilters], - query: unifiedSearchQuery, + filters: [...searchCriteria.filters, ...searchCriteria.panelFilters], + query: searchCriteria.query, title, }); - const extraActionOptions = getExtraActions(injectedLensAttributes, unifiedSearchDateRange); + const extraActionOptions = getExtraActions(injectedLensAttributes, searchCriteria.dateRange); const extraAction: Action[] = [extraActionOptions.openInLens]; const handleBrushEnd = ({ range }: BrushTriggerEvent['data']) => { @@ -109,9 +103,9 @@ export const MetricChart = ({ title, type, breakdownSize }: MetricChartProps) => style={{ height: MIN_HEIGHT }} attributes={injectedLensAttributes} viewMode={ViewMode.VIEW} - timeRange={unifiedSearchDateRange} - query={unifiedSearchQuery} - filters={unifiedSearchFilters} + timeRange={searchCriteria.dateRange} + query={searchCriteria.query} + filters={searchCriteria.filters} extraActions={extraAction} lastReloadRequestTime={baseRequest.requestTs} executionContext={{ diff --git a/x-pack/plugins/infra/public/pages/metrics/hosts/components/unified_search_bar.tsx b/x-pack/plugins/infra/public/pages/metrics/hosts/components/unified_search_bar.tsx index 3f65990f80b50..e808c7a730fa0 100644 --- a/x-pack/plugins/infra/public/pages/metrics/hosts/components/unified_search_bar.tsx +++ b/x-pack/plugins/infra/public/pages/metrics/hosts/components/unified_search_bar.tsx @@ -7,12 +7,16 @@ import React from 'react'; import { useKibana } from '@kbn/kibana-react-plugin/public'; -import type { Filter, Query, TimeRange } from '@kbn/es-query'; +import { + compareFilters, + COMPARE_ALL_OPTIONS, + type Filter, + type Query, + type TimeRange, +} from '@kbn/es-query'; import type { DataView } from '@kbn/data-views-plugin/public'; -import type { SavedQuery } from '@kbn/data-plugin/public'; import { i18n } from '@kbn/i18n'; import { EuiFlexGrid } from '@elastic/eui'; -import deepEqual from 'fast-deep-equal'; import type { InfraClientStartDeps } from '../../../../types'; import { useUnifiedSearchContext } from '../hooks/use_unified_search'; import { ControlsContent } from './controls_content'; @@ -25,37 +29,16 @@ export const UnifiedSearchBar = ({ dataView }: Props) => { const { services: { unifiedSearch, application }, } = useKibana(); - const { - unifiedSearchDateRange, - unifiedSearchQuery, - unifiedSearchFilters, - controlPanelFilters, - onSubmit, - saveQuery, - clearSavedQuery, - } = useUnifiedSearchContext(); + const { searchCriteria, onSubmit } = useUnifiedSearchContext(); const { SearchBar } = unifiedSearch.ui; - const onQuerySubmit = (payload: { dateRange: TimeRange; query?: Query }) => { - onQueryChange({ payload }); - }; - const onPanelFiltersChange = (panelFilters: Filter[]) => { - // triggers this event 2 times during its loading lifecycle - if (!deepEqual(controlPanelFilters, panelFilters)) { + if (!compareFilters(searchCriteria.panelFilters, panelFilters, COMPARE_ALL_OPTIONS)) { onQueryChange({ panelFilters }); } }; - const onClearSavedQuery = () => { - clearSavedQuery(); - }; - - const onQuerySave = (savedQuery: SavedQuery) => { - saveQuery(savedQuery); - }; - const onQueryChange = ({ payload, panelFilters, @@ -66,31 +49,36 @@ export const UnifiedSearchBar = ({ dataView }: Props) => { onSubmit({ query: payload?.query, dateRange: payload?.dateRange, panelFilters }); }; + const handleRefresh = (payload: { dateRange: TimeRange; query?: Query }, isUpdate?: boolean) => { + // This makes sure `onQueryChange` is only called when the submit button is clicked + if (isUpdate === false) { + onQueryChange({ payload }); + } + }; + return ( 0.5)', })} - indexPatterns={[dataView]} - query={unifiedSearchQuery} - dateRangeFrom={unifiedSearchDateRange.from} - dateRangeTo={unifiedSearchDateRange.to} - onQuerySubmit={onQuerySubmit} - onSaved={onQuerySave} - onSavedQueryUpdated={onQuerySave} - onClearSavedQuery={onClearSavedQuery} + onQuerySubmit={handleRefresh} showSaveQuery={Boolean(application?.capabilities?.visualize?.saveQuery)} + showDatePicker + showFilterBar showQueryInput - displayStyle="inPage" + showQueryMenu + useDefaultBehaviors /> ); diff --git a/x-pack/plugins/infra/public/pages/metrics/hosts/hooks/use_alerts_query.ts b/x-pack/plugins/infra/public/pages/metrics/hosts/hooks/use_alerts_query.ts index 134991122f1c2..751cb6d1cb36f 100644 --- a/x-pack/plugins/infra/public/pages/metrics/hosts/hooks/use_alerts_query.ts +++ b/x-pack/plugins/infra/public/pages/metrics/hosts/hooks/use_alerts_query.ts @@ -23,14 +23,14 @@ export interface AlertsEsQuery { export const useAlertsQueryImpl = () => { const { hostNodes } = useHostsViewContext(); - const { unifiedSearchDateRange } = useUnifiedSearchContext(); + const { searchCriteria } = useUnifiedSearchContext(); const [alertStatus, setAlertStatus] = useState('all'); const getAlertsEsQuery = useCallback( (status?: AlertStatus) => - createAlertsEsQuery({ dateRange: unifiedSearchDateRange, hostNodes, status }), - [hostNodes, unifiedSearchDateRange] + createAlertsEsQuery({ dateRange: searchCriteria.dateRange, hostNodes, status }), + [hostNodes, searchCriteria.dateRange] ); // Regenerate the query when status change even if is not used. diff --git a/x-pack/plugins/infra/public/pages/metrics/hosts/hooks/use_control_panels_url_state.ts b/x-pack/plugins/infra/public/pages/metrics/hosts/hooks/use_control_panels_url_state.ts index 2e9d760a5e3d5..ed3519c210276 100644 --- a/x-pack/plugins/infra/public/pages/metrics/hosts/hooks/use_control_panels_url_state.ts +++ b/x-pack/plugins/infra/public/pages/metrics/hosts/hooks/use_control_panels_url_state.ts @@ -129,6 +129,7 @@ const PanelRT = rt.type({ dataViewId: rt.string, fieldName: rt.string, title: rt.union([rt.string, rt.undefined]), + selectedOptions: rt.array(rt.string), }), ]), }); diff --git a/x-pack/plugins/infra/public/pages/metrics/hosts/hooks/use_hosts_view.ts b/x-pack/plugins/infra/public/pages/metrics/hosts/hooks/use_hosts_view.ts index 012dcd42fe556..73e771cf096ff 100644 --- a/x-pack/plugins/infra/public/pages/metrics/hosts/hooks/use_hosts_view.ts +++ b/x-pack/plugins/infra/public/pages/metrics/hosts/hooks/use_hosts_view.ts @@ -51,7 +51,13 @@ export const useHostsView = () => { loading, error, nodes: hostNodes, - } = useSnapshot({ ...baseRequest, metrics: HOST_TABLE_METRICS }); + } = useSnapshot( + { + ...baseRequest, + metrics: HOST_TABLE_METRICS, + }, + { abortable: true } + ); return { baseRequest, diff --git a/x-pack/plugins/infra/public/pages/metrics/hosts/hooks/use_unified_search.ts b/x-pack/plugins/infra/public/pages/metrics/hosts/hooks/use_unified_search.ts index 898d384bcdc23..a8104e1aeae52 100644 --- a/x-pack/plugins/infra/public/pages/metrics/hosts/hooks/use_unified_search.ts +++ b/x-pack/plugins/infra/public/pages/metrics/hosts/hooks/use_unified_search.ts @@ -7,19 +7,18 @@ import { useKibana } from '@kbn/kibana-react-plugin/public'; import createContainer from 'constate'; import { useCallback, useEffect } from 'react'; -import { buildEsQuery, Filter, Query, TimeRange } from '@kbn/es-query'; -import type { SavedQuery } from '@kbn/data-plugin/public'; -import { debounce } from 'lodash'; +import { buildEsQuery, type Filter, type Query, type TimeRange } from '@kbn/es-query'; +import { map, skip, startWith } from 'rxjs/operators'; +import { combineLatest } from 'rxjs'; import deepEqual from 'fast-deep-equal'; +import useEffectOnce from 'react-use/lib/useEffectOnce'; import { telemetryTimeRangeFormatter } from '../../../../../common/formatters/telemetry_time_range'; import type { InfraClientStartDeps } from '../../../../types'; import { useMetricsDataViewContext } from './use_data_view'; -import { useSyncKibanaTimeFilterTime } from '../../../../hooks/use_kibana_timefilter_time'; import { useHostsUrlState, - INITIAL_DATE_RANGE, - HostsState, - StringDateRangeTimestamp, + type HostsState, + type StringDateRangeTimestamp, } from './use_unified_search_url_state'; const buildQuerySubmittedPayload = ( @@ -40,56 +39,16 @@ export const useUnifiedSearch = () => { const { metricsDataView } = useMetricsDataViewContext(); const { services } = useKibana(); const { - data: { query: queryManager }, + data: { + query: { + filterManager: filterManagerService, + timefilter: timeFilterService, + queryString: queryStringService, + }, + }, telemetry, } = services; - useSyncKibanaTimeFilterTime(INITIAL_DATE_RANGE, { - from: state.dateRange.from, - to: state.dateRange.to, - }); - - const { filterManager } = queryManager; - - useEffect(() => { - const { filters } = state; - if (!deepEqual(filters, filterManager.getFilters())) { - filterManager.setFilters(filters); - } - }, [filterManager, state]); - - // This will listen and react to all changes in filterManager and timefilter values, - // to allow other components in the page to communicate with the unified search - useEffect(() => { - const next = () => { - const globalFilters = filterManager.getFilters(); - debounceOnSubmit({ - filters: globalFilters, - dateRange: getTime(), - }); - }; - - const filterSubscription = filterManager.getUpdates$().subscribe({ - next, - }); - const timeSubscription = queryManager.timefilter.timefilter.getTimeUpdate$().subscribe({ - next, - }); - - return () => { - filterSubscription.unsubscribe(); - timeSubscription.unsubscribe(); - }; - }); - - // Track telemetry event on query/filter/date changes - useEffect(() => { - const dateRangeTimestamp = getDateRangeAsTimestamp(); - telemetry.reportHostsViewQuerySubmitted( - buildQuerySubmittedPayload({ ...state, dateRangeTimestamp }) - ); - }, [getDateRangeAsTimestamp, state, telemetry]); - const onSubmit = useCallback( (data?: { query?: Query; @@ -97,73 +56,108 @@ export const useUnifiedSearch = () => { filters?: Filter[]; panelFilters?: Filter[]; }) => { - const { query, dateRange, filters, panelFilters } = data ?? {}; - const newDateRange = dateRange ?? getTime(); + const { + panelFilters, + query, + // Makes sure default values are set in case `onSubmit` is called outside the unified search observables subscription + // and prevents their state values from being cleared. + dateRange = getTime(), + filters = filterManagerService.getFilters(), + } = data ?? {}; dispatch({ type: 'setQuery', payload: { query, filters, - dateRange: newDateRange, + dateRange, panelFilters, }, }); }, - [getTime, dispatch] + [dispatch, filterManagerService, getTime] ); - // This won't prevent onSubmit from being fired twice when `clear filters` is clicked, - // that happens because both onQuerySubmit and onFiltersUpdated are internally triggered on same event by SearchBar. - // This just delays potential duplicate onSubmit calls - // eslint-disable-next-line react-hooks/exhaustive-deps - const debounceOnSubmit = useCallback(debounce(onSubmit, 100), [onSubmit]); + const loadFiltersFromState = useCallback(() => { + if (!deepEqual(filterManagerService.getFilters(), state.filters)) { + filterManagerService.setFilters(state.filters); + } + }, [filterManagerService, state.filters]); - const saveQuery = useCallback( - (newSavedQuery: SavedQuery) => { - const savedQueryFilters = newSavedQuery.attributes.filters ?? []; - const globalFilters = filterManager.getGlobalFilters(); + const loadQueryFromState = useCallback(() => { + if (!deepEqual(queryStringService.getQuery(), state.query)) { + queryStringService.setQuery(state.query); + } + }, [queryStringService, state.query]); - const query = newSavedQuery.attributes.query; + const loadDateRangeFromState = useCallback(() => { + if (!deepEqual(timeFilterService.timefilter.getTime(), state.dateRange)) { + timeFilterService.timefilter.setTime(state.dateRange); + } + }, [timeFilterService, state.dateRange]); - dispatch({ - type: 'setQuery', - payload: { + useEffectOnce(() => { + loadFiltersFromState(); + loadQueryFromState(); + loadDateRangeFromState(); + }); + + useEffect(() => { + const filters$ = filterManagerService.getUpdates$().pipe( + startWith(undefined), + map(() => filterManagerService.getFilters()) + ); + + const query$ = queryStringService.getUpdates$().pipe( + startWith(undefined), + map(() => queryStringService.getQuery() as Query) + ); + + const dateRange$ = timeFilterService.timefilter.getTimeUpdate$().pipe( + startWith(undefined), + map(() => getTime()) + ); + + const subscription = combineLatest({ + filters: filters$, + query: query$, + dateRange: dateRange$, + }) + .pipe(skip(1)) + .subscribe(({ filters, query, dateRange }) => { + onSubmit({ query, - filters: [...savedQueryFilters, ...globalFilters], - }, + filters, + dateRange, + }); }); - }, - [filterManager, dispatch] - ); - const clearSavedQuery = useCallback(() => { - dispatch({ - type: 'setFilter', - payload: filterManager.getGlobalFilters(), - }); - }, [filterManager, dispatch]); + return () => { + subscription.unsubscribe(); + }; + }, [filterManagerService, getTime, onSubmit, queryStringService, timeFilterService.timefilter]); + // Track telemetry event on query/filter/date changes + useEffect(() => { + const dateRangeTimestamp = getDateRangeAsTimestamp(); + telemetry.reportHostsViewQuerySubmitted( + buildQuerySubmittedPayload({ ...state, dateRangeTimestamp }) + ); + }, [getDateRangeAsTimestamp, state, telemetry]); + + const getAllFilters = useCallback( + () => [...filterManagerService.getFilters(), ...state.panelFilters], + [filterManagerService, state.panelFilters] + ); const buildQuery = useCallback(() => { - if (!metricsDataView) { - return null; - } - return buildEsQuery(metricsDataView, state.query, [ - ...state.filters, - ...(state.panelFilters ?? []), - ]); - }, [metricsDataView, state.query, state.filters, state.panelFilters]); + return buildEsQuery(metricsDataView, queryStringService.getQuery(), getAllFilters()); + }, [metricsDataView, queryStringService, getAllFilters]); return { buildQuery, - clearSavedQuery, - controlPanelFilters: state.panelFilters, - onSubmit: debounceOnSubmit, - saveQuery, + onSubmit, getDateRangeAsTimestamp, - unifiedSearchQuery: state.query, - unifiedSearchDateRange: state.dateRange, - unifiedSearchFilters: state.filters, + searchCriteria: { ...state }, }; }; diff --git a/x-pack/plugins/infra/public/pages/metrics/hosts/hooks/use_unified_search_url_state.ts b/x-pack/plugins/infra/public/pages/metrics/hosts/hooks/use_unified_search_url_state.ts index 2c7eb84dd43bf..032e2610845a2 100644 --- a/x-pack/plugins/infra/public/pages/metrics/hosts/hooks/use_unified_search_url_state.ts +++ b/x-pack/plugins/infra/public/pages/metrics/hosts/hooks/use_unified_search_url_state.ts @@ -49,16 +49,11 @@ const reducer = (state: HostsState, action: Action): HostsState => { case 'setFilter': return { ...state, filters: [...action.payload] }; case 'setQuery': - const { filters, query, panelFilters, ...payload } = action.payload; - const newFilters = !filters ? state.filters : filters; - const newControlPanelFilters = !panelFilters ? state.panelFilters : panelFilters; - const newQuery = !query ? state.query : query; + const payload = Object.fromEntries(Object.entries(action.payload).filter(([_, v]) => !!v)); + return { ...state, ...payload, - filters: [...newFilters], - query: { ...newQuery }, - panelFilters: [...newControlPanelFilters], }; default: throw new Error(); diff --git a/x-pack/plugins/infra/public/pages/metrics/inventory_view/hooks/use_snaphot.ts b/x-pack/plugins/infra/public/pages/metrics/inventory_view/hooks/use_snaphot.ts index ae39a5f03ea7e..e0890ebf3d02f 100644 --- a/x-pack/plugins/infra/public/pages/metrics/inventory_view/hooks/use_snaphot.ts +++ b/x-pack/plugins/infra/public/pages/metrics/inventory_view/hooks/use_snaphot.ts @@ -26,18 +26,26 @@ export interface UseSnapshotRequest timerange?: InfraTimerangeInput; requestTs?: number; } -export function useSnapshot({ - timerange, - currentTime, - accountId = '', - region = '', - groupBy = null, - sendRequestImmediately = true, - includeTimeseries = true, - dropPartialBuckets = true, - requestTs, - ...args -}: UseSnapshotRequest) { + +export interface UseSnapshotRequestOptions { + abortable?: boolean; +} + +export function useSnapshot( + { + timerange, + currentTime, + accountId = '', + region = '', + groupBy = null, + sendRequestImmediately = true, + includeTimeseries = true, + dropPartialBuckets = true, + requestTs, + ...args + }: UseSnapshotRequest, + options?: UseSnapshotRequestOptions +) { const decodeResponse = (response: any) => { return pipe( SnapshotNodeResponseRT.decode(response), @@ -64,7 +72,10 @@ export function useSnapshot({ '/api/metrics/snapshot', 'POST', JSON.stringify(payload), - decodeResponse + decodeResponse, + undefined, + undefined, + options?.abortable ); useEffect(() => { diff --git a/x-pack/plugins/infra/public/utils/use_tracked_promise.ts b/x-pack/plugins/infra/public/utils/use_tracked_promise.ts index 4380db2dcb4c4..b0f75a7727d81 100644 --- a/x-pack/plugins/infra/public/utils/use_tracked_promise.ts +++ b/x-pack/plugins/infra/public/utils/use_tracked_promise.ts @@ -201,9 +201,9 @@ export const useTrackedPromise = ( if (shouldTriggerOrThrow()) { if (onReject) { onReject(value); + } else { + throw value; } - - throw value; } } ), diff --git a/x-pack/plugins/infra/server/lib/alerting/inventory_metric_threshold/register_inventory_metric_threshold_rule_type.ts b/x-pack/plugins/infra/server/lib/alerting/inventory_metric_threshold/register_inventory_metric_threshold_rule_type.ts index 77fa814a622a4..ba7a0794757b0 100644 --- a/x-pack/plugins/infra/server/lib/alerting/inventory_metric_threshold/register_inventory_metric_threshold_rule_type.ts +++ b/x-pack/plugins/infra/server/lib/alerting/inventory_metric_threshold/register_inventory_metric_threshold_rule_type.ts @@ -52,6 +52,7 @@ import { FIRED_ACTIONS_ID, WARNING_ACTIONS, } from './inventory_metric_threshold_executor'; +import { MetricsRulesTypeAlertDefinition } from '../register_rule_types'; const condition = schema.object({ threshold: schema.arrayOf(schema.number()), @@ -154,5 +155,6 @@ export async function registerMetricInventoryThresholdRuleType( ], }, getSummarizedAlerts: libs.metricsRules.createGetSummarizedAlerts(), + alerts: MetricsRulesTypeAlertDefinition, }); } diff --git a/x-pack/plugins/infra/server/lib/alerting/log_threshold/register_log_threshold_rule_type.ts b/x-pack/plugins/infra/server/lib/alerting/log_threshold/register_log_threshold_rule_type.ts index 297416d0d6632..70cf8bfb59f76 100644 --- a/x-pack/plugins/infra/server/lib/alerting/log_threshold/register_log_threshold_rule_type.ts +++ b/x-pack/plugins/infra/server/lib/alerting/log_threshold/register_log_threshold_rule_type.ts @@ -26,6 +26,7 @@ import { orchestratorActionVariableDescription, tagsActionVariableDescription, } from '../common/messages'; +import { LogsRulesTypeAlertDefinition } from '../register_rule_types'; const timestampActionVariableDescription = i18n.translate( 'xpack.infra.logs.alerting.threshold.timestampActionVariableDescription', @@ -171,5 +172,6 @@ export async function registerLogThresholdRuleType( extractReferences, injectReferences, }, + alerts: LogsRulesTypeAlertDefinition, }); } diff --git a/x-pack/plugins/infra/server/lib/alerting/metric_threshold/register_metric_threshold_rule_type.ts b/x-pack/plugins/infra/server/lib/alerting/metric_threshold/register_metric_threshold_rule_type.ts index d2d0d36f42ac5..0c18288236af7 100644 --- a/x-pack/plugins/infra/server/lib/alerting/metric_threshold/register_metric_threshold_rule_type.ts +++ b/x-pack/plugins/infra/server/lib/alerting/metric_threshold/register_metric_threshold_rule_type.ts @@ -42,6 +42,7 @@ import { WARNING_ACTIONS, NO_DATA_ACTIONS, } from './metric_threshold_executor'; +import { MetricsRulesTypeAlertDefinition } from '../register_rule_types'; type MetricThresholdAllowedActionGroups = ActionGroupIdsOf< typeof FIRED_ACTIONS | typeof WARNING_ACTIONS | typeof NO_DATA_ACTIONS @@ -192,5 +193,6 @@ export async function registerMetricThresholdRuleType( }, producer: 'infrastructure', getSummarizedAlerts: libs.metricsRules.createGetSummarizedAlerts(), + alerts: MetricsRulesTypeAlertDefinition, }); } diff --git a/x-pack/plugins/infra/server/lib/alerting/register_rule_types.ts b/x-pack/plugins/infra/server/lib/alerting/register_rule_types.ts index e27c5b7fd4809..ee05dc38cc1f5 100644 --- a/x-pack/plugins/infra/server/lib/alerting/register_rule_types.ts +++ b/x-pack/plugins/infra/server/lib/alerting/register_rule_types.ts @@ -5,7 +5,8 @@ * 2.0. */ -import { PluginSetupContract } from '@kbn/alerting-plugin/server'; +import { legacyExperimentalFieldMap } from '@kbn/alerts-as-data-utils'; +import { type IRuleTypeAlerts, PluginSetupContract } from '@kbn/alerting-plugin/server'; import { MlPluginSetup } from '@kbn/ml-plugin/server'; import { registerMetricThresholdRuleType } from './metric_threshold/register_metric_threshold_rule_type'; import { registerMetricInventoryThresholdRuleType } from './inventory_metric_threshold/register_inventory_metric_threshold_rule_type'; @@ -13,6 +14,24 @@ import { registerMetricAnomalyRuleType } from './metric_anomaly/register_metric_ import { registerLogThresholdRuleType } from './log_threshold/register_log_threshold_rule_type'; import { InfraBackendLibs } from '../infra_types'; +export const LOGS_RULES_ALERT_CONTEXT = 'observability.logs'; +// Defines which alerts-as-data index logs rules will use +export const LogsRulesTypeAlertDefinition: IRuleTypeAlerts = { + context: LOGS_RULES_ALERT_CONTEXT, + mappings: { fieldMap: legacyExperimentalFieldMap }, + useEcs: true, + useLegacyAlerts: true, +}; + +export const METRICS_RULES_ALERT_CONTEXT = 'observability.metrics'; +// Defines which alerts-as-data index metrics rules will use +export const MetricsRulesTypeAlertDefinition: IRuleTypeAlerts = { + context: METRICS_RULES_ALERT_CONTEXT, + mappings: { fieldMap: legacyExperimentalFieldMap }, + useEcs: true, + useLegacyAlerts: true, +}; + const registerRuleTypes = ( alertingPlugin: PluginSetupContract, libs: InfraBackendLibs, diff --git a/x-pack/plugins/infra/server/plugin.ts b/x-pack/plugins/infra/server/plugin.ts index 67563f857aedb..0fb0aad42d0cc 100644 --- a/x-pack/plugins/infra/server/plugin.ts +++ b/x-pack/plugins/infra/server/plugin.ts @@ -32,6 +32,10 @@ import { InfraKibanaLogEntriesAdapter } from './lib/adapters/log_entries/kibana_ import { KibanaMetricsAdapter } from './lib/adapters/metrics/kibana_metrics_adapter'; import { InfraElasticsearchSourceStatusAdapter } from './lib/adapters/source_status'; import { registerRuleTypes } from './lib/alerting'; +import { + LOGS_RULES_ALERT_CONTEXT, + METRICS_RULES_ALERT_CONTEXT, +} from './lib/alerting/register_rule_types'; import { InfraFieldsDomain } from './lib/domains/fields_domain'; import { InfraLogEntriesDomain } from './lib/domains/log_entries_domain'; import { InfraMetricsDomain } from './lib/domains/metrics_domain'; @@ -116,12 +120,12 @@ export class InfraServerPlugin this.logsRules = new RulesService( LOGS_FEATURE_ID, - 'observability.logs', + LOGS_RULES_ALERT_CONTEXT, this.logger.get('logsRules') ); this.metricsRules = new RulesService( METRICS_FEATURE_ID, - 'observability.metrics', + METRICS_RULES_ALERT_CONTEXT, this.logger.get('metricsRules') ); diff --git a/x-pack/plugins/infra/server/services/rules/rule_data_client.ts b/x-pack/plugins/infra/server/services/rules/rule_data_client.ts index 3a81f957e9314..ebbe3139167c3 100644 --- a/x-pack/plugins/infra/server/services/rules/rule_data_client.ts +++ b/x-pack/plugins/infra/server/services/rules/rule_data_client.ts @@ -6,7 +6,7 @@ */ import { CoreSetup, Logger } from '@kbn/core/server'; -import { experimentalRuleFieldMap } from '@kbn/rule-registry-plugin/common/assets/field_maps/experimental_rule_field_map'; +import { legacyExperimentalFieldMap } from '@kbn/alerts-as-data-utils'; import { Dataset, RuleRegistryPluginSetupContract } from '@kbn/rule-registry-plugin/server'; import { mappingFromFieldMap } from '@kbn/alerting-plugin/common'; @@ -35,7 +35,7 @@ export const createRuleDataClient = ({ componentTemplates: [ { name: 'mappings', - mappings: mappingFromFieldMap(experimentalRuleFieldMap, 'strict'), + mappings: mappingFromFieldMap(legacyExperimentalFieldMap, 'strict'), }, ], }); diff --git a/x-pack/plugins/infra/tsconfig.json b/x-pack/plugins/infra/tsconfig.json index 07586b6a25e1c..2d9c6911435fd 100644 --- a/x-pack/plugins/infra/tsconfig.json +++ b/x-pack/plugins/infra/tsconfig.json @@ -53,8 +53,9 @@ "@kbn/core-saved-objects-common", "@kbn/core-analytics-server", "@kbn/analytics-client", + "@kbn/shared-ux-router", + "@kbn/alerts-as-data-utils", "@kbn/cases-plugin", - "@kbn/shared-ux-router" ], "exclude": ["target/**/*"] } diff --git a/x-pack/plugins/lens/public/datasources/form_based/operations/definitions/calculations/utils.ts b/x-pack/plugins/lens/public/datasources/form_based/operations/definitions/calculations/utils.ts index bac4a8940c689..a90fb9ce24ae9 100644 --- a/x-pack/plugins/lens/public/datasources/form_based/operations/definitions/calculations/utils.ts +++ b/x-pack/plugins/lens/public/datasources/form_based/operations/definitions/calculations/utils.ts @@ -17,6 +17,8 @@ import { adjustTimeScaleLabelSuffix } from '../../time_scale_utils'; import type { ReferenceBasedIndexPatternColumn } from '../column_types'; import { getManagedColumnsFrom, isColumnValidAsReference } from '../../layer_helpers'; import { operationDefinitionMap } from '..'; +import { FieldBasedIndexPatternColumn } from '../../../types'; +import { IndexPatternField } from '../../../../../types'; export const buildLabelFunction = (ofName: (name?: string) => string) => @@ -203,3 +205,45 @@ export function optionallHistogramBasedOperationToExpression( }, ]; } + +function isMetricCounterField(field?: IndexPatternField) { + return field?.timeSeriesMetric === 'counter'; +} + +function checkReferencedColumnMetric( + layer: FormBasedLayer, + columnId: string, + indexPattern: IndexPattern +) { + const column = layer.columns[columnId] as ReferenceBasedIndexPatternColumn; + return column.references + .filter((referencedId) => 'sourceField' in layer.columns[referencedId]) + .map((referencedId) => { + const fieldName = (layer.columns[referencedId] as FieldBasedIndexPatternColumn).sourceField; + if (!isMetricCounterField(indexPattern.getFieldByName(fieldName))) { + return i18n.translate('xpack.lens.indexPattern.invalidReferenceConfiguration', { + defaultMessage: 'Dimension "{dimensionLabel}" is configured incorrectly', + values: { + dimensionLabel: layer.columns[referencedId].label, + }, + }); + } + }); +} + +export function getErrorForRateReference( + layer: FormBasedLayer, + columnId: string, + name: string, + indexPattern: IndexPattern +) { + const dateErrors = checkForDateHistogram(layer, name) ?? []; + const referenceErrors = checkReferences(layer, columnId) ?? []; + const metricCounterErrors = checkReferencedColumnMetric(layer, columnId, indexPattern) ?? []; + if (metricCounterErrors.length) { + return metricCounterErrors.concat(referenceErrors); + } + if (dateErrors.length) { + return dateErrors.concat(referenceErrors); + } +} diff --git a/x-pack/plugins/lens/public/datasources/form_based/operations/definitions/cardinality.tsx b/x-pack/plugins/lens/public/datasources/form_based/operations/definitions/cardinality.tsx index 5537d2c6ee5fc..4290b968d2ca5 100644 --- a/x-pack/plugins/lens/public/datasources/form_based/operations/definitions/cardinality.tsx +++ b/x-pack/plugins/lens/public/datasources/form_based/operations/definitions/cardinality.tsx @@ -80,10 +80,16 @@ export const cardinalityOperation: OperationDefinition< }), allowAsReference: true, input: 'field', - getPossibleOperationForField: ({ aggregationRestrictions, aggregatable, type }) => { + getPossibleOperationForField: ({ + aggregationRestrictions, + aggregatable, + type, + timeSeriesMetric, + }) => { if ( supportedTypes.has(type) && aggregatable && + timeSeriesMetric !== 'counter' && (!aggregationRestrictions || aggregationRestrictions.cardinality) ) { return { dataType: 'number', isBucketed: IS_BUCKETED, scale: SCALE }; diff --git a/x-pack/plugins/lens/public/datasources/form_based/operations/definitions/count.tsx b/x-pack/plugins/lens/public/datasources/form_based/operations/definitions/count.tsx index 2295442f35ad5..174567b371ef7 100644 --- a/x-pack/plugins/lens/public/datasources/form_based/operations/definitions/count.tsx +++ b/x-pack/plugins/lens/public/datasources/form_based/operations/definitions/count.tsx @@ -101,10 +101,16 @@ export const countOperation: OperationDefinition { + getPossibleOperationForField: ({ + aggregationRestrictions, + aggregatable, + type, + timeSeriesMetric, + }) => { if ( type === 'document' || (aggregatable && + timeSeriesMetric !== 'counter' && (!aggregationRestrictions || aggregationRestrictions.value_count) && supportedTypes.has(type)) ) { diff --git a/x-pack/plugins/lens/public/datasources/form_based/operations/definitions/helpers.tsx b/x-pack/plugins/lens/public/datasources/form_based/operations/definitions/helpers.tsx index 69b442ae97d48..c723ff03c3012 100644 --- a/x-pack/plugins/lens/public/datasources/form_based/operations/definitions/helpers.tsx +++ b/x-pack/plugins/lens/public/datasources/form_based/operations/definitions/helpers.tsx @@ -204,3 +204,7 @@ export function getFilter( } return filter; } + +export function isMetricCounterField(field?: IndexPatternField) { + return field?.timeSeriesMetric === 'counter'; +} diff --git a/x-pack/plugins/lens/public/datasources/form_based/operations/definitions/index.ts b/x-pack/plugins/lens/public/datasources/form_based/operations/definitions/index.ts index 4ab267d36d2f2..67232e5861aa1 100644 --- a/x-pack/plugins/lens/public/datasources/form_based/operations/definitions/index.ts +++ b/x-pack/plugins/lens/public/datasources/form_based/operations/definitions/index.ts @@ -489,7 +489,7 @@ interface FieldlessOperationDefinition * Returns the meta data of the operation if applied. Undefined * if the field is not applicable. */ - getPossibleOperation: () => OperationMetadata; + getPossibleOperation: (index?: IndexPattern) => OperationMetadata | undefined; /** * Function turning a column into an agg config passed to the `esaggs` function * together with the agg configs returned from other columns. diff --git a/x-pack/plugins/lens/public/datasources/form_based/operations/definitions/last_value.tsx b/x-pack/plugins/lens/public/datasources/form_based/operations/definitions/last_value.tsx index 1f0e50ecb2ac6..f5abbc74683c6 100644 --- a/x-pack/plugins/lens/public/datasources/form_based/operations/definitions/last_value.tsx +++ b/x-pack/plugins/lens/public/datasources/form_based/operations/definitions/last_value.tsx @@ -199,8 +199,8 @@ export const lastValueOperation: OperationDefinition< : oldColumn.filter, }; }, - getPossibleOperationForField: ({ aggregationRestrictions, type }) => { - if (supportedTypes.has(type) && !aggregationRestrictions) { + getPossibleOperationForField: ({ aggregationRestrictions, type, timeSeriesMetric }) => { + if (supportedTypes.has(type) && !aggregationRestrictions && timeSeriesMetric !== 'counter') { return { dataType: type as DataType, isBucketed: false, diff --git a/x-pack/plugins/lens/public/datasources/form_based/operations/definitions/metrics.tsx b/x-pack/plugins/lens/public/datasources/form_based/operations/definitions/metrics.tsx index 5477879a9a3fa..b672d50232172 100644 --- a/x-pack/plugins/lens/public/datasources/form_based/operations/definitions/metrics.tsx +++ b/x-pack/plugins/lens/public/datasources/form_based/operations/definitions/metrics.tsx @@ -48,6 +48,10 @@ const typeToFn: Record = { const supportedTypes = ['number', 'histogram']; +function isTimeSeriesCompatible(type: string, timeSeriesMetric?: string) { + return timeSeriesMetric !== 'counter' || ['min', 'max'].includes(type); +} + function buildMetricOperation>({ type, displayName, @@ -94,10 +98,16 @@ function buildMetricOperation>({ description, input: 'field', timeScalingMode: optionalTimeScaling ? 'optional' : undefined, - getPossibleOperationForField: ({ aggregationRestrictions, aggregatable, type: fieldType }) => { + getPossibleOperationForField: ({ + aggregationRestrictions, + aggregatable, + type: fieldType, + timeSeriesMetric, + }) => { if ( (supportedTypes.includes(fieldType) || (supportsDate && fieldType === 'date')) && aggregatable && + isTimeSeriesCompatible(type, timeSeriesMetric) && (!aggregationRestrictions || aggregationRestrictions[type]) ) { return { @@ -113,6 +123,7 @@ function buildMetricOperation>({ newField && supportedTypes.includes(newField.type) && newField.aggregatable && + isTimeSeriesCompatible(type, newField.timeSeriesMetric) && (!newField.aggregationRestrictions || newField.aggregationRestrictions![type]) ); }, diff --git a/x-pack/plugins/lens/public/datasources/form_based/operations/definitions/percentile.tsx b/x-pack/plugins/lens/public/datasources/form_based/operations/definitions/percentile.tsx index a42268276613f..2c507cee5ffcd 100644 --- a/x-pack/plugins/lens/public/datasources/form_based/operations/definitions/percentile.tsx +++ b/x-pack/plugins/lens/public/datasources/form_based/operations/definitions/percentile.tsx @@ -88,10 +88,16 @@ export const percentileOperation: OperationDefinition< filterable: true, shiftable: true, canReduceTimeRange: true, - getPossibleOperationForField: ({ aggregationRestrictions, aggregatable, type: fieldType }) => { + getPossibleOperationForField: ({ + aggregationRestrictions, + aggregatable, + type: fieldType, + timeSeriesMetric, + }) => { if ( supportedFieldTypes.includes(fieldType) && aggregatable && + timeSeriesMetric !== 'counter' && (!aggregationRestrictions || aggregationRestrictions.percentiles) ) { return { diff --git a/x-pack/plugins/lens/public/datasources/form_based/operations/definitions/percentile_ranks.tsx b/x-pack/plugins/lens/public/datasources/form_based/operations/definitions/percentile_ranks.tsx index 15275c81ff9c3..0f44acb92de49 100644 --- a/x-pack/plugins/lens/public/datasources/form_based/operations/definitions/percentile_ranks.tsx +++ b/x-pack/plugins/lens/public/datasources/form_based/operations/definitions/percentile_ranks.tsx @@ -80,10 +80,16 @@ export const percentileRanksOperation: OperationDefinition< filterable: true, shiftable: true, canReduceTimeRange: true, - getPossibleOperationForField: ({ aggregationRestrictions, aggregatable, type: fieldType }) => { + getPossibleOperationForField: ({ + aggregationRestrictions, + aggregatable, + type: fieldType, + timeSeriesMetric, + }) => { if ( supportedFieldTypes.includes(fieldType) && aggregatable && + timeSeriesMetric !== 'counter' && (!aggregationRestrictions || !aggregationRestrictions.percentile_ranks) ) { return { diff --git a/x-pack/plugins/lens/public/datasources/form_based/operations/layer_helpers.ts b/x-pack/plugins/lens/public/datasources/form_based/operations/layer_helpers.ts index f1b93b33467a5..23938baa83365 100644 --- a/x-pack/plugins/lens/public/datasources/form_based/operations/layer_helpers.ts +++ b/x-pack/plugins/lens/public/datasources/form_based/operations/layer_helpers.ts @@ -360,7 +360,7 @@ export function insertNewColumn({ // TODO: need to create on the fly the new columns for Formula, // like we do for fullReferences to show a seamless transition } - const possibleOperation = operationDefinition.getPossibleOperation(); + const possibleOperation = operationDefinition.getPossibleOperation(indexPattern); const isBucketed = Boolean(possibleOperation?.isBucketed); const addOperationFn = isBucketed ? addBucket : addMetric; const buildColumnFn = columnParams @@ -1689,7 +1689,7 @@ export function isOperationAllowedAsReference({ hasValidMetadata = Boolean(metadata) && validation.validateMetadata(metadata!, operationType, field.name); } else if (operationDefinition.input === 'none') { - const metadata = operationDefinition.getPossibleOperation(); + const metadata = operationDefinition.getPossibleOperation(indexPattern); hasValidMetadata = Boolean(metadata) && validation.validateMetadata(metadata!, operationType); } else if (operationDefinition.input === 'fullReference') { const metadata = operationDefinition.getPossibleOperation(indexPattern); diff --git a/x-pack/plugins/lens/public/datasources/form_based/operations/operations.ts b/x-pack/plugins/lens/public/datasources/form_based/operations/operations.ts index b44c0a0e4cc55..151081e190a68 100644 --- a/x-pack/plugins/lens/public/datasources/form_based/operations/operations.ts +++ b/x-pack/plugins/lens/public/datasources/form_based/operations/operations.ts @@ -241,13 +241,16 @@ export function getAvailableOperationsByMetadata( ); }); } else if (operationDefinition.input === 'none') { - addToMap( - { - type: 'none', - operationType: operationDefinition.type, - }, - operationDefinition.getPossibleOperation() - ); + const validOperation = operationDefinition.getPossibleOperation(indexPattern); + if (validOperation) { + addToMap( + { + type: 'none', + operationType: operationDefinition.type, + }, + validOperation + ); + } } else if (operationDefinition.input === 'fullReference') { const validOperation = operationDefinition.getPossibleOperation(indexPattern); if (validOperation) { diff --git a/x-pack/plugins/lens/public/embeddable/embeddable.test.tsx b/x-pack/plugins/lens/public/embeddable/embeddable.test.tsx index 92bfa95eb2aef..ee8f733ea5bc7 100644 --- a/x-pack/plugins/lens/public/embeddable/embeddable.test.tsx +++ b/x-pack/plugins/lens/public/embeddable/embeddable.test.tsx @@ -544,11 +544,7 @@ describe('embeddable', () => { expect(outputIndexPatterns[1].id).toEqual('456'); }); - it('should re-render if new input is pushed', async () => { - const timeRange: TimeRange = { from: 'now-15d', to: 'now' }; - const query: Query = { language: 'kquery', query: '' }; - const filters: Filter[] = [{ meta: { alias: 'test', negate: false, disabled: false } }]; - + it('should re-render once on filter change', async () => { const embeddable = new Embeddable( { timefilter: dataPluginMock.createSetupContract().query.timefilter.timefilter, @@ -592,10 +588,7 @@ describe('embeddable', () => { expect(expressionRenderer).toHaveBeenCalledTimes(1); embeddable.updateInput({ - timeRange, - query, - filters, - searchSessionId: 'searchSessionId', + filters: [{ meta: { alias: 'test', negate: false, disabled: false } }], }); await new Promise((resolve) => setTimeout(resolve, 0)); @@ -603,7 +596,7 @@ describe('embeddable', () => { expect(expressionRenderer).toHaveBeenCalledTimes(2); }); - it('should re-render once if session id changes and ', async () => { + it('should re-render once on search session change', async () => { const embeddable = new Embeddable( { timefilter: dataPluginMock.createSetupContract().query.timefilter.timefilter, @@ -639,7 +632,7 @@ describe('embeddable', () => { indexPatternRefs: [], }), }, - { id: '123' } as LensEmbeddableInput + { id: '123', searchSessionId: 'firstSession' } as LensEmbeddableInput ); await embeddable.initializeSavedVis({ id: '123' } as LensEmbeddableInput); embeddable.render(mountpoint); @@ -647,10 +640,13 @@ describe('embeddable', () => { expect(expressionRenderer).toHaveBeenCalledTimes(1); embeddable.updateInput({ - searchSessionId: 'newSession', + filters: [{ meta: { alias: 'test', negate: false, disabled: false } }], }); - embeddable.reload(); + await new Promise((resolve) => setTimeout(resolve, 0)); + embeddable.updateInput({ + searchSessionId: 'nextSession', + }); await new Promise((resolve) => setTimeout(resolve, 0)); expect(expressionRenderer).toHaveBeenCalledTimes(2); @@ -1134,13 +1130,14 @@ describe('embeddable', () => { }); it('should call onload after rerender and onData$ call ', async () => { + const onDataTimeout = 10; const onLoad = jest.fn(); const adapters = { tables: {} }; expressionRenderer = jest.fn(({ onData$ }) => { setTimeout(() => { onData$?.({}, adapters); - }, 10); + }, onDataTimeout); return null; }); @@ -1189,7 +1186,7 @@ describe('embeddable', () => { expect(onLoad).toHaveBeenCalledWith(true); expect(onLoad).toHaveBeenCalledTimes(1); - await new Promise((resolve) => setTimeout(resolve, 20)); + await new Promise((resolve) => setTimeout(resolve, onDataTimeout * 1.5)); // loading should become false expect(onLoad).toHaveBeenCalledTimes(2); @@ -1200,17 +1197,15 @@ describe('embeddable', () => { embeddable.updateInput({ searchSessionId: 'newSession', }); - embeddable.reload(); + + await new Promise((resolve) => setTimeout(resolve, 0)); // loading should become again true expect(onLoad).toHaveBeenCalledTimes(3); expect(onLoad).toHaveBeenNthCalledWith(3, true); - - await new Promise((resolve) => setTimeout(resolve, 0)); - expect(expressionRenderer).toHaveBeenCalledTimes(2); - await new Promise((resolve) => setTimeout(resolve, 20)); + await new Promise((resolve) => setTimeout(resolve, onDataTimeout * 1.5)); // loading should again become false expect(onLoad).toHaveBeenCalledTimes(4); diff --git a/x-pack/plugins/lens/public/embeddable/embeddable.tsx b/x-pack/plugins/lens/public/embeddable/embeddable.tsx index 2f6d57bc86942..f156a0959dcf5 100644 --- a/x-pack/plugins/lens/public/embeddable/embeddable.tsx +++ b/x-pack/plugins/lens/public/embeddable/embeddable.tsx @@ -5,7 +5,7 @@ * 2.0. */ -import { isEqual, uniqBy } from 'lodash'; +import { uniqBy } from 'lodash'; import React from 'react'; import { css } from '@emotion/react'; import { i18n } from '@kbn/i18n'; @@ -382,11 +382,9 @@ export class Embeddable private savedVis: Document | undefined; private expression: string | undefined | null; private domNode: HTMLElement | Element | undefined; - private subscription: Subscription; private isInitialized = false; private inputReloadSubscriptions: Subscription[]; private isDestroyed?: boolean; - private embeddableTitle?: string; private lensInspector: LensInspector; private logError(type: 'runtime' | 'validation') { @@ -396,13 +394,6 @@ export class Embeddable ); } - private externalSearchContext: { - timeRange?: TimeRange; - query?: Query; - filters?: Filter[]; - searchSessionId?: string; - } = {}; - private activeData?: TableInspectorAdapter; private dataViews: DataView[] = []; @@ -424,25 +415,14 @@ export class Embeddable this.lensInspector = getLensInspectorService(deps.inspector); this.expressionRenderer = deps.expressionRenderer; - let containerStateChangedCalledAlready = false; this.initializeSavedVis(initialInput) .then(() => { this.loadUserMessages(); - if (!containerStateChangedCalledAlready) { - this.onContainerStateChanged(initialInput); - } else { - this.reload(); - } + this.reload(); }) .catch((e) => this.onFatalError(e)); - this.subscription = this.getUpdated$().subscribe(() => { - containerStateChangedCalledAlready = true; - this.onContainerStateChanged(this.input); - }); - const input$ = this.getInput$(); - this.embeddableTitle = this.getTitle(); this.inputReloadSubscriptions = []; @@ -482,7 +462,6 @@ export class Embeddable ); // Re-initialize the visualization if either the attributes or the saved object id changes - this.inputReloadSubscriptions.push( input$ .pipe( @@ -507,7 +486,7 @@ export class Embeddable // reset removable messages // Dashboard search/context changes are detected here this.additionalUserMessages = {}; - this.onContainerStateChanged(input); + this.reload(); } ) ); @@ -728,45 +707,6 @@ export class Embeddable this.isInitialized = true; } - onContainerStateChanged(containerState: LensEmbeddableInput) { - if (this.handleContainerStateChanged(containerState)) { - this.reload(); - } - } - - handleContainerStateChanged(containerState: LensEmbeddableInput): boolean { - let isDirty = false; - const cleanedFilters = containerState.filters - ? containerState.filters.filter((filter) => !filter.meta.disabled) - : undefined; - const nextTimeRange = - containerState.timeslice !== undefined - ? { - from: new Date(containerState.timeslice[0]).toISOString(), - to: new Date(containerState.timeslice[1]).toISOString(), - mode: 'absolute' as 'absolute', - } - : containerState.timeRange; - if ( - !isEqual(nextTimeRange, this.externalSearchContext.timeRange) || - !isEqual(containerState.query, this.externalSearchContext.query) || - !isEqual(cleanedFilters, this.externalSearchContext.filters) || - this.externalSearchContext.searchSessionId !== containerState.searchSessionId || - this.embeddableTitle !== this.getTitle() - ) { - this.externalSearchContext = { - timeRange: nextTimeRange, - query: containerState.query, - filters: cleanedFilters, - searchSessionId: containerState.searchSessionId, - }; - this.embeddableTitle = this.getTitle(); - isDirty = true; - } - - return isDirty; - } - private getSearchWarningMessages(adapters?: Partial): UserMessage[] { if (!this.activeDatasource || !this.activeDatasourceId || !adapters?.requests) { return []; @@ -928,7 +868,7 @@ export class Embeddable embeddableTitle: this.getTitle(), ...(input.palette ? { theme: { palette: input.palette } } : {}), }} - searchSessionId={this.externalSearchContext.searchSessionId} + searchSessionId={this.getInput().searchSessionId} handleEvent={this.handleEvent} onData$={this.updateActiveData} onRender$={this.onRender} @@ -1068,8 +1008,16 @@ export class Embeddable throw new Error('savedVis is required for getMergedSearchContext'); } + const input = this.getInput(); const context: ExecutionContextSearch = { - timeRange: this.externalSearchContext.timeRange, + timeRange: + input.timeslice !== undefined + ? { + from: new Date(input.timeslice[0]).toISOString(), + to: new Date(input.timeslice[1]).toISOString(), + mode: 'absolute' as 'absolute', + } + : input.timeRange, query: [this.savedVis.state.query], filters: this.deps.injectFilterReferences( this.savedVis.state.filters, @@ -1078,12 +1026,15 @@ export class Embeddable disableShardWarnings: true, }; - if (this.externalSearchContext.query) { - context.query = [this.externalSearchContext.query, ...(context.query as Query[])]; + if (input.query) { + context.query = [input.query, ...(context.query as Query[])]; } - if (this.externalSearchContext.filters?.length) { - context.filters = [...this.externalSearchContext.filters, ...(context.filters as Filter[])]; + if (input.filters?.length) { + context.filters = [ + ...input.filters.filter((filter) => !filter.meta.disabled), + ...(context.filters as Filter[]), + ]; } return context; @@ -1172,11 +1123,7 @@ export class Embeddable if (!this.savedVis || !this.isInitialized || this.isDestroyed) { return; } - if (this.handleContainerStateChanged(this.input)) { - // reset removable messages - // Unified histogram search/context changes are detected here - this.additionalUserMessages = {}; - } + if (this.domNode) { this.render(this.domNode); } @@ -1362,9 +1309,6 @@ export class Embeddable if (this.domNode) { unmountComponentAtNode(this.domNode); } - if (this.subscription) { - this.subscription.unsubscribe(); - } } public getSelfStyledOptions() { diff --git a/x-pack/plugins/lists/public/exceptions/components/builder/entry_renderer.test.tsx b/x-pack/plugins/lists/public/exceptions/components/builder/entry_renderer.test.tsx index 842f1bc2b8db9..f2b820fe71769 100644 --- a/x-pack/plugins/lists/public/exceptions/components/builder/entry_renderer.test.tsx +++ b/x-pack/plugins/lists/public/exceptions/components/builder/entry_renderer.test.tsx @@ -122,6 +122,9 @@ describe('BuilderEntryItem', () => { expect(wrapper.find('.euiFormHelpText.euiFormRow__text').at(0).text()).toEqual( i18n.CUSTOM_COMBOBOX_OPTION_TEXT ); + expect(wrapper.find('[data-test-subj="exceptionBuilderEntryField"]').at(0).props()).toEqual( + expect.objectContaining({ acceptsCustomOptions: true }) + ); }); test('it does not render custom option text when "allowCustomOptions" is "true" and it is a nested entry', () => { @@ -154,6 +157,44 @@ describe('BuilderEntryItem', () => { ); expect(wrapper.find('.euiFormHelpText.euiFormRow__text').exists()).toBeFalsy(); + expect(wrapper.find('[data-test-subj="exceptionBuilderEntryField"]').at(0).props()).toEqual( + expect.objectContaining({ acceptsCustomOptions: false }) + ); + }); + + test('it does not render custom option text when "allowCustomOptions" is "false"', () => { + wrapper = mount( + + ); + + expect(wrapper.find('.euiFormHelpText.euiFormRow__text').exists()).toBeFalsy(); + expect(wrapper.find('[data-test-subj="exceptionBuilderEntryField"]').at(0).props()).toEqual( + expect.objectContaining({ acceptsCustomOptions: false }) + ); }); test('it render mapping issues warning text when field has mapping conflicts', () => { diff --git a/x-pack/plugins/lists/public/exceptions/components/builder/entry_renderer.tsx b/x-pack/plugins/lists/public/exceptions/components/builder/entry_renderer.tsx index 30cc6e3cd0d91..e2fbc06acca60 100644 --- a/x-pack/plugins/lists/public/exceptions/components/builder/entry_renderer.tsx +++ b/x-pack/plugins/lists/public/exceptions/components/builder/entry_renderer.tsx @@ -205,7 +205,7 @@ export const BuilderEntryItem: React.FC = ({ isLoading={false} isDisabled={isDisabled || indexPattern == null} onChange={handleFieldChange} - acceptsCustomOptions={entry.nested == null} + acceptsCustomOptions={entry.nested == null && allowCustomOptions} data-test-subj="exceptionBuilderEntryField" showMappingConflicts={true} /> diff --git a/x-pack/plugins/maps/public/components/metrics_editor/metric_editor.tsx b/x-pack/plugins/maps/public/components/metrics_editor/metric_editor.tsx index 42dbfe6107b43..26cf6d5313821 100644 --- a/x-pack/plugins/maps/public/components/metrics_editor/metric_editor.tsx +++ b/x-pack/plugins/maps/public/components/metrics_editor/metric_editor.tsx @@ -34,7 +34,11 @@ function filterFieldsForAgg(fields: DataViewField[], aggType: AGG_TYPE) { } return fields.filter((field) => { - return field.aggregatable && metricAggFieldTypes.includes(field.type); + return ( + field.aggregatable && + metricAggFieldTypes.includes(field.type) && + (field.timeSeriesMetric !== 'counter' || ['min', 'max'].includes(aggType)) + ); }); } diff --git a/x-pack/plugins/observability/public/pages/overview/containers/overview_page/helpers/index.ts b/x-pack/plugins/ml/common/constants/cases.ts similarity index 58% rename from x-pack/plugins/observability/public/pages/overview/containers/overview_page/helpers/index.ts rename to x-pack/plugins/ml/common/constants/cases.ts index bc5d96d4ba7d6..ba3b5b913d1b8 100644 --- a/x-pack/plugins/observability/public/pages/overview/containers/overview_page/helpers/index.ts +++ b/x-pack/plugins/ml/common/constants/cases.ts @@ -5,5 +5,5 @@ * 2.0. */ -export { calculateBucketSize, calculateTimeRangeBucketSize } from './calculate_bucket_size'; -export { useOverviewMetrics } from './use_metrics'; +export const CASE_ATTACHMENT_TYPE_ID_ANOMALY_SWIMLANE = 'ml_anomaly_swimlane' as const; +export const CASE_ATTACHMENT_TYPE_ID_ANOMALY_EXPLORER_CHARTS = 'ml_anomaly_charts' as const; diff --git a/x-pack/plugins/ml/public/cases/register_anomaly_charts_attachment.tsx b/x-pack/plugins/ml/public/cases/register_anomaly_charts_attachment.tsx index 5651e197b9617..0a69c8d4b8c12 100644 --- a/x-pack/plugins/ml/public/cases/register_anomaly_charts_attachment.tsx +++ b/x-pack/plugins/ml/public/cases/register_anomaly_charts_attachment.tsx @@ -10,7 +10,8 @@ import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n-react'; import type { CasesUiSetup } from '@kbn/cases-plugin/public'; import type { CoreStart } from '@kbn/core/public'; -import { ANOMALY_EXPLORER_CHARTS_EMBEDDABLE_TYPE, getEmbeddableComponent } from '../embeddables'; +import { CASE_ATTACHMENT_TYPE_ID_ANOMALY_EXPLORER_CHARTS } from '../../common/constants/cases'; +import { getEmbeddableComponent } from '../embeddables'; import type { MlStartDependencies } from '../plugin'; import { PLUGIN_ICON } from '../../common/constants/app'; @@ -20,13 +21,13 @@ export function registerAnomalyChartsCasesAttachment( pluginStart: MlStartDependencies ) { const EmbeddableComponent = getEmbeddableComponent( - ANOMALY_EXPLORER_CHARTS_EMBEDDABLE_TYPE, + CASE_ATTACHMENT_TYPE_ID_ANOMALY_EXPLORER_CHARTS, coreStart, pluginStart ); cases.attachmentFramework.registerPersistableState({ - id: ANOMALY_EXPLORER_CHARTS_EMBEDDABLE_TYPE, + id: CASE_ATTACHMENT_TYPE_ID_ANOMALY_EXPLORER_CHARTS, icon: PLUGIN_ICON, displayName: i18n.translate('xpack.ml.cases.anomalyCharts.displayName', { defaultMessage: 'Anomaly charts', diff --git a/x-pack/plugins/ml/public/cases/register_anomaly_swim_lane_attachment.tsx b/x-pack/plugins/ml/public/cases/register_anomaly_swim_lane_attachment.tsx index 06c441ac05b61..670a1a266cc41 100644 --- a/x-pack/plugins/ml/public/cases/register_anomaly_swim_lane_attachment.tsx +++ b/x-pack/plugins/ml/public/cases/register_anomaly_swim_lane_attachment.tsx @@ -10,9 +10,9 @@ import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n-react'; import type { CasesUiSetup } from '@kbn/cases-plugin/public'; import type { CoreStart } from '@kbn/core/public'; +import { CASE_ATTACHMENT_TYPE_ID_ANOMALY_SWIMLANE } from '../../common/constants/cases'; import { getEmbeddableComponent } from '../embeddables'; import type { MlStartDependencies } from '../plugin'; -import { ANOMALY_SWIMLANE_EMBEDDABLE_TYPE } from '..'; import { PLUGIN_ICON } from '../../common/constants/app'; export function registerAnomalySwimLaneCasesAttachment( @@ -21,13 +21,13 @@ export function registerAnomalySwimLaneCasesAttachment( pluginStart: MlStartDependencies ) { const EmbeddableComponent = getEmbeddableComponent( - ANOMALY_SWIMLANE_EMBEDDABLE_TYPE, + CASE_ATTACHMENT_TYPE_ID_ANOMALY_SWIMLANE, coreStart, pluginStart ); cases.attachmentFramework.registerPersistableState({ - id: ANOMALY_SWIMLANE_EMBEDDABLE_TYPE, + id: CASE_ATTACHMENT_TYPE_ID_ANOMALY_SWIMLANE, icon: PLUGIN_ICON, displayName: i18n.translate('xpack.ml.cases.anomalySwimLane.displayName', { defaultMessage: 'Anomaly swim lane', diff --git a/x-pack/plugins/ml/server/lib/capabilities/capabilities_switcher.ts b/x-pack/plugins/ml/server/lib/capabilities/capabilities_switcher.ts index a3fa37920e7f9..bc90437e8fcc6 100644 --- a/x-pack/plugins/ml/server/lib/capabilities/capabilities_switcher.ts +++ b/x-pack/plugins/ml/server/lib/capabilities/capabilities_switcher.ts @@ -23,9 +23,8 @@ export const setupCapabilitiesSwitcher = ( function getSwitcher(license$: Observable, logger: Logger): CapabilitiesSwitcher { return async (request, capabilities) => { const isAnonymousRequest = !request.route.options.authRequired; - if (isAnonymousRequest) { - return capabilities; + return {}; } try { @@ -34,11 +33,11 @@ function getSwitcher(license$: Observable, logger: Logger): Capabiliti // full license, leave capabilities as they were if (mlEnabled && isFullLicense(license)) { - return capabilities; + return {}; } - const mlCaps = capabilities.ml as MlCapabilities; - const originalCapabilities = cloneDeep(mlCaps); + const originalCapabilities = capabilities.ml as MlCapabilities; + const mlCaps = cloneDeep(originalCapabilities); // not full licence, switch off all capabilities Object.keys(mlCaps).forEach((k) => { @@ -50,10 +49,10 @@ function getSwitcher(license$: Observable, logger: Logger): Capabiliti basicLicenseMlCapabilities.forEach((c) => (mlCaps[c] = originalCapabilities[c])); } - return capabilities; + return { ml: mlCaps }; } catch (e) { logger.debug(`Error updating capabilities for ML based on licensing: ${e}`); - return capabilities; + return {}; } }; } diff --git a/x-pack/plugins/ml/server/plugin.ts b/x-pack/plugins/ml/server/plugin.ts index f9b69c1c6ab2c..75c3082a61f63 100644 --- a/x-pack/plugins/ml/server/plugin.ts +++ b/x-pack/plugins/ml/server/plugin.ts @@ -67,6 +67,10 @@ import { ML_ALERT_TYPES } from '../common/constants/alerts'; import { alertingRoutes } from './routes/alerting'; import { registerCollector } from './usage'; import { SavedObjectsSyncService } from './saved_objects/sync_task'; +import { + CASE_ATTACHMENT_TYPE_ID_ANOMALY_SWIMLANE, + CASE_ATTACHMENT_TYPE_ID_ANOMALY_EXPLORER_CHARTS, +} from '../common/constants/cases'; export type MlPluginSetup = SharedServices; export type MlPluginStart = void; @@ -249,6 +253,16 @@ export class MlServerPlugin registerCollector(plugins.usageCollection, coreSetup.savedObjects.getKibanaIndex()); } + if (plugins.cases) { + plugins.cases.attachmentFramework.registerPersistableState({ + id: CASE_ATTACHMENT_TYPE_ID_ANOMALY_SWIMLANE, + }); + + plugins.cases.attachmentFramework.registerPersistableState({ + id: CASE_ATTACHMENT_TYPE_ID_ANOMALY_EXPLORER_CHARTS, + }); + } + return sharedServicesProviders; } diff --git a/x-pack/plugins/ml/server/types.ts b/x-pack/plugins/ml/server/types.ts index a66cae1c945b8..d054a2289be50 100644 --- a/x-pack/plugins/ml/server/types.ts +++ b/x-pack/plugins/ml/server/types.ts @@ -26,6 +26,7 @@ import { TaskManagerSetupContract, TaskManagerStartContract, } from '@kbn/task-manager-plugin/server'; +import type { CasesSetup } from '@kbn/cases-plugin/server'; import type { RouteGuard } from './lib/route_guard'; import type { ResolveMlCapabilities } from '../common/types/capabilities'; import type { MlLicense } from '../common/license'; @@ -63,6 +64,7 @@ export interface PluginsSetup { actions?: ActionsPlugin['setup']; usageCollection?: UsageCollectionSetup; taskManager: TaskManagerSetupContract; + cases?: CasesSetup; } export interface PluginsStart { diff --git a/x-pack/plugins/observability/README.md b/x-pack/plugins/observability/README.md index fecaabddf6580..809611c3f648f 100644 --- a/x-pack/plugins/observability/README.md +++ b/x-pack/plugins/observability/README.md @@ -13,16 +13,6 @@ xpack.ruleRegistry.write.enabled: true When this is set to `true`, your alerts should show on the alerts page. -## SLOs - - -If you have: - -```yaml -xpack.observability.unsafe.slo.enabled: true -``` - -In your Kibana configuration, the SLO feature will be available. ## Shared navigation diff --git a/x-pack/plugins/observability/common/index.ts b/x-pack/plugins/observability/common/index.ts index 60bb04b3c733d..9368f48829d67 100644 --- a/x-pack/plugins/observability/common/index.ts +++ b/x-pack/plugins/observability/common/index.ts @@ -5,9 +5,13 @@ * 2.0. */ -export type { AsDuration, AsPercent, TimeUnitChar } from './utils/formatters'; +export type { AsDuration, AsPercent, TimeUnitChar, TimeFormatter } from './utils/formatters'; -export { formatDurationFromTimeUnitChar } from './utils/formatters'; +export { + formatDurationFromTimeUnitChar, + asPercent, + getDurationFormatter, +} from './utils/formatters'; export { getInspectResponse } from './utils/get_inspect_response'; export { ProcessorEvent } from './processor_event'; diff --git a/x-pack/plugins/observability/dev_docs/slo.md b/x-pack/plugins/observability/dev_docs/slo.md index 595eac175ae57..1030efa1f87dd 100644 --- a/x-pack/plugins/observability/dev_docs/slo.md +++ b/x-pack/plugins/observability/dev_docs/slo.md @@ -1,6 +1,6 @@ # SLO -Add the feature flag: `xpack.observability.unsafe.slo.enabled: true` in your Kibana config to enable the various SLO APIs. +Starting in 8.8, SLO is enabled by default. ## Supported SLI diff --git a/x-pack/plugins/observability/public/application/index.tsx b/x-pack/plugins/observability/public/application/index.tsx index 8a36ffe166710..bce6ad7918632 100644 --- a/x-pack/plugins/observability/public/application/index.tsx +++ b/x-pack/plugins/observability/public/application/index.tsx @@ -5,6 +5,7 @@ * 2.0. */ +import { EuiErrorBoundary } from '@elastic/eui'; import React from 'react'; import ReactDOM from 'react-dom'; import { Router, Switch } from 'react-router-dom'; @@ -87,43 +88,45 @@ export const renderApp = ({ const ApplicationUsageTrackingProvider = usageCollection?.components.ApplicationUsageTrackingProvider ?? React.Fragment; ReactDOM.render( - - - - + + + - - - - - - - - - - - - - - - - - - , + + + + + + + + + + + + + + + + + + + + , element ); return () => { diff --git a/x-pack/plugins/observability/public/components/app/empty_sections/index.tsx b/x-pack/plugins/observability/public/components/app/empty_sections/index.tsx deleted file mode 100644 index 41fb539de2042..0000000000000 --- a/x-pack/plugins/observability/public/components/app/empty_sections/index.tsx +++ /dev/null @@ -1,56 +0,0 @@ -/* - * 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 { EuiFlexGrid, EuiFlexItem, EuiSpacer } from '@elastic/eui'; -import React, { useContext } from 'react'; -import { ThemeContext } from 'styled-components'; -import { useKibana } from '@kbn/kibana-react-plugin/public'; -import { ObservabilityAppServices } from '../../../application/types'; -import { FETCH_STATUS } from '../../../hooks/use_fetcher'; -import { useHasData } from '../../../hooks/use_has_data'; -import { getEmptySections } from '../../../pages/overview'; -import { EmptySection } from './empty_section'; - -export function EmptySections() { - const { http } = useKibana().services; - const theme = useContext(ThemeContext); - const { hasDataMap } = useHasData(); - - const appEmptySections = getEmptySections({ http }).filter(({ id }) => { - const app = hasDataMap[id]; - if (app) { - return app.status === FETCH_STATUS.FAILURE || !app.hasData; - } - return false; - }); - return ( - - - 2 ? 2 : 1 - } - gutterSize="s" - > - {appEmptySections.map((app) => { - return ( - - - - ); - })} - - - ); -} diff --git a/x-pack/plugins/observability/public/components/app/fleet_panel/index.tsx b/x-pack/plugins/observability/public/components/app/fleet_panel/index.tsx deleted file mode 100644 index 09fea69fa2775..0000000000000 --- a/x-pack/plugins/observability/public/components/app/fleet_panel/index.tsx +++ /dev/null @@ -1,40 +0,0 @@ -/* - * 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 { EuiCard, EuiLink, EuiTextColor } from '@elastic/eui'; -import { i18n } from '@kbn/i18n'; -import React from 'react'; -import { useKibana } from '@kbn/kibana-react-plugin/public'; -import { ObservabilityAppServices } from '../../../application/types'; - -export function FleetPanel() { - const { http } = useKibana().services; - - return ( - - {i18n.translate('xpack.observability.fleet.text', { - defaultMessage: - 'The Elastic Agent provides a simple, unified way to add monitoring for logs, metrics, and other types of data to your hosts. You no longer need to install multiple Beats and other agents, making it easier and faster to deploy configurations across your infrastructure.', - })} - - } - footer={ - - {i18n.translate('xpack.observability.fleet.button', { - defaultMessage: 'Try Fleet', - })} - - } - title={i18n.translate('xpack.observability.fleet.title', { - defaultMessage: 'Have you seen our new Fleet?', - })} - /> - ); -} diff --git a/x-pack/plugins/observability/public/components/app/section/apm/index.tsx b/x-pack/plugins/observability/public/components/app/section/apm/index.tsx index a8d7aafcbaef4..7765015c2d1d2 100644 --- a/x-pack/plugins/observability/public/components/app/section/apm/index.tsx +++ b/x-pack/plugins/observability/public/components/app/section/apm/index.tsx @@ -31,7 +31,7 @@ import { useHasData } from '../../../../hooks/use_has_data'; import { ChartContainer } from '../../chart_container'; import { StyledStat } from '../../styled_stat'; import { onBrushEnd } from '../helper'; -import { BucketSize } from '../../../../pages/overview'; +import type { BucketSize } from '../../../../pages/overview/helpers/calculate_bucket_size'; interface Props { bucketSize: BucketSize; diff --git a/x-pack/plugins/observability/public/components/app/section/logs/index.tsx b/x-pack/plugins/observability/public/components/app/section/logs/index.tsx index c8538c5582683..b9e6478a94036 100644 --- a/x-pack/plugins/observability/public/components/app/section/logs/index.tsx +++ b/x-pack/plugins/observability/public/components/app/section/logs/index.tsx @@ -33,7 +33,7 @@ import { formatStatValue } from '../../../../utils/format_stat_value'; import { ChartContainer } from '../../chart_container'; import { StyledStat } from '../../styled_stat'; import { onBrushEnd } from '../helper'; -import { BucketSize } from '../../../../pages/overview'; +import type { BucketSize } from '../../../../pages/overview/helpers/calculate_bucket_size'; interface Props { bucketSize: BucketSize; diff --git a/x-pack/plugins/observability/public/components/app/section/metrics/index.tsx b/x-pack/plugins/observability/public/components/app/section/metrics/index.tsx index 2d02a4cbb3566..481bf8476f1d7 100644 --- a/x-pack/plugins/observability/public/components/app/section/metrics/index.tsx +++ b/x-pack/plugins/observability/public/components/app/section/metrics/index.tsx @@ -30,7 +30,7 @@ import { useDatePickerContext } from '../../../../hooks/use_date_picker_context' import { HostLink } from './host_link'; import { formatDuration } from './lib/format_duration'; import { MetricWithSparkline } from './metric_with_sparkline'; -import { BucketSize } from '../../../../pages/overview'; +import type { BucketSize } from '../../../../pages/overview/helpers/calculate_bucket_size'; const COLOR_ORANGE = 7; const COLOR_BLUE = 1; diff --git a/x-pack/plugins/observability/public/components/app/section/uptime/index.tsx b/x-pack/plugins/observability/public/components/app/section/uptime/index.tsx index 04f23fd5a4baf..8e76c1316be21 100644 --- a/x-pack/plugins/observability/public/components/app/section/uptime/index.tsx +++ b/x-pack/plugins/observability/public/components/app/section/uptime/index.tsx @@ -34,7 +34,7 @@ import { Series } from '../../../../typings'; import { ChartContainer } from '../../chart_container'; import { StyledStat } from '../../styled_stat'; import { onBrushEnd } from '../helper'; -import { BucketSize } from '../../../../pages/overview'; +import type { BucketSize } from '../../../../pages/overview/helpers/calculate_bucket_size'; interface Props { bucketSize: BucketSize; diff --git a/x-pack/plugins/observability/public/components/app/section/ux/index.tsx b/x-pack/plugins/observability/public/components/app/section/ux/index.tsx index 2bfc534c641db..9c59fb763bc1e 100644 --- a/x-pack/plugins/observability/public/components/app/section/ux/index.tsx +++ b/x-pack/plugins/observability/public/components/app/section/ux/index.tsx @@ -17,14 +17,13 @@ import { FETCH_STATUS, useFetcher } from '../../../../hooks/use_fetcher'; import { useHasData } from '../../../../hooks/use_has_data'; import { useDatePickerContext } from '../../../../hooks/use_date_picker_context'; import CoreVitals from '../../../shared/core_web_vitals'; -import { BucketSize } from '../../../../pages/overview'; import { getExploratoryViewEmbeddable } from '../../../shared/exploratory_view/embeddable'; import { AllSeries } from '../../../shared/exploratory_view/hooks/use_series_storage'; import { SERVICE_NAME, TRANSACTION_DURATION, } from '../../../shared/exploratory_view/configurations/constants/elasticsearch_fieldnames'; - +import type { BucketSize } from '../../../../pages/overview/helpers/calculate_bucket_size'; interface Props { bucketSize: BucketSize; } diff --git a/x-pack/plugins/observability/public/pages/overview/components/loading_observability.tsx b/x-pack/plugins/observability/public/components/loading_observability.tsx similarity index 86% rename from x-pack/plugins/observability/public/pages/overview/components/loading_observability.tsx rename to x-pack/plugins/observability/public/components/loading_observability.tsx index dd0e19169874d..889b9c9a31d04 100644 --- a/x-pack/plugins/observability/public/pages/overview/components/loading_observability.tsx +++ b/x-pack/plugins/observability/public/components/loading_observability.tsx @@ -5,18 +5,18 @@ * 2.0. */ +import React from 'react'; import { EuiFlexGroup, EuiFlexItem, EuiLoadingSpinner, EuiText } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; -import React from 'react'; -import { ObservabilityHeaderMenu } from '../../../components/app/header'; -import { usePluginContext } from '../../../hooks/use_plugin_context'; +import { usePluginContext } from '../hooks/use_plugin_context'; +import { HeaderMenu } from '../pages/overview/components/header_menu'; export function LoadingObservability() { const { ObservabilityPageTemplate } = usePluginContext(); return ( - + diff --git a/x-pack/plugins/observability/public/components/shared/page_template/page_template.tsx b/x-pack/plugins/observability/public/components/shared/page_template/page_template.tsx index cafe693b8832b..aa6f99d862a4c 100644 --- a/x-pack/plugins/observability/public/components/shared/page_template/page_template.tsx +++ b/x-pack/plugins/observability/public/components/shared/page_template/page_template.tsx @@ -5,7 +5,7 @@ * 2.0. */ -import { EuiSideNavItemType, EuiPageSectionProps } from '@elastic/eui'; +import { EuiSideNavItemType, EuiPageSectionProps, EuiErrorBoundary } from '@elastic/eui'; import { _EuiPageBottomBarProps } from '@elastic/eui/src/components/page_template/bottom_bar/page_bottom_bar'; import { i18n } from '@kbn/i18n'; import React, { useMemo } from 'react'; @@ -178,13 +178,15 @@ export function ObservabilityPageTemplate({ : undefined } > - - {children} - + + + {children} + + {bottomBar && ( {bottomBar} diff --git a/x-pack/plugins/observability/public/index.ts b/x-pack/plugins/observability/public/index.ts index 27522c7b8a8c4..e95258e4155ed 100644 --- a/x-pack/plugins/observability/public/index.ts +++ b/x-pack/plugins/observability/public/index.ts @@ -120,7 +120,8 @@ export { export { ExploratoryViewContextProvider } from './components/shared/exploratory_view/contexts/exploratory_view_config'; export { fromQuery, toQuery } from './utils/url'; export { getAlertSummaryTimeRange } from './utils/alert_summary_widget'; -export { calculateTimeRangeBucketSize } from './pages/overview/containers/overview_page/helpers'; +export { calculateTimeRangeBucketSize } from './pages/overview/helpers/calculate_bucket_size'; export type { NavigationSection } from './services/navigation_registry'; export { convertTo } from '../common/utils/formatters/duration'; +export { formatAlertEvaluationValue } from './utils/format_alert_evaluation_value'; diff --git a/x-pack/plugins/observability/public/pages/alert_details/components/alert_details.tsx b/x-pack/plugins/observability/public/pages/alert_details/components/alert_details.tsx index 58a32eb89f130..29159931c6433 100644 --- a/x-pack/plugins/observability/public/pages/alert_details/components/alert_details.tsx +++ b/x-pack/plugins/observability/public/pages/alert_details/components/alert_details.tsx @@ -9,7 +9,6 @@ import React, { useEffect, useState } from 'react'; import { i18n } from '@kbn/i18n'; import { useParams } from 'react-router-dom'; import { EuiEmptyPrompt, EuiPanel, EuiSpacer } from '@elastic/eui'; - import { ALERT_RULE_TYPE_ID, ALERT_RULE_UUID } from '@kbn/rule-data-utils'; import { RuleTypeModel } from '@kbn/triggers-actions-ui-plugin/public'; import { getTimeZone } from '../../../utils/get_time_zone'; @@ -25,7 +24,7 @@ import { CenterJustifiedSpinner } from '../../rule_details/components/center_jus import PageNotFound from '../../404'; import { ObservabilityAppServices } from '../../../application/types'; -import { AlertDetailsPathParams } from '../types'; +import { AlertDetailsPathParams, AlertSummaryField } from '../types'; import { observabilityFeatureId } from '../../../../common'; import { paths } from '../../../config/paths'; @@ -50,6 +49,7 @@ export function AlertDetails() { ruleId: alert?.fields[ALERT_RULE_UUID], http, }); + const [summaryFields, setSummaryFields] = useState(); useEffect(() => { if (alert) { @@ -116,10 +116,15 @@ export function AlertDetails() { }} data-test-subj="alertDetails" > - + {AlertDetailsAppSection && rule && ( - + )} ); diff --git a/x-pack/plugins/observability/public/pages/alert_details/components/alert_summary.test.tsx b/x-pack/plugins/observability/public/pages/alert_details/components/alert_summary.test.tsx index e29771ee169de..5fa0ba8a1dca1 100644 --- a/x-pack/plugins/observability/public/pages/alert_details/components/alert_summary.test.tsx +++ b/x-pack/plugins/observability/public/pages/alert_details/components/alert_summary.test.tsx @@ -11,6 +11,7 @@ import { render } from '../../../utils/test_helper'; import { AlertSummary } from './alert_summary'; import { asDuration } from '../../../../common/utils/formatters'; import { alertWithTags, alertWithNoData, tags } from '../mock/alert'; +import { alertSummaryFieldsMock } from '../mock/alert_summary_fields'; jest.mock('react-router-dom', () => ({ ...jest.requireActual('react-router-dom'), @@ -29,7 +30,9 @@ describe('Alert summary', () => { }); it('should show alert data', async () => { - const alertSummary = render(); + const alertSummary = render( + + ); expect(alertSummary.queryByText('1957')).toBeInTheDocument(); expect(alertSummary.queryByText(asDuration(882076000))).toBeInTheDocument(); diff --git a/x-pack/plugins/observability/public/pages/alert_details/components/alert_summary.tsx b/x-pack/plugins/observability/public/pages/alert_details/components/alert_summary.tsx index a3662c665ce88..7ea95cc1319ba 100644 --- a/x-pack/plugins/observability/public/pages/alert_details/components/alert_summary.tsx +++ b/x-pack/plugins/observability/public/pages/alert_details/components/alert_summary.tsx @@ -7,179 +7,152 @@ import React from 'react'; import { EuiText, - EuiFlexGroup, EuiFlexItem, EuiSpacer, EuiTitle, EuiBadge, EuiBadgeGroup, + EuiFlexGrid, + useIsWithinBreakpoints, } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n-react'; import moment from 'moment'; import { ALERT_DURATION, - ALERT_EVALUATION_THRESHOLD, - ALERT_EVALUATION_VALUE, ALERT_RULE_TAGS, - ALERT_RULE_TYPE_ID, ALERT_START, ALERT_STATUS, ALERT_STATUS_ACTIVE, ALERT_STATUS_RECOVERED, TIMESTAMP, } from '@kbn/rule-data-utils'; -import { formatAlertEvaluationValue } from '../../../utils/format_alert_evaluation_value'; import { asDuration } from '../../../../common/utils/formatters'; import { AlertSummaryProps } from '../types'; import { AlertStatusIndicator } from '../../../components/shared/alert_status_indicator'; import { DEFAULT_DATE_FORMAT } from '../constants'; +import { TopAlert } from '../../alerts'; -export function AlertSummary({ alert }: AlertSummaryProps) { +const getAndFormatAlertSummaryBasicFields = (alert: TopAlert | null): React.ReactElement => { const tags = alert?.fields[ALERT_RULE_TAGS]; return ( -
- - - -
- -
-
- - - {formatAlertEvaluationValue( - alert?.fields[ALERT_RULE_TYPE_ID], - alert?.fields[ALERT_EVALUATION_VALUE] - )} - -
- - -
- -
-
- - - {formatAlertEvaluationValue( - alert?.fields[ALERT_RULE_TYPE_ID], - alert?.fields[ALERT_EVALUATION_THRESHOLD] - )} - -
- - -
- -
-
- - - {asDuration(Number(alert?.fields[ALERT_DURATION]))} - -
- - -
- -
-
- - {alert?.fields[ALERT_STATUS] ? ( - + + +
+ +
+
+ + {alert?.fields[ALERT_STATUS] ? ( + + ) : ( +
-
+ )} +
+ + +
+ +
+
+ + + {asDuration(Number(alert?.fields[ALERT_DURATION]))} + +
+ + +
+ +
+
+ + + {moment(alert?.fields[ALERT_START]?.toString()).format(DEFAULT_DATE_FORMAT)} + +
+ + +
+ +
+
+ + + {moment(alert?.fields[TIMESTAMP]?.toString()).fromNow()},  + {moment(alert?.fields[TIMESTAMP]?.toString()).format(DEFAULT_DATE_FORMAT)} + +
+ + +
+ +
+
+ +
+ {tags && tags.length > 0 ? ( + + {tags.map((tag, index) => ( + + {tag} + + ))} + ) : ( -
-
+
-
)} - - - - - - -
- -
-
-
- - -
-
- - -
- -
-
- - - {moment(alert?.fields[ALERT_START]?.toString()).format(DEFAULT_DATE_FORMAT)} - -
- - -
- -
-
- - - {moment(alert?.fields[TIMESTAMP]?.toString()).fromNow()},  - {moment(alert?.fields[TIMESTAMP]?.toString()).format(DEFAULT_DATE_FORMAT)} - -
- - -
- -
-
- -
- {tags && tags.length > 0 ? ( - - {tags.map((tag, index) => ( - - {tag} - - ))} - - ) : ( -
-
- )} -
-
-
-
+
+
+ + ); +}; +export function AlertSummary({ alert, alertSummaryFields }: AlertSummaryProps) { + const isMobile = useIsWithinBreakpoints(['xs', 's']); + return ( + + {getAndFormatAlertSummaryBasicFields(alert)} + {alertSummaryFields?.map((field) => { + return ( + + +
{field.label}
+
+ + + {field.value} + +
+ ); + })} +
); } diff --git a/x-pack/plugins/observability/public/pages/alert_details/mock/alert_summary_fields.ts b/x-pack/plugins/observability/public/pages/alert_details/mock/alert_summary_fields.ts new file mode 100644 index 0000000000000..ef19875541396 --- /dev/null +++ b/x-pack/plugins/observability/public/pages/alert_details/mock/alert_summary_fields.ts @@ -0,0 +1,23 @@ +/* + * 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 { AlertSummaryField } from '../types'; +import { alertWithTags } from './alert'; +export const alertSummaryFieldsMock: AlertSummaryField[] = [ + { + label: 'Actual value', + value: alertWithTags.fields['kibana.alert.evaluation.value']!, + }, + { + label: 'Expected value', + value: alertWithTags.fields['kibana.alert.evaluation.threshold']!, + }, + { + label: 'Source', + value: '-', + }, +]; diff --git a/x-pack/plugins/observability/public/pages/alert_details/types.ts b/x-pack/plugins/observability/public/pages/alert_details/types.ts index d05e0f4c73ff6..d04f856249add 100644 --- a/x-pack/plugins/observability/public/pages/alert_details/types.ts +++ b/x-pack/plugins/observability/public/pages/alert_details/types.ts @@ -5,10 +5,16 @@ * 2.0. */ +import { ReactNode } from 'react'; import { TopAlert } from '../alerts/containers/alerts_page/types'; +export interface AlertSummaryField { + label: ReactNode | string; + value: string | number; +} export interface AlertSummaryProps { alert: TopAlert | null; + alertSummaryFields?: AlertSummaryField[]; } export interface AlertDetailsPathParams { diff --git a/x-pack/plugins/observability/public/pages/alerts/components/parse_alert.ts b/x-pack/plugins/observability/public/pages/alerts/components/parse_alert.ts index 6c34d6157fd0b..29f4e94f70f3e 100644 --- a/x-pack/plugins/observability/public/pages/alerts/components/parse_alert.ts +++ b/x-pack/plugins/observability/public/pages/alerts/components/parse_alert.ts @@ -14,7 +14,7 @@ import { ALERT_RULE_NAME, ALERT_REASON, } from '@kbn/rule-data-utils'; -import { experimentalRuleFieldMap } from '@kbn/rule-registry-plugin/common/assets/field_maps/experimental_rule_field_map'; +import { legacyExperimentalFieldMap } from '@kbn/alerts-as-data-utils'; import { parseTechnicalFields } from '@kbn/rule-registry-plugin/common/parse_technical_fields'; import { parseExperimentalFields } from '@kbn/rule-registry-plugin/common/parse_experimental_fields'; import type { TopAlert } from '..'; @@ -24,7 +24,7 @@ import { ObservabilityRuleTypeRegistry } from '../../../rules/create_observabili export const parseAlert = (observabilityRuleTypeRegistry: ObservabilityRuleTypeRegistry) => (alert: Record): TopAlert => { - const experimentalFields = Object.keys(experimentalRuleFieldMap); + const experimentalFields = Object.keys(legacyExperimentalFieldMap); const alertWithExperimentalFields = experimentalFields.reduce((acc, key) => { if (alert[key]) { return { ...acc, [key]: alert[key] }; diff --git a/x-pack/plugins/observability/public/pages/alerts/containers/alerts_page/alerts_page.tsx b/x-pack/plugins/observability/public/pages/alerts/containers/alerts_page/alerts_page.tsx index 4eb7aabc66202..0c5f9dc4d74ec 100644 --- a/x-pack/plugins/observability/public/pages/alerts/containers/alerts_page/alerts_page.tsx +++ b/x-pack/plugins/observability/public/pages/alerts/containers/alerts_page/alerts_page.tsx @@ -12,11 +12,8 @@ import { i18n } from '@kbn/i18n'; import { useKibana } from '@kbn/kibana-react-plugin/public'; import { loadRuleAggregations } from '@kbn/triggers-actions-ui-plugin/public'; import { AlertConsumers } from '@kbn/rule-data-utils'; -import { calculateTimeRangeBucketSize } from '../../../overview/containers/overview_page/helpers/calculate_bucket_size'; -import { - DEFAULT_DATE_FORMAT, - DEFAULT_INTERVAL, -} from '../../../overview/containers/overview_page/constants'; +import { calculateTimeRangeBucketSize } from '../../../overview/helpers/calculate_bucket_size'; +import { DEFAULT_DATE_FORMAT, DEFAULT_INTERVAL } from '../../../constants'; import { useToasts } from '../../../../hooks/use_toast'; import { alertSearchBarStateContainer, @@ -33,7 +30,7 @@ import { usePluginContext } from '../../../../hooks/use_plugin_context'; import { useTimeBuckets } from '../../../../hooks/use_time_buckets'; import { getNoDataConfig } from '../../../../utils/no_data_config'; import { getAlertSummaryTimeRange } from '../../../../utils/alert_summary_widget'; -import { LoadingObservability } from '../../../overview'; +import { LoadingObservability } from '../../../../components/loading_observability'; import './styles.scss'; import { renderRuleStats } from '../../components/rule_stats'; import { ObservabilityAppServices } from '../../../../application/types'; diff --git a/x-pack/plugins/observability/public/pages/cases/index.tsx b/x-pack/plugins/observability/public/pages/cases/index.tsx index 48b8eb2d9462c..6713aaef7f12f 100644 --- a/x-pack/plugins/observability/public/pages/cases/index.tsx +++ b/x-pack/plugins/observability/public/pages/cases/index.tsx @@ -14,7 +14,7 @@ import { CaseFeatureNoPermissions } from './feature_no_permissions'; import { useGetUserCasesPermissions } from '../../hooks/use_get_user_cases_permissions'; import { usePluginContext } from '../../hooks/use_plugin_context'; import { useHasData } from '../../hooks/use_has_data'; -import { LoadingObservability } from '../overview'; +import { LoadingObservability } from '../../components/loading_observability'; import { getNoDataConfig } from '../../utils/no_data_config'; import { ObservabilityAppServices } from '../../application/types'; diff --git a/x-pack/plugins/observability/public/pages/overview/containers/overview_page/index.ts b/x-pack/plugins/observability/public/pages/constants.ts similarity index 72% rename from x-pack/plugins/observability/public/pages/overview/containers/overview_page/index.ts rename to x-pack/plugins/observability/public/pages/constants.ts index 699bbd51e2b36..ec7c51f10c3cf 100644 --- a/x-pack/plugins/observability/public/pages/overview/containers/overview_page/index.ts +++ b/x-pack/plugins/observability/public/pages/constants.ts @@ -5,5 +5,5 @@ * 2.0. */ -export { OverviewPage } from './overview_page'; -export type { BucketSize } from './types'; +export const DEFAULT_INTERVAL = '60s'; +export const DEFAULT_DATE_FORMAT = 'YYYY-MM-DD HH:mm'; diff --git a/x-pack/plugins/observability/public/pages/overview/components/data_sections.tsx b/x-pack/plugins/observability/public/pages/overview/components/data_sections.tsx index 2c38a9dc7bcb4..848d469e0e4f1 100644 --- a/x-pack/plugins/observability/public/pages/overview/components/data_sections.tsx +++ b/x-pack/plugins/observability/public/pages/overview/components/data_sections.tsx @@ -13,7 +13,7 @@ import { MetricsSection } from '../../../components/app/section/metrics'; import { UptimeSection } from '../../../components/app/section/uptime'; import { UXSection } from '../../../components/app/section/ux'; import { HasDataMap } from '../../../context/has_data_context'; -import { BucketSize } from '../containers'; +import type { BucketSize } from '../helpers/calculate_bucket_size'; interface Props { bucketSize: BucketSize; diff --git a/x-pack/plugins/observability/public/components/app/empty_sections/empty_section.test.tsx b/x-pack/plugins/observability/public/pages/overview/components/empty_section.test.tsx similarity index 100% rename from x-pack/plugins/observability/public/components/app/empty_sections/empty_section.test.tsx rename to x-pack/plugins/observability/public/pages/overview/components/empty_section.test.tsx diff --git a/x-pack/plugins/observability/public/components/app/empty_sections/empty_section.tsx b/x-pack/plugins/observability/public/pages/overview/components/empty_section.tsx similarity index 100% rename from x-pack/plugins/observability/public/components/app/empty_sections/empty_section.tsx rename to x-pack/plugins/observability/public/pages/overview/components/empty_section.tsx diff --git a/x-pack/plugins/observability/public/pages/overview/components/empty_section.ts b/x-pack/plugins/observability/public/pages/overview/components/empty_sections.tsx similarity index 72% rename from x-pack/plugins/observability/public/pages/overview/components/empty_section.ts rename to x-pack/plugins/observability/public/pages/overview/components/empty_sections.tsx index 0d172e1a83cb4..704d0c6432ddd 100644 --- a/x-pack/plugins/observability/public/pages/overview/components/empty_section.ts +++ b/x-pack/plugins/observability/public/pages/overview/components/empty_sections.tsx @@ -5,12 +5,61 @@ * 2.0. */ +import { EuiFlexGrid, EuiFlexItem, EuiSpacer } from '@elastic/eui'; +import React, { useContext } from 'react'; +import { ThemeContext } from 'styled-components'; +import { useKibana } from '@kbn/kibana-react-plugin/public'; import { i18n } from '@kbn/i18n'; import { HttpSetup } from '@kbn/core/public'; + import { ISection } from '../../../typings/section'; import { paths } from '../../../config/paths'; +import { ObservabilityAppServices } from '../../../application/types'; +import { FETCH_STATUS } from '../../../hooks/use_fetcher'; +import { useHasData } from '../../../hooks/use_has_data'; +import { EmptySection } from './empty_section'; + +export function EmptySections() { + const { http } = useKibana().services; + const theme = useContext(ThemeContext); + const { hasDataMap } = useHasData(); + + const appEmptySections = getEmptySections({ http }).filter(({ id }) => { + const app = hasDataMap[id]; + if (app) { + return app.status === FETCH_STATUS.FAILURE || !app.hasData; + } + return false; + }); + return ( + + + 2 ? 2 : 1 + } + gutterSize="s" + > + {appEmptySections.map((app) => { + return ( + + + + ); + })} + + + ); +} -export const getEmptySections = ({ http }: { http: HttpSetup }): ISection[] => { +const getEmptySections = ({ http }: { http: HttpSetup }): ISection[] => { return [ { id: 'infra_logs', diff --git a/x-pack/plugins/observability/public/components/app/header/header_menu.tsx b/x-pack/plugins/observability/public/pages/overview/components/header_menu.tsx similarity index 90% rename from x-pack/plugins/observability/public/components/app/header/header_menu.tsx rename to x-pack/plugins/observability/public/pages/overview/components/header_menu.tsx index 0d1b08adf9d20..45401437b2dc0 100644 --- a/x-pack/plugins/observability/public/components/app/header/header_menu.tsx +++ b/x-pack/plugins/observability/public/pages/overview/components/header_menu.tsx @@ -11,9 +11,9 @@ import React from 'react'; import { useKibana } from '@kbn/kibana-react-plugin/public'; import { ObservabilityAppServices } from '../../../application/types'; import { usePluginContext } from '../../../hooks/use_plugin_context'; -import HeaderMenuPortal from '../../shared/header_menu_portal'; +import HeaderMenuPortal from '../../../components/shared/header_menu_portal'; -export function ObservabilityHeaderMenu(): React.ReactElement | null { +export function HeaderMenu(): React.ReactElement | null { const { appMountParameters: { setHeaderActionMenu }, } = usePluginContext(); diff --git a/x-pack/plugins/observability/public/pages/overview/components/index.ts b/x-pack/plugins/observability/public/pages/overview/components/index.ts deleted file mode 100644 index 9a6b29a787239..0000000000000 --- a/x-pack/plugins/observability/public/pages/overview/components/index.ts +++ /dev/null @@ -1,12 +0,0 @@ -/* - * 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. - */ - -export * from './data_assistant_flyout'; -export * from './data_sections'; -export * from './empty_section'; -export * from './header_actions'; -export * from './loading_observability'; diff --git a/x-pack/plugins/observability/public/components/app/news_feed/index.scss b/x-pack/plugins/observability/public/pages/overview/components/news_feed.scss similarity index 100% rename from x-pack/plugins/observability/public/components/app/news_feed/index.scss rename to x-pack/plugins/observability/public/pages/overview/components/news_feed.scss diff --git a/x-pack/plugins/observability/public/components/app/news_feed/index.test.tsx b/x-pack/plugins/observability/public/pages/overview/components/news_feed.test.tsx similarity index 98% rename from x-pack/plugins/observability/public/components/app/news_feed/index.test.tsx rename to x-pack/plugins/observability/public/pages/overview/components/news_feed.test.tsx index 065822aba0a05..f8b9ddc7efd0e 100644 --- a/x-pack/plugins/observability/public/components/app/news_feed/index.test.tsx +++ b/x-pack/plugins/observability/public/pages/overview/components/news_feed.test.tsx @@ -8,7 +8,7 @@ import React from 'react'; import { NewsItem } from '../../../services/get_news_feed'; import { render } from '../../../utils/test_helper'; -import { NewsFeed } from '.'; +import { NewsFeed } from './news_feed'; const newsFeedItems = [ { diff --git a/x-pack/plugins/observability/public/components/app/news_feed/index.tsx b/x-pack/plugins/observability/public/pages/overview/components/news_feed.tsx similarity index 99% rename from x-pack/plugins/observability/public/components/app/news_feed/index.tsx rename to x-pack/plugins/observability/public/pages/overview/components/news_feed.tsx index acf8e267c15fd..1e9ce4b868f79 100644 --- a/x-pack/plugins/observability/public/components/app/news_feed/index.tsx +++ b/x-pack/plugins/observability/public/pages/overview/components/news_feed.tsx @@ -20,7 +20,7 @@ import { truncate } from 'lodash'; import React, { useContext } from 'react'; import { ThemeContext } from 'styled-components'; import { NewsItem as INewsItem } from '../../../services/get_news_feed'; -import './index.scss'; +import './news_feed.scss'; interface Props { items: INewsItem[]; diff --git a/x-pack/plugins/observability/public/components/app/resources/index.test.tsx b/x-pack/plugins/observability/public/pages/overview/components/resources.test.tsx similarity index 94% rename from x-pack/plugins/observability/public/components/app/resources/index.test.tsx rename to x-pack/plugins/observability/public/pages/overview/components/resources.test.tsx index 8d51814ecd074..db8b662edaa3a 100644 --- a/x-pack/plugins/observability/public/components/app/resources/index.test.tsx +++ b/x-pack/plugins/observability/public/pages/overview/components/resources.test.tsx @@ -7,7 +7,7 @@ import { render } from '@testing-library/react'; import React from 'react'; -import { Resources } from '.'; +import { Resources } from './resources'; describe('Resources', () => { it('renders resources with all elements', () => { diff --git a/x-pack/plugins/observability/public/components/app/resources/index.tsx b/x-pack/plugins/observability/public/pages/overview/components/resources.tsx similarity index 100% rename from x-pack/plugins/observability/public/components/app/resources/index.tsx rename to x-pack/plugins/observability/public/pages/overview/components/resources.tsx index f5e093901479f..6f8572ce7252e 100644 --- a/x-pack/plugins/observability/public/components/app/resources/index.tsx +++ b/x-pack/plugins/observability/public/pages/overview/components/resources.tsx @@ -9,6 +9,23 @@ import { EuiFlexGrid, EuiFlexItem, EuiListGroup, EuiTitle } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import React from 'react'; +export function Resources() { + return ( + + + +

+ {i18n.translate('xpack.observability.resources.title', { + defaultMessage: 'Resources', + })} +

+
+
+ +
+ ); +} + const resources = [ { iconType: 'documents', @@ -39,20 +56,3 @@ const resources = [ href: 'https://ela.st/observability-training', }, ]; - -export function Resources() { - return ( - - - -

- {i18n.translate('xpack.observability.resources.title', { - defaultMessage: 'Resources', - })} -

-
-
- -
- ); -} diff --git a/x-pack/plugins/observability/public/pages/overview/containers/overview_page/constants.ts b/x-pack/plugins/observability/public/pages/overview/containers/overview_page/constants.ts deleted file mode 100644 index d394e5aa53b3e..0000000000000 --- a/x-pack/plugins/observability/public/pages/overview/containers/overview_page/constants.ts +++ /dev/null @@ -1,15 +0,0 @@ -/* - * 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. - */ - -export const CAPABILITIES_KEYS = ['logs', 'infrastructure', 'apm', 'uptime']; - -export const DEFAULT_INTERVAL = '60s'; -export const DEFAULT_DATE_FORMAT = 'YYYY-MM-DD HH:mm'; - -export const ALERTS_TABLE_ID = 'xpack.observability.overview.alert.table'; -export const ALERT_TABLE_STATE_STORAGE_KEY = 'xpack.observability.overview.alert.tableState'; -export const ALERTS_PER_PAGE = 10; diff --git a/x-pack/plugins/observability/public/pages/overview/containers/overview_page/types.ts b/x-pack/plugins/observability/public/pages/overview/containers/overview_page/types.ts deleted file mode 100644 index 7d66709025480..0000000000000 --- a/x-pack/plugins/observability/public/pages/overview/containers/overview_page/types.ts +++ /dev/null @@ -1,17 +0,0 @@ -/* - * 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 { TimeBuckets } from '@kbn/data-plugin/common'; - -export interface Bucket { - start?: number; - end?: number; - timeBuckets: TimeBuckets; -} -export type BucketSize = - | { bucketSize: number; intervalString: string; dateFormat: string } - | undefined; diff --git a/x-pack/plugins/observability/public/pages/overview/containers/overview_page/helpers/calculate_bucket_size.test.ts b/x-pack/plugins/observability/public/pages/overview/helpers/calculate_bucket_size.test.ts similarity index 100% rename from x-pack/plugins/observability/public/pages/overview/containers/overview_page/helpers/calculate_bucket_size.test.ts rename to x-pack/plugins/observability/public/pages/overview/helpers/calculate_bucket_size.test.ts diff --git a/x-pack/plugins/observability/public/pages/overview/containers/overview_page/helpers/calculate_bucket_size.ts b/x-pack/plugins/observability/public/pages/overview/helpers/calculate_bucket_size.ts similarity index 73% rename from x-pack/plugins/observability/public/pages/overview/containers/overview_page/helpers/calculate_bucket_size.ts rename to x-pack/plugins/observability/public/pages/overview/helpers/calculate_bucket_size.ts index c188aab6160fa..2bb6b7ba348cd 100644 --- a/x-pack/plugins/observability/public/pages/overview/containers/overview_page/helpers/calculate_bucket_size.ts +++ b/x-pack/plugins/observability/public/pages/overview/helpers/calculate_bucket_size.ts @@ -7,10 +7,19 @@ import { TimeBuckets } from '@kbn/data-plugin/common'; import { TimeRange } from '@kbn/es-query'; -import { getAbsoluteTime } from '../../../../../utils/date'; -import { DEFAULT_INTERVAL } from '../constants'; -import { Bucket, BucketSize } from '../types'; -import { getBucketSize } from '../../../../../utils/get_bucket_size'; +import { getAbsoluteTime } from '../../../utils/date'; +import { DEFAULT_INTERVAL } from '../../constants'; +import { getBucketSize } from '../../../utils/get_bucket_size'; + +export type BucketSize = + | { bucketSize: number; intervalString: string; dateFormat: string } + | undefined; + +interface Bucket { + start?: number; + end?: number; + timeBuckets: TimeBuckets; +} export function calculateBucketSize({ start, end, timeBuckets }: Bucket): BucketSize { if (start && end) { diff --git a/x-pack/plugins/observability/public/pages/overview/helpers/on_brush_end.test.ts b/x-pack/plugins/observability/public/pages/overview/helpers/on_brush_end.test.ts new file mode 100644 index 0000000000000..0ffd928826b91 --- /dev/null +++ b/x-pack/plugins/observability/public/pages/overview/helpers/on_brush_end.test.ts @@ -0,0 +1,39 @@ +/* + * 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 { onBrushEnd } from './on_brush_end'; +import { History } from 'history'; + +describe('Chart helper', () => { + describe('onBrushEnd', () => { + const history = { + push: jest.fn(), + location: { + search: '', + }, + } as unknown as History; + it("doesn't push a new history when x is not defined", () => { + onBrushEnd({ x: undefined, history }); + expect(history.push).not.toBeCalled(); + }); + + it('pushes a new history with time range converted to ISO', () => { + onBrushEnd({ x: [1593409448167, 1593415727797], history }); + expect(history.push).toBeCalledWith({ + search: 'rangeFrom=2020-06-29T05:44:08.167Z&rangeTo=2020-06-29T07:28:47.797Z', + }); + }); + + it('pushes a new history keeping current search', () => { + history.location.search = '?foo=bar'; + onBrushEnd({ x: [1593409448167, 1593415727797], history }); + expect(history.push).toBeCalledWith({ + search: 'foo=bar&rangeFrom=2020-06-29T05:44:08.167Z&rangeTo=2020-06-29T07:28:47.797Z', + }); + }); + }); +}); diff --git a/x-pack/plugins/observability/public/pages/overview/helpers/on_brush_end.ts b/x-pack/plugins/observability/public/pages/overview/helpers/on_brush_end.ts new file mode 100644 index 0000000000000..077bd67a8590c --- /dev/null +++ b/x-pack/plugins/observability/public/pages/overview/helpers/on_brush_end.ts @@ -0,0 +1,30 @@ +/* + * 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 { XYBrushEvent } from '@elastic/charts'; +import { History } from 'history'; +import { fromQuery, toQuery } from '../../../utils/url'; + +export const onBrushEnd = ({ x, history }: { x: XYBrushEvent['x']; history: History }) => { + if (x) { + const start = x[0]; + const end = x[1]; + + const currentSearch = toQuery(history.location.search); + const nextSearch = { + rangeFrom: new Date(start).toISOString(), + rangeTo: new Date(end).toISOString(), + }; + history.push({ + ...history.location, + search: fromQuery({ + ...currentSearch, + ...nextSearch, + }), + }); + } +}; diff --git a/x-pack/plugins/observability/public/pages/overview/containers/overview_page/helpers/use_metrics.ts b/x-pack/plugins/observability/public/pages/overview/helpers/use_overview_metrics.ts similarity index 87% rename from x-pack/plugins/observability/public/pages/overview/containers/overview_page/helpers/use_metrics.ts rename to x-pack/plugins/observability/public/pages/overview/helpers/use_overview_metrics.ts index dd9581bd67867..fc92052ac77c5 100644 --- a/x-pack/plugins/observability/public/pages/overview/containers/overview_page/helpers/use_metrics.ts +++ b/x-pack/plugins/observability/public/pages/overview/helpers/use_overview_metrics.ts @@ -8,10 +8,11 @@ import { useEffect } from 'react'; import { useKibana } from '@kbn/kibana-react-plugin/public'; -import { useTrackPageview } from '../../../../..'; -import { useUiTracker } from '../../../../../hooks/use_track_metric'; -import { ObservabilityAppServices } from '../../../../../application/types'; -import { CAPABILITIES_KEYS } from '../constants'; +import { useTrackPageview } from '../../..'; +import { useUiTracker } from '../../../hooks/use_track_metric'; +import { ObservabilityAppServices } from '../../../application/types'; + +const CAPABILITIES_KEYS = ['logs', 'infrastructure', 'apm', 'uptime']; export const useOverviewMetrics = ({ hasAnyData }: { hasAnyData: boolean | undefined }) => { const { diff --git a/x-pack/plugins/observability/public/pages/overview/overview.stories.tsx b/x-pack/plugins/observability/public/pages/overview/overview.stories.tsx index aa7470545f05e..3db883e550773 100644 --- a/x-pack/plugins/observability/public/pages/overview/overview.stories.tsx +++ b/x-pack/plugins/observability/public/pages/overview/overview.stories.tsx @@ -16,7 +16,7 @@ import { KibanaPageTemplate } from '@kbn/shared-ux-page-kibana-template'; import { HasDataContextProvider } from '../../context/has_data_context'; import { PluginContext } from '../../context/plugin_context'; import { registerDataHandler, unregisterDataHandler } from '../../data_handler'; -import { OverviewPage } from '.'; +import { OverviewPage } from './overview'; import { alertsFetchData } from './mock/alerts.mock'; import { emptyResponse as emptyAPMResponse, fetchApmData } from './mock/apm.mock'; import { emptyResponse as emptyLogsResponse, fetchLogsData } from './mock/logs.mock'; @@ -77,7 +77,6 @@ const withCore = makeDecorator({ const config: ConfigSchema = { unsafe: { - slo: { enabled: false }, alertDetails: { apm: { enabled: false }, logs: { enabled: false }, diff --git a/x-pack/plugins/observability/public/pages/overview/containers/overview_page/overview_page.tsx b/x-pack/plugins/observability/public/pages/overview/overview.tsx similarity index 77% rename from x-pack/plugins/observability/public/pages/overview/containers/overview_page/overview_page.tsx rename to x-pack/plugins/observability/public/pages/overview/overview.tsx index 0bad50a97abe6..ab8c043496444 100644 --- a/x-pack/plugins/observability/public/pages/overview/containers/overview_page/overview_page.tsx +++ b/x-pack/plugins/observability/public/pages/overview/overview.tsx @@ -12,40 +12,37 @@ import { useKibana } from '@kbn/kibana-react-plugin/public'; import { AlertConsumers } from '@kbn/rule-data-utils'; import React, { useEffect, useMemo, useCallback, useState } from 'react'; -import { observabilityFeatureId } from '../../../../../common'; -import type { ObservabilityAppServices } from '../../../../application/types'; -import { - DataSections, - LoadingObservability, - HeaderActions, - DataAssistantFlyout, -} from '../../components'; -import { EmptySections } from '../../../../components/app/empty_sections'; -import { ObservabilityHeaderMenu } from '../../../../components/app/header'; -import { Resources } from '../../../../components/app/resources'; -import { NewsFeed } from '../../../../components/app/news_feed'; -import { SectionContainer } from '../../../../components/app/section'; -import { ObservabilityStatusProgress } from '../../../../components/app/observability_status/observability_status_progress'; -import { observabilityAlertFeatureIds, paths } from '../../../../config'; -import { useBreadcrumbs } from '../../../../hooks/use_breadcrumbs'; -import { useDatePickerContext } from '../../../../hooks/use_date_picker_context'; -import { useFetcher } from '../../../../hooks/use_fetcher'; -import { useGetUserCasesPermissions } from '../../../../hooks/use_get_user_cases_permissions'; -import { useGuidedSetupProgress } from '../../../../hooks/use_guided_setup_progress'; -import { useHasData } from '../../../../hooks/use_has_data'; -import { usePluginContext } from '../../../../hooks/use_plugin_context'; -import { useTimeBuckets } from '../../../../hooks/use_time_buckets'; -import { getNewsFeed } from '../../../../services/get_news_feed'; -import { buildEsQuery } from '../../../../utils/build_es_query'; -import { getAlertSummaryTimeRange } from '../../../../utils/alert_summary_widget'; +import { observabilityFeatureId } from '../../../common'; +import type { ObservabilityAppServices } from '../../application/types'; +import { LoadingObservability } from '../../components/loading_observability'; +import { HeaderActions } from './components/header_actions'; +import { DataAssistantFlyout } from './components/data_assistant_flyout'; +import { EmptySections } from './components/empty_sections'; +import { HeaderMenu } from './components/header_menu'; +import { Resources } from './components/resources'; +import { NewsFeed } from './components/news_feed'; +import { ObservabilityStatusProgress } from '../../components/app/observability_status/observability_status_progress'; +import { observabilityAlertFeatureIds, paths } from '../../config'; +import { useBreadcrumbs } from '../../hooks/use_breadcrumbs'; +import { useDatePickerContext } from '../../hooks/use_date_picker_context'; +import { useFetcher } from '../../hooks/use_fetcher'; +import { useGetUserCasesPermissions } from '../../hooks/use_get_user_cases_permissions'; +import { useGuidedSetupProgress } from '../../hooks/use_guided_setup_progress'; +import { useHasData } from '../../hooks/use_has_data'; +import { usePluginContext } from '../../hooks/use_plugin_context'; +import { useTimeBuckets } from '../../hooks/use_time_buckets'; +import { getNewsFeed } from '../../services/get_news_feed'; +import { buildEsQuery } from '../../utils/build_es_query'; +import { getAlertSummaryTimeRange } from '../../utils/alert_summary_widget'; -import { - ALERTS_PER_PAGE, - ALERTS_TABLE_ID, - DEFAULT_DATE_FORMAT, - DEFAULT_INTERVAL, -} from './constants'; -import { calculateBucketSize, useOverviewMetrics } from './helpers'; +import { DEFAULT_DATE_FORMAT, DEFAULT_INTERVAL } from '../constants'; +import { calculateBucketSize } from './helpers/calculate_bucket_size'; +import { useOverviewMetrics } from './helpers/use_overview_metrics'; +import { SectionContainer } from '../../components/app/section'; +import { DataSections } from './components/data_sections'; + +const ALERTS_PER_PAGE = 10; +const ALERTS_TABLE_ID = 'xpack.observability.overview.alert.table'; export function OverviewPage() { const { @@ -181,7 +178,7 @@ export function OverviewPage() { }, }} > - + setGuidedSetupTourVisible(true)} diff --git a/x-pack/plugins/observability/public/pages/slo_details/index.test.tsx b/x-pack/plugins/observability/public/pages/slo_details/slo_details.test.tsx similarity index 55% rename from x-pack/plugins/observability/public/pages/slo_details/index.test.tsx rename to x-pack/plugins/observability/public/pages/slo_details/slo_details.test.tsx index d361c0748feae..27a07587ade48 100644 --- a/x-pack/plugins/observability/public/pages/slo_details/index.test.tsx +++ b/x-pack/plugins/observability/public/pages/slo_details/slo_details.test.tsx @@ -13,10 +13,8 @@ import { useParams } from 'react-router-dom'; import { useLicense } from '../../hooks/use_license'; import { useFetchSloDetails } from '../../hooks/slo/use_fetch_slo_details'; import { render } from '../../utils/test_helper'; -import { SloDetailsPage } from '.'; +import { SloDetailsPage } from './slo_details'; import { buildSlo } from '../../data/slo/slo'; -import type { ConfigSchema } from '../../plugin'; -import type { Subset } from '../../typings'; import { paths } from '../../config'; import { useFetchHistoricalSummary } from '../../hooks/slo/use_fetch_historical_summary'; import { useCapabilities } from '../../hooks/slo/use_capabilities'; @@ -65,12 +63,6 @@ const mockKibana = () => { }); }; -const config: Subset = { - unsafe: { - slo: { enabled: true }, - }, -}; - describe('SLO Details Page', () => { beforeEach(() => { jest.clearAllMocks(); @@ -82,67 +74,52 @@ describe('SLO Details Page', () => { }); }); - describe('when the feature flag is not enabled', () => { - it('renders the not found page', async () => { + describe('when the incorrect license is found', () => { + it('navigates to the SLO List page', async () => { const slo = buildSlo(); useParamsMock.mockReturnValue(slo.id); useFetchSloDetailsMock.mockReturnValue({ isLoading: false, slo }); - useLicenseMock.mockReturnValue({ hasAtLeast: () => true }); + useLicenseMock.mockReturnValue({ hasAtLeast: () => false }); - render(, { unsafe: { slo: { enabled: false } } }); + render(); - expect(screen.queryByTestId('pageNotFound')).toBeTruthy(); + expect(mockNavigate).toBeCalledWith(mockBasePathPrepend(paths.observability.slos)); }); }); - describe('when the feature flag is enabled', () => { - describe('when the incorrect license is found', () => { - it('navigates to the SLO List page', async () => { - const slo = buildSlo(); - useParamsMock.mockReturnValue(slo.id); - useFetchSloDetailsMock.mockReturnValue({ isLoading: false, slo }); - useLicenseMock.mockReturnValue({ hasAtLeast: () => false }); + describe('when the correct license is found', () => { + it('renders the not found page when the SLO cannot be found', async () => { + useParamsMock.mockReturnValue('nonexistent'); + useFetchSloDetailsMock.mockReturnValue({ isLoading: false, slo: undefined }); + useLicenseMock.mockReturnValue({ hasAtLeast: () => true }); - render(, { unsafe: { slo: { enabled: true } } }); + render(); - expect(mockNavigate).toBeCalledWith(mockBasePathPrepend(paths.observability.slos)); - }); + expect(screen.queryByTestId('pageNotFound')).toBeTruthy(); }); - describe('when the correct license is found', () => { - it('renders the not found page when the SLO cannot be found', async () => { - useParamsMock.mockReturnValue('nonexistent'); - useFetchSloDetailsMock.mockReturnValue({ isLoading: false, slo: undefined }); - useLicenseMock.mockReturnValue({ hasAtLeast: () => true }); - - render(, config); - - expect(screen.queryByTestId('pageNotFound')).toBeTruthy(); - }); - - it('renders the loading spinner when fetching the SLO', async () => { - const slo = buildSlo(); - useParamsMock.mockReturnValue(slo.id); - useFetchSloDetailsMock.mockReturnValue({ isLoading: true, slo: undefined }); - useLicenseMock.mockReturnValue({ hasAtLeast: () => true }); + it('renders the loading spinner when fetching the SLO', async () => { + const slo = buildSlo(); + useParamsMock.mockReturnValue(slo.id); + useFetchSloDetailsMock.mockReturnValue({ isLoading: true, slo: undefined }); + useLicenseMock.mockReturnValue({ hasAtLeast: () => true }); - render(, config); + render(); - expect(screen.queryByTestId('pageNotFound')).toBeFalsy(); - expect(screen.queryByTestId('loadingTitle')).toBeTruthy(); - expect(screen.queryByTestId('sloDetailsLoading')).toBeTruthy(); - }); + expect(screen.queryByTestId('pageNotFound')).toBeFalsy(); + expect(screen.queryByTestId('loadingTitle')).toBeTruthy(); + expect(screen.queryByTestId('sloDetailsLoading')).toBeTruthy(); + }); - it('renders the SLO details page', async () => { - const slo = buildSlo(); - useParamsMock.mockReturnValue(slo.id); - useFetchSloDetailsMock.mockReturnValue({ isLoading: false, slo }); - useLicenseMock.mockReturnValue({ hasAtLeast: () => true }); + it('renders the SLO details page', async () => { + const slo = buildSlo(); + useParamsMock.mockReturnValue(slo.id); + useFetchSloDetailsMock.mockReturnValue({ isLoading: false, slo }); + useLicenseMock.mockReturnValue({ hasAtLeast: () => true }); - render(, config); + render(); - expect(screen.queryByTestId('sloDetailsPage')).toBeTruthy(); - }); + expect(screen.queryByTestId('sloDetailsPage')).toBeTruthy(); }); }); }); diff --git a/x-pack/plugins/observability/public/pages/slo_details/index.tsx b/x-pack/plugins/observability/public/pages/slo_details/slo_details.tsx similarity index 93% rename from x-pack/plugins/observability/public/pages/slo_details/index.tsx rename to x-pack/plugins/observability/public/pages/slo_details/slo_details.tsx index dc0770b0f4ef0..0f5729c26c8c0 100644 --- a/x-pack/plugins/observability/public/pages/slo_details/index.tsx +++ b/x-pack/plugins/observability/public/pages/slo_details/slo_details.tsx @@ -19,7 +19,6 @@ import { useBreadcrumbs } from '../../hooks/use_breadcrumbs'; import { useFetchSloDetails } from '../../hooks/slo/use_fetch_slo_details'; import { useLicense } from '../../hooks/use_license'; import PageNotFound from '../404'; -import { isSloFeatureEnabled } from '../slos/helpers/is_slo_feature_enabled'; import { SloDetails } from './components/slo_details'; import { HeaderTitle } from './components/header_title'; import { paths } from '../../config'; @@ -32,7 +31,7 @@ export function SloDetailsPage() { application: { navigateToUrl }, http: { basePath }, } = useKibana().services; - const { ObservabilityPageTemplate, config } = usePluginContext(); + const { ObservabilityPageTemplate } = usePluginContext(); const { hasAtLeast } = useLicense(); const hasRightLicense = hasAtLeast('platinum'); @@ -41,7 +40,7 @@ export function SloDetailsPage() { useBreadcrumbs(getBreadcrumbs(basePath, slo)); const isSloNotFound = !isLoading && slo === undefined; - if (!isSloFeatureEnabled(config) || isSloNotFound) { + if (isSloNotFound) { return ; } diff --git a/x-pack/plugins/observability/public/pages/slo_edit/index.test.tsx b/x-pack/plugins/observability/public/pages/slo_edit/index.test.tsx deleted file mode 100644 index a4a10b6075056..0000000000000 --- a/x-pack/plugins/observability/public/pages/slo_edit/index.test.tsx +++ /dev/null @@ -1,624 +0,0 @@ -/* - * 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 React from 'react'; -import Router from 'react-router-dom'; -import { waitFor, fireEvent, screen } from '@testing-library/dom'; -import { cleanup } from '@testing-library/react'; -import userEvent from '@testing-library/user-event'; - -import { render } from '../../utils/test_helper'; -import { useKibana } from '../../utils/kibana_react'; -import { useLicense } from '../../hooks/use_license'; -import { useFetchIndices } from '../../hooks/use_fetch_indices'; -import { useFetchSloDetails } from '../../hooks/slo/use_fetch_slo_details'; -import { useCreateSlo } from '../../hooks/slo/use_create_slo'; -import { useUpdateSlo } from '../../hooks/slo/use_update_slo'; -import { kibanaStartMock } from '../../utils/kibana_react.mock'; -import { ConfigSchema } from '../../plugin'; -import { Subset } from '../../typings'; -import { SLO_EDIT_FORM_DEFAULT_VALUES } from './constants'; -import { buildSlo } from '../../data/slo/slo'; -import { paths } from '../../config'; -import { SloEditPage } from '.'; - -jest.mock('react-router-dom', () => ({ - ...jest.requireActual('react-router-dom'), - useParams: jest.fn(), -})); - -jest.mock('../../hooks/use_breadcrumbs'); -jest.mock('../../hooks/use_license'); -jest.mock('../../hooks/use_fetch_indices'); -jest.mock('../../hooks/slo/use_fetch_slo_details'); -jest.mock('../../hooks/slo/use_create_slo'); -jest.mock('../../hooks/slo/use_update_slo'); - -const mockUseKibanaReturnValue = kibanaStartMock.startContract(); - -jest.mock('../../utils/kibana_react', () => ({ - useKibana: jest.fn(() => mockUseKibanaReturnValue), -})); - -const useKibanaMock = useKibana as jest.Mock; -const useLicenseMock = useLicense as jest.Mock; -const useFetchIndicesMock = useFetchIndices as jest.Mock; -const useFetchSloMock = useFetchSloDetails as jest.Mock; -const useCreateSloMock = useCreateSlo as jest.Mock; -const useUpdateSloMock = useUpdateSlo as jest.Mock; - -const mockAddSuccess = jest.fn(); -const mockAddError = jest.fn(); -const mockNavigate = jest.fn(); -const mockBasePathPrepend = jest.fn(); - -const mockKibana = () => { - useKibanaMock.mockReturnValue({ - services: { - application: { - navigateToUrl: mockNavigate, - }, - data: { - dataViews: { - find: jest.fn().mockReturnValue([]), - get: jest.fn().mockReturnValue([]), - }, - }, - dataViews: { - create: jest.fn().mockResolvedValue(42), - }, - docLinks: { - links: { - query: {}, - }, - }, - http: { - basePath: { - prepend: mockBasePathPrepend, - }, - }, - notifications: { - toasts: { - addError: mockAddError, - addSuccess: mockAddSuccess, - }, - }, - storage: { - get: () => {}, - }, - uiSettings: { - get: () => {}, - }, - unifiedSearch: { - autocomplete: { - hasQuerySuggestions: () => {}, - }, - }, - }, - }); -}; - -const config: Subset = { - unsafe: { - slo: { enabled: true }, - }, -}; - -describe('SLO Edit Page', () => { - beforeEach(() => { - jest.clearAllMocks(); - mockKibana(); - - // Silence all the ref errors in Eui components. - jest.spyOn(console, 'error').mockImplementation(() => {}); - }); - - afterEach(cleanup); - - describe('when the feature flag is disabled', () => { - it('renders the not found page when no sloId param is passed', async () => { - jest.spyOn(Router, 'useParams').mockReturnValue({ sloId: undefined }); - - useLicenseMock.mockReturnValue({ hasAtLeast: () => false }); - useFetchSloMock.mockReturnValue({ isLoading: false, slo: undefined }); - - render(, { unsafe: { slo: { enabled: false } } }); - - expect(screen.queryByTestId('pageNotFound')).toBeTruthy(); - }); - - it('renders the not found page when sloId param is passed', async () => { - jest.spyOn(Router, 'useParams').mockReturnValue({ sloId: '1234' }); - - useLicenseMock.mockReturnValue({ hasAtLeast: () => false }); - useFetchSloMock.mockReturnValue({ isLoading: false, slo: undefined }); - - render(, { unsafe: { slo: { enabled: false } } }); - - expect(screen.queryByTestId('pageNotFound')).toBeTruthy(); - }); - }); - - describe('when the feature flag is enabled', () => { - describe('when the incorrect license is found', () => { - beforeEach(() => { - useLicenseMock.mockReturnValue({ hasAtLeast: () => false }); - }); - - it('navigates to the SLO List page', async () => { - jest.spyOn(Router, 'useParams').mockReturnValue({ sloId: '1234' }); - - useFetchSloMock.mockReturnValue({ isLoading: false, slo: undefined }); - - useFetchIndicesMock.mockReturnValue({ - isLoading: false, - indices: [{ name: 'some-index' }], - }); - - useCreateSloMock.mockReturnValue({ - isLoading: false, - isSuccess: false, - isError: false, - mutate: jest.fn(), - mutateAsync: jest.fn(), - }); - - useUpdateSloMock.mockReturnValue({ - isLoading: false, - isSuccess: false, - isError: false, - mutate: jest.fn(), - mutateAsync: jest.fn(), - }); - - render(, config); - - expect(mockNavigate).toBeCalledWith(mockBasePathPrepend(paths.observability.slos)); - }); - }); - - describe('when the correct license is found', () => { - beforeEach(() => { - useLicenseMock.mockReturnValue({ hasAtLeast: () => true }); - }); - - describe('when no sloId route param is provided', () => { - it('renders the SLO Edit page in pristine state', async () => { - jest.spyOn(Router, 'useParams').mockReturnValue({ sloId: undefined }); - - useFetchSloMock.mockReturnValue({ isLoading: false, slo: undefined }); - - useFetchIndicesMock.mockReturnValue({ - isLoading: false, - indices: [{ name: 'some-index' }], - }); - - useCreateSloMock.mockReturnValue({ - mutateAsync: jest.fn(), - isLoading: false, - isSuccess: false, - isError: false, - }); - - useUpdateSloMock.mockReturnValue({ - mutateAsync: jest.fn(), - isLoading: false, - isSuccess: false, - isError: false, - }); - - render(, config); - - expect(screen.queryByTestId('slosEditPage')).toBeTruthy(); - expect(screen.queryByTestId('sloForm')).toBeTruthy(); - - expect(screen.queryByTestId('sloFormIndicatorTypeSelect')).toHaveValue( - SLO_EDIT_FORM_DEFAULT_VALUES.indicator.type - ); - - expect(screen.queryByTestId('indexSelectionSelectedValue')).toBeNull(); - - expect(screen.queryByTestId('customKqlIndicatorFormQueryFilterInput')).toHaveValue( - SLO_EDIT_FORM_DEFAULT_VALUES.indicator.type === 'sli.kql.custom' - ? SLO_EDIT_FORM_DEFAULT_VALUES.indicator.params.filter - : '' - ); - expect(screen.queryByTestId('customKqlIndicatorFormGoodQueryInput')).toHaveValue( - SLO_EDIT_FORM_DEFAULT_VALUES.indicator.type === 'sli.kql.custom' - ? SLO_EDIT_FORM_DEFAULT_VALUES.indicator.params.good - : '' - ); - expect(screen.queryByTestId('customKqlIndicatorFormTotalQueryInput')).toHaveValue( - SLO_EDIT_FORM_DEFAULT_VALUES.indicator.type === 'sli.kql.custom' - ? SLO_EDIT_FORM_DEFAULT_VALUES.indicator.params.total - : '' - ); - - expect(screen.queryByTestId('sloFormBudgetingMethodSelect')).toHaveValue( - SLO_EDIT_FORM_DEFAULT_VALUES.budgetingMethod - ); - expect(screen.queryByTestId('sloFormTimeWindowDurationSelect')).toHaveValue( - SLO_EDIT_FORM_DEFAULT_VALUES.timeWindow.duration as any - ); - expect(screen.queryByTestId('sloFormObjectiveTargetInput')).toHaveValue( - SLO_EDIT_FORM_DEFAULT_VALUES.objective.target - ); - - expect(screen.queryByTestId('sloFormNameInput')).toHaveValue( - SLO_EDIT_FORM_DEFAULT_VALUES.name - ); - expect(screen.queryByTestId('sloFormDescriptionTextArea')).toHaveValue( - SLO_EDIT_FORM_DEFAULT_VALUES.description - ); - }); - - it.skip('calls the createSlo hook if all required values are filled in', async () => { - jest.spyOn(Router, 'useParams').mockReturnValue({ sloId: undefined }); - useFetchIndicesMock.mockReturnValue({ - isLoading: false, - indices: [{ name: 'some-index' }], - }); - - useFetchSloMock.mockReturnValue({ isLoading: false, slo: undefined }); - - const mockCreate = jest.fn(); - const mockUpdate = jest.fn(); - - useCreateSloMock.mockReturnValue({ - mutateAsync: mockCreate, - isLoading: false, - isSuccess: false, - isError: false, - }); - - useUpdateSloMock.mockReturnValue({ - mutateAsync: mockUpdate, - isLoading: false, - isSuccess: false, - isError: false, - }); - - render(, config); - - userEvent.type(screen.getByTestId('indexSelection'), 'some-index'); - userEvent.type( - screen.getByTestId('customKqlIndicatorFormQueryFilterInput'), - 'irrelevant' - ); - userEvent.type(screen.getByTestId('customKqlIndicatorFormGoodQueryInput'), 'irrelevant'); - userEvent.type(screen.getByTestId('customKqlIndicatorFormTotalQueryInput'), 'irrelevant'); - userEvent.selectOptions( - screen.getByTestId('sloFormBudgetingMethodSelect'), - 'occurrences' - ); - userEvent.selectOptions(screen.getByTestId('sloFormTimeWindowDurationSelect'), '7d'); - userEvent.clear(screen.getByTestId('sloFormObjectiveTargetInput')); - userEvent.type(screen.getByTestId('sloFormObjectiveTargetInput'), '98.5'); - userEvent.type(screen.getByTestId('sloFormNameInput'), 'irrelevant'); - userEvent.type(screen.getByTestId('sloFormDescriptionTextArea'), 'irrelevant'); - - const t = Date.now(); - await waitFor(() => expect(screen.getByTestId('sloFormSubmitButton')).toBeEnabled()); - console.log('end waiting for submit button: ', Math.ceil(Date.now() - t)); - - fireEvent.click(screen.getByTestId('sloFormSubmitButton')!); - - expect(mockCreate).toMatchInlineSnapshot(` - [MockFunction] { - "calls": Array [ - Array [ - Object { - "budgetingMethod": "occurrences", - "description": "irrelevant", - "indicator": Object { - "params": Object { - "filter": "irrelevant", - "good": "irrelevant", - "index": "some-index", - "total": "irrelevant", - }, - "type": "sli.kql.custom", - }, - "name": "irrelevant", - "objective": Object { - "target": 0.985, - }, - "timeWindow": Object { - "duration": "7d", - "isRolling": true, - }, - }, - ], - ], - "results": Array [ - Object { - "type": "return", - "value": undefined, - }, - ], - } - `); - }); - }); - - describe('when a sloId route param is provided', () => { - it('renders the SLO Edit page with prefilled form values', async () => { - const slo = buildSlo({ id: '123' }); - jest.spyOn(Router, 'useParams').mockReturnValue({ sloId: '123' }); - - useFetchSloMock.mockReturnValue({ isLoading: false, slo }); - - useFetchIndicesMock.mockReturnValue({ - isLoading: false, - indices: [{ name: 'some-index' }], - }); - - useCreateSloMock.mockReturnValue({ - mutateAsync: jest.fn(), - isLoading: false, - isSuccess: false, - isError: false, - }); - - useUpdateSloMock.mockReturnValue({ - mutateAsync: jest.fn(), - isLoading: false, - isSuccess: false, - isError: false, - }); - - render(, config); - - expect(screen.queryByTestId('slosEditPage')).toBeTruthy(); - expect(screen.queryByTestId('sloForm')).toBeTruthy(); - - expect(screen.queryByTestId('sloFormIndicatorTypeSelect')).toHaveValue( - slo.indicator.type - ); - - expect(screen.queryByTestId('indexSelectionSelectedValue')).toHaveTextContent( - slo.indicator.params.index! - ); - - expect(screen.queryByTestId('customKqlIndicatorFormQueryFilterInput')).toHaveValue( - slo.indicator.type === 'sli.kql.custom' ? slo.indicator.params.filter : '' - ); - expect(screen.queryByTestId('customKqlIndicatorFormGoodQueryInput')).toHaveValue( - slo.indicator.type === 'sli.kql.custom' ? slo.indicator.params.good : '' - ); - expect(screen.queryByTestId('customKqlIndicatorFormTotalQueryInput')).toHaveValue( - slo.indicator.type === 'sli.kql.custom' ? slo.indicator.params.total : '' - ); - - expect(screen.queryByTestId('sloFormBudgetingMethodSelect')).toHaveValue( - slo.budgetingMethod - ); - expect(screen.queryByTestId('sloFormTimeWindowDurationSelect')).toHaveValue( - slo.timeWindow.duration - ); - expect(screen.queryByTestId('sloFormObjectiveTargetInput')).toHaveValue( - slo.objective.target * 100 - ); - - expect(screen.queryByTestId('sloFormNameInput')).toHaveValue(slo.name); - expect(screen.queryByTestId('sloFormDescriptionTextArea')).toHaveValue(slo.description); - }); - - it('calls the updateSlo hook if all required values are filled in', async () => { - const slo = buildSlo({ id: '123' }); - - jest.spyOn(Router, 'useParams').mockReturnValue({ sloId: '123' }); - - useFetchIndicesMock.mockReturnValue({ - isLoading: false, - indices: [{ name: 'some-index' }], - }); - - useFetchSloMock.mockReturnValue({ isLoading: false, slo }); - - const mockCreate = jest.fn(); - const mockUpdate = jest.fn(); - - useCreateSloMock.mockReturnValue({ - mutateAsync: mockCreate, - isLoading: false, - isSuccess: false, - isError: false, - }); - - useUpdateSloMock.mockReturnValue({ - mutateAsync: mockUpdate, - isLoading: false, - isSuccess: false, - isError: false, - }); - - render(, config); - - await waitFor(() => expect(screen.queryByTestId('sloFormSubmitButton')).toBeEnabled()); - - fireEvent.click(screen.queryByTestId('sloFormSubmitButton')!); - - expect(mockUpdate).toMatchInlineSnapshot(` - [MockFunction] { - "calls": Array [ - Array [ - Object { - "slo": Object { - "budgetingMethod": "occurrences", - "description": "some description useful", - "indicator": Object { - "params": Object { - "filter": "baz: foo and bar > 2", - "good": "http_status: 2xx", - "index": "some-index", - "total": "a query", - }, - "type": "sli.kql.custom", - }, - "name": "super important level service", - "objective": Object { - "target": 0.98, - }, - "settings": Object { - "frequency": "1m", - "syncDelay": "1m", - "timestampField": "@timestamp", - }, - "timeWindow": Object { - "duration": "30d", - "isRolling": true, - }, - }, - "sloId": "123", - }, - ], - ], - "results": Array [ - Object { - "type": "return", - "value": undefined, - }, - ], - } - `); - }); - - it('blocks submitting if not all required values are filled in', async () => { - const slo = buildSlo(); - - jest.spyOn(Router, 'useParams').mockReturnValue({ sloId: '123' }); - - useFetchIndicesMock.mockReturnValue({ - isLoading: false, - indices: [], - }); - - useFetchSloMock.mockReturnValue({ isLoading: false, slo: { ...slo, name: '' } }); - - render(, config); - - await waitFor(() => { - expect(screen.queryByTestId('sloFormSubmitButton')).toBeDisabled(); - }); - }); - }); - - describe('when submitting has completed successfully', () => { - it('renders a success toast', async () => { - const slo = buildSlo(); - - jest.spyOn(Router, 'useParams').mockReturnValue({ sloId: '123' }); - - useFetchSloMock.mockReturnValue({ isLoading: false, slo }); - - useFetchIndicesMock.mockReturnValue({ - isLoading: false, - indices: [{ name: 'some-index' }], - }); - - useCreateSloMock.mockReturnValue({ - mutateAsync: jest.fn().mockResolvedValue('success'), - isLoading: false, - isSuccess: false, - isError: false, - }); - - useUpdateSloMock.mockReturnValue({ - mutateAsync: jest.fn().mockResolvedValue('success'), - isLoading: false, - isSuccess: false, - isError: false, - }); - - render(, config); - - await waitFor(() => { - expect(screen.queryByTestId('sloFormSubmitButton')).toBeEnabled(); - fireEvent.click(screen.getByTestId('sloFormSubmitButton')); - }); - - expect(mockAddSuccess).toBeCalled(); - }); - - it('navigates to the SLO List page', async () => { - const slo = buildSlo(); - - jest.spyOn(Router, 'useParams').mockReturnValue({ sloId: '123' }); - - useFetchSloMock.mockReturnValue({ isLoading: false, slo }); - - useFetchIndicesMock.mockReturnValue({ - isLoading: false, - indices: [{ name: 'some-index' }], - }); - - useCreateSloMock.mockReturnValue({ - mutateAsync: jest.fn(), - isLoading: false, - isSuccess: false, - isError: false, - }); - - useUpdateSloMock.mockReturnValue({ - mutateAsync: jest.fn(), - isLoading: false, - isSuccess: false, - isError: false, - }); - - render(, config); - - await waitFor(() => { - expect(screen.queryByTestId('sloFormSubmitButton')).toBeEnabled(); - fireEvent.click(screen.getByTestId('sloFormSubmitButton')); - }); - - expect(mockNavigate).toBeCalledWith(mockBasePathPrepend(paths.observability.slos)); - }); - }); - - describe('when submitting has not completed successfully', () => { - it('renders an error toast', async () => { - const slo = buildSlo(); - - jest.spyOn(Router, 'useParams').mockReturnValue({ sloId: '123' }); - - useFetchSloMock.mockReturnValue({ isLoading: false, slo }); - - useFetchIndicesMock.mockReturnValue({ - isLoading: false, - indices: [{ name: 'some-index' }], - }); - - useCreateSloMock.mockReturnValue({ - mutateAsync: jest.fn().mockRejectedValue('argh, I died'), - isLoading: false, - isSuccess: false, - isError: false, - }); - - useUpdateSloMock.mockReturnValue({ - mutateAsync: jest.fn().mockRejectedValue('argh, I died'), - isLoading: false, - isSuccess: false, - isError: false, - }); - - render(, config); - - await waitFor(() => { - expect(screen.queryByTestId('sloFormSubmitButton')).toBeEnabled(); - fireEvent.click(screen.getByTestId('sloFormSubmitButton')); - }); - - expect(mockAddError).toBeCalled(); - }); - }); - }); - }); -}); diff --git a/x-pack/plugins/observability/public/pages/slo_edit/slo_edit.test.tsx b/x-pack/plugins/observability/public/pages/slo_edit/slo_edit.test.tsx new file mode 100644 index 0000000000000..6adbfc4f0589e --- /dev/null +++ b/x-pack/plugins/observability/public/pages/slo_edit/slo_edit.test.tsx @@ -0,0 +1,582 @@ +/* + * 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 React from 'react'; +import Router from 'react-router-dom'; +import { waitFor, fireEvent, screen } from '@testing-library/dom'; +import { cleanup } from '@testing-library/react'; +import userEvent from '@testing-library/user-event'; + +import { render } from '../../utils/test_helper'; +import { useKibana } from '../../utils/kibana_react'; +import { useLicense } from '../../hooks/use_license'; +import { useFetchIndices } from '../../hooks/use_fetch_indices'; +import { useFetchSloDetails } from '../../hooks/slo/use_fetch_slo_details'; +import { useCreateSlo } from '../../hooks/slo/use_create_slo'; +import { useUpdateSlo } from '../../hooks/slo/use_update_slo'; +import { kibanaStartMock } from '../../utils/kibana_react.mock'; +import { SLO_EDIT_FORM_DEFAULT_VALUES } from './constants'; +import { buildSlo } from '../../data/slo/slo'; +import { paths } from '../../config'; +import { SloEditPage } from './slo_edit'; + +jest.mock('react-router-dom', () => ({ + ...jest.requireActual('react-router-dom'), + useParams: jest.fn(), +})); + +jest.mock('../../hooks/use_breadcrumbs'); +jest.mock('../../hooks/use_license'); +jest.mock('../../hooks/use_fetch_indices'); +jest.mock('../../hooks/slo/use_fetch_slo_details'); +jest.mock('../../hooks/slo/use_create_slo'); +jest.mock('../../hooks/slo/use_update_slo'); + +const mockUseKibanaReturnValue = kibanaStartMock.startContract(); + +jest.mock('../../utils/kibana_react', () => ({ + useKibana: jest.fn(() => mockUseKibanaReturnValue), +})); + +const useKibanaMock = useKibana as jest.Mock; +const useLicenseMock = useLicense as jest.Mock; +const useFetchIndicesMock = useFetchIndices as jest.Mock; +const useFetchSloMock = useFetchSloDetails as jest.Mock; +const useCreateSloMock = useCreateSlo as jest.Mock; +const useUpdateSloMock = useUpdateSlo as jest.Mock; + +const mockAddSuccess = jest.fn(); +const mockAddError = jest.fn(); +const mockNavigate = jest.fn(); +const mockBasePathPrepend = jest.fn(); + +const mockKibana = () => { + useKibanaMock.mockReturnValue({ + services: { + application: { + navigateToUrl: mockNavigate, + }, + data: { + dataViews: { + find: jest.fn().mockReturnValue([]), + get: jest.fn().mockReturnValue([]), + }, + }, + dataViews: { + create: jest.fn().mockResolvedValue(42), + }, + docLinks: { + links: { + query: {}, + }, + }, + http: { + basePath: { + prepend: mockBasePathPrepend, + }, + }, + notifications: { + toasts: { + addError: mockAddError, + addSuccess: mockAddSuccess, + }, + }, + storage: { + get: () => {}, + }, + uiSettings: { + get: () => {}, + }, + unifiedSearch: { + autocomplete: { + hasQuerySuggestions: () => {}, + }, + }, + }, + }); +}; + +describe('SLO Edit Page', () => { + beforeEach(() => { + jest.clearAllMocks(); + mockKibana(); + + // Silence all the ref errors in Eui components. + jest.spyOn(console, 'error').mockImplementation(() => {}); + }); + + afterEach(cleanup); + + describe('when the incorrect license is found', () => { + beforeEach(() => { + useLicenseMock.mockReturnValue({ hasAtLeast: () => false }); + }); + + it('navigates to the SLO List page', async () => { + jest.spyOn(Router, 'useParams').mockReturnValue({ sloId: '1234' }); + + useFetchSloMock.mockReturnValue({ isLoading: false, slo: undefined }); + + useFetchIndicesMock.mockReturnValue({ + isLoading: false, + indices: [{ name: 'some-index' }], + }); + + useCreateSloMock.mockReturnValue({ + isLoading: false, + isSuccess: false, + isError: false, + mutate: jest.fn(), + mutateAsync: jest.fn(), + }); + + useUpdateSloMock.mockReturnValue({ + isLoading: false, + isSuccess: false, + isError: false, + mutate: jest.fn(), + mutateAsync: jest.fn(), + }); + + render(); + + expect(mockNavigate).toBeCalledWith(mockBasePathPrepend(paths.observability.slos)); + }); + }); + + describe('when the correct license is found', () => { + beforeEach(() => { + useLicenseMock.mockReturnValue({ hasAtLeast: () => true }); + }); + + describe('when no sloId route param is provided', () => { + it('renders the SLO Edit page in pristine state', async () => { + jest.spyOn(Router, 'useParams').mockReturnValue({ sloId: undefined }); + + useFetchSloMock.mockReturnValue({ isLoading: false, slo: undefined }); + + useFetchIndicesMock.mockReturnValue({ + isLoading: false, + indices: [{ name: 'some-index' }], + }); + + useCreateSloMock.mockReturnValue({ + mutateAsync: jest.fn(), + isLoading: false, + isSuccess: false, + isError: false, + }); + + useUpdateSloMock.mockReturnValue({ + mutateAsync: jest.fn(), + isLoading: false, + isSuccess: false, + isError: false, + }); + + render(); + + expect(screen.queryByTestId('slosEditPage')).toBeTruthy(); + expect(screen.queryByTestId('sloForm')).toBeTruthy(); + + expect(screen.queryByTestId('sloFormIndicatorTypeSelect')).toHaveValue( + SLO_EDIT_FORM_DEFAULT_VALUES.indicator.type + ); + + expect(screen.queryByTestId('indexSelectionSelectedValue')).toBeNull(); + + expect(screen.queryByTestId('customKqlIndicatorFormQueryFilterInput')).toHaveValue( + SLO_EDIT_FORM_DEFAULT_VALUES.indicator.type === 'sli.kql.custom' + ? SLO_EDIT_FORM_DEFAULT_VALUES.indicator.params.filter + : '' + ); + expect(screen.queryByTestId('customKqlIndicatorFormGoodQueryInput')).toHaveValue( + SLO_EDIT_FORM_DEFAULT_VALUES.indicator.type === 'sli.kql.custom' + ? SLO_EDIT_FORM_DEFAULT_VALUES.indicator.params.good + : '' + ); + expect(screen.queryByTestId('customKqlIndicatorFormTotalQueryInput')).toHaveValue( + SLO_EDIT_FORM_DEFAULT_VALUES.indicator.type === 'sli.kql.custom' + ? SLO_EDIT_FORM_DEFAULT_VALUES.indicator.params.total + : '' + ); + + expect(screen.queryByTestId('sloFormBudgetingMethodSelect')).toHaveValue( + SLO_EDIT_FORM_DEFAULT_VALUES.budgetingMethod + ); + expect(screen.queryByTestId('sloFormTimeWindowDurationSelect')).toHaveValue( + SLO_EDIT_FORM_DEFAULT_VALUES.timeWindow.duration as any + ); + expect(screen.queryByTestId('sloFormObjectiveTargetInput')).toHaveValue( + SLO_EDIT_FORM_DEFAULT_VALUES.objective.target + ); + + expect(screen.queryByTestId('sloFormNameInput')).toHaveValue( + SLO_EDIT_FORM_DEFAULT_VALUES.name + ); + expect(screen.queryByTestId('sloFormDescriptionTextArea')).toHaveValue( + SLO_EDIT_FORM_DEFAULT_VALUES.description + ); + }); + + it.skip('calls the createSlo hook if all required values are filled in', async () => { + jest.spyOn(Router, 'useParams').mockReturnValue({ sloId: undefined }); + useFetchIndicesMock.mockReturnValue({ + isLoading: false, + indices: [{ name: 'some-index' }], + }); + + useFetchSloMock.mockReturnValue({ isLoading: false, slo: undefined }); + + const mockCreate = jest.fn(); + const mockUpdate = jest.fn(); + + useCreateSloMock.mockReturnValue({ + mutateAsync: mockCreate, + isLoading: false, + isSuccess: false, + isError: false, + }); + + useUpdateSloMock.mockReturnValue({ + mutateAsync: mockUpdate, + isLoading: false, + isSuccess: false, + isError: false, + }); + + render(); + + userEvent.type(screen.getByTestId('indexSelection'), 'some-index'); + userEvent.type(screen.getByTestId('customKqlIndicatorFormQueryFilterInput'), 'irrelevant'); + userEvent.type(screen.getByTestId('customKqlIndicatorFormGoodQueryInput'), 'irrelevant'); + userEvent.type(screen.getByTestId('customKqlIndicatorFormTotalQueryInput'), 'irrelevant'); + userEvent.selectOptions(screen.getByTestId('sloFormBudgetingMethodSelect'), 'occurrences'); + userEvent.selectOptions(screen.getByTestId('sloFormTimeWindowDurationSelect'), '7d'); + userEvent.clear(screen.getByTestId('sloFormObjectiveTargetInput')); + userEvent.type(screen.getByTestId('sloFormObjectiveTargetInput'), '98.5'); + userEvent.type(screen.getByTestId('sloFormNameInput'), 'irrelevant'); + userEvent.type(screen.getByTestId('sloFormDescriptionTextArea'), 'irrelevant'); + + const t = Date.now(); + await waitFor(() => expect(screen.getByTestId('sloFormSubmitButton')).toBeEnabled()); + console.log('end waiting for submit button: ', Math.ceil(Date.now() - t)); + + fireEvent.click(screen.getByTestId('sloFormSubmitButton')!); + + expect(mockCreate).toMatchInlineSnapshot(` + [MockFunction] { + "calls": Array [ + Array [ + Object { + "budgetingMethod": "occurrences", + "description": "irrelevant", + "indicator": Object { + "params": Object { + "filter": "irrelevant", + "good": "irrelevant", + "index": "some-index", + "total": "irrelevant", + }, + "type": "sli.kql.custom", + }, + "name": "irrelevant", + "objective": Object { + "target": 0.985, + }, + "timeWindow": Object { + "duration": "7d", + "isRolling": true, + }, + }, + ], + ], + "results": Array [ + Object { + "type": "return", + "value": undefined, + }, + ], + } + `); + }); + }); + + describe('when a sloId route param is provided', () => { + it('renders the SLO Edit page with prefilled form values', async () => { + const slo = buildSlo({ id: '123' }); + jest.spyOn(Router, 'useParams').mockReturnValue({ sloId: '123' }); + + useFetchSloMock.mockReturnValue({ isLoading: false, slo }); + + useFetchIndicesMock.mockReturnValue({ + isLoading: false, + indices: [{ name: 'some-index' }], + }); + + useCreateSloMock.mockReturnValue({ + mutateAsync: jest.fn(), + isLoading: false, + isSuccess: false, + isError: false, + }); + + useUpdateSloMock.mockReturnValue({ + mutateAsync: jest.fn(), + isLoading: false, + isSuccess: false, + isError: false, + }); + + render(); + + expect(screen.queryByTestId('slosEditPage')).toBeTruthy(); + expect(screen.queryByTestId('sloForm')).toBeTruthy(); + + expect(screen.queryByTestId('sloFormIndicatorTypeSelect')).toHaveValue(slo.indicator.type); + + expect(screen.queryByTestId('indexSelectionSelectedValue')).toHaveTextContent( + slo.indicator.params.index! + ); + + expect(screen.queryByTestId('customKqlIndicatorFormQueryFilterInput')).toHaveValue( + slo.indicator.type === 'sli.kql.custom' ? slo.indicator.params.filter : '' + ); + expect(screen.queryByTestId('customKqlIndicatorFormGoodQueryInput')).toHaveValue( + slo.indicator.type === 'sli.kql.custom' ? slo.indicator.params.good : '' + ); + expect(screen.queryByTestId('customKqlIndicatorFormTotalQueryInput')).toHaveValue( + slo.indicator.type === 'sli.kql.custom' ? slo.indicator.params.total : '' + ); + + expect(screen.queryByTestId('sloFormBudgetingMethodSelect')).toHaveValue( + slo.budgetingMethod + ); + expect(screen.queryByTestId('sloFormTimeWindowDurationSelect')).toHaveValue( + slo.timeWindow.duration + ); + expect(screen.queryByTestId('sloFormObjectiveTargetInput')).toHaveValue( + slo.objective.target * 100 + ); + + expect(screen.queryByTestId('sloFormNameInput')).toHaveValue(slo.name); + expect(screen.queryByTestId('sloFormDescriptionTextArea')).toHaveValue(slo.description); + }); + + it('calls the updateSlo hook if all required values are filled in', async () => { + const slo = buildSlo({ id: '123' }); + + jest.spyOn(Router, 'useParams').mockReturnValue({ sloId: '123' }); + + useFetchIndicesMock.mockReturnValue({ + isLoading: false, + indices: [{ name: 'some-index' }], + }); + + useFetchSloMock.mockReturnValue({ isLoading: false, slo }); + + const mockCreate = jest.fn(); + const mockUpdate = jest.fn(); + + useCreateSloMock.mockReturnValue({ + mutateAsync: mockCreate, + isLoading: false, + isSuccess: false, + isError: false, + }); + + useUpdateSloMock.mockReturnValue({ + mutateAsync: mockUpdate, + isLoading: false, + isSuccess: false, + isError: false, + }); + + render(); + + await waitFor(() => expect(screen.queryByTestId('sloFormSubmitButton')).toBeEnabled()); + + fireEvent.click(screen.queryByTestId('sloFormSubmitButton')!); + + expect(mockUpdate).toMatchInlineSnapshot(` + [MockFunction] { + "calls": Array [ + Array [ + Object { + "slo": Object { + "budgetingMethod": "occurrences", + "description": "some description useful", + "indicator": Object { + "params": Object { + "filter": "baz: foo and bar > 2", + "good": "http_status: 2xx", + "index": "some-index", + "total": "a query", + }, + "type": "sli.kql.custom", + }, + "name": "super important level service", + "objective": Object { + "target": 0.98, + }, + "settings": Object { + "frequency": "1m", + "syncDelay": "1m", + "timestampField": "@timestamp", + }, + "timeWindow": Object { + "duration": "30d", + "isRolling": true, + }, + }, + "sloId": "123", + }, + ], + ], + "results": Array [ + Object { + "type": "return", + "value": undefined, + }, + ], + } + `); + }); + + it('blocks submitting if not all required values are filled in', async () => { + const slo = buildSlo(); + + jest.spyOn(Router, 'useParams').mockReturnValue({ sloId: '123' }); + + useFetchIndicesMock.mockReturnValue({ + isLoading: false, + indices: [], + }); + + useFetchSloMock.mockReturnValue({ isLoading: false, slo: { ...slo, name: '' } }); + + render(); + + await waitFor(() => { + expect(screen.queryByTestId('sloFormSubmitButton')).toBeDisabled(); + }); + }); + }); + + describe('when submitting has completed successfully', () => { + it('renders a success toast', async () => { + const slo = buildSlo(); + + jest.spyOn(Router, 'useParams').mockReturnValue({ sloId: '123' }); + + useFetchSloMock.mockReturnValue({ isLoading: false, slo }); + + useFetchIndicesMock.mockReturnValue({ + isLoading: false, + indices: [{ name: 'some-index' }], + }); + + useCreateSloMock.mockReturnValue({ + mutateAsync: jest.fn().mockResolvedValue('success'), + isLoading: false, + isSuccess: false, + isError: false, + }); + + useUpdateSloMock.mockReturnValue({ + mutateAsync: jest.fn().mockResolvedValue('success'), + isLoading: false, + isSuccess: false, + isError: false, + }); + + render(); + + await waitFor(() => { + expect(screen.queryByTestId('sloFormSubmitButton')).toBeEnabled(); + fireEvent.click(screen.getByTestId('sloFormSubmitButton')); + }); + + expect(mockAddSuccess).toBeCalled(); + }); + + it('navigates to the SLO List page', async () => { + const slo = buildSlo(); + + jest.spyOn(Router, 'useParams').mockReturnValue({ sloId: '123' }); + + useFetchSloMock.mockReturnValue({ isLoading: false, slo }); + + useFetchIndicesMock.mockReturnValue({ + isLoading: false, + indices: [{ name: 'some-index' }], + }); + + useCreateSloMock.mockReturnValue({ + mutateAsync: jest.fn(), + isLoading: false, + isSuccess: false, + isError: false, + }); + + useUpdateSloMock.mockReturnValue({ + mutateAsync: jest.fn(), + isLoading: false, + isSuccess: false, + isError: false, + }); + + render(); + + await waitFor(() => { + expect(screen.queryByTestId('sloFormSubmitButton')).toBeEnabled(); + fireEvent.click(screen.getByTestId('sloFormSubmitButton')); + }); + + expect(mockNavigate).toBeCalledWith(mockBasePathPrepend(paths.observability.slos)); + }); + }); + + describe('when submitting has not completed successfully', () => { + it('renders an error toast', async () => { + const slo = buildSlo(); + + jest.spyOn(Router, 'useParams').mockReturnValue({ sloId: '123' }); + + useFetchSloMock.mockReturnValue({ isLoading: false, slo }); + + useFetchIndicesMock.mockReturnValue({ + isLoading: false, + indices: [{ name: 'some-index' }], + }); + + useCreateSloMock.mockReturnValue({ + mutateAsync: jest.fn().mockRejectedValue('argh, I died'), + isLoading: false, + isSuccess: false, + isError: false, + }); + + useUpdateSloMock.mockReturnValue({ + mutateAsync: jest.fn().mockRejectedValue('argh, I died'), + isLoading: false, + isSuccess: false, + isError: false, + }); + + render(); + + await waitFor(() => { + expect(screen.queryByTestId('sloFormSubmitButton')).toBeEnabled(); + fireEvent.click(screen.getByTestId('sloFormSubmitButton')); + }); + + expect(mockAddError).toBeCalled(); + }); + }); + }); +}); diff --git a/x-pack/plugins/observability/public/pages/slo_edit/index.tsx b/x-pack/plugins/observability/public/pages/slo_edit/slo_edit.tsx similarity index 88% rename from x-pack/plugins/observability/public/pages/slo_edit/index.tsx rename to x-pack/plugins/observability/public/pages/slo_edit/slo_edit.tsx index 0cc6a3a4558d1..6239b265ed732 100644 --- a/x-pack/plugins/observability/public/pages/slo_edit/index.tsx +++ b/x-pack/plugins/observability/public/pages/slo_edit/slo_edit.tsx @@ -16,15 +16,13 @@ import { useBreadcrumbs } from '../../hooks/use_breadcrumbs'; import { useFetchSloDetails } from '../../hooks/slo/use_fetch_slo_details'; import { useLicense } from '../../hooks/use_license'; import { SloEditForm } from './components/slo_edit_form'; -import PageNotFound from '../404'; -import { isSloFeatureEnabled } from '../slos/helpers/is_slo_feature_enabled'; export function SloEditPage() { const { application: { navigateToUrl }, http: { basePath }, } = useKibana().services; - const { ObservabilityPageTemplate, config } = usePluginContext(); + const { ObservabilityPageTemplate } = usePluginContext(); const { sloId } = useParams<{ sloId: string | undefined }>(); @@ -42,10 +40,6 @@ export function SloEditPage() { const { slo, isLoading } = useFetchSloDetails(sloId || ''); - if (!isSloFeatureEnabled(config)) { - return ; - } - if (hasRightLicense === false) { navigateToUrl(basePath.prepend(paths.observability.slos)); } diff --git a/x-pack/plugins/observability/public/pages/slos/components/header_title.stories.tsx b/x-pack/plugins/observability/public/pages/slos/components/header_title.stories.tsx new file mode 100644 index 0000000000000..05ecb3ab4bde3 --- /dev/null +++ b/x-pack/plugins/observability/public/pages/slos/components/header_title.stories.tsx @@ -0,0 +1,25 @@ +/* + * 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 React from 'react'; +import { ComponentStory } from '@storybook/react'; + +import { KibanaReactStorybookDecorator } from '../../../utils/kibana_react.storybook_decorator'; +import { HeaderTitle as Component } from './header_title'; + +export default { + component: Component, + title: 'app/SLO/ListPage/HeaderTitle', + decorators: [KibanaReactStorybookDecorator], +}; + +const Template: ComponentStory = () => ; + +const defaultProps = {}; + +export const Default = Template.bind({}); +Default.args = defaultProps; diff --git a/x-pack/plugins/observability/public/pages/slos/components/header_title.tsx b/x-pack/plugins/observability/public/pages/slos/components/header_title.tsx new file mode 100644 index 0000000000000..ad2b80eae4025 --- /dev/null +++ b/x-pack/plugins/observability/public/pages/slos/components/header_title.tsx @@ -0,0 +1,41 @@ +/* + * 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 { EuiBetaBadge, EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; +import React from 'react'; + +export function HeaderTitle() { + return ( + + + {i18n.translate('xpack.observability.slosPageTitle', { + defaultMessage: 'SLOs', + })} + + + + + + ); +} diff --git a/x-pack/plugins/observability/public/pages/slos/index.test.tsx b/x-pack/plugins/observability/public/pages/slos/index.test.tsx index cfa07011159e6..3ec1c3bf01c83 100644 --- a/x-pack/plugins/observability/public/pages/slos/index.test.tsx +++ b/x-pack/plugins/observability/public/pages/slos/index.test.tsx @@ -21,8 +21,6 @@ import { useFetchHistoricalSummary } from '../../hooks/slo/use_fetch_historical_ import { useLicense } from '../../hooks/use_license'; import { SlosPage } from '.'; import { emptySloList, sloList } from '../../data/slo/slo'; -import type { ConfigSchema } from '../../plugin'; -import type { Subset } from '../../typings'; import { historicalSummaryData } from '../../data/slo/historical_summary_data'; import { useCapabilities } from '../../hooks/slo/use_capabilities'; @@ -83,12 +81,6 @@ const mockKibana = () => { }); }; -const config: Subset = { - unsafe: { - slo: { enabled: true }, - }, -}; - describe('SLOs Page', () => { beforeEach(() => { jest.clearAllMocks(); @@ -96,51 +88,68 @@ describe('SLOs Page', () => { useCapabilitiesMock.mockReturnValue({ hasWriteCapabilities: true, hasReadCapabilities: true }); }); - describe('when the feature flag is not enabled', () => { - it('renders the not found page ', async () => { + describe('when the incorrect license is found', () => { + it('renders the welcome prompt with subscription buttons', async () => { useFetchSloListMock.mockReturnValue({ isLoading: false, sloList: emptySloList }); - useLicenseMock.mockReturnValue({ hasAtLeast: () => true }); + useLicenseMock.mockReturnValue({ hasAtLeast: () => false }); await act(async () => { - render(, { unsafe: { slo: { enabled: false } } }); + render(); }); - expect(screen.queryByTestId('pageNotFound')).toBeTruthy(); + expect(screen.queryByTestId('slosPageWelcomePrompt')).toBeTruthy(); + expect(screen.queryByTestId('slosPageWelcomePromptSignupForCloudButton')).toBeTruthy(); + expect(screen.queryByTestId('slosPageWelcomePromptSignupForLicenseButton')).toBeTruthy(); }); }); - describe('when the feature flag is enabled', () => { - describe('when the incorrect license is found', () => { - it('renders the welcome prompt with subscription buttons', async () => { - useFetchSloListMock.mockReturnValue({ isLoading: false, sloList: emptySloList }); - useLicenseMock.mockReturnValue({ hasAtLeast: () => false }); + describe('when the correct license is found', () => { + beforeEach(() => { + useLicenseMock.mockReturnValue({ hasAtLeast: () => true }); + }); - await act(async () => { - render(, config); - }); + it('renders the SLOs Welcome Prompt when the API has finished loading and there are no results', async () => { + useFetchSloListMock.mockReturnValue({ isLoading: false, sloList: emptySloList }); - expect(screen.queryByTestId('slosPageWelcomePrompt')).toBeTruthy(); - expect(screen.queryByTestId('slosPageWelcomePromptSignupForCloudButton')).toBeTruthy(); - expect(screen.queryByTestId('slosPageWelcomePromptSignupForLicenseButton')).toBeTruthy(); + await act(async () => { + render(); }); + + expect(screen.queryByTestId('slosPageWelcomePrompt')).toBeTruthy(); }); - describe('when the correct license is found', () => { - beforeEach(() => { - useLicenseMock.mockReturnValue({ hasAtLeast: () => true }); + it('should have a create new SLO button', async () => { + useFetchSloListMock.mockReturnValue({ isLoading: false, sloList }); + + useFetchHistoricalSummaryMock.mockReturnValue({ + isLoading: false, + sloHistoricalSummaryResponse: historicalSummaryData, + }); + + await act(async () => { + render(); }); - it('renders the SLOs Welcome Prompt when the API has finished loading and there are no results', async () => { - useFetchSloListMock.mockReturnValue({ isLoading: false, sloList: emptySloList }); + expect(screen.getByText('Create new SLO')).toBeTruthy(); + }); + + it('should have an Auto Refresh button', async () => { + useFetchSloListMock.mockReturnValue({ isLoading: false, sloList }); - await act(async () => { - render(, config); - }); + useFetchHistoricalSummaryMock.mockReturnValue({ + isLoading: false, + sloHistoricalSummaryResponse: historicalSummaryData, + }); - expect(screen.queryByTestId('slosPageWelcomePrompt')).toBeTruthy(); + await act(async () => { + render(); }); - it('should have a create new SLO button', async () => { + expect(screen.getByTestId('autoRefreshButton')).toBeTruthy(); + }); + + describe('when API has returned results', () => { + it('renders the SLO list with SLO items', async () => { useFetchSloListMock.mockReturnValue({ isLoading: false, sloList }); useFetchHistoricalSummaryMock.mockReturnValue({ @@ -149,13 +158,16 @@ describe('SLOs Page', () => { }); await act(async () => { - render(, config); + render(); }); - expect(screen.getByText('Create new SLO')).toBeTruthy(); + expect(screen.queryByTestId('slosPage')).toBeTruthy(); + expect(screen.queryByTestId('sloList')).toBeTruthy(); + expect(screen.queryAllByTestId('sloItem')).toBeTruthy(); + expect(screen.queryAllByTestId('sloItem').length).toBe(sloList.results.length); }); - it('should have an Auto Refresh button', async () => { + it('allows editing an SLO', async () => { useFetchSloListMock.mockReturnValue({ isLoading: false, sloList }); useFetchHistoricalSummaryMock.mockReturnValue({ @@ -164,107 +176,72 @@ describe('SLOs Page', () => { }); await act(async () => { - render(, config); + render(); }); - expect(screen.getByTestId('autoRefreshButton')).toBeTruthy(); - }); + screen.getAllByLabelText('Actions').at(0)?.click(); - describe('when API has returned results', () => { - it('renders the SLO list with SLO items', async () => { - useFetchSloListMock.mockReturnValue({ isLoading: false, sloList }); + await waitForEuiPopoverOpen(); - useFetchHistoricalSummaryMock.mockReturnValue({ - isLoading: false, - sloHistoricalSummaryResponse: historicalSummaryData, - }); + const button = screen.getByTestId('sloActionsEdit'); - await act(async () => { - render(, config); - }); + expect(button).toBeTruthy(); - expect(screen.queryByTestId('slosPage')).toBeTruthy(); - expect(screen.queryByTestId('sloList')).toBeTruthy(); - expect(screen.queryAllByTestId('sloItem')).toBeTruthy(); - expect(screen.queryAllByTestId('sloItem').length).toBe(sloList.results.length); - }); + button.click(); - it('allows editing an SLO', async () => { - useFetchSloListMock.mockReturnValue({ isLoading: false, sloList }); - - useFetchHistoricalSummaryMock.mockReturnValue({ - isLoading: false, - sloHistoricalSummaryResponse: historicalSummaryData, - }); - - await act(async () => { - render(, config); - }); - - screen.getAllByLabelText('Actions').at(0)?.click(); - - await waitForEuiPopoverOpen(); - - const button = screen.getByTestId('sloActionsEdit'); - - expect(button).toBeTruthy(); + expect(mockNavigate).toBeCalled(); + }); - button.click(); + it('allows deleting an SLO', async () => { + useFetchSloListMock.mockReturnValue({ isLoading: false, sloList }); - expect(mockNavigate).toBeCalled(); + useFetchHistoricalSummaryMock.mockReturnValue({ + isLoading: false, + sloHistoricalSummaryResponse: historicalSummaryData, }); - it('allows deleting an SLO', async () => { - useFetchSloListMock.mockReturnValue({ isLoading: false, sloList }); + await act(async () => { + render(); + }); - useFetchHistoricalSummaryMock.mockReturnValue({ - isLoading: false, - sloHistoricalSummaryResponse: historicalSummaryData, - }); + screen.getAllByLabelText('Actions').at(0)?.click(); - await act(async () => { - render(, config); - }); + await waitForEuiPopoverOpen(); - screen.getAllByLabelText('Actions').at(0)?.click(); + const button = screen.getByTestId('sloActionsDelete'); - await waitForEuiPopoverOpen(); + expect(button).toBeTruthy(); - const button = screen.getByTestId('sloActionsDelete'); + button.click(); - expect(button).toBeTruthy(); + screen.getByTestId('confirmModalConfirmButton').click(); - button.click(); + expect(mockDeleteSlo).toBeCalledWith({ id: sloList.results.at(0)?.id }); + }); - screen.getByTestId('confirmModalConfirmButton').click(); + it('allows cloning an SLO', async () => { + useFetchSloListMock.mockReturnValue({ isLoading: false, sloList }); - expect(mockDeleteSlo).toBeCalledWith({ id: sloList.results.at(0)?.id }); + useFetchHistoricalSummaryMock.mockReturnValue({ + isLoading: false, + sloHistoricalSummaryResponse: historicalSummaryData, }); - it('allows cloning an SLO', async () => { - useFetchSloListMock.mockReturnValue({ isLoading: false, sloList }); - - useFetchHistoricalSummaryMock.mockReturnValue({ - isLoading: false, - sloHistoricalSummaryResponse: historicalSummaryData, - }); - - await act(async () => { - render(, config); - }); + await act(async () => { + render(); + }); - screen.getAllByLabelText('Actions').at(0)?.click(); + screen.getAllByLabelText('Actions').at(0)?.click(); - await waitForEuiPopoverOpen(); + await waitForEuiPopoverOpen(); - const button = screen.getByTestId('sloActionsClone'); + const button = screen.getByTestId('sloActionsClone'); - expect(button).toBeTruthy(); + expect(button).toBeTruthy(); - button.click(); + button.click(); - expect(mockCloneSlo).toBeCalled(); - }); + expect(mockCloneSlo).toBeCalled(); }); }); }); diff --git a/x-pack/plugins/observability/public/pages/slos/index.tsx b/x-pack/plugins/observability/public/pages/slos/index.tsx index c0a51bf2cf0c0..df2e6c125951b 100644 --- a/x-pack/plugins/observability/public/pages/slos/index.tsx +++ b/x-pack/plugins/observability/public/pages/slos/index.tsx @@ -18,17 +18,16 @@ import { useFetchSloList } from '../../hooks/slo/use_fetch_slo_list'; import { SloList } from './components/slo_list'; import { SloListWelcomePrompt } from './components/slo_list_welcome_prompt'; import { AutoRefreshButton } from './components/auto_refresh_button'; -import PageNotFound from '../404'; import { paths } from '../../config'; -import { isSloFeatureEnabled } from './helpers/is_slo_feature_enabled'; import type { ObservabilityAppServices } from '../../application/types'; +import { HeaderTitle } from './components/header_title'; export function SlosPage() { const { application: { navigateToUrl }, http: { basePath }, } = useKibana().services; - const { ObservabilityPageTemplate, config } = usePluginContext(); + const { ObservabilityPageTemplate } = usePluginContext(); const { hasWriteCapabilities } = useCapabilities(); const { hasAtLeast } = useLicense(); @@ -55,10 +54,6 @@ export function SlosPage() { setIsAutoRefreshing(!isAutoRefreshing); }; - if (!isSloFeatureEnabled(config)) { - return ; - } - if (isInitialLoading) { return null; } @@ -70,9 +65,7 @@ export function SlosPage() { return ( , rightSideItems: [ link.navLinkStatus === AppNavLinkStatus.visible) - .filter((link) => (link.id === 'slos' ? config.unsafe.slo.enabled : link)) // might not be useful anymore .map((link) => ({ app: observabilityAppId, label: link.title, path: link.path ?? '', + isBetaFeature: link.id === 'slos' ? true : false, })); const sections = [ @@ -325,12 +322,9 @@ export class Plugin const { application } = coreStart; const config = this.initContext.config.get(); - const filterSlo = (link: AppDeepLink) => - link.id === 'slos' ? config.unsafe.slo.enabled : link; - updateGlobalNavigation({ capabilities: application.capabilities, - deepLinks: this.deepLinks.filter(filterSlo), + deepLinks: this.deepLinks, updater$: this.appUpdater$, }); diff --git a/x-pack/plugins/observability/public/routes/index.tsx b/x-pack/plugins/observability/public/routes/index.tsx index 872f06dced9fe..63be9e6062523 100644 --- a/x-pack/plugins/observability/public/routes/index.tsx +++ b/x-pack/plugins/observability/public/routes/index.tsx @@ -12,7 +12,7 @@ import { TrackApplicationView } from '@kbn/usage-collection-plugin/public'; import { casesPath } from '../../common'; import { CasesPage } from '../pages/cases'; import { AlertsPage } from '../pages/alerts/containers/alerts_page'; -import { OverviewPage } from '../pages/overview'; +import { OverviewPage } from '../pages/overview/overview'; import { jsonRt } from './json_rt'; import { ObservabilityExploratoryView } from '../components/shared/exploratory_view/obsv_exploratory_view'; import { RulesPage } from '../pages/rules'; @@ -21,8 +21,8 @@ import { AlertingPages } from '../config'; import { AlertDetails } from '../pages/alert_details'; import { DatePickerContextProvider } from '../context/date_picker_context'; import { SlosPage } from '../pages/slos'; -import { SloDetailsPage } from '../pages/slo_details'; -import { SloEditPage } from '../pages/slo_edit'; +import { SloDetailsPage } from '../pages/slo_details/slo_details'; +import { SloEditPage } from '../pages/slo_edit/slo_edit'; export type RouteParams = DecodeParams; diff --git a/x-pack/plugins/observability/public/rules/register_observability_rule_types.ts b/x-pack/plugins/observability/public/rules/register_observability_rule_types.ts index 2d779e4497a7a..67a058f077beb 100644 --- a/x-pack/plugins/observability/public/rules/register_observability_rule_types.ts +++ b/x-pack/plugins/observability/public/rules/register_observability_rule_types.ts @@ -17,32 +17,30 @@ export const registerObservabilityRuleTypes = ( config: ConfigSchema, observabilityRuleTypeRegistry: ObservabilityRuleTypeRegistry ) => { - if (config.unsafe.slo.enabled) { - observabilityRuleTypeRegistry.register({ - id: SLO_BURN_RATE_RULE_ID, - description: i18n.translate('xpack.observability.slo.rules.burnRate.description', { - defaultMessage: 'Alert when your SLO burn rate is too high over a defined period of time.', - }), - format: ({ fields }) => { - return { - reason: fields[ALERT_REASON] ?? '-', - link: '/app/observability/slos', - }; - }, - iconClass: 'bell', - documentationUrl(docLinks) { - return '/unknown/docs'; - }, - ruleParamsExpression: lazy(() => import('../components/app/burn_rate_rule_editor')), - validate: validateBurnRateRule, - requiresAppContext: false, - defaultActionMessage: i18n.translate( - 'xpack.observability.slo.rules.burnRate.defaultActionMessage', - { - defaultMessage: `\\{\\{rule.name\\}\\} is firing: + observabilityRuleTypeRegistry.register({ + id: SLO_BURN_RATE_RULE_ID, + description: i18n.translate('xpack.observability.slo.rules.burnRate.description', { + defaultMessage: 'Alert when your SLO burn rate is too high over a defined period of time.', + }), + format: ({ fields }) => { + return { + reason: fields[ALERT_REASON] ?? '-', + link: '/app/observability/slos', + }; + }, + iconClass: 'bell', + documentationUrl(docLinks) { + return '/unknown/docs'; + }, + ruleParamsExpression: lazy(() => import('../components/app/burn_rate_rule_editor')), + validate: validateBurnRateRule, + requiresAppContext: false, + defaultActionMessage: i18n.translate( + 'xpack.observability.slo.rules.burnRate.defaultActionMessage', + { + defaultMessage: `\\{\\{rule.name\\}\\} is firing: - Reason: \\{\\{context.reason\\}\\}`, - } - ), - }); - } + } + ), + }); }; diff --git a/x-pack/plugins/observability/public/utils/get_time_zone.ts b/x-pack/plugins/observability/public/utils/get_time_zone.ts index eccd86d837804..94a9dbda0369d 100644 --- a/x-pack/plugins/observability/public/utils/get_time_zone.ts +++ b/x-pack/plugins/observability/public/utils/get_time_zone.ts @@ -10,7 +10,6 @@ import { UI_SETTINGS } from '../hooks/use_kibana_ui_settings'; export function getTimeZone(uiSettings?: IUiSettingsClient) { const kibanaTimeZone = uiSettings?.get<'Browser' | string>(UI_SETTINGS.DATEFORMAT_TZ); - if (!kibanaTimeZone || kibanaTimeZone === 'Browser') { return 'local'; } diff --git a/x-pack/plugins/observability/public/utils/kibana_react.storybook_decorator.tsx b/x-pack/plugins/observability/public/utils/kibana_react.storybook_decorator.tsx index 24cd7454edf17..228f9802f3896 100644 --- a/x-pack/plugins/observability/public/utils/kibana_react.storybook_decorator.tsx +++ b/x-pack/plugins/observability/public/utils/kibana_react.storybook_decorator.tsx @@ -22,9 +22,6 @@ export function KibanaReactStorybookDecorator(Story: ComponentType) { const config: ConfigSchema = { unsafe: { - slo: { - enabled: false, - }, alertDetails: { apm: { enabled: false }, logs: { enabled: false }, diff --git a/x-pack/plugins/observability/public/utils/test_helper.tsx b/x-pack/plugins/observability/public/utils/test_helper.tsx index f8ab1231ec364..dfaa651f093f4 100644 --- a/x-pack/plugins/observability/public/utils/test_helper.tsx +++ b/x-pack/plugins/observability/public/utils/test_helper.tsx @@ -31,9 +31,6 @@ export const data = dataPluginMock.createStartContract(); const defaultConfig: ConfigSchema = { unsafe: { - slo: { - enabled: false, - }, alertDetails: { apm: { enabled: false }, logs: { enabled: false }, diff --git a/x-pack/plugins/observability/server/index.ts b/x-pack/plugins/observability/server/index.ts index ec930b813fb36..f06b6f049a86b 100644 --- a/x-pack/plugins/observability/server/index.ts +++ b/x-pack/plugins/observability/server/index.ts @@ -29,9 +29,6 @@ const configSchema = schema.object({ index: schema.string({ defaultValue: 'observability-annotations' }), }), unsafe: schema.object({ - slo: schema.object({ - enabled: schema.boolean({ defaultValue: false }), - }), alertDetails: schema.object({ apm: schema.object({ enabled: schema.boolean({ defaultValue: false }), diff --git a/x-pack/plugins/observability/server/lib/rules/slo_burn_rate/register.ts b/x-pack/plugins/observability/server/lib/rules/slo_burn_rate/register.ts index 4f01d59f70174..c847aa5fc484e 100644 --- a/x-pack/plugins/observability/server/lib/rules/slo_burn_rate/register.ts +++ b/x-pack/plugins/observability/server/lib/rules/slo_burn_rate/register.ts @@ -9,6 +9,9 @@ import { schema } from '@kbn/config-schema'; import { i18n } from '@kbn/i18n'; import { LicenseType } from '@kbn/licensing-plugin/server'; import { createLifecycleExecutor } from '@kbn/rule-registry-plugin/server'; +import { legacyExperimentalFieldMap } from '@kbn/alerts-as-data-utils'; +import { sloFeatureId } from '../../../../common'; +import { SLO_RULE_REGISTRATION_CONTEXT } from '../../../common/constants'; import { SLO_BURN_RATE_RULE_ID } from '../../../../common/constants'; import { FIRED_ACTION, getRuleExecutor } from './executor'; @@ -37,7 +40,7 @@ export function sloBurnRateRuleType(createLifecycleRuleExecutor: CreateLifecycle }, defaultActionGroupId: FIRED_ACTION.id, actionGroups: [FIRED_ACTION], - producer: 'slo', + producer: sloFeatureId, minimumLicenseRequired: 'platinum' as LicenseType, isExportable: true, executor: createLifecycleRuleExecutor(getRuleExecutor()), @@ -51,6 +54,12 @@ export function sloBurnRateRuleType(createLifecycleRuleExecutor: CreateLifecycle { name: 'shortWindow', description: windowActionVariableDescription }, ], }, + alerts: { + context: SLO_RULE_REGISTRATION_CONTEXT, + mappings: { fieldMap: legacyExperimentalFieldMap }, + useEcs: true, + useLegacyAlerts: true, + }, }; } diff --git a/x-pack/plugins/observability/server/plugin.ts b/x-pack/plugins/observability/server/plugin.ts index 4b2e2c722230b..b521241240660 100644 --- a/x-pack/plugins/observability/server/plugin.ts +++ b/x-pack/plugins/observability/server/plugin.ts @@ -17,12 +17,12 @@ import { hiddenTypes as filesSavedObjectTypes } from '@kbn/files-plugin/server/s import { PluginSetupContract } from '@kbn/alerting-plugin/server'; import { Dataset, RuleRegistryPluginSetupContract } from '@kbn/rule-registry-plugin/server'; import { PluginSetupContract as FeaturesSetup } from '@kbn/features-plugin/server'; +import { legacyExperimentalFieldMap } from '@kbn/alerts-as-data-utils'; import { createUICapabilities as createCasesUICapabilities, getApiTags as getCasesApiTags, } from '@kbn/cases-plugin/common'; import { SpacesPluginSetup } from '@kbn/spaces-plugin/server'; -import { experimentalRuleFieldMap } from '@kbn/rule-registry-plugin/common/assets/field_maps/experimental_rule_field_map'; import { ECS_COMPONENT_TEMPLATE_NAME } from '@kbn/alerting-plugin/server'; import type { GuidedOnboardingPluginSetup } from '@kbn/guided-onboarding-plugin/server'; @@ -167,76 +167,72 @@ export class ObservabilityPlugin implements Plugin { const { ruleDataService } = plugins.ruleRegistry; - if (config.unsafe.slo.enabled) { - plugins.features.registerKibanaFeature({ - id: sloFeatureId, - name: i18n.translate('xpack.observability.featureRegistry.linkSloTitle', { - defaultMessage: 'SLOs', - }), - order: 1200, - category: DEFAULT_APP_CATEGORIES.observability, - app: [sloFeatureId, 'kibana'], - catalogue: [sloFeatureId, 'observability'], - alerting: [SLO_BURN_RATE_RULE_ID], - privileges: { - all: { - app: [sloFeatureId, 'kibana'], - catalogue: [sloFeatureId, 'observability'], - api: ['slo_write', 'slo_read', 'rac'], - savedObject: { - all: [SO_SLO_TYPE], - read: [], + plugins.features.registerKibanaFeature({ + id: sloFeatureId, + name: i18n.translate('xpack.observability.featureRegistry.linkSloTitle', { + defaultMessage: 'SLOs', + }), + order: 1200, + category: DEFAULT_APP_CATEGORIES.observability, + app: [sloFeatureId, 'kibana'], + catalogue: [sloFeatureId, 'observability'], + alerting: [SLO_BURN_RATE_RULE_ID], + privileges: { + all: { + app: [sloFeatureId, 'kibana'], + catalogue: [sloFeatureId, 'observability'], + api: ['slo_write', 'slo_read', 'rac'], + savedObject: { + all: [SO_SLO_TYPE], + read: [], + }, + alerting: { + rule: { + all: [SLO_BURN_RATE_RULE_ID], }, - alerting: { - rule: { - all: [SLO_BURN_RATE_RULE_ID], - }, - alert: { - all: [SLO_BURN_RATE_RULE_ID], - }, + alert: { + all: [SLO_BURN_RATE_RULE_ID], }, - ui: ['read', 'write'], }, - read: { - app: [sloFeatureId, 'kibana'], - catalogue: [sloFeatureId, 'observability'], - api: ['slo_read', 'rac'], - savedObject: { - all: [], - read: [SO_SLO_TYPE], + ui: ['read', 'write'], + }, + read: { + app: [sloFeatureId, 'kibana'], + catalogue: [sloFeatureId, 'observability'], + api: ['slo_read', 'rac'], + savedObject: { + all: [], + read: [SO_SLO_TYPE], + }, + alerting: { + rule: { + read: [SLO_BURN_RATE_RULE_ID], }, - alerting: { - rule: { - read: [SLO_BURN_RATE_RULE_ID], - }, - alert: { - read: [SLO_BURN_RATE_RULE_ID], - }, + alert: { + read: [SLO_BURN_RATE_RULE_ID], }, - ui: ['read'], }, + ui: ['read'], }, - }); - - core.savedObjects.registerType(slo); - - const ruleDataClient = ruleDataService.initializeIndex({ - feature: sloFeatureId, - registrationContext: SLO_RULE_REGISTRATION_CONTEXT, - dataset: Dataset.alerts, - componentTemplateRefs: [ECS_COMPONENT_TEMPLATE_NAME], - componentTemplates: [ - { - name: 'mappings', - mappings: mappingFromFieldMap(experimentalRuleFieldMap, 'strict'), - }, - ], - }); + }, + }); - registerRuleTypes(plugins.alerting, this.logger, ruleDataClient); + core.savedObjects.registerType(slo); - registerSloUsageCollector(plugins.usageCollection); - } + const ruleDataClient = ruleDataService.initializeIndex({ + feature: sloFeatureId, + registrationContext: SLO_RULE_REGISTRATION_CONTEXT, + dataset: Dataset.alerts, + componentTemplateRefs: [ECS_COMPONENT_TEMPLATE_NAME], + componentTemplates: [ + { + name: 'mappings', + mappings: mappingFromFieldMap(legacyExperimentalFieldMap, 'strict'), + }, + ], + }); + registerRuleTypes(plugins.alerting, this.logger, ruleDataClient); + registerSloUsageCollector(plugins.usageCollection); registerRoutes({ core, @@ -244,7 +240,7 @@ export class ObservabilityPlugin implements Plugin { ruleDataService, }, logger: this.logger, - repository: getObservabilityServerRouteRepository(config), + repository: getObservabilityServerRouteRepository(), }); /** diff --git a/x-pack/plugins/observability/server/routes/get_global_observability_server_route_repository.ts b/x-pack/plugins/observability/server/routes/get_global_observability_server_route_repository.ts index 312d81cc3df5c..9fe40c7d0abe9 100644 --- a/x-pack/plugins/observability/server/routes/get_global_observability_server_route_repository.ts +++ b/x-pack/plugins/observability/server/routes/get_global_observability_server_route_repository.ts @@ -4,16 +4,14 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ -import { ObservabilityConfig } from '..'; + import { rulesRouteRepository } from './rules/route'; import { slosRouteRepository } from './slo/route'; -export function getObservabilityServerRouteRepository(config: ObservabilityConfig) { - const isSloFeatureEnabled = config.unsafe.slo.enabled; - +export function getObservabilityServerRouteRepository() { const repository = { ...rulesRouteRepository, - ...(isSloFeatureEnabled ? slosRouteRepository : {}), + ...slosRouteRepository, }; return repository; } diff --git a/x-pack/plugins/observability/tsconfig.json b/x-pack/plugins/observability/tsconfig.json index eadf340054f52..37471473183d7 100644 --- a/x-pack/plugins/observability/tsconfig.json +++ b/x-pack/plugins/observability/tsconfig.json @@ -70,6 +70,7 @@ "@kbn/securitysolution-ecs", "@kbn/shared-ux-router", "@kbn/alerts-ui-shared", + "@kbn/alerts-as-data-utils", "@kbn/core-application-browser", "@kbn/files-plugin", ], diff --git a/x-pack/plugins/osquery/common/constants.ts b/x-pack/plugins/osquery/common/constants.ts index 1307d719a5835..be8c3fc4b0cad 100644 --- a/x-pack/plugins/osquery/common/constants.ts +++ b/x-pack/plugins/osquery/common/constants.ts @@ -13,3 +13,5 @@ export const ACTIONS_INDEX = `.logs-${OSQUERY_INTEGRATION_NAME}.actions`; export const ACTION_RESPONSES_INDEX = `.logs-${OSQUERY_INTEGRATION_NAME}.action.responses`; export const DEFAULT_PLATFORM = 'linux,windows,darwin'; + +export const CASE_ATTACHMENT_TYPE_ID = 'osquery'; diff --git a/x-pack/plugins/osquery/common/ecs/agent/index.ts b/x-pack/plugins/osquery/common/ecs/agent/index.ts deleted file mode 100644 index 2332b60f1a3ca..0000000000000 --- a/x-pack/plugins/osquery/common/ecs/agent/index.ts +++ /dev/null @@ -1,10 +0,0 @@ -/* - * 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. - */ - -export interface AgentEcs { - type?: string[]; -} diff --git a/x-pack/plugins/osquery/common/ecs/auditd/index.ts b/x-pack/plugins/osquery/common/ecs/auditd/index.ts deleted file mode 100644 index 934f6afa04cff..0000000000000 --- a/x-pack/plugins/osquery/common/ecs/auditd/index.ts +++ /dev/null @@ -1,34 +0,0 @@ -/* - * 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. - */ - -export interface AuditdEcs { - result?: string[]; - session?: string[]; - data?: AuditdDataEcs; - summary?: SummaryEcs; - sequence?: string[]; -} - -export interface AuditdDataEcs { - acct?: string[]; - terminal?: string[]; - op?: string[]; -} - -export interface SummaryEcs { - actor?: PrimarySecondaryEcs; - object?: PrimarySecondaryEcs; - how?: string[]; - message_type?: string[]; - sequence?: string[]; -} - -export interface PrimarySecondaryEcs { - primary?: string[]; - secondary?: string[]; - type?: string[]; -} diff --git a/x-pack/plugins/osquery/common/ecs/cloud/index.ts b/x-pack/plugins/osquery/common/ecs/cloud/index.ts deleted file mode 100644 index a169e5561c6b6..0000000000000 --- a/x-pack/plugins/osquery/common/ecs/cloud/index.ts +++ /dev/null @@ -1,21 +0,0 @@ -/* - * 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. - */ - -export interface CloudEcs { - instance?: CloudInstanceEcs; - machine?: CloudMachineEcs; - provider?: string[]; - region?: string[]; -} - -export interface CloudMachineEcs { - type?: string[]; -} - -export interface CloudInstanceEcs { - id?: string[]; -} diff --git a/x-pack/plugins/osquery/common/ecs/destination/index.ts b/x-pack/plugins/osquery/common/ecs/destination/index.ts deleted file mode 100644 index e8fbdac6578b2..0000000000000 --- a/x-pack/plugins/osquery/common/ecs/destination/index.ts +++ /dev/null @@ -1,17 +0,0 @@ -/* - * 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 type { GeoEcs } from '../geo'; - -export interface DestinationEcs { - bytes?: number[]; - ip?: string[]; - port?: number[]; - domain?: string[]; - geo?: GeoEcs; - packets?: number[]; -} diff --git a/x-pack/plugins/osquery/common/ecs/dns/index.ts b/x-pack/plugins/osquery/common/ecs/dns/index.ts deleted file mode 100644 index 6efd8b34dd8e7..0000000000000 --- a/x-pack/plugins/osquery/common/ecs/dns/index.ts +++ /dev/null @@ -1,17 +0,0 @@ -/* - * 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. - */ - -export interface DnsEcs { - question?: DnsQuestionEcs; - resolved_ip?: string[]; - response_code?: string[]; -} - -export interface DnsQuestionEcs { - name?: string[]; - type?: string[]; -} diff --git a/x-pack/plugins/osquery/common/ecs/ecs_fields/extend_map.test.ts b/x-pack/plugins/osquery/common/ecs/ecs_fields/extend_map.test.ts deleted file mode 100644 index e27b15f021257..0000000000000 --- a/x-pack/plugins/osquery/common/ecs/ecs_fields/extend_map.test.ts +++ /dev/null @@ -1,57 +0,0 @@ -/* - * 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 { extendMap } from './extend_map'; - -describe('ecs_fields test', () => { - describe('extendMap', () => { - test('it should extend a record', () => { - const osFieldsMap: Readonly> = { - 'os.platform': 'os.platform', - 'os.full': 'os.full', - 'os.family': 'os.family', - 'os.version': 'os.version', - 'os.kernel': 'os.kernel', - }; - const expected: Record = { - 'host.os.family': 'host.os.family', - 'host.os.full': 'host.os.full', - 'host.os.kernel': 'host.os.kernel', - 'host.os.platform': 'host.os.platform', - 'host.os.version': 'host.os.version', - }; - expect(extendMap('host', osFieldsMap)).toEqual(expected); - }); - - test('it should extend a sample hosts record', () => { - const hostMap: Record = { - 'host.id': 'host.id', - 'host.ip': 'host.ip', - 'host.name': 'host.name', - }; - const osFieldsMap: Readonly> = { - 'os.platform': 'os.platform', - 'os.full': 'os.full', - 'os.family': 'os.family', - 'os.version': 'os.version', - 'os.kernel': 'os.kernel', - }; - const expected: Record = { - 'host.id': 'host.id', - 'host.ip': 'host.ip', - 'host.name': 'host.name', - 'host.os.family': 'host.os.family', - 'host.os.full': 'host.os.full', - 'host.os.kernel': 'host.os.kernel', - 'host.os.platform': 'host.os.platform', - 'host.os.version': 'host.os.version', - }; - const output = { ...hostMap, ...extendMap('host', osFieldsMap) }; - expect(output).toEqual(expected); - }); - }); -}); diff --git a/x-pack/plugins/osquery/common/ecs/ecs_fields/extend_map.ts b/x-pack/plugins/osquery/common/ecs/ecs_fields/extend_map.ts deleted file mode 100644 index af8c3ded4a1d1..0000000000000 --- a/x-pack/plugins/osquery/common/ecs/ecs_fields/extend_map.ts +++ /dev/null @@ -1,16 +0,0 @@ -/* - * 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. - */ - -export const extendMap = ( - path: string, - map: Readonly> -): Readonly> => - Object.entries(map).reduce>((accum, [key, value]) => { - accum[`${path}.${key}`] = `${path}.${value}`; - - return accum; - }, {}); diff --git a/x-pack/plugins/osquery/common/ecs/ecs_fields/index.ts b/x-pack/plugins/osquery/common/ecs/ecs_fields/index.ts deleted file mode 100644 index 292822019fc9c..0000000000000 --- a/x-pack/plugins/osquery/common/ecs/ecs_fields/index.ts +++ /dev/null @@ -1,359 +0,0 @@ -/* - * 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 { extendMap } from './extend_map'; - -export const auditdMap: Readonly> = { - 'auditd.result': 'auditd.result', - 'auditd.session': 'auditd.session', - 'auditd.data.acct': 'auditd.data.acct', - 'auditd.data.terminal': 'auditd.data.terminal', - 'auditd.data.op': 'auditd.data.op', - 'auditd.summary.actor.primary': 'auditd.summary.actor.primary', - 'auditd.summary.actor.secondary': 'auditd.summary.actor.secondary', - 'auditd.summary.object.primary': 'auditd.summary.object.primary', - 'auditd.summary.object.secondary': 'auditd.summary.object.secondary', - 'auditd.summary.object.type': 'auditd.summary.object.type', - 'auditd.summary.how': 'auditd.summary.how', - 'auditd.summary.message_type': 'auditd.summary.message_type', - 'auditd.summary.sequence': 'auditd.summary.sequence', -}; - -export const cloudFieldsMap: Readonly> = { - 'cloud.account.id': 'cloud.account.id', - 'cloud.availability_zone': 'cloud.availability_zone', - 'cloud.instance.id': 'cloud.instance.id', - 'cloud.instance.name': 'cloud.instance.name', - 'cloud.machine.type': 'cloud.machine.type', - 'cloud.provider': 'cloud.provider', - 'cloud.region': 'cloud.region', -}; - -export const fileMap: Readonly> = { - 'file.name': 'file.name', - 'file.path': 'file.path', - 'file.target_path': 'file.target_path', - 'file.extension': 'file.extension', - 'file.type': 'file.type', - 'file.device': 'file.device', - 'file.inode': 'file.inode', - 'file.uid': 'file.uid', - 'file.owner': 'file.owner', - 'file.gid': 'file.gid', - 'file.group': 'file.group', - 'file.mode': 'file.mode', - 'file.size': 'file.size', - 'file.mtime': 'file.mtime', - 'file.ctime': 'file.ctime', -}; - -export const osFieldsMap: Readonly> = { - 'os.platform': 'os.platform', - 'os.name': 'os.name', - 'os.full': 'os.full', - 'os.family': 'os.family', - 'os.version': 'os.version', - 'os.kernel': 'os.kernel', -}; - -export const hostFieldsMap: Readonly> = { - 'host.architecture': 'host.architecture', - 'host.id': 'host.id', - 'host.ip': 'host.ip', - 'host.mac': 'host.mac', - 'host.name': 'host.name', - ...extendMap('host', osFieldsMap), -}; - -export const processFieldsMap: Readonly> = { - 'process.hash.md5': 'process.hash.md5', - 'process.hash.sha1': 'process.hash.sha1', - 'process.hash.sha256': 'process.hash.sha256', - 'process.pid': 'process.pid', - 'process.name': 'process.name', - 'process.ppid': 'process.ppid', - 'process.args': 'process.args', - 'process.entity_id': 'process.entity_id', - 'process.executable': 'process.executable', - 'process.title': 'process.title', - 'process.thread': 'process.thread', - 'process.working_directory': 'process.working_directory', -}; - -export const agentFieldsMap: Readonly> = { - 'agent.type': 'agent.type', -}; - -export const userFieldsMap: Readonly> = { - 'user.domain': 'user.domain', - 'user.id': 'user.id', - 'user.name': 'user.name', - // NOTE: This field is not tested and available from ECS. Please remove this tag once it is - 'user.full_name': 'user.full_name', - // NOTE: This field is not tested and available from ECS. Please remove this tag once it is - 'user.email': 'user.email', - // NOTE: This field is not tested and available from ECS. Please remove this tag once it is - 'user.hash': 'user.hash', - // NOTE: This field is not tested and available from ECS. Please remove this tag once it is - 'user.group': 'user.group', -}; - -export const winlogFieldsMap: Readonly> = { - 'winlog.event_id': 'winlog.event_id', -}; - -export const suricataFieldsMap: Readonly> = { - 'suricata.eve.flow_id': 'suricata.eve.flow_id', - 'suricata.eve.proto': 'suricata.eve.proto', - 'suricata.eve.alert.signature': 'suricata.eve.alert.signature', - 'suricata.eve.alert.signature_id': 'suricata.eve.alert.signature_id', -}; - -export const tlsFieldsMap: Readonly> = { - 'tls.client_certificate.fingerprint.sha1': 'tls.client_certificate.fingerprint.sha1', - 'tls.fingerprints.ja3.hash': 'tls.fingerprints.ja3.hash', - 'tls.server_certificate.fingerprint.sha1': 'tls.server_certificate.fingerprint.sha1', -}; - -export const urlFieldsMap: Readonly> = { - 'url.original': 'url.original', - 'url.domain': 'url.domain', - 'user.username': 'user.username', - 'user.password': 'user.password', -}; - -export const httpFieldsMap: Readonly> = { - 'http.version': 'http.version', - 'http.request': 'http.request', - 'http.request.method': 'http.request.method', - 'http.request.body.bytes': 'http.request.body.bytes', - 'http.request.body.content': 'http.request.body.content', - 'http.request.referrer': 'http.request.referrer', - 'http.response.status_code': 'http.response.status_code', - 'http.response.body': 'http.response.body', - 'http.response.body.bytes': 'http.response.body.bytes', - 'http.response.body.content': 'http.response.body.content', -}; - -export const zeekFieldsMap: Readonly> = { - 'zeek.session_id': 'zeek.session_id', - 'zeek.connection.local_resp': 'zeek.connection.local_resp', - 'zeek.connection.local_orig': 'zeek.connection.local_orig', - 'zeek.connection.missed_bytes': 'zeek.connection.missed_bytes', - 'zeek.connection.state': 'zeek.connection.state', - 'zeek.connection.history': 'zeek.connection.history', - 'zeek.notice.suppress_for': 'zeek.notice.suppress_for', - 'zeek.notice.msg': 'zeek.notice.msg', - 'zeek.notice.note': 'zeek.notice.note', - 'zeek.notice.sub': 'zeek.notice.sub', - 'zeek.notice.dst': 'zeek.notice.dst', - 'zeek.notice.dropped': 'zeek.notice.dropped', - 'zeek.notice.peer_descr': 'zeek.notice.peer_descr', - 'zeek.dns.AA': 'zeek.dns.AA', - 'zeek.dns.qclass_name': 'zeek.dns.qclass_name', - 'zeek.dns.RD': 'zeek.dns.RD', - 'zeek.dns.qtype_name': 'zeek.dns.qtype_name', - 'zeek.dns.qtype': 'zeek.dns.qtype', - 'zeek.dns.query': 'zeek.dns.query', - 'zeek.dns.trans_id': 'zeek.dns.trans_id', - 'zeek.dns.qclass': 'zeek.dns.qclass', - 'zeek.dns.RA': 'zeek.dns.RA', - 'zeek.dns.TC': 'zeek.dns.TC', - 'zeek.http.resp_mime_types': 'zeek.http.resp_mime_types', - 'zeek.http.trans_depth': 'zeek.http.trans_depth', - 'zeek.http.status_msg': 'zeek.http.status_msg', - 'zeek.http.resp_fuids': 'zeek.http.resp_fuids', - 'zeek.http.tags': 'zeek.http.tags', - 'zeek.files.session_ids': 'zeek.files.session_ids', - 'zeek.files.timedout': 'zeek.files.timedout', - 'zeek.files.local_orig': 'zeek.files.local_orig', - 'zeek.files.tx_host': 'zeek.files.tx_host', - 'zeek.files.source': 'zeek.files.source', - 'zeek.files.is_orig': 'zeek.files.is_orig', - 'zeek.files.overflow_bytes': 'zeek.files.overflow_bytes', - 'zeek.files.sha1': 'zeek.files.sha1', - 'zeek.files.duration': 'zeek.files.duration', - 'zeek.files.depth': 'zeek.files.depth', - 'zeek.files.analyzers': 'zeek.files.analyzers', - 'zeek.files.mime_type': 'zeek.files.mime_type', - 'zeek.files.rx_host': 'zeek.files.rx_host', - 'zeek.files.total_bytes': 'zeek.files.total_bytes', - 'zeek.files.fuid': 'zeek.files.fuid', - 'zeek.files.seen_bytes': 'zeek.files.seen_bytes', - 'zeek.files.missing_bytes': 'zeek.files.missing_bytes', - 'zeek.files.md5': 'zeek.files.md5', - 'zeek.ssl.cipher': 'zeek.ssl.cipher', - 'zeek.ssl.established': 'zeek.ssl.established', - 'zeek.ssl.resumed': 'zeek.ssl.resumed', - 'zeek.ssl.version': 'zeek.ssl.version', -}; - -export const sourceFieldsMap: Readonly> = { - 'source.bytes': 'source.bytes', - 'source.ip': 'source.ip', - 'source.packets': 'source.packets', - 'source.port': 'source.port', - 'source.domain': 'source.domain', - 'source.geo.continent_name': 'source.geo.continent_name', - 'source.geo.country_name': 'source.geo.country_name', - 'source.geo.country_iso_code': 'source.geo.country_iso_code', - 'source.geo.city_name': 'source.geo.city_name', - 'source.geo.region_iso_code': 'source.geo.region_iso_code', - 'source.geo.region_name': 'source.geo.region_name', -}; - -export const destinationFieldsMap: Readonly> = { - 'destination.bytes': 'destination.bytes', - 'destination.ip': 'destination.ip', - 'destination.packets': 'destination.packets', - 'destination.port': 'destination.port', - 'destination.domain': 'destination.domain', - 'destination.geo.continent_name': 'destination.geo.continent_name', - 'destination.geo.country_name': 'destination.geo.country_name', - 'destination.geo.country_iso_code': 'destination.geo.country_iso_code', - 'destination.geo.city_name': 'destination.geo.city_name', - 'destination.geo.region_iso_code': 'destination.geo.region_iso_code', - 'destination.geo.region_name': 'destination.geo.region_name', -}; - -export const networkFieldsMap: Readonly> = { - 'network.bytes': 'network.bytes', - 'network.community_id': 'network.community_id', - 'network.direction': 'network.direction', - 'network.packets': 'network.packets', - 'network.protocol': 'network.protocol', - 'network.transport': 'network.transport', -}; - -export const geoFieldsMap: Readonly> = { - 'geo.region_name': 'destination.geo.region_name', - 'geo.country_iso_code': 'destination.geo.country_iso_code', -}; - -export const dnsFieldsMap: Readonly> = { - 'dns.question.name': 'dns.question.name', - 'dns.question.type': 'dns.question.type', - 'dns.resolved_ip': 'dns.resolved_ip', - 'dns.response_code': 'dns.response_code', -}; - -export const endgameFieldsMap: Readonly> = { - 'endgame.exit_code': 'endgame.exit_code', - 'endgame.file_name': 'endgame.file_name', - 'endgame.file_path': 'endgame.file_path', - 'endgame.logon_type': 'endgame.logon_type', - 'endgame.parent_process_name': 'endgame.parent_process_name', - 'endgame.pid': 'endgame.pid', - 'endgame.process_name': 'endgame.process_name', - 'endgame.subject_domain_name': 'endgame.subject_domain_name', - 'endgame.subject_logon_id': 'endgame.subject_logon_id', - 'endgame.subject_user_name': 'endgame.subject_user_name', - 'endgame.target_domain_name': 'endgame.target_domain_name', - 'endgame.target_logon_id': 'endgame.target_logon_id', - 'endgame.target_user_name': 'endgame.target_user_name', -}; - -export const eventBaseFieldsMap: Readonly> = { - 'event.action': 'event.action', - 'event.category': 'event.category', - 'event.code': 'event.code', - 'event.created': 'event.created', - 'event.dataset': 'event.dataset', - 'event.duration': 'event.duration', - 'event.end': 'event.end', - 'event.hash': 'event.hash', - 'event.id': 'event.id', - 'event.kind': 'event.kind', - 'event.module': 'event.module', - 'event.original': 'event.original', - 'event.outcome': 'event.outcome', - 'event.risk_score': 'event.risk_score', - 'event.risk_score_norm': 'event.risk_score_norm', - 'event.severity': 'event.severity', - 'event.start': 'event.start', - 'event.timezone': 'event.timezone', - 'event.type': 'event.type', -}; - -export const systemFieldsMap: Readonly> = { - 'system.audit.package.arch': 'system.audit.package.arch', - 'system.audit.package.entity_id': 'system.audit.package.entity_id', - 'system.audit.package.name': 'system.audit.package.name', - 'system.audit.package.size': 'system.audit.package.size', - 'system.audit.package.summary': 'system.audit.package.summary', - 'system.audit.package.version': 'system.audit.package.version', - 'system.auth.ssh.signature': 'system.auth.ssh.signature', - 'system.auth.ssh.method': 'system.auth.ssh.method', -}; - -export const signalFieldsMap: Readonly> = { - 'signal.original_time': 'signal.original_time', - 'signal.rule.id': 'signal.rule.id', - 'signal.rule.saved_id': 'signal.rule.saved_id', - 'signal.rule.timeline_id': 'signal.rule.timeline_id', - 'signal.rule.timeline_title': 'signal.rule.timeline_title', - 'signal.rule.output_index': 'signal.rule.output_index', - 'signal.rule.from': 'signal.rule.from', - 'signal.rule.index': 'signal.rule.index', - 'signal.rule.language': 'signal.rule.language', - 'signal.rule.query': 'signal.rule.query', - 'signal.rule.to': 'signal.rule.to', - 'signal.rule.filters': 'signal.rule.filters', - 'signal.rule.rule_id': 'signal.rule.rule_id', - 'signal.rule.false_positives': 'signal.rule.false_positives', - 'signal.rule.max_signals': 'signal.rule.max_signals', - 'signal.rule.risk_score': 'signal.rule.risk_score', - 'signal.rule.description': 'signal.rule.description', - 'signal.rule.name': 'signal.rule.name', - 'signal.rule.immutable': 'signal.rule.immutable', - 'signal.rule.references': 'signal.rule.references', - 'signal.rule.severity': 'signal.rule.severity', - 'signal.rule.tags': 'signal.rule.tags', - 'signal.rule.threat': 'signal.rule.threat', - 'signal.rule.type': 'signal.rule.type', - 'signal.rule.size': 'signal.rule.size', - 'signal.rule.enabled': 'signal.rule.enabled', - 'signal.rule.created_at': 'signal.rule.created_at', - 'signal.rule.updated_at': 'signal.rule.updated_at', - 'signal.rule.created_by': 'signal.rule.created_by', - 'signal.rule.updated_by': 'signal.rule.updated_by', - 'signal.rule.version': 'signal.rule.version', - 'signal.rule.note': 'signal.rule.note', - 'signal.rule.threshold': 'signal.rule.threshold', - 'signal.rule.exceptions_list': 'signal.rule.exceptions_list', -}; - -export const ruleFieldsMap: Readonly> = { - 'rule.reference': 'rule.reference', -}; - -export const eventFieldsMap: Readonly> = { - timestamp: '@timestamp', - '@timestamp': '@timestamp', - message: 'message', - ...{ ...agentFieldsMap }, - ...{ ...auditdMap }, - ...{ ...destinationFieldsMap }, - ...{ ...dnsFieldsMap }, - ...{ ...endgameFieldsMap }, - ...{ ...eventBaseFieldsMap }, - ...{ ...fileMap }, - ...{ ...geoFieldsMap }, - ...{ ...hostFieldsMap }, - ...{ ...networkFieldsMap }, - ...{ ...ruleFieldsMap }, - ...{ ...signalFieldsMap }, - ...{ ...sourceFieldsMap }, - ...{ ...suricataFieldsMap }, - ...{ ...systemFieldsMap }, - ...{ ...tlsFieldsMap }, - ...{ ...zeekFieldsMap }, - ...{ ...httpFieldsMap }, - ...{ ...userFieldsMap }, - ...{ ...winlogFieldsMap }, - ...{ ...processFieldsMap }, -}; diff --git a/x-pack/plugins/osquery/common/ecs/endgame/index.ts b/x-pack/plugins/osquery/common/ecs/endgame/index.ts deleted file mode 100644 index f82a9587c75c3..0000000000000 --- a/x-pack/plugins/osquery/common/ecs/endgame/index.ts +++ /dev/null @@ -1,22 +0,0 @@ -/* - * 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. - */ - -export interface EndgameEcs { - exit_code?: number[]; - file_name?: string[]; - file_path?: string[]; - logon_type?: number[]; - parent_process_name?: string[]; - pid?: number[]; - process_name?: string[]; - subject_domain_name?: string[]; - subject_logon_id?: string[]; - subject_user_name?: string[]; - target_domain_name?: string[]; - target_logon_id?: string[]; - target_user_name?: string[]; -} diff --git a/x-pack/plugins/osquery/common/ecs/event/index.ts b/x-pack/plugins/osquery/common/ecs/event/index.ts deleted file mode 100644 index 24931066dc684..0000000000000 --- a/x-pack/plugins/osquery/common/ecs/event/index.ts +++ /dev/null @@ -1,28 +0,0 @@ -/* - * 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. - */ - -export interface EventEcs { - action?: string[]; - category?: string[]; - code?: string[]; - created?: string[]; - dataset?: string[]; - duration?: number[]; - end?: string[]; - hash?: string[]; - id?: string[]; - kind?: string[]; - module?: string[]; - original?: string[]; - outcome?: string[]; - risk_score?: number[]; - risk_score_norm?: number[]; - severity?: number[]; - start?: string[]; - timezone?: string[]; - type?: string[]; -} diff --git a/x-pack/plugins/osquery/common/ecs/file/index.ts b/x-pack/plugins/osquery/common/ecs/file/index.ts deleted file mode 100644 index 6a69ea1b70ae5..0000000000000 --- a/x-pack/plugins/osquery/common/ecs/file/index.ts +++ /dev/null @@ -1,37 +0,0 @@ -/* - * 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. - */ - -export interface CodeSignature { - subject_name: string[]; - trusted: string[]; -} -export interface Ext { - code_signature?: CodeSignature[] | CodeSignature; -} -export interface Hash { - sha256: string[]; -} - -export interface FileEcs { - name?: string[]; - path?: string[]; - target_path?: string[]; - extension?: string[]; - Ext?: Ext; - type?: string[]; - device?: string[]; - inode?: string[]; - uid?: string[]; - owner?: string[]; - gid?: string[]; - group?: string[]; - mode?: string[]; - size?: number[]; - mtime?: string[]; - ctime?: string[]; - hash?: Hash; -} diff --git a/x-pack/plugins/osquery/common/ecs/geo/index.ts b/x-pack/plugins/osquery/common/ecs/geo/index.ts deleted file mode 100644 index b6bf0f7b8aaad..0000000000000 --- a/x-pack/plugins/osquery/common/ecs/geo/index.ts +++ /dev/null @@ -1,21 +0,0 @@ -/* - * 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. - */ - -export interface GeoEcs { - city_name?: string[]; - continent_name?: string[]; - country_iso_code?: string[]; - country_name?: string[]; - location?: Location; - region_iso_code?: string[]; - region_name?: string[]; -} - -export interface Location { - lon?: number[]; - lat?: number[]; -} diff --git a/x-pack/plugins/osquery/common/ecs/host/index.ts b/x-pack/plugins/osquery/common/ecs/host/index.ts deleted file mode 100644 index 614f23c292f66..0000000000000 --- a/x-pack/plugins/osquery/common/ecs/host/index.ts +++ /dev/null @@ -1,25 +0,0 @@ -/* - * 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. - */ - -export interface HostEcs { - architecture?: string[]; - id?: string[]; - ip?: string[]; - mac?: string[]; - name?: string[]; - os?: OsEcs; - type?: string[]; -} - -export interface OsEcs { - platform?: string[]; - name?: string[]; - full?: string[]; - family?: string[]; - version?: string[]; - kernel?: string[]; -} diff --git a/x-pack/plugins/osquery/common/ecs/http/index.ts b/x-pack/plugins/osquery/common/ecs/http/index.ts deleted file mode 100644 index 1ce591dfb60df..0000000000000 --- a/x-pack/plugins/osquery/common/ecs/http/index.ts +++ /dev/null @@ -1,30 +0,0 @@ -/* - * 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. - */ - -export interface HttpEcs { - version?: string[]; - request?: HttpRequestData; - response?: HttpResponseData; -} - -export interface HttpRequestData { - method?: string[]; - body?: HttpBodyData; - referrer?: string[]; - bytes?: number[]; -} - -export interface HttpBodyData { - content?: string[]; - bytes?: number[]; -} - -export interface HttpResponseData { - status_code?: number[]; - body?: HttpBodyData; - bytes?: number[]; -} diff --git a/x-pack/plugins/osquery/common/ecs/index.ts b/x-pack/plugins/osquery/common/ecs/index.ts deleted file mode 100644 index 570cb2b13917f..0000000000000 --- a/x-pack/plugins/osquery/common/ecs/index.ts +++ /dev/null @@ -1,58 +0,0 @@ -/* - * 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 type { AgentEcs } from './agent'; -import type { AuditdEcs } from './auditd'; -import type { DestinationEcs } from './destination'; -import type { DnsEcs } from './dns'; -import type { EndgameEcs } from './endgame'; -import type { EventEcs } from './event'; -import type { FileEcs } from './file'; -import type { GeoEcs } from './geo'; -import type { HostEcs } from './host'; -import type { NetworkEcs } from './network'; -import type { RuleEcs } from './rule'; -import type { SignalEcs } from './signal'; -import type { SourceEcs } from './source'; -import type { SuricataEcs } from './suricata'; -import type { TlsEcs } from './tls'; -import type { ZeekEcs } from './zeek'; -import type { HttpEcs } from './http'; -import type { UrlEcs } from './url'; -import type { UserEcs } from './user'; -import type { WinlogEcs } from './winlog'; -import type { ProcessEcs } from './process'; -import type { SystemEcs } from './system'; - -export interface Ecs { - _id: string; - _index?: string; - agent?: AgentEcs; - auditd?: AuditdEcs; - destination?: DestinationEcs; - dns?: DnsEcs; - endgame?: EndgameEcs; - event?: EventEcs; - geo?: GeoEcs; - host?: HostEcs; - network?: NetworkEcs; - rule?: RuleEcs; - signal?: SignalEcs; - source?: SourceEcs; - suricata?: SuricataEcs; - tls?: TlsEcs; - zeek?: ZeekEcs; - http?: HttpEcs; - url?: UrlEcs; - timestamp?: string; - message?: string[]; - user?: UserEcs; - winlog?: WinlogEcs; - process?: ProcessEcs; - file?: FileEcs; - system?: SystemEcs; -} diff --git a/x-pack/plugins/osquery/common/ecs/network/index.ts b/x-pack/plugins/osquery/common/ecs/network/index.ts deleted file mode 100644 index 6cc5dacab1e53..0000000000000 --- a/x-pack/plugins/osquery/common/ecs/network/index.ts +++ /dev/null @@ -1,15 +0,0 @@ -/* - * 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. - */ - -export interface NetworkEcs { - bytes?: number[]; - community_id?: string[]; - direction?: string[]; - packets?: number[]; - protocol?: string[]; - transport?: string[]; -} diff --git a/x-pack/plugins/osquery/common/ecs/process/index.ts b/x-pack/plugins/osquery/common/ecs/process/index.ts deleted file mode 100644 index cc4a961a5b528..0000000000000 --- a/x-pack/plugins/osquery/common/ecs/process/index.ts +++ /dev/null @@ -1,30 +0,0 @@ -/* - * 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. - */ - -export interface ProcessEcs { - entity_id?: string[]; - hash?: ProcessHashData; - pid?: number[]; - name?: string[]; - ppid?: number[]; - args?: string[]; - executable?: string[]; - title?: string[]; - thread?: Thread; - working_directory?: string[]; -} - -export interface ProcessHashData { - md5?: string[]; - sha1?: string[]; - sha256?: string[]; -} - -export interface Thread { - id?: number[]; - start?: string[]; -} diff --git a/x-pack/plugins/osquery/common/ecs/rule/index.ts b/x-pack/plugins/osquery/common/ecs/rule/index.ts deleted file mode 100644 index ae7e5064a8ece..0000000000000 --- a/x-pack/plugins/osquery/common/ecs/rule/index.ts +++ /dev/null @@ -1,43 +0,0 @@ -/* - * 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. - */ - -export interface RuleEcs { - id?: string[]; - rule_id?: string[]; - name?: string[]; - false_positives?: string[]; - saved_id?: string[]; - timeline_id?: string[]; - timeline_title?: string[]; - max_signals?: number[]; - risk_score?: string[]; - output_index?: string[]; - description?: string[]; - from?: string[]; - immutable?: boolean[]; - index?: string[]; - interval?: string[]; - language?: string[]; - query?: string[]; - references?: string[]; - severity?: string[]; - tags?: string[]; - threat?: unknown; - threshold?: unknown; - type?: string[]; - size?: string[]; - to?: string[]; - enabled?: boolean[]; - filters?: unknown; - created_at?: string[]; - updated_at?: string[]; - created_by?: string[]; - updated_by?: string[]; - version?: string[]; - note?: string[]; - building_block_type?: string[]; -} diff --git a/x-pack/plugins/osquery/common/ecs/signal/index.ts b/x-pack/plugins/osquery/common/ecs/signal/index.ts deleted file mode 100644 index a130270c1e308..0000000000000 --- a/x-pack/plugins/osquery/common/ecs/signal/index.ts +++ /dev/null @@ -1,17 +0,0 @@ -/* - * 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 type { RuleEcs } from '../rule'; - -export interface SignalEcs { - rule?: RuleEcs; - original_time?: string[]; - status?: string[]; - group?: { - id?: string[]; - }; -} diff --git a/x-pack/plugins/osquery/common/ecs/source/index.ts b/x-pack/plugins/osquery/common/ecs/source/index.ts deleted file mode 100644 index fcc2bf31aed3c..0000000000000 --- a/x-pack/plugins/osquery/common/ecs/source/index.ts +++ /dev/null @@ -1,17 +0,0 @@ -/* - * 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 type { GeoEcs } from '../geo'; - -export interface SourceEcs { - bytes?: number[]; - ip?: string[]; - port?: number[]; - domain?: string[]; - geo?: GeoEcs; - packets?: number[]; -} diff --git a/x-pack/plugins/osquery/common/ecs/suricata/index.ts b/x-pack/plugins/osquery/common/ecs/suricata/index.ts deleted file mode 100644 index ac3d12eb3718d..0000000000000 --- a/x-pack/plugins/osquery/common/ecs/suricata/index.ts +++ /dev/null @@ -1,21 +0,0 @@ -/* - * 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. - */ - -export interface SuricataEcs { - eve?: SuricataEveData; -} - -export interface SuricataEveData { - alert?: SuricataAlertData; - flow_id?: number[]; - proto?: string[]; -} - -export interface SuricataAlertData { - signature?: string[]; - signature_id?: number[]; -} diff --git a/x-pack/plugins/osquery/common/ecs/system/index.ts b/x-pack/plugins/osquery/common/ecs/system/index.ts deleted file mode 100644 index 337373af646d9..0000000000000 --- a/x-pack/plugins/osquery/common/ecs/system/index.ts +++ /dev/null @@ -1,33 +0,0 @@ -/* - * 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. - */ - -export interface SystemEcs { - audit?: AuditEcs; - auth?: AuthEcs; -} - -export interface AuditEcs { - package?: PackageEcs; -} - -export interface PackageEcs { - arch?: string[]; - entity_id?: string[]; - name?: string[]; - size?: number[]; - summary?: string[]; - version?: string[]; -} - -export interface AuthEcs { - ssh?: SshEcs; -} - -export interface SshEcs { - method?: string[]; - signature?: string[]; -} diff --git a/x-pack/plugins/osquery/common/ecs/tls/index.ts b/x-pack/plugins/osquery/common/ecs/tls/index.ts deleted file mode 100644 index c1a2c76633ada..0000000000000 --- a/x-pack/plugins/osquery/common/ecs/tls/index.ts +++ /dev/null @@ -1,32 +0,0 @@ -/* - * 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. - */ - -export interface TlsEcs { - client_certificate?: TlsClientCertificateData; - fingerprints?: TlsFingerprintsData; - server_certificate?: TlsServerCertificateData; -} - -export interface TlsClientCertificateData { - fingerprint?: FingerprintData; -} - -export interface FingerprintData { - sha1?: string[]; -} - -export interface TlsFingerprintsData { - ja3?: TlsJa3Data; -} - -export interface TlsJa3Data { - hash?: string[]; -} - -export interface TlsServerCertificateData { - fingerprint?: FingerprintData; -} diff --git a/x-pack/plugins/osquery/common/ecs/url/index.ts b/x-pack/plugins/osquery/common/ecs/url/index.ts deleted file mode 100644 index 3197b208fd33e..0000000000000 --- a/x-pack/plugins/osquery/common/ecs/url/index.ts +++ /dev/null @@ -1,13 +0,0 @@ -/* - * 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. - */ - -export interface UrlEcs { - domain?: string[]; - original?: string[]; - username?: string[]; - password?: string[]; -} diff --git a/x-pack/plugins/osquery/common/ecs/user/index.ts b/x-pack/plugins/osquery/common/ecs/user/index.ts deleted file mode 100644 index 794c5f4a3697e..0000000000000 --- a/x-pack/plugins/osquery/common/ecs/user/index.ts +++ /dev/null @@ -1,16 +0,0 @@ -/* - * 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. - */ - -export interface UserEcs { - domain?: string[]; - id?: string[]; - name?: string[]; - full_name?: string[]; - email?: string[]; - hash?: string[]; - group?: string[]; -} diff --git a/x-pack/plugins/osquery/common/ecs/winlog/index.ts b/x-pack/plugins/osquery/common/ecs/winlog/index.ts deleted file mode 100644 index 27757d05ba6ec..0000000000000 --- a/x-pack/plugins/osquery/common/ecs/winlog/index.ts +++ /dev/null @@ -1,10 +0,0 @@ -/* - * 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. - */ - -export interface WinlogEcs { - event_id?: number[]; -} diff --git a/x-pack/plugins/osquery/common/ecs/zeek/index.ts b/x-pack/plugins/osquery/common/ecs/zeek/index.ts deleted file mode 100644 index 0512b833e527c..0000000000000 --- a/x-pack/plugins/osquery/common/ecs/zeek/index.ts +++ /dev/null @@ -1,84 +0,0 @@ -/* - * 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. - */ - -export interface ZeekEcs { - session_id?: string[]; - connection?: ZeekConnectionData; - notice?: ZeekNoticeData; - dns?: ZeekDnsData; - http?: ZeekHttpData; - files?: ZeekFileData; - ssl?: ZeekSslData; -} - -export interface ZeekConnectionData { - local_resp?: boolean[]; - local_orig?: boolean[]; - missed_bytes?: number[]; - state?: string[]; - history?: string[]; -} - -export interface ZeekNoticeData { - suppress_for?: number[]; - msg?: string[]; - note?: string[]; - sub?: string[]; - dst?: string[]; - dropped?: boolean[]; - peer_descr?: string[]; -} - -export interface ZeekDnsData { - AA?: boolean[]; - qclass_name?: string[]; - RD?: boolean[]; - qtype_name?: string[]; - rejected?: boolean[]; - qtype?: string[]; - query?: string[]; - trans_id?: number[]; - qclass?: string[]; - RA?: boolean[]; - TC?: boolean[]; -} - -export interface ZeekHttpData { - resp_mime_types?: string[]; - trans_depth?: string[]; - status_msg?: string[]; - resp_fuids?: string[]; - tags?: string[]; -} - -export interface ZeekFileData { - session_ids?: string[]; - timedout?: boolean[]; - local_orig?: boolean[]; - tx_host?: string[]; - source?: string[]; - is_orig?: boolean[]; - overflow_bytes?: number[]; - sha1?: string[]; - duration?: number[]; - depth?: number[]; - analyzers?: string[]; - mime_type?: string[]; - rx_host?: string[]; - total_bytes?: number[]; - fuid?: string[]; - seen_bytes?: number[]; - missing_bytes?: number[]; - md5?: string[]; -} - -export interface ZeekSslData { - cipher?: string[]; - established?: boolean[]; - resumed?: boolean[]; - version?: string[]; -} diff --git a/x-pack/plugins/osquery/cypress/e2e/all/ecs_mappings.cy.ts b/x-pack/plugins/osquery/cypress/e2e/all/ecs_mappings.cy.ts index 9471ca8311737..352b47d634104 100644 --- a/x-pack/plugins/osquery/cypress/e2e/all/ecs_mappings.cy.ts +++ b/x-pack/plugins/osquery/cypress/e2e/all/ecs_mappings.cy.ts @@ -19,12 +19,12 @@ import { } from '../../tasks/live_query'; describe('EcsMapping', () => { - before(() => { + beforeEach(() => { login(ROLE.soc_manager); - navigateTo('/app/osquery'); }); it('should properly show static values in form and results', () => { + navigateTo('/app/osquery'); cy.contains('New live query').click(); selectAllAgents(); inputQuery('select * from processes;'); @@ -50,4 +50,22 @@ describe('EcsMapping', () => { cy.contains('[ "test2" ]'); cy.contains('test3'); }); + + it('should hide and show ecs mappings on Advanced accordion click', () => { + navigateTo('/app/osquery'); + cy.contains('New live query').click(); + selectAllAgents(); + cy.getBySel('savedQuerySelect').within(() => { + cy.getBySel('comboBoxInput').type('processes_elastic{downArrow}{enter}'); + }); + cy.react('EuiAccordionClass', { + props: { buttonContent: 'Advanced', forceState: 'open' }, + }).should('exist'); + cy.getBySel('advanced-accordion-content').within(() => { + cy.contains('Advanced').click(); + }); + cy.react('EuiAccordionClass', { + props: { buttonContent: 'Advanced', forceState: 'closed' }, + }).should('exist'); + }); }); diff --git a/x-pack/plugins/osquery/public/common/contexts.tsx b/x-pack/plugins/osquery/public/common/contexts.tsx index f0967f9c94d44..e8aa3d2966bdb 100644 --- a/x-pack/plugins/osquery/public/common/contexts.tsx +++ b/x-pack/plugins/osquery/public/common/contexts.tsx @@ -6,6 +6,6 @@ */ import React from 'react'; -import type { Ecs } from '../../common/ecs'; +import type { EcsSecurityExtension } from '@kbn/securitysolution-ecs'; -export const AlertAttachmentContext = React.createContext(null); +export const AlertAttachmentContext = React.createContext(null); diff --git a/x-pack/plugins/osquery/public/live_queries/form/live_query_query_field.tsx b/x-pack/plugins/osquery/public/live_queries/form/live_query_query_field.tsx index 05f9e947fe841..7a707d1a4f4c8 100644 --- a/x-pack/plugins/osquery/public/live_queries/form/live_query_query_field.tsx +++ b/x-pack/plugins/osquery/public/live_queries/form/live_query_query_field.tsx @@ -8,10 +8,11 @@ import { isEmpty } from 'lodash'; import type { EuiAccordionProps } from '@elastic/eui'; import { EuiCodeBlock, EuiFormRow, EuiAccordion, EuiSpacer } from '@elastic/eui'; -import React, { useCallback, useEffect, useMemo, useState } from 'react'; +import React, { useCallback, useMemo, useState } from 'react'; import styled from 'styled-components'; import { useController, useFormContext } from 'react-hook-form'; import { i18n } from '@kbn/i18n'; +import type { LiveQueryFormFields } from '.'; import { OsqueryEditor } from '../../editor'; import { useKibana } from '../../common/lib/kibana'; import { ECSMappingEditorField } from '../../packs/queries/lazy_ecs_mapping_editor_field'; @@ -23,6 +24,9 @@ const StyledEuiAccordion = styled(EuiAccordion)` .euiAccordion__button { color: ${({ theme }) => theme.eui.euiColorPrimary}; } + .euiAccordion__childWrapper { + -webkit-transition: none; + } `; const StyledEuiCodeBlock = styled(EuiCodeBlock)` @@ -38,12 +42,12 @@ const LiveQueryQueryFieldComponent: React.FC = ({ disabled, handleSubmitForm, }) => { - const { watch, resetField } = useFormContext(); - const [advancedContentState, setAdvancedContentState] = - useState('closed'); + const { formState, watch, resetField } = useFormContext(); + const [advancedContentState, setAdvancedContentState] = useState( + () => (isEmpty(formState.defaultValues?.ecs_mapping) ? 'closed' : 'open') + ); const permissions = useKibana().services.application.capabilities.osquery; - const [ecsMapping, queryType] = watch(['ecs_mapping', 'queryType']); - + const [queryType] = watch(['queryType']); const { field: { onChange, value }, fieldState: { error }, @@ -60,12 +64,6 @@ const LiveQueryQueryFieldComponent: React.FC = ({ defaultValue: '', }); - useEffect(() => { - if (!isEmpty(ecsMapping) && advancedContentState === 'closed') { - setAdvancedContentState('open'); - } - }, [advancedContentState, ecsMapping]); - const handleSavedQueryChange: SavedQueriesDropdownProps['onChange'] = useCallback( (savedQuery) => { if (savedQuery) { @@ -155,6 +153,7 @@ const LiveQueryQueryFieldComponent: React.FC = ({ forceState={advancedContentState} onToggle={handleToggle} buttonContent="Advanced" + data-test-subj="advanced-accordion-content" > diff --git a/x-pack/plugins/osquery/public/shared_components/attachments/external_reference.tsx b/x-pack/plugins/osquery/public/shared_components/attachments/external_reference.tsx index 51bfc6ab1c22f..7a1f8fdbacbed 100644 --- a/x-pack/plugins/osquery/public/shared_components/attachments/external_reference.tsx +++ b/x-pack/plugins/osquery/public/shared_components/attachments/external_reference.tsx @@ -8,6 +8,7 @@ import { EuiAvatar } from '@elastic/eui'; import React from 'react'; import type { ExternalReferenceAttachmentType } from '@kbn/cases-plugin/public/client/attachment_framework/types'; +import { CASE_ATTACHMENT_TYPE_ID } from '../../../common/constants'; import { getLazyExternalContent } from './lazy_external_reference_content'; import type { ServicesWrapperProps } from '../services_wrapper'; import OsqueryLogo from '../../components/osquery_icon/osquery.svg'; @@ -15,7 +16,7 @@ import OsqueryLogo from '../../components/osquery_icon/osquery.svg'; export const getExternalReferenceAttachmentRegular = ( services: ServicesWrapperProps['services'] ): ExternalReferenceAttachmentType => ({ - id: 'osquery', + id: CASE_ATTACHMENT_TYPE_ID, displayName: 'Osquery', getAttachmentViewObject: () => ({ type: 'regular', diff --git a/x-pack/plugins/osquery/public/shared_components/lazy_osquery_action.tsx b/x-pack/plugins/osquery/public/shared_components/lazy_osquery_action.tsx index 968e43cea5477..bd8f0e64d2d17 100644 --- a/x-pack/plugins/osquery/public/shared_components/lazy_osquery_action.tsx +++ b/x-pack/plugins/osquery/public/shared_components/lazy_osquery_action.tsx @@ -6,7 +6,7 @@ */ import React, { lazy, Suspense, useMemo } from 'react'; -import type { Ecs } from '../../common/ecs'; +import type { EcsSecurityExtension } from '@kbn/securitysolution-ecs'; import ServicesWrapper from './services_wrapper'; import type { ServicesWrapperProps } from './services_wrapper'; import type { OsqueryActionProps } from './osquery_action'; @@ -16,7 +16,7 @@ const OsqueryAction = lazy(() => import('./osquery_action')); export const getLazyOsqueryAction = (services: ServicesWrapperProps['services']) => // eslint-disable-next-line react/display-name - (props: OsqueryActionProps & { ecsData?: Ecs }) => { + (props: OsqueryActionProps & { ecsData?: EcsSecurityExtension }) => { const { ecsData, ...restProps } = props; const renderAction = useMemo(() => { if (ecsData && ecsData?._id) { diff --git a/x-pack/plugins/osquery/public/shared_components/osquery_results/types.ts b/x-pack/plugins/osquery/public/shared_components/osquery_results/types.ts index 54ae88ddc6c9d..b1c613fe032f9 100644 --- a/x-pack/plugins/osquery/public/shared_components/osquery_results/types.ts +++ b/x-pack/plugins/osquery/public/shared_components/osquery_results/types.ts @@ -5,11 +5,11 @@ * 2.0. */ -import type { Ecs } from '../../../common/ecs'; +import type { EcsSecurityExtension } from '@kbn/securitysolution-ecs'; import type { ActionEdges } from '../../../common/search_strategy'; export interface OsqueryActionResultsProps { ruleName?: string[]; - ecsData: Ecs; + ecsData: EcsSecurityExtension; actionItems?: ActionEdges; } diff --git a/x-pack/plugins/osquery/server/plugin.ts b/x-pack/plugins/osquery/server/plugin.ts index b1178285b9f17..9c74329ef2a69 100644 --- a/x-pack/plugins/osquery/server/plugin.ts +++ b/x-pack/plugins/osquery/server/plugin.ts @@ -42,6 +42,7 @@ import { createDataViews } from './create_data_views'; import { createActionHandler } from './handlers/action'; import { registerFeatures } from './utils/register_features'; +import { CASE_ATTACHMENT_TYPE_ID } from '../common/constants'; export class OsqueryPlugin implements Plugin { private readonly logger: Logger; @@ -93,6 +94,8 @@ export class OsqueryPlugin implements Plugin { + describe('getCategoryColor', () => { + const categories = [ + { category: 'elasticsearch', expectedColor: '#D6BF57' }, + { category: 'metricbeat', expectedColor: '#B9A888' }, + { category: 'auditbeat', expectedColor: '#E7664C' }, + { category: 'dockerd', expectedColor: '#B9A888' }, + { category: 'Other', expectedColor: '#CA8EAE' }, + { category: 'node', expectedColor: '#D36086' }, + { category: 'filebeat', expectedColor: '#54B399' }, + { category: 'containerd', expectedColor: '#DA8B45' }, + { category: 'C2 CompilerThre', expectedColor: '#6092C0' }, + { category: '[metrics]>worke', expectedColor: '#D6BF57' }, + ]; + const colors = euiPaletteColorBlind({ + rotations: Math.ceil(categories.length / 10), + }); + categories.map(({ category, expectedColor }) => { + it(`returns correct color for category ${category}`, () => { + expect(getCategoryColor({ category, subChartSize: categories.length, colors })).toEqual( + expectedColor + ); + }); + }); + }); +}); diff --git a/x-pack/plugins/profiling/common/topn.ts b/x-pack/plugins/profiling/common/topn.ts index 70d0f38624559..41d820729b48f 100644 --- a/x-pack/plugins/profiling/common/topn.ts +++ b/x-pack/plugins/profiling/common/topn.ts @@ -220,6 +220,31 @@ export interface TopNSubchart { Metadata: StackFrameMetadata[]; } +export function getCategoryColor({ + category, + subChartSize, + colors, +}: { + category: string; + subChartSize: number; + colors: ReturnType; +}) { + // We want the mapping from the category string to the color to be constant, + // so that the same category string will always map to the same color. + const stringhash = (s: string): number => { + let hash: number = 0; + for (let i = 0; i < s.length; i++) { + const ch = s.charCodeAt(i); + hash = (hash << 5) - hash + ch; // eslint-disable-line no-bitwise + // Apply bit mask to ensure positive value. + hash &= 0x7fffffff; // eslint-disable-line no-bitwise + } + return hash % subChartSize; + }; + + return colors[stringhash(category)]; +} + export function groupSamplesByCategory({ samples, totalCount, @@ -264,22 +289,10 @@ export function groupSamplesByCategory({ rotations: Math.ceil(subcharts.length / 10), }); - // We want the mapping from the category string to the color to be constant, - // so that the same category string will always map to the same color. - const stringhash = (s: string): number => { - let hash: number = 0; - for (let i = 0; i < s.length; i++) { - const ch = s.charCodeAt(i); - hash = (hash << 5) - hash + ch; // eslint-disable-line no-bitwise - hash &= hash; // eslint-disable-line no-bitwise - } - return hash % subcharts.length; - }; - return orderBy(subcharts, ['Percentage', 'Category'], ['desc', 'asc']).map((chart, index) => { return { ...chart, - Color: colors[stringhash(chart.Category)], + Color: getCategoryColor({ category: chart.Category, colors, subChartSize: subcharts.length }), Index: index + 1, Series: chart.Series.map((value) => { return { diff --git a/x-pack/plugins/profiling/public/components/profiling_app_page_template/index.tsx b/x-pack/plugins/profiling/public/components/profiling_app_page_template/index.tsx index 9ae0a455e15f1..38a6f379643ed 100644 --- a/x-pack/plugins/profiling/public/components/profiling_app_page_template/index.tsx +++ b/x-pack/plugins/profiling/public/components/profiling_app_page_template/index.tsx @@ -82,6 +82,7 @@ export function ProfilingAppPageTemplate({
), + tabs, }} restrictWidth={restrictWidth} pageSectionProps={{ diff --git a/x-pack/plugins/profiling/public/components/stack_traces_view/index.tsx b/x-pack/plugins/profiling/public/components/stack_traces_view/index.tsx index 849a927d649a9..c6f21db870801 100644 --- a/x-pack/plugins/profiling/public/components/stack_traces_view/index.tsx +++ b/x-pack/plugins/profiling/public/components/stack_traces_view/index.tsx @@ -6,9 +6,9 @@ */ import { EuiButton, EuiButtonGroup, EuiFlexGroup, EuiFlexItem, EuiPanel } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; -import React, { useState } from 'react'; +import React from 'react'; import { StackTracesDisplayOption, TopNType } from '../../../common/stack_traces'; -import { groupSamplesByCategory, TopNResponse, TopNSample } from '../../../common/topn'; +import { groupSamplesByCategory, TopNResponse } from '../../../common/topn'; import { useProfilingParams } from '../../hooks/use_profiling_params'; import { useProfilingRouter } from '../../hooks/use_profiling_router'; import { useProfilingRoutePath } from '../../hooks/use_profiling_route_path'; @@ -78,13 +78,6 @@ export function StackTracesView() { [topNType, timeRange.start, timeRange.end, fetchTopN, kuery] ); - const [highlightedSample, setHighlightedSample] = useState(null); - - const highlightedSubchart = - (highlightedSample && - state.data?.charts.find((chart) => chart.Category === highlightedSample?.Category)) || - null; - const { data } = state; return ( @@ -146,14 +139,6 @@ export function StackTracesView() { }, }); }} - onSampleOver={(sample) => { - setHighlightedSample(sample); - }} - onSampleOut={() => { - setHighlightedSample(null); - }} - highlightedSample={highlightedSample} - highlightedSubchart={highlightedSubchart} showFrames={topNType === TopNType.Traces} /> diff --git a/x-pack/plugins/profiling/public/components/stacked_bar_chart.tsx b/x-pack/plugins/profiling/public/components/stacked_bar_chart.tsx index ac74141f19040..ec7bb9af5b810 100644 --- a/x-pack/plugins/profiling/public/components/stacked_bar_chart.tsx +++ b/x-pack/plugins/profiling/public/components/stacked_bar_chart.tsx @@ -15,61 +15,26 @@ import { StackMode, timeFormatter, Tooltip, - TooltipInfo, XYChartElementEvent, - CustomTooltip, TooltipContainer, + TooltipInfo, } from '@elastic/charts'; import { EuiPanel } from '@elastic/eui'; -import React from 'react'; +import { keyBy } from 'lodash'; +import React, { useMemo, useState } from 'react'; import { TopNSample, TopNSubchart } from '../../common/topn'; import { useKibanaTimeZoneSetting } from '../hooks/use_kibana_timezone_setting'; import { useProfilingChartsTheme } from '../hooks/use_profiling_charts_theme'; import { asPercentage } from '../utils/formatters/as_percentage'; import { SubChart } from './subchart'; -const SubchartTooltip = ({ - highlightedSubchart, - highlightedSample, - showFrames, -}: TooltipInfo & { - highlightedSubchart: TopNSubchart; - highlightedSample: TopNSample | null; - showFrames: boolean; -}) => { - // max tooltip width - 2 * padding (16px) - const width = 224; - return ( - - - - ); -}; +// 2 * padding (16px) +const MAX_TOOLTIP_WIDTH = 224; export interface StackedBarChartProps { height: number; asPercentages: boolean; onBrushEnd: (range: { rangeFrom: string; rangeTo: string }) => void; - onSampleOver: (sample: TopNSample) => void; - onSampleOut: () => void; - highlightedSample: TopNSample | null; - highlightedSubchart: TopNSubchart | null; charts: TopNSubchart[]; showFrames: boolean; } @@ -78,29 +43,52 @@ export const StackedBarChart: React.FC = ({ height, asPercentages, onBrushEnd, - onSampleOut, - onSampleOver, - highlightedSample, - highlightedSubchart, charts, showFrames, }) => { + const chartsbyCategoryMap = useMemo(() => { + return keyBy(charts, 'Category'); + }, [charts]); + const timeZone = useKibanaTimeZoneSetting(); + const [highlightedSample, setHighlightedSample] = useState(); const { chartsBaseTheme, chartsTheme } = useProfilingChartsTheme(); - const customTooltip: CustomTooltip = highlightedSubchart - ? (props) => ( - - + + - - ) - : () => <>; + + + ); + } return ( @@ -117,13 +105,13 @@ export const StackedBarChart: React.FC = ({ theme={chartsTheme} onElementOver={(events) => { const [value] = events[0] as XYChartElementEvent; - onSampleOver(value.datum as TopNSample); + setHighlightedSample(value.datum as TopNSample); }} onElementOut={() => { - onSampleOut(); + setHighlightedSample(undefined); }} /> - + {charts.map((chart) => ( { await generateCsv.generateData(); + expect(mockEsClient.asCurrentUser.openPointInTime).toHaveBeenCalledWith( + { + ignore_unavailable: true, + ignore_throttled: false, + index: 'logstash-*', + keep_alive: '30s', + }, + { maxRetries: 0, requestTimeout: '30s' } + ); + expect(mockDataClient.search).toBeCalledWith( { params: { body: {}, - ignore_throttled: false, }, }, { strategy: 'es', transport: { maxRetries: 0, requestTimeout: '30s' } } diff --git a/x-pack/plugins/reporting/server/export_types/csv_searchsource/generate_csv/generate_csv.ts b/x-pack/plugins/reporting/server/export_types/csv_searchsource/generate_csv/generate_csv.ts index e5b38430f9fdc..44438a3208624 100644 --- a/x-pack/plugins/reporting/server/export_types/csv_searchsource/generate_csv/generate_csv.ts +++ b/x-pack/plugins/reporting/server/export_types/csv_searchsource/generate_csv/generate_csv.ts @@ -66,6 +66,12 @@ export class CsvGenerator { index: indexPatternTitle, keep_alive: duration, ignore_unavailable: true, + // TODO: currently this doesn't do anything as es has a bug that throttled indices are always included when using PIT api + // if es fixes the issue, everything should work as expected. Just needs to be tested and this comment removed + // if es decides to not fix, then we can close the issue and remove the `ignore_throttled` code from here + // https://github.com/elastic/kibana/issues/152884 + // @ts-expect-error ignore_throttled is not in the type definition, but it is accepted by es + ignore_throttled: settings.includeFrozen ? false : undefined, // "true" will cause deprecation warnings logged in ES }, { requestTimeout: duration, @@ -91,7 +97,7 @@ export class CsvGenerator { settings: CsvExportSettings, searchAfter?: estypes.SortResults ) { - const { scroll: scrollSettings, includeFrozen } = settings; + const { scroll: scrollSettings } = settings; searchSource.setField('size', scrollSettings.size); if (searchAfter) { @@ -112,7 +118,6 @@ export class CsvGenerator { const searchParams = { params: { body: searchBody, - ignore_throttled: includeFrozen ? false : undefined, // "true" will cause deprecation warnings logged in ES }, }; diff --git a/x-pack/plugins/rule_registry/common/assets/field_maps/experimental_rule_field_map.test.ts b/x-pack/plugins/rule_registry/common/assets/field_maps/experimental_rule_field_map.test.ts deleted file mode 100644 index 3a6dbc4f20982..0000000000000 --- a/x-pack/plugins/rule_registry/common/assets/field_maps/experimental_rule_field_map.test.ts +++ /dev/null @@ -1,27 +0,0 @@ -/* - * 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 { experimentalRuleFieldMap } from './experimental_rule_field_map'; - -// This test purely exists to see what the resultant mappings are and -// make it obvious when some dependency results in the mappings changing -it('matches snapshot', () => { - expect(experimentalRuleFieldMap).toMatchInlineSnapshot(` - Object { - "kibana.alert.evaluation.threshold": Object { - "required": false, - "scaling_factor": 100, - "type": "scaled_float", - }, - "kibana.alert.evaluation.value": Object { - "required": false, - "scaling_factor": 100, - "type": "scaled_float", - }, - } - `); -}); diff --git a/x-pack/plugins/rule_registry/common/assets/field_maps/experimental_rule_field_map.ts b/x-pack/plugins/rule_registry/common/assets/field_maps/experimental_rule_field_map.ts deleted file mode 100644 index 3859ebe6df9b6..0000000000000 --- a/x-pack/plugins/rule_registry/common/assets/field_maps/experimental_rule_field_map.ts +++ /dev/null @@ -1,19 +0,0 @@ -/* - * 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 * as Fields from '../../technical_rule_data_field_names'; - -export const experimentalRuleFieldMap = { - [Fields.ALERT_EVALUATION_THRESHOLD]: { - type: 'scaled_float', - scaling_factor: 100, - required: false, - }, - [Fields.ALERT_EVALUATION_VALUE]: { type: 'scaled_float', scaling_factor: 100, required: false }, -} as const; - -export type ExperimentalRuleFieldMap = typeof experimentalRuleFieldMap; diff --git a/x-pack/plugins/rule_registry/common/parse_experimental_fields.ts b/x-pack/plugins/rule_registry/common/parse_experimental_fields.ts index 4347a7e5b8d48..77db88f2f9766 100644 --- a/x-pack/plugins/rule_registry/common/parse_experimental_fields.ts +++ b/x-pack/plugins/rule_registry/common/parse_experimental_fields.ts @@ -7,19 +7,17 @@ import { isLeft } from 'fp-ts/lib/Either'; import { PathReporter } from 'io-ts/lib/PathReporter'; import { pick } from 'lodash'; -import { - experimentalRuleFieldMap, - ExperimentalRuleFieldMap, -} from './assets/field_maps/experimental_rule_field_map'; +import { legacyExperimentalFieldMap, ExperimentalRuleFieldMap } from '@kbn/alerts-as-data-utils'; import { runtimeTypeFromFieldMap } from './field_map'; -const experimentalFieldRuntimeType = - runtimeTypeFromFieldMap(experimentalRuleFieldMap); +const experimentalFieldRuntimeType = runtimeTypeFromFieldMap( + legacyExperimentalFieldMap +); export const parseExperimentalFields = (input: unknown, partial = false) => { const decodePartial = (alert: unknown) => { - const limitedFields = pick(experimentalRuleFieldMap, Object.keys(alert as object)); + const limitedFields = pick(legacyExperimentalFieldMap, Object.keys(alert as object)); const partialTechnicalFieldRuntimeType = runtimeTypeFromFieldMap( limitedFields as unknown as ExperimentalRuleFieldMap ); diff --git a/x-pack/plugins/rule_registry/server/plugin.ts b/x-pack/plugins/rule_registry/server/plugin.ts index 8b5c754e5b908..12fec80f53bb6 100644 --- a/x-pack/plugins/rule_registry/server/plugin.ts +++ b/x-pack/plugins/rule_registry/server/plugin.ts @@ -110,7 +110,7 @@ export class RuleRegistryPlugin const deps = await startDependencies; return deps.core.elasticsearch.client.asInternalUser; }, - areFrameworkAlertsEnabled: plugins.alerting.getFrameworkAlertsEnabled(), + frameworkAlerts: plugins.alerting.frameworkAlerts, pluginStop$: this.pluginStop$, }); diff --git a/x-pack/plugins/rule_registry/server/rule_data_plugin_service/resource_installer.test.ts b/x-pack/plugins/rule_registry/server/rule_data_plugin_service/resource_installer.test.ts index 083c4d08d4253..1b4668bb2fe80 100644 --- a/x-pack/plugins/rule_registry/server/rule_data_plugin_service/resource_installer.test.ts +++ b/x-pack/plugins/rule_registry/server/rule_data_plugin_service/resource_installer.test.ts @@ -16,6 +16,11 @@ import { ECS_COMPONENT_TEMPLATE_NAME } from '@kbn/alerting-plugin/server'; import { elasticsearchServiceMock, ElasticsearchClientMock } from '@kbn/core/server/mocks'; import { TECHNICAL_COMPONENT_TEMPLATE_NAME } from '../../common/assets'; +const frameworkAlertsService = { + enabled: () => false, + getContextInitializationPromise: async () => ({ result: false, error: `failed` }), +}; + describe('resourceInstaller', () => { let pluginStop$: Subject; @@ -38,7 +43,7 @@ describe('resourceInstaller', () => { disabledRegistrationContexts: [], getResourceName: jest.fn(), getClusterClient, - areFrameworkAlertsEnabled: false, + frameworkAlerts: frameworkAlertsService, pluginStop$, }); installer.installCommonResources(); @@ -55,7 +60,7 @@ describe('resourceInstaller', () => { disabledRegistrationContexts: [], getResourceName: jest.fn(), getClusterClient, - areFrameworkAlertsEnabled: false, + frameworkAlerts: frameworkAlertsService, pluginStop$, }); const indexOptions = { @@ -86,7 +91,7 @@ describe('resourceInstaller', () => { disabledRegistrationContexts: [], getResourceName: jest.fn(), getClusterClient, - areFrameworkAlertsEnabled: false, + frameworkAlerts: frameworkAlertsService, pluginStop$, }); @@ -113,7 +118,10 @@ describe('resourceInstaller', () => { disabledRegistrationContexts: [], getResourceName: jest.fn(), getClusterClient, - areFrameworkAlertsEnabled: true, + frameworkAlerts: { + ...frameworkAlertsService, + enabled: () => true, + }, pluginStop$, }); @@ -128,6 +136,7 @@ describe('resourceInstaller', () => { expect.objectContaining({ name: TECHNICAL_COMPONENT_TEMPLATE_NAME }) ); }); + it('should install index level resources', async () => { const mockClusterClient = elasticsearchServiceMock.createElasticsearchClient(); const getClusterClient = jest.fn(() => Promise.resolve(mockClusterClient)); @@ -137,7 +146,7 @@ describe('resourceInstaller', () => { disabledRegistrationContexts: [], getResourceName: jest.fn(), getClusterClient, - areFrameworkAlertsEnabled: false, + frameworkAlerts: frameworkAlertsService, pluginStop$, }); @@ -159,6 +168,265 @@ describe('resourceInstaller', () => { expect.objectContaining({ name: '.alerts-observability.logs.alerts-mappings' }) ); }); + + it('should not install index level component template when framework alerts are enabled', async () => { + const mockClusterClient = elasticsearchServiceMock.createElasticsearchClient(); + const getClusterClient = jest.fn(() => Promise.resolve(mockClusterClient)); + const installer = new ResourceInstaller({ + logger: loggerMock.create(), + isWriteEnabled: true, + disabledRegistrationContexts: [], + getResourceName: jest.fn(), + getClusterClient, + frameworkAlerts: { + ...frameworkAlertsService, + enabled: () => true, + }, + pluginStop$, + }); + + const indexOptions = { + feature: AlertConsumers.LOGS, + registrationContext: 'observability.logs', + dataset: Dataset.alerts, + componentTemplateRefs: [], + componentTemplates: [ + { + name: 'mappings', + }, + ], + }; + const indexInfo = new IndexInfo({ indexOptions, kibanaVersion: '8.1.0' }); + + await installer.installIndexLevelResources(indexInfo); + expect(mockClusterClient.cluster.putComponentTemplate).not.toHaveBeenCalled(); + }); + + it('should install namespace level resources for the default space', async () => { + const mockClusterClient = elasticsearchServiceMock.createElasticsearchClient(); + mockClusterClient.indices.simulateTemplate.mockImplementation(async () => ({ + template: { + aliases: { + alias_name_1: { + is_hidden: true, + }, + alias_name_2: { + is_hidden: true, + }, + }, + mappings: { enabled: false }, + settings: {}, + }, + })); + mockClusterClient.indices.getAlias.mockImplementation(async () => ({ + real_index: { + aliases: { + alias_1: { + is_hidden: true, + }, + alias_2: { + is_hidden: true, + }, + }, + }, + })); + const getClusterClient = jest.fn(() => Promise.resolve(mockClusterClient)); + const installer = new ResourceInstaller({ + logger: loggerMock.create(), + isWriteEnabled: true, + disabledRegistrationContexts: [], + getResourceName: jest.fn(), + getClusterClient, + frameworkAlerts: frameworkAlertsService, + pluginStop$, + }); + + const indexOptions = { + feature: AlertConsumers.LOGS, + registrationContext: 'observability.logs', + dataset: Dataset.alerts, + componentTemplateRefs: [], + componentTemplates: [ + { + name: 'mappings', + }, + ], + }; + const indexInfo = new IndexInfo({ indexOptions, kibanaVersion: '8.1.0' }); + + await installer.installAndUpdateNamespaceLevelResources(indexInfo, 'default'); + expect(mockClusterClient.indices.simulateTemplate).toHaveBeenCalledWith( + expect.objectContaining({ + name: '.alerts-observability.logs.alerts-default-index-template', + }) + ); + expect(mockClusterClient.indices.putIndexTemplate).toHaveBeenCalledWith( + expect.objectContaining({ + name: '.alerts-observability.logs.alerts-default-index-template', + }) + ); + expect(mockClusterClient.indices.getAlias).toHaveBeenCalledWith( + expect.objectContaining({ name: '.alerts-observability.logs.alerts-*' }) + ); + expect(mockClusterClient.indices.create).toHaveBeenCalledWith( + expect.objectContaining({ + index: '.internal.alerts-observability.logs.alerts-default-000001', + }) + ); + }); + + it('should not install namespace level resources for the default space when framework alerts are available', async () => { + const mockClusterClient = elasticsearchServiceMock.createElasticsearchClient(); + const getClusterClient = jest.fn(() => Promise.resolve(mockClusterClient)); + const installer = new ResourceInstaller({ + logger: loggerMock.create(), + isWriteEnabled: true, + disabledRegistrationContexts: [], + getResourceName: jest.fn(), + getClusterClient, + frameworkAlerts: { + ...frameworkAlertsService, + enabled: () => true, + getContextInitializationPromise: async () => ({ result: true }), + }, + pluginStop$, + }); + + const indexOptions = { + feature: AlertConsumers.LOGS, + registrationContext: 'observability.logs', + dataset: Dataset.alerts, + componentTemplateRefs: [], + componentTemplates: [ + { + name: 'mappings', + }, + ], + }; + const indexInfo = new IndexInfo({ indexOptions, kibanaVersion: '8.1.0' }); + + await installer.installAndUpdateNamespaceLevelResources(indexInfo, 'default'); + expect(mockClusterClient.indices.simulateTemplate).not.toHaveBeenCalled(); + expect(mockClusterClient.indices.putIndexTemplate).not.toHaveBeenCalled(); + expect(mockClusterClient.indices.getAlias).not.toHaveBeenCalled(); + expect(mockClusterClient.indices.create).not.toHaveBeenCalled(); + }); + + it('should throw error if framework was unable to install namespace level resources', async () => { + const mockClusterClient = elasticsearchServiceMock.createElasticsearchClient(); + const getClusterClient = jest.fn(() => Promise.resolve(mockClusterClient)); + const installer = new ResourceInstaller({ + logger: loggerMock.create(), + isWriteEnabled: true, + disabledRegistrationContexts: [], + getResourceName: jest.fn(), + getClusterClient, + frameworkAlerts: { + ...frameworkAlertsService, + enabled: () => true, + }, + pluginStop$, + }); + + const indexOptions = { + feature: AlertConsumers.LOGS, + registrationContext: 'observability.logs', + dataset: Dataset.alerts, + componentTemplateRefs: [], + componentTemplates: [ + { + name: 'mappings', + }, + ], + }; + const indexInfo = new IndexInfo({ indexOptions, kibanaVersion: '8.1.0' }); + + await expect( + installer.installAndUpdateNamespaceLevelResources(indexInfo, 'default') + ).rejects.toThrowErrorMatchingInlineSnapshot( + `"There was an error in the framework installing namespace-level resources and creating concrete indices for .alerts-observability.logs.alerts-default - failed"` + ); + expect(mockClusterClient.indices.simulateTemplate).not.toHaveBeenCalled(); + expect(mockClusterClient.indices.putIndexTemplate).not.toHaveBeenCalled(); + expect(mockClusterClient.indices.getAlias).not.toHaveBeenCalled(); + expect(mockClusterClient.indices.create).not.toHaveBeenCalled(); + }); + + it('should install namespace level resources for non-default space even when framework alerts are available', async () => { + const mockClusterClient = elasticsearchServiceMock.createElasticsearchClient(); + mockClusterClient.indices.simulateTemplate.mockImplementation(async () => ({ + template: { + aliases: { + alias_name_1: { + is_hidden: true, + }, + alias_name_2: { + is_hidden: true, + }, + }, + mappings: { enabled: false }, + settings: {}, + }, + })); + mockClusterClient.indices.getAlias.mockImplementation(async () => ({ + real_index: { + aliases: { + alias_1: { + is_hidden: true, + }, + alias_2: { + is_hidden: true, + }, + }, + }, + })); + const getClusterClient = jest.fn(() => Promise.resolve(mockClusterClient)); + const installer = new ResourceInstaller({ + logger: loggerMock.create(), + isWriteEnabled: true, + disabledRegistrationContexts: [], + getResourceName: jest.fn(), + getClusterClient, + frameworkAlerts: { + ...frameworkAlertsService, + enabled: () => true, + }, + pluginStop$, + }); + + const indexOptions = { + feature: AlertConsumers.LOGS, + registrationContext: 'observability.logs', + dataset: Dataset.alerts, + componentTemplateRefs: [], + componentTemplates: [ + { + name: 'mappings', + }, + ], + }; + const indexInfo = new IndexInfo({ indexOptions, kibanaVersion: '8.1.0' }); + + await installer.installAndUpdateNamespaceLevelResources(indexInfo, 'my-staging-space'); + expect(mockClusterClient.indices.simulateTemplate).toHaveBeenCalledWith( + expect.objectContaining({ + name: '.alerts-observability.logs.alerts-my-staging-space-index-template', + }) + ); + expect(mockClusterClient.indices.putIndexTemplate).toHaveBeenCalledWith( + expect.objectContaining({ + name: '.alerts-observability.logs.alerts-my-staging-space-index-template', + }) + ); + expect(mockClusterClient.indices.getAlias).toHaveBeenCalledWith( + expect.objectContaining({ name: '.alerts-observability.logs.alerts-*' }) + ); + expect(mockClusterClient.indices.create).toHaveBeenCalledWith( + expect.objectContaining({ + index: '.internal.alerts-observability.logs.alerts-my-staging-space-000001', + }) + ); + }); }); // These tests only test the updateAliasWriteIndexMapping() @@ -209,7 +477,7 @@ describe('resourceInstaller', () => { disabledRegistrationContexts: [], getResourceName: jest.fn(), getClusterClient: async () => mockClusterClient, - areFrameworkAlertsEnabled: false, + frameworkAlerts: frameworkAlertsService, pluginStop$, }; const indexOptions = { diff --git a/x-pack/plugins/rule_registry/server/rule_data_plugin_service/resource_installer.ts b/x-pack/plugins/rule_registry/server/rule_data_plugin_service/resource_installer.ts index 59c74b81712d8..266b548e01c06 100644 --- a/x-pack/plugins/rule_registry/server/rule_data_plugin_service/resource_installer.ts +++ b/x-pack/plugins/rule_registry/server/rule_data_plugin_service/resource_installer.ts @@ -16,6 +16,7 @@ import { DEFAULT_ALERTS_ILM_POLICY, DEFAULT_ALERTS_ILM_POLICY_NAME, ECS_COMPONENT_TEMPLATE_NAME, + type PublicFrameworkAlertsService, } from '@kbn/alerting-plugin/server'; import { TECHNICAL_COMPONENT_TEMPLATE_NAME } from '../../common/assets'; import { technicalComponentTemplate } from '../../common/assets/component_templates/technical_component_template'; @@ -31,7 +32,7 @@ interface ConstructorOptions { logger: Logger; isWriteEnabled: boolean; disabledRegistrationContexts: string[]; - areFrameworkAlertsEnabled: boolean; + frameworkAlerts: PublicFrameworkAlertsService; pluginStop$: Observable; } @@ -96,14 +97,14 @@ export class ResourceInstaller { */ public async installCommonResources(): Promise { await this.installWithTimeout('common resources shared between all indices', async () => { - const { logger, areFrameworkAlertsEnabled } = this.options; + const { logger, frameworkAlerts } = this.options; try { // We can install them in parallel await Promise.all([ - // Install ILM policy only if framework alerts are not enabled - // If framework alerts are enabled, the alerting framework will install this ILM policy - ...(areFrameworkAlertsEnabled + // Install ILM policy and ECS component template only if framework alerts are not enabled + // If framework alerts are enabled, the alerting framework will install these + ...(frameworkAlerts.enabled() ? [] : [ this.createOrUpdateLifecyclePolicy({ @@ -139,7 +140,8 @@ export class ResourceInstaller { */ public async installIndexLevelResources(indexInfo: IndexInfo): Promise { await this.installWithTimeout(`resources for index ${indexInfo.baseName}`, async () => { - const { componentTemplates, ilmPolicy } = indexInfo.indexOptions; + const { frameworkAlerts } = this.options; + const { componentTemplates, ilmPolicy, additionalPrefix } = indexInfo.indexOptions; if (ilmPolicy != null) { await this.createOrUpdateLifecyclePolicy({ name: indexInfo.getIlmPolicyName(), @@ -147,20 +149,22 @@ export class ResourceInstaller { }); } - await Promise.all( - componentTemplates.map(async (ct) => { - await this.createOrUpdateComponentTemplate({ - name: indexInfo.getComponentTemplateName(ct.name), - body: { - template: { - settings: ct.settings ?? {}, - mappings: ct.mappings, + if (!frameworkAlerts.enabled() || additionalPrefix) { + await Promise.all( + componentTemplates.map(async (ct) => { + await this.createOrUpdateComponentTemplate({ + name: indexInfo.getComponentTemplateName(ct.name), + body: { + template: { + settings: ct.settings ?? {}, + mappings: ct.mappings, + }, + _meta: ct._meta, }, - _meta: ct._meta, - }, - }); - }) - ); + }); + }) + ); + } }); } @@ -252,10 +256,28 @@ export class ResourceInstaller { indexInfo: IndexInfo, namespace: string ): Promise { - const { logger } = this.options; + const { logger, frameworkAlerts } = this.options; const alias = indexInfo.getPrimaryAlias(namespace); + if ( + namespace === 'default' && + !indexInfo.indexOptions.additionalPrefix && + frameworkAlerts.enabled() + ) { + const { result: initialized, error } = await frameworkAlerts.getContextInitializationPromise( + indexInfo.indexOptions.registrationContext + ); + + if (!initialized) { + throw new Error( + `There was an error in the framework installing namespace-level resources and creating concrete indices for ${alias} - ${error}` + ); + } else { + return; + } + } + logger.info(`Installing namespace-level resources and creating concrete index for ${alias}`); // Install / update the index template diff --git a/x-pack/plugins/rule_registry/server/rule_data_plugin_service/rule_data_plugin_service.test.ts b/x-pack/plugins/rule_registry/server/rule_data_plugin_service/rule_data_plugin_service.test.ts index 1022ea038bc3e..2ba9146f4f2db 100644 --- a/x-pack/plugins/rule_registry/server/rule_data_plugin_service/rule_data_plugin_service.test.ts +++ b/x-pack/plugins/rule_registry/server/rule_data_plugin_service/rule_data_plugin_service.test.ts @@ -18,6 +18,11 @@ jest.mock('../rule_data_client/rule_data_client', () => ({ RuleDataClient: jest.fn().mockImplementation(() => mockCreateRuleDataClient()), })); +const frameworkAlertsService = { + enabled: () => false, + getContextInitializationPromise: async () => ({ result: false }), +}; + describe('ruleDataPluginService', () => { let pluginStop$: Subject; @@ -43,7 +48,7 @@ describe('ruleDataPluginService', () => { isWriteEnabled: true, disabledRegistrationContexts: ['observability.logs'], isWriterCacheEnabled: true, - areFrameworkAlertsEnabled: false, + frameworkAlerts: frameworkAlertsService, pluginStop$, }); expect(ruleDataService.isRegistrationContextDisabled('observability.logs')).toBe(true); @@ -60,7 +65,7 @@ describe('ruleDataPluginService', () => { isWriteEnabled: true, disabledRegistrationContexts: ['observability.logs'], isWriterCacheEnabled: true, - areFrameworkAlertsEnabled: false, + frameworkAlerts: frameworkAlertsService, pluginStop$, }); expect(ruleDataService.isRegistrationContextDisabled('observability.apm')).toBe(false); @@ -79,7 +84,7 @@ describe('ruleDataPluginService', () => { isWriteEnabled: true, disabledRegistrationContexts: ['observability.logs'], isWriterCacheEnabled: true, - areFrameworkAlertsEnabled: false, + frameworkAlerts: frameworkAlertsService, pluginStop$, }); @@ -99,7 +104,7 @@ describe('ruleDataPluginService', () => { isWriteEnabled: true, disabledRegistrationContexts: ['observability.logs'], isWriterCacheEnabled: true, - areFrameworkAlertsEnabled: false, + frameworkAlerts: frameworkAlertsService, pluginStop$, }); const indexOptions = { diff --git a/x-pack/plugins/rule_registry/server/rule_data_plugin_service/rule_data_plugin_service.ts b/x-pack/plugins/rule_registry/server/rule_data_plugin_service/rule_data_plugin_service.ts index b3f54a1d3794d..62f8cc88ca221 100644 --- a/x-pack/plugins/rule_registry/server/rule_data_plugin_service/rule_data_plugin_service.ts +++ b/x-pack/plugins/rule_registry/server/rule_data_plugin_service/rule_data_plugin_service.ts @@ -11,6 +11,7 @@ import type { ValidFeatureId } from '@kbn/rule-data-utils'; import type { ElasticsearchClient, Logger } from '@kbn/core/server'; +import { type PublicFrameworkAlertsService } from '@kbn/alerting-plugin/server'; import { INDEX_PREFIX } from '../config'; import { type IRuleDataClient, RuleDataClient, WaitResult } from '../rule_data_client'; import { IndexInfo } from './index_info'; @@ -91,7 +92,7 @@ interface ConstructorOptions { isWriteEnabled: boolean; isWriterCacheEnabled: boolean; disabledRegistrationContexts: string[]; - areFrameworkAlertsEnabled: boolean; + frameworkAlerts: PublicFrameworkAlertsService; pluginStop$: Observable; } @@ -113,7 +114,7 @@ export class RuleDataService implements IRuleDataService { logger: options.logger, disabledRegistrationContexts: options.disabledRegistrationContexts, isWriteEnabled: options.isWriteEnabled, - areFrameworkAlertsEnabled: options.areFrameworkAlertsEnabled, + frameworkAlerts: options.frameworkAlerts, pluginStop$: options.pluginStop$, }); diff --git a/x-pack/plugins/security/server/authorization/authorization_service.tsx b/x-pack/plugins/security/server/authorization/authorization_service.tsx index e190ec994c83c..7a148c6981cf4 100644 --- a/x-pack/plugins/security/server/authorization/authorization_service.tsx +++ b/x-pack/plugins/security/server/authorization/authorization_service.tsx @@ -156,7 +156,7 @@ export class AuthorizationService { // If we have a license which doesn't enable security, or we're a legacy user we shouldn't // disable any ui capabilities if (!mode.useRbacForRequest(request)) { - return uiCapabilities; + return {}; } const disableUICapabilities = disableUICapabilitiesFactory( diff --git a/x-pack/plugins/security/server/authorization/privileges/privileges.test.ts b/x-pack/plugins/security/server/authorization/privileges/privileges.test.ts index 17a157263cf52..d0409a932e448 100644 --- a/x-pack/plugins/security/server/authorization/privileges/privileges.test.ts +++ b/x-pack/plugins/security/server/authorization/privileges/privileges.test.ts @@ -216,6 +216,7 @@ describe('features', () => { expectGetFeatures: true, expectEnterpriseSearch: true, expectDecryptedTelemetry: true, + expectGlobalSettings: true, }, { group: 'space', @@ -223,6 +224,7 @@ describe('features', () => { expectGetFeatures: false, expectEnterpriseSearch: false, expectDecryptedTelemetry: false, + expectGlobalSettings: false, }, ].forEach( ({ @@ -231,6 +233,7 @@ describe('features', () => { expectGetFeatures, expectEnterpriseSearch, expectDecryptedTelemetry, + expectGlobalSettings, }) => { describe(`${group}`, () => { test('actions defined in any feature privilege are included in `all`', () => { @@ -291,6 +294,8 @@ describe('features', () => { ] : []), ...(expectEnterpriseSearch ? [actions.ui.get('enterpriseSearch', 'all')] : []), + ...(expectGlobalSettings ? [actions.ui.get('globalSettings', 'save')] : []), + ...(expectGlobalSettings ? [actions.ui.get('globalSettings', 'show')] : []), actions.ui.get('catalogue', 'all-catalogue-1'), actions.ui.get('catalogue', 'all-catalogue-2'), actions.ui.get('management', 'all-management', 'all-management-1'), @@ -421,6 +426,7 @@ describe('features', () => { actions.login, actions.version, ...(expectDecryptedTelemetry ? [actions.api.get('decryptedTelemetry')] : []), + ...(expectGlobalSettings ? [actions.ui.get('globalSettings', 'show')] : []), actions.ui.get('catalogue', 'read-catalogue-1'), actions.ui.get('catalogue', 'read-catalogue-2'), actions.ui.get('management', 'read-management', 'read-management-1'), @@ -514,11 +520,14 @@ describe('features', () => { ] : []), ...(expectEnterpriseSearch ? [actions.ui.get('enterpriseSearch', 'all')] : []), + ...(expectGlobalSettings ? [actions.ui.get('globalSettings', 'save')] : []), + ...(expectGlobalSettings ? [actions.ui.get('globalSettings', 'show')] : []), ]); expect(actual).toHaveProperty(`${group}.read`, [ actions.login, actions.version, ...(expectDecryptedTelemetry ? [actions.api.get('decryptedTelemetry')] : []), + ...(expectGlobalSettings ? [actions.ui.get('globalSettings', 'show')] : []), ]); }); @@ -581,11 +590,14 @@ describe('features', () => { ] : []), ...(expectEnterpriseSearch ? [actions.ui.get('enterpriseSearch', 'all')] : []), + ...(expectGlobalSettings ? [actions.ui.get('globalSettings', 'save')] : []), + ...(expectGlobalSettings ? [actions.ui.get('globalSettings', 'show')] : []), ]); expect(actual).toHaveProperty(`${group}.read`, [ actions.login, actions.version, ...(expectDecryptedTelemetry ? [actions.api.get('decryptedTelemetry')] : []), + ...(expectGlobalSettings ? [actions.ui.get('globalSettings', 'show')] : []), ]); }); @@ -649,11 +661,14 @@ describe('features', () => { ] : []), ...(expectEnterpriseSearch ? [actions.ui.get('enterpriseSearch', 'all')] : []), + ...(expectGlobalSettings ? [actions.ui.get('globalSettings', 'save')] : []), + ...(expectGlobalSettings ? [actions.ui.get('globalSettings', 'show')] : []), ]); expect(actual).toHaveProperty(`${group}.read`, [ actions.login, actions.version, ...(expectDecryptedTelemetry ? [actions.api.get('decryptedTelemetry')] : []), + ...(expectGlobalSettings ? [actions.ui.get('globalSettings', 'show')] : []), ]); }); }); @@ -917,12 +932,15 @@ describe('subFeatures', () => { actions.ui.get('management', 'kibana', 'spaces'), actions.ui.get('catalogue', 'spaces'), actions.ui.get('enterpriseSearch', 'all'), + actions.ui.get('globalSettings', 'save'), + actions.ui.get('globalSettings', 'show'), actions.ui.get('foo', 'foo'), ]); expect(actual).toHaveProperty('global.read', [ actions.login, actions.version, actions.api.get('decryptedTelemetry'), + actions.ui.get('globalSettings', 'show'), actions.ui.get('foo', 'foo'), ]); @@ -1087,6 +1105,8 @@ describe('subFeatures', () => { actions.ui.get('management', 'kibana', 'spaces'), actions.ui.get('catalogue', 'spaces'), actions.ui.get('enterpriseSearch', 'all'), + actions.ui.get('globalSettings', 'save'), + actions.ui.get('globalSettings', 'show'), actions.savedObject.get('all-sub-feature-type', 'bulk_get'), actions.savedObject.get('all-sub-feature-type', 'get'), actions.savedObject.get('all-sub-feature-type', 'find'), @@ -1111,6 +1131,7 @@ describe('subFeatures', () => { actions.login, actions.version, actions.api.get('decryptedTelemetry'), + actions.ui.get('globalSettings', 'show'), actions.savedObject.get('all-sub-feature-type', 'bulk_get'), actions.savedObject.get('all-sub-feature-type', 'get'), actions.savedObject.get('all-sub-feature-type', 'find'), @@ -1328,11 +1349,14 @@ describe('subFeatures', () => { actions.ui.get('management', 'kibana', 'spaces'), actions.ui.get('catalogue', 'spaces'), actions.ui.get('enterpriseSearch', 'all'), + actions.ui.get('globalSettings', 'save'), + actions.ui.get('globalSettings', 'show'), ]); expect(actual).toHaveProperty('global.read', [ actions.login, actions.version, actions.api.get('decryptedTelemetry'), + actions.ui.get('globalSettings', 'show'), ]); expect(actual).toHaveProperty('space.all', [actions.login, actions.version]); @@ -1470,6 +1494,8 @@ describe('subFeatures', () => { actions.ui.get('management', 'kibana', 'spaces'), actions.ui.get('catalogue', 'spaces'), actions.ui.get('enterpriseSearch', 'all'), + actions.ui.get('globalSettings', 'save'), + actions.ui.get('globalSettings', 'show'), actions.savedObject.get('all-sub-feature-type', 'bulk_get'), actions.savedObject.get('all-sub-feature-type', 'get'), actions.savedObject.get('all-sub-feature-type', 'find'), @@ -1494,6 +1520,7 @@ describe('subFeatures', () => { actions.login, actions.version, actions.api.get('decryptedTelemetry'), + actions.ui.get('globalSettings', 'show'), actions.ui.get('foo', 'foo'), ]); @@ -1657,11 +1684,14 @@ describe('subFeatures', () => { actions.ui.get('management', 'kibana', 'spaces'), actions.ui.get('catalogue', 'spaces'), actions.ui.get('enterpriseSearch', 'all'), + actions.ui.get('globalSettings', 'save'), + actions.ui.get('globalSettings', 'show'), ]); expect(actual).toHaveProperty('global.read', [ actions.login, actions.version, actions.api.get('decryptedTelemetry'), + actions.ui.get('globalSettings', 'show'), ]); expect(actual).toHaveProperty('space.all', [actions.login, actions.version]); @@ -1796,6 +1826,8 @@ describe('subFeatures', () => { actions.ui.get('management', 'kibana', 'spaces'), actions.ui.get('catalogue', 'spaces'), actions.ui.get('enterpriseSearch', 'all'), + actions.ui.get('globalSettings', 'save'), + actions.ui.get('globalSettings', 'show'), actions.savedObject.get('all-sub-feature-type', 'bulk_get'), actions.savedObject.get('all-sub-feature-type', 'get'), actions.savedObject.get('all-sub-feature-type', 'find'), @@ -1820,6 +1852,7 @@ describe('subFeatures', () => { actions.login, actions.version, actions.api.get('decryptedTelemetry'), + actions.ui.get('globalSettings', 'show'), actions.savedObject.get('all-sub-feature-type', 'bulk_get'), actions.savedObject.get('all-sub-feature-type', 'get'), actions.savedObject.get('all-sub-feature-type', 'find'), @@ -2015,6 +2048,8 @@ describe('subFeatures', () => { actions.ui.get('management', 'kibana', 'spaces'), actions.ui.get('catalogue', 'spaces'), actions.ui.get('enterpriseSearch', 'all'), + actions.ui.get('globalSettings', 'save'), + actions.ui.get('globalSettings', 'show'), actions.savedObject.get('all-sub-feature-type', 'bulk_get'), actions.savedObject.get('all-sub-feature-type', 'get'), actions.savedObject.get('all-sub-feature-type', 'find'), @@ -2039,6 +2074,7 @@ describe('subFeatures', () => { actions.login, actions.version, actions.api.get('decryptedTelemetry'), + actions.ui.get('globalSettings', 'show'), actions.savedObject.get('all-sub-feature-type', 'bulk_get'), actions.savedObject.get('all-sub-feature-type', 'get'), actions.savedObject.get('all-sub-feature-type', 'find'), @@ -2253,6 +2289,8 @@ describe('subFeatures', () => { actions.ui.get('management', 'kibana', 'spaces'), actions.ui.get('catalogue', 'spaces'), actions.ui.get('enterpriseSearch', 'all'), + actions.ui.get('globalSettings', 'save'), + actions.ui.get('globalSettings', 'show'), actions.savedObject.get('all-sub-feature-type', 'bulk_get'), actions.savedObject.get('all-sub-feature-type', 'get'), actions.savedObject.get('all-sub-feature-type', 'find'), @@ -2277,6 +2315,7 @@ describe('subFeatures', () => { actions.login, actions.version, actions.api.get('decryptedTelemetry'), + actions.ui.get('globalSettings', 'show'), actions.savedObject.get('all-sub-feature-type', 'bulk_get'), actions.savedObject.get('all-sub-feature-type', 'get'), actions.savedObject.get('all-sub-feature-type', 'find'), @@ -2527,6 +2566,8 @@ describe('subFeatures', () => { actions.ui.get('management', 'kibana', 'spaces'), actions.ui.get('catalogue', 'spaces'), actions.ui.get('enterpriseSearch', 'all'), + actions.ui.get('globalSettings', 'save'), + actions.ui.get('globalSettings', 'show'), actions.savedObject.get('all-sub-feature-type', 'bulk_get'), actions.savedObject.get('all-sub-feature-type', 'get'), actions.savedObject.get('all-sub-feature-type', 'find'), @@ -2569,6 +2610,7 @@ describe('subFeatures', () => { actions.login, actions.version, actions.api.get('decryptedTelemetry'), + actions.ui.get('globalSettings', 'show'), actions.savedObject.get('all-sub-feature-type', 'bulk_get'), actions.savedObject.get('all-sub-feature-type', 'get'), actions.savedObject.get('all-sub-feature-type', 'find'), diff --git a/x-pack/plugins/security/server/authorization/privileges/privileges.ts b/x-pack/plugins/security/server/authorization/privileges/privileges.ts index 546a392c73e1e..d324108c2dde1 100644 --- a/x-pack/plugins/security/server/authorization/privileges/privileges.ts +++ b/x-pack/plugins/security/server/authorization/privileges/privileges.ts @@ -116,12 +116,15 @@ export function privilegesFactory( actions.ui.get('management', 'kibana', 'spaces'), actions.ui.get('catalogue', 'spaces'), actions.ui.get('enterpriseSearch', 'all'), + actions.ui.get('globalSettings', 'save'), + actions.ui.get('globalSettings', 'show'), ...allActions, ], read: [ actions.login, actions.version, actions.api.get('decryptedTelemetry'), + actions.ui.get('globalSettings', 'show'), ...readActions, ], }, diff --git a/x-pack/plugins/security_solution/common/detection_engine/prebuilt_rules/api/get_prebuilt_rules_status/response_schema.ts b/x-pack/plugins/security_solution/common/detection_engine/prebuilt_rules/api/get_prebuilt_rules_status/response_schema.ts new file mode 100644 index 0000000000000..ea131df38375d --- /dev/null +++ b/x-pack/plugins/security_solution/common/detection_engine/prebuilt_rules/api/get_prebuilt_rules_status/response_schema.ts @@ -0,0 +1,33 @@ +/* + * 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. + */ + +export interface GetPrebuiltRulesStatusResponseBody { + status_code: number; + message: string; + attributes: { + /** Aggregated info about all prebuilt rules */ + stats: PrebuiltRulesStatusStats; + }; +} + +export interface PrebuiltRulesStatusStats { + /** Total number of existing (known) prebuilt rules */ + num_prebuilt_rules_total: number; + + /** Number of installed prebuilt rules */ + num_prebuilt_rules_installed: number; + + /** Number of prebuilt rules available for installation (not yet installed) */ + num_prebuilt_rules_to_install: number; + + /** Number of installed prebuilt rules available for upgrade (stock + customized) */ + num_prebuilt_rules_to_upgrade: number; + + // In the future we could add more stats such as: + // - number of installed prebuilt rules which were deprecated + // - number of installed prebuilt rules which are not compatible with the current version of Kibana +} diff --git a/x-pack/plugins/security_solution/common/detection_engine/prebuilt_rules/api/review_rule_installation/response_schema.ts b/x-pack/plugins/security_solution/common/detection_engine/prebuilt_rules/api/review_rule_installation/response_schema.ts new file mode 100644 index 0000000000000..999e58c524883 --- /dev/null +++ b/x-pack/plugins/security_solution/common/detection_engine/prebuilt_rules/api/review_rule_installation/response_schema.ts @@ -0,0 +1,34 @@ +/* + * 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 type { RuleSignatureId, RuleTagArray, RuleVersion } from '../../../rule_schema'; +import type { DiffableRule } from '../../model/diff/diffable_rule/diffable_rule'; + +export interface ReviewRuleInstallationResponseBody { + status_code: number; + message: string; + attributes: { + /** Aggregated info about all rules available for installation */ + stats: RuleInstallationStatsForReview; + + /** Info about individual rules: one object per each rule available for installation */ + rules: RuleInstallationInfoForReview[]; + }; +} + +export interface RuleInstallationStatsForReview { + /** Number of prebuilt rules available for installation */ + num_rules_to_install: number; + + /** A union of all tags of all rules available for installation */ + tags: RuleTagArray; +} + +export type RuleInstallationInfoForReview = DiffableRule & { + rule_id: RuleSignatureId; + version: RuleVersion; +}; diff --git a/x-pack/plugins/security_solution/common/detection_engine/prebuilt_rules/api/review_rule_upgrade/response_schema.ts b/x-pack/plugins/security_solution/common/detection_engine/prebuilt_rules/api/review_rule_upgrade/response_schema.ts new file mode 100644 index 0000000000000..97a4cee4c993f --- /dev/null +++ b/x-pack/plugins/security_solution/common/detection_engine/prebuilt_rules/api/review_rule_upgrade/response_schema.ts @@ -0,0 +1,46 @@ +/* + * 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 type { RuleObjectId, RuleSignatureId, RuleTagArray } from '../../../rule_schema'; +import type { DiffableRule } from '../../model/diff/diffable_rule/diffable_rule'; +import type { PartialRuleDiff } from '../../model/diff/rule_diff/rule_diff'; + +export interface ReviewRuleUpgradeResponseBody { + status_code: number; + message: string; + attributes: { + /** Aggregated info about all rules available for upgrade */ + stats: RuleUpgradeStatsForReview; + + /** Info about individual rules: one object per each rule available for upgrade */ + rules: RuleUpgradeInfoForReview[]; + }; +} + +export interface RuleUpgradeStatsForReview { + /** Number of installed prebuilt rules available for upgrade (stock + customized) */ + num_rules_to_upgrade_total: number; + + /** Number of installed prebuilt rules available for upgrade which are stock (non-customized) */ + num_rules_to_upgrade_not_customized: number; + + /** Number of installed prebuilt rules available for upgrade which are customized by the user */ + num_rules_to_upgrade_customized: number; + + /** A union of all tags of all rules available for upgrade */ + tags: RuleTagArray; + + /** A union of all fields "to be upgraded" across all the rules available for upgrade. An array of field names. */ + fields: string[]; +} + +export interface RuleUpgradeInfoForReview { + id: RuleObjectId; + rule_id: RuleSignatureId; + rule: DiffableRule; + diff: PartialRuleDiff; +} diff --git a/x-pack/plugins/security_solution/common/detection_engine/prebuilt_rules/api/urls.ts b/x-pack/plugins/security_solution/common/detection_engine/prebuilt_rules/api/urls.ts index 449960916f239..44727fcf693cf 100644 --- a/x-pack/plugins/security_solution/common/detection_engine/prebuilt_rules/api/urls.ts +++ b/x-pack/plugins/security_solution/common/detection_engine/prebuilt_rules/api/urls.ts @@ -5,7 +5,22 @@ * 2.0. */ -import { DETECTION_ENGINE_RULES_URL as RULES } from '../../../constants'; +import { + DETECTION_ENGINE_RULES_URL as RULES, + INTERNAL_DETECTION_ENGINE_URL as INTERNAL, +} from '../../../constants'; -export const PREBUILT_RULES_URL = `${RULES}/prepackaged` as const; -export const PREBUILT_RULES_STATUS_URL = `${RULES}/prepackaged/_status` as const; +const OLD_BASE_URL = `${RULES}/prepackaged` as const; +const NEW_BASE_URL = `${INTERNAL}/prebuilt_rules` as const; + +export const PREBUILT_RULES_URL = OLD_BASE_URL; +export const PREBUILT_RULES_STATUS_URL = `${OLD_BASE_URL}/_status` as const; + +export const GET_PREBUILT_RULES_STATUS_URL = `${NEW_BASE_URL}/status` as const; +export const REVIEW_RULE_UPGRADE_URL = `${NEW_BASE_URL}/upgrade/_review` as const; +export const PERFORM_RULE_UPGRADE_URL = `${NEW_BASE_URL}/upgrade/_perform` as const; +export const REVIEW_RULE_INSTALLATION_URL = `${NEW_BASE_URL}/installation/_review` as const; +export const PERFORM_RULE_INSTALLATION_URL = `${NEW_BASE_URL}/installation/_perform` as const; + +// Helper endpoints for development and testing. Should be removed later. +export const GENERATE_ASSETS_URL = `${NEW_BASE_URL}/_generate_assets` as const; diff --git a/x-pack/plugins/security_solution/common/detection_engine/prebuilt_rules/index.ts b/x-pack/plugins/security_solution/common/detection_engine/prebuilt_rules/index.ts index 5ec8f617f1546..20669682c63f9 100644 --- a/x-pack/plugins/security_solution/common/detection_engine/prebuilt_rules/index.ts +++ b/x-pack/plugins/security_solution/common/detection_engine/prebuilt_rules/index.ts @@ -8,5 +8,3 @@ export * from './api/get_prebuilt_rules_and_timelines_status/response_schema'; export * from './api/install_prebuilt_rules_and_timelines/response_schema'; export * from './api/urls'; - -export * from './model/prebuilt_rule'; diff --git a/x-pack/plugins/security_solution/common/detection_engine/prebuilt_rules/model/diff/diffable_rule/build_schema.ts b/x-pack/plugins/security_solution/common/detection_engine/prebuilt_rules/model/diff/diffable_rule/build_schema.ts new file mode 100644 index 0000000000000..b0b60f70d1e63 --- /dev/null +++ b/x-pack/plugins/security_solution/common/detection_engine/prebuilt_rules/model/diff/diffable_rule/build_schema.ts @@ -0,0 +1,23 @@ +/* + * 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 * as t from 'io-ts'; +import { orUndefined } from '../../../../rule_schema/model/build_rule_schemas'; + +interface RuleFields { + required: TRequired; + optional: TOptional; +} + +export const buildSchema = ( + fields: RuleFields +) => { + return t.intersection([ + t.exact(t.type(fields.required)), + t.exact(t.type(orUndefined(fields.optional))), + ]); +}; diff --git a/x-pack/plugins/security_solution/common/detection_engine/prebuilt_rules/model/diff/diffable_rule/diffable_field_types.ts b/x-pack/plugins/security_solution/common/detection_engine/prebuilt_rules/model/diff/diffable_rule/diffable_field_types.ts new file mode 100644 index 0000000000000..3e07566afbf96 --- /dev/null +++ b/x-pack/plugins/security_solution/common/detection_engine/prebuilt_rules/model/diff/diffable_rule/diffable_field_types.ts @@ -0,0 +1,141 @@ +/* + * 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 * as t from 'io-ts'; +import { TimeDuration } from '@kbn/securitysolution-io-ts-types'; +import { + BuildingBlockType, + DataViewId, + IndexPatternArray, + KqlQueryLanguage, + RuleFilterArray, + RuleNameOverride as RuleNameOverrideFieldName, + RuleQuery, + TimelineTemplateId, + TimelineTemplateTitle, + TimestampOverride as TimestampOverrideFieldName, + TimestampOverrideFallbackDisabled, +} from '../../../../rule_schema'; +import { saved_id } from '../../../../schemas/common'; + +// ------------------------------------------------------------------------------------------------- +// Rule data source + +export enum DataSourceType { + 'index_patterns' = 'index_patterns', + 'data_view' = 'data_view', +} + +export type DataSourceIndexPatterns = t.TypeOf; +export const DataSourceIndexPatterns = t.exact( + t.type({ + type: t.literal(DataSourceType.index_patterns), + index_patterns: IndexPatternArray, + }) +); + +export type DataSourceDataView = t.TypeOf; +export const DataSourceDataView = t.exact( + t.type({ + type: t.literal(DataSourceType.data_view), + data_view_id: DataViewId, + }) +); + +export type RuleDataSource = t.TypeOf; +export const RuleDataSource = t.union([DataSourceIndexPatterns, DataSourceDataView]); + +// ------------------------------------------------------------------------------------------------- +// Rule data query + +export enum KqlQueryType { + 'inline_query' = 'inline_query', + 'saved_query' = 'saved_query', +} + +export type InlineKqlQuery = t.TypeOf; +export const InlineKqlQuery = t.exact( + t.type({ + type: t.literal(KqlQueryType.inline_query), + query: RuleQuery, + language: KqlQueryLanguage, + filters: RuleFilterArray, + }) +); + +export type SavedKqlQuery = t.TypeOf; +export const SavedKqlQuery = t.exact( + t.type({ + type: t.literal(KqlQueryType.saved_query), + saved_query_id: saved_id, + }) +); + +export type RuleKqlQuery = t.TypeOf; +export const RuleKqlQuery = t.union([InlineKqlQuery, SavedKqlQuery]); + +export type RuleEqlQuery = t.TypeOf; +export const RuleEqlQuery = t.exact( + t.type({ + query: RuleQuery, + language: t.literal('eql'), + filters: RuleFilterArray, + }) +); + +// ------------------------------------------------------------------------------------------------- +// Rule schedule + +export type RuleSchedule = t.TypeOf; +export const RuleSchedule = t.exact( + t.type({ + interval: TimeDuration({ allowedUnits: ['s', 'm', 'h'] }), + lookback: TimeDuration({ allowedUnits: ['s', 'm', 'h'] }), + }) +); + +// ------------------------------------------------------------------------------------------------- +// Rule name override + +export type RuleNameOverrideObject = t.TypeOf; +export const RuleNameOverrideObject = t.exact( + t.type({ + field_name: RuleNameOverrideFieldName, + }) +); + +// ------------------------------------------------------------------------------------------------- +// Timestamp override + +export type TimestampOverrideObject = t.TypeOf; +export const TimestampOverrideObject = t.exact( + t.type({ + field_name: TimestampOverrideFieldName, + fallback_disabled: TimestampOverrideFallbackDisabled, + }) +); + +// ------------------------------------------------------------------------------------------------- +// Reference to a timeline template + +export type TimelineTemplateReference = t.TypeOf; +export const TimelineTemplateReference = t.exact( + t.type({ + timeline_id: TimelineTemplateId, + timeline_title: TimelineTemplateTitle, + }) +); + +// ------------------------------------------------------------------------------------------------- +// Building block + +export type BuildingBlockObject = t.TypeOf; +export const BuildingBlockObject = t.exact( + t.type({ + type: BuildingBlockType, + }) +); diff --git a/x-pack/plugins/security_solution/common/detection_engine/prebuilt_rules/model/diff/diffable_rule/diffable_rule.ts b/x-pack/plugins/security_solution/common/detection_engine/prebuilt_rules/model/diff/diffable_rule/diffable_rule.ts new file mode 100644 index 0000000000000..81dc7741a2bf4 --- /dev/null +++ b/x-pack/plugins/security_solution/common/detection_engine/prebuilt_rules/model/diff/diffable_rule/diffable_rule.ts @@ -0,0 +1,241 @@ +/* + * 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 * as t from 'io-ts'; + +import { + concurrent_searches, + items_per_search, + machine_learning_job_id, + RiskScore, + RiskScoreMapping, + RuleActionArray, + RuleActionThrottle, + Severity, + SeverityMapping, + threat_index, + threat_indicator_path, + threat_mapping, +} from '@kbn/securitysolution-io-ts-alerting-types'; + +import { + AlertSuppression, + EventCategoryOverride, + ExceptionListArray, + HistoryWindowStart, + InvestigationGuide, + MaxSignals, + NewTermsFields, + RelatedIntegrationArray, + RequiredFieldArray, + RuleAuthorArray, + RuleDescription, + RuleFalsePositiveArray, + RuleLicense, + RuleMetadata, + RuleName, + RuleReferenceArray, + RuleSignatureId, + RuleTagArray, + RuleVersion, + SetupGuide, + ThreatArray, + Threshold, + TiebreakerField, + TimestampField, +} from '../../../../rule_schema'; + +import { anomaly_threshold } from '../../../../schemas/common'; + +import { + BuildingBlockObject, + RuleEqlQuery, + InlineKqlQuery, + RuleKqlQuery, + RuleDataSource, + RuleNameOverrideObject, + RuleSchedule, + TimelineTemplateReference, + TimestampOverrideObject, +} from './diffable_field_types'; + +import { buildSchema } from './build_schema'; + +export type DiffableCommonFields = t.TypeOf; +export const DiffableCommonFields = buildSchema({ + required: { + // Technical fields + // NOTE: We might consider removing them from the schema and returning from the API + // not via the fields diff, but via dedicated properties in the response body. + rule_id: RuleSignatureId, + version: RuleVersion, + meta: RuleMetadata, + + // Main domain fields + name: RuleName, + tags: RuleTagArray, + description: RuleDescription, + severity: Severity, + severity_mapping: SeverityMapping, + risk_score: RiskScore, + risk_score_mapping: RiskScoreMapping, + + // About -> Advanced settings + references: RuleReferenceArray, + false_positives: RuleFalsePositiveArray, + threat: ThreatArray, + note: InvestigationGuide, + setup: SetupGuide, + related_integrations: RelatedIntegrationArray, + required_fields: RequiredFieldArray, + author: RuleAuthorArray, + license: RuleLicense, + + // Other domain fields + rule_schedule: RuleSchedule, // NOTE: new field + actions: RuleActionArray, + throttle: RuleActionThrottle, + exceptions_list: ExceptionListArray, + max_signals: MaxSignals, + }, + optional: { + rule_name_override: RuleNameOverrideObject, // NOTE: new field + timestamp_override: TimestampOverrideObject, // NOTE: new field + timeline_template: TimelineTemplateReference, // NOTE: new field + building_block: BuildingBlockObject, // NOTE: new field + }, +}); + +export type DiffableCustomQueryFields = t.TypeOf; +export const DiffableCustomQueryFields = buildSchema({ + required: { + type: t.literal('query'), + data_query: RuleKqlQuery, // NOTE: new field + }, + optional: { + data_source: RuleDataSource, // NOTE: new field + alert_suppression: AlertSuppression, + }, +}); + +export type DiffableSavedQueryFields = t.TypeOf; +export const DiffableSavedQueryFields = buildSchema({ + required: { + type: t.literal('saved_query'), + data_query: RuleKqlQuery, // NOTE: new field + }, + optional: { + data_source: RuleDataSource, // NOTE: new field + alert_suppression: AlertSuppression, + }, +}); + +export type DiffableEqlFields = t.TypeOf; +export const DiffableEqlFields = buildSchema({ + required: { + type: t.literal('eql'), + data_query: RuleEqlQuery, // NOTE: new field + }, + optional: { + data_source: RuleDataSource, // NOTE: new field + event_category_override: EventCategoryOverride, + timestamp_field: TimestampField, + tiebreaker_field: TiebreakerField, + }, +}); + +export type DiffableThreatMatchFields = t.TypeOf; +export const DiffableThreatMatchFields = buildSchema({ + required: { + type: t.literal('threat_match'), + data_query: RuleKqlQuery, // NOTE: new field + threat_query: InlineKqlQuery, // NOTE: new field + threat_index, + threat_mapping, + }, + optional: { + data_source: RuleDataSource, // NOTE: new field + threat_indicator_path, + concurrent_searches, // Should combine concurrent_searches and items_per_search? + items_per_search, + }, +}); + +export type DiffableThresholdFields = t.TypeOf; +export const DiffableThresholdFields = buildSchema({ + required: { + type: t.literal('threshold'), + data_query: RuleKqlQuery, // NOTE: new field + threshold: Threshold, + }, + optional: { + data_source: RuleDataSource, // NOTE: new field + }, +}); + +export type DiffableMachineLearningFields = t.TypeOf; +export const DiffableMachineLearningFields = buildSchema({ + required: { + type: t.literal('machine_learning'), + machine_learning_job_id, + anomaly_threshold, + }, + optional: {}, +}); + +export type DiffableNewTermsFields = t.TypeOf; +export const DiffableNewTermsFields = buildSchema({ + required: { + type: t.literal('new_terms'), + data_query: InlineKqlQuery, // NOTE: new field + new_terms_fields: NewTermsFields, + history_window_start: HistoryWindowStart, + }, + optional: { + data_source: RuleDataSource, // NOTE: new field + }, +}); + +/** + * Represents a normalized rule object that is suitable for passing to the diff algorithm. + * Every top-level field of a diffable rule can be compared separately on its own. + * + * It's important to do such normalization because: + * + * 1. We need to compare installed rules with prebuilt rule content. These objects have similar but not exactly + * the same interfaces. In order to compare them we need to convert them to a common interface. + * + * 2. It only makes sense to compare certain rule fields in combination with other fields. For example, + * we combine `index` and `data_view_id` fields into a `RuleDataSource` object, so that later we could + * calculate a diff for this whole object. If we don't combine them the app would successfully merge the + * following values independently from each other without a conflict: + * + * Base version: index=[logs-*], data_view_id=undefined + * Current version: index=[], data_view_id=some-data-view // user switched to a data view + * Target version: index=[logs-*, filebeat-*], data_view_id=undefined // Elastic added a new index pattern + * Merged version: index=[filebeat-*], data_view_id=some-data-view ??? + * + * Instead, semantically such change represents a conflict because the data source of the rule was changed + * in a potentially incompatible way, and the user might want to review the change and resolve it manually. + * The user must either pick index patterns or a data view, but not both at the same time. + * + * NOTE: Every top-level field in a DiffableRule MUST BE LOGICALLY INDEPENDENT from other + * top-level fields. + */ +export type DiffableRule = t.TypeOf; +export const DiffableRule = t.intersection([ + DiffableCommonFields, + t.union([ + DiffableCustomQueryFields, + DiffableSavedQueryFields, + DiffableEqlFields, + DiffableThreatMatchFields, + DiffableThresholdFields, + DiffableMachineLearningFields, + DiffableNewTermsFields, + ]), +]); diff --git a/x-pack/plugins/security_solution/common/detection_engine/prebuilt_rules/model/diff/rule_diff/fields_diff.ts b/x-pack/plugins/security_solution/common/detection_engine/prebuilt_rules/model/diff/rule_diff/fields_diff.ts new file mode 100644 index 0000000000000..a1b8a0ae8d104 --- /dev/null +++ b/x-pack/plugins/security_solution/common/detection_engine/prebuilt_rules/model/diff/rule_diff/fields_diff.ts @@ -0,0 +1,16 @@ +/* + * 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 type { ThreeWayDiff, ThreeWayDiffAlgorithm } from '../three_way_diff/three_way_diff'; + +export type FieldsDiff = { + [Field in keyof TObject]: ThreeWayDiff; +}; + +export type FieldsDiffAlgorithmsFor = { + [Field in keyof TObject]: ThreeWayDiffAlgorithm; +}; diff --git a/x-pack/plugins/security_solution/common/detection_engine/prebuilt_rules/model/diff/rule_diff/rule_diff.ts b/x-pack/plugins/security_solution/common/detection_engine/prebuilt_rules/model/diff/rule_diff/rule_diff.ts new file mode 100644 index 0000000000000..8a28b358f5303 --- /dev/null +++ b/x-pack/plugins/security_solution/common/detection_engine/prebuilt_rules/model/diff/rule_diff/rule_diff.ts @@ -0,0 +1,70 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import type { + DiffableCommonFields, + DiffableCustomQueryFields, + DiffableEqlFields, + DiffableMachineLearningFields, + DiffableNewTermsFields, + DiffableSavedQueryFields, + DiffableThreatMatchFields, + DiffableThresholdFields, +} from '../diffable_rule/diffable_rule'; + +import type { FieldsDiff } from './fields_diff'; + +export type CommonFieldsDiff = FieldsDiff; +export type CustomQueryFieldsDiff = FieldsDiff; +export type SavedQueryFieldsDiff = FieldsDiff; +export type EqlFieldsDiff = FieldsDiff; +export type ThreatMatchFieldsDiff = FieldsDiff; +export type ThresholdFieldsDiff = FieldsDiff; +export type MachineLearningFieldsDiff = FieldsDiff; +export type NewTermsFieldsDiff = FieldsDiff; + +/** + * It's an object which keys are the same as keys of DiffableRule, but values are + * three-way diffs calculated for their values. + * + * @example + * { + * name: ThreeWayDiff; + * tags: ThreeWayDiff; + * // etc + * } + */ +export type RuleFieldsDiff = CommonFieldsDiff & + ( + | CustomQueryFieldsDiff + | SavedQueryFieldsDiff + | EqlFieldsDiff + | ThreatMatchFieldsDiff + | ThresholdFieldsDiff + | MachineLearningFieldsDiff + | NewTermsFieldsDiff + ); + +/** + * Full rule diff contains diffs for all the top-level rule fields. + * Even if there's no change at all to a given field, its diff will be included in this object. + * This diff can be useful for internal server-side calculations or debugging. + * Note that this is a pretty large object so returning it from the API might be undesirable. + */ +export interface FullRuleDiff { + fields: RuleFieldsDiff; + has_conflict: boolean; +} + +/** + * Partial rule diff contains diffs only for those rule fields that have some changes to them. + * This diff can be useful for returning info from REST API endpoints because its size is tolerable. + */ +export interface PartialRuleDiff { + fields: Partial; + has_conflict: boolean; +} diff --git a/x-pack/plugins/security_solution/common/detection_engine/prebuilt_rules/model/diff/three_way_diff/three_way_diff.ts b/x-pack/plugins/security_solution/common/detection_engine/prebuilt_rules/model/diff/three_way_diff/three_way_diff.ts new file mode 100644 index 0000000000000..521b31dc4ea15 --- /dev/null +++ b/x-pack/plugins/security_solution/common/detection_engine/prebuilt_rules/model/diff/three_way_diff/three_way_diff.ts @@ -0,0 +1,114 @@ +/* + * 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 type { ThreeWayDiffOutcome } from './three_way_diff_outcome'; +import type { ThreeWayMergeOutcome } from './three_way_merge_outcome'; + +/** + * Three versions of a value to pass to a diff algorithm. + */ +export interface ThreeVersionsOf { + /** + * Corresponds to the stock version of the currently installed prebuilt rule. + */ + base_version: TValue; + + /** + * Corresponds exactly to the currently installed prebuilt rule: + * - to the customized version (if it's customized) + * - to the stock version (if it's not customized) + */ + current_version: TValue; + + /** + * Corresponds to the "new" stock version that the user is trying to upgrade to. + */ + target_version: TValue; +} + +/** + * Represents a result of an abstract three-way diff/merge operation on a value + * (could be a whole rule JSON or a given rule field). + * + * Typical situations: + * + * 1. base=A, current=A, target=A => merged=A, conflict=false + * Stock rule, the value hasn't changed. + * + * 2. base=A, current=A, target=B => merged=B, conflict=false + * Stock rule, the value has changed. + * + * 3. base=A, current=B, target=A => merged=B, conflict=false + * Customized rule, the value hasn't changed. + * + * 4. base=A, current=B, target=B => merged=B, conflict=false + * Customized rule, the value has changed exactly the same way as in the user customization. + * + * 5. base=A, current=B, target=C => merged=D, conflict=false + * Customized rule, the value has changed, conflict between B and C resolved automatically. + * + * 6. base=A, current=B, target=C => merged=C, conflict=true + * Customized rule, the value has changed, conflict between B and C couldn't be resolved automatically. + */ +export interface ThreeWayDiff extends ThreeVersionsOf { + /** + * The result of an automatic three-way merge of three values: + * - base version + * - current version + * - target version + * + * Exact merge algorithm depends on the value: + * - one algo could be used for single-line strings and keywords (e.g. rule name) + * - another one could be used for multiline text (e.g. rule description) + * - another one could be used for arrays of keywords (e.g. rule tags) + * - another one could be used for the MITRE ATT&CK data structure + * - etc + * + * Merged version always has a value. We do our best to resolve conflicts automatically. + * If they can't be resolved automatically, merged version is equal to target version. + */ + merged_version: TValue; + + /** + * Tells which combination corresponds to the three input versions of the value for this specific diff. + */ + diff_outcome: ThreeWayDiffOutcome; + + /** + * The type of result of an automatic three-way merge of three values: + * - base version + * - current version + * - target version + */ + merge_outcome: ThreeWayMergeOutcome; + + /** + * Tells if the value has changed in the target version and the current version could be updated. + * True if: + * - base=A, current=A, target=B + * - base=A, current=B, target=C + */ + has_update: boolean; + + /** + * True if: + * - current != target and we couldn't automatically resolve the conflict between them + * + * False if: + * - current == target (value won't change) + * - current != target && current == base (stock rule will get a new value) + * - current != target and we automatically resolved the conflict between them + */ + has_conflict: boolean; +} + +/** + * Given the three versions of a value, calculates a three-way diff for it. + */ +export type ThreeWayDiffAlgorithm = ( + versions: ThreeVersionsOf +) => ThreeWayDiff; diff --git a/x-pack/plugins/security_solution/common/detection_engine/prebuilt_rules/model/diff/three_way_diff/three_way_diff_outcome.ts b/x-pack/plugins/security_solution/common/detection_engine/prebuilt_rules/model/diff/three_way_diff/three_way_diff_outcome.ts new file mode 100644 index 0000000000000..07d3fb526c209 --- /dev/null +++ b/x-pack/plugins/security_solution/common/detection_engine/prebuilt_rules/model/diff/three_way_diff/three_way_diff_outcome.ts @@ -0,0 +1,60 @@ +/* + * 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 { isEqual } from 'lodash'; + +/** + * Result of comparing three versions of a value against each other. + * Defines 5 typical combinations of 3 versions of a value. + */ +export enum ThreeWayDiffOutcome { + /** Stock rule, the value hasn't changed in the target version. */ + StockValueNoUpdate = 'BASE=A, CURRENT=A, TARGET=A', + + /** Stock rule, the value has changed in the target version. */ + StockValueCanUpdate = 'BASE=A, CURRENT=A, TARGET=B', + + /** Customized rule, the value hasn't changed in the target version comparing to the base one. */ + CustomizedValueNoUpdate = 'BASE=A, CURRENT=B, TARGET=A', + + /** Customized rule, the value has changed in the target version exactly the same way as in the user customization. */ + CustomizedValueSameUpdate = 'BASE=A, CURRENT=B, TARGET=B', + + /** Customized rule, the value has changed in the target version and is not equal to the current version. */ + CustomizedValueCanUpdate = 'BASE=A, CURRENT=B, TARGET=C', +} + +export const determineDiffOutcome = ( + baseVersion: TValue, + currentVersion: TValue, + targetVersion: TValue +): ThreeWayDiffOutcome => { + const baseEqlCurrent = isEqual(baseVersion, currentVersion); + const baseEqlTarget = isEqual(baseVersion, targetVersion); + const currentEqlTarget = isEqual(currentVersion, targetVersion); + + if (baseEqlCurrent) { + return currentEqlTarget + ? ThreeWayDiffOutcome.StockValueNoUpdate + : ThreeWayDiffOutcome.StockValueCanUpdate; + } + + if (baseEqlTarget) { + return ThreeWayDiffOutcome.CustomizedValueNoUpdate; + } + + return currentEqlTarget + ? ThreeWayDiffOutcome.CustomizedValueSameUpdate + : ThreeWayDiffOutcome.CustomizedValueCanUpdate; +}; + +export const determineIfValueCanUpdate = (diffCase: ThreeWayDiffOutcome): boolean => { + return ( + diffCase === ThreeWayDiffOutcome.StockValueCanUpdate || + diffCase === ThreeWayDiffOutcome.CustomizedValueCanUpdate + ); +}; diff --git a/x-pack/plugins/security_solution/common/detection_engine/prebuilt_rules/model/diff/three_way_diff/three_way_merge_outcome.ts b/x-pack/plugins/security_solution/common/detection_engine/prebuilt_rules/model/diff/three_way_diff/three_way_merge_outcome.ts new file mode 100644 index 0000000000000..5c2caea0130dd --- /dev/null +++ b/x-pack/plugins/security_solution/common/detection_engine/prebuilt_rules/model/diff/three_way_diff/three_way_merge_outcome.ts @@ -0,0 +1,26 @@ +/* + * 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. + */ + +/** + * Type of result of an automatic three-way merge of three values: + * - base version + * - current version + * - target version + */ +export enum ThreeWayMergeOutcome { + /** Took current version and returned as the merged one. */ + Current = 'CURRENT', + + /** Took target version and returned as the merged one. */ + Target = 'TARGET', + + /** Merged three versions successfully into a new one. */ + Merged = 'MERGED', + + /** Couldn't merge three versions because of a conflict. */ + Conflict = 'CONFLICT', +} diff --git a/x-pack/plugins/security_solution/common/detection_engine/prebuilt_rules/model/prebuilt_rule.ts b/x-pack/plugins/security_solution/common/detection_engine/prebuilt_rules/model/prebuilt_rule.ts deleted file mode 100644 index e46615ee992b7..0000000000000 --- a/x-pack/plugins/security_solution/common/detection_engine/prebuilt_rules/model/prebuilt_rule.ts +++ /dev/null @@ -1,43 +0,0 @@ -/* - * 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 * as t from 'io-ts'; -import { - RelatedIntegrationArray, - RequiredFieldArray, - SetupGuide, - RuleSignatureId, - RuleVersion, - BaseCreateProps, - TypeSpecificCreateProps, -} from '../../rule_schema'; - -/** - * Big differences between this schema and the createRulesSchema - * - rule_id is required here - * - version is a required field that must exist - */ -export type PrebuiltRuleToInstall = t.TypeOf; -export const PrebuiltRuleToInstall = t.intersection([ - BaseCreateProps, - TypeSpecificCreateProps, - // version is required in PrebuiltRuleToInstall, so this supercedes the defaultable - // version in baseParams - t.exact( - t.type({ - rule_id: RuleSignatureId, - version: RuleVersion, - }) - ), - t.exact( - t.partial({ - related_integrations: RelatedIntegrationArray, - required_fields: RequiredFieldArray, - setup: SetupGuide, - }) - ), -]); diff --git a/x-pack/plugins/security_solution/common/detection_engine/rule_schema/model/build_rule_schemas.ts b/x-pack/plugins/security_solution/common/detection_engine/rule_schema/model/build_rule_schemas.ts index f7d52c682d191..c77ba322b1c79 100644 --- a/x-pack/plugins/security_solution/common/detection_engine/rule_schema/model/build_rule_schemas.ts +++ b/x-pack/plugins/security_solution/common/detection_engine/rule_schema/model/build_rule_schemas.ts @@ -21,6 +21,7 @@ export const buildRuleSchemas = ) => { return { + ...fields, create: buildCreateRuleSchema(fields.required, fields.optional, fields.defaultable), patch: buildPatchRuleSchema(fields.required, fields.optional, fields.defaultable), response: buildResponseRuleSchema(fields.required, fields.optional, fields.defaultable), @@ -59,10 +60,17 @@ const buildPatchRuleSchema = < ]); }; -type OrUndefined

= { +export type OrUndefined

= { [K in keyof P]: P[K] | t.UndefinedC; }; +export const orUndefined =

(props: P): OrUndefined

=> { + return Object.keys(props).reduce((acc, key) => { + acc[key] = t.union([props[key], t.undefined]); + return acc; + }, {}) as OrUndefined

; +}; + export const buildResponseRuleSchema = < Required extends t.Props, Optional extends t.Props, @@ -78,10 +86,7 @@ export const buildResponseRuleSchema = < // the conversion from internal schema to response schema TS will report an error. If we just used t.partial // instead, then optional fields can be accidentally omitted from the conversion - and any actual values // in those fields internally will be stripped in the response. - const optionalWithUndefined = Object.keys(optionalFields).reduce((acc, key) => { - acc[key] = t.union([optionalFields[key], t.undefined]); - return acc; - }, {}) as OrUndefined; + const optionalWithUndefined = orUndefined(optionalFields); return t.intersection([ t.exact(t.type(requiredFields)), t.exact(t.type(optionalWithUndefined)), diff --git a/x-pack/plugins/security_solution/common/detection_engine/rule_schema/model/common_attributes/misc_attributes.ts b/x-pack/plugins/security_solution/common/detection_engine/rule_schema/model/common_attributes/misc_attributes.ts index 315a15190ec28..c4992da7f2702 100644 --- a/x-pack/plugins/security_solution/common/detection_engine/rule_schema/model/common_attributes/misc_attributes.ts +++ b/x-pack/plugins/security_solution/common/detection_engine/rule_schema/model/common_attributes/misc_attributes.ts @@ -47,7 +47,7 @@ export type RuleMetadata = t.TypeOf; export const RuleMetadata = t.object; // should be a more specific type? export type RuleLicense = t.TypeOf; -export const RuleLicense = t.string; // should be non-empty string? +export const RuleLicense = t.string; export type RuleAuthorArray = t.TypeOf; export const RuleAuthorArray = t.array(t.string); // should be non-empty strings? diff --git a/x-pack/plugins/security_solution/common/detection_engine/rule_schema/model/rule_schemas.ts b/x-pack/plugins/security_solution/common/detection_engine/rule_schema/model/rule_schemas.ts index 5d35811368b39..636c5ee5f6fa8 100644 --- a/x-pack/plugins/security_solution/common/detection_engine/rule_schema/model/rule_schemas.ts +++ b/x-pack/plugins/security_solution/common/detection_engine/rule_schema/model/rule_schemas.ts @@ -92,7 +92,7 @@ import { buildRuleSchemas } from './build_rule_schemas'; // ------------------------------------------------------------------------------------------------- // Base schema -const baseSchema = buildRuleSchemas({ +export const baseSchema = buildRuleSchemas({ required: { name: RuleName, description: RuleDescription, @@ -207,10 +207,22 @@ export const SharedResponseProps = t.intersection([ // ------------------------------------------------------------------------------------------------- // EQL rule schema +export enum QueryLanguage { + 'kuery' = 'kuery', + 'lucene' = 'lucene', + 'eql' = 'eql', +} + +export type KqlQueryLanguage = t.TypeOf; +export const KqlQueryLanguage = t.keyof({ kuery: null, lucene: null }); + +export type EqlQueryLanguage = t.TypeOf; +export const EqlQueryLanguage = t.literal('eql'); + const eqlSchema = buildRuleSchemas({ required: { type: t.literal('eql'), - language: t.literal('eql'), + language: EqlQueryLanguage, query: RuleQuery, }, optional: { @@ -257,12 +269,12 @@ const threatMatchSchema = buildRuleSchemas({ saved_id, threat_filters, threat_indicator_path, - threat_language: t.keyof({ kuery: null, lucene: null }), + threat_language: KqlQueryLanguage, concurrent_searches, items_per_search, }, defaultable: { - language: t.keyof({ kuery: null, lucene: null }), + language: KqlQueryLanguage, }, }); @@ -307,7 +319,7 @@ const querySchema = buildRuleSchemas({ }, defaultable: { query: RuleQuery, - language: t.keyof({ kuery: null, lucene: null }), + language: KqlQueryLanguage, }, }); @@ -345,7 +357,7 @@ const savedQuerySchema = buildRuleSchemas({ alert_suppression: AlertSuppression, }, defaultable: { - language: t.keyof({ kuery: null, lucene: null }), + language: KqlQueryLanguage, }, }); @@ -386,7 +398,7 @@ const thresholdSchema = buildRuleSchemas({ saved_id, }, defaultable: { - language: t.keyof({ kuery: null, lucene: null }), + language: KqlQueryLanguage, }, }); @@ -461,7 +473,7 @@ const newTermsSchema = buildRuleSchemas({ filters: RuleFilterArray, }, defaultable: { - language: t.keyof({ kuery: null, lucene: null }), + language: KqlQueryLanguage, }, }); diff --git a/x-pack/plugins/security_solution/common/experimental_features.ts b/x-pack/plugins/security_solution/common/experimental_features.ts index f1ae3ec9c488d..100ff37586c5f 100644 --- a/x-pack/plugins/security_solution/common/experimental_features.ts +++ b/x-pack/plugins/security_solution/common/experimental_features.ts @@ -52,6 +52,12 @@ export const allowedExperimentalValues = Object.freeze({ */ extendedRuleExecutionLoggingEnabled: false, + /** + * Enables the new API and UI for https://github.com/elastic/security-team/issues/1974. + * It's a temporary feature flag that will be removed once the feature gets a basic production-ready implementation. + */ + prebuiltRulesNewUpgradeAndInstallationWorkflowsEnabled: false, + /** * Enables the SOC trends timerange and stats on D&R page */ @@ -91,6 +97,7 @@ export const allowedExperimentalValues = Object.freeze({ * Enables top charts on Alerts Page */ alertsPageChartsEnabled: true, + alertTypeEnabled: false, /** * Enables the new security flyout over the current alert details flyout */ @@ -110,7 +117,7 @@ export const allowedExperimentalValues = Object.freeze({ * Enables new Set of filters on the Alerts page. * **/ - alertsPageFiltersEnabled: false, + alertsPageFiltersEnabled: true, }); type ExperimentalConfigKeys = Array; diff --git a/x-pack/plugins/security_solution/common/utils/field_formatters.ts b/x-pack/plugins/security_solution/common/utils/field_formatters.ts index 65fc3871c7fc3..da41cbd3bee94 100644 --- a/x-pack/plugins/security_solution/common/utils/field_formatters.ts +++ b/x-pack/plugins/security_solution/common/utils/field_formatters.ts @@ -6,7 +6,7 @@ */ import { ecsFieldMap } from '@kbn/rule-registry-plugin/common/assets/field_maps/ecs_field_map'; -import { experimentalRuleFieldMap } from '@kbn/rule-registry-plugin/common/assets/field_maps/experimental_rule_field_map'; +import { legacyExperimentalFieldMap } from '@kbn/alerts-as-data-utils'; import { technicalRuleFieldMap } from '@kbn/rule-registry-plugin/common/assets/field_maps/technical_rule_field_map'; import { isEmpty } from 'lodash/fp'; import { ENRICHMENT_DESTINATION_PATH } from '../constants'; @@ -76,7 +76,7 @@ export const getDataFromFieldsHits = ( // return simple field value (non-esc object, non-array) if ( !isObjectArray || - Object.keys({ ...ecsFieldMap, ...technicalRuleFieldMap, ...experimentalRuleFieldMap }).find( + Object.keys({ ...ecsFieldMap, ...technicalRuleFieldMap, ...legacyExperimentalFieldMap }).find( (ecsField) => ecsField === field ) === undefined ) { diff --git a/x-pack/plugins/security_solution/cypress/ccs_e2e/detection_alerts/alerts_details.cy.ts b/x-pack/plugins/security_solution/cypress/ccs_e2e/detection_alerts/alerts_details.cy.ts index 3141423038f6a..e59b96d95dd74 100644 --- a/x-pack/plugins/security_solution/cypress/ccs_e2e/detection_alerts/alerts_details.cy.ts +++ b/x-pack/plugins/security_solution/cypress/ccs_e2e/detection_alerts/alerts_details.cy.ts @@ -9,7 +9,7 @@ import { JSON_TEXT } from '../../screens/alerts_details'; import { expandFirstAlert, waitForAlerts } from '../../tasks/alerts'; import { openJsonView } from '../../tasks/alerts_details'; -import { createCustomRuleEnabled } from '../../tasks/api_calls/rules'; +import { createRule } from '../../tasks/api_calls/rules'; import { cleanKibana } from '../../tasks/common'; import { esArchiverCCSLoad } from '../../tasks/es_archiver'; import { login, visitWithoutDateRange } from '../../tasks/login'; @@ -23,7 +23,7 @@ describe('Alert details with unmapped fields', () => { login(); cleanKibana(); esArchiverCCSLoad('unmapped_fields'); - createCustomRuleEnabled(getUnmappedCCSRule()); + createRule(getUnmappedCCSRule()); visitWithoutDateRange(ALERTS_URL); waitForAlerts(); expandFirstAlert(); diff --git a/x-pack/plugins/security_solution/cypress/ccs_e2e/detection_rules/event_correlation_rule.cy.ts b/x-pack/plugins/security_solution/cypress/ccs_e2e/detection_rules/event_correlation_rule.cy.ts index d8cd2d2b10e6c..b6760a11b2896 100644 --- a/x-pack/plugins/security_solution/cypress/ccs_e2e/detection_rules/event_correlation_rule.cy.ts +++ b/x-pack/plugins/security_solution/cypress/ccs_e2e/detection_rules/event_correlation_rule.cy.ts @@ -15,7 +15,7 @@ import { goToRuleDetails, waitForRulesTableToBeLoaded, } from '../../tasks/alerts_detection_rules'; -import { createEventCorrelationRule } from '../../tasks/api_calls/rules'; +import { createRule } from '../../tasks/api_calls/rules'; import { cleanKibana } from '../../tasks/common'; import { waitForAlertsToPopulate, waitForTheRuleToBeExecuted } from '../../tasks/create_new_rule'; import { login, visitWithoutDateRange } from '../../tasks/login'; @@ -31,9 +31,9 @@ describe('Detection rules', function () { it('EQL rule on remote indices generates alerts', function () { esArchiverCCSLoad('linux_process'); - this.rule = getCCSEqlRule(); + const rule = getCCSEqlRule(); login(); - createEventCorrelationRule(this.rule); + createRule(rule); visitWithoutDateRange(DETECTIONS_RULE_MANAGEMENT_URL); waitForRulesTableToBeLoaded(); filterByCustomRules(); @@ -46,9 +46,9 @@ describe('Detection rules', function () { .invoke('text') .then((text) => { cy.log('ALERT_DATA_GRID', text); - expect(text).contains(this.rule.name); - expect(text).contains(this.rule.severity.toLowerCase()); - expect(text).contains(this.rule.riskScore); + expect(text).contains(rule.name); + expect(text).contains(rule.severity); + expect(text).contains(rule.risk_score); }); }); }); diff --git a/x-pack/plugins/security_solution/cypress/e2e/cases/attach_alert_to_case.cy.ts b/x-pack/plugins/security_solution/cypress/e2e/cases/attach_alert_to_case.cy.ts index 642ebb372ae2b..32f5c894ded46 100644 --- a/x-pack/plugins/security_solution/cypress/e2e/cases/attach_alert_to_case.cy.ts +++ b/x-pack/plugins/security_solution/cypress/e2e/cases/attach_alert_to_case.cy.ts @@ -9,7 +9,7 @@ import { getNewRule } from '../../objects/rule'; import { ROLES } from '../../../common/test'; import { expandFirstAlertActions } from '../../tasks/alerts'; -import { createCustomRuleEnabled } from '../../tasks/api_calls/rules'; +import { createRule } from '../../tasks/api_calls/rules'; import { cleanKibana } from '../../tasks/common'; import { waitForAlertsToPopulate } from '../../tasks/create_new_rule'; import { login, visit, waitForPageWithoutDateRange } from '../../tasks/login'; @@ -28,7 +28,7 @@ describe('Alerts timeline', () => { // First we login as a privileged user to create alerts. cleanKibana(); login(); - createCustomRuleEnabled(getNewRule()); + createRule(getNewRule()); visit(ALERTS_URL); waitForAlertsToPopulate(); }); diff --git a/x-pack/plugins/security_solution/cypress/e2e/dashboards/enable_risk_score.cy.ts b/x-pack/plugins/security_solution/cypress/e2e/dashboards/enable_risk_score.cy.ts index bb30a0f40a45e..7453270906bdf 100644 --- a/x-pack/plugins/security_solution/cypress/e2e/dashboards/enable_risk_score.cy.ts +++ b/x-pack/plugins/security_solution/cypress/e2e/dashboards/enable_risk_score.cy.ts @@ -18,7 +18,7 @@ import { waitForInstallRiskScoreModule, } from '../../tasks/api_calls/risk_scores'; import { findSavedObjects } from '../../tasks/api_calls/risk_scores/saved_objects'; -import { createCustomRuleEnabled } from '../../tasks/api_calls/rules'; +import { createRule } from '../../tasks/api_calls/rules'; import { cleanKibana } from '../../tasks/common'; import { login, visit } from '../../tasks/login'; import { clickEnableRiskScore } from '../../tasks/risk_scores'; @@ -36,7 +36,7 @@ describe('Enable risk scores', () => { before(() => { cleanKibana(); login(); - createCustomRuleEnabled(getNewRule(), 'rule1'); + createRule({ ...getNewRule(), rule_id: 'rule1' }); }); beforeEach(() => { diff --git a/x-pack/plugins/security_solution/cypress/e2e/dashboards/entity_analytics.cy.ts b/x-pack/plugins/security_solution/cypress/e2e/dashboards/entity_analytics.cy.ts index 525ee79a8e1dc..b62dc94c219ce 100644 --- a/x-pack/plugins/security_solution/cypress/e2e/dashboards/entity_analytics.cy.ts +++ b/x-pack/plugins/security_solution/cypress/e2e/dashboards/entity_analytics.cy.ts @@ -30,7 +30,7 @@ import { HOSTS_TABLE_ALERT_CELL, } from '../../screens/entity_analytics'; import { openRiskTableFilterAndSelectTheLowOption } from '../../tasks/host_risk'; -import { createCustomRuleEnabled } from '../../tasks/api_calls/rules'; +import { createRule } from '../../tasks/api_calls/rules'; import { waitForAlertsToPopulate } from '../../tasks/create_new_rule'; import { getNewRule } from '../../objects/rule'; import { QUERY_TAB_BUTTON } from '../../screens/timeline'; @@ -143,7 +143,7 @@ describe('Entity Analytics Dashboard', () => { describe('With alerts data', () => { before(() => { - createCustomRuleEnabled(getNewRule()); + createRule(getNewRule()); }); beforeEach(() => { @@ -203,7 +203,7 @@ describe('Entity Analytics Dashboard', () => { describe('With alerts data', () => { before(() => { - createCustomRuleEnabled(getNewRule()); + createRule(getNewRule()); }); beforeEach(() => { diff --git a/x-pack/plugins/security_solution/cypress/e2e/dashboards/upgrade_risk_score.cy.ts b/x-pack/plugins/security_solution/cypress/e2e/dashboards/upgrade_risk_score.cy.ts index 1a6c5f294caba..a12553ba9cdba 100644 --- a/x-pack/plugins/security_solution/cypress/e2e/dashboards/upgrade_risk_score.cy.ts +++ b/x-pack/plugins/security_solution/cypress/e2e/dashboards/upgrade_risk_score.cy.ts @@ -16,7 +16,7 @@ import { } from '../../screens/entity_analytics'; import { deleteRiskScore, installLegacyRiskScoreModule } from '../../tasks/api_calls/risk_scores'; import { findSavedObjects } from '../../tasks/api_calls/risk_scores/saved_objects'; -import { createCustomRuleEnabled } from '../../tasks/api_calls/rules'; +import { createRule } from '../../tasks/api_calls/rules'; import { cleanKibana } from '../../tasks/common'; import { login, visit } from '../../tasks/login'; import { @@ -39,7 +39,7 @@ describe('Upgrade risk scores', () => { before(() => { cleanKibana(); login(); - createCustomRuleEnabled(getNewRule(), 'rule1'); + createRule({ ...getNewRule(), rule_id: 'rule1' }); }); beforeEach(() => { @@ -88,7 +88,7 @@ versions.forEach((version) => before(() => { cleanKibana(); login(); - createCustomRuleEnabled(getNewRule(), 'rule1'); + createRule({ ...getNewRule(), rule_id: 'rule1' }); }); beforeEach(() => { diff --git a/x-pack/plugins/security_solution/cypress/e2e/data_sources/create_runtime_field.cy.ts b/x-pack/plugins/security_solution/cypress/e2e/data_sources/create_runtime_field.cy.ts index e377c7482f3d3..a83129d638b73 100644 --- a/x-pack/plugins/security_solution/cypress/e2e/data_sources/create_runtime_field.cy.ts +++ b/x-pack/plugins/security_solution/cypress/e2e/data_sources/create_runtime_field.cy.ts @@ -11,7 +11,7 @@ import { openTimelineFieldsBrowser, populateTimeline } from '../../tasks/timelin import { HOSTS_URL, ALERTS_URL } from '../../urls/navigation'; -import { createCustomRuleEnabled } from '../../tasks/api_calls/rules'; +import { createRule } from '../../tasks/api_calls/rules'; import { getNewRule } from '../../objects/rule'; import { refreshPage } from '../../tasks/security_header'; @@ -34,7 +34,7 @@ describe('Create DataView runtime field', () => { it('adds field to alert table', () => { visit(ALERTS_URL); - createCustomRuleEnabled(getNewRule()); + createRule(getNewRule()); refreshPage(); waitForAlertsToPopulate(); openAlertsFieldBrowser(); diff --git a/x-pack/plugins/security_solution/cypress/e2e/detection_alerts/alerts_cell_actions.cy.ts b/x-pack/plugins/security_solution/cypress/e2e/detection_alerts/alerts_cell_actions.cy.ts index d8087294d4ed4..c4c58a9f2b218 100644 --- a/x-pack/plugins/security_solution/cypress/e2e/detection_alerts/alerts_cell_actions.cy.ts +++ b/x-pack/plugins/security_solution/cypress/e2e/detection_alerts/alerts_cell_actions.cy.ts @@ -25,7 +25,7 @@ import { filterOutAlertProperty, closeTopNAlertProperty, } from '../../tasks/alerts'; -import { createCustomRuleEnabled } from '../../tasks/api_calls/rules'; +import { createRule } from '../../tasks/api_calls/rules'; import { cleanKibana } from '../../tasks/common'; import { waitForAlertsToPopulate } from '../../tasks/create_new_rule'; import { login, visit } from '../../tasks/login'; @@ -43,7 +43,7 @@ describe('Alerts cell actions', { testIsolation: false }, () => { before(() => { cleanKibana(); login(); - createCustomRuleEnabled(getNewRule()); + createRule(getNewRule()); visit(ALERTS_URL); waitForAlertsToPopulate(); }); diff --git a/x-pack/plugins/security_solution/cypress/e2e/detection_alerts/alerts_charts.cy.ts b/x-pack/plugins/security_solution/cypress/e2e/detection_alerts/alerts_charts.cy.ts index 30e1bf39d0dcc..9ec3feac694a7 100644 --- a/x-pack/plugins/security_solution/cypress/e2e/detection_alerts/alerts_charts.cy.ts +++ b/x-pack/plugins/security_solution/cypress/e2e/detection_alerts/alerts_charts.cy.ts @@ -14,7 +14,7 @@ import { clickAlertsHistogramLegendFilterOut, selectAlertsHistogram, } from '../../tasks/alerts'; -import { createCustomRuleEnabled } from '../../tasks/api_calls/rules'; +import { createRule } from '../../tasks/api_calls/rules'; import { cleanKibana } from '../../tasks/common'; import { login, visit } from '../../tasks/login'; import { ALERTS_URL } from '../../urls/navigation'; @@ -30,7 +30,7 @@ describe('Histogram legend hover actions', { testIsolation: false }, () => { before(() => { cleanKibana(); login(); - createCustomRuleEnabled(ruleConfigs, 'new custom rule'); + createRule({ ...getNewRule(), rule_id: 'new custom rule' }); visit(ALERTS_URL); selectAlertsHistogram(); }); diff --git a/x-pack/plugins/security_solution/cypress/e2e/detection_alerts/alerts_details.cy.ts b/x-pack/plugins/security_solution/cypress/e2e/detection_alerts/alerts_details.cy.ts index af17e1bad39f5..e218207e84db8 100644 --- a/x-pack/plugins/security_solution/cypress/e2e/detection_alerts/alerts_details.cy.ts +++ b/x-pack/plugins/security_solution/cypress/e2e/detection_alerts/alerts_details.cy.ts @@ -15,7 +15,7 @@ import { import { expandFirstAlert } from '../../tasks/alerts'; import { openJsonView, openTable } from '../../tasks/alerts_details'; -import { createCustomRuleEnabled } from '../../tasks/api_calls/rules'; +import { createRule } from '../../tasks/api_calls/rules'; import { cleanKibana } from '../../tasks/common'; import { waitForAlertsToPopulate } from '../../tasks/create_new_rule'; import { esArchiverLoad, esArchiverUnload } from '../../tasks/es_archiver'; @@ -31,7 +31,7 @@ describe('Alert details with unmapped fields', { testIsolation: false }, () => { cleanKibana(); esArchiverLoad('unmapped_fields'); login(); - createCustomRuleEnabled(getUnmappedRule()); + createRule(getUnmappedRule()); visitWithoutDateRange(ALERTS_URL); waitForAlertsToPopulate(); expandFirstAlert(); @@ -59,7 +59,7 @@ describe('Alert details with unmapped fields', { testIsolation: false }, () => { }; openTable(); - cy.get(ALERT_FLYOUT).find(tablePageSelector(4)).click({ force: true }); + cy.get(ALERT_FLYOUT).find(tablePageSelector(6)).click({ force: true }); cy.get(ALERT_FLYOUT) .find(TABLE_ROWS) .last() diff --git a/x-pack/plugins/security_solution/cypress/e2e/detection_alerts/alerts_detection_callouts_index_outdated.cy.ts b/x-pack/plugins/security_solution/cypress/e2e/detection_alerts/alerts_detection_callouts_index_outdated.cy.ts index 8804979a158eb..0564618870f16 100644 --- a/x-pack/plugins/security_solution/cypress/e2e/detection_alerts/alerts_detection_callouts_index_outdated.cy.ts +++ b/x-pack/plugins/security_solution/cypress/e2e/detection_alerts/alerts_detection_callouts_index_outdated.cy.ts @@ -12,7 +12,7 @@ import { PAGE_TITLE } from '../../screens/common/page'; import { login, visitWithoutDateRange, waitForPageWithoutDateRange } from '../../tasks/login'; import { goToRuleDetails } from '../../tasks/alerts_detection_rules'; -import { createCustomRule, deleteCustomRule } from '../../tasks/api_calls/rules'; +import { createRule, deleteCustomRule } from '../../tasks/api_calls/rules'; import { getCallOut, waitForCallOutToBeShown } from '../../tasks/common/callouts'; const loadPageAsPlatformEngineerUser = (url: string) => { @@ -73,7 +73,7 @@ describe('Detections > Need Admin Callouts indicating an admin is needed to migr context('On Rule Details page', () => { beforeEach(() => { - createCustomRule(getNewRule()); + createRule({ ...getNewRule(), rule_id: 'rule_testing' }); loadPageAsPlatformEngineerUser(DETECTIONS_RULE_MANAGEMENT_URL); waitForPageTitleToBeShown(); goToRuleDetails(); @@ -123,7 +123,7 @@ describe('Detections > Need Admin Callouts indicating an admin is needed to migr context('On Rule Details page', () => { beforeEach(() => { - createCustomRule(getNewRule()); + createRule({ ...getNewRule(), rule_id: 'rule_testing' }); loadPageAsPlatformEngineerUser(DETECTIONS_RULE_MANAGEMENT_URL); waitForPageTitleToBeShown(); goToRuleDetails(); @@ -173,7 +173,7 @@ describe('Detections > Need Admin Callouts indicating an admin is needed to migr context('On Rule Details page', () => { beforeEach(() => { - createCustomRule(getNewRule()); + createRule({ ...getNewRule(), rule_id: 'rule_testing' }); loadPageAsPlatformEngineerUser(DETECTIONS_RULE_MANAGEMENT_URL); waitForPageTitleToBeShown(); goToRuleDetails(); diff --git a/x-pack/plugins/security_solution/cypress/e2e/detection_alerts/building_block_alerts.cy.ts b/x-pack/plugins/security_solution/cypress/e2e/detection_alerts/building_block_alerts.cy.ts index 0b9d77468c5db..8d831f29bd653 100644 --- a/x-pack/plugins/security_solution/cypress/e2e/detection_alerts/building_block_alerts.cy.ts +++ b/x-pack/plugins/security_solution/cypress/e2e/detection_alerts/building_block_alerts.cy.ts @@ -9,7 +9,7 @@ import { getBuildingBlockRule } from '../../objects/rule'; import { OVERVIEW_ALERTS_HISTOGRAM_EMPTY } from '../../screens/overview'; import { OVERVIEW } from '../../screens/security_header'; import { goToRuleDetails } from '../../tasks/alerts_detection_rules'; -import { createCustomRuleEnabled } from '../../tasks/api_calls/rules'; +import { createRule } from '../../tasks/api_calls/rules'; import { cleanKibana } from '../../tasks/common'; import { waitForAlertsToPopulate, waitForTheRuleToBeExecuted } from '../../tasks/create_new_rule'; import { esArchiverLoad, esArchiverUnload } from '../../tasks/es_archiver'; @@ -26,7 +26,7 @@ describe('Alerts generated by building block rules', () => { login(); }); beforeEach(() => { - createCustomRuleEnabled(getBuildingBlockRule()); + createRule(getBuildingBlockRule()); }); after(() => { esArchiverUnload('auditbeat_big'); diff --git a/x-pack/plugins/security_solution/cypress/e2e/detection_alerts/changing_alert_status.cy.ts b/x-pack/plugins/security_solution/cypress/e2e/detection_alerts/changing_alert_status.cy.ts index c638047fdad1b..c491633e33124 100644 --- a/x-pack/plugins/security_solution/cypress/e2e/detection_alerts/changing_alert_status.cy.ts +++ b/x-pack/plugins/security_solution/cypress/e2e/detection_alerts/changing_alert_status.cy.ts @@ -27,10 +27,11 @@ import { openAlerts, openFirstAlert, selectCountTable, + waitForPageFilters, sumAlertCountFromAlertCountTable, parseAlertsCountToInt, } from '../../tasks/alerts'; -import { createCustomRuleEnabled } from '../../tasks/api_calls/rules'; +import { createRule } from '../../tasks/api_calls/rules'; import { cleanKibana, deleteAlertsAndRules } from '../../tasks/common'; import { waitForAlertsToPopulate } from '../../tasks/create_new_rule'; import { esArchiverLoad, esArchiverUnload } from '../../tasks/es_archiver'; @@ -49,13 +50,14 @@ describe('Changing alert status', () => { }); context('Opening alerts', () => { beforeEach(() => { - createCustomRuleEnabled(getNewRule()); + createRule(getNewRule()); visit(ALERTS_URL); waitForAlertsToPopulate(); selectNumberOfAlerts(3); cy.get(SELECTED_ALERTS).should('have.text', `Selected 3 alerts`); closeAlerts(); waitForAlerts(); + waitForPageFilters(); selectCountTable(); }); @@ -117,12 +119,12 @@ describe('Changing alert status', () => { context('Marking alerts as acknowledged', () => { beforeEach(() => { deleteAlertsAndRules(); - createCustomRuleEnabled(getNewRule()); + createRule(getNewRule()); visit(ALERTS_URL); waitForAlertsToPopulate(); selectCountTable(); }); - it('Mark one alert as acknowledged when more than one open alerts are selected', () => { + it.skip('Mark one alert as acknowledged when more than one open alerts are selected', () => { cy.get(ALERTS_COUNT) .invoke('text') .then((alertNumberString) => { @@ -155,7 +157,7 @@ describe('Changing alert status', () => { context('Closing alerts', () => { beforeEach(() => { deleteAlertsAndRules(); - createCustomRuleEnabled(getNewRule(), '1', 100); + createRule({ ...getNewRule(), rule_id: '1', max_signals: 100 }); visit(ALERTS_URL); waitForAlertsToPopulate(); selectCountTable(); @@ -223,7 +225,7 @@ describe('Changing alert status', () => { }); }); - it('Closes one alert when more than one opened alerts are selected', () => { + it.skip('Closes one alert when more than one opened alerts are selected', () => { cy.get(ALERTS_COUNT) .invoke('text') .then((alertNumberString) => { @@ -309,12 +311,12 @@ describe('Changing alert status', () => { }); beforeEach(() => { deleteAlertsAndRules(); - createCustomRuleEnabled(getNewRule()); + createRule(getNewRule()); visit(ALERTS_URL); waitForAlertsToPopulate(); selectCountTable(); }); - it('Mark one alert as acknowledged when more than one open alerts are selected', () => { + it.skip('Mark one alert as acknowledged when more than one open alerts are selected', () => { cy.get(ALERTS_COUNT) .invoke('text') .then((alertNumberString) => { diff --git a/x-pack/plugins/security_solution/cypress/e2e/detection_alerts/cti_enrichments.cy.ts b/x-pack/plugins/security_solution/cypress/e2e/detection_alerts/cti_enrichments.cy.ts index 70898cc27e053..d84ccc3d80a82 100644 --- a/x-pack/plugins/security_solution/cypress/e2e/detection_alerts/cti_enrichments.cy.ts +++ b/x-pack/plugins/security_solution/cypress/e2e/detection_alerts/cti_enrichments.cy.ts @@ -5,7 +5,7 @@ * 2.0. */ -import { getNewThreatIndicatorRule } from '../../objects/rule'; +import { getNewThreatIndicatorRule, indicatorRuleMatchingDoc } from '../../objects/rule'; import { cleanKibana } from '../../tasks/common'; import { esArchiverLoad, esArchiverUnload } from '../../tasks/es_archiver'; import { login, visitWithoutDateRange } from '../../tasks/login'; @@ -22,7 +22,7 @@ import { import { TIMELINE_FIELD } from '../../screens/rule_details'; import { goToRuleDetails } from '../../tasks/alerts_detection_rules'; import { expandFirstAlert, setEnrichmentDates, viewThreatIntelTab } from '../../tasks/alerts'; -import { createCustomIndicatorRule } from '../../tasks/api_calls/rules'; +import { createRule } from '../../tasks/api_calls/rules'; import { openJsonView, openThreatIndicatorDetails } from '../../tasks/alerts_details'; import { DETECTIONS_RULE_MANAGEMENT_URL } from '../../urls/navigation'; @@ -34,7 +34,7 @@ describe('CTI Enrichment', () => { esArchiverLoad('threat_indicator'); esArchiverLoad('suspicious_source_event'); login(); - createCustomIndicatorRule(getNewThreatIndicatorRule()); + createRule({ ...getNewThreatIndicatorRule(), rule_id: 'rule_testing', enabled: true }); }); after(() => { @@ -49,11 +49,12 @@ describe('CTI Enrichment', () => { it('Displays enrichment matched.* fields on the timeline', () => { const expectedFields = { - 'threat.enrichments.matched.atomic': getNewThreatIndicatorRule().atomic, - 'threat.enrichments.matched.type': getNewThreatIndicatorRule().matchedType, - 'threat.enrichments.matched.field': getNewThreatIndicatorRule().indicatorMappingField, - 'threat.enrichments.matched.id': getNewThreatIndicatorRule().matchedId, - 'threat.enrichments.matched.index': getNewThreatIndicatorRule().matchedIndex, + 'threat.enrichments.matched.atomic': indicatorRuleMatchingDoc.atomic, + 'threat.enrichments.matched.type': indicatorRuleMatchingDoc.matchedType, + 'threat.enrichments.matched.field': + getNewThreatIndicatorRule().threat_mapping[0].entries[0].field, + 'threat.enrichments.matched.id': indicatorRuleMatchingDoc.matchedId, + 'threat.enrichments.matched.index': indicatorRuleMatchingDoc.matchedIndex, }; const fields = Object.keys(expectedFields) as Array; diff --git a/x-pack/plugins/security_solution/cypress/e2e/detection_alerts/detection_page_filters.cy.ts b/x-pack/plugins/security_solution/cypress/e2e/detection_alerts/detection_page_filters.cy.ts index 86e7393fd7938..528df8412da5f 100644 --- a/x-pack/plugins/security_solution/cypress/e2e/detection_alerts/detection_page_filters.cy.ts +++ b/x-pack/plugins/security_solution/cypress/e2e/detection_alerts/detection_page_filters.cy.ts @@ -9,32 +9,74 @@ import { encode } from '@kbn/rison'; import { getNewRule } from '../../objects/rule'; import { CONTROL_FRAMES, + CONTROL_FRAME_TITLE, + FILTER_GROUP_CHANGED_BANNER, + FILTER_GROUP_DISCARD_CHANGES, + FILTER_GROUP_SAVE_CHANGES_POPOVER, OPTION_LIST_LABELS, OPTION_LIST_VALUES, OPTION_SELECTABLE, } from '../../screens/common/filter_group'; -import { createCustomRuleEnabled } from '../../tasks/api_calls/rules'; +import { createRule } from '../../tasks/api_calls/rules'; import { cleanKibana } from '../../tasks/common'; import { login, visit } from '../../tasks/login'; import { ALERTS_URL } from '../../urls/navigation'; -import { APP_ID, DEFAULT_DETECTION_PAGE_FILTERS } from '../../../common/constants'; +import { DEFAULT_DETECTION_PAGE_FILTERS } from '../../../common/constants'; import { formatPageFilterSearchParam } from '../../../common/utils/format_page_filter_search_param'; import { + closePageFilterPopover, markAcknowledgedFirstAlert, + openPageFilterPopover, resetFilters, selectCountTable, + visitAlertsPageWithCustomFilters, waitForAlerts, waitForPageFilters, } from '../../tasks/alerts'; import { ALERTS_COUNT } from '../../screens/alerts'; import { navigateFromHeaderTo } from '../../tasks/security_header'; import { ALERTS, CASES } from '../../screens/security_header'; +import { + addNewFilterGroupControlValues, + deleteFilterGroupControl, + discardFilterGroupControls, + editFilterGroupControls, + saveFilterGroupControls, +} from '../../tasks/common/filter_group'; +const customFilters = [ + { + fieldName: 'kibana.alert.workflow_status', + title: 'Workflow Status', + }, + { + fieldName: 'kibana.alert.severity', + title: 'Severity', + }, + { + fieldName: 'user.name', + title: 'User Name', + }, + { + fieldName: 'process.name', + title: 'ProcessName', + }, + { + fieldName: 'event.module', + title: 'EventModule', + }, + { + fieldName: 'agent.type', + title: 'AgentType', + }, + { + fieldName: 'kibana.alert.rule.name', + title: 'Rule Name', + }, +]; const assertFilterControlsWithFilterObject = (filterObject = DEFAULT_DETECTION_PAGE_FILTERS) => { - cy.log(JSON.stringify({ filterObject })); - cy.get(CONTROL_FRAMES).should((sub) => { - expect(sub.length).eq(4); + expect(sub.length).eq(filterObject.length); }); cy.get(OPTION_LIST_LABELS).should((sub) => { @@ -43,34 +85,30 @@ const assertFilterControlsWithFilterObject = (filterObject = DEFAULT_DETECTION_P }); }); - cy.get(OPTION_LIST_VALUES).should((sub) => { - filterObject.forEach((filter, idx) => { - expect(sub.eq(idx).text().replace(',', '')).eq( - filter.selectedOptions && filter.selectedOptions.length > 0 - ? filter.selectedOptions.join('') - : '' - ); + filterObject.forEach((filter, idx) => { + cy.get(OPTION_LIST_VALUES(idx)).should((sub) => { + expect(sub.text().replace(',', '')).satisfy((txt: string) => { + return txt.startsWith( + filter.selectedOptions && filter.selectedOptions.length > 0 + ? filter.selectedOptions.join('') + : '' + ); + }); }); }); }; -// Skipped because this featured is behind feature flag -describe.skip('Detections : Page Filters', () => { +describe('Detections : Page Filters', { testIsolation: false }, () => { before(() => { cleanKibana(); login(); - createCustomRuleEnabled(getNewRule(), 'custom_rule_filters'); - }); - - beforeEach(() => { - login(); + createRule({ ...getNewRule(), rule_id: 'custom_rule_filters' }); visit(ALERTS_URL); waitForAlerts(); waitForPageFilters(); }); afterEach(() => { - cy.clearLocalStorage(`${APP_ID}.pageFilters`); resetFilters(); }); @@ -78,6 +116,41 @@ describe.skip('Detections : Page Filters', () => { assertFilterControlsWithFilterObject(); }); + context('Alert Page Filters Customization ', { testIsolation: false }, () => { + it('Add New Controls', () => { + const fieldName = 'event.module'; + const label = 'EventModule'; + editFilterGroupControls(); + addNewFilterGroupControlValues({ + fieldName, + label, + }); + cy.get(CONTROL_FRAME_TITLE).should('contain.text', label); + cy.get(FILTER_GROUP_SAVE_CHANGES_POPOVER).should('be.visible'); + cy.get(FILTER_GROUP_DISCARD_CHANGES).click(); + cy.get(CONTROL_FRAME_TITLE).should('not.contain.text', label); + }); + it('Delete Controls', () => { + waitForPageFilters(); + editFilterGroupControls(); + deleteFilterGroupControl(3); + cy.get(CONTROL_FRAMES).should((sub) => { + expect(sub.length).lt(4); + }); + cy.get(FILTER_GROUP_DISCARD_CHANGES).trigger('click', { force: true }); + }); + it('should not sync to the URL in edit mode but only in view mode', () => { + cy.url().then((urlString) => { + editFilterGroupControls(); + addNewFilterGroupControlValues({ fieldName: 'event.module', label: 'Event Module' }); + cy.url().should('eq', urlString); + saveFilterGroupControls(); + cy.url().should('not.eq', urlString); + }); + }); + it('should not sync to the localstorage', () => {}); + }); + it('Page filters are loaded with custom values provided in the URL', () => { const NEW_FILTERS = DEFAULT_DETECTION_PAGE_FILTERS.map((filter) => { if (filter.title === 'Status') { @@ -124,27 +197,13 @@ describe.skip('Detections : Page Filters', () => { }); }); }); - }); - - it('URL is updated when ever page filters are loaded', (done) => { - cy.on('url:changed', () => { - const NEW_FILTERS = DEFAULT_DETECTION_PAGE_FILTERS.map((filter) => { - if (filter.title === 'Status') { - filter.selectedOptions = []; - } - return filter; - }); - cy.url().should('have.text', formatPageFilterSearchParam(NEW_FILTERS)); - done(); - }); - cy.get(OPTION_LIST_VALUES).eq(0).click(); - // unselect status open - cy.get(OPTION_SELECTABLE(0, 'open')).trigger('click', { force: true }); + cy.get(FILTER_GROUP_SAVE_CHANGES_POPOVER).should('be.visible'); }); it(`Alert list is updated when the alerts are updated`, () => { // mark status of one alert to be acknowledged + cy.visit(ALERTS_URL); selectCountTable(); cy.get(ALERTS_COUNT) .invoke('text') @@ -152,7 +211,7 @@ describe.skip('Detections : Page Filters', () => { const originalAlertCount = noOfAlerts.split(' ')[0]; markAcknowledgedFirstAlert(); waitForAlerts(); - cy.get(OPTION_LIST_VALUES).eq(0).click(); + cy.get(OPTION_LIST_VALUES(0)).click(); cy.get(OPTION_SELECTABLE(0, 'acknowledged')).should('be.visible'); cy.get(ALERTS_COUNT) .invoke('text') @@ -162,32 +221,36 @@ describe.skip('Detections : Page Filters', () => { }); }); - it(`URL is updated when filters are updated`, (done) => { - const NEW_FILTERS = DEFAULT_DETECTION_PAGE_FILTERS.map((filter) => { - if (filter.title === 'Severity') { - filter.selectedOptions = ['high']; - } - return filter; - }); + it(`URL is updated when filters are updated`, () => { + cy.visit(ALERTS_URL); - cy.on('url:changed', () => { - // we want assertion to run only once URL Changes. - cy.url().should('have.text', formatPageFilterSearchParam(NEW_FILTERS)); - done(); + cy.on('url:changed', (urlString) => { + const NEW_FILTERS = DEFAULT_DETECTION_PAGE_FILTERS.map((filter) => { + if (filter.title === 'Severity') { + filter.selectedOptions = ['high']; + } + return filter; + }); + const expectedVal = encode(formatPageFilterSearchParam(NEW_FILTERS)); + expect(urlString).to.contain.text(expectedVal); }); - cy.get(OPTION_LIST_VALUES).eq(1).click(); + + openPageFilterPopover(1); cy.get(OPTION_SELECTABLE(1, 'high')).should('be.visible'); cy.get(OPTION_SELECTABLE(1, 'high')).click({ force: true }); + closePageFilterPopover(1); }); it(`Filters are restored from localstorage when user navigates back to the page.`, () => { // change severity filter to high - cy.get(OPTION_LIST_VALUES).eq(1).click(); + cy.visit(ALERTS_URL); + cy.get(OPTION_LIST_VALUES(1)).click(); cy.get(OPTION_SELECTABLE(1, 'high')).should('be.visible'); cy.get(OPTION_SELECTABLE(1, 'high')).click({ force: true }); // high should be scuccessfully selected. - cy.get(OPTION_LIST_VALUES).eq(1).contains('high'); + cy.get(OPTION_LIST_VALUES(1)).contains('high'); + waitForPageFilters(); navigateFromHeaderTo(CASES); // navigate away from alert page @@ -195,7 +258,36 @@ describe.skip('Detections : Page Filters', () => { waitForPageFilters(); - cy.get(OPTION_LIST_VALUES).eq(0).contains('open'); // status should be Open as previously selected - cy.get(OPTION_LIST_VALUES).eq(1).contains('high'); // severity should be low as previously selected + cy.get(OPTION_LIST_VALUES(0)).contains('open'); // status should be Open as previously selected + cy.get(OPTION_LIST_VALUES(1)).contains('high'); // severity should be low as previously selected + }); + + it('Custom filters from URLS are populated & changed banner is displayed', () => { + visitAlertsPageWithCustomFilters(customFilters); + + assertFilterControlsWithFilterObject(customFilters); + + cy.get(FILTER_GROUP_CHANGED_BANNER).should('be.visible'); + }); + + it('Changed banner should hide on saving changes', () => { + visitAlertsPageWithCustomFilters(customFilters); + + cy.get(FILTER_GROUP_CHANGED_BANNER).should('be.visible'); + saveFilterGroupControls(); + cy.get(FILTER_GROUP_CHANGED_BANNER).should('not.exist'); + }); + it('Changed banner should hide on discarding changes', () => { + visitAlertsPageWithCustomFilters(customFilters); + + cy.get(FILTER_GROUP_CHANGED_BANNER).should('be.visible'); + discardFilterGroupControls(); + cy.get(FILTER_GROUP_CHANGED_BANNER).should('not.exist'); + }); + + it('Changed banner should hide on Reset', () => { + visitAlertsPageWithCustomFilters(customFilters); + resetFilters(); + cy.get(FILTER_GROUP_CHANGED_BANNER).should('not.exist'); }); }); diff --git a/x-pack/plugins/security_solution/cypress/e2e/detection_alerts/enrichments.cy.ts b/x-pack/plugins/security_solution/cypress/e2e/detection_alerts/enrichments.cy.ts index de610f3fa1808..a5e4289751d15 100644 --- a/x-pack/plugins/security_solution/cypress/e2e/detection_alerts/enrichments.cy.ts +++ b/x-pack/plugins/security_solution/cypress/e2e/detection_alerts/enrichments.cy.ts @@ -17,7 +17,7 @@ import { import { ENRICHED_DATA_ROW } from '../../screens/alerts_details'; import { esArchiverLoad, esArchiverUnload } from '../../tasks/es_archiver'; -import { createCustomRuleEnabled } from '../../tasks/api_calls/rules'; +import { createRule } from '../../tasks/api_calls/rules'; import { cleanKibana, deleteAlertsAndRules } from '../../tasks/common'; import { waitForAlertsToPopulate } from '../../tasks/create_new_rule'; import { @@ -45,7 +45,7 @@ describe('Enrichment', () => { beforeEach(() => { esArchiverLoad('risk_hosts'); deleteAlertsAndRules(); - createCustomRuleEnabled(getNewRule(), 'rule1'); + createRule({ ...getNewRule(), rule_id: 'rule1' }); visit(ALERTS_URL); waitForAlertsToPopulate(); }); diff --git a/x-pack/plugins/security_solution/cypress/e2e/detection_alerts/expandable_flyout/alert_details_right_panel.cy.ts b/x-pack/plugins/security_solution/cypress/e2e/detection_alerts/expandable_flyout/alert_details_right_panel.cy.ts index 2c2340497a586..465977616eea9 100644 --- a/x-pack/plugins/security_solution/cypress/e2e/detection_alerts/expandable_flyout/alert_details_right_panel.cy.ts +++ b/x-pack/plugins/security_solution/cypress/e2e/detection_alerts/expandable_flyout/alert_details_right_panel.cy.ts @@ -22,7 +22,7 @@ import { } from '../../../tasks/alert_details_expandable_flyout'; import { cleanKibana } from '../../../tasks/common'; import { login, visit } from '../../../tasks/login'; -import { createCustomRuleEnabled } from '../../../tasks/api_calls/rules'; +import { createRule } from '../../../tasks/api_calls/rules'; import { getNewRule } from '../../../objects/rule'; import { ALERTS_URL } from '../../../urls/navigation'; import { waitForAlertsToPopulate } from '../../../tasks/create_new_rule'; @@ -33,7 +33,7 @@ describe.skip('Alert details expandable flyout right panel', { testIsolation: fa before(() => { cleanKibana(); login(); - createCustomRuleEnabled(getNewRule()); + createRule(getNewRule()); visit(ALERTS_URL); waitForAlertsToPopulate(); expandFirstAlertExpandableFlyout(); diff --git a/x-pack/plugins/security_solution/cypress/e2e/detection_alerts/investigate_in_timeline.cy.ts b/x-pack/plugins/security_solution/cypress/e2e/detection_alerts/investigate_in_timeline.cy.ts index 15052359a54a9..e2d1204be84b2 100644 --- a/x-pack/plugins/security_solution/cypress/e2e/detection_alerts/investigate_in_timeline.cy.ts +++ b/x-pack/plugins/security_solution/cypress/e2e/detection_alerts/investigate_in_timeline.cy.ts @@ -10,7 +10,7 @@ import { getNewRule } from '../../objects/rule'; import { PROVIDER_BADGE, QUERY_TAB_BUTTON, TIMELINE_TITLE } from '../../screens/timeline'; import { expandFirstAlert, investigateFirstAlertInTimeline } from '../../tasks/alerts'; -import { createCustomRuleEnabled } from '../../tasks/api_calls/rules'; +import { createRule } from '../../tasks/api_calls/rules'; import { cleanKibana } from '../../tasks/common'; import { waitForAlertsToPopulate } from '../../tasks/create_new_rule'; import { login, visit } from '../../tasks/login'; @@ -31,7 +31,7 @@ describe('Investigate in timeline', { testIsolation: false }, () => { before(() => { cleanKibana(); login(); - createCustomRuleEnabled(getNewRule()); + createRule(getNewRule()); visit(ALERTS_URL); waitForAlertsToPopulate(); }); diff --git a/x-pack/plugins/security_solution/cypress/e2e/detection_alerts/missing_privileges_callout.cy.ts b/x-pack/plugins/security_solution/cypress/e2e/detection_alerts/missing_privileges_callout.cy.ts index d25dfe45c4009..f6fb4a904dd0a 100644 --- a/x-pack/plugins/security_solution/cypress/e2e/detection_alerts/missing_privileges_callout.cy.ts +++ b/x-pack/plugins/security_solution/cypress/e2e/detection_alerts/missing_privileges_callout.cy.ts @@ -12,7 +12,7 @@ import { PAGE_TITLE } from '../../screens/common/page'; import { login, visitWithoutDateRange, waitForPageWithoutDateRange } from '../../tasks/login'; import { goToRuleDetails } from '../../tasks/alerts_detection_rules'; -import { createCustomRule, deleteCustomRule } from '../../tasks/api_calls/rules'; +import { createRule, deleteCustomRule } from '../../tasks/api_calls/rules'; import { getCallOut, waitForCallOutToBeShown, dismissCallOut } from '../../tasks/common/callouts'; const loadPageAsReadOnlyUser = (url: string) => { @@ -71,7 +71,7 @@ describe('Detections > Callouts', () => { context('On Rule Details page', () => { beforeEach(() => { - createCustomRule(getNewRule()); + createRule(getNewRule()); loadPageAsReadOnlyUser(DETECTIONS_RULE_MANAGEMENT_URL); waitForPageTitleToBeShown(); goToRuleDetails(); @@ -121,7 +121,7 @@ describe('Detections > Callouts', () => { context('On Rule Details page', () => { beforeEach(() => { - createCustomRule(getNewRule()); + createRule(getNewRule()); loadPageAsPlatformEngineer(DETECTIONS_RULE_MANAGEMENT_URL); waitForPageTitleToBeShown(); goToRuleDetails(); diff --git a/x-pack/plugins/security_solution/cypress/e2e/detection_alerts/navigation.cy.ts b/x-pack/plugins/security_solution/cypress/e2e/detection_alerts/navigation.cy.ts index 94d2bc8256a4f..60cf6c4f1195c 100644 --- a/x-pack/plugins/security_solution/cypress/e2e/detection_alerts/navigation.cy.ts +++ b/x-pack/plugins/security_solution/cypress/e2e/detection_alerts/navigation.cy.ts @@ -6,12 +6,11 @@ */ import { expandFirstAlert, waitForAlerts } from '../../tasks/alerts'; -import { createCustomRuleEnabled } from '../../tasks/api_calls/rules'; +import { createRule } from '../../tasks/api_calls/rules'; import { cleanKibana } from '../../tasks/common'; import { login, visit } from '../../tasks/login'; import { getNewRule } from '../../objects/rule'; -import type { CustomRule } from '../../objects/rule'; import { ALERTS_URL } from '../../urls/navigation'; import { @@ -24,12 +23,11 @@ import { OPEN_ALERT_DETAILS_PAGE } from '../../screens/alerts_details'; describe('Alert Details Page Navigation', () => { describe('navigating to alert details page', () => { - let rule: CustomRule; + const rule = getNewRule(); before(() => { - rule = getNewRule(); cleanKibana(); login(); - createCustomRuleEnabled(rule, 'rule1'); + createRule({ ...rule, rule_id: 'rule1' }); }); describe('context menu', () => { diff --git a/x-pack/plugins/security_solution/cypress/e2e/detection_alerts/resolver.cy.ts b/x-pack/plugins/security_solution/cypress/e2e/detection_alerts/resolver.cy.ts index c2436f3f2de9a..5702cb3d5d7b1 100644 --- a/x-pack/plugins/security_solution/cypress/e2e/detection_alerts/resolver.cy.ts +++ b/x-pack/plugins/security_solution/cypress/e2e/detection_alerts/resolver.cy.ts @@ -8,7 +8,7 @@ import { ANALYZER_NODE } from '../../screens/alerts'; import { openAnalyzerForFirstAlertInTimeline } from '../../tasks/alerts'; -import { createCustomRuleEnabled } from '../../tasks/api_calls/rules'; +import { createRule } from '../../tasks/api_calls/rules'; import { getNewRule } from '../../objects/rule'; import { cleanKibana } from '../../tasks/common'; import { setStartDate } from '../../tasks/date_picker'; @@ -21,7 +21,7 @@ describe('Analyze events view for alerts', () => { before(() => { cleanKibana(); login(); - createCustomRuleEnabled(getNewRule()); + createRule(getNewRule()); }); beforeEach(() => { visit(ALERTS_URL); diff --git a/x-pack/plugins/security_solution/cypress/e2e/detection_response/open_alerts_in_timeline.cy.ts b/x-pack/plugins/security_solution/cypress/e2e/detection_response/open_alerts_in_timeline.cy.ts index 50d7ebf1baf02..b52b2d12d5181 100644 --- a/x-pack/plugins/security_solution/cypress/e2e/detection_response/open_alerts_in_timeline.cy.ts +++ b/x-pack/plugins/security_solution/cypress/e2e/detection_response/open_alerts_in_timeline.cy.ts @@ -12,7 +12,7 @@ import { USER_TABLE_ROW_TOTAL_ALERTS, } from '../../screens/detection_response'; import { QUERY_TAB_BUTTON } from '../../screens/timeline'; -import { createCustomRuleEnabled } from '../../tasks/api_calls/rules'; +import { createRule } from '../../tasks/api_calls/rules'; import { cleanKibana } from '../../tasks/common'; import { login, visit } from '../../tasks/login'; import { closeTimeline } from '../../tasks/timeline'; @@ -25,7 +25,7 @@ describe.skip('Detection response view', () => { before(() => { cleanKibana(); login(); - createCustomRuleEnabled(getNewRule()); + createRule(getNewRule()); visit(DETECTIONS_RESPONSE_URL); }); diff --git a/x-pack/plugins/security_solution/cypress/e2e/detection_rules/all_rules_read_only.cy.ts b/x-pack/plugins/security_solution/cypress/e2e/detection_rules/all_rules_read_only.cy.ts index b0915305e23ba..26a5990ee83a8 100644 --- a/x-pack/plugins/security_solution/cypress/e2e/detection_rules/all_rules_read_only.cy.ts +++ b/x-pack/plugins/security_solution/cypress/e2e/detection_rules/all_rules_read_only.cy.ts @@ -14,7 +14,7 @@ import { } from '../../screens/alerts_detection_rules'; import { VALUE_LISTS_MODAL_ACTIVATOR } from '../../screens/lists'; import { waitForRulesTableToBeLoaded } from '../../tasks/alerts_detection_rules'; -import { createCustomRule } from '../../tasks/api_calls/rules'; +import { createRule } from '../../tasks/api_calls/rules'; import { cleanKibana } from '../../tasks/common'; import { dismissCallOut, getCallOut, waitForCallOutToBeShown } from '../../tasks/common/callouts'; import { login, visitWithoutDateRange } from '../../tasks/login'; @@ -25,7 +25,7 @@ const MISSING_PRIVILEGES_CALLOUT = 'missing-user-privileges'; describe('All rules - read only', () => { before(() => { cleanKibana(); - createCustomRule(getNewRule(), '1'); + createRule({ ...getNewRule(), rule_id: '1' }); login(ROLES.reader); }); diff --git a/x-pack/plugins/security_solution/cypress/e2e/detection_rules/bulk_edit_rules.cy.ts b/x-pack/plugins/security_solution/cypress/e2e/detection_rules/bulk_edit_rules.cy.ts index f5219e2b152e5..2329d6d65c424 100644 --- a/x-pack/plugins/security_solution/cypress/e2e/detection_rules/bulk_edit_rules.cy.ts +++ b/x-pack/plugins/security_solution/cypress/e2e/detection_rules/bulk_edit_rules.cy.ts @@ -76,14 +76,7 @@ import { hasIndexPatterns, getDetails } from '../../tasks/rule_details'; import { login, visitWithoutDateRange } from '../../tasks/login'; import { SECURITY_DETECTIONS_RULES_URL } from '../../urls/navigation'; -import { - createCustomRule, - createMachineLearningRule, - createCustomIndicatorRule, - createEventCorrelationRule, - createThresholdRule, - createNewTermsRule, -} from '../../tasks/api_calls/rules'; +import { createRule } from '../../tasks/api_calls/rules'; import { loadPrepackagedTimelineTemplates } from '../../tasks/api_calls/timelines'; import { cleanKibana, resetRulesTableState, deleteAlertsAndRules } from '../../tasks/common'; @@ -95,7 +88,6 @@ import { getMachineLearningRule, getNewTermsRule, } from '../../objects/rule'; -import { getIndicatorMatchTimelineTemplate } from '../../objects/timeline'; import { esArchiverResetKibana } from '../../tasks/es_archiver'; import { getAvailablePrebuiltRulesCount } from '../../tasks/api_calls/prebuilt_rules'; @@ -109,7 +101,6 @@ const prePopulatedTags = ['test-default-tag-1', 'test-default-tag-2']; const expectedNumberOfCustomRulesToBeEdited = 6; const expectedNumberOfMachineLearningRulesToBeEdited = 1; -const timelineTemplate = getIndicatorMatchTimelineTemplate(); /** * total number of custom rules that are not Machine learning */ @@ -117,12 +108,11 @@ const expectedNumberOfNotMLRules = expectedNumberOfCustomRulesToBeEdited - expectedNumberOfMachineLearningRulesToBeEdited; const numberOfRulesPerPage = 5; -const indexDataSource = { index: prePopulatedIndexPatterns, type: 'indexPatterns' } as const; - const defaultRuleData = { - dataSource: indexDataSource, + index: prePopulatedIndexPatterns, tags: prePopulatedTags, - timeline: timelineTemplate, + timeline_title: 'Generic Threat Match Timeline', + timeline_id: '495ad7a7-316e-4544-8a0f-9c098daee76e', }; describe('Detection rules, bulk edit', () => { @@ -135,19 +125,17 @@ describe('Detection rules, bulk edit', () => { resetRulesTableState(); deleteAlertsAndRules(); esArchiverResetKibana(); - createCustomRule( - { - ...getNewRule(), - name: RULE_NAME, - ...defaultRuleData, - }, - '1' - ); - createEventCorrelationRule({ ...getEqlRule(), ...defaultRuleData }, '2'); - createMachineLearningRule({ ...getMachineLearningRule(), ...defaultRuleData }); - createCustomIndicatorRule({ ...getNewThreatIndicatorRule(), ...defaultRuleData }, '4'); - createThresholdRule({ ...getNewThresholdRule(), ...defaultRuleData }, '5'); - createNewTermsRule({ ...getNewTermsRule(), ...defaultRuleData }, '6'); + createRule({ + ...getNewRule(), + name: RULE_NAME, + ...defaultRuleData, + rule_id: '1', + }); + createRule({ ...getEqlRule(), ...defaultRuleData, rule_id: '2' }); + createRule({ ...getMachineLearningRule(), tags: ['test-default-tag-1', 'test-default-tag-2'] }); + createRule({ ...getNewThreatIndicatorRule(), ...defaultRuleData, rule_id: '4' }); + createRule({ ...getNewThresholdRule(), ...defaultRuleData, rule_id: '5' }); + createRule({ ...getNewTermsRule(), ...defaultRuleData, rule_id: '6' }); visitWithoutDateRange(SECURITY_DETECTIONS_RULES_URL); diff --git a/x-pack/plugins/security_solution/cypress/e2e/detection_rules/bulk_edit_rules_actions.cy.ts b/x-pack/plugins/security_solution/cypress/e2e/detection_rules/bulk_edit_rules_actions.cy.ts index 8de71890326d6..875613e18e7c4 100644 --- a/x-pack/plugins/security_solution/cypress/e2e/detection_rules/bulk_edit_rules_actions.cy.ts +++ b/x-pack/plugins/security_solution/cypress/e2e/detection_rules/bulk_edit_rules_actions.cy.ts @@ -41,15 +41,7 @@ import { esArchiverResetKibana } from '../../tasks/es_archiver'; import { SECURITY_DETECTIONS_RULES_URL } from '../../urls/navigation'; -import { - createMachineLearningRule, - createCustomIndicatorRule, - createEventCorrelationRule, - createThresholdRule, - createNewTermsRule, - createSavedQueryRule, - createCustomRuleEnabled, -} from '../../tasks/api_calls/rules'; +import { createRule } from '../../tasks/api_calls/rules'; import { createSlackConnector } from '../../tasks/api_calls/connectors'; import { @@ -93,23 +85,21 @@ describe('Detection rules, bulk edit of rule actions', () => { }, ]; - createCustomRuleEnabled( - { - ...getNewRule(), - name: ruleNameToAssert, - }, - '1', - 500, - actions - ); + createRule({ + ...getNewRule(), + name: ruleNameToAssert, + rule_id: '1', + max_signals: 500, + actions, + }); }); - createEventCorrelationRule(getEqlRule(), '2'); - createMachineLearningRule(getMachineLearningRule(), '3'); - createCustomIndicatorRule(getNewThreatIndicatorRule(), '4'); - createThresholdRule(getNewThresholdRule(), '5'); - createNewTermsRule(getNewTermsRule(), '6'); - createSavedQueryRule({ ...getNewRule(), savedId: 'mocked' }, '7'); + createRule({ ...getEqlRule(), rule_id: '2' }); + createRule({ ...getMachineLearningRule(), rule_id: '3' }); + createRule({ ...getNewThreatIndicatorRule(), rule_id: '4' }); + createRule({ ...getNewThresholdRule(), rule_id: '5' }); + createRule({ ...getNewTermsRule(), rule_id: '6' }); + createRule({ ...getNewRule(), saved_id: 'mocked', rule_id: '7' }); createSlackConnector(); }); diff --git a/x-pack/plugins/security_solution/cypress/e2e/detection_rules/bulk_edit_rules_data_view.cy.ts b/x-pack/plugins/security_solution/cypress/e2e/detection_rules/bulk_edit_rules_data_view.cy.ts index 4644ed8a436d4..3f1f99c8e3646 100644 --- a/x-pack/plugins/security_solution/cypress/e2e/detection_rules/bulk_edit_rules_data_view.cy.ts +++ b/x-pack/plugins/security_solution/cypress/e2e/detection_rules/bulk_edit_rules_data_view.cy.ts @@ -32,14 +32,7 @@ import { hasIndexPatterns, getDetails, assertDetailsNotExist } from '../../tasks import { login, visitWithoutDateRange } from '../../tasks/login'; import { SECURITY_DETECTIONS_RULES_URL } from '../../urls/navigation'; -import { - createCustomRule, - createCustomIndicatorRule, - createEventCorrelationRule, - createThresholdRule, - createNewTermsRule, - createSavedQueryRule, -} from '../../tasks/api_calls/rules'; +import { createRule } from '../../tasks/api_calls/rules'; import { cleanKibana, deleteAlertsAndRules, postDataView } from '../../tasks/common'; import { @@ -58,12 +51,6 @@ const expectedIndexPatterns = ['index-1-*', 'index-2-*']; const expectedNumberOfCustomRulesToBeEdited = 6; -const dataViewDataSource = { dataView: DATA_VIEW_ID, type: 'dataView' } as const; - -const dataViewRuleData = { - dataSource: dataViewDataSource, -}; - describe('Bulk editing index patterns of rules with a data view only', () => { before(() => { cleanKibana(); @@ -75,12 +62,33 @@ describe('Bulk editing index patterns of rules with a data view only', () => { postDataView(DATA_VIEW_ID); - createCustomRule({ ...getNewRule(), ...dataViewRuleData }, '1'); - createEventCorrelationRule({ ...getEqlRule(), ...dataViewRuleData }, '2'); - createCustomIndicatorRule({ ...getNewThreatIndicatorRule(), ...dataViewRuleData }, '3'); - createThresholdRule({ ...getNewThresholdRule(), ...dataViewRuleData }, '4'); - createNewTermsRule({ ...getNewTermsRule(), ...dataViewRuleData }, '5'); - createSavedQueryRule({ ...getNewRule(), ...dataViewRuleData, savedId: 'mocked' }, '6'); + createRule({ ...getNewRule(), index: undefined, data_view_id: DATA_VIEW_ID, rule_id: '1' }); + createRule({ ...getEqlRule(), index: undefined, data_view_id: DATA_VIEW_ID, rule_id: '2' }); + createRule({ + ...getNewThreatIndicatorRule(), + index: undefined, + data_view_id: DATA_VIEW_ID, + rule_id: '3', + }); + createRule({ + ...getNewThresholdRule(), + index: undefined, + data_view_id: DATA_VIEW_ID, + rule_id: '4', + }); + createRule({ + ...getNewTermsRule(), + index: undefined, + data_view_id: DATA_VIEW_ID, + rule_id: '5', + }); + createRule({ + ...getNewRule(), + index: undefined, + data_view_id: DATA_VIEW_ID, + saved_id: 'mocked', + rule_id: '6', + }); visitWithoutDateRange(SECURITY_DETECTIONS_RULES_URL); @@ -197,17 +205,12 @@ describe('Bulk editing index patterns of rules with index patterns and rules wit postDataView(DATA_VIEW_ID); - createCustomRule({ ...getNewRule(), ...dataViewRuleData }, '1'); - createCustomRule( - { - ...getNewRule(), - dataSource: { - type: 'indexPatterns', - index: ['test-index-1-*'], - }, - }, - '2' - ); + createRule({ ...getNewRule(), index: undefined, data_view_id: DATA_VIEW_ID, rule_id: '1' }); + createRule({ + ...getNewRule(), + index: ['test-index-1-*'], + rule_id: '2', + }); visitWithoutDateRange(SECURITY_DETECTIONS_RULES_URL); diff --git a/x-pack/plugins/security_solution/cypress/e2e/detection_rules/custom_query_rule.cy.ts b/x-pack/plugins/security_solution/cypress/e2e/detection_rules/custom_query_rule.cy.ts index 053eda0ab98c4..809b0dd23cc06 100644 --- a/x-pack/plugins/security_solution/cypress/e2e/detection_rules/custom_query_rule.cy.ts +++ b/x-pack/plugins/security_solution/cypress/e2e/detection_rules/custom_query_rule.cy.ts @@ -6,13 +6,7 @@ */ import { ruleFields } from '../../data/detection_engine'; -import { - getNewRule, - getExistingRule, - getIndexPatterns, - getEditedRule, - getNewOverrideRule, -} from '../../objects/rule'; +import { getNewRule, getExistingRule, getEditedRule, getNewOverrideRule } from '../../objects/rule'; import { getTimeline } from '../../objects/timeline'; import { ALERTS_COUNT, ALERT_GRID_CELL } from '../../screens/alerts'; @@ -77,7 +71,7 @@ import { goToRuleDetails, selectNumberOfRules, } from '../../tasks/alerts_detection_rules'; -import { createCustomRuleEnabled } from '../../tasks/api_calls/rules'; +import { createRule } from '../../tasks/api_calls/rules'; import { createTimeline } from '../../tasks/api_calls/timelines'; import { cleanKibana, deleteAlertsAndRules, deleteConnectors } from '../../tasks/common'; import { addEmailConnectorAndRuleAction } from '../../tasks/common/rule_actions'; @@ -240,9 +234,9 @@ describe('Custom query rules', () => { context('Deletion', () => { beforeEach(() => { deleteAlertsAndRules(); - createCustomRuleEnabled(getNewRule(), 'rule1'); - createCustomRuleEnabled(getNewOverrideRule(), 'rule2'); - createCustomRuleEnabled(getExistingRule(), 'rule3'); + createRule({ ...getNewRule(), rule_id: 'rule1', enabled: true, max_signals: 500 }); + createRule({ ...getNewOverrideRule(), rule_id: 'rule2', enabled: true, max_signals: 500 }); + createRule({ ...getExistingRule(), rule_id: 'rule3', enabled: true }); visit(DETECTIONS_RULE_MANAGEMENT_URL); }); @@ -347,17 +341,12 @@ describe('Custom query rules', () => { context('Edition', () => { const rule = getEditedRule(); const expectedEditedtags = rule.tags?.join(''); - const expectedEditedIndexPatterns = - rule.dataSource.type === 'indexPatterns' && - rule.dataSource.index && - rule.dataSource.index.length - ? rule.dataSource.index - : getIndexPatterns(); + const expectedEditedIndexPatterns = rule.index; before(() => { deleteAlertsAndRules(); deleteConnectors(); - createCustomRuleEnabled(getExistingRule(), 'rule1'); + createRule({ ...getExistingRule(), rule_id: 'rule1', enabled: true }); }); beforeEach(() => { visit(DETECTIONS_RULE_MANAGEMENT_URL); @@ -373,7 +362,7 @@ describe('Custom query rules', () => { cy.wait('@fetchRuleDetails').then(({ response }) => { cy.wrap(response?.statusCode).should('eql', 200); - cy.wrap(response?.body.max_signals).should('eql', getExistingRule().maxSignals); + cy.wrap(response?.body.max_signals).should('eql', getExistingRule().max_signals); cy.wrap(response?.body.enabled).should('eql', false); }); }); @@ -384,13 +373,9 @@ describe('Custom query rules', () => { editFirstRule(); // expect define step to populate - cy.get(CUSTOM_QUERY_INPUT).should('have.value', existingRule.customQuery); - if ( - existingRule.dataSource.type === 'indexPatterns' && - existingRule.dataSource.index.length > 0 - ) { - cy.get(DEFINE_INDEX_INPUT).should('have.text', existingRule.dataSource.index.join('')); - } + cy.get(CUSTOM_QUERY_INPUT).should('have.value', existingRule.query); + + cy.get(DEFINE_INDEX_INPUT).should('have.text', existingRule.index?.join('')); goToAboutStepTab(); @@ -398,8 +383,8 @@ describe('Custom query rules', () => { cy.get(RULE_NAME_INPUT).invoke('val').should('eql', existingRule.name); cy.get(RULE_DESCRIPTION_INPUT).should('have.text', existingRule.description); cy.get(TAGS_FIELD).should('have.text', existingRule.tags?.join('')); - cy.get(SEVERITY_DROPDOWN).should('have.text', existingRule.severity); - cy.get(DEFAULT_RISK_SCORE_INPUT).invoke('val').should('eql', existingRule.riskScore); + cy.get(SEVERITY_DROPDOWN).should('have.text', 'High'); + cy.get(DEFAULT_RISK_SCORE_INPUT).invoke('val').should('eql', `${existingRule.risk_score}`); goToScheduleStepTab(); @@ -433,14 +418,14 @@ describe('Custom query rules', () => { cy.wait('@getRule').then(({ response }) => { cy.wrap(response?.statusCode).should('eql', 200); // ensure that editing rule does not modify max_signals - cy.wrap(response?.body.max_signals).should('eql', existingRule.maxSignals); + cy.wrap(response?.body.max_signals).should('eql', existingRule.max_signals); }); cy.get(RULE_NAME_HEADER).should('contain', `${getEditedRule().name}`); cy.get(ABOUT_RULE_DESCRIPTION).should('have.text', getEditedRule().description); cy.get(ABOUT_DETAILS).within(() => { - getDetails(SEVERITY_DETAILS).should('have.text', getEditedRule().severity); - getDetails(RISK_SCORE_DETAILS).should('have.text', getEditedRule().riskScore); + getDetails(SEVERITY_DETAILS).should('have.text', 'Medium'); + getDetails(RISK_SCORE_DETAILS).should('have.text', `${getEditedRule().risk_score}`); getDetails(TAGS_DETAILS).should('have.text', expectedEditedtags); }); cy.get(INVESTIGATION_NOTES_TOGGLE).click({ force: true }); @@ -450,7 +435,7 @@ describe('Custom query rules', () => { 'have.text', expectedEditedIndexPatterns?.join('') ); - getDetails(CUSTOM_QUERY_DETAILS).should('have.text', getEditedRule().customQuery); + getDetails(CUSTOM_QUERY_DETAILS).should('have.text', getEditedRule().query); getDetails(RULE_TYPE_DETAILS).should('have.text', 'Query'); getDetails(TIMELINE_TEMPLATE_DETAILS).should('have.text', 'None'); }); diff --git a/x-pack/plugins/security_solution/cypress/e2e/detection_rules/custom_query_rule_data_view.cy.ts b/x-pack/plugins/security_solution/cypress/e2e/detection_rules/custom_query_rule_data_view.cy.ts index dac3461a3c9d8..b0227919b6885 100644 --- a/x-pack/plugins/security_solution/cypress/e2e/detection_rules/custom_query_rule_data_view.cy.ts +++ b/x-pack/plugins/security_solution/cypress/e2e/detection_rules/custom_query_rule_data_view.cy.ts @@ -5,10 +5,8 @@ * 2.0. */ -import { formatMitreAttackDescription } from '../../helpers/rules'; -import type { Mitre } from '../../objects/rule'; +import { formatMitreAttackDescription, getHumanizedDuration } from '../../helpers/rules'; import { getDataViewRule } from '../../objects/rule'; -import type { CompleteTimeline } from '../../objects/timeline'; import { ALERTS_COUNT, ALERT_GRID_CELL } from '../../screens/alerts'; import { @@ -53,7 +51,6 @@ import { } from '../../screens/rule_details'; import { goToRuleDetails } from '../../tasks/alerts_detection_rules'; -import { createTimeline } from '../../tasks/api_calls/timelines'; import { postDataView } from '../../tasks/common'; import { createAndEnableRule, @@ -78,11 +75,11 @@ describe('Custom query rules', () => { describe('Custom detection rules creation with data views', () => { const rule = getDataViewRule(); - const expectedUrls = rule.referenceUrls?.join(''); - const expectedFalsePositives = rule.falsePositivesExamples?.join(''); + const expectedUrls = rule.references?.join(''); + const expectedFalsePositives = rule.false_positives?.join(''); const expectedTags = rule.tags?.join(''); - const mitreAttack = rule.mitre as Mitre[]; - const expectedMitre = formatMitreAttackDescription(mitreAttack); + const mitreAttack = rule.threat; + const expectedMitre = formatMitreAttackDescription(mitreAttack ?? []); const expectedNumberOfRules = 1; beforeEach(() => { @@ -90,44 +87,34 @@ describe('Custom query rules', () => { are creating a data view we'll use after and cleanKibana does not delete all the data views created, esArchiverReseKibana does. We don't use esArchiverReseKibana in all the tests because is a time-consuming method and we don't need to perform an exhaustive cleaning in all the other tests. */ - const timeline = rule.timeline as CompleteTimeline; esArchiverResetKibana(); - createTimeline(timeline).then((response) => { - cy.wrap({ - ...rule, - timeline: { - ...timeline, - id: response.body.data.persistTimeline.timeline.savedObjectId, - }, - }).as('rule'); - }); - if (rule.dataSource.type === 'dataView') { - postDataView(rule.dataSource.dataView); + if (rule.data_view_id != null) { + postDataView(rule.data_view_id); } }); it('Creates and enables a new rule', function () { visit(RULE_CREATION); - fillDefineCustomRuleAndContinue(this.rule); - fillAboutRuleAndContinue(this.rule); - fillScheduleRuleAndContinue(this.rule); + fillDefineCustomRuleAndContinue(rule); + fillAboutRuleAndContinue(rule); + fillScheduleRuleAndContinue(rule); createAndEnableRule(); cy.get(CUSTOM_RULES_BTN).should('have.text', 'Custom rules (1)'); cy.get(RULES_MANAGEMENT_TABLE).find(RULES_ROW).should('have.length', expectedNumberOfRules); - cy.get(RULE_NAME).should('have.text', this.rule.name); - cy.get(RISK_SCORE).should('have.text', this.rule.riskScore); - cy.get(SEVERITY).should('have.text', this.rule.severity); + cy.get(RULE_NAME).should('have.text', rule.name); + cy.get(RISK_SCORE).should('have.text', rule.risk_score); + cy.get(SEVERITY).should('have.text', 'High'); cy.get(RULE_SWITCH).should('have.attr', 'aria-checked', 'true'); goToRuleDetails(); - cy.get(RULE_NAME_HEADER).should('contain', `${this.rule.name}`); - cy.get(ABOUT_RULE_DESCRIPTION).should('have.text', this.rule.description); + cy.get(RULE_NAME_HEADER).should('contain', `${rule.name}`); + cy.get(ABOUT_RULE_DESCRIPTION).should('have.text', rule.description); cy.get(ABOUT_DETAILS).within(() => { - getDetails(SEVERITY_DETAILS).should('have.text', this.rule.severity); - getDetails(RISK_SCORE_DETAILS).should('have.text', this.rule.riskScore); + getDetails(SEVERITY_DETAILS).should('have.text', 'High'); + getDetails(RISK_SCORE_DETAILS).should('have.text', rule.risk_score); getDetails(REFERENCE_URLS_DETAILS).should((details) => { expect(removeExternalLinkText(details.text())).equal(expectedUrls); }); @@ -140,21 +127,19 @@ describe('Custom query rules', () => { cy.get(INVESTIGATION_NOTES_TOGGLE).click({ force: true }); cy.get(ABOUT_INVESTIGATION_NOTES).should('have.text', INVESTIGATION_NOTES_MARKDOWN); cy.get(DEFINITION_DETAILS).within(() => { - getDetails(DATA_VIEW_DETAILS).should('have.text', this.rule.dataSource.dataView); - getDetails(CUSTOM_QUERY_DETAILS).should('have.text', this.rule.customQuery); + getDetails(DATA_VIEW_DETAILS).should('have.text', rule.data_view_id); + getDetails(CUSTOM_QUERY_DETAILS).should('have.text', rule.query); getDetails(RULE_TYPE_DETAILS).should('have.text', 'Query'); getDetails(TIMELINE_TEMPLATE_DETAILS).should('have.text', 'None'); }); cy.get(DEFINITION_DETAILS).should('not.contain', INDEX_PATTERNS_DETAILS); cy.get(SCHEDULE_DETAILS).within(() => { - getDetails(RUNS_EVERY_DETAILS).should( - 'have.text', - `${getDataViewRule().runsEvery?.interval}${getDataViewRule().runsEvery?.type}` - ); - getDetails(ADDITIONAL_LOOK_BACK_DETAILS).should( - 'have.text', - `${getDataViewRule().lookBack?.interval}${getDataViewRule().lookBack?.type}` + getDetails(RUNS_EVERY_DETAILS).should('have.text', `${rule.interval}`); + const humanizedDuration = getHumanizedDuration( + rule.from ?? 'now-6m', + rule.interval ?? '5m' ); + getDetails(ADDITIONAL_LOOK_BACK_DETAILS).should('have.text', `${humanizedDuration}`); }); waitForTheRuleToBeExecuted(); @@ -163,19 +148,17 @@ describe('Custom query rules', () => { cy.get(ALERTS_COUNT) .invoke('text') .should('match', /^[1-9].+$/); - cy.get(ALERT_GRID_CELL).contains(this.rule.name); + cy.get(ALERT_GRID_CELL).contains(rule.name); }); it('Creates and edits a new rule with a data view', function () { visit(RULE_CREATION); - fillDefineCustomRuleAndContinue(this.rule); - cy.get(RULE_NAME_INPUT).clear({ force: true }).type(this.rule.name, { force: true }); - cy.get(RULE_DESCRIPTION_INPUT) - .clear({ force: true }) - .type(this.rule.description, { force: true }); + fillDefineCustomRuleAndContinue(rule); + cy.get(RULE_NAME_INPUT).clear({ force: true }).type(rule.name, { force: true }); + cy.get(RULE_DESCRIPTION_INPUT).clear({ force: true }).type(rule.description, { force: true }); cy.get(ABOUT_CONTINUE_BTN).should('exist').click({ force: true }); - fillScheduleRuleAndContinue(this.rule); + fillScheduleRuleAndContinue(rule); createRuleWithoutEnabling(); goToRuleDetails(); diff --git a/x-pack/plugins/security_solution/cypress/e2e/detection_rules/custom_saved_query_rule.cy.ts b/x-pack/plugins/security_solution/cypress/e2e/detection_rules/custom_saved_query_rule.cy.ts index 7a4117f0d58a3..4c16068049677 100644 --- a/x-pack/plugins/security_solution/cypress/e2e/detection_rules/custom_saved_query_rule.cy.ts +++ b/x-pack/plugins/security_solution/cypress/e2e/detection_rules/custom_saved_query_rule.cy.ts @@ -24,7 +24,6 @@ import { } from '../../screens/rule_details'; import { goToRuleDetails, editFirstRule } from '../../tasks/alerts_detection_rules'; -import { createTimeline } from '../../tasks/api_calls/timelines'; import { createSavedQuery, deleteSavedQueries } from '../../tasks/api_calls/saved_queries'; import { cleanKibana, deleteAlertsAndRules } from '../../tasks/common'; import { @@ -39,10 +38,9 @@ import { import { saveEditedRule } from '../../tasks/edit_rule'; import { login, visit } from '../../tasks/login'; import { getDetails } from '../../tasks/rule_details'; -import { createSavedQueryRule, createCustomRule } from '../../tasks/api_calls/rules'; +import { createRule } from '../../tasks/api_calls/rules'; import { RULE_CREATION, SECURITY_DETECTIONS_RULES_URL } from '../../urls/navigation'; -import type { CompleteTimeline } from '../../objects/timeline'; const savedQueryName = 'custom saved query'; const savedQueryQuery = 'process.name: test'; @@ -57,19 +55,10 @@ describe('Custom saved_query rules', () => { beforeEach(() => { deleteAlertsAndRules(); deleteSavedQueries(); - const timeline = getNewRule().timeline as CompleteTimeline; - createTimeline(timeline).then((response) => { - cy.wrap({ - ...getNewRule(), - timeline: { - ...timeline, - id: response.body.data.persistTimeline.timeline.savedObjectId, - }, - }).as('rule'); - }); }); it('Creates saved query rule', function () { + const rule = getNewRule(); createSavedQuery(savedQueryName, savedQueryQuery, savedQueryFilterKey); visit(RULE_CREATION); @@ -88,8 +77,8 @@ describe('Custom saved_query rules', () => { cy.get(DEFINE_CONTINUE_BUTTON).should('exist').click({ force: true }); cy.get(DEFINE_CONTINUE_BUTTON).should('not.exist'); - fillAboutRuleAndContinue(this.rule); - fillScheduleRuleAndContinue(this.rule); + fillAboutRuleAndContinue(rule); + fillScheduleRuleAndContinue(rule); cy.intercept('POST', '/api/detection_engine/rules').as('savedQueryRule'); createAndEnableRule(); @@ -100,7 +89,7 @@ describe('Custom saved_query rules', () => { goToRuleDetails(); - cy.get(RULE_NAME_HEADER).should('contain', `${this.rule.name}`); + cy.get(RULE_NAME_HEADER).should('contain', `${rule.name}`); cy.get(DEFINE_RULE_PANEL_PROGRESS).should('not.exist'); @@ -112,7 +101,12 @@ describe('Custom saved_query rules', () => { context('Non existent saved query', () => { const FAILED_TO_LOAD_ERROR = 'Failed to load the saved query'; beforeEach(() => { - createSavedQueryRule({ ...getNewRule(), savedId: 'non-existent' }); + createRule({ + ...getNewRule(), + type: 'saved_query', + saved_id: 'non-existent', + query: undefined, + }); cy.visit(SECURITY_DETECTIONS_RULES_URL); }); it('Shows error toast on details page when saved query can not be loaded', function () { @@ -131,7 +125,7 @@ describe('Custom saved_query rules', () => { context('Editing', () => { it('Allows to update query rule as saved_query rule type', () => { createSavedQuery(savedQueryName, savedQueryQuery); - createCustomRule(getNewRule()); + createRule(getNewRule()); cy.visit(SECURITY_DETECTIONS_RULES_URL); @@ -158,7 +152,12 @@ describe('Custom saved_query rules', () => { const expectedCustomTestQuery = 'random test query'; createSavedQuery(savedQueryName, savedQueryQuery).then((response) => { cy.log(JSON.stringify(response.body, null, 2)); - createSavedQueryRule({ ...getNewRule(), savedId: response.body.id }); + createRule({ + ...getNewRule(), + type: 'saved_query', + saved_id: response.body.id, + query: undefined, + }); }); cy.visit(SECURITY_DETECTIONS_RULES_URL); @@ -185,7 +184,12 @@ describe('Custom saved_query rules', () => { it('Allows to update saved_query rule with non-existent query by adding custom query', () => { const expectedCustomTestQuery = 'random test query'; - createSavedQueryRule({ ...getNewRule(), savedId: 'non-existent' }); + createRule({ + ...getNewRule(), + type: 'saved_query', + saved_id: 'non-existent', + query: undefined, + }); cy.visit(SECURITY_DETECTIONS_RULES_URL); @@ -208,7 +212,12 @@ describe('Custom saved_query rules', () => { it('Allows to update saved_query rule with non-existent query by selecting another saved query', () => { createSavedQuery(savedQueryName, savedQueryQuery); - createSavedQueryRule({ ...getNewRule(), savedId: 'non-existent' }); + createRule({ + ...getNewRule(), + type: 'saved_query', + saved_id: 'non-existent', + query: undefined, + }); cy.visit(SECURITY_DETECTIONS_RULES_URL); diff --git a/x-pack/plugins/security_solution/cypress/e2e/detection_rules/event_correlation_rule.cy.ts b/x-pack/plugins/security_solution/cypress/e2e/detection_rules/event_correlation_rule.cy.ts index 8c906661db8e2..6eb47d6bc7765 100644 --- a/x-pack/plugins/security_solution/cypress/e2e/detection_rules/event_correlation_rule.cy.ts +++ b/x-pack/plugins/security_solution/cypress/e2e/detection_rules/event_correlation_rule.cy.ts @@ -5,8 +5,7 @@ * 2.0. */ -import { formatMitreAttackDescription } from '../../helpers/rules'; -import type { Mitre } from '../../objects/rule'; +import { formatMitreAttackDescription, getHumanizedDuration } from '../../helpers/rules'; import { getEqlRule, getEqlSequenceRule, getIndexPatterns } from '../../objects/rule'; import { ALERTS_COUNT, ALERT_DATA_GRID } from '../../screens/alerts'; @@ -48,7 +47,6 @@ import { goToRuleDetails, goToTheRuleDetailsOf, } from '../../tasks/alerts_detection_rules'; -import { createTimeline } from '../../tasks/api_calls/timelines'; import { cleanKibana, deleteAlertsAndRules } from '../../tasks/common'; import { createAndEnableRule, @@ -63,7 +61,6 @@ import { login, visit } from '../../tasks/login'; import { RULE_CREATION } from '../../urls/navigation'; import { esArchiverLoad, esArchiverUnload } from '../../tasks/es_archiver'; -import type { CompleteTimeline } from '../../objects/timeline'; describe('EQL rules', () => { before(() => { @@ -72,51 +69,39 @@ describe('EQL rules', () => { deleteAlertsAndRules(); }); describe('Detection rules, EQL', () => { - const expectedUrls = getEqlRule().referenceUrls?.join(''); - const expectedFalsePositives = getEqlRule().falsePositivesExamples?.join(''); - const expectedTags = getEqlRule().tags?.join(''); - const mitreAttack = getEqlRule().mitre as Mitre[]; - const expectedMitre = formatMitreAttackDescription(mitreAttack); + const rule = getEqlRule(); + const expectedUrls = rule.references?.join(''); + const expectedFalsePositives = rule.false_positives?.join(''); + const expectedTags = rule.tags?.join(''); + const mitreAttack = rule.threat; + const expectedMitre = formatMitreAttackDescription(mitreAttack ?? []); const expectedNumberOfRules = 1; const expectedNumberOfAlerts = '2 alerts'; - beforeEach(() => { - const timeline = getEqlRule().timeline as CompleteTimeline; - createTimeline(timeline).then((response) => { - cy.wrap({ - ...getEqlRule(), - timeline: { - ...timeline, - id: response.body.data.persistTimeline.timeline.savedObjectId, - }, - }).as('rule'); - }); - }); - it('Creates and enables a new EQL rule', function () { visit(RULE_CREATION); selectEqlRuleType(); - fillDefineEqlRuleAndContinue(this.rule); - fillAboutRuleAndContinue(this.rule); - fillScheduleRuleAndContinue(this.rule); + fillDefineEqlRuleAndContinue(rule); + fillAboutRuleAndContinue(rule); + fillScheduleRuleAndContinue(rule); createAndEnableRule(); cy.get(CUSTOM_RULES_BTN).should('have.text', 'Custom rules (1)'); expectNumberOfRules(RULES_MANAGEMENT_TABLE, expectedNumberOfRules); - cy.get(RULE_NAME).should('have.text', this.rule.name); - cy.get(RISK_SCORE).should('have.text', this.rule.riskScore); - cy.get(SEVERITY).should('have.text', this.rule.severity); + cy.get(RULE_NAME).should('have.text', rule.name); + cy.get(RISK_SCORE).should('have.text', rule.risk_score); + cy.get(SEVERITY).should('have.text', 'High'); cy.get(RULE_SWITCH).should('have.attr', 'aria-checked', 'true'); goToRuleDetails(); - cy.get(RULE_NAME_HEADER).should('contain', `${this.rule.name}`); - cy.get(ABOUT_RULE_DESCRIPTION).should('have.text', this.rule.description); + cy.get(RULE_NAME_HEADER).should('contain', `${rule.name}`); + cy.get(ABOUT_RULE_DESCRIPTION).should('have.text', rule.description); cy.get(ABOUT_DETAILS).within(() => { - getDetails(SEVERITY_DETAILS).should('have.text', this.rule.severity); - getDetails(RISK_SCORE_DETAILS).should('have.text', this.rule.riskScore); + getDetails(SEVERITY_DETAILS).should('have.text', 'High'); + getDetails(RISK_SCORE_DETAILS).should('have.text', rule.risk_score); getDetails(REFERENCE_URLS_DETAILS).should((details) => { expect(removeExternalLinkText(details.text())).equal(expectedUrls); }); @@ -130,19 +115,17 @@ describe('EQL rules', () => { cy.get(ABOUT_INVESTIGATION_NOTES).should('have.text', INVESTIGATION_NOTES_MARKDOWN); cy.get(DEFINITION_DETAILS).within(() => { getDetails(INDEX_PATTERNS_DETAILS).should('have.text', getIndexPatterns().join('')); - getDetails(CUSTOM_QUERY_DETAILS).should('have.text', this.rule.customQuery); + getDetails(CUSTOM_QUERY_DETAILS).should('have.text', rule.query); getDetails(RULE_TYPE_DETAILS).should('have.text', 'Event Correlation'); getDetails(TIMELINE_TEMPLATE_DETAILS).should('have.text', 'None'); }); cy.get(SCHEDULE_DETAILS).within(() => { - getDetails(RUNS_EVERY_DETAILS).should( - 'have.text', - `${this.rule.runsEvery.interval}${this.rule.runsEvery.type}` - ); - getDetails(ADDITIONAL_LOOK_BACK_DETAILS).should( - 'have.text', - `${this.rule.lookBack.interval}${this.rule.lookBack.type}` + getDetails(RUNS_EVERY_DETAILS).should('have.text', `${rule.interval}`); + const humanizedDuration = getHumanizedDuration( + rule.from ?? 'now-6m', + rule.interval ?? '5m' ); + getDetails(ADDITIONAL_LOOK_BACK_DETAILS).should('have.text', `${humanizedDuration}`); }); waitForTheRuleToBeExecuted(); @@ -152,9 +135,9 @@ describe('EQL rules', () => { cy.get(ALERT_DATA_GRID) .invoke('text') .then((text) => { - expect(text).contains(this.rule.name); - expect(text).contains(this.rule.severity.toLowerCase()); - expect(text).contains(this.rule.riskScore); + expect(text).contains(rule.name); + expect(text).contains(rule.severity); + expect(text).contains(rule.risk_score); }); }); }); @@ -162,21 +145,11 @@ describe('EQL rules', () => { describe('Detection rules, sequence EQL', () => { const expectedNumberOfSequenceAlerts = '2 alerts'; + const rule = getEqlSequenceRule(); + before(() => { esArchiverLoad('auditbeat_big'); }); - beforeEach(() => { - const timeline = getEqlSequenceRule().timeline as CompleteTimeline; - createTimeline(timeline).then((response) => { - cy.wrap({ - ...getEqlSequenceRule(), - timeline: { - ...timeline, - id: response.body.data.persistTimeline.timeline.savedObjectId, - }, - }).as('rule'); - }); - }); after(() => { esArchiverUnload('auditbeat_big'); }); @@ -184,11 +157,11 @@ describe('EQL rules', () => { it('Creates and enables a new EQL rule with a sequence', function () { visit(RULE_CREATION); selectEqlRuleType(); - fillDefineEqlRuleAndContinue(this.rule); - fillAboutRuleAndContinue(this.rule); - fillScheduleRuleAndContinue(this.rule); + fillDefineEqlRuleAndContinue(rule); + fillAboutRuleAndContinue(rule); + fillScheduleRuleAndContinue(rule); createAndEnableRule(); - goToTheRuleDetailsOf(this.rule.name); + goToTheRuleDetailsOf(rule.name); waitForTheRuleToBeExecuted(); waitForAlertsToPopulate(); @@ -197,8 +170,8 @@ describe('EQL rules', () => { .invoke('text') .then((text) => { cy.log('ALERT_DATA_GRID', text); - expect(text).contains(this.rule.name); - expect(text).contains(this.rule.severity.toLowerCase()); + expect(text).contains(rule.name); + expect(text).contains(rule.severity); }); }); }); diff --git a/x-pack/plugins/security_solution/cypress/e2e/detection_rules/export_rule.cy.ts b/x-pack/plugins/security_solution/cypress/e2e/detection_rules/export_rule.cy.ts index f919a54a3ab70..9a360d211f3fa 100644 --- a/x-pack/plugins/security_solution/cypress/e2e/detection_rules/export_rule.cy.ts +++ b/x-pack/plugins/security_solution/cypress/e2e/detection_rules/export_rule.cy.ts @@ -23,7 +23,7 @@ import { } from '../../tasks/alerts_detection_rules'; import { createExceptionList, deleteExceptionList } from '../../tasks/api_calls/exceptions'; import { getExceptionList } from '../../objects/exception'; -import { createCustomRule } from '../../tasks/api_calls/rules'; +import { createRule } from '../../tasks/api_calls/rules'; import { cleanKibana, resetRulesTableState, deleteAlertsAndRules } from '../../tasks/common'; import { login, visitWithoutDateRange } from '../../tasks/login'; @@ -45,7 +45,7 @@ describe('Export rules', () => { // Rules get exported via _bulk_action endpoint cy.intercept('POST', '/api/detection_engine/rules/_bulk_action').as('bulk_action'); visitWithoutDateRange(DETECTIONS_RULE_MANAGEMENT_URL); - createCustomRule(getNewRule()).as('ruleResponse'); + createRule(getNewRule()).as('ruleResponse'); }); it('Exports a custom rule', function () { @@ -104,21 +104,19 @@ describe('Export rules', () => { deleteExceptionList(exceptionList.list_id, exceptionList.namespace_type); // create rule with exceptions createExceptionList(exceptionList, exceptionList.list_id).then((response) => - createCustomRule( - { - ...getNewRule(), - name: 'rule with exceptions', - exceptionLists: [ - { - id: response.body.id, - list_id: exceptionList.list_id, - type: exceptionList.type, - namespace_type: exceptionList.namespace_type, - }, - ], - }, - '2' - ) + createRule({ + ...getNewRule(), + name: 'rule with exceptions', + exceptions_list: [ + { + id: response.body.id, + list_id: exceptionList.list_id, + type: exceptionList.type, + namespace_type: exceptionList.namespace_type, + }, + ], + rule_id: '2', + }) ); }); diff --git a/x-pack/plugins/security_solution/cypress/e2e/detection_rules/indicator_match_rule.cy.ts b/x-pack/plugins/security_solution/cypress/e2e/detection_rules/indicator_match_rule.cy.ts index 453de4a7f0e81..4836d7b24ed11 100644 --- a/x-pack/plugins/security_solution/cypress/e2e/detection_rules/indicator_match_rule.cy.ts +++ b/x-pack/plugins/security_solution/cypress/e2e/detection_rules/indicator_match_rule.cy.ts @@ -5,12 +5,12 @@ * 2.0. */ -import { formatMitreAttackDescription } from '../../helpers/rules'; -import type { Mitre } from '../../objects/rule'; +import { formatMitreAttackDescription, getHumanizedDuration } from '../../helpers/rules'; import { getIndexPatterns, getNewThreatIndicatorRule, getThreatIndexPatterns, + indicatorRuleMatchingDoc, } from '../../objects/rule'; import { @@ -65,7 +65,7 @@ import { checkDuplicatedRule, expectNumberOfRules, } from '../../tasks/alerts_detection_rules'; -import { createCustomIndicatorRule } from '../../tasks/api_calls/rules'; +import { createRule } from '../../tasks/api_calls/rules'; import { loadPrepackagedTimelineTemplates } from '../../tasks/api_calls/timelines'; import { cleanKibana, deleteAlertsAndRules } from '../../tasks/common'; import { @@ -106,15 +106,16 @@ import { login, visit, visitWithoutDateRange } from '../../tasks/login'; import { goBackToRulesTable, getDetails } from '../../tasks/rule_details'; import { DETECTIONS_RULE_MANAGEMENT_URL, RULE_CREATION } from '../../urls/navigation'; + const DEFAULT_THREAT_MATCH_QUERY = '@timestamp >= "now-30d/d"'; describe('indicator match', () => { describe('Detection rules, Indicator Match', () => { - const expectedUrls = getNewThreatIndicatorRule().referenceUrls?.join(''); - const expectedFalsePositives = getNewThreatIndicatorRule().falsePositivesExamples?.join(''); + const expectedUrls = getNewThreatIndicatorRule().references?.join(''); + const expectedFalsePositives = getNewThreatIndicatorRule().false_positives?.join(''); const expectedTags = getNewThreatIndicatorRule().tags?.join(''); - const mitreAttack = getNewThreatIndicatorRule().mitre as Mitre[]; - const expectedMitre = formatMitreAttackDescription(mitreAttack); + const mitreAttack = getNewThreatIndicatorRule().threat; + const expectedMitre = formatMitreAttackDescription(mitreAttack ?? []); const expectedNumberOfRules = 1; const expectedNumberOfAlerts = '1 alert'; @@ -212,8 +213,8 @@ describe('indicator match', () => { const rule = getNewThreatIndicatorRule(); visitWithoutDateRange(RULE_CREATION); selectIndicatorMatchType(); - if (rule.dataSource.type === 'indexPatterns') { - fillIndexAndIndicatorIndexPattern(rule.dataSource.index, rule.indicatorIndexPattern); + if (rule.index) { + fillIndexAndIndicatorIndexPattern(rule.index, rule.threat_index); } }); @@ -238,8 +239,8 @@ describe('indicator match', () => { it('Does NOT show invalidation text when there is a valid "index field" and a valid "indicator index field"', () => { fillIndicatorMatchRow({ - indexField: getNewThreatIndicatorRule().indicatorMappingField, - indicatorIndexField: getNewThreatIndicatorRule().indicatorIndexField, + indexField: getNewThreatIndicatorRule().threat_mapping[0].entries[0].field, + indicatorIndexField: getNewThreatIndicatorRule().threat_mapping[0].entries[0].value, }); getDefineContinueButton().click(); getIndicatorInvalidationText().should('not.exist'); @@ -248,7 +249,7 @@ describe('indicator match', () => { it('Shows invalidation text when there is an invalid "index field" and a valid "indicator index field"', () => { fillIndicatorMatchRow({ indexField: 'non-existent-value', - indicatorIndexField: getNewThreatIndicatorRule().indicatorIndexField, + indicatorIndexField: getNewThreatIndicatorRule().threat_mapping[0].entries[0].value, validColumns: 'indicatorField', }); getDefineContinueButton().click(); @@ -257,7 +258,7 @@ describe('indicator match', () => { it('Shows invalidation text when there is a valid "index field" and an invalid "indicator index field"', () => { fillIndicatorMatchRow({ - indexField: getNewThreatIndicatorRule().indicatorMappingField, + indexField: getNewThreatIndicatorRule().threat_mapping[0].entries[0].field, indicatorIndexField: 'non-existent-value', validColumns: 'indexField', }); @@ -267,21 +268,21 @@ describe('indicator match', () => { it('Deletes the first row when you have two rows. Both rows valid rows of "index fields" and valid "indicator index fields". The second row should become the first row', () => { fillIndicatorMatchRow({ - indexField: getNewThreatIndicatorRule().indicatorMappingField, - indicatorIndexField: getNewThreatIndicatorRule().indicatorIndexField, + indexField: getNewThreatIndicatorRule().threat_mapping[0].entries[0].field, + indicatorIndexField: getNewThreatIndicatorRule().threat_mapping[0].entries[0].value, }); getIndicatorAndButton().click(); fillIndicatorMatchRow({ rowNumber: 2, indexField: 'agent.name', - indicatorIndexField: getNewThreatIndicatorRule().indicatorIndexField, + indicatorIndexField: getNewThreatIndicatorRule().threat_mapping[0].entries[0].value, validColumns: 'indicatorField', }); getIndicatorDeleteButton().click(); getIndicatorIndexComboField().should('have.text', 'agent.name'); getIndicatorMappingComboField().should( 'have.text', - getNewThreatIndicatorRule().indicatorIndexField + getNewThreatIndicatorRule().threat_mapping[0].entries[0].value ); getIndicatorIndexComboField(2).should('not.exist'); getIndicatorMappingComboField(2).should('not.exist'); @@ -289,14 +290,14 @@ describe('indicator match', () => { it('Deletes the first row when you have two rows. Both rows have valid "index fields" and invalid "indicator index fields". The second row should become the first row', () => { fillIndicatorMatchRow({ - indexField: getNewThreatIndicatorRule().indicatorMappingField, + indexField: getNewThreatIndicatorRule().threat_mapping[0].entries[0].field, indicatorIndexField: 'non-existent-value', validColumns: 'indexField', }); getIndicatorAndButton().click(); fillIndicatorMatchRow({ rowNumber: 2, - indexField: getNewThreatIndicatorRule().indicatorMappingField, + indexField: getNewThreatIndicatorRule().threat_mapping[0].entries[0].field, indicatorIndexField: 'second-non-existent-value', validColumns: 'indexField', }); @@ -309,14 +310,14 @@ describe('indicator match', () => { it('Deletes the first row when you have two rows. Both rows have valid "indicator index fields" and invalid "index fields". The second row should become the first row', () => { fillIndicatorMatchRow({ indexField: 'non-existent-value', - indicatorIndexField: getNewThreatIndicatorRule().indicatorIndexField, + indicatorIndexField: getNewThreatIndicatorRule().threat_mapping[0].entries[0].value, validColumns: 'indicatorField', }); getIndicatorAndButton().click(); fillIndicatorMatchRow({ rowNumber: 2, indexField: 'second-non-existent-value', - indicatorIndexField: getNewThreatIndicatorRule().indicatorIndexField, + indicatorIndexField: getNewThreatIndicatorRule().threat_mapping[0].entries[0].value, validColumns: 'indicatorField', }); getIndicatorDeleteButton().click(); @@ -327,8 +328,8 @@ describe('indicator match', () => { it('Deletes the first row of data but not the UI elements and the text defaults back to the placeholder of Search', () => { fillIndicatorMatchRow({ - indexField: getNewThreatIndicatorRule().indicatorMappingField, - indicatorIndexField: getNewThreatIndicatorRule().indicatorIndexField, + indexField: getNewThreatIndicatorRule().threat_mapping[0].entries[0].field, + indicatorIndexField: getNewThreatIndicatorRule().threat_mapping[0].entries[0].value, }); getIndicatorDeleteButton().click(); getIndicatorIndexComboField().should('text', 'Search'); @@ -339,8 +340,8 @@ describe('indicator match', () => { it('Deletes the second row when you have three rows. The first row is valid data, the second row is invalid data, and the third row is valid data. Third row should shift up correctly', () => { fillIndicatorMatchRow({ - indexField: getNewThreatIndicatorRule().indicatorMappingField, - indicatorIndexField: getNewThreatIndicatorRule().indicatorIndexField, + indexField: getNewThreatIndicatorRule().threat_mapping[0].entries[0].field, + indicatorIndexField: getNewThreatIndicatorRule().threat_mapping[0].entries[0].value, }); getIndicatorAndButton().click(); fillIndicatorMatchRow({ @@ -352,25 +353,25 @@ describe('indicator match', () => { getIndicatorAndButton().click(); fillIndicatorMatchRow({ rowNumber: 3, - indexField: getNewThreatIndicatorRule().indicatorMappingField, - indicatorIndexField: getNewThreatIndicatorRule().indicatorIndexField, + indexField: getNewThreatIndicatorRule().threat_mapping[0].entries[0].field, + indicatorIndexField: getNewThreatIndicatorRule().threat_mapping[0].entries[0].value, }); getIndicatorDeleteButton(2).click(); getIndicatorIndexComboField(1).should( 'text', - getNewThreatIndicatorRule().indicatorMappingField + getNewThreatIndicatorRule().threat_mapping[0].entries[0].field ); getIndicatorMappingComboField(1).should( 'text', - getNewThreatIndicatorRule().indicatorIndexField + getNewThreatIndicatorRule().threat_mapping[0].entries[0].value ); getIndicatorIndexComboField(2).should( 'text', - getNewThreatIndicatorRule().indicatorMappingField + getNewThreatIndicatorRule().threat_mapping[0].entries[0].field ); getIndicatorMappingComboField(2).should( 'text', - getNewThreatIndicatorRule().indicatorIndexField + getNewThreatIndicatorRule().threat_mapping[0].entries[0].value ); getIndicatorIndexComboField(3).should('not.exist'); getIndicatorMappingComboField(3).should('not.exist'); @@ -385,17 +386,17 @@ describe('indicator match', () => { getIndicatorOrButton().click(); fillIndicatorMatchRow({ rowNumber: 2, - indexField: getNewThreatIndicatorRule().indicatorMappingField, - indicatorIndexField: getNewThreatIndicatorRule().indicatorIndexField, + indexField: getNewThreatIndicatorRule().threat_mapping[0].entries[0].field, + indicatorIndexField: getNewThreatIndicatorRule().threat_mapping[0].entries[0].value, }); getIndicatorDeleteButton().click(); getIndicatorIndexComboField().should( 'text', - getNewThreatIndicatorRule().indicatorMappingField + getNewThreatIndicatorRule().threat_mapping[0].entries[0].field ); getIndicatorMappingComboField().should( 'text', - getNewThreatIndicatorRule().indicatorIndexField + getNewThreatIndicatorRule().threat_mapping[0].entries[0].value ); getIndicatorIndexComboField(2).should('not.exist'); getIndicatorMappingComboField(2).should('not.exist'); @@ -436,8 +437,8 @@ describe('indicator match', () => { expectNumberOfRules(RULES_MANAGEMENT_TABLE, expectedNumberOfRules); cy.get(RULE_NAME).should('have.text', rule.name); - cy.get(RISK_SCORE).should('have.text', rule.riskScore); - cy.get(SEVERITY).should('have.text', rule.severity); + cy.get(RISK_SCORE).should('have.text', rule.risk_score); + cy.get(SEVERITY).should('have.text', 'Critical'); cy.get(RULE_SWITCH).should('have.attr', 'aria-checked', 'true'); goToRuleDetails(); @@ -445,9 +446,9 @@ describe('indicator match', () => { cy.get(RULE_NAME_HEADER).should('contain', `${rule.name}`); cy.get(ABOUT_RULE_DESCRIPTION).should('have.text', rule.description); cy.get(ABOUT_DETAILS).within(() => { - getDetails(SEVERITY_DETAILS).should('have.text', rule.severity); - getDetails(RISK_SCORE_DETAILS).should('have.text', rule.riskScore); - getDetails(INDICATOR_PREFIX_OVERRIDE).should('have.text', rule.threatIndicatorPath); + getDetails(SEVERITY_DETAILS).should('have.text', 'Critical'); + getDetails(RISK_SCORE_DETAILS).should('have.text', rule.risk_score); + getDetails(INDICATOR_PREFIX_OVERRIDE).should('have.text', rule.threat_indicator_path); getDetails(REFERENCE_URLS_DETAILS).should((details) => { expect(removeExternalLinkText(details.text())).equal(expectedUrls); }); @@ -461,32 +462,27 @@ describe('indicator match', () => { cy.get(ABOUT_INVESTIGATION_NOTES).should('have.text', INVESTIGATION_NOTES_MARKDOWN); cy.get(DEFINITION_DETAILS).within(() => { - if (rule.dataSource.type === 'indexPatterns') { - getDetails(INDEX_PATTERNS_DETAILS).should('have.text', rule.dataSource.index?.join('')); + if (rule.index) { + getDetails(INDEX_PATTERNS_DETAILS).should('have.text', rule.index.join('')); } getDetails(CUSTOM_QUERY_DETAILS).should('have.text', '*:*'); getDetails(RULE_TYPE_DETAILS).should('have.text', 'Indicator Match'); getDetails(TIMELINE_TEMPLATE_DETAILS).should('have.text', 'None'); - getDetails(INDICATOR_INDEX_PATTERNS).should( - 'have.text', - rule.indicatorIndexPattern.join('') - ); + getDetails(INDICATOR_INDEX_PATTERNS).should('have.text', rule.threat_index.join('')); getDetails(INDICATOR_MAPPING).should( 'have.text', - `${rule.indicatorMappingField} MATCHES ${rule.indicatorIndexField}` + `${rule.threat_mapping[0].entries[0].field} MATCHES ${rule.threat_mapping[0].entries[0].value}` ); getDetails(INDICATOR_INDEX_QUERY).should('have.text', '*:*'); }); cy.get(SCHEDULE_DETAILS).within(() => { - getDetails(RUNS_EVERY_DETAILS).should( - 'have.text', - `${rule.runsEvery?.interval}${rule.runsEvery?.type}` - ); - getDetails(ADDITIONAL_LOOK_BACK_DETAILS).should( - 'have.text', - `${rule.lookBack?.interval}${rule.lookBack?.type}` + getDetails(RUNS_EVERY_DETAILS).should('have.text', `${rule.interval}`); + const humanizedDuration = getHumanizedDuration( + rule.from ?? 'now-6m', + rule.interval ?? '5m' ); + getDetails(ADDITIONAL_LOOK_BACK_DETAILS).should('have.text', `${humanizedDuration}`); }); waitForTheRuleToBeExecuted(); @@ -495,14 +491,14 @@ describe('indicator match', () => { cy.get(ALERTS_COUNT).should('have.text', expectedNumberOfAlerts); cy.get(ALERT_RULE_NAME).first().should('have.text', rule.name); cy.get(ALERT_SEVERITY).first().should('have.text', rule.severity?.toLowerCase()); - cy.get(ALERT_RISK_SCORE).first().should('have.text', rule.riskScore); + cy.get(ALERT_RISK_SCORE).first().should('have.text', rule.risk_score); }); it('Investigate alert in timeline', () => { const accessibilityText = `Press enter for options, or press space to begin dragging.`; loadPrepackagedTimelineTemplates(); - createCustomIndicatorRule(getNewThreatIndicatorRule()); + createRule({ ...getNewThreatIndicatorRule(), rule_id: 'rule_testing', enabled: true }); visit(DETECTIONS_RULE_MANAGEMENT_URL); goToRuleDetails(); waitForAlertsToPopulate(); @@ -512,18 +508,20 @@ describe('indicator match', () => { cy.get(PROVIDER_BADGE).should( 'have.text', `threat.enrichments.matched.atomic: "${ - getNewThreatIndicatorRule().atomic + indicatorRuleMatchingDoc.atomic }"threat.enrichments.matched.type: "indicator_match_rule"threat.enrichments.matched.field: "${ - getNewThreatIndicatorRule().indicatorMappingField + getNewThreatIndicatorRule().threat_mapping[0].entries[0].field }"` ); cy.get(INDICATOR_MATCH_ROW_RENDER).should( 'have.text', `threat.enrichments.matched.field${ - getNewThreatIndicatorRule().indicatorMappingField - }${accessibilityText}matched${getNewThreatIndicatorRule().indicatorMappingField}${ - getNewThreatIndicatorRule().atomic + getNewThreatIndicatorRule().threat_mapping[0].entries[0].field + }${accessibilityText}matched${ + getNewThreatIndicatorRule().threat_mapping[0].entries[0].field + }${ + indicatorRuleMatchingDoc.atomic }${accessibilityText}threat.enrichments.matched.typeindicator_match_rule${accessibilityText}provided` + ` byfeed.nameAbuseCH malware${accessibilityText}` ); @@ -533,7 +531,7 @@ describe('indicator match', () => { describe('Duplicates the indicator rule', () => { beforeEach(() => { deleteAlertsAndRules(); - createCustomIndicatorRule(getNewThreatIndicatorRule()); + createRule({ ...getNewThreatIndicatorRule(), rule_id: 'rule_testing', enabled: true }); visitWithoutDateRange(DETECTIONS_RULE_MANAGEMENT_URL); }); diff --git a/x-pack/plugins/security_solution/cypress/e2e/detection_rules/links.cy.ts b/x-pack/plugins/security_solution/cypress/e2e/detection_rules/links.cy.ts index e4fb2da2a84e6..aafe56643f44e 100644 --- a/x-pack/plugins/security_solution/cypress/e2e/detection_rules/links.cy.ts +++ b/x-pack/plugins/security_solution/cypress/e2e/detection_rules/links.cy.ts @@ -7,7 +7,7 @@ import { getNewRule } from '../../objects/rule'; import { RULES_MONITORING_TAB, RULE_NAME } from '../../screens/alerts_detection_rules'; -import { createCustomRuleEnabled } from '../../tasks/api_calls/rules'; +import { createRule } from '../../tasks/api_calls/rules'; import { cleanKibana, deleteAlertsAndRules } from '../../tasks/common'; import { login, visitWithoutDateRange } from '../../tasks/login'; import { DETECTIONS_RULE_MANAGEMENT_URL } from '../../urls/navigation'; @@ -19,7 +19,7 @@ describe('Rules talbes links', () => { }); beforeEach(() => { deleteAlertsAndRules(); - createCustomRuleEnabled(getNewRule(), 'rule1'); + createRule({ ...getNewRule(), rule_id: 'rule1' }); visitWithoutDateRange(DETECTIONS_RULE_MANAGEMENT_URL); }); diff --git a/x-pack/plugins/security_solution/cypress/e2e/detection_rules/machine_learning_rule.cy.ts b/x-pack/plugins/security_solution/cypress/e2e/detection_rules/machine_learning_rule.cy.ts index 7c98027f72248..cceeb2b4962c4 100644 --- a/x-pack/plugins/security_solution/cypress/e2e/detection_rules/machine_learning_rule.cy.ts +++ b/x-pack/plugins/security_solution/cypress/e2e/detection_rules/machine_learning_rule.cy.ts @@ -5,7 +5,9 @@ * 2.0. */ -import { formatMitreAttackDescription } from '../../helpers/rules'; +import { isArray } from 'lodash'; + +import { formatMitreAttackDescription, getHumanizedDuration } from '../../helpers/rules'; import { getMachineLearningRule } from '../../objects/rule'; import { @@ -53,10 +55,10 @@ import { login, visitWithoutDateRange } from '../../tasks/login'; import { RULE_CREATION } from '../../urls/navigation'; describe('Detection rules, machine learning', () => { - const expectedUrls = getMachineLearningRule().referenceUrls.join(''); - const expectedFalsePositives = getMachineLearningRule().falsePositivesExamples.join(''); - const expectedTags = getMachineLearningRule().tags.join(''); - const expectedMitre = formatMitreAttackDescription(getMachineLearningRule().mitre); + const expectedUrls = (getMachineLearningRule().references ?? []).join(''); + const expectedFalsePositives = (getMachineLearningRule().false_positives ?? []).join(''); + const expectedTags = (getMachineLearningRule().tags ?? []).join(''); + const expectedMitre = formatMitreAttackDescription(getMachineLearningRule().threat ?? []); const expectedNumberOfRules = 1; before(() => { @@ -66,28 +68,29 @@ describe('Detection rules, machine learning', () => { }); it('Creates and enables a new ml rule', () => { + const mlRule = getMachineLearningRule(); selectMachineLearningRuleType(); - fillDefineMachineLearningRuleAndContinue(getMachineLearningRule()); - fillAboutRuleAndContinue(getMachineLearningRule()); - fillScheduleRuleAndContinue(getMachineLearningRule()); + fillDefineMachineLearningRuleAndContinue(mlRule); + fillAboutRuleAndContinue(mlRule); + fillScheduleRuleAndContinue(mlRule); createAndEnableRule(); cy.get(CUSTOM_RULES_BTN).should('have.text', 'Custom rules (1)'); expectNumberOfRules(RULES_MANAGEMENT_TABLE, expectedNumberOfRules); - cy.get(RULE_NAME).should('have.text', getMachineLearningRule().name); - cy.get(RISK_SCORE).should('have.text', getMachineLearningRule().riskScore); - cy.get(SEVERITY).should('have.text', getMachineLearningRule().severity); + cy.get(RULE_NAME).should('have.text', mlRule.name); + cy.get(RISK_SCORE).should('have.text', mlRule.risk_score); + cy.get(SEVERITY).should('have.text', 'Critical'); cy.get(RULE_SWITCH).should('have.attr', 'aria-checked', 'true'); goToRuleDetails(); - cy.get(RULE_NAME_HEADER).should('contain', `${getMachineLearningRule().name}`); - cy.get(ABOUT_RULE_DESCRIPTION).should('have.text', getMachineLearningRule().description); + cy.get(RULE_NAME_HEADER).should('contain', `${mlRule.name}`); + cy.get(ABOUT_RULE_DESCRIPTION).should('have.text', mlRule.description); cy.get(ABOUT_DETAILS).within(() => { - getDetails(SEVERITY_DETAILS).should('have.text', getMachineLearningRule().severity); - getDetails(RISK_SCORE_DETAILS).should('have.text', getMachineLearningRule().riskScore); + getDetails(SEVERITY_DETAILS).should('have.text', 'Critical'); + getDetails(RISK_SCORE_DETAILS).should('have.text', mlRule.risk_score); getDetails(REFERENCE_URLS_DETAILS).should((details) => { expect(removeExternalLinkText(details.text())).equal(expectedUrls); }); @@ -98,31 +101,26 @@ describe('Detection rules, machine learning', () => { getDetails(TAGS_DETAILS).should('have.text', expectedTags); }); cy.get(DEFINITION_DETAILS).within(() => { - getDetails(ANOMALY_SCORE_DETAILS).should( - 'have.text', - getMachineLearningRule().anomalyScoreThreshold - ); + getDetails(ANOMALY_SCORE_DETAILS).should('have.text', mlRule.anomaly_threshold); getDetails(RULE_TYPE_DETAILS).should('have.text', 'Machine Learning'); getDetails(TIMELINE_TEMPLATE_DETAILS).should('have.text', 'None'); + const machineLearningJobsArray = isArray(mlRule.machine_learning_job_id) + ? mlRule.machine_learning_job_id + : [mlRule.machine_learning_job_id]; // With the #1912 ML rule improvement changes we enable jobs on rule creation. // Though, in cypress jobs enabling does not work reliably and job can be started or stopped. // Thus, we disable next check till we fix the issue with enabling jobs in cypress. // Relevant ticket: https://github.com/elastic/security-team/issues/5389 // cy.get(MACHINE_LEARNING_JOB_STATUS).should('have.text', 'StoppedStopped'); - cy.get(MACHINE_LEARNING_JOB_ID).should( - 'have.text', - getMachineLearningRule().machineLearningJobs.join('') - ); + cy.get(MACHINE_LEARNING_JOB_ID).should('have.text', machineLearningJobsArray.join('')); }); cy.get(SCHEDULE_DETAILS).within(() => { - getDetails(RUNS_EVERY_DETAILS).should( - 'have.text', - `${getMachineLearningRule().runsEvery.interval}${getMachineLearningRule().runsEvery.type}` - ); - getDetails(ADDITIONAL_LOOK_BACK_DETAILS).should( - 'have.text', - `${getMachineLearningRule().lookBack.interval}${getMachineLearningRule().lookBack.type}` + getDetails(RUNS_EVERY_DETAILS).should('have.text', `${mlRule.interval}`); + const humanizedDuration = getHumanizedDuration( + mlRule.from ?? 'now-6m', + mlRule.interval ?? '5m' ); + getDetails(ADDITIONAL_LOOK_BACK_DETAILS).should('have.text', `${humanizedDuration}`); }); }); }); diff --git a/x-pack/plugins/security_solution/cypress/e2e/detection_rules/new_terms_rule.cy.ts b/x-pack/plugins/security_solution/cypress/e2e/detection_rules/new_terms_rule.cy.ts index 12c884e4a8b18..8eb5fc0ea90b7 100644 --- a/x-pack/plugins/security_solution/cypress/e2e/detection_rules/new_terms_rule.cy.ts +++ b/x-pack/plugins/security_solution/cypress/e2e/detection_rules/new_terms_rule.cy.ts @@ -5,9 +5,8 @@ * 2.0. */ -import { formatMitreAttackDescription } from '../../helpers/rules'; -import type { Mitre } from '../../objects/rule'; -import { getNewTermsRule } from '../../objects/rule'; +import { formatMitreAttackDescription, getHumanizedDuration } from '../../helpers/rules'; +import { getIndexPatterns, getNewTermsRule } from '../../objects/rule'; import { ALERT_DATA_GRID } from '../../screens/alerts'; import { @@ -46,7 +45,6 @@ import { import { getDetails } from '../../tasks/rule_details'; import { expectNumberOfRules, goToRuleDetails } from '../../tasks/alerts_detection_rules'; -import { createTimeline } from '../../tasks/api_calls/timelines'; import { cleanKibana, deleteAlertsAndRules } from '../../tasks/common'; import { createAndEnableRule, @@ -60,7 +58,6 @@ import { import { login, visit } from '../../tasks/login'; import { RULE_CREATION } from '../../urls/navigation'; -import type { CompleteTimeline } from '../../objects/timeline'; describe('New Terms rules', () => { before(() => { @@ -68,51 +65,42 @@ describe('New Terms rules', () => { login(); }); describe('Detection rules, New Terms', () => { - const expectedUrls = getNewTermsRule().referenceUrls?.join(''); - const expectedFalsePositives = getNewTermsRule().falsePositivesExamples?.join(''); - const expectedTags = getNewTermsRule().tags?.join(''); - const mitreAttack = getNewTermsRule().mitre as Mitre[]; - const expectedMitre = formatMitreAttackDescription(mitreAttack); + const rule = getNewTermsRule(); + const expectedUrls = rule.references?.join(''); + const expectedFalsePositives = rule.false_positives?.join(''); + const expectedTags = rule.tags?.join(''); + const mitreAttack = rule.threat; + const expectedMitre = formatMitreAttackDescription(mitreAttack ?? []); const expectedNumberOfRules = 1; beforeEach(() => { - const timeline = getNewTermsRule().timeline as CompleteTimeline; deleteAlertsAndRules(); - createTimeline(timeline).then((response) => { - cy.wrap({ - ...getNewTermsRule(), - timeline: { - ...timeline, - id: response.body.data.persistTimeline.timeline.savedObjectId, - }, - }).as('rule'); - }); }); it('Creates and enables a new terms rule', function () { visit(RULE_CREATION); selectNewTermsRuleType(); - fillDefineNewTermsRuleAndContinue(this.rule); - fillAboutRuleAndContinue(this.rule); - fillScheduleRuleAndContinue(this.rule); + fillDefineNewTermsRuleAndContinue(rule); + fillAboutRuleAndContinue(rule); + fillScheduleRuleAndContinue(rule); createAndEnableRule(); cy.get(CUSTOM_RULES_BTN).should('have.text', 'Custom rules (1)'); expectNumberOfRules(RULES_MANAGEMENT_TABLE, expectedNumberOfRules); - cy.get(RULE_NAME).should('have.text', this.rule.name); - cy.get(RISK_SCORE).should('have.text', this.rule.riskScore); - cy.get(SEVERITY).should('have.text', this.rule.severity); + cy.get(RULE_NAME).should('have.text', rule.name); + cy.get(RISK_SCORE).should('have.text', rule.risk_score); + cy.get(SEVERITY).should('have.text', 'High'); cy.get(RULE_SWITCH).should('have.attr', 'aria-checked', 'true'); goToRuleDetails(); - cy.get(RULE_NAME_HEADER).should('contain', `${this.rule.name}`); - cy.get(ABOUT_RULE_DESCRIPTION).should('have.text', this.rule.description); + cy.get(RULE_NAME_HEADER).should('contain', `${rule.name}`); + cy.get(ABOUT_RULE_DESCRIPTION).should('have.text', rule.description); cy.get(ABOUT_DETAILS).within(() => { - getDetails(SEVERITY_DETAILS).should('have.text', this.rule.severity); - getDetails(RISK_SCORE_DETAILS).should('have.text', this.rule.riskScore); + getDetails(SEVERITY_DETAILS).should('have.text', 'High'); + getDetails(RISK_SCORE_DETAILS).should('have.text', rule.risk_score); getDetails(REFERENCE_URLS_DETAILS).should((details) => { expect(removeExternalLinkText(details.text())).equal(expectedUrls); }); @@ -125,22 +113,20 @@ describe('New Terms rules', () => { cy.get(INVESTIGATION_NOTES_TOGGLE).click({ force: true }); cy.get(ABOUT_INVESTIGATION_NOTES).should('have.text', INVESTIGATION_NOTES_MARKDOWN); cy.get(DEFINITION_DETAILS).within(() => { - getDetails(INDEX_PATTERNS_DETAILS).should('have.text', 'auditbeat-*'); - getDetails(CUSTOM_QUERY_DETAILS).should('have.text', this.rule.customQuery); + getDetails(INDEX_PATTERNS_DETAILS).should('have.text', getIndexPatterns().join('')); + getDetails(CUSTOM_QUERY_DETAILS).should('have.text', rule.query); getDetails(RULE_TYPE_DETAILS).should('have.text', 'New Terms'); getDetails(TIMELINE_TEMPLATE_DETAILS).should('have.text', 'None'); getDetails(NEW_TERMS_FIELDS_DETAILS).should('have.text', 'host.name'); getDetails(NEW_TERMS_HISTORY_WINDOW_DETAILS).should('have.text', '51000h'); }); cy.get(SCHEDULE_DETAILS).within(() => { - getDetails(RUNS_EVERY_DETAILS).should( - 'have.text', - `${this.rule.runsEvery.interval}${this.rule.runsEvery.type}` - ); - getDetails(ADDITIONAL_LOOK_BACK_DETAILS).should( - 'have.text', - `${this.rule.lookBack.interval}${this.rule.lookBack.type}` + getDetails(RUNS_EVERY_DETAILS).should('have.text', `${rule.interval}`); + const humanizedDuration = getHumanizedDuration( + rule.from ?? 'now-6m', + rule.interval ?? '5m' ); + getDetails(ADDITIONAL_LOOK_BACK_DETAILS).should('have.text', `${humanizedDuration}`); }); waitForTheRuleToBeExecuted(); @@ -149,9 +135,9 @@ describe('New Terms rules', () => { cy.get(ALERT_DATA_GRID) .invoke('text') .then((text) => { - expect(text).contains(this.rule.name); - expect(text).contains(this.rule.severity.toLowerCase()); - expect(text).contains(this.rule.riskScore); + expect(text).contains(rule.name); + expect(text).contains(rule.severity); + expect(text).contains(rule.risk_score); }); }); }); diff --git a/x-pack/plugins/security_solution/cypress/e2e/detection_rules/override.cy.ts b/x-pack/plugins/security_solution/cypress/e2e/detection_rules/override.cy.ts index 2da675dd458e1..5b24bf20bff00 100644 --- a/x-pack/plugins/security_solution/cypress/e2e/detection_rules/override.cy.ts +++ b/x-pack/plugins/security_solution/cypress/e2e/detection_rules/override.cy.ts @@ -5,10 +5,8 @@ * 2.0. */ -import { formatMitreAttackDescription } from '../../helpers/rules'; -import type { Mitre, OverrideRule } from '../../objects/rule'; -import { getNewOverrideRule, getSeveritiesOverride } from '../../objects/rule'; -import type { CompleteTimeline } from '../../objects/timeline'; +import { formatMitreAttackDescription, getHumanizedDuration } from '../../helpers/rules'; +import { getIndexPatterns, getNewOverrideRule, getSeveritiesOverride } from '../../objects/rule'; import { ALERT_GRID_CELL, ALERTS_COUNT } from '../../screens/alerts'; @@ -50,7 +48,6 @@ import { } from '../../screens/rule_details'; import { expectNumberOfRules, goToRuleDetails } from '../../tasks/alerts_detection_rules'; -import { createTimeline } from '../../tasks/api_calls/timelines'; import { cleanKibana } from '../../tasks/common'; import { createAndEnableRule, @@ -66,57 +63,46 @@ import { getDetails } from '../../tasks/rule_details'; import { RULE_CREATION } from '../../urls/navigation'; describe('Detection rules, override', () => { - const expectedUrls = getNewOverrideRule().referenceUrls?.join(''); - const expectedFalsePositives = getNewOverrideRule().falsePositivesExamples?.join(''); - const expectedTags = getNewOverrideRule().tags?.join(''); - const mitreAttack = getNewOverrideRule().mitre as Mitre[]; - const expectedMitre = formatMitreAttackDescription(mitreAttack); + const rule = getNewOverrideRule(); + const expectedUrls = rule.references?.join(''); + const expectedFalsePositives = rule.false_positives?.join(''); + const expectedTags = rule.tags?.join(''); + const mitreAttack = rule.threat; + const expectedMitre = formatMitreAttackDescription(mitreAttack ?? []); before(() => { cleanKibana(); login(); }); - beforeEach(() => { - const timeline = getNewOverrideRule().timeline as CompleteTimeline; - createTimeline(timeline).then((response) => { - cy.wrap({ - ...getNewOverrideRule(), - timeline: { - ...timeline, - id: response.body.data.persistTimeline.timeline.savedObjectId, - }, - }).as('rule'); - }); - }); it('Creates and enables a new custom rule with override option', function () { visitWithoutDateRange(RULE_CREATION); - fillDefineCustomRuleAndContinue(this.rule); - fillAboutRuleWithOverrideAndContinue(this.rule); - fillScheduleRuleAndContinue(this.rule); + fillDefineCustomRuleAndContinue(rule); + fillAboutRuleWithOverrideAndContinue(rule); + fillScheduleRuleAndContinue(rule); createAndEnableRule(); cy.get(CUSTOM_RULES_BTN).should('have.text', 'Custom rules (1)'); expectNumberOfRules(RULES_MANAGEMENT_TABLE, 1); - cy.get(RULE_NAME).should('have.text', this.rule.name); - cy.get(RISK_SCORE).should('have.text', this.rule.riskScore); - cy.get(SEVERITY).should('have.text', this.rule.severity); + cy.get(RULE_NAME).should('have.text', rule.name); + cy.get(RISK_SCORE).should('have.text', rule.risk_score); + cy.get(SEVERITY).should('have.text', 'High'); cy.get(RULE_SWITCH).should('have.attr', 'aria-checked', 'true'); goToRuleDetails(); - cy.get(RULE_NAME_HEADER).should('contain', `${this.rule.name}`); - cy.get(ABOUT_RULE_DESCRIPTION).should('have.text', this.rule.description); + cy.get(RULE_NAME_HEADER).should('contain', `${rule.name}`); + cy.get(ABOUT_RULE_DESCRIPTION).should('have.text', rule.description); cy.get(ABOUT_DETAILS).within(() => { - getDetails(SEVERITY_DETAILS).should('have.text', this.rule.severity); - getDetails(RISK_SCORE_DETAILS).should('have.text', this.rule.riskScore); + getDetails(SEVERITY_DETAILS).should('have.text', 'High'); + getDetails(RISK_SCORE_DETAILS).should('have.text', rule.risk_score); getDetails(RISK_SCORE_OVERRIDE_DETAILS).should( 'have.text', - `${this.rule.riskOverride}kibana.alert.risk_score` + `${rule.risk_score_mapping?.[0].field}kibana.alert.risk_score` ); - getDetails(RULE_NAME_OVERRIDE_DETAILS).should('have.text', this.rule.nameOverride); + getDetails(RULE_NAME_OVERRIDE_DETAILS).should('have.text', rule.rule_name_override); getDetails(REFERENCE_URLS_DETAILS).should((details) => { expect(removeExternalLinkText(details.text())).equal(expectedUrls); }); @@ -125,16 +111,16 @@ describe('Detection rules, override', () => { expect(removeExternalLinkText(mitre.text())).equal(expectedMitre); }); getDetails(TAGS_DETAILS).should('have.text', expectedTags); - getDetails(TIMESTAMP_OVERRIDE_DETAILS).should('have.text', this.rule.timestampOverride); + getDetails(TIMESTAMP_OVERRIDE_DETAILS).should('have.text', rule.timestamp_override); cy.contains(DETAILS_TITLE, 'Severity override') .invoke('index', DETAILS_TITLE) // get index relative to other titles, not all siblings .then((severityOverrideIndex) => { - (this.rule as OverrideRule).severityOverride.forEach((severity, i) => { + rule.severity_mapping?.forEach((severity, i) => { cy.get(DETAILS_DESCRIPTION) .eq(severityOverrideIndex + i) .should( 'have.text', - `${severity.sourceField}:${severity.sourceValue}${getSeveritiesOverride()[i]}` + `${severity.field}:${severity.value}${getSeveritiesOverride()[i]}` ); }); }); @@ -142,20 +128,15 @@ describe('Detection rules, override', () => { cy.get(INVESTIGATION_NOTES_TOGGLE).click({ force: true }); cy.get(ABOUT_INVESTIGATION_NOTES).should('have.text', INVESTIGATION_NOTES_MARKDOWN); cy.get(DEFINITION_DETAILS).within(() => { - getDetails(INDEX_PATTERNS_DETAILS).should('have.text', 'auditbeat-*'); - getDetails(CUSTOM_QUERY_DETAILS).should('have.text', this.rule.customQuery); + getDetails(INDEX_PATTERNS_DETAILS).should('have.text', getIndexPatterns().join('')); + getDetails(CUSTOM_QUERY_DETAILS).should('have.text', rule.query); getDetails(RULE_TYPE_DETAILS).should('have.text', 'Query'); getDetails(TIMELINE_TEMPLATE_DETAILS).should('have.text', 'None'); }); cy.get(SCHEDULE_DETAILS).within(() => { - getDetails(RUNS_EVERY_DETAILS).should( - 'have.text', - `${this.rule.runsEvery.interval}${this.rule.runsEvery.type}` - ); - getDetails(ADDITIONAL_LOOK_BACK_DETAILS).should( - 'have.text', - `${this.rule.lookBack.interval}${this.rule.lookBack.type}` - ); + getDetails(RUNS_EVERY_DETAILS).should('have.text', `${rule.interval}`); + const humanizedDuration = getHumanizedDuration(rule.from ?? 'now-6m', rule.interval ?? '5m'); + getDetails(ADDITIONAL_LOOK_BACK_DETAILS).should('have.text', `${humanizedDuration}`); }); waitForTheRuleToBeExecuted(); diff --git a/x-pack/plugins/security_solution/cypress/e2e/detection_rules/persistent_rules_table_state.cy.ts b/x-pack/plugins/security_solution/cypress/e2e/detection_rules/persistent_rules_table_state.cy.ts index 560c6417759c3..3d9653ae383ee 100644 --- a/x-pack/plugins/security_solution/cypress/e2e/detection_rules/persistent_rules_table_state.cy.ts +++ b/x-pack/plugins/security_solution/cypress/e2e/detection_rules/persistent_rules_table_state.cy.ts @@ -37,7 +37,7 @@ import { expectFilterByEnabledRules, } from '../../tasks/alerts_detection_rules'; import { RULES_MANAGEMENT_TABLE } from '../../screens/alerts_detection_rules'; -import { createCustomRule } from '../../tasks/api_calls/rules'; +import { createRule } from '../../tasks/api_calls/rules'; import { expectRowsPerPage, expectTablePage, @@ -47,25 +47,27 @@ import { sortByTableColumn, } from '../../tasks/table_pagination'; -function createRule(id: string, name: string, tags?: string[], enabled = false): void { - const rule = getNewRule(); - - rule.name = name; - rule.tags = tags; - rule.enabled = enabled; - - createCustomRule(rule, id); -} - function createTestRules(): void { - createRule('1', 'test 1', ['tag-a']); - createRule('2', 'rule 1', ['tag-b']); - createRule('3', 'rule 2', ['tag-b']); - createRule('4', 'rule 3', ['tag-b', 'tag-c']); - createRule('5', 'rule 4', ['tag-b']); - createRule('6', 'rule 5', ['tag-b', 'tag-c']); - createRule('7', 'rule 6', ['tag-b']); - createRule('8', 'rule 7', ['tag-b'], true); + createRule({ ...getNewRule(), rule_id: '1', name: 'test 1', tags: ['tag-a'], enabled: false }); + createRule({ ...getNewRule(), rule_id: '2', name: 'rule 1', tags: ['tag-b'], enabled: false }); + createRule({ ...getNewRule(), rule_id: '3', name: 'rule 2', tags: ['tag-b'], enabled: false }); + createRule({ + ...getNewRule(), + rule_id: '4', + name: 'rule 3', + tags: ['tag-b', 'tag-c'], + enabled: false, + }); + createRule({ ...getNewRule(), rule_id: '5', name: 'rule 4', tags: ['tag-b'], enabled: false }); + createRule({ + ...getNewRule(), + rule_id: '6', + name: 'rule 5', + tags: ['tag-b', 'tag-c'], + enabled: false, + }); + createRule({ ...getNewRule(), rule_id: '7', name: 'rule 6', tags: ['tag-b'], enabled: false }); + createRule({ ...getNewRule(), rule_id: '8', name: 'rule 7', tags: ['tag-b'], enabled: true }); } function visitRulesTableWithState(urlTableState: Record): void { diff --git a/x-pack/plugins/security_solution/cypress/e2e/detection_rules/rule_actions.cy.ts b/x-pack/plugins/security_solution/cypress/e2e/detection_rules/rule_actions.cy.ts index b0f78cc76881c..5ed5ef8be059a 100644 --- a/x-pack/plugins/security_solution/cypress/e2e/detection_rules/rule_actions.cy.ts +++ b/x-pack/plugins/security_solution/cypress/e2e/detection_rules/rule_actions.cy.ts @@ -42,20 +42,18 @@ describe('Rule actions during detection rule creation', () => { deleteDataView(indexConnector.index); }); - const rule = { - ...getSimpleCustomQueryRule(), - actions: { throttle: 'rule', connectors: [indexConnector] }, - }; - const index = rule.actions.connectors[0].index; + const rule = getSimpleCustomQueryRule(); + const actions = { throttle: 'rule', connectors: [indexConnector] }; + const index = actions.connectors[0].index; const initialNumberOfDocuments = 0; - const expectedJson = JSON.parse(rule.actions.connectors[0].document); + const expectedJson = JSON.parse(actions.connectors[0].document); it('Indexes a new document after the index action is triggered ', function () { visit(RULE_CREATION); fillDefineCustomRuleAndContinue(rule); fillAboutRuleAndContinue(rule); fillScheduleRuleAndContinue(rule); - fillRuleAction(rule); + fillRuleAction(actions); createAndEnableRule(); goToRuleDetails(); diff --git a/x-pack/plugins/security_solution/cypress/e2e/detection_rules/rules_table_auto_refresh.cy.ts b/x-pack/plugins/security_solution/cypress/e2e/detection_rules/rules_table_auto_refresh.cy.ts index 1ac6490ff3097..24cc716fae8c1 100644 --- a/x-pack/plugins/security_solution/cypress/e2e/detection_rules/rules_table_auto_refresh.cy.ts +++ b/x-pack/plugins/security_solution/cypress/e2e/detection_rules/rules_table_auto_refresh.cy.ts @@ -26,7 +26,7 @@ import { import { login, visit, visitWithoutDateRange } from '../../tasks/login'; import { DETECTIONS_RULE_MANAGEMENT_URL } from '../../urls/navigation'; -import { createCustomRule } from '../../tasks/api_calls/rules'; +import { createRule } from '../../tasks/api_calls/rules'; import { cleanKibana } from '../../tasks/common'; import { getNewRule } from '../../objects/rule'; import { setRowsPerPageTo } from '../../tasks/table_pagination'; @@ -38,7 +38,7 @@ describe('Alerts detection rules table auto-refresh', () => { cleanKibana(); login(); for (let i = 1; i < 7; i += 1) { - createCustomRule({ ...getNewRule(), name: `Test rule ${i}` }, `${i}`); + createRule({ ...getNewRule(), name: `Test rule ${i}`, rule_id: `${i}` }); } }); diff --git a/x-pack/plugins/security_solution/cypress/e2e/detection_rules/sorting.cy.ts b/x-pack/plugins/security_solution/cypress/e2e/detection_rules/sorting.cy.ts index 2364da76bddf3..33aa52abfe41b 100644 --- a/x-pack/plugins/security_solution/cypress/e2e/detection_rules/sorting.cy.ts +++ b/x-pack/plugins/security_solution/cypress/e2e/detection_rules/sorting.cy.ts @@ -22,7 +22,7 @@ import { import { login, visit } from '../../tasks/login'; import { DETECTIONS_RULE_MANAGEMENT_URL } from '../../urls/navigation'; -import { createCustomRule } from '../../tasks/api_calls/rules'; +import { createRule } from '../../tasks/api_calls/rules'; import { cleanKibana } from '../../tasks/common'; import { getExistingRule, @@ -37,10 +37,10 @@ describe('Alerts detection rules', () => { before(() => { cleanKibana(); login(); - createCustomRule(getNewRule(), '1'); - createCustomRule(getExistingRule(), '2'); - createCustomRule(getNewOverrideRule(), '3'); - createCustomRule(getNewThresholdRule(), '4'); + createRule({ ...getNewRule(), rule_id: '1' }); + createRule({ ...getExistingRule(), rule_id: '2' }); + createRule({ ...getNewOverrideRule(), rule_id: '3' }); + createRule({ ...getNewThresholdRule(), rule_id: '4' }); }); it('Sorts by enabled rules', () => { @@ -62,8 +62,8 @@ describe('Alerts detection rules', () => { }); it('Pagination updates page number and results', () => { - createCustomRule({ ...getNewRule(), name: 'Test a rule' }, '5'); - createCustomRule({ ...getNewRule(), name: 'Not same as first rule' }, '6'); + createRule({ ...getNewRule(), name: 'Test a rule', rule_id: '5' }); + createRule({ ...getNewRule(), name: 'Not same as first rule', rule_id: '6' }); visit(DETECTIONS_RULE_MANAGEMENT_URL); waitForRulesTableToBeLoaded(); diff --git a/x-pack/plugins/security_solution/cypress/e2e/detection_rules/threshold_rule.cy.ts b/x-pack/plugins/security_solution/cypress/e2e/detection_rules/threshold_rule.cy.ts index 5f19e56755f89..e20fac10624ac 100644 --- a/x-pack/plugins/security_solution/cypress/e2e/detection_rules/threshold_rule.cy.ts +++ b/x-pack/plugins/security_solution/cypress/e2e/detection_rules/threshold_rule.cy.ts @@ -5,9 +5,8 @@ * 2.0. */ -import { formatMitreAttackDescription } from '../../helpers/rules'; -import type { Mitre } from '../../objects/rule'; -import { getNewThresholdRule } from '../../objects/rule'; +import { formatMitreAttackDescription, getHumanizedDuration } from '../../helpers/rules'; +import { getIndexPatterns, getNewThresholdRule } from '../../objects/rule'; import { ALERTS_COUNT, ALERT_GRID_CELL } from '../../screens/alerts'; @@ -46,7 +45,6 @@ import { import { getDetails } from '../../tasks/rule_details'; import { expectNumberOfRules, goToRuleDetails } from '../../tasks/alerts_detection_rules'; -import { createTimeline } from '../../tasks/api_calls/timelines'; import { cleanKibana, deleteAlertsAndRules } from '../../tasks/common'; import { createAndEnableRule, @@ -60,15 +58,14 @@ import { import { login, visitWithoutDateRange } from '../../tasks/login'; import { RULE_CREATION } from '../../urls/navigation'; -import type { CompleteTimeline } from '../../objects/timeline'; describe('Detection rules, threshold', () => { - let rule = getNewThresholdRule(); - const expectedUrls = getNewThresholdRule().referenceUrls?.join(''); - const expectedFalsePositives = getNewThresholdRule().falsePositivesExamples?.join(''); - const expectedTags = getNewThresholdRule().tags?.join(''); - const mitreAttack = getNewThresholdRule().mitre as Mitre[]; - const expectedMitre = formatMitreAttackDescription(mitreAttack); + const rule = getNewThresholdRule(); + const expectedUrls = rule.references?.join(''); + const expectedFalsePositives = rule.false_positives?.join(''); + const expectedTags = rule.tags?.join(''); + const mitreAttack = rule.threat; + const expectedMitre = formatMitreAttackDescription(mitreAttack ?? []); before(() => { cleanKibana(); @@ -76,12 +73,7 @@ describe('Detection rules, threshold', () => { }); beforeEach(() => { - rule = getNewThresholdRule(); - const timeline = rule.timeline as CompleteTimeline; deleteAlertsAndRules(); - createTimeline(timeline).then((response) => { - timeline.id = response.body.data.persistTimeline.timeline.savedObjectId; - }); visitWithoutDateRange(RULE_CREATION); }); @@ -97,8 +89,8 @@ describe('Detection rules, threshold', () => { expectNumberOfRules(RULES_MANAGEMENT_TABLE, 1); cy.get(RULE_NAME).should('have.text', rule.name); - cy.get(RISK_SCORE).should('have.text', rule.riskScore); - cy.get(SEVERITY).should('have.text', rule.severity); + cy.get(RISK_SCORE).should('have.text', rule.risk_score); + cy.get(SEVERITY).should('have.text', 'High'); cy.get(RULE_SWITCH).should('have.attr', 'aria-checked', 'true'); goToRuleDetails(); @@ -106,8 +98,8 @@ describe('Detection rules, threshold', () => { cy.get(RULE_NAME_HEADER).should('contain', `${rule.name}`); cy.get(ABOUT_RULE_DESCRIPTION).should('have.text', rule.description); cy.get(ABOUT_DETAILS).within(() => { - getDetails(SEVERITY_DETAILS).should('have.text', rule.severity); - getDetails(RISK_SCORE_DETAILS).should('have.text', rule.riskScore); + getDetails(SEVERITY_DETAILS).should('have.text', 'High'); + getDetails(RISK_SCORE_DETAILS).should('have.text', rule.risk_score); getDetails(REFERENCE_URLS_DETAILS).should((details) => { expect(removeExternalLinkText(details.text())).equal(expectedUrls); }); @@ -120,24 +112,19 @@ describe('Detection rules, threshold', () => { cy.get(INVESTIGATION_NOTES_TOGGLE).click({ force: true }); cy.get(ABOUT_INVESTIGATION_NOTES).should('have.text', INVESTIGATION_NOTES_MARKDOWN); cy.get(DEFINITION_DETAILS).within(() => { - getDetails(INDEX_PATTERNS_DETAILS).should('have.text', 'auditbeat-*'); - getDetails(CUSTOM_QUERY_DETAILS).should('have.text', rule.customQuery); + getDetails(INDEX_PATTERNS_DETAILS).should('have.text', getIndexPatterns().join('')); + getDetails(CUSTOM_QUERY_DETAILS).should('have.text', rule.query); getDetails(RULE_TYPE_DETAILS).should('have.text', 'Threshold'); getDetails(TIMELINE_TEMPLATE_DETAILS).should('have.text', 'None'); getDetails(THRESHOLD_DETAILS).should( 'have.text', - `Results aggregated by ${rule.thresholdField} >= ${rule.threshold}` + `Results aggregated by ${rule.threshold.field} >= ${rule.threshold.value}` ); }); cy.get(SCHEDULE_DETAILS).within(() => { - getDetails(RUNS_EVERY_DETAILS).should( - 'have.text', - `${rule.runsEvery?.interval}${rule.runsEvery?.type}` - ); - getDetails(ADDITIONAL_LOOK_BACK_DETAILS).should( - 'have.text', - `${rule.lookBack?.interval}${rule.lookBack?.type}` - ); + getDetails(RUNS_EVERY_DETAILS).should('have.text', `${rule.interval}`); + const humanizedDuration = getHumanizedDuration(rule.from ?? 'now-6m', rule.interval ?? '5m'); + getDetails(ADDITIONAL_LOOK_BACK_DETAILS).should('have.text', `${humanizedDuration}`); }); waitForTheRuleToBeExecuted(); diff --git a/x-pack/plugins/security_solution/cypress/e2e/exceptions/add_edit_flyout/flyout_validation.cy.ts b/x-pack/plugins/security_solution/cypress/e2e/exceptions/add_edit_flyout/flyout_validation.cy.ts index 8a0789b438363..5a174cfc6c1b1 100644 --- a/x-pack/plugins/security_solution/cypress/e2e/exceptions/add_edit_flyout/flyout_validation.cy.ts +++ b/x-pack/plugins/security_solution/cypress/e2e/exceptions/add_edit_flyout/flyout_validation.cy.ts @@ -9,7 +9,7 @@ import { getNewRule } from '../../../objects/rule'; import { RULE_STATUS } from '../../../screens/create_new_rule'; -import { createCustomRule } from '../../../tasks/api_calls/rules'; +import { createRule } from '../../../tasks/api_calls/rules'; import { goToRuleDetails } from '../../../tasks/alerts_detection_rules'; import { esArchiverLoad, @@ -78,13 +78,11 @@ describe('Exceptions flyout', { testIsolation: false }, () => { esArchiverLoad('conflicts_2'); login(); createExceptionList(getExceptionList(), getExceptionList().list_id).then((response) => - createCustomRule({ + createRule({ ...getNewRule(), - dataSource: { - index: ['auditbeat-*', 'exceptions-*', 'conflicts-*'], - type: 'indexPatterns', - }, - exceptionLists: [ + index: ['auditbeat-*', 'exceptions-*', 'conflicts-*'], + enabled: false, + exceptions_list: [ { id: response.body.id, list_id: getExceptionList().list_id, @@ -318,7 +316,7 @@ describe('Exceptions flyout', { testIsolation: false }, () => { cy.get(CONFIRM_BTN).should('be.enabled'); }); - it('Warns users about mapping conflicts on problematic field selection', async () => { + it.skip('Warns users about mapping conflicts on problematic field selection', async () => { // open add exception modal openExceptionFlyoutFromEmptyViewerPrompt(); diff --git a/x-pack/plugins/security_solution/cypress/e2e/exceptions/alerts_table_flow/add_exception.cy.ts b/x-pack/plugins/security_solution/cypress/e2e/exceptions/alerts_table_flow/add_exception.cy.ts index 41b190c8ccc0d..774659fd67848 100644 --- a/x-pack/plugins/security_solution/cypress/e2e/exceptions/alerts_table_flow/add_exception.cy.ts +++ b/x-pack/plugins/security_solution/cypress/e2e/exceptions/alerts_table_flow/add_exception.cy.ts @@ -9,7 +9,7 @@ import { ROLES } from '../../../../common/test'; import { getExceptionList, expectedExportedExceptionList } from '../../../objects/exception'; import { getNewRule } from '../../../objects/rule'; -import { createCustomRule } from '../../../tasks/api_calls/rules'; +import { createRule } from '../../../tasks/api_calls/rules'; import { login, visitWithoutDateRange, waitForPageWithoutDateRange } from '../../../tasks/login'; import { EXCEPTIONS_URL } from '../../../urls/navigation'; @@ -48,9 +48,9 @@ describe('Exceptions Table', () => { // Create exception list associated with a rule createExceptionList(getExceptionList2(), getExceptionList2().list_id).then((response) => - createCustomRule({ + createRule({ ...getNewRule(), - exceptionLists: [ + exceptions_list: [ { id: response.body.id, list_id: getExceptionList2().list_id, diff --git a/x-pack/plugins/security_solution/cypress/e2e/exceptions/exceptions_management_flow/add_edit_exception.cy.ts b/x-pack/plugins/security_solution/cypress/e2e/exceptions/exceptions_management_flow/add_edit_exception.cy.ts index e5b559c402b59..4ba1f4b8e7d44 100644 --- a/x-pack/plugins/security_solution/cypress/e2e/exceptions/exceptions_management_flow/add_edit_exception.cy.ts +++ b/x-pack/plugins/security_solution/cypress/e2e/exceptions/exceptions_management_flow/add_edit_exception.cy.ts @@ -11,7 +11,7 @@ import { } from '../../../tasks/es_archiver'; import { getNewRule } from '../../../objects/rule'; import { login, visitWithoutDateRange } from '../../../tasks/login'; -import { createCustomRule, deleteCustomRule } from '../../../tasks/api_calls/rules'; +import { createRule, deleteCustomRule } from '../../../tasks/api_calls/rules'; import { editException, editExceptionFlyoutItemName } from '../../../tasks/exceptions'; import { EXCEPTIONS_URL } from '../../../urls/navigation'; @@ -28,7 +28,7 @@ describe('Add/edit exception from exception management page', () => { esArchiverLoad('exceptions'); login(); visitWithoutDateRange(EXCEPTIONS_URL); - createCustomRule(getNewRule()); + createRule(getNewRule()); }); after(() => { diff --git a/x-pack/plugins/security_solution/cypress/e2e/exceptions/exceptions_management_flow/exceptions_table.cy.ts b/x-pack/plugins/security_solution/cypress/e2e/exceptions/exceptions_management_flow/exceptions_table.cy.ts index 99d38ef3c5819..3eaf3baa245f8 100644 --- a/x-pack/plugins/security_solution/cypress/e2e/exceptions/exceptions_management_flow/exceptions_table.cy.ts +++ b/x-pack/plugins/security_solution/cypress/e2e/exceptions/exceptions_management_flow/exceptions_table.cy.ts @@ -9,7 +9,7 @@ import { ROLES } from '../../../../common/test'; import { getExceptionList, expectedExportedExceptionList } from '../../../objects/exception'; import { getNewRule } from '../../../objects/rule'; -import { createCustomRule } from '../../../tasks/api_calls/rules'; +import { createRule } from '../../../tasks/api_calls/rules'; import { login, visitWithoutDateRange, waitForPageWithoutDateRange } from '../../../tasks/login'; import { EXCEPTIONS_URL } from '../../../urls/navigation'; @@ -48,9 +48,9 @@ describe('Exceptions Table', () => { // Create exception list associated with a rule createExceptionList(getExceptionList2(), getExceptionList2().list_id).then((response) => - createCustomRule({ + createRule({ ...getNewRule(), - exceptionLists: [ + exceptions_list: [ { id: response.body.id, list_id: getExceptionList2().list_id, diff --git a/x-pack/plugins/security_solution/cypress/e2e/exceptions/list_management_flow/list_details.cy.ts b/x-pack/plugins/security_solution/cypress/e2e/exceptions/list_management_flow/list_details.cy.ts new file mode 100644 index 0000000000000..2266636f8ca1f --- /dev/null +++ b/x-pack/plugins/security_solution/cypress/e2e/exceptions/list_management_flow/list_details.cy.ts @@ -0,0 +1,93 @@ +/* + * 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 { getExceptionList } from '../../../objects/exception'; +import { getNewRule } from '../../../objects/rule'; + +import { login, visitWithoutDateRange } from '../../../tasks/login'; +import { createRule } from '../../../tasks/api_calls/rules'; +import { exceptionsListDetailsUrl } from '../../../urls/navigation'; +import { + editExceptionLisDetails, + waitForExceptionListDetailToBeLoaded, +} from '../../../tasks/exceptions_table'; +import { createExceptionList } from '../../../tasks/api_calls/exceptions'; +import { esArchiverResetKibana } from '../../../tasks/es_archiver'; +import { + EXCEPTIONS_LIST_MANAGEMENT_NAME, + EXCEPTIONS_LIST_MANAGEMENT_DESCRIPTION, +} from '../../../screens/exceptions'; + +const LIST_NAME = 'My exception list'; +const UPDATED_LIST_NAME = 'Updated exception list'; +const LIST_DESCRIPTION = 'This is the exception list description.'; +const UPDATED_LIST_DESCRIPTION = 'This is an updated version of the exception list description.'; + +const getExceptionList1 = () => ({ + ...getExceptionList(), + name: LIST_NAME, + description: LIST_DESCRIPTION, + list_id: 'exception_list_test', +}); + +describe('Exception list management page', () => { + before(() => { + esArchiverResetKibana(); + login(); + + // Create exception list associated with a rule + createExceptionList(getExceptionList1(), getExceptionList1().list_id).then((response) => + createRule({ + ...getNewRule(), + exceptions_list: [ + { + id: response.body.id, + list_id: getExceptionList1().list_id, + type: getExceptionList1().type, + namespace_type: getExceptionList1().namespace_type, + }, + ], + }) + ); + }); + + beforeEach(() => { + visitWithoutDateRange(exceptionsListDetailsUrl(getExceptionList1().list_id)); + waitForExceptionListDetailToBeLoaded(); + }); + + it('Edits list details', () => { + // Check list details are loaded + cy.get(EXCEPTIONS_LIST_MANAGEMENT_NAME).should('have.text', LIST_NAME); + cy.get(EXCEPTIONS_LIST_MANAGEMENT_DESCRIPTION).should('have.text', LIST_DESCRIPTION); + + // Update list details in edit modal + editExceptionLisDetails({ + name: { original: LIST_NAME, updated: UPDATED_LIST_NAME }, + description: { original: LIST_DESCRIPTION, updated: UPDATED_LIST_DESCRIPTION }, + }); + + // Ensure that list details were updated + cy.get(EXCEPTIONS_LIST_MANAGEMENT_NAME).should('have.text', UPDATED_LIST_NAME); + cy.get(EXCEPTIONS_LIST_MANAGEMENT_DESCRIPTION).should('have.text', UPDATED_LIST_DESCRIPTION); + + // Ensure that list details changes persisted + visitWithoutDateRange(exceptionsListDetailsUrl(getExceptionList1().list_id)); + cy.get(EXCEPTIONS_LIST_MANAGEMENT_NAME).should('have.text', UPDATED_LIST_NAME); + cy.get(EXCEPTIONS_LIST_MANAGEMENT_DESCRIPTION).should('have.text', UPDATED_LIST_DESCRIPTION); + + // Remove description + editExceptionLisDetails({ + description: { original: UPDATED_LIST_DESCRIPTION, updated: null }, + }); + cy.get(EXCEPTIONS_LIST_MANAGEMENT_DESCRIPTION).should('have.text', 'Add a description'); + + // Ensure description removal persisted + visitWithoutDateRange(exceptionsListDetailsUrl(getExceptionList1().list_id)); + cy.get(EXCEPTIONS_LIST_MANAGEMENT_DESCRIPTION).should('have.text', 'Add a description'); + }); +}); diff --git a/x-pack/plugins/security_solution/cypress/e2e/exceptions/rule_details_flow/add_edit_endpoint_exception.cy.ts b/x-pack/plugins/security_solution/cypress/e2e/exceptions/rule_details_flow/add_edit_endpoint_exception.cy.ts index 8232f14b5259f..340db59c651bb 100644 --- a/x-pack/plugins/security_solution/cypress/e2e/exceptions/rule_details_flow/add_edit_endpoint_exception.cy.ts +++ b/x-pack/plugins/security_solution/cypress/e2e/exceptions/rule_details_flow/add_edit_endpoint_exception.cy.ts @@ -7,7 +7,7 @@ import { getNewRule } from '../../../objects/rule'; -import { createCustomRule } from '../../../tasks/api_calls/rules'; +import { createRule } from '../../../tasks/api_calls/rules'; import { goToRuleDetails } from '../../../tasks/alerts_detection_rules'; import { esArchiverLoad, @@ -59,22 +59,20 @@ describe('Add endpoint exception from rule details', () => { deleteAlertsAndRules(); // create rule with exception createEndpointExceptionList().then((response) => { - createCustomRule( - { - ...getNewRule(), - customQuery: 'event.code:*', - dataSource: { index: ['auditbeat*'], type: 'indexPatterns' }, - exceptionLists: [ - { - id: response.body.id, - list_id: response.body.list_id, - type: response.body.type, - namespace_type: response.body.namespace_type, - }, - ], - }, - '2' - ); + createRule({ + ...getNewRule(), + query: 'event.code:*', + index: ['auditbeat*'], + exceptions_list: [ + { + id: response.body.id, + list_id: response.body.list_id, + type: response.body.type, + namespace_type: response.body.namespace_type, + }, + ], + rule_id: '2', + }); }); }); @@ -128,7 +126,7 @@ describe('Add endpoint exception from rule details', () => { it('edits an endpoint exception item', () => { const NEW_ITEM_NAME = 'Exception item-EDITED'; const ITEM_FIELD = 'event.code'; - const FIELD_DIFFERENT_FROM_EXISTING_ITEM_FIELD = 'agent.name'; + const FIELD_DIFFERENT_FROM_EXISTING_ITEM_FIELD = 'agent.type'; // displays existing exception items cy.get(EXCEPTION_ITEM_VIEWER_CONTAINER).should('have.length', 1); @@ -161,7 +159,7 @@ describe('Add endpoint exception from rule details', () => { // check that updates stuck cy.get(EXCEPTION_CARD_ITEM_NAME).should('have.text', NEW_ITEM_NAME); - cy.get(EXCEPTION_CARD_ITEM_CONDITIONS).should('have.text', ' agent.nameIS foo'); + cy.get(EXCEPTION_CARD_ITEM_CONDITIONS).should('have.text', ' agent.typeIS foo'); }); it('allows user to search for items', () => { diff --git a/x-pack/plugins/security_solution/cypress/e2e/exceptions/rule_details_flow/add_edit_exception.cy.ts b/x-pack/plugins/security_solution/cypress/e2e/exceptions/rule_details_flow/add_edit_exception.cy.ts index 1439e5ed88210..7253b118cecd0 100644 --- a/x-pack/plugins/security_solution/cypress/e2e/exceptions/rule_details_flow/add_edit_exception.cy.ts +++ b/x-pack/plugins/security_solution/cypress/e2e/exceptions/rule_details_flow/add_edit_exception.cy.ts @@ -9,7 +9,7 @@ import { getException, getExceptionList } from '../../../objects/exception'; import { getNewRule } from '../../../objects/rule'; import { ALERTS_COUNT, EMPTY_ALERT_TABLE } from '../../../screens/alerts'; -import { createCustomRule, createCustomRuleEnabled } from '../../../tasks/api_calls/rules'; +import { createRule } from '../../../tasks/api_calls/rules'; import { goToRuleDetails } from '../../../tasks/alerts_detection_rules'; import { goToClosedAlertsOnRuleDetailsPage, @@ -86,22 +86,20 @@ describe('Add/edit exception from rule details', () => { deleteExceptionList(exceptionList.list_id, exceptionList.namespace_type); // create rule with exceptions createExceptionList(exceptionList, exceptionList.list_id).then((response) => { - createCustomRule( - { - ...getNewRule(), - customQuery: 'agent.name:*', - dataSource: { index: ['exceptions*'], type: 'indexPatterns' }, - exceptionLists: [ - { - id: response.body.id, - list_id: exceptionList.list_id, - type: exceptionList.type, - namespace_type: exceptionList.namespace_type, - }, - ], - }, - '2' - ); + createRule({ + ...getNewRule(), + query: 'agent.name:*', + index: ['exceptions*'], + exceptions_list: [ + { + id: response.body.id, + list_id: exceptionList.list_id, + type: exceptionList.type, + namespace_type: exceptionList.namespace_type, + }, + ], + rule_id: '2', + }); createExceptionListItem(exceptionList.list_id, { list_id: exceptionList.list_id, item_id: 'simple_list_item', @@ -251,19 +249,13 @@ describe('Add/edit exception from rule details', () => { describe('rule without existing exceptions', () => { beforeEach(() => { deleteAlertsAndRules(); - createCustomRuleEnabled( - { - ...getNewRule(), - customQuery: 'agent.name:*', - dataSource: { index: ['exceptions*'], type: 'indexPatterns' }, - runsEvery: { - interval: '10', - timeType: 'Seconds', - type: 's', - }, - }, - 'rule_testing' - ); + createRule({ + ...getNewRule(), + query: 'agent.name:*', + index: ['exceptions*'], + interval: '10s', + rule_id: 'rule_testing', + }); visitWithoutDateRange(DETECTIONS_RULE_MANAGEMENT_URL); goToRuleDetails(); goToExceptionsTab(); diff --git a/x-pack/plugins/security_solution/cypress/e2e/exceptions/rule_details_flow/add_edit_exception_data_view.cy.ts b/x-pack/plugins/security_solution/cypress/e2e/exceptions/rule_details_flow/add_edit_exception_data_view.cy.ts index b939f11219eab..c1216e0b1efa0 100644 --- a/x-pack/plugins/security_solution/cypress/e2e/exceptions/rule_details_flow/add_edit_exception_data_view.cy.ts +++ b/x-pack/plugins/security_solution/cypress/e2e/exceptions/rule_details_flow/add_edit_exception_data_view.cy.ts @@ -8,7 +8,7 @@ import { LOADING_INDICATOR } from '../../../screens/security_header'; import { getNewRule } from '../../../objects/rule'; import { ALERTS_COUNT, EMPTY_ALERT_TABLE } from '../../../screens/alerts'; -import { createCustomRuleEnabled } from '../../../tasks/api_calls/rules'; +import { createRule } from '../../../tasks/api_calls/rules'; import { goToRuleDetails } from '../../../tasks/alerts_detection_rules'; import { addExceptionFromFirstAlert, @@ -71,19 +71,13 @@ describe('Add exception using data views from rule details', () => { beforeEach(() => { deleteAlertsAndRules(); - createCustomRuleEnabled( - { - ...getNewRule(), - customQuery: 'agent.name:*', - dataSource: { dataView: 'exceptions-*', type: 'dataView' }, - runsEvery: { - interval: '10', - timeType: 'Seconds', - type: 's', - }, - }, - 'rule_testing' - ); + createRule({ + ...getNewRule(), + query: 'agent.name:*', + data_view_id: 'exceptions-*', + interval: '10s', + rule_id: 'rule_testing', + }); visitWithoutDateRange(DETECTIONS_RULE_MANAGEMENT_URL); goToRuleDetails(); waitForAlertsToPopulate(); diff --git a/x-pack/plugins/security_solution/cypress/e2e/exceptions/rule_details_flow/read_only_view.cy.ts b/x-pack/plugins/security_solution/cypress/e2e/exceptions/rule_details_flow/read_only_view.cy.ts index 9bc7d19eb44f7..8c346bb603d5e 100644 --- a/x-pack/plugins/security_solution/cypress/e2e/exceptions/rule_details_flow/read_only_view.cy.ts +++ b/x-pack/plugins/security_solution/cypress/e2e/exceptions/rule_details_flow/read_only_view.cy.ts @@ -8,7 +8,7 @@ import { getExceptionList } from '../../../objects/exception'; import { getNewRule } from '../../../objects/rule'; import { ROLES } from '../../../../common/test'; -import { createCustomRule } from '../../../tasks/api_calls/rules'; +import { createRule } from '../../../tasks/api_calls/rules'; import { esArchiverResetKibana } from '../../../tasks/es_archiver'; import { login, visitWithoutDateRange } from '../../../tasks/login'; import { goToExceptionsTab, goToAlertsTab } from '../../../tasks/rule_details'; @@ -35,22 +35,20 @@ describe('Exceptions viewer read only', () => { esArchiverResetKibana(); // create rule with exceptions createExceptionList(exceptionList, exceptionList.list_id).then((response) => { - createCustomRule( - { - ...getNewRule(), - customQuery: 'agent.name:*', - dataSource: { index: ['exceptions*'], type: 'indexPatterns' }, - exceptionLists: [ - { - id: response.body.id, - list_id: exceptionList.list_id, - type: exceptionList.type, - namespace_type: exceptionList.namespace_type, - }, - ], - }, - '2' - ); + createRule({ + ...getNewRule(), + query: 'agent.name:*', + index: ['exceptions*'], + exceptions_list: [ + { + id: response.body.id, + list_id: exceptionList.list_id, + type: exceptionList.type, + namespace_type: exceptionList.namespace_type, + }, + ], + rule_id: '2', + }); }); login(ROLES.reader); diff --git a/x-pack/plugins/security_solution/cypress/e2e/guided_onboarding/tour.cy.ts b/x-pack/plugins/security_solution/cypress/e2e/guided_onboarding/tour.cy.ts index e1dc50d8d28c8..10b4827f721b4 100644 --- a/x-pack/plugins/security_solution/cypress/e2e/guided_onboarding/tour.cy.ts +++ b/x-pack/plugins/security_solution/cypress/e2e/guided_onboarding/tour.cy.ts @@ -20,7 +20,7 @@ import { startTour, } from '../../tasks/guided_onboarding'; import { cleanKibana } from '../../tasks/common'; -import { createCustomRuleEnabled } from '../../tasks/api_calls/rules'; +import { createRule } from '../../tasks/api_calls/rules'; import { getNewRule } from '../../objects/rule'; import { ALERTS_URL, DASHBOARDS_URL } from '../../urls/navigation'; import { waitForAlertsToPopulate } from '../../tasks/create_new_rule'; @@ -32,7 +32,7 @@ describe('Guided onboarding tour', () => { before(() => { cleanKibana(); login(); - createCustomRuleEnabled({ ...getNewRule(), customQuery: 'user.name:*' }); + createRule({ ...getNewRule(), query: 'user.name:*' }); }); beforeEach(() => { startAlertsCasesTour(); diff --git a/x-pack/plugins/security_solution/cypress/e2e/hosts/events_viewer.cy.ts b/x-pack/plugins/security_solution/cypress/e2e/hosts/events_viewer.cy.ts index 0929a673828c1..c3600e459d4bd 100644 --- a/x-pack/plugins/security_solution/cypress/e2e/hosts/events_viewer.cy.ts +++ b/x-pack/plugins/security_solution/cypress/e2e/hosts/events_viewer.cy.ts @@ -14,7 +14,6 @@ import { import { HOST_GEO_CITY_NAME_HEADER, HOST_GEO_COUNTRY_NAME_HEADER, - INSPECT_MODAL, SERVER_SIDE_EVENT_COUNT, } from '../../screens/hosts/events'; @@ -30,7 +29,6 @@ import { addsHostGeoCityNameToHeader, addsHostGeoCountryNameToHeader, openEventsViewerFieldsBrowser, - opensInspectQueryModal, waitsForEventsToBeLoaded, } from '../../tasks/hosts/events'; import { clearSearchBar, kqlSearch } from '../../tasks/security_header'; @@ -93,12 +91,6 @@ describe('Events Viewer', () => { visit(HOSTS_URL); openEvents(); }); - - it('launches the inspect query modal when the inspect button is clicked', () => { - waitsForEventsToBeLoaded(); - opensInspectQueryModal(); - cy.get(INSPECT_MODAL).should('exist'); - }); }); context('Events viewer fields behaviour', () => { diff --git a/x-pack/plugins/security_solution/cypress/e2e/hosts/inspect.cy.ts b/x-pack/plugins/security_solution/cypress/e2e/hosts/inspect.cy.ts deleted file mode 100644 index c5c8fb532b98a..0000000000000 --- a/x-pack/plugins/security_solution/cypress/e2e/hosts/inspect.cy.ts +++ /dev/null @@ -1,50 +0,0 @@ -/* - * 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 { INSPECT_HOSTS_BUTTONS_IN_SECURITY, INSPECT_MODAL } from '../../screens/inspect'; -import { HOST_OVERVIEW } from '../../screens/hosts/main'; - -import { clickInspectButton, closesModal, openStatsAndTables } from '../../tasks/inspect'; - -import { login, visit, visitHostDetailsPage } from '../../tasks/login'; - -import { HOSTS_URL } from '../../urls/navigation'; - -// This will be fixed in a follow up PR, https://github.com/elastic/kibana/issues/152359 -describe.skip('Inspect', () => { - before(() => { - login(); - }); - - context('Hosts stats and tables', () => { - beforeEach(() => { - visit(HOSTS_URL); - }); - - afterEach(() => { - closesModal(); - }); - - INSPECT_HOSTS_BUTTONS_IN_SECURITY.forEach((table) => - it(`inspects the ${table.title}`, () => { - openStatsAndTables(table); - cy.get(INSPECT_MODAL).should('be.visible'); - }) - ); - }); - - context('Hosts details', () => { - beforeEach(() => { - visitHostDetailsPage('test.local'); - }); - - it(`inspects the host details`, () => { - clickInspectButton(HOST_OVERVIEW); - cy.get(INSPECT_MODAL).should('be.visible'); - }); - }); -}); diff --git a/x-pack/plugins/security_solution/cypress/e2e/inspect/inspect_button.cy.ts b/x-pack/plugins/security_solution/cypress/e2e/inspect/inspect_button.cy.ts new file mode 100644 index 0000000000000..19073735c3883 --- /dev/null +++ b/x-pack/plugins/security_solution/cypress/e2e/inspect/inspect_button.cy.ts @@ -0,0 +1,80 @@ +/* + * 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 { + INSPECT_BUTTONS_IN_SECURITY, + INSPECT_MODAL, + INSPECT_MODAL_INDEX_PATTERN, +} from '../../screens/inspect'; +import { + closesModal, + openLensVisualizationsInspectModal, + openTab, + openTableInspectModal, +} from '../../tasks/inspect'; +import { login, visit } from '../../tasks/login'; +import { HOSTS_URL } from '../../urls/navigation'; +import { postDataView, waitForPageToBeLoaded } from '../../tasks/common'; +import { esArchiverLoad, esArchiverUnload } from '../../tasks/es_archiver'; +import { selectDataView } from '../../tasks/sourcerer'; + +const DATA_VIEW = 'auditbeat-*'; + +describe('Inspect Explore pages', () => { + before(() => { + esArchiverLoad('risk_users'); + esArchiverLoad('risk_hosts'); + login(); + + // Create and select data view + postDataView(DATA_VIEW); + visit(HOSTS_URL); + selectDataView(DATA_VIEW); + }); + + after(() => { + esArchiverUnload('risk_users'); + esArchiverUnload('risk_hosts'); + }); + + INSPECT_BUTTONS_IN_SECURITY.forEach(({ pageName, url, lensVisualizations, tables }) => { + /** + * Group all tests of a page into one "it" call to improve speed + */ + it(`inspect ${pageName} page`, () => { + visit(url); + waitForPageToBeLoaded(); + + lensVisualizations.forEach((lens) => { + cy.log(`inspects the ${lens.title} visualization`); + openTab(lens.tab); + + openLensVisualizationsInspectModal(lens, () => { + cy.get(INSPECT_MODAL).should('be.visible'); + cy.get(INSPECT_MODAL_INDEX_PATTERN).should( + 'contain.text', + lens.customIndexPattern ? lens.customIndexPattern : DATA_VIEW + ); + }); + }); + + tables.forEach((table) => { + cy.log(`inspects the ${table.title}`); + openTab(table.tab); + + openTableInspectModal(table); + cy.get(INSPECT_MODAL).should('be.visible'); + cy.get(INSPECT_MODAL_INDEX_PATTERN).should( + 'contain.text', + table.customIndexPattern ? table.customIndexPattern : DATA_VIEW + ); + + closesModal(); + }); + }); + }); +}); diff --git a/x-pack/plugins/security_solution/cypress/e2e/network/inspect.cy.ts b/x-pack/plugins/security_solution/cypress/e2e/network/inspect.cy.ts deleted file mode 100644 index b9e9f8cc897d8..0000000000000 --- a/x-pack/plugins/security_solution/cypress/e2e/network/inspect.cy.ts +++ /dev/null @@ -1,37 +0,0 @@ -/* - * 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 { INSPECT_MODAL, INSPECT_NETWORK_BUTTONS_IN_SECURITY } from '../../screens/inspect'; - -import { closesModal, openStatsAndTables } from '../../tasks/inspect'; -import { login, visit } from '../../tasks/login'; - -import { NETWORK_URL } from '../../urls/navigation'; - -// This will be fixed in a follow up PR, https://github.com/elastic/kibana/issues/152359 -describe.skip('Inspect', () => { - context('Network stats and tables', () => { - before(() => { - login(); - }); - - beforeEach(() => { - visit(NETWORK_URL); - }); - - afterEach(() => { - closesModal(); - }); - - INSPECT_NETWORK_BUTTONS_IN_SECURITY.forEach((table) => - it(`inspects the ${table.title}`, () => { - openStatsAndTables(table); - cy.get(INSPECT_MODAL).should('be.visible'); - }) - ); - }); -}); diff --git a/x-pack/plugins/security_solution/cypress/e2e/timelines/bulk_add_to_timeline.cy.ts b/x-pack/plugins/security_solution/cypress/e2e/timelines/bulk_add_to_timeline.cy.ts index 22a156b57737e..ef5303e8b3f4c 100644 --- a/x-pack/plugins/security_solution/cypress/e2e/timelines/bulk_add_to_timeline.cy.ts +++ b/x-pack/plugins/security_solution/cypress/e2e/timelines/bulk_add_to_timeline.cy.ts @@ -9,7 +9,7 @@ import { getNewRule } from '../../objects/rule'; import { SELECTED_ALERTS } from '../../screens/alerts'; import { SERVER_SIDE_EVENT_COUNT } from '../../screens/timeline'; import { selectAllAlerts, selectFirstPageAlerts } from '../../tasks/alerts'; -import { createCustomRuleEnabled } from '../../tasks/api_calls/rules'; +import { createRule } from '../../tasks/api_calls/rules'; import { cleanKibana } from '../../tasks/common'; import { bulkInvestigateSelectedEventsInTimeline, @@ -36,7 +36,7 @@ describe('Bulk Investigate in Timeline', () => { context('Alerts', () => { before(() => { - createCustomRuleEnabled(getNewRule()); + createRule(getNewRule()); }); beforeEach(() => { diff --git a/x-pack/plugins/security_solution/cypress/e2e/urls/state.cy.ts b/x-pack/plugins/security_solution/cypress/e2e/urls/state.cy.ts index 0cbd85ffcba43..e8dabce2ff661 100644 --- a/x-pack/plugins/security_solution/cypress/e2e/urls/state.cy.ts +++ b/x-pack/plugins/security_solution/cypress/e2e/urls/state.cy.ts @@ -287,9 +287,8 @@ describe('url state', () => { cy.intercept('PATCH', '/api/timeline').as('timeline'); - addNameToTimeline(getTimeline().title); - cy.wait('@timeline').then(({ response }) => { + addNameToTimeline(getTimeline().title); closeTimeline(); cy.wrap(response?.statusCode).should('eql', 200); const timelineId = response?.body.data.persistTimeline.timeline.savedObjectId; diff --git a/x-pack/plugins/security_solution/cypress/e2e/users/inspect.cy.ts b/x-pack/plugins/security_solution/cypress/e2e/users/inspect.cy.ts deleted file mode 100644 index 9eb36415fff82..0000000000000 --- a/x-pack/plugins/security_solution/cypress/e2e/users/inspect.cy.ts +++ /dev/null @@ -1,40 +0,0 @@ -/* - * 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 { AUTHENTICATIONS_TABLE } from '../../screens/hosts/authentications'; -import { INSPECT_MODAL } from '../../screens/inspect'; -import { ALL_USERS_TABLE } from '../../screens/users/all_users'; -import { AUTHENTICATIONS_TAB } from '../../screens/users/user_authentications'; - -import { clickInspectButton } from '../../tasks/inspect'; -import { login, visit } from '../../tasks/login'; - -import { USERS_URL } from '../../urls/navigation'; - -// This will be fixed in a follow up PR, https://github.com/elastic/kibana/issues/152359 -describe.skip('Inspect', () => { - context('Users stats and tables', () => { - before(() => { - login(); - }); - - beforeEach(() => { - visit(USERS_URL); - }); - - it(`inspects all users table`, () => { - clickInspectButton(ALL_USERS_TABLE); - cy.get(INSPECT_MODAL).should('be.visible'); - }); - - it(`inspects authentications table`, () => { - cy.get(AUTHENTICATIONS_TAB).click(); - clickInspectButton(AUTHENTICATIONS_TABLE); - cy.get(INSPECT_MODAL).should('be.visible'); - }); - }); -}); diff --git a/x-pack/plugins/security_solution/cypress/e2e/users/user_details.cy.ts b/x-pack/plugins/security_solution/cypress/e2e/users/user_details.cy.ts index 83eae1d259b2c..3233854f225ad 100644 --- a/x-pack/plugins/security_solution/cypress/e2e/users/user_details.cy.ts +++ b/x-pack/plugins/security_solution/cypress/e2e/users/user_details.cy.ts @@ -6,7 +6,7 @@ */ import { ALERT_FLYOUT } from '../../screens/alerts_details'; -import { createCustomRuleEnabled } from '../../tasks/api_calls/rules'; +import { createRule } from '../../tasks/api_calls/rules'; import { cleanKibana } from '../../tasks/common'; import { waitForAlertsToPopulate } from '../../tasks/create_new_rule'; import { login, visitWithoutDateRange } from '../../tasks/login'; @@ -28,7 +28,7 @@ describe('user details flyout', () => { it('shows user detail flyout from alert table', () => { visitWithoutDateRange(ALERTS_URL); - createCustomRuleEnabled({ ...getNewRule(), customQuery: 'user.name:*' }); + createRule({ ...getNewRule(), query: 'user.name:*' }); refreshPage(); waitForAlertsToPopulate(); diff --git a/x-pack/plugins/security_solution/cypress/helpers/rules.ts b/x-pack/plugins/security_solution/cypress/helpers/rules.ts index 5806654e71d88..b8ad823d669cf 100644 --- a/x-pack/plugins/security_solution/cypress/helpers/rules.ts +++ b/x-pack/plugins/security_solution/cypress/helpers/rules.ts @@ -5,18 +5,30 @@ * 2.0. */ -import type { Mitre } from '../objects/rule'; +import dateMath from '@kbn/datemath'; +import moment from 'moment'; -export const formatMitreAttackDescription = (mitre: Mitre[]) => { +import type { ThreatArray } from '../../common/detection_engine/rule_schema'; + +export const formatMitreAttackDescription = (mitre: ThreatArray) => { return mitre .map( (threat) => - threat.tactic + - threat.techniques - .map((technique) => { - return technique.name + technique.subtechniques.join(''); - }) - .join('') + `${threat.tactic.name} (${threat.tactic.id})${ + threat.technique + ? threat.technique + .map((technique) => { + return `${technique.name} (${technique.id})${ + technique.subtechnique + ? technique.subtechnique + .map((subtechnique) => `${subtechnique.name} (${subtechnique.id})`) + .join('') + : '' + }`; + }) + .join('') + : '' + }` ) .join(''); }; @@ -35,3 +47,33 @@ export const elementsOverlap = ($element1: JQuery, $element2: JQuer return true; } }; + +export const getHumanizedDuration = (from: string, interval: string): string => { + const fromValue = dateMath.parse(from) ?? moment(); + const intervalValue = dateMath.parse(`now-${interval}`) ?? moment(); + + const fromDuration = moment.duration(intervalValue.diff(fromValue)); + + // Basing calculations off floored seconds count as moment durations weren't precise + const intervalDuration = Math.floor(fromDuration.asSeconds()); + // For consistency of display value + if (intervalDuration === 0) { + return `0s`; + } + + if (intervalDuration % 3600 === 0) { + return `${intervalDuration / 3600}h`; + } else if (intervalDuration % 60 === 0) { + return `${intervalDuration / 60}m`; + } else { + return `${intervalDuration}s`; + } +}; + +export const convertHistoryStartToSize = (relativeTime: string) => { + if (relativeTime.startsWith('now-')) { + return relativeTime.substring(4); + } else { + return relativeTime; + } +}; diff --git a/x-pack/plugins/security_solution/cypress/objects/rule.ts b/x-pack/plugins/security_solution/cypress/objects/rule.ts index 58ab3f76fa6db..5506fa091f478 100644 --- a/x-pack/plugins/security_solution/cypress/objects/rule.ts +++ b/x-pack/plugins/security_solution/cypress/objects/rule.ts @@ -5,120 +5,30 @@ * 2.0. */ -import type { RuleActionThrottle } from '@kbn/securitysolution-io-ts-alerting-types'; +import type { + RuleActionThrottle, + SeverityMappingItem, + Threat, +} from '@kbn/securitysolution-io-ts-alerting-types'; import { getMockThreatData } from '../../public/detections/mitre/mitre_tactics_techniques'; -import type { CompleteTimeline } from './timeline'; -import { getTimeline, getIndicatorMatchTimelineTemplate } from './timeline'; -import type { RuleResponse } from '../../common/detection_engine/rule_schema'; +import type { + EqlRuleCreateProps, + MachineLearningRuleCreateProps, + NewTermsRuleCreateProps, + QueryRuleCreateProps, + RuleResponse, + ThreatMatchRuleCreateProps, + ThresholdRuleCreateProps, +} from '../../common/detection_engine/rule_schema'; import type { Connectors } from './connector'; const ccsRemoteName: string = Cypress.env('CCS_REMOTE_NAME'); -interface MitreAttackTechnique { - name: string; - subtechniques: string[]; -} - -export interface Mitre { - tactic: string; - techniques: MitreAttackTechnique[]; -} - -interface SeverityOverride { - sourceField: string; - sourceValue: string; -} - -interface Interval { - interval: string; - timeType: string; - type: string; -} - export interface Actions { throttle: RuleActionThrottle; connectors: Connectors[]; } -export type RuleDataSource = - | { type: 'indexPatterns'; index: string[] } - | { type: 'dataView'; dataView: string }; - -export interface CustomRule { - customQuery?: string; - name: string; - description: string; - dataSource: RuleDataSource; - severity?: string; - riskScore?: string; - tags?: string[]; - timelineTemplate?: string; - referenceUrls?: string[]; - falsePositivesExamples?: string[]; - mitre?: Mitre[]; - note?: string; - runsEvery?: Interval; - interval?: string; - lookBack?: Interval; - timeline?: CompleteTimeline; - maxSignals?: number; - buildingBlockType?: string; - exceptionLists?: Array<{ id: string; list_id: string; type: string; namespace_type: string }>; - actions?: Actions; - enabled?: boolean; -} - -export interface ThresholdRule extends CustomRule { - thresholdField: string; - threshold: string; -} - -export interface SavedQueryRule extends CustomRule { - savedId: string; -} - -export interface OverrideRule extends CustomRule { - severityOverride: SeverityOverride[]; - riskOverride: string; - nameOverride: string; - timestampOverride: string; -} - -export interface ThreatIndicatorRule extends CustomRule { - indicatorIndexPattern: string[]; - indicatorMappingField: string; - indicatorIndexField: string; - threatIndicatorPath: string; - type?: string; - atomic?: string; - matchedType?: string; - matchedId?: string; - matchedIndex?: string; -} - -export interface NewTermsRule extends CustomRule { - newTermsFields: string[]; - historyWindowSize: Interval; -} - -export interface MachineLearningRule { - machineLearningJobs: string[]; - anomalyScoreThreshold: number; - name: string; - description: string; - severity: string; - riskScore: string; - tags: string[]; - timelineTemplate?: string; - referenceUrls: string[]; - falsePositivesExamples: string[]; - mitre: Mitre[]; - note: string; - runsEvery: Interval; - lookBack: Interval; - interval?: string; -} - export const getIndexPatterns = (): string[] => [ 'apm-*-transaction*', 'auditbeat-*', @@ -133,368 +43,408 @@ export const getIndexPatterns = (): string[] => [ export const getThreatIndexPatterns = (): string[] => ['logs-ti_*']; -const getMitre1 = (): Mitre => ({ - tactic: `${getMockThreatData().tactic.name} (${getMockThreatData().tactic.id})`, - techniques: [ +const getMitre1 = (): Threat => ({ + framework: 'MITRE ATT&CK', + tactic: { + name: getMockThreatData().tactic.name, + id: getMockThreatData().tactic.id, + reference: getMockThreatData().tactic.reference, + }, + technique: [ { - name: `${getMockThreatData().technique.name} (${getMockThreatData().technique.id})`, - subtechniques: [ - `${getMockThreatData().subtechnique.name} (${getMockThreatData().subtechnique.id})`, + id: getMockThreatData().technique.id, + reference: getMockThreatData().technique.reference, + name: getMockThreatData().technique.name, + subtechnique: [ + { + id: getMockThreatData().subtechnique.id, + name: getMockThreatData().subtechnique.name, + reference: getMockThreatData().subtechnique.reference, + }, ], }, { - name: `${getMockThreatData().technique.name} (${getMockThreatData().technique.id})`, - subtechniques: [], + name: getMockThreatData().technique.name, + id: getMockThreatData().technique.id, + reference: getMockThreatData().technique.reference, + subtechnique: [], }, ], }); -const getMitre2 = (): Mitre => ({ - tactic: `${getMockThreatData().tactic.name} (${getMockThreatData().tactic.id})`, - techniques: [ +const getMitre2 = (): Threat => ({ + framework: 'MITRE ATT&CK', + tactic: { + name: getMockThreatData().tactic.name, + id: getMockThreatData().tactic.id, + reference: getMockThreatData().tactic.reference, + }, + technique: [ { - name: `${getMockThreatData().technique.name} (${getMockThreatData().technique.id})`, - subtechniques: [ - `${getMockThreatData().subtechnique.name} (${getMockThreatData().subtechnique.id})`, + id: getMockThreatData().technique.id, + reference: getMockThreatData().technique.reference, + name: getMockThreatData().technique.name, + subtechnique: [ + { + id: getMockThreatData().subtechnique.id, + name: getMockThreatData().subtechnique.name, + reference: getMockThreatData().subtechnique.reference, + }, ], }, ], }); -const getSeverityOverride1 = (): SeverityOverride => ({ - sourceField: 'host.name', - sourceValue: 'host', +const getSeverityOverride1 = (): SeverityMappingItem => ({ + field: 'host.name', + value: 'host', + operator: 'equals', + severity: 'low', }); -const getSeverityOverride2 = (): SeverityOverride => ({ - sourceField: '@timestamp', - sourceValue: '10/02/2020', +const getSeverityOverride2 = (): SeverityMappingItem => ({ + field: '@timestamp', + value: '10/02/2020', + operator: 'equals', + severity: 'medium', }); -const getSeverityOverride3 = (): SeverityOverride => ({ - sourceField: 'host.geo.name', - sourceValue: 'atack', +const getSeverityOverride3 = (): SeverityMappingItem => ({ + field: 'host.geo.name', + value: 'atack', + operator: 'equals', + severity: 'high', }); -const getSeverityOverride4 = (): SeverityOverride => ({ - sourceField: 'agent.type', - sourceValue: 'auditbeat', +const getSeverityOverride4 = (): SeverityMappingItem => ({ + field: 'agent.type', + value: 'auditbeat', + operator: 'equals', + severity: 'critical', }); -const getRunsEvery = (): Interval => ({ - interval: '100', - timeType: 'Minutes', - type: 'm', -}); - -const getLookBack = (): Interval => ({ - interval: '50000', - timeType: 'Hours', - type: 'h', -}); - -export const getDataViewRule = (): CustomRule => ({ - customQuery: 'host.name: *', - dataSource: { dataView: 'auditbeat-2022', type: 'dataView' }, +export const getDataViewRule = (): QueryRuleCreateProps => ({ + type: 'query', + query: 'host.name: *', + data_view_id: 'auditbeat-2022', name: 'New Data View Rule', description: 'The new rule description.', - severity: 'High', - riskScore: '17', + severity: 'high', + risk_score: 17, tags: ['test', 'newRule'], - referenceUrls: ['http://example.com/', 'https://example.com/'], - falsePositivesExamples: ['False1', 'False2'], - mitre: [getMitre1(), getMitre2()], + references: ['http://example.com/', 'https://example.com/'], + false_positives: ['False1', 'False2'], + threat: [getMitre1(), getMitre2()], note: '# test markdown', - runsEvery: getRunsEvery(), - lookBack: getLookBack(), - timeline: getTimeline(), - maxSignals: 100, + interval: '100m', + from: 'now-50000h', + max_signals: 100, }); -export const getNewRule = (): CustomRule => ({ - customQuery: 'host.name: *', - dataSource: { index: getIndexPatterns(), type: 'indexPatterns' }, +export const getNewRule = (): QueryRuleCreateProps => ({ + type: 'query', + query: 'host.name: *', + index: getIndexPatterns(), name: 'New Rule Test', description: 'The new rule description.', - severity: 'High', - riskScore: '17', + severity: 'high', + risk_score: 17, tags: ['test', 'newRule'], - referenceUrls: ['http://example.com/', 'https://example.com/'], - falsePositivesExamples: ['False1', 'False2'], - mitre: [getMitre1(), getMitre2()], + references: ['http://example.com/', 'https://example.com/'], + false_positives: ['False1', 'False2'], + threat: [getMitre1(), getMitre2()], note: '# test markdown', - runsEvery: getRunsEvery(), - lookBack: getLookBack(), - timeline: getTimeline(), - maxSignals: 100, + interval: '100m', + from: 'now-50000h', + max_signals: 100, }); -export const getSimpleCustomQueryRule = (): CustomRule => ({ - customQuery: 'host.name: *', - dataSource: { index: getIndexPatterns(), type: 'indexPatterns' }, +export const getSimpleCustomQueryRule = (): QueryRuleCreateProps => ({ + type: 'query', + query: 'host.name: *', + index: getIndexPatterns(), name: 'New Rule Test', description: 'The new rule description.', - runsEvery: getRunsEvery(), - lookBack: getLookBack(), + interval: '100m', + from: 'now-50000h', + severity: 'low', + risk_score: 21, }); -export const getBuildingBlockRule = (): CustomRule => ({ - customQuery: 'host.name: *', - dataSource: { index: getIndexPatterns(), type: 'indexPatterns' }, +export const getBuildingBlockRule = (): QueryRuleCreateProps => ({ + type: 'query', + query: 'host.name: *', + index: getIndexPatterns(), name: 'Building Block Rule Test', description: 'The new rule description.', - severity: 'High', - riskScore: '17', + severity: 'high', + risk_score: 17, tags: ['test', 'newRule'], - referenceUrls: ['http://example.com/', 'https://example.com/'], - falsePositivesExamples: ['False1', 'False2'], - mitre: [getMitre1(), getMitre2()], + references: ['http://example.com/', 'https://example.com/'], + false_positives: ['False1', 'False2'], + threat: [getMitre1(), getMitre2()], note: '# test markdown', - runsEvery: getRunsEvery(), - lookBack: getLookBack(), - timeline: getTimeline(), - maxSignals: 100, - buildingBlockType: 'default', + interval: '100m', + from: 'now-50000h', + max_signals: 100, + building_block_type: 'default', }); -export const getUnmappedRule = (): CustomRule => ({ - customQuery: '*:*', - dataSource: { index: ['unmapped*'], type: 'indexPatterns' }, +export const getUnmappedRule = (): QueryRuleCreateProps => ({ + type: 'query', + query: '*:*', + index: ['unmapped*'], name: 'Rule with unmapped fields', description: 'The new rule description.', - severity: 'High', - riskScore: '17', + severity: 'high', + risk_score: 17, tags: ['test', 'newRule'], - referenceUrls: ['http://example.com/', 'https://example.com/'], - falsePositivesExamples: ['False1', 'False2'], - mitre: [getMitre1(), getMitre2()], + references: ['http://example.com/', 'https://example.com/'], + false_positives: ['False1', 'False2'], + threat: [getMitre1(), getMitre2()], note: '# test markdown', - runsEvery: getRunsEvery(), - lookBack: getLookBack(), - timeline: getTimeline(), - maxSignals: 100, + interval: '100m', + from: 'now-50000h', + max_signals: 100, }); -export const getUnmappedCCSRule = (): CustomRule => ({ - customQuery: '*:*', - dataSource: { index: [`${ccsRemoteName}:unmapped*`], type: 'indexPatterns' }, +export const getUnmappedCCSRule = (): QueryRuleCreateProps => ({ + type: 'query', + query: '*:*', + index: [`${ccsRemoteName}:unmapped*`], name: 'Rule with unmapped fields', description: 'The new rule description.', - severity: 'High', - riskScore: '17', + severity: 'high', + risk_score: 17, tags: ['test', 'newRule'], - referenceUrls: ['http://example.com/', 'https://example.com/'], - falsePositivesExamples: ['False1', 'False2'], - mitre: [getMitre1(), getMitre2()], + references: ['http://example.com/', 'https://example.com/'], + false_positives: ['False1', 'False2'], + threat: [getMitre1(), getMitre2()], note: '# test markdown', - runsEvery: getRunsEvery(), - lookBack: getLookBack(), - timeline: getTimeline(), - maxSignals: 100, + interval: '100m', + from: 'now-50000h', + max_signals: 100, }); -export const getExistingRule = (): CustomRule => ({ - customQuery: 'host.name: *', +export const getExistingRule = (): QueryRuleCreateProps => ({ + type: 'query', + query: 'host.name: *', name: 'Rule 1', description: 'Description for Rule 1', - dataSource: { index: ['auditbeat-*'], type: 'indexPatterns' }, - severity: 'High', - riskScore: '19', + index: ['auditbeat-*'], + severity: 'high', + risk_score: 19, tags: ['rule1'], - referenceUrls: [], - falsePositivesExamples: [], - mitre: [], + references: [], + false_positives: [], + threat: [], note: 'This is my note', - runsEvery: getRunsEvery(), interval: '100m', - lookBack: getLookBack(), - timeline: getTimeline(), + from: 'now-50000h', // Please do not change, or if you do, needs // to be any number other than default value - maxSignals: 500, + max_signals: 500, }); -export const getNewOverrideRule = (): OverrideRule => ({ - customQuery: 'host.name: *', - dataSource: { index: getIndexPatterns(), type: 'indexPatterns' }, +export const getNewOverrideRule = (): QueryRuleCreateProps => ({ + type: 'query', + query: 'host.name: *', + index: getIndexPatterns(), name: 'Override Rule', description: 'The new rule description.', - severity: 'High', - riskScore: '17', + severity: 'high', + risk_score: 17, tags: ['test', 'newRule'], - referenceUrls: ['http://example.com/', 'https://example.com/'], - falsePositivesExamples: ['False1', 'False2'], - mitre: [getMitre1(), getMitre2()], + references: ['http://example.com/', 'https://example.com/'], + false_positives: ['False1', 'False2'], + threat: [getMitre1(), getMitre2()], note: '# test markdown', - severityOverride: [ + severity_mapping: [ getSeverityOverride1(), getSeverityOverride2(), getSeverityOverride3(), getSeverityOverride4(), ], - riskOverride: 'destination.port', - nameOverride: 'agent.type', - timestampOverride: '@timestamp', - runsEvery: getRunsEvery(), - lookBack: getLookBack(), - timeline: getTimeline(), - maxSignals: 100, + risk_score_mapping: [ + { field: 'destination.port', value: '', operator: 'equals', risk_score: undefined }, + ], + rule_name_override: 'agent.type', + timestamp_override: '@timestamp', + interval: '100m', + from: 'now-50000h', + max_signals: 100, }); -export const getNewThresholdRule = (): ThresholdRule => ({ - customQuery: 'host.name: *', - dataSource: { index: getIndexPatterns(), type: 'indexPatterns' }, +export const getNewThresholdRule = (): ThresholdRuleCreateProps => ({ + type: 'threshold', + query: 'host.name: *', + index: getIndexPatterns(), name: 'Threshold Rule', description: 'The new rule description.', - severity: 'High', - riskScore: '17', + severity: 'high', + risk_score: 17, tags: ['test', 'newRule'], - referenceUrls: ['http://example.com/', 'https://example.com/'], - falsePositivesExamples: ['False1', 'False2'], - mitre: [getMitre1(), getMitre2()], + references: ['http://example.com/', 'https://example.com/'], + false_positives: ['False1', 'False2'], + threat: [getMitre1(), getMitre2()], note: '# test markdown', - thresholdField: 'host.name', - threshold: '1', - runsEvery: getRunsEvery(), - lookBack: getLookBack(), - timeline: getTimeline(), - maxSignals: 100, + threshold: { + field: 'host.name', + value: 1, + }, + interval: '100m', + from: 'now-50000h', + max_signals: 100, }); -export const getNewTermsRule = (): NewTermsRule => ({ - customQuery: 'host.name: *', - dataSource: { index: getIndexPatterns(), type: 'indexPatterns' }, +export const getNewTermsRule = (): NewTermsRuleCreateProps => ({ + type: 'new_terms', + query: 'host.name: *', + index: getIndexPatterns(), name: 'New Terms Rule', description: 'The new rule description.', - severity: 'High', - riskScore: '17', + severity: 'high', + risk_score: 17, tags: ['test', 'newRule'], - referenceUrls: ['http://example.com/', 'https://example.com/'], - falsePositivesExamples: ['False1', 'False2'], - mitre: [getMitre1(), getMitre2()], + references: ['http://example.com/', 'https://example.com/'], + false_positives: ['False1', 'False2'], + threat: [getMitre1(), getMitre2()], note: '# test markdown', - newTermsFields: ['host.name'], - historyWindowSize: { - // historyWindowSize needs to be larger than the rule's lookback value - interval: '51000', - timeType: 'Hours', - type: 'h', - }, - runsEvery: getRunsEvery(), - lookBack: getLookBack(), - timeline: getTimeline(), - maxSignals: 100, + new_terms_fields: ['host.name'], + history_window_start: 'now-51000h', + interval: '100m', + from: 'now-50000h', + max_signals: 100, }); -export const getMachineLearningRule = (): MachineLearningRule => ({ - machineLearningJobs: [ +export const getMachineLearningRule = (): MachineLearningRuleCreateProps => ({ + type: 'machine_learning', + machine_learning_job_id: [ 'Unusual Linux Network Activity', 'Anomalous Process for a Linux Population', ], - anomalyScoreThreshold: 20, + anomaly_threshold: 20, name: 'New ML Rule Test', description: 'The new ML rule description.', - severity: 'Critical', - riskScore: '70', + severity: 'critical', + risk_score: 70, tags: ['ML'], - referenceUrls: ['https://elastic.co/'], - falsePositivesExamples: ['False1'], - mitre: [getMitre1()], + references: ['https://elastic.co/'], + false_positives: ['False1'], + threat: [getMitre1()], note: '# test markdown', - runsEvery: getRunsEvery(), - lookBack: getLookBack(), + interval: '100m', + from: 'now-50000h', }); -export const getEqlRule = (): CustomRule => ({ - customQuery: 'any where process.name == "zsh"', +export const getEqlRule = (): EqlRuleCreateProps => ({ + type: 'eql', + language: 'eql', + query: 'any where process.name == "zsh"', name: 'New EQL Rule', - dataSource: { index: getIndexPatterns(), type: 'indexPatterns' }, + index: getIndexPatterns(), description: 'New EQL rule description.', - severity: 'High', - riskScore: '17', + severity: 'high', + risk_score: 17, tags: ['test', 'newRule'], - referenceUrls: ['http://example.com/', 'https://example.com/'], - falsePositivesExamples: ['False1', 'False2'], - mitre: [getMitre1(), getMitre2()], + references: ['http://example.com/', 'https://example.com/'], + false_positives: ['False1', 'False2'], + threat: [getMitre1(), getMitre2()], note: '# test markdown', - runsEvery: getRunsEvery(), - lookBack: getLookBack(), - timeline: getTimeline(), - maxSignals: 100, + interval: '100m', + from: 'now-50000h', + max_signals: 100, }); -export const getCCSEqlRule = (): CustomRule => ({ - customQuery: 'any where process.name == "run-parts"', +export const getCCSEqlRule = (): EqlRuleCreateProps => ({ + type: 'eql', + language: 'eql', + query: 'any where process.name == "run-parts"', name: 'New EQL Rule', - dataSource: { index: [`${ccsRemoteName}:run-parts`], type: 'indexPatterns' }, + index: [`${ccsRemoteName}:run-parts`], description: 'New EQL rule description.', - severity: 'High', - riskScore: '17', + severity: 'high', + risk_score: 17, tags: ['test', 'newRule'], - referenceUrls: ['http://example.com/', 'https://example.com/'], - falsePositivesExamples: ['False1', 'False2'], - mitre: [getMitre1(), getMitre2()], + references: ['http://example.com/', 'https://example.com/'], + false_positives: ['False1', 'False2'], + threat: [getMitre1(), getMitre2()], note: '# test markdown', - runsEvery: getRunsEvery(), - lookBack: getLookBack(), - timeline: getTimeline(), - maxSignals: 100, + interval: '100m', + from: 'now-50000h', + max_signals: 100, }); -export const getEqlSequenceRule = (): CustomRule => ({ - customQuery: +export const getEqlSequenceRule = (): EqlRuleCreateProps => ({ + type: 'eql', + language: 'eql', + query: 'sequence with maxspan=30s\ [any where agent.name == "test.local"]\ [any where host.name == "test.local"]', name: 'New EQL Sequence Rule', - dataSource: { index: getIndexPatterns(), type: 'indexPatterns' }, + index: getIndexPatterns(), description: 'New EQL rule description.', - severity: 'High', - riskScore: '17', + severity: 'high', + risk_score: 17, tags: ['test', 'newRule'], - referenceUrls: ['http://example.com/', 'https://example.com/'], - falsePositivesExamples: ['False1', 'False2'], - mitre: [getMitre1(), getMitre2()], + references: ['http://example.com/', 'https://example.com/'], + false_positives: ['False1', 'False2'], + threat: [getMitre1(), getMitre2()], note: '# test markdown', - runsEvery: getRunsEvery(), - lookBack: getLookBack(), - timeline: getTimeline(), - maxSignals: 100, + interval: '100m', + from: 'now-50000h', + max_signals: 100, }); -export const getNewThreatIndicatorRule = (): ThreatIndicatorRule => ({ +export const getNewThreatIndicatorRule = (): ThreatMatchRuleCreateProps => ({ + type: 'threat_match', name: 'Threat Indicator Rule Test', description: 'The threat indicator rule description.', - dataSource: { index: ['suspicious-*'], type: 'indexPatterns' }, - severity: 'Critical', - riskScore: '20', + query: '*:*', + threat_query: '*:*', + index: ['suspicious-*'], + severity: 'critical', + risk_score: 20, tags: ['test', 'threat'], - referenceUrls: ['http://example.com/', 'https://example.com/'], - falsePositivesExamples: ['False1', 'False2'], - mitre: [getMitre1(), getMitre2()], + references: ['http://example.com/', 'https://example.com/'], + false_positives: ['False1', 'False2'], + threat: [getMitre1(), getMitre2()], note: '# test markdown', - runsEvery: getRunsEvery(), - lookBack: getLookBack(), - indicatorIndexPattern: ['filebeat-*'], - indicatorMappingField: 'myhash.mysha256', - indicatorIndexField: 'threat.indicator.file.hash.sha256', - type: 'file', + interval: '100m', + from: 'now-50000h', + threat_index: ['filebeat-*'], + threat_mapping: [ + { + entries: [ + { + field: 'myhash.mysha256', + value: 'threat.indicator.file.hash.sha256', + type: 'mapping', + }, + ], + }, + ], + max_signals: 100, + threat_indicator_path: 'threat.indicator', + timeline_title: 'Generic Threat Match Timeline', + timeline_id: '495ad7a7-316e-4544-8a0f-9c098daee76e', +}); + +export const indicatorRuleMatchingDoc = { atomic: 'a04ac6d98ad989312783d4fe3456c53730b212c79a426fb215708b6c6daa3de3', - timeline: getIndicatorMatchTimelineTemplate(), - maxSignals: 100, - threatIndicatorPath: 'threat.indicator', matchedType: 'indicator_match_rule', matchedId: '84cf452c1e0375c3d4412cb550bd1783358468a3b3b777da4829d72c7d6fb74f', matchedIndex: 'logs-ti_abusech.malware', -}); +}; export const duplicatedRuleName = `${getNewThreatIndicatorRule().name} [Duplicate]`; export const getSeveritiesOverride = (): string[] => ['Low', 'Medium', 'High', 'Critical']; -export const getEditedRule = (): CustomRule => ({ +export const getEditedRule = (): QueryRuleCreateProps => ({ ...getExistingRule(), - severity: 'Medium', + severity: 'medium', description: 'Edited Rule description', tags: [...(getExistingRule().tags || []), 'edited'], }); @@ -505,13 +455,30 @@ export const expectedExportedRule = (ruleResponse: Cypress.Response `[data-test-subj="optionsList-control-${idx}"]`; export const OPTION_LIST_NUMBER_OFF = '.euiFilterButton__notification'; export const OPTION_LISTS_LOADING = '.optionsList--filterBtnWrapper .euiLoadingSpinner'; +export const OPTION_LISTS_EXISTS = '[data-test-subj="optionsList-control-selection-exists"]'; + export const OPTION_LIST_ACTIVE_CLEAR_SELECTION = '[data-test-subj="optionsList-control-clear-all-selections"]'; @@ -38,3 +44,37 @@ export const DETECTION_PAGE_FILTER_GROUP_CONTEXT_MENU = '[data-test-subj="filter export const DETECTION_PAGE_FILTER_GROUP_RESET_BUTTON = '[data-test-subj="filter-group__context--reset"]'; + +export const FILTER_GROUP_CONTEXT_EDIT_CONTROLS = '[data-test-subj="filter_group__context--edit"]'; + +export const FILTER_GROUP_CONTEXT_SAVE_CONTROLS = '[data-test-subj="filter_group__context--save"]'; + +export const FILTER_GROUP_ADD_CONTROL = '[data-test-subj="filter-group__add-control"]'; + +export const FILTER_GROUP_SAVE_CHANGES = '[data-test-subj="filter-group__save"]'; + +export const FILTER_GROUP_DISCARD_CHANGES = '[data-test-subj="filter-group__discard"]'; + +export const FILTER_GROUP_SAVE_CHANGES_POPOVER = '[data-test-subj="filter-group__save-popover"]'; + +export const FILTER_GROUP_EDIT_CONTROLS_PANEL = '[data-test-subj="control-editor-flyout"]'; + +export const FILTER_GROUP_EDIT_CONTROL_PANEL_ITEMS = { + FIELD_SEARCH: '[data-test-subj="field-search-input"]', + FIELD_PICKER: (fieldName: string) => `[data-test-subj="field-picker-select-${fieldName}"]`, + FIELD_LABEL: '[data-test-subj="control-editor-title-input"]', + SAVE: '[data-test-subj="control-editor-save"]', +}; + +export const FILTER_GROUP_CONTROL_ACTION_DELETE = (idx: number) => { + return `[data-test-subj="control-action-${idx}-delete"]`; +}; + +export const FILTER_GROUP_CONTROL_ACTION_EDIT = (idx: number) => { + return `[data-test-subj="control-action-${idx}-edit"]`; +}; + +export const FILTER_GROUP_CONTROL_CONFIRM_DIALOG = `[data-test-subj="confirmModalTitleText"]`; +export const FILTER_GROUP_CONTROL_CONFIRM_BTN = `[data-test-subj="confirmModalConfirmButton"]`; + +export const FILTER_GROUP_CHANGED_BANNER = `[data-test-subj="filter-group--changed-banner"]`; diff --git a/x-pack/plugins/security_solution/cypress/screens/create_new_rule.ts b/x-pack/plugins/security_solution/cypress/screens/create_new_rule.ts index 61677bbd73b78..a0cccb508ed66 100644 --- a/x-pack/plugins/security_solution/cypress/screens/create_new_rule.ts +++ b/x-pack/plugins/security_solution/cypress/screens/create_new_rule.ts @@ -22,6 +22,12 @@ export const ADD_FALSE_POSITIVE_BTN = export const ADD_REFERENCE_URL_BTN = '[data-test-subj="detectionEngineStepAboutRuleReferenceUrls"] .euiButtonEmpty__text'; +export const ALERT_SUPPRESSION_FIELDS = + '[data-test-subj="alertSuppressionInput"] [data-test-subj="comboBoxInput"]'; + +export const ALERT_SUPPRESSION_DURATION_OPTIONS = + '[data-test-subj="alertSuppressionDuration"] [data-test-subj="groupByDurationOptions"]'; + export const ANOMALY_THRESHOLD_INPUT = '[data-test-subj="anomalyThresholdSlider"] .euiFieldNumber'; export const ADVANCED_SETTINGS_BTN = '[data-test-subj="advancedSettings"] .euiAccordion__button'; diff --git a/x-pack/plugins/security_solution/cypress/screens/exceptions.ts b/x-pack/plugins/security_solution/cypress/screens/exceptions.ts index 921a0a167f9a4..f179c49f7ece4 100644 --- a/x-pack/plugins/security_solution/cypress/screens/exceptions.ts +++ b/x-pack/plugins/security_solution/cypress/screens/exceptions.ts @@ -135,3 +135,24 @@ export const MANAGE_EXCEPTION_CREATE_BUTTON_EXCEPTION = '[data-test-subj="manageExceptionListCreateExceptionButton"]'; export const RULE_ACTION_LINK_RULE_SWITCH = '[data-test-subj="ruleActionLinkRuleSwitch"]'; + +// Exception list management +export const EXCEPTIONS_LIST_MANAGEMENT_NAME = + '[data-test-subj="exceptionListManagementTitleText"]'; + +export const EXCEPTIONS_LIST_MANAGEMENT_EDIT_NAME_BTN = + '[data-test-subj="exceptionListManagementTitleEditIcon"]'; + +export const EXCEPTIONS_LIST_MANAGEMENT_EDIT_MODAL_NAME_INPUT = + '[data-test-subj="editModalNameTextField"]'; + +export const EXCEPTIONS_LIST_MANAGEMENT_DESCRIPTION = + '[data-test-subj="exceptionListManagementDescriptionText"]'; + +export const EXCEPTIONS_LIST_MANAGEMENT_EDIT_MODAL_DESCRIPTION_INPUT = + '[data-test-subj="editModalDescriptionTextField"]'; + +export const EXCEPTIONS_LIST_EDIT_DETAILS_SAVE_BTN = '[data-test-subj="editModalSaveBtn"]'; + +export const EXCEPTIONS_LIST_DETAILS_HEADER = + '[data-test-subj="exceptionListManagementPageHeader"]'; diff --git a/x-pack/plugins/security_solution/cypress/screens/hosts/all_hosts.ts b/x-pack/plugins/security_solution/cypress/screens/hosts/all_hosts.ts index 38d26708cad0f..c76f315876fed 100644 --- a/x-pack/plugins/security_solution/cypress/screens/hosts/all_hosts.ts +++ b/x-pack/plugins/security_solution/cypress/screens/hosts/all_hosts.ts @@ -7,4 +7,8 @@ export const ALL_HOSTS_TABLE = '[data-test-subj="table-allHosts-loading-false"]'; +export const ALL_HOSTS_TAB = '[data-test-subj="navigation-allHosts"]'; + export const HOSTS_NAMES = '[data-test-subj="cellActions-renderContent-host.name"] a.euiLink'; + +export const UNIQUE_IPS_VISUALIZATIONS = '[data-test-subj="stat-uniqueIps"]'; diff --git a/x-pack/plugins/security_solution/cypress/screens/hosts/host_risk.ts b/x-pack/plugins/security_solution/cypress/screens/hosts/host_risk.ts index 7de7c6ccd9a37..fc62b4752dd44 100644 --- a/x-pack/plugins/security_solution/cypress/screens/hosts/host_risk.ts +++ b/x-pack/plugins/security_solution/cypress/screens/hosts/host_risk.ts @@ -12,6 +12,8 @@ export const LOADING_SPINNER = '[data-test-subj="loading-spinner"]'; export const HOST_BY_RISK_TABLE_CELL = '[data-test-subj="table-hostRisk-loading-false"] .euiTableCellContent'; +export const HOST_BY_RISK_TABLE = '[data-test-subj="table-hostRisk-loading-false"]'; + export const HOST_BY_RISK_TABLE_FILTER = '[data-test-subj="risk-filter-button"]'; export const HOST_BY_RISK_TABLE_FILTER_CRITICAL = '[data-test-subj="risk-filter-item-Critical"]'; diff --git a/x-pack/plugins/security_solution/cypress/screens/hosts/uncommon_processes.ts b/x-pack/plugins/security_solution/cypress/screens/hosts/uncommon_processes.ts index d06b435fd772c..f1bf25fbcb1f7 100644 --- a/x-pack/plugins/security_solution/cypress/screens/hosts/uncommon_processes.ts +++ b/x-pack/plugins/security_solution/cypress/screens/hosts/uncommon_processes.ts @@ -8,3 +8,5 @@ export const PROCESS_NAME_FIELD = '[data-test-subj="cellActions-renderContent-process.name"]'; export const UNCOMMON_PROCESSES_TABLE = '[data-test-subj="table-uncommonProcesses-loading-false"]'; + +export const HOSTS_VISUALIZATION = '[data-test-subj="stat-hosts"]'; diff --git a/x-pack/plugins/security_solution/cypress/screens/inspect.ts b/x-pack/plugins/security_solution/cypress/screens/inspect.ts index 3ee675ae7ca8c..df509b06b0819 100644 --- a/x-pack/plugins/security_solution/cypress/screens/inspect.ts +++ b/x-pack/plugins/security_solution/cypress/screens/inspect.ts @@ -5,47 +5,235 @@ * 2.0. */ +import { HOSTS_URL, NETWORK_URL, USERS_URL } from '../urls/navigation'; +import { EVENT_CONTAINER_TABLE_NOT_LOADING } from './alerts'; +import { ALL_HOSTS_TAB, ALL_HOSTS_TABLE, UNIQUE_IPS_VISUALIZATIONS } from './hosts/all_hosts'; +import { HOST_BY_RISK_TABLE, RISK_DETAILS_NAV } from './hosts/host_risk'; +import { UNCOMMON_PROCESSES_TAB } from './hosts/main'; +import { HOSTS_VISUALIZATION, UNCOMMON_PROCESSES_TABLE } from './hosts/uncommon_processes'; +import { + IPS_TABLE_LOADED, + NETWORK_DESTINATION_COUNTRIES_TABLE, + NETWORK_DESTINATION_IPS_TABLE, + NETWORK_DNS_VISUALIZATION, + NETWORK_EVENTS_VISUALIZATION, + NETWORK_FLOW_TAB, + NETWORK_SOURCE_COUNTRIES_TABLE, + NETWORK_TLS_HANDSHAKE_VISUALIZATION, + NETWORK_UNIQUE_FLOW_VISUALIZATION, +} from './network/flows'; + +import { DNS_TAB, DNS_TABLE } from './network/dns'; +import { + ALL_USERS_TAB, + ALL_USERS_TABLE, + AUTHENTICATION_VISUALIZATION, + USERS_VISUALIZATION, +} from './users/all_users'; +import { AUTHENTICATIONS_TAB, AUTHENTICATIONS_TABLE } from './users/user_authentications'; +import { EVENTS_HISTOGRAM, EVENTS_TAB } from './users/user_events'; +import { HTTP_TAB, HTTP_TABLE } from './network/http'; +import { TLS_TAB, TLS_TABLE } from './network/tls'; +import { RISK_SCORE_TAB, RISK_SCORE_TAB_CONTENT } from './users/user_risk_score'; + export const INSPECT_BUTTON_ICON = '[data-test-subj="inspect-icon-button"]'; export const INSPECT_MODAL = '[data-test-subj="modal-inspect-euiModal"]'; +export const INSPECT_MODAL_INDEX_PATTERN = '[data-test-subj="index-pattern-description"]'; +export const EMBEDDABLE_PANEL_TOGGLE_ICON = '[data-test-subj="embeddablePanelToggleMenuIcon"]'; +export const EMBEDDABLE_PANEL_INSPECT = '[data-test-subj="embeddablePanelAction-inspect"]'; -export interface InspectButtonMetadata { +export interface InspectTableMetadata { altInspectId?: string; id: string; title: string; - tabId?: string; + tab: string; + customIndexPattern?: string; } -export const INSPECT_HOSTS_BUTTONS_IN_SECURITY: InspectButtonMetadata[] = [ - { - id: '[data-test-subj="table-allHosts-loading-false"]', - title: 'All Hosts Table', - tabId: '[data-test-subj="navigation-allHosts"]', - }, - { - id: '[data-test-subj="table-uncommonProcesses-loading-false"]', - title: 'Uncommon processes Table', - tabId: '[data-test-subj="navigation-uncommonProcesses"]', - }, - { - altInspectId: `[data-test-subj="events-viewer-panel"] ${INSPECT_BUTTON_ICON}`, - id: '[data-test-subj="events-container-loading-false"]', - title: 'Events Table', - tabId: '[data-test-subj="navigation-events"]', - }, -]; +export interface InspectLensVisualizationsMetadata { + embeddableId: string; + title: string; + panelSelector: string; + customIndexPattern?: string; + tab: string; +} + +export interface InspectButtonMetadata { + pageName: string; + url: string; + tables: InspectTableMetadata[]; + lensVisualizations: InspectLensVisualizationsMetadata[]; +} -export const INSPECT_NETWORK_BUTTONS_IN_SECURITY: InspectButtonMetadata[] = [ +export const INSPECT_BUTTONS_IN_SECURITY: InspectButtonMetadata[] = [ { - id: '[data-test-subj="table-topNFlowSource-loading-false"]', - title: 'Source IPs Table', + pageName: 'Hosts', + url: HOSTS_URL, + tables: [ + { + title: 'All Hosts Table', + tab: ALL_HOSTS_TAB, + id: ALL_HOSTS_TABLE, + }, + { + title: 'Uncommon processes Table', + tab: UNCOMMON_PROCESSES_TAB, + id: UNCOMMON_PROCESSES_TABLE, + }, + { + title: 'Events Table', + tab: EVENTS_TAB, + altInspectId: `[data-test-subj="events-viewer-panel"] ${INSPECT_BUTTON_ICON}`, + id: EVENT_CONTAINER_TABLE_NOT_LOADING, + }, + + { + title: 'Host risk', + tab: RISK_DETAILS_NAV, + customIndexPattern: 'ml_host_risk_score_latest_default', + id: HOST_BY_RISK_TABLE, + }, + ], + lensVisualizations: [ + { + title: 'all hosts', + panelSelector: HOSTS_VISUALIZATION, + tab: ALL_HOSTS_TAB, + embeddableId: 'hostsKpiHostsQuery', + }, + { + title: 'Unique IPs', + panelSelector: UNIQUE_IPS_VISUALIZATIONS, + tab: ALL_HOSTS_TAB, + embeddableId: 'hostsKpiUniqueIpsQuery', + }, + ], }, { - id: '[data-test-subj="table-topNFlowDestination-loading-false"]', - title: 'Destination IPs Table', + pageName: 'Network', + url: NETWORK_URL, + lensVisualizations: [ + { + title: 'Network events', + panelSelector: NETWORK_EVENTS_VISUALIZATION, + tab: NETWORK_FLOW_TAB, + embeddableId: 'networkKpiNetworkEventsQuery', + }, + { + title: 'DNS queries', + panelSelector: NETWORK_DNS_VISUALIZATION, + tab: NETWORK_FLOW_TAB, + embeddableId: 'networkKpiDnsQuery', + }, + + { + title: 'Unique flow IDs', + panelSelector: NETWORK_UNIQUE_FLOW_VISUALIZATION, + tab: NETWORK_FLOW_TAB, + embeddableId: 'networkKpiUniqueFlowsQuery', + }, + { + title: 'TLS handshakes', + panelSelector: NETWORK_TLS_HANDSHAKE_VISUALIZATION, + tab: NETWORK_FLOW_TAB, + embeddableId: 'networkKpiTlsHandshakesQuery', + }, + { + title: 'Unique private IPs', + panelSelector: UNIQUE_IPS_VISUALIZATIONS, + tab: NETWORK_FLOW_TAB, + embeddableId: 'networkKpiUniquePrivateIpsQuery', + }, + ], + tables: [ + { + title: 'Source IPs', + tab: NETWORK_FLOW_TAB, + id: IPS_TABLE_LOADED, + }, + { + title: 'Destination IPs', + tab: NETWORK_FLOW_TAB, + id: NETWORK_DESTINATION_IPS_TABLE, + }, + { + title: 'Source countries', + tab: NETWORK_FLOW_TAB, + id: NETWORK_SOURCE_COUNTRIES_TABLE, + }, + { + title: 'Destination countries', + tab: NETWORK_FLOW_TAB, + id: NETWORK_DESTINATION_COUNTRIES_TABLE, + }, + { + title: 'Top DNS Domains', + tab: DNS_TAB, + id: DNS_TABLE, + }, + { + title: 'HTTP Requests', + tab: HTTP_TAB, + id: HTTP_TABLE, + }, + { + title: 'Transport Layer Security', + tab: TLS_TAB, + id: TLS_TABLE, + }, + { + title: 'Events Table', + tab: EVENTS_TAB, + id: EVENT_CONTAINER_TABLE_NOT_LOADING, + altInspectId: `[data-test-subj="events-viewer-panel"] ${INSPECT_BUTTON_ICON}`, + }, + ], }, { - id: '[data-test-subj="table-dns-loading-false"]', - title: 'Top DNS Domains Table', - tabId: '[data-test-subj="navigation-dns"]', + pageName: 'Users', + url: USERS_URL, + lensVisualizations: [ + { + title: 'Users', + panelSelector: USERS_VISUALIZATION, + embeddableId: 'TotalUsersKpiQuery', + tab: ALL_USERS_TAB, + }, + { + title: 'User authentications', + panelSelector: AUTHENTICATION_VISUALIZATION, + embeddableId: 'usersKpiAuthenticationsQuery', + tab: ALL_USERS_TAB, + }, + { + title: 'Events', + panelSelector: EVENTS_HISTOGRAM, + embeddableId: 'alertsOrEventsHistogramQuery-embeddable', + tab: EVENTS_TAB, + }, + ], + tables: [ + { + title: 'Users Table', + tab: ALL_USERS_TAB, + id: ALL_USERS_TABLE, + }, + { + title: 'Destination IPs Table', + tab: AUTHENTICATIONS_TAB, + id: AUTHENTICATIONS_TABLE, + }, + { + title: 'Destination IPs Table', + tab: EVENTS_TAB, + id: EVENT_CONTAINER_TABLE_NOT_LOADING, + }, + { + title: 'User risk', + tab: RISK_SCORE_TAB, + id: RISK_SCORE_TAB_CONTENT, + customIndexPattern: 'ml_user_risk_score_latest_default', + }, + ], }, ]; diff --git a/x-pack/plugins/observability/public/pages/overview/index.ts b/x-pack/plugins/security_solution/cypress/screens/network/dns.ts similarity index 65% rename from x-pack/plugins/observability/public/pages/overview/index.ts rename to x-pack/plugins/security_solution/cypress/screens/network/dns.ts index 525f3441c4470..ca5fc0410d166 100644 --- a/x-pack/plugins/observability/public/pages/overview/index.ts +++ b/x-pack/plugins/security_solution/cypress/screens/network/dns.ts @@ -5,5 +5,5 @@ * 2.0. */ -export * from './components'; -export * from './containers'; +export const DNS_TAB = '[data-test-subj="navigation-dns"]'; +export const DNS_TABLE = '[data-test-subj="table-dns-loading-false"]'; diff --git a/x-pack/plugins/security_solution/cypress/screens/network/flows.ts b/x-pack/plugins/security_solution/cypress/screens/network/flows.ts index e29d2a85a69a7..eb9b22aea0350 100644 --- a/x-pack/plugins/security_solution/cypress/screens/network/flows.ts +++ b/x-pack/plugins/security_solution/cypress/screens/network/flows.ts @@ -26,3 +26,22 @@ export const DESTINATION_DOMAIN = `[data-test-subj="more-container"] [data-test- export const OVERFLOW_ITEM = '[data-test-subj="more-container"] [data-test-subj="cellActions-renderContent-destination.domain"]'; + +export const NETWORK_FLOW_TAB = '[data-test-subj="navigation-flows"]'; + +export const NETWORK_EVENTS_VISUALIZATION = '[data-test-subj="stat-networkEvents"]'; + +export const NETWORK_DNS_VISUALIZATION = '[data-test-subj="stat-dnsQueries"]'; + +export const NETWORK_UNIQUE_FLOW_VISUALIZATION = '[data-test-subj="stat-uniqueFlowId"]'; + +export const NETWORK_TLS_HANDSHAKE_VISUALIZATION = '[data-test-subj="stat-tlsHandshakes"]'; + +export const NETWORK_DESTINATION_IPS_TABLE = + '[data-test-subj="table-topNFlowDestination-loading-false"]'; + +export const NETWORK_SOURCE_COUNTRIES_TABLE = + '[data-test-subj="table-topCountriesSource-loading-false"]'; + +export const NETWORK_DESTINATION_COUNTRIES_TABLE = + '[data-test-subj="table-topCountriesDestination-loading-false"]'; diff --git a/x-pack/plugins/security_solution/cypress/screens/network/http.ts b/x-pack/plugins/security_solution/cypress/screens/network/http.ts new file mode 100644 index 0000000000000..b6c17c0e62287 --- /dev/null +++ b/x-pack/plugins/security_solution/cypress/screens/network/http.ts @@ -0,0 +1,9 @@ +/* + * 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. + */ + +export const HTTP_TAB = '[data-test-subj="navigation-http"]'; +export const HTTP_TABLE = '[data-test-subj="table-http-loading-false"]'; diff --git a/x-pack/plugins/observability/public/pages/overview/containers/index.ts b/x-pack/plugins/security_solution/cypress/screens/network/tls.ts similarity index 65% rename from x-pack/plugins/observability/public/pages/overview/containers/index.ts rename to x-pack/plugins/security_solution/cypress/screens/network/tls.ts index d547b4dcf916e..c1f658569638c 100644 --- a/x-pack/plugins/observability/public/pages/overview/containers/index.ts +++ b/x-pack/plugins/security_solution/cypress/screens/network/tls.ts @@ -5,4 +5,5 @@ * 2.0. */ -export * from './overview_page'; +export const TLS_TAB = '[data-test-subj="navigation-tls"]'; +export const TLS_TABLE = '[data-test-subj="table-tls-loading-false"]'; diff --git a/x-pack/plugins/security_solution/cypress/screens/rule_details.ts b/x-pack/plugins/security_solution/cypress/screens/rule_details.ts index 3fe364a0b06c3..5487c5be0aded 100644 --- a/x-pack/plugins/security_solution/cypress/screens/rule_details.ts +++ b/x-pack/plugins/security_solution/cypress/screens/rule_details.ts @@ -112,6 +112,10 @@ export const TIMELINE_TEMPLATE_DETAILS = 'Timeline template'; export const TIMESTAMP_OVERRIDE_DETAILS = 'Timestamp override'; +export const SUPPRESS_BY_DETAILS = 'Suppress alerts by'; + +export const SUPPRESS_FOR_DETAILS = 'Suppress alerts for'; + export const TIMELINE_FIELD = (field: string) => { return `[data-test-subj="formatted-field-${field}"]`; }; diff --git a/x-pack/plugins/security_solution/cypress/screens/users/all_users.ts b/x-pack/plugins/security_solution/cypress/screens/users/all_users.ts index dd05054fea1fb..c9b44d2d6b972 100644 --- a/x-pack/plugins/security_solution/cypress/screens/users/all_users.ts +++ b/x-pack/plugins/security_solution/cypress/screens/users/all_users.ts @@ -8,3 +8,9 @@ export const ALL_USERS_TABLE = '[data-test-subj="table-allUsers-loading-false"]'; export const HEADER_SUBTITLE = '[data-test-subj="header-panel-subtitle"]'; + +export const ALL_USERS_TAB = '[data-test-subj="navigation-allUsers"]'; + +export const USERS_VISUALIZATION = '[data-test-subj="stat-users"]'; + +export const AUTHENTICATION_VISUALIZATION = '[data-test-subj="stat-authentication"]'; diff --git a/x-pack/plugins/security_solution/cypress/screens/users/user_events.ts b/x-pack/plugins/security_solution/cypress/screens/users/user_events.ts index c2bcd30f9d1c2..06b64c70dca41 100644 --- a/x-pack/plugins/security_solution/cypress/screens/users/user_events.ts +++ b/x-pack/plugins/security_solution/cypress/screens/users/user_events.ts @@ -7,3 +7,5 @@ export const EVENTS_TAB = '[data-test-subj="navigation-events"]'; export const EVENTS_TAB_CONTENT = '[data-test-subj="events-viewer-panel"]'; + +export const EVENTS_HISTOGRAM = '[data-test-subj="alertsOrEventsHistogramQueryPanel"]'; diff --git a/x-pack/plugins/security_solution/cypress/tasks/alerts.ts b/x-pack/plugins/security_solution/cypress/tasks/alerts.ts index 574e6bd4857c5..f44c10d010f73 100644 --- a/x-pack/plugins/security_solution/cypress/tasks/alerts.ts +++ b/x-pack/plugins/security_solution/cypress/tasks/alerts.ts @@ -5,6 +5,8 @@ * 2.0. */ +import { encode } from '@kbn/rison'; +import { formatPageFilterSearchParam } from '../../common/utils/format_page_filter_search_param'; import { TOP_N_CONTAINER } from '../screens/network/flows'; import { ADD_EXCEPTION_BTN, @@ -31,7 +33,6 @@ import { EVENT_CONTAINER_TABLE_LOADING, SELECT_ALL_ALERTS, SELECT_ALL_VISIBLE_ALERTS, - ACKNOWLEDGED_ALERTS_FILTER_BTN, CELL_ADD_TO_TIMELINE_BUTTON, CELL_FILTER_IN_BUTTON, CELL_SHOW_TOP_FIELD_BUTTON, @@ -64,14 +65,16 @@ import { DETECTION_PAGE_FILTER_GROUP_LOADING, DETECTION_PAGE_FILTER_GROUP_RESET_BUTTON, DETECTION_PAGE_FILTER_GROUP_WRAPPER, + OPTION_LISTS_EXISTS, OPTION_LISTS_LOADING, - OPTION_LIST_ACTIVE_CLEAR_SELECTION, OPTION_LIST_VALUES, OPTION_SELECTABLE, } from '../screens/common/filter_group'; import { LOADING_SPINNER } from '../screens/common/page'; import { ALERTS_URL } from '../urls/navigation'; import { FIELDS_BROWSER_BTN } from '../screens/rule_details'; +import type { FilterItemObj } from '../../public/common/components/filter_group/types'; +import { visit } from './login'; export const addExceptionFromFirstAlert = () => { expandFirstAlertActions(); @@ -191,22 +194,61 @@ export const refreshAlertPageFilter = () => { }; export const togglePageFilterPopover = (filterIndex: number) => { - cy.get(OPTION_LIST_VALUES).eq(filterIndex).click({ force: true }); + cy.get(OPTION_LIST_VALUES(filterIndex)).click({ force: true }); +}; + +export const openPageFilterPopover = (filterIndex: number) => { + cy.log(`Opening Page filter popover for index ${filterIndex}`); + cy.get(OPTION_LIST_VALUES(filterIndex)) + .pipe(($el) => $el.trigger('click')) + .should('have.class', 'euiFilterButton-isSelected'); + // cy.root().then(($el) => { + // const existsOption = $el.find(OPTION_LISTS_EXISTS); + // const optionsList = $el.find(OPTION_LIST_VALUES(filterIndex)); + // if (!existsOption || existsOption.length === 0) { + // optionsList.trigger('click'); + // } + // }); + // cy.get(OPTION_LISTS_EXISTS).should('be.visible'); +}; + +export const closePageFilterPopover = (filterIndex: number) => { + cy.log(`Closing Page filter popover for index ${filterIndex}`); + cy.get(OPTION_LIST_VALUES(filterIndex)) + .pipe(($el) => $el.trigger('click')) + .should('not.have.class', 'euiFilterButton-isSelected'); + // cy.root().then(($el) => { + // const existsOption = $el.find(OPTION_LISTS_EXISTS); + // if (existsOption.length > 0) { + // const optionsList = $el.find(OPTION_LIST_VALUES(filterIndex)); + // optionsList.trigger('click'); + // } + // }); + // cy.get(OPTION_LISTS_EXISTS).should('not.be.visible'); }; export const clearAllSelections = () => { - cy.get(OPTION_LIST_ACTIVE_CLEAR_SELECTION).click({ force: true }); + cy.get(OPTION_LISTS_EXISTS) + .click({ force: true }) + .then(($el) => { + if ($el.attr('aria-checked', 'false')) { + // check it + $el.trigger('click'); + } + // uncheck it + $el.trigger('click'); + }); }; export const selectPageFilterValue = (filterIndex: number, ...values: string[]) => { refreshAlertPageFilter(); - togglePageFilterPopover(filterIndex); + openPageFilterPopover(filterIndex); clearAllSelections(); values.forEach((value) => { cy.get(OPTION_SELECTABLE(filterIndex, value)).click({ force: true }); }); + closePageFilterPopover(filterIndex); waitForAlerts(); - togglePageFilterPopover(filterIndex); }; export const goToClosedAlertsOnRuleDetailsPage = () => { @@ -217,7 +259,7 @@ export const goToClosedAlertsOnRuleDetailsPage = () => { }; export const goToClosedAlerts = () => { - cy.get(CLOSED_ALERTS_FILTER_BTN).click(); + // cy.get(CLOSED_ALERTS_FILTER_BTN).click(); /* * below line commented because alertPageFiltersEnabled feature flag * is disabled by default @@ -226,6 +268,8 @@ export const goToClosedAlerts = () => { * selectPageFilterValue(0, 'closed'); * * */ + waitForPageFilters(); + selectPageFilterValue(0, 'closed'); cy.get(REFRESH_BUTTON).should('not.have.attr', 'aria-label', 'Needs updating'); cy.get(REFRESH_BUTTON).should('have.attr', 'aria-label', 'Refresh query'); cy.get(TIMELINE_COLUMN_SPINNER).should('not.exist'); @@ -242,7 +286,7 @@ export const goToOpenedAlertsOnRuleDetailsPage = () => { }; export const goToOpenedAlerts = () => { - cy.get(OPENED_ALERTS_FILTER_BTN).click({ force: true }); + // cy.get(OPENED_ALERTS_FILTER_BTN).click({ force: true }); /* * below line commented because alertPageFiltersEnabled feature flag * is disabled by default @@ -251,6 +295,7 @@ export const goToOpenedAlerts = () => { * selectPageFilterValue(0, 'open'); * */ + selectPageFilterValue(0, 'open'); cy.get(REFRESH_BUTTON).should('not.have.attr', 'aria-label', 'Needs updating'); cy.get(REFRESH_BUTTON).should('have.attr', 'aria-label', 'Refresh query'); }; @@ -287,7 +332,8 @@ export const goToAcknowledgedAlerts = () => { * selectPageFilterValue(0, 'acknowledged'); * */ - cy.get(ACKNOWLEDGED_ALERTS_FILTER_BTN).click(); + // cy.get(ACKNOWLEDGED_ALERTS_FILTER_BTN).click(); + selectPageFilterValue(0, 'acknowledged'); cy.get(REFRESH_BUTTON).should('not.have.attr', 'aria-label', 'Needs updating'); cy.get(REFRESH_BUTTON).should('have.attr', 'aria-label', 'Refresh query'); cy.get(TIMELINE_COLUMN_SPINNER).should('not.exist'); @@ -369,6 +415,7 @@ export const waitForAlerts = () => { * waitforpagefilters(); * * */ + waitForPageFilters(); cy.get(REFRESH_BUTTON).should('not.have.attr', 'aria-label', 'Needs updating'); cy.get(DATAGRID_CHANGES_IN_PROGRESS).should('not.be.true'); cy.get(EVENT_CONTAINER_TABLE_LOADING).should('not.exist'); @@ -412,14 +459,7 @@ export const waitForPageFilters = () => { export const resetFilters = () => { cy.get(DETECTION_PAGE_FILTER_GROUP_CONTEXT_MENU).click({ force: true }); cy.get(DETECTION_PAGE_FILTER_GROUP_RESET_BUTTON).click({ force: true }); - /* - * below line commented because alertpagefiltersenabled feature flag - * is disabled by default - * target: enable by default in v8.8 - * - * waitforpagefilters(); - * - * */ + waitForPageFilters(); }; export const parseAlertsCountToInt = (count: string | number) => @@ -450,3 +490,9 @@ export const selectAllAlerts = () => { selectFirstPageAlerts(); cy.get(SELECT_ALL_ALERTS).click(); }; + +export const visitAlertsPageWithCustomFilters = (pageFilters: FilterItemObj[]) => { + const pageFilterUrlVal = encode(formatPageFilterSearchParam(pageFilters)); + const newURL = `${ALERTS_URL}?pageFilters=${pageFilterUrlVal}`; + visit(newURL); +}; diff --git a/x-pack/plugins/security_solution/cypress/tasks/api_calls/rules.ts b/x-pack/plugins/security_solution/cypress/tasks/api_calls/rules.ts index aa0e28185be5f..1be6c76951b56 100644 --- a/x-pack/plugins/security_solution/cypress/tasks/api_calls/rules.ts +++ b/x-pack/plugins/security_solution/cypress/tasks/api_calls/rules.ts @@ -5,323 +5,19 @@ * 2.0. */ -import type { RuleActionArray } from '@kbn/securitysolution-io-ts-alerting-types'; - -import type { - CustomRule, - ThreatIndicatorRule, - MachineLearningRule, - ThresholdRule, - NewTermsRule, - SavedQueryRule, -} from '../../objects/rule'; - -export const createMachineLearningRule = (rule: MachineLearningRule, ruleId = 'ml_rule_testing') => - cy.request({ - method: 'POST', - url: 'api/detection_engine/rules', - body: { - rule_id: ruleId, - risk_score: parseInt(rule.riskScore, 10), - description: rule.description, - interval: rule.interval, - name: rule.name, - severity: rule.severity.toLocaleLowerCase(), - type: 'machine_learning', - from: 'now-50000h', - enabled: false, - machine_learning_job_id: rule.machineLearningJobs, - anomaly_threshold: rule.anomalyScoreThreshold, - tags: rule.tags, - }, - headers: { 'kbn-xsrf': 'cypress-creds' }, - failOnStatusCode: false, - }); - -export const createCustomRule = ( - rule: CustomRule, - ruleId = 'rule_testing' -): Cypress.Chainable> => { - const riskScore = rule.riskScore != null ? parseInt(rule.riskScore, 10) : undefined; - const severity = rule.severity != null ? rule.severity.toLocaleLowerCase() : undefined; - const timeline = rule.timeline != null ? rule.timeline : undefined; - const interval = rule.runsEvery ? `${rule.runsEvery.interval}${rule.runsEvery.type}` : '100m'; +import { DETECTION_ENGINE_RULES_URL } from '../../../common/constants'; +import type { RuleCreateProps } from '../../../common/detection_engine/rule_schema'; +export const createRule = (rule: RuleCreateProps) => { return cy.request({ method: 'POST', - url: 'api/detection_engine/rules', - body: { - rule_id: ruleId, - risk_score: riskScore, - description: rule.description, - interval, - name: rule.name, - severity, - type: 'query', - from: 'now-50000h', - index: rule.dataSource.type === 'indexPatterns' ? rule.dataSource.index : undefined, - data_view_id: rule.dataSource.type === 'dataView' ? rule.dataSource.dataView : undefined, - query: rule.customQuery, - language: 'kuery', - enabled: rule.enabled ?? false, - exceptions_list: rule.exceptionLists ?? [], - tags: rule.tags, - ...(timeline?.id ?? timeline?.templateTimelineId - ? { - timeline_id: timeline.id ?? timeline.templateTimelineId, - timeline_title: timeline.title, - } - : {}), - actions: rule.actions, - }, + url: DETECTION_ENGINE_RULES_URL, + body: rule, headers: { 'kbn-xsrf': 'cypress-creds' }, failOnStatusCode: false, }); }; -export const createEventCorrelationRule = (rule: CustomRule, ruleId = 'rule_testing') => { - const riskScore = rule.riskScore != null ? parseInt(rule.riskScore, 10) : undefined; - const severity = rule.severity != null ? rule.severity.toLowerCase() : undefined; - const interval = rule.runsEvery ? `${rule.runsEvery.interval}${rule.runsEvery.type}` : '100m'; - - cy.request({ - method: 'POST', - url: 'api/detection_engine/rules', - body: { - rule_id: ruleId, - risk_score: riskScore, - description: rule.description, - interval, - from: `now-${rule.lookBack?.interval}${rule.lookBack?.type}`, - name: rule.name, - severity, - type: 'eql', - index: rule.dataSource.type === 'indexPatterns' ? rule.dataSource.index : undefined, - data_view_id: rule.dataSource.type === 'dataView' ? rule.dataSource.dataView : undefined, - query: rule.customQuery, - language: 'eql', - enabled: true, - tags: rule.tags, - }, - headers: { 'kbn-xsrf': 'cypress-creds' }, - }); -}; - -export const createThresholdRule = (rule: ThresholdRule, ruleId = 'rule_testing') => { - const riskScore = rule.riskScore != null ? parseInt(rule.riskScore, 10) : undefined; - const severity = rule.severity != null ? rule.severity.toLocaleLowerCase() : undefined; - const interval = rule.runsEvery ? `${rule.runsEvery.interval}${rule.runsEvery.type}` : '100m'; - - cy.request({ - method: 'POST', - url: 'api/detection_engine/rules', - body: { - rule_id: ruleId, - risk_score: riskScore, - description: rule.description, - interval, - from: `now-${rule.lookBack?.interval}${rule.lookBack?.type}`, - name: rule.name, - severity, - type: 'threshold', - index: rule.dataSource.type === 'indexPatterns' ? rule.dataSource.index : undefined, - data_view_id: rule.dataSource.type === 'dataView' ? rule.dataSource.dataView : undefined, - query: rule.customQuery, - threshold: { - field: [rule.thresholdField], - value: parseInt(rule.threshold, 10), - cardinality: [], - }, - enabled: true, - tags: rule.tags, - }, - headers: { 'kbn-xsrf': 'cypress-creds' }, - }); -}; - -export const createNewTermsRule = (rule: NewTermsRule, ruleId = 'rule_testing') => { - const riskScore = rule.riskScore != null ? parseInt(rule.riskScore, 10) : undefined; - const severity = rule.severity != null ? rule.severity.toLocaleLowerCase() : undefined; - const interval = rule.runsEvery ? `${rule.runsEvery.interval}${rule.runsEvery.type}` : '100m'; - - cy.request({ - method: 'POST', - url: 'api/detection_engine/rules', - body: { - rule_id: ruleId, - risk_score: riskScore, - description: rule.description, - interval, - from: `now-${rule.lookBack?.interval}${rule.lookBack?.type}`, - name: rule.name, - severity, - type: 'new_terms', - index: rule.dataSource.type === 'indexPatterns' ? rule.dataSource.index : undefined, - data_view_id: rule.dataSource.type === 'dataView' ? rule.dataSource.dataView : undefined, - query: rule.customQuery, - new_terms_fields: rule.newTermsFields, - history_window_start: `now-${rule.historyWindowSize.interval}${rule.historyWindowSize.type}`, - enabled: true, - tags: rule.tags, - }, - headers: { 'kbn-xsrf': 'cypress-creds' }, - }); -}; - -export const createSavedQueryRule = ( - rule: SavedQueryRule, - ruleId = 'saved_query_rule_testing' -): Cypress.Chainable> => { - const riskScore = rule.riskScore != null ? parseInt(rule.riskScore, 10) : undefined; - const severity = rule.severity != null ? rule.severity.toLocaleLowerCase() : undefined; - const timeline = rule.timeline != null ? rule.timeline : undefined; - const interval = rule.runsEvery ? `${rule.runsEvery.interval}${rule.runsEvery.type}` : '100m'; - - return cy.request({ - method: 'POST', - url: 'api/detection_engine/rules', - body: { - rule_id: ruleId, - risk_score: riskScore, - description: rule.description, - interval, - name: rule.name, - severity, - type: 'saved_query', - from: 'now-50000h', - index: rule.dataSource.type === 'indexPatterns' ? rule.dataSource.index : undefined, - data_view_id: rule.dataSource.type === 'dataView' ? rule.dataSource.dataView : undefined, - saved_id: rule.savedId, - language: 'kuery', - enabled: false, - exceptions_list: rule.exceptionLists ?? [], - tags: rule.tags, - ...(timeline?.id ?? timeline?.templateTimelineId - ? { - timeline_id: timeline.id ?? timeline.templateTimelineId, - timeline_title: timeline.title, - } - : {}), - }, - headers: { 'kbn-xsrf': 'cypress-creds' }, - failOnStatusCode: false, - }); -}; - -export const createCustomIndicatorRule = (rule: ThreatIndicatorRule, ruleId = 'rule_testing') => { - const riskScore = rule.riskScore != null ? parseInt(rule.riskScore, 10) : undefined; - const severity = rule.severity != null ? rule.severity.toLocaleLowerCase() : undefined; - const timeline = rule.timeline != null ? rule.timeline : undefined; - const interval = rule.runsEvery ? `${rule.runsEvery.interval}${rule.runsEvery.type}` : '100m'; - - cy.request({ - method: 'POST', - url: 'api/detection_engine/rules', - body: { - rule_id: ruleId, - risk_score: riskScore, - description: rule.description, - interval, - name: rule.name, - severity, - type: 'threat_match', - timeline_id: timeline?.templateTimelineId, - timeline_title: timeline?.title, - threat_mapping: [ - { - entries: [ - { - field: rule.indicatorMappingField, - type: 'mapping', - value: rule.indicatorIndexField, - }, - ], - }, - ], - threat_query: '*:*', - threat_language: 'kuery', - threat_filters: [], - threat_index: rule.indicatorIndexPattern, - threat_indicator_path: rule.threatIndicatorPath, - from: 'now-50000h', - index: rule.dataSource.type === 'indexPatterns' ? rule.dataSource.index : undefined, - data_view_id: rule.dataSource.type === 'dataView' ? rule.dataSource.dataView : undefined, - query: rule.customQuery || '*:*', - language: 'kuery', - enabled: true, - tags: rule.tags, - }, - headers: { 'kbn-xsrf': 'cypress-creds' }, - failOnStatusCode: false, - }); -}; - -export const createCustomRuleEnabled = ( - rule: CustomRule, - ruleId = '1', - maxSignals = 500, - actions?: RuleActionArray -) => { - const riskScore = rule.riskScore != null ? parseInt(rule.riskScore, 10) : undefined; - const severity = rule.severity != null ? rule.severity.toLocaleLowerCase() : undefined; - const interval = rule.runsEvery ? `${rule.runsEvery.interval}${rule.runsEvery.type}` : '100m'; - - if (rule.dataSource.type === 'indexPatterns') { - cy.request({ - method: 'POST', - url: 'api/detection_engine/rules', - body: { - rule_id: ruleId, - risk_score: riskScore, - description: rule.description, - interval, - name: rule.name, - severity, - type: 'query', - from: 'now-50000h', - index: rule.dataSource.index, - query: rule.customQuery, - language: 'kuery', - enabled: true, - exceptions_list: rule.exceptionLists ?? [], - tags: ['rule1'], - max_signals: maxSignals, - building_block_type: rule.buildingBlockType, - actions, - }, - headers: { 'kbn-xsrf': 'cypress-creds' }, - failOnStatusCode: false, - }); - } else if (rule.dataSource.type === 'dataView') { - cy.request({ - method: 'POST', - url: 'api/detection_engine/rules', - body: { - rule_id: ruleId, - risk_score: riskScore, - description: rule.description, - interval, - name: rule.name, - severity, - type: 'query', - from: 'now-50000h', - index: [], - data_view_id: rule.dataSource.dataView, - query: rule.customQuery, - language: 'kuery', - enabled: true, - exceptions_list: rule.exceptionLists ?? [], - tags: ['rule1'], - max_signals: maxSignals, - building_block_type: rule.buildingBlockType, - actions, - }, - headers: { 'kbn-xsrf': 'cypress-creds' }, - failOnStatusCode: false, - }); - } -}; - export const deleteCustomRule = (ruleId = '1') => { cy.request({ method: 'DELETE', diff --git a/x-pack/plugins/security_solution/cypress/tasks/common/filter_group.ts b/x-pack/plugins/security_solution/cypress/tasks/common/filter_group.ts new file mode 100644 index 0000000000000..467db67d9361e --- /dev/null +++ b/x-pack/plugins/security_solution/cypress/tasks/common/filter_group.ts @@ -0,0 +1,92 @@ +/* + * 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 { + DETECTION_PAGE_FILTER_GROUP_CONTEXT_MENU, + DETECTION_PAGE_FILTER_GROUP_RESET_BUTTON, + FILTER_GROUP_ADD_CONTROL, + FILTER_GROUP_DISCARD_CHANGES, + FILTER_GROUP_CONTEXT_EDIT_CONTROLS, + FILTER_GROUP_EDIT_CONTROLS_PANEL, + FILTER_GROUP_EDIT_CONTROL_PANEL_ITEMS, + FILTER_GROUP_SAVE_CHANGES, + CONTROL_FRAME_TITLE, + FILTER_GROUP_CONTROL_ACTION_DELETE, + FILTER_GROUP_CONTROL_CONFIRM_DIALOG, + FILTER_GROUP_CONTROL_CONFIRM_BTN, + DETECTION_PAGE_FILTER_GROUP_WRAPPER, + DETECTION_PAGE_FILTER_GROUP_LOADING, + DETECTION_PAGE_FILTERS_LOADING, + OPTION_LISTS_LOADING, +} from '../../screens/common/filter_group'; +import { waitForPageFilters } from '../alerts'; + +export const openFilterGroupContextMenu = () => { + cy.get(DETECTION_PAGE_FILTER_GROUP_CONTEXT_MENU).click({ force: true }); +}; + +export const waitForFilterGroups = () => { + cy.log('Waiting for Page Filters'); + // since these are only valid on the alert page + cy.get(DETECTION_PAGE_FILTER_GROUP_WRAPPER).should('exist'); + cy.get(DETECTION_PAGE_FILTER_GROUP_LOADING).should('not.exist'); + cy.get(DETECTION_PAGE_FILTERS_LOADING).should('not.exist'); + cy.get(OPTION_LISTS_LOADING).should('have.lengthOf', 0); +}; + +export const resetFilterGroup = () => { + openFilterGroupContextMenu(); + cy.get(DETECTION_PAGE_FILTER_GROUP_RESET_BUTTON).click({ force: true }); +}; + +export const editFilterGroupControls = () => { + openFilterGroupContextMenu(); + cy.get(FILTER_GROUP_CONTEXT_EDIT_CONTROLS).trigger('click'); +}; + +export const saveFilterGroupControls = () => { + cy.get(FILTER_GROUP_SAVE_CHANGES).trigger('click'); + cy.get(FILTER_GROUP_SAVE_CHANGES).should('not.exist'); + waitForPageFilters(); +}; + +export const discardFilterGroupControls = () => { + cy.get(FILTER_GROUP_DISCARD_CHANGES).trigger('click'); + cy.get(FILTER_GROUP_DISCARD_CHANGES).should('not.exist'); +}; + +export const openAddFilterGroupControlPanel = () => { + cy.get(FILTER_GROUP_ADD_CONTROL).trigger('click'); + cy.get(FILTER_GROUP_EDIT_CONTROLS_PANEL).should('be.visible'); +}; + +export const addNewFilterGroupControlValues = ({ + fieldName, + label, +}: { + fieldName: string; + label: string; +}) => { + const { FIELD_SEARCH, FIELD_PICKER, FIELD_LABEL, SAVE } = FILTER_GROUP_EDIT_CONTROL_PANEL_ITEMS; + openAddFilterGroupControlPanel(); + cy.get(FIELD_SEARCH).type(fieldName); + cy.get(FIELD_PICKER(fieldName)).should('exist').trigger('click'); + + cy.get(FIELD_LABEL).should('have.value', fieldName); + cy.get(FIELD_LABEL).clear(); + cy.get(FIELD_LABEL).type(label).should('have.value', label); + cy.get(SAVE).trigger('click'); + cy.get(FILTER_GROUP_EDIT_CONTROLS_PANEL).should('not.exist'); + waitForPageFilters(); +}; + +export const deleteFilterGroupControl = (idx: number) => { + cy.get(CONTROL_FRAME_TITLE).eq(idx).trigger('mouseover'); + cy.get(FILTER_GROUP_CONTROL_ACTION_DELETE(idx)).trigger('click', { force: true }); + cy.get(FILTER_GROUP_CONTROL_CONFIRM_DIALOG).should('be.visible'); + cy.get(FILTER_GROUP_CONTROL_CONFIRM_BTN).trigger('click'); +}; diff --git a/x-pack/plugins/security_solution/cypress/tasks/create_new_rule.ts b/x-pack/plugins/security_solution/cypress/tasks/create_new_rule.ts index 29f8670459649..9b22f4eeadde2 100644 --- a/x-pack/plugins/security_solution/cypress/tasks/create_new_rule.ts +++ b/x-pack/plugins/security_solution/cypress/tasks/create_new_rule.ts @@ -5,23 +5,20 @@ * 2.0. */ +import { isArray, parseInt } from 'lodash'; + import type { RuleIntervalFrom, Threat, ThreatSubtechnique, ThreatTechnique, } from '@kbn/securitysolution-io-ts-alerting-types'; -import { parseInt } from 'lodash'; -import type { - CustomRule, - MachineLearningRule, - OverrideRule, - ThreatIndicatorRule, - ThresholdRule, - NewTermsRule, - Mitre, -} from '../objects/rule'; -import { getMachineLearningRule } from '../objects/rule'; +import type { Actions } from '../objects/rule'; +// For some reason importing these functions from ../../public/detections/pages/detection_engine/rules/helpers +// causes a "Webpack Compilation Error" in this file specifically, even though it imports fine in the test files +// in ../e2e/*, so we have a copy of the implementations in the cypress helpers. +import { convertHistoryStartToSize, getHumanizedDuration } from '../helpers/rules'; + import { ABOUT_CONTINUE_BTN, ABOUT_EDIT_TAB, @@ -120,6 +117,15 @@ import { TIMELINE } from '../screens/timelines'; import { EUI_FILTER_SELECT_ITEM, COMBO_BOX_INPUT } from '../screens/common/controls'; import { ruleFields } from '../data/detection_engine'; import { BACK_TO_RULES_TABLE } from '../screens/rule_details'; +import type { + EqlRuleCreateProps, + MachineLearningRuleCreateProps, + NewTermsRuleCreateProps, + QueryRuleCreateProps, + RuleCreateProps, + ThreatMatchRuleCreateProps, + ThresholdRuleCreateProps, +} from '../../common/detection_engine/rule_schema'; import { waitForAlerts } from './alerts'; import { refreshPage } from './security_header'; import { EMPTY_ALERT_TABLE } from '../screens/alerts'; @@ -138,32 +144,30 @@ export const createRuleWithoutEnabling = () => { cy.get(BACK_TO_RULES_TABLE).should('not.exist'); }; -export const fillAboutRule = ( - rule: CustomRule | MachineLearningRule | ThresholdRule | ThreatIndicatorRule -) => { +export const fillAboutRule = (rule: RuleCreateProps) => { cy.get(RULE_NAME_INPUT).clear({ force: true }).type(rule.name, { force: true }); cy.get(RULE_DESCRIPTION_INPUT).clear({ force: true }).type(rule.description, { force: true }); if (rule.severity) { fillSeverity(rule.severity); } - if (rule.riskScore) { - fillRiskScore(rule.riskScore); + if (rule.risk_score) { + fillRiskScore(rule.risk_score); } if (rule.tags) { fillRuleTags(rule.tags); } cy.get(ADVANCED_SETTINGS_BTN).click({ force: true }); - if (rule.referenceUrls) { - fillReferenceUrls(rule.referenceUrls); + if (rule.references) { + fillReferenceUrls(rule.references); } - if (rule.falsePositivesExamples) { - fillFalsePositiveExamples(rule.falsePositivesExamples); + if (rule.false_positives) { + fillFalsePositiveExamples(rule.false_positives); } - if (rule.mitre) { - fillMitre(rule.mitre); + if (rule.threat) { + fillMitre(rule.threat); } if (rule.note) { fillNote(rule.note); @@ -179,28 +183,31 @@ export const fillNote = (note: string = ruleFields.investigationGuide) => { return note; }; -export const fillMitre = (mitreAttacks: Mitre[]) => { +export const fillMitre = (mitreAttacks: Threat[]) => { let techniqueIndex = 0; let subtechniqueInputIndex = 0; mitreAttacks.forEach((mitre, tacticIndex) => { cy.get(MITRE_ATTACK_TACTIC_DROPDOWN).eq(tacticIndex).click({ force: true }); - cy.contains(MITRE_TACTIC, mitre.tactic).click(); - - mitre.techniques.forEach((technique) => { - cy.get(MITRE_ATTACK_ADD_TECHNIQUE_BUTTON).eq(tacticIndex).click({ force: true }); - cy.get(MITRE_ATTACK_TECHNIQUE_DROPDOWN).eq(techniqueIndex).click({ force: true }); - cy.contains(MITRE_TACTIC, technique.name).click(); - - technique.subtechniques.forEach((subtechnique) => { - cy.get(MITRE_ATTACK_ADD_SUBTECHNIQUE_BUTTON).eq(techniqueIndex).click({ force: true }); - cy.get(MITRE_ATTACK_SUBTECHNIQUE_DROPDOWN) - .eq(subtechniqueInputIndex) - .click({ force: true }); - cy.contains(MITRE_TACTIC, subtechnique).click(); - subtechniqueInputIndex++; + cy.contains(MITRE_TACTIC, `${mitre.tactic.name} (${mitre.tactic.id})`).click(); + + if (mitre.technique) { + mitre.technique.forEach((technique) => { + cy.get(MITRE_ATTACK_ADD_TECHNIQUE_BUTTON).eq(tacticIndex).click({ force: true }); + cy.get(MITRE_ATTACK_TECHNIQUE_DROPDOWN).eq(techniqueIndex).click({ force: true }); + cy.contains(MITRE_TACTIC, `${technique.name} (${technique.id})`).click(); + if (technique.subtechnique) { + technique.subtechnique.forEach((subtechnique) => { + cy.get(MITRE_ATTACK_ADD_SUBTECHNIQUE_BUTTON).eq(techniqueIndex).click({ force: true }); + cy.get(MITRE_ATTACK_SUBTECHNIQUE_DROPDOWN) + .eq(subtechniqueInputIndex) + .click({ force: true }); + cy.contains(MITRE_TACTIC, `${subtechnique.name} (${subtechnique.id})`).click(); + subtechniqueInputIndex++; + }); + techniqueIndex++; + } }); - techniqueIndex++; - }); + } cy.get(MITRE_ATTACK_ADD_TACTIC_BUTTON).click({ force: true }); }); @@ -263,7 +270,7 @@ export const fillSeverity = (severity: string = ruleFields.ruleSeverity) => { return severity; }; -export const fillRiskScore = (riskScore: string = ruleFields.riskScore.toString()) => { +export const fillRiskScore = (riskScore: number = ruleFields.riskScore) => { cy.get(DEFAULT_RISK_SCORE_INPUT).type(`{selectall}${riskScore}`, { force: true }); return riskScore; }; @@ -283,37 +290,40 @@ export const fillReferenceUrls = (referenceUrls: string[] = ruleFields.reference return referenceUrls; }; -export const fillAboutRuleAndContinue = ( - rule: CustomRule | MachineLearningRule | ThresholdRule | ThreatIndicatorRule -) => { +export const fillAboutRuleAndContinue = (rule: RuleCreateProps) => { fillAboutRule(rule); getAboutContinueButton().should('exist').click({ force: true }); }; -export const fillAboutRuleWithOverrideAndContinue = (rule: OverrideRule) => { +export const fillAboutRuleWithOverrideAndContinue = (rule: RuleCreateProps) => { cy.get(RULE_NAME_INPUT).type(rule.name, { force: true }); cy.get(RULE_DESCRIPTION_INPUT).type(rule.description, { force: true }); cy.get(SEVERITY_MAPPING_OVERRIDE_OPTION).click(); - rule.severityOverride.forEach((severity, i) => { - cy.get(SEVERITY_OVERRIDE_ROW) - .eq(i) - .within(() => { - cy.get(COMBO_BOX_INPUT).eq(0).type(`${severity.sourceField}{enter}`); - cy.get(COMBO_BOX_INPUT).eq(1).type(`${severity.sourceValue}{enter}`); - }); - }); + if (rule.severity_mapping) { + rule.severity_mapping.forEach((severity, i) => { + cy.get(SEVERITY_OVERRIDE_ROW) + .eq(i) + .within(() => { + cy.get(COMBO_BOX_INPUT).eq(0).type(`${severity.field}{enter}`); + cy.get(COMBO_BOX_INPUT).eq(1).type(`${severity.value}{enter}`); + }); + }); + } if (rule.severity) { fillSeverity(rule.severity); } cy.get(RISK_MAPPING_OVERRIDE_OPTION).click(); - cy.get(RISK_OVERRIDE).within(() => { - cy.get(COMBO_BOX_INPUT).type(`${rule.riskOverride}{enter}`); - }); + if (rule.risk_score_mapping) { + const field = rule.risk_score_mapping[0].field; + cy.get(RISK_OVERRIDE).within(() => { + cy.get(COMBO_BOX_INPUT).type(`${field}{enter}`); + }); + } - cy.get(DEFAULT_RISK_SCORE_INPUT).type(`{selectall}${rule.riskScore}`, { force: true }); + cy.get(DEFAULT_RISK_SCORE_INPUT).type(`{selectall}${rule.risk_score}`, { force: true }); if (rule.tags) { fillRuleTags(rule.tags); @@ -321,45 +331,30 @@ export const fillAboutRuleWithOverrideAndContinue = (rule: OverrideRule) => { cy.get(ADVANCED_SETTINGS_BTN).click({ force: true }); - if (rule.referenceUrls) { - fillReferenceUrls(rule.referenceUrls); + if (rule.references) { + fillReferenceUrls(rule.references); } - if (rule.falsePositivesExamples) { - fillFalsePositiveExamples(rule.falsePositivesExamples); + if (rule.false_positives) { + fillFalsePositiveExamples(rule.false_positives); } - if (rule.mitre) { - fillMitre(rule.mitre); + if (rule.threat) { + fillMitre(rule.threat); } if (rule.note) { fillNote(rule.note); } cy.get(RULE_NAME_OVERRIDE).within(() => { - cy.get(COMBO_BOX_INPUT).type(`${rule.nameOverride}{enter}`); + cy.get(COMBO_BOX_INPUT).type(`${rule.rule_name_override}{enter}`); }); cy.get(RULE_TIMESTAMP_OVERRIDE).within(() => { - cy.get(COMBO_BOX_INPUT).type(`${rule.timestampOverride}{enter}`); + cy.get(COMBO_BOX_INPUT).type(`${rule.timestamp_override}{enter}`); }); getAboutContinueButton().should('exist').click({ force: true }); }; -const fillCustomQuery = (rule: CustomRule | OverrideRule) => { - if (rule.timeline?.id) { - cy.get(IMPORT_QUERY_FROM_SAVED_TIMELINE_LINK).click(); - cy.get(TIMELINE(rule.timeline.id)).click(); - cy.get(CUSTOM_QUERY_INPUT).should('have.value', rule.customQuery); - if (rule.dataSource.type === 'indexPatterns') { - removeAlertsIndex(); - } - } else { - cy.get(CUSTOM_QUERY_INPUT) - .first() - .type(rule.customQuery || ''); - } -}; - // called after import rule from saved timeline // if alerts index is created, it is included in the timeline // to be consistent in multiple test runs, remove it if it's there @@ -375,24 +370,31 @@ export const continueWithNextSection = () => { cy.get(CONTINUE_BUTTON).should('exist').click(); }; -export const fillDefineCustomRuleAndContinue = (rule: CustomRule | OverrideRule) => { - if (rule.dataSource.type === 'dataView') { +export const fillDefineCustomRuleAndContinue = (rule: QueryRuleCreateProps) => { + if (rule.data_view_id !== undefined) { cy.get(DATA_VIEW_OPTION).click(); - cy.get(DATA_VIEW_COMBO_BOX).type(`${rule.dataSource.dataView}{enter}`); + cy.get(DATA_VIEW_COMBO_BOX).type(`${rule.data_view_id}{enter}`); } - fillCustomQuery(rule); + cy.get(CUSTOM_QUERY_INPUT) + .first() + .type(rule.query || ''); cy.get(DEFINE_CONTINUE_BUTTON).should('exist').click({ force: true }); cy.get(CUSTOM_QUERY_INPUT).should('not.exist'); }; -export const fillScheduleRuleAndContinue = (rule: CustomRule | MachineLearningRule) => { - if (rule.runsEvery) { - cy.get(RUNS_EVERY_INTERVAL).type('{selectall}').type(rule.runsEvery.interval); - cy.get(RUNS_EVERY_TIME_TYPE).select(rule.runsEvery.timeType); +export const fillScheduleRuleAndContinue = (rule: RuleCreateProps) => { + if (rule.interval) { + const intervalNumber = rule.interval.slice(0, rule.interval.length - 1); + const intervalType = rule.interval.charAt(rule.interval.length - 1); + cy.get(RUNS_EVERY_INTERVAL).type('{selectall}').type(intervalNumber); + cy.get(RUNS_EVERY_TIME_TYPE).select(intervalType); } - if (rule.lookBack) { - cy.get(LOOK_BACK_INTERVAL).type('{selectAll}').type(rule.lookBack.interval); - cy.get(LOOK_BACK_TIME_TYPE).select(rule.lookBack.timeType); + if (rule.from) { + const additionalLookback = getHumanizedDuration(rule.from, rule.interval ?? '5m'); + const additionalLookbackNumber = additionalLookback.slice(0, additionalLookback.length - 1); + const additionalLookbackType = additionalLookback.charAt(additionalLookback.length - 1); + cy.get(LOOK_BACK_INTERVAL).type('{selectAll}').type(additionalLookbackNumber); + cy.get(LOOK_BACK_TIME_TYPE).select(additionalLookbackType); } cy.get(SCHEDULE_CONTINUE_BUTTON).click({ force: true }); }; @@ -404,55 +406,55 @@ export const fillFrom = (from: RuleIntervalFrom = ruleFields.ruleIntervalFrom) = cy.get(LOOK_BACK_TIME_TYPE).select(type); }; -export const fillRuleAction = (rule: CustomRule) => { - if (rule.actions) { - cy.get(ACTIONS_THROTTLE_INPUT).select(rule.actions.throttle); - rule.actions?.connectors.forEach((connector) => { - switch (connector.type) { - case 'index': - cy.get(INDEX_SELECTOR).click(); - cy.get(CREATE_ACTION_CONNECTOR_BTN).click(); - fillIndexConnectorForm(connector); - break; - case 'email': - cy.get(EMAIL_ACTION_BTN).click(); - cy.get(CREATE_ACTION_CONNECTOR_BTN).click(); - fillEmailConnectorForm(connector); - break; - } - }); - } +export const fillRuleAction = (actions: Actions) => { + cy.get(ACTIONS_THROTTLE_INPUT).select(actions.throttle); + actions.connectors.forEach((connector) => { + switch (connector.type) { + case 'index': + cy.get(INDEX_SELECTOR).click(); + cy.get(CREATE_ACTION_CONNECTOR_BTN).click(); + fillIndexConnectorForm(connector); + break; + case 'email': + cy.get(EMAIL_ACTION_BTN).click(); + cy.get(CREATE_ACTION_CONNECTOR_BTN).click(); + fillEmailConnectorForm(connector); + break; + } + }); }; -export const fillDefineThresholdRuleAndContinue = (rule: ThresholdRule) => { +export const fillDefineThresholdRuleAndContinue = (rule: ThresholdRuleCreateProps) => { const thresholdField = 0; const threshold = 1; const typeThresholdField = ($el: Cypress.ObjectLike) => - cy.wrap($el).type(rule.thresholdField, { delay: 35 }); + cy + .wrap($el) + .type(isArray(rule.threshold.field) ? rule.threshold.field[0] : rule.threshold.field, { + delay: 35, + }); - fillCustomQuery(rule); + cy.get(CUSTOM_QUERY_INPUT) + .first() + .type(rule.query || ''); cy.get(THRESHOLD_INPUT_AREA) .find(INPUT) .then((inputs) => { cy.wrap(inputs[thresholdField]).click(); cy.wrap(inputs[thresholdField]).pipe(typeThresholdField); cy.get(EUI_FILTER_SELECT_ITEM).click({ force: true }); - cy.wrap(inputs[threshold]).clear().type(rule.threshold); + cy.wrap(inputs[threshold]).clear().type(`${rule.threshold.value}`); }); cy.get(DEFINE_CONTINUE_BUTTON).should('exist').click({ force: true }); cy.get(CUSTOM_QUERY_INPUT).should('not.exist'); }; -export const fillDefineEqlRuleAndContinue = (rule: CustomRule) => { - if (rule.customQuery == null) { - throw new TypeError('The rule custom query should never be undefined or null '); - } - +export const fillDefineEqlRuleAndContinue = (rule: EqlRuleCreateProps) => { cy.get(RULES_CREATION_FORM).find(EQL_QUERY_INPUT).should('exist'); cy.get(RULES_CREATION_FORM).find(EQL_QUERY_INPUT).should('be.visible'); - cy.get(RULES_CREATION_FORM).find(EQL_QUERY_INPUT).type(rule.customQuery); + cy.get(RULES_CREATION_FORM).find(EQL_QUERY_INPUT).type(rule.query); cy.get(RULES_CREATION_FORM).find(EQL_QUERY_VALIDATION_SPINNER).should('not.exist'); cy.get(RULES_CREATION_PREVIEW_BUTTON).should('not.be.disabled').click({ force: true }); cy.get(RULES_CREATION_PREVIEW_REFRESH_BUTTON).should('not.be.disabled').click({ force: true }); @@ -470,18 +472,21 @@ export const fillDefineEqlRuleAndContinue = (rule: CustomRule) => { cy.get(`${RULES_CREATION_FORM} ${EQL_QUERY_INPUT}`).should('not.exist'); }; -export const fillDefineNewTermsRuleAndContinue = (rule: NewTermsRule) => { - fillCustomQuery(rule); - cy.get(NEW_TERMS_INPUT_AREA).find(INPUT).click().type(rule.newTermsFields[0], { delay: 35 }); +export const fillDefineNewTermsRuleAndContinue = (rule: NewTermsRuleCreateProps) => { + cy.get(CUSTOM_QUERY_INPUT) + .first() + .type(rule.query || ''); + cy.get(NEW_TERMS_INPUT_AREA).find(INPUT).click().type(rule.new_terms_fields[0], { delay: 35 }); cy.get(EUI_FILTER_SELECT_ITEM).click({ force: true }); cy.focused().type('{esc}'); // Close combobox dropdown so next inputs can be interacted with + const historySize = convertHistoryStartToSize(rule.history_window_start); + const historySizeNumber = historySize.slice(0, historySize.length - 1); + const historySizeType = historySize.charAt(historySize.length - 1); cy.get(NEW_TERMS_INPUT_AREA) .find(NEW_TERMS_HISTORY_SIZE) .type('{selectAll}') - .type(rule.historyWindowSize.interval); - cy.get(NEW_TERMS_INPUT_AREA) - .find(NEW_TERMS_HISTORY_TIME_TYPE) - .select(rule.historyWindowSize.timeType); + .type(historySizeNumber); + cy.get(NEW_TERMS_INPUT_AREA).find(NEW_TERMS_HISTORY_TIME_TYPE).select(historySizeType); cy.get(DEFINE_CONTINUE_BUTTON).should('exist').click({ force: true }); cy.get(CUSTOM_QUERY_INPUT).should('not.exist'); @@ -606,21 +611,24 @@ export const getCustomQueryInvalidationText = () => cy.contains(CUSTOM_QUERY_REQ * Fills in the define indicator match rules and then presses the continue button * @param rule The rule to use to fill in everything */ -export const fillDefineIndicatorMatchRuleAndContinue = (rule: ThreatIndicatorRule) => { - if (rule.dataSource.type === 'indexPatterns') { - fillIndexAndIndicatorIndexPattern(rule.dataSource.index, rule.indicatorIndexPattern); +export const fillDefineIndicatorMatchRuleAndContinue = (rule: ThreatMatchRuleCreateProps) => { + if (rule.index) { + fillIndexAndIndicatorIndexPattern(rule.index, rule.threat_index); } fillIndicatorMatchRow({ - indexField: rule.indicatorMappingField, - indicatorIndexField: rule.indicatorIndexField, + indexField: rule.threat_mapping[0].entries[0].field, + indicatorIndexField: rule.threat_mapping[0].entries[0].value, }); getCustomIndicatorQueryInput().type('{selectall}{enter}*:*'); getDefineContinueButton().should('exist').click({ force: true }); cy.get(CUSTOM_QUERY_INPUT).should('not.exist'); }; -export const fillDefineMachineLearningRuleAndContinue = (rule: MachineLearningRule) => { - const text = rule.machineLearningJobs +export const fillDefineMachineLearningRuleAndContinue = (rule: MachineLearningRuleCreateProps) => { + const jobsAsArray = isArray(rule.machine_learning_job_id) + ? rule.machine_learning_job_id + : [rule.machine_learning_job_id]; + const text = jobsAsArray .map((machineLearningJob) => `${machineLearningJob}{downArrow}{enter}`) .join(''); cy.get(MACHINE_LEARNING_DROPDOWN_INPUT).click({ force: true }); @@ -628,12 +636,9 @@ export const fillDefineMachineLearningRuleAndContinue = (rule: MachineLearningRu cy.get(MACHINE_LEARNING_DROPDOWN_INPUT).type('{esc}'); - cy.get(ANOMALY_THRESHOLD_INPUT).type( - `{selectall}${getMachineLearningRule().anomalyScoreThreshold}`, - { - force: true, - } - ); + cy.get(ANOMALY_THRESHOLD_INPUT).type(`{selectall}${rule.anomaly_threshold}`, { + force: true, + }); getDefineContinueButton().should('exist').click({ force: true }); }; diff --git a/x-pack/plugins/security_solution/cypress/tasks/es_archiver.ts b/x-pack/plugins/security_solution/cypress/tasks/es_archiver.ts index 32c29d6193561..6708e44efe171 100644 --- a/x-pack/plugins/security_solution/cypress/tasks/es_archiver.ts +++ b/x-pack/plugins/security_solution/cypress/tasks/es_archiver.ts @@ -19,6 +19,7 @@ const NODE_TLS_REJECT_UNAUTHORIZED = '1'; export const esArchiverLoad = (folder: string) => { const path = Path.join(ES_ARCHIVE_DIR, folder); + cy.log(`exec esArchiverLoad`, path); cy.exec( `node ../../../scripts/es_archiver load "${path}" --config "${CONFIG_PATH}" --es-url "${ES_URL}" --kibana-url "${KIBANA_URL}"`, { env: { NODE_TLS_REJECT_UNAUTHORIZED } } @@ -27,6 +28,7 @@ export const esArchiverLoad = (folder: string) => { export const esArchiverUnload = (folder: string) => { const path = Path.join(ES_ARCHIVE_DIR, folder); + cy.log(`exec esArchiverUnload`, path); cy.exec( `node ../../../scripts/es_archiver unload "${path}" --config "${CONFIG_PATH}" --es-url "${ES_URL}" --kibana-url "${KIBANA_URL}"`, { env: { NODE_TLS_REJECT_UNAUTHORIZED } } @@ -34,6 +36,7 @@ export const esArchiverUnload = (folder: string) => { }; export const esArchiverResetKibana = () => { + cy.log(`exec esArchiverResetKibana`); cy.exec( `node ../../../scripts/es_archiver empty-kibana-index --config "${CONFIG_PATH}" --es-url "${ES_URL}" --kibana-url "${KIBANA_URL}"`, { env: { NODE_TLS_REJECT_UNAUTHORIZED }, failOnNonZeroExit: false } diff --git a/x-pack/plugins/security_solution/cypress/tasks/exceptions_table.ts b/x-pack/plugins/security_solution/cypress/tasks/exceptions_table.ts index 38f2542a22db4..d80b3a4f1eb08 100644 --- a/x-pack/plugins/security_solution/cypress/tasks/exceptions_table.ts +++ b/x-pack/plugins/security_solution/cypress/tasks/exceptions_table.ts @@ -15,6 +15,12 @@ import { EXCEPTIONS_TABLE_EXPORT_MODAL_BTN, EXCEPTIONS_OVERFLOW_ACTIONS_BTN, EXCEPTIONS_TABLE_EXPORT_CONFIRM_BTN, + EXCEPTIONS_LIST_MANAGEMENT_NAME, + EXCEPTIONS_LIST_MANAGEMENT_EDIT_NAME_BTN, + EXCEPTIONS_LIST_MANAGEMENT_EDIT_MODAL_NAME_INPUT, + EXCEPTIONS_LIST_MANAGEMENT_EDIT_MODAL_DESCRIPTION_INPUT, + EXCEPTIONS_LIST_EDIT_DETAILS_SAVE_BTN, + EXCEPTIONS_LIST_DETAILS_HEADER, } from '../screens/exceptions'; export const clearSearchSelection = () => { @@ -59,3 +65,45 @@ export const waitForExceptionsTableToBeLoaded = () => { cy.get(EXCEPTIONS_TABLE).should('exist'); cy.get(EXCEPTIONS_TABLE_SEARCH).should('exist'); }; + +export const waitForExceptionListDetailToBeLoaded = () => { + cy.get(EXCEPTIONS_LIST_DETAILS_HEADER).should('exist'); +}; + +export const editExceptionLisDetails = ({ + name, + description, +}: { + name?: { original: string; updated: string }; + description?: { original: string; updated: string | null }; +}) => { + cy.get(EXCEPTIONS_LIST_MANAGEMENT_NAME).should('exist'); + cy.get(EXCEPTIONS_LIST_MANAGEMENT_EDIT_NAME_BTN).first().click(); + + if (name != null) { + cy.get(EXCEPTIONS_LIST_MANAGEMENT_NAME).should('have.text', name.original); + cy.get(EXCEPTIONS_LIST_MANAGEMENT_EDIT_MODAL_NAME_INPUT) + .should('have.value', name.original) + .clear({ force: true }) + .type(`${name.updated}`); + cy.get(EXCEPTIONS_LIST_MANAGEMENT_EDIT_MODAL_NAME_INPUT).should('have.value', name.updated); + } + + if (description != null) { + cy.get(EXCEPTIONS_LIST_MANAGEMENT_EDIT_MODAL_DESCRIPTION_INPUT) + .should('have.value', description.original) + .clear({ force: true }) + .should('not.have.value'); + if (description.updated != null) { + cy.get(EXCEPTIONS_LIST_MANAGEMENT_EDIT_MODAL_DESCRIPTION_INPUT).type( + `${description.updated}` + ); + cy.get(EXCEPTIONS_LIST_MANAGEMENT_EDIT_MODAL_DESCRIPTION_INPUT).should( + 'have.value', + description.updated + ); + } + } + + cy.get(EXCEPTIONS_LIST_EDIT_DETAILS_SAVE_BTN).first().click(); +}; diff --git a/x-pack/plugins/security_solution/cypress/tasks/inspect.ts b/x-pack/plugins/security_solution/cypress/tasks/inspect.ts index fbb64430c59e9..78657f2b80d34 100644 --- a/x-pack/plugins/security_solution/cypress/tasks/inspect.ts +++ b/x-pack/plugins/security_solution/cypress/tasks/inspect.ts @@ -5,8 +5,12 @@ * 2.0. */ -import type { InspectButtonMetadata } from '../screens/inspect'; -import { INSPECT_BUTTON_ICON } from '../screens/inspect'; +import type { InspectLensVisualizationsMetadata, InspectTableMetadata } from '../screens/inspect'; +import { + EMBEDDABLE_PANEL_INSPECT, + EMBEDDABLE_PANEL_TOGGLE_ICON, + INSPECT_BUTTON_ICON, +} from '../screens/inspect'; export const closesModal = () => { cy.get('[data-test-subj="modal-inspect-close"]').click(); @@ -18,12 +22,17 @@ export const clickInspectButton = (container: string) => { cy.get(`${container} ${INSPECT_BUTTON_ICON}`).trigger('click', { force: true }); }; -export const openStatsAndTables = (table: InspectButtonMetadata) => { - if (table.tabId) { - cy.get(table.tabId).invoke('show'); - cy.get(table.tabId).click({ force: true }); - } - cy.get(table.id); +const LOADER_ARIA = '[aria-label="Loading"]'; +const TABLE_LOADER = `[data-test-subj="initialLoadingPanelPaginatedTable"],${LOADER_ARIA}`; + +export const openTableInspectModal = (table: InspectTableMetadata) => { + // wait for table to load + cy.get(table.id).then(($table) => { + if ($table.find(TABLE_LOADER).length > 0) { + cy.get(TABLE_LOADER).should('not.exist'); + } + }); + if (table.altInspectId) { cy.get(table.altInspectId).invoke('show'); cy.get(table.altInspectId).trigger('click', { @@ -33,3 +42,31 @@ export const openStatsAndTables = (table: InspectButtonMetadata) => { clickInspectButton(table.id); } }; + +export const openLensVisualizationsInspectModal = ( + { panelSelector, embeddableId, tab }: InspectLensVisualizationsMetadata, + onOpen: () => void +) => { + cy.get(panelSelector) + .get(`[data-test-embeddable-id="${embeddableId}"]`) + .each(($el) => { + const container = cy.wrap($el); + + // wait for visualization to load + if ($el.find(LOADER_ARIA).length > 0) { + cy.get(LOADER_ARIA).should('not.exist'); + } + + container.find(EMBEDDABLE_PANEL_TOGGLE_ICON).click(); + cy.get(EMBEDDABLE_PANEL_INSPECT).click(); + + onOpen(); + + closesModal(); + }); +}; + +export const openTab = (tab: string) => { + cy.get(tab).invoke('show'); + cy.get(tab).click({ force: true }); +}; diff --git a/x-pack/plugins/security_solution/cypress/tasks/sourcerer.ts b/x-pack/plugins/security_solution/cypress/tasks/sourcerer.ts index 266e3655a8342..01e3716131749 100644 --- a/x-pack/plugins/security_solution/cypress/tasks/sourcerer.ts +++ b/x-pack/plugins/security_solution/cypress/tasks/sourcerer.ts @@ -10,7 +10,7 @@ import { HOSTS_URL } from '../urls/navigation'; import { waitForPage } from './login'; import { openTimelineUsingToggle } from './security_main'; import { DEFAULT_ALERTS_INDEX } from '../../common/constants'; -import { createCustomRuleEnabled } from './api_calls/rules'; +import { createRule } from './api_calls/rules'; import { getNewRule } from '../objects/rule'; export const openSourcerer = (sourcererScope?: string) => { @@ -23,6 +23,12 @@ export const openSourcerer = (sourcererScope?: string) => { cy.get(SOURCERER.wrapper).should('be.visible'); }; +export const selectDataView = (dataView: string, sourcererScope?: string) => { + openSourcerer(sourcererScope); + openDataViewSelection(); + cy.get(SOURCERER.selectListOption).contains(dataView).click(); +}; + const openTimelineSourcerer = () => { cy.get(SOURCERER.triggerTimeline).should('be.enabled'); cy.get(SOURCERER.triggerTimeline).should('be.visible'); @@ -148,7 +154,7 @@ const refreshUntilAlertsIndexExists = async () => { }; export const waitForAlertsIndexToExist = () => { - createCustomRuleEnabled(getNewRule(), '1', 100); + createRule({ ...getNewRule(), rule_id: '1', max_signals: 100 }); refreshUntilAlertsIndexExists(); }; diff --git a/x-pack/plugins/security_solution/cypress/tsconfig.json b/x-pack/plugins/security_solution/cypress/tsconfig.json index fdb6b295104b1..4af63c6d1b406 100644 --- a/x-pack/plugins/security_solution/cypress/tsconfig.json +++ b/x-pack/plugins/security_solution/cypress/tsconfig.json @@ -27,6 +27,7 @@ "path": "../tsconfig.json", "force": true }, - "@kbn/rison" + "@kbn/rison", + "@kbn/datemath" ] } diff --git a/x-pack/plugins/security_solution/cypress/urls/navigation.ts b/x-pack/plugins/security_solution/cypress/urls/navigation.ts index 21cad5ff1a8df..6048b2d9305c6 100644 --- a/x-pack/plugins/security_solution/cypress/urls/navigation.ts +++ b/x-pack/plugins/security_solution/cypress/urls/navigation.ts @@ -32,6 +32,7 @@ export const TIMELINES_URL = '/app/security/timelines'; export const TIMELINE_TEMPLATES_URL = '/app/security/timelines/template'; export const CASES_URL = '/app/security/cases'; export const EXCEPTIONS_URL = 'app/security/exceptions'; +export const EXCEPTIONS_LIST_URL = 'app/security/exceptions/details'; export const HOSTS_URL = '/app/security/hosts/allHosts'; export const CSP_FINDINGS_URL = 'app/security/cloud_security_posture/findings'; export const DETECTIONS_RULE_MANAGEMENT_URL = 'app/security/rules'; @@ -66,3 +67,5 @@ export const hostDetailsUrl = (hostName: string) => `/app/security/hosts/${hostName}/authentications`; export const userDetailsUrl = (userName: string) => `/app/security/users/${userName}/allUsers`; + +export const exceptionsListDetailsUrl = (listId: string) => `${EXCEPTIONS_LIST_URL}/${listId}`; diff --git a/x-pack/plugins/security_solution/public/common/components/filter_group/buttons.tsx b/x-pack/plugins/security_solution/public/common/components/filter_group/buttons.tsx new file mode 100644 index 0000000000000..de92ed6ba608f --- /dev/null +++ b/x-pack/plugins/security_solution/public/common/components/filter_group/buttons.tsx @@ -0,0 +1,96 @@ +/* + * 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 type { FC } from 'react'; +import React from 'react'; +import type { EuiButtonIconProps } from '@elastic/eui'; +import { EuiButtonIcon, EuiCallOut, EuiPopover, EuiToolTip } from '@elastic/eui'; +import { useFilterGroupInternalContext } from './hooks/use_filters'; +import { DISCARD_CHANGES, PENDING_CHANGES_REMINDER } from './translations'; + +interface AddControlProps extends Partial { + onClick: () => void; +} + +export const AddControl: FC = ({ onClick, ...rest }) => { + return ( + + ); +}; + +interface SaveControlsProps { + onClick: () => void; +} + +export const SaveControls: FC = ({ onClick }) => { + const { + hasPendingChanges, + openPendingChangesPopover, + closePendingChangesPopover, + pendingChangesPopoverOpen, + } = useFilterGroupInternalContext(); + + return ( + + } + isOpen={pendingChangesPopoverOpen} + anchorPosition={'upCenter'} + panelPaddingSize="none" + closePopover={closePendingChangesPopover} + panelProps={{ + 'data-test-subj': 'filter-group__save-popover', + }} + > +

+ +
+ + ); +}; + +interface DiscardChangesProps { + onClick: () => void; +} + +export const DiscardChanges: FC = ({ onClick }) => { + return ( + + + + ); +}; diff --git a/x-pack/plugins/observability/public/components/app/header/index.tsx b/x-pack/plugins/security_solution/public/common/components/filter_group/config.ts similarity index 81% rename from x-pack/plugins/observability/public/components/app/header/index.tsx rename to x-pack/plugins/security_solution/public/common/components/filter_group/config.ts index f7f69c22173fc..49e7196300dbb 100644 --- a/x-pack/plugins/observability/public/components/app/header/index.tsx +++ b/x-pack/plugins/security_solution/public/common/components/filter_group/config.ts @@ -5,4 +5,7 @@ * 2.0. */ -export * from './header_menu'; +export const NUM_OF_CONTROLS = { + MIN: 2, + MAX: 6, +}; diff --git a/x-pack/plugins/security_solution/public/common/components/filter_group/context_menu.tsx b/x-pack/plugins/security_solution/public/common/components/filter_group/context_menu.tsx new file mode 100644 index 0000000000000..c70e33148aeee --- /dev/null +++ b/x-pack/plugins/security_solution/public/common/components/filter_group/context_menu.tsx @@ -0,0 +1,140 @@ +/* + * 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 { EuiButtonIcon, EuiContextMenuItem, EuiContextMenuPanel, EuiPopover } from '@elastic/eui'; +import React, { useCallback, useMemo, useState } from 'react'; +import { useFilterGroupInternalContext } from './hooks/use_filters'; +import { + CONTEXT_MENU_RESET, + CONTEXT_MENU_RESET_TOOLTIP, + EDIT_CONTROLS, + FILTER_GROUP_MENU, + SAVE_CONTROLS, +} from './translations'; + +export const FilterGroupContextMenu = () => { + const [isContextMenuVisible, setIsContextMenuVisible] = useState(false); + + const { + isViewMode, + controlGroupInputUpdates, + controlGroup, + switchToViewMode, + switchToEditMode, + initialControls, + dataViewId, + setShowFiltersChangedBanner, + } = useFilterGroupInternalContext(); + + const toggleContextMenu = useCallback(() => { + setIsContextMenuVisible((prev) => !prev); + }, []); + + const withContextMenuAction = useCallback( + (fn: unknown) => { + return () => { + if (typeof fn === 'function') { + fn(); + } + toggleContextMenu(); + }; + }, + [toggleContextMenu] + ); + + const resetSelection = useCallback(() => { + if (!controlGroupInputUpdates) return; + + // remove existing embeddables + controlGroup?.updateInput({ + panels: {}, + }); + + initialControls.forEach((control, idx) => { + controlGroup?.addOptionsListControl({ + controlId: String(idx), + hideExclude: true, + hideSort: true, + hidePanelTitles: true, + placeholder: '', + // option List controls will handle an invalid dataview + // & display an appropriate message + dataViewId: dataViewId ?? '', + ...control, + }); + }); + + controlGroup?.reload(); + switchToViewMode(); + setShowFiltersChangedBanner(false); + }, [ + controlGroupInputUpdates, + controlGroup, + initialControls, + dataViewId, + switchToViewMode, + setShowFiltersChangedBanner, + ]); + + const resetButton = useMemo( + () => ( + + {CONTEXT_MENU_RESET} + + ), + [withContextMenuAction, resetSelection] + ); + + const editControlsButton = useMemo( + () => ( + + {isViewMode ? EDIT_CONTROLS : SAVE_CONTROLS} + + ), + [withContextMenuAction, isViewMode, switchToEditMode, switchToViewMode] + ); + + const contextMenuItems = useMemo( + () => [resetButton, editControlsButton], + [resetButton, editControlsButton] + ); + + return ( + + } + isOpen={isContextMenuVisible} + closePopover={toggleContextMenu} + panelPaddingSize="none" + anchorPosition="downLeft" + > + + + ); +}; diff --git a/x-pack/plugins/security_solution/public/common/components/filter_group/filter_group_context.tsx b/x-pack/plugins/security_solution/public/common/components/filter_group/filter_group_context.tsx new file mode 100644 index 0000000000000..69271b74bd1b8 --- /dev/null +++ b/x-pack/plugins/security_solution/public/common/components/filter_group/filter_group_context.tsx @@ -0,0 +1,28 @@ +/* + * 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 type { ControlGroupContainer, ControlGroupInput } from '@kbn/controls-plugin/public'; +import { createContext } from 'react'; +import type { FilterItemObj } from './types'; + +export interface FilterGroupContextType { + initialControls: FilterItemObj[]; + dataViewId: string; + controlGroup: ControlGroupContainer | undefined; + controlGroupInputUpdates: ControlGroupInput | undefined; + isViewMode: boolean; + hasPendingChanges: boolean; + pendingChangesPopoverOpen: boolean; + closePendingChangesPopover: () => void; + openPendingChangesPopover: () => void; + switchToViewMode: () => void; + switchToEditMode: () => void; + setHasPendingChanges: (value: boolean) => void; + setShowFiltersChangedBanner: (value: boolean) => void; +} + +export const FilterGroupContext = createContext(undefined); diff --git a/x-pack/plugins/security_solution/public/common/components/filter_group/filters_changed_banner.tsx b/x-pack/plugins/security_solution/public/common/components/filter_group/filters_changed_banner.tsx new file mode 100644 index 0000000000000..ec9b48de8ae43 --- /dev/null +++ b/x-pack/plugins/security_solution/public/common/components/filter_group/filters_changed_banner.tsx @@ -0,0 +1,26 @@ +/* + * 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 { EuiCallOut, EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; +import React from 'react'; +import { FILTER_GROUP_BANNER_MESSAGE, FILTER_GROUP_BANNER_TITLE } from './translations'; + +export const FiltersChangedBanner = () => { + return ( + + + +

{FILTER_GROUP_BANNER_MESSAGE}

+
+
+
+ ); +}; diff --git a/x-pack/plugins/security_solution/public/common/components/filter_group/hooks/__tests__/use_filters_sync_to_local_storage.test.ts b/x-pack/plugins/security_solution/public/common/components/filter_group/hooks/__tests__/use_filters_sync_to_local_storage.test.ts new file mode 100644 index 0000000000000..703c0442b3086 --- /dev/null +++ b/x-pack/plugins/security_solution/public/common/components/filter_group/hooks/__tests__/use_filters_sync_to_local_storage.test.ts @@ -0,0 +1,108 @@ +/* + * 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 type { ControlGroupInput } from '@kbn/controls-plugin/common'; +import { renderHook } from '@testing-library/react-hooks'; +import { useControlGroupSyncToLocalStorage } from '../use_control_group_sync_to_local_storage'; + +const TEST_STORAGE_KEY = 'test_key'; +const DEFAULT_STORED_VALUE = { + val: 'default_local_storage_value', +} as unknown as ControlGroupInput; + +const ANOTHER_SAMPLE_VALUE = { + val: 'another_local_storage_value', +} as unknown as ControlGroupInput; + +let mockLocalStorage: Record = {}; +describe('Filters Sync to Local Storage', () => { + beforeEach(() => { + Object.defineProperty(global, 'localStorage', { + value: { + getItem: jest.fn((key) => { + return key in mockLocalStorage ? mockLocalStorage[key] : undefined; + }), + setItem: jest.fn((key, val) => (mockLocalStorage[key] = val)), + }, + writable: true, + }); + }); + afterEach(() => { + mockLocalStorage = {}; + }); + it('should not be undefined if localStorage has initial value', () => { + global.localStorage.setItem(TEST_STORAGE_KEY, JSON.stringify(DEFAULT_STORED_VALUE)); + const { result, waitForNextUpdate } = renderHook(() => + useControlGroupSyncToLocalStorage({ + storageKey: TEST_STORAGE_KEY, + shouldSync: true, + }) + ); + waitForNextUpdate(); + expect(result.current.controlGroupInput).toMatchObject(DEFAULT_STORED_VALUE); + }); + it('should be undefined if localstorage as NO initial value', () => { + const { result, waitForNextUpdate } = renderHook(() => + useControlGroupSyncToLocalStorage({ + storageKey: TEST_STORAGE_KEY, + shouldSync: true, + }) + ); + waitForNextUpdate(); + expect(result.current.controlGroupInput).toBeUndefined(); + expect(result.current.setControlGroupInput).toBeTruthy(); + }); + it('should be update values to local storage when sync is ON', () => { + const { result, waitFor } = renderHook(() => + useControlGroupSyncToLocalStorage({ + storageKey: TEST_STORAGE_KEY, + shouldSync: true, + }) + ); + waitFor(() => { + expect(result.current.controlGroupInput).toBeUndefined(); + expect(result.current.setControlGroupInput).toBeTruthy(); + }); + result.current.setControlGroupInput(DEFAULT_STORED_VALUE); + waitFor(() => { + expect(result.current.controlGroupInput).toMatchObject(DEFAULT_STORED_VALUE); + expect(global.localStorage.getItem(TEST_STORAGE_KEY)).toBe( + JSON.stringify(DEFAULT_STORED_VALUE) + ); + }); + }); + it('should not update values to local storage when sync is OFF', () => { + const { waitFor, result, rerender } = renderHook(() => + useControlGroupSyncToLocalStorage({ + storageKey: TEST_STORAGE_KEY, + shouldSync: true, + }) + ); + + // Sync is ON + waitFor(() => { + expect(result.current.controlGroupInput).toBeUndefined(); + expect(result.current.setControlGroupInput).toBeTruthy(); + }); + + result.current.setControlGroupInput(DEFAULT_STORED_VALUE); + waitFor(() => { + expect(result.current.controlGroupInput).toMatchObject(DEFAULT_STORED_VALUE); + }); + + // Sync is OFF + rerender({ storageKey: TEST_STORAGE_KEY, shouldSync: false }); + result.current.setControlGroupInput(ANOTHER_SAMPLE_VALUE); + waitFor(() => { + expect(result.current.controlGroupInput).toMatchObject(ANOTHER_SAMPLE_VALUE); + // old value + expect(global.localStorage.getItem(TEST_STORAGE_KEY)).toBe( + JSON.stringify(DEFAULT_STORED_VALUE) + ); + }); + }); +}); diff --git a/x-pack/plugins/security_solution/public/common/components/filter_group/hooks/use_control_group_sync_to_local_storage.ts b/x-pack/plugins/security_solution/public/common/components/filter_group/hooks/use_control_group_sync_to_local_storage.ts new file mode 100644 index 0000000000000..fd59ecd315d34 --- /dev/null +++ b/x-pack/plugins/security_solution/public/common/components/filter_group/hooks/use_control_group_sync_to_local_storage.ts @@ -0,0 +1,49 @@ +/* + * 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 type { ControlGroupInput } from '@kbn/controls-plugin/common'; +import { Storage } from '@kbn/kibana-utils-plugin/public'; +import { useEffect, useRef, useState } from 'react'; +import type { Dispatch, SetStateAction } from 'react'; + +interface UseControlGroupSyncToLocalStorageArgs { + storageKey: string; + shouldSync: boolean; +} + +type UseControlGroupSyncToLocalStorage = (args: UseControlGroupSyncToLocalStorageArgs) => { + controlGroupInput: ControlGroupInput; + setControlGroupInput: Dispatch>; + getStoredControlGroupInput: () => ControlGroupInput | undefined; +}; + +export const useControlGroupSyncToLocalStorage: UseControlGroupSyncToLocalStorage = ({ + storageKey, + shouldSync, +}) => { + const storage = useRef(new Storage(localStorage)); + + const [controlGroupInput, setControlGroupInput] = useState( + () => (storage.current.get(storageKey) as ControlGroupInput) ?? undefined + ); + + useEffect(() => { + if (shouldSync && controlGroupInput) { + storage.current.set(storageKey, controlGroupInput); + } + }, [shouldSync, controlGroupInput, storageKey]); + + const getStoredControlGroupInput = () => { + return (storage.current.get(storageKey) as ControlGroupInput) ?? undefined; + }; + + return { + controlGroupInput, + setControlGroupInput, + getStoredControlGroupInput, + }; +}; diff --git a/x-pack/plugins/security_solution/public/common/components/filter_group/hooks/use_filter_update_to_url_sync.ts b/x-pack/plugins/security_solution/public/common/components/filter_group/hooks/use_filter_update_to_url_sync.ts index 642a382661804..d4c14605b1027 100644 --- a/x-pack/plugins/security_solution/public/common/components/filter_group/hooks/use_filter_update_to_url_sync.ts +++ b/x-pack/plugins/security_solution/public/common/components/filter_group/hooks/use_filter_update_to_url_sync.ts @@ -5,17 +5,15 @@ * 2.0. */ -import type { - ControlGroupInput, - ControlPanelState, - OptionsListEmbeddableInput, -} from '@kbn/controls-plugin/common'; +import type { ControlGroupInput } from '@kbn/controls-plugin/common'; +import { ViewMode } from '@kbn/embeddable-plugin/public'; import { useEffect, useMemo } from 'react'; import { useDispatch } from 'react-redux'; import { formatPageFilterSearchParam } from '../../../../../common/utils/format_page_filter_search_param'; import { URL_PARAM_KEY } from '../../../hooks/use_url_state'; import { updateUrlParam } from '../../../store/global_url_param/actions'; import type { FilterItemObj } from '../types'; +import { getFilterItemObjListFromControlInput } from '../utils'; export interface UseFilterUrlSyncParams { controlGroupInput: ControlGroupInput | undefined; @@ -26,28 +24,17 @@ export const useFilterUpdatesToUrlSync = ({ controlGroupInput }: UseFilterUrlSyn const formattedFilters: FilterItemObj[] | undefined = useMemo(() => { if (!controlGroupInput) return; - const { panels } = controlGroupInput; - return Object.keys(panels).map((panelId) => { - const { - explicitInput: { fieldName, selectedOptions, title, existsSelected, exclude }, - } = panels[panelId] as ControlPanelState; - return { - fieldName: fieldName as string, - selectedOptions: selectedOptions ?? [], - title, - existsSelected, - exclude, - }; - }); + return getFilterItemObjListFromControlInput(controlGroupInput); }, [controlGroupInput]); useEffect(() => { if (!formattedFilters) return; + if (controlGroupInput?.viewMode !== ViewMode.VIEW) return; dispatch( updateUrlParam({ key: URL_PARAM_KEY.pageFilter, value: formatPageFilterSearchParam(formattedFilters), }) ); - }, [formattedFilters, dispatch]); + }, [formattedFilters, dispatch, controlGroupInput]); }; diff --git a/x-pack/plugins/security_solution/public/common/components/filter_group/hooks/use_filters.ts b/x-pack/plugins/security_solution/public/common/components/filter_group/hooks/use_filters.ts index d03374930f7ba..3816d19f77d2e 100644 --- a/x-pack/plugins/security_solution/public/common/components/filter_group/hooks/use_filters.ts +++ b/x-pack/plugins/security_solution/public/common/components/filter_group/hooks/use_filters.ts @@ -6,14 +6,14 @@ */ import { useContext } from 'react'; -import { FilterContext } from '..'; +import { FilterGroupContext } from '../filter_group_context'; -export const useFilters = () => { - const context = useContext(FilterContext); +export const useFilterGroupInternalContext = () => { + const filterContext = useContext(FilterGroupContext); - if (!context) { - throw new Error('hook must be used with in FilterGroup Component'); + if (!filterContext) { + throw new Error('FilterContext should only be used inside FilterGroup Wrapper'); } - return context; + return filterContext; }; diff --git a/x-pack/plugins/security_solution/public/common/components/filter_group/hooks/use_view_edit_mode.ts b/x-pack/plugins/security_solution/public/common/components/filter_group/hooks/use_view_edit_mode.ts new file mode 100644 index 0000000000000..52464a04ff465 --- /dev/null +++ b/x-pack/plugins/security_solution/public/common/components/filter_group/hooks/use_view_edit_mode.ts @@ -0,0 +1,67 @@ +/* + * 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 type { ControlGroupContainer } from '@kbn/controls-plugin/public'; +import { ViewMode } from '@kbn/embeddable-plugin/public'; +import { useCallback, useEffect, useState } from 'react'; + +interface UseViewEditModeArgs { + controlGroup: ControlGroupContainer | undefined; + initialMode?: ViewMode; +} + +export const useViewEditMode = ({ + controlGroup, + initialMode = ViewMode.VIEW, +}: UseViewEditModeArgs) => { + const [filterGroupMode, setFilterGroupMode] = useState(initialMode); + + const [hasPendingChanges, setHasPendingChanges] = useState(false); + const [pendingChangesPopoverOpen, setPendingChangesPopoverOpen] = useState(false); + + useEffect(() => { + if (controlGroup && controlGroup.getInput().viewMode !== filterGroupMode) { + controlGroup.updateInput({ viewMode: filterGroupMode }); + } + }, [controlGroup, filterGroupMode]); + + useEffect(() => { + setPendingChangesPopoverOpen(hasPendingChanges); + }, [hasPendingChanges]); + + const closePendingChangesPopover = useCallback(() => { + setPendingChangesPopoverOpen(false); + }, []); + + const openPendingChangesPopover = useCallback(() => { + if (hasPendingChanges) setPendingChangesPopoverOpen(true); + }, [hasPendingChanges]); + + const switchToEditMode = useCallback(() => { + controlGroup?.updateInput({ viewMode: ViewMode.EDIT }); + setFilterGroupMode(ViewMode.EDIT); + }, [controlGroup]); + + const switchToViewMode = useCallback(() => { + controlGroup?.updateInput({ viewMode: ViewMode.VIEW }); + setHasPendingChanges(false); + setFilterGroupMode(ViewMode.VIEW); + }, [controlGroup]); + + const isViewMode = filterGroupMode === ViewMode.VIEW; + + return { + isViewMode, + hasPendingChanges, + pendingChangesPopoverOpen, + closePendingChangesPopover, + openPendingChangesPopover, + switchToEditMode, + switchToViewMode, + setHasPendingChanges, + }; +}; diff --git a/x-pack/plugins/security_solution/public/common/components/filter_group/index.tsx b/x-pack/plugins/security_solution/public/common/components/filter_group/index.tsx index be736952a5d34..1a0347424f20d 100644 --- a/x-pack/plugins/security_solution/public/common/components/filter_group/index.tsx +++ b/x-pack/plugins/security_solution/public/common/components/filter_group/index.tsx @@ -5,46 +5,44 @@ * 2.0. */ +import type { Filter } from '@kbn/es-query'; import type { ControlGroupInput, controlGroupInputBuilder, ControlGroupOutput, - OptionsListEmbeddableInput, ControlGroupContainer, + ControlGroupRendererProps, } from '@kbn/controls-plugin/public'; -import { i18n } from '@kbn/i18n'; import { LazyControlGroupRenderer } from '@kbn/controls-plugin/public'; import type { PropsWithChildren } from 'react'; -import React, { createContext, useCallback, useEffect, useState, useRef, useMemo } from 'react'; +import React, { useCallback, useEffect, useState, useRef, useMemo } from 'react'; import { ViewMode } from '@kbn/embeddable-plugin/public'; -import { - EuiButtonIcon, - EuiContextMenuItem, - EuiContextMenuPanel, - EuiFlexGroup, - EuiFlexItem, - EuiPopover, -} from '@elastic/eui'; +import { EuiFlexGroup, EuiFlexItem, EuiSpacer } from '@elastic/eui'; import type { Subscription } from 'rxjs'; import styled from 'styled-components'; -import { cloneDeep, debounce } from 'lodash'; +import { cloneDeep, debounce, isEqual } from 'lodash'; import { withSuspense } from '@kbn/shared-ux-utility'; -import useLocalStorage from 'react-use/lib/useLocalStorage'; import { useInitializeUrlParam } from '../../utils/global_query_string'; import { URL_PARAM_KEY } from '../../hooks/use_url_state'; -import type { FilterContextType, FilterGroupProps, FilterItemObj } from './types'; +import type { FilterGroupProps, FilterItemObj } from './types'; import { useFilterUpdatesToUrlSync } from './hooks/use_filter_update_to_url_sync'; import { APP_ID } from '../../../../common/constants'; import './index.scss'; import { FilterGroupLoading } from './loading'; import { withSpaceId } from '../with_space_id'; +import { NUM_OF_CONTROLS } from './config'; +import { useControlGroupSyncToLocalStorage } from './hooks/use_control_group_sync_to_local_storage'; +import { useViewEditMode } from './hooks/use_view_edit_mode'; +import { FilterGroupContextMenu } from './context_menu'; +import { AddControl, DiscardChanges, SaveControls } from './buttons'; +import { getFilterItemObjListFromControlInput } from './utils'; +import { FiltersChangedBanner } from './filters_changed_banner'; +import { FilterGroupContext } from './filter_group_context'; type ControlGroupBuilder = typeof controlGroupInputBuilder; const ControlGroupRenderer = withSuspense(LazyControlGroupRenderer); -export const FilterContext = createContext(undefined); - const FilterWrapper = styled.div.attrs((props) => ({ className: props.className, }))` @@ -80,28 +78,56 @@ const FilterGroupComponent = (props: PropsWithChildren) => { [spaceId] ); - const [controlGroupInputUpdates, setControlGroupInputUpdates] = useLocalStorage< - ControlGroupInput | undefined - >(localStoragePageFilterKey, undefined); + const currentFiltersRef = useRef(); - const [initialUrlParam, setInitialUrlParam] = useState(); + const { + isViewMode, + hasPendingChanges, + pendingChangesPopoverOpen, + closePendingChangesPopover, + openPendingChangesPopover, + switchToViewMode, + switchToEditMode, + setHasPendingChanges, + } = useViewEditMode({ + controlGroup, + }); - const urlDataApplied = useRef(false); + const { + controlGroupInput: controlGroupInputUpdates, + setControlGroupInput: setControlGroupInputUpdates, + getStoredControlGroupInput: getStoredControlInput, + } = useControlGroupSyncToLocalStorage({ + storageKey: localStoragePageFilterKey, + shouldSync: isViewMode, + }); - const [isContextMenuVisible, setIsContextMenuVisible] = useState(false); + const [initialUrlParam, setInitialUrlParam] = useState(); - const toggleContextMenu = useCallback(() => { - setIsContextMenuVisible((prev) => !prev); - }, []); + const [showFiltersChangedBanner, setShowFiltersChangedBanner] = useState(false); + + const urlDataApplied = useRef(false); const onUrlParamInit = (param: FilterItemObj[] | null) => { - if (param == null) return; + if (!param) { + setInitialUrlParam([]); + return; + } try { + const storedControlGroupInput = getStoredControlInput(); + if (storedControlGroupInput) { + const panelsFormatted = getFilterItemObjListFromControlInput(storedControlGroupInput); + if (!isEqual(panelsFormatted, param)) { + setShowFiltersChangedBanner(true); + switchToEditMode(); + } + } setInitialUrlParam(param); } catch (err) { // if there is an error ignore url Param // eslint-disable-next-line no-console console.error(err); + setInitialUrlParam([]); } }; @@ -109,12 +135,9 @@ const FilterGroupComponent = (props: PropsWithChildren) => { useEffect(() => { const cleanup = () => { - if (filterChangedSubscription.current) { - filterChangedSubscription.current.unsubscribe(); - } - if (inputChangedSubscription.current) { - inputChangedSubscription.current.unsubscribe(); - } + [filterChangedSubscription.current, inputChangedSubscription.current].forEach((sub) => { + if (sub) sub.unsubscribe(); + }); }; return cleanup; }, []); @@ -128,9 +151,22 @@ const FilterGroupComponent = (props: PropsWithChildren) => { }); }, [timeRange, filters, query, chainingSystem, controlGroup]); + const handleInputUpdates = useCallback( + (newInput: ControlGroupInput) => { + if (isEqual(getStoredControlInput(), newInput)) return; + if (!isEqual(newInput.panels, getStoredControlInput()?.panels) && !isViewMode) { + setHasPendingChanges(true); + } + setControlGroupInputUpdates(newInput); + }, + [setControlGroupInputUpdates, getStoredControlInput, isViewMode, setHasPendingChanges] + ); + const handleFilterUpdates = useCallback( ({ filters: newFilters }: ControlGroupOutput) => { + if (isEqual(currentFiltersRef.current, newFilters)) return; if (onFilterChange) onFilterChange(newFilters ?? []); + currentFiltersRef.current = newFilters ?? []; }, [onFilterChange] ); @@ -140,29 +176,23 @@ const FilterGroupComponent = (props: PropsWithChildren) => { [handleFilterUpdates] ); - const handleInputUpdates = useCallback( - (newInput: ControlGroupInput) => { - setControlGroupInputUpdates(newInput); - }, - [setControlGroupInputUpdates] - ); - - const debouncedInputUpdatesHandler = useMemo( - () => debounce(handleInputUpdates, 500), - [handleInputUpdates] - ); - useEffect(() => { if (!controlGroup) return; - controlGroup.reload(); filterChangedSubscription.current = controlGroup.getOutput$().subscribe({ next: debouncedFilterUpdates, }); inputChangedSubscription.current = controlGroup.getInput$().subscribe({ - next: debouncedInputUpdatesHandler, + next: handleInputUpdates, }); - }, [controlGroup, debouncedFilterUpdates, debouncedInputUpdatesHandler]); + + const cleanup = () => { + [filterChangedSubscription.current, inputChangedSubscription.current].forEach((sub) => { + if (sub) sub.unsubscribe(); + }); + }; + return cleanup; + }, [controlGroup, debouncedFilterUpdates, handleInputUpdates]); const onControlGroupLoadHandler = useCallback( (controlGroupContainer: ControlGroupContainer) => { @@ -183,32 +213,28 @@ const FilterGroupComponent = (props: PropsWithChildren) => { * */ const localInitialControls = cloneDeep(initialControls); + const resultControls = cloneDeep(initialControls); let overridingControls = initialUrlParam; - if (!initialUrlParam && controlGroupInputUpdates) { + if (!initialUrlParam || initialUrlParam.length === 0) { // if nothing is found in URL Param.. read from local storage - const urlParamsFromLocalStorage: FilterItemObj[] = Object.keys( - controlGroupInputUpdates?.panels - ).map((panelIdx) => { - const panel = controlGroupInputUpdates?.panels[panelIdx]; - - const { fieldName, title, selectedOptions, existsSelected, exclude } = - panel.explicitInput as OptionsListEmbeddableInput; - return { - fieldName, - title, - selectedOptions, - existsSelected, - exclude, - }; - }); + const storedControlGroupInput = getStoredControlInput(); + if (storedControlGroupInput) { + const urlParamsFromLocalStorage: FilterItemObj[] = + getFilterItemObjListFromControlInput(storedControlGroupInput); - overridingControls = urlParamsFromLocalStorage; + overridingControls = urlParamsFromLocalStorage; + } } + if (!overridingControls || overridingControls.length === 0) return initialControls; + // if initialUrlParam Exists... replace localInitialControls with what was provided in the Url if (overridingControls && !urlDataApplied.current) { - let maxInitialControlIdx = localInitialControls.length - 1; + let maxInitialControlIdx = Math.max( + localInitialControls.length - 1, + (overridingControls?.length ?? 1) - 1 + ); for (let counter = overridingControls.length - 1; counter >= 0; counter--) { const urlControl = overridingControls[counter]; const idx = localInitialControls.findIndex( @@ -217,7 +243,7 @@ const FilterGroupComponent = (props: PropsWithChildren) => { if (idx !== -1) { // if index found, replace that with what was provided in the Url - localInitialControls[idx] = { + resultControls[idx] = { ...localInitialControls[idx], fieldName: urlControl.fieldName, title: urlControl.title ?? urlControl.fieldName, @@ -229,7 +255,7 @@ const FilterGroupComponent = (props: PropsWithChildren) => { // if url param is not available in initialControl, start replacing the last slot in the // initial Control with the last `not found` element in the Url Param // - localInitialControls[maxInitialControlIdx] = { + resultControls[maxInitialControlIdx] = { fieldName: urlControl.fieldName, selectedOptions: urlControl.selectedOptions ?? [], title: urlControl.title ?? urlControl.fieldName, @@ -241,10 +267,10 @@ const FilterGroupComponent = (props: PropsWithChildren) => { } } - return localInitialControls; - }, [initialUrlParam, initialControls, controlGroupInputUpdates]); + return resultControls; + }, [initialUrlParam, initialControls, getStoredControlInput]); - const setOptions = useCallback( + const getCreationOptions: ControlGroupRendererProps['getCreationOptions'] = useCallback( async ( defaultInput: Partial, { addOptionsListControl }: ControlGroupBuilder @@ -277,7 +303,18 @@ const FilterGroupComponent = (props: PropsWithChildren) => { }); }); - return { initialInput }; + return { + initialInput, + settings: { + showAddButton: false, + staticDataViewId: dataViewId ?? '', + editorConfig: { + hideWidthSettings: true, + hideDataViewSelector: true, + hideAdditionalSettings: true, + }, + }, + }; }, [dataViewId, timeRange, filters, chainingSystem, query, selectControlsWithPriority] ); @@ -286,86 +323,81 @@ const FilterGroupComponent = (props: PropsWithChildren) => { controlGroupInput: controlGroupInputUpdates, }); - const withContextMenuAction = useCallback( - (fn: unknown) => { - return () => { - if (typeof fn === 'function') { - fn(); - } - toggleContextMenu(); - }; - }, - [toggleContextMenu] - ); - - const resetSelection = useCallback(() => { - if (!controlGroupInputUpdates) return; - - const { panels } = controlGroupInputUpdates; - Object.values(panels).forEach((control, idx) => { - controlGroup?.updateInputForChild(String(idx), { - ...control.explicitInput, - selectedOptions: initialControls[idx].selectedOptions ?? [], - existsSelected: false, - exclude: false, - title: initialControls[idx].title ?? initialControls[idx].fieldName, - fieldName: initialControls[idx].fieldName, + const discardChangesHandler = useCallback(() => { + if (hasPendingChanges) { + controlGroup?.updateInput({ + panels: getStoredControlInput()?.panels, }); - }); - controlGroup?.reload(); - }, [controlGroupInputUpdates, controlGroup, initialControls]); - - const resetButton = useMemo( - () => ( - - {`Reset`} - - ), - [withContextMenuAction, resetSelection] - ); + } + switchToViewMode(); + setShowFiltersChangedBanner(false); + }, [controlGroup, switchToViewMode, getStoredControlInput, hasPendingChanges]); + + const saveChangesHandler = useCallback(() => { + switchToViewMode(); + setShowFiltersChangedBanner(false); + }, [switchToViewMode]); - const contextMenuItems = useMemo(() => [resetButton], [resetButton]); + const addControlsHandler = useCallback(() => { + controlGroup?.openAddDataControlFlyout(); + }, [controlGroup]); return ( - - - - - {!controlGroup ? : null} - - - + + + {Array.isArray(initialUrlParam) ? ( + + - } - isOpen={isContextMenuVisible} - closePopover={toggleContextMenu} - panelPaddingSize="none" - anchorPosition="downLeft" - > - - - - - {props.children} - + {!controlGroup ? : null} + + ) : null} + {!isViewMode && + (Object.keys(controlGroupInputUpdates?.panels ?? {}).length > NUM_OF_CONTROLS.MIN || + Object.keys(controlGroupInputUpdates?.panels ?? {}).length < NUM_OF_CONTROLS.MAX) ? ( + <> + + + + + + + + + + + ) : null} + + + +
+ {showFiltersChangedBanner ? ( + <> + + + + ) : null} + + ); }; diff --git a/x-pack/plugins/security_solution/public/common/components/filter_group/translations.ts b/x-pack/plugins/security_solution/public/common/components/filter_group/translations.ts new file mode 100644 index 0000000000000..081407f2bed1f --- /dev/null +++ b/x-pack/plugins/security_solution/public/common/components/filter_group/translations.ts @@ -0,0 +1,72 @@ +/* + * 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 { i18n } from '@kbn/i18n'; + +export const PENDING_CHANGES_REMINDER = i18n.translate( + 'xpack.securitySolution.filtersGroup.pendingChanges', + { + defaultMessage: 'Save pending changes', + } +); + +export const EDIT_CONTROLS = i18n.translate( + 'xpack.securitySolution.filtersGroup.contextMenu.editControls', + { + defaultMessage: 'Edit Controls', + } +); + +export const SAVE_CONTROLS = i18n.translate( + 'xpack.securitySolution.filtersGroup.contextMenu.saveControls', + { + defaultMessage: 'Save Controls', + } +); + +export const DISCARD_CHANGES = i18n.translate( + 'xpack.securitySolution.filtersGroup.discardChanges', + { + defaultMessage: 'Discard Changes', + } +); + +export const FILTER_GROUP_MENU = i18n.translate( + 'xpack.securitySolution.filterGroup.groupMenuTitle', + { + defaultMessage: 'Filter group menu', + } +); + +export const FILTER_GROUP_BANNER_TITLE = i18n.translate( + 'xpack.securitySolution.filterGroup.filtersChangedBanner', + { + defaultMessage: 'Filter Controls have changed', + } +); + +export const FILTER_GROUP_BANNER_MESSAGE = i18n.translate( + 'xpack.securitySolution.filterGroup.filtersChangedTitle', + { + defaultMessage: `New filter controls on this page are different from what you have previously saved. You can either save the changes or discard them. + Navigating away will automatically discard these changes`, + } +); + +export const CONTEXT_MENU_RESET_TOOLTIP = i18n.translate( + 'xpack.securitySolution.filterGroup.contextMenu.resetTooltip', + { + defaultMessage: 'Reset Controls to factory settings', + } +); + +export const CONTEXT_MENU_RESET = i18n.translate( + 'xpack.securitySolution.filterGroup.contextMenu.reset', + { + defaultMessage: 'Reset Controls', + } +); diff --git a/x-pack/plugins/security_solution/public/common/components/filter_group/utils.ts b/x-pack/plugins/security_solution/public/common/components/filter_group/utils.ts new file mode 100644 index 0000000000000..6523ff61b2060 --- /dev/null +++ b/x-pack/plugins/security_solution/public/common/components/filter_group/utils.ts @@ -0,0 +1,35 @@ +/* + * 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 type { + ControlGroupInput, + ControlPanelState, + OptionsListEmbeddableInput, +} from '@kbn/controls-plugin/common'; + +export const getPanelsInOrderFromControlsInput = (controlInput: ControlGroupInput) => { + const panels = controlInput.panels; + + return Object.values(panels).sort((a, b) => a.order - b.order); +}; + +export const getFilterItemObjListFromControlInput = (controlInput: ControlGroupInput) => { + const panels = getPanelsInOrderFromControlsInput(controlInput); + return panels.map((panel) => { + const { + explicitInput: { fieldName, selectedOptions, title, existsSelected, exclude }, + } = panel as ControlPanelState; + + return { + fieldName: fieldName as string, + selectedOptions: selectedOptions ?? [], + title, + existsSelected: existsSelected ?? false, + exclude: exclude ?? false, + }; + }); +}; diff --git a/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_by_type_panel/alerts_by_type.test.tsx b/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_by_type_panel/alerts_by_type.test.tsx index a3e2638b61db1..3e8c7b6c009c8 100644 --- a/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_by_type_panel/alerts_by_type.test.tsx +++ b/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_by_type_panel/alerts_by_type.test.tsx @@ -8,7 +8,8 @@ import { act, render } from '@testing-library/react'; import React from 'react'; import { TestProviders } from '../../../../common/mock'; import { AlertsByType } from './alerts_by_type'; -import { parsedAlerts } from './mock_data'; +import { parsedAlerts } from './mock_type_data'; +import { useIsExperimentalFeatureEnabled } from '../../../../common/hooks/use_experimental_features'; const display = 'alerts-by-type-palette-display'; @@ -19,6 +20,9 @@ jest.mock('react-router-dom', () => { return { ...actual, useLocation: jest.fn().mockReturnValue({ pathname: '' }) }; }); +const mockUseIsExperimentalFeatureEnabled = useIsExperimentalFeatureEnabled as jest.Mock; +jest.mock('../../../../common/hooks/use_experimental_features'); + describe('Alert by type chart', () => { const defaultProps = { data: [], @@ -30,70 +34,139 @@ describe('Alert by type chart', () => { jest.restoreAllMocks(); }); - test('renders health and pallette display correctly without data', () => { - act(() => { - const { container } = render( - - - - ); - expect(container.querySelector(`[data-test-subj="${display}"]`)).toBeInTheDocument(); - expect(container.querySelector(`[data-test-subj="${display}"]`)?.textContent).toContain( - 'Detection:0' - ); - expect(container.querySelector(`[data-test-subj="${display}"]`)?.textContent).toContain( - 'Prevention:0' - ); + describe('isAlertTypeEnabled flag is true', () => { + beforeEach(() => { + mockUseIsExperimentalFeatureEnabled.mockReturnValue(true); }); - }); - test('renders table correctly without data', () => { - act(() => { - const { container } = render( - - - - ); - expect( - container.querySelector('[data-test-subj="alerts-by-type-table"]') - ).toBeInTheDocument(); - expect( - container.querySelector('[data-test-subj="alerts-by-type-table"] tbody')?.textContent - ).toEqual('No items found'); + test('renders health and pallette display correctly without data', () => { + act(() => { + const { container } = render( + + + + ); + expect(container.querySelector(`[data-test-subj="${display}"]`)).toBeInTheDocument(); + expect(container.querySelector(`[data-test-subj="${display}"]`)?.textContent).toContain( + 'Detection:0' + ); + expect(container.querySelector(`[data-test-subj="${display}"]`)?.textContent).toContain( + 'Prevention:0' + ); + }); + }); + + test('renders table correctly without data', () => { + act(() => { + const { container } = render( + + + + ); + expect( + container.querySelector('[data-test-subj="alerts-by-type-table"]') + ).toBeInTheDocument(); + expect( + container.querySelector('[data-test-subj="alerts-by-type-table"] tbody')?.textContent + ).toEqual('No items found'); + }); }); - }); - test('renders health and pallette display correctly with data', () => { - act(() => { - const { container } = render( - - - - ); - expect(container.querySelector(`[data-test-subj="${display}"]`)).toBeInTheDocument(); - expect(container.querySelector(`[data-test-subj="${display}"]`)?.textContent).toContain( - 'Detection:583' - ); - expect(container.querySelector(`[data-test-subj="${display}"]`)?.textContent).toContain( - 'Prevention:6' - ); + test('renders health and pallette display correctly with data', () => { + act(() => { + const { container } = render( + + + + ); + expect(container.querySelector(`[data-test-subj="${display}"]`)).toBeInTheDocument(); + expect(container.querySelector(`[data-test-subj="${display}"]`)?.textContent).toContain( + 'Detection:583' + ); + expect(container.querySelector(`[data-test-subj="${display}"]`)?.textContent).toContain( + 'Prevention:6' + ); + }); + }); + + test('renders table correctly with data', () => { + act(() => { + const { queryAllByRole } = render( + + + + ); + + parsedAlerts.forEach((_, i) => { + expect(queryAllByRole('row')[i + 1].textContent).toContain(parsedAlerts[i].rule); + expect(queryAllByRole('row')[i + 1].textContent).toContain(parsedAlerts[i].type); + expect(queryAllByRole('row')[i + 1].textContent).toContain( + parsedAlerts[i].value.toString() + ); + }); + }); }); }); - test('renders table correctly with data', () => { - act(() => { - const { queryAllByRole } = render( - - - - ); - - parsedAlerts.forEach((_, i) => { - expect(queryAllByRole('row')[i + 1].textContent).toContain(parsedAlerts[i].rule); - expect(queryAllByRole('row')[i + 1].textContent).toContain(parsedAlerts[i].type); - expect(queryAllByRole('row')[i + 1].textContent).toContain( - parsedAlerts[i].value.toString() + describe('isAlertTypeEnabled flag is false', () => { + beforeEach(() => { + mockUseIsExperimentalFeatureEnabled.mockReturnValue(false); + }); + + test('do not renders health and pallette display correctly without data', () => { + act(() => { + const { container } = render( + + + ); + expect(container.querySelector(`[data-test-subj="${display}"]`)).not.toBeInTheDocument(); + }); + }); + + test('renders table correctly without data', () => { + act(() => { + const { container } = render( + + + + ); + expect( + container.querySelector('[data-test-subj="alerts-by-type-table"]') + ).toBeInTheDocument(); + expect( + container.querySelector('[data-test-subj="alerts-by-type-table"] tbody')?.textContent + ).toEqual('No items found'); + }); + }); + + test('do not renders health and pallette display correctly with data', () => { + mockUseIsExperimentalFeatureEnabled.mockReturnValue(false); + act(() => { + const { container } = render( + + + + ); + expect(container.querySelector(`[data-test-subj="${display}"]`)).not.toBeInTheDocument(); + }); + }); + + test('renders table correctly with data', () => { + mockUseIsExperimentalFeatureEnabled.mockReturnValue(false); + act(() => { + const { queryAllByRole } = render( + + + + ); + + parsedAlerts.forEach((_, i) => { + expect(queryAllByRole('row')[i + 1].textContent).toContain(parsedAlerts[i].rule); + expect(queryAllByRole('row')[i + 1].textContent).toContain( + parsedAlerts[i].value.toString() + ); + }); }); }); }); diff --git a/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_by_type_panel/alerts_by_type.tsx b/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_by_type_panel/alerts_by_type.tsx index cbed898e75dc6..5208e74470fa8 100644 --- a/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_by_type_panel/alerts_by_type.tsx +++ b/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_by_type_panel/alerts_by_type.tsx @@ -21,6 +21,7 @@ import type { AlertsTypeData, AlertType } from './types'; import { FormattedCount } from '../../../../common/components/formatted_number'; import { getAlertsTypeTableColumns } from './columns'; import { ALERT_TYPE_COLOR } from './helpers'; +import { useIsExperimentalFeatureEnabled } from '../../../../common/hooks/use_experimental_features'; const Wrapper = styled.div` margin-top: -${({ theme }) => theme.eui.euiSizeM}; @@ -43,7 +44,11 @@ export interface AlertsByTypeProps { } export const AlertsByType: React.FC = ({ data, isLoading }) => { - const columns = useMemo(() => getAlertsTypeTableColumns(), []); + const isAlertTypeEnabled = useIsExperimentalFeatureEnabled('alertTypeEnabled'); + const columns = useMemo( + () => getAlertsTypeTableColumns(isAlertTypeEnabled), + [isAlertTypeEnabled] + ); const subtotals = useMemo( () => @@ -92,30 +97,33 @@ export const AlertsByType: React.FC = ({ data, isLoading }) = return ( - - {(Object.keys(subtotals) as AlertType[]).map((type) => ( - - - - - -

{`${type}:`}

-
-
-
- - - - + {isAlertTypeEnabled && ( + <> + + {(Object.keys(subtotals) as AlertType[]).map((type) => ( + + + + + +

{`${type}:`}

+
+
+
+ + + + + +
-
-
- ))} - -
- - - + ))} + +
+ + + + )} > => [ +export const getAlertsTypeTableColumns = ( + isAlertTypeEnabled: boolean +): Array> => [ { field: 'rule', name: ALERTS_HEADERS_RULE_NAME, @@ -42,35 +44,39 @@ export const getAlertsTypeTableColumns = (): Array ), }, - { - field: 'type', - name: i18n.ALERTS_TYPE_COLUMN_TITLE, - 'data-test-subj': 'detectionsTable-type', - truncateText: true, - render: (type: string) => { - return ( - - - - {ALERT_TYPE_LABEL[type as AlertType]} - - - - ); - }, - width: '30%', - }, + ...(isAlertTypeEnabled + ? [ + { + field: 'type', + name: i18n.ALERTS_TYPE_COLUMN_TITLE, + 'data-test-subj': 'detectionsTable-type', + truncateText: true, + render: (type: string) => { + return ( + + + + {ALERT_TYPE_LABEL[type as AlertType]} + + + + ); + }, + width: '30%', + }, + ] + : []), { field: 'value', name: COUNT_TABLE_TITLE, diff --git a/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_by_type_panel/helpers.test.tsx b/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_by_type_panel/helpers.test.tsx index 3ec44344ea44a..e0118b349e6b1 100644 --- a/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_by_type_panel/helpers.test.tsx +++ b/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_by_type_panel/helpers.test.tsx @@ -4,22 +4,39 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ -import { parseAlertsTypeData } from './helpers'; -import * as mock from './mock_data'; -import type { AlertsByTypeAgg } from './types'; +import { parseAlertsTypeData, parseAlertsRuleData } from './helpers'; +import * as mockType from './mock_type_data'; +import * as mockRule from './mock_rule_data'; +import type { AlertsByTypeAgg, AlertsByRuleAgg } from './types'; import type { AlertSearchResponse } from '../../../containers/detection_engine/alerts/types'; describe('parse alerts by type data', () => { test('parse alerts with data', () => { const res = parseAlertsTypeData( - mock.mockAlertsData as AlertSearchResponse<{}, AlertsByTypeAgg> + mockType.mockAlertsData as AlertSearchResponse<{}, AlertsByTypeAgg> ); - expect(res).toEqual(mock.parsedAlerts); + expect(res).toEqual(mockType.parsedAlerts); }); test('parse alerts without data', () => { const res = parseAlertsTypeData( - mock.mockAlertsEmptyData as AlertSearchResponse<{}, AlertsByTypeAgg> + mockType.mockAlertsEmptyData as AlertSearchResponse<{}, AlertsByTypeAgg> + ); + expect(res).toEqual([]); + }); +}); + +describe('parse alerts by rule data', () => { + test('parse alerts with data', () => { + const res = parseAlertsRuleData( + mockRule.mockAlertsData as AlertSearchResponse<{}, AlertsByRuleAgg> + ); + expect(res).toEqual(mockRule.parsedAlerts); + }); + + test('parse alerts without data', () => { + const res = parseAlertsRuleData( + mockRule.mockAlertsEmptyData as AlertSearchResponse<{}, AlertsByRuleAgg> ); expect(res).toEqual([]); }); diff --git a/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_by_type_panel/helpers.tsx b/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_by_type_panel/helpers.tsx index 55cc705cf78bb..5ad677bea154a 100644 --- a/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_by_type_panel/helpers.tsx +++ b/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_by_type_panel/helpers.tsx @@ -5,7 +5,7 @@ * 2.0. */ import { has } from 'lodash'; -import type { AlertType, AlertsByTypeAgg, AlertsTypeData } from './types'; +import type { AlertType, AlertsByTypeAgg, AlertsTypeData, AlertsByRuleAgg } from './types'; import type { AlertSearchResponse } from '../../../containers/detection_engine/alerts/types'; import type { SummaryChartsData, SummaryChartsAgg } from '../alerts_summary_charts_panel/types'; import { DETECTION, PREVENTION } from './translations'; @@ -19,10 +19,27 @@ export const ALERT_TYPE_LABEL = { Prevention: PREVENTION, }; +export const parseAlertsRuleData = ( + response: AlertSearchResponse<{}, AlertsByRuleAgg> +): AlertsTypeData[] => { + const rulesBuckets = response?.aggregations?.alertsByRule?.buckets ?? []; + + return rulesBuckets.length === 0 + ? [] + : rulesBuckets.map((rule) => { + return { + rule: rule.key, + type: 'Detection' as AlertType, + value: rule.doc_count, + color: ALERT_TYPE_COLOR.Detection, + }; + }); +}; + export const parseAlertsTypeData = ( response: AlertSearchResponse<{}, AlertsByTypeAgg> ): AlertsTypeData[] => { - const rulesBuckets = response?.aggregations?.alertsByRule?.buckets ?? []; + const rulesBuckets = response?.aggregations?.alertsByType?.buckets ?? []; return rulesBuckets.length === 0 ? [] : rulesBuckets.flatMap((rule) => { @@ -75,5 +92,11 @@ export const getIsAlertsTypeData = (data: SummaryChartsData[]): data is AlertsTy export const getIsAlertsByTypeAgg = ( data: AlertSearchResponse<{}, SummaryChartsAgg> ): data is AlertSearchResponse<{}, AlertsByTypeAgg> => { + return has(data, 'aggregations.alertsByType'); +}; + +export const getIsAlertsByRuleAgg = ( + data: AlertSearchResponse<{}, SummaryChartsAgg> +): data is AlertSearchResponse<{}, AlertsByRuleAgg> => { return has(data, 'aggregations.alertsByRule'); }; diff --git a/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_by_type_panel/index.tsx b/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_by_type_panel/index.tsx index 391057796dd7d..92d88d28ec419 100644 --- a/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_by_type_panel/index.tsx +++ b/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_by_type_panel/index.tsx @@ -13,9 +13,13 @@ import { AlertsByType } from './alerts_by_type'; import { HeaderSection } from '../../../../common/components/header_section'; import { InspectButtonContainer } from '../../../../common/components/inspect'; import { useSummaryChartData } from '../alerts_summary_charts_panel/use_summary_chart_data'; -import { alertTypeAggregations } from '../alerts_summary_charts_panel/aggregations'; +import { + alertTypeAggregations, + alertRuleAggregations, +} from '../alerts_summary_charts_panel/aggregations'; import { getIsAlertsTypeData } from './helpers'; import * as i18n from './translations'; +import { useIsExperimentalFeatureEnabled } from '../../../../common/hooks/use_experimental_features'; const ALERTS_BY_TYPE_CHART_ID = 'alerts-summary-alert_by_type'; @@ -26,10 +30,11 @@ export const AlertsByTypePanel: React.FC = ({ runtimeMappings, skip, }) => { + const isAlertTypeEnabled = useIsExperimentalFeatureEnabled('alertTypeEnabled'); const uniqueQueryId = useMemo(() => `${ALERTS_BY_TYPE_CHART_ID}-${uuid()}`, []); const { items, isLoading } = useSummaryChartData({ - aggregations: alertTypeAggregations, + aggregations: isAlertTypeEnabled ? alertTypeAggregations : alertRuleAggregations, filters, query, signalIndexName, @@ -44,9 +49,9 @@ export const AlertsByTypePanel: React.FC = ({ diff --git a/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_by_type_panel/mock_rule_data.ts b/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_by_type_panel/mock_rule_data.ts new file mode 100644 index 0000000000000..62dc3f3f885f1 --- /dev/null +++ b/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_by_type_panel/mock_rule_data.ts @@ -0,0 +1,109 @@ +/* + * 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 type { AlertsTypeData } from './types'; + +const from = '2022-04-05T12:00:00.000Z'; +const to = '2022-04-08T12:00:00.000Z'; + +export const mockAlertsData = { + took: 0, + timeout: false, + _shards: { + total: 1, + successful: 1, + skipped: 0, + failed: 0, + }, + hits: { + total: { + value: 589, + relation: 'eq', + }, + max_score: null, + hits: [], + }, + aggregations: { + alertsByRule: { + doc_count_error_upper_bound: 0, + sum_other_doc_count: 0, + buckets: [ + { + key: 'Test rule 1', + doc_count: 537, + }, + { + key: 'Test rule 2', + doc_count: 27, + }, + { + key: 'Test rule 3', + doc_count: 25, + }, + ], + }, + }, +}; + +export const mockAlertsEmptyData = { + took: 0, + timeout: false, + _shards: { + total: 1, + successful: 1, + skipped: 0, + failed: 0, + }, + hits: { + total: { + value: 0, + relation: 'eq', + }, + max_score: null, + hits: [], + }, + aggregations: { + alertsByRule: { + doc_count_error_upper_bound: 0, + sum_other_doc_count: 0, + buckets: [], + }, + }, +}; + +export const query = { + size: 0, + query: { + bool: { + filter: [ + { + bool: { + filter: [], + must: [], + must_not: [], + should: [], + }, + }, + { range: { '@timestamp': { gte: from, lte: to } } }, + ], + }, + }, + aggs: { + alertsByRule: { + terms: { + field: 'kibana.alert.rule.name', + size: 1000, + }, + }, + }, + runtime_mappings: undefined, +}; + +export const parsedAlerts: AlertsTypeData[] = [ + { rule: 'Test rule 1', type: 'Detection', value: 537, color: '#D36086' }, + { rule: 'Test rule 2', type: 'Detection', value: 27, color: '#D36086' }, + { rule: 'Test rule 3', type: 'Detection', value: 25, color: '#D36086' }, +]; diff --git a/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_by_type_panel/mock_data.ts b/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_by_type_panel/mock_type_data.ts similarity index 98% rename from x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_by_type_panel/mock_data.ts rename to x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_by_type_panel/mock_type_data.ts index e1949dd8893d5..4c9cea8e63206 100644 --- a/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_by_type_panel/mock_data.ts +++ b/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_by_type_panel/mock_type_data.ts @@ -27,7 +27,7 @@ export const mockAlertsData = { hits: [], }, aggregations: { - alertsByRule: { + alertsByType: { doc_count_error_upper_bound: 0, sum_other_doc_count: 0, buckets: [ @@ -108,7 +108,7 @@ export const mockAlertsEmptyData = { hits: [], }, aggregations: { - alertsByRule: { + alertsByType: { doc_count_error_upper_bound: 0, sum_other_doc_count: 0, buckets: [], @@ -134,7 +134,7 @@ export const query = { }, }, aggs: { - alertsByRule: { + alertsByType: { terms: { field: 'kibana.alert.rule.name', size: 1000, diff --git a/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_by_type_panel/translations.ts b/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_by_type_panel/translations.ts index 5f1c6c4ada642..66fa31c29a448 100644 --- a/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_by_type_panel/translations.ts +++ b/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_by_type_panel/translations.ts @@ -13,6 +13,13 @@ export const ALERTS_TYPE_TITLE = i18n.translate( } ); +export const ALERTS_RULE_TITLE = i18n.translate( + 'xpack.securitySolution.detectionEngine.alerts.alertsByType.alertRuleChartTitle', + { + defaultMessage: 'Alerts by name', + } +); + export const ALERTS_TYPE_COLUMN_TITLE = i18n.translate( 'xpack.securitySolution.detectionEngine.alerts.alertsByType.typeColumn', { diff --git a/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_by_type_panel/types.ts b/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_by_type_panel/types.ts index 8f87d1ed81ed4..3f1a97096cca7 100644 --- a/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_by_type_panel/types.ts +++ b/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_by_type_panel/types.ts @@ -9,13 +9,21 @@ import type { BucketItem } from '../../../../../common/search_strategy/security_ export type AlertType = 'Detection' | 'Prevention'; export interface AlertsByTypeAgg { - alertsByRule: { + alertsByType: { doc_count_error_upper_bound: number; sum_other_doc_count: number; buckets: RuleBucket[]; }; } +export interface AlertsByRuleAgg { + alertsByRule: { + doc_count_error_upper_bound: number; + sum_other_doc_count: number; + buckets: BucketItem[]; + }; +} + interface RuleBucket { key: string; doc_count: number; diff --git a/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_summary_charts_panel/aggregations.ts b/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_summary_charts_panel/aggregations.ts index 6a6eb36003748..dd646a20931f6 100644 --- a/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_summary_charts_panel/aggregations.ts +++ b/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_summary_charts_panel/aggregations.ts @@ -18,7 +18,7 @@ export const severityAggregations = { }; export const alertTypeAggregations = { - alertsByRule: { + alertsByType: { terms: { field: ALERT_RULE_NAME, size: DEFAULT_QUERY_SIZE, @@ -34,6 +34,15 @@ export const alertTypeAggregations = { }, }; +export const alertRuleAggregations = { + alertsByRule: { + terms: { + field: ALERT_RULE_NAME, + size: DEFAULT_QUERY_SIZE, + }, + }, +}; + export const alertsGroupingAggregations = (stackByField: GroupBySelection) => { return { alertsByGrouping: { diff --git a/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_summary_charts_panel/helpers.test.tsx b/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_summary_charts_panel/helpers.test.tsx index 37bd7e7c4b1a8..7ff8c949e0624 100644 --- a/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_summary_charts_panel/helpers.test.tsx +++ b/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_summary_charts_panel/helpers.test.tsx @@ -6,7 +6,8 @@ */ import { parseData } from './helpers'; import * as severityMock from '../severity_level_panel/mock_data'; -import * as alertsTypeMock from '../alerts_by_type_panel/mock_data'; +import * as alertsTypeMock from '../alerts_by_type_panel/mock_type_data'; +import * as alertsRuleMock from '../alerts_by_type_panel/mock_rule_data'; import * as alertsGroupingMock from '../alerts_progress_bar_panel/mock_data'; import type { SummaryChartsAgg } from './types'; import type { AlertSearchResponse } from '../../../containers/detection_engine/alerts/types'; @@ -17,14 +18,19 @@ describe('parse data by aggregation type', () => { expect(res).toEqual(severityMock.parsedAlerts); }); - test('parse detections data', () => { - const res = parseData( + test('parse alert type data', () => { + const resType = parseData( alertsTypeMock.mockAlertsData as AlertSearchResponse<{}, SummaryChartsAgg> ); - expect(res).toEqual(alertsTypeMock.parsedAlerts); + expect(resType).toEqual(alertsTypeMock.parsedAlerts); + + const resRule = parseData( + alertsRuleMock.mockAlertsData as AlertSearchResponse<{}, SummaryChartsAgg> + ); + expect(resRule).toEqual(alertsRuleMock.parsedAlerts); }); - test('parse host data', () => { + test('parse alert groupping data', () => { const res = parseData( alertsGroupingMock.mockAlertsData as AlertSearchResponse<{}, SummaryChartsAgg> ); diff --git a/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_summary_charts_panel/helpers.tsx b/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_summary_charts_panel/helpers.tsx index a6985bd7901f5..03f8571aa8bc0 100644 --- a/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_summary_charts_panel/helpers.tsx +++ b/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_summary_charts_panel/helpers.tsx @@ -7,7 +7,12 @@ import type { SummaryChartsAgg } from './types'; import type { AlertSearchResponse } from '../../../containers/detection_engine/alerts/types'; import { parseSeverityData, getIsAlertsBySeverityAgg } from '../severity_level_panel/helpers'; -import { parseAlertsTypeData, getIsAlertsByTypeAgg } from '../alerts_by_type_panel/helpers'; +import { + parseAlertsTypeData, + getIsAlertsByTypeAgg, + parseAlertsRuleData, + getIsAlertsByRuleAgg, +} from '../alerts_by_type_panel/helpers'; import { parseAlertsGroupingData, getIsAlertsByGroupingAgg, @@ -24,6 +29,9 @@ export const parseData = (data: AlertSearchResponse<{}, SummaryChartsAgg>) => { if (getIsAlertsByTypeAgg(data)) { return parseAlertsTypeData(data); } + if (getIsAlertsByRuleAgg(data)) { + return parseAlertsRuleData(data); + } if (getIsAlertsByGroupingAgg(data)) { return parseAlertsGroupingData(data); } diff --git a/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_summary_charts_panel/types.ts b/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_summary_charts_panel/types.ts index 8d3821ce1db62..b54518140cb0e 100644 --- a/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_summary_charts_panel/types.ts +++ b/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_summary_charts_panel/types.ts @@ -8,7 +8,11 @@ import type { MappingRuntimeFields } from '@elastic/elasticsearch/lib/api/types' import type { Filter, Query } from '@kbn/es-query'; import type { SeverityBuckets as SeverityData } from '../../../../overview/components/detection_response/alerts_by_status/types'; import type { AlertsBySeverityAgg } from '../severity_level_panel/types'; -import type { AlertsByTypeAgg, AlertsTypeData } from '../alerts_by_type_panel/types'; +import type { + AlertsByTypeAgg, + AlertsTypeData, + AlertsByRuleAgg, +} from '../alerts_by_type_panel/types'; import type { AlertsByGroupingAgg, AlertsProgressBarData, @@ -19,7 +23,7 @@ import type { } from '../../../pages/detection_engine/chart_panels/chart_collapse/types'; export type SummaryChartsAgg = Partial< - AlertsBySeverityAgg | AlertsByTypeAgg | AlertsByGroupingAgg | ChartCollapseAgg + AlertsBySeverityAgg | AlertsByTypeAgg | AlertsByGroupingAgg | ChartCollapseAgg | AlertsByRuleAgg >; export type SummaryChartsData = diff --git a/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_summary_charts_panel/use_summary_chart_data.test.tsx b/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_summary_charts_panel/use_summary_chart_data.test.tsx index e9f8916e6e3a4..f25e4d9803c8b 100644 --- a/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_summary_charts_panel/use_summary_chart_data.test.tsx +++ b/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_summary_charts_panel/use_summary_chart_data.test.tsx @@ -12,8 +12,10 @@ import type { UseAlerts, UseAlertsQueryProps } from './use_summary_chart_data'; import { useSummaryChartData, getAlertsQuery } from './use_summary_chart_data'; import * as aggregations from './aggregations'; import * as severityMock from '../severity_level_panel/mock_data'; -import * as alertTypeMock from '../alerts_by_type_panel/mock_data'; +import * as alertTypeMock from '../alerts_by_type_panel/mock_type_data'; +import * as alertRuleMock from '../alerts_by_type_panel/mock_rule_data'; import * as alertsGroupingMock from '../alerts_progress_bar_panel/mock_data'; +import { useIsExperimentalFeatureEnabled } from '../../../../common/hooks/use_experimental_features'; const from = '2022-04-05T12:00:00.000Z'; const to = '2022-04-08T12:00:00.000Z'; @@ -47,6 +49,9 @@ jest.mock('../../../../common/containers/use_global_time', () => { }; }); +const mockUseIsExperimentalFeatureEnabled = useIsExperimentalFeatureEnabled as jest.Mock; +jest.mock('../../../../common/hooks/use_experimental_features'); + describe('getAlertsQuery', () => { test('it returns the expected severity query', () => { expect( @@ -68,6 +73,14 @@ describe('getAlertsQuery', () => { aggregations: aggregations.alertTypeAggregations, }) ).toEqual(alertTypeMock.query); + expect( + getAlertsQuery({ + from, + to, + additionalFilters, + aggregations: aggregations.alertRuleAggregations, + }) + ).toEqual(alertRuleMock.query); }); test('it returns the expected alerts by grouping query', () => { @@ -177,8 +190,9 @@ describe('get summary charts data', () => { jest.clearAllMocks(); mockDateNow.mockReturnValue(dateNow); mockUseQueryAlerts.mockReturnValue(defaultUseQueryAlertsReturn); + mockUseIsExperimentalFeatureEnabled.mockReturnValue(true); }); - it('should return default values', () => { + it('should return correct default values when alertsTypeChartsEnabled is true', () => { const { result } = renderUseSummaryChartData({ aggregations: aggregations.alertTypeAggregations, }); @@ -197,7 +211,27 @@ describe('get summary charts data', () => { }); }); - it('should return parsed alerts by type items', () => { + it('should return correct default values when alertsTypeChartsEnabled is false', () => { + mockUseIsExperimentalFeatureEnabled.mockReturnValue(false); + const { result } = renderUseSummaryChartData({ + aggregations: aggregations.alertRuleAggregations, + }); + + expect(result.current).toEqual({ + items: [], + isLoading: false, + updatedAt: dateNow, + }); + + expect(mockUseQueryAlerts).toBeCalledWith({ + query: alertRuleMock.query, + indexName: 'signal-alerts', + skip: false, + queryName: ALERTS_QUERY_NAMES.COUNT, + }); + }); + + it('should return parsed alerts by type items when alertsTypeChartsEnabled is true', () => { mockUseQueryAlerts.mockReturnValue({ ...defaultUseQueryAlertsReturn, data: alertTypeMock.mockAlertsData, @@ -212,6 +246,23 @@ describe('get summary charts data', () => { updatedAt: dateNow, }); }); + + it('should return parsed alerts by type items when alertsTypeChartsEnabled is false', () => { + mockUseIsExperimentalFeatureEnabled.mockReturnValue(false); + mockUseQueryAlerts.mockReturnValue({ + ...defaultUseQueryAlertsReturn, + data: alertRuleMock.mockAlertsData, + }); + + const { result } = renderUseSummaryChartData({ + aggregations: aggregations.alertRuleAggregations, + }); + expect(result.current).toEqual({ + items: alertRuleMock.parsedAlerts, + isLoading: false, + updatedAt: dateNow, + }); + }); }); describe('get top alerts data', () => { diff --git a/x-pack/plugins/security_solution/public/detections/components/alerts_table/alerts_grouping.tsx b/x-pack/plugins/security_solution/public/detections/components/alerts_table/alerts_grouping.tsx index 0f52c0320362b..57ef10a5285e6 100644 --- a/x-pack/plugins/security_solution/public/detections/components/alerts_table/alerts_grouping.tsx +++ b/x-pack/plugins/security_solution/public/detections/components/alerts_table/alerts_grouping.tsx @@ -115,6 +115,7 @@ export const GroupedAlertsTableComponent: React.FC = groupingId: tableId, fields: indexPattern.fields, }); + const resetPagination = pagination.reset; useEffect(() => { dispatch(updateGroupSelector({ groupSelector })); @@ -138,6 +139,7 @@ export const GroupedAlertsTableComponent: React.FC = const uniqueQueryId = useMemo(() => `${ALERTS_GROUPING_ID}-${uuidv4()}`, []); const additionalFilters = useMemo(() => { + resetPagination(); try { return [ buildEsQuery(undefined, globalQuery != null ? [globalQuery] : [], [ @@ -148,7 +150,7 @@ export const GroupedAlertsTableComponent: React.FC = } catch (e) { return []; } - }, [defaultFilters, globalFilters, globalQuery]); + }, [defaultFilters, globalFilters, globalQuery, resetPagination]); const queryGroups = useMemo( () => diff --git a/x-pack/plugins/security_solution/public/detections/components/alerts_table/grouping_settings/query_builder.test.ts b/x-pack/plugins/security_solution/public/detections/components/alerts_table/grouping_settings/query_builder.test.ts index dbb1b15b8236f..b34263c521b26 100644 --- a/x-pack/plugins/security_solution/public/detections/components/alerts_table/grouping_settings/query_builder.test.ts +++ b/x-pack/plugins/security_solution/public/detections/components/alerts_table/grouping_settings/query_builder.test.ts @@ -43,19 +43,19 @@ describe('getAlertsGroupingQuery', () => { expect(groupingQuery).toStrictEqual({ _source: false, aggs: { - alertsCount: { + unitCount0: { value_count: { field: 'kibana.alert.rule.name', }, }, - groupsNumber: { + groupCount0: { cardinality: { field: 'kibana.alert.rule.name', }, }, stackByMultipleFields0: { aggs: { - alertsCount: { + unitCount0: { cardinality: { field: 'kibana.alert.uuid', }, @@ -180,19 +180,19 @@ describe('getAlertsGroupingQuery', () => { expect(groupingQuery).toStrictEqual({ _source: false, aggs: { - alertsCount: { + unitCount0: { value_count: { field: 'process.name', }, }, - groupsNumber: { + groupCount0: { cardinality: { field: 'process.name', }, }, stackByMultipleFields0: { aggs: { - alertsCount: { + unitCount0: { cardinality: { field: 'kibana.alert.uuid', }, diff --git a/x-pack/plugins/security_solution/public/detections/components/alerts_table/grouping_settings/query_builder.ts b/x-pack/plugins/security_solution/public/detections/components/alerts_table/grouping_settings/query_builder.ts index 96e4dd047769d..5b4cb882c30c8 100644 --- a/x-pack/plugins/security_solution/public/detections/components/alerts_table/grouping_settings/query_builder.ts +++ b/x-pack/plugins/security_solution/public/detections/components/alerts_table/grouping_settings/query_builder.ts @@ -43,12 +43,12 @@ export const getAlertsGroupingQuery = ({ additionalFilters, additionalAggregationsRoot: [ { - alertsCount: { value_count: { field: selectedGroup } }, + unitCount0: { value_count: { field: selectedGroup } }, }, ...(selectedGroup !== 'none' ? [ { - groupsNumber: { + groupCount0: { cardinality: { field: selectedGroup, }, @@ -72,7 +72,7 @@ export const getAlertsGroupingQuery = ({ const getAggregationsByGroupField = (field: string): NamedAggregation[] => { const aggMetrics: NamedAggregation[] = [ { - alertsCount: { + unitCount0: { cardinality: { field: 'kibana.alert.uuid', }, diff --git a/x-pack/plugins/security_solution/public/detections/components/alerts_table/grouping_settings/types.ts b/x-pack/plugins/security_solution/public/detections/components/alerts_table/grouping_settings/types.ts index 4bb336807e6cf..1b504b5454be9 100644 --- a/x-pack/plugins/security_solution/public/detections/components/alerts_table/grouping_settings/types.ts +++ b/x-pack/plugins/security_solution/public/detections/components/alerts_table/grouping_settings/types.ts @@ -9,7 +9,7 @@ import type { GenericBuckets } from '@kbn/securitysolution-grouping/src'; // Elasticsearch returns `null` when a sub-aggregation cannot be computed type NumberOrNull = number | null; export interface AlertsGroupingAggregation { - alertsCount?: { + unitCount0?: { value?: NumberOrNull; }; severitiesSubAggregation?: { diff --git a/x-pack/plugins/security_solution/public/detections/components/alerts_table/index.test.tsx b/x-pack/plugins/security_solution/public/detections/components/alerts_table/index.test.tsx index 08fe773f9fe35..c121733b6542f 100644 --- a/x-pack/plugins/security_solution/public/detections/components/alerts_table/index.test.tsx +++ b/x-pack/plugins/security_solution/public/detections/components/alerts_table/index.test.tsx @@ -188,6 +188,8 @@ const testProps: AlertsTableComponentProps = { to: '2020-07-08T08:20:18.966Z', }; +const resetPagination = jest.fn(); + describe('GroupedAlertsTable', () => { const getGrouping = jest.fn().mockReturnValue(); beforeEach(() => { @@ -201,7 +203,7 @@ describe('GroupedAlertsTable', () => { groupSelector: <>, getGrouping, selectedGroup: 'host.name', - pagination: { pageSize: 1, pageIndex: 0 }, + pagination: { pageSize: 1, pageIndex: 0, reset: resetPagination }, }); }); @@ -252,4 +254,23 @@ describe('GroupedAlertsTable', () => { ); expect(getGrouping.mock.calls[0][0].isLoading).toEqual(true); }); + + it('resets grouping pagination when global query updates', () => { + (isNoneGroup as jest.Mock).mockReturnValue(false); + const { rerender } = render( + + + + ); + // called on initial query definition + expect(resetPagination).toHaveBeenCalledTimes(1); + rerender( + + + + ); + expect(resetPagination).toHaveBeenCalledTimes(2); + }); }); diff --git a/x-pack/plugins/security_solution/public/detections/components/rules/step_define_rule/index.tsx b/x-pack/plugins/security_solution/public/detections/components/rules/step_define_rule/index.tsx index 39f04d8e54e62..01062c45693ce 100644 --- a/x-pack/plugins/security_solution/public/detections/components/rules/step_define_rule/index.tsx +++ b/x-pack/plugins/security_solution/public/detections/components/rules/step_define_rule/index.tsx @@ -555,6 +555,7 @@ const StepDefineRuleComponent: FC = ({ onChange={(id: string) => { groupByRadioSelection.setValue(id); }} + data-test-subj="groupByDurationOptions" /> ), [license, groupByFields] @@ -867,7 +868,10 @@ const StepDefineRuleComponent: FC = ({ )} - + = ({ }} /> - + { list_id: exceptionListId, type: list.type, name: listDetails.name, - description: listDetails.description || list.description, + description: listDetails.description ?? '', namespace_type: list.namespace_type, }, }); diff --git a/x-pack/plugins/security_solution/public/exceptions/pages/list_detail_view/index.tsx b/x-pack/plugins/security_solution/public/exceptions/pages/list_detail_view/index.tsx index d4e2c8f4f0977..1e5b0af5768a4 100644 --- a/x-pack/plugins/security_solution/public/exceptions/pages/list_detail_view/index.tsx +++ b/x-pack/plugins/security_solution/public/exceptions/pages/list_detail_view/index.tsx @@ -99,6 +99,7 @@ export const ListsDetailViewComponent: FC = () => { onExportList={handleExportList} onDeleteList={handleDelete} onManageRules={onManageRules} + dataTestSubj="exceptionListManagement" /> diff --git a/x-pack/plugins/security_solution/public/explore/network/components/kpi_network/mock.ts b/x-pack/plugins/security_solution/public/explore/network/components/kpi_network/mock.ts index cc30a9b920edc..4f77abd79dfd4 100644 --- a/x-pack/plugins/security_solution/public/explore/network/components/kpi_network/mock.ts +++ b/x-pack/plugins/security_solution/public/explore/network/components/kpi_network/mock.ts @@ -149,7 +149,7 @@ export const mockEnableChartsData = { from: '2019-06-15T06:00:00.000Z', id: 'statItem', loading: false, - statKey: 'UniqueIps', + statKey: 'uniqueIps', setQuerySkip: jest.fn(), to: '2019-06-18T06:00:00.000Z', updateDateRange: mockUpdateDateRange, diff --git a/x-pack/plugins/security_solution/public/explore/network/components/kpi_network/unique_private_ips/index.tsx b/x-pack/plugins/security_solution/public/explore/network/components/kpi_network/unique_private_ips/index.tsx index 1f1e8e5d63724..e4e9b7fc7a0b8 100644 --- a/x-pack/plugins/security_solution/public/explore/network/components/kpi_network/unique_private_ips/index.tsx +++ b/x-pack/plugins/security_solution/public/explore/network/components/kpi_network/unique_private_ips/index.tsx @@ -31,7 +31,7 @@ const euiColorVis3 = euiVisColorPalette[3]; export const fieldsMapping: Readonly = [ { - key: 'UniqueIps', + key: 'uniqueIps', fields: [ { key: 'uniqueSourcePrivateIps', diff --git a/x-pack/plugins/security_solution/public/management/components/endpoint_execute_action/execute_action_host_response_output.test.tsx b/x-pack/plugins/security_solution/public/management/components/endpoint_execute_action/execute_action_host_response_output.test.tsx index 835e816f85f47..83c4c69b9fe1c 100644 --- a/x-pack/plugins/security_solution/public/management/components/endpoint_execute_action/execute_action_host_response_output.test.tsx +++ b/x-pack/plugins/security_solution/public/management/components/endpoint_execute_action/execute_action_host_response_output.test.tsx @@ -17,22 +17,15 @@ import { ExecuteActionHostResponseOutput, type ExecuteActionHostResponseOutputProps, } from './execute_action_host_response_output'; -import { responseActionsHttpMocks } from '../../mocks/response_actions_http_mocks'; -import { getDeferred } from '../../mocks/utils'; -import { waitFor } from '@testing-library/react'; -import type { IHttpFetchError } from '@kbn/core-http-browser'; describe('When using the `ExecuteActionHostResponseOutput` component', () => { let render: () => ReturnType; let renderResult: ReturnType; let renderProps: ExecuteActionHostResponseOutputProps; - let apiMocks: ReturnType; beforeEach(() => { const appTestContext = createAppRootMockRenderer(); - apiMocks = responseActionsHttpMocks(appTestContext.coreStart.http); - renderProps = { action: new EndpointActionGenerator('seed').generateActionDetails< ResponseActionExecuteOutputContent, @@ -52,27 +45,12 @@ describe('When using the `ExecuteActionHostResponseOutput` component', () => { expect(renderResult.getByTestId('test')).toBeTruthy(); }); - it('should show loading when details are fetching', async () => { - (renderProps.action as ActionDetails).outputs = {}; - - const deferred = getDeferred(); - - apiMocks.responseProvider.actionDetails.mockDelay.mockReturnValue(deferred.promise); - (renderProps.action as ActionDetails).completedAt = '2021-04-15T16:08:47.449Z'; - + it('should show execute output as `open`', async () => { render(); - expect(renderResult.getByTestId('test-loading')).toBeTruthy(); - - // Release the `action details` api - deferred.resolve(); - - await waitFor(() => { - expect(apiMocks.responseProvider.actionDetails).toHaveBeenCalledWith({ - path: '/api/endpoint/action/123', - }); - }); - expect(renderResult.queryByTestId('test-loading')).toBeNull(); - expect(renderResult.getByTestId('test')).toBeTruthy(); + const accordionOutputButton = Array.from( + renderResult.getByTestId('test').querySelectorAll('.euiAccordion') + )[0]; + expect(accordionOutputButton.className).toContain('isOpen'); }); it('should show nothing when no output in action details', () => { @@ -80,20 +58,4 @@ describe('When using the `ExecuteActionHostResponseOutput` component', () => { render(); expect(renderResult.queryByTestId('test')).toBeNull(); }); - - it('should handle API error', async () => { - (renderProps.action as ActionDetails).outputs = {}; - const error = { message: 'server error', response: { status: 500 } } as IHttpFetchError; - - (renderProps.action as ActionDetails).completedAt = '2021-04-15T16:08:47.449Z'; - apiMocks.responseProvider.actionDetails.mockImplementation(() => { - throw error; - }); - - render(); - - await waitFor(() => { - expect(renderResult.getByTestId('test-apiError')).toHaveTextContent('server error'); - }); - }); }); diff --git a/x-pack/plugins/security_solution/public/management/components/endpoint_execute_action/execute_action_host_response_output.tsx b/x-pack/plugins/security_solution/public/management/components/endpoint_execute_action/execute_action_host_response_output.tsx index 959c6ad0ce5e0..15d05e1d5c847 100644 --- a/x-pack/plugins/security_solution/public/management/components/endpoint_execute_action/execute_action_host_response_output.tsx +++ b/x-pack/plugins/security_solution/public/management/components/endpoint_execute_action/execute_action_host_response_output.tsx @@ -4,24 +4,15 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ -import React, { memo, useEffect, useMemo, useState } from 'react'; -import { - EuiAccordion, - EuiFlexItem, - EuiSkeletonText, - EuiSpacer, - EuiText, - useGeneratedHtmlId, -} from '@elastic/eui'; +import React, { memo, useMemo } from 'react'; +import { EuiAccordion, EuiFlexItem, EuiSpacer, EuiText, useGeneratedHtmlId } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; -import { useIsMounted } from '@kbn/securitysolution-hook-utils'; -import { useGetActionDetails } from '../../hooks/response_actions/use_get_action_details'; + import type { ActionDetails, MaybeImmutable, ResponseActionExecuteOutputContent, } from '../../../../common/endpoint/types'; -import { FormattedError } from '../formatted_error'; const ACCORDION_BUTTON_TEXT = Object.freeze({ output: { @@ -98,7 +89,6 @@ export interface ExecuteActionHostResponseOutputProps { export const ExecuteActionHostResponseOutput = memo( ({ action, agentId = action.agents[0], 'data-test-subj': dataTestSubj, textSize = 'xs' }) => { - const isMounted = useIsMounted(); const outputContent = useMemo( () => action.outputs && @@ -107,61 +97,23 @@ export const ExecuteActionHostResponseOutput = memo(outputContent); - - useEffect(() => { - if ( - isMounted() && - isFetched && - actionDetails && - actionDetails.data && - actionDetails.data.outputs && - actionDetails.data.outputs[agentId] - ) { - setExecuteOutputContent( - actionDetails.data.outputs[agentId].content as ResponseActionExecuteOutputContent - ); - } - - return () => { - setExecuteOutputContent(undefined); - }; - }, [actionDetails, agentId, isFetched, isMounted]); - - if (isFetching && !executeOutputContent) { - return ( - - ); - } - - if (error) { - return ; + if (!outputContent) { + return <>; } return ( diff --git a/x-pack/plugins/security_solution/public/management/components/endpoint_response_actions_list/components/actions_log_table.tsx b/x-pack/plugins/security_solution/public/management/components/endpoint_response_actions_list/components/actions_log_table.tsx index 09767754dc7fb..51af5a943cc98 100644 --- a/x-pack/plugins/security_solution/public/management/components/endpoint_response_actions_list/components/actions_log_table.tsx +++ b/x-pack/plugins/security_solution/public/management/components/endpoint_response_actions_list/components/actions_log_table.tsx @@ -4,7 +4,7 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ -import React, { memo, useCallback, useMemo, useState } from 'react'; +import React, { memo, useCallback, useEffect, useMemo, useState } from 'react'; import type { CriteriaWithPagination } from '@elastic/eui'; import { @@ -46,6 +46,197 @@ const StyledFacetButton = euiStyled(EuiFacetButton)` } `; +interface ExpandedRowMapType { + [k: string]: React.ReactNode; +} + +const getResponseActionListTableColumns = ({ + getTestId, + itemIdToExpandedRowMap, + showHostNames, + onClickCallback, +}: { + getTestId: (suffix?: string | undefined) => string | undefined; + itemIdToExpandedRowMap: ExpandedRowMapType; + showHostNames: boolean; + onClickCallback: (actionListDataItem: ActionListApiResponse['data'][number]) => () => void; +}) => { + const columns = [ + { + field: 'startedAt', + name: TABLE_COLUMN_NAMES.time, + width: !showHostNames ? '21%' : '15%', + truncateText: true, + render: (startedAt: ActionListApiResponse['data'][number]['startedAt']) => { + return ( + + ); + }, + }, + { + field: 'command', + name: TABLE_COLUMN_NAMES.command, + width: !showHostNames ? '21%' : '10%', + truncateText: true, + render: (_command: ActionListApiResponse['data'][number]['command']) => { + const command = getUiCommand(_command); + return ( + + + {command} + + + ); + }, + }, + { + field: 'createdBy', + name: TABLE_COLUMN_NAMES.user, + width: !showHostNames ? '21%' : '14%', + truncateText: true, + render: (userId: ActionListApiResponse['data'][number]['createdBy']) => { + return ( + + } + > + + + {userId} + + + + ); + }, + }, + // conditional hostnames column + { + field: 'hosts', + name: TABLE_COLUMN_NAMES.hosts, + width: '20%', + truncateText: true, + render: (_hosts: ActionListApiResponse['data'][number]['hosts']) => { + const hosts = _hosts && Object.values(_hosts); + // join hostnames if the action is for multiple agents + // and skip empty strings for names if any + const _hostnames = hosts + .reduce((acc, host) => { + if (host.name.trim()) { + acc.push(host.name); + } + return acc; + }, []) + .join(', '); + + let hostnames = _hostnames; + if (!_hostnames) { + if (hosts.length > 1) { + // when action was for a single agent and no host name + hostnames = UX_MESSAGES.unenrolled.hosts; + } else if (hosts.length === 1) { + // when action was for a multiple agents + // and none of them have a host name + hostnames = UX_MESSAGES.unenrolled.host; + } + } + return ( + + + {hostnames} + + + ); + }, + }, + { + field: 'comment', + name: TABLE_COLUMN_NAMES.comments, + width: !showHostNames ? '21%' : '30%', + truncateText: true, + render: (comment: ActionListApiResponse['data'][number]['comment']) => { + return ( + + + {comment ?? emptyValue} + + + ); + }, + }, + { + field: 'status', + name: TABLE_COLUMN_NAMES.status, + width: !showHostNames ? '15%' : '10%', + render: (_status: ActionListApiResponse['data'][number]['status']) => { + const status = getActionStatus(_status); + + return ( + + + + ); + }, + }, + { + field: '', + align: RIGHT_ALIGNMENT as HorizontalAlignment, + width: '40px', + isExpander: true, + name: ( + + {UX_MESSAGES.screenReaderExpand} + + ), + render: (actionListDataItem: ActionListApiResponse['data'][number]) => { + const actionId = actionListDataItem.id; + return ( + + ); + }, + }, + ]; + // filter out the `hosts` column + // if showHostNames is FALSE + if (!showHostNames) { + return columns.filter((column) => column.field !== 'hosts'); + } + return columns; +}; + interface ActionsLogTableProps { error?: string; 'data-test-subj'?: string; @@ -55,13 +246,11 @@ interface ActionsLogTableProps { onChange: ({ page: _page, }: CriteriaWithPagination) => void; + onShowActionDetails: (actionIds: string[]) => void; queryParams: EndpointActionListRequestQuery; showHostNames: boolean; totalItemCount: number; } -interface ExpandedRowMapType { - [k: ActionListApiResponse['data'][number]['id']]: React.ReactNode; -} export const ActionsLogTable = memo( ({ @@ -71,52 +260,66 @@ export const ActionsLogTable = memo( isFlyout, loading, onChange, + onShowActionDetails, queryParams, showHostNames, totalItemCount, }) => { - const getTestId = useTestIdGenerator(dataTestSubj); + const [itemIdToExpandedRowMap, setItemIdToExpandedRowMap] = useState({}); + const getTestId = useTestIdGenerator(dataTestSubj); const { pagination: paginationFromUrlParams } = useUrlPagination(); - const { withOutputs, setUrlWithOutputs } = useActionHistoryUrlParams(); + const { withOutputs: withOutputsFromUrl } = useActionHistoryUrlParams(); - const [itemIdToExpandedRowMap, setItemIdToExpandedRowMap] = useState( - withOutputs - ? withOutputs.reduce((idToRowMap, actionId) => { - if (actionId.length) { - idToRowMap[actionId] = ( - item.id === actionId)[0]} - data-test-subj={dataTestSubj} - /> - ); - } - return idToRowMap; - }, {}) - : {} - ); + const getActionIdsWithDetails = useCallback((): string[] => { + // get the list of action ids from URL params on the history page + if (!isFlyout) { + return withOutputsFromUrl ?? []; + } + // get the list of action ids form the query params for flyout view + return queryParams.withOutputs + ? typeof queryParams.withOutputs === 'string' + ? [queryParams.withOutputs] + : queryParams.withOutputs + : []; + }, [isFlyout, queryParams.withOutputs, withOutputsFromUrl]); + + useEffect(() => { + const actionIdsWithDetails = getActionIdsWithDetails(); + const openDetails = actionIdsWithDetails.reduce( + (idToRowMap, actionId) => { + idToRowMap[actionId] = ( + item.id === actionId)[0]} + data-test-subj={dataTestSubj} + /> + ); + return idToRowMap; + }, + {} + ); + setItemIdToExpandedRowMap(openDetails); + }, [dataTestSubj, getActionIdsWithDetails, items, queryParams.withOutputs, withOutputsFromUrl]); const toggleDetails = useCallback( - (item: ActionListApiResponse['data'][number]) => { + (action: ActionListApiResponse['data'][number]) => { const itemIdToExpandedRowMapValues = { ...itemIdToExpandedRowMap }; - if (itemIdToExpandedRowMapValues[item.id]) { + if (itemIdToExpandedRowMapValues[action.id]) { // close tray - delete itemIdToExpandedRowMapValues[item.id]; + delete itemIdToExpandedRowMapValues[action.id]; } else { - // expanded tray contents - itemIdToExpandedRowMapValues[item.id] = ( - + // assign the expanded tray content to the map + // with action details + itemIdToExpandedRowMapValues[action.id] = ( + ); } - const expandedActionIds = Object.keys(itemIdToExpandedRowMapValues); - if (!isFlyout) { - // set and show `withOutputs` URL param on history page - setUrlWithOutputs(expandedActionIds.join()); - } + onShowActionDetails(Object.keys(itemIdToExpandedRowMapValues)); setItemIdToExpandedRowMap(itemIdToExpandedRowMapValues); }, - [dataTestSubj, isFlyout, itemIdToExpandedRowMap, setUrlWithOutputs] + [itemIdToExpandedRowMap, onShowActionDetails, dataTestSubj] ); + // memoized callback for toggleDetails const onClickCallback = useCallback( (actionListDataItem: ActionListApiResponse['data'][number]) => () => @@ -124,192 +327,6 @@ export const ActionsLogTable = memo( [toggleDetails] ); - const responseActionListColumns = useMemo(() => { - const columns = [ - { - field: 'startedAt', - name: TABLE_COLUMN_NAMES.time, - width: !showHostNames ? '21%' : '15%', - truncateText: true, - render: (startedAt: ActionListApiResponse['data'][number]['startedAt']) => { - return ( - - ); - }, - }, - { - field: 'command', - name: TABLE_COLUMN_NAMES.command, - width: !showHostNames ? '21%' : '10%', - truncateText: true, - render: (_command: ActionListApiResponse['data'][number]['command']) => { - const command = getUiCommand(_command); - return ( - - - {command} - - - ); - }, - }, - { - field: 'createdBy', - name: TABLE_COLUMN_NAMES.user, - width: !showHostNames ? '21%' : '14%', - truncateText: true, - render: (userId: ActionListApiResponse['data'][number]['createdBy']) => { - return ( - - } - > - - - {userId} - - - - ); - }, - }, - // conditional hostnames column - { - field: 'hosts', - name: TABLE_COLUMN_NAMES.hosts, - width: '20%', - truncateText: true, - render: (_hosts: ActionListApiResponse['data'][number]['hosts']) => { - const hosts = _hosts && Object.values(_hosts); - // join hostnames if the action is for multiple agents - // and skip empty strings for names if any - const _hostnames = hosts - .reduce((acc, host) => { - if (host.name.trim()) { - acc.push(host.name); - } - return acc; - }, []) - .join(', '); - - let hostnames = _hostnames; - if (!_hostnames) { - if (hosts.length > 1) { - // when action was for a single agent and no host name - hostnames = UX_MESSAGES.unenrolled.hosts; - } else if (hosts.length === 1) { - // when action was for a multiple agents - // and none of them have a host name - hostnames = UX_MESSAGES.unenrolled.host; - } - } - return ( - - - {hostnames} - - - ); - }, - }, - { - field: 'comment', - name: TABLE_COLUMN_NAMES.comments, - width: !showHostNames ? '21%' : '30%', - truncateText: true, - render: (comment: ActionListApiResponse['data'][number]['comment']) => { - return ( - - - {comment ?? emptyValue} - - - ); - }, - }, - { - field: 'status', - name: TABLE_COLUMN_NAMES.status, - width: !showHostNames ? '15%' : '10%', - render: (_status: ActionListApiResponse['data'][number]['status']) => { - const status = getActionStatus(_status); - - return ( - - - - ); - }, - }, - { - field: '', - align: RIGHT_ALIGNMENT as HorizontalAlignment, - width: '40px', - isExpander: true, - name: ( - - {UX_MESSAGES.screenReaderExpand} - - ), - render: (actionListDataItem: ActionListApiResponse['data'][number]) => { - return ( - - ); - }, - }, - ]; - // filter out the `hosts` column - // if showHostNames is FALSE - if (!showHostNames) { - return columns.filter((column) => column.field !== 'hosts'); - } - return columns; - }, [showHostNames, getTestId, itemIdToExpandedRowMap, onClickCallback]); - // table pagination const tablePagination = useMemo(() => { return { @@ -363,6 +380,17 @@ export const ActionsLogTable = memo( [getTestId, pagedResultsCount.fromCount, pagedResultsCount.toCount, totalItemCount] ); + const columns = useMemo( + () => + getResponseActionListTableColumns({ + getTestId, + itemIdToExpandedRowMap, + onClickCallback, + showHostNames, + }), + [itemIdToExpandedRowMap, getTestId, onClickCallback, showHostNames] + ); + return ( <> {recordRangeLabel} @@ -370,7 +398,7 @@ export const ActionsLogTable = memo( { const executeAccordions = getByTestId(`${testPrefix}-executeResponseOutput`); expect(executeAccordions).toBeTruthy(); - expect(executeAccordions).toHaveTextContent('Execution outputExecution error'); + const accordionButtons = Array.from(executeAccordions.querySelectorAll('.euiAccordion')); + expect(accordionButtons[0]).toHaveTextContent('Execution output (truncated)'); + expect(accordionButtons[1]).toHaveTextContent('Execution error (truncated)'); }); it('should contain execute output for `execute` action WITHOUT execute operation privilege', async () => { diff --git a/x-pack/plugins/security_solution/public/management/components/endpoint_response_actions_list/response_actions_log.tsx b/x-pack/plugins/security_solution/public/management/components/endpoint_response_actions_list/response_actions_log.tsx index 39601035250e9..e94cc67c81bdc 100644 --- a/x-pack/plugins/security_solution/public/management/components/endpoint_response_actions_list/response_actions_log.tsx +++ b/x-pack/plugins/security_solution/public/management/components/endpoint_response_actions_list/response_actions_log.tsx @@ -53,6 +53,8 @@ export const ResponseActionsLog = memo< startDate: startDateFromUrl, endDate: endDateFromUrl, users: usersFromUrl, + withOutputs: withOutputsFromUrl, + setUrlWithOutputs, } = useActionHistoryUrlParams(); const getTestId = useTestIdGenerator(dataTestSubj); @@ -67,6 +69,7 @@ export const ResponseActionsLog = memo< commands: [], statuses: [], userIds: [], + withOutputs: [], }); // update query state from URL params @@ -82,9 +85,18 @@ export const ResponseActionsLog = memo< ? (statusesFromUrl as ResponseActionStatus[]) : prevState.statuses, userIds: usersFromUrl?.length ? usersFromUrl : prevState.userIds, + withOutputs: withOutputsFromUrl?.length ? withOutputsFromUrl : prevState.withOutputs, })); } - }, [commandsFromUrl, agentIdsFromUrl, isFlyout, statusesFromUrl, setQueryParams, usersFromUrl]); + }, [ + commandsFromUrl, + agentIdsFromUrl, + isFlyout, + statusesFromUrl, + setQueryParams, + usersFromUrl, + withOutputsFromUrl, + ]); // date range picker state and handlers const { dateRangePickerState, onRefreshChange, onTimeChange } = useDateRangePicker(isFlyout); @@ -201,6 +213,18 @@ export const ResponseActionsLog = memo< [isFlyout, reFetchEndpointActionList, setQueryParams, setPaginationOnUrlParams] ); + // handle on details open + const onShowActionDetails = useCallback( + (actionIds: string[]) => { + setQueryParams((prevState) => ({ ...prevState, withOutputs: actionIds })); + if (!isFlyout) { + // set and show `withOutputs` URL param on history page + setUrlWithOutputs(actionIds.join()); + } + }, + [isFlyout, setUrlWithOutputs] + ); + if (error?.body?.statusCode === 404 && error?.body?.message === 'index_not_found_exception') { return ; } else if (isFetching && isFirstAttempt) { @@ -257,6 +281,7 @@ export const ResponseActionsLog = memo< isFlyout={isFlyout} loading={isFetching} onChange={handleTableOnChange} + onShowActionDetails={onShowActionDetails} queryParams={queryParams} showHostNames={showHostNames} totalItemCount={totalItemCount} diff --git a/x-pack/plugins/security_solution/public/management/hooks/response_actions/use_get_endpoint_action_list.ts b/x-pack/plugins/security_solution/public/management/hooks/response_actions/use_get_endpoint_action_list.ts index c59b422b04028..546b1b5f42669 100644 --- a/x-pack/plugins/security_solution/public/management/hooks/response_actions/use_get_endpoint_action_list.ts +++ b/x-pack/plugins/security_solution/public/management/hooks/response_actions/use_get_endpoint_action_list.ts @@ -46,6 +46,7 @@ export const useGetEndpointActionList = ( startDate: query.startDate, statuses: query.statuses, userIds, + withOutputs: query.withOutputs, }, }); }, diff --git a/x-pack/plugins/security_solution/scripts/endpoint/endpoint_agent_runner/elastic_endpoint.ts b/x-pack/plugins/security_solution/scripts/endpoint/endpoint_agent_runner/elastic_endpoint.ts index dd123622d9c49..34d2036411280 100644 --- a/x-pack/plugins/security_solution/scripts/endpoint/endpoint_agent_runner/elastic_endpoint.ts +++ b/x-pack/plugins/security_solution/scripts/endpoint/endpoint_agent_runner/elastic_endpoint.ts @@ -15,7 +15,6 @@ import { type UpdatePackagePolicy, } from '@kbn/fleet-plugin/common'; import chalk from 'chalk'; -import { inspect } from 'util'; import { getEndpointPackageInfo } from '../../../common/endpoint/index_data'; import { indexFleetEndpointPolicy } from '../../../common/endpoint/data_loaders/index_fleet_endpoint_policy'; import { @@ -26,6 +25,7 @@ import { } from '../common/fleet_services'; import { getRuntimeServices } from './runtime'; import { type PolicyData, ProtectionModes } from '../../../common/endpoint/types'; +import { dump } from './utils'; interface ElasticArtifactSearchResponse { manifest: { @@ -56,7 +56,7 @@ export const enrollEndpointHost = async (): Promise => { log.indent(4); try { - const uniqueId = Math.random().toString(32).substring(2).substring(0, 4); + const uniqueId = Math.random().toString().substring(2, 6); const username = userInfo().username.toLowerCase(); const policyId: string = policy || (await getOrCreateAgentPolicyId()); @@ -85,7 +85,7 @@ export const enrollEndpointHost = async (): Promise => { log.info(`Creating VM named: ${vmName}`); - await execa.command(`multipass launch --name ${vmName}`); + await execa.command(`multipass launch --name ${vmName} --disk 8G`); log.verbose(await execa('multipass', ['info', vmName])); @@ -102,7 +102,7 @@ export const enrollEndpointHost = async (): Promise => { await execa.command(`multipass exec ${vmName} -- tar -zxf ${agentDownloadedFile}`); await execa.command(`multipass exec ${vmName} -- rm -f ${agentDownloadedFile}`); - const agentEnrollArgs = [ + const agentInstallArguments = [ 'exec', vmName, @@ -116,7 +116,7 @@ export const enrollEndpointHost = async (): Promise => { './elastic-agent', - 'enroll', + 'install', '--insecure', @@ -130,25 +130,9 @@ export const enrollEndpointHost = async (): Promise => { ]; log.info(`Enrolling elastic agent with Fleet`); - log.verbose(`Command: multipass ${agentEnrollArgs.join(' ')}`); + log.verbose(`Command: multipass ${agentInstallArguments.join(' ')}`); - await execa(`multipass`, agentEnrollArgs); - - const runAgentCommand = `multipass exec ${vmName} --working-directory /home/ubuntu/${vmDirName} -- sudo ./elastic-agent \&>/dev/null`; - - log.info(`Running elastic agent`); - log.verbose(`Command: ${runAgentCommand}`); - - // About `timeout` option below - // The `multipass exec` command seems to have some issues when a command pass to it redirects output, - // as is with the command that runs endpoint. See https://github.com/canonical/multipass/issues/667 - // To get around it, `timeout` is set to 5s, which should be enough time for the command to be executed - // in the VM. - await execa.command(runAgentCommand, { timeout: 5000 }).catch((error) => { - if (error.originalMessage !== 'Timed out') { - throw error; - } - }); + await execa(`multipass`, agentInstallArguments); log.info(`Waiting for Agent to check-in with Fleet`); await waitForHostToEnroll(kbnClient, vmName); @@ -161,7 +145,7 @@ export const enrollEndpointHost = async (): Promise => { Delete VM: ${chalk.bold(`multipass delete -p ${vmName}${await getVmCountNotice()}`)} `); } catch (error) { - log.error(inspect(error, { depth: 4 })); + log.error(dump(error)); log.indent(-4); throw error; } @@ -173,8 +157,9 @@ export const enrollEndpointHost = async (): Promise => { const getAgentDownloadUrl = async (version: string): Promise => { const { log } = getRuntimeServices(); - // TODO:PT use arch and platform of VM to build download file name below (will be needed if tools ever supports different types of VMs) - const agentFile = `elastic-agent-${version}-linux-arm64.tar.gz`; + const downloadArch = + { arm64: 'arm64', x64: 'x86_64' }[process.arch] ?? `UNSUPPORTED_ARCHITECTURE_${process.arch}`; + const agentFile = `elastic-agent-${version}-linux-${downloadArch}.tar.gz`; const artifactSearchUrl = `https://artifacts-api.elastic.co/v1/search/${version}/${agentFile}`; log.verbose(`Retrieving elastic agent download URL from:\n ${artifactSearchUrl}`); diff --git a/x-pack/plugins/security_solution/scripts/endpoint/endpoint_agent_runner/fleet_server.ts b/x-pack/plugins/security_solution/scripts/endpoint/endpoint_agent_runner/fleet_server.ts index 893890fb84802..d954659676bb0 100644 --- a/x-pack/plugins/security_solution/scripts/endpoint/endpoint_agent_runner/fleet_server.ts +++ b/x-pack/plugins/security_solution/scripts/endpoint/endpoint_agent_runner/fleet_server.ts @@ -36,6 +36,7 @@ import type { PostFleetServerHostsResponse, } from '@kbn/fleet-plugin/common/types/rest_spec/fleet_server_hosts'; import chalk from 'chalk'; +import { dump } from './utils'; import { isLocalhost } from '../common/localhost_services'; import { fetchFleetAgents, @@ -79,7 +80,7 @@ export const runFleetServerIfNeeded = async (): Promise< serviceToken, }); } catch (error) { - log.error(error); + log.error(dump(error)); log.indent(-4); throw error; } @@ -280,9 +281,10 @@ export const startFleetServerWithDocker = async ({ View running output: ${chalk.bold(`docker attach ---sig-proxy=false ${containerName}`)} Shell access: ${chalk.bold(`docker exec -it ${containerName} /bin/bash`)} + Kill container: ${chalk.bold(`docker kill ${containerId}`)} `); } catch (error) { - log.error(error); + log.error(dump(error)); log.indent(-4); throw error; } @@ -349,7 +351,7 @@ const configureFleetIfNeeded = async () => { } } } catch (error) { - log.error(error); + log.error(dump(error)); log.indent(-4); throw error; } @@ -387,7 +389,7 @@ const addFleetServerHostToFleetSettings = async ( return item; } catch (error) { - log.error(error); + log.error(dump(error)); log.indent(-4); throw error; } diff --git a/x-pack/plugins/security_solution/scripts/endpoint/endpoint_agent_runner/utils.ts b/x-pack/plugins/security_solution/scripts/endpoint/endpoint_agent_runner/utils.ts new file mode 100644 index 0000000000000..669d5f830c08e --- /dev/null +++ b/x-pack/plugins/security_solution/scripts/endpoint/endpoint_agent_runner/utils.ts @@ -0,0 +1,17 @@ +/* + * 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 { inspect } from 'util'; + +/** + * Safely traverse some content (object, array, etc) and stringify it + * @param content + */ +// eslint-disable-next-line @typescript-eslint/no-explicit-any +export const dump = (content: any): string => { + return inspect(content, { depth: 5 }); +}; diff --git a/x-pack/plugins/security_solution/server/endpoint/endpoint_app_context_services.ts b/x-pack/plugins/security_solution/server/endpoint/endpoint_app_context_services.ts index 0f29e1d46801d..3de8b23de51c3 100644 --- a/x-pack/plugins/security_solution/server/endpoint/endpoint_app_context_services.ts +++ b/x-pack/plugins/security_solution/server/endpoint/endpoint_app_context_services.ts @@ -7,10 +7,7 @@ import type { KibanaRequest, Logger } from '@kbn/core/server'; import type { ExceptionListClient, ListsServerExtensionRegistrar } from '@kbn/lists-plugin/server'; -import type { - CasesClient, - PluginStartContract as CasesPluginStartContract, -} from '@kbn/cases-plugin/server'; +import type { CasesClient, CasesStart } from '@kbn/cases-plugin/server'; import type { SecurityPluginStart } from '@kbn/security-plugin/server'; import type { FleetStartContract, MessageSigningServiceInterface } from '@kbn/fleet-plugin/server'; import type { PluginStartContract as AlertsPluginStartContract } from '@kbn/alerting-plugin/server'; @@ -58,7 +55,7 @@ export interface EndpointAppContextServiceStartContract { registerListsServerExtension?: ListsServerExtensionRegistrar; licenseService: LicenseService; exceptionListsClient: ExceptionListClient | undefined; - cases: CasesPluginStartContract | undefined; + cases: CasesStart | undefined; featureUsageService: FeatureUsageService; experimentalFeatures: ExperimentalFeatures; messageSigningService: MessageSigningServiceInterface | undefined; diff --git a/x-pack/plugins/security_solution/server/endpoint/mocks.ts b/x-pack/plugins/security_solution/server/endpoint/mocks.ts index 7b57a384e0e10..a9b8832b03d7a 100644 --- a/x-pack/plugins/security_solution/server/endpoint/mocks.ts +++ b/x-pack/plugins/security_solution/server/endpoint/mocks.ts @@ -35,14 +35,10 @@ import { createMockPackageService, createMessageSigningServiceMock, } from '@kbn/fleet-plugin/server/mocks'; -// A TS error (TS2403) is thrown when attempting to export the mock function below from Cases -// plugin server `index.ts`. Its unclear what is actually causing the error. Since this is a Mock -// file and not bundled with the application, adding a eslint disable below and using import from -// a restricted path. -import { createCasesClientMock } from '@kbn/cases-plugin/server/client/mocks'; import { createFleetAuthzMock } from '@kbn/fleet-plugin/common/mocks'; import type { RequestFixtureOptions } from '@kbn/core-http-router-server-mocks'; import type { ElasticsearchClientMock } from '@kbn/core-elasticsearch-client-server-mocks'; +import { casesPluginMock } from '@kbn/cases-plugin/server/mocks'; import { getEndpointAuthzInitialStateMock } from '../../common/endpoint/service/authz/mocks'; import { xpackMocks } from '../fixtures'; import { createMockConfig, requestContextMock } from '../lib/detection_engine/routes/__mocks__'; @@ -116,7 +112,6 @@ export const createMockEndpointAppContextServiceStartContract = const config = createMockConfig(); const logger = loggingSystemMock.create().get('mock_endpoint_app_context'); - const casesClientMock = createCasesClientMock(); const savedObjectsStart = savedObjectsServiceMock.createStartContract(); const security = securityMock.createStart(); const agentService = createMockAgentService(); @@ -157,6 +152,8 @@ export const createMockEndpointAppContextServiceStartContract = jest.fn(() => ({ privileges: { kibana: [] } })) ); + const casesMock = casesPluginMock.createStartContract(); + return { endpointMetadataService, endpointFleetServicesFactory, @@ -172,9 +169,7 @@ export const createMockEndpointAppContextServiceStartContract = Parameters >(), exceptionListsClient: listMock.getExceptionListClient(), - cases: { - getCasesClientWithRequest: jest.fn(async () => casesClientMock), - }, + cases: casesMock, featureUsageService: createFeatureUsageServiceMock(), experimentalFeatures: createMockConfig().experimentalFeatures, messageSigningService: createMessageSigningServiceMock(), diff --git a/x-pack/plugins/security_solution/server/endpoint/routes/actions/response_actions.test.ts b/x-pack/plugins/security_solution/server/endpoint/routes/actions/response_actions.test.ts index 42e337efba2d9..582d0457f0680 100644 --- a/x-pack/plugins/security_solution/server/endpoint/routes/actions/response_actions.test.ts +++ b/x-pack/plugins/security_solution/server/endpoint/routes/actions/response_actions.test.ts @@ -777,6 +777,8 @@ describe('Response actions', () => { {} as KibanaRequest )) as CasesClientMock; + casesClient.attachments.add.mockClear(); + let counter = 1; casesClient.cases.getCasesByAlertID.mockImplementation(async () => { return [ diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/api/generate_assets/route.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/api/generate_assets/route.ts new file mode 100644 index 0000000000000..1157fe9d3b9ef --- /dev/null +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/api/generate_assets/route.ts @@ -0,0 +1,119 @@ +/* + * 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 * as t from 'io-ts'; +import moment from 'moment'; +import { transformError } from '@kbn/securitysolution-es-utils'; +import { PositiveIntegerGreaterThanZero } from '@kbn/securitysolution-io-ts-types'; + +import { GENERATE_ASSETS_URL } from '../../../../../../common/detection_engine/prebuilt_rules'; + +import type { SecuritySolutionPluginRouter } from '../../../../../types'; +import { buildRouteValidation } from '../../../../../utils/build_validation/route_validation'; +import { buildSiemResponse } from '../../../routes/utils'; + +import type { PrebuiltRuleAsset } from '../../model/rule_assets/prebuilt_rule_asset'; +import { createPrebuiltRuleAssetsClient } from '../../logic/rule_assets/prebuilt_rule_assets_client'; + +type RequestBody = t.TypeOf; +const RequestBody = t.exact( + t.type({ + num_versions_per_rule: PositiveIntegerGreaterThanZero, + }) +); + +/** + * NOTE: This is a helper endpoint for development and testing. It should be removed later. + * This endpoint: + * - reads currently installed latest assets (saved objects of type security-rule) + * - generates more versions of rule assets based on the latest ones (multiple versions per rule) + * - writes the generated saved objects back to the kibana index + */ +export const generateAssetsRoute = (router: SecuritySolutionPluginRouter) => { + router.post( + { + path: GENERATE_ASSETS_URL, + validate: { + body: buildRouteValidation(RequestBody), + }, + options: { + tags: ['access:securitySolution'], + timeout: { + // FUNFACT: If we do not add a very long timeout what will happen + // is that Chrome which receive a 408 error and then do a retry. + // This retry can cause lots of connections to happen. Using a very + // long timeout will ensure that Chrome does not do retries and saturate the connections. + idleSocket: moment.duration('1', 'hour').asMilliseconds(), + }, + }, + }, + async (context, request, response) => { + const siemResponse = buildSiemResponse(response); + + try { + const ctx = await context.resolve(['core']); + const soClient = ctx.core.savedObjects.client; + const ruleAssetsClient = createPrebuiltRuleAssetsClient(soClient); + + const latestRules = await ruleAssetsClient.fetchLatestAssets(); + + const historicalRules = generateHistoricalVersionsForManyRules( + latestRules, + request.body.num_versions_per_rule + ); + + await ruleAssetsClient.bulkCreateAssets(historicalRules); + + return response.ok({ + body: { + num_latest_rules: latestRules.length, + num_installed_versions: historicalRules.length, + }, + }); + } catch (err) { + const error = transformError(err); + return siemResponse.error({ + body: error.message, + statusCode: error.statusCode, + }); + } + } + ); +}; + +const generateHistoricalVersionsForManyRules = ( + rules: PrebuiltRuleAsset[], + numberOfVersionsPerRule: number +) => { + const result: PrebuiltRuleAsset[] = []; + + rules.forEach((rule) => { + result.push(...generateHistoricalVersionsForOneRule(rule, numberOfVersionsPerRule)); + }); + + return result; +}; + +const generateHistoricalVersionsForOneRule = ( + rule: PrebuiltRuleAsset, + numberOfVersionsPerRule: number +): PrebuiltRuleAsset[] => { + const { name: ruleName, version: latestVersion, ...restOfRuleAttributes } = rule; + const nextToLatestVersion = latestVersion + 1; + const result: PrebuiltRuleAsset[] = []; + + for (let i = 0; i < numberOfVersionsPerRule; i++) { + const historicalVersion = nextToLatestVersion + i; + result.push({ + name: `${ruleName} v${historicalVersion}`, + version: historicalVersion, + ...restOfRuleAttributes, + }); + } + + return result; +}; diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/api/get_prebuilt_rules_and_timelines_status/route.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/api/get_prebuilt_rules_and_timelines_status/route.test.ts index 3d3accea3fa24..64206b257d9f2 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/api/get_prebuilt_rules_and_timelines_status/route.test.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/api/get_prebuilt_rules_and_timelines_status/route.test.ts @@ -20,25 +20,29 @@ import { mockCheckTimelinesStatusAfterInstallResult, } from '../../../../timeline/__mocks__/import_timelines'; -jest.mock('../../logic/get_latest_prebuilt_rules', () => { +jest.mock('../../logic/rule_assets/prebuilt_rule_assets_client', () => { return { - getLatestPrebuiltRules: async () => { - return [ - { - rule_id: 'rule-1', - output_index: '.siem-signals', - risk_score: 50, - description: 'some description', - from: 'now-5m', - to: 'now', - index: ['index-1'], - name: 'some-name', - severity: 'low', - interval: '5m', - type: 'query', - version: 2, // set one higher than the mocks which is set to 1 to trigger updates + createPrebuiltRuleAssetsClient: () => { + return { + fetchLatestAssets: async () => { + return [ + { + rule_id: 'rule-1', + output_index: '.siem-signals', + risk_score: 50, + description: 'some description', + from: 'now-5m', + to: 'now', + index: ['index-1'], + name: 'some-name', + severity: 'low', + interval: '5m', + type: 'query', + version: 2, // set one higher than the mocks which is set to 1 to trigger updates + }, + ]; }, - ]; + }; }, }; }); diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/api/get_prebuilt_rules_and_timelines_status/route.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/api/get_prebuilt_rules_and_timelines_status/route.ts index ced2a0e8ea663..40b3078fe06eb 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/api/get_prebuilt_rules_and_timelines_status/route.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/api/get_prebuilt_rules_and_timelines_status/route.ts @@ -18,10 +18,9 @@ import { import { getExistingPrepackagedRules } from '../../../rule_management/logic/search/get_existing_prepackaged_rules'; import { findRules } from '../../../rule_management/logic/search/find_rules'; -import { getLatestPrebuiltRules } from '../../logic/get_latest_prebuilt_rules'; import { getRulesToInstall } from '../../logic/get_rules_to_install'; import { getRulesToUpdate } from '../../logic/get_rules_to_update'; -import { ruleAssetsClientFactory } from '../../logic/rule_asset/rule_asset_saved_objects_client'; +import { createPrebuiltRuleAssetsClient } from '../../logic/rule_assets/prebuilt_rule_assets_client'; import { rulesToMap } from '../../logic/utils'; import { buildFrameworkRequest } from '../../../../timeline/utils/common'; @@ -47,10 +46,10 @@ export const getPrebuiltRulesAndTimelinesStatusRoute = ( const ctx = await context.resolve(['core', 'alerting']); const savedObjectsClient = ctx.core.savedObjects.client; const rulesClient = ctx.alerting.getRulesClient(); - const ruleAssetsClient = ruleAssetsClientFactory(savedObjectsClient); + const ruleAssetsClient = createPrebuiltRuleAssetsClient(savedObjectsClient); try { - const latestPrebuiltRules = await getLatestPrebuiltRules(ruleAssetsClient); + const latestPrebuiltRules = await ruleAssetsClient.fetchLatestAssets(); const customRules = await findRules({ rulesClient, diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/api/get_prebuilt_rules_status/route.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/api/get_prebuilt_rules_status/route.ts new file mode 100644 index 0000000000000..1926320114a17 --- /dev/null +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/api/get_prebuilt_rules_status/route.ts @@ -0,0 +1,85 @@ +/* + * 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 { transformError } from '@kbn/securitysolution-es-utils'; + +import { GET_PREBUILT_RULES_STATUS_URL } from '../../../../../../common/detection_engine/prebuilt_rules'; +import type { + GetPrebuiltRulesStatusResponseBody, + PrebuiltRulesStatusStats, +} from '../../../../../../common/detection_engine/prebuilt_rules/api/get_prebuilt_rules_status/response_schema'; + +import type { SecuritySolutionPluginRouter } from '../../../../../types'; +import { buildSiemResponse } from '../../../routes/utils'; + +import { createPrebuiltRuleAssetsClient } from '../../logic/rule_assets/prebuilt_rule_assets_client'; +import { createPrebuiltRuleObjectsClient } from '../../logic/rule_objects/prebuilt_rule_objects_client'; +import type { VersionBuckets } from '../../model/rule_versions/get_version_buckets'; +import { getVersionBuckets } from '../../model/rule_versions/get_version_buckets'; + +export const getPrebuiltRulesStatusRoute = (router: SecuritySolutionPluginRouter) => { + router.get( + { + path: GET_PREBUILT_RULES_STATUS_URL, + validate: {}, + options: { + tags: ['access:securitySolution'], + }, + }, + async (context, request, response) => { + const siemResponse = buildSiemResponse(response); + + try { + const ctx = await context.resolve(['core', 'alerting']); + const soClient = ctx.core.savedObjects.client; + const rulesClient = ctx.alerting.getRulesClient(); + const ruleAssetsClient = createPrebuiltRuleAssetsClient(soClient); + const ruleObjectsClient = createPrebuiltRuleObjectsClient(rulesClient); + + const [latestVersions, { installedVersions }] = await Promise.all([ + ruleAssetsClient.fetchLatestVersions(), + ruleObjectsClient.fetchInstalledRules(), + ]); + + const versionBuckets = getVersionBuckets({ + latestVersions, + installedVersions, + }); + + const stats = calculateRuleStats(versionBuckets); + + const body: GetPrebuiltRulesStatusResponseBody = { + status_code: 200, + message: 'OK', + attributes: { + stats, + }, + }; + + return response.ok({ body }); + } catch (err) { + const error = transformError(err); + return siemResponse.error({ + body: error.message, + statusCode: error.statusCode, + }); + } + } + ); +}; + +const calculateRuleStats = (buckets: VersionBuckets): PrebuiltRulesStatusStats => { + const { latestVersions, installedVersions, latestVersionsToInstall, installedVersionsToUpgrade } = + buckets; + + return { + num_prebuilt_rules_total: latestVersions.length, + num_prebuilt_rules_installed: installedVersions.length, + num_prebuilt_rules_to_install: latestVersionsToInstall.length, + num_prebuilt_rules_to_upgrade: installedVersionsToUpgrade.length, + }; +}; diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/api/install_prebuilt_rules_and_timelines/route.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/api/install_prebuilt_rules_and_timelines/route.test.ts index f529369438742..af6fca55078fb 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/api/install_prebuilt_rules_and_timelines/route.test.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/api/install_prebuilt_rules_and_timelines/route.test.ts @@ -13,7 +13,6 @@ import { getBasicEmptySearchResponse, } from '../../../routes/__mocks__/request_responses'; import { requestContextMock, serverMock } from '../../../routes/__mocks__'; -import type { PrebuiltRuleToInstall } from '../../../../../../common/detection_engine/prebuilt_rules'; import { installPrebuiltRulesAndTimelinesRoute, createPrepackagedRules } from './route'; import { listMock } from '@kbn/lists-plugin/server/mocks'; import type { ExceptionListClient } from '@kbn/lists-plugin/server'; @@ -32,38 +31,42 @@ jest.mock('../../../rule_management/logic/rule_actions/legacy_action_migration', }; }); -jest.mock('../../logic/get_latest_prebuilt_rules', () => { +jest.mock('../../logic/rule_assets/prebuilt_rule_assets_client', () => { return { - getLatestPrebuiltRules: async (): Promise => { - return [ - { - author: ['Elastic'], - tags: [], - rule_id: 'rule-1', - risk_score: 50, - risk_score_mapping: [], - severity_mapping: [], - description: 'some description', - from: 'now-5m', - to: 'now', - index: ['index-1'], - name: 'some-name', - severity: 'low', - interval: '5m', - type: 'query', - query: 'user.name: root or user.name: admin', - language: 'kuery', - references: [], - actions: [], - enabled: false, - false_positives: [], - max_signals: 100, - threat: [], - throttle: undefined, - exceptions_list: [], - version: 2, // set one higher than the mocks which is set to 1 to trigger updates + createPrebuiltRuleAssetsClient: () => { + return { + fetchLatestAssets: async () => { + return [ + { + author: ['Elastic'], + tags: [], + rule_id: 'rule-1', + risk_score: 50, + risk_score_mapping: [], + severity_mapping: [], + description: 'some description', + from: 'now-5m', + to: 'now', + index: ['index-1'], + name: 'some-name', + severity: 'low', + interval: '5m', + type: 'query', + query: 'user.name: root or user.name: admin', + language: 'kuery', + references: [], + actions: [], + enabled: false, + false_positives: [], + max_signals: 100, + threat: [], + throttle: undefined, + exceptions_list: [], + version: 2, // set one higher than the mocks which is set to 1 to trigger updates + }, + ]; }, - ]; + }; }, }; }); diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/api/install_prebuilt_rules_and_timelines/route.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/api/install_prebuilt_rules_and_timelines/route.ts index e3f8bdff812c7..23209a613f597 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/api/install_prebuilt_rules_and_timelines/route.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/api/install_prebuilt_rules_and_timelines/route.ts @@ -22,12 +22,11 @@ import { import { importTimelineResultSchema } from '../../../../../../common/types/timeline'; import { getExistingPrepackagedRules } from '../../../rule_management/logic/search/get_existing_prepackaged_rules'; -import { getLatestPrebuiltRules } from '../../logic/get_latest_prebuilt_rules'; -import { createPrebuiltRules } from '../../logic/create_prebuilt_rules'; -import { updatePrebuiltRules } from '../../logic/update_prebuilt_rules'; +import { createPrebuiltRules } from '../../logic/rule_objects/create_prebuilt_rules'; +import { updatePrebuiltRules } from '../../logic/rule_objects/update_prebuilt_rules'; import { getRulesToInstall } from '../../logic/get_rules_to_install'; import { getRulesToUpdate } from '../../logic/get_rules_to_update'; -import { ruleAssetsClientFactory } from '../../logic/rule_asset/rule_asset_saved_objects_client'; +import { createPrebuiltRuleAssetsClient } from '../../logic/rule_assets/prebuilt_rule_assets_client'; import { rulesToMap } from '../../logic/utils'; import { installPrepackagedTimelines } from '../../../../timeline/routes/prepackaged_timelines/install_prepackaged_timelines'; @@ -91,7 +90,7 @@ export const createPrepackagedRules = async ( const savedObjectsClient = context.core.savedObjects.client; const siemClient = context.getAppClient(); const exceptionsListClient = context.getExceptionListClient() ?? exceptionsClient; - const ruleAssetsClient = ruleAssetsClientFactory(savedObjectsClient); + const ruleAssetsClient = createPrebuiltRuleAssetsClient(savedObjectsClient); const { maxTimelineImportExportSize } = config; @@ -104,17 +103,18 @@ export const createPrepackagedRules = async ( await exceptionsListClient.createEndpointList(); } - let latestPrepackagedRulesMap = await getLatestPrebuiltRules(ruleAssetsClient); - if (latestPrepackagedRulesMap.size === 0) { + let latestPrebuiltRules = await ruleAssetsClient.fetchLatestAssets(); + if (latestPrebuiltRules.length === 0) { // Seems no packages with prepackaged rules were installed, try to install the default rules package await installPrebuiltRulesPackage(config, context); // Try to get the prepackaged rules again - latestPrepackagedRulesMap = await getLatestPrebuiltRules(ruleAssetsClient); + latestPrebuiltRules = await ruleAssetsClient.fetchLatestAssets(); } - const installedPrePackagedRules = rulesToMap(await getExistingPrepackagedRules({ rulesClient })); - const rulesToInstall = getRulesToInstall(latestPrepackagedRulesMap, installedPrePackagedRules); - const rulesToUpdate = getRulesToUpdate(latestPrepackagedRulesMap, installedPrePackagedRules); + + const installedPrebuiltRules = rulesToMap(await getExistingPrepackagedRules({ rulesClient })); + const rulesToInstall = getRulesToInstall(latestPrebuiltRules, installedPrebuiltRules); + const rulesToUpdate = getRulesToUpdate(latestPrebuiltRules, installedPrebuiltRules); await createPrebuiltRules(rulesClient, rulesToInstall); @@ -128,14 +128,9 @@ export const createPrepackagedRules = async ( importTimelineResultSchema ); - await updatePrebuiltRules( - rulesClient, - savedObjectsClient, - rulesToUpdate, - context.getRuleExecutionLog() - ); + await updatePrebuiltRules(rulesClient, savedObjectsClient, rulesToUpdate); - const prepackagedRulesOutput: InstallPrebuiltRulesAndTimelinesResponse = { + const prebuiltRulesOutput: InstallPrebuiltRulesAndTimelinesResponse = { rules_installed: rulesToInstall.length, rules_updated: rulesToUpdate.length, timelines_installed: prepackagedTimelinesResult?.timelines_installed ?? 0, @@ -143,7 +138,7 @@ export const createPrepackagedRules = async ( }; const [validated, genericErrors] = validate( - prepackagedRulesOutput, + prebuiltRulesOutput, InstallPrebuiltRulesAndTimelinesResponse ); diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/api/register_routes.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/api/register_routes.ts index c396eea87da9d..da4bd66748a6b 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/api/register_routes.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/api/register_routes.ts @@ -5,16 +5,35 @@ * 2.0. */ +import type { ConfigType } from '../../../../config'; import type { SetupPlugins } from '../../../../plugin_contract'; import type { SecuritySolutionPluginRouter } from '../../../../types'; import { getPrebuiltRulesAndTimelinesStatusRoute } from './get_prebuilt_rules_and_timelines_status/route'; +import { getPrebuiltRulesStatusRoute } from './get_prebuilt_rules_status/route'; import { installPrebuiltRulesAndTimelinesRoute } from './install_prebuilt_rules_and_timelines/route'; +import { generateAssetsRoute } from './generate_assets/route'; +import { reviewRuleInstallationRoute } from './review_rule_installation/route'; +import { reviewRuleUpgradeRoute } from './review_rule_upgrade/route'; export const registerPrebuiltRulesRoutes = ( router: SecuritySolutionPluginRouter, + config: ConfigType, security: SetupPlugins['security'] ) => { + const { prebuiltRulesNewUpgradeAndInstallationWorkflowsEnabled } = config.experimentalFeatures; + + // Legacy endpoints that we're going to deprecate getPrebuiltRulesAndTimelinesStatusRoute(router, security); installPrebuiltRulesAndTimelinesRoute(router); + + if (prebuiltRulesNewUpgradeAndInstallationWorkflowsEnabled) { + // New endpoints for the rule upgrade and installation workflows + getPrebuiltRulesStatusRoute(router); + reviewRuleInstallationRoute(router); + reviewRuleUpgradeRoute(router); + + // Helper endpoints for development and testing. Should be removed later. + generateAssetsRoute(router); + } }; diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/api/review_rule_installation/route.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/api/review_rule_installation/route.ts new file mode 100644 index 0000000000000..74d9c6ee80030 --- /dev/null +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/api/review_rule_installation/route.ts @@ -0,0 +1,99 @@ +/* + * 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 { transformError } from '@kbn/securitysolution-es-utils'; + +import type { PrebuiltRuleAsset } from '../../model/rule_assets/prebuilt_rule_asset'; +import { REVIEW_RULE_INSTALLATION_URL } from '../../../../../../common/detection_engine/prebuilt_rules'; +import type { + ReviewRuleInstallationResponseBody, + RuleInstallationInfoForReview, + RuleInstallationStatsForReview, +} from '../../../../../../common/detection_engine/prebuilt_rules/api/review_rule_installation/response_schema'; + +import type { SecuritySolutionPluginRouter } from '../../../../../types'; +import { buildSiemResponse } from '../../../routes/utils'; + +import { convertRuleToDiffable } from '../../logic/diff/normalization/convert_rule_to_diffable'; +import { createPrebuiltRuleAssetsClient } from '../../logic/rule_assets/prebuilt_rule_assets_client'; +import { createPrebuiltRuleObjectsClient } from '../../logic/rule_objects/prebuilt_rule_objects_client'; +import { getVersionBuckets } from '../../model/rule_versions/get_version_buckets'; + +export const reviewRuleInstallationRoute = (router: SecuritySolutionPluginRouter) => { + router.post( + { + path: REVIEW_RULE_INSTALLATION_URL, + validate: {}, + options: { + tags: ['access:securitySolution'], + }, + }, + async (context, request, response) => { + const siemResponse = buildSiemResponse(response); + + try { + const ctx = await context.resolve(['core', 'alerting']); + const soClient = ctx.core.savedObjects.client; + const rulesClient = ctx.alerting.getRulesClient(); + const ruleAssetsClient = createPrebuiltRuleAssetsClient(soClient); + const ruleObjectsClient = createPrebuiltRuleObjectsClient(rulesClient); + + const [latestVersions, { installedVersions }] = await Promise.all([ + ruleAssetsClient.fetchLatestVersions(), + ruleObjectsClient.fetchInstalledRules(), + ]); + + const versionBuckets = getVersionBuckets({ + latestVersions, + installedVersions, + }); + + const rulesToInstall = await ruleAssetsClient.fetchAssetsByVersionInfo( + versionBuckets.latestVersionsToInstall + ); + + const body: ReviewRuleInstallationResponseBody = { + status_code: 200, + message: 'OK', + attributes: { + stats: calculateRuleStats(rulesToInstall), + rules: calculateRuleInfos(rulesToInstall), + }, + }; + + return response.ok({ body }); + } catch (err) { + const error = transformError(err); + return siemResponse.error({ + body: error.message, + statusCode: error.statusCode, + }); + } + } + ); +}; + +const getAggregatedTags = (rules: PrebuiltRuleAsset[]): string[] => { + const set = new Set(rules.flatMap((rule) => rule.tags || [])); + return Array.from(set.values()); +}; + +const calculateRuleStats = ( + rulesToInstall: PrebuiltRuleAsset[] +): RuleInstallationStatsForReview => { + const tagsOfRulesToInstall = getAggregatedTags(rulesToInstall); + return { + num_rules_to_install: rulesToInstall.length, + tags: tagsOfRulesToInstall, + }; +}; + +const calculateRuleInfos = ( + rulesToInstall: PrebuiltRuleAsset[] +): RuleInstallationInfoForReview[] => { + return rulesToInstall.map((rule) => convertRuleToDiffable(rule)); +}; diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/api/review_rule_upgrade/route.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/api/review_rule_upgrade/route.ts new file mode 100644 index 0000000000000..3b52cc102110e --- /dev/null +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/api/review_rule_upgrade/route.ts @@ -0,0 +1,161 @@ +/* + * 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 { pickBy } from 'lodash'; +import { transformError } from '@kbn/securitysolution-es-utils'; + +import type { PrebuiltRuleAsset } from '../../model/rule_assets/prebuilt_rule_asset'; +import { REVIEW_RULE_UPGRADE_URL } from '../../../../../../common/detection_engine/prebuilt_rules'; +import type { + ReviewRuleUpgradeResponseBody, + RuleUpgradeInfoForReview, + RuleUpgradeStatsForReview, +} from '../../../../../../common/detection_engine/prebuilt_rules/api/review_rule_upgrade/response_schema'; +import type { PrebuiltRuleVersionInfo } from '../../model/rule_versions/prebuilt_rule_version_info'; +import type { + CalculateRuleDiffArgs, + CalculateRuleDiffResult, +} from '../../logic/diff/calculate_rule_diff'; +import { calculateRuleDiff } from '../../logic/diff/calculate_rule_diff'; +import type { ThreeWayDiff } from '../../../../../../common/detection_engine/prebuilt_rules/model/diff/three_way_diff/three_way_diff'; +import type { RuleResponse } from '../../../../../../common/detection_engine/rule_schema'; + +import type { SecuritySolutionPluginRouter } from '../../../../../types'; +import { buildSiemResponse } from '../../../routes/utils'; + +import { createPrebuiltRuleAssetsClient } from '../../logic/rule_assets/prebuilt_rule_assets_client'; +import { createPrebuiltRuleObjectsClient } from '../../logic/rule_objects/prebuilt_rule_objects_client'; +import { getVersionBuckets } from '../../model/rule_versions/get_version_buckets'; + +export const reviewRuleUpgradeRoute = (router: SecuritySolutionPluginRouter) => { + router.post( + { + path: REVIEW_RULE_UPGRADE_URL, + validate: {}, + options: { + tags: ['access:securitySolution'], + }, + }, + async (context, request, response) => { + const siemResponse = buildSiemResponse(response); + + try { + const ctx = await context.resolve(['core', 'alerting']); + const soClient = ctx.core.savedObjects.client; + const rulesClient = ctx.alerting.getRulesClient(); + const ruleAssetsClient = createPrebuiltRuleAssetsClient(soClient); + const ruleObjectsClient = createPrebuiltRuleObjectsClient(rulesClient); + + const [latestVersions, { installedVersions, installedRules }] = await Promise.all([ + ruleAssetsClient.fetchLatestVersions(), + ruleObjectsClient.fetchInstalledRules(), + ]); + + const versionBuckets = getVersionBuckets({ + latestVersions, + installedVersions, + }); + + const [baseRules, latestRules] = await Promise.all([ + ruleAssetsClient.fetchAssetsByVersionInfo(versionBuckets.installedVersionsToUpgrade), + ruleAssetsClient.fetchAssetsByVersionInfo(versionBuckets.latestVersionsToUpgrade), + ]); + + const ruleDiffCalculationArgs = getRuleDiffCalculationArgs( + versionBuckets.installedVersionsToUpgrade, + installedRules, + baseRules, + latestRules + ); + const ruleDiffCalculationResults = ruleDiffCalculationArgs.map((args) => { + return calculateRuleDiff(args); + }); + + const body: ReviewRuleUpgradeResponseBody = { + status_code: 200, + message: 'OK', + attributes: { + stats: calculateRuleStats(ruleDiffCalculationResults), + rules: calculateRuleInfos(ruleDiffCalculationResults), + }, + }; + + return response.ok({ body }); + } catch (err) { + const error = transformError(err); + return siemResponse.error({ + body: error.message, + statusCode: error.statusCode, + }); + } + } + ); +}; + +const getRuleDiffCalculationArgs = ( + installedVersionsToUpgrade: PrebuiltRuleVersionInfo[], + installedRules: RuleResponse[], + baseRules: PrebuiltRuleAsset[], + latestRules: PrebuiltRuleAsset[] +): CalculateRuleDiffArgs[] => { + const installedRulesMap = new Map(installedRules.map((r) => [r.rule_id, r])); + const baseRulesMap = new Map(baseRules.map((r) => [r.rule_id, r])); + const latestRulesMap = new Map(latestRules.map((r) => [r.rule_id, r])); + + const result: CalculateRuleDiffArgs[] = []; + + installedVersionsToUpgrade.forEach((versionToUpgrade) => { + const ruleId = versionToUpgrade.rule_id; + const installedRule = installedRulesMap.get(ruleId); + const baseRule = baseRulesMap.get(ruleId); + const latestRule = latestRulesMap.get(ruleId); + + // TODO: https://github.com/elastic/kibana/issues/148189 + // Make base versions optional for diff calculation. We need to support this in order to be able + // to still show diffs for rule assets coming from packages without historical versions. + if (installedRule != null && baseRule != null && latestRule != null) { + result.push({ + currentVersion: installedRule, + baseVersion: baseRule, + targetVersion: latestRule, + }); + } + }); + + return result; +}; + +const calculateRuleStats = (results: CalculateRuleDiffResult[]): RuleUpgradeStatsForReview => { + return { + num_rules_to_upgrade_total: results.length, + num_rules_to_upgrade_not_customized: results.length, + num_rules_to_upgrade_customized: 0, + tags: [], + fields: [], + }; +}; + +const calculateRuleInfos = (results: CalculateRuleDiffResult[]): RuleUpgradeInfoForReview[] => { + return results.map((result) => { + const { ruleDiff, ruleVersions } = result; + const installedCurrentVersion = ruleVersions.input.current; + const diffableCurrentVersion = ruleVersions.output.current; + + return { + id: installedCurrentVersion.id, + rule_id: installedCurrentVersion.rule_id, + rule: diffableCurrentVersion, + diff: { + fields: pickBy>( + ruleDiff.fields, + (fieldDiff) => fieldDiff.has_update || fieldDiff.has_conflict + ), + has_conflict: ruleDiff.has_conflict, + }, + }; + }); +}; diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/index.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/index.ts index 30a12c10bdb90..1181e5b174480 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/index.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/index.ts @@ -6,4 +6,6 @@ */ export { createPrepackagedRules } from './api/install_prebuilt_rules_and_timelines/route'; -export * from './api/register_routes'; +export { registerPrebuiltRulesRoutes } from './api/register_routes'; +export { prebuiltRuleAssetType } from './logic/rule_assets/prebuilt_rule_assets_type'; +export { PrebuiltRuleAsset } from './model/rule_assets/prebuilt_rule_asset'; diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/logic/diff/calculate_rule_diff.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/logic/diff/calculate_rule_diff.ts new file mode 100644 index 0000000000000..026589ad0ca0d --- /dev/null +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/logic/diff/calculate_rule_diff.ts @@ -0,0 +1,95 @@ +/* + * 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 type { DiffableRule } from '../../../../../../common/detection_engine/prebuilt_rules/model/diff/diffable_rule/diffable_rule'; +import type { FullRuleDiff } from '../../../../../../common/detection_engine/prebuilt_rules/model/diff/rule_diff/rule_diff'; +import type { ThreeWayDiff } from '../../../../../../common/detection_engine/prebuilt_rules/model/diff/three_way_diff/three_way_diff'; +import type { RuleResponse } from '../../../../../../common/detection_engine/rule_schema'; +import type { PrebuiltRuleAsset } from '../../model/rule_assets/prebuilt_rule_asset'; + +import { calculateRuleFieldsDiff } from './calculation/calculate_rule_fields_diff'; +import { convertRuleToDiffable } from './normalization/convert_rule_to_diffable'; + +export interface CalculateRuleDiffArgs { + currentVersion: RuleResponse; + baseVersion: PrebuiltRuleAsset; + targetVersion: PrebuiltRuleAsset; +} + +export interface CalculateRuleDiffResult { + ruleDiff: FullRuleDiff; + ruleVersions: { + input: { + current: RuleResponse; + base: PrebuiltRuleAsset; + target: PrebuiltRuleAsset; + }; + output: { + current: DiffableRule; + base: DiffableRule; + target: DiffableRule; + }; + }; +} + +/** + * Calculates a rule diff for a given set of 3 versions of the rule: + * - currenly installed version + * - base version that is the corresponding stock rule content + * - target version which is the stock rule content the user wants to update the rule to + */ +export const calculateRuleDiff = (args: CalculateRuleDiffArgs): CalculateRuleDiffResult => { + /* + 1. Convert current, base and target versions to `DiffableRule`. + 2. Calculate a `RuleFieldsDiff`. For every top-level field of `DiffableRule`: + 2.1. Pick a code path based on the rule type. + 2.2. Pick a concrete diff algorithm (function) per rule field based on the field name or type. + - one algo for rule name and other simple string fields + - another one for tags and other arrays of keywords + - another one for multiline text fields (investigation guide, setup guide, etc) + - another one for `data_source` + - etc + 2.3. Call the picked diff function to get a `ThreeWayDiff` result + 2.4. Add the result to the `RuleFieldsDiff` object as a key-value pair "fieldName: ThreeWayDiff". + 3. Create and return a result based on the `RuleFieldsDiff`. + */ + + const { baseVersion, currentVersion, targetVersion } = args; + + const diffableBaseVersion = convertRuleToDiffable(baseVersion); + const diffableCurrentVersion = convertRuleToDiffable(currentVersion); + const diffableTargetVersion = convertRuleToDiffable(targetVersion); + + const fieldsDiff = calculateRuleFieldsDiff({ + base_version: diffableBaseVersion, + current_version: diffableCurrentVersion, + target_version: diffableTargetVersion, + }); + + const hasAnyFieldConflict = Object.values>(fieldsDiff).some( + (fieldDiff) => fieldDiff.has_conflict + ); + + return { + ruleDiff: { + fields: fieldsDiff, + has_conflict: hasAnyFieldConflict, + }, + ruleVersions: { + input: { + current: currentVersion, + base: baseVersion, + target: targetVersion, + }, + output: { + current: diffableCurrentVersion, + base: diffableBaseVersion, + target: diffableTargetVersion, + }, + }, + }; +}; diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/logic/diff/calculation/algorithms/simple_diff_algorithm.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/logic/diff/calculation/algorithms/simple_diff_algorithm.ts new file mode 100644 index 0000000000000..699f8f20bcf1f --- /dev/null +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/logic/diff/calculation/algorithms/simple_diff_algorithm.ts @@ -0,0 +1,87 @@ +/* + * 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 { assertUnreachable } from '../../../../../../../../common/utility_types'; +import type { + ThreeVersionsOf, + ThreeWayDiff, +} from '../../../../../../../../common/detection_engine/prebuilt_rules/model/diff/three_way_diff/three_way_diff'; +import { + determineDiffOutcome, + determineIfValueCanUpdate, + ThreeWayDiffOutcome, +} from '../../../../../../../../common/detection_engine/prebuilt_rules/model/diff/three_way_diff/three_way_diff_outcome'; +import { ThreeWayMergeOutcome } from '../../../../../../../../common/detection_engine/prebuilt_rules/model/diff/three_way_diff/three_way_merge_outcome'; + +export const simpleDiffAlgorithm = ( + versions: ThreeVersionsOf +): ThreeWayDiff => { + const { + base_version: baseVersion, + current_version: currentVersion, + target_version: targetVersion, + } = versions; + + const diffOutcome = determineDiffOutcome(baseVersion, currentVersion, targetVersion); + const valueCanUpdate = determineIfValueCanUpdate(diffOutcome); + + const { mergeOutcome, mergedVersion } = mergeVersions( + baseVersion, + currentVersion, + targetVersion, + diffOutcome + ); + + return { + base_version: baseVersion, + current_version: currentVersion, + target_version: targetVersion, + merged_version: mergedVersion, + + diff_outcome: diffOutcome, + merge_outcome: mergeOutcome, + has_update: valueCanUpdate, + has_conflict: mergeOutcome === ThreeWayMergeOutcome.Conflict, + }; +}; + +interface MergeResult { + mergeOutcome: ThreeWayMergeOutcome; + mergedVersion: TValue; +} + +const mergeVersions = ( + baseVersion: TValue, + currentVersion: TValue, + targetVersion: TValue, + diffOutcome: ThreeWayDiffOutcome +): MergeResult => { + switch (diffOutcome) { + case ThreeWayDiffOutcome.StockValueNoUpdate: + case ThreeWayDiffOutcome.CustomizedValueNoUpdate: + case ThreeWayDiffOutcome.CustomizedValueSameUpdate: { + return { + mergeOutcome: ThreeWayMergeOutcome.Current, + mergedVersion: currentVersion, + }; + } + case ThreeWayDiffOutcome.StockValueCanUpdate: { + return { + mergeOutcome: ThreeWayMergeOutcome.Target, + mergedVersion: targetVersion, + }; + } + case ThreeWayDiffOutcome.CustomizedValueCanUpdate: { + return { + mergeOutcome: ThreeWayMergeOutcome.Conflict, + mergedVersion: targetVersion, + }; + } + default: + return assertUnreachable(diffOutcome); + } +}; diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/logic/diff/calculation/calculate_rule_fields_diff.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/logic/diff/calculation/calculate_rule_fields_diff.ts new file mode 100644 index 0000000000000..bb7a83ff20b23 --- /dev/null +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/logic/diff/calculation/calculate_rule_fields_diff.ts @@ -0,0 +1,263 @@ +/* + * 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 { assertUnreachable } from '../../../../../../../common/utility_types'; +import { invariant } from '../../../../../../../common/utils/invariant'; + +import type { + DiffableCommonFields, + DiffableCustomQueryFields, + DiffableEqlFields, + DiffableMachineLearningFields, + DiffableNewTermsFields, + DiffableRule, + DiffableSavedQueryFields, + DiffableThreatMatchFields, + DiffableThresholdFields, +} from '../../../../../../../common/detection_engine/prebuilt_rules/model/diff/diffable_rule/diffable_rule'; +import type { + CommonFieldsDiff, + CustomQueryFieldsDiff, + EqlFieldsDiff, + MachineLearningFieldsDiff, + NewTermsFieldsDiff, + RuleFieldsDiff, + SavedQueryFieldsDiff, + ThreatMatchFieldsDiff, + ThresholdFieldsDiff, +} from '../../../../../../../common/detection_engine/prebuilt_rules/model/diff/rule_diff/rule_diff'; + +import type { FieldsDiffAlgorithmsFor } from '../../../../../../../common/detection_engine/prebuilt_rules/model/diff/rule_diff/fields_diff'; +import type { ThreeVersionsOf } from '../../../../../../../common/detection_engine/prebuilt_rules/model/diff/three_way_diff/three_way_diff'; +import { calculateFieldsDiffFor } from './diff_calculation_helpers'; +import { simpleDiffAlgorithm } from './algorithms/simple_diff_algorithm'; + +const BASE_TYPE_ERROR = `Base version can't be of different rule type`; +const TARGET_TYPE_ERROR = `Target version can't be of different rule type`; + +/** + * Calculates a three-way diff per each top-level rule field. + * Returns an object which keys are equal to rule's field names and values are + * three-way diffs calculated for those fields. + */ +export const calculateRuleFieldsDiff = ( + ruleVersions: ThreeVersionsOf +): RuleFieldsDiff => { + validateRuleVersions(ruleVersions); + + const commonFieldsDiff = calculateCommonFieldsDiff(ruleVersions); + // eslint-disable-next-line @typescript-eslint/naming-convention + const { base_version, current_version, target_version } = ruleVersions; + + switch (current_version.type) { + case 'query': { + invariant(base_version.type === 'query', BASE_TYPE_ERROR); + invariant(target_version.type === 'query', TARGET_TYPE_ERROR); + return { + ...commonFieldsDiff, + ...calculateCustomQueryFieldsDiff({ base_version, current_version, target_version }), + }; + } + case 'saved_query': { + invariant(base_version.type === 'saved_query', BASE_TYPE_ERROR); + invariant(target_version.type === 'saved_query', TARGET_TYPE_ERROR); + return { + ...commonFieldsDiff, + ...calculateSavedQueryFieldsDiff({ base_version, current_version, target_version }), + }; + } + case 'eql': { + invariant(base_version.type === 'eql', BASE_TYPE_ERROR); + invariant(target_version.type === 'eql', TARGET_TYPE_ERROR); + return { + ...commonFieldsDiff, + ...calculateEqlFieldsDiff({ base_version, current_version, target_version }), + }; + } + case 'threat_match': { + invariant(base_version.type === 'threat_match', BASE_TYPE_ERROR); + invariant(target_version.type === 'threat_match', TARGET_TYPE_ERROR); + return { + ...commonFieldsDiff, + ...calculateThreatMatchFieldsDiff({ base_version, current_version, target_version }), + }; + } + case 'threshold': { + invariant(base_version.type === 'threshold', BASE_TYPE_ERROR); + invariant(target_version.type === 'threshold', TARGET_TYPE_ERROR); + return { + ...commonFieldsDiff, + ...calculateThresholdFieldsDiff({ base_version, current_version, target_version }), + }; + } + case 'machine_learning': { + invariant(base_version.type === 'machine_learning', BASE_TYPE_ERROR); + invariant(target_version.type === 'machine_learning', TARGET_TYPE_ERROR); + return { + ...commonFieldsDiff, + ...calculateMachineLearningFieldsDiff({ base_version, current_version, target_version }), + }; + } + case 'new_terms': { + invariant(base_version.type === 'new_terms', BASE_TYPE_ERROR); + invariant(target_version.type === 'new_terms', TARGET_TYPE_ERROR); + return { + ...commonFieldsDiff, + ...calculateNewTermsFieldsDiff({ base_version, current_version, target_version }), + }; + } + default: { + return assertUnreachable(current_version, 'Unhandled rule type'); + } + } +}; + +const validateRuleVersions = (ruleVersions: ThreeVersionsOf): void => { + // eslint-disable-next-line @typescript-eslint/naming-convention + const { base_version, current_version, target_version } = ruleVersions; + const types = new Set([base_version.type, current_version.type, target_version.type]); + + if (types.size > 1) { + throw new Error('Cannot change rule type during rule upgrade'); + } +}; + +const calculateCommonFieldsDiff = ( + fieldsVersions: ThreeVersionsOf +): CommonFieldsDiff => { + return calculateFieldsDiffFor(fieldsVersions, commonFieldsDiffAlgorithms); +}; + +const commonFieldsDiffAlgorithms: FieldsDiffAlgorithmsFor = { + rule_id: simpleDiffAlgorithm, + version: simpleDiffAlgorithm, + meta: simpleDiffAlgorithm, + name: simpleDiffAlgorithm, + tags: simpleDiffAlgorithm, + description: simpleDiffAlgorithm, + severity: simpleDiffAlgorithm, + severity_mapping: simpleDiffAlgorithm, + risk_score: simpleDiffAlgorithm, + risk_score_mapping: simpleDiffAlgorithm, + references: simpleDiffAlgorithm, + false_positives: simpleDiffAlgorithm, + threat: simpleDiffAlgorithm, + note: simpleDiffAlgorithm, + setup: simpleDiffAlgorithm, + related_integrations: simpleDiffAlgorithm, + required_fields: simpleDiffAlgorithm, + author: simpleDiffAlgorithm, + license: simpleDiffAlgorithm, + rule_schedule: simpleDiffAlgorithm, + actions: simpleDiffAlgorithm, + throttle: simpleDiffAlgorithm, + exceptions_list: simpleDiffAlgorithm, + max_signals: simpleDiffAlgorithm, + rule_name_override: simpleDiffAlgorithm, + timestamp_override: simpleDiffAlgorithm, + timeline_template: simpleDiffAlgorithm, + building_block: simpleDiffAlgorithm, +}; + +const calculateCustomQueryFieldsDiff = ( + fieldsVersions: ThreeVersionsOf +): CustomQueryFieldsDiff => { + return calculateFieldsDiffFor(fieldsVersions, customQueryFieldsDiffAlgorithms); +}; + +const customQueryFieldsDiffAlgorithms: FieldsDiffAlgorithmsFor = { + type: simpleDiffAlgorithm, + data_query: simpleDiffAlgorithm, + data_source: simpleDiffAlgorithm, + alert_suppression: simpleDiffAlgorithm, +}; + +const calculateSavedQueryFieldsDiff = ( + fieldsVersions: ThreeVersionsOf +): SavedQueryFieldsDiff => { + return calculateFieldsDiffFor(fieldsVersions, savedQueryFieldsDiffAlgorithms); +}; + +const savedQueryFieldsDiffAlgorithms: FieldsDiffAlgorithmsFor = { + type: simpleDiffAlgorithm, + data_query: simpleDiffAlgorithm, + data_source: simpleDiffAlgorithm, + alert_suppression: simpleDiffAlgorithm, +}; + +const calculateEqlFieldsDiff = ( + fieldsVersions: ThreeVersionsOf +): EqlFieldsDiff => { + return calculateFieldsDiffFor(fieldsVersions, eqlFieldsDiffAlgorithms); +}; + +const eqlFieldsDiffAlgorithms: FieldsDiffAlgorithmsFor = { + type: simpleDiffAlgorithm, + data_query: simpleDiffAlgorithm, + data_source: simpleDiffAlgorithm, + event_category_override: simpleDiffAlgorithm, + timestamp_field: simpleDiffAlgorithm, + tiebreaker_field: simpleDiffAlgorithm, +}; + +const calculateThreatMatchFieldsDiff = ( + fieldsVersions: ThreeVersionsOf +): ThreatMatchFieldsDiff => { + return calculateFieldsDiffFor(fieldsVersions, threatMatchFieldsDiffAlgorithms); +}; + +const threatMatchFieldsDiffAlgorithms: FieldsDiffAlgorithmsFor = { + type: simpleDiffAlgorithm, + data_query: simpleDiffAlgorithm, + data_source: simpleDiffAlgorithm, + threat_query: simpleDiffAlgorithm, + threat_index: simpleDiffAlgorithm, + threat_mapping: simpleDiffAlgorithm, + threat_indicator_path: simpleDiffAlgorithm, + concurrent_searches: simpleDiffAlgorithm, + items_per_search: simpleDiffAlgorithm, +}; + +const calculateThresholdFieldsDiff = ( + fieldsVersions: ThreeVersionsOf +): ThresholdFieldsDiff => { + return calculateFieldsDiffFor(fieldsVersions, thresholdFieldsDiffAlgorithms); +}; + +const thresholdFieldsDiffAlgorithms: FieldsDiffAlgorithmsFor = { + type: simpleDiffAlgorithm, + data_query: simpleDiffAlgorithm, + data_source: simpleDiffAlgorithm, + threshold: simpleDiffAlgorithm, +}; + +const calculateMachineLearningFieldsDiff = ( + fieldsVersions: ThreeVersionsOf +): MachineLearningFieldsDiff => { + return calculateFieldsDiffFor(fieldsVersions, machineLearningFieldsDiffAlgorithms); +}; + +const machineLearningFieldsDiffAlgorithms: FieldsDiffAlgorithmsFor = + { + type: simpleDiffAlgorithm, + machine_learning_job_id: simpleDiffAlgorithm, + anomaly_threshold: simpleDiffAlgorithm, + }; + +const calculateNewTermsFieldsDiff = ( + fieldsVersions: ThreeVersionsOf +): NewTermsFieldsDiff => { + return calculateFieldsDiffFor(fieldsVersions, newTermsFieldsDiffAlgorithms); +}; + +const newTermsFieldsDiffAlgorithms: FieldsDiffAlgorithmsFor = { + type: simpleDiffAlgorithm, + data_query: simpleDiffAlgorithm, + data_source: simpleDiffAlgorithm, + new_terms_fields: simpleDiffAlgorithm, + history_window_start: simpleDiffAlgorithm, +}; diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/logic/diff/calculation/diff_calculation_helpers.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/logic/diff/calculation/diff_calculation_helpers.ts new file mode 100644 index 0000000000000..eb1b6e2d38aca --- /dev/null +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/logic/diff/calculation/diff_calculation_helpers.ts @@ -0,0 +1,38 @@ +/* + * 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 { mapValues } from 'lodash'; +import type { + FieldsDiff, + FieldsDiffAlgorithmsFor, +} from '../../../../../../../common/detection_engine/prebuilt_rules/model/diff/rule_diff/fields_diff'; +import type { ThreeVersionsOf } from '../../../../../../../common/detection_engine/prebuilt_rules/model/diff/three_way_diff/three_way_diff'; + +export const calculateFieldsDiffFor = ( + objectVersions: ThreeVersionsOf, + fieldsDiffAlgorithms: FieldsDiffAlgorithmsFor +): FieldsDiff => { + const result = mapValues(fieldsDiffAlgorithms, (calculateFieldDiff, fieldName) => { + const fieldVersions = pickField(fieldName as keyof TObject, objectVersions); + const fieldDiff = calculateFieldDiff(fieldVersions); + return fieldDiff; + }); + + // TODO: try to improve strict typing and get rid of this "as" operator. + return result as FieldsDiff; +}; + +const pickField = ( + fieldName: keyof TObject, + versions: ThreeVersionsOf +): ThreeVersionsOf => { + return { + base_version: versions.base_version[fieldName], + current_version: versions.current_version[fieldName], + target_version: versions.target_version[fieldName], + }; +}; diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/logic/diff/normalization/convert_rule_to_diffable.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/logic/diff/normalization/convert_rule_to_diffable.ts new file mode 100644 index 0000000000000..f917b7154a163 --- /dev/null +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/logic/diff/normalization/convert_rule_to_diffable.ts @@ -0,0 +1,230 @@ +/* + * 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 { DEFAULT_MAX_SIGNALS } from '../../../../../../../common/constants'; +import { assertUnreachable } from '../../../../../../../common/utility_types'; +import type { + EqlRule, + EqlRuleCreateProps, + MachineLearningRule, + MachineLearningRuleCreateProps, + NewTermsRule, + NewTermsRuleCreateProps, + QueryRule, + QueryRuleCreateProps, + RuleResponse, + SavedQueryRule, + SavedQueryRuleCreateProps, + ThreatMatchRule, + ThreatMatchRuleCreateProps, + ThresholdRule, + ThresholdRuleCreateProps, +} from '../../../../../../../common/detection_engine/rule_schema'; +import type { PrebuiltRuleAsset } from '../../../model/rule_assets/prebuilt_rule_asset'; +import type { + DiffableCommonFields, + DiffableCustomQueryFields, + DiffableEqlFields, + DiffableMachineLearningFields, + DiffableNewTermsFields, + DiffableRule, + DiffableSavedQueryFields, + DiffableThreatMatchFields, + DiffableThresholdFields, +} from '../../../../../../../common/detection_engine/prebuilt_rules/model/diff/diffable_rule/diffable_rule'; +import { extractBuildingBlockObject } from './extract_building_block_object'; +import { + extractInlineKqlQuery, + extractRuleEqlQuery, + extractRuleKqlQuery, +} from './extract_rule_data_query'; +import { extractRuleDataSource } from './extract_rule_data_source'; +import { extractRuleNameOverrideObject } from './extract_rule_name_override_object'; +import { extractRuleSchedule } from './extract_rule_schedule'; +import { extractTimelineTemplateReference } from './extract_timeline_template_reference'; +import { extractTimestampOverrideObject } from './extract_timestamp_override_object'; + +/** + * Normalizes a given rule to the form which is suitable for passing to the diff algorithm. + * Read more in the JSDoc description of DiffableRule. + */ +export const convertRuleToDiffable = (rule: RuleResponse | PrebuiltRuleAsset): DiffableRule => { + const commonFields = extractDiffableCommonFields(rule); + + switch (rule.type) { + case 'query': + return { + ...commonFields, + ...extractDiffableCustomQueryFields(rule), + }; + case 'saved_query': + return { + ...commonFields, + ...extractDiffableSavedQueryFieldsFromRuleObject(rule), + }; + case 'eql': + return { + ...commonFields, + ...extractDiffableEqlFieldsFromRuleObject(rule), + }; + case 'threat_match': + return { + ...commonFields, + ...extractDiffableThreatMatchFieldsFromRuleObject(rule), + }; + case 'threshold': + return { + ...commonFields, + ...extractDiffableThresholdFieldsFromRuleObject(rule), + }; + case 'machine_learning': + return { + ...commonFields, + ...extractDiffableMachineLearningFieldsFromRuleObject(rule), + }; + case 'new_terms': + return { + ...commonFields, + ...extractDiffableNewTermsFieldsFromRuleObject(rule), + }; + default: + return assertUnreachable(rule, 'Unhandled rule type'); + } +}; + +const extractDiffableCommonFields = ( + rule: RuleResponse | PrebuiltRuleAsset +): DiffableCommonFields => { + return { + // --------------------- REQUIRED FIELDS + // Technical fields + rule_id: rule.rule_id, + version: rule.version, + meta: rule.meta ?? {}, + + // Main domain fields + name: rule.name, + tags: rule.tags ?? [], + description: rule.description, + severity: rule.severity, + severity_mapping: rule.severity_mapping ?? [], + risk_score: rule.risk_score, + risk_score_mapping: rule.risk_score_mapping ?? [], + + // About -> Advanced settings + references: rule.references ?? [], + false_positives: rule.false_positives ?? [], + threat: rule.threat ?? [], + note: rule.note ?? '', + setup: rule.setup ?? '', + related_integrations: rule.related_integrations ?? [], + required_fields: rule.required_fields ?? [], + author: rule.author ?? [], + license: rule.license ?? '', + + // Other domain fields + rule_schedule: extractRuleSchedule(rule), + actions: rule.actions ?? [], + throttle: rule.throttle ?? 'no_actions', + exceptions_list: rule.exceptions_list ?? [], + max_signals: rule.max_signals ?? DEFAULT_MAX_SIGNALS, + + // --------------------- OPTIONAL FIELDS + rule_name_override: extractRuleNameOverrideObject(rule), + timestamp_override: extractTimestampOverrideObject(rule), + timeline_template: extractTimelineTemplateReference(rule), + building_block: extractBuildingBlockObject(rule), + }; +}; + +const extractDiffableCustomQueryFields = ( + rule: QueryRule | QueryRuleCreateProps +): DiffableCustomQueryFields => { + return { + type: rule.type, + data_query: extractRuleKqlQuery(rule.query, rule.language, rule.filters, rule.saved_id), + data_source: extractRuleDataSource(rule.index, rule.data_view_id), + alert_suppression: rule.alert_suppression, + }; +}; + +const extractDiffableSavedQueryFieldsFromRuleObject = ( + rule: SavedQueryRule | SavedQueryRuleCreateProps +): DiffableSavedQueryFields => { + return { + type: rule.type, + data_query: extractRuleKqlQuery(rule.query, rule.language, rule.filters, rule.saved_id), + data_source: extractRuleDataSource(rule.index, rule.data_view_id), + alert_suppression: rule.alert_suppression, + }; +}; + +const extractDiffableEqlFieldsFromRuleObject = ( + rule: EqlRule | EqlRuleCreateProps +): DiffableEqlFields => { + return { + type: rule.type, + data_query: extractRuleEqlQuery(rule.query, rule.language, rule.filters), + data_source: extractRuleDataSource(rule.index, rule.data_view_id), + event_category_override: rule.event_category_override, + timestamp_field: rule.timestamp_field, + tiebreaker_field: rule.tiebreaker_field, + }; +}; + +const extractDiffableThreatMatchFieldsFromRuleObject = ( + rule: ThreatMatchRule | ThreatMatchRuleCreateProps +): DiffableThreatMatchFields => { + return { + type: rule.type, + data_query: extractRuleKqlQuery(rule.query, rule.language, rule.filters, rule.saved_id), + data_source: extractRuleDataSource(rule.index, rule.data_view_id), + threat_query: extractInlineKqlQuery( + rule.threat_query, + rule.threat_language, + rule.threat_filters + ), + threat_index: rule.threat_index, + threat_mapping: rule.threat_mapping, + threat_indicator_path: rule.threat_indicator_path, + concurrent_searches: rule.concurrent_searches, + items_per_search: rule.items_per_search, + }; +}; + +const extractDiffableThresholdFieldsFromRuleObject = ( + rule: ThresholdRule | ThresholdRuleCreateProps +): DiffableThresholdFields => { + return { + type: rule.type, + data_query: extractRuleKqlQuery(rule.query, rule.language, rule.filters, rule.saved_id), + data_source: extractRuleDataSource(rule.index, rule.data_view_id), + threshold: rule.threshold, + }; +}; + +const extractDiffableMachineLearningFieldsFromRuleObject = ( + rule: MachineLearningRule | MachineLearningRuleCreateProps +): DiffableMachineLearningFields => { + return { + type: rule.type, + machine_learning_job_id: rule.machine_learning_job_id, + anomaly_threshold: rule.anomaly_threshold, + }; +}; + +const extractDiffableNewTermsFieldsFromRuleObject = ( + rule: NewTermsRule | NewTermsRuleCreateProps +): DiffableNewTermsFields => { + return { + type: rule.type, + data_query: extractInlineKqlQuery(rule.query, rule.language, rule.filters), + data_source: extractRuleDataSource(rule.index, rule.data_view_id), + new_terms_fields: rule.new_terms_fields, + history_window_start: rule.history_window_start, + }; +}; diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/logic/diff/normalization/extract_building_block_object.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/logic/diff/normalization/extract_building_block_object.ts new file mode 100644 index 0000000000000..fda8efafea6b5 --- /dev/null +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/logic/diff/normalization/extract_building_block_object.ts @@ -0,0 +1,21 @@ +/* + * 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 type { RuleResponse } from '../../../../../../../common/detection_engine/rule_schema'; +import type { BuildingBlockObject } from '../../../../../../../common/detection_engine/prebuilt_rules/model/diff/diffable_rule/diffable_field_types'; +import type { PrebuiltRuleAsset } from '../../../model/rule_assets/prebuilt_rule_asset'; + +export const extractBuildingBlockObject = ( + rule: RuleResponse | PrebuiltRuleAsset +): BuildingBlockObject | undefined => { + if (rule.building_block_type == null) { + return undefined; + } + return { + type: rule.building_block_type, + }; +}; diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/logic/diff/normalization/extract_rule_data_query.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/logic/diff/normalization/extract_rule_data_query.ts new file mode 100644 index 0000000000000..3672dddca4e64 --- /dev/null +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/logic/diff/normalization/extract_rule_data_query.ts @@ -0,0 +1,60 @@ +/* + * 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 type { + EqlQueryLanguage, + KqlQueryLanguage, + RuleFilterArray, + RuleQuery, +} from '../../../../../../../common/detection_engine/rule_schema'; +import type { + InlineKqlQuery, + RuleEqlQuery, + RuleKqlQuery, +} from '../../../../../../../common/detection_engine/prebuilt_rules/model/diff/diffable_rule/diffable_field_types'; +import { KqlQueryType } from '../../../../../../../common/detection_engine/prebuilt_rules/model/diff/diffable_rule/diffable_field_types'; + +export const extractRuleKqlQuery = ( + query: RuleQuery | undefined, + language: KqlQueryLanguage | undefined, + filters: RuleFilterArray | undefined, + savedQueryId: string | undefined +): RuleKqlQuery => { + if (savedQueryId != null) { + return { + type: KqlQueryType.saved_query, + saved_query_id: savedQueryId, + }; + } else { + return extractInlineKqlQuery(query, language, filters); + } +}; + +export const extractInlineKqlQuery = ( + query: RuleQuery | undefined, + language: KqlQueryLanguage | undefined, + filters: RuleFilterArray | undefined +): InlineKqlQuery => { + return { + type: KqlQueryType.inline_query, + query: query ?? '', + language: language ?? 'kuery', + filters: filters ?? [], + }; +}; + +export const extractRuleEqlQuery = ( + query: RuleQuery, + language: EqlQueryLanguage, + filters: RuleFilterArray | undefined +): RuleEqlQuery => { + return { + query, + language, + filters: filters ?? [], + }; +}; diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/logic/diff/normalization/extract_rule_data_source.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/logic/diff/normalization/extract_rule_data_source.ts new file mode 100644 index 0000000000000..3391407e856dc --- /dev/null +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/logic/diff/normalization/extract_rule_data_source.ts @@ -0,0 +1,34 @@ +/* + * 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 type { + DataViewId, + IndexPatternArray, +} from '../../../../../../../common/detection_engine/rule_schema'; +import type { RuleDataSource } from '../../../../../../../common/detection_engine/prebuilt_rules/model/diff/diffable_rule/diffable_field_types'; +import { DataSourceType } from '../../../../../../../common/detection_engine/prebuilt_rules/model/diff/diffable_rule/diffable_field_types'; + +export const extractRuleDataSource = ( + indexPatterns: IndexPatternArray | undefined, + dataViewId: DataViewId | undefined +): RuleDataSource | undefined => { + if (indexPatterns != null) { + return { + type: DataSourceType.index_patterns, + index_patterns: indexPatterns, + }; + } + + if (dataViewId != null) { + return { + type: DataSourceType.data_view, + data_view_id: dataViewId, + }; + } + + return undefined; +}; diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/logic/diff/normalization/extract_rule_name_override_object.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/logic/diff/normalization/extract_rule_name_override_object.ts new file mode 100644 index 0000000000000..e1e95c12cc9c0 --- /dev/null +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/logic/diff/normalization/extract_rule_name_override_object.ts @@ -0,0 +1,21 @@ +/* + * 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 type { RuleResponse } from '../../../../../../../common/detection_engine/rule_schema'; +import type { RuleNameOverrideObject } from '../../../../../../../common/detection_engine/prebuilt_rules/model/diff/diffable_rule/diffable_field_types'; +import type { PrebuiltRuleAsset } from '../../../model/rule_assets/prebuilt_rule_asset'; + +export const extractRuleNameOverrideObject = ( + rule: RuleResponse | PrebuiltRuleAsset +): RuleNameOverrideObject | undefined => { + if (rule.rule_name_override == null) { + return undefined; + } + return { + field_name: rule.rule_name_override, + }; +}; diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/logic/diff/normalization/extract_rule_schedule.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/logic/diff/normalization/extract_rule_schedule.ts new file mode 100644 index 0000000000000..21d81d218680c --- /dev/null +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/logic/diff/normalization/extract_rule_schedule.ts @@ -0,0 +1,86 @@ +/* + * 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 moment from 'moment'; +import dateMath from '@elastic/datemath'; +import { parseDuration } from '@kbn/alerting-plugin/common'; + +import type { RuleResponse } from '../../../../../../../common/detection_engine/rule_schema'; +import type { RuleSchedule } from '../../../../../../../common/detection_engine/prebuilt_rules/model/diff/diffable_rule/diffable_field_types'; +import type { PrebuiltRuleAsset } from '../../../model/rule_assets/prebuilt_rule_asset'; + +export const extractRuleSchedule = (rule: RuleResponse | PrebuiltRuleAsset): RuleSchedule => { + const interval = rule.interval ?? '5m'; + const from = rule.from ?? 'now-6m'; + const to = rule.to ?? 'now'; + + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const ruleMeta = (rule.meta ?? {}) as any; + const lookbackFromMeta = String(ruleMeta.from ?? ''); + + const intervalDuration = parseInterval(interval); + const lookbackFromMetaDuration = parseInterval(lookbackFromMeta); + const driftToleranceDuration = parseDriftTolerance(from, to); + + if (lookbackFromMetaDuration != null) { + if (intervalDuration != null) { + return { + interval, + lookback: lookbackFromMeta, + }; + } + return { + interval: `Cannot parse: interval="${interval}"`, + lookback: lookbackFromMeta, + }; + } + + if (intervalDuration == null) { + return { + interval: `Cannot parse: interval="${interval}"`, + lookback: `Cannot calculate due to invalid interval`, + }; + } + + if (driftToleranceDuration == null) { + return { + interval, + lookback: `Cannot parse: from="${from}", to="${to}"`, + }; + } + + const lookbackDuration = moment.duration().add(driftToleranceDuration).subtract(intervalDuration); + const lookback = `${lookbackDuration.asSeconds()}s`; + + return { interval, lookback }; +}; + +const parseInterval = (intervalString: string): moment.Duration | null => { + try { + const milliseconds = parseDuration(intervalString); + return moment.duration(milliseconds); + } catch (e) { + return null; + } +}; + +const parseDriftTolerance = (from: string, to: string): moment.Duration | null => { + const now = new Date(); + const fromDate = parseDateMathString(from, now); + const toDate = parseDateMathString(to, now); + + if (fromDate == null || toDate == null) { + return null; + } + + return moment.duration(toDate.diff(fromDate)); +}; + +const parseDateMathString = (dateMathString: string, now: Date): moment.Moment | null => { + const parsedDate = dateMath.parse(dateMathString, { forceNow: now }); + return parsedDate != null && parsedDate.isValid() ? parsedDate : null; +}; diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/logic/diff/normalization/extract_timeline_template_reference.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/logic/diff/normalization/extract_timeline_template_reference.ts new file mode 100644 index 0000000000000..f213f8a5a2189 --- /dev/null +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/logic/diff/normalization/extract_timeline_template_reference.ts @@ -0,0 +1,22 @@ +/* + * 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 type { RuleResponse } from '../../../../../../../common/detection_engine/rule_schema'; +import type { TimelineTemplateReference } from '../../../../../../../common/detection_engine/prebuilt_rules/model/diff/diffable_rule/diffable_field_types'; +import type { PrebuiltRuleAsset } from '../../../model/rule_assets/prebuilt_rule_asset'; + +export const extractTimelineTemplateReference = ( + rule: RuleResponse | PrebuiltRuleAsset +): TimelineTemplateReference | undefined => { + if (rule.timeline_id == null) { + return undefined; + } + return { + timeline_id: rule.timeline_id, + timeline_title: rule.timeline_title ?? '', + }; +}; diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/logic/diff/normalization/extract_timestamp_override_object.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/logic/diff/normalization/extract_timestamp_override_object.ts new file mode 100644 index 0000000000000..b311557b7d260 --- /dev/null +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/logic/diff/normalization/extract_timestamp_override_object.ts @@ -0,0 +1,22 @@ +/* + * 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 type { RuleResponse } from '../../../../../../../common/detection_engine/rule_schema'; +import type { TimestampOverrideObject } from '../../../../../../../common/detection_engine/prebuilt_rules/model/diff/diffable_rule/diffable_field_types'; +import type { PrebuiltRuleAsset } from '../../../model/rule_assets/prebuilt_rule_asset'; + +export const extractTimestampOverrideObject = ( + rule: RuleResponse | PrebuiltRuleAsset +): TimestampOverrideObject | undefined => { + if (rule.timestamp_override == null) { + return undefined; + } + return { + field_name: rule.timestamp_override, + fallback_disabled: rule.timestamp_override_fallback_disabled ?? false, + }; +}; diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/logic/get_latest_prebuilt_rules.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/logic/get_latest_prebuilt_rules.ts deleted file mode 100644 index 36fb03c9bac7a..0000000000000 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/logic/get_latest_prebuilt_rules.ts +++ /dev/null @@ -1,52 +0,0 @@ -/* - * 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 { BadRequestError } from '@kbn/securitysolution-es-utils'; -import { exactCheck, formatErrors } from '@kbn/securitysolution-io-ts-utils'; -import { getOrElse } from 'fp-ts/lib/Either'; -import type * as t from 'io-ts'; -import { PrebuiltRuleToInstall } from '../../../../../common/detection_engine/prebuilt_rules'; -import { withSecuritySpan } from '../../../../utils/with_security_span'; -import type { - IRuleAssetSOAttributes, - IRuleAssetsClient, -} from './rule_asset/rule_asset_saved_objects_client'; - -import { prebuiltRulesToMap } from './utils'; - -export const getLatestPrebuiltRules = async ( - ruleAssetsClient: IRuleAssetsClient -): Promise> => - withSecuritySpan('getLatestPrebuiltRules', async () => { - const ruleAssets = await ruleAssetsClient.fetchLatestVersions(); - return prebuiltRulesToMap(validateRuleAssets(ruleAssets)); - }); - -/** - * Validate the rules from Saved Objects created by Fleet. - */ -const validateRuleAssets = (rules: IRuleAssetSOAttributes[]): PrebuiltRuleToInstall[] => { - return rules.map((rule) => { - const decoded = PrebuiltRuleToInstall.decode(rule); - const checked = exactCheck(rule, decoded); - - const onLeft = (errors: t.Errors): PrebuiltRuleToInstall => { - const ruleName = rule.name ? rule.name : '(rule name unknown)'; - const ruleId = rule.rule_id ? rule.rule_id : '(rule rule_id unknown)'; - throw new BadRequestError( - `name: "${ruleName}", rule_id: "${ruleId}" within the security-rule saved object ` + - `is not a valid detection engine rule. Expect the system ` + - `to not work with pre-packaged rules until this rule is fixed ` + - `or the file is removed. Error is: ${formatErrors( - errors - ).join()}, Full rule contents are:\n${JSON.stringify(rule, null, 2)}` - ); - }; - - return getOrElse(onLeft)(checked); - }); -}; diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/logic/get_rules_to_install.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/logic/get_rules_to_install.test.ts index b93ef2cc5178f..fc14ab92bbcbb 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/logic/get_rules_to_install.test.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/logic/get_rules_to_install.test.ts @@ -7,74 +7,65 @@ import { getRulesToInstall } from './get_rules_to_install'; import { getRuleMock } from '../../routes/__mocks__/request_responses'; -import { getPrebuiltRuleMock } from '../../../../../common/detection_engine/prebuilt_rules/mocks'; +import { getPrebuiltRuleMock } from '../mocks'; import { getQueryRuleParams } from '../../rule_schema/mocks'; -import { prebuiltRulesToMap, rulesToMap } from './utils'; +import { rulesToMap } from './utils'; describe('get_rules_to_install', () => { test('should return empty array if both rule sets are empty', () => { - const update = getRulesToInstall(prebuiltRulesToMap([]), rulesToMap([])); + const update = getRulesToInstall([], rulesToMap([])); expect(update).toEqual([]); }); test('should return empty array if the two rule ids match', () => { - const ruleFromFileSystem = getPrebuiltRuleMock(); - ruleFromFileSystem.rule_id = 'rule-1'; + const ruleAsset = getPrebuiltRuleMock(); + ruleAsset.rule_id = 'rule-1'; const installedRule = getRuleMock(getQueryRuleParams()); installedRule.params.ruleId = 'rule-1'; - const update = getRulesToInstall( - prebuiltRulesToMap([ruleFromFileSystem]), - rulesToMap([installedRule]) - ); + const update = getRulesToInstall([ruleAsset], rulesToMap([installedRule])); expect(update).toEqual([]); }); test('should return the rule to install if the id of the two rules do not match', () => { - const ruleFromFileSystem = getPrebuiltRuleMock(); - ruleFromFileSystem.rule_id = 'rule-1'; + const ruleAsset = getPrebuiltRuleMock(); + ruleAsset.rule_id = 'rule-1'; const installedRule = getRuleMock(getQueryRuleParams()); installedRule.params.ruleId = 'rule-2'; - const update = getRulesToInstall( - prebuiltRulesToMap([ruleFromFileSystem]), - rulesToMap([installedRule]) - ); - expect(update).toEqual([ruleFromFileSystem]); + const update = getRulesToInstall([ruleAsset], rulesToMap([installedRule])); + expect(update).toEqual([ruleAsset]); }); test('should return two rules to install if both the ids of the two rules do not match', () => { - const ruleFromFileSystem1 = getPrebuiltRuleMock(); - ruleFromFileSystem1.rule_id = 'rule-1'; + const ruleAsset1 = getPrebuiltRuleMock(); + ruleAsset1.rule_id = 'rule-1'; - const ruleFromFileSystem2 = getPrebuiltRuleMock(); - ruleFromFileSystem2.rule_id = 'rule-2'; + const ruleAsset2 = getPrebuiltRuleMock(); + ruleAsset2.rule_id = 'rule-2'; const installedRule = getRuleMock(getQueryRuleParams()); installedRule.params.ruleId = 'rule-3'; - const update = getRulesToInstall( - prebuiltRulesToMap([ruleFromFileSystem1, ruleFromFileSystem2]), - rulesToMap([installedRule]) - ); - expect(update).toEqual([ruleFromFileSystem1, ruleFromFileSystem2]); + const update = getRulesToInstall([ruleAsset1, ruleAsset2], rulesToMap([installedRule])); + expect(update).toEqual([ruleAsset1, ruleAsset2]); }); test('should return two rules of three to install if both the ids of the two rules do not match but the third does', () => { - const ruleFromFileSystem1 = getPrebuiltRuleMock(); - ruleFromFileSystem1.rule_id = 'rule-1'; + const ruleAsset1 = getPrebuiltRuleMock(); + ruleAsset1.rule_id = 'rule-1'; - const ruleFromFileSystem2 = getPrebuiltRuleMock(); - ruleFromFileSystem2.rule_id = 'rule-2'; + const ruleAsset2 = getPrebuiltRuleMock(); + ruleAsset2.rule_id = 'rule-2'; - const ruleFromFileSystem3 = getPrebuiltRuleMock(); - ruleFromFileSystem3.rule_id = 'rule-3'; + const ruleAsset3 = getPrebuiltRuleMock(); + ruleAsset3.rule_id = 'rule-3'; const installedRule = getRuleMock(getQueryRuleParams()); installedRule.params.ruleId = 'rule-3'; const update = getRulesToInstall( - prebuiltRulesToMap([ruleFromFileSystem1, ruleFromFileSystem2, ruleFromFileSystem3]), + [ruleAsset1, ruleAsset2, ruleAsset3], rulesToMap([installedRule]) ); - expect(update).toEqual([ruleFromFileSystem1, ruleFromFileSystem2]); + expect(update).toEqual([ruleAsset1, ruleAsset2]); }); }); diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/logic/get_rules_to_install.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/logic/get_rules_to_install.ts index 69d76cdc98e4a..d2e6bc4acf7b4 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/logic/get_rules_to_install.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/logic/get_rules_to_install.ts @@ -5,14 +5,12 @@ * 2.0. */ -import type { PrebuiltRuleToInstall } from '../../../../../common/detection_engine/prebuilt_rules'; import type { RuleAlertType } from '../../rule_schema'; +import type { PrebuiltRuleAsset } from '../model/rule_assets/prebuilt_rule_asset'; export const getRulesToInstall = ( - latestPrebuiltRules: Map, + latestPrebuiltRules: PrebuiltRuleAsset[], installedRules: Map ) => { - return Array.from(latestPrebuiltRules.values()).filter( - (rule) => !installedRules.has(rule.rule_id) - ); + return latestPrebuiltRules.filter((rule) => !installedRules.has(rule.rule_id)); }; diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/logic/get_rules_to_update.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/logic/get_rules_to_update.test.ts index bcf85d4095556..89d59ec8f77e3 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/logic/get_rules_to_update.test.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/logic/get_rules_to_update.test.ts @@ -7,82 +7,70 @@ import { filterInstalledRules, getRulesToUpdate, mergeExceptionLists } from './get_rules_to_update'; import { getRuleMock } from '../../routes/__mocks__/request_responses'; -import { getPrebuiltRuleMock } from '../../../../../common/detection_engine/prebuilt_rules/mocks'; +import { getPrebuiltRuleMock } from '../mocks'; import { getQueryRuleParams } from '../../rule_schema/mocks'; -import { prebuiltRulesToMap, rulesToMap } from './utils'; +import { rulesToMap } from './utils'; describe('get_rules_to_update', () => { test('should return empty array if both rule sets are empty', () => { - const update = getRulesToUpdate(prebuiltRulesToMap([]), rulesToMap([])); + const update = getRulesToUpdate([], rulesToMap([])); expect(update).toEqual([]); }); test('should return empty array if the rule_id of the two rules do not match', () => { - const ruleFromFileSystem = getPrebuiltRuleMock(); - ruleFromFileSystem.rule_id = 'rule-1'; - ruleFromFileSystem.version = 2; + const ruleAsset = getPrebuiltRuleMock(); + ruleAsset.rule_id = 'rule-1'; + ruleAsset.version = 2; const installedRule = getRuleMock(getQueryRuleParams()); installedRule.params.ruleId = 'rule-2'; installedRule.params.version = 1; - const update = getRulesToUpdate( - prebuiltRulesToMap([ruleFromFileSystem]), - rulesToMap([installedRule]) - ); + const update = getRulesToUpdate([ruleAsset], rulesToMap([installedRule])); expect(update).toEqual([]); }); test('should return empty array if the version of file system rule is less than the installed version', () => { - const ruleFromFileSystem = getPrebuiltRuleMock(); - ruleFromFileSystem.rule_id = 'rule-1'; - ruleFromFileSystem.version = 1; + const ruleAsset = getPrebuiltRuleMock(); + ruleAsset.rule_id = 'rule-1'; + ruleAsset.version = 1; const installedRule = getRuleMock(getQueryRuleParams()); installedRule.params.ruleId = 'rule-1'; installedRule.params.version = 2; - const update = getRulesToUpdate( - prebuiltRulesToMap([ruleFromFileSystem]), - rulesToMap([installedRule]) - ); + const update = getRulesToUpdate([ruleAsset], rulesToMap([installedRule])); expect(update).toEqual([]); }); test('should return empty array if the version of file system rule is the same as the installed version', () => { - const ruleFromFileSystem = getPrebuiltRuleMock(); - ruleFromFileSystem.rule_id = 'rule-1'; - ruleFromFileSystem.version = 1; + const ruleAsset = getPrebuiltRuleMock(); + ruleAsset.rule_id = 'rule-1'; + ruleAsset.version = 1; const installedRule = getRuleMock(getQueryRuleParams()); installedRule.params.ruleId = 'rule-1'; installedRule.params.version = 1; - const update = getRulesToUpdate( - prebuiltRulesToMap([ruleFromFileSystem]), - rulesToMap([installedRule]) - ); + const update = getRulesToUpdate([ruleAsset], rulesToMap([installedRule])); expect(update).toEqual([]); }); test('should return the rule to update if the version of file system rule is greater than the installed version', () => { - const ruleFromFileSystem = getPrebuiltRuleMock(); - ruleFromFileSystem.rule_id = 'rule-1'; - ruleFromFileSystem.version = 2; + const ruleAsset = getPrebuiltRuleMock(); + ruleAsset.rule_id = 'rule-1'; + ruleAsset.version = 2; const installedRule = getRuleMock(getQueryRuleParams()); installedRule.params.ruleId = 'rule-1'; installedRule.params.version = 1; installedRule.params.exceptionsList = []; - const update = getRulesToUpdate( - prebuiltRulesToMap([ruleFromFileSystem]), - rulesToMap([installedRule]) - ); - expect(update).toEqual([ruleFromFileSystem]); + const update = getRulesToUpdate([ruleAsset], rulesToMap([installedRule])); + expect(update).toEqual([ruleAsset]); }); test('should return 1 rule out of 2 to update if the version of file system rule is greater than the installed version of just one', () => { - const ruleFromFileSystem = getPrebuiltRuleMock(); - ruleFromFileSystem.rule_id = 'rule-1'; - ruleFromFileSystem.version = 2; + const ruleAsset = getPrebuiltRuleMock(); + ruleAsset.rule_id = 'rule-1'; + ruleAsset.version = 2; const installedRule1 = getRuleMock(getQueryRuleParams()); installedRule1.params.ruleId = 'rule-1'; @@ -94,21 +82,18 @@ describe('get_rules_to_update', () => { installedRule2.params.version = 1; installedRule2.params.exceptionsList = []; - const update = getRulesToUpdate( - prebuiltRulesToMap([ruleFromFileSystem]), - rulesToMap([installedRule1, installedRule2]) - ); - expect(update).toEqual([ruleFromFileSystem]); + const update = getRulesToUpdate([ruleAsset], rulesToMap([installedRule1, installedRule2])); + expect(update).toEqual([ruleAsset]); }); test('should return 2 rules out of 2 to update if the version of file system rule is greater than the installed version of both', () => { - const ruleFromFileSystem1 = getPrebuiltRuleMock(); - ruleFromFileSystem1.rule_id = 'rule-1'; - ruleFromFileSystem1.version = 2; + const ruleAsset1 = getPrebuiltRuleMock(); + ruleAsset1.rule_id = 'rule-1'; + ruleAsset1.version = 2; - const ruleFromFileSystem2 = getPrebuiltRuleMock(); - ruleFromFileSystem2.rule_id = 'rule-2'; - ruleFromFileSystem2.version = 2; + const ruleAsset2 = getPrebuiltRuleMock(); + ruleAsset2.rule_id = 'rule-2'; + ruleAsset2.version = 2; const installedRule1 = getRuleMock(getQueryRuleParams()); installedRule1.params.ruleId = 'rule-1'; @@ -121,15 +106,15 @@ describe('get_rules_to_update', () => { installedRule2.params.exceptionsList = []; const update = getRulesToUpdate( - prebuiltRulesToMap([ruleFromFileSystem1, ruleFromFileSystem2]), + [ruleAsset1, ruleAsset2], rulesToMap([installedRule1, installedRule2]) ); - expect(update).toEqual([ruleFromFileSystem1, ruleFromFileSystem2]); + expect(update).toEqual([ruleAsset1, ruleAsset2]); }); test('should add back an exception_list if it was removed by the end user on an immutable rule during an upgrade', () => { - const ruleFromFileSystem1 = getPrebuiltRuleMock(); - ruleFromFileSystem1.exceptions_list = [ + const ruleAsset1 = getPrebuiltRuleMock(); + ruleAsset1.exceptions_list = [ { id: 'endpoint_list', list_id: 'endpoint_list', @@ -137,24 +122,21 @@ describe('get_rules_to_update', () => { type: 'endpoint', }, ]; - ruleFromFileSystem1.rule_id = 'rule-1'; - ruleFromFileSystem1.version = 2; + ruleAsset1.rule_id = 'rule-1'; + ruleAsset1.version = 2; const installedRule1 = getRuleMock(getQueryRuleParams()); installedRule1.params.ruleId = 'rule-1'; installedRule1.params.version = 1; installedRule1.params.exceptionsList = []; - const [update] = getRulesToUpdate( - prebuiltRulesToMap([ruleFromFileSystem1]), - rulesToMap([installedRule1]) - ); - expect(update.exceptions_list).toEqual(ruleFromFileSystem1.exceptions_list); + const [update] = getRulesToUpdate([ruleAsset1], rulesToMap([installedRule1])); + expect(update.exceptions_list).toEqual(ruleAsset1.exceptions_list); }); test('should not remove an additional exception_list if an additional one was added by the end user on an immutable rule during an upgrade', () => { - const ruleFromFileSystem1 = getPrebuiltRuleMock(); - ruleFromFileSystem1.exceptions_list = [ + const ruleAsset1 = getPrebuiltRuleMock(); + ruleAsset1.exceptions_list = [ { id: 'endpoint_list', list_id: 'endpoint_list', @@ -162,8 +144,8 @@ describe('get_rules_to_update', () => { type: 'endpoint', }, ]; - ruleFromFileSystem1.rule_id = 'rule-1'; - ruleFromFileSystem1.version = 2; + ruleAsset1.rule_id = 'rule-1'; + ruleAsset1.version = 2; const installedRule1 = getRuleMock(getQueryRuleParams()); installedRule1.params.ruleId = 'rule-1'; @@ -177,19 +159,16 @@ describe('get_rules_to_update', () => { }, ]; - const [update] = getRulesToUpdate( - prebuiltRulesToMap([ruleFromFileSystem1]), - rulesToMap([installedRule1]) - ); + const [update] = getRulesToUpdate([ruleAsset1], rulesToMap([installedRule1])); expect(update.exceptions_list).toEqual([ - ...ruleFromFileSystem1.exceptions_list, + ...ruleAsset1.exceptions_list, ...installedRule1.params.exceptionsList, ]); }); test('should not remove an existing exception_list if they are the same between the current installed one and the upgraded one', () => { - const ruleFromFileSystem1 = getPrebuiltRuleMock(); - ruleFromFileSystem1.exceptions_list = [ + const ruleAsset1 = getPrebuiltRuleMock(); + ruleAsset1.exceptions_list = [ { id: 'endpoint_list', list_id: 'endpoint_list', @@ -197,8 +176,8 @@ describe('get_rules_to_update', () => { type: 'endpoint', }, ]; - ruleFromFileSystem1.rule_id = 'rule-1'; - ruleFromFileSystem1.version = 2; + ruleAsset1.rule_id = 'rule-1'; + ruleAsset1.version = 2; const installedRule1 = getRuleMock(getQueryRuleParams()); installedRule1.params.ruleId = 'rule-1'; @@ -212,18 +191,15 @@ describe('get_rules_to_update', () => { }, ]; - const [update] = getRulesToUpdate( - prebuiltRulesToMap([ruleFromFileSystem1]), - rulesToMap([installedRule1]) - ); - expect(update.exceptions_list).toEqual(ruleFromFileSystem1.exceptions_list); + const [update] = getRulesToUpdate([ruleAsset1], rulesToMap([installedRule1])); + expect(update.exceptions_list).toEqual(ruleAsset1.exceptions_list); }); test('should not remove an existing exception_list if the rule has an empty exceptions list', () => { - const ruleFromFileSystem1 = getPrebuiltRuleMock(); - ruleFromFileSystem1.exceptions_list = []; - ruleFromFileSystem1.rule_id = 'rule-1'; - ruleFromFileSystem1.version = 2; + const ruleAsset1 = getPrebuiltRuleMock(); + ruleAsset1.exceptions_list = []; + ruleAsset1.rule_id = 'rule-1'; + ruleAsset1.version = 2; const installedRule1 = getRuleMock(getQueryRuleParams()); installedRule1.params.ruleId = 'rule-1'; @@ -237,23 +213,20 @@ describe('get_rules_to_update', () => { }, ]; - const [update] = getRulesToUpdate( - prebuiltRulesToMap([ruleFromFileSystem1]), - rulesToMap([installedRule1]) - ); + const [update] = getRulesToUpdate([ruleAsset1], rulesToMap([installedRule1])); expect(update.exceptions_list).toEqual(installedRule1.params.exceptionsList); }); test('should not remove an existing exception_list if the rule has an empty exceptions list for multiple rules', () => { - const ruleFromFileSystem1 = getPrebuiltRuleMock(); - ruleFromFileSystem1.exceptions_list = []; - ruleFromFileSystem1.rule_id = 'rule-1'; - ruleFromFileSystem1.version = 2; + const ruleAsset1 = getPrebuiltRuleMock(); + ruleAsset1.exceptions_list = []; + ruleAsset1.rule_id = 'rule-1'; + ruleAsset1.version = 2; - const ruleFromFileSystem2 = getPrebuiltRuleMock(); - ruleFromFileSystem2.exceptions_list = []; - ruleFromFileSystem2.rule_id = 'rule-2'; - ruleFromFileSystem2.version = 2; + const ruleAsset2 = getPrebuiltRuleMock(); + ruleAsset2.exceptions_list = []; + ruleAsset2.rule_id = 'rule-2'; + ruleAsset2.version = 2; const installedRule1 = getRuleMock(getQueryRuleParams()); installedRule1.params.ruleId = 'rule-1'; @@ -279,7 +252,7 @@ describe('get_rules_to_update', () => { ]; const [update1, update2] = getRulesToUpdate( - prebuiltRulesToMap([ruleFromFileSystem1, ruleFromFileSystem2]), + [ruleAsset1, ruleAsset2], rulesToMap([installedRule1, installedRule2]) ); expect(update1.exceptions_list).toEqual(installedRule1.params.exceptionsList); @@ -287,16 +260,16 @@ describe('get_rules_to_update', () => { }); test('should not remove an existing exception_list if the rule has an empty exceptions list for mixed rules', () => { - const ruleFromFileSystem1 = getPrebuiltRuleMock(); - ruleFromFileSystem1.exceptions_list = []; - ruleFromFileSystem1.rule_id = 'rule-1'; - ruleFromFileSystem1.version = 2; - - const ruleFromFileSystem2 = getPrebuiltRuleMock(); - ruleFromFileSystem2.exceptions_list = []; - ruleFromFileSystem2.rule_id = 'rule-2'; - ruleFromFileSystem2.version = 2; - ruleFromFileSystem2.exceptions_list = [ + const ruleAsset1 = getPrebuiltRuleMock(); + ruleAsset1.exceptions_list = []; + ruleAsset1.rule_id = 'rule-1'; + ruleAsset1.version = 2; + + const ruleAsset2 = getPrebuiltRuleMock(); + ruleAsset2.exceptions_list = []; + ruleAsset2.rule_id = 'rule-2'; + ruleAsset2.version = 2; + ruleAsset2.exceptions_list = [ { id: 'second_list', list_id: 'second_list', @@ -330,12 +303,12 @@ describe('get_rules_to_update', () => { ]; const [update1, update2] = getRulesToUpdate( - prebuiltRulesToMap([ruleFromFileSystem1, ruleFromFileSystem2]), + [ruleAsset1, ruleAsset2], rulesToMap([installedRule1, installedRule2]) ); expect(update1.exceptions_list).toEqual(installedRule1.params.exceptionsList); expect(update2.exceptions_list).toEqual([ - ...ruleFromFileSystem2.exceptions_list, + ...ruleAsset2.exceptions_list, ...installedRule2.params.exceptionsList, ]); }); @@ -343,60 +316,60 @@ describe('get_rules_to_update', () => { describe('filterInstalledRules', () => { test('should return "false" if the id of the two rules do not match', () => { - const ruleFromFileSystem = getPrebuiltRuleMock(); - ruleFromFileSystem.rule_id = 'rule-1'; - ruleFromFileSystem.version = 2; + const ruleAsset = getPrebuiltRuleMock(); + ruleAsset.rule_id = 'rule-1'; + ruleAsset.version = 2; const installedRule = getRuleMock(getQueryRuleParams()); installedRule.params.ruleId = 'rule-2'; installedRule.params.version = 1; - const shouldUpdate = filterInstalledRules(ruleFromFileSystem, rulesToMap([installedRule])); + const shouldUpdate = filterInstalledRules(ruleAsset, rulesToMap([installedRule])); expect(shouldUpdate).toEqual(false); }); test('should return "false" if the version of file system rule is less than the installed version', () => { - const ruleFromFileSystem = getPrebuiltRuleMock(); - ruleFromFileSystem.rule_id = 'rule-1'; - ruleFromFileSystem.version = 1; + const ruleAsset = getPrebuiltRuleMock(); + ruleAsset.rule_id = 'rule-1'; + ruleAsset.version = 1; const installedRule = getRuleMock(getQueryRuleParams()); installedRule.params.ruleId = 'rule-1'; installedRule.params.version = 2; - const shouldUpdate = filterInstalledRules(ruleFromFileSystem, rulesToMap([installedRule])); + const shouldUpdate = filterInstalledRules(ruleAsset, rulesToMap([installedRule])); expect(shouldUpdate).toEqual(false); }); test('should return "false" if the version of file system rule is the same as the installed version', () => { - const ruleFromFileSystem = getPrebuiltRuleMock(); - ruleFromFileSystem.rule_id = 'rule-1'; - ruleFromFileSystem.version = 1; + const ruleAsset = getPrebuiltRuleMock(); + ruleAsset.rule_id = 'rule-1'; + ruleAsset.version = 1; const installedRule = getRuleMock(getQueryRuleParams()); installedRule.params.ruleId = 'rule-1'; installedRule.params.version = 1; - const shouldUpdate = filterInstalledRules(ruleFromFileSystem, rulesToMap([installedRule])); + const shouldUpdate = filterInstalledRules(ruleAsset, rulesToMap([installedRule])); expect(shouldUpdate).toEqual(false); }); test('should return "true" to update if the version of file system rule is greater than the installed version', () => { - const ruleFromFileSystem = getPrebuiltRuleMock(); - ruleFromFileSystem.rule_id = 'rule-1'; - ruleFromFileSystem.version = 2; + const ruleAsset = getPrebuiltRuleMock(); + ruleAsset.rule_id = 'rule-1'; + ruleAsset.version = 2; const installedRule = getRuleMock(getQueryRuleParams()); installedRule.params.ruleId = 'rule-1'; installedRule.params.version = 1; installedRule.params.exceptionsList = []; - const shouldUpdate = filterInstalledRules(ruleFromFileSystem, rulesToMap([installedRule])); + const shouldUpdate = filterInstalledRules(ruleAsset, rulesToMap([installedRule])); expect(shouldUpdate).toEqual(true); }); }); describe('mergeExceptionLists', () => { test('should add back an exception_list if it was removed by the end user on an immutable rule during an upgrade', () => { - const ruleFromFileSystem1 = getPrebuiltRuleMock(); - ruleFromFileSystem1.exceptions_list = [ + const ruleAsset1 = getPrebuiltRuleMock(); + ruleAsset1.exceptions_list = [ { id: 'endpoint_list', list_id: 'endpoint_list', @@ -404,21 +377,21 @@ describe('mergeExceptionLists', () => { type: 'endpoint', }, ]; - ruleFromFileSystem1.rule_id = 'rule-1'; - ruleFromFileSystem1.version = 2; + ruleAsset1.rule_id = 'rule-1'; + ruleAsset1.version = 2; const installedRule1 = getRuleMock(getQueryRuleParams()); installedRule1.params.ruleId = 'rule-1'; installedRule1.params.version = 1; installedRule1.params.exceptionsList = []; - const update = mergeExceptionLists(ruleFromFileSystem1, rulesToMap([installedRule1])); - expect(update.exceptions_list).toEqual(ruleFromFileSystem1.exceptions_list); + const update = mergeExceptionLists(ruleAsset1, rulesToMap([installedRule1])); + expect(update.exceptions_list).toEqual(ruleAsset1.exceptions_list); }); test('should not remove an additional exception_list if an additional one was added by the end user on an immutable rule during an upgrade', () => { - const ruleFromFileSystem1 = getPrebuiltRuleMock(); - ruleFromFileSystem1.exceptions_list = [ + const ruleAsset1 = getPrebuiltRuleMock(); + ruleAsset1.exceptions_list = [ { id: 'endpoint_list', list_id: 'endpoint_list', @@ -426,8 +399,8 @@ describe('mergeExceptionLists', () => { type: 'endpoint', }, ]; - ruleFromFileSystem1.rule_id = 'rule-1'; - ruleFromFileSystem1.version = 2; + ruleAsset1.rule_id = 'rule-1'; + ruleAsset1.version = 2; const installedRule1 = getRuleMock(getQueryRuleParams()); installedRule1.params.ruleId = 'rule-1'; @@ -441,16 +414,16 @@ describe('mergeExceptionLists', () => { }, ]; - const update = mergeExceptionLists(ruleFromFileSystem1, rulesToMap([installedRule1])); + const update = mergeExceptionLists(ruleAsset1, rulesToMap([installedRule1])); expect(update.exceptions_list).toEqual([ - ...ruleFromFileSystem1.exceptions_list, + ...ruleAsset1.exceptions_list, ...installedRule1.params.exceptionsList, ]); }); test('should not remove an existing exception_list if they are the same between the current installed one and the upgraded one', () => { - const ruleFromFileSystem1 = getPrebuiltRuleMock(); - ruleFromFileSystem1.exceptions_list = [ + const ruleAsset1 = getPrebuiltRuleMock(); + ruleAsset1.exceptions_list = [ { id: 'endpoint_list', list_id: 'endpoint_list', @@ -458,8 +431,8 @@ describe('mergeExceptionLists', () => { type: 'endpoint', }, ]; - ruleFromFileSystem1.rule_id = 'rule-1'; - ruleFromFileSystem1.version = 2; + ruleAsset1.rule_id = 'rule-1'; + ruleAsset1.version = 2; const installedRule1 = getRuleMock(getQueryRuleParams()); installedRule1.params.ruleId = 'rule-1'; @@ -473,15 +446,15 @@ describe('mergeExceptionLists', () => { }, ]; - const update = mergeExceptionLists(ruleFromFileSystem1, rulesToMap([installedRule1])); - expect(update.exceptions_list).toEqual(ruleFromFileSystem1.exceptions_list); + const update = mergeExceptionLists(ruleAsset1, rulesToMap([installedRule1])); + expect(update.exceptions_list).toEqual(ruleAsset1.exceptions_list); }); test('should not remove an existing exception_list if the rule has an empty exceptions list', () => { - const ruleFromFileSystem1 = getPrebuiltRuleMock(); - ruleFromFileSystem1.exceptions_list = []; - ruleFromFileSystem1.rule_id = 'rule-1'; - ruleFromFileSystem1.version = 2; + const ruleAsset1 = getPrebuiltRuleMock(); + ruleAsset1.exceptions_list = []; + ruleAsset1.rule_id = 'rule-1'; + ruleAsset1.version = 2; const installedRule1 = getRuleMock(getQueryRuleParams()); installedRule1.params.ruleId = 'rule-1'; @@ -495,7 +468,7 @@ describe('mergeExceptionLists', () => { }, ]; - const update = mergeExceptionLists(ruleFromFileSystem1, rulesToMap([installedRule1])); + const update = mergeExceptionLists(ruleAsset1, rulesToMap([installedRule1])); expect(update.exceptions_list).toEqual(installedRule1.params.exceptionsList); }); }); diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/logic/get_rules_to_update.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/logic/get_rules_to_update.ts index 7547f16961cc1..e25ca4f1348e1 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/logic/get_rules_to_update.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/logic/get_rules_to_update.ts @@ -5,8 +5,8 @@ * 2.0. */ -import type { PrebuiltRuleToInstall } from '../../../../../common/detection_engine/prebuilt_rules'; import type { RuleAlertType } from '../../rule_schema'; +import type { PrebuiltRuleAsset } from '../model/rule_assets/prebuilt_rule_asset'; /** * Returns the rules to update by doing a compare to the rules from the file system against @@ -16,10 +16,10 @@ import type { RuleAlertType } from '../../rule_schema'; * @param installedRules The installed rules */ export const getRulesToUpdate = ( - latestPrebuiltRules: Map, + latestPrebuiltRules: PrebuiltRuleAsset[], installedRules: Map ) => { - return Array.from(latestPrebuiltRules.values()) + return latestPrebuiltRules .filter((latestRule) => filterInstalledRules(latestRule, installedRules)) .map((latestRule) => mergeExceptionLists(latestRule, installedRules)); }; @@ -31,7 +31,7 @@ export const getRulesToUpdate = ( * @param installedRules The installed rules to compare against for updates */ export const filterInstalledRules = ( - latestPrebuiltRule: PrebuiltRuleToInstall, + latestPrebuiltRule: PrebuiltRuleAsset, installedRules: Map ): boolean => { const installedRule = installedRules.get(latestPrebuiltRule.rule_id); @@ -46,9 +46,9 @@ export const filterInstalledRules = ( * @param installedRules The installed rules which might have user driven exceptions_lists */ export const mergeExceptionLists = ( - latestPrebuiltRule: PrebuiltRuleToInstall, + latestPrebuiltRule: PrebuiltRuleAsset, installedRules: Map -): PrebuiltRuleToInstall => { +): PrebuiltRuleAsset => { if (latestPrebuiltRule.exceptions_list != null) { const installedRule = installedRules.get(latestPrebuiltRule.rule_id); diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/logic/rule_asset/rule_asset_saved_objects_client.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/logic/rule_asset/rule_asset_saved_objects_client.ts deleted file mode 100644 index 556ebbe2df108..0000000000000 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/logic/rule_asset/rule_asset_saved_objects_client.ts +++ /dev/null @@ -1,78 +0,0 @@ -/* - * 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 type { AggregationsMultiBucketAggregateBase } from '@elastic/elasticsearch/lib/api/types'; -import type { AggregationsTopHitsAggregate } from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; -import type { SavedObjectsClientContract } from '@kbn/core/server'; -import { invariant } from '../../../../../../common/utils/invariant'; -import { withSecuritySpan } from '../../../../../utils/with_security_span'; -import { ruleAssetSavedObjectType } from './rule_asset_saved_object_mappings'; - -const MAX_PREBUILT_RULES_COUNT = 10_000; - -export interface IRuleAssetSOAttributes extends Record { - rule_id: string | null | undefined; - version: string | null | undefined; - name: string | null | undefined; -} - -export interface IRuleAssetSavedObject { - type: string; - id: string; - attributes: IRuleAssetSOAttributes; -} - -export interface IRuleAssetsClient { - fetchLatestVersions: () => Promise; -} - -export const ruleAssetsClientFactory = ( - savedObjectsClient: SavedObjectsClientContract -): IRuleAssetsClient => { - return { - fetchLatestVersions: () => { - return withSecuritySpan('RuleAssetsClient.fetchLatestVersions', async () => { - const findResult = await savedObjectsClient.find< - IRuleAssetSavedObject, - { - rules: AggregationsMultiBucketAggregateBase<{ - latest_version: AggregationsTopHitsAggregate; - }>; - } - >({ - type: ruleAssetSavedObjectType, - aggs: { - rules: { - terms: { - field: `${ruleAssetSavedObjectType}.attributes.rule_id`, - size: MAX_PREBUILT_RULES_COUNT, - }, - aggs: { - latest_version: { - top_hits: { - size: 1, - sort: { - [`${ruleAssetSavedObjectType}.version`]: 'desc', - }, - }, - }, - }, - }, - }, - }); - const buckets = findResult.aggregations?.rules?.buckets ?? []; - - invariant(Array.isArray(buckets), 'Expected buckets to be an array'); - - return buckets.map((bucket) => { - const hit = bucket.latest_version.hits.hits[0]; - return hit._source[ruleAssetSavedObjectType]; - }); - }); - }, - }; -}; diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/logic/rule_assets/prebuilt_rule_assets_client.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/logic/rule_assets/prebuilt_rule_assets_client.ts new file mode 100644 index 0000000000000..96cb684a152d9 --- /dev/null +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/logic/rule_assets/prebuilt_rule_assets_client.ts @@ -0,0 +1,177 @@ +/* + * 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 { chunk } from 'lodash'; +import type { AggregationsMultiBucketAggregateBase } from '@elastic/elasticsearch/lib/api/types'; +import type { AggregationsTopHitsAggregate } from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; +import type { SavedObjectsBulkCreateObject, SavedObjectsClientContract } from '@kbn/core/server'; +import { invariant } from '../../../../../../common/utils/invariant'; +import { withSecuritySpan } from '../../../../../utils/with_security_span'; +import type { PrebuiltRuleAsset } from '../../model/rule_assets/prebuilt_rule_asset'; +import { validatePrebuiltRuleAssets } from './prebuilt_rule_assets_validation'; +import { PREBUILT_RULE_ASSETS_SO_TYPE } from './prebuilt_rule_assets_type'; +import type { PrebuiltRuleVersionInfo } from '../../model/rule_versions/prebuilt_rule_version_info'; + +const MAX_PREBUILT_RULES_COUNT = 10_000; +const MAX_ASSETS_PER_BULK_CREATE_REQUEST = 500; + +export interface IPrebuiltRuleAssetsClient { + fetchLatestAssets: () => Promise; + + fetchLatestVersions(): Promise; + + fetchAssetsByVersionInfo(versions: PrebuiltRuleVersionInfo[]): Promise; + + bulkCreateAssets(assets: PrebuiltRuleAsset[]): Promise; +} + +export const createPrebuiltRuleAssetsClient = ( + savedObjectsClient: SavedObjectsClientContract +): IPrebuiltRuleAssetsClient => { + return { + fetchLatestAssets: () => { + return withSecuritySpan('IPrebuiltRuleAssetsClient.fetchLatestAssets', async () => { + const findResult = await savedObjectsClient.find< + PrebuiltRuleAsset, + { + rules: AggregationsMultiBucketAggregateBase<{ + latest_version: AggregationsTopHitsAggregate; + }>; + } + >({ + type: PREBUILT_RULE_ASSETS_SO_TYPE, + aggs: { + rules: { + terms: { + field: `${PREBUILT_RULE_ASSETS_SO_TYPE}.attributes.rule_id`, + size: MAX_PREBUILT_RULES_COUNT, + }, + aggs: { + latest_version: { + top_hits: { + size: 1, + sort: { + [`${PREBUILT_RULE_ASSETS_SO_TYPE}.version`]: 'desc', + }, + }, + }, + }, + }, + }, + }); + + const buckets = findResult.aggregations?.rules?.buckets ?? []; + invariant(Array.isArray(buckets), 'Expected buckets to be an array'); + + const ruleAssets = buckets.map((bucket) => { + const hit = bucket.latest_version.hits.hits[0]; + return hit._source[PREBUILT_RULE_ASSETS_SO_TYPE]; + }); + + return validatePrebuiltRuleAssets(ruleAssets); + }); + }, + + fetchLatestVersions: (): Promise => { + return withSecuritySpan('IPrebuiltRuleAssetsClient.fetchLatestVersions', async () => { + const findResult = await savedObjectsClient.find< + PrebuiltRuleAsset, + { + rules: AggregationsMultiBucketAggregateBase<{ + latest_version: AggregationsTopHitsAggregate; + }>; + } + >({ + type: PREBUILT_RULE_ASSETS_SO_TYPE, + aggs: { + rules: { + terms: { + field: `${PREBUILT_RULE_ASSETS_SO_TYPE}.attributes.rule_id`, + size: MAX_PREBUILT_RULES_COUNT, + }, + aggs: { + latest_version: { + top_hits: { + size: 1, + sort: [ + { + [`${PREBUILT_RULE_ASSETS_SO_TYPE}.version`]: 'desc', + }, + ], + _source: [ + `${PREBUILT_RULE_ASSETS_SO_TYPE}.rule_id`, + `${PREBUILT_RULE_ASSETS_SO_TYPE}.version`, + ], + }, + }, + }, + }, + }, + }); + + const buckets = findResult.aggregations?.rules?.buckets ?? []; + invariant(Array.isArray(buckets), 'Expected buckets to be an array'); + + return buckets.map((bucket) => { + const hit = bucket.latest_version.hits.hits[0]; + const soAttributes = hit._source[PREBUILT_RULE_ASSETS_SO_TYPE]; + const versionInfo: PrebuiltRuleVersionInfo = { + rule_id: soAttributes.rule_id, + version: soAttributes.version, + }; + return versionInfo; + }); + }); + }, + + fetchAssetsByVersionInfo: ( + versions: PrebuiltRuleVersionInfo[] + ): Promise => { + return withSecuritySpan('IPrebuiltRuleAssetsClient.fetchAssetsByVersionInfo', async () => { + if (versions.length === 0) { + // NOTE: without early return it would build incorrect filter and fetch all existing saved objects + return []; + } + + const attr = `${PREBUILT_RULE_ASSETS_SO_TYPE}.attributes`; + const filter = versions + .map((v) => `(${attr}.rule_id: ${v.rule_id} AND ${attr}.version: ${v.version})`) + .join(' OR '); + + const findResult = await savedObjectsClient.find({ + type: PREBUILT_RULE_ASSETS_SO_TYPE, + filter, + perPage: MAX_PREBUILT_RULES_COUNT, + }); + + const ruleAssets = findResult.saved_objects.map((so) => so.attributes); + return validatePrebuiltRuleAssets(ruleAssets); + }); + }, + + bulkCreateAssets: (assets: PrebuiltRuleAsset[]): Promise => { + return withSecuritySpan('IPrebuiltRuleAssetsClient.bulkCreateAssets', async () => { + const validAssets = validatePrebuiltRuleAssets(assets); + const bulkCreateObjects: Array> = + validAssets.map((asset) => ({ + id: `${asset.rule_id}_${asset.version}`, + type: PREBUILT_RULE_ASSETS_SO_TYPE, + attributes: asset, + })); + + const bulkCreateChunks = chunk(bulkCreateObjects, MAX_ASSETS_PER_BULK_CREATE_REQUEST); + + for (const chunkOfObjects of bulkCreateChunks) { + await savedObjectsClient.bulkCreate(chunkOfObjects, { + refresh: false, + overwrite: true, + }); + } + }); + }, + }; +}; diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/logic/rule_asset/rule_asset_saved_object_mappings.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/logic/rule_assets/prebuilt_rule_assets_type.ts similarity index 70% rename from x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/logic/rule_asset/rule_asset_saved_object_mappings.ts rename to x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/logic/rule_assets/prebuilt_rule_assets_type.ts index 71505fac2d4c5..04bdf7ca28fca 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/logic/rule_asset/rule_asset_saved_object_mappings.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/logic/rule_assets/prebuilt_rule_assets_type.ts @@ -7,9 +7,9 @@ import type { SavedObjectsType } from '@kbn/core/server'; -export const ruleAssetSavedObjectType = 'security-rule'; +export const PREBUILT_RULE_ASSETS_SO_TYPE = 'security-rule'; -export const ruleAssetSavedObjectMappings: SavedObjectsType['mappings'] = { +const prebuiltRuleAssetMappings: SavedObjectsType['mappings'] = { dynamic: false, properties: { name: { @@ -24,13 +24,13 @@ export const ruleAssetSavedObjectMappings: SavedObjectsType['mappings'] = { }, }; -export const ruleAssetType: SavedObjectsType = { - name: ruleAssetSavedObjectType, +export const prebuiltRuleAssetType: SavedObjectsType = { + name: PREBUILT_RULE_ASSETS_SO_TYPE, hidden: false, management: { importableAndExportable: true, visibleInManagement: false, }, namespaceType: 'agnostic', - mappings: ruleAssetSavedObjectMappings, + mappings: prebuiltRuleAssetMappings, }; diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/logic/rule_assets/prebuilt_rule_assets_validation.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/logic/rule_assets/prebuilt_rule_assets_validation.ts new file mode 100644 index 0000000000000..6ba2417fc5263 --- /dev/null +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/logic/rule_assets/prebuilt_rule_assets_validation.ts @@ -0,0 +1,36 @@ +/* + * 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 type * as t from 'io-ts'; +import { getOrElse } from 'fp-ts/lib/Either'; +import { BadRequestError } from '@kbn/securitysolution-es-utils'; +import { exactCheck, formatErrors } from '@kbn/securitysolution-io-ts-utils'; +import { PrebuiltRuleAsset } from '../../model/rule_assets/prebuilt_rule_asset'; + +export const validatePrebuiltRuleAssets = (rules: PrebuiltRuleAsset[]): PrebuiltRuleAsset[] => { + return rules.map((rule) => validatePrebuiltRuleAsset(rule)); +}; + +export const validatePrebuiltRuleAsset = (rule: PrebuiltRuleAsset): PrebuiltRuleAsset => { + const decoded = PrebuiltRuleAsset.decode(rule); + const checked = exactCheck(rule, decoded); + + const onLeft = (errors: t.Errors): PrebuiltRuleAsset => { + const ruleName = rule.name ? rule.name : '(rule name unknown)'; + const ruleId = rule.rule_id ? rule.rule_id : '(rule rule_id unknown)'; + throw new BadRequestError( + `name: "${ruleName}", rule_id: "${ruleId}" within the security-rule saved object ` + + `is not a valid detection engine rule. Expect the system ` + + `to not work with pre-packaged rules until this rule is fixed ` + + `or the file is removed. Error is: ${formatErrors( + errors + ).join()}, Full rule contents are:\n${JSON.stringify(rule, null, 2)}` + ); + }; + + return getOrElse(onLeft)(checked); +}; diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/logic/create_prebuilt_rules.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/logic/rule_objects/create_prebuilt_rules.ts similarity index 72% rename from x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/logic/create_prebuilt_rules.ts rename to x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/logic/rule_objects/create_prebuilt_rules.ts index 54554e50a7dd7..2fd8ef266e6b6 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/logic/create_prebuilt_rules.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/logic/rule_objects/create_prebuilt_rules.ts @@ -6,13 +6,13 @@ */ import type { RulesClient } from '@kbn/alerting-plugin/server'; -import { MAX_RULES_TO_UPDATE_IN_PARALLEL } from '../../../../../common/constants'; -import type { PrebuiltRuleToInstall } from '../../../../../common/detection_engine/prebuilt_rules'; -import { initPromisePool } from '../../../../utils/promise_pool'; -import { withSecuritySpan } from '../../../../utils/with_security_span'; -import { createRules } from '../../rule_management/logic/crud/create_rules'; +import { MAX_RULES_TO_UPDATE_IN_PARALLEL } from '../../../../../../common/constants'; +import { initPromisePool } from '../../../../../utils/promise_pool'; +import { withSecuritySpan } from '../../../../../utils/with_security_span'; +import { createRules } from '../../../rule_management/logic/crud/create_rules'; +import type { PrebuiltRuleAsset } from '../../model/rule_assets/prebuilt_rule_asset'; -export const createPrebuiltRules = (rulesClient: RulesClient, rules: PrebuiltRuleToInstall[]) => +export const createPrebuiltRules = (rulesClient: RulesClient, rules: PrebuiltRuleAsset[]) => withSecuritySpan('createPrebuiltRules', async () => { const result = await initPromisePool({ concurrency: MAX_RULES_TO_UPDATE_IN_PARALLEL, diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/logic/rule_objects/prebuilt_rule_objects_client.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/logic/rule_objects/prebuilt_rule_objects_client.ts new file mode 100644 index 0000000000000..f0d978376d330 --- /dev/null +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/logic/rule_objects/prebuilt_rule_objects_client.ts @@ -0,0 +1,48 @@ +/* + * 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 type { RulesClient } from '@kbn/alerting-plugin/server'; +import type { RuleResponse } from '../../../../../../common/detection_engine/rule_schema'; +import { withSecuritySpan } from '../../../../../utils/with_security_span'; +import { getExistingPrepackagedRules } from '../../../rule_management/logic/search/get_existing_prepackaged_rules'; +import { internalRuleToAPIResponse } from '../../../rule_management/normalization/rule_converters'; +import type { PrebuiltRuleVersionInfo } from '../../model/rule_versions/prebuilt_rule_version_info'; + +export interface IPrebuiltRuleObjectsClient { + fetchInstalledRules(): Promise; +} + +export interface FetchInstalledRulesResult { + installedRules: RuleResponse[]; + installedVersions: PrebuiltRuleVersionInfo[]; +} + +export const createPrebuiltRuleObjectsClient = ( + rulesClient: RulesClient +): IPrebuiltRuleObjectsClient => { + return { + fetchInstalledRules: (): Promise => { + return withSecuritySpan('IPrebuiltRuleObjectsClient.fetchInstalledRules', async () => { + const rulesData = await getExistingPrepackagedRules({ rulesClient }); + const rules = rulesData.map((rule) => internalRuleToAPIResponse(rule)); + const versions = rules.map((rule) => convertRuleToVersionInfo(rule)); + return { + installedRules: rules, + installedVersions: versions, + }; + }); + }, + }; +}; + +const convertRuleToVersionInfo = (rule: RuleResponse): PrebuiltRuleVersionInfo => { + const versionInfo: PrebuiltRuleVersionInfo = { + rule_id: rule.rule_id, + version: rule.version, + }; + return versionInfo; +}; diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/logic/update_prebuilt_rules.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/logic/rule_objects/update_prebuilt_rules.test.ts similarity index 70% rename from x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/logic/update_prebuilt_rules.test.ts rename to x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/logic/rule_objects/update_prebuilt_rules.test.ts index 955d288068e33..13e91b1bc2dfa 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/logic/update_prebuilt_rules.test.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/logic/rule_objects/update_prebuilt_rules.test.ts @@ -7,22 +7,21 @@ import { rulesClientMock } from '@kbn/alerting-plugin/server/mocks'; import { savedObjectsClientMock } from '@kbn/core/server/mocks'; -import { getRuleMock, getFindResultWithSingleHit } from '../../routes/__mocks__/request_responses'; -import { updatePrebuiltRules } from './update_prebuilt_rules'; -import { patchRules } from '../../rule_management/logic/crud/patch_rules'; import { - getPrebuiltRuleMock, - getPrebuiltThreatMatchRuleMock, -} from '../../../../../common/detection_engine/prebuilt_rules/mocks'; -import { ruleExecutionLogMock } from '../../rule_monitoring/mocks'; -import { legacyMigrate } from '../../rule_management'; -import { getQueryRuleParams, getThreatRuleParams } from '../../rule_schema/mocks'; + getRuleMock, + getFindResultWithSingleHit, +} from '../../../routes/__mocks__/request_responses'; +import { updatePrebuiltRules } from './update_prebuilt_rules'; +import { patchRules } from '../../../rule_management/logic/crud/patch_rules'; +import { getPrebuiltRuleMock, getPrebuiltThreatMatchRuleMock } from '../../mocks'; +import { legacyMigrate } from '../../../rule_management'; +import { getQueryRuleParams, getThreatRuleParams } from '../../../rule_schema/mocks'; -jest.mock('../../rule_management/logic/crud/patch_rules'); +jest.mock('../../../rule_management/logic/crud/patch_rules'); -jest.mock('../../rule_management/logic/rule_actions/legacy_action_migration', () => { +jest.mock('../../../rule_management/logic/rule_actions/legacy_action_migration', () => { const actual = jest.requireActual( - '../../rule_management/logic/rule_actions/legacy_action_migration' + '../../../rule_management/logic/rule_actions/legacy_action_migration' ); return { ...actual, @@ -33,12 +32,10 @@ jest.mock('../../rule_management/logic/rule_actions/legacy_action_migration', () describe('updatePrebuiltRules', () => { let rulesClient: ReturnType; let savedObjectsClient: ReturnType; - let ruleExecutionLog: ReturnType; beforeEach(() => { rulesClient = rulesClientMock.create(); savedObjectsClient = savedObjectsClientMock.create(); - ruleExecutionLog = ruleExecutionLogMock.forRoutes.create(); (legacyMigrate as jest.Mock).mockResolvedValue(getRuleMock(getQueryRuleParams())); }); @@ -55,12 +52,7 @@ describe('updatePrebuiltRules', () => { const prepackagedRule = getPrebuiltRuleMock(); rulesClient.find.mockResolvedValue(getFindResultWithSingleHit()); - await updatePrebuiltRules( - rulesClient, - savedObjectsClient, - [{ ...prepackagedRule, actions }], - ruleExecutionLog - ); + await updatePrebuiltRules(rulesClient, savedObjectsClient, [{ ...prepackagedRule, actions }]); expect(patchRules).toHaveBeenCalledWith( expect.objectContaining({ @@ -92,12 +84,9 @@ describe('updatePrebuiltRules', () => { }); (legacyMigrate as jest.Mock).mockResolvedValue(getRuleMock(getThreatRuleParams())); - await updatePrebuiltRules( - rulesClient, - savedObjectsClient, - [{ ...prepackagedRule, ...updatedThreatParams }], - ruleExecutionLog - ); + await updatePrebuiltRules(rulesClient, savedObjectsClient, [ + { ...prepackagedRule, ...updatedThreatParams }, + ]); expect(patchRules).toHaveBeenCalledWith( expect.objectContaining({ diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/logic/update_prebuilt_rules.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/logic/rule_objects/update_prebuilt_rules.ts similarity index 73% rename from x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/logic/update_prebuilt_rules.ts rename to x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/logic/rule_objects/update_prebuilt_rules.ts index 4956b9377c947..407ffac13e48c 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/logic/update_prebuilt_rules.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/logic/rule_objects/update_prebuilt_rules.ts @@ -9,20 +9,19 @@ import { chunk } from 'lodash/fp'; import type { SavedObjectsClientContract } from '@kbn/core/server'; import type { RulesClient, PartialRule } from '@kbn/alerting-plugin/server'; -import type { PrebuiltRuleToInstall } from '../../../../../common/detection_engine/prebuilt_rules'; -import { transformAlertToRuleAction } from '../../../../../common/detection_engine/transform_actions'; -import { MAX_RULES_TO_UPDATE_IN_PARALLEL } from '../../../../../common/constants'; +import { transformAlertToRuleAction } from '../../../../../../common/detection_engine/transform_actions'; +import { MAX_RULES_TO_UPDATE_IN_PARALLEL } from '../../../../../../common/constants'; -import { legacyMigrate } from '../../rule_management'; -import { createRules } from '../../rule_management/logic/crud/create_rules'; -import { readRules } from '../../rule_management/logic/crud/read_rules'; -import { patchRules } from '../../rule_management/logic/crud/patch_rules'; -import { deleteRules } from '../../rule_management/logic/crud/delete_rules'; +import { legacyMigrate } from '../../../rule_management'; +import { createRules } from '../../../rule_management/logic/crud/create_rules'; +import { readRules } from '../../../rule_management/logic/crud/read_rules'; +import { patchRules } from '../../../rule_management/logic/crud/patch_rules'; +import { deleteRules } from '../../../rule_management/logic/crud/delete_rules'; -import type { IRuleExecutionLogForRoutes } from '../../rule_monitoring'; -import type { RuleParams } from '../../rule_schema'; +import type { RuleParams } from '../../../rule_schema'; -import { PrepackagedRulesError } from '../api/install_prebuilt_rules_and_timelines/route'; +import { PrepackagedRulesError } from '../../api/install_prebuilt_rules_and_timelines/route'; +import type { PrebuiltRuleAsset } from '../../model/rule_assets/prebuilt_rule_asset'; /** * Updates existing prebuilt rules given a set of rules and output index. @@ -35,17 +34,11 @@ import { PrepackagedRulesError } from '../api/install_prebuilt_rules_and_timelin export const updatePrebuiltRules = async ( rulesClient: RulesClient, savedObjectsClient: SavedObjectsClientContract, - rules: PrebuiltRuleToInstall[], - ruleExecutionLog: IRuleExecutionLogForRoutes + rules: PrebuiltRuleAsset[] ): Promise => { const ruleChunks = chunk(MAX_RULES_TO_UPDATE_IN_PARALLEL, rules); for (const ruleChunk of ruleChunks) { - const rulePromises = createPromises( - rulesClient, - savedObjectsClient, - ruleChunk, - ruleExecutionLog - ); + const rulePromises = createPromises(rulesClient, savedObjectsClient, ruleChunk); await Promise.all(rulePromises); } }; @@ -60,8 +53,7 @@ export const updatePrebuiltRules = async ( const createPromises = ( rulesClient: RulesClient, savedObjectsClient: SavedObjectsClientContract, - rules: PrebuiltRuleToInstall[], - ruleExecutionLog: IRuleExecutionLogForRoutes + rules: PrebuiltRuleAsset[] ): Array | null>> => { return rules.map(async (rule) => { const existingRule = await readRules({ diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/logic/utils.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/logic/utils.ts index 09f231f51d4ac..9c1b3fdff54b3 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/logic/utils.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/logic/utils.ts @@ -5,18 +5,8 @@ * 2.0. */ -import type { PrebuiltRuleToInstall } from '../../../../../common/detection_engine/prebuilt_rules'; import type { RuleAlertType } from '../../rule_schema'; -/** - * Converts an array of prebuilt rules to a Map with rule IDs as keys - * - * @param rules Array of prebuilt rules - * @returns Map - */ -export const prebuiltRulesToMap = (rules: PrebuiltRuleToInstall[]) => - new Map(rules.map((rule) => [rule.rule_id, rule])); - /** * Converts an array of rules to a Map with rule IDs as keys * diff --git a/x-pack/plugins/security_solution/common/detection_engine/prebuilt_rules/mocks.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/mocks.ts similarity index 80% rename from x-pack/plugins/security_solution/common/detection_engine/prebuilt_rules/mocks.ts rename to x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/mocks.ts index 2850442ce975c..81cc9e7b93bff 100644 --- a/x-pack/plugins/security_solution/common/detection_engine/prebuilt_rules/mocks.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/mocks.ts @@ -5,4 +5,4 @@ * 2.0. */ -export * from './model/prebuilt_rule.mock'; +export * from './model/rule_assets/prebuilt_rule_asset.mock'; diff --git a/x-pack/plugins/security_solution/common/detection_engine/prebuilt_rules/model/prebuilt_rule.mock.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/model/rule_assets/prebuilt_rule_asset.mock.ts similarity index 88% rename from x-pack/plugins/security_solution/common/detection_engine/prebuilt_rules/model/prebuilt_rule.mock.ts rename to x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/model/rule_assets/prebuilt_rule_asset.mock.ts index 24edacde6f715..8329204637c3b 100644 --- a/x-pack/plugins/security_solution/common/detection_engine/prebuilt_rules/model/prebuilt_rule.mock.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/model/rule_assets/prebuilt_rule_asset.mock.ts @@ -5,9 +5,9 @@ * 2.0. */ -import type { PrebuiltRuleToInstall } from './prebuilt_rule'; +import type { PrebuiltRuleAsset } from './prebuilt_rule_asset'; -export const getPrebuiltRuleMock = (): PrebuiltRuleToInstall => ({ +export const getPrebuiltRuleMock = (): PrebuiltRuleAsset => ({ description: 'some description', name: 'Query with a rule id', query: 'user.name: root or user.name: admin', @@ -19,7 +19,7 @@ export const getPrebuiltRuleMock = (): PrebuiltRuleToInstall => ({ version: 1, }); -export const getPrebuiltRuleWithExceptionsMock = (): PrebuiltRuleToInstall => ({ +export const getPrebuiltRuleWithExceptionsMock = (): PrebuiltRuleAsset => ({ description: 'A rule with an exception list', name: 'A rule with an exception list', query: 'user.name: root or user.name: admin', @@ -39,7 +39,7 @@ export const getPrebuiltRuleWithExceptionsMock = (): PrebuiltRuleToInstall => ({ version: 2, }); -export const getPrebuiltThreatMatchRuleMock = (): PrebuiltRuleToInstall => ({ +export const getPrebuiltThreatMatchRuleMock = (): PrebuiltRuleAsset => ({ description: 'some description', name: 'Query with a rule id', query: 'user.name: root or user.name: admin', diff --git a/x-pack/plugins/security_solution/common/detection_engine/prebuilt_rules/model/prebuilt_rule.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/model/rule_assets/prebuilt_rule_asset.test.ts similarity index 81% rename from x-pack/plugins/security_solution/common/detection_engine/prebuilt_rules/model/prebuilt_rule.test.ts rename to x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/model/rule_assets/prebuilt_rule_asset.test.ts index 4d08c9391fab8..7960167a993ef 100644 --- a/x-pack/plugins/security_solution/common/detection_engine/prebuilt_rules/model/prebuilt_rule.test.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/model/rule_assets/prebuilt_rule_asset.test.ts @@ -9,16 +9,16 @@ import { left } from 'fp-ts/lib/Either'; import { pipe } from 'fp-ts/lib/pipeable'; import { exactCheck, foldLeftRight, getPaths } from '@kbn/securitysolution-io-ts-utils'; -import { getListArrayMock } from '../../schemas/types/lists.mock'; +import { getListArrayMock } from '../../../../../../common/detection_engine/schemas/types/lists.mock'; -import { PrebuiltRuleToInstall } from './prebuilt_rule'; -import { getPrebuiltRuleMock, getPrebuiltThreatMatchRuleMock } from './prebuilt_rule.mock'; +import { PrebuiltRuleAsset } from './prebuilt_rule_asset'; +import { getPrebuiltRuleMock, getPrebuiltThreatMatchRuleMock } from './prebuilt_rule_asset.mock'; -describe('Prebuilt rule schema', () => { +describe('Prebuilt rule asset schema', () => { test('empty objects do not validate', () => { - const payload: Partial = {}; + const payload: Partial = {}; - const decoded = PrebuiltRuleToInstall.decode(payload); + const decoded = PrebuiltRuleAsset.decode(payload); const checked = exactCheck(payload, decoded); const message = pipe(checked, foldLeftRight); expect(getPaths(left(message.errors))).toContain( @@ -43,12 +43,12 @@ describe('Prebuilt rule schema', () => { }); test('made up values do not validate', () => { - const payload: PrebuiltRuleToInstall & { madeUp: string } = { + const payload: PrebuiltRuleAsset & { madeUp: string } = { ...getPrebuiltRuleMock(), madeUp: 'hi', }; - const decoded = PrebuiltRuleToInstall.decode(payload); + const decoded = PrebuiltRuleAsset.decode(payload); const checked = exactCheck(payload, decoded); const message = pipe(checked, foldLeftRight); expect(getPaths(left(message.errors))).toEqual(['invalid keys "madeUp"']); @@ -56,11 +56,11 @@ describe('Prebuilt rule schema', () => { }); test('[rule_id] does not validate', () => { - const payload: Partial = { + const payload: Partial = { rule_id: 'rule-1', }; - const decoded = PrebuiltRuleToInstall.decode(payload); + const decoded = PrebuiltRuleAsset.decode(payload); const checked = exactCheck(payload, decoded); const message = pipe(checked, foldLeftRight); expect(getPaths(left(message.errors))).toContain( @@ -82,12 +82,12 @@ describe('Prebuilt rule schema', () => { }); test('[rule_id, description] does not validate', () => { - const payload: Partial = { + const payload: Partial = { rule_id: 'rule-1', description: 'some description', }; - const decoded = PrebuiltRuleToInstall.decode(payload); + const decoded = PrebuiltRuleAsset.decode(payload); const checked = exactCheck(payload, decoded); const message = pipe(checked, foldLeftRight); expect(getPaths(left(message.errors))).toContain( @@ -106,13 +106,13 @@ describe('Prebuilt rule schema', () => { }); test('[rule_id, description, from] does not validate', () => { - const payload: Partial = { + const payload: Partial = { rule_id: 'rule-1', description: 'some description', from: 'now-5m', }; - const decoded = PrebuiltRuleToInstall.decode(payload); + const decoded = PrebuiltRuleAsset.decode(payload); const checked = exactCheck(payload, decoded); const message = pipe(checked, foldLeftRight); expect(getPaths(left(message.errors))).toContain( @@ -131,14 +131,14 @@ describe('Prebuilt rule schema', () => { }); test('[rule_id, description, from, to] does not validate', () => { - const payload: Partial = { + const payload: Partial = { rule_id: 'rule-1', description: 'some description', from: 'now-5m', to: 'now', }; - const decoded = PrebuiltRuleToInstall.decode(payload); + const decoded = PrebuiltRuleAsset.decode(payload); const checked = exactCheck(payload, decoded); const message = pipe(checked, foldLeftRight); expect(getPaths(left(message.errors))).toContain( @@ -157,7 +157,7 @@ describe('Prebuilt rule schema', () => { }); test('[rule_id, description, from, to, name] does not validate', () => { - const payload: Partial = { + const payload: Partial = { rule_id: 'rule-1', description: 'some description', from: 'now-5m', @@ -165,7 +165,7 @@ describe('Prebuilt rule schema', () => { name: 'some-name', }; - const decoded = PrebuiltRuleToInstall.decode(payload); + const decoded = PrebuiltRuleAsset.decode(payload); const checked = exactCheck(payload, decoded); const message = pipe(checked, foldLeftRight); expect(getPaths(left(message.errors))).toContain( @@ -181,7 +181,7 @@ describe('Prebuilt rule schema', () => { }); test('[rule_id, description, from, to, name, severity] does not validate', () => { - const payload: Partial = { + const payload: Partial = { rule_id: 'rule-1', description: 'some description', from: 'now-5m', @@ -190,7 +190,7 @@ describe('Prebuilt rule schema', () => { severity: 'low', }; - const decoded = PrebuiltRuleToInstall.decode(payload); + const decoded = PrebuiltRuleAsset.decode(payload); const checked = exactCheck(payload, decoded); const message = pipe(checked, foldLeftRight); expect(getPaths(left(message.errors))).toContain( @@ -203,7 +203,7 @@ describe('Prebuilt rule schema', () => { }); test('[rule_id, description, from, to, name, severity, type] does not validate', () => { - const payload: Partial = { + const payload: Partial = { rule_id: 'rule-1', description: 'some description', from: 'now-5m', @@ -213,7 +213,7 @@ describe('Prebuilt rule schema', () => { type: 'query', }; - const decoded = PrebuiltRuleToInstall.decode(payload); + const decoded = PrebuiltRuleAsset.decode(payload); const checked = exactCheck(payload, decoded); const message = pipe(checked, foldLeftRight); expect(getPaths(left(message.errors))).toEqual([ @@ -224,7 +224,7 @@ describe('Prebuilt rule schema', () => { }); test('[rule_id, description, from, to, name, severity, type, interval] does not validate', () => { - const payload: Partial = { + const payload: Partial = { rule_id: 'rule-1', description: 'some description', from: 'now-5m', @@ -235,7 +235,7 @@ describe('Prebuilt rule schema', () => { type: 'query', }; - const decoded = PrebuiltRuleToInstall.decode(payload); + const decoded = PrebuiltRuleAsset.decode(payload); const checked = exactCheck(payload, decoded); const message = pipe(checked, foldLeftRight); expect(getPaths(left(message.errors))).toEqual([ @@ -246,7 +246,7 @@ describe('Prebuilt rule schema', () => { }); test('[rule_id, description, from, to, name, severity, type, interval, index] does not validate', () => { - const payload: Partial = { + const payload: Partial = { rule_id: 'rule-1', description: 'some description', from: 'now-5m', @@ -258,7 +258,7 @@ describe('Prebuilt rule schema', () => { index: ['index-1'], }; - const decoded = PrebuiltRuleToInstall.decode(payload); + const decoded = PrebuiltRuleAsset.decode(payload); const checked = exactCheck(payload, decoded); const message = pipe(checked, foldLeftRight); expect(getPaths(left(message.errors))).toEqual([ @@ -269,7 +269,7 @@ describe('Prebuilt rule schema', () => { }); test('[rule_id, description, from, to, name, severity, type, query, index, interval, version] does validate', () => { - const payload: PrebuiltRuleToInstall = { + const payload: PrebuiltRuleAsset = { rule_id: 'rule-1', risk_score: 50, description: 'some description', @@ -284,14 +284,14 @@ describe('Prebuilt rule schema', () => { version: 1, }; - const decoded = PrebuiltRuleToInstall.decode(payload); + const decoded = PrebuiltRuleAsset.decode(payload); const checked = exactCheck(payload, decoded); const message = pipe(checked, foldLeftRight); expect(getPaths(left(message.errors))).toEqual([]); }); test('[rule_id, description, from, to, index, name, severity, interval, type, query, language] does not validate', () => { - const payload: Partial = { + const payload: Partial = { rule_id: 'rule-1', description: 'some description', from: 'now-5m', @@ -306,7 +306,7 @@ describe('Prebuilt rule schema', () => { risk_score: 50, }; - const decoded = PrebuiltRuleToInstall.decode(payload); + const decoded = PrebuiltRuleAsset.decode(payload); const checked = exactCheck(payload, decoded); const message = pipe(checked, foldLeftRight); expect(getPaths(left(message.errors))).toEqual([ @@ -316,7 +316,7 @@ describe('Prebuilt rule schema', () => { }); test('[rule_id, description, from, to, index, name, severity, interval, type, query, language, version] does validate', () => { - const payload: Partial = { + const payload: Partial = { rule_id: 'rule-1', description: 'some description', from: 'now-5m', @@ -332,14 +332,14 @@ describe('Prebuilt rule schema', () => { version: 1, }; - const decoded = PrebuiltRuleToInstall.decode(payload); + const decoded = PrebuiltRuleAsset.decode(payload); const checked = exactCheck(payload, decoded); const message = pipe(checked, foldLeftRight); expect(getPaths(left(message.errors))).toEqual([]); }); test('[rule_id, description, from, to, index, name, severity, interval, type, query, language, risk_score, output_index] does not validate', () => { - const payload: Partial & { output_index: string } = { + const payload: Partial & { output_index: string } = { rule_id: 'rule-1', output_index: '.siem-signals', risk_score: 50, @@ -355,7 +355,7 @@ describe('Prebuilt rule schema', () => { language: 'kuery', }; - const decoded = PrebuiltRuleToInstall.decode(payload); + const decoded = PrebuiltRuleAsset.decode(payload); const checked = exactCheck(payload, decoded); const message = pipe(checked, foldLeftRight); expect(getPaths(left(message.errors))).toEqual([ @@ -365,7 +365,7 @@ describe('Prebuilt rule schema', () => { }); test('[rule_id, description, from, to, index, name, severity, interval, type, filter, risk_score, version] does validate', () => { - const payload: Partial = { + const payload: Partial = { rule_id: 'rule-1', description: 'some description', from: 'now-5m', @@ -379,38 +379,38 @@ describe('Prebuilt rule schema', () => { version: 1, }; - const decoded = PrebuiltRuleToInstall.decode(payload); + const decoded = PrebuiltRuleAsset.decode(payload); const checked = exactCheck(payload, decoded); const message = pipe(checked, foldLeftRight); expect(getPaths(left(message.errors))).toEqual([]); }); test('You can send in a namespace', () => { - const payload: PrebuiltRuleToInstall = { + const payload: PrebuiltRuleAsset = { ...getPrebuiltRuleMock(), namespace: 'a namespace', }; - const decoded = PrebuiltRuleToInstall.decode(payload); + const decoded = PrebuiltRuleAsset.decode(payload); const checked = exactCheck(payload, decoded); const message = pipe(checked, foldLeftRight); expect(getPaths(left(message.errors))).toEqual([]); }); test('You can send in an empty array to threat', () => { - const payload: PrebuiltRuleToInstall = { + const payload: PrebuiltRuleAsset = { ...getPrebuiltRuleMock(), threat: [], }; - const decoded = PrebuiltRuleToInstall.decode(payload); + const decoded = PrebuiltRuleAsset.decode(payload); const checked = exactCheck(payload, decoded); const message = pipe(checked, foldLeftRight); expect(getPaths(left(message.errors))).toEqual([]); }); test('[rule_id, description, from, to, index, name, severity, interval, type, filter, risk_score, output_index, threat] does validate', () => { - const payload: PrebuiltRuleToInstall = { + const payload: PrebuiltRuleAsset = { rule_id: 'rule-1', risk_score: 50, description: 'some description', @@ -441,31 +441,31 @@ describe('Prebuilt rule schema', () => { version: 1, }; - const decoded = PrebuiltRuleToInstall.decode(payload); + const decoded = PrebuiltRuleAsset.decode(payload); const checked = exactCheck(payload, decoded); const message = pipe(checked, foldLeftRight); expect(getPaths(left(message.errors))).toEqual([]); }); test('allows references to be sent as valid', () => { - const payload: PrebuiltRuleToInstall = { + const payload: PrebuiltRuleAsset = { ...getPrebuiltRuleMock(), references: ['index-1'], }; - const decoded = PrebuiltRuleToInstall.decode(payload); + const decoded = PrebuiltRuleAsset.decode(payload); const checked = exactCheck(payload, decoded); const message = pipe(checked, foldLeftRight); expect(getPaths(left(message.errors))).toEqual([]); }); test('immutable cannot be set in a pre-packaged rule', () => { - const payload: PrebuiltRuleToInstall & { immutable: boolean } = { + const payload: PrebuiltRuleAsset & { immutable: boolean } = { ...getPrebuiltRuleMock(), immutable: true, }; - const decoded = PrebuiltRuleToInstall.decode(payload); + const decoded = PrebuiltRuleAsset.decode(payload); const checked = exactCheck(payload, decoded); const message = pipe(checked, foldLeftRight); expect(getPaths(left(message.errors))).toEqual(['invalid keys "immutable"']); @@ -473,11 +473,11 @@ describe('Prebuilt rule schema', () => { }); test('rule_id is required', () => { - const payload: PrebuiltRuleToInstall = getPrebuiltRuleMock(); + const payload: PrebuiltRuleAsset = getPrebuiltRuleMock(); // @ts-expect-error delete payload.rule_id; - const decoded = PrebuiltRuleToInstall.decode(payload); + const decoded = PrebuiltRuleAsset.decode(payload); const checked = exactCheck(payload, decoded); const message = pipe(checked, foldLeftRight); expect(getPaths(left(message.errors))).toEqual([ @@ -487,12 +487,12 @@ describe('Prebuilt rule schema', () => { }); test('references cannot be numbers', () => { - const payload: Omit & { references: number[] } = { + const payload: Omit & { references: number[] } = { ...getPrebuiltRuleMock(), references: [5], }; - const decoded = PrebuiltRuleToInstall.decode(payload); + const decoded = PrebuiltRuleAsset.decode(payload); const checked = exactCheck(payload, decoded); const message = pipe(checked, foldLeftRight); expect(getPaths(left(message.errors))).toEqual(['Invalid value "5" supplied to "references"']); @@ -500,12 +500,12 @@ describe('Prebuilt rule schema', () => { }); test('indexes cannot be numbers', () => { - const payload: Omit & { index: number[] } = { + const payload: Omit & { index: number[] } = { ...getPrebuiltRuleMock(), index: [5], }; - const decoded = PrebuiltRuleToInstall.decode(payload); + const decoded = PrebuiltRuleAsset.decode(payload); const checked = exactCheck(payload, decoded); const message = pipe(checked, foldLeftRight); expect(getPaths(left(message.errors))).toEqual(['Invalid value "5" supplied to "index"']); @@ -518,19 +518,19 @@ describe('Prebuilt rule schema', () => { filters: [], }; - const decoded = PrebuiltRuleToInstall.decode(payload); + const decoded = PrebuiltRuleAsset.decode(payload); const checked = exactCheck(payload, decoded); const message = pipe(checked, foldLeftRight); expect(getPaths(left(message.errors))).toEqual([]); }); test('filters cannot be a string', () => { - const payload: Omit & { filters: string } = { + const payload: Omit & { filters: string } = { ...getPrebuiltRuleMock(), filters: 'some string', }; - const decoded = PrebuiltRuleToInstall.decode(payload); + const decoded = PrebuiltRuleAsset.decode(payload); const checked = exactCheck(payload, decoded); const message = pipe(checked, foldLeftRight); expect(getPaths(left(message.errors))).toEqual([ @@ -545,7 +545,7 @@ describe('Prebuilt rule schema', () => { language: 'kuery', }; - const decoded = PrebuiltRuleToInstall.decode(payload); + const decoded = PrebuiltRuleAsset.decode(payload); const checked = exactCheck(payload, decoded); const message = pipe(checked, foldLeftRight); expect(getPaths(left(message.errors))).toEqual([]); @@ -557,19 +557,19 @@ describe('Prebuilt rule schema', () => { language: 'lucene', }; - const decoded = PrebuiltRuleToInstall.decode(payload); + const decoded = PrebuiltRuleAsset.decode(payload); const checked = exactCheck(payload, decoded); const message = pipe(checked, foldLeftRight); expect(getPaths(left(message.errors))).toEqual([]); }); test('language does not validate with something made up', () => { - const payload: Omit & { language: string } = { + const payload: Omit & { language: string } = { ...getPrebuiltRuleMock(), language: 'something-made-up', }; - const decoded = PrebuiltRuleToInstall.decode(payload); + const decoded = PrebuiltRuleAsset.decode(payload); const checked = exactCheck(payload, decoded); const message = pipe(checked, foldLeftRight); expect(getPaths(left(message.errors))).toEqual([ @@ -579,12 +579,12 @@ describe('Prebuilt rule schema', () => { }); test('max_signals cannot be negative', () => { - const payload: PrebuiltRuleToInstall = { + const payload: PrebuiltRuleAsset = { ...getPrebuiltRuleMock(), max_signals: -1, }; - const decoded = PrebuiltRuleToInstall.decode(payload); + const decoded = PrebuiltRuleAsset.decode(payload); const checked = exactCheck(payload, decoded); const message = pipe(checked, foldLeftRight); expect(getPaths(left(message.errors))).toEqual([ @@ -594,12 +594,12 @@ describe('Prebuilt rule schema', () => { }); test('max_signals cannot be zero', () => { - const payload: PrebuiltRuleToInstall = { + const payload: PrebuiltRuleAsset = { ...getPrebuiltRuleMock(), max_signals: 0, }; - const decoded = PrebuiltRuleToInstall.decode(payload); + const decoded = PrebuiltRuleAsset.decode(payload); const checked = exactCheck(payload, decoded); const message = pipe(checked, foldLeftRight); expect(getPaths(left(message.errors))).toEqual(['Invalid value "0" supplied to "max_signals"']); @@ -607,36 +607,36 @@ describe('Prebuilt rule schema', () => { }); test('max_signals can be 1', () => { - const payload: PrebuiltRuleToInstall = { + const payload: PrebuiltRuleAsset = { ...getPrebuiltRuleMock(), max_signals: 1, }; - const decoded = PrebuiltRuleToInstall.decode(payload); + const decoded = PrebuiltRuleAsset.decode(payload); const checked = exactCheck(payload, decoded); const message = pipe(checked, foldLeftRight); expect(getPaths(left(message.errors))).toEqual([]); }); test('You can optionally send in an array of tags', () => { - const payload: PrebuiltRuleToInstall = { + const payload: PrebuiltRuleAsset = { ...getPrebuiltRuleMock(), tags: ['tag_1', 'tag_2'], }; - const decoded = PrebuiltRuleToInstall.decode(payload); + const decoded = PrebuiltRuleAsset.decode(payload); const checked = exactCheck(payload, decoded); const message = pipe(checked, foldLeftRight); expect(getPaths(left(message.errors))).toEqual([]); }); test('You cannot send in an array of tags that are numbers', () => { - const payload: Omit & { tags: number[] } = { + const payload: Omit & { tags: number[] } = { ...getPrebuiltRuleMock(), tags: [0, 1, 2], }; - const decoded = PrebuiltRuleToInstall.decode(payload); + const decoded = PrebuiltRuleAsset.decode(payload); const checked = exactCheck(payload, decoded); const message = pipe(checked, foldLeftRight); expect(getPaths(left(message.errors))).toEqual([ @@ -648,8 +648,8 @@ describe('Prebuilt rule schema', () => { }); test('You cannot send in an array of threat that are missing "framework"', () => { - const payload: Omit & { - threat: Array>>; + const payload: Omit & { + threat: Array>>; } = { ...getPrebuiltRuleMock(), threat: [ @@ -670,7 +670,7 @@ describe('Prebuilt rule schema', () => { ], }; - const decoded = PrebuiltRuleToInstall.decode(payload); + const decoded = PrebuiltRuleAsset.decode(payload); const checked = exactCheck(payload, decoded); const message = pipe(checked, foldLeftRight); expect(getPaths(left(message.errors))).toEqual([ @@ -680,8 +680,8 @@ describe('Prebuilt rule schema', () => { }); test('You cannot send in an array of threat that are missing "tactic"', () => { - const payload: Omit & { - threat: Array>>; + const payload: Omit & { + threat: Array>>; } = { ...getPrebuiltRuleMock(), threat: [ @@ -698,7 +698,7 @@ describe('Prebuilt rule schema', () => { ], }; - const decoded = PrebuiltRuleToInstall.decode(payload); + const decoded = PrebuiltRuleAsset.decode(payload); const checked = exactCheck(payload, decoded); const message = pipe(checked, foldLeftRight); expect(getPaths(left(message.errors))).toEqual([ @@ -708,8 +708,8 @@ describe('Prebuilt rule schema', () => { }); test('You can send in an array of threat that are missing "technique"', () => { - const payload: Omit & { - threat: Array>>; + const payload: Omit & { + threat: Array>>; } = { ...getPrebuiltRuleMock(), threat: [ @@ -724,33 +724,33 @@ describe('Prebuilt rule schema', () => { ], }; - const decoded = PrebuiltRuleToInstall.decode(payload); + const decoded = PrebuiltRuleAsset.decode(payload); const checked = exactCheck(payload, decoded); const message = pipe(checked, foldLeftRight); expect(getPaths(left(message.errors))).toEqual([]); }); test('You can optionally send in an array of false positives', () => { - const payload: PrebuiltRuleToInstall = { + const payload: PrebuiltRuleAsset = { ...getPrebuiltRuleMock(), false_positives: ['false_1', 'false_2'], }; - const decoded = PrebuiltRuleToInstall.decode(payload); + const decoded = PrebuiltRuleAsset.decode(payload); const checked = exactCheck(payload, decoded); const message = pipe(checked, foldLeftRight); expect(getPaths(left(message.errors))).toEqual([]); }); test('You cannot send in an array of false positives that are numbers', () => { - const payload: Omit & { + const payload: Omit & { false_positives: number[]; } = { ...getPrebuiltRuleMock(), false_positives: [5, 4], }; - const decoded = PrebuiltRuleToInstall.decode(payload); + const decoded = PrebuiltRuleAsset.decode(payload); const checked = exactCheck(payload, decoded); const message = pipe(checked, foldLeftRight); expect(getPaths(left(message.errors))).toEqual([ @@ -760,12 +760,12 @@ describe('Prebuilt rule schema', () => { expect(message.schema).toEqual({}); }); test('You cannot set the risk_score to 101', () => { - const payload: PrebuiltRuleToInstall = { + const payload: PrebuiltRuleAsset = { ...getPrebuiltRuleMock(), risk_score: 101, }; - const decoded = PrebuiltRuleToInstall.decode(payload); + const decoded = PrebuiltRuleAsset.decode(payload); const checked = exactCheck(payload, decoded); const message = pipe(checked, foldLeftRight); expect(getPaths(left(message.errors))).toEqual([ @@ -775,12 +775,12 @@ describe('Prebuilt rule schema', () => { }); test('You cannot set the risk_score to -1', () => { - const payload: PrebuiltRuleToInstall = { + const payload: PrebuiltRuleAsset = { ...getPrebuiltRuleMock(), risk_score: -1, }; - const decoded = PrebuiltRuleToInstall.decode(payload); + const decoded = PrebuiltRuleAsset.decode(payload); const checked = exactCheck(payload, decoded); const message = pipe(checked, foldLeftRight); expect(getPaths(left(message.errors))).toEqual(['Invalid value "-1" supplied to "risk_score"']); @@ -788,50 +788,50 @@ describe('Prebuilt rule schema', () => { }); test('You can set the risk_score to 0', () => { - const payload: PrebuiltRuleToInstall = { + const payload: PrebuiltRuleAsset = { ...getPrebuiltRuleMock(), risk_score: 0, }; - const decoded = PrebuiltRuleToInstall.decode(payload); + const decoded = PrebuiltRuleAsset.decode(payload); const checked = exactCheck(payload, decoded); const message = pipe(checked, foldLeftRight); expect(getPaths(left(message.errors))).toEqual([]); }); test('You can set the risk_score to 100', () => { - const payload: PrebuiltRuleToInstall = { + const payload: PrebuiltRuleAsset = { ...getPrebuiltRuleMock(), risk_score: 100, }; - const decoded = PrebuiltRuleToInstall.decode(payload); + const decoded = PrebuiltRuleAsset.decode(payload); const checked = exactCheck(payload, decoded); const message = pipe(checked, foldLeftRight); expect(getPaths(left(message.errors))).toEqual([]); }); test('You can set meta to any object you want', () => { - const payload: PrebuiltRuleToInstall = { + const payload: PrebuiltRuleAsset = { ...getPrebuiltRuleMock(), meta: { somethingMadeUp: { somethingElse: true }, }, }; - const decoded = PrebuiltRuleToInstall.decode(payload); + const decoded = PrebuiltRuleAsset.decode(payload); const checked = exactCheck(payload, decoded); const message = pipe(checked, foldLeftRight); expect(getPaths(left(message.errors))).toEqual([]); }); test('You cannot create meta as a string', () => { - const payload: Omit & { meta: string } = { + const payload: Omit & { meta: string } = { ...getPrebuiltRuleMock(), meta: 'should not work', }; - const decoded = PrebuiltRuleToInstall.decode(payload); + const decoded = PrebuiltRuleAsset.decode(payload); const checked = exactCheck(payload, decoded); const message = pipe(checked, foldLeftRight); expect(getPaths(left(message.errors))).toEqual([ @@ -841,25 +841,25 @@ describe('Prebuilt rule schema', () => { }); test('validates with timeline_id and timeline_title', () => { - const payload: PrebuiltRuleToInstall = { + const payload: PrebuiltRuleAsset = { ...getPrebuiltRuleMock(), timeline_id: 'timeline-id', timeline_title: 'timeline-title', }; - const decoded = PrebuiltRuleToInstall.decode(payload); + const decoded = PrebuiltRuleAsset.decode(payload); const checked = exactCheck(payload, decoded); const message = pipe(checked, foldLeftRight); expect(getPaths(left(message.errors))).toEqual([]); }); test('You cannot set the severity to a value other than low, medium, high, or critical', () => { - const payload: Omit & { severity: string } = { + const payload: Omit & { severity: string } = { ...getPrebuiltRuleMock(), severity: 'junk', }; - const decoded = PrebuiltRuleToInstall.decode(payload); + const decoded = PrebuiltRuleAsset.decode(payload); const checked = exactCheck(payload, decoded); const message = pipe(checked, foldLeftRight); expect(getPaths(left(message.errors))).toEqual(['Invalid value "junk" supplied to "severity"']); @@ -867,12 +867,12 @@ describe('Prebuilt rule schema', () => { }); test('You cannot send in an array of actions that are missing "group"', () => { - const payload: Omit = { + const payload: Omit = { ...getPrebuiltRuleMock(), actions: [{ id: 'id', action_type_id: 'action_type_id', params: {} }], }; - const decoded = PrebuiltRuleToInstall.decode(payload); + const decoded = PrebuiltRuleAsset.decode(payload); const checked = exactCheck(payload, decoded); const message = pipe(checked, foldLeftRight); expect(getPaths(left(message.errors))).toEqual([ @@ -882,12 +882,12 @@ describe('Prebuilt rule schema', () => { }); test('You cannot send in an array of actions that are missing "id"', () => { - const payload: Omit = { + const payload: Omit = { ...getPrebuiltRuleMock(), actions: [{ group: 'group', action_type_id: 'action_type_id', params: {} }], }; - const decoded = PrebuiltRuleToInstall.decode(payload); + const decoded = PrebuiltRuleAsset.decode(payload); const checked = exactCheck(payload, decoded); const message = pipe(checked, foldLeftRight); expect(getPaths(left(message.errors))).toEqual([ @@ -897,12 +897,12 @@ describe('Prebuilt rule schema', () => { }); test('You cannot send in an array of actions that are missing "action_type_id"', () => { - const payload: Omit = { + const payload: Omit = { ...getPrebuiltRuleMock(), actions: [{ group: 'group', id: 'id', params: {} }], }; - const decoded = PrebuiltRuleToInstall.decode(payload); + const decoded = PrebuiltRuleAsset.decode(payload); const checked = exactCheck(payload, decoded); const message = pipe(checked, foldLeftRight); expect(getPaths(left(message.errors))).toEqual([ @@ -912,12 +912,12 @@ describe('Prebuilt rule schema', () => { }); test('You cannot send in an array of actions that are missing "params"', () => { - const payload: Omit = { + const payload: Omit = { ...getPrebuiltRuleMock(), actions: [{ group: 'group', id: 'id', action_type_id: 'action_type_id' }], }; - const decoded = PrebuiltRuleToInstall.decode(payload); + const decoded = PrebuiltRuleAsset.decode(payload); const checked = exactCheck(payload, decoded); const message = pipe(checked, foldLeftRight); expect(getPaths(left(message.errors))).toEqual([ @@ -927,7 +927,7 @@ describe('Prebuilt rule schema', () => { }); test('You cannot send in an array of actions that are including "actionTypeId"', () => { - const payload: Omit = { + const payload: Omit = { ...getPrebuiltRuleMock(), actions: [ { @@ -939,7 +939,7 @@ describe('Prebuilt rule schema', () => { ], }; - const decoded = PrebuiltRuleToInstall.decode(payload); + const decoded = PrebuiltRuleAsset.decode(payload); const checked = exactCheck(payload, decoded); const message = pipe(checked, foldLeftRight); expect(getPaths(left(message.errors))).toEqual([ @@ -950,38 +950,38 @@ describe('Prebuilt rule schema', () => { describe('note', () => { test('You can set note to a string', () => { - const payload: PrebuiltRuleToInstall = { + const payload: PrebuiltRuleAsset = { ...getPrebuiltRuleMock(), note: '# documentation markdown here', }; - const decoded = PrebuiltRuleToInstall.decode(payload); + const decoded = PrebuiltRuleAsset.decode(payload); const checked = exactCheck(payload, decoded); const message = pipe(checked, foldLeftRight); expect(getPaths(left(message.errors))).toEqual([]); }); test('You can set note to an empty string', () => { - const payload: PrebuiltRuleToInstall = { + const payload: PrebuiltRuleAsset = { ...getPrebuiltRuleMock(), note: '', }; - const decoded = PrebuiltRuleToInstall.decode(payload); + const decoded = PrebuiltRuleAsset.decode(payload); const checked = exactCheck(payload, decoded); const message = pipe(checked, foldLeftRight); expect(getPaths(left(message.errors))).toEqual([]); }); test('You cannot create note as an object', () => { - const payload: Omit & { note: {} } = { + const payload: Omit & { note: {} } = { ...getPrebuiltRuleMock(), note: { somethingHere: 'something else', }, }; - const decoded = PrebuiltRuleToInstall.decode(payload); + const decoded = PrebuiltRuleAsset.decode(payload); const checked = exactCheck(payload, decoded); const message = pipe(checked, foldLeftRight); expect(getPaths(left(message.errors))).toEqual([ @@ -991,7 +991,7 @@ describe('Prebuilt rule schema', () => { }); test('[rule_id, description, from, to, index, name, severity, interval, type, filter, risk_score, note] does validate', () => { - const payload: PrebuiltRuleToInstall = { + const payload: PrebuiltRuleAsset = { rule_id: 'rule-1', description: 'some description', from: 'now-5m', @@ -1006,7 +1006,7 @@ describe('Prebuilt rule schema', () => { version: 1, }; - const decoded = PrebuiltRuleToInstall.decode(payload); + const decoded = PrebuiltRuleAsset.decode(payload); const checked = exactCheck(payload, decoded); const message = pipe(checked, foldLeftRight); expect(getPaths(left(message.errors))).toEqual([]); @@ -1015,7 +1015,7 @@ describe('Prebuilt rule schema', () => { describe('exception_list', () => { test('[rule_id, description, from, to, index, name, severity, interval, type, filters, risk_score, note, version, and exceptions_list] does validate', () => { - const payload: PrebuiltRuleToInstall = { + const payload: PrebuiltRuleAsset = { rule_id: 'rule-1', description: 'some description', from: 'now-5m', @@ -1032,14 +1032,14 @@ describe('Prebuilt rule schema', () => { exceptions_list: getListArrayMock(), }; - const decoded = PrebuiltRuleToInstall.decode(payload); + const decoded = PrebuiltRuleAsset.decode(payload); const checked = exactCheck(payload, decoded); const message = pipe(checked, foldLeftRight); expect(getPaths(left(message.errors))).toEqual([]); }); test('[rule_id, description, from, to, index, name, severity, interval, type, filter, risk_score, note, version, and empty exceptions_list] does validate', () => { - const payload: PrebuiltRuleToInstall = { + const payload: PrebuiltRuleAsset = { rule_id: 'rule-1', description: 'some description', from: 'now-5m', @@ -1056,7 +1056,7 @@ describe('Prebuilt rule schema', () => { exceptions_list: [], }; - const decoded = PrebuiltRuleToInstall.decode(payload); + const decoded = PrebuiltRuleAsset.decode(payload); const checked = exactCheck(payload, decoded); const message = pipe(checked, foldLeftRight); expect(getPaths(left(message.errors))).toEqual([]); @@ -1080,7 +1080,7 @@ describe('Prebuilt rule schema', () => { exceptions_list: [{ id: 'uuid_here', namespace_type: 'not a namespace type' }], }; - const decoded = PrebuiltRuleToInstall.decode(payload); + const decoded = PrebuiltRuleAsset.decode(payload); const checked = exactCheck(payload, decoded); const message = pipe(checked, foldLeftRight); expect(getPaths(left(message.errors))).toEqual([ @@ -1092,7 +1092,7 @@ describe('Prebuilt rule schema', () => { }); test('[rule_id, description, from, to, index, name, severity, interval, type, filters, risk_score, note, version, and non-existent exceptions_list] does validate with empty exceptions_list', () => { - const payload: PrebuiltRuleToInstall = { + const payload: PrebuiltRuleAsset = { rule_id: 'rule-1', description: 'some description', from: 'now-5m', @@ -1108,7 +1108,7 @@ describe('Prebuilt rule schema', () => { note: '# some markdown', }; - const decoded = PrebuiltRuleToInstall.decode(payload); + const decoded = PrebuiltRuleAsset.decode(payload); const checked = exactCheck(payload, decoded); const message = pipe(checked, foldLeftRight); expect(getPaths(left(message.errors))).toEqual([]); @@ -1118,7 +1118,7 @@ describe('Prebuilt rule schema', () => { describe('threat_mapping', () => { test('You can set a threat query, index, mapping, filters on a pre-packaged rule', () => { const payload = getPrebuiltThreatMatchRuleMock(); - const decoded = PrebuiltRuleToInstall.decode(payload); + const decoded = PrebuiltRuleAsset.decode(payload); const checked = exactCheck(payload, decoded); const message = pipe(checked, foldLeftRight); expect(getPaths(left(message.errors))).toEqual([]); diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/model/rule_assets/prebuilt_rule_asset.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/model/rule_assets/prebuilt_rule_asset.ts new file mode 100644 index 0000000000000..d1e6029678073 --- /dev/null +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/model/rule_assets/prebuilt_rule_asset.ts @@ -0,0 +1,51 @@ +/* + * 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 * as t from 'io-ts'; +import { + RelatedIntegrationArray, + RequiredFieldArray, + SetupGuide, + RuleSignatureId, + RuleVersion, + BaseCreateProps, + TypeSpecificCreateProps, +} from '../../../../../../common/detection_engine/rule_schema'; + +/** + * Asset containing source content of a prebuilt Security detection rule. + * Is defined for each prebuilt rule in https://github.com/elastic/detection-rules. + * Is shipped via the `security_detection_engine` Fleet package. + * Is installed as saved objects of type "security-rule" when the package is installed. + * + * Additionally, "security-rule" assets can be shipped via other Fleet packages, such as: + * - LotL Attack Detection https://github.com/elastic/integrations/pull/2115 + * - Data Exfiltration Detection + * + * Big differences between this schema and RuleCreateProps: + * - rule_id is required here + * - version is a required field that must exist + */ +export type PrebuiltRuleAsset = t.TypeOf; +export const PrebuiltRuleAsset = t.intersection([ + BaseCreateProps, + TypeSpecificCreateProps, + // version is required here, which supercedes the defaultable version in baseSchema + t.exact( + t.type({ + rule_id: RuleSignatureId, + version: RuleVersion, + }) + ), + t.exact( + t.partial({ + related_integrations: RelatedIntegrationArray, + required_fields: RequiredFieldArray, + setup: SetupGuide, + }) + ), +]); diff --git a/x-pack/plugins/security_solution/common/detection_engine/prebuilt_rules/model/prebuilt_rule_validate_type_dependents.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/model/rule_assets/prebuilt_rule_asset_validate_type_dependents.test.ts similarity index 87% rename from x-pack/plugins/security_solution/common/detection_engine/prebuilt_rules/model/prebuilt_rule_validate_type_dependents.test.ts rename to x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/model/rule_assets/prebuilt_rule_asset_validate_type_dependents.test.ts index 33021d0fc9a1e..1964488442ce7 100644 --- a/x-pack/plugins/security_solution/common/detection_engine/prebuilt_rules/model/prebuilt_rule_validate_type_dependents.test.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/model/rule_assets/prebuilt_rule_asset_validate_type_dependents.test.ts @@ -5,13 +5,13 @@ * 2.0. */ -import type { PrebuiltRuleToInstall } from './prebuilt_rule'; -import { addPrepackagedRuleValidateTypeDependents } from './prebuilt_rule_validate_type_dependents'; -import { getPrebuiltRuleMock } from './prebuilt_rule.mock'; +import type { PrebuiltRuleAsset } from './prebuilt_rule_asset'; +import { addPrepackagedRuleValidateTypeDependents } from './prebuilt_rule_asset_validate_type_dependents'; +import { getPrebuiltRuleMock } from './prebuilt_rule_asset.mock'; describe('addPrepackagedRuleValidateTypeDependents', () => { test('You cannot omit timeline_title when timeline_id is present', () => { - const schema: PrebuiltRuleToInstall = { + const schema: PrebuiltRuleAsset = { ...getPrebuiltRuleMock(), timeline_id: '123', }; @@ -21,7 +21,7 @@ describe('addPrepackagedRuleValidateTypeDependents', () => { }); test('You cannot have empty string for timeline_title when timeline_id is present', () => { - const schema: PrebuiltRuleToInstall = { + const schema: PrebuiltRuleAsset = { ...getPrebuiltRuleMock(), timeline_id: '123', timeline_title: '', @@ -31,7 +31,7 @@ describe('addPrepackagedRuleValidateTypeDependents', () => { }); test('You cannot have timeline_title with an empty timeline_id', () => { - const schema: PrebuiltRuleToInstall = { + const schema: PrebuiltRuleAsset = { ...getPrebuiltRuleMock(), timeline_id: '', timeline_title: 'some-title', @@ -41,7 +41,7 @@ describe('addPrepackagedRuleValidateTypeDependents', () => { }); test('You cannot have timeline_title without timeline_id', () => { - const schema: PrebuiltRuleToInstall = { + const schema: PrebuiltRuleAsset = { ...getPrebuiltRuleMock(), timeline_title: 'some-title', }; @@ -59,7 +59,7 @@ describe('addPrepackagedRuleValidateTypeDependents', () => { value: -1, }, }; - const errors = addPrepackagedRuleValidateTypeDependents(schema as PrebuiltRuleToInstall); + const errors = addPrepackagedRuleValidateTypeDependents(schema as PrebuiltRuleAsset); expect(errors).toEqual(['"threshold.value" has to be bigger than 0']); }); @@ -72,7 +72,7 @@ describe('addPrepackagedRuleValidateTypeDependents', () => { value: 1, }, }; - const errors = addPrepackagedRuleValidateTypeDependents(schema as PrebuiltRuleToInstall); + const errors = addPrepackagedRuleValidateTypeDependents(schema as PrebuiltRuleAsset); expect(errors).toEqual(['Number of fields must be 3 or less']); }); @@ -91,7 +91,7 @@ describe('addPrepackagedRuleValidateTypeDependents', () => { ], }, }; - const errors = addPrepackagedRuleValidateTypeDependents(schema as PrebuiltRuleToInstall); + const errors = addPrepackagedRuleValidateTypeDependents(schema as PrebuiltRuleAsset); expect(errors).toEqual(['Cardinality of a field that is being aggregated on is always 1']); }); }); diff --git a/x-pack/plugins/security_solution/common/detection_engine/prebuilt_rules/model/prebuilt_rule_validate_type_dependents.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/model/rule_assets/prebuilt_rule_asset_validate_type_dependents.ts similarity index 85% rename from x-pack/plugins/security_solution/common/detection_engine/prebuilt_rules/model/prebuilt_rule_validate_type_dependents.ts rename to x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/model/rule_assets/prebuilt_rule_asset_validate_type_dependents.ts index 3d9264b3f0556..3ebcad04dbe55 100644 --- a/x-pack/plugins/security_solution/common/detection_engine/prebuilt_rules/model/prebuilt_rule_validate_type_dependents.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/model/rule_assets/prebuilt_rule_asset_validate_type_dependents.ts @@ -5,13 +5,13 @@ * 2.0. */ -import type { PrebuiltRuleToInstall } from './prebuilt_rule'; +import type { PrebuiltRuleAsset } from './prebuilt_rule_asset'; -export const addPrepackagedRuleValidateTypeDependents = (rule: PrebuiltRuleToInstall): string[] => { +export const addPrepackagedRuleValidateTypeDependents = (rule: PrebuiltRuleAsset): string[] => { return [...validateTimelineId(rule), ...validateTimelineTitle(rule), ...validateThreshold(rule)]; }; -const validateTimelineId = (rule: PrebuiltRuleToInstall): string[] => { +const validateTimelineId = (rule: PrebuiltRuleAsset): string[] => { if (rule.timeline_id != null) { if (rule.timeline_title == null) { return ['when "timeline_id" exists, "timeline_title" must also exist']; @@ -24,7 +24,7 @@ const validateTimelineId = (rule: PrebuiltRuleToInstall): string[] => { return []; }; -const validateTimelineTitle = (rule: PrebuiltRuleToInstall): string[] => { +const validateTimelineTitle = (rule: PrebuiltRuleAsset): string[] => { if (rule.timeline_title != null) { if (rule.timeline_id == null) { return ['when "timeline_title" exists, "timeline_id" must also exist']; @@ -37,7 +37,7 @@ const validateTimelineTitle = (rule: PrebuiltRuleToInstall): string[] => { return []; }; -const validateThreshold = (rule: PrebuiltRuleToInstall): string[] => { +const validateThreshold = (rule: PrebuiltRuleAsset): string[] => { const errors: string[] = []; if (rule.type === 'threshold') { if (!rule.threshold) { diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/model/rule_versions/get_version_buckets.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/model/rule_versions/get_version_buckets.ts new file mode 100644 index 0000000000000..9dab6da397b0d --- /dev/null +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/model/rule_versions/get_version_buckets.ts @@ -0,0 +1,54 @@ +/* + * 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 type { PrebuiltRuleVersionInfo } from './prebuilt_rule_version_info'; + +export interface GetVersionBucketsArgs { + latestVersions: PrebuiltRuleVersionInfo[]; + installedVersions: PrebuiltRuleVersionInfo[]; +} + +export interface VersionBuckets { + latestVersions: PrebuiltRuleVersionInfo[]; + installedVersions: PrebuiltRuleVersionInfo[]; + latestVersionsToInstall: PrebuiltRuleVersionInfo[]; + latestVersionsToUpgrade: PrebuiltRuleVersionInfo[]; + installedVersionsToUpgrade: PrebuiltRuleVersionInfo[]; +} + +export const getVersionBuckets = (args: GetVersionBucketsArgs): VersionBuckets => { + const { latestVersions, installedVersions } = args; + + const installedVersionsMap = new Map(installedVersions.map((item) => [item.rule_id, item])); + + const latestVersionsToInstall: PrebuiltRuleVersionInfo[] = []; + const latestVersionsToUpgrade: PrebuiltRuleVersionInfo[] = []; + const installedVersionsToUpgrade: PrebuiltRuleVersionInfo[] = []; + + latestVersions.forEach((latestVersion) => { + const installedVersion = installedVersionsMap.get(latestVersion.rule_id); + + if (installedVersion == null) { + // If this rule is not installed + latestVersionsToInstall.push(latestVersion); + } + + if (installedVersion != null && installedVersion.version < latestVersion.version) { + // If this rule is installed but outdated + latestVersionsToUpgrade.push(latestVersion); + installedVersionsToUpgrade.push(installedVersion); + } + }); + + return { + latestVersions, + installedVersions, + latestVersionsToInstall, + latestVersionsToUpgrade, + installedVersionsToUpgrade, + }; +}; diff --git a/x-pack/plugins/observability/public/pages/slos/helpers/is_slo_feature_enabled.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/model/rule_versions/prebuilt_rule_version_info.ts similarity index 55% rename from x-pack/plugins/observability/public/pages/slos/helpers/is_slo_feature_enabled.ts rename to x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/model/rule_versions/prebuilt_rule_version_info.ts index dd647913d2a1f..bd130917be980 100644 --- a/x-pack/plugins/observability/public/pages/slos/helpers/is_slo_feature_enabled.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/model/rule_versions/prebuilt_rule_version_info.ts @@ -5,8 +5,12 @@ * 2.0. */ -import { ConfigSchema } from '../../../plugin'; +import type { + RuleSignatureId, + RuleVersion, +} from '../../../../../../common/detection_engine/rule_schema'; -export function isSloFeatureEnabled(config: ConfigSchema): boolean { - return config.unsafe.slo.enabled === true; +export interface PrebuiltRuleVersionInfo { + rule_id: RuleSignatureId; + version: RuleVersion; } diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/api/rules/filters/route.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/api/rules/filters/route.test.ts index 969c8c1480df6..b03be6c2fb357 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/api/rules/filters/route.test.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/api/rules/filters/route.test.ts @@ -14,6 +14,12 @@ import { } from '../../../../routes/__mocks__/request_responses'; import { requestContextMock, serverMock } from '../../../../routes/__mocks__'; +const emptyTagAggregationResult = { + tags: { + buckets: [], + }, +}; + describe('Rule management filters route', () => { let server: ReturnType; let { clients, context } = requestContextMock.createTools(); @@ -30,6 +36,7 @@ describe('Rule management filters route', () => { describe('status codes', () => { test('returns 200', async () => { + clients.rulesClient.aggregate.mockResolvedValue(emptyTagAggregationResult); const response = await server.inject( getRuleManagementFiltersRequest(), requestContextMock.convertContext(context) @@ -41,6 +48,7 @@ describe('Rule management filters route', () => { clients.rulesClient.find.mockImplementation(async () => { throw new Error('Test error'); }); + clients.rulesClient.aggregate.mockResolvedValue(emptyTagAggregationResult); const response = await server.inject( getRuleManagementFiltersRequest(), requestContextMock.convertContext(context) @@ -57,9 +65,34 @@ describe('Rule management filters route', () => { test('1 rule installed, 1 custom rule and 3 tags', async () => { clients.rulesClient.find.mockResolvedValue(getFindResultWithSingleHit()); clients.rulesClient.aggregate.mockResolvedValue({ - alertExecutionStatus: {}, - ruleLastRunOutcome: {}, - ruleTags: ['a', 'b', 'c'], + status: { + buckets: [], + }, + outcome: { + buckets: [], + }, + tags: { + buckets: [ + { + key: { + tags: 'a', + }, + doc_count: 1, + }, + { + key: { + tags: 'b', + }, + doc_count: 1, + }, + { + key: { + tags: 'c', + }, + doc_count: 1, + }, + ], + }, }); const request = getRuleManagementFiltersRequest(); const response = await server.inject(request, requestContextMock.convertContext(context)); diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/api/tags/read_tags/read_tags.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/api/tags/read_tags/read_tags.test.ts index 1750cecdf1673..16424bee7c266 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/api/tags/read_tags/read_tags.test.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/api/tags/read_tags/read_tags.test.ts @@ -17,9 +17,34 @@ describe('read_tags', () => { test('it should return tags from the aggregation', async () => { const rulesClient = rulesClientMock.create(); rulesClient.aggregate.mockResolvedValue({ - alertExecutionStatus: {}, - ruleLastRunOutcome: {}, - ruleTags: ['tag 1', 'tag 2', 'tag 3', 'tag 4'], + tags: { + buckets: [ + { + key: { + tags: 'tag 1', + }, + doc_count: 1, + }, + { + key: { + tags: 'tag 2', + }, + doc_count: 1, + }, + { + key: { + tags: 'tag 3', + }, + doc_count: 1, + }, + { + key: { + tags: 'tag 4', + }, + doc_count: 1, + }, + ], + }, }); const tags = await readTags({ rulesClient }); diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/api/tags/read_tags/read_tags.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/api/tags/read_tags/read_tags.ts index 1453bb4796246..42b845307d533 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/api/tags/read_tags/read_tags.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/api/tags/read_tags/read_tags.ts @@ -6,6 +6,11 @@ */ import type { RulesClient } from '@kbn/alerting-plugin/server'; +import type { RuleTagsAggregationResult } from '@kbn/alerting-plugin/common'; +import { + getRuleTagsAggregation, + formatRuleTagsAggregationResult, +} from '@kbn/alerting-plugin/common'; import { enrichFilterWithRuleTypeMapping } from '../../../logic/search/enrich_filter_with_rule_type_mappings'; // This is a contrived max limit on the number of tags. In fact it can exceed this number and will be truncated to the hardcoded number. @@ -17,13 +22,14 @@ export const readTags = async ({ rulesClient: RulesClient; perPage?: number; }): Promise => { - const res = await rulesClient.aggregate({ + const res = await rulesClient.aggregate({ options: { - fields: ['tags'], filter: enrichFilterWithRuleTypeMapping(undefined), - maxTags: EXPECTED_MAX_TAGS, }, + aggs: getRuleTagsAggregation({ + maxTags: EXPECTED_MAX_TAGS, + }), }); - return res.ruleTags ?? []; + return formatRuleTagsAggregationResult(res).ruleTags; }; diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/create_security_rule_type_wrapper.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/create_security_rule_type_wrapper.ts index 38ce087fb6aae..99339a4e8fb53 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/create_security_rule_type_wrapper.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/create_security_rule_type_wrapper.ts @@ -14,6 +14,8 @@ import { createPersistenceRuleTypeWrapper } from '@kbn/rule-registry-plugin/serv import { parseScheduleDates } from '@kbn/securitysolution-io-ts-utils'; import { buildExceptionFilter } from '@kbn/lists-plugin/server/services/exception_lists'; +import { technicalRuleFieldMap } from '@kbn/rule-registry-plugin/common/assets/field_maps/technical_rule_field_map'; +import type { FieldMap } from '@kbn/alerts-as-data-utils'; import { checkPrivilegesFromEsClient, getExceptions, @@ -43,6 +45,23 @@ import { withSecuritySpan } from '../../../utils/with_security_span'; import { getInputIndex, DataViewError } from './utils/get_input_output_index'; import { TIMESTAMP_RUNTIME_FIELD } from './constants'; import { buildTimestampRuntimeMapping } from './utils/build_timestamp_runtime_mapping'; +import { alertsFieldMap, rulesFieldMap } from '../../../../common/field_maps'; + +const aliasesFieldMap: FieldMap = {}; +Object.entries(aadFieldConversion).forEach(([key, value]) => { + aliasesFieldMap[key] = { + type: 'alias', + required: false, + path: value, + }; +}); + +export const securityRuleTypeFieldMap = { + ...technicalRuleFieldMap, + ...alertsFieldMap, + ...rulesFieldMap, + ...aliasesFieldMap, +}; /* eslint-disable complexity */ export const createSecurityRuleTypeWrapper: CreateSecurityRuleTypeWrapper = @@ -509,5 +528,15 @@ export const createSecurityRuleTypeWrapper: CreateSecurityRuleTypeWrapper = return { state: result.state }; }); }, + alerts: { + context: 'security', + mappings: { + dynamic: false, + fieldMap: securityRuleTypeFieldMap, + }, + useEcs: true, + useLegacyAlerts: true, + secondaryAlias: config.signalsIndex, + }, }); }; diff --git a/x-pack/plugins/security_solution/server/plugin.ts b/x-pack/plugins/security_solution/server/plugin.ts index cbc595a22bbf0..58bfba0b9bd37 100644 --- a/x-pack/plugins/security_solution/server/plugin.ts +++ b/x-pack/plugins/security_solution/server/plugin.ts @@ -23,8 +23,6 @@ import type { UsageCounter } from '@kbn/usage-collection-plugin/server'; import { ECS_COMPONENT_TEMPLATE_NAME } from '@kbn/alerting-plugin/server'; import { mappingFromFieldMap } from '@kbn/alerting-plugin/common'; -import type { FieldMap } from '@kbn/alerts-as-data-utils'; -import { technicalRuleFieldMap } from '@kbn/rule-registry-plugin/common/assets/field_maps/technical_rule_field_map'; import type { IRuleDataClient } from '@kbn/rule-registry-plugin/server'; import { Dataset } from '@kbn/rule-registry-plugin/server'; import type { ListPluginSetup } from '@kbn/lists-plugin/server'; @@ -71,7 +69,6 @@ import { TelemetryReceiver } from './lib/telemetry/receiver'; import { licenseService } from './lib/license'; import { PolicyWatcher } from './endpoint/lib/policy/license_watch'; import { migrateArtifactsToFleet } from './endpoint/lib/artifacts/migrate_artifacts_to_fleet'; -import aadFieldConversion from './lib/detection_engine/routes/index/signal_aad_mapping.json'; import previewPolicy from './lib/detection_engine/routes/index/preview_policy.json'; import { createRuleExecutionLogService } from './lib/detection_engine/rule_monitoring'; import { getKibanaPrivilegesFeaturePrivileges, getCasesKibanaFeature } from './features'; @@ -85,7 +82,10 @@ import { legacyRulesNotificationAlertType, legacyIsNotificationAlertExecutor, } from './lib/detection_engine/rule_actions_legacy'; -import { createSecurityRuleTypeWrapper } from './lib/detection_engine/rule_types/create_security_rule_type_wrapper'; +import { + createSecurityRuleTypeWrapper, + securityRuleTypeFieldMap, +} from './lib/detection_engine/rule_types/create_security_rule_type_wrapper'; import { RequestContextFactory } from './request_context_factory'; @@ -99,7 +99,6 @@ import type { SecuritySolutionPluginStart, PluginInitializerContext, } from './plugin_contract'; -import { alertsFieldMap, rulesFieldMap } from '../common/field_maps'; import { EndpointFleetServicesFactory } from './endpoint/services/fleet'; import { featureUsageService } from './endpoint/services/feature_usage'; import { setIsElasticCloudDeployment } from './lib/telemetry/helpers'; @@ -216,15 +215,6 @@ export class Plugin implements ISecuritySolutionPlugin { version: pluginContext.env.packageInfo.version, }; - const aliasesFieldMap: FieldMap = {}; - Object.entries(aadFieldConversion).forEach(([key, value]) => { - aliasesFieldMap[key] = { - type: 'alias', - required: false, - path: value, - }; - }); - const ruleDataServiceOptions = { feature: SERVER_APP_ID, registrationContext: 'security', @@ -233,10 +223,7 @@ export class Plugin implements ISecuritySolutionPlugin { componentTemplates: [ { name: 'mappings', - mappings: mappingFromFieldMap( - { ...technicalRuleFieldMap, ...alertsFieldMap, ...rulesFieldMap, ...aliasesFieldMap }, - false - ), + mappings: mappingFromFieldMap(securityRuleTypeFieldMap, false), }, ], secondaryAlias: config.signalsIndex, diff --git a/x-pack/plugins/security_solution/server/plugin_contract.ts b/x-pack/plugins/security_solution/server/plugin_contract.ts index fffb019e5eaf7..57c05d2490c05 100644 --- a/x-pack/plugins/security_solution/server/plugin_contract.ts +++ b/x-pack/plugins/security_solution/server/plugin_contract.ts @@ -16,7 +16,7 @@ import type { PluginSetupContract as AlertingPluginSetup, PluginStartContract as AlertingPluginStart, } from '@kbn/alerting-plugin/server'; -import type { PluginStartContract as CasesPluginStart } from '@kbn/cases-plugin/server'; +import type { CasesStart } from '@kbn/cases-plugin/server'; import type { EncryptedSavedObjectsPluginSetup } from '@kbn/encrypted-saved-objects-plugin/server'; import type { IEventLogClientService, IEventLogService } from '@kbn/event-log-plugin/server'; import type { PluginSetupContract as FeaturesPluginSetup } from '@kbn/features-plugin/server'; @@ -65,7 +65,7 @@ export interface SecuritySolutionPluginSetupDependencies { export interface SecuritySolutionPluginStartDependencies { alerting: AlertingPluginStart; - cases?: CasesPluginStart; + cases?: CasesStart; cloudExperiments?: CloudExperimentsPluginStart; data: DataPluginStart; dataViews: DataViewsPluginStart; diff --git a/x-pack/plugins/security_solution/server/routes/index.ts b/x-pack/plugins/security_solution/server/routes/index.ts index 7758e74be4801..af26823416a2b 100644 --- a/x-pack/plugins/security_solution/server/routes/index.ts +++ b/x-pack/plugins/security_solution/server/routes/index.ts @@ -90,7 +90,7 @@ export const initRoutes = ( ) => { registerFleetIntegrationsRoutes(router, logger); registerLegacyRuleActionsRoutes(router, logger); - registerPrebuiltRulesRoutes(router, security); + registerPrebuiltRulesRoutes(router, config, security); registerRuleExceptionsRoutes(router); registerManageExceptionsRoutes(router); registerRuleManagementRoutes(router, config, ml, logger); diff --git a/x-pack/plugins/security_solution/server/saved_objects.ts b/x-pack/plugins/security_solution/server/saved_objects.ts index 7ce17ff0c0b51..394cab7f52455 100644 --- a/x-pack/plugins/security_solution/server/saved_objects.ts +++ b/x-pack/plugins/security_solution/server/saved_objects.ts @@ -10,7 +10,7 @@ import type { CoreSetup } from '@kbn/core/server'; import { noteType, pinnedEventType, timelineType } from './lib/timeline/saved_object_mappings'; // eslint-disable-next-line no-restricted-imports import { legacyType as legacyRuleActionsType } from './lib/detection_engine/rule_actions_legacy'; -import { ruleAssetType } from './lib/detection_engine/prebuilt_rules/logic/rule_asset/rule_asset_saved_object_mappings'; +import { prebuiltRuleAssetType } from './lib/detection_engine/prebuilt_rules'; import { type as signalsMigrationType } from './lib/detection_engine/migrations/saved_objects'; import { exceptionsArtifactType, @@ -21,7 +21,7 @@ const types = [ noteType, pinnedEventType, legacyRuleActionsType, - ruleAssetType, + prebuiltRuleAssetType, timelineType, exceptionsArtifactType, manifestType, diff --git a/x-pack/plugins/spaces/server/capabilities/capabilities_switcher.test.ts b/x-pack/plugins/spaces/server/capabilities/capabilities_switcher.test.ts index 92c1c07622ac9..5ad5e49052a71 100644 --- a/x-pack/plugins/spaces/server/capabilities/capabilities_switcher.test.ts +++ b/x-pack/plugins/spaces/server/capabilities/capabilities_switcher.test.ts @@ -173,7 +173,7 @@ describe('capabilitiesSwitcher', () => { const result = await switcher(request, capabilities, false); - expect(result).toEqual(buildCapabilities()); + expect(result).toEqual({}); expect(spacesService.getActiveSpace).not.toHaveBeenCalled(); }); @@ -191,7 +191,7 @@ describe('capabilitiesSwitcher', () => { const result = await switcher(request, capabilities, true); - expect(result).toEqual(buildCapabilities()); + expect(result).toEqual({}); expect(spacesService.getActiveSpace).not.toHaveBeenCalled(); }); diff --git a/x-pack/plugins/spaces/server/capabilities/capabilities_switcher.ts b/x-pack/plugins/spaces/server/capabilities/capabilities_switcher.ts index 4f2b61abc8025..e3183185022f6 100644 --- a/x-pack/plugins/spaces/server/capabilities/capabilities_switcher.ts +++ b/x-pack/plugins/spaces/server/capabilities/capabilities_switcher.ts @@ -24,7 +24,7 @@ export function setupCapabilitiesSwitcher( const shouldNotToggleCapabilities = isAuthRequiredOrOptional || useDefaultCapabilities; if (shouldNotToggleCapabilities) { - return capabilities; + return {}; } try { diff --git a/x-pack/plugins/synthetics/common/constants/synthetics_alerts.ts b/x-pack/plugins/synthetics/common/constants/synthetics_alerts.ts index 454dd8fd79297..7f6d306f8be88 100644 --- a/x-pack/plugins/synthetics/common/constants/synthetics_alerts.ts +++ b/x-pack/plugins/synthetics/common/constants/synthetics_alerts.ts @@ -31,3 +31,5 @@ export const SYNTHETICS_ALERT_RULE_TYPES = { }; export const SYNTHETICS_RULE_TYPES = [SYNTHETICS_STATUS_RULE]; + +export const SYNTHETICS_RULE_TYPES_ALERT_CONTEXT = 'observability.uptime'; diff --git a/x-pack/plugins/synthetics/public/apps/synthetics/components/common/links/step_details_link.tsx b/x-pack/plugins/synthetics/public/apps/synthetics/components/common/links/step_details_link.tsx index d9c2da03756b9..3d1fa76b3b13e 100644 --- a/x-pack/plugins/synthetics/public/apps/synthetics/components/common/links/step_details_link.tsx +++ b/x-pack/plugins/synthetics/public/apps/synthetics/components/common/links/step_details_link.tsx @@ -17,12 +17,14 @@ export const StepDetailsLinkIcon = ({ configId, asButton, label, + target = '_self', }: { checkGroup: string; label?: string; configId: string; stepIndex?: number; asButton?: boolean; + target?: '_self' | '_blank'; }) => { const { basePath } = useSyntheticsSettingsContext(); const selectedLocation = useSelectedLocation(); @@ -45,7 +47,7 @@ export const StepDetailsLinkIcon = ({ title={VIEW_DETAILS} size="s" href={`${basePath}/app/synthetics/monitor/${configId}/test-run/${checkGroup}/step/${stepIndex}?locationId=${selectedLocation?.id}`} - target="_self" + target={target} iconType="apmTrace" /> ); diff --git a/x-pack/plugins/synthetics/public/apps/synthetics/components/common/monitor_test_result/browser_steps_list.tsx b/x-pack/plugins/synthetics/public/apps/synthetics/components/common/monitor_test_result/browser_steps_list.tsx index d8e8f8a84b57b..fefb07614f308 100644 --- a/x-pack/plugins/synthetics/public/apps/synthetics/components/common/monitor_test_result/browser_steps_list.tsx +++ b/x-pack/plugins/synthetics/public/apps/synthetics/components/common/monitor_test_result/browser_steps_list.tsx @@ -214,6 +214,7 @@ export const BrowserStepsList = ({ checkGroup={item.monitor.check_group} stepIndex={item.synthetics?.step?.index} configId={item.config_id!} + target="_blank" /> ), }, diff --git a/x-pack/plugins/synthetics/server/alert_rules/status_rule/monitor_status_rule.ts b/x-pack/plugins/synthetics/server/alert_rules/status_rule/monitor_status_rule.ts index e1719c1e84b93..6622c68d31004 100644 --- a/x-pack/plugins/synthetics/server/alert_rules/status_rule/monitor_status_rule.ts +++ b/x-pack/plugins/synthetics/server/alert_rules/status_rule/monitor_status_rule.ts @@ -34,6 +34,7 @@ import { import { getInstanceId } from '../../legacy_uptime/lib/alerts/status_check'; import { UMServerLibs } from '../../legacy_uptime/uptime_server'; import { SyntheticsMonitorClient } from '../../synthetics_service/synthetics_monitor/synthetics_monitor_client'; +import { UptimeRuleTypeAlertDefinition } from '../../legacy_uptime/lib/alerts/common'; export type ActionGroupIds = ActionGroupIdsOf; @@ -136,5 +137,6 @@ export const registerSyntheticsStatusCheckRule = ( state: updateState(ruleState, !isEmpty(downConfigs), { downConfigs }), }; }, + alerts: UptimeRuleTypeAlertDefinition, }); }; diff --git a/x-pack/plugins/synthetics/server/legacy_uptime/lib/alerts/common.ts b/x-pack/plugins/synthetics/server/legacy_uptime/lib/alerts/common.ts index 2da05dcd42e23..4ff12c7f1623c 100644 --- a/x-pack/plugins/synthetics/server/legacy_uptime/lib/alerts/common.ts +++ b/x-pack/plugins/synthetics/server/legacy_uptime/lib/alerts/common.ts @@ -7,9 +7,12 @@ import { isRight } from 'fp-ts/lib/Either'; import Mustache from 'mustache'; +import { legacyExperimentalFieldMap } from '@kbn/alerts-as-data-utils'; import { IBasePath } from '@kbn/core/server'; -import { RuleExecutorServices } from '@kbn/alerting-plugin/server'; +import { type IRuleTypeAlerts, RuleExecutorServices } from '@kbn/alerting-plugin/server'; import { addSpaceIdToPath } from '@kbn/spaces-plugin/common'; +import { uptimeRuleFieldMap } from '../../../../common/rules/uptime_rule_field_map'; +import { SYNTHETICS_RULE_TYPES_ALERT_CONTEXT } from '../../../../common/constants/synthetics_alerts'; import { UptimeCommonState, UptimeCommonStateType } from '../../../../common/runtime_types'; import { ALERT_DETAILS_URL } from './action_variables'; @@ -103,3 +106,11 @@ export const setRecoveredAlertsContext = ({ }); } }; + +export const uptimeRuleTypeFieldMap = { ...uptimeRuleFieldMap, ...legacyExperimentalFieldMap }; + +export const UptimeRuleTypeAlertDefinition: IRuleTypeAlerts = { + context: SYNTHETICS_RULE_TYPES_ALERT_CONTEXT, + mappings: { fieldMap: uptimeRuleTypeFieldMap }, + useLegacyAlerts: true, +}; diff --git a/x-pack/plugins/synthetics/server/legacy_uptime/lib/alerts/duration_anomaly.ts b/x-pack/plugins/synthetics/server/legacy_uptime/lib/alerts/duration_anomaly.ts index b959d16d5ade0..f70e3a96d1fb8 100644 --- a/x-pack/plugins/synthetics/server/legacy_uptime/lib/alerts/duration_anomaly.ts +++ b/x-pack/plugins/synthetics/server/legacy_uptime/lib/alerts/duration_anomaly.ts @@ -21,6 +21,7 @@ import { generateAlertMessage, getViewInAppUrl, setRecoveredAlertsContext, + UptimeRuleTypeAlertDefinition, } from './common'; import { CLIENT_ALERT_TYPES, DURATION_ANOMALY } from '../../../../common/constants/uptime_alerts'; import { commonStateTranslations, durationAnomalyTranslations } from './translations'; @@ -189,4 +190,5 @@ export const durationAnomalyAlertFactory: UptimeAlertTypeFactory return { state: updateState(state, foundAnomalies) }; }, + alerts: UptimeRuleTypeAlertDefinition, }); diff --git a/x-pack/plugins/synthetics/server/legacy_uptime/lib/alerts/status_check.ts b/x-pack/plugins/synthetics/server/legacy_uptime/lib/alerts/status_check.ts index 488a3ef2d284c..6da1aabeda9f4 100644 --- a/x-pack/plugins/synthetics/server/legacy_uptime/lib/alerts/status_check.ts +++ b/x-pack/plugins/synthetics/server/legacy_uptime/lib/alerts/status_check.ts @@ -28,6 +28,7 @@ import { getViewInAppUrl, setRecoveredAlertsContext, getAlertDetailsUrl, + UptimeRuleTypeAlertDefinition, } from './common'; import { commonMonitorStateI18, @@ -533,4 +534,5 @@ export const statusCheckAlertFactory: UptimeAlertTypeFactory = ( return { state: updateState(state, downMonitorsByLocation.length > 0) }; }, + alerts: UptimeRuleTypeAlertDefinition, }); diff --git a/x-pack/plugins/synthetics/server/legacy_uptime/lib/alerts/tls.ts b/x-pack/plugins/synthetics/server/legacy_uptime/lib/alerts/tls.ts index 3d39d1d9329e6..6b23d90db0a7e 100644 --- a/x-pack/plugins/synthetics/server/legacy_uptime/lib/alerts/tls.ts +++ b/x-pack/plugins/synthetics/server/legacy_uptime/lib/alerts/tls.ts @@ -16,6 +16,7 @@ import { generateAlertMessage, setRecoveredAlertsContext, getAlertDetailsUrl, + UptimeRuleTypeAlertDefinition, } from './common'; import { CLIENT_ALERT_TYPES, TLS } from '../../../../common/constants/uptime_alerts'; import { DYNAMIC_SETTINGS_DEFAULTS } from '../../../../common/constants'; @@ -227,4 +228,5 @@ export const tlsAlertFactory: UptimeAlertTypeFactory = ( return { state: updateState(state, foundCerts) }; }, + alerts: UptimeRuleTypeAlertDefinition, }); diff --git a/x-pack/plugins/synthetics/server/plugin.ts b/x-pack/plugins/synthetics/server/plugin.ts index c6120c70c3818..298f733f3d3e4 100644 --- a/x-pack/plugins/synthetics/server/plugin.ts +++ b/x-pack/plugins/synthetics/server/plugin.ts @@ -14,13 +14,11 @@ import { SavedObjectsClientContract, } from '@kbn/core/server'; import { mappingFromFieldMap } from '@kbn/alerting-plugin/common'; -import { experimentalRuleFieldMap } from '@kbn/rule-registry-plugin/common/assets/field_maps/experimental_rule_field_map'; import { Dataset } from '@kbn/rule-registry-plugin/server'; import { SyntheticsMonitorClient } from './synthetics_service/synthetics_monitor/synthetics_monitor_client'; import { initSyntheticsServer } from './server'; import { initUptimeServer } from './legacy_uptime/uptime_server'; import { uptimeFeature } from './feature'; -import { uptimeRuleFieldMap } from '../common/rules/uptime_rule_field_map'; import { KibanaTelemetryAdapter, UptimeCorePluginsSetup, @@ -35,6 +33,8 @@ import { import { UptimeConfig } from '../common/config'; import { SyntheticsService } from './synthetics_service/synthetics_service'; import { syntheticsServiceApiKey } from './legacy_uptime/lib/saved_objects/service_api_key'; +import { SYNTHETICS_RULE_TYPES_ALERT_CONTEXT } from '../common/constants/synthetics_alerts'; +import { uptimeRuleTypeFieldMap } from './legacy_uptime/lib/alerts/common'; export type UptimeRuleRegistry = ReturnType['ruleRegistry']; @@ -63,16 +63,13 @@ export class Plugin implements PluginType { const ruleDataClient = ruleDataService.initializeIndex({ feature: 'uptime', - registrationContext: 'observability.uptime', + registrationContext: SYNTHETICS_RULE_TYPES_ALERT_CONTEXT, dataset: Dataset.alerts, componentTemplateRefs: [], componentTemplates: [ { name: 'mappings', - mappings: mappingFromFieldMap( - { ...uptimeRuleFieldMap, ...experimentalRuleFieldMap }, - 'strict' - ), + mappings: mappingFromFieldMap(uptimeRuleTypeFieldMap, 'strict'), }, ], }); diff --git a/x-pack/plugins/synthetics/tsconfig.json b/x-pack/plugins/synthetics/tsconfig.json index 730a1f381d142..6cffd71042dee 100644 --- a/x-pack/plugins/synthetics/tsconfig.json +++ b/x-pack/plugins/synthetics/tsconfig.json @@ -74,6 +74,7 @@ "@kbn/shared-ux-prompt-not-found", "@kbn/safer-lodash-set", "@kbn/shared-ux-router", + "@kbn/alerts-as-data-utils", ], "exclude": [ "target/**/*", diff --git a/x-pack/plugins/task_manager/server/task.ts b/x-pack/plugins/task_manager/server/task.ts index ebb957e54699a..1b1def5cc16df 100644 --- a/x-pack/plugins/task_manager/server/task.ts +++ b/x-pack/plugins/task_manager/server/task.ts @@ -152,15 +152,6 @@ export const taskDefinitionSchema = schema.object( * task manager. */ export type TaskDefinition = TypeOf & { - /** - * Function that customizes how the task should behave when the task fails. This - * function can return `true`, `false` or a Date. True will tell task manager - * to retry using default delay logic. False will tell task manager to stop retrying - * this task. Date will suggest when to the task manager the task should retry. - * This function isn't used for recurring tasks, those retry as per their configured recurring schedule. - */ - getRetry?: (attempts: number, error: object) => boolean | Date; - /** * Creates an object that has a run function which performs the task's work, * and an optional cancel function which cancels the task. diff --git a/x-pack/plugins/task_manager/server/task_running/errors.ts b/x-pack/plugins/task_manager/server/task_running/errors.ts index 43466fae0e2eb..d9c79a78ed126 100644 --- a/x-pack/plugins/task_manager/server/task_running/errors.ts +++ b/x-pack/plugins/task_manager/server/task_running/errors.ts @@ -8,11 +8,14 @@ import { EphemeralTask } from '../task'; // Unrecoverable const CODE_UNRECOVERABLE = 'TaskManager/unrecoverable'; +const CODE_RETRYABLE = 'TaskManager/retryable'; const code = Symbol('TaskManagerErrorCode'); +const retry = Symbol('TaskManagerErrorRetry'); export interface DecoratedError extends Error { [code]?: string; + [retry]?: Date | boolean; } export class EphemeralTaskRejectedDueToCapacityError extends Error { @@ -41,6 +44,19 @@ export function throwUnrecoverableError(error: Error) { throw error; } +export function isRetryableError(error: Error | DecoratedError) { + if (isTaskManagerError(error) && error[code] === CODE_RETRYABLE) { + return error[retry]; + } + return null; +} + +export function throwRetryableError(error: Error, shouldRetry: Date | boolean) { + (error as DecoratedError)[code] = CODE_RETRYABLE; + (error as DecoratedError)[retry] = shouldRetry; + throw error; +} + export function isEphemeralTaskRejectedDueToCapacityError( error: Error | EphemeralTaskRejectedDueToCapacityError ) { diff --git a/x-pack/plugins/task_manager/server/task_running/task_runner.test.ts b/x-pack/plugins/task_manager/server/task_running/task_runner.test.ts index b66f4bc418640..2598d8084a783 100644 --- a/x-pack/plugins/task_manager/server/task_running/task_runner.test.ts +++ b/x-pack/plugins/task_manager/server/task_running/task_runner.test.ts @@ -22,7 +22,7 @@ import { SavedObjectsErrorHelpers } from '@kbn/core/server'; import moment from 'moment'; import { TaskDefinitionRegistry, TaskTypeDictionary } from '../task_type_dictionary'; import { mockLogger } from '../test_utils'; -import { throwUnrecoverableError } from './errors'; +import { throwRetryableError, throwUnrecoverableError } from './errors'; import { taskStoreMock } from '../task_store.mock'; import apm from 'elastic-apm-node'; import { executionContextServiceMock } from '@kbn/core/server/mocks'; @@ -306,12 +306,10 @@ describe('TaskManagerRunner', () => { expect(instance.enabled).not.toBeDefined(); }); - test('uses getRetry (returning date) to set retryAt when defined', async () => { + test('sets retryAt when there is an error', async () => { const id = _.random(1, 20).toString(); const initialAttempts = _.random(1, 3); - const nextRetry = new Date(Date.now() + _.random(15, 100) * 1000); const timeoutMinutes = 1; - const getRetryStub = sinon.stub().returns(nextRetry); const { runner, store } = await pendingStageSetup({ instance: { id, @@ -323,7 +321,6 @@ describe('TaskManagerRunner', () => { bar: { title: 'Bar!', timeout: `${timeoutMinutes}m`, - getRetry: getRetryStub, createTaskRunner: () => ({ run: async () => undefined, }), @@ -334,11 +331,11 @@ describe('TaskManagerRunner', () => { await runner.markTaskAsRunning(); expect(store.update).toHaveBeenCalledTimes(1); - sinon.assert.calledWith(getRetryStub, initialAttempts + 1); const instance = store.update.mock.calls[0][0]; + const expectedRetryAt = new Date(Date.now() + calculateDelay(initialAttempts + 1)); expect(instance.retryAt!.getTime()).toEqual( - new Date(nextRetry.getTime() + timeoutMinutes * 60 * 1000).getTime() + new Date(expectedRetryAt.getTime() + timeoutMinutes * 60 * 1000).getTime() ); expect(instance.enabled).not.toBeDefined(); }); @@ -346,9 +343,7 @@ describe('TaskManagerRunner', () => { test('it returns false when markTaskAsRunning fails due to VERSION_CONFLICT_STATUS', async () => { const id = _.random(1, 20).toString(); const initialAttempts = _.random(1, 3); - const nextRetry = new Date(Date.now() + _.random(15, 100) * 1000); const timeoutMinutes = 1; - const getRetryStub = sinon.stub().returns(nextRetry); const { runner, store } = await pendingStageSetup({ instance: { id, @@ -359,7 +354,6 @@ describe('TaskManagerRunner', () => { bar: { title: 'Bar!', timeout: `${timeoutMinutes}m`, - getRetry: getRetryStub, createTaskRunner: () => ({ run: async () => undefined, }), @@ -377,9 +371,7 @@ describe('TaskManagerRunner', () => { test('it throw when markTaskAsRunning fails for unexpected reasons', async () => { const id = _.random(1, 20).toString(); const initialAttempts = _.random(1, 3); - const nextRetry = new Date(Date.now() + _.random(15, 100) * 1000); const timeoutMinutes = 1; - const getRetryStub = sinon.stub().returns(nextRetry); const { runner, store } = await pendingStageSetup({ instance: { id, @@ -390,7 +382,6 @@ describe('TaskManagerRunner', () => { bar: { title: 'Bar!', timeout: `${timeoutMinutes}m`, - getRetry: getRetryStub, createTaskRunner: () => ({ run: async () => undefined, }), @@ -410,9 +401,7 @@ describe('TaskManagerRunner', () => { test(`it tries to increment a task's attempts when markTaskAsRunning fails for unexpected reasons`, async () => { const id = _.random(1, 20).toString(); const initialAttempts = _.random(1, 3); - const nextRetry = new Date(Date.now() + _.random(15, 100) * 1000); const timeoutMinutes = 1; - const getRetryStub = sinon.stub().returns(nextRetry); const { runner, store } = await pendingStageSetup({ instance: { id, @@ -423,7 +412,6 @@ describe('TaskManagerRunner', () => { bar: { title: 'Bar!', timeout: `${timeoutMinutes}m`, - getRetry: getRetryStub, createTaskRunner: () => ({ run: async () => undefined, }), @@ -460,9 +448,7 @@ describe('TaskManagerRunner', () => { test(`it doesnt try to increment a task's attempts when markTaskAsRunning fails for version conflict`, async () => { const id = _.random(1, 20).toString(); const initialAttempts = _.random(1, 3); - const nextRetry = new Date(Date.now() + _.random(15, 100) * 1000); const timeoutMinutes = 1; - const getRetryStub = sinon.stub().returns(nextRetry); const { runner, store } = await pendingStageSetup({ instance: { id, @@ -473,7 +459,6 @@ describe('TaskManagerRunner', () => { bar: { title: 'Bar!', timeout: `${timeoutMinutes}m`, - getRetry: getRetryStub, createTaskRunner: () => ({ run: async () => undefined, }), @@ -500,9 +485,7 @@ describe('TaskManagerRunner', () => { test(`it doesnt try to increment a task's attempts when markTaskAsRunning fails due to Saved Object not being found`, async () => { const id = _.random(1, 20).toString(); const initialAttempts = _.random(1, 3); - const nextRetry = new Date(Date.now() + _.random(15, 100) * 1000); const timeoutMinutes = 1; - const getRetryStub = sinon.stub().returns(nextRetry); const { runner, store } = await pendingStageSetup({ instance: { id, @@ -513,7 +496,6 @@ describe('TaskManagerRunner', () => { bar: { title: 'Bar!', timeout: `${timeoutMinutes}m`, - getRetry: getRetryStub, createTaskRunner: () => ({ run: async () => undefined, }), @@ -539,79 +521,6 @@ describe('TaskManagerRunner', () => { expect(store.update).toHaveBeenCalledTimes(1); }); - test('uses getRetry (returning true) to set retryAt when defined', async () => { - const id = _.random(1, 20).toString(); - const initialAttempts = _.random(1, 3); - const timeoutMinutes = 1; - const getRetryStub = sinon.stub().returns(true); - const { runner, store } = await pendingStageSetup({ - instance: { - id, - attempts: initialAttempts, - schedule: undefined, - enabled: true, - }, - definitions: { - bar: { - title: 'Bar!', - timeout: `${timeoutMinutes}m`, - getRetry: getRetryStub, - createTaskRunner: () => ({ - run: async () => undefined, - }), - }, - }, - }); - - await runner.markTaskAsRunning(); - - expect(store.update).toHaveBeenCalledTimes(1); - sinon.assert.calledWith(getRetryStub, initialAttempts + 1); - const instance = store.update.mock.calls[0][0]; - - const attemptDelay = calculateDelay(initialAttempts + 1); - const timeoutDelay = timeoutMinutes * 60 * 1000; - expect(instance.retryAt!.getTime()).toEqual( - new Date(Date.now() + attemptDelay + timeoutDelay).getTime() - ); - expect(instance.enabled).not.toBeDefined(); - }); - - test('uses getRetry (returning false) to set retryAt when defined', async () => { - const id = _.random(1, 20).toString(); - const initialAttempts = _.random(1, 3); - const timeoutMinutes = 1; - const getRetryStub = sinon.stub().returns(false); - const { runner, store } = await pendingStageSetup({ - instance: { - id, - attempts: initialAttempts, - schedule: undefined, - enabled: true, - }, - definitions: { - bar: { - title: 'Bar!', - timeout: `${timeoutMinutes}m`, - getRetry: getRetryStub, - createTaskRunner: () => ({ - run: async () => undefined, - }), - }, - }, - }); - - await runner.markTaskAsRunning(); - - expect(store.update).toHaveBeenCalledTimes(1); - sinon.assert.calledWith(getRetryStub, initialAttempts + 1); - const instance = store.update.mock.calls[0][0]; - - expect(instance.retryAt!).toBeNull(); - expect(instance.status).toBe('running'); - expect(instance.enabled).not.toBeDefined(); - }); - test('bypasses getRetry (returning false) of a recurring task to set retryAt when defined', async () => { const id = _.random(1, 20).toString(); const initialAttempts = _.random(1, 3); @@ -629,7 +538,6 @@ describe('TaskManagerRunner', () => { bar: { title: 'Bar!', timeout: `${timeoutMinutes}m`, - getRetry: getRetryStub, createTaskRunner: () => ({ run: async () => undefined, }), @@ -1105,11 +1013,10 @@ describe('TaskManagerRunner', () => { expect(logger.debug).toHaveBeenCalledWith(`The task bar "foo" is not cancellable.`); }); - test('uses getRetry function (returning date) on error when defined', async () => { + test('throws retry error (returning date) on error when defined', async () => { const initialAttempts = _.random(1, 3); const nextRetry = new Date(Date.now() + _.random(15, 100) * 1000); const id = Date.now().toString(); - const getRetryStub = sinon.stub().returns(nextRetry); const error = new Error('Dangit!'); const { runner, store } = await readyToRunStageSetup({ instance: { @@ -1120,10 +1027,9 @@ describe('TaskManagerRunner', () => { definitions: { bar: { title: 'Bar!', - getRetry: getRetryStub, createTaskRunner: () => ({ async run() { - throw error; + throw throwRetryableError(error, nextRetry); }, }), }, @@ -1133,17 +1039,15 @@ describe('TaskManagerRunner', () => { await runner.run(); expect(store.update).toHaveBeenCalledTimes(1); - sinon.assert.calledWith(getRetryStub, initialAttempts, error); const instance = store.update.mock.calls[0][0]; expect(instance.runAt.getTime()).toEqual(nextRetry.getTime()); expect(instance.enabled).not.toBeDefined(); }); - test('uses getRetry function (returning true) on error when defined', async () => { + test('throws retry error (returning true) on error when defined', async () => { const initialAttempts = _.random(1, 3); const id = Date.now().toString(); - const getRetryStub = sinon.stub().returns(true); const error = new Error('Dangit!'); const { runner, store } = await readyToRunStageSetup({ instance: { @@ -1154,10 +1058,9 @@ describe('TaskManagerRunner', () => { definitions: { bar: { title: 'Bar!', - getRetry: getRetryStub, createTaskRunner: () => ({ - async run() { - throw error; + run: async () => { + throwRetryableError(error, true); }, }), }, @@ -1167,7 +1070,6 @@ describe('TaskManagerRunner', () => { await runner.run(); expect(store.update).toHaveBeenCalledTimes(1); - sinon.assert.calledWith(getRetryStub, initialAttempts, error); const instance = store.update.mock.calls[0][0]; const expectedRunAt = new Date(Date.now() + calculateDelay(initialAttempts)); @@ -1175,10 +1077,9 @@ describe('TaskManagerRunner', () => { expect(instance.enabled).not.toBeDefined(); }); - test('uses getRetry function (returning false) on error when defined', async () => { + test('throws retry error (returning false) on error when defined', async () => { const initialAttempts = _.random(1, 3); const id = Date.now().toString(); - const getRetryStub = sinon.stub().returns(false); const error = new Error('Dangit!'); const { runner, store } = await readyToRunStageSetup({ instance: { @@ -1189,10 +1090,9 @@ describe('TaskManagerRunner', () => { definitions: { bar: { title: 'Bar!', - getRetry: getRetryStub, createTaskRunner: () => ({ async run() { - throw error; + throwRetryableError(error, false); }, }), }, @@ -1202,7 +1102,6 @@ describe('TaskManagerRunner', () => { await runner.run(); expect(store.update).toHaveBeenCalledTimes(1); - sinon.assert.calledWith(getRetryStub, initialAttempts, error); const instance = store.update.mock.calls[0][0]; expect(instance.status).toBe('failed'); @@ -1225,7 +1124,6 @@ describe('TaskManagerRunner', () => { definitions: { bar: { title: 'Bar!', - getRetry: getRetryStub, createTaskRunner: () => ({ async run() { throw error; @@ -1490,10 +1388,13 @@ describe('TaskManagerRunner', () => { definitions: { bar: { title: 'Bar!', - getRetry: () => false, createTaskRunner: () => ({ async run() { - return { error, state: {} }; + try { + throwUnrecoverableError(error); + } catch (e) { + return { error: e, state: {} }; + } }, }), }, @@ -1537,7 +1438,6 @@ describe('TaskManagerRunner', () => { bar: { title: 'Bar!', timeout: '1m', - getRetry: () => false, createTaskRunner: () => ({ async run() { return { error, state: {}, runAt: moment().add(1, 'm').toDate() }; diff --git a/x-pack/plugins/task_manager/server/task_running/task_runner.ts b/x-pack/plugins/task_manager/server/task_running/task_runner.ts index 62bc9d4f19734..36a185b5658f1 100644 --- a/x-pack/plugins/task_manager/server/task_running/task_runner.ts +++ b/x-pack/plugins/task_manager/server/task_running/task_runner.ts @@ -51,7 +51,7 @@ import { TaskStatus, } from '../task'; import { TaskTypeDictionary } from '../task_type_dictionary'; -import { isUnrecoverableError } from './errors'; +import { isRetryableError, isUnrecoverableError } from './errors'; import type { EventLoopDelayConfig } from '../config'; export const EMPTY_RUN_RESULT: SuccessfulRunResult = { state: {} }; @@ -645,8 +645,7 @@ export class TaskManagerRunner implements TaskRunner { attempts: number; addDuration?: string; }): Date | undefined { - // Use custom retry logic, if any, otherwise we'll use the default logic - const retry: boolean | Date = this.definition.getRetry?.(attempts, error) ?? true; + const retry: boolean | Date = isRetryableError(error) ?? true; let result; if (retry instanceof Date) { diff --git a/x-pack/plugins/task_manager/server/task_type_dictionary.ts b/x-pack/plugins/task_manager/server/task_type_dictionary.ts index 3c0e4a0fe5542..bed5c6c7e6069 100644 --- a/x-pack/plugins/task_manager/server/task_type_dictionary.ts +++ b/x-pack/plugins/task_manager/server/task_type_dictionary.ts @@ -44,14 +44,6 @@ export interface TaskRegisterDefinition { * An optional more detailed description of what this task does. */ description?: string; - /** - * Function that customizes how the task should behave when the task fails. This - * function can return `true`, `false` or a Date. True will tell task manager - * to retry using default delay logic. False will tell task manager to stop retrying - * this task. Date will suggest when to the task manager the task should retry. - * This function isn't used for recurring tasks, those retry as per their configured recurring schedule. - */ - getRetry?: (attempts: number, error: object) => boolean | Date; /** * Creates an object that has a run function which performs the task's work, diff --git a/x-pack/plugins/threat_intelligence/common/constants.ts b/x-pack/plugins/threat_intelligence/common/constants.ts index 63d98c4a60494..b97a07b1cfee2 100644 --- a/x-pack/plugins/threat_intelligence/common/constants.ts +++ b/x-pack/plugins/threat_intelligence/common/constants.ts @@ -16,3 +16,5 @@ export enum FactoryQueryType { IndicatorGrid = 'indicatorGrid', Barchart = 'barchart', } + +export const CASE_ATTACHMENT_TYPE_ID = 'indicator'; diff --git a/x-pack/plugins/threat_intelligence/public/modules/cases/utils/attachments.tsx b/x-pack/plugins/threat_intelligence/public/modules/cases/utils/attachments.tsx index f5829379d03c1..b324cd0f71ad8 100644 --- a/x-pack/plugins/threat_intelligence/public/modules/cases/utils/attachments.tsx +++ b/x-pack/plugins/threat_intelligence/public/modules/cases/utils/attachments.tsx @@ -12,12 +12,11 @@ import { ExternalReferenceAttachmentType } from '@kbn/cases-plugin/public/client import React from 'react'; import { EuiAvatar } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n-react'; +import { CASE_ATTACHMENT_TYPE_ID } from '../../../../common/constants'; import { EMPTY_VALUE } from '../../../common/constants'; import { Indicator, RawIndicatorFieldId } from '../../../../common/types/indicator'; import { getIndicatorFieldAndValue } from '../../indicators'; -const ExternalAttachmentTypeId = 'indicator'; - /** * Indicator name, type, feed name and first seen values, * rendered in the comment section of a case's attachment or in the flyout @@ -43,7 +42,7 @@ const AttachmentChildrenLazy = React.lazy( * - the component that renders the comment in teh case attachment */ export const generateAttachmentType = (): ExternalReferenceAttachmentType => ({ - id: ExternalAttachmentTypeId, + id: CASE_ATTACHMENT_TYPE_ID, displayName: 'indicator', getAttachmentViewObject: () => ({ event: ( @@ -82,7 +81,7 @@ export const generateAttachmentsWithoutOwner = ( externalReferenceStorage: { type: ExternalReferenceStorageType.elasticSearchDoc, }, - externalReferenceAttachmentTypeId: ExternalAttachmentTypeId, + externalReferenceAttachmentTypeId: CASE_ATTACHMENT_TYPE_ID, externalReferenceMetadata: attachmentMetadata as unknown as { [p: string]: JsonValue }, }, ]; diff --git a/x-pack/plugins/threat_intelligence/server/plugin.ts b/x-pack/plugins/threat_intelligence/server/plugin.ts index 731659208dacc..d6fb0cb20aeee 100644 --- a/x-pack/plugins/threat_intelligence/server/plugin.ts +++ b/x-pack/plugins/threat_intelligence/server/plugin.ts @@ -6,7 +6,10 @@ */ import type { PluginInitializerContext, Logger } from '@kbn/core/server'; -import { THREAT_INTELLIGENCE_SEARCH_STRATEGY_NAME } from '../common/constants'; +import { + CASE_ATTACHMENT_TYPE_ID, + THREAT_INTELLIGENCE_SEARCH_STRATEGY_NAME, +} from '../common/constants'; import { IThreatIntelligencePlugin, ThreatIntelligencePluginCoreSetupDependencies, @@ -39,6 +42,8 @@ export class ThreatIntelligencePlugin implements IThreatIntelligencePlugin { this.logger.debug(`search strategy "${THREAT_INTELLIGENCE_SEARCH_STRATEGY_NAME}" registered`); }); + plugins.cases.attachmentFramework.registerExternalReference({ id: CASE_ATTACHMENT_TYPE_ID }); + return {}; } diff --git a/x-pack/plugins/threat_intelligence/server/types.ts b/x-pack/plugins/threat_intelligence/server/types.ts index a9ad87a3f27c5..b53ee6a3f6a88 100644 --- a/x-pack/plugins/threat_intelligence/server/types.ts +++ b/x-pack/plugins/threat_intelligence/server/types.ts @@ -6,11 +6,12 @@ */ import { CoreSetup, CoreStart, Plugin } from '@kbn/core/server'; - import { DataPluginSetup, DataPluginStart } from '@kbn/data-plugin/server/plugin'; +import type { CasesSetup } from '@kbn/cases-plugin/server'; export interface ThreatIntelligencePluginSetupDependencies { data: DataPluginSetup; + cases: CasesSetup; } export interface ThreatIntelligencePluginStartDependencies { diff --git a/x-pack/plugins/threat_intelligence/server/utils/indicator_name.test.ts b/x-pack/plugins/threat_intelligence/server/utils/indicator_name.test.ts index 1a1879562ba8d..598929f8042da 100644 --- a/x-pack/plugins/threat_intelligence/server/utils/indicator_name.test.ts +++ b/x-pack/plugins/threat_intelligence/server/utils/indicator_name.test.ts @@ -11,7 +11,7 @@ describe('display name generation', () => { describe('threatIndicatorNamesScript()', () => { it('should generate a valid painless script', () => { expect(threatIndicatorNamesScript()).toMatchInlineSnapshot(` - "if (doc.containsKey('threat.indicator.type') && !doc['threat.indicator.type'].empty && doc['threat.indicator.type'].size()!=0 && doc['threat.indicator.type'].value!=null && doc['threat.indicator.type'].value.toLowerCase()=='ipv4-addr') { if (doc.containsKey('threat.indicator.ip') && !doc['threat.indicator.ip'].empty && doc['threat.indicator.ip'].size()!=0 && doc['threat.indicator.ip'].value!=null) { return emit(doc['threat.indicator.ip'].value) } } + "try { if (doc.containsKey('threat.indicator.type') && !doc['threat.indicator.type'].empty && doc['threat.indicator.type'].size()!=0 && doc['threat.indicator.type'].value!=null && doc['threat.indicator.type'].value.toLowerCase()=='ipv4-addr') { if (doc.containsKey('threat.indicator.ip') && !doc['threat.indicator.ip'].empty && doc['threat.indicator.ip'].size()!=0 && doc['threat.indicator.ip'].value!=null) { return emit(doc['threat.indicator.ip'].value) } } if (doc.containsKey('threat.indicator.type') && !doc['threat.indicator.type'].empty && doc['threat.indicator.type'].size()!=0 && doc['threat.indicator.type'].value!=null && doc['threat.indicator.type'].value.toLowerCase()=='ipv6-addr') { if (doc.containsKey('threat.indicator.ip') && !doc['threat.indicator.ip'].empty && doc['threat.indicator.ip'].size()!=0 && doc['threat.indicator.ip'].value!=null) { return emit(doc['threat.indicator.ip'].value) } } if (doc.containsKey('threat.indicator.type') && !doc['threat.indicator.type'].empty && doc['threat.indicator.type'].size()!=0 && doc['threat.indicator.type'].value!=null && doc['threat.indicator.type'].value.toLowerCase()=='file') { if (doc.containsKey('threat.indicator.file.hash.sha256') && !doc['threat.indicator.file.hash.sha256'].empty && doc['threat.indicator.file.hash.sha256'].size()!=0 && doc['threat.indicator.file.hash.sha256'].value!=null) { return emit(doc['threat.indicator.file.hash.sha256'].value) } @@ -53,7 +53,7 @@ describe('display name generation', () => { if (doc.containsKey('threat.indicator.type') && !doc['threat.indicator.type'].empty && doc['threat.indicator.type'].size()!=0 && doc['threat.indicator.type'].value!=null && doc['threat.indicator.type'].value.toLowerCase()=='mac-addr') { if (doc.containsKey('threat.indicator.mac') && !doc['threat.indicator.mac'].empty && doc['threat.indicator.mac'].size()!=0 && doc['threat.indicator.mac'].value!=null) { return emit(doc['threat.indicator.mac'].value) } } - return emit('')" + return emit('') } catch (Exception e) { return emit('') }" `); }); }); @@ -61,7 +61,7 @@ describe('display name generation', () => { describe('threatIndicatorNamesOriginScript()', () => { it('should generate a valid painless script', () => { expect(threatIndicatorNamesOriginScript()).toMatchInlineSnapshot(` - "if (doc.containsKey('threat.indicator.type') && !doc['threat.indicator.type'].empty && doc['threat.indicator.type'].size()!=0 && doc['threat.indicator.type'].value!=null && doc['threat.indicator.type'].value.toLowerCase()=='ipv4-addr') { if (doc.containsKey('threat.indicator.ip') && !doc['threat.indicator.ip'].empty && doc['threat.indicator.ip'].size()!=0 && doc['threat.indicator.ip'].value!=null) { return emit('threat.indicator.ip') } } + "try { if (doc.containsKey('threat.indicator.type') && !doc['threat.indicator.type'].empty && doc['threat.indicator.type'].size()!=0 && doc['threat.indicator.type'].value!=null && doc['threat.indicator.type'].value.toLowerCase()=='ipv4-addr') { if (doc.containsKey('threat.indicator.ip') && !doc['threat.indicator.ip'].empty && doc['threat.indicator.ip'].size()!=0 && doc['threat.indicator.ip'].value!=null) { return emit('threat.indicator.ip') } } if (doc.containsKey('threat.indicator.type') && !doc['threat.indicator.type'].empty && doc['threat.indicator.type'].size()!=0 && doc['threat.indicator.type'].value!=null && doc['threat.indicator.type'].value.toLowerCase()=='ipv6-addr') { if (doc.containsKey('threat.indicator.ip') && !doc['threat.indicator.ip'].empty && doc['threat.indicator.ip'].size()!=0 && doc['threat.indicator.ip'].value!=null) { return emit('threat.indicator.ip') } } if (doc.containsKey('threat.indicator.type') && !doc['threat.indicator.type'].empty && doc['threat.indicator.type'].size()!=0 && doc['threat.indicator.type'].value!=null && doc['threat.indicator.type'].value.toLowerCase()=='file') { if (doc.containsKey('threat.indicator.file.hash.sha256') && !doc['threat.indicator.file.hash.sha256'].empty && doc['threat.indicator.file.hash.sha256'].size()!=0 && doc['threat.indicator.file.hash.sha256'].value!=null) { return emit('threat.indicator.file.hash.sha256') } @@ -103,7 +103,7 @@ describe('display name generation', () => { if (doc.containsKey('threat.indicator.type') && !doc['threat.indicator.type'].empty && doc['threat.indicator.type'].size()!=0 && doc['threat.indicator.type'].value!=null && doc['threat.indicator.type'].value.toLowerCase()=='mac-addr') { if (doc.containsKey('threat.indicator.mac') && !doc['threat.indicator.mac'].empty && doc['threat.indicator.mac'].size()!=0 && doc['threat.indicator.mac'].value!=null) { return emit('threat.indicator.mac') } } - return emit('')" + return emit('') } catch (Exception e) { return emit('') }" `); }); }); diff --git a/x-pack/plugins/threat_intelligence/server/utils/indicator_name.ts b/x-pack/plugins/threat_intelligence/server/utils/indicator_name.ts index 431fdcb3f1538..af11b68b8a57f 100644 --- a/x-pack/plugins/threat_intelligence/server/utils/indicator_name.ts +++ b/x-pack/plugins/threat_intelligence/server/utils/indicator_name.ts @@ -93,13 +93,19 @@ const mappingToIndicatorNameOriginScript = ([types, paths]: Mapping) => { .join('\n')}`; }; +/** + * Wrap painless with trycatch + */ +export const tryCatch = (script: string) => + `try { ${script} } catch (Exception e) { return emit('') }`; + /** * Generates the runtime field script computing display name for the given indicator */ export const threatIndicatorNamesScript = (mappings: Mappings = mappingsArray) => { const combined = mappings.map(mappingToIndicatorNameScript).join('\n\n'); - return `${combined}\n\nreturn emit('')`; + return tryCatch(`${combined}\n\nreturn emit('')`); }; /** @@ -108,5 +114,5 @@ export const threatIndicatorNamesScript = (mappings: Mappings = mappingsArray) = export const threatIndicatorNamesOriginScript = (mappings: Mappings = mappingsArray) => { const combined = mappings.map(mappingToIndicatorNameOriginScript).join('\n\n'); - return `${combined}\n\nreturn emit('')`; + return tryCatch(`${combined}\n\nreturn emit('')`); }; diff --git a/x-pack/plugins/timelines/common/utils/field_formatters.ts b/x-pack/plugins/timelines/common/utils/field_formatters.ts index 49590bfea54c1..e9b8b45bfefca 100644 --- a/x-pack/plugins/timelines/common/utils/field_formatters.ts +++ b/x-pack/plugins/timelines/common/utils/field_formatters.ts @@ -10,7 +10,7 @@ import { isEmpty } from 'lodash/fp'; import { ALERT_RULE_PARAMETERS } from '@kbn/rule-data-utils'; import { ecsFieldMap } from '@kbn/rule-registry-plugin/common/assets/field_maps/ecs_field_map'; import { technicalRuleFieldMap } from '@kbn/rule-registry-plugin/common/assets/field_maps/technical_rule_field_map'; -import { experimentalRuleFieldMap } from '@kbn/rule-registry-plugin/common/assets/field_maps/experimental_rule_field_map'; +import { legacyExperimentalFieldMap } from '@kbn/alerts-as-data-utils'; import { EventHit, TimelineEventsDetailsItem } from '../search_strategy'; import { toObjectArrayOfStrings, toStringArray } from './to_array'; import { ENRICHMENT_DESTINATION_PATH } from '../constants'; @@ -79,9 +79,11 @@ export const getDataFromFieldsHits = ( // return simple field value (non-ecs object, non-array) if ( !isObjectArray || - (Object.keys({ ...ecsFieldMap, ...technicalRuleFieldMap, ...experimentalRuleFieldMap }).find( - (ecsField) => ecsField === field - ) === undefined && + (Object.keys({ + ...ecsFieldMap, + ...technicalRuleFieldMap, + ...legacyExperimentalFieldMap, + }).find((ecsField) => ecsField === field) === undefined && !isRuleParametersFieldOrSubfield(field, prependField)) ) { return [ diff --git a/x-pack/plugins/timelines/tsconfig.json b/x-pack/plugins/timelines/tsconfig.json index 0612384baf8a7..91fb476e1f983 100644 --- a/x-pack/plugins/timelines/tsconfig.json +++ b/x-pack/plugins/timelines/tsconfig.json @@ -32,6 +32,7 @@ "@kbn/i18n", "@kbn/security-plugin", "@kbn/safer-lodash-set", + "@kbn/alerts-as-data-utils", "@kbn/logging", ], "exclude": [ diff --git a/x-pack/plugins/translations/translations/fr-FR.json b/x-pack/plugins/translations/translations/fr-FR.json index 9e23807197c66..8744d6d15b8f6 100644 --- a/x-pack/plugins/translations/translations/fr-FR.json +++ b/x-pack/plugins/translations/translations/fr-FR.json @@ -24575,9 +24575,6 @@ "xpack.observability.filters.url": "Url", "xpack.observability.filters.url.loadingResults": "Chargement des résultats", "xpack.observability.filters.url.noResults": "Aucun résultat disponible", - "xpack.observability.fleet.button": "Essayer Fleet", - "xpack.observability.fleet.text": "Elastic Agent offre un moyen simple et unifié d'ajouter un monitoring pour les logs, les indicateurs et d'autres types de données à vos hôtes. Vous n'avez plus besoin d'installer plusieurs agents Beats, ce qui facilite et accélère le déploiement de configurations dans votre infrastructure.", - "xpack.observability.fleet.title": "Avez-vous vu notre nouveau Fleet ?", "xpack.observability.formatters.hoursTimeUnitLabel": "h", "xpack.observability.formatters.hoursTimeUnitLabelExtended": "heures", "xpack.observability.formatters.microsTimeUnitLabel": "μs", @@ -24670,13 +24667,10 @@ "xpack.observability.page_header.addUptimeDataLink.label": "Accédez à un tutoriel sur l'ajout de données Uptime", "xpack.observability.page_header.addUXDataLink.label": "Accédez à un tutoriel sur l'ajout de données APM d'expérience utilisateur.", "xpack.observability.pageLayout.sideNavTitle": "Observabilité", - "xpack.observability.pages.alertDetails.alertSummary.actualValue": "Valeur réelle", "xpack.observability.pages.alertDetails.alertSummary.alertStatus": "Statut", "xpack.observability.pages.alertDetails.alertSummary.duration": "Durée", - "xpack.observability.pages.alertDetails.alertSummary.expectedValue": "Valeur attendue", "xpack.observability.pages.alertDetails.alertSummary.lastStatusUpdate": "Dernière mise à jour du statut", "xpack.observability.pages.alertDetails.alertSummary.ruleTags": "Balises de règle", - "xpack.observability.pages.alertDetails.alertSummary.source": "Source", "xpack.observability.pages.alertDetails.alertSummary.started": "Démarré", "xpack.observability.resources.documentation": "Documentation", "xpack.observability.resources.forum": "Forum de discussion", diff --git a/x-pack/plugins/translations/translations/ja-JP.json b/x-pack/plugins/translations/translations/ja-JP.json index 720eac36e9e81..042abb4e1f207 100644 --- a/x-pack/plugins/translations/translations/ja-JP.json +++ b/x-pack/plugins/translations/translations/ja-JP.json @@ -24553,9 +24553,6 @@ "xpack.observability.filters.url": "Url", "xpack.observability.filters.url.loadingResults": "結果を読み込み中", "xpack.observability.filters.url.noResults": "結果がありません", - "xpack.observability.fleet.button": "Fleetを試す", - "xpack.observability.fleet.text": "Elastic エージェントでは、シンプルかつ統合された方法で、ログ、メトリック、他の種類のデータの監視をホストに追加することができます。複数の Beats と他のエージェントをインストールする必要はありません。このため、インフラストラクチャ全体での構成のデプロイが簡単で高速になりました。", - "xpack.observability.fleet.title": "新しい Fleet をご覧になりましたか。", "xpack.observability.formatters.hoursTimeUnitLabel": "h", "xpack.observability.formatters.hoursTimeUnitLabelExtended": "時間", "xpack.observability.formatters.microsTimeUnitLabel": "μs", @@ -24648,13 +24645,10 @@ "xpack.observability.page_header.addUptimeDataLink.label": "アップタイムデータの追加に関するチュートリアルに移動", "xpack.observability.page_header.addUXDataLink.label": "ユーザーエクスペリエンスAPMデータの追加に関するチュートリアルに移動", "xpack.observability.pageLayout.sideNavTitle": "Observability", - "xpack.observability.pages.alertDetails.alertSummary.actualValue": "実際の値", "xpack.observability.pages.alertDetails.alertSummary.alertStatus": "ステータス", "xpack.observability.pages.alertDetails.alertSummary.duration": "期間", - "xpack.observability.pages.alertDetails.alertSummary.expectedValue": "想定された値", "xpack.observability.pages.alertDetails.alertSummary.lastStatusUpdate": "前回のステータス更新", "xpack.observability.pages.alertDetails.alertSummary.ruleTags": "ルールタグ", - "xpack.observability.pages.alertDetails.alertSummary.source": "送信元", "xpack.observability.pages.alertDetails.alertSummary.started": "開始", "xpack.observability.resources.documentation": "ドキュメント", "xpack.observability.resources.forum": "ディスカッションフォーラム", diff --git a/x-pack/plugins/translations/translations/zh-CN.json b/x-pack/plugins/translations/translations/zh-CN.json index 3de4232c9b8d7..5f784708d0584 100644 --- a/x-pack/plugins/translations/translations/zh-CN.json +++ b/x-pack/plugins/translations/translations/zh-CN.json @@ -24584,9 +24584,6 @@ "xpack.observability.filters.url": "URL", "xpack.observability.filters.url.loadingResults": "正在加载结果", "xpack.observability.filters.url.noResults": "没有可用结果", - "xpack.observability.fleet.button": "试用 Fleet", - "xpack.observability.fleet.text": "通过 Elastic 代理,可以简单统一的方式将日志、指标和其他类型数据的监测添加到主机。您无需安装多个 Beats 和其他代理,以令其更为方便快捷地在基础结构中部署配置。", - "xpack.observability.fleet.title": "您是否了解我们的全新 Fleet?", "xpack.observability.formatters.hoursTimeUnitLabel": "h", "xpack.observability.formatters.hoursTimeUnitLabelExtended": "小时", "xpack.observability.formatters.microsTimeUnitLabel": "μs", @@ -24679,13 +24676,10 @@ "xpack.observability.page_header.addUptimeDataLink.label": "导航到有关如何添加 Uptime 数据的教程", "xpack.observability.page_header.addUXDataLink.label": "导航到有关如何添加用户体验 APM 数据的教程", "xpack.observability.pageLayout.sideNavTitle": "Observability", - "xpack.observability.pages.alertDetails.alertSummary.actualValue": "实际值", "xpack.observability.pages.alertDetails.alertSummary.alertStatus": "状态", "xpack.observability.pages.alertDetails.alertSummary.duration": "持续时间", - "xpack.observability.pages.alertDetails.alertSummary.expectedValue": "预期值", "xpack.observability.pages.alertDetails.alertSummary.lastStatusUpdate": "上次状态更新", "xpack.observability.pages.alertDetails.alertSummary.ruleTags": "规则标签", - "xpack.observability.pages.alertDetails.alertSummary.source": "源", "xpack.observability.pages.alertDetails.alertSummary.started": "已启动", "xpack.observability.resources.documentation": "文档", "xpack.observability.resources.forum": "讨论论坛", diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/add_message_variables.test.tsx b/x-pack/plugins/triggers_actions_ui/public/application/components/add_message_variables.test.tsx index ecc6de8251474..641587c349382 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/components/add_message_variables.test.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/components/add_message_variables.test.tsx @@ -6,67 +6,85 @@ */ import React from 'react'; -import { mountWithIntl } from '@kbn/test-jest-helpers'; +import { render, fireEvent, screen } from '@testing-library/react'; import { AddMessageVariables } from './add_message_variables'; describe('AddMessageVariables', () => { - test('renders variables with double brances by default', () => { - const onSelectEventHandler = jest.fn(); - const wrapper = mountWithIntl( + test('it renders variables and filter bar', async () => { + render( ); - wrapper.find('[data-test-subj="fooAddVariableButton"]').first().simulate('click'); - - expect( - wrapper.find('[data-test-subj="variableMenuButton-0-templated-name"]').last().text() - ).toEqual('{{myVar}}'); + fireEvent.click(await screen.findByTestId('fooAddVariableButton')); + expect(screen.getByPlaceholderText('Filter options')).toBeInTheDocument(); + expect(screen.getByTestId('myVar-selectableOption')).toBeInTheDocument(); + expect(screen.getByTestId('myVar2-selectableOption')).toBeInTheDocument(); }); - test('renders variables with tripple braces when specified', () => { - const onSelectEventHandler = jest.fn(); - const wrapper = mountWithIntl( + test('it renders variables title and description', async () => { + render( ); - wrapper.find('[data-test-subj="fooAddVariableButton"]').first().simulate('click'); + fireEvent.click(await screen.findByTestId('fooAddVariableButton')); + expect(screen.getByText('myVar')).toBeInTheDocument(); + expect(screen.getByText('My variable description')).toBeInTheDocument(); + }); + + test('it renders tooltip when click on variable', async () => { + render( + + ); - expect( - wrapper.find('[data-test-subj="variableMenuButton-0-templated-name"]').last().text() - ).toEqual('{{{myVar}}}'); + fireEvent.click(await screen.findByTestId('fooAddVariableButton')); + fireEvent.mouseOver(screen.getByText('My variable description')); + expect(await screen.findByTestId('myVar-tooltip')).toBeInTheDocument(); }); - test('onSelectEventHandler is called with proper action variable', () => { + test('onSelectEventHandler is called with proper action variable', async () => { const onSelectEventHandler = jest.fn(); - const wrapper = mountWithIntl( + render( { /> ); - wrapper.find('[data-test-subj="fooAddVariableButton"]').first().simulate('click'); - wrapper.find('[data-test-subj="variableMenuButton-1-templated-name"]').last().simulate('click'); + fireEvent.click(await screen.findByTestId('fooAddVariableButton')); + fireEvent.click(screen.getByTestId('myVar2-selectableOption')); expect(onSelectEventHandler).toHaveBeenCalledTimes(1); expect(onSelectEventHandler).toHaveBeenCalledWith({ name: 'myVar2', - description: 'My variable 1 description', + description: 'My variable 2 description', useWithTripleBracesInTemplates: true, }); }); - test('it renders deprecated variables as disabled', () => { - const wrapper = mountWithIntl( + test('it renders deprecated variables as disabled', async () => { + render( { /> ); - wrapper.find('[data-test-subj="fooAddVariableButton"]').first().simulate('click'); - - expect( - wrapper.find('button[data-test-subj="variableMenuButton-myVar"]').getDOMNode() - ).not.toBeDisabled(); - expect( - wrapper.find('button[data-test-subj="variableMenuButton-deprecatedVar"]').getDOMNode() - ).toBeDisabled(); + fireEvent.click(await screen.findByTestId('fooAddVariableButton')); + fireEvent.click(screen.getByText('Show all')); + expect(screen.queryByTestId('myVar-selectableOption')).toBeInTheDocument(); + expect(screen.queryByTestId('deprecatedVar-selectableOption')).toBeInTheDocument(); }); - test(`it does't render when no variables exist`, () => { - const wrapper = mountWithIntl( + test(`it does't render when no variables exist`, async () => { + render( { /> ); - expect(wrapper.find('[data-test-subj="fooAddVariableButton"]')).toHaveLength(0); + expect(screen.queryByTestId('fooAddVariableButton')).not.toBeInTheDocument(); }); - test('it renders button title when passed', () => { - const wrapper = mountWithIntl( + test('it renders button title when passed', async () => { + render( { /> ); - expect(wrapper.find('[data-test-subj="fooAddVariableButton-Title"]').exists()).toEqual(true); + expect(await screen.findByText('Add variable')).toBeInTheDocument(); }); }); diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/add_message_variables.tsx b/x-pack/plugins/triggers_actions_ui/public/application/components/add_message_variables.tsx index 6cfcf09d7387a..f986930470acc 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/components/add_message_variables.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/components/add_message_variables.tsx @@ -6,18 +6,25 @@ */ import React, { useMemo, useState } from 'react'; -import { i18n } from '@kbn/i18n'; import { EuiPopover, EuiButtonIcon, - EuiContextMenuPanel, - EuiContextMenuItem, EuiText, EuiButtonEmpty, + EuiSelectable, + EuiSpacer, + EuiHighlight, + useEuiTheme, + EuiFlexGroup, + EuiFlexItem, + EuiPopoverFooter, + EuiToolTip, + EuiSelectableOption, } from '@elastic/eui'; -import './add_message_variables.scss'; import { ActionVariable } from '@kbn/alerting-plugin/common'; -import { templateActionVariable } from '../lib'; +import './add_message_variables.scss'; +import { TruncatedText } from '../../common/truncated_text'; +import * as i18n from './translations'; interface Props { buttonTitle?: string; @@ -34,39 +41,42 @@ export const AddMessageVariables: React.FunctionComponent = ({ onSelectEventHandler, showButtonTitle = false, }) => { + const [isShowAllPressed, setIsShowAllPressed] = useState(false); const [isVariablesPopoverOpen, setIsVariablesPopoverOpen] = useState(false); - const getMessageVariables = () => - messageVariables?.map((variable: ActionVariable, i: number) => ( - { - onSelectEventHandler(variable); - setIsVariablesPopoverOpen(false); - }} - > - <> - - {templateActionVariable(variable)} - - -
{variable.description}
-
- -
- )); + const { euiTheme } = useEuiTheme(); - const addVariableButtonTitle = buttonTitle - ? buttonTitle - : i18n.translate( - 'xpack.triggersActionsUI.components.addMessageVariables.addRuleVariableTitle', - { - defaultMessage: 'Add rule variable', - } - ); + const messageVariablesObject: Record | undefined = useMemo( + () => + messageVariables?.reduce((acc, variable) => { + return { + ...acc, + [variable.name]: variable, + }; + }, {}), + [messageVariables] + ); + + const messageVariablesToShow = useMemo( + () => + isShowAllPressed + ? messageVariables + : messageVariables?.filter((variable) => !variable.deprecated), + [messageVariables, isShowAllPressed] + ); + + const optionsToShow = useMemo(() => { + return messageVariablesToShow?.map((variable) => ({ + label: variable.name, + ...(variable.deprecated ? { disabled: true } : {}), + data: { + description: variable.description, + }, + 'data-test-subj': `${variable.name}-selectableOption`, + })); + }, [messageVariablesToShow]); + + const addVariableButtonTitle = buttonTitle ? buttonTitle : i18n.ADD_VARIABLE_TITLE; const Button = useMemo( () => @@ -76,13 +86,7 @@ export const AddMessageVariables: React.FunctionComponent = ({ data-test-subj={`${paramsProperty}AddVariableButton-Title`} size="xs" onClick={() => setIsVariablesPopoverOpen(true)} - iconType="indexOpen" - aria-label={i18n.translate( - 'xpack.triggersActionsUI.components.addMessageVariables.addVariablePopoverButton', - { - defaultMessage: 'Add variable', - } - )} + aria-label={i18n.ADD_VARIABLE_POPOVER_BUTTON} > {addVariableButtonTitle} @@ -93,12 +97,7 @@ export const AddMessageVariables: React.FunctionComponent = ({ title={addVariableButtonTitle} onClick={() => setIsVariablesPopoverOpen(true)} iconType="indexOpen" - aria-label={i18n.translate( - 'xpack.triggersActionsUI.components.addMessageVariables.addVariablePopoverButton', - { - defaultMessage: 'Add variable', - } - )} + aria-label={i18n.ADD_VARIABLE_POPOVER_BUTTON} /> ), [addVariableButtonTitle, paramsProperty, showButtonTitle] @@ -107,15 +106,130 @@ export const AddMessageVariables: React.FunctionComponent = ({ return <>; } + const ToolTipContent = ({ description, label }: { description: string; label: string }) => { + return ( + <> + + {label} + + +
+ + {description} + + ); + }; + + const renderOption = ( + option: EuiSelectableOption<{ description?: string }>, + searchValue: string + ) => { + return ( + + + + {option.label} + + + {option.description && ( + <> + } + data-test-subj={`${option.label}-tooltip`} + > + + + + )} + + + ); + }; + return ( setIsVariablesPopoverOpen(false)} - panelPaddingSize="none" - anchorPosition="downLeft" + panelPaddingSize="s" + anchorPosition="upRight" + panelStyle={{ minWidth: 350 }} > - + { + variables.map((variable) => { + if (variable.checked === 'on' && messageVariablesObject) { + onSelectEventHandler(messageVariablesObject[variable.label]); + } + }); + setIsVariablesPopoverOpen(false); + }} + singleSelection + searchProps={{ 'data-test-subj': 'messageVariablesSelectableSearch' }} + > + {(list, search) => ( + <> + {search} + + {list} + + + + + {isShowAllPressed + ? i18n.DEPRECATED_VARIABLES_ARE_SHOWN + : i18n.DEPRECATED_VARIABLES_ARE_HIDDEN} + + + + + isShowAllPressed ? setIsShowAllPressed(false) : setIsShowAllPressed(true) + } + > + {isShowAllPressed ? i18n.HIDE : i18n.SHOW_ALL} + + + + + + )} + ); }; diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/json_editor_with_message_variables.test.tsx b/x-pack/plugins/triggers_actions_ui/public/application/components/json_editor_with_message_variables.test.tsx index 0cac98f01f636..df82fc094eff4 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/components/json_editor_with_message_variables.test.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/components/json_editor_with_message_variables.test.tsx @@ -41,7 +41,7 @@ describe('JsonEditorWithMessageVariables', () => { const wrapper = mountWithIntl(); wrapper.find('[data-test-subj="fooAddVariableButton"]').first().simulate('click'); - wrapper.find('[data-test-subj="variableMenuButton-0-templated-name"]').last().simulate('click'); + wrapper.find('[data-test-subj="variableMenuButton-myVar"]').last().simulate('click'); expect(wrapper.find('[data-test-subj="fooJsonEditor"]').first().prop('value')).toEqual( '{{myVar}}' @@ -63,7 +63,7 @@ describe('JsonEditorWithMessageVariables', () => { ); wrapper.find('[data-test-subj="fooAddVariableButton"]').first().simulate('click'); - wrapper.find('[data-test-subj="variableMenuButton-0-templated-name"]').last().simulate('click'); + wrapper.find('[data-test-subj="variableMenuButton-myVar"]').last().simulate('click'); expect(wrapper.find('[data-test-subj="fooJsonEditor"]').first().prop('value')).toEqual( '{{{myVar}}}' diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/text_area_with_message_variables.test.tsx b/x-pack/plugins/triggers_actions_ui/public/application/components/text_area_with_message_variables.test.tsx index 29709d2484d6d..44a61b190932f 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/components/text_area_with_message_variables.test.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/components/text_area_with_message_variables.test.tsx @@ -30,7 +30,7 @@ describe('TextAreaWithMessageVariables', () => { const wrapper = mountWithIntl(); wrapper.find('[data-test-subj="fooAddVariableButton"]').first().simulate('click'); - wrapper.find('[data-test-subj="variableMenuButton-0-templated-name"]').last().simulate('click'); + wrapper.find('[data-test-subj="variableMenuButton-myVar"]').last().simulate('click'); expect(editAction).toHaveBeenCalledTimes(1); expect(editAction).toHaveBeenCalledWith(props.paramsProperty, '{{myVar}}', props.index); @@ -51,7 +51,7 @@ describe('TextAreaWithMessageVariables', () => { ); wrapper.find('[data-test-subj="fooAddVariableButton"]').first().simulate('click'); - wrapper.find('[data-test-subj="variableMenuButton-0-templated-name"]').last().simulate('click'); + wrapper.find('[data-test-subj="variableMenuButton-myVar"]').last().simulate('click'); expect(editAction).toHaveBeenCalledTimes(1); expect(editAction).toHaveBeenCalledWith(props.paramsProperty, '{{{myVar}}}', props.index); diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/text_field_with_message_variables.test.tsx b/x-pack/plugins/triggers_actions_ui/public/application/components/text_field_with_message_variables.test.tsx index c7a25e19c5c12..8c177a4eb25f1 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/components/text_field_with_message_variables.test.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/components/text_field_with_message_variables.test.tsx @@ -30,7 +30,7 @@ describe('TextFieldWithMessageVariables', () => { const wrapper = mountWithIntl(); wrapper.find('[data-test-subj="fooAddVariableButton"]').first().simulate('click'); - wrapper.find('[data-test-subj="variableMenuButton-0-templated-name"]').last().simulate('click'); + wrapper.find('[data-test-subj="variableMenuButton-myVar"]').last().simulate('click'); expect(editAction).toHaveBeenCalledTimes(1); expect(editAction).toHaveBeenCalledWith(props.paramsProperty, '{{myVar}}', props.index); @@ -51,7 +51,7 @@ describe('TextFieldWithMessageVariables', () => { ); wrapper.find('[data-test-subj="fooAddVariableButton"]').first().simulate('click'); - wrapper.find('[data-test-subj="variableMenuButton-0-templated-name"]').last().simulate('click'); + wrapper.find('[data-test-subj="variableMenuButton-myVar"]').last().simulate('click'); expect(editAction).toHaveBeenCalledTimes(1); expect(editAction).toHaveBeenCalledWith(props.paramsProperty, '{{{myVar}}}', props.index); diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/translations.js b/x-pack/plugins/triggers_actions_ui/public/application/components/translations.js new file mode 100644 index 0000000000000..0e089f1a830e8 --- /dev/null +++ b/x-pack/plugins/triggers_actions_ui/public/application/components/translations.js @@ -0,0 +1,71 @@ +/* + * 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 { i18n } from '@kbn/i18n'; + +export const LOADING_VARIABLES = i18n.translate( + 'xpack.triggersActionsUI.components.addMessageVariables.loadingMessage', + { + defaultMessage: 'Loading variables', + } +); + +export const NO_VARIABLES_FOUND = i18n.translate( + 'xpack.triggersActionsUI.components.addMessageVariables.noVariablesFound', + { + defaultMessage: 'No variables found', + } +); + +export const NO_VARIABLES_AVAILABLE = i18n.translate( + 'xpack.triggersActionsUI.components.addMessageVariables.noVariablesAvailable', + { + defaultMessage: 'No variables available', + } +); + +export const DEPRECATED_VARIABLES_ARE_SHOWN = i18n.translate( + 'xpack.triggersActionsUI.components.addMessageVariables.deprecatedVariablesAreShown', + { + defaultMessage: 'Deprecated variables are shown', + } +); + +export const DEPRECATED_VARIABLES_ARE_HIDDEN = i18n.translate( + 'xpack.triggersActionsUI.components.addMessageVariables.deprecatedVariablesAreHidden', + { + defaultMessage: 'Deprecated variables are hidden', + } +); + +export const HIDE = i18n.translate( + 'xpack.triggersActionsUI.components.addMessageVariables.hideDeprecatedVariables', + { + defaultMessage: 'Hide', + } +); + +export const SHOW_ALL = i18n.translate( + 'xpack.triggersActionsUI.components.addMessageVariables.showAllDeprecatedVariables', + { + defaultMessage: 'Show all', + } +); + +export const ADD_VARIABLE_POPOVER_BUTTON = i18n.translate( + 'xpack.triggersActionsUI.components.addMessageVariables.addVariablePopoverButton', + { + defaultMessage: 'Add variable', + } +); + +export const ADD_VARIABLE_TITLE = i18n.translate( + 'xpack.triggersActionsUI.components.addMessageVariables.addRuleVariableTitle', + { + defaultMessage: 'Add variable', + } +); diff --git a/x-pack/plugins/triggers_actions_ui/public/application/lib/rule_api/aggregate.ts b/x-pack/plugins/triggers_actions_ui/public/application/lib/rule_api/aggregate.ts index 605aa96552583..b924f4952fc90 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/lib/rule_api/aggregate.ts +++ b/x-pack/plugins/triggers_actions_ui/public/application/lib/rule_api/aggregate.ts @@ -6,19 +6,21 @@ */ import { HttpSetup } from '@kbn/core/public'; import { AsApiContract } from '@kbn/actions-plugin/common'; -import { RuleAggregations } from '../../../types'; +import { + RuleAggregationFormattedResult, + RuleTagsAggregationFormattedResult, +} from '@kbn/alerting-plugin/common'; import { INTERNAL_BASE_ALERTING_API_PATH } from '../../constants'; import { mapFiltersToKql } from './map_filters_to_kql'; -import { - LoadRuleAggregationsProps, - rewriteBodyRes, - rewriteTagsBodyRes, - RuleTagsAggregations, -} from './aggregate_helpers'; +import { LoadRuleAggregationsProps, rewriteBodyRes, rewriteTagsBodyRes } from './aggregate_helpers'; // TODO: https://github.com/elastic/kibana/issues/131682 -export async function loadRuleTags({ http }: { http: HttpSetup }): Promise { - const res = await http.get>( +export async function loadRuleTags({ + http, +}: { + http: HttpSetup; +}): Promise { + const res = await http.get>( `${INTERNAL_BASE_ALERTING_API_PATH}/rules/_aggregate` ); return rewriteTagsBodyRes(res); @@ -32,7 +34,7 @@ export async function loadRuleAggregations({ ruleExecutionStatusesFilter, ruleStatusesFilter, tagsFilter, -}: LoadRuleAggregationsProps): Promise { +}: LoadRuleAggregationsProps): Promise { const filters = mapFiltersToKql({ typesFilter, actionTypesFilter, @@ -40,7 +42,7 @@ export async function loadRuleAggregations({ ruleStatusesFilter, tagsFilter, }); - const res = await http.post>( + const res = await http.post>( `${INTERNAL_BASE_ALERTING_API_PATH}/rules/_aggregate`, { body: JSON.stringify({ diff --git a/x-pack/plugins/triggers_actions_ui/public/application/lib/rule_api/aggregate_helpers.ts b/x-pack/plugins/triggers_actions_ui/public/application/lib/rule_api/aggregate_helpers.ts index cc236ef876ee6..1150fc27867f1 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/lib/rule_api/aggregate_helpers.ts +++ b/x-pack/plugins/triggers_actions_ui/public/application/lib/rule_api/aggregate_helpers.ts @@ -7,13 +7,13 @@ import { HttpSetup } from '@kbn/core/public'; import { RewriteRequestCase } from '@kbn/actions-plugin/common'; -import { RuleAggregations, RuleStatus } from '../../../types'; +import { + RuleAggregationFormattedResult, + RuleTagsAggregationFormattedResult, +} from '@kbn/alerting-plugin/common'; +import { RuleStatus } from '../../../types'; -export interface RuleTagsAggregations { - ruleTags: string[]; -} - -export const rewriteBodyRes: RewriteRequestCase = ({ +export const rewriteBodyRes: RewriteRequestCase = ({ rule_execution_status: ruleExecutionStatus, rule_last_run_outcome: ruleLastRunOutcome, rule_enabled_status: ruleEnabledStatus, @@ -31,7 +31,7 @@ export const rewriteBodyRes: RewriteRequestCase = ({ ruleTags, }); -export const rewriteTagsBodyRes: RewriteRequestCase = ({ +export const rewriteTagsBodyRes: RewriteRequestCase = ({ rule_tags: ruleTags, }: any) => ({ ruleTags, diff --git a/x-pack/plugins/triggers_actions_ui/public/application/lib/rule_api/aggregate_kuery_filter.ts b/x-pack/plugins/triggers_actions_ui/public/application/lib/rule_api/aggregate_kuery_filter.ts index 958ccd79e95ef..20d1fc9281b48 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/lib/rule_api/aggregate_kuery_filter.ts +++ b/x-pack/plugins/triggers_actions_ui/public/application/lib/rule_api/aggregate_kuery_filter.ts @@ -5,7 +5,7 @@ * 2.0. */ import { AsApiContract } from '@kbn/actions-plugin/common'; -import { RuleAggregations } from '../../../types'; +import { RuleAggregationFormattedResult } from '@kbn/alerting-plugin/common'; import { INTERNAL_BASE_ALERTING_API_PATH } from '../../constants'; import { LoadRuleAggregationsProps, rewriteBodyRes } from './aggregate_helpers'; import { mapFiltersToKueryNode } from './map_filters_to_kuery_node'; @@ -18,7 +18,7 @@ export async function loadRuleAggregationsWithKueryFilter({ ruleExecutionStatusesFilter, ruleStatusesFilter, tagsFilter, -}: LoadRuleAggregationsProps): Promise { +}: LoadRuleAggregationsProps): Promise { const filtersKueryNode = mapFiltersToKueryNode({ typesFilter, actionTypesFilter, @@ -28,7 +28,7 @@ export async function loadRuleAggregationsWithKueryFilter({ searchText, }); - const res = await http.post>( + const res = await http.post>( `${INTERNAL_BASE_ALERTING_API_PATH}/rules/_aggregate`, { body: JSON.stringify({ diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/actions_connectors_list/components/actions_connectors_event_log_list_table.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/actions_connectors_list/components/actions_connectors_event_log_list_table.tsx index a2a674ecd73ee..ec8d0d305ba69 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/actions_connectors_list/components/actions_connectors_event_log_list_table.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/actions_connectors_list/components/actions_connectors_event_log_list_table.tsx @@ -377,6 +377,21 @@ export const ConnectorEventLogListTable = = ({ text }) => ( + + {text} + +); + +TruncatedTextComponent.displayName = 'TruncatedText'; + +export const TruncatedText = React.memo(TruncatedTextComponent); diff --git a/x-pack/plugins/triggers_actions_ui/public/types.ts b/x-pack/plugins/triggers_actions_ui/public/types.ts index d75125e056630..7c089c11bb7f6 100644 --- a/x-pack/plugins/triggers_actions_ui/public/types.ts +++ b/x-pack/plugins/triggers_actions_ui/public/types.ts @@ -41,7 +41,6 @@ import { SanitizedRule as AlertingSanitizedRule, ResolvedSanitizedRule, RuleAction, - RuleAggregations as AlertingRuleAggregations, RuleTaskState, AlertSummary as RuleSummary, ExecutionDuration, @@ -103,14 +102,10 @@ type Rule = SanitizedRule, 'alertTypeId'> & { ruleTypeId: ResolvedSanitizedRule['alertTypeId']; }; -type RuleAggregations = Omit & { - ruleExecutionStatus: AlertingRuleAggregations['alertExecutionStatus']; -}; export type { Rule, RuleAction, - RuleAggregations, RuleTaskState, RuleSummary, ExecutionDuration, diff --git a/x-pack/test/alerting_api_integration/common/plugins/alerts/server/alert_types.ts b/x-pack/test/alerting_api_integration/common/plugins/alerts/server/alert_types.ts index 3c2880d69f776..c2f6b5ca17d21 100644 --- a/x-pack/test/alerting_api_integration/common/plugins/alerts/server/alert_types.ts +++ b/x-pack/test/alerting_api_integration/common/plugins/alerts/server/alert_types.ts @@ -94,22 +94,24 @@ function getAlwaysFiringAlertType() { executor: curry(alwaysFiringExecutor)(), alerts: { context: 'test.always-firing', - fieldMap: { - instance_state_value: { - required: false, - type: 'boolean', - }, - instance_params_value: { - required: false, - type: 'boolean', - }, - instance_context_value: { - required: false, - type: 'boolean', - }, - group_in_series_index: { - required: false, - type: 'long', + mappings: { + fieldMap: { + instance_state_value: { + required: false, + type: 'boolean', + }, + instance_params_value: { + required: false, + type: 'boolean', + }, + instance_context_value: { + required: false, + type: 'boolean', + }, + group_in_series_index: { + required: false, + type: 'long', + }, }, }, }, @@ -823,6 +825,13 @@ function getAlwaysFiringAlertAsDataRuleType( return { state: {} }; }, + alerts: { + context: 'observability.test.alerts', + mappings: { + fieldMap: {}, + }, + useLegacyAlerts: true, + }, }); } diff --git a/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/execute.ts b/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/execute.ts index 6ee116d3052e2..fffe1a70b27bb 100644 --- a/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/execute.ts +++ b/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/execute.ts @@ -8,6 +8,7 @@ import expect from '@kbn/expect'; import { IValidatedEvent, nanosToMillis } from '@kbn/event-log-plugin/server'; import { ESTestIndexTool, ES_TEST_INDEX_NAME } from '@kbn/alerting-api-integration-helpers'; +import { ActionExecutionSourceType } from '@kbn/actions-plugin/server/lib/action_execution_source'; import { UserAtSpaceScenarios } from '../../../scenarios'; import { getUrlPrefix, ObjectRemover, getEventLog } from '../../../../common/lib'; import { FtrProviderContext } from '../../../../common/ftr_provider_context'; @@ -115,6 +116,7 @@ export default function ({ getService }: FtrProviderContext) { outcome: 'success', actionTypeId: 'test.index-record', message: `action executed: test.index-record:${createdAction.id}: My action`, + source: ActionExecutionSourceType.HTTP_REQUEST, }); break; default: @@ -502,10 +504,11 @@ export default function ({ getService }: FtrProviderContext) { outcome: string; message: string; errorMessage?: string; + source?: string; } async function validateEventLog(params: ValidateEventLogParams): Promise { - const { spaceId, connectorId, actionTypeId, outcome, message, errorMessage } = params; + const { spaceId, connectorId, actionTypeId, outcome, message, errorMessage, source } = params; const events: IValidatedEvent[] = await retry.try(async () => { return await getEventLog({ @@ -559,6 +562,10 @@ export default function ({ getService }: FtrProviderContext) { expect(executeEvent?.message).to.eql(message); expect(startExecuteEvent?.message).to.eql(message.replace('executed', 'started')); + if (source) { + expect(executeEvent?.kibana?.action?.execution?.source).to.eql(source.toLowerCase()); + } + if (errorMessage) { expect(executeEvent?.error?.message).to.eql(errorMessage); } diff --git a/x-pack/test/alerting_api_integration/spaces_only/tests/actions/execute.ts b/x-pack/test/alerting_api_integration/spaces_only/tests/actions/execute.ts index f66570fc2655f..cb10066a50653 100644 --- a/x-pack/test/alerting_api_integration/spaces_only/tests/actions/execute.ts +++ b/x-pack/test/alerting_api_integration/spaces_only/tests/actions/execute.ts @@ -8,6 +8,7 @@ import type { Client } from '@elastic/elasticsearch'; import expect from '@kbn/expect'; import { IValidatedEvent, nanosToMillis } from '@kbn/event-log-plugin/server'; import { ESTestIndexTool, ES_TEST_INDEX_NAME } from '@kbn/alerting-api-integration-helpers'; +import { ActionExecutionSourceType } from '@kbn/actions-plugin/server/lib/action_execution_source'; import { Spaces } from '../../scenarios'; import { getUrlPrefix, ObjectRemover, getEventLog } from '../../../common/lib'; import { FtrProviderContext } from '../../../common/ftr_provider_context'; @@ -95,6 +96,7 @@ export default function ({ getService }: FtrProviderContext) { outcome: 'success', message: `action executed: test.index-record:${createdAction.id}: My action`, startMessage: `action started: test.index-record:${createdAction.id}: My action`, + source: ActionExecutionSourceType.HTTP_REQUEST, }); }); @@ -138,6 +140,7 @@ export default function ({ getService }: FtrProviderContext) { outcome: 'failure', message: `action execution failure: test.failing:${createdAction.id}: failing action`, errorMessage: `an error occurred while running the action: expected failure for .kibana-alerting-test-data actions-failure-1:space1; retry: true`, + source: ActionExecutionSourceType.HTTP_REQUEST, }); }); @@ -336,11 +339,20 @@ export default function ({ getService }: FtrProviderContext) { message: string; errorMessage?: string; startMessage?: string; + source?: string; } async function validateEventLog(params: ValidateEventLogParams): Promise { - const { spaceId, actionId, actionTypeId, outcome, message, startMessage, errorMessage } = - params; + const { + spaceId, + actionId, + actionTypeId, + outcome, + message, + startMessage, + errorMessage, + source, + } = params; const events: IValidatedEvent[] = await retry.try(async () => { return await getEventLog({ @@ -397,6 +409,10 @@ export default function ({ getService }: FtrProviderContext) { expect(executeEvent?.kibana?.task).to.eql(undefined); + if (source) { + expect(executeEvent?.kibana?.action?.execution?.source).to.eql(source.toLowerCase()); + } + if (errorMessage) { expect(executeEvent?.error?.message).to.eql(errorMessage); } diff --git a/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/group1/event_log.ts b/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/group1/event_log.ts index ac862b55aaadc..27d4fc59d239a 100644 --- a/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/group1/event_log.ts +++ b/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/group1/event_log.ts @@ -319,6 +319,7 @@ export default function eventLogTests({ getService }: FtrProviderContext) { ruleTypeId: response.body.rule_type_id, rule: undefined, consumer: 'alertsFixture', + source: 'alert', }); break; } @@ -1138,6 +1139,7 @@ interface ValidateEventLogParams { namespace?: string; }; flapping?: boolean; + source?: string; } export function validateEvent(event: IValidatedEvent, params: ValidateEventLogParams): void { @@ -1157,6 +1159,7 @@ export function validateEvent(event: IValidatedEvent, params: ValidateEventLogPa consumer, ruleTypeId, flapping, + source, } = params; const { status, actionGroupId, instanceId, reason, shouldHaveEventEnd } = params; @@ -1210,6 +1213,10 @@ export function validateEvent(event: IValidatedEvent, params: ValidateEventLogPa expect(event?.kibana?.alert?.flapping).to.be(flapping); } + if (source) { + expect(event?.kibana?.action?.execution?.source).to.be(source); + } + expect(event?.kibana?.alert?.rule?.rule_type_id).to.be(ruleTypeId); expect(event?.kibana?.space_ids?.[0]).to.equal(spaceId); diff --git a/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/group1/get_rule_tags.ts b/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/group1/get_rule_tags.ts new file mode 100644 index 0000000000000..2ee4f15e1248a --- /dev/null +++ b/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/group1/get_rule_tags.ts @@ -0,0 +1,109 @@ +/* + * 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 expect from '@kbn/expect'; +import { Spaces } from '../../../scenarios'; +import { getUrlPrefix, getTestRuleData, ObjectRemover } from '../../../../common/lib'; +import { FtrProviderContext } from '../../../../common/ftr_provider_context'; + +const tags = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j']; + +// eslint-disable-next-line import/no-default-export +export default function createAggregateTests({ getService }: FtrProviderContext) { + const supertest = getService('supertest'); + + const createRule = async (overrides = {}) => { + const { body: createdRule } = await supertest + .post(`${getUrlPrefix(Spaces.space1.id)}/api/alerting/rule`) + .set('kbn-xsrf', 'foo') + .send(getTestRuleData(overrides)) + .expect(200); + + return createdRule.id; + }; + + describe('getRuleTags', () => { + const objectRemover = new ObjectRemover(supertest); + + afterEach(() => objectRemover.removeAll()); + + it('should get rule tags when there are no rules', async () => { + const response = await supertest.get( + `${getUrlPrefix(Spaces.space1.id)}/internal/alerting/rules/_tags` + ); + + expect(response.status).to.eql(200); + expect(response.body.rule_tags.filter((tag: string) => tag !== 'foo')).to.eql([]); + }); + + it('should get rule tags from all rules', async () => { + await Promise.all( + tags.map(async (tag) => { + const ruleId = await createRule({ tags: [tag] }); + objectRemover.add(Spaces.space1.id, ruleId, 'rule', 'alerting'); + }) + ); + + const ruleId = await createRule({ tags: ['a', 'b', 'c'] }); + objectRemover.add(Spaces.space1.id, ruleId, 'rule', 'alerting'); + + const response = await supertest.get( + `${getUrlPrefix(Spaces.space1.id)}/internal/alerting/rules/_tags` + ); + + expect(response.status).to.eql(200); + expect(response.body.rule_tags.filter((tag: string) => tag !== 'foo').sort()).to.eql( + tags.sort() + ); + }); + + it('should paginate rule tags', async () => { + await Promise.all( + tags.map(async (tag) => { + const ruleId = await createRule({ tags: [tag] }); + objectRemover.add(Spaces.space1.id, ruleId, 'rule', 'alerting'); + }) + ); + + const ruleId = await createRule({ tags: ['foo'] }); + objectRemover.add(Spaces.space1.id, ruleId, 'rule', 'alerting'); + + const response = await supertest.get( + `${getUrlPrefix(Spaces.space1.id)}/internal/alerting/rules/_tags?max_tags=5` + ); + + expect(response.status).to.eql(200); + expect(response.body.rule_tags).to.eql(tags.sort().slice(0, 5)); + + const paginatedResponse = await supertest.get( + `${getUrlPrefix( + Spaces.space1.id + )}/internal/alerting/rules/_tags?max_tags=5&after=${JSON.stringify({ + tags: 'e', + })}` + ); + + expect(paginatedResponse.status).to.eql(200); + expect(paginatedResponse.body.rule_tags).to.eql(['f', 'foo', 'g', 'h', 'i']); + }); + + it('should search rule tags', async () => { + await Promise.all( + tags.map(async (tag) => { + const ruleId = await createRule({ tags: [tag] }); + objectRemover.add(Spaces.space1.id, ruleId, 'rule', 'alerting'); + }) + ); + + const response = await supertest.get( + `${getUrlPrefix(Spaces.space1.id)}/internal/alerting/rules/_tags?search=a` + ); + + expect(response.body.rule_tags.filter((tag: string) => tag !== 'foo')).to.eql(['a']); + }); + }); +} diff --git a/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/group1/index.ts b/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/group1/index.ts index 87ef8228c7303..6dacd17642a10 100644 --- a/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/group1/index.ts +++ b/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/group1/index.ts @@ -26,6 +26,7 @@ export default function alertingTests({ loadTestFile, getService }: FtrProviderC loadTestFile(require.resolve('./get_alert_summary')); loadTestFile(require.resolve('./get_execution_log')); loadTestFile(require.resolve('./get_action_error_log')); + loadTestFile(require.resolve('./get_rule_tags')); loadTestFile(require.resolve('./rule_types')); loadTestFile(require.resolve('./event_log')); }); diff --git a/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/group4/alerts_as_data.ts b/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/group4/alerts_as_data.ts index ad2c33b079b0a..c65af87d39aa7 100644 --- a/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/group4/alerts_as_data.ts +++ b/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/group4/alerts_as_data.ts @@ -111,9 +111,9 @@ export default function createAlertsAsDataTest({ getService }: FtrProviderContex }); it('should install context specific alerts as data resources on startup', async () => { - const componentTemplateName = '.alerts-test.always-firing-mappings'; - const indexTemplateName = '.alerts-test.always-firing-default-template'; - const indexName = '.alerts-test.always-firing-default-000001'; + const componentTemplateName = '.alerts-test.always-firing.alerts-mappings'; + const indexTemplateName = '.alerts-test.always-firing.alerts-default-index-template'; + const indexName = '.internal.alerts-test.always-firing.alerts-default-000001'; const contextSpecificMappings = { instance_params_value: { type: 'boolean', @@ -139,16 +139,7 @@ export default function createAlertsAsDataTest({ getService }: FtrProviderContex dynamic: 'strict', properties: contextSpecificMappings, }); - expect(contextComponentTemplate.component_template.template.settings).to.eql({ - index: { - number_of_shards: 1, - mapping: { - total_fields: { - limit: 1500, - }, - }, - }, - }); + expect(contextComponentTemplate.component_template.template.settings).to.eql({}); const { index_templates: indexTemplates } = await es.indices.getIndexTemplate({ name: indexTemplateName, @@ -157,20 +148,25 @@ export default function createAlertsAsDataTest({ getService }: FtrProviderContex const contextIndexTemplate = indexTemplates[0]; expect(contextIndexTemplate.name).to.eql(indexTemplateName); expect(contextIndexTemplate.index_template.index_patterns).to.eql([ - '.alerts-test.always-firing-default-*', + '.internal.alerts-test.always-firing.alerts-default-*', ]); expect(contextIndexTemplate.index_template.composed_of).to.eql([ - '.alerts-test.always-firing-mappings', + '.alerts-test.always-firing.alerts-mappings', '.alerts-framework-mappings', ]); - expect(contextIndexTemplate.index_template.template!.mappings).to.eql({ - dynamic: false, - }); + expect(contextIndexTemplate.index_template.template!.mappings?.dynamic).to.eql(false); + expect(contextIndexTemplate.index_template.template!.mappings?._meta?.managed).to.eql(true); + expect(contextIndexTemplate.index_template.template!.mappings?._meta?.namespace).to.eql( + 'default' + ); + expect( + contextIndexTemplate.index_template.template!.mappings?._meta?.kibana?.version + ).to.be.a('string'); expect(contextIndexTemplate.index_template.template!.settings).to.eql({ index: { lifecycle: { name: '.alerts-ilm-policy', - rollover_alias: '.alerts-test.always-firing-default', + rollover_alias: '.alerts-test.always-firing.alerts-default', }, mapping: { total_fields: { @@ -187,22 +183,22 @@ export default function createAlertsAsDataTest({ getService }: FtrProviderContex }); expect(contextIndex[indexName].aliases).to.eql({ - '.alerts-test.always-firing-default': { + '.alerts-test.always-firing.alerts-default': { is_write_index: true, }, }); - - expect(contextIndex[indexName].mappings).to.eql({ - dynamic: 'false', - properties: { - ...contextSpecificMappings, - ...frameworkMappings.properties, - }, + expect(contextIndex[indexName].mappings?._meta?.managed).to.eql(true); + expect(contextIndex[indexName].mappings?._meta?.namespace).to.eql('default'); + expect(contextIndex[indexName].mappings?._meta?.kibana?.version).to.be.a('string'); + expect(contextIndex[indexName].mappings?.dynamic).to.eql('false'); + expect(contextIndex[indexName].mappings?.properties).to.eql({ + ...contextSpecificMappings, + ...frameworkMappings.properties, }); expect(contextIndex[indexName].settings?.index?.lifecycle).to.eql({ name: '.alerts-ilm-policy', - rollover_alias: '.alerts-test.always-firing-default', + rollover_alias: '.alerts-test.always-firing.alerts-default', }); expect(contextIndex[indexName].settings?.index?.mapping).to.eql({ @@ -215,7 +211,7 @@ export default function createAlertsAsDataTest({ getService }: FtrProviderContex expect(contextIndex[indexName].settings?.index?.number_of_shards).to.eql(1); expect(contextIndex[indexName].settings?.index?.auto_expand_replicas).to.eql('0-1'); expect(contextIndex[indexName].settings?.index?.provided_name).to.eql( - '.alerts-test.always-firing-default-000001' + '.internal.alerts-test.always-firing.alerts-default-000001' ); }); }); diff --git a/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/group4/check_registered_rule_types.ts b/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/group4/check_registered_rule_types.ts index 042db04fe4e14..101cf69fc6034 100644 --- a/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/group4/check_registered_rule_types.ts +++ b/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/group4/check_registered_rule_types.ts @@ -43,6 +43,7 @@ export default function createRegisteredRuleTypeTests({ getService }: FtrProvide 'siem.thresholdRule', 'siem.newTermsRule', 'siem.notifications', + 'slo.rules.burnRate', 'metrics.alert.anomaly', 'logs.alert.document.count', 'metrics.alert.inventory.threshold', diff --git a/x-pack/test/api_integration/apis/aiops/explain_log_rate_spikes.ts b/x-pack/test/api_integration/apis/aiops/explain_log_rate_spikes.ts index 4e4b217f939fb..420ff157ce644 100644 --- a/x-pack/test/api_integration/apis/aiops/explain_log_rate_spikes.ts +++ b/x-pack/test/api_integration/apis/aiops/explain_log_rate_spikes.ts @@ -54,12 +54,12 @@ export default ({ getService }: FtrProviderContext) => { expect(typeof d.type).to.be('string'); }); - const addChangePointsActions = data.filter( - (d) => d.type === testData.expected.changePointFilter + const addSignificantTermsActions = data.filter( + (d) => d.type === testData.expected.significantTermFilter ); - expect(addChangePointsActions.length).to.greaterThan(0); + expect(addSignificantTermsActions.length).to.greaterThan(0); - const changePoints = addChangePointsActions + const significantTerms = addSignificantTermsActions .flatMap((d) => d.payload) .sort(function (a, b) { if (a.fieldName === b.fieldName) { @@ -68,12 +68,12 @@ export default ({ getService }: FtrProviderContext) => { return a.fieldName > b.fieldName ? 1 : -1; }); - expect(changePoints.length).to.eql( - testData.expected.changePoints.length, - `Expected 'changePoints.length' to be ${testData.expected.changePoints.length}, got ${changePoints.length}.` + expect(significantTerms.length).to.eql( + testData.expected.significantTerms.length, + `Expected 'significantTerms.length' to be ${testData.expected.significantTerms.length}, got ${significantTerms.length}.` ); - changePoints.forEach((cp, index) => { - const ecp = testData.expected.changePoints[index]; + significantTerms.forEach((cp, index) => { + const ecp = testData.expected.significantTerms[index]; expect(cp.fieldName).to.eql(ecp.fieldName); expect(cp.fieldValue).to.eql(ecp.fieldValue); expect(cp.doc_count).to.eql(ecp.doc_count); @@ -82,8 +82,8 @@ export default ({ getService }: FtrProviderContext) => { const histogramActions = data.filter((d) => d.type === testData.expected.histogramFilter); const histograms = histogramActions.flatMap((d) => d.payload); - // for each change point we should get a histogram - expect(histogramActions.length).to.be(changePoints.length); + // for each significant term we should get a histogram + expect(histogramActions.length).to.be(significantTerms.length); // each histogram should have a length of 20 items. histograms.forEach((h, index) => { expect(h.histogram.length).to.be(20); @@ -101,7 +101,7 @@ export default ({ getService }: FtrProviderContext) => { (d) => d.type === testData.expected.groupHistogramFilter ); const groupHistograms = groupHistogramActions.flatMap((d) => d.payload); - // for each change point group we should get a histogram + // for each significant terms group we should get a histogram expect(groupHistograms.length).to.be(groups.length); // each histogram should have a length of 20 items. groupHistograms.forEach((h, index) => { diff --git a/x-pack/test/api_integration/apis/aiops/test_data.ts b/x-pack/test/api_integration/apis/aiops/test_data.ts index 3be75f1e875a8..fc525f3ec141e 100644 --- a/x-pack/test/api_integration/apis/aiops/test_data.ts +++ b/x-pack/test/api_integration/apis/aiops/test_data.ts @@ -8,8 +8,8 @@ // We're using the mocks for jest unit tests as expected data in the integration tests here. // This makes sure should the assertions for the integration tests need to be updated, // that also the jest unit tests use mocks that are not outdated. -import { changePoints as artificialLogChangePoints } from '@kbn/aiops-plugin/common/__mocks__/artificial_logs/change_points'; -import { finalChangePointGroups as artificialLogsChangePointGroups } from '@kbn/aiops-plugin/common/__mocks__/artificial_logs/final_change_point_groups'; +import { significantTerms as artificialLogSignificantTerms } from '@kbn/aiops-plugin/common/__mocks__/artificial_logs/significant_terms'; +import { finalSignificantTermGroups as artificialLogsSignificantTermGroups } from '@kbn/aiops-plugin/common/__mocks__/artificial_logs/final_significant_term_groups'; import type { TestData } from './types'; @@ -34,12 +34,12 @@ export const explainLogRateSpikesTestData: TestData[] = [ actionsLength: 34, noIndexChunksLength: 4, noIndexActionsLength: 3, - changePointFilter: 'add_change_points', - groupFilter: 'add_change_point_group', - groupHistogramFilter: 'add_change_point_group_histogram', - histogramFilter: 'add_change_points_histogram', + significantTermFilter: 'add_significant_terms', + groupFilter: 'add_significant_terms_group', + groupHistogramFilter: 'add_significant_terms_group_histogram', + histogramFilter: 'add_significant_terms_histogram', errorFilter: 'add_error', - changePoints: [ + significantTerms: [ { fieldName: 'day_of_week', fieldValue: 'Wednesday', @@ -87,13 +87,13 @@ export const explainLogRateSpikesTestData: TestData[] = [ actionsLength: 24, noIndexChunksLength: 4, noIndexActionsLength: 3, - changePointFilter: 'add_change_points', - groupFilter: 'add_change_point_group', - groupHistogramFilter: 'add_change_point_group_histogram', - histogramFilter: 'add_change_points_histogram', + significantTermFilter: 'add_significant_terms', + groupFilter: 'add_significant_terms_group', + groupHistogramFilter: 'add_significant_terms_group_histogram', + histogramFilter: 'add_significant_terms_histogram', errorFilter: 'add_error', - changePoints: artificialLogChangePoints, - groups: artificialLogsChangePointGroups, + significantTerms: artificialLogSignificantTerms, + groups: artificialLogsSignificantTermGroups, histogramLength: 20, }, }, diff --git a/x-pack/test/api_integration/apis/aiops/types.ts b/x-pack/test/api_integration/apis/aiops/types.ts index e19994f5e0e14..b3c6ea166eeac 100644 --- a/x-pack/test/api_integration/apis/aiops/types.ts +++ b/x-pack/test/api_integration/apis/aiops/types.ts @@ -6,7 +6,7 @@ */ import type { ApiExplainLogRateSpikes } from '@kbn/aiops-plugin/common/api'; -import type { ChangePoint, ChangePointGroup } from '@kbn/ml-agg-utils'; +import type { SignificantTerm, SignificantTermGroup } from '@kbn/ml-agg-utils'; export interface TestData { testName: string; @@ -18,13 +18,13 @@ export interface TestData { actionsLength: number; noIndexChunksLength: number; noIndexActionsLength: number; - changePointFilter: 'add_change_points'; - groupFilter: 'add_change_point_group'; - groupHistogramFilter: 'add_change_point_group_histogram'; - histogramFilter: 'add_change_points_histogram'; + significantTermFilter: 'add_significant_terms'; + groupFilter: 'add_significant_terms_group'; + groupHistogramFilter: 'add_significant_terms_group_histogram'; + histogramFilter: 'add_significant_terms_histogram'; errorFilter: 'add_error'; - changePoints: ChangePoint[]; - groups: ChangePointGroup[]; + significantTerms: SignificantTerm[]; + groups: SignificantTermGroup[]; histogramLength: number; }; } diff --git a/x-pack/test/api_integration/apis/features/features/features.ts b/x-pack/test/api_integration/apis/features/features/features.ts index 57012451eeb45..a7e7fc53c040b 100644 --- a/x-pack/test/api_integration/apis/features/features/features.ts +++ b/x-pack/test/api_integration/apis/features/features/features.ts @@ -121,6 +121,7 @@ export default function ({ getService }: FtrProviderContext) { 'rulesSettings', 'uptime', 'siem', + 'slo', 'securitySolutionCases', 'fleet', 'fleetv2', diff --git a/x-pack/test/api_integration/apis/monitoring/es_archives/logstash_8/data.json.gz b/x-pack/test/api_integration/apis/monitoring/es_archives/logstash_8/data.json.gz deleted file mode 100644 index 9d7f08e190b6e..0000000000000 Binary files a/x-pack/test/api_integration/apis/monitoring/es_archives/logstash_8/data.json.gz and /dev/null differ diff --git a/x-pack/test/api_integration/apis/monitoring/es_archives/logstash_8/mappings.json b/x-pack/test/api_integration/apis/monitoring/es_archives/logstash_8/mappings.json deleted file mode 100644 index af532355fae7a..0000000000000 --- a/x-pack/test/api_integration/apis/monitoring/es_archives/logstash_8/mappings.json +++ /dev/null @@ -1,779 +0,0 @@ -{ - "type": "data_stream", - "value": { - "data_stream": ".monitoring-logstash-8-mb", - "template": { - "data_stream": { - "allow_custom_routing": false, - "hidden": false - }, - "index_patterns": [ - ".monitoring-logstash-8-*" - ], - "name": ".monitoring-logstash-mb", - "template": { - "mappings": { - "dynamic": false, - "properties": { - "@timestamp": { - "type": "date" - }, - "cluster_uuid": { - "path": "logstash.elasticsearch.cluster.id", - "type": "alias" - }, - "ecs": { - "properties": { - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "event": { - "properties": { - "action": { - "ignore_above": 1024, - "type": "keyword" - }, - "agent_id_status": { - "ignore_above": 1024, - "type": "keyword" - }, - "category": { - "ignore_above": 1024, - "type": "keyword" - }, - "code": { - "ignore_above": 1024, - "type": "keyword" - }, - "created": { - "type": "date" - }, - "dataset": { - "ignore_above": 1024, - "type": "keyword" - }, - "duration": { - "type": "long" - }, - "end": { - "type": "date" - }, - "hash": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "ingested": { - "type": "date" - }, - "kind": { - "ignore_above": 1024, - "type": "keyword" - }, - "module": { - "ignore_above": 1024, - "type": "keyword" - }, - "original": { - "doc_values": false, - "ignore_above": 1024, - "index": false, - "type": "keyword" - }, - "outcome": { - "ignore_above": 1024, - "type": "keyword" - }, - "provider": { - "ignore_above": 1024, - "type": "keyword" - }, - "reason": { - "ignore_above": 1024, - "type": "keyword" - }, - "reference": { - "ignore_above": 1024, - "type": "keyword" - }, - "risk_score": { - "type": "float" - }, - "risk_score_norm": { - "type": "float" - }, - "sequence": { - "type": "long" - }, - "severity": { - "type": "long" - }, - "start": { - "type": "date" - }, - "timezone": { - "ignore_above": 1024, - "type": "keyword" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - }, - "url": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "host": { - "properties": { - "architecture": { - "ignore_above": 1024, - "type": "keyword" - }, - "hostname": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "logstash": { - "properties": { - "elasticsearch": { - "properties": { - "cluster": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "node": { - "properties": { - "host": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "jvm": { - "properties": { - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "state": { - "properties": { - "pipeline": { - "properties": { - "batch_size": { - "type": "long" - }, - "ephemeral_id": { - "ignore_above": 1024, - "type": "keyword" - }, - "hash": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "representation": { - "properties": { - "graph": { - "properties": { - "edges": { - "type": "object" - }, - "vertices": { - "type": "object" - } - } - }, - "hash": { - "ignore_above": 1024, - "type": "keyword" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "workers": { - "type": "long" - } - } - } - } - }, - "stats": { - "properties": { - "events": { - "properties": { - "duration_in_millis": { - "type": "long" - }, - "filtered": { - "type": "long" - }, - "in": { - "type": "long" - }, - "out": { - "type": "long" - } - } - }, - "jvm": { - "properties": { - "mem": { - "properties": { - "heap_max_in_bytes": { - "type": "long" - }, - "heap_used_in_bytes": { - "type": "long" - } - } - }, - "uptime_in_millis": { - "type": "long" - } - } - }, - "logstash": { - "properties": { - "uuid": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "os": { - "properties": { - "cgroup": { - "properties": { - "cpu": { - "properties": { - "stat": { - "properties": { - "number_of_elapsed_periods": { - "type": "long" - }, - "number_of_times_throttled": { - "type": "long" - }, - "time_throttled_nanos": { - "type": "long" - } - } - } - } - }, - "cpuacct": { - "properties": { - "usage_nanos": { - "type": "long" - } - } - } - } - }, - "cpu": { - "properties": { - "load_average": { - "properties": { - "15m": { - "type": "long" - }, - "1m": { - "type": "long" - }, - "5m": { - "type": "long" - } - } - } - } - } - } - }, - "pipelines": { - "properties": { - "ephemeral_id": { - "type": "keyword" - }, - "events": { - "properties": { - "duration_in_millis": { - "type": "long" - }, - "filtered": { - "type": "long" - }, - "in": { - "type": "long" - }, - "out": { - "type": "long" - }, - "queue_push_duration_in_millis": { - "type": "long" - } - } - }, - "hash": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "queue": { - "properties": { - "events_count": { - "type": "long" - }, - "max_queue_size_in_bytes": { - "type": "long" - }, - "queue_size_in_bytes": { - "type": "long" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "reloads": { - "properties": { - "failures": { - "type": "long" - }, - "successes": { - "type": "long" - } - } - }, - "vertices": { - "properties": { - "duration_in_millis": { - "type": "long" - }, - "events_in": { - "type": "long" - }, - "events_out": { - "type": "long" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "long_counters": { - "properties": { - "name": { - "type": "keyword" - }, - "value": { - "type": "long" - } - }, - "type": "nested" - }, - "pipeline_ephemeral_id": { - "ignore_above": 1024, - "type": "keyword" - }, - "queue_push_duration_in_millis": { - "type": "long" - } - }, - "type": "nested" - } - }, - "type": "nested" - }, - "process": { - "properties": { - "cpu": { - "properties": { - "percent": { - "type": "double" - } - } - } - } - }, - "queue": { - "properties": { - "events_count": { - "type": "long" - } - } - } - } - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "logstash_state": { - "properties": { - "pipeline": { - "properties": { - "hash": { - "path": "logstash.node.state.pipeline.hash", - "type": "alias" - }, - "id": { - "path": "logstash.node.state.pipeline.id", - "type": "alias" - } - } - } - } - }, - "logstash_stats": { - "properties": { - "events": { - "properties": { - "duration_in_millis": { - "path": "logstash.node.stats.events.duration_in_millis", - "type": "alias" - }, - "in": { - "path": "logstash.node.stats.events.in", - "type": "alias" - }, - "out": { - "path": "logstash.node.stats.events.out", - "type": "alias" - } - } - }, - "jvm": { - "properties": { - "mem": { - "properties": { - "heap_max_in_bytes": { - "path": "logstash.node.stats.jvm.mem.heap_max_in_bytes", - "type": "alias" - }, - "heap_used_in_bytes": { - "path": "logstash.node.stats.jvm.mem.heap_used_in_bytes", - "type": "alias" - } - } - }, - "uptime_in_millis": { - "path": "logstash.node.stats.jvm.uptime_in_millis", - "type": "alias" - } - } - }, - "logstash": { - "properties": { - "uuid": { - "path": "logstash.node.stats.logstash.uuid", - "type": "alias" - }, - "version": { - "path": "logstash.node.stats.logstash.version", - "type": "alias" - } - } - }, - "os": { - "properties": { - "cgroup": { - "properties": { - "cpuacct": { - "properties": { - "usage_nanos": { - "path": "logstash.node.stats.os.cgroup.cpuacct.usage_nanos", - "type": "alias" - } - } - } - } - }, - "cpu": { - "properties": { - "load_average": { - "properties": { - "15m": { - "path": "logstash.node.stats.os.cpu.load_average.15m", - "type": "alias" - }, - "1m": { - "path": "logstash.node.stats.os.cpu.load_average.1m", - "type": "alias" - }, - "5m": { - "path": "logstash.node.stats.os.cpu.load_average.5m", - "type": "alias" - } - } - }, - "stat": { - "properties": { - "number_of_elapsed_periods": { - "path": "logstash.node.stats.os.cgroup.cpu.stat.number_of_elapsed_periods", - "type": "alias" - }, - "number_of_times_throttled": { - "path": "logstash.node.stats.os.cgroup.cpu.stat.number_of_times_throttled", - "type": "alias" - }, - "time_throttled_nanos": { - "path": "logstash.node.stats.os.cgroup.cpu.stat.time_throttled_nanos", - "type": "alias" - } - } - } - } - } - } - }, - "pipelines": { - "type": "nested" - }, - "process": { - "properties": { - "cpu": { - "properties": { - "percent": { - "path": "logstash.node.stats.process.cpu.percent", - "type": "alias" - } - } - } - } - }, - "queue": { - "properties": { - "events_count": { - "path": "logstash.node.stats.queue.events_count", - "type": "alias" - } - } - }, - "timestamp": { - "path": "@timestamp", - "type": "alias" - } - } - }, - "metricset": { - "properties": { - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "period": { - "type": "long" - } - } - }, - "process": { - "properties": { - "pid": { - "type": "long" - } - } - }, - "service": { - "properties": { - "address": { - "ignore_above": 1024, - "type": "keyword" - }, - "environment": { - "ignore_above": 1024, - "type": "keyword" - }, - "ephemeral_id": { - "ignore_above": 1024, - "type": "keyword" - }, - "hostname": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "node": { - "properties": { - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "origin": { - "properties": { - "address": { - "ignore_above": 1024, - "type": "keyword" - }, - "environment": { - "ignore_above": 1024, - "type": "keyword" - }, - "ephemeral_id": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "node": { - "properties": { - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "state": { - "ignore_above": 1024, - "type": "keyword" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "state": { - "ignore_above": 1024, - "type": "keyword" - }, - "target": { - "properties": { - "address": { - "ignore_above": 1024, - "type": "keyword" - }, - "environment": { - "ignore_above": 1024, - "type": "keyword" - }, - "ephemeral_id": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "node": { - "properties": { - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "state": { - "ignore_above": 1024, - "type": "keyword" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "timestamp": { - "path": "@timestamp", - "type": "alias" - } - } - }, - "settings": { - "index": { - "lifecycle": { - "name": ".monitoring-8-ilm-policy" - }, - "mapping": { - "total_fields": { - "limit": "2000" - } - } - } - } - }, - "version": 8000101 - } - } -} \ No newline at end of file diff --git a/x-pack/test/api_integration/apis/monitoring/es_archives/logstash_package/data.json.gz b/x-pack/test/api_integration/apis/monitoring/es_archives/logstash_package/data.json.gz deleted file mode 100644 index e496354a5be28..0000000000000 Binary files a/x-pack/test/api_integration/apis/monitoring/es_archives/logstash_package/data.json.gz and /dev/null differ diff --git a/x-pack/test/api_integration/apis/monitoring/es_archives/logstash_package/mappings.json b/x-pack/test/api_integration/apis/monitoring/es_archives/logstash_package/mappings.json deleted file mode 100644 index 5d05af5111803..0000000000000 --- a/x-pack/test/api_integration/apis/monitoring/es_archives/logstash_package/mappings.json +++ /dev/null @@ -1,966 +0,0 @@ -{ - "type": "data_stream", - "value": { - "data_stream": "metrics-logstash.stack_monitoring.node-default", - "template": { - "_meta": { - "managed": true, - "managed_by": "fleet", - "package": { - "name": "logstash" - } - }, - "data_stream": { - "allow_custom_routing": false, - "hidden": false - }, - "index_patterns": [ - "metrics-logstash.stack_monitoring.node-*" - ], - "name": "metrics-logstash.stack_monitoring.node", - "priority": 200, - "template": { - "mappings": { - "_meta": { - "managed": true, - "managed_by": "fleet", - "package": { - "name": "logstash" - } - }, - "date_detection": false, - "dynamic": false, - "dynamic_templates": [ - { - "strings_as_keyword": { - "mapping": { - "ignore_above": 1024, - "type": "keyword" - }, - "match_mapping_type": "string" - } - } - ], - "properties": { - "@timestamp": { - "type": "date" - }, - "cluster_uuid": { - "path": "logstash.elasticsearch.cluster.id", - "type": "alias" - }, - "data_stream": { - "properties": { - "dataset": { - "type": "constant_keyword" - }, - "namespace": { - "type": "constant_keyword" - }, - "type": { - "type": "constant_keyword" - } - } - }, - "ecs": { - "properties": { - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "error": { - "properties": { - "message": { - "type": "match_only_text" - } - } - }, - "event": { - "properties": { - "agent_id_status": { - "ignore_above": 1024, - "type": "keyword" - }, - "dataset": { - "ignore_above": 1024, - "type": "keyword" - }, - "duration": { - "type": "long" - }, - "ingested": { - "format": "strict_date_time_no_millis||strict_date_optional_time||epoch_millis", - "type": "date" - }, - "module": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "host": { - "properties": { - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "logstash": { - "properties": { - "cluster": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "elasticsearch": { - "properties": { - "cluster": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "node": { - "properties": { - "host": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "jvm": { - "properties": { - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "state": { - "properties": { - "pipeline": { - "properties": { - "batch_size": { - "type": "long" - }, - "ephemeral_id": { - "ignore_above": 1024, - "type": "keyword" - }, - "hash": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "representation": { - "properties": { - "graph": { - "properties": { - "edges": { - "type": "object" - }, - "vertices": { - "type": "object" - } - } - }, - "hash": { - "ignore_above": 1024, - "type": "keyword" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "workers": { - "type": "long" - } - } - } - } - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "logstash_state": { - "properties": { - "pipeline": { - "properties": { - "hash": { - "path": "logstash.node.state.pipeline.hash", - "type": "alias" - }, - "id": { - "path": "logstash.node.state.pipeline.id", - "type": "alias" - } - } - } - } - }, - "process": { - "properties": { - "pid": { - "type": "long" - } - } - }, - "service": { - "properties": { - "address": { - "ignore_above": 1024, - "type": "keyword" - }, - "hostname": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "timestamp": { - "path": "@timestamp", - "type": "alias" - } - } - }, - "settings": { - "index": { - "codec": "best_compression", - "lifecycle": { - "name": "metrics" - }, - "mapping": { - "total_fields": { - "limit": "10000" - } - }, - "query": { - "default_field": [ - "service.hostname", - "service.id", - "service.type", - "service.version", - "service.address", - "service.name", - "ecs.version", - "event.dataset", - "event.module", - "host.name", - "error.message", - "logstash.cluster.id", - "logstash.elasticsearch.cluster.id", - "logstash.node.jvm.version", - "logstash.node.host", - "logstash.node.version", - "logstash.node.id", - "logstash.node.state.pipeline.id", - "logstash.node.state.pipeline.hash", - "logstash.node.state.pipeline.ephemeral_id", - "logstash.node.state.pipeline.representation.hash", - "logstash.node.state.pipeline.representation.type", - "logstash.node.state.pipeline.representation.version" - ] - } - } - } - } - } - } -} - -{ - "type": "data_stream", - "value": { - "data_stream": "metrics-logstash.stack_monitoring.node_stats-default", - "template": { - "_meta": { - "managed": true, - "managed_by": "fleet", - "package": { - "name": "logstash" - } - }, - "data_stream": { - "allow_custom_routing": false, - "hidden": false - }, - "index_patterns": [ - "metrics-logstash.stack_monitoring.node_stats-*" - ], - "name": "metrics-logstash.stack_monitoring.node_stats", - "priority": 200, - "template": { - "mappings": { - "_meta": { - "managed": true, - "managed_by": "fleet", - "package": { - "name": "logstash" - } - }, - "date_detection": false, - "dynamic": false, - "dynamic_templates": [ - { - "strings_as_keyword": { - "mapping": { - "ignore_above": 1024, - "type": "keyword" - }, - "match_mapping_type": "string" - } - } - ], - "properties": { - "@timestamp": { - "type": "date" - }, - "cluster_uuid": { - "path": "logstash.elasticsearch.cluster.id", - "type": "alias" - }, - "data_stream": { - "properties": { - "dataset": { - "type": "constant_keyword" - }, - "namespace": { - "type": "constant_keyword" - }, - "type": { - "type": "constant_keyword" - } - } - }, - "ecs": { - "properties": { - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "error": { - "properties": { - "message": { - "type": "match_only_text" - } - } - }, - "event": { - "properties": { - "agent_id_status": { - "ignore_above": 1024, - "type": "keyword" - }, - "dataset": { - "ignore_above": 1024, - "type": "keyword" - }, - "duration": { - "type": "long" - }, - "ingested": { - "format": "strict_date_time_no_millis||strict_date_optional_time||epoch_millis", - "type": "date" - }, - "module": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "host": { - "properties": { - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "logstash": { - "properties": { - "cluster": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "elasticsearch": { - "properties": { - "cluster": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "node": { - "properties": { - "state": { - "properties": { - "pipeline": { - "properties": { - "hash": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "stats": { - "properties": { - "events": { - "properties": { - "duration_in_millis": { - "type": "long" - }, - "filtered": { - "type": "long" - }, - "in": { - "type": "long" - }, - "out": { - "type": "long" - } - } - }, - "jvm": { - "properties": { - "gc": { - "properties": { - "collectors": { - "properties": { - "old": { - "properties": { - "collection_count": { - "type": "long" - }, - "collection_time_in_millis": { - "type": "long" - } - } - }, - "young": { - "properties": { - "collection_count": { - "type": "long" - }, - "collection_time_in_millis": { - "type": "long" - } - } - } - } - } - } - }, - "mem": { - "properties": { - "heap_max_in_bytes": { - "type": "long" - }, - "heap_used_in_bytes": { - "type": "long" - }, - "heap_used_percent": { - "type": "long" - } - } - }, - "uptime_in_millis": { - "type": "long" - } - } - }, - "logstash": { - "properties": { - "ephemeral_id": { - "ignore_above": 1024, - "type": "keyword" - }, - "host": { - "ignore_above": 1024, - "type": "keyword" - }, - "http_address": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "pipeline": { - "properties": { - "batch_size": { - "type": "long" - }, - "workers": { - "type": "long" - } - } - }, - "snapshot": { - "type": "boolean" - }, - "status": { - "ignore_above": 1024, - "type": "keyword" - }, - "uuid": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "os": { - "properties": { - "cgroup": { - "properties": { - "cpu": { - "properties": { - "cfs_quota_micros": { - "type": "long" - }, - "control_group": { - "type": "text" - }, - "stat": { - "type": "object" - } - } - }, - "cpuacct": { - "type": "object" - } - } - }, - "cpu": { - "properties": { - "load_average": { - "properties": { - "15m": { - "type": "half_float" - }, - "1m": { - "type": "half_float" - }, - "5m": { - "type": "half_float" - } - } - }, - "percent": { - "type": "double" - } - } - } - } - }, - "pipelines": { - "properties": { - "ephemeral_id": { - "ignore_above": 1024, - "type": "keyword" - }, - "events": { - "properties": { - "duration_in_millis": { - "type": "long" - }, - "filtered": { - "type": "long" - }, - "in": { - "type": "long" - }, - "out": { - "type": "long" - }, - "queue_push_duration_in_millis": { - "type": "long" - } - } - }, - "hash": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "queue": { - "properties": { - "events_count": { - "type": "long" - }, - "max_queue_size_in_bytes": { - "type": "long" - }, - "queue_size_in_bytes": { - "type": "long" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "reloads": { - "properties": { - "failures": { - "type": "long" - }, - "successes": { - "type": "long" - } - } - }, - "vertices": { - "properties": { - "duration_in_millis": { - "type": "long" - }, - "events_in": { - "type": "long" - }, - "events_out": { - "type": "long" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "long_counters": { - "properties": { - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "value": { - "type": "long" - } - }, - "type": "nested" - }, - "pipeline_ephemeral_id": { - "ignore_above": 1024, - "type": "keyword" - }, - "queue_push_duration_in_millis": { - "type": "long" - } - }, - "type": "nested" - } - }, - "type": "nested" - }, - "process": { - "properties": { - "cpu": { - "properties": { - "percent": { - "type": "double" - } - } - }, - "max_file_descriptors": { - "type": "long" - }, - "open_file_descriptors": { - "type": "long" - } - } - }, - "queue": { - "properties": { - "events_count": { - "type": "long" - } - } - }, - "reloads": { - "properties": { - "failures": { - "type": "long" - }, - "successes": { - "type": "long" - } - } - }, - "timestamp": { - "type": "date" - } - } - } - } - } - } - }, - "logstash_stats": { - "properties": { - "events": { - "properties": { - "duration_in_millis": { - "path": "logstash.node.stats.events.duration_in_millis", - "type": "alias" - }, - "in": { - "path": "logstash.node.stats.events.in", - "type": "alias" - }, - "out": { - "path": "logstash.node.stats.events.out", - "type": "alias" - } - } - }, - "jvm": { - "properties": { - "mem": { - "properties": { - "heap_max_in_bytes": { - "path": "logstash.node.stats.jvm.mem.heap_max_in_bytes", - "type": "alias" - }, - "heap_used_in_bytes": { - "path": "logstash.node.stats.jvm.mem.heap_used_in_bytes", - "type": "alias" - } - } - }, - "uptime_in_millis": { - "path": "logstash.node.stats.jvm.uptime_in_millis", - "type": "alias" - } - } - }, - "logstash": { - "properties": { - "uuid": { - "path": "logstash.node.stats.logstash.uuid", - "type": "alias" - }, - "version": { - "path": "logstash.node.stats.logstash.version", - "type": "alias" - } - } - }, - "os": { - "properties": { - "cgroup": { - "properties": { - "cpuacct": { - "type": "object" - } - } - }, - "cpu": { - "properties": { - "load_average": { - "properties": { - "15m": { - "path": "logstash.node.stats.os.cpu.load_average.15m", - "type": "alias" - }, - "1m": { - "path": "logstash.node.stats.os.cpu.load_average.1m", - "type": "alias" - }, - "5m": { - "path": "logstash.node.stats.os.cpu.load_average.5m", - "type": "alias" - } - } - }, - "stat": { - "type": "object" - } - } - } - } - }, - "pipelines": { - "type": "nested" - }, - "process": { - "properties": { - "cpu": { - "properties": { - "percent": { - "path": "logstash.node.stats.process.cpu.percent", - "type": "alias" - } - } - } - } - }, - "queue": { - "properties": { - "events_count": { - "path": "logstash.node.stats.queue.events_count", - "type": "alias" - } - } - }, - "timestamp": { - "path": "@timestamp", - "type": "alias" - } - } - }, - "process": { - "properties": { - "pid": { - "type": "long" - } - } - }, - "service": { - "properties": { - "address": { - "ignore_above": 1024, - "type": "keyword" - }, - "hostname": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "timestamp": { - "path": "@timestamp", - "type": "alias" - } - } - }, - "settings": { - "index": { - "codec": "best_compression", - "lifecycle": { - "name": "metrics" - }, - "mapping": { - "total_fields": { - "limit": "10000" - } - }, - "query": { - "default_field": [ - "service.hostname", - "service.id", - "service.type", - "service.version", - "service.address", - "service.name", - "ecs.version", - "event.dataset", - "event.module", - "host.name", - "error.message", - "logstash.elasticsearch.cluster.id", - "logstash.node.state.pipeline.id", - "logstash.node.state.pipeline.hash", - "logstash.node.stats.logstash.uuid", - "logstash.node.stats.logstash.version", - "logstash.node.stats.logstash.ephemeral_id", - "logstash.node.stats.logstash.host", - "logstash.node.stats.logstash.http_address", - "logstash.node.stats.logstash.name", - "logstash.node.stats.logstash.status", - "logstash.node.stats.os.cgroup.cpuacct.control_group", - "logstash.node.stats.os.cgroup.cpu.control_group", - "logstash.node.stats.pipelines.id", - "logstash.node.stats.pipelines.hash", - "logstash.node.stats.pipelines.ephemeral_id", - "logstash.node.stats.pipelines.queue.type", - "logstash.node.stats.pipelines.vertices.id", - "logstash.node.stats.pipelines.vertices.long_counters.name", - "logstash.node.stats.pipelines.vertices.pipeline_ephemeral_id", - "logstash.cluster.id" - ] - } - } - } - } - } - } -} diff --git a/x-pack/test/api_integration/apis/monitoring/logstash/fixtures/node_detail_8.json b/x-pack/test/api_integration/apis/monitoring/logstash/fixtures/node_detail_8.json deleted file mode 100644 index fde54ff12067a..0000000000000 --- a/x-pack/test/api_integration/apis/monitoring/logstash/fixtures/node_detail_8.json +++ /dev/null @@ -1,1523 +0,0 @@ -{ - "metrics": { - "logstash_os_load": [ - { - "bucket_size": "10 seconds", - "timeRange": { - "min": 1655471940000, - "max": 1655472300000 - }, - "metric": { - "app": "logstash", - "field": "logstash_stats.os.cpu.load_average.1m", - "metricAgg": "max", - "label": "1m", - "title": "System Load", - "description": "Load average over the last minute.", - "units": "", - "format": "0,0.[00]", - "hasCalculation": false, - "isDerivative": false - }, - "data": [ - [ - 1655471940000, - 3.84765625 - ], - [ - 1655471950000, - 3.703125 - ], - [ - 1655471960000, - 3.44140625 - ], - [ - 1655471970000, - 3.072265625 - ], - [ - 1655471980000, - 2.75390625 - ], - [ - 1655471990000, - 2.802734375 - ], - [ - 1655472000000, - 2.826171875 - ], - [ - 1655472010000, - 2.69921875 - ], - [ - 1655472020000, - 2.603515625 - ], - [ - 1655472030000, - 2.658203125 - ], - [ - 1655472040000, - 2.40234375 - ], - [ - 1655472050000, - 2.78125 - ], - [ - 1655472060000, - 2.5078125 - ], - [ - 1655472070000, - 2.201171875 - ], - [ - 1655472080000, - 2.08984375 - ], - [ - 1655472090000, - 2.08203125 - ], - [ - 1655472100000, - 2.0625 - ], - [ - 1655472110000, - 2.05859375 - ], - [ - 1655472120000, - 1.8154296875 - ], - [ - 1655472130000, - 1.9296875 - ], - [ - 1655472140000, - 1.712890625 - ], - [ - 1655472150000, - 1.990234375 - ], - [ - 1655472160000, - 1.837890625 - ], - [ - 1655472170000, - 2.390625 - ], - [ - 1655472180000, - 2.484375 - ], - [ - 1655472190000, - 2.5625 - ], - [ - 1655472200000, - 2.2421875 - ], - [ - 1655472210000, - 2.4453125 - ], - [ - 1655472220000, - 2.142578125 - ], - [ - 1655472230000, - 2.568359375 - ], - [ - 1655472240000, - 3.408203125 - ], - [ - 1655472250000, - 3.271484375 - ], - [ - 1655472260000, - 2.99609375 - ], - [ - 1655472270000, - 2.85546875 - ], - [ - 1655472280000, - 2.796875 - ], - [ - 1655472290000, - 2.75390625 - ] - ] - }, - { - "bucket_size": "10 seconds", - "timeRange": { - "min": 1655471940000, - "max": 1655472300000 - }, - "metric": { - "app": "logstash", - "field": "logstash_stats.os.cpu.load_average.5m", - "metricAgg": "max", - "label": "5m", - "title": "System Load", - "description": "Load average over the last 5 minutes.", - "units": "", - "format": "0,0.[00]", - "hasCalculation": false, - "isDerivative": false - }, - "data": [ - [ - 1655471940000, - null - ], - [ - 1655471950000, - null - ], - [ - 1655471960000, - null - ], - [ - 1655471970000, - null - ], - [ - 1655471980000, - null - ], - [ - 1655471990000, - null - ], - [ - 1655472000000, - null - ], - [ - 1655472010000, - null - ], - [ - 1655472020000, - null - ], - [ - 1655472030000, - null - ], - [ - 1655472040000, - null - ], - [ - 1655472050000, - null - ], - [ - 1655472060000, - null - ], - [ - 1655472070000, - null - ], - [ - 1655472080000, - null - ], - [ - 1655472090000, - null - ], - [ - 1655472100000, - null - ], - [ - 1655472110000, - null - ], - [ - 1655472120000, - null - ], - [ - 1655472130000, - null - ], - [ - 1655472140000, - null - ], - [ - 1655472150000, - null - ], - [ - 1655472160000, - null - ], - [ - 1655472170000, - null - ], - [ - 1655472180000, - null - ], - [ - 1655472190000, - null - ], - [ - 1655472200000, - null - ], - [ - 1655472210000, - null - ], - [ - 1655472220000, - null - ], - [ - 1655472230000, - null - ], - [ - 1655472240000, - null - ], - [ - 1655472250000, - null - ], - [ - 1655472260000, - null - ], - [ - 1655472270000, - null - ], - [ - 1655472280000, - null - ], - [ - 1655472290000, - null - ] - ] - }, - { - "bucket_size": "10 seconds", - "timeRange": { - "min": 1655471940000, - "max": 1655472300000 - }, - "metric": { - "app": "logstash", - "field": "logstash_stats.os.cpu.load_average.15m", - "metricAgg": "max", - "label": "15m", - "title": "System Load", - "description": "Load average over the last 15 minutes.", - "units": "", - "format": "0,0.[00]", - "hasCalculation": false, - "isDerivative": false - }, - "data": [ - [ - 1655471940000, - null - ], - [ - 1655471950000, - null - ], - [ - 1655471960000, - null - ], - [ - 1655471970000, - null - ], - [ - 1655471980000, - null - ], - [ - 1655471990000, - null - ], - [ - 1655472000000, - null - ], - [ - 1655472010000, - null - ], - [ - 1655472020000, - null - ], - [ - 1655472030000, - null - ], - [ - 1655472040000, - null - ], - [ - 1655472050000, - null - ], - [ - 1655472060000, - null - ], - [ - 1655472070000, - null - ], - [ - 1655472080000, - null - ], - [ - 1655472090000, - null - ], - [ - 1655472100000, - null - ], - [ - 1655472110000, - null - ], - [ - 1655472120000, - null - ], - [ - 1655472130000, - null - ], - [ - 1655472140000, - null - ], - [ - 1655472150000, - null - ], - [ - 1655472160000, - null - ], - [ - 1655472170000, - null - ], - [ - 1655472180000, - null - ], - [ - 1655472190000, - null - ], - [ - 1655472200000, - null - ], - [ - 1655472210000, - null - ], - [ - 1655472220000, - null - ], - [ - 1655472230000, - null - ], - [ - 1655472240000, - null - ], - [ - 1655472250000, - null - ], - [ - 1655472260000, - null - ], - [ - 1655472270000, - null - ], - [ - 1655472280000, - null - ], - [ - 1655472290000, - null - ] - ] - } - ], - "logstash_events_input_rate": [ - { - "bucket_size": "10 seconds", - "timeRange": { - "min": 1655471940000, - "max": 1655472300000 - }, - "metric": { - "app": "logstash", - "field": "logstash_stats.events.in", - "metricAgg": "max", - "label": "Events Received Rate", - "description": "Number of events received per second by the Logstash node at the inputs stage.", - "units": "/s", - "format": "0,0.[00]", - "hasCalculation": false, - "isDerivative": true - }, - "data": [ - [ - 1655471940000, - 10 - ], - [ - 1655471950000, - 10 - ], - [ - 1655471960000, - 10 - ], - [ - 1655471970000, - 10 - ], - [ - 1655471980000, - 10 - ], - [ - 1655471990000, - 10 - ], - [ - 1655472000000, - 10 - ], - [ - 1655472010000, - 10 - ], - [ - 1655472020000, - 10 - ], - [ - 1655472030000, - 10 - ], - [ - 1655472040000, - 10 - ], - [ - 1655472050000, - 10 - ], - [ - 1655472060000, - 10 - ], - [ - 1655472070000, - 10 - ], - [ - 1655472080000, - 10 - ], - [ - 1655472090000, - 10 - ], - [ - 1655472100000, - 10 - ], - [ - 1655472110000, - 10 - ], - [ - 1655472120000, - 10 - ], - [ - 1655472130000, - 10 - ], - [ - 1655472140000, - 10 - ], - [ - 1655472150000, - 10 - ], - [ - 1655472160000, - 10 - ], - [ - 1655472170000, - 10 - ], - [ - 1655472180000, - 10 - ], - [ - 1655472190000, - 10 - ], - [ - 1655472200000, - 10 - ], - [ - 1655472210000, - 10 - ], - [ - 1655472220000, - 10 - ], - [ - 1655472230000, - 10 - ], - [ - 1655472240000, - 10 - ], - [ - 1655472250000, - 10 - ], - [ - 1655472260000, - 10 - ], - [ - 1655472270000, - 10 - ], - [ - 1655472280000, - 10 - ], - [ - 1655472290000, - 10 - ] - ] - } - ], - "logstash_events_output_rate": [ - { - "bucket_size": "10 seconds", - "timeRange": { - "min": 1655471940000, - "max": 1655472300000 - }, - "metric": { - "app": "logstash", - "field": "logstash_stats.events.out", - "metricAgg": "max", - "label": "Events Emitted Rate", - "description": "Number of events emitted per second by the Logstash node at the outputs stage.", - "units": "/s", - "format": "0,0.[00]", - "hasCalculation": false, - "isDerivative": true - }, - "data": [ - [ - 1655471940000, - 7 - ], - [ - 1655471950000, - 4.3 - ], - [ - 1655471960000, - 7.3 - ], - [ - 1655471970000, - 8.2 - ], - [ - 1655471980000, - 6 - ], - [ - 1655471990000, - 3.7 - ], - [ - 1655472000000, - 10.3 - ], - [ - 1655472010000, - 10.5 - ], - [ - 1655472020000, - 6.2 - ], - [ - 1655472030000, - 4.4 - ], - [ - 1655472040000, - 2.1 - ], - [ - 1655472050000, - 10.8 - ], - [ - 1655472060000, - 12.9 - ], - [ - 1655472070000, - 4.4 - ], - [ - 1655472080000, - 0 - ], - [ - 1655472090000, - 5.9 - ], - [ - 1655472100000, - 10.4 - ], - [ - 1655472110000, - 10.8 - ], - [ - 1655472120000, - 0.7 - ], - [ - 1655472130000, - 5.6 - ], - [ - 1655472140000, - 8.3 - ], - [ - 1655472150000, - 9.3 - ], - [ - 1655472160000, - 10.7 - ], - [ - 1655472170000, - 1.3 - ], - [ - 1655472180000, - 4.3 - ], - [ - 1655472190000, - 13.7 - ], - [ - 1655472200000, - 13.1 - ], - [ - 1655472210000, - 4.2 - ], - [ - 1655472220000, - 0 - ], - [ - 1655472230000, - 3.7 - ], - [ - 1655472240000, - 10.2 - ], - [ - 1655472250000, - 0 - ], - [ - 1655472260000, - 8.2 - ], - [ - 1655472270000, - 5.9 - ], - [ - 1655472280000, - 8.8 - ], - [ - 1655472290000, - 2.5 - ] - ] - } - ], - "logstash_events_latency": [ - { - "bucket_size": "10 seconds", - "timeRange": { - "min": 1655471940000, - "max": 1655472300000 - }, - "metric": { - "app": "logstash", - "field": "logstash_stats.events.out", - "metricAgg": "sum", - "label": "Event Latency", - "description": "Average time spent by events in the filter and output stages, which is the total time it takes to process events divided by number of events emitted.", - "units": "ms", - "format": "0,0.[00]", - "hasCalculation": true, - "isDerivative": false - }, - "data": [ - [ - 1655471940000, - null - ], - [ - 1655471950000, - 2003.279069767442 - ], - [ - 1655471960000, - 2003.150684931507 - ], - [ - 1655471970000, - 1978.9146341463418 - ], - [ - 1655471980000, - 1969.45 - ], - [ - 1655471990000, - 2003.4864864864862 - ], - [ - 1655472000000, - 2002.9708737864075 - ], - [ - 1655472010000, - 1984.1809523809525 - ], - [ - 1655472020000, - 1970.5483870967741 - ], - [ - 1655472030000, - 2002.7954545454543 - ], - [ - 1655472040000, - 2002.6666666666667 - ], - [ - 1655472050000, - 2002.9629629629628 - ], - [ - 1655472060000, - 2002.7519379844962 - ], - [ - 1655472070000, - 2002.931818181818 - ], - [ - 1655472080000, - 0 - ], - [ - 1655472090000, - 2002.6440677966102 - ], - [ - 1655472100000, - 1983.9326923076924 - ], - [ - 1655472110000, - 2002.8425925925926 - ], - [ - 1655472120000, - 2003.4285714285718 - ], - [ - 1655472130000, - 2002.964285714286 - ], - [ - 1655472140000, - 2002.9518072289154 - ], - [ - 1655472150000, - 2002.9462365591398 - ], - [ - 1655472160000, - 2003.0093457943926 - ], - [ - 1655472170000, - 2002.5384615384617 - ], - [ - 1655472180000, - 2002.7906976744187 - ], - [ - 1655472190000, - 1973.7956204379564 - ], - [ - 1655472200000, - 1957.0381679389313 - ], - [ - 1655472210000, - 2002.6666666666667 - ], - [ - 1655472220000, - 0 - ], - [ - 1655472230000, - 2003.2432432432431 - ], - [ - 1655472240000, - 2003.029411764706 - ], - [ - 1655472250000, - 0 - ], - [ - 1655472260000, - 2003.0243902439024 - ], - [ - 1655472270000, - 2003.0338983050847 - ], - [ - 1655472280000, - 2002.9772727272727 - ], - [ - 1655472290000, - 2002.6799999999998 - ] - ] - } - ], - "logstash_node_cpu_metric": [ - { - "bucket_size": "10 seconds", - "timeRange": { - "min": 1655471940000, - "max": 1655472300000 - }, - "metric": { - "app": "logstash", - "field": "logstash_stats.process.cpu.percent", - "metricAgg": "max", - "label": "CPU Utilization", - "description": "Percentage of CPU usage reported by the OS (100% is the max).", - "units": "%", - "format": "0,0.[00]", - "hasCalculation": false, - "isDerivative": false - }, - "data": [ - [ - 1655471940000, - 0 - ], - [ - 1655471950000, - 0 - ], - [ - 1655471960000, - 0 - ], - [ - 1655471970000, - 0 - ], - [ - 1655471980000, - 0 - ], - [ - 1655471990000, - 0 - ], - [ - 1655472000000, - 0 - ], - [ - 1655472010000, - 0 - ], - [ - 1655472020000, - 0 - ], - [ - 1655472030000, - 0 - ], - [ - 1655472040000, - 0 - ], - [ - 1655472050000, - 0 - ], - [ - 1655472060000, - 0 - ], - [ - 1655472070000, - 0 - ], - [ - 1655472080000, - 0 - ], - [ - 1655472090000, - 0 - ], - [ - 1655472100000, - 0 - ], - [ - 1655472110000, - 0 - ], - [ - 1655472120000, - 0 - ], - [ - 1655472130000, - 0 - ], - [ - 1655472140000, - 0 - ], - [ - 1655472150000, - 0 - ], - [ - 1655472160000, - 0 - ], - [ - 1655472170000, - 0 - ], - [ - 1655472180000, - 0 - ], - [ - 1655472190000, - 0 - ], - [ - 1655472200000, - 0 - ], - [ - 1655472210000, - 0 - ], - [ - 1655472220000, - 0 - ], - [ - 1655472230000, - 0 - ], - [ - 1655472240000, - 0 - ], - [ - 1655472250000, - 0 - ], - [ - 1655472260000, - 0 - ], - [ - 1655472270000, - 0 - ], - [ - 1655472280000, - 0 - ], - [ - 1655472290000, - 0 - ] - ] - } - ], - "logstash_jvm_usage": [ - { - "bucket_size": "10 seconds", - "timeRange": { - "min": 1655471940000, - "max": 1655472300000 - }, - "metric": { - "app": "logstash", - "field": "logstash_stats.jvm.mem.heap_max_in_bytes", - "metricAgg": "max", - "label": "Max Heap", - "title": "JVM Heap", - "description": "Total heap available to Logstash running in the JVM.", - "units": "B", - "format": "0.0 b", - "hasCalculation": false, - "isDerivative": false - }, - "data": [ - [ - 1655471940000, - 1037959168 - ], - [ - 1655471950000, - 1037959168 - ], - [ - 1655471960000, - 1037959168 - ], - [ - 1655471970000, - 1037959168 - ], - [ - 1655471980000, - 1037959168 - ], - [ - 1655471990000, - 1037959168 - ], - [ - 1655472000000, - 1037959168 - ], - [ - 1655472010000, - 1037959168 - ], - [ - 1655472020000, - 1037959168 - ], - [ - 1655472030000, - 1037959168 - ], - [ - 1655472040000, - 1037959168 - ], - [ - 1655472050000, - 1037959168 - ], - [ - 1655472060000, - 1037959168 - ], - [ - 1655472070000, - 1037959168 - ], - [ - 1655472080000, - 1037959168 - ], - [ - 1655472090000, - 1037959168 - ], - [ - 1655472100000, - 1037959168 - ], - [ - 1655472110000, - 1037959168 - ], - [ - 1655472120000, - 1037959168 - ], - [ - 1655472130000, - 1037959168 - ], - [ - 1655472140000, - 1037959168 - ], - [ - 1655472150000, - 1037959168 - ], - [ - 1655472160000, - 1037959168 - ], - [ - 1655472170000, - 1037959168 - ], - [ - 1655472180000, - 1037959168 - ], - [ - 1655472190000, - 1037959168 - ], - [ - 1655472200000, - 1037959168 - ], - [ - 1655472210000, - 1037959168 - ], - [ - 1655472220000, - 1037959168 - ], - [ - 1655472230000, - 1037959168 - ], - [ - 1655472240000, - 1037959168 - ], - [ - 1655472250000, - 1037959168 - ], - [ - 1655472260000, - 1037959168 - ], - [ - 1655472270000, - 1037959168 - ], - [ - 1655472280000, - 1037959168 - ], - [ - 1655472290000, - 1037959168 - ] - ] - }, - { - "bucket_size": "10 seconds", - "timeRange": { - "min": 1655471940000, - "max": 1655472300000 - }, - "metric": { - "app": "logstash", - "field": "logstash_stats.jvm.mem.heap_used_in_bytes", - "metricAgg": "max", - "label": "Used Heap", - "title": "JVM Heap", - "description": "Total heap used by Logstash running in the JVM.", - "units": "B", - "format": "0.0 b", - "hasCalculation": false, - "isDerivative": false - }, - "data": [ - [ - 1655471940000, - 248983808 - ], - [ - 1655471950000, - 254463104 - ], - [ - 1655471960000, - 260278488 - ], - [ - 1655471970000, - 265273976 - ], - [ - 1655471980000, - 269846232 - ], - [ - 1655471990000, - 274251248 - ], - [ - 1655472000000, - 278416256 - ], - [ - 1655472010000, - 282938928 - ], - [ - 1655472020000, - 287769440 - ], - [ - 1655472030000, - 292220848 - ], - [ - 1655472040000, - 295623816 - ], - [ - 1655472050000, - 299332480 - ], - [ - 1655472060000, - 305022864 - ], - [ - 1655472070000, - 310001816 - ], - [ - 1655472080000, - 312599256 - ], - [ - 1655472090000, - 317015008 - ], - [ - 1655472100000, - 320427488 - ], - [ - 1655472110000, - 325544640 - ], - [ - 1655472120000, - 329544016 - ], - [ - 1655472130000, - 332205376 - ], - [ - 1655472140000, - 337552416 - ], - [ - 1655472150000, - 341823888 - ], - [ - 1655472160000, - 345948696 - ], - [ - 1655472170000, - 348979208 - ], - [ - 1655472180000, - 353352704 - ], - [ - 1655472190000, - 358819752 - ], - [ - 1655472200000, - 363752360 - ], - [ - 1655472210000, - 367726528 - ], - [ - 1655472220000, - 371043320 - ], - [ - 1655472230000, - 374659232 - ], - [ - 1655472240000, - 379147672 - ], - [ - 1655472250000, - 383031600 - ], - [ - 1655472260000, - 392741408 - ], - [ - 1655472270000, - 395632912 - ], - [ - 1655472280000, - 400452624 - ], - [ - 1655472290000, - 405401144 - ] - ] - } - ] - }, - "nodeSummary": { - "ephemeral_id": "88c01586-1891-4a0e-a2ca-ca5c988f8cbb", - "host": "kevins-macbook-pro.home", - "http_address": "127.0.0.1:9600", - "name": "kevins-macbook-pro.home", - "pipeline": { - "batch_size": 125, - "workers": 16 - }, - "snapshot": false, - "status": "green", - "uuid": "f9efd237-3bbf-4a9b-9ce7-a16141b9d981", - "version": "8.2.2", - "availability": false, - "events": { - "duration_in_millis": 5145881, - "filtered": 2579, - "in": 3978, - "out": 2579 - }, - "reloads": { - "failures": 0, - "successes": 0 - }, - "uptime": 407358 - } -} diff --git a/x-pack/test/api_integration/apis/monitoring/logstash/fixtures/node_detail_advanced_8.json b/x-pack/test/api_integration/apis/monitoring/logstash/fixtures/node_detail_advanced_8.json deleted file mode 100644 index 3a3e619f065c0..0000000000000 --- a/x-pack/test/api_integration/apis/monitoring/logstash/fixtures/node_detail_advanced_8.json +++ /dev/null @@ -1,1521 +0,0 @@ -{ - "metrics": { - "logstash_node_cpu_utilization": [ - { - "bucket_size": "10 seconds", - "timeRange": { - "min": 1655471940000, - "max": 1655472300000 - }, - "metric": { - "app": "logstash", - "field": "logstash_stats.process.cpu.percent", - "metricAgg": "max", - "label": "CPU Utilization", - "description": "Percentage of CPU usage reported by the OS (100% is the max).", - "units": "%", - "format": "0,0.[00]", - "hasCalculation": false, - "isDerivative": false - }, - "data": [ - [ - 1655471940000, - 0 - ], - [ - 1655471950000, - 0 - ], - [ - 1655471960000, - 0 - ], - [ - 1655471970000, - 0 - ], - [ - 1655471980000, - 0 - ], - [ - 1655471990000, - 0 - ], - [ - 1655472000000, - 0 - ], - [ - 1655472010000, - 0 - ], - [ - 1655472020000, - 0 - ], - [ - 1655472030000, - 0 - ], - [ - 1655472040000, - 0 - ], - [ - 1655472050000, - 0 - ], - [ - 1655472060000, - 0 - ], - [ - 1655472070000, - 0 - ], - [ - 1655472080000, - 0 - ], - [ - 1655472090000, - 0 - ], - [ - 1655472100000, - 0 - ], - [ - 1655472110000, - 0 - ], - [ - 1655472120000, - 0 - ], - [ - 1655472130000, - 0 - ], - [ - 1655472140000, - 0 - ], - [ - 1655472150000, - 0 - ], - [ - 1655472160000, - 0 - ], - [ - 1655472170000, - 0 - ], - [ - 1655472180000, - 0 - ], - [ - 1655472190000, - 0 - ], - [ - 1655472200000, - 0 - ], - [ - 1655472210000, - 0 - ], - [ - 1655472220000, - 0 - ], - [ - 1655472230000, - 0 - ], - [ - 1655472240000, - 0 - ], - [ - 1655472250000, - 0 - ], - [ - 1655472260000, - 0 - ], - [ - 1655472270000, - 0 - ], - [ - 1655472280000, - 0 - ], - [ - 1655472290000, - 0 - ] - ] - }, - { - "bucket_size": "10 seconds", - "timeRange": { - "min": 1655471940000, - "max": 1655472300000 - }, - "metric": { - "app": "logstash", - "field": "logstash_stats.process.cpu.percent", - "metricAgg": "max", - "label": "Cgroup CPU Utilization", - "title": "CPU Utilization", - "description": "CPU Usage time compared to the CPU quota shown in percentage. If CPU quotas are not set, then no data will be shown.", - "units": "%", - "format": "0,0.[00]", - "hasCalculation": true, - "isDerivative": true - }, - "data": [ - [ - 1655471940000, - null - ], - [ - 1655471950000, - null - ], - [ - 1655471960000, - null - ], - [ - 1655471970000, - null - ], - [ - 1655471980000, - null - ], - [ - 1655471990000, - null - ], - [ - 1655472000000, - null - ], - [ - 1655472010000, - null - ], - [ - 1655472020000, - null - ], - [ - 1655472030000, - null - ], - [ - 1655472040000, - null - ], - [ - 1655472050000, - null - ], - [ - 1655472060000, - null - ], - [ - 1655472070000, - null - ], - [ - 1655472080000, - null - ], - [ - 1655472090000, - null - ], - [ - 1655472100000, - null - ], - [ - 1655472110000, - null - ], - [ - 1655472120000, - null - ], - [ - 1655472130000, - null - ], - [ - 1655472140000, - null - ], - [ - 1655472150000, - null - ], - [ - 1655472160000, - null - ], - [ - 1655472170000, - null - ], - [ - 1655472180000, - null - ], - [ - 1655472190000, - null - ], - [ - 1655472200000, - null - ], - [ - 1655472210000, - null - ], - [ - 1655472220000, - null - ], - [ - 1655472230000, - null - ], - [ - 1655472240000, - null - ], - [ - 1655472250000, - null - ], - [ - 1655472260000, - null - ], - [ - 1655472270000, - null - ], - [ - 1655472280000, - null - ], - [ - 1655472290000, - null - ] - ] - } - ], - "logstash_node_cgroup_cpu": [ - { - "bucket_size": "10 seconds", - "timeRange": { - "min": 1655471940000, - "max": 1655472300000 - }, - "metric": { - "app": "logstash", - "field": "logstash_stats.os.cgroup.cpuacct.usage_nanos", - "metricAgg": "max", - "label": "Cgroup Usage", - "title": "Cgroup CPU Performance", - "description": "The usage, reported in nanoseconds, of the Cgroup. Compare this with the throttling to discover issues.", - "units": "ns", - "format": "0,0.[0]a", - "hasCalculation": false, - "isDerivative": true - }, - "data": [ - [ - 1655471940000, - null - ], - [ - 1655471950000, - null - ], - [ - 1655471960000, - null - ], - [ - 1655471970000, - null - ], - [ - 1655471980000, - null - ], - [ - 1655471990000, - null - ], - [ - 1655472000000, - null - ], - [ - 1655472010000, - null - ], - [ - 1655472020000, - null - ], - [ - 1655472030000, - null - ], - [ - 1655472040000, - null - ], - [ - 1655472050000, - null - ], - [ - 1655472060000, - null - ], - [ - 1655472070000, - null - ], - [ - 1655472080000, - null - ], - [ - 1655472090000, - null - ], - [ - 1655472100000, - null - ], - [ - 1655472110000, - null - ], - [ - 1655472120000, - null - ], - [ - 1655472130000, - null - ], - [ - 1655472140000, - null - ], - [ - 1655472150000, - null - ], - [ - 1655472160000, - null - ], - [ - 1655472170000, - null - ], - [ - 1655472180000, - null - ], - [ - 1655472190000, - null - ], - [ - 1655472200000, - null - ], - [ - 1655472210000, - null - ], - [ - 1655472220000, - null - ], - [ - 1655472230000, - null - ], - [ - 1655472240000, - null - ], - [ - 1655472250000, - null - ], - [ - 1655472260000, - null - ], - [ - 1655472270000, - null - ], - [ - 1655472280000, - null - ], - [ - 1655472290000, - null - ] - ] - }, - { - "bucket_size": "10 seconds", - "timeRange": { - "min": 1655471940000, - "max": 1655472300000 - }, - "metric": { - "app": "logstash", - "field": "logstash_stats.os.cgroup.cpu.stat.time_throttled_nanos", - "metricAgg": "max", - "label": "Cgroup Throttling", - "title": "Cgroup CPU Performance", - "description": "The amount of throttled time, reported in nanoseconds, of the Cgroup.", - "units": "ns", - "format": "0,0.[0]a", - "hasCalculation": false, - "isDerivative": true - }, - "data": [ - [ - 1655471940000, - null - ], - [ - 1655471950000, - null - ], - [ - 1655471960000, - null - ], - [ - 1655471970000, - null - ], - [ - 1655471980000, - null - ], - [ - 1655471990000, - null - ], - [ - 1655472000000, - null - ], - [ - 1655472010000, - null - ], - [ - 1655472020000, - null - ], - [ - 1655472030000, - null - ], - [ - 1655472040000, - null - ], - [ - 1655472050000, - null - ], - [ - 1655472060000, - null - ], - [ - 1655472070000, - null - ], - [ - 1655472080000, - null - ], - [ - 1655472090000, - null - ], - [ - 1655472100000, - null - ], - [ - 1655472110000, - null - ], - [ - 1655472120000, - null - ], - [ - 1655472130000, - null - ], - [ - 1655472140000, - null - ], - [ - 1655472150000, - null - ], - [ - 1655472160000, - null - ], - [ - 1655472170000, - null - ], - [ - 1655472180000, - null - ], - [ - 1655472190000, - null - ], - [ - 1655472200000, - null - ], - [ - 1655472210000, - null - ], - [ - 1655472220000, - null - ], - [ - 1655472230000, - null - ], - [ - 1655472240000, - null - ], - [ - 1655472250000, - null - ], - [ - 1655472260000, - null - ], - [ - 1655472270000, - null - ], - [ - 1655472280000, - null - ], - [ - 1655472290000, - null - ] - ] - } - ], - "logstash_node_cgroup_stats": [ - { - "bucket_size": "10 seconds", - "timeRange": { - "min": 1655471940000, - "max": 1655472300000 - }, - "metric": { - "app": "logstash", - "field": "logstash_stats.os.cgroup.cpu.stat.number_of_elapsed_periods", - "metricAgg": "max", - "label": "Cgroup Elapsed Periods", - "title": "Cgroup CFS Stats", - "description": "The number of sampling periods from the Completely Fair Scheduler (CFS). Compare against the number of times throttled.", - "units": "", - "format": "0,0.[00]", - "hasCalculation": false, - "isDerivative": true - }, - "data": [ - [ - 1655471940000, - null - ], - [ - 1655471950000, - null - ], - [ - 1655471960000, - null - ], - [ - 1655471970000, - null - ], - [ - 1655471980000, - null - ], - [ - 1655471990000, - null - ], - [ - 1655472000000, - null - ], - [ - 1655472010000, - null - ], - [ - 1655472020000, - null - ], - [ - 1655472030000, - null - ], - [ - 1655472040000, - null - ], - [ - 1655472050000, - null - ], - [ - 1655472060000, - null - ], - [ - 1655472070000, - null - ], - [ - 1655472080000, - null - ], - [ - 1655472090000, - null - ], - [ - 1655472100000, - null - ], - [ - 1655472110000, - null - ], - [ - 1655472120000, - null - ], - [ - 1655472130000, - null - ], - [ - 1655472140000, - null - ], - [ - 1655472150000, - null - ], - [ - 1655472160000, - null - ], - [ - 1655472170000, - null - ], - [ - 1655472180000, - null - ], - [ - 1655472190000, - null - ], - [ - 1655472200000, - null - ], - [ - 1655472210000, - null - ], - [ - 1655472220000, - null - ], - [ - 1655472230000, - null - ], - [ - 1655472240000, - null - ], - [ - 1655472250000, - null - ], - [ - 1655472260000, - null - ], - [ - 1655472270000, - null - ], - [ - 1655472280000, - null - ], - [ - 1655472290000, - null - ] - ] - }, - { - "bucket_size": "10 seconds", - "timeRange": { - "min": 1655471940000, - "max": 1655472300000 - }, - "metric": { - "app": "logstash", - "field": "logstash_stats.os.cgroup.cpu.stat.number_of_times_throttled", - "metricAgg": "max", - "label": "Cgroup Throttled Count", - "title": "Cgroup CFS Stats", - "description": "The number of times that the CPU was throttled by the Cgroup.", - "units": "", - "format": "0,0.[00]", - "hasCalculation": false, - "isDerivative": true - }, - "data": [ - [ - 1655471940000, - null - ], - [ - 1655471950000, - null - ], - [ - 1655471960000, - null - ], - [ - 1655471970000, - null - ], - [ - 1655471980000, - null - ], - [ - 1655471990000, - null - ], - [ - 1655472000000, - null - ], - [ - 1655472010000, - null - ], - [ - 1655472020000, - null - ], - [ - 1655472030000, - null - ], - [ - 1655472040000, - null - ], - [ - 1655472050000, - null - ], - [ - 1655472060000, - null - ], - [ - 1655472070000, - null - ], - [ - 1655472080000, - null - ], - [ - 1655472090000, - null - ], - [ - 1655472100000, - null - ], - [ - 1655472110000, - null - ], - [ - 1655472120000, - null - ], - [ - 1655472130000, - null - ], - [ - 1655472140000, - null - ], - [ - 1655472150000, - null - ], - [ - 1655472160000, - null - ], - [ - 1655472170000, - null - ], - [ - 1655472180000, - null - ], - [ - 1655472190000, - null - ], - [ - 1655472200000, - null - ], - [ - 1655472210000, - null - ], - [ - 1655472220000, - null - ], - [ - 1655472230000, - null - ], - [ - 1655472240000, - null - ], - [ - 1655472250000, - null - ], - [ - 1655472260000, - null - ], - [ - 1655472270000, - null - ], - [ - 1655472280000, - null - ], - [ - 1655472290000, - null - ] - ] - } - ], - "logstash_queue_events_count": [ - { - "bucket_size": "10 seconds", - "timeRange": { - "min": 1655471940000, - "max": 1655472300000 - }, - "metric": { - "app": "logstash", - "field": "logstash_stats.queue.events_count", - "metricAgg": "max", - "label": "Events Queued", - "title": "Persistent Queue Events", - "description": "Average number of events in the persistent queue waiting to be processed by the filter and output stages.", - "units": "", - "format": "0,0.[00]", - "hasCalculation": false, - "isDerivative": false - }, - "data": [ - [ - 1655471940000, - 7 - ], - [ - 1655471950000, - 2 - ], - [ - 1655471960000, - 0 - ], - [ - 1655471970000, - 15 - ], - [ - 1655471980000, - 7 - ], - [ - 1655471990000, - 2 - ], - [ - 1655472000000, - 2 - ], - [ - 1655472010000, - 14 - ], - [ - 1655472020000, - 28 - ], - [ - 1655472030000, - 21 - ], - [ - 1655472040000, - 61 - ], - [ - 1655472050000, - 14 - ], - [ - 1655472060000, - 6 - ], - [ - 1655472070000, - 0 - ], - [ - 1655472080000, - 101 - ], - [ - 1655472090000, - 76 - ], - [ - 1655472100000, - 6 - ], - [ - 1655472110000, - 23 - ], - [ - 1655472120000, - 23 - ], - [ - 1655472130000, - 123 - ], - [ - 1655472140000, - 51 - ], - [ - 1655472150000, - 23 - ], - [ - 1655472160000, - 2 - ], - [ - 1655472170000, - 102 - ], - [ - 1655472180000, - 78 - ], - [ - 1655472190000, - 22 - ], - [ - 1655472200000, - 17 - ], - [ - 1655472210000, - 14 - ], - [ - 1655472220000, - 70 - ], - [ - 1655472230000, - 45 - ], - [ - 1655472240000, - 17 - ], - [ - 1655472250000, - 75 - ], - [ - 1655472260000, - 52 - ], - [ - 1655472270000, - 153 - ], - [ - 1655472280000, - 1 - ], - [ - 1655472290000, - 16 - ] - ] - } - ], - "logstash_pipeline_queue_size": [ - { - "bucket_size": "10 seconds", - "timeRange": { - "min": 1655471940000, - "max": 1655472300000 - }, - "metric": { - "app": "logstash", - "field": "logstash_stats.pipelines.queue.queue_size_in_bytes", - "label": "Queue Size", - "title": "Persistent Queue Size", - "description": "Current size of all persistent queues in the Logstash pipelines on this node.", - "units": "B", - "format": "0,0.0 b", - "hasCalculation": true, - "isDerivative": false - }, - "data": [ - [ - 1655471940000, - 141265 - ], - [ - 1655471950000, - 173965 - ], - [ - 1655471960000, - 206665 - ], - [ - 1655471970000, - 239692 - ], - [ - 1655471980000, - 272392 - ], - [ - 1655471990000, - 305092 - ], - [ - 1655472000000, - 337792 - ], - [ - 1655472010000, - 370489 - ], - [ - 1655472020000, - 403189 - ], - [ - 1655472030000, - 435886 - ], - [ - 1655472040000, - 468586 - ], - [ - 1655472050000, - 501286 - ], - [ - 1655472060000, - 533986 - ], - [ - 1655472070000, - 566686 - ], - [ - 1655472080000, - 599713 - ], - [ - 1655472090000, - 632413 - ], - [ - 1655472100000, - 665110 - ], - [ - 1655472110000, - 697810 - ], - [ - 1655472120000, - 730510 - ], - [ - 1655472130000, - 763210 - ], - [ - 1655472140000, - 795910 - ], - [ - 1655472150000, - 828610 - ], - [ - 1655472160000, - 861307 - ], - [ - 1655472170000, - 894007 - ], - [ - 1655472180000, - 927034 - ], - [ - 1655472190000, - 959731 - ], - [ - 1655472200000, - 992431 - ], - [ - 1655472210000, - 1025131 - ], - [ - 1655472220000, - 1057831 - ], - [ - 1655472230000, - 1090531 - ], - [ - 1655472240000, - 1123231 - ], - [ - 1655472250000, - 1155931 - ], - [ - 1655472260000, - 1188631 - ], - [ - 1655472270000, - 1221331 - ], - [ - 1655472280000, - 1254358 - ], - [ - 1655472290000, - 1287058 - ] - ] - }, - { - "bucket_size": "10 seconds", - "timeRange": { - "min": 1655471940000, - "max": 1655472300000 - }, - "metric": { - "app": "logstash", - "field": "logstash_stats.pipelines.queue.max_queue_size_in_bytes", - "label": "Max Queue Size", - "description": "Maximum size set for the persistent queues on this node.", - "units": "B", - "format": "0,0.0 b", - "hasCalculation": true, - "isDerivative": false - }, - "data": [ - [ - 1655471940000, - 1073741824 - ], - [ - 1655471950000, - 1073741824 - ], - [ - 1655471960000, - 1073741824 - ], - [ - 1655471970000, - 1073741824 - ], - [ - 1655471980000, - 1073741824 - ], - [ - 1655471990000, - 1073741824 - ], - [ - 1655472000000, - 1073741824 - ], - [ - 1655472010000, - 1073741824 - ], - [ - 1655472020000, - 1073741824 - ], - [ - 1655472030000, - 1073741824 - ], - [ - 1655472040000, - 1073741824 - ], - [ - 1655472050000, - 1073741824 - ], - [ - 1655472060000, - 1073741824 - ], - [ - 1655472070000, - 1073741824 - ], - [ - 1655472080000, - 1073741824 - ], - [ - 1655472090000, - 1073741824 - ], - [ - 1655472100000, - 1073741824 - ], - [ - 1655472110000, - 1073741824 - ], - [ - 1655472120000, - 1073741824 - ], - [ - 1655472130000, - 1073741824 - ], - [ - 1655472140000, - 1073741824 - ], - [ - 1655472150000, - 1073741824 - ], - [ - 1655472160000, - 1073741824 - ], - [ - 1655472170000, - 1073741824 - ], - [ - 1655472180000, - 1073741824 - ], - [ - 1655472190000, - 1073741824 - ], - [ - 1655472200000, - 1073741824 - ], - [ - 1655472210000, - 1073741824 - ], - [ - 1655472220000, - 1073741824 - ], - [ - 1655472230000, - 1073741824 - ], - [ - 1655472240000, - 1073741824 - ], - [ - 1655472250000, - 1073741824 - ], - [ - 1655472260000, - 1073741824 - ], - [ - 1655472270000, - 1073741824 - ], - [ - 1655472280000, - 1073741824 - ], - [ - 1655472290000, - 1073741824 - ] - ] - } - ] - }, - "nodeSummary": { - "ephemeral_id": "88c01586-1891-4a0e-a2ca-ca5c988f8cbb", - "host": "kevins-macbook-pro.home", - "http_address": "127.0.0.1:9600", - "name": "kevins-macbook-pro.home", - "pipeline": { - "batch_size": 125, - "workers": 16 - }, - "snapshot": false, - "status": "green", - "uuid": "f9efd237-3bbf-4a9b-9ce7-a16141b9d981", - "version": "8.2.2", - "availability": false, - "events": { - "duration_in_millis": 5145881, - "filtered": 2579, - "in": 3978, - "out": 2579 - }, - "reloads": { - "failures": 0, - "successes": 0 - }, - "uptime": 407358 - } -} diff --git a/x-pack/test/api_integration/apis/monitoring/logstash/index.js b/x-pack/test/api_integration/apis/monitoring/logstash/index.js index 0caf7e360ef3a..d76b656fcbaa3 100644 --- a/x-pack/test/api_integration/apis/monitoring/logstash/index.js +++ b/x-pack/test/api_integration/apis/monitoring/logstash/index.js @@ -8,14 +8,9 @@ export default function ({ loadTestFile }) { describe('Logstash', () => { loadTestFile(require.resolve('./overview')); - loadTestFile(require.resolve('./overview_mb')); loadTestFile(require.resolve('./nodes')); - loadTestFile(require.resolve('./nodes_mb')); loadTestFile(require.resolve('./node_detail')); - loadTestFile(require.resolve('./node_detail_mb')); loadTestFile(require.resolve('./multicluster_pipelines')); - loadTestFile(require.resolve('./multicluster_pipelines_mb')); loadTestFile(require.resolve('./pipelines')); - loadTestFile(require.resolve('./pipelines_mb')); }); } diff --git a/x-pack/test/api_integration/apis/monitoring/logstash/multicluster_pipelines_mb.js b/x-pack/test/api_integration/apis/monitoring/logstash/multicluster_pipelines_mb.js deleted file mode 100644 index a7d5194c35f79..0000000000000 --- a/x-pack/test/api_integration/apis/monitoring/logstash/multicluster_pipelines_mb.js +++ /dev/null @@ -1,49 +0,0 @@ -/* - * 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 expect from '@kbn/expect'; -import fixture from './fixtures/multicluster_pipelines.json'; -import { getLifecycleMethods } from '../data_stream'; - -export default function ({ getService }) { - const supertest = getService('supertest'); - const { setup, tearDown } = getLifecycleMethods(getService); - - describe('pipelines listing multicluster - metricbeat and package', () => { - ['mb', 'package'].forEach((source) => { - describe(`pipelines listing multicluster ${source}`, () => { - const archive = `x-pack/test/functional/es_archives/monitoring/logstash_pipelines_multicluster_${source}`; - const timeRange = { - min: '2019-11-11T15:13:45.266Z', - max: '2019-11-11T15:17:05.399Z', - }; - const pagination = { - size: 10, - index: 0, - }; - - before('load archive', () => { - return setup(archive); - }); - - after('unload archive', () => { - return tearDown(archive); - }); - - it('should get the pipelines', async () => { - const { body } = await supertest - .post('/api/monitoring/v1/clusters/hJS0FZ7wR9GGdYs8RNW8pw/logstash/pipelines') - .set('kbn-xsrf', 'xxx') - .send({ timeRange, pagination }) - .expect(200); - - expect(body).to.eql(fixture); - }); - }); - }); - }); -} diff --git a/x-pack/test/api_integration/apis/monitoring/logstash/node_detail_mb.js b/x-pack/test/api_integration/apis/monitoring/logstash/node_detail_mb.js deleted file mode 100644 index 6a8068fe5b9b8..0000000000000 --- a/x-pack/test/api_integration/apis/monitoring/logstash/node_detail_mb.js +++ /dev/null @@ -1,59 +0,0 @@ -/* - * 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 expect from '@kbn/expect'; -import nodeDetailFixture from './fixtures/node_detail_8.json'; -import nodeDetailAdvancedFixture from './fixtures/node_detail_advanced_8.json'; - -export default function ({ getService }) { - const supertest = getService('supertest'); - const esArchiver = getService('esArchiver'); - - describe('node detail - metricbeat and package', () => { - ['logstash_8', 'logstash_package'].forEach((source) => { - describe(`node detail ${source}`, () => { - const archive = `x-pack/test/api_integration/apis/monitoring/es_archives/${source}`; - const timeRange = { - min: '2022-06-17T13:19:00.000Z', - max: '2022-06-17T13:25:00.000Z', - }; - - before('load archive', () => { - return esArchiver.load(archive); - }); - - after('unload archive', () => { - return esArchiver.unload(archive); - }); - - it('should summarize the Logstash node with non-advanced chart data metrics', async () => { - const { body } = await supertest - .post( - '/api/monitoring/v1/clusters/__standalone_cluster__/logstash/node/f9efd237-3bbf-4a9b-9ce7-a16141b9d981' - ) - .set('kbn-xsrf', 'xxx') - .send({ timeRange, is_advanced: false }) - .expect(200); - - expect(body).to.eql(nodeDetailFixture); - }); - - it('should summarize the Logstash node with advanced chart data metrics', async () => { - const { body } = await supertest - .post( - '/api/monitoring/v1/clusters/__standalone_cluster__/logstash/node/f9efd237-3bbf-4a9b-9ce7-a16141b9d981' - ) - .set('kbn-xsrf', 'xxx') - .send({ timeRange, is_advanced: true }) - .expect(200); - - expect(body).to.eql(nodeDetailAdvancedFixture); - }); - }); - }); - }); -} diff --git a/x-pack/test/api_integration/apis/monitoring/logstash/nodes_mb.js b/x-pack/test/api_integration/apis/monitoring/logstash/nodes_mb.js deleted file mode 100644 index d518a37d81a04..0000000000000 --- a/x-pack/test/api_integration/apis/monitoring/logstash/nodes_mb.js +++ /dev/null @@ -1,45 +0,0 @@ -/* - * 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 expect from '@kbn/expect'; -import nodesFixture from './fixtures/nodes.json'; -import { getLifecycleMethods } from '../data_stream'; - -export default function ({ getService }) { - const supertest = getService('supertest'); - const { setup, tearDown } = getLifecycleMethods(getService); - - describe('node listing - metricbeat and package', () => { - ['mb', 'package'].forEach((source) => { - describe(`node listing ${source}`, () => { - const archive = `x-pack/test/functional/es_archives/monitoring/logstash_pipelines_${source}`; - const timeRange = { - min: '2018-01-22T09:33:13.000Z', - max: '2018-01-22T09:41:04.000Z', - }; - - before('load archive', () => { - return setup(archive); - }); - - after('unload archive', () => { - return tearDown(archive); - }); - - it('should summarize the Logstash nodes with stats', async () => { - const { body } = await supertest - .post('/api/monitoring/v1/clusters/1rhApLfQShSh3JsNqYCkmA/logstash/nodes') - .set('kbn-xsrf', 'xxx') - .send({ timeRange }) - .expect(200); - - expect(body).to.eql(nodesFixture); - }); - }); - }); - }); -} diff --git a/x-pack/test/api_integration/apis/monitoring/logstash/overview_mb.js b/x-pack/test/api_integration/apis/monitoring/logstash/overview_mb.js deleted file mode 100644 index 1c298e2a422da..0000000000000 --- a/x-pack/test/api_integration/apis/monitoring/logstash/overview_mb.js +++ /dev/null @@ -1,45 +0,0 @@ -/* - * 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 expect from '@kbn/expect'; -import overviewFixture from './fixtures/overview.json'; -import { getLifecycleMethods } from '../data_stream'; - -export default function ({ getService }) { - const supertest = getService('supertest'); - const { setup, tearDown } = getLifecycleMethods(getService); - - describe('overview - metricbeat and package', () => { - ['mb', 'package'].forEach((source) => { - describe(`overview ${source}`, () => { - const archive = `x-pack/test/functional/es_archives/monitoring/logstash_pipelines_${source}`; - const timeRange = { - min: '2018-01-22T09:33:13.000Z', - max: '2018-01-22T09:41:04.000Z', - }; - - before('load archive', () => { - return setup(archive); - }); - - after('unload archive', () => { - return tearDown(archive); - }); - - it('should summarize two Logstash nodes with metrics', async () => { - const { body } = await supertest - .post('/api/monitoring/v1/clusters/1rhApLfQShSh3JsNqYCkmA/logstash') - .set('kbn-xsrf', 'xxx') - .send({ timeRange }) - .expect(200); - - expect(body).to.eql(overviewFixture); - }); - }); - }); - }); -} diff --git a/x-pack/test/api_integration/apis/monitoring/logstash/pipelines_mb.js b/x-pack/test/api_integration/apis/monitoring/logstash/pipelines_mb.js deleted file mode 100644 index 43a00c8185b58..0000000000000 --- a/x-pack/test/api_integration/apis/monitoring/logstash/pipelines_mb.js +++ /dev/null @@ -1,90 +0,0 @@ -/* - * 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 expect from '@kbn/expect'; -import pipelinesFixture from './fixtures/pipelines.json'; -import { getLifecycleMethods } from '../data_stream'; - -export default function ({ getService }) { - const supertest = getService('supertest'); - const { setup, tearDown } = getLifecycleMethods(getService); - - describe('pipelines - metricbeat and package', () => { - ['mb', 'package'].forEach((source) => { - describe(`pipelines ${source}`, () => { - const archive = `x-pack/test/functional/es_archives/monitoring/logstash/changing_pipelines_${source}`; - const timeRange = { - min: '2019-11-04T15:40:44.855Z', - max: '2019-11-04T15:50:38.667Z', - }; - const pagination = { - size: 10, - index: 0, - }; - const sort = { - field: 'id', - direction: 'asc', - }; - - before('load archive', () => { - return setup(archive); - }); - - after('unload archive', () => { - return tearDown(archive); - }); - - it('should return paginated pipelines', async () => { - const { body } = await supertest - .post('/api/monitoring/v1/clusters/TUjQLdHNTh2SB9Wy0gOtWg/logstash/pipelines') - .set('kbn-xsrf', 'xxx') - .send({ timeRange, pagination, sort }) - .expect(200); - - expect(body).to.eql(pipelinesFixture); - }); - - it('should get one of each after enough pagination', async () => { - async function getIds(page) { - const { body } = await supertest - .post('/api/monitoring/v1/clusters/TUjQLdHNTh2SB9Wy0gOtWg/logstash/pipelines') - .set('kbn-xsrf', 'xxx') - .send({ timeRange, pagination: { ...pagination, index: page }, sort }) - .expect(200); - - return body.pipelines.map((pipeline) => pipeline.id); - } - - const ids = [...(await getIds(0)), ...(await getIds(1)), ...(await getIds(2))]; - expect(ids.length).to.be(26); - }); - - it('should not error out if there is missing data for part of the time series', async () => { - const customTimeRange = { - ...timeRange, - max: '2019-11-04T15:59:38.667Z', - }; - - const customSort = { - ...sort, - field: 'logstash_cluster_pipeline_throughput', - }; - - await supertest - .post('/api/monitoring/v1/clusters/TUjQLdHNTh2SB9Wy0gOtWg/logstash/pipelines') - .set('kbn-xsrf', 'xxx') - .send({ - timeRange: customTimeRange, - pagination: { ...pagination, index: 1 }, - sort: customSort, - }) - .expect(200); - }); - }); - }); - }); -} diff --git a/x-pack/test/api_integration/apis/security/privileges.ts b/x-pack/test/api_integration/apis/security/privileges.ts index 9fae13d374ddc..288290ee1df57 100644 --- a/x-pack/test/api_integration/apis/security/privileges.ts +++ b/x-pack/test/api_integration/apis/security/privileges.ts @@ -24,6 +24,7 @@ export default function ({ getService }: FtrProviderContext) { maps: ['all', 'read', 'minimal_all', 'minimal_read'], generalCases: ['all', 'read', 'minimal_all', 'minimal_read', 'cases_delete'], observabilityCases: ['all', 'read', 'minimal_all', 'minimal_read', 'cases_delete'], + slo: ['all', 'read', 'minimal_all', 'minimal_read'], fleetv2: ['all', 'read', 'minimal_all', 'minimal_read'], fleet: ['all', 'read', 'minimal_all', 'minimal_read'], actions: ['all', 'read', 'minimal_all', 'minimal_read'], diff --git a/x-pack/test/api_integration/apis/security/privileges_basic.ts b/x-pack/test/api_integration/apis/security/privileges_basic.ts index 9762ab2e343e1..2c0e299c263e6 100644 --- a/x-pack/test/api_integration/apis/security/privileges_basic.ts +++ b/x-pack/test/api_integration/apis/security/privileges_basic.ts @@ -32,6 +32,7 @@ export default function ({ getService }: FtrProviderContext) { maps: ['all', 'read', 'minimal_all', 'minimal_read'], generalCases: ['all', 'read', 'minimal_all', 'minimal_read'], observabilityCases: ['all', 'read', 'minimal_all', 'minimal_read'], + slo: ['all', 'read', 'minimal_all', 'minimal_read'], canvas: ['all', 'read', 'minimal_all', 'minimal_read'], infrastructure: ['all', 'read', 'minimal_all', 'minimal_read'], logs: ['all', 'read', 'minimal_all', 'minimal_read'], @@ -94,6 +95,7 @@ export default function ({ getService }: FtrProviderContext) { maps: ['all', 'read', 'minimal_all', 'minimal_read'], generalCases: ['all', 'read', 'minimal_all', 'minimal_read', 'cases_delete'], observabilityCases: ['all', 'read', 'minimal_all', 'minimal_read', 'cases_delete'], + slo: ['all', 'read', 'minimal_all', 'minimal_read'], fleetv2: ['all', 'read', 'minimal_all', 'minimal_read'], fleet: ['all', 'read', 'minimal_all', 'minimal_read'], actions: ['all', 'read', 'minimal_all', 'minimal_read'], diff --git a/x-pack/test/apm_api_integration/tests/mobile/generate_mobile_data.ts b/x-pack/test/apm_api_integration/tests/mobile/generate_mobile_data.ts index aa6f3470ab9b8..c2e536f0ce930 100644 --- a/x-pack/test/apm_api_integration/tests/mobile/generate_mobile_data.ts +++ b/x-pack/test/apm_api_integration/tests/mobile/generate_mobile_data.ts @@ -22,11 +22,11 @@ export async function generateMobileData({ environment: 'production', agentName: 'android/java', }) - .mobileDevice() + .mobileDevice({ serviceVersion: '2.3' }) .deviceInfo({ manufacturer: 'Samsung', - modelIdentifier: 'SM-G930F', - modelName: 'Galaxy S7', + modelIdentifier: 'SM-G973F', + modelName: 'Galaxy S10', }) .osInfo({ osType: 'android', @@ -52,7 +52,7 @@ export async function generateMobileData({ environment: 'production', agentName: 'android/java', }) - .mobileDevice() + .mobileDevice({ serviceVersion: '1.2' }) .deviceInfo({ manufacturer: 'Samsung', modelIdentifier: 'SM-G930F', @@ -83,6 +83,41 @@ export async function generateMobileData({ carrierMCC: '525', }); + const huaweiP2 = apm + .mobileApp({ + name: 'synth-android', + environment: 'production', + agentName: 'android/java', + }) + .mobileDevice({ serviceVersion: '1.1' }) + .deviceInfo({ + manufacturer: 'Huawei', + modelIdentifier: 'HUAWEI P2-0000', + modelName: 'HuaweiP2', + }) + .osInfo({ + osType: 'android', + osVersion: '10', + osFull: 'Android 10, API level 29, BUILD A022MUBU2AUD1', + runtimeVersion: '2.1.0', + }) + .setGeoInfo({ + clientIp: '20.24.184.101', + cityName: 'Singapore', + continentName: 'Asia', + countryIsoCode: 'SG', + countryName: 'Singapore', + location: { coordinates: [103.8554, 1.3036], type: 'Point' }, + }) + .setNetworkConnection({ + type: 'cell', + subType: 'edge', + carrierName: 'Osaka Gas Business Create Co., Ltd.', + carrierMNC: '17', + carrierICC: 'JP', + carrierMCC: '440', + }); + return await synthtraceEsClient.index([ timerange(start, end) .interval('5m') @@ -90,6 +125,7 @@ export async function generateMobileData({ .generator((timestamp) => { galaxy10.startNewSession(); galaxy7.startNewSession(); + huaweiP2.startNewSession(); return [ galaxy10 .transaction('Start View - View Appearing', 'Android Activity') @@ -134,6 +170,33 @@ export async function generateMobileData({ .success() .timestamp(10000 + timestamp + 250) ), + huaweiP2 + .transaction('Start View - View Appearing', 'huaweiP2 Activity') + .timestamp(timestamp) + .duration(20) + .success() + .children( + huaweiP2 + .span({ + spanName: 'onCreate', + spanType: 'app', + spanSubtype: 'external', + 'service.target.type': 'http', + 'span.destination.service.resource': 'external', + }) + .duration(50) + .success() + .timestamp(timestamp + 20), + huaweiP2 + .httpSpan({ + spanName: 'GET backend:1234', + httpMethod: 'GET', + httpUrl: 'https://backend:1234/api/start', + }) + .duration(800) + .success() + .timestamp(timestamp + 400) + ), galaxy7 .transaction('Start View - View Appearing', 'Android Activity') .timestamp(timestamp) diff --git a/x-pack/test/apm_api_integration/tests/mobile/mobile_http_requests_timeseries.spec.ts b/x-pack/test/apm_api_integration/tests/mobile/mobile_http_requests_timeseries.spec.ts index 31c7fe770e1e2..587f91ccbe6b8 100644 --- a/x-pack/test/apm_api_integration/tests/mobile/mobile_http_requests_timeseries.spec.ts +++ b/x-pack/test/apm_api_integration/tests/mobile/mobile_http_requests_timeseries.spec.ts @@ -92,7 +92,7 @@ export default function ApiTest({ getService }: FtrProviderContext) { response.body.currentPeriod.timeseries.some((item) => item.y === 0 && item.x) ).to.eql(true); - expect(response.body.currentPeriod.timeseries[0].y).to.eql(3); + expect(response.body.currentPeriod.timeseries[0].y).to.eql(4); expect(response.body.previousPeriod.timeseries).to.eql([]); }); }); @@ -126,7 +126,7 @@ export default function ApiTest({ getService }: FtrProviderContext) { expect(response.status).to.be(200); expect(ntcCell.status).to.be(200); expect(response.body.currentPeriod.timeseries[0].y).to.eql(2); - expect(ntcCell.body.currentPeriod.timeseries[0].y).to.eql(1); + expect(ntcCell.body.currentPeriod.timeseries[0].y).to.eql(2); }); }); }); diff --git a/x-pack/test/apm_api_integration/tests/mobile/mobile_location_stats.spec.ts b/x-pack/test/apm_api_integration/tests/mobile/mobile_location_stats.spec.ts index 8dbfb8c873ff9..5cbffe4ab8c00 100644 --- a/x-pack/test/apm_api_integration/tests/mobile/mobile_location_stats.spec.ts +++ b/x-pack/test/apm_api_integration/tests/mobile/mobile_location_stats.spec.ts @@ -119,20 +119,20 @@ export default function ApiTest({ getService }: FtrProviderContext) { const response = await getMobileLocationStats({ serviceName: 'synth-android', environment: 'production', - kuery: `service.version:"1.0"`, + kuery: `service.version:"2.3"`, }); - expect(response.currentPeriod.mostSessions.value).to.eql(6); + expect(response.currentPeriod.mostSessions.value).to.eql(3); expect(response.currentPeriod.mostRequests.value).to.eql(0); }); it('returns the correct values when multiple filters are applied', async () => { const response = await getMobileLocationStats({ serviceName: 'synth-android', - kuery: `service.version:"1.0" and service.environment: "production"`, + kuery: `service.version:"2.3" and service.environment: "production"`, }); - expect(response.currentPeriod.mostSessions.value).to.eql(6); + expect(response.currentPeriod.mostSessions.value).to.eql(3); expect(response.currentPeriod.mostRequests.value).to.eql(0); }); }); diff --git a/x-pack/test/apm_api_integration/tests/mobile/mobile_sessions_timeseries.spec.ts b/x-pack/test/apm_api_integration/tests/mobile/mobile_sessions_timeseries.spec.ts index aeb6122cd7d16..8e1c2dceb0b5f 100644 --- a/x-pack/test/apm_api_integration/tests/mobile/mobile_sessions_timeseries.spec.ts +++ b/x-pack/test/apm_api_integration/tests/mobile/mobile_sessions_timeseries.spec.ts @@ -78,7 +78,7 @@ export default function ApiTest({ getService }: FtrProviderContext) { true ); - expect(response.body.currentPeriod.timeseries[0].y).to.eql(2); + expect(response.body.currentPeriod.timeseries[0].y).to.eql(3); expect(response.body.previousPeriod.timeseries[0].y).to.eql(0); }); @@ -90,7 +90,7 @@ export default function ApiTest({ getService }: FtrProviderContext) { true ); - expect(response.body.currentPeriod.timeseries[0].y).to.eql(2); + expect(response.body.currentPeriod.timeseries[0].y).to.eql(3); expect(response.body.previousPeriod.timeseries).to.eql([]); }); }); @@ -119,7 +119,7 @@ export default function ApiTest({ getService }: FtrProviderContext) { true ); - expect(response.body.currentPeriod.timeseries[0].y).to.eql(2); + expect(response.body.currentPeriod.timeseries[0].y).to.eql(3); expect(response.body.previousPeriod.timeseries).to.eql([]); }); }); diff --git a/x-pack/test/apm_api_integration/tests/mobile/mobile_stats.spec.ts b/x-pack/test/apm_api_integration/tests/mobile/mobile_stats.spec.ts index 77508a34d7ddd..a6f7d50fc717a 100644 --- a/x-pack/test/apm_api_integration/tests/mobile/mobile_stats.spec.ts +++ b/x-pack/test/apm_api_integration/tests/mobile/mobile_stats.spec.ts @@ -121,20 +121,20 @@ export default function ApiTest({ getService }: FtrProviderContext) { const response = await getMobileStats({ serviceName: 'synth-android', environment: 'production', - kuery: `service.version:"1.0"`, + kuery: `service.version:"1.2"`, }); - expect(response.currentPeriod.sessions.value).to.eql(6); + expect(response.currentPeriod.sessions.value).to.eql(3); expect(response.currentPeriod.requests.value).to.eql(0); }); it('returns the correct values when multiple filters are applied', async () => { const response = await getMobileStats({ serviceName: 'synth-android', - kuery: `service.version:"1.0" and service.environment: "production"`, + kuery: `service.version:"2.3" and service.environment: "production"`, }); - expect(response.currentPeriod.sessions.value).to.eql(6); + expect(response.currentPeriod.sessions.value).to.eql(3); expect(response.currentPeriod.requests.value).to.eql(0); }); }); diff --git a/x-pack/test/apm_api_integration/tests/mobile/mobile_terms_by_field.spec.ts b/x-pack/test/apm_api_integration/tests/mobile/mobile_terms_by_field.spec.ts new file mode 100644 index 0000000000000..ae3201bb79a28 --- /dev/null +++ b/x-pack/test/apm_api_integration/tests/mobile/mobile_terms_by_field.spec.ts @@ -0,0 +1,148 @@ +/* + * 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 expect from '@kbn/expect'; +import { ENVIRONMENT_ALL } from '@kbn/apm-plugin/common/environment_filter_values'; +import { FtrProviderContext } from '../../common/ftr_provider_context'; +import { generateMobileData } from './generate_mobile_data'; + +export default function ApiTest({ getService }: FtrProviderContext) { + const apmApiClient = getService('apmApiClient'); + const registry = getService('registry'); + const synthtraceEsClient = getService('synthtraceEsClient'); + + const start = new Date('2023-01-01T00:00:00.000Z').getTime(); + const end = new Date('2023-01-01T00:15:00.000Z').getTime() - 1; + + async function getMobileTermsByField({ + environment = ENVIRONMENT_ALL.value, + kuery = '', + serviceName, + fieldName, + size, + }: { + environment?: string; + kuery?: string; + serviceName: string; + fieldName: string; + size: number; + }) { + return await apmApiClient + .readUser({ + endpoint: 'GET /internal/apm/mobile-services/{serviceName}/terms', + params: { + path: { serviceName }, + query: { + environment, + start: new Date(start).toISOString(), + end: new Date(end).toISOString(), + kuery, + size, + fieldName, + }, + }, + }) + .then(({ body }) => body); + } + + registry.when('Mobile terms when data is not loaded', { config: 'basic', archives: [] }, () => { + describe('when no data', () => { + it('handles empty state', async () => { + const response = await getMobileTermsByField({ + serviceName: 'foo', + fieldName: 'bar', + size: 1, + }); + expect(response.terms).to.eql([]); + }); + + it('handles empty fieldName', async () => { + const response = await getMobileTermsByField({ + serviceName: 'synth-android', + fieldName: '', + size: 1, + }); + expect(response.terms).to.eql([]); + }); + }); + }); + + registry.when('Mobile terms', { config: 'basic', archives: [] }, () => { + before(async () => { + await generateMobileData({ + synthtraceEsClient, + start, + end, + }); + }); + + after(() => synthtraceEsClient.clean()); + + describe('when data is loaded', () => { + it('returns mobile devices', async () => { + const response = await getMobileTermsByField({ + serviceName: 'synth-android', + environment: 'production', + fieldName: 'device.model.identifier', + size: 10, + }); + expect(response.terms).to.eql([ + { + label: 'SM-G973F', + count: 6, + }, + { + label: 'HUAWEI P2-0000', + count: 3, + }, + { + label: 'SM-G930F', + count: 3, + }, + ]); + }); + + it('returns mobile versions', async () => { + const response = await getMobileTermsByField({ + serviceName: 'synth-android', + environment: 'production', + fieldName: 'service.version', + size: 10, + }); + expect(response.terms).to.eql([ + { + label: '2.3', + count: 6, + }, + { + label: '1.1', + count: 3, + }, + { + label: '1.2', + count: 3, + }, + ]); + }); + + it('return the most used mobile version', async () => { + const response = await getMobileTermsByField({ + serviceName: 'synth-android', + environment: 'production', + fieldName: 'service.version', + size: 1, + }); + expect(response.terms).to.eql([ + { + label: '2.3', + count: 6, + }, + ]); + }); + }); + }); +} diff --git a/x-pack/test/cases_api_integration/common/plugins/cases/server/plugin.ts b/x-pack/test/cases_api_integration/common/plugins/cases/server/plugin.ts index 8fc826581dda6..9b92917544d4b 100644 --- a/x-pack/test/cases_api_integration/common/plugins/cases/server/plugin.ts +++ b/x-pack/test/cases_api_integration/common/plugins/cases/server/plugin.ts @@ -6,16 +6,13 @@ */ import { Plugin, CoreSetup, CoreStart, PluginInitializerContext, Logger } from '@kbn/core/server'; -import { schema } from '@kbn/config-schema'; - import { PluginSetupContract as FeaturesPluginSetup } from '@kbn/features-plugin/server'; import { SpacesPluginStart } from '@kbn/spaces-plugin/server'; import { SecurityPluginStart } from '@kbn/security-plugin/server'; -import { PluginStartContract as CasesPluginStart } from '@kbn/cases-plugin/server'; -import { CasesPatchRequest } from '@kbn/cases-plugin/common/api'; -import { PluginSetupContract as CasesSetup } from '@kbn/cases-plugin/server/types'; +import type { CasesStart, CasesSetup } from '@kbn/cases-plugin/server'; import { getPersistableStateAttachment } from './attachments/persistable_state'; import { getExternalReferenceAttachment } from './attachments/external_reference'; +import { registerRoutes } from './routes'; export interface FixtureSetupDeps { features: FeaturesPluginSetup; @@ -25,12 +22,11 @@ export interface FixtureSetupDeps { export interface FixtureStartDeps { security?: SecurityPluginStart; spaces?: SpacesPluginStart; - cases?: CasesPluginStart; + cases: CasesStart; } export class FixturePlugin implements Plugin { private readonly log: Logger; - private casesPluginStart?: CasesPluginStart; constructor(initContext: PluginInitializerContext) { this.log = initContext.logger.get(); } @@ -39,37 +35,9 @@ export class FixturePlugin implements Plugin { - try { - const client = await this.casesPluginStart?.getCasesClientWithRequest(request); - if (!client) { - throw new Error('Cases client was undefined'); - } - - return response.ok({ - body: await client.cases.update(request.body as CasesPatchRequest), - }); - } catch (error) { - this.log.error(`CasesClientUser failure: ${error}`); - throw error; - } - } - ); - } - public start(core: CoreStart, plugins: FixtureStartDeps) { - this.casesPluginStart = plugins.cases; + registerRoutes(core, this.log); } + + public start(core: CoreStart, plugins: FixtureStartDeps) {} public stop() {} } diff --git a/x-pack/test/cases_api_integration/common/plugins/cases/server/routes.ts b/x-pack/test/cases_api_integration/common/plugins/cases/server/routes.ts new file mode 100644 index 0000000000000..2937e309c2dda --- /dev/null +++ b/x-pack/test/cases_api_integration/common/plugins/cases/server/routes.ts @@ -0,0 +1,120 @@ +/* + * 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 { createHash } from 'crypto'; +import semverCompare from 'semver/functions/compare'; +import type { CasesPatchRequest } from '@kbn/cases-plugin/common/api'; +import { schema } from '@kbn/config-schema'; +import type { CoreSetup, Logger } from '@kbn/core/server'; +import type { + ExternalReferenceAttachmentType, + PersistableStateAttachmentTypeSetup, +} from '@kbn/cases-plugin/server/attachment_framework/types'; +import type { FixtureStartDeps } from './plugin'; + +const hashParts = (parts: string[]): string => { + const hash = createHash('sha1'); + const hashFeed = parts.join('-'); + return hash.update(hashFeed).digest('hex'); +}; + +const extractMigrationInfo = (type: PersistableStateAttachmentTypeSetup) => { + const migrationMap = typeof type.migrations === 'function' ? type.migrations() : type.migrations; + const migrationVersions = Object.keys(migrationMap ?? {}); + migrationVersions.sort(semverCompare); + + return { migrationVersions }; +}; + +const getExternalReferenceAttachmentTypeHash = (type: ExternalReferenceAttachmentType) => { + return hashParts([type.id]); +}; + +const getPersistableStateAttachmentTypeHash = (type: PersistableStateAttachmentTypeSetup) => { + const { migrationVersions } = extractMigrationInfo(type); + + return hashParts([type.id, migrationVersions.join(',')]); +}; + +export const registerRoutes = (core: CoreSetup, logger: Logger) => { + const router = core.http.createRouter(); + /** + * This simply wraps the cases patch case api so that we can test updating the status of an alert using + * the cases client interface instead of going through the case plugin's RESTful interface + */ + router.patch( + { + path: '/api/cases_user/cases', + validate: { + body: schema.object({}, { unknowns: 'allow' }), + }, + }, + async (context, request, response) => { + try { + const [_, { cases }] = await core.getStartServices(); + const client = await cases.getCasesClientWithRequest(request); + + return response.ok({ + body: await client.cases.update(request.body as CasesPatchRequest), + }); + } catch (error) { + logger.error(`CasesClientUser failure: ${error}`); + throw error; + } + } + ); + + router.get( + { path: '/api/cases_fixture/registered_external_reference_attachments', validate: {} }, + async (context, request, response) => { + try { + const [_, { cases }] = await core.getStartServices(); + const externalReferenceAttachmentTypeRegistry = + cases.getExternalReferenceAttachmentTypeRegistry(); + + const allTypes = externalReferenceAttachmentTypeRegistry.list(); + + const hashMap = allTypes.reduce((map, type) => { + map[type.id] = getExternalReferenceAttachmentTypeHash(type); + return map; + }, {} as Record); + + return response.ok({ + body: hashMap, + }); + } catch (error) { + logger.error(`Error : ${error}`); + throw error; + } + } + ); + + router.get( + { path: '/api/cases_fixture/registered_persistable_state_attachments', validate: {} }, + async (context, request, response) => { + try { + const [_, { cases }] = await core.getStartServices(); + const persistableStateAttachmentTypeRegistry = + cases.getPersistableStateAttachmentTypeRegistry(); + + const allTypes = persistableStateAttachmentTypeRegistry.list(); + + const hashMap = allTypes.reduce((map, type) => { + map[type.id] = getPersistableStateAttachmentTypeHash(type); + return map; + }, {} as Record); + + return response.ok({ + body: hashMap, + }); + } catch (error) { + logger.error(`Error : ${error}`); + throw error; + } + } + ); +}; diff --git a/x-pack/test/cases_api_integration/security_and_spaces/tests/common/attachments_framework/external_references.ts b/x-pack/test/cases_api_integration/security_and_spaces/tests/common/attachments_framework/external_references.ts index 56ede2f7390ca..12201c4a886cb 100644 --- a/x-pack/test/cases_api_integration/security_and_spaces/tests/common/attachments_framework/external_references.ts +++ b/x-pack/test/cases_api_integration/security_and_spaces/tests/common/attachments_framework/external_references.ts @@ -469,5 +469,41 @@ export default ({ getService }: FtrProviderContext): void => { expectedHttpCode: 400, }); }); + + it('400s when bulk creating a non registered external reference attachment type', async () => { + const postedCase = await createCase(supertest, postCaseReq); + await bulkCreateAttachments({ + supertest, + caseId: postedCase.id, + params: [ + postExternalReferenceSOReq, + { ...postExternalReferenceSOReq, externalReferenceAttachmentTypeId: 'not-exists' }, + ], + expectedHttpCode: 400, + }); + }); + + // This test is intended to fail when new external reference attachment types are registered. + // To resolve, add the new external reference attachment types ID to this list. This will trigger + // a CODEOWNERS review by Response Ops. + describe('check registered external reference attachment types', () => { + const getRegisteredTypes = () => { + return supertest + .get('/api/cases_fixture/registered_external_reference_attachments') + .expect(200) + .then((response) => response.body); + }; + + it('should check changes on all registered external reference attachment types', async () => { + const types = await getRegisteredTypes(); + + expect(types).to.eql({ + '.files': '559a37324c84f1f2eadcc5bce43115d09501ffe4', + '.test': 'ab2204830c67f5cf992c9aa2f7e3ead752cc60a1', + indicator: 'e1ea6f0518f2e0e4b0b5c0739efe805598cf2516', + osquery: '99bee68fce8ee84e81d67c536e063d3e1a2cee96', + }); + }); + }); }); }; diff --git a/x-pack/test/cases_api_integration/security_and_spaces/tests/common/attachments_framework/persistable_state.ts b/x-pack/test/cases_api_integration/security_and_spaces/tests/common/attachments_framework/persistable_state.ts index 0eef2b98c61e4..9c1fcb8871589 100644 --- a/x-pack/test/cases_api_integration/security_and_spaces/tests/common/attachments_framework/persistable_state.ts +++ b/x-pack/test/cases_api_integration/security_and_spaces/tests/common/attachments_framework/persistable_state.ts @@ -261,6 +261,19 @@ export default ({ getService }: FtrProviderContext): void => { expectedHttpCode: 400, }); }); + + it('400s when bulk creating a non registered persistable state attachment type', async () => { + const postedCase = await createCase(supertest, postCaseReq); + await bulkCreateAttachments({ + supertest, + caseId: postedCase.id, + params: [ + persistableStateAttachment, + { ...persistableStateAttachment, persistableStateAttachmentTypeId: 'not-exists' }, + ], + expectedHttpCode: 400, + }); + }); }); describe('Migrations', () => { @@ -319,5 +332,27 @@ export default ({ getService }: FtrProviderContext): void => { }); }); }); + + // This test is intended to fail when new persistable state attachment types are registered. + // To resolve, add the new persistable state attachment types ID to this list. This will trigger + // a CODEOWNERS review by Response Ops. + describe('check registered persistable state attachment types', () => { + const getRegisteredTypes = () => { + return supertest + .get('/api/cases_fixture/registered_persistable_state_attachments') + .expect(200) + .then((response) => response.body); + }; + + it('should check changes on all registered persistable state attachment types', async () => { + const types = await getRegisteredTypes(); + + expect(types).to.eql({ + '.test': 'dde5bd7492d266a0d54b77b5eddbeca95e19651c', + ml_anomaly_charts: 'f9bab0d17e31b89ae52a1b0d25fe117d9f23b38d', + ml_anomaly_swimlane: 'cf30664ea040f8e4190c816d093566ae22df54fe', + }); + }); + }); }); }; diff --git a/x-pack/test/detection_engine_api_integration/utils/prebuilt_rules/create_prebuilt_rule_saved_objects.ts b/x-pack/test/detection_engine_api_integration/utils/prebuilt_rules/create_prebuilt_rule_saved_objects.ts index 974cc4a001247..22cb88687e33b 100644 --- a/x-pack/test/detection_engine_api_integration/utils/prebuilt_rules/create_prebuilt_rule_saved_objects.ts +++ b/x-pack/test/detection_engine_api_integration/utils/prebuilt_rules/create_prebuilt_rule_saved_objects.ts @@ -6,11 +6,11 @@ */ import { Client } from '@elastic/elasticsearch'; +import { PrebuiltRuleAsset } from '@kbn/security-solution-plugin/server/lib/detection_engine/prebuilt_rules'; import { getPrebuiltRuleMock, getPrebuiltRuleWithExceptionsMock, -} from '@kbn/security-solution-plugin/common/detection_engine/prebuilt_rules/model/prebuilt_rule.mock'; -import { PrebuiltRuleToInstall } from '@kbn/security-solution-plugin/common/detection_engine/prebuilt_rules'; +} from '@kbn/security-solution-plugin/server/lib/detection_engine/prebuilt_rules/mocks'; /** * Rule signature id (`rule.rule_id`) of the prebuilt "Endpoint Security" rule. @@ -23,7 +23,7 @@ export const ELASTIC_SECURITY_RULE_ID = '9a1a2dae-0b5f-4c3d-8305-a268d404c306'; * @param overrideParams Params to override the default mock * @returns Created rule asset saved object */ -export const createRuleAssetSavedObject = (overrideParams: Partial) => ({ +export const createRuleAssetSavedObject = (overrideParams: Partial) => ({ 'security-rule': { ...getPrebuiltRuleMock(), ...overrideParams, diff --git a/x-pack/test/functional/apps/dashboard/group3/drilldowns/dashboard_to_dashboard_drilldown.ts b/x-pack/test/functional/apps/dashboard/group3/drilldowns/dashboard_to_dashboard_drilldown.ts index 4678df4038408..7cd8d5c8a455d 100644 --- a/x-pack/test/functional/apps/dashboard/group3/drilldowns/dashboard_to_dashboard_drilldown.ts +++ b/x-pack/test/functional/apps/dashboard/group3/drilldowns/dashboard_to_dashboard_drilldown.ts @@ -37,121 +37,270 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { const spaces = getService('spaces'); const elasticChart = getService('elasticChart'); + const createDrilldown = async () => { + await PageObjects.dashboard.gotoDashboardEditMode( + dashboardDrilldownsManage.DASHBOARD_WITH_PIE_CHART_NAME + ); + await PageObjects.common.clearAllToasts(); // toasts get in the way of bottom "Create drilldown" button in flyout + + // create drilldown + await dashboardPanelActions.openContextMenu(); + await dashboardDrilldownPanelActions.expectExistsCreateDrilldownAction(); + await dashboardDrilldownPanelActions.clickCreateDrilldown(); + await dashboardDrilldownsManage.expectsCreateDrilldownFlyoutOpen(); + await testSubjects.click('actionFactoryItem-DASHBOARD_TO_DASHBOARD_DRILLDOWN'); + await dashboardDrilldownsManage.fillInDashboardToDashboardDrilldownWizard({ + drilldownName: DRILLDOWN_TO_AREA_CHART_NAME, + destinationDashboardTitle: dashboardDrilldownsManage.DASHBOARD_WITH_AREA_CHART_NAME, + }); + await dashboardDrilldownsManage.saveChanges(); + await dashboardDrilldownsManage.closeFlyout(); + + // check that drilldown notification badge is shown + expect(await PageObjects.dashboard.getPanelDrilldownCount()).to.be(1); + + // save dashboard, navigate to view mode + await testSubjects.existOrFail('dashboardUnsavedChangesBadge'); + await PageObjects.dashboard.saveDashboard( + dashboardDrilldownsManage.DASHBOARD_WITH_PIE_CHART_NAME, + { + saveAsNew: false, + waitDialogIsClosed: true, + exitFromEditMode: true, + } + ); + await testSubjects.missingOrFail('dashboardUnsavedChangesBadge'); + }; + + const createControls = async ( + dashboardName: string, + controls: Array<{ field: string; type: string }> + ) => { + await PageObjects.dashboard.gotoDashboardEditMode(dashboardName); + await PageObjects.common.clearAllToasts(); // toasts get in the way of bottom "Save and close" button in create control flyout + + for (const control of controls) { + await PageObjects.dashboardControls.createControl({ + controlType: control.type, + dataViewTitle: 'logstash-*', + fieldName: control.field, + }); + } + await PageObjects.dashboard.clickQuickSave(); + }; + + const brushAreaChart = async () => { + const areaChart = await testSubjects.find('visualizationLoader'); + expect(await areaChart.getAttribute('data-title')).to.be('Visualization漢字 AreaChart'); + await browser.dragAndDrop( + { + location: areaChart, + offset: { + x: -100, + y: 0, + }, + }, + { + location: areaChart, + offset: { + x: 100, + y: 0, + }, + } + ); + }; + + const navigateAndEnsureIDChange = async (navigationTrigger: () => Promise) => { + // before executing action which would trigger navigation: remember current dashboard id in url + const oldDashboardId = await PageObjects.dashboard.getDashboardIdFromCurrentUrl(); + // execute navigation action + await navigationTrigger(); + // wait until dashboard navigates to a new dashboard with area chart + await retry.waitFor('navigate to different dashboard', async () => { + const newDashboardId = await PageObjects.dashboard.getDashboardIdFromCurrentUrl(); + return typeof newDashboardId === 'string' && oldDashboardId !== newDashboardId; + }); + await PageObjects.header.waitUntilLoadingHasFinished(); + await PageObjects.dashboard.waitForRenderComplete(); + }; + describe('Dashboard to dashboard drilldown', function () { - // FLAKY: https://github.com/elastic/kibana/issues/142715 - describe.skip('Create & use drilldowns', () => { + describe('Create & use drilldowns', () => { before(async () => { log.debug('Dashboard Drilldowns:initTests'); await security.testUser.setRoles(['test_logstash_reader', 'global_dashboard_all']); await PageObjects.common.navigateToApp('dashboard'); await PageObjects.dashboard.preserveCrossAppState(); await elasticChart.setNewChartUiDebugFlag(); + + await createDrilldown(); }); after(async () => { await security.testUser.restoreDefaults(); }); + const openContextMenuFromPieSlice = async () => { + await retry.waitFor('drilldown action menu to appear', async () => { + await pieChart.clickOnPieSlice('40000'); + return await testSubjects.exists('multipleActionsContextMenu'); + }); + }; + describe('test dashboard to dashboard drilldown', async () => { - before(async () => { - await createDrilldown(); + beforeEach(async () => { + await PageObjects.dashboard.gotoDashboardEditMode( + dashboardDrilldownsManage.DASHBOARD_WITH_PIE_CHART_NAME + ); }); - after(async () => { - await cleanFiltersAndTimePicker(dashboardDrilldownsManage.DASHBOARD_WITH_PIE_CHART_NAME); - await cleanFiltersAndTimePicker(dashboardDrilldownsManage.DASHBOARD_WITH_AREA_CHART_NAME); + afterEach(async () => { + // reset all dashboards to their last saved state by clearing session storage + await browser.clearSessionStorage(); }); - it('use dashboard to dashboard drilldown via onClick action', async () => { - await testCircularDashboardDrilldowns( - dashboardDrilldownPanelActions.clickActionByText.bind(dashboardDrilldownPanelActions) // preserve 'this' + it('drills to destination dashboard via onClick action', async () => { + await openContextMenuFromPieSlice(); + + // Navigate via drilldown, ensuring that the ID changes. + await navigateAndEnsureIDChange(async () => { + await dashboardDrilldownPanelActions.clickActionByText(DRILLDOWN_TO_AREA_CHART_NAME); + }); + + // ensure that we ended up on the correct destination dashboard. + await PageObjects.dashboard.expectOnDashboard( + dashboardDrilldownsManage.DASHBOARD_WITH_AREA_CHART_NAME ); + + // ensure drilldown creates filter on destination. + expect(await filterBar.hasFilter('memory', '40,000 to 80,000')).to.be(true); }); - it('use dashboard to dashboard drilldown via getHref action', async () => { - await testCircularDashboardDrilldowns( - dashboardDrilldownPanelActions.openHrefByText.bind(dashboardDrilldownPanelActions) // preserve 'this' + it('drills to destination dashboard via getHref action', async () => { + await openContextMenuFromPieSlice(); + + const href = await dashboardDrilldownPanelActions.getActionHrefByText( + DRILLDOWN_TO_AREA_CHART_NAME + ); + expect(typeof href).to.be('string'); // checking that action has a href + const dashboardIdFromHref = PageObjects.dashboard.getDashboardIdFromUrl(href); + + await navigateAndEnsureIDChange(async () => { + await dashboardDrilldownPanelActions.openHrefByText(DRILLDOWN_TO_AREA_CHART_NAME); + }); + + // checking that href points to the dashboard id that we have navigated to. + expect(dashboardIdFromHref).to.be( + await PageObjects.dashboard.getDashboardIdFromCurrentUrl() ); + + // ensure that we ended up on the correct destination dashboard. + await PageObjects.dashboard.expectOnDashboard( + dashboardDrilldownsManage.DASHBOARD_WITH_AREA_CHART_NAME + ); + + // ensure drilldown creates filter on destination. + expect(await filterBar.hasFilter('memory', '40,000 to 80,000')).to.be(true); }); - it('delete dashboard to dashboard drilldown', async () => { - // delete drilldown - await PageObjects.dashboard.switchToEditMode(); - await dashboardPanelActions.openContextMenu(); - await dashboardDrilldownPanelActions.expectExistsManageDrilldownsAction(); - await dashboardDrilldownPanelActions.clickManageDrilldowns(); - await dashboardDrilldownsManage.expectsManageDrilldownsFlyoutOpen(); + it('carries over all filters from source dashboard', async () => { + // add a new unrelated filter. + await filterBar.addFilter({ field: 'machine.os', operation: 'is', value: 'ios' }); - await dashboardDrilldownsManage.deleteDrilldownsByTitles([DRILLDOWN_TO_AREA_CHART_NAME]); - await dashboardDrilldownsManage.closeFlyout(); + await openContextMenuFromPieSlice(); - // check that drilldown notification badge is not shown - expect(await PageObjects.dashboard.getPanelDrilldownCount()).to.be(0); + // Navigate via drilldown, ensuring that the ID changes. + await navigateAndEnsureIDChange(async () => { + await dashboardDrilldownPanelActions.clickActionByText(DRILLDOWN_TO_AREA_CHART_NAME); + }); + + // ensure that we ended up on the correct destination dashboard. + await PageObjects.dashboard.expectOnDashboard( + dashboardDrilldownsManage.DASHBOARD_WITH_AREA_CHART_NAME + ); + + // ensure that the unrelated filter has been carried over. + expect(await filterBar.hasFilter('machine.os', 'ios')).to.be(true); }); - it('browser back/forward navigation works after drilldown navigation', async () => { - await PageObjects.dashboard.loadSavedDashboard( + it('carries over time range from source dashboard', async () => { + const startTime = 'Sep 22, 2015 @ 06:31:44.000'; + const endTime = 'Sep 23, 2015 @ 06:31:44.000'; + await PageObjects.timePicker.setAbsoluteRange(startTime, endTime); + + // Navigate via drilldown, ensuring that the ID changes. + await openContextMenuFromPieSlice(); + await navigateAndEnsureIDChange(async () => { + await dashboardDrilldownPanelActions.clickActionByText(DRILLDOWN_TO_AREA_CHART_NAME); + }); + + // ensure that we ended up on the correct destination dashboard. + await PageObjects.dashboard.expectOnDashboard( dashboardDrilldownsManage.DASHBOARD_WITH_AREA_CHART_NAME ); - const originalTimeRangeDurationHours = - await PageObjects.timePicker.getTimeDurationInHours(); - await brushAreaChart(); - await dashboardDrilldownPanelActions.expectMultipleActionsMenuOpened(); - await navigateWithinDashboard(async () => { - await dashboardDrilldownPanelActions.clickActionByText(DRILLDOWN_TO_PIE_CHART_NAME); + + // ensure that the time range has been carried over. + expect(await PageObjects.timePicker.getTimeDurationInHours()).to.be(24); + + // reset time range + await PageObjects.timePicker.setDefaultAbsoluteRange(); + }); + + it('browser back navigation works after drilldown navigation', async () => { + await openContextMenuFromPieSlice(); + + // Navigate via drilldown, ensuring that the ID changes. + await navigateAndEnsureIDChange(async () => { + await dashboardDrilldownPanelActions.clickActionByText(DRILLDOWN_TO_AREA_CHART_NAME); }); - // check that new time range duration was applied - const newTimeRangeDurationHours = await PageObjects.timePicker.getTimeDurationInHours(); - expect(newTimeRangeDurationHours).to.be.lessThan(originalTimeRangeDurationHours); - await navigateWithinDashboard(async () => { + // ensure that we ended up on the correct destination dashboard. + await PageObjects.dashboard.expectOnDashboard( + dashboardDrilldownsManage.DASHBOARD_WITH_AREA_CHART_NAME + ); + + await navigateAndEnsureIDChange(async () => { await browser.goBack(); }); - expect(await PageObjects.timePicker.getTimeDurationInHours()).to.be( - originalTimeRangeDurationHours + // ensure that we have returned to the origin dashboard. + await PageObjects.dashboard.clickCancelOutOfEditMode(); + await PageObjects.dashboard.waitForRenderComplete(); + + await PageObjects.dashboard.expectOnDashboard( + dashboardDrilldownsManage.DASHBOARD_WITH_PIE_CHART_NAME ); }); - const testCircularDashboardDrilldowns = async ( - drilldownAction: (text: string) => Promise - ) => { - await testPieChartDashboardDrilldown(drilldownAction); - expect(await filterBar.getFilterCount()).to.be(1); - - const originalTimeRangeDurationHours = - await PageObjects.timePicker.getTimeDurationInHours(); - await PageObjects.dashboard.clearUnsavedChanges(); - - // brush area chart and drilldown back to pie chat dashboard - await brushAreaChart(); - await dashboardDrilldownPanelActions.expectMultipleActionsMenuOpened(); - await navigateWithinDashboard(async () => { - await drilldownAction(DRILLDOWN_TO_PIE_CHART_NAME); - }); + it('delete dashboard to dashboard drilldown', async () => { + // delete drilldown + await PageObjects.dashboard.switchToEditMode(); + await dashboardPanelActions.openContextMenu(); + await dashboardDrilldownPanelActions.expectExistsManageDrilldownsAction(); + await dashboardDrilldownPanelActions.clickManageDrilldowns(); + await dashboardDrilldownsManage.expectsManageDrilldownsFlyoutOpen(); - // because filters are preserved during navigation, we expect that only one slice is displayed (filter is still applied) - expect(await filterBar.getFilterCount()).to.be(1); - await pieChart.expectPieSliceCount(1); - // check that new time range duration was applied - const newTimeRangeDurationHours = await PageObjects.timePicker.getTimeDurationInHours(); - expect(newTimeRangeDurationHours).to.be.lessThan(originalTimeRangeDurationHours); - await PageObjects.dashboard.clearUnsavedChanges(); - }; + await dashboardDrilldownsManage.deleteDrilldownsByTitles([DRILLDOWN_TO_AREA_CHART_NAME]); + await dashboardDrilldownsManage.closeFlyout(); + + // check that drilldown notification badge is not shown + expect(await PageObjects.dashboard.getPanelDrilldownCount()).to.be(0); + + // this drilldown will be available again in the next test because the session storage is cleared. + }); + }); - const cleanFiltersAndTimePicker = async (dashboardName: string) => { + describe('test dashboard to dashboard drilldown with controls', async () => { + const cleanFiltersAndControls = async (dashboardName: string) => { await PageObjects.dashboard.gotoDashboardEditMode(dashboardName); await filterBar.removeAllFilters(); - await PageObjects.timePicker.setDefaultAbsoluteRange(); - await PageObjects.dashboard.clearUnsavedChanges(); + await PageObjects.dashboardControls.deleteAllControls(); + await PageObjects.dashboard.clickQuickSave(); }; - }); - describe('test dashboard to dashboard drilldown with controls', async () => { before('add controls and make selections', async () => { /** Source Dashboard */ - await createDrilldown(); - await addControls(dashboardDrilldownsManage.DASHBOARD_WITH_PIE_CHART_NAME, [ + await createControls(dashboardDrilldownsManage.DASHBOARD_WITH_PIE_CHART_NAME, [ { field: 'geo.src', type: OPTIONS_LIST_CONTROL }, { field: 'bytes', type: RANGE_SLIDER_CONTROL }, ]); @@ -167,7 +316,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { await PageObjects.dashboard.waitForRenderComplete(); /** Destination Dashboard */ - await addControls(dashboardDrilldownsManage.DASHBOARD_WITH_AREA_CHART_NAME, [ + await createControls(dashboardDrilldownsManage.DASHBOARD_WITH_AREA_CHART_NAME, [ { field: 'geo.src', type: OPTIONS_LIST_CONTROL }, ]); }); @@ -177,39 +326,19 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { await cleanFiltersAndControls(dashboardDrilldownsManage.DASHBOARD_WITH_AREA_CHART_NAME); }); - it('use dashboard to dashboard drilldown via onClick action', async () => { - await testSingleDashboardDrilldown( - dashboardDrilldownPanelActions.clickActionByText.bind(dashboardDrilldownPanelActions) // preserve 'this' + it('creates filter pills representing controls selections', async () => { + await PageObjects.dashboard.gotoDashboardEditMode( + dashboardDrilldownsManage.DASHBOARD_WITH_PIE_CHART_NAME ); - }); - it('use dashboard to dashboard drilldown via getHref action', async () => { - await testSingleDashboardDrilldown( - dashboardDrilldownPanelActions.openHrefByText.bind(dashboardDrilldownPanelActions) // preserve 'this' - ); - }); + await openContextMenuFromPieSlice(); - const addControls = async ( - dashboardName: string, - controls: Array<{ field: string; type: string }> - ) => { - await PageObjects.dashboard.gotoDashboardEditMode(dashboardName); - await PageObjects.common.clearAllToasts(); // toasts get in the way of bottom "Save and close" button in create control flyout - - for (const control of controls) { - await PageObjects.dashboardControls.createControl({ - controlType: control.type, - dataViewTitle: 'logstash-*', - fieldName: control.field, - }); - } - await PageObjects.dashboard.clickQuickSave(); - }; + await navigateAndEnsureIDChange(async () => { + await dashboardDrilldownPanelActions.clickActionByText(DRILLDOWN_TO_AREA_CHART_NAME); + }); - const testSingleDashboardDrilldown = async ( - drilldownAction: (text: string) => Promise - ) => { - await testPieChartDashboardDrilldown(drilldownAction); + // check that we drilled-down with filter from pie chart + expect(await filterBar.hasFilter('memory', '40,000 to 80,000')).to.be(true); // drilldown creates filter pills for control selections expect(await filterBar.hasFilter('geo.src', 'CN, US')).to.be(true); @@ -223,93 +352,13 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { await PageObjects.dashboardControls.optionsListPopoverGetAvailableOptionsCount() ).to.equal(2); await PageObjects.dashboardControls.optionsListEnsurePopoverIsClosed(optionsListControl); - - // can clear unsaved changes badge after drilldown with controls - await PageObjects.dashboard.clearUnsavedChanges(); - - // clean up filters in destination dashboard - await filterBar.removeAllFilters(); - expect(await filterBar.getFilterCount()).to.be(0); - await PageObjects.dashboard.clickQuickSave(); - }; - - const cleanFiltersAndControls = async (dashboardName: string) => { - await PageObjects.dashboard.gotoDashboardEditMode(dashboardName); - await filterBar.removeAllFilters(); - await PageObjects.dashboardControls.deleteAllControls(); - await PageObjects.dashboard.clickQuickSave(); - }; - }); - - const createDrilldown = async () => { - await PageObjects.dashboard.gotoDashboardEditMode( - dashboardDrilldownsManage.DASHBOARD_WITH_PIE_CHART_NAME - ); - await PageObjects.common.clearAllToasts(); // toasts get in the way of bottom "Create drilldown" button in flyout - // create drilldown - await dashboardPanelActions.openContextMenu(); - await dashboardDrilldownPanelActions.expectExistsCreateDrilldownAction(); - await dashboardDrilldownPanelActions.clickCreateDrilldown(); - await dashboardDrilldownsManage.expectsCreateDrilldownFlyoutOpen(); - await testSubjects.click('actionFactoryItem-DASHBOARD_TO_DASHBOARD_DRILLDOWN'); - await dashboardDrilldownsManage.fillInDashboardToDashboardDrilldownWizard({ - drilldownName: DRILLDOWN_TO_AREA_CHART_NAME, - destinationDashboardTitle: dashboardDrilldownsManage.DASHBOARD_WITH_AREA_CHART_NAME, }); - await dashboardDrilldownsManage.saveChanges(); - await dashboardDrilldownsManage.closeFlyout(); - - // check that drilldown notification badge is shown - expect(await PageObjects.dashboard.getPanelDrilldownCount()).to.be(1); - - // save dashboard, navigate to view mode - await testSubjects.existOrFail('dashboardUnsavedChangesBadge'); - await PageObjects.dashboard.saveDashboard( - dashboardDrilldownsManage.DASHBOARD_WITH_PIE_CHART_NAME, - { - saveAsNew: false, - waitDialogIsClosed: true, - exitFromEditMode: true, - } - ); - await testSubjects.missingOrFail('dashboardUnsavedChangesBadge'); - }; - - const testPieChartDashboardDrilldown = async ( - drilldownAction: (text: string) => Promise - ) => { - await PageObjects.dashboard.gotoDashboardEditMode( - dashboardDrilldownsManage.DASHBOARD_WITH_PIE_CHART_NAME - ); - - // trigger drilldown action by clicking on a pie and picking drilldown action by it's name - await retry.waitFor('drilldown action menu to appear', async () => { - // avoid flakiness of context menu opening - await pieChart.clickOnPieSlice('40000'); // - return await testSubjects.exists('multipleActionsContextMenu'); - }); - - const href = await dashboardDrilldownPanelActions.getActionHrefByText( - DRILLDOWN_TO_AREA_CHART_NAME - ); - expect(typeof href).to.be('string'); // checking that action has a href - const dashboardIdFromHref = PageObjects.dashboard.getDashboardIdFromUrl(href); - - await navigateWithinDashboard(async () => { - await drilldownAction(DRILLDOWN_TO_AREA_CHART_NAME); - }); - // checking that href is at least pointing to the same dashboard that we are navigated to by regular click - expect(dashboardIdFromHref).to.be( - await PageObjects.dashboard.getDashboardIdFromCurrentUrl() - ); - - // check that we drilled-down with filter from pie chart - expect(await filterBar.hasFilter('memory', '40,000 to 80,000')).to.be(true); - }; + }); }); describe('Copy to space', () => { const destinationSpaceId = 'custom_space'; + before(async () => { await spaces.create({ id: destinationSpaceId, @@ -364,7 +413,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { await brushAreaChart(); await dashboardDrilldownPanelActions.expectMultipleActionsMenuOpened(); - await navigateWithinDashboard(async () => { + await navigateAndEnsureIDChange(async () => { await dashboardDrilldownPanelActions.clickActionByText(DRILLDOWN_TO_PIE_CHART_NAME); }); await elasticChart.setNewChartUiDebugFlag(); @@ -373,40 +422,4 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { }); }); }); - - // utils which shouldn't be a part of test flow, but also too specific to be moved to pageobject or service - async function brushAreaChart() { - const areaChart = await testSubjects.find('visualizationLoader'); - expect(await areaChart.getAttribute('data-title')).to.be('Visualization漢字 AreaChart'); - await browser.dragAndDrop( - { - location: areaChart, - offset: { - x: -100, - y: 0, - }, - }, - { - location: areaChart, - offset: { - x: 100, - y: 0, - }, - } - ); - } - - async function navigateWithinDashboard(navigationTrigger: () => Promise) { - // before executing action which would trigger navigation: remember current dashboard id in url - const oldDashboardId = await PageObjects.dashboard.getDashboardIdFromCurrentUrl(); - // execute navigation action - await navigationTrigger(); - // wait until dashboard navigates to a new dashboard with area chart - await retry.waitFor('navigate to different dashboard', async () => { - const newDashboardId = await PageObjects.dashboard.getDashboardIdFromCurrentUrl(); - return typeof newDashboardId === 'string' && oldDashboardId !== newDashboardId; - }); - await PageObjects.header.waitUntilLoadingHasFinished(); - await PageObjects.dashboard.waitForRenderComplete(); - } } diff --git a/x-pack/test/functional/es_archives/monitoring/logstash/changing_pipelines_mb/data.json.gz b/x-pack/test/functional/es_archives/monitoring/logstash/changing_pipelines_mb/data.json.gz deleted file mode 100644 index b6085b3119f2b..0000000000000 Binary files a/x-pack/test/functional/es_archives/monitoring/logstash/changing_pipelines_mb/data.json.gz and /dev/null differ diff --git a/x-pack/test/functional/es_archives/monitoring/logstash/changing_pipelines_package/data.json.gz b/x-pack/test/functional/es_archives/monitoring/logstash/changing_pipelines_package/data.json.gz deleted file mode 100644 index 1e969bb77a409..0000000000000 Binary files a/x-pack/test/functional/es_archives/monitoring/logstash/changing_pipelines_package/data.json.gz and /dev/null differ diff --git a/x-pack/test/functional/es_archives/monitoring/logstash/changing_pipelines_package/mappings.json b/x-pack/test/functional/es_archives/monitoring/logstash/changing_pipelines_package/mappings.json deleted file mode 100644 index 5684702273ac7..0000000000000 --- a/x-pack/test/functional/es_archives/monitoring/logstash/changing_pipelines_package/mappings.json +++ /dev/null @@ -1,7138 +0,0 @@ -{ - "type": "data_stream", - "value": { - "data_stream": "metrics-elasticsearch.stack_monitoring.cluster_stats-default", - "template": { - "_meta": { - "managed": true, - "managed_by": "fleet", - "package": { - "name": "elasticsearch" - } - }, - "data_stream": { - "allow_custom_routing": false, - "hidden": false - }, - "index_patterns": [ - "metrics-elasticsearch.stack_monitoring.cluster_stats-*" - ], - "name": "metrics-elasticsearch.stack_monitoring.cluster_stats", - "priority": 200, - "template": { - "mappings": { - "_meta": { - "managed": true, - "managed_by": "fleet", - "package": { - "name": "elasticsearch" - } - }, - "date_detection": false, - "dynamic": false, - "dynamic_templates": [ - { - "strings_as_keyword": { - "mapping": { - "ignore_above": 1024, - "type": "keyword" - }, - "match_mapping_type": "string" - } - } - ], - "properties": { - "@timestamp": { - "type": "date" - }, - "cluster_state": { - "properties": { - "master_node": { - "path": "elasticsearch.cluster.stats.state.master_node", - "type": "alias" - }, - "nodes_hash": { - "path": "elasticsearch.cluster.stats.state.nodes_hash", - "type": "alias" - }, - "state_uuid": { - "path": "elasticsearch.cluster.stats.state.state_uuid", - "type": "alias" - }, - "status": { - "path": "elasticsearch.cluster.stats.status", - "type": "alias" - }, - "version": { - "path": "elasticsearch.cluster.stats.state.version", - "type": "alias" - } - } - }, - "cluster_stats": { - "properties": { - "indices": { - "properties": { - "count": { - "path": "elasticsearch.cluster.stats.indices.total", - "type": "alias" - }, - "shards": { - "properties": { - "total": { - "path": "elasticsearch.cluster.stats.indices.shards.count", - "type": "alias" - } - } - } - } - }, - "nodes": { - "properties": { - "count": { - "properties": { - "total": { - "path": "elasticsearch.cluster.stats.nodes.count", - "type": "alias" - } - } - }, - "jvm": { - "properties": { - "max_uptime_in_millis": { - "path": "elasticsearch.cluster.stats.nodes.jvm.max_uptime.ms", - "type": "alias" - }, - "mem": { - "properties": { - "heap_max_in_bytes": { - "path": "elasticsearch.cluster.stats.nodes.jvm.memory.heap.max.bytes", - "type": "alias" - }, - "heap_used_in_bytes": { - "path": "elasticsearch.cluster.stats.nodes.jvm.memory.heap.used.bytes", - "type": "alias" - } - } - } - } - } - } - } - } - }, - "cluster_uuid": { - "path": "elasticsearch.cluster.id", - "type": "alias" - }, - "data_stream": { - "properties": { - "dataset": { - "type": "constant_keyword" - }, - "namespace": { - "type": "constant_keyword" - }, - "type": { - "type": "constant_keyword" - } - } - }, - "ecs": { - "properties": { - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "elasticsearch": { - "properties": { - "cluster": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "state": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "stats": { - "properties": { - "indices": { - "properties": { - "docs": { - "properties": { - "total": { - "type": "long" - } - } - }, - "fielddata": { - "properties": { - "memory": { - "properties": { - "bytes": { - "type": "long" - } - } - } - } - }, - "shards": { - "properties": { - "count": { - "type": "long" - }, - "primaries": { - "type": "long" - } - } - }, - "store": { - "properties": { - "size": { - "properties": { - "bytes": { - "type": "long" - } - } - } - } - }, - "total": { - "type": "long" - } - } - }, - "license": { - "properties": { - "cluster_needs_tls": { - "type": "boolean" - }, - "expiry_date": { - "type": "date" - }, - "expiry_date_in_millis": { - "type": "long" - }, - "issue_date": { - "type": "date" - }, - "issue_date_in_millis": { - "type": "long" - }, - "issued_to": { - "ignore_above": 1024, - "type": "keyword" - }, - "issuer": { - "ignore_above": 1024, - "type": "keyword" - }, - "max_nodes": { - "type": "long" - }, - "start_date_in_millis": { - "type": "long" - }, - "status": { - "ignore_above": 1024, - "type": "keyword" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - }, - "uid": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "nodes": { - "properties": { - "count": { - "type": "long" - }, - "data": { - "type": "long" - }, - "fs": { - "properties": { - "available": { - "properties": { - "bytes": { - "type": "long" - } - } - }, - "total": { - "properties": { - "bytes": { - "type": "long" - } - } - } - } - }, - "jvm": { - "properties": { - "max_uptime": { - "properties": { - "ms": { - "type": "long" - } - } - }, - "memory": { - "properties": { - "heap": { - "properties": { - "max": { - "properties": { - "bytes": { - "type": "long" - } - } - }, - "used": { - "properties": { - "bytes": { - "type": "long" - } - } - } - } - } - } - } - } - }, - "master": { - "type": "long" - }, - "stats": { - "properties": { - "data": { - "type": "long" - } - } - }, - "versions": { - "type": "text" - } - } - }, - "stack": { - "properties": { - "apm": { - "properties": { - "found": { - "type": "boolean" - } - } - }, - "xpack": { - "properties": { - "ccr": { - "properties": { - "available": { - "type": "boolean" - }, - "enabled": { - "type": "boolean" - } - } - } - } - } - } - }, - "state": { - "properties": { - "master_node": { - "ignore_above": 1024, - "type": "keyword" - }, - "nodes": { - "type": "flattened" - }, - "nodes_hash": { - "ignore_above": 1024, - "type": "keyword" - }, - "state_uuid": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "status": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "node": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "master": { - "type": "boolean" - }, - "mlockall": { - "type": "boolean" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "error": { - "properties": { - "message": { - "type": "match_only_text" - } - } - }, - "event": { - "properties": { - "agent_id_status": { - "ignore_above": 1024, - "type": "keyword" - }, - "dataset": { - "ignore_above": 1024, - "type": "keyword" - }, - "duration": { - "type": "long" - }, - "ingested": { - "format": "strict_date_time_no_millis||strict_date_optional_time||epoch_millis", - "type": "date" - }, - "module": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "host": { - "properties": { - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "license": { - "properties": { - "status": { - "path": "elasticsearch.cluster.stats.license.status", - "type": "alias" - }, - "type": { - "path": "elasticsearch.cluster.stats.license.type", - "type": "alias" - } - } - }, - "service": { - "properties": { - "address": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "source_node": { - "properties": { - "name": { - "path": "elasticsearch.node.name", - "type": "alias" - }, - "uuid": { - "path": "elasticsearch.node.id", - "type": "alias" - } - } - }, - "stack_stats": { - "properties": { - "apm": { - "properties": { - "found": { - "path": "elasticsearch.cluster.stats.stack.apm.found", - "type": "alias" - } - } - }, - "xpack": { - "properties": { - "ccr": { - "properties": { - "available": { - "path": "elasticsearch.cluster.stats.stack.xpack.ccr.available", - "type": "alias" - }, - "enabled": { - "path": "elasticsearch.cluster.stats.stack.xpack.ccr.enabled", - "type": "alias" - } - } - } - } - } - } - }, - "timestamp": { - "path": "@timestamp", - "type": "alias" - } - } - }, - "settings": { - "index": { - "codec": "best_compression", - "lifecycle": { - "name": "metrics" - }, - "mapping": { - "total_fields": { - "limit": "10000" - } - }, - "query": { - "default_field": [ - "ecs.version", - "event.dataset", - "event.module", - "host.name", - "service.address", - "service.type", - "service.name", - "error.message", - "elasticsearch.cluster.stats.version", - "elasticsearch.cluster.stats.state.nodes_hash", - "elasticsearch.cluster.stats.state.master_node", - "elasticsearch.cluster.stats.state.version", - "elasticsearch.cluster.stats.state.state_uuid", - "elasticsearch.cluster.stats.status", - "elasticsearch.cluster.stats.nodes.versions", - "elasticsearch.cluster.stats.license.issued_to", - "elasticsearch.cluster.stats.license.issuer", - "elasticsearch.cluster.stats.license.status", - "elasticsearch.cluster.stats.license.type", - "elasticsearch.cluster.stats.license.uid", - "elasticsearch.cluster.name", - "elasticsearch.cluster.id", - "elasticsearch.cluster.state.id", - "elasticsearch.version", - "elasticsearch.node.id", - "elasticsearch.node.name" - ] - } - } - } - } - } - } -} - -{ - "type": "data_stream", - "value": { - "data_stream": "metrics-elasticsearch.stack_monitoring.enrich-default", - "template": { - "_meta": { - "managed": true, - "managed_by": "fleet", - "package": { - "name": "elasticsearch" - } - }, - "data_stream": { - "allow_custom_routing": false, - "hidden": false - }, - "index_patterns": [ - "metrics-elasticsearch.stack_monitoring.enrich-*" - ], - "name": "metrics-elasticsearch.stack_monitoring.enrich", - "priority": 200, - "template": { - "mappings": { - "_meta": { - "managed": true, - "managed_by": "fleet", - "package": { - "name": "elasticsearch" - } - }, - "date_detection": false, - "dynamic": false, - "dynamic_templates": [ - { - "strings_as_keyword": { - "mapping": { - "ignore_above": 1024, - "type": "keyword" - }, - "match_mapping_type": "string" - } - } - ], - "properties": { - "@timestamp": { - "type": "date" - }, - "cluster_uuid": { - "path": "elasticsearch.cluster.id", - "type": "alias" - }, - "data_stream": { - "properties": { - "dataset": { - "type": "constant_keyword" - }, - "namespace": { - "type": "constant_keyword" - }, - "type": { - "type": "constant_keyword" - } - } - }, - "ecs": { - "properties": { - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "elasticsearch": { - "properties": { - "cluster": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "state": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "enrich": { - "properties": { - "executed_searches": { - "properties": { - "total": { - "type": "long" - } - } - }, - "executing_policy": { - "properties": { - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "task": { - "properties": { - "action": { - "ignore_above": 1024, - "type": "keyword" - }, - "cancellable": { - "type": "boolean" - }, - "id": { - "type": "long" - }, - "parent_task_id": { - "ignore_above": 1024, - "type": "keyword" - }, - "task": { - "ignore_above": 1024, - "type": "keyword" - }, - "time": { - "properties": { - "running": { - "properties": { - "nano": { - "type": "long" - } - } - }, - "start": { - "properties": { - "ms": { - "type": "long" - } - } - } - } - } - } - } - } - }, - "queue": { - "properties": { - "size": { - "type": "long" - } - } - }, - "remote_requests": { - "properties": { - "current": { - "type": "long" - }, - "total": { - "type": "long" - } - } - } - } - }, - "node": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "master": { - "type": "boolean" - }, - "mlockall": { - "type": "boolean" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "error": { - "properties": { - "message": { - "type": "match_only_text" - } - } - }, - "event": { - "properties": { - "agent_id_status": { - "ignore_above": 1024, - "type": "keyword" - }, - "dataset": { - "ignore_above": 1024, - "type": "keyword" - }, - "duration": { - "type": "long" - }, - "ingested": { - "format": "strict_date_time_no_millis||strict_date_optional_time||epoch_millis", - "type": "date" - }, - "module": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "host": { - "properties": { - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "service": { - "properties": { - "address": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "source_node": { - "properties": { - "name": { - "path": "elasticsearch.node.name", - "type": "alias" - }, - "uuid": { - "path": "elasticsearch.node.id", - "type": "alias" - } - } - }, - "timestamp": { - "path": "@timestamp", - "type": "alias" - } - } - }, - "settings": { - "index": { - "codec": "best_compression", - "lifecycle": { - "name": "metrics" - }, - "mapping": { - "total_fields": { - "limit": "10000" - } - }, - "query": { - "default_field": [ - "ecs.version", - "event.dataset", - "event.module", - "host.name", - "service.address", - "service.type", - "service.name", - "error.message", - "elasticsearch.enrich.executing_policy.name", - "elasticsearch.enrich.executing_policy.task.task", - "elasticsearch.enrich.executing_policy.task.action", - "elasticsearch.enrich.executing_policy.task.parent_task_id", - "elasticsearch.cluster.name", - "elasticsearch.cluster.id", - "elasticsearch.cluster.state.id", - "elasticsearch.node.id", - "elasticsearch.node.name" - ] - } - } - } - } - } - } -} - -{ - "type": "data_stream", - "value": { - "data_stream": "metrics-elasticsearch.stack_monitoring.index-default", - "template": { - "_meta": { - "managed": true, - "managed_by": "fleet", - "package": { - "name": "elasticsearch" - } - }, - "data_stream": { - "allow_custom_routing": false, - "hidden": false - }, - "index_patterns": [ - "metrics-elasticsearch.stack_monitoring.index-*" - ], - "name": "metrics-elasticsearch.stack_monitoring.index", - "priority": 200, - "template": { - "mappings": { - "_meta": { - "managed": true, - "managed_by": "fleet", - "package": { - "name": "elasticsearch" - } - }, - "date_detection": false, - "dynamic": false, - "dynamic_templates": [ - { - "strings_as_keyword": { - "mapping": { - "ignore_above": 1024, - "type": "keyword" - }, - "match_mapping_type": "string" - } - } - ], - "properties": { - "@timestamp": { - "type": "date" - }, - "cluster_uuid": { - "path": "elasticsearch.cluster.id", - "type": "alias" - }, - "data_stream": { - "properties": { - "dataset": { - "type": "constant_keyword" - }, - "namespace": { - "type": "constant_keyword" - }, - "type": { - "type": "constant_keyword" - } - } - }, - "ecs": { - "properties": { - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "elasticsearch": { - "properties": { - "cluster": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "state": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "index": { - "properties": { - "created": { - "type": "long" - }, - "hidden": { - "type": "boolean" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "primaries": { - "properties": { - "docs": { - "properties": { - "count": { - "type": "long" - }, - "deleted": { - "type": "long" - } - } - }, - "indexing": { - "properties": { - "index_time_in_millis": { - "type": "long" - }, - "index_total": { - "type": "long" - }, - "throttle_time_in_millis": { - "type": "long" - } - } - }, - "merges": { - "properties": { - "total_size_in_bytes": { - "type": "long" - } - } - }, - "query_cache": { - "properties": { - "hit_count": { - "type": "long" - }, - "memory_size_in_bytes": { - "type": "long" - }, - "miss_count": { - "type": "long" - } - } - }, - "refresh": { - "properties": { - "external_total_time_in_millis": { - "type": "long" - }, - "total_time_in_millis": { - "type": "long" - } - } - }, - "request_cache": { - "properties": { - "evictions": { - "type": "long" - }, - "hit_count": { - "type": "long" - }, - "memory_size_in_bytes": { - "type": "long" - }, - "miss_count": { - "type": "long" - } - } - }, - "search": { - "properties": { - "query_time_in_millis": { - "type": "long" - }, - "query_total": { - "type": "long" - } - } - }, - "segments": { - "properties": { - "count": { - "type": "long" - }, - "doc_values_memory_in_bytes": { - "type": "long" - }, - "fixed_bit_set_memory_in_bytes": { - "type": "long" - }, - "index_writer_memory_in_bytes": { - "type": "long" - }, - "memory_in_bytes": { - "type": "long" - }, - "norms_memory_in_bytes": { - "type": "long" - }, - "points_memory_in_bytes": { - "type": "long" - }, - "stored_fields_memory_in_bytes": { - "type": "long" - }, - "term_vectors_memory_in_bytes": { - "type": "long" - }, - "terms_memory_in_bytes": { - "type": "long" - }, - "version_map_memory_in_bytes": { - "type": "long" - } - } - }, - "store": { - "properties": { - "size_in_bytes": { - "type": "long" - } - } - } - } - }, - "shards": { - "properties": { - "primaries": { - "type": "long" - }, - "total": { - "type": "long" - } - } - }, - "status": { - "ignore_above": 1024, - "type": "keyword" - }, - "total": { - "properties": { - "bulk": { - "properties": { - "avg_size_in_bytes": { - "type": "long" - }, - "avg_time_in_millis": { - "type": "long" - }, - "total_operations": { - "type": "long" - }, - "total_size_in_bytes": { - "type": "long" - }, - "total_time_in_millis": { - "type": "long" - } - } - }, - "docs": { - "properties": { - "count": { - "type": "long" - }, - "deleted": { - "type": "long" - } - } - }, - "fielddata": { - "properties": { - "evictions": { - "type": "long" - }, - "memory_size_in_bytes": { - "type": "long" - } - } - }, - "indexing": { - "properties": { - "index_time_in_millis": { - "type": "long" - }, - "index_total": { - "type": "long" - }, - "throttle_time_in_millis": { - "type": "long" - } - } - }, - "merges": { - "properties": { - "total_size_in_bytes": { - "type": "long" - } - } - }, - "query_cache": { - "properties": { - "evictions": { - "type": "long" - }, - "hit_count": { - "type": "long" - }, - "memory_size_in_bytes": { - "type": "long" - }, - "miss_count": { - "type": "long" - } - } - }, - "refresh": { - "properties": { - "external_total_time_in_millis": { - "type": "long" - }, - "total_time_in_millis": { - "type": "long" - } - } - }, - "request_cache": { - "properties": { - "evictions": { - "type": "long" - }, - "hit_count": { - "type": "long" - }, - "memory_size_in_bytes": { - "type": "long" - }, - "miss_count": { - "type": "long" - } - } - }, - "search": { - "properties": { - "query_time_in_millis": { - "type": "long" - }, - "query_total": { - "type": "long" - } - } - }, - "segments": { - "properties": { - "count": { - "type": "long" - }, - "doc_values_memory_in_bytes": { - "type": "long" - }, - "fixed_bit_set_memory_in_bytes": { - "type": "long" - }, - "index_writer_memory_in_bytes": { - "type": "long" - }, - "memory": { - "properties": { - "bytes": { - "type": "long" - } - } - }, - "memory_in_bytes": { - "type": "long" - }, - "norms_memory_in_bytes": { - "type": "long" - }, - "points_memory_in_bytes": { - "type": "long" - }, - "stored_fields_memory_in_bytes": { - "type": "long" - }, - "term_vectors_memory_in_bytes": { - "type": "long" - }, - "terms_memory_in_bytes": { - "type": "long" - }, - "version_map_memory_in_bytes": { - "type": "long" - } - } - }, - "store": { - "properties": { - "size": { - "properties": { - "bytes": { - "type": "long" - } - } - }, - "size_in_bytes": { - "type": "long" - } - } - } - } - }, - "uuid": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "node": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "master": { - "type": "boolean" - }, - "mlockall": { - "type": "boolean" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "error": { - "properties": { - "message": { - "type": "match_only_text" - } - } - }, - "event": { - "properties": { - "agent_id_status": { - "ignore_above": 1024, - "type": "keyword" - }, - "dataset": { - "ignore_above": 1024, - "type": "keyword" - }, - "duration": { - "type": "long" - }, - "ingested": { - "format": "strict_date_time_no_millis||strict_date_optional_time||epoch_millis", - "type": "date" - }, - "module": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "host": { - "properties": { - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "index_stats": { - "properties": { - "index": { - "path": "elasticsearch.index.name", - "type": "alias" - }, - "primaries": { - "properties": { - "docs": { - "properties": { - "count": { - "path": "elasticsearch.index.primaries.docs.count", - "type": "alias" - } - } - }, - "indexing": { - "properties": { - "index_time_in_millis": { - "path": "elasticsearch.index.primaries.indexing.index_time_in_millis", - "type": "alias" - }, - "index_total": { - "path": "elasticsearch.index.primaries.indexing.index_total", - "type": "alias" - }, - "throttle_time_in_millis": { - "path": "elasticsearch.index.primaries.indexing.throttle_time_in_millis", - "type": "alias" - } - } - }, - "merges": { - "properties": { - "total_size_in_bytes": { - "path": "elasticsearch.index.primaries.merges.total_size_in_bytes", - "type": "alias" - } - } - }, - "refresh": { - "properties": { - "total_time_in_millis": { - "path": "elasticsearch.index.primaries.refresh.total_time_in_millis", - "type": "alias" - } - } - }, - "segments": { - "properties": { - "count": { - "path": "elasticsearch.index.primaries.segments.count", - "type": "alias" - } - } - }, - "store": { - "properties": { - "size_in_bytes": { - "path": "elasticsearch.index.primaries.store.size_in_bytes", - "type": "alias" - } - } - } - } - }, - "total": { - "properties": { - "fielddata": { - "properties": { - "memory_size_in_bytes": { - "path": "elasticsearch.index.total.fielddata.memory_size_in_bytes", - "type": "alias" - } - } - }, - "indexing": { - "properties": { - "index_time_in_millis": { - "path": "elasticsearch.index.total.indexing.index_time_in_millis", - "type": "alias" - }, - "index_total": { - "path": "elasticsearch.index.total.indexing.index_total", - "type": "alias" - }, - "throttle_time_in_millis": { - "path": "elasticsearch.index.total.indexing.throttle_time_in_millis", - "type": "alias" - } - } - }, - "merges": { - "properties": { - "total_size_in_bytes": { - "path": "elasticsearch.index.total.merges.total_size_in_bytes", - "type": "alias" - } - } - }, - "query_cache": { - "properties": { - "memory_size_in_bytes": { - "path": "elasticsearch.index.total.query_cache.memory_size_in_bytes", - "type": "alias" - } - } - }, - "refresh": { - "properties": { - "total_time_in_millis": { - "path": "elasticsearch.index.total.refresh.total_time_in_millis", - "type": "alias" - } - } - }, - "request_cache": { - "properties": { - "memory_size_in_bytes": { - "path": "elasticsearch.index.total.request_cache.memory_size_in_bytes", - "type": "alias" - } - } - }, - "search": { - "properties": { - "query_time_in_millis": { - "path": "elasticsearch.index.total.search.query_time_in_millis", - "type": "alias" - }, - "query_total": { - "path": "elasticsearch.index.total.search.query_total", - "type": "alias" - } - } - }, - "segments": { - "properties": { - "count": { - "path": "elasticsearch.index.total.segments.count", - "type": "alias" - }, - "doc_values_memory_in_bytes": { - "path": "elasticsearch.index.total.segments.doc_values_memory_in_bytes", - "type": "alias" - }, - "fixed_bit_set_memory_in_bytes": { - "path": "elasticsearch.index.total.segments.fixed_bit_set_memory_in_bytes", - "type": "alias" - }, - "index_writer_memory_in_bytes": { - "path": "elasticsearch.index.total.segments.index_writer_memory_in_bytes", - "type": "alias" - }, - "memory_in_bytes": { - "path": "elasticsearch.index.total.segments.memory_in_bytes", - "type": "alias" - }, - "norms_memory_in_bytes": { - "path": "elasticsearch.index.total.segments.norms_memory_in_bytes", - "type": "alias" - }, - "points_memory_in_bytes": { - "path": "elasticsearch.index.total.segments.points_memory_in_bytes", - "type": "alias" - }, - "stored_fields_memory_in_bytes": { - "path": "elasticsearch.index.total.segments.stored_fields_memory_in_bytes", - "type": "alias" - }, - "term_vectors_memory_in_bytes": { - "path": "elasticsearch.index.total.segments.term_vectors_memory_in_bytes", - "type": "alias" - }, - "terms_memory_in_bytes": { - "path": "elasticsearch.index.total.segments.terms_memory_in_bytes", - "type": "alias" - }, - "version_map_memory_in_bytes": { - "path": "elasticsearch.index.total.segments.version_map_memory_in_bytes", - "type": "alias" - } - } - }, - "store": { - "properties": { - "size_in_bytes": { - "path": "elasticsearch.index.total.store.size_in_bytes", - "type": "alias" - } - } - } - } - } - } - }, - "service": { - "properties": { - "address": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "source_node": { - "properties": { - "name": { - "path": "elasticsearch.node.name", - "type": "alias" - }, - "uuid": { - "path": "elasticsearch.node.id", - "type": "alias" - } - } - }, - "timestamp": { - "path": "@timestamp", - "type": "alias" - } - } - }, - "settings": { - "index": { - "codec": "best_compression", - "lifecycle": { - "name": "metrics" - }, - "mapping": { - "total_fields": { - "limit": "10000" - } - }, - "query": { - "default_field": [ - "ecs.version", - "event.dataset", - "event.module", - "host.name", - "service.address", - "service.type", - "service.name", - "error.message", - "elasticsearch.index.uuid", - "elasticsearch.index.status", - "elasticsearch.index.name", - "elasticsearch.cluster.name", - "elasticsearch.cluster.id", - "elasticsearch.cluster.state.id", - "elasticsearch.node.id", - "elasticsearch.node.name" - ] - } - } - } - } - } - } -} - -{ - "type": "data_stream", - "value": { - "data_stream": "metrics-elasticsearch.stack_monitoring.index_recovery-default", - "template": { - "_meta": { - "managed": true, - "managed_by": "fleet", - "package": { - "name": "elasticsearch" - } - }, - "data_stream": { - "allow_custom_routing": false, - "hidden": false - }, - "index_patterns": [ - "metrics-elasticsearch.stack_monitoring.index_recovery-*" - ], - "name": "metrics-elasticsearch.stack_monitoring.index_recovery", - "priority": 200, - "template": { - "mappings": { - "_meta": { - "managed": true, - "managed_by": "fleet", - "package": { - "name": "elasticsearch" - } - }, - "date_detection": false, - "dynamic": false, - "dynamic_templates": [ - { - "strings_as_keyword": { - "mapping": { - "ignore_above": 1024, - "type": "keyword" - }, - "match_mapping_type": "string" - } - } - ], - "properties": { - "@timestamp": { - "type": "date" - }, - "cluster_uuid": { - "path": "elasticsearch.cluster.id", - "type": "alias" - }, - "data_stream": { - "properties": { - "dataset": { - "type": "constant_keyword" - }, - "namespace": { - "type": "constant_keyword" - }, - "type": { - "type": "constant_keyword" - } - } - }, - "ecs": { - "properties": { - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "elasticsearch": { - "properties": { - "cluster": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "state": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "index": { - "properties": { - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "recovery": { - "properties": { - "id": { - "type": "long" - }, - "index": { - "properties": { - "files": { - "properties": { - "percent": { - "ignore_above": 1024, - "type": "keyword" - }, - "recovered": { - "type": "long" - }, - "reused": { - "type": "long" - }, - "total": { - "type": "long" - } - } - }, - "size": { - "properties": { - "recovered_in_bytes": { - "type": "long" - }, - "reused_in_bytes": { - "type": "long" - }, - "total_in_bytes": { - "type": "long" - } - } - } - } - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "primary": { - "type": "boolean" - }, - "source": { - "properties": { - "host": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "transport_address": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "stage": { - "ignore_above": 1024, - "type": "keyword" - }, - "start_time": { - "properties": { - "ms": { - "type": "long" - } - } - }, - "stop_time": { - "properties": { - "ms": { - "type": "long" - } - } - }, - "target": { - "properties": { - "host": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "transport_address": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "total_time": { - "properties": { - "ms": { - "type": "long" - } - } - }, - "translog": { - "properties": { - "percent": { - "ignore_above": 1024, - "type": "keyword" - }, - "total": { - "type": "long" - }, - "total_on_start": { - "type": "long" - } - } - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - }, - "verify_index": { - "properties": { - "check_index_time": { - "properties": { - "ms": { - "type": "long" - } - } - }, - "total_time": { - "properties": { - "ms": { - "type": "long" - } - } - } - } - } - } - } - } - }, - "node": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "master": { - "type": "boolean" - }, - "mlockall": { - "type": "boolean" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "error": { - "properties": { - "message": { - "type": "match_only_text" - } - } - }, - "event": { - "properties": { - "agent_id_status": { - "ignore_above": 1024, - "type": "keyword" - }, - "dataset": { - "ignore_above": 1024, - "type": "keyword" - }, - "duration": { - "type": "long" - }, - "ingested": { - "format": "strict_date_time_no_millis||strict_date_optional_time||epoch_millis", - "type": "date" - }, - "module": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "host": { - "properties": { - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "index_recovery": { - "properties": { - "shards": { - "properties": { - "start_time_in_millis": { - "path": "elasticsearch.index.recovery.start_time.ms", - "type": "alias" - }, - "stop_time_in_millis": { - "path": "elasticsearch.index.recovery.stop_time.ms", - "type": "alias" - } - } - } - } - }, - "service": { - "properties": { - "address": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "source_node": { - "properties": { - "name": { - "path": "elasticsearch.node.name", - "type": "alias" - }, - "uuid": { - "path": "elasticsearch.node.id", - "type": "alias" - } - } - }, - "timestamp": { - "path": "@timestamp", - "type": "alias" - } - } - }, - "settings": { - "index": { - "codec": "best_compression", - "lifecycle": { - "name": "metrics" - }, - "mapping": { - "total_fields": { - "limit": "10000" - } - }, - "query": { - "default_field": [ - "ecs.version", - "event.dataset", - "event.module", - "host.name", - "service.address", - "service.type", - "service.name", - "error.message", - "elasticsearch.index.name", - "elasticsearch.index.recovery.index.files.percent", - "elasticsearch.index.recovery.name", - "elasticsearch.index.recovery.type", - "elasticsearch.index.recovery.stage", - "elasticsearch.index.recovery.translog.percent", - "elasticsearch.index.recovery.target.transport_address", - "elasticsearch.index.recovery.target.id", - "elasticsearch.index.recovery.target.host", - "elasticsearch.index.recovery.target.name", - "elasticsearch.index.recovery.source.transport_address", - "elasticsearch.index.recovery.source.id", - "elasticsearch.index.recovery.source.host", - "elasticsearch.index.recovery.source.name", - "elasticsearch.cluster.name", - "elasticsearch.cluster.id", - "elasticsearch.cluster.state.id", - "elasticsearch.node.id", - "elasticsearch.node.name" - ] - } - } - } - } - } - } -} - -{ - "type": "data_stream", - "value": { - "data_stream": "metrics-elasticsearch.stack_monitoring.index_summary-default", - "template": { - "_meta": { - "managed": true, - "managed_by": "fleet", - "package": { - "name": "elasticsearch" - } - }, - "data_stream": { - "allow_custom_routing": false, - "hidden": false - }, - "index_patterns": [ - "metrics-elasticsearch.stack_monitoring.index_summary-*" - ], - "name": "metrics-elasticsearch.stack_monitoring.index_summary", - "priority": 200, - "template": { - "mappings": { - "_meta": { - "managed": true, - "managed_by": "fleet", - "package": { - "name": "elasticsearch" - } - }, - "date_detection": false, - "dynamic": false, - "dynamic_templates": [ - { - "strings_as_keyword": { - "mapping": { - "ignore_above": 1024, - "type": "keyword" - }, - "match_mapping_type": "string" - } - } - ], - "properties": { - "@timestamp": { - "type": "date" - }, - "cluster_uuid": { - "path": "elasticsearch.cluster.id", - "type": "alias" - }, - "data_stream": { - "properties": { - "dataset": { - "type": "constant_keyword" - }, - "namespace": { - "type": "constant_keyword" - }, - "type": { - "type": "constant_keyword" - } - } - }, - "ecs": { - "properties": { - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "elasticsearch": { - "properties": { - "cluster": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "state": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "index": { - "properties": { - "summary": { - "properties": { - "primaries": { - "properties": { - "bulk": { - "properties": { - "operations": { - "properties": { - "count": { - "type": "long" - } - } - }, - "size": { - "properties": { - "bytes": { - "type": "long" - } - } - }, - "time": { - "properties": { - "avg": { - "properties": { - "bytes": { - "type": "long" - }, - "ms": { - "type": "long" - } - } - }, - "count": { - "properties": { - "ms": { - "type": "long" - } - } - } - } - } - } - }, - "docs": { - "properties": { - "count": { - "type": "long" - }, - "deleted": { - "type": "long" - } - } - }, - "indexing": { - "properties": { - "index": { - "properties": { - "count": { - "type": "long" - }, - "time": { - "properties": { - "ms": { - "type": "long" - } - } - } - } - } - } - }, - "search": { - "properties": { - "query": { - "properties": { - "count": { - "type": "long" - }, - "time": { - "properties": { - "ms": { - "type": "long" - } - } - } - } - } - } - }, - "segments": { - "properties": { - "count": { - "type": "long" - }, - "memory": { - "properties": { - "bytes": { - "type": "long" - } - } - } - } - }, - "store": { - "properties": { - "size": { - "properties": { - "bytes": { - "type": "long" - } - } - } - } - } - } - }, - "total": { - "properties": { - "bulk": { - "properties": { - "operations": { - "properties": { - "count": { - "type": "long" - } - } - }, - "size": { - "properties": { - "bytes": { - "type": "long" - } - } - }, - "time": { - "properties": { - "avg": { - "properties": { - "bytes": { - "type": "long" - }, - "ms": { - "type": "long" - } - } - } - } - } - } - }, - "docs": { - "properties": { - "count": { - "type": "long" - }, - "deleted": { - "type": "long" - } - } - }, - "indexing": { - "properties": { - "index": { - "properties": { - "count": { - "type": "long" - }, - "time": { - "properties": { - "ms": { - "type": "long" - } - } - } - } - }, - "is_throttled": { - "type": "boolean" - }, - "throttle_time": { - "properties": { - "ms": { - "type": "long" - } - } - } - } - }, - "search": { - "properties": { - "query": { - "properties": { - "count": { - "type": "long" - }, - "time": { - "properties": { - "ms": { - "type": "long" - } - } - } - } - } - } - }, - "segments": { - "properties": { - "count": { - "type": "long" - }, - "memory": { - "properties": { - "bytes": { - "type": "long" - } - } - } - } - }, - "store": { - "properties": { - "size": { - "properties": { - "bytes": { - "type": "long" - } - } - } - } - } - } - } - } - } - } - }, - "node": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "master": { - "type": "boolean" - }, - "mlockall": { - "type": "boolean" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "error": { - "properties": { - "message": { - "type": "match_only_text" - } - } - }, - "event": { - "properties": { - "agent_id_status": { - "ignore_above": 1024, - "type": "keyword" - }, - "dataset": { - "ignore_above": 1024, - "type": "keyword" - }, - "duration": { - "type": "long" - }, - "ingested": { - "format": "strict_date_time_no_millis||strict_date_optional_time||epoch_millis", - "type": "date" - }, - "module": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "host": { - "properties": { - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "indices_stats": { - "properties": { - "_all": { - "properties": { - "primaries": { - "properties": { - "indexing": { - "properties": { - "index_time_in_millis": { - "path": "elasticsearch.index.summary.primaries.indexing.index.time.ms", - "type": "alias" - }, - "index_total": { - "path": "elasticsearch.index.summary.primaries.indexing.index.count", - "type": "alias" - } - } - } - } - }, - "total": { - "properties": { - "indexing": { - "properties": { - "index_total": { - "path": "elasticsearch.index.summary.total.indexing.index.count", - "type": "alias" - } - } - }, - "search": { - "properties": { - "query_time_in_millis": { - "path": "elasticsearch.index.summary.total.search.query.time.ms", - "type": "alias" - }, - "query_total": { - "path": "elasticsearch.index.summary.total.search.query.count", - "type": "alias" - } - } - } - } - } - } - } - } - }, - "service": { - "properties": { - "address": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "source_node": { - "properties": { - "name": { - "path": "elasticsearch.node.name", - "type": "alias" - }, - "uuid": { - "path": "elasticsearch.node.id", - "type": "alias" - } - } - }, - "timestamp": { - "path": "@timestamp", - "type": "alias" - } - } - }, - "settings": { - "index": { - "codec": "best_compression", - "lifecycle": { - "name": "metrics" - }, - "mapping": { - "total_fields": { - "limit": "10000" - } - }, - "query": { - "default_field": [ - "ecs.version", - "event.dataset", - "event.module", - "host.name", - "service.address", - "service.type", - "service.name", - "error.message", - "elasticsearch.cluster.name", - "elasticsearch.cluster.id", - "elasticsearch.cluster.state.id", - "elasticsearch.node.id", - "elasticsearch.node.name" - ] - } - } - } - } - } - } -} - -{ - "type": "data_stream", - "value": { - "data_stream": "metrics-elasticsearch.stack_monitoring.node-default", - "template": { - "_meta": { - "managed": true, - "managed_by": "fleet", - "package": { - "name": "elasticsearch" - } - }, - "data_stream": { - "allow_custom_routing": false, - "hidden": false - }, - "index_patterns": [ - "metrics-elasticsearch.stack_monitoring.node-*" - ], - "name": "metrics-elasticsearch.stack_monitoring.node", - "priority": 200, - "template": { - "mappings": { - "_meta": { - "managed": true, - "managed_by": "fleet", - "package": { - "name": "elasticsearch" - } - }, - "date_detection": false, - "dynamic": false, - "dynamic_templates": [ - { - "strings_as_keyword": { - "mapping": { - "ignore_above": 1024, - "type": "keyword" - }, - "match_mapping_type": "string" - } - } - ], - "properties": { - "@timestamp": { - "type": "date" - }, - "cluster_uuid": { - "path": "elasticsearch.cluster.id", - "type": "alias" - }, - "data_stream": { - "properties": { - "dataset": { - "type": "constant_keyword" - }, - "namespace": { - "type": "constant_keyword" - }, - "type": { - "type": "constant_keyword" - } - } - }, - "ecs": { - "properties": { - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "elasticsearch": { - "properties": { - "cluster": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "state": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "node": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "jvm": { - "properties": { - "memory": { - "properties": { - "heap": { - "properties": { - "init": { - "properties": { - "bytes": { - "type": "long" - } - } - }, - "max": { - "properties": { - "bytes": { - "type": "long" - } - } - } - } - }, - "nonheap": { - "properties": { - "init": { - "properties": { - "bytes": { - "type": "long" - } - } - }, - "max": { - "properties": { - "bytes": { - "type": "long" - } - } - } - } - } - } - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "master": { - "type": "boolean" - }, - "mlockall": { - "type": "boolean" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "process": { - "properties": { - "mlockall": { - "type": "boolean" - } - } - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "error": { - "properties": { - "message": { - "type": "match_only_text" - } - } - }, - "event": { - "properties": { - "agent_id_status": { - "ignore_above": 1024, - "type": "keyword" - }, - "dataset": { - "ignore_above": 1024, - "type": "keyword" - }, - "duration": { - "type": "long" - }, - "ingested": { - "format": "strict_date_time_no_millis||strict_date_optional_time||epoch_millis", - "type": "date" - }, - "module": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "host": { - "properties": { - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "service": { - "properties": { - "address": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "source_node": { - "properties": { - "name": { - "path": "elasticsearch.node.name", - "type": "alias" - }, - "uuid": { - "path": "elasticsearch.node.id", - "type": "alias" - } - } - }, - "timestamp": { - "path": "@timestamp", - "type": "alias" - } - } - }, - "settings": { - "index": { - "codec": "best_compression", - "lifecycle": { - "name": "metrics" - }, - "mapping": { - "total_fields": { - "limit": "10000" - } - }, - "query": { - "default_field": [ - "ecs.version", - "event.dataset", - "event.module", - "host.name", - "service.address", - "service.type", - "service.name", - "error.message", - "elasticsearch.node.version", - "elasticsearch.node.jvm.version", - "elasticsearch.node.id", - "elasticsearch.node.name", - "elasticsearch.cluster.name", - "elasticsearch.cluster.id", - "elasticsearch.cluster.state.id" - ] - } - } - } - } - } - } -} - -{ - "type": "data_stream", - "value": { - "data_stream": "metrics-elasticsearch.stack_monitoring.node_stats-default", - "template": { - "_meta": { - "managed": true, - "managed_by": "fleet", - "package": { - "name": "elasticsearch" - } - }, - "data_stream": { - "allow_custom_routing": false, - "hidden": false - }, - "index_patterns": [ - "metrics-elasticsearch.stack_monitoring.node_stats-*" - ], - "name": "metrics-elasticsearch.stack_monitoring.node_stats", - "priority": 200, - "template": { - "mappings": { - "_meta": { - "managed": true, - "managed_by": "fleet", - "package": { - "name": "elasticsearch" - } - }, - "date_detection": false, - "dynamic": false, - "dynamic_templates": [ - { - "strings_as_keyword": { - "mapping": { - "ignore_above": 1024, - "type": "keyword" - }, - "match_mapping_type": "string" - } - } - ], - "properties": { - "@timestamp": { - "type": "date" - }, - "cluster_uuid": { - "path": "elasticsearch.cluster.id", - "type": "alias" - }, - "data_stream": { - "properties": { - "dataset": { - "type": "constant_keyword" - }, - "namespace": { - "type": "constant_keyword" - }, - "type": { - "type": "constant_keyword" - } - } - }, - "ecs": { - "properties": { - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "elasticsearch": { - "properties": { - "cluster": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "state": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "node": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "master": { - "type": "boolean" - }, - "mlockall": { - "type": "boolean" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "stats": { - "properties": { - "fs": { - "properties": { - "io_stats": { - "properties": { - "total": { - "properties": { - "operations": { - "properties": { - "count": { - "type": "long" - } - } - }, - "read": { - "properties": { - "operations": { - "properties": { - "count": { - "type": "long" - } - } - } - } - }, - "write": { - "properties": { - "operations": { - "properties": { - "count": { - "type": "long" - } - } - } - } - } - } - } - } - }, - "summary": { - "properties": { - "available": { - "properties": { - "bytes": { - "type": "long" - } - } - }, - "free": { - "properties": { - "bytes": { - "type": "long" - } - } - }, - "total": { - "properties": { - "bytes": { - "type": "long" - } - } - } - } - }, - "total": { - "properties": { - "available_in_bytes": { - "type": "long" - }, - "total_in_bytes": { - "type": "long" - } - } - } - } - }, - "indexing_pressure": { - "properties": { - "memory": { - "properties": { - "current": { - "properties": { - "all": { - "properties": { - "bytes": { - "type": "long" - } - } - }, - "combined_coordinating_and_primary": { - "properties": { - "bytes": { - "type": "long" - } - } - }, - "coordinating": { - "properties": { - "bytes": { - "type": "long" - } - } - }, - "primary": { - "properties": { - "bytes": { - "type": "long" - } - } - }, - "replica": { - "properties": { - "bytes": { - "type": "long" - } - } - } - } - }, - "limit_in_bytes": { - "type": "long" - }, - "total": { - "properties": { - "all": { - "properties": { - "bytes": { - "type": "long" - } - } - }, - "combined_coordinating_and_primary": { - "properties": { - "bytes": { - "type": "long" - } - } - }, - "coordinating": { - "properties": { - "bytes": { - "type": "long" - }, - "rejections": { - "type": "long" - } - } - }, - "primary": { - "properties": { - "bytes": { - "type": "long" - }, - "rejections": { - "type": "long" - } - } - }, - "replica": { - "properties": { - "bytes": { - "type": "long" - }, - "rejections": { - "type": "long" - } - } - } - } - } - } - } - } - }, - "indices": { - "properties": { - "bulk": { - "properties": { - "avg_size": { - "properties": { - "bytes": { - "type": "long" - } - } - }, - "avg_time": { - "properties": { - "ms": { - "type": "long" - } - } - }, - "operations": { - "properties": { - "total": { - "properties": { - "count": { - "type": "long" - } - } - } - } - }, - "total_size": { - "properties": { - "bytes": { - "type": "long" - } - } - }, - "total_time": { - "properties": { - "ms": { - "type": "long" - } - } - } - } - }, - "docs": { - "properties": { - "count": { - "type": "long" - }, - "deleted": { - "type": "long" - } - } - }, - "fielddata": { - "properties": { - "memory": { - "properties": { - "bytes": { - "type": "long" - } - } - } - } - }, - "indexing": { - "properties": { - "index_time": { - "properties": { - "ms": { - "type": "long" - } - } - }, - "index_total": { - "properties": { - "count": { - "type": "long" - } - } - }, - "throttle_time": { - "properties": { - "ms": { - "type": "long" - } - } - } - } - }, - "query_cache": { - "properties": { - "memory": { - "properties": { - "bytes": { - "type": "long" - } - } - } - } - }, - "request_cache": { - "properties": { - "memory": { - "properties": { - "bytes": { - "type": "long" - } - } - } - } - }, - "search": { - "properties": { - "query_time": { - "properties": { - "ms": { - "type": "long" - } - } - }, - "query_total": { - "properties": { - "count": { - "type": "long" - } - } - } - } - }, - "segments": { - "properties": { - "count": { - "type": "long" - }, - "doc_values": { - "properties": { - "memory": { - "properties": { - "bytes": { - "type": "long" - } - } - } - } - }, - "fixed_bit_set": { - "properties": { - "memory": { - "properties": { - "bytes": { - "type": "long" - } - } - } - } - }, - "index_writer": { - "properties": { - "memory": { - "properties": { - "bytes": { - "type": "long" - } - } - } - } - }, - "memory": { - "properties": { - "bytes": { - "type": "long" - } - } - }, - "norms": { - "properties": { - "memory": { - "properties": { - "bytes": { - "type": "long" - } - } - } - } - }, - "points": { - "properties": { - "memory": { - "properties": { - "bytes": { - "type": "long" - } - } - } - } - }, - "stored_fields": { - "properties": { - "memory": { - "properties": { - "bytes": { - "type": "long" - } - } - } - } - }, - "term_vectors": { - "properties": { - "memory": { - "properties": { - "bytes": { - "type": "long" - } - } - } - } - }, - "terms": { - "properties": { - "memory": { - "properties": { - "bytes": { - "type": "long" - } - } - } - } - }, - "version_map": { - "properties": { - "memory": { - "properties": { - "bytes": { - "type": "long" - } - } - } - } - } - } - }, - "store": { - "properties": { - "size": { - "properties": { - "bytes": { - "type": "long" - } - } - } - } - } - } - }, - "ingest": { - "properties": { - "total": { - "properties": { - "count": { - "type": "long" - }, - "current": { - "type": "long" - }, - "failed": { - "type": "long" - }, - "time_in_millis": { - "type": "long" - } - } - } - } - }, - "jvm": { - "properties": { - "gc": { - "properties": { - "collectors": { - "properties": { - "old": { - "properties": { - "collection": { - "properties": { - "count": { - "type": "long" - }, - "ms": { - "type": "long" - } - } - } - } - }, - "young": { - "properties": { - "collection": { - "properties": { - "count": { - "type": "long" - }, - "ms": { - "type": "long" - } - } - } - } - } - } - } - } - }, - "mem": { - "properties": { - "heap": { - "properties": { - "max": { - "properties": { - "bytes": { - "type": "long" - } - } - }, - "used": { - "properties": { - "bytes": { - "type": "long" - }, - "pct": { - "type": "double" - } - } - } - } - }, - "pools": { - "properties": { - "old": { - "properties": { - "max": { - "properties": { - "bytes": { - "type": "long" - } - } - }, - "peak": { - "properties": { - "bytes": { - "type": "long" - } - } - }, - "peak_max": { - "properties": { - "bytes": { - "type": "long" - } - } - }, - "used": { - "properties": { - "bytes": { - "type": "long" - } - } - } - } - }, - "survivor": { - "properties": { - "max": { - "properties": { - "bytes": { - "type": "long" - } - } - }, - "peak": { - "properties": { - "bytes": { - "type": "long" - } - } - }, - "peak_max": { - "properties": { - "bytes": { - "type": "long" - } - } - }, - "used": { - "properties": { - "bytes": { - "type": "long" - } - } - } - } - }, - "young": { - "properties": { - "max": { - "properties": { - "bytes": { - "type": "long" - } - } - }, - "peak": { - "properties": { - "bytes": { - "type": "long" - } - } - }, - "peak_max": { - "properties": { - "bytes": { - "type": "long" - } - } - }, - "used": { - "properties": { - "bytes": { - "type": "long" - } - } - } - } - } - } - } - } - } - } - }, - "os": { - "properties": { - "cgroup": { - "properties": { - "cpu": { - "properties": { - "cfs": { - "properties": { - "quota": { - "properties": { - "us": { - "type": "long" - } - } - } - } - }, - "stat": { - "properties": { - "elapsed_periods": { - "properties": { - "count": { - "type": "long" - } - } - }, - "time_throttled": { - "properties": { - "ns": { - "type": "long" - } - } - }, - "times_throttled": { - "properties": { - "count": { - "type": "long" - } - } - } - } - } - } - }, - "cpuacct": { - "properties": { - "usage": { - "properties": { - "ns": { - "type": "long" - } - } - } - } - }, - "memory": { - "properties": { - "control_group": { - "ignore_above": 1024, - "type": "keyword" - }, - "limit": { - "properties": { - "bytes": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "usage": { - "properties": { - "bytes": { - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - } - } - }, - "cpu": { - "properties": { - "load_avg": { - "properties": { - "1m": { - "type": "half_float" - } - } - } - } - } - } - }, - "process": { - "properties": { - "cpu": { - "properties": { - "pct": { - "type": "double" - } - } - } - } - }, - "thread_pool": { - "properties": { - "bulk": { - "properties": { - "queue": { - "properties": { - "count": { - "type": "long" - } - } - }, - "rejected": { - "properties": { - "count": { - "type": "long" - } - } - } - } - }, - "get": { - "properties": { - "queue": { - "properties": { - "count": { - "type": "long" - } - } - }, - "rejected": { - "properties": { - "count": { - "type": "long" - } - } - } - } - }, - "index": { - "properties": { - "queue": { - "properties": { - "count": { - "type": "long" - } - } - }, - "rejected": { - "properties": { - "count": { - "type": "long" - } - } - } - } - }, - "search": { - "properties": { - "queue": { - "properties": { - "count": { - "type": "long" - } - } - }, - "rejected": { - "properties": { - "count": { - "type": "long" - } - } - } - } - }, - "write": { - "properties": { - "queue": { - "properties": { - "count": { - "type": "long" - } - } - }, - "rejected": { - "properties": { - "count": { - "type": "long" - } - } - } - } - } - } - } - } - } - } - } - } - }, - "error": { - "properties": { - "message": { - "type": "match_only_text" - } - } - }, - "event": { - "properties": { - "agent_id_status": { - "ignore_above": 1024, - "type": "keyword" - }, - "dataset": { - "ignore_above": 1024, - "type": "keyword" - }, - "duration": { - "type": "long" - }, - "ingested": { - "format": "strict_date_time_no_millis||strict_date_optional_time||epoch_millis", - "type": "date" - }, - "module": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "host": { - "properties": { - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "node_stats": { - "properties": { - "fs": { - "properties": { - "io_stats": { - "properties": { - "total": { - "properties": { - "operations": { - "path": "elasticsearch.node.stats.fs.io_stats.total.operations.count", - "type": "alias" - }, - "read_operations": { - "path": "elasticsearch.node.stats.fs.io_stats.total.read.operations.count", - "type": "alias" - }, - "write_operations": { - "path": "elasticsearch.node.stats.fs.io_stats.total.write.operations.count", - "type": "alias" - } - } - } - } - }, - "summary": { - "properties": { - "available": { - "properties": { - "bytes": { - "path": "elasticsearch.node.stats.fs.summary.available.bytes", - "type": "alias" - } - } - }, - "total": { - "properties": { - "bytes": { - "path": "elasticsearch.node.stats.fs.summary.total.bytes", - "type": "alias" - } - } - } - } - }, - "total": { - "properties": { - "available_in_bytes": { - "path": "elasticsearch.node.stats.fs.summary.available.bytes", - "type": "alias" - }, - "total_in_bytes": { - "path": "elasticsearch.node.stats.fs.summary.total.bytes", - "type": "alias" - } - } - } - } - }, - "indices": { - "properties": { - "docs": { - "properties": { - "count": { - "path": "elasticsearch.node.stats.indices.docs.count", - "type": "alias" - } - } - }, - "fielddata": { - "properties": { - "memory_size_in_bytes": { - "path": "elasticsearch.node.stats.indices.fielddata.memory.bytes", - "type": "alias" - } - } - }, - "indexing": { - "properties": { - "index_time_in_millis": { - "path": "elasticsearch.node.stats.indices.indexing.index_time.ms", - "type": "alias" - }, - "index_total": { - "path": "elasticsearch.node.stats.indices.indexing.index_total.count", - "type": "alias" - }, - "throttle_time_in_millis": { - "path": "elasticsearch.node.stats.indices.indexing.throttle_time.ms", - "type": "alias" - } - } - }, - "query_cache": { - "properties": { - "memory_size_in_bytes": { - "path": "elasticsearch.node.stats.indices.query_cache.memory.bytes", - "type": "alias" - } - } - }, - "request_cache": { - "properties": { - "memory_size_in_bytes": { - "path": "elasticsearch.node.stats.indices.request_cache.memory.bytes", - "type": "alias" - } - } - }, - "search": { - "properties": { - "query_time_in_millis": { - "path": "elasticsearch.node.stats.indices.search.query_time.ms", - "type": "alias" - }, - "query_total": { - "path": "elasticsearch.node.stats.indices.search.query_total.count", - "type": "alias" - } - } - }, - "segments": { - "properties": { - "count": { - "path": "elasticsearch.node.stats.indices.segments.count", - "type": "alias" - }, - "doc_values_memory_in_bytes": { - "path": "elasticsearch.node.stats.indices.segments.doc_values.memory.bytes", - "type": "alias" - }, - "fixed_bit_set_memory_in_bytes": { - "path": "elasticsearch.node.stats.indices.segments.fixed_bit_set.memory.bytes", - "type": "alias" - }, - "index_writer_memory_in_bytes": { - "path": "elasticsearch.node.stats.indices.segments.index_writer.memory.bytes", - "type": "alias" - }, - "memory_in_bytes": { - "path": "elasticsearch.node.stats.indices.segments.memory.bytes", - "type": "alias" - }, - "norms_memory_in_bytes": { - "path": "elasticsearch.node.stats.indices.segments.norms.memory.bytes", - "type": "alias" - }, - "points_memory_in_bytes": { - "path": "elasticsearch.node.stats.indices.segments.points.memory.bytes", - "type": "alias" - }, - "stored_fields_memory_in_bytes": { - "path": "elasticsearch.node.stats.indices.segments.stored_fields.memory.bytes", - "type": "alias" - }, - "term_vectors_memory_in_bytes": { - "path": "elasticsearch.node.stats.indices.segments.term_vectors.memory.bytes", - "type": "alias" - }, - "terms_memory_in_bytes": { - "path": "elasticsearch.node.stats.indices.segments.terms.memory.bytes", - "type": "alias" - }, - "version_map_memory_in_bytes": { - "path": "elasticsearch.node.stats.indices.segments.version_map.memory.bytes", - "type": "alias" - } - } - }, - "store": { - "properties": { - "size": { - "properties": { - "bytes": { - "path": "elasticsearch.node.stats.indices.store.size.bytes", - "type": "alias" - } - } - }, - "size_in_bytes": { - "path": "elasticsearch.node.stats.indices.store.size.bytes", - "type": "alias" - } - } - } - } - }, - "jvm": { - "properties": { - "gc": { - "properties": { - "collectors": { - "properties": { - "old": { - "properties": { - "collection_count": { - "path": "elasticsearch.node.stats.jvm.gc.collectors.old.collection.count", - "type": "alias" - }, - "collection_time_in_millis": { - "path": "elasticsearch.node.stats.jvm.gc.collectors.old.collection.ms", - "type": "alias" - } - } - }, - "young": { - "properties": { - "collection_count": { - "path": "elasticsearch.node.stats.jvm.gc.collectors.young.collection.count", - "type": "alias" - }, - "collection_time_in_millis": { - "path": "elasticsearch.node.stats.jvm.gc.collectors.young.collection.ms", - "type": "alias" - } - } - } - } - } - } - }, - "mem": { - "properties": { - "heap_max_in_bytes": { - "path": "elasticsearch.node.stats.jvm.mem.heap.max.bytes", - "type": "alias" - }, - "heap_used_in_bytes": { - "path": "elasticsearch.node.stats.jvm.mem.heap.used.bytes", - "type": "alias" - }, - "heap_used_percent": { - "path": "elasticsearch.node.stats.jvm.mem.heap.used.pct", - "type": "alias" - } - } - } - } - }, - "node_id": { - "path": "elasticsearch.node.id", - "type": "alias" - }, - "os": { - "properties": { - "cgroup": { - "properties": { - "cpu": { - "properties": { - "cfs_quota_micros": { - "path": "elasticsearch.node.stats.os.cgroup.cpu.cfs.quota.us", - "type": "alias" - }, - "stat": { - "properties": { - "number_of_elapsed_periods": { - "path": "elasticsearch.node.stats.os.cgroup.cpu.stat.elapsed_periods.count", - "type": "alias" - }, - "number_of_times_throttled": { - "path": "elasticsearch.node.stats.os.cgroup.cpu.stat.times_throttled.count", - "type": "alias" - }, - "time_throttled_nanos": { - "path": "elasticsearch.node.stats.os.cgroup.cpu.stat.time_throttled.ns", - "type": "alias" - } - } - } - } - }, - "cpuacct": { - "properties": { - "usage_nanos": { - "path": "elasticsearch.node.stats.os.cgroup.cpuacct.usage.ns", - "type": "alias" - } - } - }, - "memory": { - "properties": { - "control_group": { - "path": "elasticsearch.node.stats.os.cgroup.memory.control_group", - "type": "alias" - }, - "limit_in_bytes": { - "path": "elasticsearch.node.stats.os.cgroup.memory.limit.bytes", - "type": "alias" - }, - "usage_in_bytes": { - "path": "elasticsearch.node.stats.os.cgroup.memory.usage.bytes", - "type": "alias" - } - } - } - } - }, - "cpu": { - "properties": { - "load_average": { - "properties": { - "1m": { - "path": "elasticsearch.node.stats.os.cpu.load_avg.1m", - "type": "alias" - } - } - } - } - } - } - }, - "process": { - "properties": { - "cpu": { - "properties": { - "percent": { - "path": "elasticsearch.node.stats.process.cpu.pct", - "type": "alias" - } - } - } - } - }, - "thread_pool": { - "properties": { - "bulk": { - "properties": { - "queue": { - "path": "elasticsearch.node.stats.thread_pool.bulk.queue.count", - "type": "alias" - }, - "rejected": { - "path": "elasticsearch.node.stats.thread_pool.bulk.rejected.count", - "type": "alias" - } - } - }, - "get": { - "properties": { - "queue": { - "path": "elasticsearch.node.stats.thread_pool.get.queue.count", - "type": "alias" - }, - "rejected": { - "path": "elasticsearch.node.stats.thread_pool.get.rejected.count", - "type": "alias" - } - } - }, - "index": { - "properties": { - "queue": { - "path": "elasticsearch.node.stats.thread_pool.index.queue.count", - "type": "alias" - }, - "rejected": { - "path": "elasticsearch.node.stats.thread_pool.index.rejected.count", - "type": "alias" - } - } - }, - "search": { - "properties": { - "queue": { - "path": "elasticsearch.node.stats.thread_pool.search.queue.count", - "type": "alias" - }, - "rejected": { - "path": "elasticsearch.node.stats.thread_pool.search.rejected.count", - "type": "alias" - } - } - }, - "write": { - "properties": { - "queue": { - "path": "elasticsearch.node.stats.thread_pool.write.queue.count", - "type": "alias" - }, - "rejected": { - "path": "elasticsearch.node.stats.thread_pool.write.rejected.count", - "type": "alias" - } - } - } - } - } - } - }, - "service": { - "properties": { - "address": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "source_node": { - "properties": { - "name": { - "path": "elasticsearch.node.name", - "type": "alias" - }, - "uuid": { - "path": "elasticsearch.node.id", - "type": "alias" - } - } - }, - "timestamp": { - "path": "@timestamp", - "type": "alias" - } - } - }, - "settings": { - "index": { - "codec": "best_compression", - "lifecycle": { - "name": "metrics" - }, - "mapping": { - "total_fields": { - "limit": "10000" - } - }, - "query": { - "default_field": [ - "ecs.version", - "event.dataset", - "event.module", - "host.name", - "service.address", - "service.type", - "service.name", - "error.message", - "elasticsearch.node.stats.os.cgroup.memory.control_group", - "elasticsearch.node.stats.os.cgroup.memory.limit.bytes", - "elasticsearch.node.stats.os.cgroup.memory.usage.bytes", - "elasticsearch.node.id", - "elasticsearch.node.name", - "elasticsearch.cluster.name", - "elasticsearch.cluster.id", - "elasticsearch.cluster.state.id" - ] - } - } - } - } - } - } -} - -{ - "type": "data_stream", - "value": { - "data_stream": "metrics-elasticsearch.stack_monitoring.shard-default", - "template": { - "_meta": { - "managed": true, - "managed_by": "fleet", - "package": { - "name": "elasticsearch" - } - }, - "data_stream": { - "allow_custom_routing": false, - "hidden": false - }, - "index_patterns": [ - "metrics-elasticsearch.stack_monitoring.shard-*" - ], - "name": "metrics-elasticsearch.stack_monitoring.shard", - "priority": 200, - "template": { - "mappings": { - "_meta": { - "managed": true, - "managed_by": "fleet", - "package": { - "name": "elasticsearch" - } - }, - "date_detection": false, - "dynamic": false, - "dynamic_templates": [ - { - "strings_as_keyword": { - "mapping": { - "ignore_above": 1024, - "type": "keyword" - }, - "match_mapping_type": "string" - } - } - ], - "properties": { - "@timestamp": { - "type": "date" - }, - "cluster_uuid": { - "path": "elasticsearch.cluster.id", - "type": "alias" - }, - "data_stream": { - "properties": { - "dataset": { - "type": "constant_keyword" - }, - "namespace": { - "type": "constant_keyword" - }, - "type": { - "type": "constant_keyword" - } - } - }, - "ecs": { - "properties": { - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "elasticsearch": { - "properties": { - "cluster": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "state": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "stats": { - "properties": { - "state": { - "properties": { - "state_uuid": { - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - } - } - }, - "index": { - "properties": { - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "node": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "master": { - "type": "boolean" - }, - "mlockall": { - "type": "boolean" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "shard": { - "properties": { - "number": { - "type": "long" - }, - "primary": { - "type": "boolean" - }, - "relocating_node": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "source_node": { - "properties": { - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "uuid": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "state": { - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "error": { - "properties": { - "message": { - "type": "match_only_text" - } - } - }, - "event": { - "properties": { - "agent_id_status": { - "ignore_above": 1024, - "type": "keyword" - }, - "dataset": { - "ignore_above": 1024, - "type": "keyword" - }, - "duration": { - "type": "long" - }, - "ingested": { - "format": "strict_date_time_no_millis||strict_date_optional_time||epoch_millis", - "type": "date" - }, - "module": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "host": { - "properties": { - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "service": { - "properties": { - "address": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "shard": { - "properties": { - "index": { - "path": "elasticsearch.index.name", - "type": "alias" - }, - "node": { - "path": "elasticsearch.node.id", - "type": "alias" - }, - "primary": { - "path": "elasticsearch.shard.primary", - "type": "alias" - }, - "shard": { - "path": "elasticsearch.shard.number", - "type": "alias" - }, - "state": { - "path": "elasticsearch.shard.state", - "type": "alias" - } - } - }, - "source_node": { - "properties": { - "name": { - "path": "elasticsearch.node.name", - "type": "alias" - }, - "uuid": { - "path": "elasticsearch.node.id", - "type": "alias" - } - } - }, - "timestamp": { - "path": "@timestamp", - "type": "alias" - } - } - }, - "settings": { - "index": { - "codec": "best_compression", - "lifecycle": { - "name": "metrics" - }, - "mapping": { - "total_fields": { - "limit": "10000" - } - }, - "query": { - "default_field": [ - "ecs.version", - "event.dataset", - "event.module", - "host.name", - "service.address", - "service.type", - "service.name", - "error.message", - "elasticsearch.shard.state", - "elasticsearch.shard.relocating_node.name", - "elasticsearch.shard.relocating_node.id", - "elasticsearch.shard.source_node.name", - "elasticsearch.shard.source_node.uuid", - "elasticsearch.index.name", - "elasticsearch.cluster.name", - "elasticsearch.cluster.id", - "elasticsearch.cluster.state.id", - "elasticsearch.cluster.stats.state.state_uuid", - "elasticsearch.node.id", - "elasticsearch.node.name" - ] - } - } - } - } - } - } -} - -{ - "type": "data_stream", - "value": { - "data_stream": "metrics-kibana.stack_monitoring.cluster_actions-default", - "template": { - "_meta": { - "managed": true, - "managed_by": "fleet", - "package": { - "name": "kibana" - } - }, - "data_stream": { - "allow_custom_routing": false, - "hidden": false - }, - "index_patterns": [ - "metrics-kibana.stack_monitoring.cluster_actions-*" - ], - "name": "metrics-kibana.stack_monitoring.cluster_actions", - "priority": 200, - "template": { - "mappings": { - "_meta": { - "managed": true, - "managed_by": "fleet", - "package": { - "name": "kibana" - } - }, - "date_detection": false, - "dynamic": false, - "dynamic_templates": [ - { - "strings_as_keyword": { - "mapping": { - "ignore_above": 1024, - "type": "keyword" - }, - "match_mapping_type": "string" - } - } - ], - "properties": { - "@timestamp": { - "type": "date" - }, - "cluster_uuid": { - "path": "kibana.elasticsearch.cluster.id", - "type": "alias" - }, - "data_stream": { - "properties": { - "dataset": { - "type": "constant_keyword" - }, - "namespace": { - "type": "constant_keyword" - }, - "type": { - "type": "constant_keyword" - } - } - }, - "ecs": { - "properties": { - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "error": { - "properties": { - "message": { - "type": "match_only_text" - } - } - }, - "event": { - "properties": { - "agent_id_status": { - "ignore_above": 1024, - "type": "keyword" - }, - "dataset": { - "ignore_above": 1024, - "type": "keyword" - }, - "duration": { - "type": "long" - }, - "ingested": { - "format": "strict_date_time_no_millis||strict_date_optional_time||epoch_millis", - "type": "date" - }, - "module": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "host": { - "properties": { - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "kibana": { - "properties": { - "cluster_actions": { - "properties": { - "overdue": { - "properties": { - "count": { - "type": "long" - }, - "delay": { - "properties": { - "p50": { - "type": "float" - }, - "p99": { - "type": "float" - } - } - } - } - } - } - }, - "elasticsearch": { - "properties": { - "cluster": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - } - } - }, - "kibana_stats": { - "properties": { - "kibana": { - "properties": { - "uuid": { - "path": "service.id", - "type": "alias" - }, - "version": { - "path": "service.version", - "type": "alias" - } - } - }, - "timestamp": { - "path": "@timestamp", - "type": "alias" - } - } - }, - "process": { - "properties": { - "pid": { - "type": "long" - } - } - }, - "service": { - "properties": { - "address": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "timestamp": { - "path": "@timestamp", - "type": "alias" - } - } - }, - "settings": { - "index": { - "codec": "best_compression", - "lifecycle": { - "name": "metrics" - }, - "mapping": { - "total_fields": { - "limit": "10000" - } - }, - "query": { - "default_field": [ - "service.id", - "service.address", - "service.version", - "service.type", - "ecs.version", - "event.dataset", - "event.module", - "host.name", - "error.message", - "kibana.elasticsearch.cluster.id" - ] - } - } - } - } - } - } -} - -{ - "type": "data_stream", - "value": { - "data_stream": "metrics-kibana.stack_monitoring.cluster_rules-default", - "template": { - "_meta": { - "managed": true, - "managed_by": "fleet", - "package": { - "name": "kibana" - } - }, - "data_stream": { - "allow_custom_routing": false, - "hidden": false - }, - "index_patterns": [ - "metrics-kibana.stack_monitoring.cluster_rules-*" - ], - "name": "metrics-kibana.stack_monitoring.cluster_rules", - "priority": 200, - "template": { - "mappings": { - "_meta": { - "managed": true, - "managed_by": "fleet", - "package": { - "name": "kibana" - } - }, - "date_detection": false, - "dynamic": false, - "dynamic_templates": [ - { - "strings_as_keyword": { - "mapping": { - "ignore_above": 1024, - "type": "keyword" - }, - "match_mapping_type": "string" - } - } - ], - "properties": { - "@timestamp": { - "type": "date" - }, - "cluster_uuid": { - "path": "kibana.elasticsearch.cluster.id", - "type": "alias" - }, - "data_stream": { - "properties": { - "dataset": { - "type": "constant_keyword" - }, - "namespace": { - "type": "constant_keyword" - }, - "type": { - "type": "constant_keyword" - } - } - }, - "ecs": { - "properties": { - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "error": { - "properties": { - "message": { - "type": "match_only_text" - } - } - }, - "event": { - "properties": { - "agent_id_status": { - "ignore_above": 1024, - "type": "keyword" - }, - "dataset": { - "ignore_above": 1024, - "type": "keyword" - }, - "duration": { - "type": "long" - }, - "ingested": { - "format": "strict_date_time_no_millis||strict_date_optional_time||epoch_millis", - "type": "date" - }, - "module": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "host": { - "properties": { - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "kibana": { - "properties": { - "cluster_rules": { - "properties": { - "overdue": { - "properties": { - "count": { - "type": "long" - }, - "delay": { - "properties": { - "p50": { - "type": "float" - }, - "p99": { - "type": "float" - } - } - } - } - } - } - }, - "elasticsearch": { - "properties": { - "cluster": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - } - } - }, - "kibana_stats": { - "properties": { - "kibana": { - "properties": { - "uuid": { - "path": "service.id", - "type": "alias" - }, - "version": { - "path": "service.version", - "type": "alias" - } - } - }, - "timestamp": { - "path": "@timestamp", - "type": "alias" - } - } - }, - "process": { - "properties": { - "pid": { - "type": "long" - } - } - }, - "service": { - "properties": { - "address": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "timestamp": { - "path": "@timestamp", - "type": "alias" - } - } - }, - "settings": { - "index": { - "codec": "best_compression", - "lifecycle": { - "name": "metrics" - }, - "mapping": { - "total_fields": { - "limit": "10000" - } - }, - "query": { - "default_field": [ - "service.id", - "service.address", - "service.version", - "service.type", - "ecs.version", - "event.dataset", - "event.module", - "host.name", - "error.message", - "kibana.elasticsearch.cluster.id" - ] - } - } - } - } - } - } -} - -{ - "type": "data_stream", - "value": { - "data_stream": "metrics-kibana.stack_monitoring.node_actions-default", - "template": { - "_meta": { - "managed": true, - "managed_by": "fleet", - "package": { - "name": "kibana" - } - }, - "data_stream": { - "allow_custom_routing": false, - "hidden": false - }, - "index_patterns": [ - "metrics-kibana.stack_monitoring.node_actions-*" - ], - "name": "metrics-kibana.stack_monitoring.node_actions", - "priority": 200, - "template": { - "mappings": { - "_meta": { - "managed": true, - "managed_by": "fleet", - "package": { - "name": "kibana" - } - }, - "date_detection": false, - "dynamic": false, - "dynamic_templates": [ - { - "strings_as_keyword": { - "mapping": { - "ignore_above": 1024, - "type": "keyword" - }, - "match_mapping_type": "string" - } - } - ], - "properties": { - "@timestamp": { - "type": "date" - }, - "cluster_uuid": { - "path": "kibana.elasticsearch.cluster.id", - "type": "alias" - }, - "data_stream": { - "properties": { - "dataset": { - "type": "constant_keyword" - }, - "namespace": { - "type": "constant_keyword" - }, - "type": { - "type": "constant_keyword" - } - } - }, - "ecs": { - "properties": { - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "error": { - "properties": { - "message": { - "type": "match_only_text" - } - } - }, - "event": { - "properties": { - "agent_id_status": { - "ignore_above": 1024, - "type": "keyword" - }, - "dataset": { - "ignore_above": 1024, - "type": "keyword" - }, - "duration": { - "type": "long" - }, - "ingested": { - "format": "strict_date_time_no_millis||strict_date_optional_time||epoch_millis", - "type": "date" - }, - "module": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "host": { - "properties": { - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "kibana": { - "properties": { - "elasticsearch": { - "properties": { - "cluster": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "node_actions": { - "properties": { - "executions": { - "type": "long" - }, - "failures": { - "type": "long" - }, - "timeouts": { - "type": "long" - } - } - } - } - }, - "kibana_stats": { - "properties": { - "kibana": { - "properties": { - "uuid": { - "path": "service.id", - "type": "alias" - }, - "version": { - "path": "service.version", - "type": "alias" - } - } - }, - "timestamp": { - "path": "@timestamp", - "type": "alias" - } - } - }, - "process": { - "properties": { - "pid": { - "type": "long" - } - } - }, - "service": { - "properties": { - "address": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "timestamp": { - "path": "@timestamp", - "type": "alias" - } - } - }, - "settings": { - "index": { - "codec": "best_compression", - "lifecycle": { - "name": "metrics" - }, - "mapping": { - "total_fields": { - "limit": "10000" - } - }, - "query": { - "default_field": [ - "service.id", - "service.address", - "service.version", - "service.type", - "ecs.version", - "event.dataset", - "event.module", - "host.name", - "error.message", - "kibana.elasticsearch.cluster.id" - ] - } - } - } - } - } - } -} - -{ - "type": "data_stream", - "value": { - "data_stream": "metrics-kibana.stack_monitoring.node_rules-default", - "template": { - "_meta": { - "managed": true, - "managed_by": "fleet", - "package": { - "name": "kibana" - } - }, - "data_stream": { - "allow_custom_routing": false, - "hidden": false - }, - "index_patterns": [ - "metrics-kibana.stack_monitoring.node_rules-*" - ], - "name": "metrics-kibana.stack_monitoring.node_rules", - "priority": 200, - "template": { - "mappings": { - "_meta": { - "managed": true, - "managed_by": "fleet", - "package": { - "name": "kibana" - } - }, - "date_detection": false, - "dynamic": false, - "dynamic_templates": [ - { - "strings_as_keyword": { - "mapping": { - "ignore_above": 1024, - "type": "keyword" - }, - "match_mapping_type": "string" - } - } - ], - "properties": { - "@timestamp": { - "type": "date" - }, - "cluster_uuid": { - "path": "kibana.elasticsearch.cluster.id", - "type": "alias" - }, - "data_stream": { - "properties": { - "dataset": { - "type": "constant_keyword" - }, - "namespace": { - "type": "constant_keyword" - }, - "type": { - "type": "constant_keyword" - } - } - }, - "ecs": { - "properties": { - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "error": { - "properties": { - "message": { - "type": "match_only_text" - } - } - }, - "event": { - "properties": { - "agent_id_status": { - "ignore_above": 1024, - "type": "keyword" - }, - "dataset": { - "ignore_above": 1024, - "type": "keyword" - }, - "duration": { - "type": "long" - }, - "ingested": { - "format": "strict_date_time_no_millis||strict_date_optional_time||epoch_millis", - "type": "date" - }, - "module": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "host": { - "properties": { - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "kibana": { - "properties": { - "elasticsearch": { - "properties": { - "cluster": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "node_rules": { - "properties": { - "executions": { - "type": "long" - }, - "failures": { - "type": "long" - }, - "timeouts": { - "type": "long" - } - } - } - } - }, - "kibana_stats": { - "properties": { - "kibana": { - "properties": { - "uuid": { - "path": "service.id", - "type": "alias" - }, - "version": { - "path": "service.version", - "type": "alias" - } - } - }, - "timestamp": { - "path": "@timestamp", - "type": "alias" - } - } - }, - "process": { - "properties": { - "pid": { - "type": "long" - } - } - }, - "service": { - "properties": { - "address": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "timestamp": { - "path": "@timestamp", - "type": "alias" - } - } - }, - "settings": { - "index": { - "codec": "best_compression", - "lifecycle": { - "name": "metrics" - }, - "mapping": { - "total_fields": { - "limit": "10000" - } - }, - "query": { - "default_field": [ - "service.id", - "service.address", - "service.version", - "service.type", - "ecs.version", - "event.dataset", - "event.module", - "host.name", - "error.message", - "kibana.elasticsearch.cluster.id" - ] - } - } - } - } - } - } -} - -{ - "type": "data_stream", - "value": { - "data_stream": "metrics-kibana.stack_monitoring.stats-default", - "template": { - "_meta": { - "managed": true, - "managed_by": "fleet", - "package": { - "name": "kibana" - } - }, - "data_stream": { - "allow_custom_routing": false, - "hidden": false - }, - "index_patterns": [ - "metrics-kibana.stack_monitoring.stats-*" - ], - "name": "metrics-kibana.stack_monitoring.stats", - "priority": 200, - "template": { - "mappings": { - "_meta": { - "managed": true, - "managed_by": "fleet", - "package": { - "name": "kibana" - } - }, - "date_detection": false, - "dynamic": false, - "dynamic_templates": [ - { - "strings_as_keyword": { - "mapping": { - "ignore_above": 1024, - "type": "keyword" - }, - "match_mapping_type": "string" - } - } - ], - "properties": { - "@timestamp": { - "type": "date" - }, - "cluster_uuid": { - "path": "kibana.elasticsearch.cluster.id", - "type": "alias" - }, - "data_stream": { - "properties": { - "dataset": { - "type": "constant_keyword" - }, - "namespace": { - "type": "constant_keyword" - }, - "type": { - "type": "constant_keyword" - } - } - }, - "ecs": { - "properties": { - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "error": { - "properties": { - "message": { - "type": "match_only_text" - } - } - }, - "event": { - "properties": { - "agent_id_status": { - "ignore_above": 1024, - "type": "keyword" - }, - "dataset": { - "ignore_above": 1024, - "type": "keyword" - }, - "duration": { - "type": "long" - }, - "ingested": { - "format": "strict_date_time_no_millis||strict_date_optional_time||epoch_millis", - "type": "date" - }, - "module": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "host": { - "properties": { - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "kibana": { - "properties": { - "elasticsearch": { - "properties": { - "cluster": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "stats": { - "properties": { - "concurrent_connections": { - "type": "long" - }, - "host": { - "properties": { - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "index": { - "ignore_above": 1024, - "type": "keyword" - }, - "kibana": { - "properties": { - "status": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "os": { - "properties": { - "distro": { - "ignore_above": 1024, - "type": "keyword" - }, - "distroRelease": { - "ignore_above": 1024, - "type": "keyword" - }, - "load": { - "properties": { - "15m": { - "type": "half_float" - }, - "1m": { - "type": "half_float" - }, - "5m": { - "type": "half_float" - } - } - }, - "memory": { - "properties": { - "free_in_bytes": { - "type": "long" - }, - "total_in_bytes": { - "type": "long" - }, - "used_in_bytes": { - "type": "long" - } - } - }, - "platform": { - "ignore_above": 1024, - "type": "keyword" - }, - "platformRelease": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "process": { - "properties": { - "event_loop_delay": { - "properties": { - "ms": { - "scaling_factor": 1000, - "type": "scaled_float" - } - } - }, - "memory": { - "properties": { - "heap": { - "properties": { - "size_limit": { - "properties": { - "bytes": { - "type": "long" - } - } - }, - "total": { - "properties": { - "bytes": { - "type": "long" - } - } - }, - "uptime": { - "properties": { - "ms": { - "type": "long" - } - } - }, - "used": { - "properties": { - "bytes": { - "type": "long" - } - } - } - } - }, - "resident_set_size": { - "properties": { - "bytes": { - "type": "long" - } - } - } - } - }, - "uptime": { - "properties": { - "ms": { - "type": "long" - } - } - } - } - }, - "request": { - "properties": { - "disconnects": { - "type": "long" - }, - "total": { - "type": "long" - } - } - }, - "response_time": { - "properties": { - "avg": { - "properties": { - "ms": { - "type": "long" - } - } - }, - "max": { - "properties": { - "ms": { - "type": "long" - } - } - } - } - }, - "snapshot": { - "type": "boolean" - }, - "status": { - "ignore_above": 1024, - "type": "keyword" - }, - "transport_address": { - "ignore_above": 1024, - "type": "keyword" - }, - "usage": { - "properties": { - "index": { - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - } - } - }, - "kibana_stats": { - "properties": { - "concurrent_connections": { - "path": "kibana.stats.concurrent_connections", - "type": "alias" - }, - "kibana": { - "properties": { - "response_time": { - "properties": { - "max": { - "path": "kibana.stats.response_time.max.ms", - "type": "alias" - } - } - }, - "status": { - "path": "kibana.stats.status", - "type": "alias" - }, - "uuid": { - "path": "service.id", - "type": "alias" - }, - "version": { - "path": "service.version", - "type": "alias" - } - } - }, - "os": { - "properties": { - "load": { - "properties": { - "15m": { - "path": "kibana.stats.os.load.15m", - "type": "alias" - }, - "1m": { - "path": "kibana.stats.os.load.1m", - "type": "alias" - }, - "5m": { - "path": "kibana.stats.os.load.5m", - "type": "alias" - } - } - }, - "memory": { - "properties": { - "free_in_bytes": { - "path": "kibana.stats.os.memory.free_in_bytes", - "type": "alias" - } - } - } - } - }, - "process": { - "properties": { - "event_loop_delay": { - "path": "kibana.stats.process.event_loop_delay.ms", - "type": "alias" - }, - "memory": { - "properties": { - "heap": { - "properties": { - "size_limit": { - "path": "kibana.stats.process.memory.heap.size_limit.bytes", - "type": "alias" - } - } - }, - "resident_set_size_in_bytes": { - "path": "kibana.stats.process.memory.resident_set_size.bytes", - "type": "alias" - } - } - }, - "uptime_in_millis": { - "path": "kibana.stats.process.uptime.ms", - "type": "alias" - } - } - }, - "requests": { - "properties": { - "disconnects": { - "path": "kibana.stats.request.disconnects", - "type": "alias" - }, - "total": { - "path": "kibana.stats.request.total", - "type": "alias" - } - } - }, - "response_times": { - "properties": { - "average": { - "path": "kibana.stats.response_time.avg.ms", - "type": "alias" - }, - "max": { - "path": "kibana.stats.response_time.max.ms", - "type": "alias" - } - } - }, - "timestamp": { - "path": "@timestamp", - "type": "alias" - } - } - }, - "process": { - "properties": { - "pid": { - "type": "long" - } - } - }, - "service": { - "properties": { - "address": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "timestamp": { - "path": "@timestamp", - "type": "alias" - } - } - }, - "settings": { - "index": { - "codec": "best_compression", - "lifecycle": { - "name": "metrics" - }, - "mapping": { - "total_fields": { - "limit": "10000" - } - }, - "query": { - "default_field": [ - "service.id", - "service.address", - "service.version", - "service.type", - "ecs.version", - "event.dataset", - "event.module", - "host.name", - "error.message", - "kibana.elasticsearch.cluster.id", - "kibana.stats.kibana.status", - "kibana.stats.usage.index", - "kibana.stats.name", - "kibana.stats.index", - "kibana.stats.host.name", - "kibana.stats.status", - "kibana.stats.transport_address", - "kibana.stats.os.distro", - "kibana.stats.os.distroRelease", - "kibana.stats.os.platform", - "kibana.stats.os.platformRelease" - ] - } - } - } - } - } - } -} - -{ - "type": "data_stream", - "value": { - "data_stream": "metrics-kibana.stack_monitoring.status-default", - "template": { - "_meta": { - "managed": true, - "managed_by": "fleet", - "package": { - "name": "kibana" - } - }, - "data_stream": { - "allow_custom_routing": false, - "hidden": false - }, - "index_patterns": [ - "metrics-kibana.stack_monitoring.status-*" - ], - "name": "metrics-kibana.stack_monitoring.status", - "priority": 200, - "template": { - "mappings": { - "_meta": { - "managed": true, - "managed_by": "fleet", - "package": { - "name": "kibana" - } - }, - "date_detection": false, - "dynamic": false, - "dynamic_templates": [ - { - "strings_as_keyword": { - "mapping": { - "ignore_above": 1024, - "type": "keyword" - }, - "match_mapping_type": "string" - } - } - ], - "properties": { - "@timestamp": { - "type": "date" - }, - "data_stream": { - "properties": { - "dataset": { - "type": "constant_keyword" - }, - "namespace": { - "type": "constant_keyword" - }, - "type": { - "type": "constant_keyword" - } - } - }, - "ecs": { - "properties": { - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "error": { - "properties": { - "message": { - "type": "match_only_text" - } - } - }, - "event": { - "properties": { - "agent_id_status": { - "ignore_above": 1024, - "type": "keyword" - }, - "ingested": { - "format": "strict_date_time_no_millis||strict_date_optional_time||epoch_millis", - "type": "date" - } - } - }, - "kibana": { - "properties": { - "status": { - "properties": { - "metrics": { - "properties": { - "concurrent_connections": { - "type": "long" - }, - "requests": { - "properties": { - "disconnects": { - "type": "long" - }, - "total": { - "type": "long" - } - } - } - } - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "status": { - "properties": { - "overall": { - "properties": { - "state": { - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - } - } - } - } - }, - "service": { - "properties": { - "address": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "settings": { - "index": { - "codec": "best_compression", - "lifecycle": { - "name": "metrics" - }, - "mapping": { - "total_fields": { - "limit": "10000" - } - }, - "query": { - "default_field": [ - "service.id", - "service.name", - "service.version", - "service.type", - "service.address", - "ecs.version", - "error.message", - "kibana.status.name", - "kibana.status.status.overall.state" - ] - } - } - } - } - } - } -} - -{ - "type": "data_stream", - "value": { - "data_stream": "metrics-logstash.stack_monitoring.node-default", - "template": { - "_meta": { - "managed": true, - "managed_by": "fleet", - "package": { - "name": "logstash" - } - }, - "data_stream": { - "allow_custom_routing": false, - "hidden": false - }, - "index_patterns": [ - "metrics-logstash.stack_monitoring.node-*" - ], - "name": "metrics-logstash.stack_monitoring.node", - "priority": 200, - "template": { - "mappings": { - "_meta": { - "managed": true, - "managed_by": "fleet", - "package": { - "name": "logstash" - } - }, - "date_detection": false, - "dynamic": false, - "dynamic_templates": [ - { - "strings_as_keyword": { - "mapping": { - "ignore_above": 1024, - "type": "keyword" - }, - "match_mapping_type": "string" - } - } - ], - "properties": { - "@timestamp": { - "type": "date" - }, - "cluster_uuid": { - "path": "logstash.elasticsearch.cluster.id", - "type": "alias" - }, - "data_stream": { - "properties": { - "dataset": { - "type": "constant_keyword" - }, - "namespace": { - "type": "constant_keyword" - }, - "type": { - "type": "constant_keyword" - } - } - }, - "ecs": { - "properties": { - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "error": { - "properties": { - "message": { - "type": "match_only_text" - } - } - }, - "event": { - "properties": { - "agent_id_status": { - "ignore_above": 1024, - "type": "keyword" - }, - "dataset": { - "ignore_above": 1024, - "type": "keyword" - }, - "duration": { - "type": "long" - }, - "ingested": { - "format": "strict_date_time_no_millis||strict_date_optional_time||epoch_millis", - "type": "date" - }, - "module": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "host": { - "properties": { - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "logstash": { - "properties": { - "cluster": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "elasticsearch": { - "properties": { - "cluster": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "node": { - "properties": { - "host": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "jvm": { - "properties": { - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "state": { - "properties": { - "pipeline": { - "properties": { - "batch_size": { - "type": "long" - }, - "ephemeral_id": { - "ignore_above": 1024, - "type": "keyword" - }, - "hash": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "representation": { - "properties": { - "graph": { - "properties": { - "edges": { - "type": "object" - }, - "vertices": { - "type": "object" - } - } - }, - "hash": { - "ignore_above": 1024, - "type": "keyword" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "workers": { - "type": "long" - } - } - } - } - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "logstash_state": { - "properties": { - "pipeline": { - "properties": { - "hash": { - "path": "logstash.node.state.pipeline.hash", - "type": "alias" - }, - "id": { - "path": "logstash.node.state.pipeline.id", - "type": "alias" - } - } - } - } - }, - "process": { - "properties": { - "pid": { - "type": "long" - } - } - }, - "service": { - "properties": { - "address": { - "ignore_above": 1024, - "type": "keyword" - }, - "hostname": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "timestamp": { - "path": "@timestamp", - "type": "alias" - } - } - }, - "settings": { - "index": { - "codec": "best_compression", - "lifecycle": { - "name": "metrics" - }, - "mapping": { - "total_fields": { - "limit": "10000" - } - }, - "query": { - "default_field": [ - "service.hostname", - "service.id", - "service.type", - "service.version", - "service.address", - "service.name", - "ecs.version", - "event.dataset", - "event.module", - "host.name", - "error.message", - "logstash.cluster.id", - "logstash.elasticsearch.cluster.id", - "logstash.node.jvm.version", - "logstash.node.host", - "logstash.node.version", - "logstash.node.id", - "logstash.node.state.pipeline.id", - "logstash.node.state.pipeline.hash", - "logstash.node.state.pipeline.ephemeral_id", - "logstash.node.state.pipeline.representation.hash", - "logstash.node.state.pipeline.representation.type", - "logstash.node.state.pipeline.representation.version" - ] - } - } - } - } - } - } -} - -{ - "type": "data_stream", - "value": { - "data_stream": "metrics-logstash.stack_monitoring.node_stats-default", - "template": { - "_meta": { - "managed": true, - "managed_by": "fleet", - "package": { - "name": "logstash" - } - }, - "data_stream": { - "allow_custom_routing": false, - "hidden": false - }, - "index_patterns": [ - "metrics-logstash.stack_monitoring.node_stats-*" - ], - "name": "metrics-logstash.stack_monitoring.node_stats", - "priority": 200, - "template": { - "mappings": { - "_meta": { - "managed": true, - "managed_by": "fleet", - "package": { - "name": "logstash" - } - }, - "date_detection": false, - "dynamic": false, - "dynamic_templates": [ - { - "strings_as_keyword": { - "mapping": { - "ignore_above": 1024, - "type": "keyword" - }, - "match_mapping_type": "string" - } - } - ], - "properties": { - "@timestamp": { - "type": "date" - }, - "cluster_uuid": { - "path": "logstash.elasticsearch.cluster.id", - "type": "alias" - }, - "data_stream": { - "properties": { - "dataset": { - "type": "constant_keyword" - }, - "namespace": { - "type": "constant_keyword" - }, - "type": { - "type": "constant_keyword" - } - } - }, - "ecs": { - "properties": { - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "error": { - "properties": { - "message": { - "type": "match_only_text" - } - } - }, - "event": { - "properties": { - "agent_id_status": { - "ignore_above": 1024, - "type": "keyword" - }, - "dataset": { - "ignore_above": 1024, - "type": "keyword" - }, - "duration": { - "type": "long" - }, - "ingested": { - "format": "strict_date_time_no_millis||strict_date_optional_time||epoch_millis", - "type": "date" - }, - "module": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "host": { - "properties": { - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "logstash": { - "properties": { - "cluster": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "elasticsearch": { - "properties": { - "cluster": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "node": { - "properties": { - "state": { - "properties": { - "pipeline": { - "properties": { - "hash": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "stats": { - "properties": { - "events": { - "properties": { - "duration_in_millis": { - "type": "long" - }, - "filtered": { - "type": "long" - }, - "in": { - "type": "long" - }, - "out": { - "type": "long" - } - } - }, - "jvm": { - "properties": { - "gc": { - "properties": { - "collectors": { - "properties": { - "old": { - "properties": { - "collection_count": { - "type": "long" - }, - "collection_time_in_millis": { - "type": "long" - } - } - }, - "young": { - "properties": { - "collection_count": { - "type": "long" - }, - "collection_time_in_millis": { - "type": "long" - } - } - } - } - } - } - }, - "mem": { - "properties": { - "heap_max_in_bytes": { - "type": "long" - }, - "heap_used_in_bytes": { - "type": "long" - }, - "heap_used_percent": { - "type": "long" - } - } - }, - "uptime_in_millis": { - "type": "long" - } - } - }, - "logstash": { - "properties": { - "ephemeral_id": { - "ignore_above": 1024, - "type": "keyword" - }, - "host": { - "ignore_above": 1024, - "type": "keyword" - }, - "http_address": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "pipeline": { - "properties": { - "batch_size": { - "type": "long" - }, - "workers": { - "type": "long" - } - } - }, - "snapshot": { - "type": "boolean" - }, - "status": { - "ignore_above": 1024, - "type": "keyword" - }, - "uuid": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "os": { - "properties": { - "cgroup": { - "properties": { - "cpu": { - "properties": { - "cfs_quota_micros": { - "type": "long" - }, - "control_group": { - "type": "text" - }, - "stat": { - "type": "object" - } - } - }, - "cpuacct": { - "type": "object" - } - } - }, - "cpu": { - "properties": { - "load_average": { - "properties": { - "15m": { - "type": "half_float" - }, - "1m": { - "type": "half_float" - }, - "5m": { - "type": "half_float" - } - } - }, - "percent": { - "type": "double" - } - } - } - } - }, - "pipelines": { - "properties": { - "ephemeral_id": { - "ignore_above": 1024, - "type": "keyword" - }, - "events": { - "properties": { - "duration_in_millis": { - "type": "long" - }, - "filtered": { - "type": "long" - }, - "in": { - "type": "long" - }, - "out": { - "type": "long" - }, - "queue_push_duration_in_millis": { - "type": "long" - } - } - }, - "hash": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "queue": { - "properties": { - "events_count": { - "type": "long" - }, - "max_queue_size_in_bytes": { - "type": "long" - }, - "queue_size_in_bytes": { - "type": "long" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "reloads": { - "properties": { - "failures": { - "type": "long" - }, - "successes": { - "type": "long" - } - } - }, - "vertices": { - "properties": { - "duration_in_millis": { - "type": "long" - }, - "events_in": { - "type": "long" - }, - "events_out": { - "type": "long" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "long_counters": { - "properties": { - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "value": { - "type": "long" - } - }, - "type": "nested" - }, - "pipeline_ephemeral_id": { - "ignore_above": 1024, - "type": "keyword" - }, - "queue_push_duration_in_millis": { - "type": "long" - } - }, - "type": "nested" - } - }, - "type": "nested" - }, - "process": { - "properties": { - "cpu": { - "properties": { - "percent": { - "type": "double" - } - } - }, - "max_file_descriptors": { - "type": "long" - }, - "open_file_descriptors": { - "type": "long" - } - } - }, - "queue": { - "properties": { - "events_count": { - "type": "long" - } - } - }, - "reloads": { - "properties": { - "failures": { - "type": "long" - }, - "successes": { - "type": "long" - } - } - }, - "timestamp": { - "type": "date" - } - } - } - } - } - } - }, - "logstash_stats": { - "properties": { - "events": { - "properties": { - "duration_in_millis": { - "path": "logstash.node.stats.events.duration_in_millis", - "type": "alias" - }, - "in": { - "path": "logstash.node.stats.events.in", - "type": "alias" - }, - "out": { - "path": "logstash.node.stats.events.out", - "type": "alias" - } - } - }, - "jvm": { - "properties": { - "mem": { - "properties": { - "heap_max_in_bytes": { - "path": "logstash.node.stats.jvm.mem.heap_max_in_bytes", - "type": "alias" - }, - "heap_used_in_bytes": { - "path": "logstash.node.stats.jvm.mem.heap_used_in_bytes", - "type": "alias" - } - } - }, - "uptime_in_millis": { - "path": "logstash.node.stats.jvm.uptime_in_millis", - "type": "alias" - } - } - }, - "logstash": { - "properties": { - "uuid": { - "path": "logstash.node.stats.logstash.uuid", - "type": "alias" - }, - "version": { - "path": "logstash.node.stats.logstash.version", - "type": "alias" - } - } - }, - "os": { - "properties": { - "cgroup": { - "properties": { - "cpuacct": { - "type": "object" - } - } - }, - "cpu": { - "properties": { - "load_average": { - "properties": { - "15m": { - "path": "logstash.node.stats.os.cpu.load_average.15m", - "type": "alias" - }, - "1m": { - "path": "logstash.node.stats.os.cpu.load_average.1m", - "type": "alias" - }, - "5m": { - "path": "logstash.node.stats.os.cpu.load_average.5m", - "type": "alias" - } - } - }, - "stat": { - "type": "object" - } - } - } - } - }, - "pipelines": { - "type": "nested" - }, - "process": { - "properties": { - "cpu": { - "properties": { - "percent": { - "path": "logstash.node.stats.process.cpu.percent", - "type": "alias" - } - } - } - } - }, - "queue": { - "properties": { - "events_count": { - "path": "logstash.node.stats.queue.events_count", - "type": "alias" - } - } - }, - "timestamp": { - "path": "@timestamp", - "type": "alias" - } - } - }, - "process": { - "properties": { - "pid": { - "type": "long" - } - } - }, - "service": { - "properties": { - "address": { - "ignore_above": 1024, - "type": "keyword" - }, - "hostname": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "timestamp": { - "path": "@timestamp", - "type": "alias" - } - } - }, - "settings": { - "index": { - "codec": "best_compression", - "lifecycle": { - "name": "metrics" - }, - "mapping": { - "total_fields": { - "limit": "10000" - } - }, - "query": { - "default_field": [ - "service.hostname", - "service.id", - "service.type", - "service.version", - "service.address", - "service.name", - "ecs.version", - "event.dataset", - "event.module", - "host.name", - "error.message", - "logstash.elasticsearch.cluster.id", - "logstash.node.state.pipeline.id", - "logstash.node.state.pipeline.hash", - "logstash.node.stats.logstash.uuid", - "logstash.node.stats.logstash.version", - "logstash.node.stats.logstash.ephemeral_id", - "logstash.node.stats.logstash.host", - "logstash.node.stats.logstash.http_address", - "logstash.node.stats.logstash.name", - "logstash.node.stats.logstash.status", - "logstash.node.stats.os.cgroup.cpuacct.control_group", - "logstash.node.stats.os.cgroup.cpu.control_group", - "logstash.node.stats.pipelines.id", - "logstash.node.stats.pipelines.hash", - "logstash.node.stats.pipelines.ephemeral_id", - "logstash.node.stats.pipelines.queue.type", - "logstash.node.stats.pipelines.vertices.id", - "logstash.node.stats.pipelines.vertices.long_counters.name", - "logstash.node.stats.pipelines.vertices.pipeline_ephemeral_id", - "logstash.cluster.id" - ] - } - } - } - } - } - } -} diff --git a/x-pack/test/functional/es_archives/monitoring/logstash_pipelines_multicluster_mb/data.json.gz b/x-pack/test/functional/es_archives/monitoring/logstash_pipelines_multicluster_mb/data.json.gz deleted file mode 100644 index 578641fafe17f..0000000000000 Binary files a/x-pack/test/functional/es_archives/monitoring/logstash_pipelines_multicluster_mb/data.json.gz and /dev/null differ diff --git a/x-pack/test/functional/es_archives/monitoring/logstash_pipelines_multicluster_package/data.json.gz b/x-pack/test/functional/es_archives/monitoring/logstash_pipelines_multicluster_package/data.json.gz deleted file mode 100644 index d80460974b2f0..0000000000000 Binary files a/x-pack/test/functional/es_archives/monitoring/logstash_pipelines_multicluster_package/data.json.gz and /dev/null differ diff --git a/x-pack/test/functional/es_archives/monitoring/logstash_pipelines_multicluster_package/mappings.json b/x-pack/test/functional/es_archives/monitoring/logstash_pipelines_multicluster_package/mappings.json deleted file mode 100644 index 5684702273ac7..0000000000000 --- a/x-pack/test/functional/es_archives/monitoring/logstash_pipelines_multicluster_package/mappings.json +++ /dev/null @@ -1,7138 +0,0 @@ -{ - "type": "data_stream", - "value": { - "data_stream": "metrics-elasticsearch.stack_monitoring.cluster_stats-default", - "template": { - "_meta": { - "managed": true, - "managed_by": "fleet", - "package": { - "name": "elasticsearch" - } - }, - "data_stream": { - "allow_custom_routing": false, - "hidden": false - }, - "index_patterns": [ - "metrics-elasticsearch.stack_monitoring.cluster_stats-*" - ], - "name": "metrics-elasticsearch.stack_monitoring.cluster_stats", - "priority": 200, - "template": { - "mappings": { - "_meta": { - "managed": true, - "managed_by": "fleet", - "package": { - "name": "elasticsearch" - } - }, - "date_detection": false, - "dynamic": false, - "dynamic_templates": [ - { - "strings_as_keyword": { - "mapping": { - "ignore_above": 1024, - "type": "keyword" - }, - "match_mapping_type": "string" - } - } - ], - "properties": { - "@timestamp": { - "type": "date" - }, - "cluster_state": { - "properties": { - "master_node": { - "path": "elasticsearch.cluster.stats.state.master_node", - "type": "alias" - }, - "nodes_hash": { - "path": "elasticsearch.cluster.stats.state.nodes_hash", - "type": "alias" - }, - "state_uuid": { - "path": "elasticsearch.cluster.stats.state.state_uuid", - "type": "alias" - }, - "status": { - "path": "elasticsearch.cluster.stats.status", - "type": "alias" - }, - "version": { - "path": "elasticsearch.cluster.stats.state.version", - "type": "alias" - } - } - }, - "cluster_stats": { - "properties": { - "indices": { - "properties": { - "count": { - "path": "elasticsearch.cluster.stats.indices.total", - "type": "alias" - }, - "shards": { - "properties": { - "total": { - "path": "elasticsearch.cluster.stats.indices.shards.count", - "type": "alias" - } - } - } - } - }, - "nodes": { - "properties": { - "count": { - "properties": { - "total": { - "path": "elasticsearch.cluster.stats.nodes.count", - "type": "alias" - } - } - }, - "jvm": { - "properties": { - "max_uptime_in_millis": { - "path": "elasticsearch.cluster.stats.nodes.jvm.max_uptime.ms", - "type": "alias" - }, - "mem": { - "properties": { - "heap_max_in_bytes": { - "path": "elasticsearch.cluster.stats.nodes.jvm.memory.heap.max.bytes", - "type": "alias" - }, - "heap_used_in_bytes": { - "path": "elasticsearch.cluster.stats.nodes.jvm.memory.heap.used.bytes", - "type": "alias" - } - } - } - } - } - } - } - } - }, - "cluster_uuid": { - "path": "elasticsearch.cluster.id", - "type": "alias" - }, - "data_stream": { - "properties": { - "dataset": { - "type": "constant_keyword" - }, - "namespace": { - "type": "constant_keyword" - }, - "type": { - "type": "constant_keyword" - } - } - }, - "ecs": { - "properties": { - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "elasticsearch": { - "properties": { - "cluster": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "state": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "stats": { - "properties": { - "indices": { - "properties": { - "docs": { - "properties": { - "total": { - "type": "long" - } - } - }, - "fielddata": { - "properties": { - "memory": { - "properties": { - "bytes": { - "type": "long" - } - } - } - } - }, - "shards": { - "properties": { - "count": { - "type": "long" - }, - "primaries": { - "type": "long" - } - } - }, - "store": { - "properties": { - "size": { - "properties": { - "bytes": { - "type": "long" - } - } - } - } - }, - "total": { - "type": "long" - } - } - }, - "license": { - "properties": { - "cluster_needs_tls": { - "type": "boolean" - }, - "expiry_date": { - "type": "date" - }, - "expiry_date_in_millis": { - "type": "long" - }, - "issue_date": { - "type": "date" - }, - "issue_date_in_millis": { - "type": "long" - }, - "issued_to": { - "ignore_above": 1024, - "type": "keyword" - }, - "issuer": { - "ignore_above": 1024, - "type": "keyword" - }, - "max_nodes": { - "type": "long" - }, - "start_date_in_millis": { - "type": "long" - }, - "status": { - "ignore_above": 1024, - "type": "keyword" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - }, - "uid": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "nodes": { - "properties": { - "count": { - "type": "long" - }, - "data": { - "type": "long" - }, - "fs": { - "properties": { - "available": { - "properties": { - "bytes": { - "type": "long" - } - } - }, - "total": { - "properties": { - "bytes": { - "type": "long" - } - } - } - } - }, - "jvm": { - "properties": { - "max_uptime": { - "properties": { - "ms": { - "type": "long" - } - } - }, - "memory": { - "properties": { - "heap": { - "properties": { - "max": { - "properties": { - "bytes": { - "type": "long" - } - } - }, - "used": { - "properties": { - "bytes": { - "type": "long" - } - } - } - } - } - } - } - } - }, - "master": { - "type": "long" - }, - "stats": { - "properties": { - "data": { - "type": "long" - } - } - }, - "versions": { - "type": "text" - } - } - }, - "stack": { - "properties": { - "apm": { - "properties": { - "found": { - "type": "boolean" - } - } - }, - "xpack": { - "properties": { - "ccr": { - "properties": { - "available": { - "type": "boolean" - }, - "enabled": { - "type": "boolean" - } - } - } - } - } - } - }, - "state": { - "properties": { - "master_node": { - "ignore_above": 1024, - "type": "keyword" - }, - "nodes": { - "type": "flattened" - }, - "nodes_hash": { - "ignore_above": 1024, - "type": "keyword" - }, - "state_uuid": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "status": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "node": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "master": { - "type": "boolean" - }, - "mlockall": { - "type": "boolean" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "error": { - "properties": { - "message": { - "type": "match_only_text" - } - } - }, - "event": { - "properties": { - "agent_id_status": { - "ignore_above": 1024, - "type": "keyword" - }, - "dataset": { - "ignore_above": 1024, - "type": "keyword" - }, - "duration": { - "type": "long" - }, - "ingested": { - "format": "strict_date_time_no_millis||strict_date_optional_time||epoch_millis", - "type": "date" - }, - "module": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "host": { - "properties": { - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "license": { - "properties": { - "status": { - "path": "elasticsearch.cluster.stats.license.status", - "type": "alias" - }, - "type": { - "path": "elasticsearch.cluster.stats.license.type", - "type": "alias" - } - } - }, - "service": { - "properties": { - "address": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "source_node": { - "properties": { - "name": { - "path": "elasticsearch.node.name", - "type": "alias" - }, - "uuid": { - "path": "elasticsearch.node.id", - "type": "alias" - } - } - }, - "stack_stats": { - "properties": { - "apm": { - "properties": { - "found": { - "path": "elasticsearch.cluster.stats.stack.apm.found", - "type": "alias" - } - } - }, - "xpack": { - "properties": { - "ccr": { - "properties": { - "available": { - "path": "elasticsearch.cluster.stats.stack.xpack.ccr.available", - "type": "alias" - }, - "enabled": { - "path": "elasticsearch.cluster.stats.stack.xpack.ccr.enabled", - "type": "alias" - } - } - } - } - } - } - }, - "timestamp": { - "path": "@timestamp", - "type": "alias" - } - } - }, - "settings": { - "index": { - "codec": "best_compression", - "lifecycle": { - "name": "metrics" - }, - "mapping": { - "total_fields": { - "limit": "10000" - } - }, - "query": { - "default_field": [ - "ecs.version", - "event.dataset", - "event.module", - "host.name", - "service.address", - "service.type", - "service.name", - "error.message", - "elasticsearch.cluster.stats.version", - "elasticsearch.cluster.stats.state.nodes_hash", - "elasticsearch.cluster.stats.state.master_node", - "elasticsearch.cluster.stats.state.version", - "elasticsearch.cluster.stats.state.state_uuid", - "elasticsearch.cluster.stats.status", - "elasticsearch.cluster.stats.nodes.versions", - "elasticsearch.cluster.stats.license.issued_to", - "elasticsearch.cluster.stats.license.issuer", - "elasticsearch.cluster.stats.license.status", - "elasticsearch.cluster.stats.license.type", - "elasticsearch.cluster.stats.license.uid", - "elasticsearch.cluster.name", - "elasticsearch.cluster.id", - "elasticsearch.cluster.state.id", - "elasticsearch.version", - "elasticsearch.node.id", - "elasticsearch.node.name" - ] - } - } - } - } - } - } -} - -{ - "type": "data_stream", - "value": { - "data_stream": "metrics-elasticsearch.stack_monitoring.enrich-default", - "template": { - "_meta": { - "managed": true, - "managed_by": "fleet", - "package": { - "name": "elasticsearch" - } - }, - "data_stream": { - "allow_custom_routing": false, - "hidden": false - }, - "index_patterns": [ - "metrics-elasticsearch.stack_monitoring.enrich-*" - ], - "name": "metrics-elasticsearch.stack_monitoring.enrich", - "priority": 200, - "template": { - "mappings": { - "_meta": { - "managed": true, - "managed_by": "fleet", - "package": { - "name": "elasticsearch" - } - }, - "date_detection": false, - "dynamic": false, - "dynamic_templates": [ - { - "strings_as_keyword": { - "mapping": { - "ignore_above": 1024, - "type": "keyword" - }, - "match_mapping_type": "string" - } - } - ], - "properties": { - "@timestamp": { - "type": "date" - }, - "cluster_uuid": { - "path": "elasticsearch.cluster.id", - "type": "alias" - }, - "data_stream": { - "properties": { - "dataset": { - "type": "constant_keyword" - }, - "namespace": { - "type": "constant_keyword" - }, - "type": { - "type": "constant_keyword" - } - } - }, - "ecs": { - "properties": { - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "elasticsearch": { - "properties": { - "cluster": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "state": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "enrich": { - "properties": { - "executed_searches": { - "properties": { - "total": { - "type": "long" - } - } - }, - "executing_policy": { - "properties": { - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "task": { - "properties": { - "action": { - "ignore_above": 1024, - "type": "keyword" - }, - "cancellable": { - "type": "boolean" - }, - "id": { - "type": "long" - }, - "parent_task_id": { - "ignore_above": 1024, - "type": "keyword" - }, - "task": { - "ignore_above": 1024, - "type": "keyword" - }, - "time": { - "properties": { - "running": { - "properties": { - "nano": { - "type": "long" - } - } - }, - "start": { - "properties": { - "ms": { - "type": "long" - } - } - } - } - } - } - } - } - }, - "queue": { - "properties": { - "size": { - "type": "long" - } - } - }, - "remote_requests": { - "properties": { - "current": { - "type": "long" - }, - "total": { - "type": "long" - } - } - } - } - }, - "node": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "master": { - "type": "boolean" - }, - "mlockall": { - "type": "boolean" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "error": { - "properties": { - "message": { - "type": "match_only_text" - } - } - }, - "event": { - "properties": { - "agent_id_status": { - "ignore_above": 1024, - "type": "keyword" - }, - "dataset": { - "ignore_above": 1024, - "type": "keyword" - }, - "duration": { - "type": "long" - }, - "ingested": { - "format": "strict_date_time_no_millis||strict_date_optional_time||epoch_millis", - "type": "date" - }, - "module": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "host": { - "properties": { - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "service": { - "properties": { - "address": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "source_node": { - "properties": { - "name": { - "path": "elasticsearch.node.name", - "type": "alias" - }, - "uuid": { - "path": "elasticsearch.node.id", - "type": "alias" - } - } - }, - "timestamp": { - "path": "@timestamp", - "type": "alias" - } - } - }, - "settings": { - "index": { - "codec": "best_compression", - "lifecycle": { - "name": "metrics" - }, - "mapping": { - "total_fields": { - "limit": "10000" - } - }, - "query": { - "default_field": [ - "ecs.version", - "event.dataset", - "event.module", - "host.name", - "service.address", - "service.type", - "service.name", - "error.message", - "elasticsearch.enrich.executing_policy.name", - "elasticsearch.enrich.executing_policy.task.task", - "elasticsearch.enrich.executing_policy.task.action", - "elasticsearch.enrich.executing_policy.task.parent_task_id", - "elasticsearch.cluster.name", - "elasticsearch.cluster.id", - "elasticsearch.cluster.state.id", - "elasticsearch.node.id", - "elasticsearch.node.name" - ] - } - } - } - } - } - } -} - -{ - "type": "data_stream", - "value": { - "data_stream": "metrics-elasticsearch.stack_monitoring.index-default", - "template": { - "_meta": { - "managed": true, - "managed_by": "fleet", - "package": { - "name": "elasticsearch" - } - }, - "data_stream": { - "allow_custom_routing": false, - "hidden": false - }, - "index_patterns": [ - "metrics-elasticsearch.stack_monitoring.index-*" - ], - "name": "metrics-elasticsearch.stack_monitoring.index", - "priority": 200, - "template": { - "mappings": { - "_meta": { - "managed": true, - "managed_by": "fleet", - "package": { - "name": "elasticsearch" - } - }, - "date_detection": false, - "dynamic": false, - "dynamic_templates": [ - { - "strings_as_keyword": { - "mapping": { - "ignore_above": 1024, - "type": "keyword" - }, - "match_mapping_type": "string" - } - } - ], - "properties": { - "@timestamp": { - "type": "date" - }, - "cluster_uuid": { - "path": "elasticsearch.cluster.id", - "type": "alias" - }, - "data_stream": { - "properties": { - "dataset": { - "type": "constant_keyword" - }, - "namespace": { - "type": "constant_keyword" - }, - "type": { - "type": "constant_keyword" - } - } - }, - "ecs": { - "properties": { - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "elasticsearch": { - "properties": { - "cluster": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "state": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "index": { - "properties": { - "created": { - "type": "long" - }, - "hidden": { - "type": "boolean" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "primaries": { - "properties": { - "docs": { - "properties": { - "count": { - "type": "long" - }, - "deleted": { - "type": "long" - } - } - }, - "indexing": { - "properties": { - "index_time_in_millis": { - "type": "long" - }, - "index_total": { - "type": "long" - }, - "throttle_time_in_millis": { - "type": "long" - } - } - }, - "merges": { - "properties": { - "total_size_in_bytes": { - "type": "long" - } - } - }, - "query_cache": { - "properties": { - "hit_count": { - "type": "long" - }, - "memory_size_in_bytes": { - "type": "long" - }, - "miss_count": { - "type": "long" - } - } - }, - "refresh": { - "properties": { - "external_total_time_in_millis": { - "type": "long" - }, - "total_time_in_millis": { - "type": "long" - } - } - }, - "request_cache": { - "properties": { - "evictions": { - "type": "long" - }, - "hit_count": { - "type": "long" - }, - "memory_size_in_bytes": { - "type": "long" - }, - "miss_count": { - "type": "long" - } - } - }, - "search": { - "properties": { - "query_time_in_millis": { - "type": "long" - }, - "query_total": { - "type": "long" - } - } - }, - "segments": { - "properties": { - "count": { - "type": "long" - }, - "doc_values_memory_in_bytes": { - "type": "long" - }, - "fixed_bit_set_memory_in_bytes": { - "type": "long" - }, - "index_writer_memory_in_bytes": { - "type": "long" - }, - "memory_in_bytes": { - "type": "long" - }, - "norms_memory_in_bytes": { - "type": "long" - }, - "points_memory_in_bytes": { - "type": "long" - }, - "stored_fields_memory_in_bytes": { - "type": "long" - }, - "term_vectors_memory_in_bytes": { - "type": "long" - }, - "terms_memory_in_bytes": { - "type": "long" - }, - "version_map_memory_in_bytes": { - "type": "long" - } - } - }, - "store": { - "properties": { - "size_in_bytes": { - "type": "long" - } - } - } - } - }, - "shards": { - "properties": { - "primaries": { - "type": "long" - }, - "total": { - "type": "long" - } - } - }, - "status": { - "ignore_above": 1024, - "type": "keyword" - }, - "total": { - "properties": { - "bulk": { - "properties": { - "avg_size_in_bytes": { - "type": "long" - }, - "avg_time_in_millis": { - "type": "long" - }, - "total_operations": { - "type": "long" - }, - "total_size_in_bytes": { - "type": "long" - }, - "total_time_in_millis": { - "type": "long" - } - } - }, - "docs": { - "properties": { - "count": { - "type": "long" - }, - "deleted": { - "type": "long" - } - } - }, - "fielddata": { - "properties": { - "evictions": { - "type": "long" - }, - "memory_size_in_bytes": { - "type": "long" - } - } - }, - "indexing": { - "properties": { - "index_time_in_millis": { - "type": "long" - }, - "index_total": { - "type": "long" - }, - "throttle_time_in_millis": { - "type": "long" - } - } - }, - "merges": { - "properties": { - "total_size_in_bytes": { - "type": "long" - } - } - }, - "query_cache": { - "properties": { - "evictions": { - "type": "long" - }, - "hit_count": { - "type": "long" - }, - "memory_size_in_bytes": { - "type": "long" - }, - "miss_count": { - "type": "long" - } - } - }, - "refresh": { - "properties": { - "external_total_time_in_millis": { - "type": "long" - }, - "total_time_in_millis": { - "type": "long" - } - } - }, - "request_cache": { - "properties": { - "evictions": { - "type": "long" - }, - "hit_count": { - "type": "long" - }, - "memory_size_in_bytes": { - "type": "long" - }, - "miss_count": { - "type": "long" - } - } - }, - "search": { - "properties": { - "query_time_in_millis": { - "type": "long" - }, - "query_total": { - "type": "long" - } - } - }, - "segments": { - "properties": { - "count": { - "type": "long" - }, - "doc_values_memory_in_bytes": { - "type": "long" - }, - "fixed_bit_set_memory_in_bytes": { - "type": "long" - }, - "index_writer_memory_in_bytes": { - "type": "long" - }, - "memory": { - "properties": { - "bytes": { - "type": "long" - } - } - }, - "memory_in_bytes": { - "type": "long" - }, - "norms_memory_in_bytes": { - "type": "long" - }, - "points_memory_in_bytes": { - "type": "long" - }, - "stored_fields_memory_in_bytes": { - "type": "long" - }, - "term_vectors_memory_in_bytes": { - "type": "long" - }, - "terms_memory_in_bytes": { - "type": "long" - }, - "version_map_memory_in_bytes": { - "type": "long" - } - } - }, - "store": { - "properties": { - "size": { - "properties": { - "bytes": { - "type": "long" - } - } - }, - "size_in_bytes": { - "type": "long" - } - } - } - } - }, - "uuid": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "node": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "master": { - "type": "boolean" - }, - "mlockall": { - "type": "boolean" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "error": { - "properties": { - "message": { - "type": "match_only_text" - } - } - }, - "event": { - "properties": { - "agent_id_status": { - "ignore_above": 1024, - "type": "keyword" - }, - "dataset": { - "ignore_above": 1024, - "type": "keyword" - }, - "duration": { - "type": "long" - }, - "ingested": { - "format": "strict_date_time_no_millis||strict_date_optional_time||epoch_millis", - "type": "date" - }, - "module": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "host": { - "properties": { - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "index_stats": { - "properties": { - "index": { - "path": "elasticsearch.index.name", - "type": "alias" - }, - "primaries": { - "properties": { - "docs": { - "properties": { - "count": { - "path": "elasticsearch.index.primaries.docs.count", - "type": "alias" - } - } - }, - "indexing": { - "properties": { - "index_time_in_millis": { - "path": "elasticsearch.index.primaries.indexing.index_time_in_millis", - "type": "alias" - }, - "index_total": { - "path": "elasticsearch.index.primaries.indexing.index_total", - "type": "alias" - }, - "throttle_time_in_millis": { - "path": "elasticsearch.index.primaries.indexing.throttle_time_in_millis", - "type": "alias" - } - } - }, - "merges": { - "properties": { - "total_size_in_bytes": { - "path": "elasticsearch.index.primaries.merges.total_size_in_bytes", - "type": "alias" - } - } - }, - "refresh": { - "properties": { - "total_time_in_millis": { - "path": "elasticsearch.index.primaries.refresh.total_time_in_millis", - "type": "alias" - } - } - }, - "segments": { - "properties": { - "count": { - "path": "elasticsearch.index.primaries.segments.count", - "type": "alias" - } - } - }, - "store": { - "properties": { - "size_in_bytes": { - "path": "elasticsearch.index.primaries.store.size_in_bytes", - "type": "alias" - } - } - } - } - }, - "total": { - "properties": { - "fielddata": { - "properties": { - "memory_size_in_bytes": { - "path": "elasticsearch.index.total.fielddata.memory_size_in_bytes", - "type": "alias" - } - } - }, - "indexing": { - "properties": { - "index_time_in_millis": { - "path": "elasticsearch.index.total.indexing.index_time_in_millis", - "type": "alias" - }, - "index_total": { - "path": "elasticsearch.index.total.indexing.index_total", - "type": "alias" - }, - "throttle_time_in_millis": { - "path": "elasticsearch.index.total.indexing.throttle_time_in_millis", - "type": "alias" - } - } - }, - "merges": { - "properties": { - "total_size_in_bytes": { - "path": "elasticsearch.index.total.merges.total_size_in_bytes", - "type": "alias" - } - } - }, - "query_cache": { - "properties": { - "memory_size_in_bytes": { - "path": "elasticsearch.index.total.query_cache.memory_size_in_bytes", - "type": "alias" - } - } - }, - "refresh": { - "properties": { - "total_time_in_millis": { - "path": "elasticsearch.index.total.refresh.total_time_in_millis", - "type": "alias" - } - } - }, - "request_cache": { - "properties": { - "memory_size_in_bytes": { - "path": "elasticsearch.index.total.request_cache.memory_size_in_bytes", - "type": "alias" - } - } - }, - "search": { - "properties": { - "query_time_in_millis": { - "path": "elasticsearch.index.total.search.query_time_in_millis", - "type": "alias" - }, - "query_total": { - "path": "elasticsearch.index.total.search.query_total", - "type": "alias" - } - } - }, - "segments": { - "properties": { - "count": { - "path": "elasticsearch.index.total.segments.count", - "type": "alias" - }, - "doc_values_memory_in_bytes": { - "path": "elasticsearch.index.total.segments.doc_values_memory_in_bytes", - "type": "alias" - }, - "fixed_bit_set_memory_in_bytes": { - "path": "elasticsearch.index.total.segments.fixed_bit_set_memory_in_bytes", - "type": "alias" - }, - "index_writer_memory_in_bytes": { - "path": "elasticsearch.index.total.segments.index_writer_memory_in_bytes", - "type": "alias" - }, - "memory_in_bytes": { - "path": "elasticsearch.index.total.segments.memory_in_bytes", - "type": "alias" - }, - "norms_memory_in_bytes": { - "path": "elasticsearch.index.total.segments.norms_memory_in_bytes", - "type": "alias" - }, - "points_memory_in_bytes": { - "path": "elasticsearch.index.total.segments.points_memory_in_bytes", - "type": "alias" - }, - "stored_fields_memory_in_bytes": { - "path": "elasticsearch.index.total.segments.stored_fields_memory_in_bytes", - "type": "alias" - }, - "term_vectors_memory_in_bytes": { - "path": "elasticsearch.index.total.segments.term_vectors_memory_in_bytes", - "type": "alias" - }, - "terms_memory_in_bytes": { - "path": "elasticsearch.index.total.segments.terms_memory_in_bytes", - "type": "alias" - }, - "version_map_memory_in_bytes": { - "path": "elasticsearch.index.total.segments.version_map_memory_in_bytes", - "type": "alias" - } - } - }, - "store": { - "properties": { - "size_in_bytes": { - "path": "elasticsearch.index.total.store.size_in_bytes", - "type": "alias" - } - } - } - } - } - } - }, - "service": { - "properties": { - "address": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "source_node": { - "properties": { - "name": { - "path": "elasticsearch.node.name", - "type": "alias" - }, - "uuid": { - "path": "elasticsearch.node.id", - "type": "alias" - } - } - }, - "timestamp": { - "path": "@timestamp", - "type": "alias" - } - } - }, - "settings": { - "index": { - "codec": "best_compression", - "lifecycle": { - "name": "metrics" - }, - "mapping": { - "total_fields": { - "limit": "10000" - } - }, - "query": { - "default_field": [ - "ecs.version", - "event.dataset", - "event.module", - "host.name", - "service.address", - "service.type", - "service.name", - "error.message", - "elasticsearch.index.uuid", - "elasticsearch.index.status", - "elasticsearch.index.name", - "elasticsearch.cluster.name", - "elasticsearch.cluster.id", - "elasticsearch.cluster.state.id", - "elasticsearch.node.id", - "elasticsearch.node.name" - ] - } - } - } - } - } - } -} - -{ - "type": "data_stream", - "value": { - "data_stream": "metrics-elasticsearch.stack_monitoring.index_recovery-default", - "template": { - "_meta": { - "managed": true, - "managed_by": "fleet", - "package": { - "name": "elasticsearch" - } - }, - "data_stream": { - "allow_custom_routing": false, - "hidden": false - }, - "index_patterns": [ - "metrics-elasticsearch.stack_monitoring.index_recovery-*" - ], - "name": "metrics-elasticsearch.stack_monitoring.index_recovery", - "priority": 200, - "template": { - "mappings": { - "_meta": { - "managed": true, - "managed_by": "fleet", - "package": { - "name": "elasticsearch" - } - }, - "date_detection": false, - "dynamic": false, - "dynamic_templates": [ - { - "strings_as_keyword": { - "mapping": { - "ignore_above": 1024, - "type": "keyword" - }, - "match_mapping_type": "string" - } - } - ], - "properties": { - "@timestamp": { - "type": "date" - }, - "cluster_uuid": { - "path": "elasticsearch.cluster.id", - "type": "alias" - }, - "data_stream": { - "properties": { - "dataset": { - "type": "constant_keyword" - }, - "namespace": { - "type": "constant_keyword" - }, - "type": { - "type": "constant_keyword" - } - } - }, - "ecs": { - "properties": { - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "elasticsearch": { - "properties": { - "cluster": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "state": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "index": { - "properties": { - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "recovery": { - "properties": { - "id": { - "type": "long" - }, - "index": { - "properties": { - "files": { - "properties": { - "percent": { - "ignore_above": 1024, - "type": "keyword" - }, - "recovered": { - "type": "long" - }, - "reused": { - "type": "long" - }, - "total": { - "type": "long" - } - } - }, - "size": { - "properties": { - "recovered_in_bytes": { - "type": "long" - }, - "reused_in_bytes": { - "type": "long" - }, - "total_in_bytes": { - "type": "long" - } - } - } - } - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "primary": { - "type": "boolean" - }, - "source": { - "properties": { - "host": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "transport_address": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "stage": { - "ignore_above": 1024, - "type": "keyword" - }, - "start_time": { - "properties": { - "ms": { - "type": "long" - } - } - }, - "stop_time": { - "properties": { - "ms": { - "type": "long" - } - } - }, - "target": { - "properties": { - "host": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "transport_address": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "total_time": { - "properties": { - "ms": { - "type": "long" - } - } - }, - "translog": { - "properties": { - "percent": { - "ignore_above": 1024, - "type": "keyword" - }, - "total": { - "type": "long" - }, - "total_on_start": { - "type": "long" - } - } - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - }, - "verify_index": { - "properties": { - "check_index_time": { - "properties": { - "ms": { - "type": "long" - } - } - }, - "total_time": { - "properties": { - "ms": { - "type": "long" - } - } - } - } - } - } - } - } - }, - "node": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "master": { - "type": "boolean" - }, - "mlockall": { - "type": "boolean" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "error": { - "properties": { - "message": { - "type": "match_only_text" - } - } - }, - "event": { - "properties": { - "agent_id_status": { - "ignore_above": 1024, - "type": "keyword" - }, - "dataset": { - "ignore_above": 1024, - "type": "keyword" - }, - "duration": { - "type": "long" - }, - "ingested": { - "format": "strict_date_time_no_millis||strict_date_optional_time||epoch_millis", - "type": "date" - }, - "module": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "host": { - "properties": { - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "index_recovery": { - "properties": { - "shards": { - "properties": { - "start_time_in_millis": { - "path": "elasticsearch.index.recovery.start_time.ms", - "type": "alias" - }, - "stop_time_in_millis": { - "path": "elasticsearch.index.recovery.stop_time.ms", - "type": "alias" - } - } - } - } - }, - "service": { - "properties": { - "address": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "source_node": { - "properties": { - "name": { - "path": "elasticsearch.node.name", - "type": "alias" - }, - "uuid": { - "path": "elasticsearch.node.id", - "type": "alias" - } - } - }, - "timestamp": { - "path": "@timestamp", - "type": "alias" - } - } - }, - "settings": { - "index": { - "codec": "best_compression", - "lifecycle": { - "name": "metrics" - }, - "mapping": { - "total_fields": { - "limit": "10000" - } - }, - "query": { - "default_field": [ - "ecs.version", - "event.dataset", - "event.module", - "host.name", - "service.address", - "service.type", - "service.name", - "error.message", - "elasticsearch.index.name", - "elasticsearch.index.recovery.index.files.percent", - "elasticsearch.index.recovery.name", - "elasticsearch.index.recovery.type", - "elasticsearch.index.recovery.stage", - "elasticsearch.index.recovery.translog.percent", - "elasticsearch.index.recovery.target.transport_address", - "elasticsearch.index.recovery.target.id", - "elasticsearch.index.recovery.target.host", - "elasticsearch.index.recovery.target.name", - "elasticsearch.index.recovery.source.transport_address", - "elasticsearch.index.recovery.source.id", - "elasticsearch.index.recovery.source.host", - "elasticsearch.index.recovery.source.name", - "elasticsearch.cluster.name", - "elasticsearch.cluster.id", - "elasticsearch.cluster.state.id", - "elasticsearch.node.id", - "elasticsearch.node.name" - ] - } - } - } - } - } - } -} - -{ - "type": "data_stream", - "value": { - "data_stream": "metrics-elasticsearch.stack_monitoring.index_summary-default", - "template": { - "_meta": { - "managed": true, - "managed_by": "fleet", - "package": { - "name": "elasticsearch" - } - }, - "data_stream": { - "allow_custom_routing": false, - "hidden": false - }, - "index_patterns": [ - "metrics-elasticsearch.stack_monitoring.index_summary-*" - ], - "name": "metrics-elasticsearch.stack_monitoring.index_summary", - "priority": 200, - "template": { - "mappings": { - "_meta": { - "managed": true, - "managed_by": "fleet", - "package": { - "name": "elasticsearch" - } - }, - "date_detection": false, - "dynamic": false, - "dynamic_templates": [ - { - "strings_as_keyword": { - "mapping": { - "ignore_above": 1024, - "type": "keyword" - }, - "match_mapping_type": "string" - } - } - ], - "properties": { - "@timestamp": { - "type": "date" - }, - "cluster_uuid": { - "path": "elasticsearch.cluster.id", - "type": "alias" - }, - "data_stream": { - "properties": { - "dataset": { - "type": "constant_keyword" - }, - "namespace": { - "type": "constant_keyword" - }, - "type": { - "type": "constant_keyword" - } - } - }, - "ecs": { - "properties": { - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "elasticsearch": { - "properties": { - "cluster": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "state": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "index": { - "properties": { - "summary": { - "properties": { - "primaries": { - "properties": { - "bulk": { - "properties": { - "operations": { - "properties": { - "count": { - "type": "long" - } - } - }, - "size": { - "properties": { - "bytes": { - "type": "long" - } - } - }, - "time": { - "properties": { - "avg": { - "properties": { - "bytes": { - "type": "long" - }, - "ms": { - "type": "long" - } - } - }, - "count": { - "properties": { - "ms": { - "type": "long" - } - } - } - } - } - } - }, - "docs": { - "properties": { - "count": { - "type": "long" - }, - "deleted": { - "type": "long" - } - } - }, - "indexing": { - "properties": { - "index": { - "properties": { - "count": { - "type": "long" - }, - "time": { - "properties": { - "ms": { - "type": "long" - } - } - } - } - } - } - }, - "search": { - "properties": { - "query": { - "properties": { - "count": { - "type": "long" - }, - "time": { - "properties": { - "ms": { - "type": "long" - } - } - } - } - } - } - }, - "segments": { - "properties": { - "count": { - "type": "long" - }, - "memory": { - "properties": { - "bytes": { - "type": "long" - } - } - } - } - }, - "store": { - "properties": { - "size": { - "properties": { - "bytes": { - "type": "long" - } - } - } - } - } - } - }, - "total": { - "properties": { - "bulk": { - "properties": { - "operations": { - "properties": { - "count": { - "type": "long" - } - } - }, - "size": { - "properties": { - "bytes": { - "type": "long" - } - } - }, - "time": { - "properties": { - "avg": { - "properties": { - "bytes": { - "type": "long" - }, - "ms": { - "type": "long" - } - } - } - } - } - } - }, - "docs": { - "properties": { - "count": { - "type": "long" - }, - "deleted": { - "type": "long" - } - } - }, - "indexing": { - "properties": { - "index": { - "properties": { - "count": { - "type": "long" - }, - "time": { - "properties": { - "ms": { - "type": "long" - } - } - } - } - }, - "is_throttled": { - "type": "boolean" - }, - "throttle_time": { - "properties": { - "ms": { - "type": "long" - } - } - } - } - }, - "search": { - "properties": { - "query": { - "properties": { - "count": { - "type": "long" - }, - "time": { - "properties": { - "ms": { - "type": "long" - } - } - } - } - } - } - }, - "segments": { - "properties": { - "count": { - "type": "long" - }, - "memory": { - "properties": { - "bytes": { - "type": "long" - } - } - } - } - }, - "store": { - "properties": { - "size": { - "properties": { - "bytes": { - "type": "long" - } - } - } - } - } - } - } - } - } - } - }, - "node": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "master": { - "type": "boolean" - }, - "mlockall": { - "type": "boolean" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "error": { - "properties": { - "message": { - "type": "match_only_text" - } - } - }, - "event": { - "properties": { - "agent_id_status": { - "ignore_above": 1024, - "type": "keyword" - }, - "dataset": { - "ignore_above": 1024, - "type": "keyword" - }, - "duration": { - "type": "long" - }, - "ingested": { - "format": "strict_date_time_no_millis||strict_date_optional_time||epoch_millis", - "type": "date" - }, - "module": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "host": { - "properties": { - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "indices_stats": { - "properties": { - "_all": { - "properties": { - "primaries": { - "properties": { - "indexing": { - "properties": { - "index_time_in_millis": { - "path": "elasticsearch.index.summary.primaries.indexing.index.time.ms", - "type": "alias" - }, - "index_total": { - "path": "elasticsearch.index.summary.primaries.indexing.index.count", - "type": "alias" - } - } - } - } - }, - "total": { - "properties": { - "indexing": { - "properties": { - "index_total": { - "path": "elasticsearch.index.summary.total.indexing.index.count", - "type": "alias" - } - } - }, - "search": { - "properties": { - "query_time_in_millis": { - "path": "elasticsearch.index.summary.total.search.query.time.ms", - "type": "alias" - }, - "query_total": { - "path": "elasticsearch.index.summary.total.search.query.count", - "type": "alias" - } - } - } - } - } - } - } - } - }, - "service": { - "properties": { - "address": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "source_node": { - "properties": { - "name": { - "path": "elasticsearch.node.name", - "type": "alias" - }, - "uuid": { - "path": "elasticsearch.node.id", - "type": "alias" - } - } - }, - "timestamp": { - "path": "@timestamp", - "type": "alias" - } - } - }, - "settings": { - "index": { - "codec": "best_compression", - "lifecycle": { - "name": "metrics" - }, - "mapping": { - "total_fields": { - "limit": "10000" - } - }, - "query": { - "default_field": [ - "ecs.version", - "event.dataset", - "event.module", - "host.name", - "service.address", - "service.type", - "service.name", - "error.message", - "elasticsearch.cluster.name", - "elasticsearch.cluster.id", - "elasticsearch.cluster.state.id", - "elasticsearch.node.id", - "elasticsearch.node.name" - ] - } - } - } - } - } - } -} - -{ - "type": "data_stream", - "value": { - "data_stream": "metrics-elasticsearch.stack_monitoring.node-default", - "template": { - "_meta": { - "managed": true, - "managed_by": "fleet", - "package": { - "name": "elasticsearch" - } - }, - "data_stream": { - "allow_custom_routing": false, - "hidden": false - }, - "index_patterns": [ - "metrics-elasticsearch.stack_monitoring.node-*" - ], - "name": "metrics-elasticsearch.stack_monitoring.node", - "priority": 200, - "template": { - "mappings": { - "_meta": { - "managed": true, - "managed_by": "fleet", - "package": { - "name": "elasticsearch" - } - }, - "date_detection": false, - "dynamic": false, - "dynamic_templates": [ - { - "strings_as_keyword": { - "mapping": { - "ignore_above": 1024, - "type": "keyword" - }, - "match_mapping_type": "string" - } - } - ], - "properties": { - "@timestamp": { - "type": "date" - }, - "cluster_uuid": { - "path": "elasticsearch.cluster.id", - "type": "alias" - }, - "data_stream": { - "properties": { - "dataset": { - "type": "constant_keyword" - }, - "namespace": { - "type": "constant_keyword" - }, - "type": { - "type": "constant_keyword" - } - } - }, - "ecs": { - "properties": { - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "elasticsearch": { - "properties": { - "cluster": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "state": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "node": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "jvm": { - "properties": { - "memory": { - "properties": { - "heap": { - "properties": { - "init": { - "properties": { - "bytes": { - "type": "long" - } - } - }, - "max": { - "properties": { - "bytes": { - "type": "long" - } - } - } - } - }, - "nonheap": { - "properties": { - "init": { - "properties": { - "bytes": { - "type": "long" - } - } - }, - "max": { - "properties": { - "bytes": { - "type": "long" - } - } - } - } - } - } - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "master": { - "type": "boolean" - }, - "mlockall": { - "type": "boolean" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "process": { - "properties": { - "mlockall": { - "type": "boolean" - } - } - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "error": { - "properties": { - "message": { - "type": "match_only_text" - } - } - }, - "event": { - "properties": { - "agent_id_status": { - "ignore_above": 1024, - "type": "keyword" - }, - "dataset": { - "ignore_above": 1024, - "type": "keyword" - }, - "duration": { - "type": "long" - }, - "ingested": { - "format": "strict_date_time_no_millis||strict_date_optional_time||epoch_millis", - "type": "date" - }, - "module": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "host": { - "properties": { - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "service": { - "properties": { - "address": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "source_node": { - "properties": { - "name": { - "path": "elasticsearch.node.name", - "type": "alias" - }, - "uuid": { - "path": "elasticsearch.node.id", - "type": "alias" - } - } - }, - "timestamp": { - "path": "@timestamp", - "type": "alias" - } - } - }, - "settings": { - "index": { - "codec": "best_compression", - "lifecycle": { - "name": "metrics" - }, - "mapping": { - "total_fields": { - "limit": "10000" - } - }, - "query": { - "default_field": [ - "ecs.version", - "event.dataset", - "event.module", - "host.name", - "service.address", - "service.type", - "service.name", - "error.message", - "elasticsearch.node.version", - "elasticsearch.node.jvm.version", - "elasticsearch.node.id", - "elasticsearch.node.name", - "elasticsearch.cluster.name", - "elasticsearch.cluster.id", - "elasticsearch.cluster.state.id" - ] - } - } - } - } - } - } -} - -{ - "type": "data_stream", - "value": { - "data_stream": "metrics-elasticsearch.stack_monitoring.node_stats-default", - "template": { - "_meta": { - "managed": true, - "managed_by": "fleet", - "package": { - "name": "elasticsearch" - } - }, - "data_stream": { - "allow_custom_routing": false, - "hidden": false - }, - "index_patterns": [ - "metrics-elasticsearch.stack_monitoring.node_stats-*" - ], - "name": "metrics-elasticsearch.stack_monitoring.node_stats", - "priority": 200, - "template": { - "mappings": { - "_meta": { - "managed": true, - "managed_by": "fleet", - "package": { - "name": "elasticsearch" - } - }, - "date_detection": false, - "dynamic": false, - "dynamic_templates": [ - { - "strings_as_keyword": { - "mapping": { - "ignore_above": 1024, - "type": "keyword" - }, - "match_mapping_type": "string" - } - } - ], - "properties": { - "@timestamp": { - "type": "date" - }, - "cluster_uuid": { - "path": "elasticsearch.cluster.id", - "type": "alias" - }, - "data_stream": { - "properties": { - "dataset": { - "type": "constant_keyword" - }, - "namespace": { - "type": "constant_keyword" - }, - "type": { - "type": "constant_keyword" - } - } - }, - "ecs": { - "properties": { - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "elasticsearch": { - "properties": { - "cluster": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "state": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "node": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "master": { - "type": "boolean" - }, - "mlockall": { - "type": "boolean" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "stats": { - "properties": { - "fs": { - "properties": { - "io_stats": { - "properties": { - "total": { - "properties": { - "operations": { - "properties": { - "count": { - "type": "long" - } - } - }, - "read": { - "properties": { - "operations": { - "properties": { - "count": { - "type": "long" - } - } - } - } - }, - "write": { - "properties": { - "operations": { - "properties": { - "count": { - "type": "long" - } - } - } - } - } - } - } - } - }, - "summary": { - "properties": { - "available": { - "properties": { - "bytes": { - "type": "long" - } - } - }, - "free": { - "properties": { - "bytes": { - "type": "long" - } - } - }, - "total": { - "properties": { - "bytes": { - "type": "long" - } - } - } - } - }, - "total": { - "properties": { - "available_in_bytes": { - "type": "long" - }, - "total_in_bytes": { - "type": "long" - } - } - } - } - }, - "indexing_pressure": { - "properties": { - "memory": { - "properties": { - "current": { - "properties": { - "all": { - "properties": { - "bytes": { - "type": "long" - } - } - }, - "combined_coordinating_and_primary": { - "properties": { - "bytes": { - "type": "long" - } - } - }, - "coordinating": { - "properties": { - "bytes": { - "type": "long" - } - } - }, - "primary": { - "properties": { - "bytes": { - "type": "long" - } - } - }, - "replica": { - "properties": { - "bytes": { - "type": "long" - } - } - } - } - }, - "limit_in_bytes": { - "type": "long" - }, - "total": { - "properties": { - "all": { - "properties": { - "bytes": { - "type": "long" - } - } - }, - "combined_coordinating_and_primary": { - "properties": { - "bytes": { - "type": "long" - } - } - }, - "coordinating": { - "properties": { - "bytes": { - "type": "long" - }, - "rejections": { - "type": "long" - } - } - }, - "primary": { - "properties": { - "bytes": { - "type": "long" - }, - "rejections": { - "type": "long" - } - } - }, - "replica": { - "properties": { - "bytes": { - "type": "long" - }, - "rejections": { - "type": "long" - } - } - } - } - } - } - } - } - }, - "indices": { - "properties": { - "bulk": { - "properties": { - "avg_size": { - "properties": { - "bytes": { - "type": "long" - } - } - }, - "avg_time": { - "properties": { - "ms": { - "type": "long" - } - } - }, - "operations": { - "properties": { - "total": { - "properties": { - "count": { - "type": "long" - } - } - } - } - }, - "total_size": { - "properties": { - "bytes": { - "type": "long" - } - } - }, - "total_time": { - "properties": { - "ms": { - "type": "long" - } - } - } - } - }, - "docs": { - "properties": { - "count": { - "type": "long" - }, - "deleted": { - "type": "long" - } - } - }, - "fielddata": { - "properties": { - "memory": { - "properties": { - "bytes": { - "type": "long" - } - } - } - } - }, - "indexing": { - "properties": { - "index_time": { - "properties": { - "ms": { - "type": "long" - } - } - }, - "index_total": { - "properties": { - "count": { - "type": "long" - } - } - }, - "throttle_time": { - "properties": { - "ms": { - "type": "long" - } - } - } - } - }, - "query_cache": { - "properties": { - "memory": { - "properties": { - "bytes": { - "type": "long" - } - } - } - } - }, - "request_cache": { - "properties": { - "memory": { - "properties": { - "bytes": { - "type": "long" - } - } - } - } - }, - "search": { - "properties": { - "query_time": { - "properties": { - "ms": { - "type": "long" - } - } - }, - "query_total": { - "properties": { - "count": { - "type": "long" - } - } - } - } - }, - "segments": { - "properties": { - "count": { - "type": "long" - }, - "doc_values": { - "properties": { - "memory": { - "properties": { - "bytes": { - "type": "long" - } - } - } - } - }, - "fixed_bit_set": { - "properties": { - "memory": { - "properties": { - "bytes": { - "type": "long" - } - } - } - } - }, - "index_writer": { - "properties": { - "memory": { - "properties": { - "bytes": { - "type": "long" - } - } - } - } - }, - "memory": { - "properties": { - "bytes": { - "type": "long" - } - } - }, - "norms": { - "properties": { - "memory": { - "properties": { - "bytes": { - "type": "long" - } - } - } - } - }, - "points": { - "properties": { - "memory": { - "properties": { - "bytes": { - "type": "long" - } - } - } - } - }, - "stored_fields": { - "properties": { - "memory": { - "properties": { - "bytes": { - "type": "long" - } - } - } - } - }, - "term_vectors": { - "properties": { - "memory": { - "properties": { - "bytes": { - "type": "long" - } - } - } - } - }, - "terms": { - "properties": { - "memory": { - "properties": { - "bytes": { - "type": "long" - } - } - } - } - }, - "version_map": { - "properties": { - "memory": { - "properties": { - "bytes": { - "type": "long" - } - } - } - } - } - } - }, - "store": { - "properties": { - "size": { - "properties": { - "bytes": { - "type": "long" - } - } - } - } - } - } - }, - "ingest": { - "properties": { - "total": { - "properties": { - "count": { - "type": "long" - }, - "current": { - "type": "long" - }, - "failed": { - "type": "long" - }, - "time_in_millis": { - "type": "long" - } - } - } - } - }, - "jvm": { - "properties": { - "gc": { - "properties": { - "collectors": { - "properties": { - "old": { - "properties": { - "collection": { - "properties": { - "count": { - "type": "long" - }, - "ms": { - "type": "long" - } - } - } - } - }, - "young": { - "properties": { - "collection": { - "properties": { - "count": { - "type": "long" - }, - "ms": { - "type": "long" - } - } - } - } - } - } - } - } - }, - "mem": { - "properties": { - "heap": { - "properties": { - "max": { - "properties": { - "bytes": { - "type": "long" - } - } - }, - "used": { - "properties": { - "bytes": { - "type": "long" - }, - "pct": { - "type": "double" - } - } - } - } - }, - "pools": { - "properties": { - "old": { - "properties": { - "max": { - "properties": { - "bytes": { - "type": "long" - } - } - }, - "peak": { - "properties": { - "bytes": { - "type": "long" - } - } - }, - "peak_max": { - "properties": { - "bytes": { - "type": "long" - } - } - }, - "used": { - "properties": { - "bytes": { - "type": "long" - } - } - } - } - }, - "survivor": { - "properties": { - "max": { - "properties": { - "bytes": { - "type": "long" - } - } - }, - "peak": { - "properties": { - "bytes": { - "type": "long" - } - } - }, - "peak_max": { - "properties": { - "bytes": { - "type": "long" - } - } - }, - "used": { - "properties": { - "bytes": { - "type": "long" - } - } - } - } - }, - "young": { - "properties": { - "max": { - "properties": { - "bytes": { - "type": "long" - } - } - }, - "peak": { - "properties": { - "bytes": { - "type": "long" - } - } - }, - "peak_max": { - "properties": { - "bytes": { - "type": "long" - } - } - }, - "used": { - "properties": { - "bytes": { - "type": "long" - } - } - } - } - } - } - } - } - } - } - }, - "os": { - "properties": { - "cgroup": { - "properties": { - "cpu": { - "properties": { - "cfs": { - "properties": { - "quota": { - "properties": { - "us": { - "type": "long" - } - } - } - } - }, - "stat": { - "properties": { - "elapsed_periods": { - "properties": { - "count": { - "type": "long" - } - } - }, - "time_throttled": { - "properties": { - "ns": { - "type": "long" - } - } - }, - "times_throttled": { - "properties": { - "count": { - "type": "long" - } - } - } - } - } - } - }, - "cpuacct": { - "properties": { - "usage": { - "properties": { - "ns": { - "type": "long" - } - } - } - } - }, - "memory": { - "properties": { - "control_group": { - "ignore_above": 1024, - "type": "keyword" - }, - "limit": { - "properties": { - "bytes": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "usage": { - "properties": { - "bytes": { - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - } - } - }, - "cpu": { - "properties": { - "load_avg": { - "properties": { - "1m": { - "type": "half_float" - } - } - } - } - } - } - }, - "process": { - "properties": { - "cpu": { - "properties": { - "pct": { - "type": "double" - } - } - } - } - }, - "thread_pool": { - "properties": { - "bulk": { - "properties": { - "queue": { - "properties": { - "count": { - "type": "long" - } - } - }, - "rejected": { - "properties": { - "count": { - "type": "long" - } - } - } - } - }, - "get": { - "properties": { - "queue": { - "properties": { - "count": { - "type": "long" - } - } - }, - "rejected": { - "properties": { - "count": { - "type": "long" - } - } - } - } - }, - "index": { - "properties": { - "queue": { - "properties": { - "count": { - "type": "long" - } - } - }, - "rejected": { - "properties": { - "count": { - "type": "long" - } - } - } - } - }, - "search": { - "properties": { - "queue": { - "properties": { - "count": { - "type": "long" - } - } - }, - "rejected": { - "properties": { - "count": { - "type": "long" - } - } - } - } - }, - "write": { - "properties": { - "queue": { - "properties": { - "count": { - "type": "long" - } - } - }, - "rejected": { - "properties": { - "count": { - "type": "long" - } - } - } - } - } - } - } - } - } - } - } - } - }, - "error": { - "properties": { - "message": { - "type": "match_only_text" - } - } - }, - "event": { - "properties": { - "agent_id_status": { - "ignore_above": 1024, - "type": "keyword" - }, - "dataset": { - "ignore_above": 1024, - "type": "keyword" - }, - "duration": { - "type": "long" - }, - "ingested": { - "format": "strict_date_time_no_millis||strict_date_optional_time||epoch_millis", - "type": "date" - }, - "module": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "host": { - "properties": { - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "node_stats": { - "properties": { - "fs": { - "properties": { - "io_stats": { - "properties": { - "total": { - "properties": { - "operations": { - "path": "elasticsearch.node.stats.fs.io_stats.total.operations.count", - "type": "alias" - }, - "read_operations": { - "path": "elasticsearch.node.stats.fs.io_stats.total.read.operations.count", - "type": "alias" - }, - "write_operations": { - "path": "elasticsearch.node.stats.fs.io_stats.total.write.operations.count", - "type": "alias" - } - } - } - } - }, - "summary": { - "properties": { - "available": { - "properties": { - "bytes": { - "path": "elasticsearch.node.stats.fs.summary.available.bytes", - "type": "alias" - } - } - }, - "total": { - "properties": { - "bytes": { - "path": "elasticsearch.node.stats.fs.summary.total.bytes", - "type": "alias" - } - } - } - } - }, - "total": { - "properties": { - "available_in_bytes": { - "path": "elasticsearch.node.stats.fs.summary.available.bytes", - "type": "alias" - }, - "total_in_bytes": { - "path": "elasticsearch.node.stats.fs.summary.total.bytes", - "type": "alias" - } - } - } - } - }, - "indices": { - "properties": { - "docs": { - "properties": { - "count": { - "path": "elasticsearch.node.stats.indices.docs.count", - "type": "alias" - } - } - }, - "fielddata": { - "properties": { - "memory_size_in_bytes": { - "path": "elasticsearch.node.stats.indices.fielddata.memory.bytes", - "type": "alias" - } - } - }, - "indexing": { - "properties": { - "index_time_in_millis": { - "path": "elasticsearch.node.stats.indices.indexing.index_time.ms", - "type": "alias" - }, - "index_total": { - "path": "elasticsearch.node.stats.indices.indexing.index_total.count", - "type": "alias" - }, - "throttle_time_in_millis": { - "path": "elasticsearch.node.stats.indices.indexing.throttle_time.ms", - "type": "alias" - } - } - }, - "query_cache": { - "properties": { - "memory_size_in_bytes": { - "path": "elasticsearch.node.stats.indices.query_cache.memory.bytes", - "type": "alias" - } - } - }, - "request_cache": { - "properties": { - "memory_size_in_bytes": { - "path": "elasticsearch.node.stats.indices.request_cache.memory.bytes", - "type": "alias" - } - } - }, - "search": { - "properties": { - "query_time_in_millis": { - "path": "elasticsearch.node.stats.indices.search.query_time.ms", - "type": "alias" - }, - "query_total": { - "path": "elasticsearch.node.stats.indices.search.query_total.count", - "type": "alias" - } - } - }, - "segments": { - "properties": { - "count": { - "path": "elasticsearch.node.stats.indices.segments.count", - "type": "alias" - }, - "doc_values_memory_in_bytes": { - "path": "elasticsearch.node.stats.indices.segments.doc_values.memory.bytes", - "type": "alias" - }, - "fixed_bit_set_memory_in_bytes": { - "path": "elasticsearch.node.stats.indices.segments.fixed_bit_set.memory.bytes", - "type": "alias" - }, - "index_writer_memory_in_bytes": { - "path": "elasticsearch.node.stats.indices.segments.index_writer.memory.bytes", - "type": "alias" - }, - "memory_in_bytes": { - "path": "elasticsearch.node.stats.indices.segments.memory.bytes", - "type": "alias" - }, - "norms_memory_in_bytes": { - "path": "elasticsearch.node.stats.indices.segments.norms.memory.bytes", - "type": "alias" - }, - "points_memory_in_bytes": { - "path": "elasticsearch.node.stats.indices.segments.points.memory.bytes", - "type": "alias" - }, - "stored_fields_memory_in_bytes": { - "path": "elasticsearch.node.stats.indices.segments.stored_fields.memory.bytes", - "type": "alias" - }, - "term_vectors_memory_in_bytes": { - "path": "elasticsearch.node.stats.indices.segments.term_vectors.memory.bytes", - "type": "alias" - }, - "terms_memory_in_bytes": { - "path": "elasticsearch.node.stats.indices.segments.terms.memory.bytes", - "type": "alias" - }, - "version_map_memory_in_bytes": { - "path": "elasticsearch.node.stats.indices.segments.version_map.memory.bytes", - "type": "alias" - } - } - }, - "store": { - "properties": { - "size": { - "properties": { - "bytes": { - "path": "elasticsearch.node.stats.indices.store.size.bytes", - "type": "alias" - } - } - }, - "size_in_bytes": { - "path": "elasticsearch.node.stats.indices.store.size.bytes", - "type": "alias" - } - } - } - } - }, - "jvm": { - "properties": { - "gc": { - "properties": { - "collectors": { - "properties": { - "old": { - "properties": { - "collection_count": { - "path": "elasticsearch.node.stats.jvm.gc.collectors.old.collection.count", - "type": "alias" - }, - "collection_time_in_millis": { - "path": "elasticsearch.node.stats.jvm.gc.collectors.old.collection.ms", - "type": "alias" - } - } - }, - "young": { - "properties": { - "collection_count": { - "path": "elasticsearch.node.stats.jvm.gc.collectors.young.collection.count", - "type": "alias" - }, - "collection_time_in_millis": { - "path": "elasticsearch.node.stats.jvm.gc.collectors.young.collection.ms", - "type": "alias" - } - } - } - } - } - } - }, - "mem": { - "properties": { - "heap_max_in_bytes": { - "path": "elasticsearch.node.stats.jvm.mem.heap.max.bytes", - "type": "alias" - }, - "heap_used_in_bytes": { - "path": "elasticsearch.node.stats.jvm.mem.heap.used.bytes", - "type": "alias" - }, - "heap_used_percent": { - "path": "elasticsearch.node.stats.jvm.mem.heap.used.pct", - "type": "alias" - } - } - } - } - }, - "node_id": { - "path": "elasticsearch.node.id", - "type": "alias" - }, - "os": { - "properties": { - "cgroup": { - "properties": { - "cpu": { - "properties": { - "cfs_quota_micros": { - "path": "elasticsearch.node.stats.os.cgroup.cpu.cfs.quota.us", - "type": "alias" - }, - "stat": { - "properties": { - "number_of_elapsed_periods": { - "path": "elasticsearch.node.stats.os.cgroup.cpu.stat.elapsed_periods.count", - "type": "alias" - }, - "number_of_times_throttled": { - "path": "elasticsearch.node.stats.os.cgroup.cpu.stat.times_throttled.count", - "type": "alias" - }, - "time_throttled_nanos": { - "path": "elasticsearch.node.stats.os.cgroup.cpu.stat.time_throttled.ns", - "type": "alias" - } - } - } - } - }, - "cpuacct": { - "properties": { - "usage_nanos": { - "path": "elasticsearch.node.stats.os.cgroup.cpuacct.usage.ns", - "type": "alias" - } - } - }, - "memory": { - "properties": { - "control_group": { - "path": "elasticsearch.node.stats.os.cgroup.memory.control_group", - "type": "alias" - }, - "limit_in_bytes": { - "path": "elasticsearch.node.stats.os.cgroup.memory.limit.bytes", - "type": "alias" - }, - "usage_in_bytes": { - "path": "elasticsearch.node.stats.os.cgroup.memory.usage.bytes", - "type": "alias" - } - } - } - } - }, - "cpu": { - "properties": { - "load_average": { - "properties": { - "1m": { - "path": "elasticsearch.node.stats.os.cpu.load_avg.1m", - "type": "alias" - } - } - } - } - } - } - }, - "process": { - "properties": { - "cpu": { - "properties": { - "percent": { - "path": "elasticsearch.node.stats.process.cpu.pct", - "type": "alias" - } - } - } - } - }, - "thread_pool": { - "properties": { - "bulk": { - "properties": { - "queue": { - "path": "elasticsearch.node.stats.thread_pool.bulk.queue.count", - "type": "alias" - }, - "rejected": { - "path": "elasticsearch.node.stats.thread_pool.bulk.rejected.count", - "type": "alias" - } - } - }, - "get": { - "properties": { - "queue": { - "path": "elasticsearch.node.stats.thread_pool.get.queue.count", - "type": "alias" - }, - "rejected": { - "path": "elasticsearch.node.stats.thread_pool.get.rejected.count", - "type": "alias" - } - } - }, - "index": { - "properties": { - "queue": { - "path": "elasticsearch.node.stats.thread_pool.index.queue.count", - "type": "alias" - }, - "rejected": { - "path": "elasticsearch.node.stats.thread_pool.index.rejected.count", - "type": "alias" - } - } - }, - "search": { - "properties": { - "queue": { - "path": "elasticsearch.node.stats.thread_pool.search.queue.count", - "type": "alias" - }, - "rejected": { - "path": "elasticsearch.node.stats.thread_pool.search.rejected.count", - "type": "alias" - } - } - }, - "write": { - "properties": { - "queue": { - "path": "elasticsearch.node.stats.thread_pool.write.queue.count", - "type": "alias" - }, - "rejected": { - "path": "elasticsearch.node.stats.thread_pool.write.rejected.count", - "type": "alias" - } - } - } - } - } - } - }, - "service": { - "properties": { - "address": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "source_node": { - "properties": { - "name": { - "path": "elasticsearch.node.name", - "type": "alias" - }, - "uuid": { - "path": "elasticsearch.node.id", - "type": "alias" - } - } - }, - "timestamp": { - "path": "@timestamp", - "type": "alias" - } - } - }, - "settings": { - "index": { - "codec": "best_compression", - "lifecycle": { - "name": "metrics" - }, - "mapping": { - "total_fields": { - "limit": "10000" - } - }, - "query": { - "default_field": [ - "ecs.version", - "event.dataset", - "event.module", - "host.name", - "service.address", - "service.type", - "service.name", - "error.message", - "elasticsearch.node.stats.os.cgroup.memory.control_group", - "elasticsearch.node.stats.os.cgroup.memory.limit.bytes", - "elasticsearch.node.stats.os.cgroup.memory.usage.bytes", - "elasticsearch.node.id", - "elasticsearch.node.name", - "elasticsearch.cluster.name", - "elasticsearch.cluster.id", - "elasticsearch.cluster.state.id" - ] - } - } - } - } - } - } -} - -{ - "type": "data_stream", - "value": { - "data_stream": "metrics-elasticsearch.stack_monitoring.shard-default", - "template": { - "_meta": { - "managed": true, - "managed_by": "fleet", - "package": { - "name": "elasticsearch" - } - }, - "data_stream": { - "allow_custom_routing": false, - "hidden": false - }, - "index_patterns": [ - "metrics-elasticsearch.stack_monitoring.shard-*" - ], - "name": "metrics-elasticsearch.stack_monitoring.shard", - "priority": 200, - "template": { - "mappings": { - "_meta": { - "managed": true, - "managed_by": "fleet", - "package": { - "name": "elasticsearch" - } - }, - "date_detection": false, - "dynamic": false, - "dynamic_templates": [ - { - "strings_as_keyword": { - "mapping": { - "ignore_above": 1024, - "type": "keyword" - }, - "match_mapping_type": "string" - } - } - ], - "properties": { - "@timestamp": { - "type": "date" - }, - "cluster_uuid": { - "path": "elasticsearch.cluster.id", - "type": "alias" - }, - "data_stream": { - "properties": { - "dataset": { - "type": "constant_keyword" - }, - "namespace": { - "type": "constant_keyword" - }, - "type": { - "type": "constant_keyword" - } - } - }, - "ecs": { - "properties": { - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "elasticsearch": { - "properties": { - "cluster": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "state": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "stats": { - "properties": { - "state": { - "properties": { - "state_uuid": { - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - } - } - }, - "index": { - "properties": { - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "node": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "master": { - "type": "boolean" - }, - "mlockall": { - "type": "boolean" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "shard": { - "properties": { - "number": { - "type": "long" - }, - "primary": { - "type": "boolean" - }, - "relocating_node": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "source_node": { - "properties": { - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "uuid": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "state": { - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "error": { - "properties": { - "message": { - "type": "match_only_text" - } - } - }, - "event": { - "properties": { - "agent_id_status": { - "ignore_above": 1024, - "type": "keyword" - }, - "dataset": { - "ignore_above": 1024, - "type": "keyword" - }, - "duration": { - "type": "long" - }, - "ingested": { - "format": "strict_date_time_no_millis||strict_date_optional_time||epoch_millis", - "type": "date" - }, - "module": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "host": { - "properties": { - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "service": { - "properties": { - "address": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "shard": { - "properties": { - "index": { - "path": "elasticsearch.index.name", - "type": "alias" - }, - "node": { - "path": "elasticsearch.node.id", - "type": "alias" - }, - "primary": { - "path": "elasticsearch.shard.primary", - "type": "alias" - }, - "shard": { - "path": "elasticsearch.shard.number", - "type": "alias" - }, - "state": { - "path": "elasticsearch.shard.state", - "type": "alias" - } - } - }, - "source_node": { - "properties": { - "name": { - "path": "elasticsearch.node.name", - "type": "alias" - }, - "uuid": { - "path": "elasticsearch.node.id", - "type": "alias" - } - } - }, - "timestamp": { - "path": "@timestamp", - "type": "alias" - } - } - }, - "settings": { - "index": { - "codec": "best_compression", - "lifecycle": { - "name": "metrics" - }, - "mapping": { - "total_fields": { - "limit": "10000" - } - }, - "query": { - "default_field": [ - "ecs.version", - "event.dataset", - "event.module", - "host.name", - "service.address", - "service.type", - "service.name", - "error.message", - "elasticsearch.shard.state", - "elasticsearch.shard.relocating_node.name", - "elasticsearch.shard.relocating_node.id", - "elasticsearch.shard.source_node.name", - "elasticsearch.shard.source_node.uuid", - "elasticsearch.index.name", - "elasticsearch.cluster.name", - "elasticsearch.cluster.id", - "elasticsearch.cluster.state.id", - "elasticsearch.cluster.stats.state.state_uuid", - "elasticsearch.node.id", - "elasticsearch.node.name" - ] - } - } - } - } - } - } -} - -{ - "type": "data_stream", - "value": { - "data_stream": "metrics-kibana.stack_monitoring.cluster_actions-default", - "template": { - "_meta": { - "managed": true, - "managed_by": "fleet", - "package": { - "name": "kibana" - } - }, - "data_stream": { - "allow_custom_routing": false, - "hidden": false - }, - "index_patterns": [ - "metrics-kibana.stack_monitoring.cluster_actions-*" - ], - "name": "metrics-kibana.stack_monitoring.cluster_actions", - "priority": 200, - "template": { - "mappings": { - "_meta": { - "managed": true, - "managed_by": "fleet", - "package": { - "name": "kibana" - } - }, - "date_detection": false, - "dynamic": false, - "dynamic_templates": [ - { - "strings_as_keyword": { - "mapping": { - "ignore_above": 1024, - "type": "keyword" - }, - "match_mapping_type": "string" - } - } - ], - "properties": { - "@timestamp": { - "type": "date" - }, - "cluster_uuid": { - "path": "kibana.elasticsearch.cluster.id", - "type": "alias" - }, - "data_stream": { - "properties": { - "dataset": { - "type": "constant_keyword" - }, - "namespace": { - "type": "constant_keyword" - }, - "type": { - "type": "constant_keyword" - } - } - }, - "ecs": { - "properties": { - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "error": { - "properties": { - "message": { - "type": "match_only_text" - } - } - }, - "event": { - "properties": { - "agent_id_status": { - "ignore_above": 1024, - "type": "keyword" - }, - "dataset": { - "ignore_above": 1024, - "type": "keyword" - }, - "duration": { - "type": "long" - }, - "ingested": { - "format": "strict_date_time_no_millis||strict_date_optional_time||epoch_millis", - "type": "date" - }, - "module": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "host": { - "properties": { - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "kibana": { - "properties": { - "cluster_actions": { - "properties": { - "overdue": { - "properties": { - "count": { - "type": "long" - }, - "delay": { - "properties": { - "p50": { - "type": "float" - }, - "p99": { - "type": "float" - } - } - } - } - } - } - }, - "elasticsearch": { - "properties": { - "cluster": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - } - } - }, - "kibana_stats": { - "properties": { - "kibana": { - "properties": { - "uuid": { - "path": "service.id", - "type": "alias" - }, - "version": { - "path": "service.version", - "type": "alias" - } - } - }, - "timestamp": { - "path": "@timestamp", - "type": "alias" - } - } - }, - "process": { - "properties": { - "pid": { - "type": "long" - } - } - }, - "service": { - "properties": { - "address": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "timestamp": { - "path": "@timestamp", - "type": "alias" - } - } - }, - "settings": { - "index": { - "codec": "best_compression", - "lifecycle": { - "name": "metrics" - }, - "mapping": { - "total_fields": { - "limit": "10000" - } - }, - "query": { - "default_field": [ - "service.id", - "service.address", - "service.version", - "service.type", - "ecs.version", - "event.dataset", - "event.module", - "host.name", - "error.message", - "kibana.elasticsearch.cluster.id" - ] - } - } - } - } - } - } -} - -{ - "type": "data_stream", - "value": { - "data_stream": "metrics-kibana.stack_monitoring.cluster_rules-default", - "template": { - "_meta": { - "managed": true, - "managed_by": "fleet", - "package": { - "name": "kibana" - } - }, - "data_stream": { - "allow_custom_routing": false, - "hidden": false - }, - "index_patterns": [ - "metrics-kibana.stack_monitoring.cluster_rules-*" - ], - "name": "metrics-kibana.stack_monitoring.cluster_rules", - "priority": 200, - "template": { - "mappings": { - "_meta": { - "managed": true, - "managed_by": "fleet", - "package": { - "name": "kibana" - } - }, - "date_detection": false, - "dynamic": false, - "dynamic_templates": [ - { - "strings_as_keyword": { - "mapping": { - "ignore_above": 1024, - "type": "keyword" - }, - "match_mapping_type": "string" - } - } - ], - "properties": { - "@timestamp": { - "type": "date" - }, - "cluster_uuid": { - "path": "kibana.elasticsearch.cluster.id", - "type": "alias" - }, - "data_stream": { - "properties": { - "dataset": { - "type": "constant_keyword" - }, - "namespace": { - "type": "constant_keyword" - }, - "type": { - "type": "constant_keyword" - } - } - }, - "ecs": { - "properties": { - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "error": { - "properties": { - "message": { - "type": "match_only_text" - } - } - }, - "event": { - "properties": { - "agent_id_status": { - "ignore_above": 1024, - "type": "keyword" - }, - "dataset": { - "ignore_above": 1024, - "type": "keyword" - }, - "duration": { - "type": "long" - }, - "ingested": { - "format": "strict_date_time_no_millis||strict_date_optional_time||epoch_millis", - "type": "date" - }, - "module": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "host": { - "properties": { - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "kibana": { - "properties": { - "cluster_rules": { - "properties": { - "overdue": { - "properties": { - "count": { - "type": "long" - }, - "delay": { - "properties": { - "p50": { - "type": "float" - }, - "p99": { - "type": "float" - } - } - } - } - } - } - }, - "elasticsearch": { - "properties": { - "cluster": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - } - } - }, - "kibana_stats": { - "properties": { - "kibana": { - "properties": { - "uuid": { - "path": "service.id", - "type": "alias" - }, - "version": { - "path": "service.version", - "type": "alias" - } - } - }, - "timestamp": { - "path": "@timestamp", - "type": "alias" - } - } - }, - "process": { - "properties": { - "pid": { - "type": "long" - } - } - }, - "service": { - "properties": { - "address": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "timestamp": { - "path": "@timestamp", - "type": "alias" - } - } - }, - "settings": { - "index": { - "codec": "best_compression", - "lifecycle": { - "name": "metrics" - }, - "mapping": { - "total_fields": { - "limit": "10000" - } - }, - "query": { - "default_field": [ - "service.id", - "service.address", - "service.version", - "service.type", - "ecs.version", - "event.dataset", - "event.module", - "host.name", - "error.message", - "kibana.elasticsearch.cluster.id" - ] - } - } - } - } - } - } -} - -{ - "type": "data_stream", - "value": { - "data_stream": "metrics-kibana.stack_monitoring.node_actions-default", - "template": { - "_meta": { - "managed": true, - "managed_by": "fleet", - "package": { - "name": "kibana" - } - }, - "data_stream": { - "allow_custom_routing": false, - "hidden": false - }, - "index_patterns": [ - "metrics-kibana.stack_monitoring.node_actions-*" - ], - "name": "metrics-kibana.stack_monitoring.node_actions", - "priority": 200, - "template": { - "mappings": { - "_meta": { - "managed": true, - "managed_by": "fleet", - "package": { - "name": "kibana" - } - }, - "date_detection": false, - "dynamic": false, - "dynamic_templates": [ - { - "strings_as_keyword": { - "mapping": { - "ignore_above": 1024, - "type": "keyword" - }, - "match_mapping_type": "string" - } - } - ], - "properties": { - "@timestamp": { - "type": "date" - }, - "cluster_uuid": { - "path": "kibana.elasticsearch.cluster.id", - "type": "alias" - }, - "data_stream": { - "properties": { - "dataset": { - "type": "constant_keyword" - }, - "namespace": { - "type": "constant_keyword" - }, - "type": { - "type": "constant_keyword" - } - } - }, - "ecs": { - "properties": { - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "error": { - "properties": { - "message": { - "type": "match_only_text" - } - } - }, - "event": { - "properties": { - "agent_id_status": { - "ignore_above": 1024, - "type": "keyword" - }, - "dataset": { - "ignore_above": 1024, - "type": "keyword" - }, - "duration": { - "type": "long" - }, - "ingested": { - "format": "strict_date_time_no_millis||strict_date_optional_time||epoch_millis", - "type": "date" - }, - "module": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "host": { - "properties": { - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "kibana": { - "properties": { - "elasticsearch": { - "properties": { - "cluster": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "node_actions": { - "properties": { - "executions": { - "type": "long" - }, - "failures": { - "type": "long" - }, - "timeouts": { - "type": "long" - } - } - } - } - }, - "kibana_stats": { - "properties": { - "kibana": { - "properties": { - "uuid": { - "path": "service.id", - "type": "alias" - }, - "version": { - "path": "service.version", - "type": "alias" - } - } - }, - "timestamp": { - "path": "@timestamp", - "type": "alias" - } - } - }, - "process": { - "properties": { - "pid": { - "type": "long" - } - } - }, - "service": { - "properties": { - "address": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "timestamp": { - "path": "@timestamp", - "type": "alias" - } - } - }, - "settings": { - "index": { - "codec": "best_compression", - "lifecycle": { - "name": "metrics" - }, - "mapping": { - "total_fields": { - "limit": "10000" - } - }, - "query": { - "default_field": [ - "service.id", - "service.address", - "service.version", - "service.type", - "ecs.version", - "event.dataset", - "event.module", - "host.name", - "error.message", - "kibana.elasticsearch.cluster.id" - ] - } - } - } - } - } - } -} - -{ - "type": "data_stream", - "value": { - "data_stream": "metrics-kibana.stack_monitoring.node_rules-default", - "template": { - "_meta": { - "managed": true, - "managed_by": "fleet", - "package": { - "name": "kibana" - } - }, - "data_stream": { - "allow_custom_routing": false, - "hidden": false - }, - "index_patterns": [ - "metrics-kibana.stack_monitoring.node_rules-*" - ], - "name": "metrics-kibana.stack_monitoring.node_rules", - "priority": 200, - "template": { - "mappings": { - "_meta": { - "managed": true, - "managed_by": "fleet", - "package": { - "name": "kibana" - } - }, - "date_detection": false, - "dynamic": false, - "dynamic_templates": [ - { - "strings_as_keyword": { - "mapping": { - "ignore_above": 1024, - "type": "keyword" - }, - "match_mapping_type": "string" - } - } - ], - "properties": { - "@timestamp": { - "type": "date" - }, - "cluster_uuid": { - "path": "kibana.elasticsearch.cluster.id", - "type": "alias" - }, - "data_stream": { - "properties": { - "dataset": { - "type": "constant_keyword" - }, - "namespace": { - "type": "constant_keyword" - }, - "type": { - "type": "constant_keyword" - } - } - }, - "ecs": { - "properties": { - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "error": { - "properties": { - "message": { - "type": "match_only_text" - } - } - }, - "event": { - "properties": { - "agent_id_status": { - "ignore_above": 1024, - "type": "keyword" - }, - "dataset": { - "ignore_above": 1024, - "type": "keyword" - }, - "duration": { - "type": "long" - }, - "ingested": { - "format": "strict_date_time_no_millis||strict_date_optional_time||epoch_millis", - "type": "date" - }, - "module": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "host": { - "properties": { - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "kibana": { - "properties": { - "elasticsearch": { - "properties": { - "cluster": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "node_rules": { - "properties": { - "executions": { - "type": "long" - }, - "failures": { - "type": "long" - }, - "timeouts": { - "type": "long" - } - } - } - } - }, - "kibana_stats": { - "properties": { - "kibana": { - "properties": { - "uuid": { - "path": "service.id", - "type": "alias" - }, - "version": { - "path": "service.version", - "type": "alias" - } - } - }, - "timestamp": { - "path": "@timestamp", - "type": "alias" - } - } - }, - "process": { - "properties": { - "pid": { - "type": "long" - } - } - }, - "service": { - "properties": { - "address": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "timestamp": { - "path": "@timestamp", - "type": "alias" - } - } - }, - "settings": { - "index": { - "codec": "best_compression", - "lifecycle": { - "name": "metrics" - }, - "mapping": { - "total_fields": { - "limit": "10000" - } - }, - "query": { - "default_field": [ - "service.id", - "service.address", - "service.version", - "service.type", - "ecs.version", - "event.dataset", - "event.module", - "host.name", - "error.message", - "kibana.elasticsearch.cluster.id" - ] - } - } - } - } - } - } -} - -{ - "type": "data_stream", - "value": { - "data_stream": "metrics-kibana.stack_monitoring.stats-default", - "template": { - "_meta": { - "managed": true, - "managed_by": "fleet", - "package": { - "name": "kibana" - } - }, - "data_stream": { - "allow_custom_routing": false, - "hidden": false - }, - "index_patterns": [ - "metrics-kibana.stack_monitoring.stats-*" - ], - "name": "metrics-kibana.stack_monitoring.stats", - "priority": 200, - "template": { - "mappings": { - "_meta": { - "managed": true, - "managed_by": "fleet", - "package": { - "name": "kibana" - } - }, - "date_detection": false, - "dynamic": false, - "dynamic_templates": [ - { - "strings_as_keyword": { - "mapping": { - "ignore_above": 1024, - "type": "keyword" - }, - "match_mapping_type": "string" - } - } - ], - "properties": { - "@timestamp": { - "type": "date" - }, - "cluster_uuid": { - "path": "kibana.elasticsearch.cluster.id", - "type": "alias" - }, - "data_stream": { - "properties": { - "dataset": { - "type": "constant_keyword" - }, - "namespace": { - "type": "constant_keyword" - }, - "type": { - "type": "constant_keyword" - } - } - }, - "ecs": { - "properties": { - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "error": { - "properties": { - "message": { - "type": "match_only_text" - } - } - }, - "event": { - "properties": { - "agent_id_status": { - "ignore_above": 1024, - "type": "keyword" - }, - "dataset": { - "ignore_above": 1024, - "type": "keyword" - }, - "duration": { - "type": "long" - }, - "ingested": { - "format": "strict_date_time_no_millis||strict_date_optional_time||epoch_millis", - "type": "date" - }, - "module": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "host": { - "properties": { - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "kibana": { - "properties": { - "elasticsearch": { - "properties": { - "cluster": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "stats": { - "properties": { - "concurrent_connections": { - "type": "long" - }, - "host": { - "properties": { - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "index": { - "ignore_above": 1024, - "type": "keyword" - }, - "kibana": { - "properties": { - "status": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "os": { - "properties": { - "distro": { - "ignore_above": 1024, - "type": "keyword" - }, - "distroRelease": { - "ignore_above": 1024, - "type": "keyword" - }, - "load": { - "properties": { - "15m": { - "type": "half_float" - }, - "1m": { - "type": "half_float" - }, - "5m": { - "type": "half_float" - } - } - }, - "memory": { - "properties": { - "free_in_bytes": { - "type": "long" - }, - "total_in_bytes": { - "type": "long" - }, - "used_in_bytes": { - "type": "long" - } - } - }, - "platform": { - "ignore_above": 1024, - "type": "keyword" - }, - "platformRelease": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "process": { - "properties": { - "event_loop_delay": { - "properties": { - "ms": { - "scaling_factor": 1000, - "type": "scaled_float" - } - } - }, - "memory": { - "properties": { - "heap": { - "properties": { - "size_limit": { - "properties": { - "bytes": { - "type": "long" - } - } - }, - "total": { - "properties": { - "bytes": { - "type": "long" - } - } - }, - "uptime": { - "properties": { - "ms": { - "type": "long" - } - } - }, - "used": { - "properties": { - "bytes": { - "type": "long" - } - } - } - } - }, - "resident_set_size": { - "properties": { - "bytes": { - "type": "long" - } - } - } - } - }, - "uptime": { - "properties": { - "ms": { - "type": "long" - } - } - } - } - }, - "request": { - "properties": { - "disconnects": { - "type": "long" - }, - "total": { - "type": "long" - } - } - }, - "response_time": { - "properties": { - "avg": { - "properties": { - "ms": { - "type": "long" - } - } - }, - "max": { - "properties": { - "ms": { - "type": "long" - } - } - } - } - }, - "snapshot": { - "type": "boolean" - }, - "status": { - "ignore_above": 1024, - "type": "keyword" - }, - "transport_address": { - "ignore_above": 1024, - "type": "keyword" - }, - "usage": { - "properties": { - "index": { - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - } - } - }, - "kibana_stats": { - "properties": { - "concurrent_connections": { - "path": "kibana.stats.concurrent_connections", - "type": "alias" - }, - "kibana": { - "properties": { - "response_time": { - "properties": { - "max": { - "path": "kibana.stats.response_time.max.ms", - "type": "alias" - } - } - }, - "status": { - "path": "kibana.stats.status", - "type": "alias" - }, - "uuid": { - "path": "service.id", - "type": "alias" - }, - "version": { - "path": "service.version", - "type": "alias" - } - } - }, - "os": { - "properties": { - "load": { - "properties": { - "15m": { - "path": "kibana.stats.os.load.15m", - "type": "alias" - }, - "1m": { - "path": "kibana.stats.os.load.1m", - "type": "alias" - }, - "5m": { - "path": "kibana.stats.os.load.5m", - "type": "alias" - } - } - }, - "memory": { - "properties": { - "free_in_bytes": { - "path": "kibana.stats.os.memory.free_in_bytes", - "type": "alias" - } - } - } - } - }, - "process": { - "properties": { - "event_loop_delay": { - "path": "kibana.stats.process.event_loop_delay.ms", - "type": "alias" - }, - "memory": { - "properties": { - "heap": { - "properties": { - "size_limit": { - "path": "kibana.stats.process.memory.heap.size_limit.bytes", - "type": "alias" - } - } - }, - "resident_set_size_in_bytes": { - "path": "kibana.stats.process.memory.resident_set_size.bytes", - "type": "alias" - } - } - }, - "uptime_in_millis": { - "path": "kibana.stats.process.uptime.ms", - "type": "alias" - } - } - }, - "requests": { - "properties": { - "disconnects": { - "path": "kibana.stats.request.disconnects", - "type": "alias" - }, - "total": { - "path": "kibana.stats.request.total", - "type": "alias" - } - } - }, - "response_times": { - "properties": { - "average": { - "path": "kibana.stats.response_time.avg.ms", - "type": "alias" - }, - "max": { - "path": "kibana.stats.response_time.max.ms", - "type": "alias" - } - } - }, - "timestamp": { - "path": "@timestamp", - "type": "alias" - } - } - }, - "process": { - "properties": { - "pid": { - "type": "long" - } - } - }, - "service": { - "properties": { - "address": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "timestamp": { - "path": "@timestamp", - "type": "alias" - } - } - }, - "settings": { - "index": { - "codec": "best_compression", - "lifecycle": { - "name": "metrics" - }, - "mapping": { - "total_fields": { - "limit": "10000" - } - }, - "query": { - "default_field": [ - "service.id", - "service.address", - "service.version", - "service.type", - "ecs.version", - "event.dataset", - "event.module", - "host.name", - "error.message", - "kibana.elasticsearch.cluster.id", - "kibana.stats.kibana.status", - "kibana.stats.usage.index", - "kibana.stats.name", - "kibana.stats.index", - "kibana.stats.host.name", - "kibana.stats.status", - "kibana.stats.transport_address", - "kibana.stats.os.distro", - "kibana.stats.os.distroRelease", - "kibana.stats.os.platform", - "kibana.stats.os.platformRelease" - ] - } - } - } - } - } - } -} - -{ - "type": "data_stream", - "value": { - "data_stream": "metrics-kibana.stack_monitoring.status-default", - "template": { - "_meta": { - "managed": true, - "managed_by": "fleet", - "package": { - "name": "kibana" - } - }, - "data_stream": { - "allow_custom_routing": false, - "hidden": false - }, - "index_patterns": [ - "metrics-kibana.stack_monitoring.status-*" - ], - "name": "metrics-kibana.stack_monitoring.status", - "priority": 200, - "template": { - "mappings": { - "_meta": { - "managed": true, - "managed_by": "fleet", - "package": { - "name": "kibana" - } - }, - "date_detection": false, - "dynamic": false, - "dynamic_templates": [ - { - "strings_as_keyword": { - "mapping": { - "ignore_above": 1024, - "type": "keyword" - }, - "match_mapping_type": "string" - } - } - ], - "properties": { - "@timestamp": { - "type": "date" - }, - "data_stream": { - "properties": { - "dataset": { - "type": "constant_keyword" - }, - "namespace": { - "type": "constant_keyword" - }, - "type": { - "type": "constant_keyword" - } - } - }, - "ecs": { - "properties": { - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "error": { - "properties": { - "message": { - "type": "match_only_text" - } - } - }, - "event": { - "properties": { - "agent_id_status": { - "ignore_above": 1024, - "type": "keyword" - }, - "ingested": { - "format": "strict_date_time_no_millis||strict_date_optional_time||epoch_millis", - "type": "date" - } - } - }, - "kibana": { - "properties": { - "status": { - "properties": { - "metrics": { - "properties": { - "concurrent_connections": { - "type": "long" - }, - "requests": { - "properties": { - "disconnects": { - "type": "long" - }, - "total": { - "type": "long" - } - } - } - } - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "status": { - "properties": { - "overall": { - "properties": { - "state": { - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - } - } - } - } - }, - "service": { - "properties": { - "address": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "settings": { - "index": { - "codec": "best_compression", - "lifecycle": { - "name": "metrics" - }, - "mapping": { - "total_fields": { - "limit": "10000" - } - }, - "query": { - "default_field": [ - "service.id", - "service.name", - "service.version", - "service.type", - "service.address", - "ecs.version", - "error.message", - "kibana.status.name", - "kibana.status.status.overall.state" - ] - } - } - } - } - } - } -} - -{ - "type": "data_stream", - "value": { - "data_stream": "metrics-logstash.stack_monitoring.node-default", - "template": { - "_meta": { - "managed": true, - "managed_by": "fleet", - "package": { - "name": "logstash" - } - }, - "data_stream": { - "allow_custom_routing": false, - "hidden": false - }, - "index_patterns": [ - "metrics-logstash.stack_monitoring.node-*" - ], - "name": "metrics-logstash.stack_monitoring.node", - "priority": 200, - "template": { - "mappings": { - "_meta": { - "managed": true, - "managed_by": "fleet", - "package": { - "name": "logstash" - } - }, - "date_detection": false, - "dynamic": false, - "dynamic_templates": [ - { - "strings_as_keyword": { - "mapping": { - "ignore_above": 1024, - "type": "keyword" - }, - "match_mapping_type": "string" - } - } - ], - "properties": { - "@timestamp": { - "type": "date" - }, - "cluster_uuid": { - "path": "logstash.elasticsearch.cluster.id", - "type": "alias" - }, - "data_stream": { - "properties": { - "dataset": { - "type": "constant_keyword" - }, - "namespace": { - "type": "constant_keyword" - }, - "type": { - "type": "constant_keyword" - } - } - }, - "ecs": { - "properties": { - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "error": { - "properties": { - "message": { - "type": "match_only_text" - } - } - }, - "event": { - "properties": { - "agent_id_status": { - "ignore_above": 1024, - "type": "keyword" - }, - "dataset": { - "ignore_above": 1024, - "type": "keyword" - }, - "duration": { - "type": "long" - }, - "ingested": { - "format": "strict_date_time_no_millis||strict_date_optional_time||epoch_millis", - "type": "date" - }, - "module": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "host": { - "properties": { - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "logstash": { - "properties": { - "cluster": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "elasticsearch": { - "properties": { - "cluster": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "node": { - "properties": { - "host": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "jvm": { - "properties": { - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "state": { - "properties": { - "pipeline": { - "properties": { - "batch_size": { - "type": "long" - }, - "ephemeral_id": { - "ignore_above": 1024, - "type": "keyword" - }, - "hash": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "representation": { - "properties": { - "graph": { - "properties": { - "edges": { - "type": "object" - }, - "vertices": { - "type": "object" - } - } - }, - "hash": { - "ignore_above": 1024, - "type": "keyword" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "workers": { - "type": "long" - } - } - } - } - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "logstash_state": { - "properties": { - "pipeline": { - "properties": { - "hash": { - "path": "logstash.node.state.pipeline.hash", - "type": "alias" - }, - "id": { - "path": "logstash.node.state.pipeline.id", - "type": "alias" - } - } - } - } - }, - "process": { - "properties": { - "pid": { - "type": "long" - } - } - }, - "service": { - "properties": { - "address": { - "ignore_above": 1024, - "type": "keyword" - }, - "hostname": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "timestamp": { - "path": "@timestamp", - "type": "alias" - } - } - }, - "settings": { - "index": { - "codec": "best_compression", - "lifecycle": { - "name": "metrics" - }, - "mapping": { - "total_fields": { - "limit": "10000" - } - }, - "query": { - "default_field": [ - "service.hostname", - "service.id", - "service.type", - "service.version", - "service.address", - "service.name", - "ecs.version", - "event.dataset", - "event.module", - "host.name", - "error.message", - "logstash.cluster.id", - "logstash.elasticsearch.cluster.id", - "logstash.node.jvm.version", - "logstash.node.host", - "logstash.node.version", - "logstash.node.id", - "logstash.node.state.pipeline.id", - "logstash.node.state.pipeline.hash", - "logstash.node.state.pipeline.ephemeral_id", - "logstash.node.state.pipeline.representation.hash", - "logstash.node.state.pipeline.representation.type", - "logstash.node.state.pipeline.representation.version" - ] - } - } - } - } - } - } -} - -{ - "type": "data_stream", - "value": { - "data_stream": "metrics-logstash.stack_monitoring.node_stats-default", - "template": { - "_meta": { - "managed": true, - "managed_by": "fleet", - "package": { - "name": "logstash" - } - }, - "data_stream": { - "allow_custom_routing": false, - "hidden": false - }, - "index_patterns": [ - "metrics-logstash.stack_monitoring.node_stats-*" - ], - "name": "metrics-logstash.stack_monitoring.node_stats", - "priority": 200, - "template": { - "mappings": { - "_meta": { - "managed": true, - "managed_by": "fleet", - "package": { - "name": "logstash" - } - }, - "date_detection": false, - "dynamic": false, - "dynamic_templates": [ - { - "strings_as_keyword": { - "mapping": { - "ignore_above": 1024, - "type": "keyword" - }, - "match_mapping_type": "string" - } - } - ], - "properties": { - "@timestamp": { - "type": "date" - }, - "cluster_uuid": { - "path": "logstash.elasticsearch.cluster.id", - "type": "alias" - }, - "data_stream": { - "properties": { - "dataset": { - "type": "constant_keyword" - }, - "namespace": { - "type": "constant_keyword" - }, - "type": { - "type": "constant_keyword" - } - } - }, - "ecs": { - "properties": { - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "error": { - "properties": { - "message": { - "type": "match_only_text" - } - } - }, - "event": { - "properties": { - "agent_id_status": { - "ignore_above": 1024, - "type": "keyword" - }, - "dataset": { - "ignore_above": 1024, - "type": "keyword" - }, - "duration": { - "type": "long" - }, - "ingested": { - "format": "strict_date_time_no_millis||strict_date_optional_time||epoch_millis", - "type": "date" - }, - "module": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "host": { - "properties": { - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "logstash": { - "properties": { - "cluster": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "elasticsearch": { - "properties": { - "cluster": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "node": { - "properties": { - "state": { - "properties": { - "pipeline": { - "properties": { - "hash": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "stats": { - "properties": { - "events": { - "properties": { - "duration_in_millis": { - "type": "long" - }, - "filtered": { - "type": "long" - }, - "in": { - "type": "long" - }, - "out": { - "type": "long" - } - } - }, - "jvm": { - "properties": { - "gc": { - "properties": { - "collectors": { - "properties": { - "old": { - "properties": { - "collection_count": { - "type": "long" - }, - "collection_time_in_millis": { - "type": "long" - } - } - }, - "young": { - "properties": { - "collection_count": { - "type": "long" - }, - "collection_time_in_millis": { - "type": "long" - } - } - } - } - } - } - }, - "mem": { - "properties": { - "heap_max_in_bytes": { - "type": "long" - }, - "heap_used_in_bytes": { - "type": "long" - }, - "heap_used_percent": { - "type": "long" - } - } - }, - "uptime_in_millis": { - "type": "long" - } - } - }, - "logstash": { - "properties": { - "ephemeral_id": { - "ignore_above": 1024, - "type": "keyword" - }, - "host": { - "ignore_above": 1024, - "type": "keyword" - }, - "http_address": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "pipeline": { - "properties": { - "batch_size": { - "type": "long" - }, - "workers": { - "type": "long" - } - } - }, - "snapshot": { - "type": "boolean" - }, - "status": { - "ignore_above": 1024, - "type": "keyword" - }, - "uuid": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "os": { - "properties": { - "cgroup": { - "properties": { - "cpu": { - "properties": { - "cfs_quota_micros": { - "type": "long" - }, - "control_group": { - "type": "text" - }, - "stat": { - "type": "object" - } - } - }, - "cpuacct": { - "type": "object" - } - } - }, - "cpu": { - "properties": { - "load_average": { - "properties": { - "15m": { - "type": "half_float" - }, - "1m": { - "type": "half_float" - }, - "5m": { - "type": "half_float" - } - } - }, - "percent": { - "type": "double" - } - } - } - } - }, - "pipelines": { - "properties": { - "ephemeral_id": { - "ignore_above": 1024, - "type": "keyword" - }, - "events": { - "properties": { - "duration_in_millis": { - "type": "long" - }, - "filtered": { - "type": "long" - }, - "in": { - "type": "long" - }, - "out": { - "type": "long" - }, - "queue_push_duration_in_millis": { - "type": "long" - } - } - }, - "hash": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "queue": { - "properties": { - "events_count": { - "type": "long" - }, - "max_queue_size_in_bytes": { - "type": "long" - }, - "queue_size_in_bytes": { - "type": "long" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "reloads": { - "properties": { - "failures": { - "type": "long" - }, - "successes": { - "type": "long" - } - } - }, - "vertices": { - "properties": { - "duration_in_millis": { - "type": "long" - }, - "events_in": { - "type": "long" - }, - "events_out": { - "type": "long" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "long_counters": { - "properties": { - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "value": { - "type": "long" - } - }, - "type": "nested" - }, - "pipeline_ephemeral_id": { - "ignore_above": 1024, - "type": "keyword" - }, - "queue_push_duration_in_millis": { - "type": "long" - } - }, - "type": "nested" - } - }, - "type": "nested" - }, - "process": { - "properties": { - "cpu": { - "properties": { - "percent": { - "type": "double" - } - } - }, - "max_file_descriptors": { - "type": "long" - }, - "open_file_descriptors": { - "type": "long" - } - } - }, - "queue": { - "properties": { - "events_count": { - "type": "long" - } - } - }, - "reloads": { - "properties": { - "failures": { - "type": "long" - }, - "successes": { - "type": "long" - } - } - }, - "timestamp": { - "type": "date" - } - } - } - } - } - } - }, - "logstash_stats": { - "properties": { - "events": { - "properties": { - "duration_in_millis": { - "path": "logstash.node.stats.events.duration_in_millis", - "type": "alias" - }, - "in": { - "path": "logstash.node.stats.events.in", - "type": "alias" - }, - "out": { - "path": "logstash.node.stats.events.out", - "type": "alias" - } - } - }, - "jvm": { - "properties": { - "mem": { - "properties": { - "heap_max_in_bytes": { - "path": "logstash.node.stats.jvm.mem.heap_max_in_bytes", - "type": "alias" - }, - "heap_used_in_bytes": { - "path": "logstash.node.stats.jvm.mem.heap_used_in_bytes", - "type": "alias" - } - } - }, - "uptime_in_millis": { - "path": "logstash.node.stats.jvm.uptime_in_millis", - "type": "alias" - } - } - }, - "logstash": { - "properties": { - "uuid": { - "path": "logstash.node.stats.logstash.uuid", - "type": "alias" - }, - "version": { - "path": "logstash.node.stats.logstash.version", - "type": "alias" - } - } - }, - "os": { - "properties": { - "cgroup": { - "properties": { - "cpuacct": { - "type": "object" - } - } - }, - "cpu": { - "properties": { - "load_average": { - "properties": { - "15m": { - "path": "logstash.node.stats.os.cpu.load_average.15m", - "type": "alias" - }, - "1m": { - "path": "logstash.node.stats.os.cpu.load_average.1m", - "type": "alias" - }, - "5m": { - "path": "logstash.node.stats.os.cpu.load_average.5m", - "type": "alias" - } - } - }, - "stat": { - "type": "object" - } - } - } - } - }, - "pipelines": { - "type": "nested" - }, - "process": { - "properties": { - "cpu": { - "properties": { - "percent": { - "path": "logstash.node.stats.process.cpu.percent", - "type": "alias" - } - } - } - } - }, - "queue": { - "properties": { - "events_count": { - "path": "logstash.node.stats.queue.events_count", - "type": "alias" - } - } - }, - "timestamp": { - "path": "@timestamp", - "type": "alias" - } - } - }, - "process": { - "properties": { - "pid": { - "type": "long" - } - } - }, - "service": { - "properties": { - "address": { - "ignore_above": 1024, - "type": "keyword" - }, - "hostname": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "timestamp": { - "path": "@timestamp", - "type": "alias" - } - } - }, - "settings": { - "index": { - "codec": "best_compression", - "lifecycle": { - "name": "metrics" - }, - "mapping": { - "total_fields": { - "limit": "10000" - } - }, - "query": { - "default_field": [ - "service.hostname", - "service.id", - "service.type", - "service.version", - "service.address", - "service.name", - "ecs.version", - "event.dataset", - "event.module", - "host.name", - "error.message", - "logstash.elasticsearch.cluster.id", - "logstash.node.state.pipeline.id", - "logstash.node.state.pipeline.hash", - "logstash.node.stats.logstash.uuid", - "logstash.node.stats.logstash.version", - "logstash.node.stats.logstash.ephemeral_id", - "logstash.node.stats.logstash.host", - "logstash.node.stats.logstash.http_address", - "logstash.node.stats.logstash.name", - "logstash.node.stats.logstash.status", - "logstash.node.stats.os.cgroup.cpuacct.control_group", - "logstash.node.stats.os.cgroup.cpu.control_group", - "logstash.node.stats.pipelines.id", - "logstash.node.stats.pipelines.hash", - "logstash.node.stats.pipelines.ephemeral_id", - "logstash.node.stats.pipelines.queue.type", - "logstash.node.stats.pipelines.vertices.id", - "logstash.node.stats.pipelines.vertices.long_counters.name", - "logstash.node.stats.pipelines.vertices.pipeline_ephemeral_id", - "logstash.cluster.id" - ] - } - } - } - } - } - } -} diff --git a/x-pack/test/functional/es_archives/monitoring/logstash_pipelines_package/data.json.gz b/x-pack/test/functional/es_archives/monitoring/logstash_pipelines_package/data.json.gz deleted file mode 100644 index c42533016d52e..0000000000000 Binary files a/x-pack/test/functional/es_archives/monitoring/logstash_pipelines_package/data.json.gz and /dev/null differ diff --git a/x-pack/test/functional/es_archives/monitoring/logstash_pipelines_package/mappings.json b/x-pack/test/functional/es_archives/monitoring/logstash_pipelines_package/mappings.json deleted file mode 100644 index 5684702273ac7..0000000000000 --- a/x-pack/test/functional/es_archives/monitoring/logstash_pipelines_package/mappings.json +++ /dev/null @@ -1,7138 +0,0 @@ -{ - "type": "data_stream", - "value": { - "data_stream": "metrics-elasticsearch.stack_monitoring.cluster_stats-default", - "template": { - "_meta": { - "managed": true, - "managed_by": "fleet", - "package": { - "name": "elasticsearch" - } - }, - "data_stream": { - "allow_custom_routing": false, - "hidden": false - }, - "index_patterns": [ - "metrics-elasticsearch.stack_monitoring.cluster_stats-*" - ], - "name": "metrics-elasticsearch.stack_monitoring.cluster_stats", - "priority": 200, - "template": { - "mappings": { - "_meta": { - "managed": true, - "managed_by": "fleet", - "package": { - "name": "elasticsearch" - } - }, - "date_detection": false, - "dynamic": false, - "dynamic_templates": [ - { - "strings_as_keyword": { - "mapping": { - "ignore_above": 1024, - "type": "keyword" - }, - "match_mapping_type": "string" - } - } - ], - "properties": { - "@timestamp": { - "type": "date" - }, - "cluster_state": { - "properties": { - "master_node": { - "path": "elasticsearch.cluster.stats.state.master_node", - "type": "alias" - }, - "nodes_hash": { - "path": "elasticsearch.cluster.stats.state.nodes_hash", - "type": "alias" - }, - "state_uuid": { - "path": "elasticsearch.cluster.stats.state.state_uuid", - "type": "alias" - }, - "status": { - "path": "elasticsearch.cluster.stats.status", - "type": "alias" - }, - "version": { - "path": "elasticsearch.cluster.stats.state.version", - "type": "alias" - } - } - }, - "cluster_stats": { - "properties": { - "indices": { - "properties": { - "count": { - "path": "elasticsearch.cluster.stats.indices.total", - "type": "alias" - }, - "shards": { - "properties": { - "total": { - "path": "elasticsearch.cluster.stats.indices.shards.count", - "type": "alias" - } - } - } - } - }, - "nodes": { - "properties": { - "count": { - "properties": { - "total": { - "path": "elasticsearch.cluster.stats.nodes.count", - "type": "alias" - } - } - }, - "jvm": { - "properties": { - "max_uptime_in_millis": { - "path": "elasticsearch.cluster.stats.nodes.jvm.max_uptime.ms", - "type": "alias" - }, - "mem": { - "properties": { - "heap_max_in_bytes": { - "path": "elasticsearch.cluster.stats.nodes.jvm.memory.heap.max.bytes", - "type": "alias" - }, - "heap_used_in_bytes": { - "path": "elasticsearch.cluster.stats.nodes.jvm.memory.heap.used.bytes", - "type": "alias" - } - } - } - } - } - } - } - } - }, - "cluster_uuid": { - "path": "elasticsearch.cluster.id", - "type": "alias" - }, - "data_stream": { - "properties": { - "dataset": { - "type": "constant_keyword" - }, - "namespace": { - "type": "constant_keyword" - }, - "type": { - "type": "constant_keyword" - } - } - }, - "ecs": { - "properties": { - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "elasticsearch": { - "properties": { - "cluster": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "state": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "stats": { - "properties": { - "indices": { - "properties": { - "docs": { - "properties": { - "total": { - "type": "long" - } - } - }, - "fielddata": { - "properties": { - "memory": { - "properties": { - "bytes": { - "type": "long" - } - } - } - } - }, - "shards": { - "properties": { - "count": { - "type": "long" - }, - "primaries": { - "type": "long" - } - } - }, - "store": { - "properties": { - "size": { - "properties": { - "bytes": { - "type": "long" - } - } - } - } - }, - "total": { - "type": "long" - } - } - }, - "license": { - "properties": { - "cluster_needs_tls": { - "type": "boolean" - }, - "expiry_date": { - "type": "date" - }, - "expiry_date_in_millis": { - "type": "long" - }, - "issue_date": { - "type": "date" - }, - "issue_date_in_millis": { - "type": "long" - }, - "issued_to": { - "ignore_above": 1024, - "type": "keyword" - }, - "issuer": { - "ignore_above": 1024, - "type": "keyword" - }, - "max_nodes": { - "type": "long" - }, - "start_date_in_millis": { - "type": "long" - }, - "status": { - "ignore_above": 1024, - "type": "keyword" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - }, - "uid": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "nodes": { - "properties": { - "count": { - "type": "long" - }, - "data": { - "type": "long" - }, - "fs": { - "properties": { - "available": { - "properties": { - "bytes": { - "type": "long" - } - } - }, - "total": { - "properties": { - "bytes": { - "type": "long" - } - } - } - } - }, - "jvm": { - "properties": { - "max_uptime": { - "properties": { - "ms": { - "type": "long" - } - } - }, - "memory": { - "properties": { - "heap": { - "properties": { - "max": { - "properties": { - "bytes": { - "type": "long" - } - } - }, - "used": { - "properties": { - "bytes": { - "type": "long" - } - } - } - } - } - } - } - } - }, - "master": { - "type": "long" - }, - "stats": { - "properties": { - "data": { - "type": "long" - } - } - }, - "versions": { - "type": "text" - } - } - }, - "stack": { - "properties": { - "apm": { - "properties": { - "found": { - "type": "boolean" - } - } - }, - "xpack": { - "properties": { - "ccr": { - "properties": { - "available": { - "type": "boolean" - }, - "enabled": { - "type": "boolean" - } - } - } - } - } - } - }, - "state": { - "properties": { - "master_node": { - "ignore_above": 1024, - "type": "keyword" - }, - "nodes": { - "type": "flattened" - }, - "nodes_hash": { - "ignore_above": 1024, - "type": "keyword" - }, - "state_uuid": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "status": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "node": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "master": { - "type": "boolean" - }, - "mlockall": { - "type": "boolean" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "error": { - "properties": { - "message": { - "type": "match_only_text" - } - } - }, - "event": { - "properties": { - "agent_id_status": { - "ignore_above": 1024, - "type": "keyword" - }, - "dataset": { - "ignore_above": 1024, - "type": "keyword" - }, - "duration": { - "type": "long" - }, - "ingested": { - "format": "strict_date_time_no_millis||strict_date_optional_time||epoch_millis", - "type": "date" - }, - "module": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "host": { - "properties": { - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "license": { - "properties": { - "status": { - "path": "elasticsearch.cluster.stats.license.status", - "type": "alias" - }, - "type": { - "path": "elasticsearch.cluster.stats.license.type", - "type": "alias" - } - } - }, - "service": { - "properties": { - "address": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "source_node": { - "properties": { - "name": { - "path": "elasticsearch.node.name", - "type": "alias" - }, - "uuid": { - "path": "elasticsearch.node.id", - "type": "alias" - } - } - }, - "stack_stats": { - "properties": { - "apm": { - "properties": { - "found": { - "path": "elasticsearch.cluster.stats.stack.apm.found", - "type": "alias" - } - } - }, - "xpack": { - "properties": { - "ccr": { - "properties": { - "available": { - "path": "elasticsearch.cluster.stats.stack.xpack.ccr.available", - "type": "alias" - }, - "enabled": { - "path": "elasticsearch.cluster.stats.stack.xpack.ccr.enabled", - "type": "alias" - } - } - } - } - } - } - }, - "timestamp": { - "path": "@timestamp", - "type": "alias" - } - } - }, - "settings": { - "index": { - "codec": "best_compression", - "lifecycle": { - "name": "metrics" - }, - "mapping": { - "total_fields": { - "limit": "10000" - } - }, - "query": { - "default_field": [ - "ecs.version", - "event.dataset", - "event.module", - "host.name", - "service.address", - "service.type", - "service.name", - "error.message", - "elasticsearch.cluster.stats.version", - "elasticsearch.cluster.stats.state.nodes_hash", - "elasticsearch.cluster.stats.state.master_node", - "elasticsearch.cluster.stats.state.version", - "elasticsearch.cluster.stats.state.state_uuid", - "elasticsearch.cluster.stats.status", - "elasticsearch.cluster.stats.nodes.versions", - "elasticsearch.cluster.stats.license.issued_to", - "elasticsearch.cluster.stats.license.issuer", - "elasticsearch.cluster.stats.license.status", - "elasticsearch.cluster.stats.license.type", - "elasticsearch.cluster.stats.license.uid", - "elasticsearch.cluster.name", - "elasticsearch.cluster.id", - "elasticsearch.cluster.state.id", - "elasticsearch.version", - "elasticsearch.node.id", - "elasticsearch.node.name" - ] - } - } - } - } - } - } -} - -{ - "type": "data_stream", - "value": { - "data_stream": "metrics-elasticsearch.stack_monitoring.enrich-default", - "template": { - "_meta": { - "managed": true, - "managed_by": "fleet", - "package": { - "name": "elasticsearch" - } - }, - "data_stream": { - "allow_custom_routing": false, - "hidden": false - }, - "index_patterns": [ - "metrics-elasticsearch.stack_monitoring.enrich-*" - ], - "name": "metrics-elasticsearch.stack_monitoring.enrich", - "priority": 200, - "template": { - "mappings": { - "_meta": { - "managed": true, - "managed_by": "fleet", - "package": { - "name": "elasticsearch" - } - }, - "date_detection": false, - "dynamic": false, - "dynamic_templates": [ - { - "strings_as_keyword": { - "mapping": { - "ignore_above": 1024, - "type": "keyword" - }, - "match_mapping_type": "string" - } - } - ], - "properties": { - "@timestamp": { - "type": "date" - }, - "cluster_uuid": { - "path": "elasticsearch.cluster.id", - "type": "alias" - }, - "data_stream": { - "properties": { - "dataset": { - "type": "constant_keyword" - }, - "namespace": { - "type": "constant_keyword" - }, - "type": { - "type": "constant_keyword" - } - } - }, - "ecs": { - "properties": { - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "elasticsearch": { - "properties": { - "cluster": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "state": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "enrich": { - "properties": { - "executed_searches": { - "properties": { - "total": { - "type": "long" - } - } - }, - "executing_policy": { - "properties": { - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "task": { - "properties": { - "action": { - "ignore_above": 1024, - "type": "keyword" - }, - "cancellable": { - "type": "boolean" - }, - "id": { - "type": "long" - }, - "parent_task_id": { - "ignore_above": 1024, - "type": "keyword" - }, - "task": { - "ignore_above": 1024, - "type": "keyword" - }, - "time": { - "properties": { - "running": { - "properties": { - "nano": { - "type": "long" - } - } - }, - "start": { - "properties": { - "ms": { - "type": "long" - } - } - } - } - } - } - } - } - }, - "queue": { - "properties": { - "size": { - "type": "long" - } - } - }, - "remote_requests": { - "properties": { - "current": { - "type": "long" - }, - "total": { - "type": "long" - } - } - } - } - }, - "node": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "master": { - "type": "boolean" - }, - "mlockall": { - "type": "boolean" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "error": { - "properties": { - "message": { - "type": "match_only_text" - } - } - }, - "event": { - "properties": { - "agent_id_status": { - "ignore_above": 1024, - "type": "keyword" - }, - "dataset": { - "ignore_above": 1024, - "type": "keyword" - }, - "duration": { - "type": "long" - }, - "ingested": { - "format": "strict_date_time_no_millis||strict_date_optional_time||epoch_millis", - "type": "date" - }, - "module": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "host": { - "properties": { - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "service": { - "properties": { - "address": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "source_node": { - "properties": { - "name": { - "path": "elasticsearch.node.name", - "type": "alias" - }, - "uuid": { - "path": "elasticsearch.node.id", - "type": "alias" - } - } - }, - "timestamp": { - "path": "@timestamp", - "type": "alias" - } - } - }, - "settings": { - "index": { - "codec": "best_compression", - "lifecycle": { - "name": "metrics" - }, - "mapping": { - "total_fields": { - "limit": "10000" - } - }, - "query": { - "default_field": [ - "ecs.version", - "event.dataset", - "event.module", - "host.name", - "service.address", - "service.type", - "service.name", - "error.message", - "elasticsearch.enrich.executing_policy.name", - "elasticsearch.enrich.executing_policy.task.task", - "elasticsearch.enrich.executing_policy.task.action", - "elasticsearch.enrich.executing_policy.task.parent_task_id", - "elasticsearch.cluster.name", - "elasticsearch.cluster.id", - "elasticsearch.cluster.state.id", - "elasticsearch.node.id", - "elasticsearch.node.name" - ] - } - } - } - } - } - } -} - -{ - "type": "data_stream", - "value": { - "data_stream": "metrics-elasticsearch.stack_monitoring.index-default", - "template": { - "_meta": { - "managed": true, - "managed_by": "fleet", - "package": { - "name": "elasticsearch" - } - }, - "data_stream": { - "allow_custom_routing": false, - "hidden": false - }, - "index_patterns": [ - "metrics-elasticsearch.stack_monitoring.index-*" - ], - "name": "metrics-elasticsearch.stack_monitoring.index", - "priority": 200, - "template": { - "mappings": { - "_meta": { - "managed": true, - "managed_by": "fleet", - "package": { - "name": "elasticsearch" - } - }, - "date_detection": false, - "dynamic": false, - "dynamic_templates": [ - { - "strings_as_keyword": { - "mapping": { - "ignore_above": 1024, - "type": "keyword" - }, - "match_mapping_type": "string" - } - } - ], - "properties": { - "@timestamp": { - "type": "date" - }, - "cluster_uuid": { - "path": "elasticsearch.cluster.id", - "type": "alias" - }, - "data_stream": { - "properties": { - "dataset": { - "type": "constant_keyword" - }, - "namespace": { - "type": "constant_keyword" - }, - "type": { - "type": "constant_keyword" - } - } - }, - "ecs": { - "properties": { - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "elasticsearch": { - "properties": { - "cluster": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "state": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "index": { - "properties": { - "created": { - "type": "long" - }, - "hidden": { - "type": "boolean" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "primaries": { - "properties": { - "docs": { - "properties": { - "count": { - "type": "long" - }, - "deleted": { - "type": "long" - } - } - }, - "indexing": { - "properties": { - "index_time_in_millis": { - "type": "long" - }, - "index_total": { - "type": "long" - }, - "throttle_time_in_millis": { - "type": "long" - } - } - }, - "merges": { - "properties": { - "total_size_in_bytes": { - "type": "long" - } - } - }, - "query_cache": { - "properties": { - "hit_count": { - "type": "long" - }, - "memory_size_in_bytes": { - "type": "long" - }, - "miss_count": { - "type": "long" - } - } - }, - "refresh": { - "properties": { - "external_total_time_in_millis": { - "type": "long" - }, - "total_time_in_millis": { - "type": "long" - } - } - }, - "request_cache": { - "properties": { - "evictions": { - "type": "long" - }, - "hit_count": { - "type": "long" - }, - "memory_size_in_bytes": { - "type": "long" - }, - "miss_count": { - "type": "long" - } - } - }, - "search": { - "properties": { - "query_time_in_millis": { - "type": "long" - }, - "query_total": { - "type": "long" - } - } - }, - "segments": { - "properties": { - "count": { - "type": "long" - }, - "doc_values_memory_in_bytes": { - "type": "long" - }, - "fixed_bit_set_memory_in_bytes": { - "type": "long" - }, - "index_writer_memory_in_bytes": { - "type": "long" - }, - "memory_in_bytes": { - "type": "long" - }, - "norms_memory_in_bytes": { - "type": "long" - }, - "points_memory_in_bytes": { - "type": "long" - }, - "stored_fields_memory_in_bytes": { - "type": "long" - }, - "term_vectors_memory_in_bytes": { - "type": "long" - }, - "terms_memory_in_bytes": { - "type": "long" - }, - "version_map_memory_in_bytes": { - "type": "long" - } - } - }, - "store": { - "properties": { - "size_in_bytes": { - "type": "long" - } - } - } - } - }, - "shards": { - "properties": { - "primaries": { - "type": "long" - }, - "total": { - "type": "long" - } - } - }, - "status": { - "ignore_above": 1024, - "type": "keyword" - }, - "total": { - "properties": { - "bulk": { - "properties": { - "avg_size_in_bytes": { - "type": "long" - }, - "avg_time_in_millis": { - "type": "long" - }, - "total_operations": { - "type": "long" - }, - "total_size_in_bytes": { - "type": "long" - }, - "total_time_in_millis": { - "type": "long" - } - } - }, - "docs": { - "properties": { - "count": { - "type": "long" - }, - "deleted": { - "type": "long" - } - } - }, - "fielddata": { - "properties": { - "evictions": { - "type": "long" - }, - "memory_size_in_bytes": { - "type": "long" - } - } - }, - "indexing": { - "properties": { - "index_time_in_millis": { - "type": "long" - }, - "index_total": { - "type": "long" - }, - "throttle_time_in_millis": { - "type": "long" - } - } - }, - "merges": { - "properties": { - "total_size_in_bytes": { - "type": "long" - } - } - }, - "query_cache": { - "properties": { - "evictions": { - "type": "long" - }, - "hit_count": { - "type": "long" - }, - "memory_size_in_bytes": { - "type": "long" - }, - "miss_count": { - "type": "long" - } - } - }, - "refresh": { - "properties": { - "external_total_time_in_millis": { - "type": "long" - }, - "total_time_in_millis": { - "type": "long" - } - } - }, - "request_cache": { - "properties": { - "evictions": { - "type": "long" - }, - "hit_count": { - "type": "long" - }, - "memory_size_in_bytes": { - "type": "long" - }, - "miss_count": { - "type": "long" - } - } - }, - "search": { - "properties": { - "query_time_in_millis": { - "type": "long" - }, - "query_total": { - "type": "long" - } - } - }, - "segments": { - "properties": { - "count": { - "type": "long" - }, - "doc_values_memory_in_bytes": { - "type": "long" - }, - "fixed_bit_set_memory_in_bytes": { - "type": "long" - }, - "index_writer_memory_in_bytes": { - "type": "long" - }, - "memory": { - "properties": { - "bytes": { - "type": "long" - } - } - }, - "memory_in_bytes": { - "type": "long" - }, - "norms_memory_in_bytes": { - "type": "long" - }, - "points_memory_in_bytes": { - "type": "long" - }, - "stored_fields_memory_in_bytes": { - "type": "long" - }, - "term_vectors_memory_in_bytes": { - "type": "long" - }, - "terms_memory_in_bytes": { - "type": "long" - }, - "version_map_memory_in_bytes": { - "type": "long" - } - } - }, - "store": { - "properties": { - "size": { - "properties": { - "bytes": { - "type": "long" - } - } - }, - "size_in_bytes": { - "type": "long" - } - } - } - } - }, - "uuid": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "node": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "master": { - "type": "boolean" - }, - "mlockall": { - "type": "boolean" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "error": { - "properties": { - "message": { - "type": "match_only_text" - } - } - }, - "event": { - "properties": { - "agent_id_status": { - "ignore_above": 1024, - "type": "keyword" - }, - "dataset": { - "ignore_above": 1024, - "type": "keyword" - }, - "duration": { - "type": "long" - }, - "ingested": { - "format": "strict_date_time_no_millis||strict_date_optional_time||epoch_millis", - "type": "date" - }, - "module": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "host": { - "properties": { - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "index_stats": { - "properties": { - "index": { - "path": "elasticsearch.index.name", - "type": "alias" - }, - "primaries": { - "properties": { - "docs": { - "properties": { - "count": { - "path": "elasticsearch.index.primaries.docs.count", - "type": "alias" - } - } - }, - "indexing": { - "properties": { - "index_time_in_millis": { - "path": "elasticsearch.index.primaries.indexing.index_time_in_millis", - "type": "alias" - }, - "index_total": { - "path": "elasticsearch.index.primaries.indexing.index_total", - "type": "alias" - }, - "throttle_time_in_millis": { - "path": "elasticsearch.index.primaries.indexing.throttle_time_in_millis", - "type": "alias" - } - } - }, - "merges": { - "properties": { - "total_size_in_bytes": { - "path": "elasticsearch.index.primaries.merges.total_size_in_bytes", - "type": "alias" - } - } - }, - "refresh": { - "properties": { - "total_time_in_millis": { - "path": "elasticsearch.index.primaries.refresh.total_time_in_millis", - "type": "alias" - } - } - }, - "segments": { - "properties": { - "count": { - "path": "elasticsearch.index.primaries.segments.count", - "type": "alias" - } - } - }, - "store": { - "properties": { - "size_in_bytes": { - "path": "elasticsearch.index.primaries.store.size_in_bytes", - "type": "alias" - } - } - } - } - }, - "total": { - "properties": { - "fielddata": { - "properties": { - "memory_size_in_bytes": { - "path": "elasticsearch.index.total.fielddata.memory_size_in_bytes", - "type": "alias" - } - } - }, - "indexing": { - "properties": { - "index_time_in_millis": { - "path": "elasticsearch.index.total.indexing.index_time_in_millis", - "type": "alias" - }, - "index_total": { - "path": "elasticsearch.index.total.indexing.index_total", - "type": "alias" - }, - "throttle_time_in_millis": { - "path": "elasticsearch.index.total.indexing.throttle_time_in_millis", - "type": "alias" - } - } - }, - "merges": { - "properties": { - "total_size_in_bytes": { - "path": "elasticsearch.index.total.merges.total_size_in_bytes", - "type": "alias" - } - } - }, - "query_cache": { - "properties": { - "memory_size_in_bytes": { - "path": "elasticsearch.index.total.query_cache.memory_size_in_bytes", - "type": "alias" - } - } - }, - "refresh": { - "properties": { - "total_time_in_millis": { - "path": "elasticsearch.index.total.refresh.total_time_in_millis", - "type": "alias" - } - } - }, - "request_cache": { - "properties": { - "memory_size_in_bytes": { - "path": "elasticsearch.index.total.request_cache.memory_size_in_bytes", - "type": "alias" - } - } - }, - "search": { - "properties": { - "query_time_in_millis": { - "path": "elasticsearch.index.total.search.query_time_in_millis", - "type": "alias" - }, - "query_total": { - "path": "elasticsearch.index.total.search.query_total", - "type": "alias" - } - } - }, - "segments": { - "properties": { - "count": { - "path": "elasticsearch.index.total.segments.count", - "type": "alias" - }, - "doc_values_memory_in_bytes": { - "path": "elasticsearch.index.total.segments.doc_values_memory_in_bytes", - "type": "alias" - }, - "fixed_bit_set_memory_in_bytes": { - "path": "elasticsearch.index.total.segments.fixed_bit_set_memory_in_bytes", - "type": "alias" - }, - "index_writer_memory_in_bytes": { - "path": "elasticsearch.index.total.segments.index_writer_memory_in_bytes", - "type": "alias" - }, - "memory_in_bytes": { - "path": "elasticsearch.index.total.segments.memory_in_bytes", - "type": "alias" - }, - "norms_memory_in_bytes": { - "path": "elasticsearch.index.total.segments.norms_memory_in_bytes", - "type": "alias" - }, - "points_memory_in_bytes": { - "path": "elasticsearch.index.total.segments.points_memory_in_bytes", - "type": "alias" - }, - "stored_fields_memory_in_bytes": { - "path": "elasticsearch.index.total.segments.stored_fields_memory_in_bytes", - "type": "alias" - }, - "term_vectors_memory_in_bytes": { - "path": "elasticsearch.index.total.segments.term_vectors_memory_in_bytes", - "type": "alias" - }, - "terms_memory_in_bytes": { - "path": "elasticsearch.index.total.segments.terms_memory_in_bytes", - "type": "alias" - }, - "version_map_memory_in_bytes": { - "path": "elasticsearch.index.total.segments.version_map_memory_in_bytes", - "type": "alias" - } - } - }, - "store": { - "properties": { - "size_in_bytes": { - "path": "elasticsearch.index.total.store.size_in_bytes", - "type": "alias" - } - } - } - } - } - } - }, - "service": { - "properties": { - "address": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "source_node": { - "properties": { - "name": { - "path": "elasticsearch.node.name", - "type": "alias" - }, - "uuid": { - "path": "elasticsearch.node.id", - "type": "alias" - } - } - }, - "timestamp": { - "path": "@timestamp", - "type": "alias" - } - } - }, - "settings": { - "index": { - "codec": "best_compression", - "lifecycle": { - "name": "metrics" - }, - "mapping": { - "total_fields": { - "limit": "10000" - } - }, - "query": { - "default_field": [ - "ecs.version", - "event.dataset", - "event.module", - "host.name", - "service.address", - "service.type", - "service.name", - "error.message", - "elasticsearch.index.uuid", - "elasticsearch.index.status", - "elasticsearch.index.name", - "elasticsearch.cluster.name", - "elasticsearch.cluster.id", - "elasticsearch.cluster.state.id", - "elasticsearch.node.id", - "elasticsearch.node.name" - ] - } - } - } - } - } - } -} - -{ - "type": "data_stream", - "value": { - "data_stream": "metrics-elasticsearch.stack_monitoring.index_recovery-default", - "template": { - "_meta": { - "managed": true, - "managed_by": "fleet", - "package": { - "name": "elasticsearch" - } - }, - "data_stream": { - "allow_custom_routing": false, - "hidden": false - }, - "index_patterns": [ - "metrics-elasticsearch.stack_monitoring.index_recovery-*" - ], - "name": "metrics-elasticsearch.stack_monitoring.index_recovery", - "priority": 200, - "template": { - "mappings": { - "_meta": { - "managed": true, - "managed_by": "fleet", - "package": { - "name": "elasticsearch" - } - }, - "date_detection": false, - "dynamic": false, - "dynamic_templates": [ - { - "strings_as_keyword": { - "mapping": { - "ignore_above": 1024, - "type": "keyword" - }, - "match_mapping_type": "string" - } - } - ], - "properties": { - "@timestamp": { - "type": "date" - }, - "cluster_uuid": { - "path": "elasticsearch.cluster.id", - "type": "alias" - }, - "data_stream": { - "properties": { - "dataset": { - "type": "constant_keyword" - }, - "namespace": { - "type": "constant_keyword" - }, - "type": { - "type": "constant_keyword" - } - } - }, - "ecs": { - "properties": { - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "elasticsearch": { - "properties": { - "cluster": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "state": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "index": { - "properties": { - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "recovery": { - "properties": { - "id": { - "type": "long" - }, - "index": { - "properties": { - "files": { - "properties": { - "percent": { - "ignore_above": 1024, - "type": "keyword" - }, - "recovered": { - "type": "long" - }, - "reused": { - "type": "long" - }, - "total": { - "type": "long" - } - } - }, - "size": { - "properties": { - "recovered_in_bytes": { - "type": "long" - }, - "reused_in_bytes": { - "type": "long" - }, - "total_in_bytes": { - "type": "long" - } - } - } - } - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "primary": { - "type": "boolean" - }, - "source": { - "properties": { - "host": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "transport_address": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "stage": { - "ignore_above": 1024, - "type": "keyword" - }, - "start_time": { - "properties": { - "ms": { - "type": "long" - } - } - }, - "stop_time": { - "properties": { - "ms": { - "type": "long" - } - } - }, - "target": { - "properties": { - "host": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "transport_address": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "total_time": { - "properties": { - "ms": { - "type": "long" - } - } - }, - "translog": { - "properties": { - "percent": { - "ignore_above": 1024, - "type": "keyword" - }, - "total": { - "type": "long" - }, - "total_on_start": { - "type": "long" - } - } - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - }, - "verify_index": { - "properties": { - "check_index_time": { - "properties": { - "ms": { - "type": "long" - } - } - }, - "total_time": { - "properties": { - "ms": { - "type": "long" - } - } - } - } - } - } - } - } - }, - "node": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "master": { - "type": "boolean" - }, - "mlockall": { - "type": "boolean" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "error": { - "properties": { - "message": { - "type": "match_only_text" - } - } - }, - "event": { - "properties": { - "agent_id_status": { - "ignore_above": 1024, - "type": "keyword" - }, - "dataset": { - "ignore_above": 1024, - "type": "keyword" - }, - "duration": { - "type": "long" - }, - "ingested": { - "format": "strict_date_time_no_millis||strict_date_optional_time||epoch_millis", - "type": "date" - }, - "module": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "host": { - "properties": { - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "index_recovery": { - "properties": { - "shards": { - "properties": { - "start_time_in_millis": { - "path": "elasticsearch.index.recovery.start_time.ms", - "type": "alias" - }, - "stop_time_in_millis": { - "path": "elasticsearch.index.recovery.stop_time.ms", - "type": "alias" - } - } - } - } - }, - "service": { - "properties": { - "address": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "source_node": { - "properties": { - "name": { - "path": "elasticsearch.node.name", - "type": "alias" - }, - "uuid": { - "path": "elasticsearch.node.id", - "type": "alias" - } - } - }, - "timestamp": { - "path": "@timestamp", - "type": "alias" - } - } - }, - "settings": { - "index": { - "codec": "best_compression", - "lifecycle": { - "name": "metrics" - }, - "mapping": { - "total_fields": { - "limit": "10000" - } - }, - "query": { - "default_field": [ - "ecs.version", - "event.dataset", - "event.module", - "host.name", - "service.address", - "service.type", - "service.name", - "error.message", - "elasticsearch.index.name", - "elasticsearch.index.recovery.index.files.percent", - "elasticsearch.index.recovery.name", - "elasticsearch.index.recovery.type", - "elasticsearch.index.recovery.stage", - "elasticsearch.index.recovery.translog.percent", - "elasticsearch.index.recovery.target.transport_address", - "elasticsearch.index.recovery.target.id", - "elasticsearch.index.recovery.target.host", - "elasticsearch.index.recovery.target.name", - "elasticsearch.index.recovery.source.transport_address", - "elasticsearch.index.recovery.source.id", - "elasticsearch.index.recovery.source.host", - "elasticsearch.index.recovery.source.name", - "elasticsearch.cluster.name", - "elasticsearch.cluster.id", - "elasticsearch.cluster.state.id", - "elasticsearch.node.id", - "elasticsearch.node.name" - ] - } - } - } - } - } - } -} - -{ - "type": "data_stream", - "value": { - "data_stream": "metrics-elasticsearch.stack_monitoring.index_summary-default", - "template": { - "_meta": { - "managed": true, - "managed_by": "fleet", - "package": { - "name": "elasticsearch" - } - }, - "data_stream": { - "allow_custom_routing": false, - "hidden": false - }, - "index_patterns": [ - "metrics-elasticsearch.stack_monitoring.index_summary-*" - ], - "name": "metrics-elasticsearch.stack_monitoring.index_summary", - "priority": 200, - "template": { - "mappings": { - "_meta": { - "managed": true, - "managed_by": "fleet", - "package": { - "name": "elasticsearch" - } - }, - "date_detection": false, - "dynamic": false, - "dynamic_templates": [ - { - "strings_as_keyword": { - "mapping": { - "ignore_above": 1024, - "type": "keyword" - }, - "match_mapping_type": "string" - } - } - ], - "properties": { - "@timestamp": { - "type": "date" - }, - "cluster_uuid": { - "path": "elasticsearch.cluster.id", - "type": "alias" - }, - "data_stream": { - "properties": { - "dataset": { - "type": "constant_keyword" - }, - "namespace": { - "type": "constant_keyword" - }, - "type": { - "type": "constant_keyword" - } - } - }, - "ecs": { - "properties": { - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "elasticsearch": { - "properties": { - "cluster": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "state": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "index": { - "properties": { - "summary": { - "properties": { - "primaries": { - "properties": { - "bulk": { - "properties": { - "operations": { - "properties": { - "count": { - "type": "long" - } - } - }, - "size": { - "properties": { - "bytes": { - "type": "long" - } - } - }, - "time": { - "properties": { - "avg": { - "properties": { - "bytes": { - "type": "long" - }, - "ms": { - "type": "long" - } - } - }, - "count": { - "properties": { - "ms": { - "type": "long" - } - } - } - } - } - } - }, - "docs": { - "properties": { - "count": { - "type": "long" - }, - "deleted": { - "type": "long" - } - } - }, - "indexing": { - "properties": { - "index": { - "properties": { - "count": { - "type": "long" - }, - "time": { - "properties": { - "ms": { - "type": "long" - } - } - } - } - } - } - }, - "search": { - "properties": { - "query": { - "properties": { - "count": { - "type": "long" - }, - "time": { - "properties": { - "ms": { - "type": "long" - } - } - } - } - } - } - }, - "segments": { - "properties": { - "count": { - "type": "long" - }, - "memory": { - "properties": { - "bytes": { - "type": "long" - } - } - } - } - }, - "store": { - "properties": { - "size": { - "properties": { - "bytes": { - "type": "long" - } - } - } - } - } - } - }, - "total": { - "properties": { - "bulk": { - "properties": { - "operations": { - "properties": { - "count": { - "type": "long" - } - } - }, - "size": { - "properties": { - "bytes": { - "type": "long" - } - } - }, - "time": { - "properties": { - "avg": { - "properties": { - "bytes": { - "type": "long" - }, - "ms": { - "type": "long" - } - } - } - } - } - } - }, - "docs": { - "properties": { - "count": { - "type": "long" - }, - "deleted": { - "type": "long" - } - } - }, - "indexing": { - "properties": { - "index": { - "properties": { - "count": { - "type": "long" - }, - "time": { - "properties": { - "ms": { - "type": "long" - } - } - } - } - }, - "is_throttled": { - "type": "boolean" - }, - "throttle_time": { - "properties": { - "ms": { - "type": "long" - } - } - } - } - }, - "search": { - "properties": { - "query": { - "properties": { - "count": { - "type": "long" - }, - "time": { - "properties": { - "ms": { - "type": "long" - } - } - } - } - } - } - }, - "segments": { - "properties": { - "count": { - "type": "long" - }, - "memory": { - "properties": { - "bytes": { - "type": "long" - } - } - } - } - }, - "store": { - "properties": { - "size": { - "properties": { - "bytes": { - "type": "long" - } - } - } - } - } - } - } - } - } - } - }, - "node": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "master": { - "type": "boolean" - }, - "mlockall": { - "type": "boolean" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "error": { - "properties": { - "message": { - "type": "match_only_text" - } - } - }, - "event": { - "properties": { - "agent_id_status": { - "ignore_above": 1024, - "type": "keyword" - }, - "dataset": { - "ignore_above": 1024, - "type": "keyword" - }, - "duration": { - "type": "long" - }, - "ingested": { - "format": "strict_date_time_no_millis||strict_date_optional_time||epoch_millis", - "type": "date" - }, - "module": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "host": { - "properties": { - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "indices_stats": { - "properties": { - "_all": { - "properties": { - "primaries": { - "properties": { - "indexing": { - "properties": { - "index_time_in_millis": { - "path": "elasticsearch.index.summary.primaries.indexing.index.time.ms", - "type": "alias" - }, - "index_total": { - "path": "elasticsearch.index.summary.primaries.indexing.index.count", - "type": "alias" - } - } - } - } - }, - "total": { - "properties": { - "indexing": { - "properties": { - "index_total": { - "path": "elasticsearch.index.summary.total.indexing.index.count", - "type": "alias" - } - } - }, - "search": { - "properties": { - "query_time_in_millis": { - "path": "elasticsearch.index.summary.total.search.query.time.ms", - "type": "alias" - }, - "query_total": { - "path": "elasticsearch.index.summary.total.search.query.count", - "type": "alias" - } - } - } - } - } - } - } - } - }, - "service": { - "properties": { - "address": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "source_node": { - "properties": { - "name": { - "path": "elasticsearch.node.name", - "type": "alias" - }, - "uuid": { - "path": "elasticsearch.node.id", - "type": "alias" - } - } - }, - "timestamp": { - "path": "@timestamp", - "type": "alias" - } - } - }, - "settings": { - "index": { - "codec": "best_compression", - "lifecycle": { - "name": "metrics" - }, - "mapping": { - "total_fields": { - "limit": "10000" - } - }, - "query": { - "default_field": [ - "ecs.version", - "event.dataset", - "event.module", - "host.name", - "service.address", - "service.type", - "service.name", - "error.message", - "elasticsearch.cluster.name", - "elasticsearch.cluster.id", - "elasticsearch.cluster.state.id", - "elasticsearch.node.id", - "elasticsearch.node.name" - ] - } - } - } - } - } - } -} - -{ - "type": "data_stream", - "value": { - "data_stream": "metrics-elasticsearch.stack_monitoring.node-default", - "template": { - "_meta": { - "managed": true, - "managed_by": "fleet", - "package": { - "name": "elasticsearch" - } - }, - "data_stream": { - "allow_custom_routing": false, - "hidden": false - }, - "index_patterns": [ - "metrics-elasticsearch.stack_monitoring.node-*" - ], - "name": "metrics-elasticsearch.stack_monitoring.node", - "priority": 200, - "template": { - "mappings": { - "_meta": { - "managed": true, - "managed_by": "fleet", - "package": { - "name": "elasticsearch" - } - }, - "date_detection": false, - "dynamic": false, - "dynamic_templates": [ - { - "strings_as_keyword": { - "mapping": { - "ignore_above": 1024, - "type": "keyword" - }, - "match_mapping_type": "string" - } - } - ], - "properties": { - "@timestamp": { - "type": "date" - }, - "cluster_uuid": { - "path": "elasticsearch.cluster.id", - "type": "alias" - }, - "data_stream": { - "properties": { - "dataset": { - "type": "constant_keyword" - }, - "namespace": { - "type": "constant_keyword" - }, - "type": { - "type": "constant_keyword" - } - } - }, - "ecs": { - "properties": { - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "elasticsearch": { - "properties": { - "cluster": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "state": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "node": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "jvm": { - "properties": { - "memory": { - "properties": { - "heap": { - "properties": { - "init": { - "properties": { - "bytes": { - "type": "long" - } - } - }, - "max": { - "properties": { - "bytes": { - "type": "long" - } - } - } - } - }, - "nonheap": { - "properties": { - "init": { - "properties": { - "bytes": { - "type": "long" - } - } - }, - "max": { - "properties": { - "bytes": { - "type": "long" - } - } - } - } - } - } - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "master": { - "type": "boolean" - }, - "mlockall": { - "type": "boolean" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "process": { - "properties": { - "mlockall": { - "type": "boolean" - } - } - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "error": { - "properties": { - "message": { - "type": "match_only_text" - } - } - }, - "event": { - "properties": { - "agent_id_status": { - "ignore_above": 1024, - "type": "keyword" - }, - "dataset": { - "ignore_above": 1024, - "type": "keyword" - }, - "duration": { - "type": "long" - }, - "ingested": { - "format": "strict_date_time_no_millis||strict_date_optional_time||epoch_millis", - "type": "date" - }, - "module": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "host": { - "properties": { - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "service": { - "properties": { - "address": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "source_node": { - "properties": { - "name": { - "path": "elasticsearch.node.name", - "type": "alias" - }, - "uuid": { - "path": "elasticsearch.node.id", - "type": "alias" - } - } - }, - "timestamp": { - "path": "@timestamp", - "type": "alias" - } - } - }, - "settings": { - "index": { - "codec": "best_compression", - "lifecycle": { - "name": "metrics" - }, - "mapping": { - "total_fields": { - "limit": "10000" - } - }, - "query": { - "default_field": [ - "ecs.version", - "event.dataset", - "event.module", - "host.name", - "service.address", - "service.type", - "service.name", - "error.message", - "elasticsearch.node.version", - "elasticsearch.node.jvm.version", - "elasticsearch.node.id", - "elasticsearch.node.name", - "elasticsearch.cluster.name", - "elasticsearch.cluster.id", - "elasticsearch.cluster.state.id" - ] - } - } - } - } - } - } -} - -{ - "type": "data_stream", - "value": { - "data_stream": "metrics-elasticsearch.stack_monitoring.node_stats-default", - "template": { - "_meta": { - "managed": true, - "managed_by": "fleet", - "package": { - "name": "elasticsearch" - } - }, - "data_stream": { - "allow_custom_routing": false, - "hidden": false - }, - "index_patterns": [ - "metrics-elasticsearch.stack_monitoring.node_stats-*" - ], - "name": "metrics-elasticsearch.stack_monitoring.node_stats", - "priority": 200, - "template": { - "mappings": { - "_meta": { - "managed": true, - "managed_by": "fleet", - "package": { - "name": "elasticsearch" - } - }, - "date_detection": false, - "dynamic": false, - "dynamic_templates": [ - { - "strings_as_keyword": { - "mapping": { - "ignore_above": 1024, - "type": "keyword" - }, - "match_mapping_type": "string" - } - } - ], - "properties": { - "@timestamp": { - "type": "date" - }, - "cluster_uuid": { - "path": "elasticsearch.cluster.id", - "type": "alias" - }, - "data_stream": { - "properties": { - "dataset": { - "type": "constant_keyword" - }, - "namespace": { - "type": "constant_keyword" - }, - "type": { - "type": "constant_keyword" - } - } - }, - "ecs": { - "properties": { - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "elasticsearch": { - "properties": { - "cluster": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "state": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "node": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "master": { - "type": "boolean" - }, - "mlockall": { - "type": "boolean" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "stats": { - "properties": { - "fs": { - "properties": { - "io_stats": { - "properties": { - "total": { - "properties": { - "operations": { - "properties": { - "count": { - "type": "long" - } - } - }, - "read": { - "properties": { - "operations": { - "properties": { - "count": { - "type": "long" - } - } - } - } - }, - "write": { - "properties": { - "operations": { - "properties": { - "count": { - "type": "long" - } - } - } - } - } - } - } - } - }, - "summary": { - "properties": { - "available": { - "properties": { - "bytes": { - "type": "long" - } - } - }, - "free": { - "properties": { - "bytes": { - "type": "long" - } - } - }, - "total": { - "properties": { - "bytes": { - "type": "long" - } - } - } - } - }, - "total": { - "properties": { - "available_in_bytes": { - "type": "long" - }, - "total_in_bytes": { - "type": "long" - } - } - } - } - }, - "indexing_pressure": { - "properties": { - "memory": { - "properties": { - "current": { - "properties": { - "all": { - "properties": { - "bytes": { - "type": "long" - } - } - }, - "combined_coordinating_and_primary": { - "properties": { - "bytes": { - "type": "long" - } - } - }, - "coordinating": { - "properties": { - "bytes": { - "type": "long" - } - } - }, - "primary": { - "properties": { - "bytes": { - "type": "long" - } - } - }, - "replica": { - "properties": { - "bytes": { - "type": "long" - } - } - } - } - }, - "limit_in_bytes": { - "type": "long" - }, - "total": { - "properties": { - "all": { - "properties": { - "bytes": { - "type": "long" - } - } - }, - "combined_coordinating_and_primary": { - "properties": { - "bytes": { - "type": "long" - } - } - }, - "coordinating": { - "properties": { - "bytes": { - "type": "long" - }, - "rejections": { - "type": "long" - } - } - }, - "primary": { - "properties": { - "bytes": { - "type": "long" - }, - "rejections": { - "type": "long" - } - } - }, - "replica": { - "properties": { - "bytes": { - "type": "long" - }, - "rejections": { - "type": "long" - } - } - } - } - } - } - } - } - }, - "indices": { - "properties": { - "bulk": { - "properties": { - "avg_size": { - "properties": { - "bytes": { - "type": "long" - } - } - }, - "avg_time": { - "properties": { - "ms": { - "type": "long" - } - } - }, - "operations": { - "properties": { - "total": { - "properties": { - "count": { - "type": "long" - } - } - } - } - }, - "total_size": { - "properties": { - "bytes": { - "type": "long" - } - } - }, - "total_time": { - "properties": { - "ms": { - "type": "long" - } - } - } - } - }, - "docs": { - "properties": { - "count": { - "type": "long" - }, - "deleted": { - "type": "long" - } - } - }, - "fielddata": { - "properties": { - "memory": { - "properties": { - "bytes": { - "type": "long" - } - } - } - } - }, - "indexing": { - "properties": { - "index_time": { - "properties": { - "ms": { - "type": "long" - } - } - }, - "index_total": { - "properties": { - "count": { - "type": "long" - } - } - }, - "throttle_time": { - "properties": { - "ms": { - "type": "long" - } - } - } - } - }, - "query_cache": { - "properties": { - "memory": { - "properties": { - "bytes": { - "type": "long" - } - } - } - } - }, - "request_cache": { - "properties": { - "memory": { - "properties": { - "bytes": { - "type": "long" - } - } - } - } - }, - "search": { - "properties": { - "query_time": { - "properties": { - "ms": { - "type": "long" - } - } - }, - "query_total": { - "properties": { - "count": { - "type": "long" - } - } - } - } - }, - "segments": { - "properties": { - "count": { - "type": "long" - }, - "doc_values": { - "properties": { - "memory": { - "properties": { - "bytes": { - "type": "long" - } - } - } - } - }, - "fixed_bit_set": { - "properties": { - "memory": { - "properties": { - "bytes": { - "type": "long" - } - } - } - } - }, - "index_writer": { - "properties": { - "memory": { - "properties": { - "bytes": { - "type": "long" - } - } - } - } - }, - "memory": { - "properties": { - "bytes": { - "type": "long" - } - } - }, - "norms": { - "properties": { - "memory": { - "properties": { - "bytes": { - "type": "long" - } - } - } - } - }, - "points": { - "properties": { - "memory": { - "properties": { - "bytes": { - "type": "long" - } - } - } - } - }, - "stored_fields": { - "properties": { - "memory": { - "properties": { - "bytes": { - "type": "long" - } - } - } - } - }, - "term_vectors": { - "properties": { - "memory": { - "properties": { - "bytes": { - "type": "long" - } - } - } - } - }, - "terms": { - "properties": { - "memory": { - "properties": { - "bytes": { - "type": "long" - } - } - } - } - }, - "version_map": { - "properties": { - "memory": { - "properties": { - "bytes": { - "type": "long" - } - } - } - } - } - } - }, - "store": { - "properties": { - "size": { - "properties": { - "bytes": { - "type": "long" - } - } - } - } - } - } - }, - "ingest": { - "properties": { - "total": { - "properties": { - "count": { - "type": "long" - }, - "current": { - "type": "long" - }, - "failed": { - "type": "long" - }, - "time_in_millis": { - "type": "long" - } - } - } - } - }, - "jvm": { - "properties": { - "gc": { - "properties": { - "collectors": { - "properties": { - "old": { - "properties": { - "collection": { - "properties": { - "count": { - "type": "long" - }, - "ms": { - "type": "long" - } - } - } - } - }, - "young": { - "properties": { - "collection": { - "properties": { - "count": { - "type": "long" - }, - "ms": { - "type": "long" - } - } - } - } - } - } - } - } - }, - "mem": { - "properties": { - "heap": { - "properties": { - "max": { - "properties": { - "bytes": { - "type": "long" - } - } - }, - "used": { - "properties": { - "bytes": { - "type": "long" - }, - "pct": { - "type": "double" - } - } - } - } - }, - "pools": { - "properties": { - "old": { - "properties": { - "max": { - "properties": { - "bytes": { - "type": "long" - } - } - }, - "peak": { - "properties": { - "bytes": { - "type": "long" - } - } - }, - "peak_max": { - "properties": { - "bytes": { - "type": "long" - } - } - }, - "used": { - "properties": { - "bytes": { - "type": "long" - } - } - } - } - }, - "survivor": { - "properties": { - "max": { - "properties": { - "bytes": { - "type": "long" - } - } - }, - "peak": { - "properties": { - "bytes": { - "type": "long" - } - } - }, - "peak_max": { - "properties": { - "bytes": { - "type": "long" - } - } - }, - "used": { - "properties": { - "bytes": { - "type": "long" - } - } - } - } - }, - "young": { - "properties": { - "max": { - "properties": { - "bytes": { - "type": "long" - } - } - }, - "peak": { - "properties": { - "bytes": { - "type": "long" - } - } - }, - "peak_max": { - "properties": { - "bytes": { - "type": "long" - } - } - }, - "used": { - "properties": { - "bytes": { - "type": "long" - } - } - } - } - } - } - } - } - } - } - }, - "os": { - "properties": { - "cgroup": { - "properties": { - "cpu": { - "properties": { - "cfs": { - "properties": { - "quota": { - "properties": { - "us": { - "type": "long" - } - } - } - } - }, - "stat": { - "properties": { - "elapsed_periods": { - "properties": { - "count": { - "type": "long" - } - } - }, - "time_throttled": { - "properties": { - "ns": { - "type": "long" - } - } - }, - "times_throttled": { - "properties": { - "count": { - "type": "long" - } - } - } - } - } - } - }, - "cpuacct": { - "properties": { - "usage": { - "properties": { - "ns": { - "type": "long" - } - } - } - } - }, - "memory": { - "properties": { - "control_group": { - "ignore_above": 1024, - "type": "keyword" - }, - "limit": { - "properties": { - "bytes": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "usage": { - "properties": { - "bytes": { - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - } - } - }, - "cpu": { - "properties": { - "load_avg": { - "properties": { - "1m": { - "type": "half_float" - } - } - } - } - } - } - }, - "process": { - "properties": { - "cpu": { - "properties": { - "pct": { - "type": "double" - } - } - } - } - }, - "thread_pool": { - "properties": { - "bulk": { - "properties": { - "queue": { - "properties": { - "count": { - "type": "long" - } - } - }, - "rejected": { - "properties": { - "count": { - "type": "long" - } - } - } - } - }, - "get": { - "properties": { - "queue": { - "properties": { - "count": { - "type": "long" - } - } - }, - "rejected": { - "properties": { - "count": { - "type": "long" - } - } - } - } - }, - "index": { - "properties": { - "queue": { - "properties": { - "count": { - "type": "long" - } - } - }, - "rejected": { - "properties": { - "count": { - "type": "long" - } - } - } - } - }, - "search": { - "properties": { - "queue": { - "properties": { - "count": { - "type": "long" - } - } - }, - "rejected": { - "properties": { - "count": { - "type": "long" - } - } - } - } - }, - "write": { - "properties": { - "queue": { - "properties": { - "count": { - "type": "long" - } - } - }, - "rejected": { - "properties": { - "count": { - "type": "long" - } - } - } - } - } - } - } - } - } - } - } - } - }, - "error": { - "properties": { - "message": { - "type": "match_only_text" - } - } - }, - "event": { - "properties": { - "agent_id_status": { - "ignore_above": 1024, - "type": "keyword" - }, - "dataset": { - "ignore_above": 1024, - "type": "keyword" - }, - "duration": { - "type": "long" - }, - "ingested": { - "format": "strict_date_time_no_millis||strict_date_optional_time||epoch_millis", - "type": "date" - }, - "module": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "host": { - "properties": { - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "node_stats": { - "properties": { - "fs": { - "properties": { - "io_stats": { - "properties": { - "total": { - "properties": { - "operations": { - "path": "elasticsearch.node.stats.fs.io_stats.total.operations.count", - "type": "alias" - }, - "read_operations": { - "path": "elasticsearch.node.stats.fs.io_stats.total.read.operations.count", - "type": "alias" - }, - "write_operations": { - "path": "elasticsearch.node.stats.fs.io_stats.total.write.operations.count", - "type": "alias" - } - } - } - } - }, - "summary": { - "properties": { - "available": { - "properties": { - "bytes": { - "path": "elasticsearch.node.stats.fs.summary.available.bytes", - "type": "alias" - } - } - }, - "total": { - "properties": { - "bytes": { - "path": "elasticsearch.node.stats.fs.summary.total.bytes", - "type": "alias" - } - } - } - } - }, - "total": { - "properties": { - "available_in_bytes": { - "path": "elasticsearch.node.stats.fs.summary.available.bytes", - "type": "alias" - }, - "total_in_bytes": { - "path": "elasticsearch.node.stats.fs.summary.total.bytes", - "type": "alias" - } - } - } - } - }, - "indices": { - "properties": { - "docs": { - "properties": { - "count": { - "path": "elasticsearch.node.stats.indices.docs.count", - "type": "alias" - } - } - }, - "fielddata": { - "properties": { - "memory_size_in_bytes": { - "path": "elasticsearch.node.stats.indices.fielddata.memory.bytes", - "type": "alias" - } - } - }, - "indexing": { - "properties": { - "index_time_in_millis": { - "path": "elasticsearch.node.stats.indices.indexing.index_time.ms", - "type": "alias" - }, - "index_total": { - "path": "elasticsearch.node.stats.indices.indexing.index_total.count", - "type": "alias" - }, - "throttle_time_in_millis": { - "path": "elasticsearch.node.stats.indices.indexing.throttle_time.ms", - "type": "alias" - } - } - }, - "query_cache": { - "properties": { - "memory_size_in_bytes": { - "path": "elasticsearch.node.stats.indices.query_cache.memory.bytes", - "type": "alias" - } - } - }, - "request_cache": { - "properties": { - "memory_size_in_bytes": { - "path": "elasticsearch.node.stats.indices.request_cache.memory.bytes", - "type": "alias" - } - } - }, - "search": { - "properties": { - "query_time_in_millis": { - "path": "elasticsearch.node.stats.indices.search.query_time.ms", - "type": "alias" - }, - "query_total": { - "path": "elasticsearch.node.stats.indices.search.query_total.count", - "type": "alias" - } - } - }, - "segments": { - "properties": { - "count": { - "path": "elasticsearch.node.stats.indices.segments.count", - "type": "alias" - }, - "doc_values_memory_in_bytes": { - "path": "elasticsearch.node.stats.indices.segments.doc_values.memory.bytes", - "type": "alias" - }, - "fixed_bit_set_memory_in_bytes": { - "path": "elasticsearch.node.stats.indices.segments.fixed_bit_set.memory.bytes", - "type": "alias" - }, - "index_writer_memory_in_bytes": { - "path": "elasticsearch.node.stats.indices.segments.index_writer.memory.bytes", - "type": "alias" - }, - "memory_in_bytes": { - "path": "elasticsearch.node.stats.indices.segments.memory.bytes", - "type": "alias" - }, - "norms_memory_in_bytes": { - "path": "elasticsearch.node.stats.indices.segments.norms.memory.bytes", - "type": "alias" - }, - "points_memory_in_bytes": { - "path": "elasticsearch.node.stats.indices.segments.points.memory.bytes", - "type": "alias" - }, - "stored_fields_memory_in_bytes": { - "path": "elasticsearch.node.stats.indices.segments.stored_fields.memory.bytes", - "type": "alias" - }, - "term_vectors_memory_in_bytes": { - "path": "elasticsearch.node.stats.indices.segments.term_vectors.memory.bytes", - "type": "alias" - }, - "terms_memory_in_bytes": { - "path": "elasticsearch.node.stats.indices.segments.terms.memory.bytes", - "type": "alias" - }, - "version_map_memory_in_bytes": { - "path": "elasticsearch.node.stats.indices.segments.version_map.memory.bytes", - "type": "alias" - } - } - }, - "store": { - "properties": { - "size": { - "properties": { - "bytes": { - "path": "elasticsearch.node.stats.indices.store.size.bytes", - "type": "alias" - } - } - }, - "size_in_bytes": { - "path": "elasticsearch.node.stats.indices.store.size.bytes", - "type": "alias" - } - } - } - } - }, - "jvm": { - "properties": { - "gc": { - "properties": { - "collectors": { - "properties": { - "old": { - "properties": { - "collection_count": { - "path": "elasticsearch.node.stats.jvm.gc.collectors.old.collection.count", - "type": "alias" - }, - "collection_time_in_millis": { - "path": "elasticsearch.node.stats.jvm.gc.collectors.old.collection.ms", - "type": "alias" - } - } - }, - "young": { - "properties": { - "collection_count": { - "path": "elasticsearch.node.stats.jvm.gc.collectors.young.collection.count", - "type": "alias" - }, - "collection_time_in_millis": { - "path": "elasticsearch.node.stats.jvm.gc.collectors.young.collection.ms", - "type": "alias" - } - } - } - } - } - } - }, - "mem": { - "properties": { - "heap_max_in_bytes": { - "path": "elasticsearch.node.stats.jvm.mem.heap.max.bytes", - "type": "alias" - }, - "heap_used_in_bytes": { - "path": "elasticsearch.node.stats.jvm.mem.heap.used.bytes", - "type": "alias" - }, - "heap_used_percent": { - "path": "elasticsearch.node.stats.jvm.mem.heap.used.pct", - "type": "alias" - } - } - } - } - }, - "node_id": { - "path": "elasticsearch.node.id", - "type": "alias" - }, - "os": { - "properties": { - "cgroup": { - "properties": { - "cpu": { - "properties": { - "cfs_quota_micros": { - "path": "elasticsearch.node.stats.os.cgroup.cpu.cfs.quota.us", - "type": "alias" - }, - "stat": { - "properties": { - "number_of_elapsed_periods": { - "path": "elasticsearch.node.stats.os.cgroup.cpu.stat.elapsed_periods.count", - "type": "alias" - }, - "number_of_times_throttled": { - "path": "elasticsearch.node.stats.os.cgroup.cpu.stat.times_throttled.count", - "type": "alias" - }, - "time_throttled_nanos": { - "path": "elasticsearch.node.stats.os.cgroup.cpu.stat.time_throttled.ns", - "type": "alias" - } - } - } - } - }, - "cpuacct": { - "properties": { - "usage_nanos": { - "path": "elasticsearch.node.stats.os.cgroup.cpuacct.usage.ns", - "type": "alias" - } - } - }, - "memory": { - "properties": { - "control_group": { - "path": "elasticsearch.node.stats.os.cgroup.memory.control_group", - "type": "alias" - }, - "limit_in_bytes": { - "path": "elasticsearch.node.stats.os.cgroup.memory.limit.bytes", - "type": "alias" - }, - "usage_in_bytes": { - "path": "elasticsearch.node.stats.os.cgroup.memory.usage.bytes", - "type": "alias" - } - } - } - } - }, - "cpu": { - "properties": { - "load_average": { - "properties": { - "1m": { - "path": "elasticsearch.node.stats.os.cpu.load_avg.1m", - "type": "alias" - } - } - } - } - } - } - }, - "process": { - "properties": { - "cpu": { - "properties": { - "percent": { - "path": "elasticsearch.node.stats.process.cpu.pct", - "type": "alias" - } - } - } - } - }, - "thread_pool": { - "properties": { - "bulk": { - "properties": { - "queue": { - "path": "elasticsearch.node.stats.thread_pool.bulk.queue.count", - "type": "alias" - }, - "rejected": { - "path": "elasticsearch.node.stats.thread_pool.bulk.rejected.count", - "type": "alias" - } - } - }, - "get": { - "properties": { - "queue": { - "path": "elasticsearch.node.stats.thread_pool.get.queue.count", - "type": "alias" - }, - "rejected": { - "path": "elasticsearch.node.stats.thread_pool.get.rejected.count", - "type": "alias" - } - } - }, - "index": { - "properties": { - "queue": { - "path": "elasticsearch.node.stats.thread_pool.index.queue.count", - "type": "alias" - }, - "rejected": { - "path": "elasticsearch.node.stats.thread_pool.index.rejected.count", - "type": "alias" - } - } - }, - "search": { - "properties": { - "queue": { - "path": "elasticsearch.node.stats.thread_pool.search.queue.count", - "type": "alias" - }, - "rejected": { - "path": "elasticsearch.node.stats.thread_pool.search.rejected.count", - "type": "alias" - } - } - }, - "write": { - "properties": { - "queue": { - "path": "elasticsearch.node.stats.thread_pool.write.queue.count", - "type": "alias" - }, - "rejected": { - "path": "elasticsearch.node.stats.thread_pool.write.rejected.count", - "type": "alias" - } - } - } - } - } - } - }, - "service": { - "properties": { - "address": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "source_node": { - "properties": { - "name": { - "path": "elasticsearch.node.name", - "type": "alias" - }, - "uuid": { - "path": "elasticsearch.node.id", - "type": "alias" - } - } - }, - "timestamp": { - "path": "@timestamp", - "type": "alias" - } - } - }, - "settings": { - "index": { - "codec": "best_compression", - "lifecycle": { - "name": "metrics" - }, - "mapping": { - "total_fields": { - "limit": "10000" - } - }, - "query": { - "default_field": [ - "ecs.version", - "event.dataset", - "event.module", - "host.name", - "service.address", - "service.type", - "service.name", - "error.message", - "elasticsearch.node.stats.os.cgroup.memory.control_group", - "elasticsearch.node.stats.os.cgroup.memory.limit.bytes", - "elasticsearch.node.stats.os.cgroup.memory.usage.bytes", - "elasticsearch.node.id", - "elasticsearch.node.name", - "elasticsearch.cluster.name", - "elasticsearch.cluster.id", - "elasticsearch.cluster.state.id" - ] - } - } - } - } - } - } -} - -{ - "type": "data_stream", - "value": { - "data_stream": "metrics-elasticsearch.stack_monitoring.shard-default", - "template": { - "_meta": { - "managed": true, - "managed_by": "fleet", - "package": { - "name": "elasticsearch" - } - }, - "data_stream": { - "allow_custom_routing": false, - "hidden": false - }, - "index_patterns": [ - "metrics-elasticsearch.stack_monitoring.shard-*" - ], - "name": "metrics-elasticsearch.stack_monitoring.shard", - "priority": 200, - "template": { - "mappings": { - "_meta": { - "managed": true, - "managed_by": "fleet", - "package": { - "name": "elasticsearch" - } - }, - "date_detection": false, - "dynamic": false, - "dynamic_templates": [ - { - "strings_as_keyword": { - "mapping": { - "ignore_above": 1024, - "type": "keyword" - }, - "match_mapping_type": "string" - } - } - ], - "properties": { - "@timestamp": { - "type": "date" - }, - "cluster_uuid": { - "path": "elasticsearch.cluster.id", - "type": "alias" - }, - "data_stream": { - "properties": { - "dataset": { - "type": "constant_keyword" - }, - "namespace": { - "type": "constant_keyword" - }, - "type": { - "type": "constant_keyword" - } - } - }, - "ecs": { - "properties": { - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "elasticsearch": { - "properties": { - "cluster": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "state": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "stats": { - "properties": { - "state": { - "properties": { - "state_uuid": { - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - } - } - }, - "index": { - "properties": { - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "node": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "master": { - "type": "boolean" - }, - "mlockall": { - "type": "boolean" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "shard": { - "properties": { - "number": { - "type": "long" - }, - "primary": { - "type": "boolean" - }, - "relocating_node": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "source_node": { - "properties": { - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "uuid": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "state": { - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "error": { - "properties": { - "message": { - "type": "match_only_text" - } - } - }, - "event": { - "properties": { - "agent_id_status": { - "ignore_above": 1024, - "type": "keyword" - }, - "dataset": { - "ignore_above": 1024, - "type": "keyword" - }, - "duration": { - "type": "long" - }, - "ingested": { - "format": "strict_date_time_no_millis||strict_date_optional_time||epoch_millis", - "type": "date" - }, - "module": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "host": { - "properties": { - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "service": { - "properties": { - "address": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "shard": { - "properties": { - "index": { - "path": "elasticsearch.index.name", - "type": "alias" - }, - "node": { - "path": "elasticsearch.node.id", - "type": "alias" - }, - "primary": { - "path": "elasticsearch.shard.primary", - "type": "alias" - }, - "shard": { - "path": "elasticsearch.shard.number", - "type": "alias" - }, - "state": { - "path": "elasticsearch.shard.state", - "type": "alias" - } - } - }, - "source_node": { - "properties": { - "name": { - "path": "elasticsearch.node.name", - "type": "alias" - }, - "uuid": { - "path": "elasticsearch.node.id", - "type": "alias" - } - } - }, - "timestamp": { - "path": "@timestamp", - "type": "alias" - } - } - }, - "settings": { - "index": { - "codec": "best_compression", - "lifecycle": { - "name": "metrics" - }, - "mapping": { - "total_fields": { - "limit": "10000" - } - }, - "query": { - "default_field": [ - "ecs.version", - "event.dataset", - "event.module", - "host.name", - "service.address", - "service.type", - "service.name", - "error.message", - "elasticsearch.shard.state", - "elasticsearch.shard.relocating_node.name", - "elasticsearch.shard.relocating_node.id", - "elasticsearch.shard.source_node.name", - "elasticsearch.shard.source_node.uuid", - "elasticsearch.index.name", - "elasticsearch.cluster.name", - "elasticsearch.cluster.id", - "elasticsearch.cluster.state.id", - "elasticsearch.cluster.stats.state.state_uuid", - "elasticsearch.node.id", - "elasticsearch.node.name" - ] - } - } - } - } - } - } -} - -{ - "type": "data_stream", - "value": { - "data_stream": "metrics-kibana.stack_monitoring.cluster_actions-default", - "template": { - "_meta": { - "managed": true, - "managed_by": "fleet", - "package": { - "name": "kibana" - } - }, - "data_stream": { - "allow_custom_routing": false, - "hidden": false - }, - "index_patterns": [ - "metrics-kibana.stack_monitoring.cluster_actions-*" - ], - "name": "metrics-kibana.stack_monitoring.cluster_actions", - "priority": 200, - "template": { - "mappings": { - "_meta": { - "managed": true, - "managed_by": "fleet", - "package": { - "name": "kibana" - } - }, - "date_detection": false, - "dynamic": false, - "dynamic_templates": [ - { - "strings_as_keyword": { - "mapping": { - "ignore_above": 1024, - "type": "keyword" - }, - "match_mapping_type": "string" - } - } - ], - "properties": { - "@timestamp": { - "type": "date" - }, - "cluster_uuid": { - "path": "kibana.elasticsearch.cluster.id", - "type": "alias" - }, - "data_stream": { - "properties": { - "dataset": { - "type": "constant_keyword" - }, - "namespace": { - "type": "constant_keyword" - }, - "type": { - "type": "constant_keyword" - } - } - }, - "ecs": { - "properties": { - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "error": { - "properties": { - "message": { - "type": "match_only_text" - } - } - }, - "event": { - "properties": { - "agent_id_status": { - "ignore_above": 1024, - "type": "keyword" - }, - "dataset": { - "ignore_above": 1024, - "type": "keyword" - }, - "duration": { - "type": "long" - }, - "ingested": { - "format": "strict_date_time_no_millis||strict_date_optional_time||epoch_millis", - "type": "date" - }, - "module": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "host": { - "properties": { - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "kibana": { - "properties": { - "cluster_actions": { - "properties": { - "overdue": { - "properties": { - "count": { - "type": "long" - }, - "delay": { - "properties": { - "p50": { - "type": "float" - }, - "p99": { - "type": "float" - } - } - } - } - } - } - }, - "elasticsearch": { - "properties": { - "cluster": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - } - } - }, - "kibana_stats": { - "properties": { - "kibana": { - "properties": { - "uuid": { - "path": "service.id", - "type": "alias" - }, - "version": { - "path": "service.version", - "type": "alias" - } - } - }, - "timestamp": { - "path": "@timestamp", - "type": "alias" - } - } - }, - "process": { - "properties": { - "pid": { - "type": "long" - } - } - }, - "service": { - "properties": { - "address": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "timestamp": { - "path": "@timestamp", - "type": "alias" - } - } - }, - "settings": { - "index": { - "codec": "best_compression", - "lifecycle": { - "name": "metrics" - }, - "mapping": { - "total_fields": { - "limit": "10000" - } - }, - "query": { - "default_field": [ - "service.id", - "service.address", - "service.version", - "service.type", - "ecs.version", - "event.dataset", - "event.module", - "host.name", - "error.message", - "kibana.elasticsearch.cluster.id" - ] - } - } - } - } - } - } -} - -{ - "type": "data_stream", - "value": { - "data_stream": "metrics-kibana.stack_monitoring.cluster_rules-default", - "template": { - "_meta": { - "managed": true, - "managed_by": "fleet", - "package": { - "name": "kibana" - } - }, - "data_stream": { - "allow_custom_routing": false, - "hidden": false - }, - "index_patterns": [ - "metrics-kibana.stack_monitoring.cluster_rules-*" - ], - "name": "metrics-kibana.stack_monitoring.cluster_rules", - "priority": 200, - "template": { - "mappings": { - "_meta": { - "managed": true, - "managed_by": "fleet", - "package": { - "name": "kibana" - } - }, - "date_detection": false, - "dynamic": false, - "dynamic_templates": [ - { - "strings_as_keyword": { - "mapping": { - "ignore_above": 1024, - "type": "keyword" - }, - "match_mapping_type": "string" - } - } - ], - "properties": { - "@timestamp": { - "type": "date" - }, - "cluster_uuid": { - "path": "kibana.elasticsearch.cluster.id", - "type": "alias" - }, - "data_stream": { - "properties": { - "dataset": { - "type": "constant_keyword" - }, - "namespace": { - "type": "constant_keyword" - }, - "type": { - "type": "constant_keyword" - } - } - }, - "ecs": { - "properties": { - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "error": { - "properties": { - "message": { - "type": "match_only_text" - } - } - }, - "event": { - "properties": { - "agent_id_status": { - "ignore_above": 1024, - "type": "keyword" - }, - "dataset": { - "ignore_above": 1024, - "type": "keyword" - }, - "duration": { - "type": "long" - }, - "ingested": { - "format": "strict_date_time_no_millis||strict_date_optional_time||epoch_millis", - "type": "date" - }, - "module": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "host": { - "properties": { - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "kibana": { - "properties": { - "cluster_rules": { - "properties": { - "overdue": { - "properties": { - "count": { - "type": "long" - }, - "delay": { - "properties": { - "p50": { - "type": "float" - }, - "p99": { - "type": "float" - } - } - } - } - } - } - }, - "elasticsearch": { - "properties": { - "cluster": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - } - } - }, - "kibana_stats": { - "properties": { - "kibana": { - "properties": { - "uuid": { - "path": "service.id", - "type": "alias" - }, - "version": { - "path": "service.version", - "type": "alias" - } - } - }, - "timestamp": { - "path": "@timestamp", - "type": "alias" - } - } - }, - "process": { - "properties": { - "pid": { - "type": "long" - } - } - }, - "service": { - "properties": { - "address": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "timestamp": { - "path": "@timestamp", - "type": "alias" - } - } - }, - "settings": { - "index": { - "codec": "best_compression", - "lifecycle": { - "name": "metrics" - }, - "mapping": { - "total_fields": { - "limit": "10000" - } - }, - "query": { - "default_field": [ - "service.id", - "service.address", - "service.version", - "service.type", - "ecs.version", - "event.dataset", - "event.module", - "host.name", - "error.message", - "kibana.elasticsearch.cluster.id" - ] - } - } - } - } - } - } -} - -{ - "type": "data_stream", - "value": { - "data_stream": "metrics-kibana.stack_monitoring.node_actions-default", - "template": { - "_meta": { - "managed": true, - "managed_by": "fleet", - "package": { - "name": "kibana" - } - }, - "data_stream": { - "allow_custom_routing": false, - "hidden": false - }, - "index_patterns": [ - "metrics-kibana.stack_monitoring.node_actions-*" - ], - "name": "metrics-kibana.stack_monitoring.node_actions", - "priority": 200, - "template": { - "mappings": { - "_meta": { - "managed": true, - "managed_by": "fleet", - "package": { - "name": "kibana" - } - }, - "date_detection": false, - "dynamic": false, - "dynamic_templates": [ - { - "strings_as_keyword": { - "mapping": { - "ignore_above": 1024, - "type": "keyword" - }, - "match_mapping_type": "string" - } - } - ], - "properties": { - "@timestamp": { - "type": "date" - }, - "cluster_uuid": { - "path": "kibana.elasticsearch.cluster.id", - "type": "alias" - }, - "data_stream": { - "properties": { - "dataset": { - "type": "constant_keyword" - }, - "namespace": { - "type": "constant_keyword" - }, - "type": { - "type": "constant_keyword" - } - } - }, - "ecs": { - "properties": { - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "error": { - "properties": { - "message": { - "type": "match_only_text" - } - } - }, - "event": { - "properties": { - "agent_id_status": { - "ignore_above": 1024, - "type": "keyword" - }, - "dataset": { - "ignore_above": 1024, - "type": "keyword" - }, - "duration": { - "type": "long" - }, - "ingested": { - "format": "strict_date_time_no_millis||strict_date_optional_time||epoch_millis", - "type": "date" - }, - "module": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "host": { - "properties": { - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "kibana": { - "properties": { - "elasticsearch": { - "properties": { - "cluster": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "node_actions": { - "properties": { - "executions": { - "type": "long" - }, - "failures": { - "type": "long" - }, - "timeouts": { - "type": "long" - } - } - } - } - }, - "kibana_stats": { - "properties": { - "kibana": { - "properties": { - "uuid": { - "path": "service.id", - "type": "alias" - }, - "version": { - "path": "service.version", - "type": "alias" - } - } - }, - "timestamp": { - "path": "@timestamp", - "type": "alias" - } - } - }, - "process": { - "properties": { - "pid": { - "type": "long" - } - } - }, - "service": { - "properties": { - "address": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "timestamp": { - "path": "@timestamp", - "type": "alias" - } - } - }, - "settings": { - "index": { - "codec": "best_compression", - "lifecycle": { - "name": "metrics" - }, - "mapping": { - "total_fields": { - "limit": "10000" - } - }, - "query": { - "default_field": [ - "service.id", - "service.address", - "service.version", - "service.type", - "ecs.version", - "event.dataset", - "event.module", - "host.name", - "error.message", - "kibana.elasticsearch.cluster.id" - ] - } - } - } - } - } - } -} - -{ - "type": "data_stream", - "value": { - "data_stream": "metrics-kibana.stack_monitoring.node_rules-default", - "template": { - "_meta": { - "managed": true, - "managed_by": "fleet", - "package": { - "name": "kibana" - } - }, - "data_stream": { - "allow_custom_routing": false, - "hidden": false - }, - "index_patterns": [ - "metrics-kibana.stack_monitoring.node_rules-*" - ], - "name": "metrics-kibana.stack_monitoring.node_rules", - "priority": 200, - "template": { - "mappings": { - "_meta": { - "managed": true, - "managed_by": "fleet", - "package": { - "name": "kibana" - } - }, - "date_detection": false, - "dynamic": false, - "dynamic_templates": [ - { - "strings_as_keyword": { - "mapping": { - "ignore_above": 1024, - "type": "keyword" - }, - "match_mapping_type": "string" - } - } - ], - "properties": { - "@timestamp": { - "type": "date" - }, - "cluster_uuid": { - "path": "kibana.elasticsearch.cluster.id", - "type": "alias" - }, - "data_stream": { - "properties": { - "dataset": { - "type": "constant_keyword" - }, - "namespace": { - "type": "constant_keyword" - }, - "type": { - "type": "constant_keyword" - } - } - }, - "ecs": { - "properties": { - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "error": { - "properties": { - "message": { - "type": "match_only_text" - } - } - }, - "event": { - "properties": { - "agent_id_status": { - "ignore_above": 1024, - "type": "keyword" - }, - "dataset": { - "ignore_above": 1024, - "type": "keyword" - }, - "duration": { - "type": "long" - }, - "ingested": { - "format": "strict_date_time_no_millis||strict_date_optional_time||epoch_millis", - "type": "date" - }, - "module": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "host": { - "properties": { - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "kibana": { - "properties": { - "elasticsearch": { - "properties": { - "cluster": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "node_rules": { - "properties": { - "executions": { - "type": "long" - }, - "failures": { - "type": "long" - }, - "timeouts": { - "type": "long" - } - } - } - } - }, - "kibana_stats": { - "properties": { - "kibana": { - "properties": { - "uuid": { - "path": "service.id", - "type": "alias" - }, - "version": { - "path": "service.version", - "type": "alias" - } - } - }, - "timestamp": { - "path": "@timestamp", - "type": "alias" - } - } - }, - "process": { - "properties": { - "pid": { - "type": "long" - } - } - }, - "service": { - "properties": { - "address": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "timestamp": { - "path": "@timestamp", - "type": "alias" - } - } - }, - "settings": { - "index": { - "codec": "best_compression", - "lifecycle": { - "name": "metrics" - }, - "mapping": { - "total_fields": { - "limit": "10000" - } - }, - "query": { - "default_field": [ - "service.id", - "service.address", - "service.version", - "service.type", - "ecs.version", - "event.dataset", - "event.module", - "host.name", - "error.message", - "kibana.elasticsearch.cluster.id" - ] - } - } - } - } - } - } -} - -{ - "type": "data_stream", - "value": { - "data_stream": "metrics-kibana.stack_monitoring.stats-default", - "template": { - "_meta": { - "managed": true, - "managed_by": "fleet", - "package": { - "name": "kibana" - } - }, - "data_stream": { - "allow_custom_routing": false, - "hidden": false - }, - "index_patterns": [ - "metrics-kibana.stack_monitoring.stats-*" - ], - "name": "metrics-kibana.stack_monitoring.stats", - "priority": 200, - "template": { - "mappings": { - "_meta": { - "managed": true, - "managed_by": "fleet", - "package": { - "name": "kibana" - } - }, - "date_detection": false, - "dynamic": false, - "dynamic_templates": [ - { - "strings_as_keyword": { - "mapping": { - "ignore_above": 1024, - "type": "keyword" - }, - "match_mapping_type": "string" - } - } - ], - "properties": { - "@timestamp": { - "type": "date" - }, - "cluster_uuid": { - "path": "kibana.elasticsearch.cluster.id", - "type": "alias" - }, - "data_stream": { - "properties": { - "dataset": { - "type": "constant_keyword" - }, - "namespace": { - "type": "constant_keyword" - }, - "type": { - "type": "constant_keyword" - } - } - }, - "ecs": { - "properties": { - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "error": { - "properties": { - "message": { - "type": "match_only_text" - } - } - }, - "event": { - "properties": { - "agent_id_status": { - "ignore_above": 1024, - "type": "keyword" - }, - "dataset": { - "ignore_above": 1024, - "type": "keyword" - }, - "duration": { - "type": "long" - }, - "ingested": { - "format": "strict_date_time_no_millis||strict_date_optional_time||epoch_millis", - "type": "date" - }, - "module": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "host": { - "properties": { - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "kibana": { - "properties": { - "elasticsearch": { - "properties": { - "cluster": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "stats": { - "properties": { - "concurrent_connections": { - "type": "long" - }, - "host": { - "properties": { - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "index": { - "ignore_above": 1024, - "type": "keyword" - }, - "kibana": { - "properties": { - "status": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "os": { - "properties": { - "distro": { - "ignore_above": 1024, - "type": "keyword" - }, - "distroRelease": { - "ignore_above": 1024, - "type": "keyword" - }, - "load": { - "properties": { - "15m": { - "type": "half_float" - }, - "1m": { - "type": "half_float" - }, - "5m": { - "type": "half_float" - } - } - }, - "memory": { - "properties": { - "free_in_bytes": { - "type": "long" - }, - "total_in_bytes": { - "type": "long" - }, - "used_in_bytes": { - "type": "long" - } - } - }, - "platform": { - "ignore_above": 1024, - "type": "keyword" - }, - "platformRelease": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "process": { - "properties": { - "event_loop_delay": { - "properties": { - "ms": { - "scaling_factor": 1000, - "type": "scaled_float" - } - } - }, - "memory": { - "properties": { - "heap": { - "properties": { - "size_limit": { - "properties": { - "bytes": { - "type": "long" - } - } - }, - "total": { - "properties": { - "bytes": { - "type": "long" - } - } - }, - "uptime": { - "properties": { - "ms": { - "type": "long" - } - } - }, - "used": { - "properties": { - "bytes": { - "type": "long" - } - } - } - } - }, - "resident_set_size": { - "properties": { - "bytes": { - "type": "long" - } - } - } - } - }, - "uptime": { - "properties": { - "ms": { - "type": "long" - } - } - } - } - }, - "request": { - "properties": { - "disconnects": { - "type": "long" - }, - "total": { - "type": "long" - } - } - }, - "response_time": { - "properties": { - "avg": { - "properties": { - "ms": { - "type": "long" - } - } - }, - "max": { - "properties": { - "ms": { - "type": "long" - } - } - } - } - }, - "snapshot": { - "type": "boolean" - }, - "status": { - "ignore_above": 1024, - "type": "keyword" - }, - "transport_address": { - "ignore_above": 1024, - "type": "keyword" - }, - "usage": { - "properties": { - "index": { - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - } - } - }, - "kibana_stats": { - "properties": { - "concurrent_connections": { - "path": "kibana.stats.concurrent_connections", - "type": "alias" - }, - "kibana": { - "properties": { - "response_time": { - "properties": { - "max": { - "path": "kibana.stats.response_time.max.ms", - "type": "alias" - } - } - }, - "status": { - "path": "kibana.stats.status", - "type": "alias" - }, - "uuid": { - "path": "service.id", - "type": "alias" - }, - "version": { - "path": "service.version", - "type": "alias" - } - } - }, - "os": { - "properties": { - "load": { - "properties": { - "15m": { - "path": "kibana.stats.os.load.15m", - "type": "alias" - }, - "1m": { - "path": "kibana.stats.os.load.1m", - "type": "alias" - }, - "5m": { - "path": "kibana.stats.os.load.5m", - "type": "alias" - } - } - }, - "memory": { - "properties": { - "free_in_bytes": { - "path": "kibana.stats.os.memory.free_in_bytes", - "type": "alias" - } - } - } - } - }, - "process": { - "properties": { - "event_loop_delay": { - "path": "kibana.stats.process.event_loop_delay.ms", - "type": "alias" - }, - "memory": { - "properties": { - "heap": { - "properties": { - "size_limit": { - "path": "kibana.stats.process.memory.heap.size_limit.bytes", - "type": "alias" - } - } - }, - "resident_set_size_in_bytes": { - "path": "kibana.stats.process.memory.resident_set_size.bytes", - "type": "alias" - } - } - }, - "uptime_in_millis": { - "path": "kibana.stats.process.uptime.ms", - "type": "alias" - } - } - }, - "requests": { - "properties": { - "disconnects": { - "path": "kibana.stats.request.disconnects", - "type": "alias" - }, - "total": { - "path": "kibana.stats.request.total", - "type": "alias" - } - } - }, - "response_times": { - "properties": { - "average": { - "path": "kibana.stats.response_time.avg.ms", - "type": "alias" - }, - "max": { - "path": "kibana.stats.response_time.max.ms", - "type": "alias" - } - } - }, - "timestamp": { - "path": "@timestamp", - "type": "alias" - } - } - }, - "process": { - "properties": { - "pid": { - "type": "long" - } - } - }, - "service": { - "properties": { - "address": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "timestamp": { - "path": "@timestamp", - "type": "alias" - } - } - }, - "settings": { - "index": { - "codec": "best_compression", - "lifecycle": { - "name": "metrics" - }, - "mapping": { - "total_fields": { - "limit": "10000" - } - }, - "query": { - "default_field": [ - "service.id", - "service.address", - "service.version", - "service.type", - "ecs.version", - "event.dataset", - "event.module", - "host.name", - "error.message", - "kibana.elasticsearch.cluster.id", - "kibana.stats.kibana.status", - "kibana.stats.usage.index", - "kibana.stats.name", - "kibana.stats.index", - "kibana.stats.host.name", - "kibana.stats.status", - "kibana.stats.transport_address", - "kibana.stats.os.distro", - "kibana.stats.os.distroRelease", - "kibana.stats.os.platform", - "kibana.stats.os.platformRelease" - ] - } - } - } - } - } - } -} - -{ - "type": "data_stream", - "value": { - "data_stream": "metrics-kibana.stack_monitoring.status-default", - "template": { - "_meta": { - "managed": true, - "managed_by": "fleet", - "package": { - "name": "kibana" - } - }, - "data_stream": { - "allow_custom_routing": false, - "hidden": false - }, - "index_patterns": [ - "metrics-kibana.stack_monitoring.status-*" - ], - "name": "metrics-kibana.stack_monitoring.status", - "priority": 200, - "template": { - "mappings": { - "_meta": { - "managed": true, - "managed_by": "fleet", - "package": { - "name": "kibana" - } - }, - "date_detection": false, - "dynamic": false, - "dynamic_templates": [ - { - "strings_as_keyword": { - "mapping": { - "ignore_above": 1024, - "type": "keyword" - }, - "match_mapping_type": "string" - } - } - ], - "properties": { - "@timestamp": { - "type": "date" - }, - "data_stream": { - "properties": { - "dataset": { - "type": "constant_keyword" - }, - "namespace": { - "type": "constant_keyword" - }, - "type": { - "type": "constant_keyword" - } - } - }, - "ecs": { - "properties": { - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "error": { - "properties": { - "message": { - "type": "match_only_text" - } - } - }, - "event": { - "properties": { - "agent_id_status": { - "ignore_above": 1024, - "type": "keyword" - }, - "ingested": { - "format": "strict_date_time_no_millis||strict_date_optional_time||epoch_millis", - "type": "date" - } - } - }, - "kibana": { - "properties": { - "status": { - "properties": { - "metrics": { - "properties": { - "concurrent_connections": { - "type": "long" - }, - "requests": { - "properties": { - "disconnects": { - "type": "long" - }, - "total": { - "type": "long" - } - } - } - } - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "status": { - "properties": { - "overall": { - "properties": { - "state": { - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - } - } - } - } - }, - "service": { - "properties": { - "address": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "settings": { - "index": { - "codec": "best_compression", - "lifecycle": { - "name": "metrics" - }, - "mapping": { - "total_fields": { - "limit": "10000" - } - }, - "query": { - "default_field": [ - "service.id", - "service.name", - "service.version", - "service.type", - "service.address", - "ecs.version", - "error.message", - "kibana.status.name", - "kibana.status.status.overall.state" - ] - } - } - } - } - } - } -} - -{ - "type": "data_stream", - "value": { - "data_stream": "metrics-logstash.stack_monitoring.node-default", - "template": { - "_meta": { - "managed": true, - "managed_by": "fleet", - "package": { - "name": "logstash" - } - }, - "data_stream": { - "allow_custom_routing": false, - "hidden": false - }, - "index_patterns": [ - "metrics-logstash.stack_monitoring.node-*" - ], - "name": "metrics-logstash.stack_monitoring.node", - "priority": 200, - "template": { - "mappings": { - "_meta": { - "managed": true, - "managed_by": "fleet", - "package": { - "name": "logstash" - } - }, - "date_detection": false, - "dynamic": false, - "dynamic_templates": [ - { - "strings_as_keyword": { - "mapping": { - "ignore_above": 1024, - "type": "keyword" - }, - "match_mapping_type": "string" - } - } - ], - "properties": { - "@timestamp": { - "type": "date" - }, - "cluster_uuid": { - "path": "logstash.elasticsearch.cluster.id", - "type": "alias" - }, - "data_stream": { - "properties": { - "dataset": { - "type": "constant_keyword" - }, - "namespace": { - "type": "constant_keyword" - }, - "type": { - "type": "constant_keyword" - } - } - }, - "ecs": { - "properties": { - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "error": { - "properties": { - "message": { - "type": "match_only_text" - } - } - }, - "event": { - "properties": { - "agent_id_status": { - "ignore_above": 1024, - "type": "keyword" - }, - "dataset": { - "ignore_above": 1024, - "type": "keyword" - }, - "duration": { - "type": "long" - }, - "ingested": { - "format": "strict_date_time_no_millis||strict_date_optional_time||epoch_millis", - "type": "date" - }, - "module": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "host": { - "properties": { - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "logstash": { - "properties": { - "cluster": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "elasticsearch": { - "properties": { - "cluster": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "node": { - "properties": { - "host": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "jvm": { - "properties": { - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "state": { - "properties": { - "pipeline": { - "properties": { - "batch_size": { - "type": "long" - }, - "ephemeral_id": { - "ignore_above": 1024, - "type": "keyword" - }, - "hash": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "representation": { - "properties": { - "graph": { - "properties": { - "edges": { - "type": "object" - }, - "vertices": { - "type": "object" - } - } - }, - "hash": { - "ignore_above": 1024, - "type": "keyword" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "workers": { - "type": "long" - } - } - } - } - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "logstash_state": { - "properties": { - "pipeline": { - "properties": { - "hash": { - "path": "logstash.node.state.pipeline.hash", - "type": "alias" - }, - "id": { - "path": "logstash.node.state.pipeline.id", - "type": "alias" - } - } - } - } - }, - "process": { - "properties": { - "pid": { - "type": "long" - } - } - }, - "service": { - "properties": { - "address": { - "ignore_above": 1024, - "type": "keyword" - }, - "hostname": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "timestamp": { - "path": "@timestamp", - "type": "alias" - } - } - }, - "settings": { - "index": { - "codec": "best_compression", - "lifecycle": { - "name": "metrics" - }, - "mapping": { - "total_fields": { - "limit": "10000" - } - }, - "query": { - "default_field": [ - "service.hostname", - "service.id", - "service.type", - "service.version", - "service.address", - "service.name", - "ecs.version", - "event.dataset", - "event.module", - "host.name", - "error.message", - "logstash.cluster.id", - "logstash.elasticsearch.cluster.id", - "logstash.node.jvm.version", - "logstash.node.host", - "logstash.node.version", - "logstash.node.id", - "logstash.node.state.pipeline.id", - "logstash.node.state.pipeline.hash", - "logstash.node.state.pipeline.ephemeral_id", - "logstash.node.state.pipeline.representation.hash", - "logstash.node.state.pipeline.representation.type", - "logstash.node.state.pipeline.representation.version" - ] - } - } - } - } - } - } -} - -{ - "type": "data_stream", - "value": { - "data_stream": "metrics-logstash.stack_monitoring.node_stats-default", - "template": { - "_meta": { - "managed": true, - "managed_by": "fleet", - "package": { - "name": "logstash" - } - }, - "data_stream": { - "allow_custom_routing": false, - "hidden": false - }, - "index_patterns": [ - "metrics-logstash.stack_monitoring.node_stats-*" - ], - "name": "metrics-logstash.stack_monitoring.node_stats", - "priority": 200, - "template": { - "mappings": { - "_meta": { - "managed": true, - "managed_by": "fleet", - "package": { - "name": "logstash" - } - }, - "date_detection": false, - "dynamic": false, - "dynamic_templates": [ - { - "strings_as_keyword": { - "mapping": { - "ignore_above": 1024, - "type": "keyword" - }, - "match_mapping_type": "string" - } - } - ], - "properties": { - "@timestamp": { - "type": "date" - }, - "cluster_uuid": { - "path": "logstash.elasticsearch.cluster.id", - "type": "alias" - }, - "data_stream": { - "properties": { - "dataset": { - "type": "constant_keyword" - }, - "namespace": { - "type": "constant_keyword" - }, - "type": { - "type": "constant_keyword" - } - } - }, - "ecs": { - "properties": { - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "error": { - "properties": { - "message": { - "type": "match_only_text" - } - } - }, - "event": { - "properties": { - "agent_id_status": { - "ignore_above": 1024, - "type": "keyword" - }, - "dataset": { - "ignore_above": 1024, - "type": "keyword" - }, - "duration": { - "type": "long" - }, - "ingested": { - "format": "strict_date_time_no_millis||strict_date_optional_time||epoch_millis", - "type": "date" - }, - "module": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "host": { - "properties": { - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "logstash": { - "properties": { - "cluster": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "elasticsearch": { - "properties": { - "cluster": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "node": { - "properties": { - "state": { - "properties": { - "pipeline": { - "properties": { - "hash": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "stats": { - "properties": { - "events": { - "properties": { - "duration_in_millis": { - "type": "long" - }, - "filtered": { - "type": "long" - }, - "in": { - "type": "long" - }, - "out": { - "type": "long" - } - } - }, - "jvm": { - "properties": { - "gc": { - "properties": { - "collectors": { - "properties": { - "old": { - "properties": { - "collection_count": { - "type": "long" - }, - "collection_time_in_millis": { - "type": "long" - } - } - }, - "young": { - "properties": { - "collection_count": { - "type": "long" - }, - "collection_time_in_millis": { - "type": "long" - } - } - } - } - } - } - }, - "mem": { - "properties": { - "heap_max_in_bytes": { - "type": "long" - }, - "heap_used_in_bytes": { - "type": "long" - }, - "heap_used_percent": { - "type": "long" - } - } - }, - "uptime_in_millis": { - "type": "long" - } - } - }, - "logstash": { - "properties": { - "ephemeral_id": { - "ignore_above": 1024, - "type": "keyword" - }, - "host": { - "ignore_above": 1024, - "type": "keyword" - }, - "http_address": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "pipeline": { - "properties": { - "batch_size": { - "type": "long" - }, - "workers": { - "type": "long" - } - } - }, - "snapshot": { - "type": "boolean" - }, - "status": { - "ignore_above": 1024, - "type": "keyword" - }, - "uuid": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "os": { - "properties": { - "cgroup": { - "properties": { - "cpu": { - "properties": { - "cfs_quota_micros": { - "type": "long" - }, - "control_group": { - "type": "text" - }, - "stat": { - "type": "object" - } - } - }, - "cpuacct": { - "type": "object" - } - } - }, - "cpu": { - "properties": { - "load_average": { - "properties": { - "15m": { - "type": "half_float" - }, - "1m": { - "type": "half_float" - }, - "5m": { - "type": "half_float" - } - } - }, - "percent": { - "type": "double" - } - } - } - } - }, - "pipelines": { - "properties": { - "ephemeral_id": { - "ignore_above": 1024, - "type": "keyword" - }, - "events": { - "properties": { - "duration_in_millis": { - "type": "long" - }, - "filtered": { - "type": "long" - }, - "in": { - "type": "long" - }, - "out": { - "type": "long" - }, - "queue_push_duration_in_millis": { - "type": "long" - } - } - }, - "hash": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "queue": { - "properties": { - "events_count": { - "type": "long" - }, - "max_queue_size_in_bytes": { - "type": "long" - }, - "queue_size_in_bytes": { - "type": "long" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "reloads": { - "properties": { - "failures": { - "type": "long" - }, - "successes": { - "type": "long" - } - } - }, - "vertices": { - "properties": { - "duration_in_millis": { - "type": "long" - }, - "events_in": { - "type": "long" - }, - "events_out": { - "type": "long" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "long_counters": { - "properties": { - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "value": { - "type": "long" - } - }, - "type": "nested" - }, - "pipeline_ephemeral_id": { - "ignore_above": 1024, - "type": "keyword" - }, - "queue_push_duration_in_millis": { - "type": "long" - } - }, - "type": "nested" - } - }, - "type": "nested" - }, - "process": { - "properties": { - "cpu": { - "properties": { - "percent": { - "type": "double" - } - } - }, - "max_file_descriptors": { - "type": "long" - }, - "open_file_descriptors": { - "type": "long" - } - } - }, - "queue": { - "properties": { - "events_count": { - "type": "long" - } - } - }, - "reloads": { - "properties": { - "failures": { - "type": "long" - }, - "successes": { - "type": "long" - } - } - }, - "timestamp": { - "type": "date" - } - } - } - } - } - } - }, - "logstash_stats": { - "properties": { - "events": { - "properties": { - "duration_in_millis": { - "path": "logstash.node.stats.events.duration_in_millis", - "type": "alias" - }, - "in": { - "path": "logstash.node.stats.events.in", - "type": "alias" - }, - "out": { - "path": "logstash.node.stats.events.out", - "type": "alias" - } - } - }, - "jvm": { - "properties": { - "mem": { - "properties": { - "heap_max_in_bytes": { - "path": "logstash.node.stats.jvm.mem.heap_max_in_bytes", - "type": "alias" - }, - "heap_used_in_bytes": { - "path": "logstash.node.stats.jvm.mem.heap_used_in_bytes", - "type": "alias" - } - } - }, - "uptime_in_millis": { - "path": "logstash.node.stats.jvm.uptime_in_millis", - "type": "alias" - } - } - }, - "logstash": { - "properties": { - "uuid": { - "path": "logstash.node.stats.logstash.uuid", - "type": "alias" - }, - "version": { - "path": "logstash.node.stats.logstash.version", - "type": "alias" - } - } - }, - "os": { - "properties": { - "cgroup": { - "properties": { - "cpuacct": { - "type": "object" - } - } - }, - "cpu": { - "properties": { - "load_average": { - "properties": { - "15m": { - "path": "logstash.node.stats.os.cpu.load_average.15m", - "type": "alias" - }, - "1m": { - "path": "logstash.node.stats.os.cpu.load_average.1m", - "type": "alias" - }, - "5m": { - "path": "logstash.node.stats.os.cpu.load_average.5m", - "type": "alias" - } - } - }, - "stat": { - "type": "object" - } - } - } - } - }, - "pipelines": { - "type": "nested" - }, - "process": { - "properties": { - "cpu": { - "properties": { - "percent": { - "path": "logstash.node.stats.process.cpu.percent", - "type": "alias" - } - } - } - } - }, - "queue": { - "properties": { - "events_count": { - "path": "logstash.node.stats.queue.events_count", - "type": "alias" - } - } - }, - "timestamp": { - "path": "@timestamp", - "type": "alias" - } - } - }, - "process": { - "properties": { - "pid": { - "type": "long" - } - } - }, - "service": { - "properties": { - "address": { - "ignore_above": 1024, - "type": "keyword" - }, - "hostname": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "timestamp": { - "path": "@timestamp", - "type": "alias" - } - } - }, - "settings": { - "index": { - "codec": "best_compression", - "lifecycle": { - "name": "metrics" - }, - "mapping": { - "total_fields": { - "limit": "10000" - } - }, - "query": { - "default_field": [ - "service.hostname", - "service.id", - "service.type", - "service.version", - "service.address", - "service.name", - "ecs.version", - "event.dataset", - "event.module", - "host.name", - "error.message", - "logstash.elasticsearch.cluster.id", - "logstash.node.state.pipeline.id", - "logstash.node.state.pipeline.hash", - "logstash.node.stats.logstash.uuid", - "logstash.node.stats.logstash.version", - "logstash.node.stats.logstash.ephemeral_id", - "logstash.node.stats.logstash.host", - "logstash.node.stats.logstash.http_address", - "logstash.node.stats.logstash.name", - "logstash.node.stats.logstash.status", - "logstash.node.stats.os.cgroup.cpuacct.control_group", - "logstash.node.stats.os.cgroup.cpu.control_group", - "logstash.node.stats.pipelines.id", - "logstash.node.stats.pipelines.hash", - "logstash.node.stats.pipelines.ephemeral_id", - "logstash.node.stats.pipelines.queue.type", - "logstash.node.stats.pipelines.vertices.id", - "logstash.node.stats.pipelines.vertices.long_counters.name", - "logstash.node.stats.pipelines.vertices.pipeline_ephemeral_id", - "logstash.cluster.id" - ] - } - } - } - } - } - } -} diff --git a/x-pack/test/functional_with_es_ssl/apps/cases/group2/list_view.ts b/x-pack/test/functional_with_es_ssl/apps/cases/group2/list_view.ts index b7b5d892681c0..1fee6d5e1b0fe 100644 --- a/x-pack/test/functional_with_es_ssl/apps/cases/group2/list_view.ts +++ b/x-pack/test/functional_with_es_ssl/apps/cases/group2/list_view.ts @@ -279,7 +279,8 @@ export default ({ getPageObject, getService }: FtrProviderContext) => { }); }); - describe('filtering', () => { + // FLAKY: https://github.com/elastic/kibana/issues/152925 + describe.skip('filtering', () => { const caseTitle = 'matchme'; const caseIds: string[] = []; @@ -448,7 +449,8 @@ export default ({ getPageObject, getService }: FtrProviderContext) => { await testSubjects.existOrFail(`case-table-column-severity-${CaseSeverity.MEDIUM}`); }); - describe('assignees filtering', () => { + // FLAKY: https://github.com/elastic/kibana/issues/152928 + describe.skip('assignees filtering', () => { it('filters cases by the first cases all user assignee', async () => { await cases.casesTable.filterByAssignee('all'); await cases.casesTable.validateCasesTableHasNthRows(1); diff --git a/x-pack/test/functional_with_es_ssl/apps/triggers_actions_ui/alert_create_flyout.ts b/x-pack/test/functional_with_es_ssl/apps/triggers_actions_ui/alert_create_flyout.ts index b788b4f875c6e..273c39adbf431 100644 --- a/x-pack/test/functional_with_es_ssl/apps/triggers_actions_ui/alert_create_flyout.ts +++ b/x-pack/test/functional_with_es_ssl/apps/triggers_actions_ui/alert_create_flyout.ts @@ -124,6 +124,7 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { await messageTextArea.type(' some additional text '); await testSubjects.click('messageAddVariableButton'); + await testSubjects.setValue('messageVariablesSelectableSearch', 'rule.id'); await testSubjects.click('variableMenuButton-rule.id'); expect(await messageTextArea.getAttribute('value')).to.eql( diff --git a/x-pack/test/functional_with_es_ssl/plugins/cases/server/plugin.ts b/x-pack/test/functional_with_es_ssl/plugins/cases/server/plugin.ts index df760a036b01e..fc3dd61fe6a44 100644 --- a/x-pack/test/functional_with_es_ssl/plugins/cases/server/plugin.ts +++ b/x-pack/test/functional_with_es_ssl/plugins/cases/server/plugin.ts @@ -5,7 +5,7 @@ * 2.0. */ -import { PluginSetupContract as CasesSetup } from '@kbn/cases-plugin/server/types'; +import { CasesSetup } from '@kbn/cases-plugin/server/types'; import { Plugin, CoreSetup } from '@kbn/core/server'; import { getExternalReferenceAttachment } from './attachments/external_reference'; import { getPersistableStateAttachmentServer } from './attachments/persistable_state'; diff --git a/x-pack/test/monitoring_api_integration/apis/index.ts b/x-pack/test/monitoring_api_integration/apis/index.ts index 13757900fad9f..3a6681fc20b05 100644 --- a/x-pack/test/monitoring_api_integration/apis/index.ts +++ b/x-pack/test/monitoring_api_integration/apis/index.ts @@ -13,5 +13,6 @@ export default function ({ loadTestFile }: FtrProviderContext) { loadTestFile(require.resolve('./beats')); loadTestFile(require.resolve('./elasticsearch')); loadTestFile(require.resolve('./enterprisesearch')); + loadTestFile(require.resolve('./logstash')); }); } diff --git a/x-pack/test/monitoring_api_integration/apis/logstash/index.ts b/x-pack/test/monitoring_api_integration/apis/logstash/index.ts new file mode 100644 index 0000000000000..88a5980273ef5 --- /dev/null +++ b/x-pack/test/monitoring_api_integration/apis/logstash/index.ts @@ -0,0 +1,16 @@ +/* + * 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 { FtrProviderContext } from '../../../api_integration/ftr_provider_context'; + +export default function ({ loadTestFile }: FtrProviderContext) { + describe('Logstash', () => { + loadTestFile(require.resolve('./overview')); + loadTestFile(require.resolve('./nodes')); + loadTestFile(require.resolve('./pipelines')); + }); +} diff --git a/x-pack/test/monitoring_api_integration/apis/logstash/nodes.ts b/x-pack/test/monitoring_api_integration/apis/logstash/nodes.ts new file mode 100644 index 0000000000000..b50ecd49ff094 --- /dev/null +++ b/x-pack/test/monitoring_api_integration/apis/logstash/nodes.ts @@ -0,0 +1,65 @@ +/* + * 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 expect from '@kbn/expect'; +import { FtrProviderContext } from '../../../api_integration/ftr_provider_context'; +import { getTestRunner } from '../../utils/test_runner'; + +import allNodesResponse from '../../fixtures/logstash/nodes.json'; +import nodeDetailResponse from '../../fixtures/logstash/node_detail.json'; +import nodeDetailAdvancedResponse from '../../fixtures/logstash/node_detail_advanced.json'; + +export default function ({ getService }: FtrProviderContext) { + const supertest = getService('supertest'); + + const testRunner = getTestRunner({ + testName: 'Nodes', + archiveRoot: 'x-pack/test/monitoring_api_integration/archives/logstash/single_node', + getService, + }); + + const timeRange = { + min: '2023-03-01T18:03:00.000Z', + max: '2023-03-01T18:06:40.000Z', + }; + + testRunner(() => { + it('should summarize logstash nodes with stats', async () => { + const { body } = await supertest + .post('/api/monitoring/v1/clusters/b0z1XQQNSyiV2y8bWz_zzQ/logstash/nodes') + .set('kbn-xsrf', 'xxx') + .send({ timeRange }) + .expect(200); + + expect(body).to.eql(allNodesResponse); + }); + + it('should summarize the Logstash node with non-advanced chart data metrics', async () => { + const { body } = await supertest + .post( + '/api/monitoring/v1/clusters/b0z1XQQNSyiV2y8bWz_zzQ/logstash/node/5d74947f-b537-4463-abfe-e7488cc04d4a' + ) + .set('kbn-xsrf', 'xxx') + .send({ timeRange, is_advanced: false }) + .expect(200); + + expect(body).to.eql(nodeDetailResponse); + }); + + it('should summarize the Logstash node with advanced chart data metrics', async () => { + const { body } = await supertest + .post( + '/api/monitoring/v1/clusters/b0z1XQQNSyiV2y8bWz_zzQ/logstash/node/5d74947f-b537-4463-abfe-e7488cc04d4a' + ) + .set('kbn-xsrf', 'xxx') + .send({ timeRange, is_advanced: true }) + .expect(200); + + expect(body).to.eql(nodeDetailAdvancedResponse); + }); + }); +} diff --git a/x-pack/test/monitoring_api_integration/apis/logstash/overview.ts b/x-pack/test/monitoring_api_integration/apis/logstash/overview.ts new file mode 100644 index 0000000000000..b0272eb2ebabb --- /dev/null +++ b/x-pack/test/monitoring_api_integration/apis/logstash/overview.ts @@ -0,0 +1,39 @@ +/* + * 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 expect from '@kbn/expect'; +import { FtrProviderContext } from '../../../api_integration/ftr_provider_context'; +import { getTestRunner } from '../../utils/test_runner'; + +import response from '../../fixtures/logstash/overview.json'; + +export default function ({ getService }: FtrProviderContext) { + const supertest = getService('supertest'); + + const testRunner = getTestRunner({ + testName: 'Overview', + archiveRoot: 'x-pack/test/monitoring_api_integration/archives/logstash/single_node', + getService, + }); + + const timeRange = { + min: '2023-03-01T18:03:00.000Z', + max: '2023-03-01T18:06:40.000Z', + }; + + testRunner(() => { + it('should summarize logstash nodes', async () => { + const { body } = await supertest + .post('/api/monitoring/v1/clusters/b0z1XQQNSyiV2y8bWz_zzQ/logstash') + .set('kbn-xsrf', 'xxx') + .send({ timeRange }) + .expect(200); + + expect(body).to.eql(response); + }); + }); +} diff --git a/x-pack/test/monitoring_api_integration/apis/logstash/pipelines.ts b/x-pack/test/monitoring_api_integration/apis/logstash/pipelines.ts new file mode 100644 index 0000000000000..cf3c2fcf2b35c --- /dev/null +++ b/x-pack/test/monitoring_api_integration/apis/logstash/pipelines.ts @@ -0,0 +1,103 @@ +/* + * 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 expect from '@kbn/expect'; +import { FtrProviderContext } from '../../../api_integration/ftr_provider_context'; +import { getTestRunner } from '../../utils/test_runner'; + +import allPipelinesResponse from '../../fixtures/logstash/pipelines.json'; +import pipelineResponse from '../../fixtures/logstash/pipeline.json'; +import vertexResponse from '../../fixtures/logstash/vertex.json'; + +export default function ({ getService }: FtrProviderContext) { + const supertest = getService('supertest'); + + const testRunner = getTestRunner({ + testName: 'Pipelines', + archiveRoot: 'x-pack/test/monitoring_api_integration/archives/logstash/single_node', + getService, + }); + + const timeRange = { + min: '2023-03-01T18:03:00.000Z', + max: '2023-03-01T18:06:40.000Z', + }; + + testRunner(() => { + it('should return paginated pipelines', async () => { + const { body } = await supertest + .post('/api/monitoring/v1/clusters/b0z1XQQNSyiV2y8bWz_zzQ/logstash/pipelines') + .set('kbn-xsrf', 'xxx') + .send({ + timeRange, + sort: { field: 'id', direction: 'asc' }, + pagination: { size: 1, index: 0 }, + }) + .expect(200); + + expect(body).to.eql(allPipelinesResponse); + }); + + it('should get all pipelines after enough pagination', async () => { + async function getIds(page: number) { + const { body } = await supertest + .post('/api/monitoring/v1/clusters/b0z1XQQNSyiV2y8bWz_zzQ/logstash/pipelines') + .set('kbn-xsrf', 'xxx') + .send({ + timeRange, + sort: { field: 'id', direction: 'asc' }, + pagination: { size: 1, index: page }, + }) + .expect(200); + + return body.pipelines; + } + + const ids = [...(await getIds(0)), ...(await getIds(1))]; + expect(ids.length).to.be(2); + }); + + it('should not error out if there is missing data for part of the time series', async () => { + await supertest + .post('/api/monitoring/v1/clusters/b0z1XQQNSyiV2y8bWz_zzQ/logstash/pipelines') + .set('kbn-xsrf', 'xxx') + .send({ + timeRange: { min: timeRange.min, max: '2023-03-01T18:15:40.000Z' }, + pagination: { size: 1, index: 1 }, + sort: { field: 'logstash_cluster_pipeline_throughput', direction: 'asc' }, + }) + .expect(200); + }); + + it('should return pipeline details', async () => { + const { body } = await supertest + .post( + '/api/monitoring/v1/clusters/b0z1XQQNSyiV2y8bWz_zzQ/logstash/pipeline/pipeline-with-memory-queue' + ) + .set('kbn-xsrf', 'xxx') + .send({ timeRange }) + .expect(200); + + expect(body).to.eql(pipelineResponse); + }); + + it('should return vertex details', async () => { + const { body } = await supertest + .post( + '/api/monitoring/v1/clusters/b0z1XQQNSyiV2y8bWz_zzQ/logstash/pipeline/pipeline-with-memory-queue' + ) + .set('kbn-xsrf', 'xxx') + .send({ + timeRange, + detailVertexId: '635a080aacc8700059852859da284a9cb92cb78a6d7112fbf55e441e51b6658a', + }) + .expect(200); + + expect(body).to.eql(vertexResponse); + }); + }); +} diff --git a/x-pack/test/monitoring_api_integration/archives/logstash/single_node/metricbeat/data.json.gz b/x-pack/test/monitoring_api_integration/archives/logstash/single_node/metricbeat/data.json.gz new file mode 100644 index 0000000000000..214c436fd9fd4 Binary files /dev/null and b/x-pack/test/monitoring_api_integration/archives/logstash/single_node/metricbeat/data.json.gz differ diff --git a/x-pack/test/monitoring_api_integration/archives/logstash/single_node/package/data.json.gz b/x-pack/test/monitoring_api_integration/archives/logstash/single_node/package/data.json.gz new file mode 100644 index 0000000000000..374eec2d58cc6 Binary files /dev/null and b/x-pack/test/monitoring_api_integration/archives/logstash/single_node/package/data.json.gz differ diff --git a/x-pack/test/monitoring_api_integration/fixtures/logstash/node_detail.json b/x-pack/test/monitoring_api_integration/fixtures/logstash/node_detail.json new file mode 100644 index 0000000000000..b48a62e1016a1 --- /dev/null +++ b/x-pack/test/monitoring_api_integration/fixtures/logstash/node_detail.json @@ -0,0 +1 @@ +{"metrics":{"logstash_os_load":[{"bucket_size":"10 seconds","timeRange":{"min":1677693780000,"max":1677694000000},"metric":{"app":"logstash","field":"logstash_stats.os.cpu.load_average.1m","metricAgg":"max","label":"1m","title":"System Load","description":"Load average over the last minute.","units":"","format":"0,0.[00]","hasCalculation":false,"isDerivative":false},"data":[[1677693780000,3.08984375],[1677693790000,2.619140625],[1677693800000,2.2109375],[1677693810000,2.240234375],[1677693820000,2.0390625],[1677693830000,1.7998046875],[1677693840000,1.51953125],[1677693850000,1.759765625],[1677693860000,2.0390625],[1677693870000,2.119140625],[1677693880000,2.25],[1677693890000,1.98046875],[1677693900000,2.220703125],[1677693910000,1.8701171875],[1677693920000,1.669921875],[1677693930000,1.7197265625],[1677693940000,1.6904296875],[1677693950000,1.509765625],[1677693960000,1.669921875],[1677693970000,1.5595703125],[1677693980000,1.7197265625],[1677693990000,1.6103515625]]},{"bucket_size":"10 seconds","timeRange":{"min":1677693780000,"max":1677694000000},"metric":{"app":"logstash","field":"logstash_stats.os.cpu.load_average.5m","metricAgg":"max","label":"5m","title":"System Load","description":"Load average over the last 5 minutes.","units":"","format":"0,0.[00]","hasCalculation":false,"isDerivative":false},"data":[[1677693780000,1.8798828125],[1677693790000,1.8203125],[1677693800000,1.759765625],[1677693810000,1.7802734375],[1677693820000,1.759765625],[1677693830000,1.7197265625],[1677693840000,1.66015625],[1677693850000,1.7001953125],[1677693860000,1.759765625],[1677693870000,1.7900390625],[1677693880000,1.830078125],[1677693890000,1.7900390625],[1677693900000,1.83984375],[1677693910000,1.7802734375],[1677693920000,1.740234375],[1677693930000,1.75],[1677693940000,1.740234375],[1677693950000,1.7001953125],[1677693960000,1.73046875],[1677693970000,1.7001953125],[1677693980000,1.73046875],[1677693990000,1.7001953125]]},{"bucket_size":"10 seconds","timeRange":{"min":1677693780000,"max":1677694000000},"metric":{"app":"logstash","field":"logstash_stats.os.cpu.load_average.15m","metricAgg":"max","label":"15m","title":"System Load","description":"Load average over the last 15 minutes.","units":"","format":"0,0.[00]","hasCalculation":false,"isDerivative":false},"data":[[1677693780000,0.8798828125],[1677693790000,0.8701171875],[1677693800000,0.85986328125],[1677693810000,0.8701171875],[1677693820000,0.8798828125],[1677693830000,0.8701171875],[1677693840000,0.85986328125],[1677693850000,0.89013671875],[1677693860000,0.91015625],[1677693870000,0.93017578125],[1677693880000,0.9501953125],[1677693890000,0.9501953125],[1677693900000,0.97998046875],[1677693910000,0.97021484375],[1677693920000,0.9599609375],[1677693930000,0.97021484375],[1677693940000,0.97998046875],[1677693950000,0.97021484375],[1677693960000,0.990234375],[1677693970000,0.990234375],[1677693980000,1.009765625],[1677693990000,1.009765625]]}],"logstash_events_input_rate":[{"bucket_size":"10 seconds","timeRange":{"min":1677693780000,"max":1677694000000},"metric":{"app":"logstash","field":"logstash_stats.events.in","metricAgg":"max","label":"Events Received Rate","description":"Number of events received per second by the Logstash node at the inputs stage.","units":"/s","format":"0,0.[00]","hasCalculation":false,"isDerivative":true},"data":[[1677693780000,60.5],[1677693790000,60.1],[1677693800000,59.9],[1677693810000,60],[1677693820000,60],[1677693830000,60],[1677693840000,60],[1677693850000,60],[1677693860000,60],[1677693870000,60],[1677693880000,60.1],[1677693890000,59.9],[1677693900000,60],[1677693910000,60],[1677693920000,60],[1677693930000,60],[1677693940000,60],[1677693950000,60],[1677693960000,60],[1677693970000,60],[1677693980000,60],[1677693990000,60]]}],"logstash_events_output_rate":[{"bucket_size":"10 seconds","timeRange":{"min":1677693780000,"max":1677694000000},"metric":{"app":"logstash","field":"logstash_stats.events.out","metricAgg":"max","label":"Events Emitted Rate","description":"Number of events emitted per second by the Logstash node at the outputs stage.","units":"/s","format":"0,0.[00]","hasCalculation":false,"isDerivative":true},"data":[[1677693780000,37.4],[1677693790000,60.4],[1677693800000,60.1],[1677693810000,59.7],[1677693820000,59.8],[1677693830000,60.4],[1677693840000,59.5],[1677693850000,59.2],[1677693860000,60.7],[1677693870000,60],[1677693880000,60.5],[1677693890000,60.1],[1677693900000,59.9],[1677693910000,59.9],[1677693920000,60.2],[1677693930000,58.6],[1677693940000,59.1],[1677693950000,60.7],[1677693960000,61.5],[1677693970000,60.1],[1677693980000,56.2],[1677693990000,63.9]]}],"logstash_events_latency":[{"bucket_size":"10 seconds","timeRange":{"min":1677693780000,"max":1677694000000},"metric":{"app":"logstash","field":"logstash_stats.events.out","metricAgg":"sum","label":"Event Latency","description":"Average time spent by events in the filter and output stages, which is the total time it takes to process events divided by number of events emitted.","units":"ms","format":"0,0.[00]","hasCalculation":true,"isDerivative":false},"data":[[1677693780000,null],[1677693790000,82.62582781456955],[1677693800000,87.62728785357736],[1677693810000,81.75879396984924],[1677693820000,86.58193979933111],[1677693830000,83.84768211920529],[1677693840000,83.54453781512605],[1677693850000,88.96790540540539],[1677693860000,80.13838550247117],[1677693870000,86.15666666666667],[1677693880000,83.99504132231405],[1677693890000,82.38768718801997],[1677693900000,87.91652754590984],[1677693910000,81.21368948247078],[1677693920000,85.8205980066445],[1677693930000,86.35665529010238],[1677693940000,83.97292724196278],[1677693950000,86.79242174629324],[1677693960000,79.06341463414634],[1677693970000,85.83194675540766],[1677693980000,90.02135231316726],[1677693990000,77.65571205007825]]}],"logstash_node_cpu_metric":[{"bucket_size":"10 seconds","timeRange":{"min":1677693780000,"max":1677694000000},"metric":{"app":"logstash","field":"logstash_stats.process.cpu.percent","metricAgg":"max","label":"CPU Utilization","description":"Percentage of CPU usage reported by the OS (100% is the max).","units":"%","format":"0,0.[00]","hasCalculation":false,"isDerivative":false},"data":[[1677693780000,3],[1677693790000,2],[1677693800000,2],[1677693810000,1],[1677693820000,1],[1677693830000,1],[1677693840000,2],[1677693850000,1],[1677693860000,1],[1677693870000,1],[1677693880000,1],[1677693890000,1],[1677693900000,1],[1677693910000,0],[1677693920000,1],[1677693930000,1],[1677693940000,1],[1677693950000,0],[1677693960000,0],[1677693970000,0],[1677693980000,0],[1677693990000,0]]}],"logstash_jvm_usage":[{"bucket_size":"10 seconds","timeRange":{"min":1677693780000,"max":1677694000000},"metric":{"app":"logstash","field":"logstash_stats.jvm.mem.heap_max_in_bytes","metricAgg":"max","label":"Max Heap","title":"JVM Heap","description":"Total heap available to Logstash running in the JVM.","units":"B","format":"0.0 b","hasCalculation":false,"isDerivative":false},"data":[[1677693780000,10527703038],[1677693790000,10527703038],[1677693800000,10527703038],[1677693810000,10527703038],[1677693820000,10527703038],[1677693830000,10527703038],[1677693840000,10527703038],[1677693850000,10527703038],[1677693860000,10527703038],[1677693870000,10527703038],[1677693880000,10527703038],[1677693890000,10527703038],[1677693900000,10527703038],[1677693910000,10527703038],[1677693920000,10527703038],[1677693930000,10527703038],[1677693940000,10527703038],[1677693950000,10527703038],[1677693960000,10527703038],[1677693970000,10527703038],[1677693980000,10527703038],[1677693990000,10527703038]]},{"bucket_size":"10 seconds","timeRange":{"min":1677693780000,"max":1677694000000},"metric":{"app":"logstash","field":"logstash_stats.jvm.mem.heap_used_in_bytes","metricAgg":"max","label":"Used Heap","title":"JVM Heap","description":"Total heap used by Logstash running in the JVM.","units":"B","format":"0.0 b","hasCalculation":false,"isDerivative":false},"data":[[1677693780000,229536256],[1677693790000,149235392],[1677693800000,199567040],[1677693810000,249898688],[1677693820000,151089064],[1677693830000,193032104],[1677693840000,226586536],[1677693850000,260140968],[1677693860000,152650176],[1677693870000,177816000],[1677693880000,253313472],[1677693890000,154029232],[1677693900000,179195056],[1677693910000,221138096],[1677693920000,254692528],[1677693930000,137901312],[1677693940000,163067136],[1677693950000,188232960],[1677693960000,221787392],[1677693970000,246953216],[1677693980000,272119040],[1677693990000,146890312]]}]},"nodeSummary":{"pipeline":{"batch_size":125,"workers":10},"http_address":"0.0.0.0:9600","host":"a868a8283bec","name":"a868a8283bec","ephemeral_id":"5554814a-6ebc-4b82-aa7a-55c8c81162d0","version":"8.7.0","uuid":"5d74947f-b537-4463-abfe-e7488cc04d4a","snapshot":true,"status":"green","availability":false,"events":{"filtered":13621,"in":14303,"duration_in_millis":1176544,"out":13621},"reloads":{"failures":0,"successes":0},"uptime":253309}} diff --git a/x-pack/test/monitoring_api_integration/fixtures/logstash/node_detail_advanced.json b/x-pack/test/monitoring_api_integration/fixtures/logstash/node_detail_advanced.json new file mode 100644 index 0000000000000..365b157f69d20 --- /dev/null +++ b/x-pack/test/monitoring_api_integration/fixtures/logstash/node_detail_advanced.json @@ -0,0 +1 @@ +{"metrics":{"logstash_node_cpu_utilization":[{"bucket_size":"10 seconds","timeRange":{"min":1677693780000,"max":1677694000000},"metric":{"app":"logstash","field":"logstash_stats.process.cpu.percent","metricAgg":"max","label":"CPU Utilization","description":"Percentage of CPU usage reported by the OS (100% is the max).","units":"%","format":"0,0.[00]","hasCalculation":false,"isDerivative":false},"data":[[1677693780000,3],[1677693790000,2],[1677693800000,2],[1677693810000,1],[1677693820000,1],[1677693830000,1],[1677693840000,2],[1677693850000,1],[1677693860000,1],[1677693870000,1],[1677693880000,1],[1677693890000,1],[1677693900000,1],[1677693910000,0],[1677693920000,1],[1677693930000,1],[1677693940000,1],[1677693950000,0],[1677693960000,0],[1677693970000,0],[1677693980000,0],[1677693990000,0]]},{"bucket_size":"10 seconds","timeRange":{"min":1677693780000,"max":1677694000000},"metric":{"app":"logstash","field":"logstash_stats.process.cpu.percent","metricAgg":"max","label":"Cgroup CPU Utilization","title":"CPU Utilization","description":"CPU Usage time compared to the CPU quota shown in percentage. If CPU quotas are not set, then no data will be shown.","units":"%","format":"0,0.[00]","hasCalculation":true,"isDerivative":true},"data":[[1677693780000,null],[1677693790000,null],[1677693800000,null],[1677693810000,null],[1677693820000,null],[1677693830000,null],[1677693840000,null],[1677693850000,null],[1677693860000,null],[1677693870000,null],[1677693880000,null],[1677693890000,null],[1677693900000,null],[1677693910000,null],[1677693920000,null],[1677693930000,null],[1677693940000,null],[1677693950000,null],[1677693960000,null],[1677693970000,null],[1677693980000,null],[1677693990000,null]]}],"logstash_node_cgroup_cpu":[{"bucket_size":"10 seconds","timeRange":{"min":1677693780000,"max":1677694000000},"metric":{"app":"logstash","field":"logstash_stats.os.cgroup.cpuacct.usage_nanos","metricAgg":"max","label":"Cgroup Usage","title":"Cgroup CPU Performance","description":"The usage, reported in nanoseconds, of the Cgroup. Compare this with the throttling to discover issues.","units":"ns","format":"0,0.[0]a","hasCalculation":false,"isDerivative":true},"data":[[1677693780000,403456030.9],[1677693790000,391652756.9],[1677693800000,319946515.7],[1677693810000,181880616.7],[1677693820000,227958639.7],[1677693830000,158849675.4],[1677693840000,159303122.1],[1677693850000,167190363.3],[1677693860000,135633131.7],[1677693870000,129958968.2],[1677693880000,180532349.7],[1677693890000,149083969.5],[1677693900000,141222768.9],[1677693910000,127174266.5],[1677693920000,134092393.9],[1677693930000,119163106.5],[1677693940000,147401105.6],[1677693950000,94999179.4],[1677693960000,94519431.3],[1677693970000,83915573.4],[1677693980000,90830102],[1677693990000,90984622.9]]},{"bucket_size":"10 seconds","timeRange":{"min":1677693780000,"max":1677694000000},"metric":{"app":"logstash","field":"logstash_stats.os.cgroup.cpu.stat.time_throttled_nanos","metricAgg":"max","label":"Cgroup Throttling","title":"Cgroup CPU Performance","description":"The amount of throttled time, reported in nanoseconds, of the Cgroup.","units":"ns","format":"0,0.[0]a","hasCalculation":false,"isDerivative":true},"data":[[1677693780000,0],[1677693790000,0],[1677693800000,0],[1677693810000,0],[1677693820000,0],[1677693830000,0],[1677693840000,0],[1677693850000,0],[1677693860000,0],[1677693870000,0],[1677693880000,0],[1677693890000,0],[1677693900000,0],[1677693910000,0],[1677693920000,0],[1677693930000,0],[1677693940000,0],[1677693950000,0],[1677693960000,0],[1677693970000,0],[1677693980000,0],[1677693990000,0]]}],"logstash_node_cgroup_stats":[{"bucket_size":"10 seconds","timeRange":{"min":1677693780000,"max":1677694000000},"metric":{"app":"logstash","field":"logstash_stats.os.cgroup.cpu.stat.number_of_elapsed_periods","metricAgg":"max","label":"Cgroup Elapsed Periods","title":"Cgroup CFS Stats","description":"The number of sampling periods from the Completely Fair Scheduler (CFS). Compare against the number of times throttled.","units":"","format":"0,0.[00]","hasCalculation":false,"isDerivative":true},"data":[[1677693780000,0],[1677693790000,0],[1677693800000,0],[1677693810000,0],[1677693820000,0],[1677693830000,0],[1677693840000,0],[1677693850000,0],[1677693860000,0],[1677693870000,0],[1677693880000,0],[1677693890000,0],[1677693900000,0],[1677693910000,0],[1677693920000,0],[1677693930000,0],[1677693940000,0],[1677693950000,0],[1677693960000,0],[1677693970000,0],[1677693980000,0],[1677693990000,0]]},{"bucket_size":"10 seconds","timeRange":{"min":1677693780000,"max":1677694000000},"metric":{"app":"logstash","field":"logstash_stats.os.cgroup.cpu.stat.number_of_times_throttled","metricAgg":"max","label":"Cgroup Throttled Count","title":"Cgroup CFS Stats","description":"The number of times that the CPU was throttled by the Cgroup.","units":"","format":"0,0.[00]","hasCalculation":false,"isDerivative":true},"data":[[1677693780000,0],[1677693790000,0],[1677693800000,0],[1677693810000,0],[1677693820000,0],[1677693830000,0],[1677693840000,0],[1677693850000,0],[1677693860000,0],[1677693870000,0],[1677693880000,0],[1677693890000,0],[1677693900000,0],[1677693910000,0],[1677693920000,0],[1677693930000,0],[1677693940000,0],[1677693950000,0],[1677693960000,0],[1677693970000,0],[1677693980000,0],[1677693990000,0]]}],"logstash_queue_events_count":[{"bucket_size":"10 seconds","timeRange":{"min":1677693780000,"max":1677694000000},"metric":{"app":"logstash","field":"logstash_stats.queue.events_count","metricAgg":"max","label":"Events Queued","title":"Persistent Queue Events","description":"Average number of events in the persistent queue waiting to be processed by the filter and output stages.","units":"","format":"0,0.[00]","hasCalculation":false,"isDerivative":false},"data":[[1677693780000,0],[1677693790000,0],[1677693800000,0],[1677693810000,0],[1677693820000,0],[1677693830000,0],[1677693840000,0],[1677693850000,0],[1677693860000,0],[1677693870000,0],[1677693880000,0],[1677693890000,0],[1677693900000,0],[1677693910000,0],[1677693920000,0],[1677693930000,0],[1677693940000,0],[1677693950000,0],[1677693960000,0],[1677693970000,0],[1677693980000,0],[1677693990000,0]]}],"logstash_pipeline_queue_size":[{"bucket_size":"10 seconds","timeRange":{"min":1677693780000,"max":1677694000000},"metric":{"app":"logstash","field":"logstash_stats.pipelines.queue.queue_size_in_bytes","label":"Queue Size","title":"Persistent Queue Size","description":"Current size of all persistent queues in the Logstash pipelines on this node.","units":"B","format":"0,0.0 b","hasCalculation":true,"isDerivative":false},"data":[[1677693780000,275663],[1677693790000,435341],[1677693800000,594661],[1677693810000,753960],[1677693820000,913599],[1677693830000,1072940],[1677693840000,1232311],[1677693850000,1391643],[1677693860000,1551306],[1677693870000,1710674],[1677693880000,1870012],[1677693890000,2029708],[1677693900000,2189070],[1677693910000,2348399],[1677693920000,2508029],[1677693930000,2667343],[1677693940000,2826714],[1677693950000,2986025],[1677693960000,3145703],[1677693970000,3305053],[1677693980000,3464418],[1677693990000,3623795]]},{"bucket_size":"10 seconds","timeRange":{"min":1677693780000,"max":1677694000000},"metric":{"app":"logstash","field":"logstash_stats.pipelines.queue.max_queue_size_in_bytes","label":"Max Queue Size","description":"Maximum size set for the persistent queues on this node.","units":"B","format":"0,0.0 b","hasCalculation":true,"isDerivative":false},"data":[[1677693780000,1073741824],[1677693790000,1073741824],[1677693800000,1073741824],[1677693810000,1073741824],[1677693820000,1073741824],[1677693830000,1073741824],[1677693840000,1073741824],[1677693850000,1073741824],[1677693860000,1073741824],[1677693870000,1073741824],[1677693880000,1073741824],[1677693890000,1073741824],[1677693900000,1073741824],[1677693910000,1073741824],[1677693920000,1073741824],[1677693930000,1073741824],[1677693940000,1073741824],[1677693950000,1073741824],[1677693960000,1073741824],[1677693970000,1073741824],[1677693980000,1073741824],[1677693990000,1073741824]]}]},"nodeSummary":{"pipeline":{"batch_size":125,"workers":10},"http_address":"0.0.0.0:9600","host":"a868a8283bec","name":"a868a8283bec","ephemeral_id":"5554814a-6ebc-4b82-aa7a-55c8c81162d0","uuid":"5d74947f-b537-4463-abfe-e7488cc04d4a","version":"8.7.0","snapshot":true,"status":"green","availability":false,"events":{"filtered":13621,"in":14303,"duration_in_millis":1176544,"out":13621},"reloads":{"failures":0,"successes":0},"uptime":253309}} diff --git a/x-pack/test/monitoring_api_integration/fixtures/logstash/nodes.json b/x-pack/test/monitoring_api_integration/fixtures/logstash/nodes.json new file mode 100644 index 0000000000000..ed194b6879431 --- /dev/null +++ b/x-pack/test/monitoring_api_integration/fixtures/logstash/nodes.json @@ -0,0 +1 @@ +{"clusterStatus":{"node_count":1,"events_in_total":13703,"events_out_total":13033,"avg_memory":10527703038,"avg_memory_used":146890312,"max_uptime":243296,"pipeline_count":2,"queue_types":{"memory":1,"persisted":1},"versions":["8.7.0"]},"nodes":[{"logstash":{"pipeline":{"batch_size":125,"workers":10},"http_address":"0.0.0.0:9600","name":"a868a8283bec","host":"a868a8283bec","version":"8.7.0","uuid":"5d74947f-b537-4463-abfe-e7488cc04d4a","status":"green"},"jvm":{"mem":{"heap_used_percent":1}},"process":{"cpu":{"percent":0}},"os":{"cpu":{"load_average":{"1m":1.61}}},"events":{"out":13033},"reloads":{"failures":0,"successes":0},"availability":false}]} diff --git a/x-pack/test/monitoring_api_integration/fixtures/logstash/overview.json b/x-pack/test/monitoring_api_integration/fixtures/logstash/overview.json new file mode 100644 index 0000000000000..1478407fd4427 --- /dev/null +++ b/x-pack/test/monitoring_api_integration/fixtures/logstash/overview.json @@ -0,0 +1 @@ +{"metrics":{"logstash_cluster_events_input_rate":[{"bucket_size":"10 seconds","timeRange":{"min":1677693780000,"max":1677694000000},"metric":{"app":"logstash","field":"logstash_stats.events.in","metricAgg":"max","label":"Events Received Rate","description":"Number of events received per second by all Logstash nodes at the inputs stage.","units":"/s","format":"0,0.[00]","hasCalculation":false,"isDerivative":true},"data":[[1677693780000,60.5],[1677693790000,60.1],[1677693800000,59.9],[1677693810000,60],[1677693820000,60],[1677693830000,60],[1677693840000,60],[1677693850000,60],[1677693860000,60],[1677693870000,60],[1677693880000,60.1],[1677693890000,59.9],[1677693900000,60],[1677693910000,60],[1677693920000,60],[1677693930000,60],[1677693940000,60],[1677693950000,60],[1677693960000,60],[1677693970000,60],[1677693980000,60],[1677693990000,60]]}],"logstash_cluster_events_output_rate":[{"bucket_size":"10 seconds","timeRange":{"min":1677693780000,"max":1677694000000},"metric":{"app":"logstash","field":"logstash_stats.events.out","metricAgg":"max","label":"Events Emitted Rate","description":"Number of events emitted per second by all Logstash nodes at the outputs stage.","units":"/s","format":"0,0.[00]","hasCalculation":false,"isDerivative":true},"data":[[1677693780000,37.4],[1677693790000,60.4],[1677693800000,60.1],[1677693810000,59.7],[1677693820000,59.8],[1677693830000,60.4],[1677693840000,59.5],[1677693850000,59.2],[1677693860000,60.7],[1677693870000,60],[1677693880000,60.5],[1677693890000,60.1],[1677693900000,59.9],[1677693910000,59.9],[1677693920000,60.2],[1677693930000,58.6],[1677693940000,59.1],[1677693950000,60.7],[1677693960000,61.5],[1677693970000,60.1],[1677693980000,56.2],[1677693990000,63.9]]}],"logstash_cluster_events_latency":[{"bucket_size":"10 seconds","timeRange":{"min":1677693780000,"max":1677694000000},"metric":{"app":"logstash","field":"logstash_stats.events.out","metricAgg":"max","label":"Event Latency","description":"Average time spent by events in the filter and output stages, which is the total time it takes to process events divided by number of events emitted.","units":"ms","format":"0,0.[00]","hasCalculation":true,"isDerivative":false},"data":[[1677693780000,null],[1677693790000,82.62582781456955],[1677693800000,87.62728785357736],[1677693810000,81.75879396984924],[1677693820000,86.58193979933111],[1677693830000,83.84768211920529],[1677693840000,83.54453781512605],[1677693850000,88.96790540540539],[1677693860000,80.13838550247117],[1677693870000,86.15666666666667],[1677693880000,83.99504132231405],[1677693890000,82.38768718801997],[1677693900000,87.91652754590984],[1677693910000,81.21368948247078],[1677693920000,85.8205980066445],[1677693930000,86.35665529010238],[1677693940000,83.97292724196278],[1677693950000,86.79242174629324],[1677693960000,79.06341463414634],[1677693970000,85.83194675540766],[1677693980000,90.02135231316726],[1677693990000,77.65571205007825]]}]},"clusterStatus":{"node_count":1,"events_in_total":13703,"events_out_total":13033,"avg_memory":10527703038,"avg_memory_used":146890312,"max_uptime":243296,"pipeline_count":2,"queue_types":{"memory":1,"persisted":1},"versions":["8.7.0"]}} diff --git a/x-pack/test/monitoring_api_integration/fixtures/logstash/pipeline.json b/x-pack/test/monitoring_api_integration/fixtures/logstash/pipeline.json new file mode 100644 index 0000000000000..5c243706837cc --- /dev/null +++ b/x-pack/test/monitoring_api_integration/fixtures/logstash/pipeline.json @@ -0,0 +1 @@ +{"versions":[{"hash":"0542fa70daa36dc3e858ea099f125cc8c9e451ebbfe8ea8867e52f9764da0a35","firstSeen":1677693776000,"lastSeen":1677694005749}],"pipeline":{"batch_size":125,"id":"pipeline-with-memory-queue","ephemeral_id":"177bd0a4-27ce-4347-a53e-bd6e509394cc","representation":{"type":"lir","version":"0.0.0","graph":{"vertices":[{"config_name":"java_generator","explicit_id":false,"meta":{"source":{"protocol":"file","line":2,"column":3,"id":"/usr/share/logstash/pipeline/memory-queue.conf"}},"plugin_type":"input","id":"4c5941552cdaa72ebc285557c697a7150c359ee3eacf9b5664c4b1048e26153b","type":"plugin","stats":{"millis_per_event":0,"events_out_per_millisecond":0.07333333333333333}},{"explicit_id":false,"meta":null,"id":"__QUEUE__","type":"queue","stats":{}},{"config_name":"elasticsearch","explicit_id":false,"meta":{"source":{"protocol":"file","line":8,"column":3,"id":"/usr/share/logstash/pipeline/memory-queue.conf"}},"plugin_type":"output","id":"635a080aacc8700059852859da284a9cb92cb78a6d7112fbf55e441e51b6658a","type":"plugin","stats":{"millis_per_event":3.927770300583221,"percent_of_total_processor_duration":0.9972662034400274,"events_out_per_millisecond":0.0743,"events_in_per_millisecond":0.0743}}],"edges":[{"from":"4c5941552cdaa72ebc285557c697a7150c359ee3eacf9b5664c4b1048e26153b","id":"dd5073edf88d34ebbedc42739631a437ac4f2ef073969e0099022a2fa8ab0dc4","to":"__QUEUE__","type":"plain"},{"from":"__QUEUE__","id":"cb9eedba9879b30496083a17bfae14f70695e887742ecd1564ac68861197cff3","to":"635a080aacc8700059852859da284a9cb92cb78a6d7112fbf55e441e51b6658a","type":"plain"}]},"hash":"0542fa70daa36dc3e858ea099f125cc8c9e451ebbfe8ea8867e52f9764da0a35"},"workers":10,"hash":"0542fa70daa36dc3e858ea099f125cc8c9e451ebbfe8ea8867e52f9764da0a35"}} diff --git a/x-pack/test/monitoring_api_integration/fixtures/logstash/pipelines.json b/x-pack/test/monitoring_api_integration/fixtures/logstash/pipelines.json new file mode 100644 index 0000000000000..4675acb4bbc39 --- /dev/null +++ b/x-pack/test/monitoring_api_integration/fixtures/logstash/pipelines.json @@ -0,0 +1 @@ +{"totalPipelineCount":2,"pipelines":[{"id":"pipeline-with-memory-queue","metrics":{"throughput":{"bucket_size":"10 seconds","timeRange":{"min":1677693780000,"max":1677694000000},"metric":{"app":"logstash","field":"logstash_stats.pipelines.events.out","label":"Pipeline Throughput","description":"Number of events emitted per second by the Logstash pipeline at the outputs stage.","units":"e/s","format":"0,0.[00]","hasCalculation":false,"isDerivative":true},"data":[[1677693780000,12.4],[1677693790000,10.4],[1677693800000,10.1],[1677693810000,9.7],[1677693820000,9.8],[1677693830000,10.4],[1677693840000,9.5],[1677693850000,9.2],[1677693860000,10.7],[1677693870000,10],[1677693880000,10.5],[1677693890000,10.1],[1677693900000,9.9],[1677693910000,9.9],[1677693920000,10.2],[1677693930000,8.6],[1677693940000,9.1],[1677693950000,10.7],[1677693960000,11.5],[1677693970000,10.1],[1677693980000,6.2],[1677693990000,13.9]]},"nodesCount":{"bucket_size":"10 seconds","timeRange":{"min":1677693780000,"max":1677694000000},"metric":{"app":"logstash","field":"logstash_stats.logstash.uuid","label":"Pipeline Node Count","description":"Number of nodes on which the Logstash pipeline is running.","units":"","format":"0,0.[00]","hasCalculation":true,"isDerivative":false},"data":[[1677693780000,1],[1677693790000,1],[1677693800000,1],[1677693810000,1],[1677693820000,1],[1677693830000,1],[1677693840000,1],[1677693850000,1],[1677693860000,1],[1677693870000,1],[1677693880000,1],[1677693890000,1],[1677693900000,1],[1677693910000,1],[1677693920000,1],[1677693930000,1],[1677693940000,1],[1677693950000,1],[1677693960000,1],[1677693970000,1],[1677693980000,1],[1677693990000,1]]}},"latestThroughput":13.9,"latestNodesCount":1}],"clusterStatus":{"node_count":1,"events_in_total":13703,"events_out_total":13033,"avg_memory":10527703038,"avg_memory_used":146890312,"max_uptime":243296,"pipeline_count":2,"queue_types":{"memory":1,"persisted":1},"versions":["8.7.0"]}} diff --git a/x-pack/test/monitoring_api_integration/fixtures/logstash/vertex.json b/x-pack/test/monitoring_api_integration/fixtures/logstash/vertex.json new file mode 100644 index 0000000000000..c30cbefbbd303 --- /dev/null +++ b/x-pack/test/monitoring_api_integration/fixtures/logstash/vertex.json @@ -0,0 +1 @@ +{"versions":[{"hash":"0542fa70daa36dc3e858ea099f125cc8c9e451ebbfe8ea8867e52f9764da0a35","firstSeen":1677693776000,"lastSeen":1677694005749}],"pipeline":{"batch_size":125,"id":"pipeline-with-memory-queue","ephemeral_id":"177bd0a4-27ce-4347-a53e-bd6e509394cc","representation":{"type":"lir","version":"0.0.0","graph":{"vertices":[{"config_name":"java_generator","explicit_id":false,"meta":{"source":{"protocol":"file","line":2,"column":3,"id":"/usr/share/logstash/pipeline/memory-queue.conf"}},"plugin_type":"input","id":"4c5941552cdaa72ebc285557c697a7150c359ee3eacf9b5664c4b1048e26153b","type":"plugin","stats":{"millis_per_event":0,"events_out_per_millisecond":0.07333333333333333}},{"explicit_id":false,"meta":null,"id":"__QUEUE__","type":"queue","stats":{}},{"config_name":"elasticsearch","explicit_id":false,"meta":{"source":{"protocol":"file","line":8,"column":3,"id":"/usr/share/logstash/pipeline/memory-queue.conf"}},"plugin_type":"output","id":"635a080aacc8700059852859da284a9cb92cb78a6d7112fbf55e441e51b6658a","type":"plugin","stats":{"millis_per_event":3.927770300583221,"percent_of_total_processor_duration":0.9972662034400274,"events_out_per_millisecond":0.0743,"events_in_per_millisecond":0.0743}}],"edges":[{"from":"4c5941552cdaa72ebc285557c697a7150c359ee3eacf9b5664c4b1048e26153b","id":"dd5073edf88d34ebbedc42739631a437ac4f2ef073969e0099022a2fa8ab0dc4","to":"__QUEUE__","type":"plain"},{"from":"__QUEUE__","id":"cb9eedba9879b30496083a17bfae14f70695e887742ecd1564ac68861197cff3","to":"635a080aacc8700059852859da284a9cb92cb78a6d7112fbf55e441e51b6658a","type":"plain"}]},"hash":"0542fa70daa36dc3e858ea099f125cc8c9e451ebbfe8ea8867e52f9764da0a35"},"workers":10,"hash":"0542fa70daa36dc3e858ea099f125cc8c9e451ebbfe8ea8867e52f9764da0a35"},"vertex":{"config_name":"elasticsearch","explicit_id":false,"meta":{"source":{"protocol":"file","line":8,"column":3,"id":"/usr/share/logstash/pipeline/memory-queue.conf"}},"plugin_type":"output","id":"635a080aacc8700059852859da284a9cb92cb78a6d7112fbf55e441e51b6658a","type":"plugin","stats":{"events_in":{"data":[[1677693780000,205],[1677693810000,202],[1677693840000,199],[1677693870000,206],[1677693900000,201],[1677693930000,198],[1677693960000,163],[1677693990000,0]]},"events_out":{"data":[[1677693780000,205],[1677693810000,202],[1677693840000,199],[1677693870000,206],[1677693900000,201],[1677693930000,198],[1677693960000,163],[1677693990000,0]]},"duration_in_millis":{"data":[[1677693780000,200],[1677693810000,224],[1677693840000,196],[1677693870000,220],[1677693900000,199],[1677693930000,189],[1677693960000,127],[1677693990000,0]]},"millis_per_event":{"data":[[1677693780000,0.975609756097561],[1677693810000,1.108910891089109],[1677693840000,0.9849246231155779],[1677693870000,1.0679611650485437],[1677693900000,0.9900497512437811],[1677693930000,0.9545454545454546],[1677693960000,0.7791411042944786],[1677693990000,null]]},"percent_of_total_processor_duration":{"data":[[1677693780000,0.9900990099009901],[1677693810000,0.9911504424778761],[1677693840000,0.9949238578680203],[1677693870000,0.990990990990991],[1677693900000,0.9851485148514851],[1677693930000,0.9895287958115183],[1677693960000,0.9921875],[1677693990000,null]]},"events_out_per_millisecond":{"data":[[1677693780000,0.006833333333333334],[1677693810000,0.006733333333333333],[1677693840000,0.006633333333333333],[1677693870000,0.006866666666666667],[1677693900000,0.0067],[1677693930000,0.0066],[1677693960000,0.005433333333333333],[1677693990000,0]]},"events_in_per_millisecond":{"data":[[1677693780000,0.006833333333333334],[1677693810000,0.006733333333333333],[1677693840000,0.006633333333333333],[1677693870000,0.006866666666666667],[1677693900000,0.0067],[1677693930000,0.0066],[1677693960000,0.005433333333333333],[1677693990000,0]]}}}} diff --git a/x-pack/test/monitoring_api_integration/fixtures/packages/elasticsearch-1.2.0.zip b/x-pack/test/monitoring_api_integration/fixtures/packages/elasticsearch-1.4.1.zip similarity index 63% rename from x-pack/test/monitoring_api_integration/fixtures/packages/elasticsearch-1.2.0.zip rename to x-pack/test/monitoring_api_integration/fixtures/packages/elasticsearch-1.4.1.zip index 9f48ad17073d4..781cfef46cdc7 100644 Binary files a/x-pack/test/monitoring_api_integration/fixtures/packages/elasticsearch-1.2.0.zip and b/x-pack/test/monitoring_api_integration/fixtures/packages/elasticsearch-1.4.1.zip differ diff --git a/x-pack/test/monitoring_api_integration/fixtures/packages/logstash-2.2.2-preview1.zip b/x-pack/test/monitoring_api_integration/fixtures/packages/logstash-2.2.2-preview1.zip new file mode 100644 index 0000000000000..180e1e2433651 Binary files /dev/null and b/x-pack/test/monitoring_api_integration/fixtures/packages/logstash-2.2.2-preview1.zip differ diff --git a/x-pack/test/monitoring_api_integration/packages.ts b/x-pack/test/monitoring_api_integration/packages.ts index 6eda6d0e94722..31567b39f03e0 100644 --- a/x-pack/test/monitoring_api_integration/packages.ts +++ b/x-pack/test/monitoring_api_integration/packages.ts @@ -9,8 +9,9 @@ import path from 'path'; const PACKAGES = [ { name: 'beat', version: '0.0.1' }, - { name: 'elasticsearch', version: '1.2.0' }, + { name: 'elasticsearch', version: '1.4.1' }, { name: 'enterprisesearch', version: '1.0.0' }, + { name: 'logstash', version: '2.2.2-preview1' }, ]; export const getPackagesArgs = (): string[] => { diff --git a/x-pack/test/plugin_api_integration/plugins/sample_task_plugin/server/plugin.ts b/x-pack/test/plugin_api_integration/plugins/sample_task_plugin/server/plugin.ts index 59cb0ee43d8c1..820d2fc392d5c 100644 --- a/x-pack/test/plugin_api_integration/plugins/sample_task_plugin/server/plugin.ts +++ b/x-pack/test/plugin_api_integration/plugins/sample_task_plugin/server/plugin.ts @@ -5,8 +5,9 @@ * 2.0. */ -import _ from 'lodash'; +import { random } from 'lodash'; import { Plugin, CoreSetup, CoreStart } from '@kbn/core/server'; +import { throwRetryableError } from '@kbn/task-manager-plugin/server/task_running'; import { EventEmitter } from 'events'; import { firstValueFrom, Subject } from 'rxjs'; import { @@ -143,15 +144,13 @@ export class SampleTaskManagerFixturePlugin }, }), }, - sampleOneTimeTaskTimingOut: { - title: 'Sample One-Time Task that Times Out', - description: 'A sample task that times out each run.', + sampleOneTimeTaskThrowingError: { + title: 'Sample One-Time Task that throws an error', + description: 'A sample task that throws an error each run.', maxAttempts: 3, - timeout: '1s', - getRetry: (attempts: number, error: object) => new Date(Date.now() + _.random(2, 5) * 1000), createTaskRunner: () => ({ async run() { - return await new Promise((resolve) => {}); + throwRetryableError(new Error('Error'), new Date(Date.now() + random(2, 5) * 1000)); }, }), }, diff --git a/x-pack/test/plugin_api_integration/test_suites/task_manager/check_registered_task_types.ts b/x-pack/test/plugin_api_integration/test_suites/task_manager/check_registered_task_types.ts index 94173300eeef5..2da99fb2745c8 100644 --- a/x-pack/test/plugin_api_integration/test_suites/task_manager/check_registered_task_types.ts +++ b/x-pack/test/plugin_api_integration/test_suites/task_manager/check_registered_task_types.ts @@ -20,7 +20,7 @@ export default function ({ getService }: FtrProviderContext) { } const TEST_TYPES = [ - 'sampleOneTimeTaskTimingOut', + 'sampleOneTimeTaskThrowingError', 'sampleRecurringTaskTimingOut', 'sampleRecurringTaskWhichHangs', 'sampleTask', @@ -97,6 +97,7 @@ export default function ({ getService }: FtrProviderContext) { 'alerting:siem.queryRule', 'alerting:siem.savedQueryRule', 'alerting:siem.thresholdRule', + 'alerting:slo.rules.burnRate', 'alerting:transform_health', 'alerting:xpack.ml.anomaly_detection_alert', 'alerting:xpack.ml.anomaly_detection_jobs_health', diff --git a/x-pack/test/plugin_api_integration/test_suites/task_manager/task_management.ts b/x-pack/test/plugin_api_integration/test_suites/task_manager/task_management.ts index 36f2742ce1c39..6cc68282ac1da 100644 --- a/x-pack/test/plugin_api_integration/test_suites/task_manager/task_management.ts +++ b/x-pack/test/plugin_api_integration/test_suites/task_manager/task_management.ts @@ -771,7 +771,7 @@ export default function ({ getService }: FtrProviderContext) { it('should mark non-recurring task as failed if task is still running but maxAttempts has been reached', async () => { const task = await scheduleTask({ - taskType: 'sampleOneTimeTaskTimingOut', + taskType: 'sampleOneTimeTaskThrowingError', params: {}, }); diff --git a/x-pack/test/rule_registry/spaces_only/tests/trial/get_summarized_alerts.ts b/x-pack/test/rule_registry/spaces_only/tests/trial/get_summarized_alerts.ts index b62cf7b39965c..3adff40258ca5 100644 --- a/x-pack/test/rule_registry/spaces_only/tests/trial/get_summarized_alerts.ts +++ b/x-pack/test/rule_registry/spaces_only/tests/trial/get_summarized_alerts.ts @@ -80,7 +80,10 @@ export default function createGetSummarizedAlertsTest({ getService }: FtrProvide isWriteEnabled: true, isWriterCacheEnabled: false, disabledRegistrationContexts: [] as string[], - areFrameworkAlertsEnabled: false, + frameworkAlerts: { + enabled: () => false, + getContextInitializationPromise: async () => ({ result: false }), + }, pluginStop$, }); diff --git a/x-pack/test/rule_registry/spaces_only/tests/trial/lifecycle_executor.ts b/x-pack/test/rule_registry/spaces_only/tests/trial/lifecycle_executor.ts index 10351fc6cf2ef..22810df3dd95d 100644 --- a/x-pack/test/rule_registry/spaces_only/tests/trial/lifecycle_executor.ts +++ b/x-pack/test/rule_registry/spaces_only/tests/trial/lifecycle_executor.ts @@ -74,7 +74,10 @@ export default function createLifecycleExecutorApiTest({ getService }: FtrProvid isWriteEnabled: true, isWriterCacheEnabled: false, disabledRegistrationContexts: [] as string[], - areFrameworkAlertsEnabled: false, + frameworkAlerts: { + enabled: () => false, + getContextInitializationPromise: async () => ({ result: false }), + }, pluginStop$, }); diff --git a/yarn.lock b/yarn.lock index 43f2a6b0bc9d1..2a508b5afa1d4 100644 --- a/yarn.lock +++ b/yarn.lock @@ -8005,7 +8005,7 @@ "@types/estree" "*" "@types/json-schema" "*" -"@types/estree@*": +"@types/estree@*", "@types/estree@^0.0.50": version "0.0.50" resolved "https://registry.yarnpkg.com/@types/estree/-/estree-0.0.50.tgz#1e0caa9364d3fccd2931c3ed96fdbeaa5d4cca83" integrity sha512-C6N5s2ZFtuZRj54k2/zyRhNDjJwwcViAM3Nbm8zjBpbqAdZ00mr0CFxvSKeO8Y/e03WVFLpQMdHYVfUd6SB+Hw== @@ -8015,11 +8015,6 @@ resolved "https://registry.yarnpkg.com/@types/estree/-/estree-0.0.51.tgz#cfd70924a25a3fd32b218e5e420e6897e1ac4f40" integrity sha512-CuPgU6f3eT/XgKKPqKd/gLZV1Xmvf1a2R5POBOGQa6uv82xpls89HU5zKeVoyR8XzHd1RGNOlQlvUe3CFkjWNQ== -"@types/estree@^1.0.0": - version "1.0.0" - resolved "https://registry.yarnpkg.com/@types/estree/-/estree-1.0.0.tgz#5fb2e536c1ae9bf35366eed879e827fa59ca41c2" - integrity sha512-WulqXMDUTYAXCjZnk6JtIHPigp55cVtDgDrO2gHRwhyJto21+1zbVCtOYB2L1F9w4qCQ0rOGWBnBe0FNTiEJIQ== - "@types/expect@^1.20.4": version "1.20.4" resolved "https://registry.yarnpkg.com/@types/expect/-/expect-1.20.4.tgz#8288e51737bf7e3ab5d7c77bfa695883745264e5" @@ -13024,10 +13019,10 @@ d3-array@1, d3-array@^1.1.1, d3-array@^1.2.0, d3-array@^1.2.4: resolved "https://registry.yarnpkg.com/d3-array/-/d3-array-1.2.4.tgz#635ce4d5eea759f6f605863dbcfc30edc737f71f" integrity sha512-KHW6M86R+FUPYGb3R5XiYjXPq7VzwxZ22buHhAEVG5ztoEcZZMLov530mmccaqA1GghZArjQV46fuc8kUqhhHw== -"d3-array@1 - 3", "d3-array@2 - 3", "d3-array@2.10.0 - 3", "d3-array@2.5.0 - 3", d3-array@3.2.2, d3-array@^3.2.2: - version "3.2.2" - resolved "https://registry.yarnpkg.com/d3-array/-/d3-array-3.2.2.tgz#f8ac4705c5b06914a7e0025bbf8d5f1513f6a86e" - integrity sha512-yEEyEAbDrF8C6Ob2myOBLjwBLck1Z89jMGFee0oPsn95GqjerpaOA4ch+vc2l0FNFFwMD5N7OCSEN5eAlsUbgQ== +"d3-array@1 - 3", "d3-array@2 - 3", "d3-array@2.10.0 - 3", "d3-array@2.5.0 - 3", d3-array@^3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/d3-array/-/d3-array-3.1.1.tgz#7797eb53ead6b9083c75a45a681e93fc41bc468c" + integrity sha512-33qQ+ZoZlli19IFiQx4QEpf2CBEayMRzhlisJHSCsSUbDXv6ZishqS1x7uFVClKG4Wr7rZVHvaAttoLow6GqdQ== dependencies: internmap "1 - 2" @@ -13071,7 +13066,7 @@ d3-color@1, d3-color@^1.0.3: resolved "https://registry.yarnpkg.com/d3-color/-/d3-color-2.0.0.tgz#8d625cab42ed9b8f601a1760a389f7ea9189d62e" integrity sha512-SPXi0TSKPD4g9tw0NMZFnR95XVgUZiBH+uUTqQuDu1OsE2zomHU7ho0FISciaPvosimixwHFl3WHLGabv6dDgQ== -"d3-color@1 - 3", d3-color@^3.1.0: +"d3-color@1 - 3", d3-color@^3.0.1: version "3.1.0" resolved "https://registry.yarnpkg.com/d3-color/-/d3-color-3.1.0.tgz#395b2833dfac71507f12ac2f7af23bf819de24e2" integrity sha512-zg/chbXyeBtMQ1LbD/WSoW2DpC3I0mpmPdW+ynRTj/x2DAWYrIY7qeZIHidozwV24m4iavr15lNwIwLxRmOxhA== @@ -13159,10 +13154,10 @@ d3-geo-projection@^4.0.0: d3-array "1 - 3" d3-geo "1.12.0 - 3" -"d3-geo@1.12.0 - 3", d3-geo@^3.1.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/d3-geo/-/d3-geo-3.1.0.tgz#74fd54e1f4cebd5185ac2039217a98d39b0a4c0e" - integrity sha512-JEo5HxXDdDYXCaWdwLRt79y7giK8SbhZJbFWXqbRTolCHFI5jRqteLzCsq51NKbUoX0PjBVSohxrx+NoOUujYA== +"d3-geo@1.12.0 - 3", d3-geo@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/d3-geo/-/d3-geo-3.0.1.tgz#4f92362fd8685d93e3b1fae0fd97dc8980b1ed7e" + integrity sha512-Wt23xBych5tSy9IYAM1FR2rWIBFWa52B/oF/GYe5zbdHrg08FU8+BuI6X4PvTwPDdqdAdq04fuWJpELtsaEjeA== dependencies: d3-array "2.5.0 - 3" @@ -13178,10 +13173,10 @@ d3-hierarchy@^1.1.4: resolved "https://registry.yarnpkg.com/d3-hierarchy/-/d3-hierarchy-1.1.9.tgz#2f6bee24caaea43f8dc37545fa01628559647a83" integrity sha512-j8tPxlqh1srJHAtxfvOUwKNYJkQuBFdM1+JAUfq6xqH5eAqf93L7oG1NVqDa4CpFZNvnNKtCYEUC8KY9yEn9lQ== -d3-hierarchy@^3.1.2: - version "3.1.2" - resolved "https://registry.yarnpkg.com/d3-hierarchy/-/d3-hierarchy-3.1.2.tgz#b01cd42c1eed3d46db77a5966cf726f8c09160c6" - integrity sha512-FX/9frcub54beBdugHjDCdikxThEqjnR93Qt7PvQTOHxyiNCAlvMrHhclk3cD5VeAaq9fxmfRp+CnWw9rEMBuA== +d3-hierarchy@^3.1.0: + version "3.1.1" + resolved "https://registry.yarnpkg.com/d3-hierarchy/-/d3-hierarchy-3.1.1.tgz#9cbb0ffd2375137a351e6cfeed344a06d4ff4597" + integrity sha512-LtAIu54UctRmhGKllleflmHalttH3zkfSi4NlKrTAoFKjC+AFBJohsCAdgCBYQwH0F8hIOGY89X1pPqAchlMkA== d3-interpolate@1, d3-interpolate@^1.1.4: version "1.4.0" @@ -13214,10 +13209,10 @@ d3-path@1: resolved "https://registry.yarnpkg.com/d3-path/-/d3-path-2.0.0.tgz#55d86ac131a0548adae241eebfb56b4582dd09d8" integrity sha512-ZwZQxKhBnv9yHaiWd6ZU4x5BtCQ7pXszEV9CU6kRgwIQVQGLMv1oiL4M+MK/n79sYzsj+gcgpPQSctJUsLN7fA== -d3-path@^3.1.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/d3-path/-/d3-path-3.1.0.tgz#22df939032fb5a71ae8b1800d61ddb7851c42526" - integrity sha512-p3KP5HCf/bvjBSSKuXid6Zqijx7wIfNW+J/maPs+iwR35at5JCbLUT0LzF1cnjbCHWhqzQTIN2Jpe8pRebIEFQ== +"d3-path@1 - 3", d3-path@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/d3-path/-/d3-path-3.0.1.tgz#f09dec0aaffd770b7995f1a399152bf93052321e" + integrity sha512-gq6gZom9AFZby0YLduxT1qmrp4xpBA1YZr19OI717WIdKE2OM5ETq5qrHLb301IgxhLwcuxvGZVLeeWc/k1I6w== "d3-quadtree@1 - 3": version "2.0.0" @@ -13287,12 +13282,12 @@ d3-shape@^2.0.0, d3-shape@^2.1.0: dependencies: d3-path "1 - 2" -d3-shape@^3.2.0: - version "3.2.0" - resolved "https://registry.yarnpkg.com/d3-shape/-/d3-shape-3.2.0.tgz#a1a839cbd9ba45f28674c69d7f855bcf91dfc6a5" - integrity sha512-SaLBuwGm3MOViRq2ABk3eLoxwZELpH6zhl3FbAoJ7Vm1gofKx6El1Ib5z23NUEhF9AsGl7y+dzLe5Cw2AArGTA== +d3-shape@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/d3-shape/-/d3-shape-3.1.0.tgz#c8a495652d83ea6f524e482fca57aa3f8bc32556" + integrity sha512-tGDh1Muf8kWjEDT/LswZJ8WF85yDZLvVJpYU9Nq+8+yW1Z5enxrmXOhTArlkaElU+CTn0OTVNli+/i+HP45QEQ== dependencies: - d3-path "^3.1.0" + d3-path "1 - 3" d3-time-format@2: version "2.2.3" @@ -13327,10 +13322,10 @@ d3-time@1: dependencies: d3-array "2" -"d3-time@1 - 3", "d3-time@2.1.1 - 3", d3-time@^3.1.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/d3-time/-/d3-time-3.1.0.tgz#9310db56e992e3c0175e1ef385e545e48a9bb5c7" - integrity sha512-VqKjzBLejbSMT4IgbmVgDjpkYrNWUYJnbCGo874u7MMKIWsILRX+OpX/gTk8MqjpT1A/c6HY2dCA77ZN0lkQ2Q== +"d3-time@1 - 3", "d3-time@2.1.1 - 3", d3-time@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/d3-time/-/d3-time-3.0.0.tgz#65972cb98ae2d4954ef5c932e8704061335d4975" + integrity sha512-zmV3lRnlaLI08y9IMRXSDshQb5Nj77smnfpnd2LrBa/2K281Jijactokeak14QacHs/kKq0AQ121nidNYlarbQ== dependencies: d3-array "2 - 3" @@ -28339,122 +28334,122 @@ vary@~1.1.2: resolved "https://registry.yarnpkg.com/vary/-/vary-1.1.2.tgz#2299f02c6ded30d4a5961b0b9f74524a18f634fc" integrity sha1-IpnwLG3tMNSllhsLn3RSShj2NPw= -vega-canvas@^1.2.6, vega-canvas@^1.2.7: - version "1.2.7" - resolved "https://registry.yarnpkg.com/vega-canvas/-/vega-canvas-1.2.7.tgz#cf62169518f5dcd91d24ad352998c2248f8974fb" - integrity sha512-OkJ9CACVcN9R5Pi9uF6MZBF06pO6qFpDYHWSKBJsdHP5o724KrsgR6UvbnXFH82FdsiTOff/HqjuaG8C7FL+9Q== +vega-canvas@^1.2.5, vega-canvas@^1.2.6: + version "1.2.6" + resolved "https://registry.yarnpkg.com/vega-canvas/-/vega-canvas-1.2.6.tgz#55e032ce9a62acd17229f6bac66d99db3d6879cd" + integrity sha512-rgeYUpslYn/amIfnuv3Sw6n4BGns94OjjZNtUc9IDji6b+K8LGS/kW+Lvay8JX/oFqtulBp8RLcHN6QjqPLA9Q== -vega-crossfilter@~4.1.1: - version "4.1.1" - resolved "https://registry.yarnpkg.com/vega-crossfilter/-/vega-crossfilter-4.1.1.tgz#3ff3ca0574883706f7a399dc6d60f4a0f065ece4" - integrity sha512-yesvlMcwRwxrtAd9IYjuxWJJuAMI0sl7JvAFfYtuDkkGDtqfLXUcCzHIATqW6igVIE7tWwGxnbfvQLhLNgK44Q== +vega-crossfilter@~4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/vega-crossfilter/-/vega-crossfilter-4.1.0.tgz#b6c5a728ce987f2514074adb22cf86b9bc63e0c8" + integrity sha512-aiOJcvVpiEDIu5uNc4Kf1hakkkPaVOO5fw5T4RSFAw6GEDbdqcB6eZ1xePcsLVic1hxYD5SGiUPdiiIs0SMh2g== dependencies: - d3-array "^3.2.2" - vega-dataflow "^5.7.5" - vega-util "^1.17.1" + d3-array "^3.1.1" + vega-dataflow "^5.7.3" + vega-util "^1.15.2" -vega-dataflow@^5.7.3, vega-dataflow@^5.7.5, vega-dataflow@~5.7.5: - version "5.7.5" - resolved "https://registry.yarnpkg.com/vega-dataflow/-/vega-dataflow-5.7.5.tgz#0d559f3c3a968831f2995e099a2e270993ddfed9" - integrity sha512-EdsIl6gouH67+8B0f22Owr2tKDiMPNNR8lEvJDcxmFw02nXd8juimclpLvjPQriqn6ta+3Dn5txqfD117H04YA== +vega-dataflow@^5.7.3, vega-dataflow@^5.7.4, vega-dataflow@~5.7.4: + version "5.7.4" + resolved "https://registry.yarnpkg.com/vega-dataflow/-/vega-dataflow-5.7.4.tgz#7cafc0a41b9d0b11dd2e34a513f8b7ca345dfd74" + integrity sha512-JGHTpUo8XGETH3b1V892we6hdjzCWB977ybycIu8DPqRoyrZuj6t1fCVImazfMgQD1LAfJlQybWP+alwKDpKig== dependencies: - vega-format "^1.1.1" - vega-loader "^4.5.1" - vega-util "^1.17.1" + vega-format "^1.0.4" + vega-loader "^4.3.2" + vega-util "^1.16.1" -vega-encode@~4.9.1: - version "4.9.1" - resolved "https://registry.yarnpkg.com/vega-encode/-/vega-encode-4.9.1.tgz#bad0e99bebec86d42184bcb898576c8accd91e89" - integrity sha512-05JB47UZaqIBS9t6rtHI/aKjEuH4EsSIH+wJWItht4BFj33eIl4XRNtlXdE31uuQT2pXWz5ZWW3KboMuaFzKLw== +vega-encode@~4.9.0: + version "4.9.0" + resolved "https://registry.yarnpkg.com/vega-encode/-/vega-encode-4.9.0.tgz#3dd1031056bb8029f262afc4b4d58372c8f073a6" + integrity sha512-etv2BHuCn9bzEc0cxyA2TnbtcAFQGVFmsaqmB4sgBCaqTSEfXMoX68LK3yxBrsdm5LU+y3otJVoewi3qWYCx2g== dependencies: - d3-array "^3.2.2" + d3-array "^3.1.1" d3-interpolate "^3.0.1" - vega-dataflow "^5.7.5" - vega-scale "^7.3.0" - vega-util "^1.17.1" + vega-dataflow "^5.7.3" + vega-scale "^7.0.3" + vega-util "^1.15.2" -vega-event-selector@^3.0.1, vega-event-selector@~3.0.0, vega-event-selector@~3.0.1: - version "3.0.1" - resolved "https://registry.yarnpkg.com/vega-event-selector/-/vega-event-selector-3.0.1.tgz#b99e92147b338158f8079d81b28b2e7199c2e259" - integrity sha512-K5zd7s5tjr1LiOOkjGpcVls8GsH/f2CWCrWcpKy74gTCp+llCdwz0Enqo013ZlGaRNjfgD/o1caJRt3GSaec4A== +vega-event-selector@^3.0.0, vega-event-selector@~3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/vega-event-selector/-/vega-event-selector-3.0.0.tgz#7b855ac0c3ddb59bc5b5caa0d96dbbc9fbd33a4c" + integrity sha512-Gls93/+7tEJGE3kUuUnxrBIxtvaNeF01VIFB2Q2Of2hBIBvtHX74jcAdDtkh5UhhoYGD8Q1J30P5cqEBEwtPoQ== -vega-expression@^5.0.1, vega-expression@~5.0.0, vega-expression@~5.0.1: - version "5.0.1" - resolved "https://registry.yarnpkg.com/vega-expression/-/vega-expression-5.0.1.tgz#e6a6eff564d2a93496a9bf34cbc78d8942f236a8" - integrity sha512-atfzrMekrcsuyUgZCMklI5ki8cV763aeo1Y6YrfYU7FBwcQEoFhIV/KAJ1vae51aPDGtfzvwbtVIo3WShFCP2Q== +vega-expression@^5.0.0, vega-expression@~5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/vega-expression/-/vega-expression-5.0.0.tgz#938f26689693a1e0d26716030cdaed43ca7abdfb" + integrity sha512-y5+c2frq0tGwJ7vYXzZcfVcIRF/QGfhf2e+bV1Z0iQs+M2lI1II1GPDdmOcMKimpoCVp/D61KUJDIGE1DSmk2w== dependencies: - "@types/estree" "^1.0.0" - vega-util "^1.17.1" + "@types/estree" "^0.0.50" + vega-util "^1.16.0" -vega-force@~4.1.1: - version "4.1.1" - resolved "https://registry.yarnpkg.com/vega-force/-/vega-force-4.1.1.tgz#27bffa96bda293f27d2a2492c2cbf99d49fec77e" - integrity sha512-T6fJAUz9zdXf2qj2Hz0VlmdtaY7eZfcKNazhUV8hza4R3F9ug6r/hSrdovfc9ExmbUjL5iyvDUsf63r8K3/wVQ== +vega-force@~4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/vega-force/-/vega-force-4.1.0.tgz#cc8dea972baa52adc60840ff744ebb9e57d8f1f5" + integrity sha512-Sssf8iH48vYlz+E7/RpU+SUaJbuLoIL87U4tG2Av4gf/hRiImU49x2TI3EuhFWg1zpaCFxlz0CAaX++Oh/gjdw== dependencies: d3-force "^3.0.0" - vega-dataflow "^5.7.5" - vega-util "^1.17.1" + vega-dataflow "^5.7.3" + vega-util "^1.15.2" -vega-format@^1.1.1, vega-format@~1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/vega-format/-/vega-format-1.1.1.tgz#92e4876e18064e7ad54f39045f7b24dede0030b8" - integrity sha512-Rll7YgpYbsgaAa54AmtEWrxaJqgOh5fXlvM2wewO4trb9vwM53KBv4Q/uBWCLK3LLGeBXIF6gjDt2LFuJAUtkQ== +vega-format@^1.0.4, vega-format@^1.1.0, vega-format@~1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/vega-format/-/vega-format-1.1.0.tgz#b9d81cf1bcf222ae5cbd94357ae70245d2c7b18d" + integrity sha512-6mgpeWw8yGdG0Zdi8aVkx5oUrpJGOpNxqazC2858RSDPvChM/jDFlgRMTYw52qk7cxU0L08ARp4BwmXaI75j0w== dependencies: - d3-array "^3.2.2" + d3-array "^3.1.1" d3-format "^3.1.0" d3-time-format "^4.1.0" - vega-time "^2.1.1" - vega-util "^1.17.1" - -vega-functions@^5.13.1, vega-functions@~5.13.1: - version "5.13.1" - resolved "https://registry.yarnpkg.com/vega-functions/-/vega-functions-5.13.1.tgz#504d672924495fe3ea844e6940c7f6e151cde151" - integrity sha512-0LhntimnvBl4VzRO/nkCwCTbtaP8bE552galKQbCg88GDxdmcmlsoTCwUzG0vZ/qmNM3IbqnP5k5/um3zwFqLw== - dependencies: - d3-array "^3.2.2" - d3-color "^3.1.0" - d3-geo "^3.1.0" - vega-dataflow "^5.7.5" - vega-expression "^5.0.1" - vega-scale "^7.3.0" - vega-scenegraph "^4.10.2" - vega-selections "^5.4.1" - vega-statistics "^1.8.1" - vega-time "^2.1.1" - vega-util "^1.17.1" - -vega-geo@~4.4.1: - version "4.4.1" - resolved "https://registry.yarnpkg.com/vega-geo/-/vega-geo-4.4.1.tgz#3850232bf28c98fab5e26c5fb401acb6fb37b5e5" - integrity sha512-s4WeZAL5M3ZUV27/eqSD3v0FyJz3PlP31XNSLFy4AJXHxHUeXT3qLiDHoVQnW5Om+uBCPDtTT1ROx1smGIf2aA== - dependencies: - d3-array "^3.2.2" - d3-color "^3.1.0" - d3-geo "^3.1.0" - vega-canvas "^1.2.7" - vega-dataflow "^5.7.5" - vega-projection "^1.6.0" - vega-statistics "^1.8.1" - vega-util "^1.17.1" - -vega-hierarchy@~4.1.1: - version "4.1.1" - resolved "https://registry.yarnpkg.com/vega-hierarchy/-/vega-hierarchy-4.1.1.tgz#897974a477dfa70cc0d4efab9465b6cc79a9071f" - integrity sha512-h5mbrDtPKHBBQ9TYbvEb/bCqmGTlUX97+4CENkyH21tJs7naza319B15KRK0NWOHuhbGhFmF8T0696tg+2c8XQ== + vega-time "^2.0.3" + vega-util "^1.15.2" + +vega-functions@^5.12.1, vega-functions@^5.13.0, vega-functions@~5.13.0: + version "5.13.0" + resolved "https://registry.yarnpkg.com/vega-functions/-/vega-functions-5.13.0.tgz#c9ab8c6eedbf39f75b424cca6776b1d0b8c74b32" + integrity sha512-Mf53zNyx+c9fFqagEI0T8zc9nMlx0zozOngr8oOpG1tZDKOgwOnUgN99zQKbLHjyv+UzWrq3LYTnSLyVe0ZmhQ== + dependencies: + d3-array "^3.1.1" + d3-color "^3.0.1" + d3-geo "^3.0.1" + vega-dataflow "^5.7.3" + vega-expression "^5.0.0" + vega-scale "^7.2.0" + vega-scenegraph "^4.9.3" + vega-selections "^5.3.1" + vega-statistics "^1.7.9" + vega-time "^2.1.0" + vega-util "^1.16.0" + +vega-geo@~4.4.0: + version "4.4.0" + resolved "https://registry.yarnpkg.com/vega-geo/-/vega-geo-4.4.0.tgz#ce792df57f8ca4c54a7a1a29467cfa24bc53f573" + integrity sha512-3YX41y+J5pu0PMjvBCASg0/lgvu9+QXWJZ+vl6FFKa8AlsIopQ67ZL7ObwqjZcoZMolJ4q0rc+ZO8aj1pXCYcw== dependencies: - d3-hierarchy "^3.1.2" - vega-dataflow "^5.7.5" - vega-util "^1.17.1" + d3-array "^3.1.1" + d3-color "^3.0.1" + d3-geo "^3.0.1" + vega-canvas "^1.2.5" + vega-dataflow "^5.7.3" + vega-projection "^1.4.5" + vega-statistics "^1.7.9" + vega-util "^1.15.2" + +vega-hierarchy@~4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/vega-hierarchy/-/vega-hierarchy-4.1.0.tgz#605edbe3a6232853f9e8b57e3b905471d33b1a48" + integrity sha512-DWBK39IEt4FiQru12twzKSFUvFFZ7KtlH9+lAaqrJnKuIZFCyQ1XOUfKScfbKIlk4KS+DuCTNLI/pxC/f7Sk9Q== + dependencies: + d3-hierarchy "^3.1.0" + vega-dataflow "^5.7.3" + vega-util "^1.15.2" vega-interpreter@^1.0.4: version "1.0.4" resolved "https://registry.yarnpkg.com/vega-interpreter/-/vega-interpreter-1.0.4.tgz#291ebf85bc2d1c3550a3da22ff75b3ba0d326a39" integrity sha512-6tpYIa/pJz0cZo5fSxDSkZkAA51pID2LjOtQkOQvbzn+sJiCaWKPFhur8MBqbcmYZ9bnap1OYNwlrvpd2qBLvg== -vega-label@~1.2.1: - version "1.2.1" - resolved "https://registry.yarnpkg.com/vega-label/-/vega-label-1.2.1.tgz#ea45fa5a407991c44edfea9c4ca40874d544a3db" - integrity sha512-n/ackJ5lc0Xs9PInCaGumYn2awomPjJ87EMVT47xNgk2bHmJoZV1Ve/1PUM6Eh/KauY211wPMrNp/9Im+7Ripg== +vega-label@~1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/vega-label/-/vega-label-1.2.0.tgz#bcb2659aec54f890f9debab3e41ab87a58292dce" + integrity sha512-1prOqkCAfXaUvMqavbGI0nbYGqV8UQR9qvuVwrPJ6Yxm3GIUIOA/JRqNY8eZR8USwMP/kzsqlfVEixj9+Y75VQ== dependencies: vega-canvas "^1.2.6" vega-dataflow "^5.7.3" @@ -28478,112 +28473,110 @@ vega-lite@^5.5.0: vega-util "~1.17.0" yargs "~17.5.1" -vega-loader@^4.5.1, vega-loader@~4.5.1: - version "4.5.1" - resolved "https://registry.yarnpkg.com/vega-loader/-/vega-loader-4.5.1.tgz#b85262b3cb8376487db0c014a8a13c3a5e6d52ad" - integrity sha512-qy5x32SaT0YkEujQM2yKqvLGV9XWQ2aEDSugBFTdYzu/1u4bxdUSRDREOlrJ9Km3RWIOgFiCkobPmFxo47SKuA== +vega-loader@^4.3.2, vega-loader@^4.4.0, vega-loader@~4.5.0: + version "4.5.0" + resolved "https://registry.yarnpkg.com/vega-loader/-/vega-loader-4.5.0.tgz#b15acc4c8f84191f500e94d35ddfb9721ac943ad" + integrity sha512-EkAyzbx0pCYxH3v3wghGVCaKINWxHfgbQ2pYDiYv0yo8e04S8Mv/IlRGTt6BAe7cLhrk1WZ4zh20QOppnGG05w== dependencies: d3-dsv "^3.0.1" node-fetch "^2.6.7" topojson-client "^3.1.0" - vega-format "^1.1.1" - vega-util "^1.17.1" + vega-format "^1.1.0" + vega-util "^1.16.0" -vega-parser@~6.2.0: - version "6.2.0" - resolved "https://registry.yarnpkg.com/vega-parser/-/vega-parser-6.2.0.tgz#c982aff0a6409486cbbe743a5799412b8b897654" - integrity sha512-as+QnX8Qxe9q51L1C2sVBd+YYYctP848+zEvkBT2jlI2g30aZ6Uv7sKsq7QTL6DUbhXQKR0XQtzlanckSFdaOQ== +vega-parser@~6.1.4: + version "6.1.4" + resolved "https://registry.yarnpkg.com/vega-parser/-/vega-parser-6.1.4.tgz#4868e41af2c9645b6d7daeeb205cfad06b9d465c" + integrity sha512-tORdpWXiH/kkXcpNdbSVEvtaxBuuDtgYp9rBunVW9oLsjFvFXbSWlM1wvJ9ZFSaTfx6CqyTyGMiJemmr1QnTjQ== dependencies: - vega-dataflow "^5.7.5" - vega-event-selector "^3.0.1" - vega-functions "^5.13.1" - vega-scale "^7.3.0" - vega-util "^1.17.1" + vega-dataflow "^5.7.3" + vega-event-selector "^3.0.0" + vega-functions "^5.12.1" + vega-scale "^7.1.1" + vega-util "^1.16.0" -vega-projection@^1.6.0, vega-projection@~1.6.0: - version "1.6.0" - resolved "https://registry.yarnpkg.com/vega-projection/-/vega-projection-1.6.0.tgz#921acd3220e7d9d04ccd5ce0109433afb3236966" - integrity sha512-LGUaO/kpOEYuTlul+x+lBzyuL9qmMwP1yShdUWYLW+zXoeyGbs5OZW+NbPPwLYqJr5lpXDr/vGztFuA/6g2xvQ== +vega-projection@^1.4.5, vega-projection@~1.5.0: + version "1.5.0" + resolved "https://registry.yarnpkg.com/vega-projection/-/vega-projection-1.5.0.tgz#51c5f0455170cd35b3c5f3e653e99c3616520640" + integrity sha512-aob7qojh555x3hQWZ/tr8cIJNSWQbm6EoWTJaheZgFOY2x3cDa4Qrg3RJbGw6KwVj/IQk2p40paRzixKZ2kr+A== dependencies: - d3-geo "^3.1.0" + d3-geo "^3.0.1" d3-geo-projection "^4.0.0" - vega-scale "^7.3.0" -vega-regression@~1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/vega-regression/-/vega-regression-1.1.1.tgz#b53a964152a2fec4847e31571f522bfda23089af" - integrity sha512-98i/z0vdDhOIEhJUdYoJ2nlfVdaHVp2CKB39Qa7G/XyRw0+QwDFFrp8ZRec2xHjHfb6bYLGNeh1pOsC13FelJg== +vega-regression@~1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/vega-regression/-/vega-regression-1.1.0.tgz#b4394db403ada93de52bb4536d04be336c983a8c" + integrity sha512-09K0RemY6cdaXBAyakDUNFfEkRcLkGjkDJyWQPAUqGK59hV2J+G3i4uxkZp18Vu0t8oqU7CgzwWim1s5uEpOcA== dependencies: - d3-array "^3.2.2" + d3-array "^3.1.1" vega-dataflow "^5.7.3" vega-statistics "^1.7.9" vega-util "^1.15.2" -vega-runtime@^6.1.4, vega-runtime@~6.1.4: - version "6.1.4" - resolved "https://registry.yarnpkg.com/vega-runtime/-/vega-runtime-6.1.4.tgz#98b67160cea9554e690bfd44719f9d17f90c4220" - integrity sha512-0dDYXyFLQcxPQ2OQU0WuBVYLRZnm+/CwVu6i6N4idS7R9VXIX5581EkCh3pZ20pQ/+oaA7oJ0pR9rJgJ6rukRQ== +vega-runtime@^6.1.3, vega-runtime@~6.1.3: + version "6.1.3" + resolved "https://registry.yarnpkg.com/vega-runtime/-/vega-runtime-6.1.3.tgz#01e18246f7a80cee034a96017ac30113b92c4034" + integrity sha512-gE+sO2IfxMUpV0RkFeQVnHdmPy3K7LjHakISZgUGsDI/ZFs9y+HhBf8KTGSL5pcZPtQsZh3GBQ0UonqL1mp9PA== dependencies: - vega-dataflow "^5.7.5" - vega-util "^1.17.1" + vega-dataflow "^5.7.3" + vega-util "^1.15.2" -vega-scale@^7.3.0, vega-scale@~7.3.0: - version "7.3.0" - resolved "https://registry.yarnpkg.com/vega-scale/-/vega-scale-7.3.0.tgz#02b83435a892c6d91a87ee7d3d350fac987f464b" - integrity sha512-pMOAI2h+e1z7lsqKG+gMfR6NKN2sTcyjZbdJwntooW0uFHwjLGjMSY7kSd3nSEquF0HQ8qF7zR6gs1eRwlGimw== +vega-scale@^7.0.3, vega-scale@^7.1.1, vega-scale@^7.2.0, vega-scale@~7.2.0: + version "7.2.0" + resolved "https://registry.yarnpkg.com/vega-scale/-/vega-scale-7.2.0.tgz#9e298cc02ad340498cb56847436b19439911f0fc" + integrity sha512-QYltO/otrZHLrCGGf06Y99XtPtqWXITr6rw7rO9oL+l3d9o5RFl9sjHrVxiM7v+vGoZVWbBd5IPbFhPsXZ6+TA== dependencies: - d3-array "^3.2.2" + d3-array "^3.1.1" d3-interpolate "^3.0.1" d3-scale "^4.0.2" - vega-time "^2.1.1" - vega-util "^1.17.1" + vega-time "^2.1.0" + vega-util "^1.17.0" -vega-scenegraph@^4.10.2, vega-scenegraph@^4.9.2, vega-scenegraph@~4.10.2: - version "4.10.2" - resolved "https://registry.yarnpkg.com/vega-scenegraph/-/vega-scenegraph-4.10.2.tgz#3ae9ad8e99bbf75e2a4f3ebf2c1f9dee7562d245" - integrity sha512-R8m6voDZO5+etwNMcXf45afVM3XAtokMqxuDyddRl9l1YqSJfS+3u8hpolJ50c2q6ZN20BQiJwKT1o0bB7vKkA== - dependencies: - d3-path "^3.1.0" - d3-shape "^3.2.0" - vega-canvas "^1.2.7" - vega-loader "^4.5.1" - vega-scale "^7.3.0" - vega-util "^1.17.1" +vega-scenegraph@^4.10.0, vega-scenegraph@^4.9.2, vega-scenegraph@^4.9.3, vega-scenegraph@~4.10.1: + version "4.10.1" + resolved "https://registry.yarnpkg.com/vega-scenegraph/-/vega-scenegraph-4.10.1.tgz#944da67b8a28758fab2e1306259fb7ff6be89f6b" + integrity sha512-takIpkmNxYHhJYALOYzhTin3EDzbys6U4g+l1yJZVlXG9YTdiCMuEVAdtaQOCqF9/7qytD6pCrMxJY2HaoN0qQ== + dependencies: + d3-path "^3.0.1" + d3-shape "^3.1.0" + vega-canvas "^1.2.5" + vega-loader "^4.4.0" + vega-scale "^7.2.0" + vega-util "^1.15.2" vega-schema-url-parser@^2.2.0: version "2.2.0" resolved "https://registry.yarnpkg.com/vega-schema-url-parser/-/vega-schema-url-parser-2.2.0.tgz#a0d1e02915adfbfcb1fd517c8c2ebe2419985c1e" integrity sha512-yAtdBnfYOhECv9YC70H2gEiqfIbVkq09aaE4y/9V/ovEFmH9gPKaEgzIZqgT7PSPQjKhsNkb6jk6XvSoboxOBw== -vega-selections@^5.4.1: - version "5.4.1" - resolved "https://registry.yarnpkg.com/vega-selections/-/vega-selections-5.4.1.tgz#3233acb920703bfc323df8b960aa52e55ac08c70" - integrity sha512-EtYc4DvA+wXqBg9tq+kDomSoVUPCmQfS7hUxy2qskXEed79YTimt3Hcl1e1fW226I4AVDBEqTTKebmKMzbSgAA== +vega-selections@^5.3.1: + version "5.3.1" + resolved "https://registry.yarnpkg.com/vega-selections/-/vega-selections-5.3.1.tgz#af5c3cc6532a55a5b692eb0fcc2a1d8d521605a4" + integrity sha512-cm4Srw1WHjcLGXX7GpxiUlfESv8XPu5b6Vh3mqMDPU94P2FO91SR9gei+EtRdt+KCFgIjr//MnRUjg/hAWwjkQ== dependencies: - d3-array "3.2.2" - vega-expression "^5.0.1" - vega-util "^1.17.1" + vega-expression "^5.0.0" + vega-util "^1.16.0" vega-spec-injector@^0.0.2: version "0.0.2" resolved "https://registry.yarnpkg.com/vega-spec-injector/-/vega-spec-injector-0.0.2.tgz#f1d990109dd9d845c524738f818baa4b72a60ca6" integrity sha512-wOMMqmpssn0/ZFPW7wl1v26vbseRX7zHPWzEyS9TwNXTRCu1TcjIBIR+X23lCWocxhoBqFxmqyn8UowMhlGtAg== -vega-statistics@^1.7.9, vega-statistics@^1.8.1, vega-statistics@~1.8.1: - version "1.8.1" - resolved "https://registry.yarnpkg.com/vega-statistics/-/vega-statistics-1.8.1.tgz#596fc3713ac68cc649bf28d0faf7def5ef34fef6" - integrity sha512-eRR3LZBusnTXUkc/uunAvWi1PjCJK+Ba4vFvEISc5Iv5xF4Aw2cBhEz1obEt6ID5fGVCTAl0E1LOSFxubS89hQ== +vega-statistics@^1.7.9, vega-statistics@^1.8.0, vega-statistics@~1.8.0: + version "1.8.0" + resolved "https://registry.yarnpkg.com/vega-statistics/-/vega-statistics-1.8.0.tgz#ad66f7461473d58bc96671588981a059ffd60b59" + integrity sha512-dl+LCRS6qS4jWDme/NEdPVt5r649uB4IK6Kyr2/czmGA5JqjuFmtQ9lHQOnRu8945XLkqLf+JIQQo7vnw+nslA== dependencies: - d3-array "^3.2.2" + d3-array "^3.1.1" -vega-time@^2.1.1, vega-time@~2.1.1: - version "2.1.1" - resolved "https://registry.yarnpkg.com/vega-time/-/vega-time-2.1.1.tgz#0f1fb4e220dd5ed57401b58fb2293241f049ada0" - integrity sha512-z1qbgyX0Af2kQSGFbApwBbX2meenGvsoX8Nga8uyWN8VIbiySo/xqizz1KrP6NbB6R+x5egKmkjdnyNThPeEWA== +vega-time@^2.0.3, vega-time@^2.1.0, vega-time@~2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/vega-time/-/vega-time-2.1.0.tgz#acfbab88d7798b87ff63913b0dce2ca5eb0d46ca" + integrity sha512-Q9/l3S6Br1RPX5HZvyLD/cQ4K6K8DtpR09/1y7D66gxNorg2+HGzYZINH9nUvN3mxoXcBWg4cCUh3+JvmkDaEg== dependencies: - d3-array "^3.2.2" - d3-time "^3.1.0" - vega-util "^1.17.1" + d3-array "^3.1.1" + d3-time "^3.0.0" + vega-util "^1.15.2" vega-tooltip@^0.28.0: version "0.28.0" @@ -28592,107 +28585,106 @@ vega-tooltip@^0.28.0: dependencies: vega-util "^1.17.0" -vega-transforms@~4.10.1: - version "4.10.1" - resolved "https://registry.yarnpkg.com/vega-transforms/-/vega-transforms-4.10.1.tgz#5e51f4f3a745d43609e0d8ba1d74a7e53014030a" - integrity sha512-0uWrUZaYl8kjWrGbvPOQSKk6kcNXQFY9moME+bUmkADAvFptphCGbaEIn/nSsG6uCxj8E3rqKmKfjSWdU5yOqA== +vega-transforms@~4.10.0: + version "4.10.0" + resolved "https://registry.yarnpkg.com/vega-transforms/-/vega-transforms-4.10.0.tgz#a1017ede13cf4e25499f588610a3be4da615d82d" + integrity sha512-Yk6ByzVq5F2niFfPlSsrU5wi+NZhsF7IBpJCcTfms4U7eoyNepUXagdFEJ3VWBD/Lit6GorLXFgO17NYcyS5gg== + dependencies: + d3-array "^3.1.1" + vega-dataflow "^5.7.4" + vega-statistics "^1.8.0" + vega-time "^2.1.0" + vega-util "^1.16.1" + +vega-typings@~0.22.0: + version "0.22.1" + resolved "https://registry.yarnpkg.com/vega-typings/-/vega-typings-0.22.1.tgz#287c646cfa93b1822d0fb6ea11d5543632f8b56e" + integrity sha512-88cIrjmoTxo/0nWTf+GuitkFhirHWVWCfymADiCUXt6s9arpQ6XPP5xjrN5KDc0LZd9xr7p4FIiEgADghgLTgw== + dependencies: + vega-event-selector "^3.0.0" + vega-expression "^5.0.0" + vega-util "^1.15.2" + +vega-util@^1.15.2, vega-util@^1.16.0, vega-util@^1.16.1, vega-util@^1.17.0, vega-util@~1.17.0: + version "1.17.0" + resolved "https://registry.yarnpkg.com/vega-util/-/vega-util-1.17.0.tgz#b72ae0baa97f943bf591f8f5bb27ceadf06834ac" + integrity sha512-HTaydZd9De3yf+8jH66zL4dXJ1d1p5OIFyoBzFiOli4IJbwkL1jrefCKz6AHDm1kYBzDJ0X4bN+CzZSCTvNk1w== + +vega-view-transforms@~4.5.8: + version "4.5.8" + resolved "https://registry.yarnpkg.com/vega-view-transforms/-/vega-view-transforms-4.5.8.tgz#c8dc42c3c7d4aa725d40b8775180c9f23bc98f4e" + integrity sha512-966m7zbzvItBL8rwmF2nKG14rBp7q+3sLCKWeMSUrxoG+M15Smg5gWEGgwTG3A/RwzrZ7rDX5M1sRaAngRH25g== dependencies: - d3-array "^3.2.2" - vega-dataflow "^5.7.5" - vega-statistics "^1.8.1" - vega-time "^2.1.1" - vega-util "^1.17.1" + vega-dataflow "^5.7.3" + vega-scenegraph "^4.9.2" + vega-util "^1.15.2" -vega-typings@~0.23.0: - version "0.23.0" - resolved "https://registry.yarnpkg.com/vega-typings/-/vega-typings-0.23.0.tgz#5b001f5b51a477e67d2446ef9b964e1dac48a20e" - integrity sha512-10ZRRGoUZoQLS5jMiIFhSZMDc3UkPhDP2VMUN/oHZXElvPCGjfjvgmiC6XzvvN4sRXdccMcZX1lZPoyYPERVkA== +vega-view@~5.11.0: + version "5.11.0" + resolved "https://registry.yarnpkg.com/vega-view/-/vega-view-5.11.0.tgz#8a7b29a36776e43cc6599e087ed7f48a918b805d" + integrity sha512-MI9NTRFmtFX6ADk6KOHhi8bhHjC9pPm42Bj2+74c6l1d3NQZf9Jv7lkiGqKohdkQDNH9LPwz/6slhKwPU9JdkQ== dependencies: - "@types/geojson" "^7946.0.10" - vega-event-selector "^3.0.1" - vega-expression "^5.0.1" - vega-util "^1.17.1" - -vega-util@^1.15.2, vega-util@^1.17.0, vega-util@^1.17.1, vega-util@~1.17.0, vega-util@~1.17.1: - version "1.17.1" - resolved "https://registry.yarnpkg.com/vega-util/-/vega-util-1.17.1.tgz#717865fc6b660ceb3ae16273d21166ed471c2db4" - integrity sha512-ToPkWoBdP6awoK+bnYaFhgdqZhsNwKxWbuMnFell+4K/Cb6Q1st5Pi9I7iI5Y6n5ZICDDsd6eL7/IhBjEg1NUQ== - -vega-view-transforms@~4.5.9: - version "4.5.9" - resolved "https://registry.yarnpkg.com/vega-view-transforms/-/vega-view-transforms-4.5.9.tgz#5f109555c08ee9ac23ff9183d578eb9cbac6fe61" - integrity sha512-NxEq4ZD4QwWGRrl2yDLnBRXM9FgCI+vvYb3ZC2+nVDtkUxOlEIKZsMMw31op5GZpfClWLbjCT3mVvzO2xaTF+g== - dependencies: - vega-dataflow "^5.7.5" - vega-scenegraph "^4.10.2" - vega-util "^1.17.1" - -vega-view@~5.11.1: - version "5.11.1" - resolved "https://registry.yarnpkg.com/vega-view/-/vega-view-5.11.1.tgz#a703d7d6344489c6a6e9e9d9c7a732519bf4432c" - integrity sha512-RoWxuoEMI7xVQJhPqNeLEHCezudsf3QkVMhH5tCovBqwBADQGqq9iWyax3ZzdyX1+P3eBgm7cnLvpqtN2hU8kA== - dependencies: - d3-array "^3.2.2" + d3-array "^3.1.1" d3-timer "^3.0.1" - vega-dataflow "^5.7.5" - vega-format "^1.1.1" - vega-functions "^5.13.1" - vega-runtime "^6.1.4" - vega-scenegraph "^4.10.2" - vega-util "^1.17.1" - -vega-voronoi@~4.2.1: - version "4.2.1" - resolved "https://registry.yarnpkg.com/vega-voronoi/-/vega-voronoi-4.2.1.tgz#521a22d3d4c545fe1d5eea19eac0fd3ac5e58b1b" - integrity sha512-zzi+fxU/SBad4irdLLsG3yhZgXWZezraGYVQfZFWe8kl7W/EHUk+Eqk/eetn4bDeJ6ltQskX+UXH3OP5Vh0Q0Q== + vega-dataflow "^5.7.3" + vega-format "^1.1.0" + vega-functions "^5.13.0" + vega-runtime "^6.1.3" + vega-scenegraph "^4.10.0" + vega-util "^1.16.1" + +vega-voronoi@~4.2.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/vega-voronoi/-/vega-voronoi-4.2.0.tgz#14c74c84f52d9a16be2facd1bede879d32d988f2" + integrity sha512-1iuNAVZgUHRlBpdq4gSga3KlQmrgFfwy+KpyDgPLQ8HbLkhcVeT7RDh2L6naluqD7Op0xVLms3clR920WsYryQ== dependencies: d3-delaunay "^6.0.2" - vega-dataflow "^5.7.5" - vega-util "^1.17.1" + vega-dataflow "^5.7.3" + vega-util "^1.15.2" -vega-wordcloud@~4.1.4: - version "4.1.4" - resolved "https://registry.yarnpkg.com/vega-wordcloud/-/vega-wordcloud-4.1.4.tgz#38584cf47ef52325d6a8dc38908b5d2378cc6e62" - integrity sha512-oeZLlnjiusLAU5vhk0IIdT5QEiJE0x6cYoGNq1th+EbwgQp153t4r026fcib9oq15glHFOzf81a8hHXHSJm1Jw== - dependencies: - vega-canvas "^1.2.7" - vega-dataflow "^5.7.5" - vega-scale "^7.3.0" - vega-statistics "^1.8.1" - vega-util "^1.17.1" - -vega@^5.23.0: - version "5.23.0" - resolved "https://registry.yarnpkg.com/vega/-/vega-5.23.0.tgz#7e3899b65f1a84095545b74dcf71289890844c49" - integrity sha512-FjgDD/VmL9yl36ByLq66mEusDF/wZGRktK4JA5MkF68hQqj3F8BFMDDVNwCASuwY97H6wr7kw/RFqNI6XocjJQ== - dependencies: - vega-crossfilter "~4.1.1" - vega-dataflow "~5.7.5" - vega-encode "~4.9.1" - vega-event-selector "~3.0.1" - vega-expression "~5.0.1" - vega-force "~4.1.1" - vega-format "~1.1.1" - vega-functions "~5.13.1" - vega-geo "~4.4.1" - vega-hierarchy "~4.1.1" - vega-label "~1.2.1" - vega-loader "~4.5.1" - vega-parser "~6.2.0" - vega-projection "~1.6.0" - vega-regression "~1.1.1" - vega-runtime "~6.1.4" - vega-scale "~7.3.0" - vega-scenegraph "~4.10.2" - vega-statistics "~1.8.1" - vega-time "~2.1.1" - vega-transforms "~4.10.1" - vega-typings "~0.23.0" - vega-util "~1.17.1" - vega-view "~5.11.1" - vega-view-transforms "~4.5.9" - vega-voronoi "~4.2.1" - vega-wordcloud "~4.1.4" +vega-wordcloud@~4.1.3: + version "4.1.3" + resolved "https://registry.yarnpkg.com/vega-wordcloud/-/vega-wordcloud-4.1.3.tgz#ce90900333f4e0d3ee706ba4f36bb0905f8b4a9f" + integrity sha512-is4zYn9FMAyp9T4SAcz2P/U/wqc0Lx3P5YtpWKCbOH02a05vHjUQrQ2TTPOuvmMfAEDCSKvbMSQIJMOE018lJA== + dependencies: + vega-canvas "^1.2.5" + vega-dataflow "^5.7.3" + vega-scale "^7.1.1" + vega-statistics "^1.7.9" + vega-util "^1.15.2" + +vega@5.22.1: + version "5.22.1" + resolved "https://registry.yarnpkg.com/vega/-/vega-5.22.1.tgz#e028f3645de18e0070317bc04410282975549e1e" + integrity sha512-KJBI7OWSzpfCPbmWl3GQCqBqbf2TIdpWS0mzO6MmWbvdMhWHf74P9IVnx1B1mhg0ZTqWFualx9ZYhWzMMwudaQ== + dependencies: + vega-crossfilter "~4.1.0" + vega-dataflow "~5.7.4" + vega-encode "~4.9.0" + vega-event-selector "~3.0.0" + vega-expression "~5.0.0" + vega-force "~4.1.0" + vega-format "~1.1.0" + vega-functions "~5.13.0" + vega-geo "~4.4.0" + vega-hierarchy "~4.1.0" + vega-label "~1.2.0" + vega-loader "~4.5.0" + vega-parser "~6.1.4" + vega-projection "~1.5.0" + vega-regression "~1.1.0" + vega-runtime "~6.1.3" + vega-scale "~7.2.0" + vega-scenegraph "~4.10.1" + vega-statistics "~1.8.0" + vega-time "~2.1.0" + vega-transforms "~4.10.0" + vega-typings "~0.22.0" + vega-util "~1.17.0" + vega-view "~5.11.0" + vega-view-transforms "~4.5.8" + vega-voronoi "~4.2.0" + vega-wordcloud "~4.1.3" verror@1.10.0: version "1.10.0"