diff --git a/.buildkite/ftr_configs.yml b/.buildkite/ftr_configs.yml index ed9b81dba9667..1c466c001cb97 100644 --- a/.buildkite/ftr_configs.yml +++ b/.buildkite/ftr_configs.yml @@ -43,7 +43,8 @@ disabled: - x-pack/test/threat_intelligence_cypress/visual_config.ts - x-pack/test/threat_intelligence_cypress/cli_config_parallel.ts - x-pack/test/threat_intelligence_cypress/config.ts - - x-pack/test/functional_enterprise_search/with_host_configured.config.ts + - x-pack/test/functional_enterprise_search/visual_config.ts + - x-pack/test/functional_enterprise_search/cli_config.ts - x-pack/plugins/apm/ftr_e2e/ftr_config_open.ts - x-pack/plugins/apm/ftr_e2e/ftr_config_run.ts - x-pack/plugins/apm/ftr_e2e/ftr_config.ts @@ -224,7 +225,6 @@ enabled: - x-pack/test/functional_basic/apps/transform/feature_controls/config.ts - x-pack/test/functional_cors/config.ts - x-pack/test/functional_embedded/config.ts - - x-pack/test/functional_enterprise_search/without_host_configured.config.ts - x-pack/test/functional_execution_context/config.ts - x-pack/test/functional_with_es_ssl/apps/cases/group1/config.ts - x-pack/test/functional_with_es_ssl/apps/cases/group2/config.ts diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 270c2ce5d2b48..63cf2327516fa 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -455,6 +455,7 @@ src/plugins/maps_ems @elastic/kibana-gis x-pack/plugins/maps @elastic/kibana-gis x-pack/packages/ml/agg_utils @elastic/ml-ui x-pack/packages/ml/date_picker @elastic/ml-ui +x-pack/packages/ml/error_utils @elastic/ml-ui x-pack/packages/ml/is_defined @elastic/ml-ui x-pack/packages/ml/is_populated_object @elastic/ml-ui x-pack/packages/ml/local_storage @elastic/ml-ui @@ -683,6 +684,7 @@ src/plugins/unified_search @elastic/kibana-visualizations x-pack/plugins/upgrade_assistant @elastic/platform-deployment-management x-pack/plugins/drilldowns/url_drilldown @elastic/kibana-app-services src/plugins/url_forwarding @elastic/kibana-visualizations +packages/kbn-url-state @elastic/security-threat-hunting-investigations src/plugins/usage_collection @elastic/kibana-core test/plugin_functional/plugins/usage_collection @elastic/kibana-core packages/kbn-user-profile-components @elastic/kibana-security @@ -1058,6 +1060,7 @@ packages/kbn-yarn-lock-validator @elastic/kibana-operations /x-pack/plugins/security_solution/public/cases @elastic/security-threat-hunting-explore /x-pack/plugins/security_solution/public/explore @elastic/security-threat-hunting-explore /x-pack/plugins/security_solution/public/overview @elastic/security-threat-hunting-explore +/x-pack/plugins/security_solution/public/dashboards @elastic/security-threat-hunting-explore /x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/hosts @elastic/security-threat-hunting-explore /x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/matrix_histogram @elastic/security-threat-hunting-explore diff --git a/api_docs/actions.mdx b/api_docs/actions.mdx index 6bfdc25b53149..9f6783794ad2c 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-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'actions'] --- import actionsObj from './actions.devdocs.json'; diff --git a/api_docs/advanced_settings.mdx b/api_docs/advanced_settings.mdx index 4cf73a72168eb..f41313a90546b 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-04-21 +date: 2023-04-24 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 c0fa0ef3e4c02..50cb3ba36668e 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-04-21 +date: 2023-04-24 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 cfec38128269c..264f3c10d43f3 100644 --- a/api_docs/alerting.devdocs.json +++ b/api_docs/alerting.devdocs.json @@ -5261,12 +5261,20 @@ { "parentPluginId": "alerting", "id": "def-common.AlertsFilter.query", - "type": "CompoundType", + "type": "Object", "tags": [], "label": "query", "description": [], "signature": [ - "{ kql: string; dsl?: string | undefined; } | null" + "{ kql: string; filters: ", + { + "pluginId": "@kbn/es-query", + "scope": "common", + "docId": "kibKbnEsQueryPluginApi", + "section": "def-common.Filter", + "text": "Filter" + }, + "[]; dsl?: string | undefined; } | undefined" ], "path": "x-pack/plugins/alerting/common/rule.ts", "deprecated": false, @@ -5275,7 +5283,7 @@ { "parentPluginId": "alerting", "id": "def-common.AlertsFilter.timeframe", - "type": "CompoundType", + "type": "Object", "tags": [], "label": "timeframe", "description": [], @@ -5287,7 +5295,7 @@ "section": "def-common.AlertsFilterTimeframe", "text": "AlertsFilterTimeframe" }, - " | null" + " | undefined" ], "path": "x-pack/plugins/alerting/common/rule.ts", "deprecated": false, @@ -5541,6 +5549,20 @@ "path": "x-pack/plugins/alerting/common/alert_summary.ts", "deprecated": false, "trackAdoption": false + }, + { + "parentPluginId": "alerting", + "id": "def-common.AlertStatus.maintenanceWindowIds", + "type": "Array", + "tags": [], + "label": "maintenanceWindowIds", + "description": [], + "signature": [ + "string[] | undefined" + ], + "path": "x-pack/plugins/alerting/common/alert_summary.ts", + "deprecated": false, + "trackAdoption": false } ], "initialIsOpen": false @@ -5755,6 +5777,17 @@ "path": "x-pack/plugins/alerting/common/alert_summary.ts", "deprecated": false, "trackAdoption": false + }, + { + "parentPluginId": "alerting", + "id": "def-common.AlertSummary.revision", + "type": "number", + "tags": [], + "label": "revision", + "description": [], + "path": "x-pack/plugins/alerting/common/alert_summary.ts", + "deprecated": false, + "trackAdoption": false } ], "initialIsOpen": false @@ -6322,6 +6355,20 @@ "path": "x-pack/plugins/alerting/common/execution_log_types.ts", "deprecated": false, "trackAdoption": false + }, + { + "parentPluginId": "alerting", + "id": "def-common.IExecutionLog.maintenance_window_ids", + "type": "Array", + "tags": [], + "label": "maintenance_window_ids", + "description": [], + "signature": [ + "string[]" + ], + "path": "x-pack/plugins/alerting/common/execution_log_types.ts", + "deprecated": false, + "trackAdoption": false } ], "initialIsOpen": false @@ -8864,12 +8911,20 @@ { "parentPluginId": "alerting", "id": "def-common.SanitizedAlertsFilter.query", - "type": "CompoundType", + "type": "Object", "tags": [], "label": "query", "description": [], "signature": [ - "{ kql: string; } | null" + "{ kql: string; filters: ", + { + "pluginId": "@kbn/es-query", + "scope": "common", + "docId": "kibKbnEsQueryPluginApi", + "section": "def-common.Filter", + "text": "Filter" + }, + "[]; } | undefined" ], "path": "x-pack/plugins/alerting/common/rule.ts", "deprecated": false, @@ -8878,7 +8933,7 @@ { "parentPluginId": "alerting", "id": "def-common.SanitizedAlertsFilter.timeframe", - "type": "CompoundType", + "type": "Object", "tags": [], "label": "timeframe", "description": [], @@ -8890,7 +8945,7 @@ "section": "def-common.AlertsFilterTimeframe", "text": "AlertsFilterTimeframe" }, - " | null" + " | undefined" ], "path": "x-pack/plugins/alerting/common/rule.ts", "deprecated": false, diff --git a/api_docs/alerting.mdx b/api_docs/alerting.mdx index ad9cd7c82bac4..05cd2479d10f4 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-04-21 +date: 2023-04-24 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 | |-------------------|-----------|------------------------|-----------------| -| 603 | 1 | 582 | 42 | +| 606 | 1 | 585 | 42 | ## Client diff --git a/api_docs/apm.devdocs.json b/api_docs/apm.devdocs.json index 93eeed1f13395..f3585cfd924b4 100644 --- a/api_docs/apm.devdocs.json +++ b/api_docs/apm.devdocs.json @@ -4193,6 +4193,8 @@ "AggregationType", ".P99>]>; serviceName: ", "StringC", + "; errorGroupingKey: ", + "StringC", "; transactionType: ", "StringC", "; transactionName: ", @@ -4269,6 +4271,8 @@ "AggregationType", ".P99>]>; serviceName: ", "StringC", + "; errorGroupingKey: ", + "StringC", "; transactionType: ", "StringC", "; transactionName: ", @@ -4343,6 +4347,8 @@ "AggregationType", ".P99>]>; serviceName: ", "StringC", + "; errorGroupingKey: ", + "StringC", "; transactionType: ", "StringC", "; transactionName: ", diff --git a/api_docs/apm.mdx b/api_docs/apm.mdx index a5226b9ec1a9f..39984c4c85e92 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-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'apm'] --- import apmObj from './apm.devdocs.json'; diff --git a/api_docs/asset_manager.mdx b/api_docs/asset_manager.mdx index 5704f9f7b9f81..3fdc2e696e3f0 100644 --- a/api_docs/asset_manager.mdx +++ b/api_docs/asset_manager.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/assetManager title: "assetManager" image: https://source.unsplash.com/400x175/?github description: API docs for the assetManager plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'assetManager'] --- import assetManagerObj from './asset_manager.devdocs.json'; diff --git a/api_docs/banners.mdx b/api_docs/banners.mdx index 5fcf3933483c9..f79d12ad047ef 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-04-21 +date: 2023-04-24 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 d3a45d4595553..fc6dbe517b369 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-04-21 +date: 2023-04-24 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 edb1a65ab929d..bbeae3846322f 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-04-21 +date: 2023-04-24 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 48d5f0ea25d03..15ec086b8eaf2 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-04-21 +date: 2023-04-24 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 e4c1c729146d8..8a55c2821385f 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-04-21 +date: 2023-04-24 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 0ef73b05d8bb0..c09cbda53a15c 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-04-21 +date: 2023-04-24 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 28bf235d4e803..53a1a634088eb 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-04-21 +date: 2023-04-24 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 ea00c024dabf2..cf1dca265856b 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-04-21 +date: 2023-04-24 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 b6680d6d7dbd1..815fae9c2fb42 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-04-21 +date: 2023-04-24 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 08bce01d73062..c56ad03988470 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-04-21 +date: 2023-04-24 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 a88a3ac25809b..7b95ed904f226 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-04-21 +date: 2023-04-24 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 aaa30afdee1ef..2f84a61fdae2e 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-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'console'] --- import consoleObj from './console.devdocs.json'; diff --git a/api_docs/content_management.devdocs.json b/api_docs/content_management.devdocs.json index b1fc3a2404541..7a800426b50ad 100644 --- a/api_docs/content_management.devdocs.json +++ b/api_docs/content_management.devdocs.json @@ -1259,7 +1259,7 @@ "section": "def-server.ContentStorage", "text": "ContentStorage" }, - "" + "" ], "path": "src/plugins/content_management/server/core/types.ts", "deprecated": false, @@ -1773,15 +1773,147 @@ { "parentPluginId": "contentManagement", "id": "def-server.ContentStorage.mSearch", - "type": "Object", + "type": "Uncategorized", "tags": [], "label": "mSearch", "description": [ "\nOpt-in to multi-type search.\nCan only be supported if the content type is backed by a saved object since `mSearch` is using the `savedObjects.find` API." ], "signature": [ - "MSearchConfig", - " | undefined" + "TMSearchConfig | undefined" + ], + "path": "src/plugins/content_management/server/core/types.ts", + "deprecated": false, + "trackAdoption": false + } + ], + "initialIsOpen": false + }, + { + "parentPluginId": "contentManagement", + "id": "def-server.MSearchConfig", + "type": "Interface", + "tags": [], + "label": "MSearchConfig", + "description": [ + "\nA configuration for multi-type search.\nBy configuring a content type with a `MSearchConfig`, it can be searched in the multi-type search.\nUnderneath content management is using the `savedObjects.find` API to search the saved objects." + ], + "signature": [ + { + "pluginId": "contentManagement", + "scope": "server", + "docId": "kibContentManagementPluginApi", + "section": "def-server.MSearchConfig", + "text": "MSearchConfig" + }, + "" + ], + "path": "src/plugins/content_management/server/core/types.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "contentManagement", + "id": "def-server.MSearchConfig.savedObjectType", + "type": "string", + "tags": [], + "label": "savedObjectType", + "description": [ + "\nThe saved object type that corresponds to this content type." + ], + "path": "src/plugins/content_management/server/core/types.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "contentManagement", + "id": "def-server.MSearchConfig.toItemResult", + "type": "Function", + "tags": [], + "label": "toItemResult", + "description": [ + "\nMapper function that transforms the saved object into the content item result." + ], + "signature": [ + "(ctx: ", + { + "pluginId": "contentManagement", + "scope": "server", + "docId": "kibContentManagementPluginApi", + "section": "def-server.StorageContext", + "text": "StorageContext" + }, + ", savedObject: ", + { + "pluginId": "@kbn/core-saved-objects-api-server", + "scope": "common", + "docId": "kibKbnCoreSavedObjectsApiServerPluginApi", + "section": "def-common.SavedObjectsFindResult", + "text": "SavedObjectsFindResult" + }, + ") => T" + ], + "path": "src/plugins/content_management/server/core/types.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "contentManagement", + "id": "def-server.MSearchConfig.toItemResult.$1", + "type": "Object", + "tags": [], + "label": "ctx", + "description": [], + "signature": [ + { + "pluginId": "contentManagement", + "scope": "server", + "docId": "kibContentManagementPluginApi", + "section": "def-server.StorageContext", + "text": "StorageContext" + } + ], + "path": "src/plugins/content_management/server/core/types.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + }, + { + "parentPluginId": "contentManagement", + "id": "def-server.MSearchConfig.toItemResult.$2", + "type": "Object", + "tags": [], + "label": "savedObject", + "description": [], + "signature": [ + { + "pluginId": "@kbn/core-saved-objects-api-server", + "scope": "common", + "docId": "kibKbnCoreSavedObjectsApiServerPluginApi", + "section": "def-common.SavedObjectsFindResult", + "text": "SavedObjectsFindResult" + }, + "" + ], + "path": "src/plugins/content_management/server/core/types.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + } + ], + "returnComment": [] + }, + { + "parentPluginId": "contentManagement", + "id": "def-server.MSearchConfig.additionalSearchFields", + "type": "Array", + "tags": [], + "label": "additionalSearchFields", + "description": [ + "\nAdditional fields to search on. These fields will be added to the search query.\nBy default, only `title` and `description` are searched." + ], + "signature": [ + "string[] | undefined" ], "path": "src/plugins/content_management/server/core/types.ts", "deprecated": false, diff --git a/api_docs/content_management.mdx b/api_docs/content_management.mdx index 1f578689d0a00..21f71777a11e1 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-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'contentManagement'] --- import contentManagementObj from './content_management.devdocs.json'; @@ -21,7 +21,7 @@ Contact [@elastic/appex-sharedux](https://github.com/orgs/elastic/teams/appex-sh | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 143 | 0 | 124 | 7 | +| 149 | 0 | 126 | 6 | ## Client diff --git a/api_docs/controls.mdx b/api_docs/controls.mdx index 9e673b08f099a..c280197a6417a 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-04-21 +date: 2023-04-24 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 5f75f99f25e24..53316962496a5 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-04-21 +date: 2023-04-24 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 eee68e6d1bd7b..ee4d46dc9145f 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-04-21 +date: 2023-04-24 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 707e03a38207b..e79d7c3abd944 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-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'dashboardEnhanced'] --- import dashboardEnhancedObj from './dashboard_enhanced.devdocs.json'; diff --git a/api_docs/data.mdx b/api_docs/data.mdx index f22998f41aaf7..f58d4af3b6e61 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-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'data'] --- import dataObj from './data.devdocs.json'; diff --git a/api_docs/data_query.mdx b/api_docs/data_query.mdx index d08cdb5429fa8..8fd1bc5eca79f 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-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'data.query'] --- import dataQueryObj from './data_query.devdocs.json'; diff --git a/api_docs/data_search.mdx b/api_docs/data_search.mdx index 810eacf23336f..f56c297a354ba 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-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'data.search'] --- import dataSearchObj from './data_search.devdocs.json'; diff --git a/api_docs/data_view_editor.mdx b/api_docs/data_view_editor.mdx index 9c8b060ef7919..776cf231e1e62 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-04-21 +date: 2023-04-24 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 c5097f4dc01c1..4ac05486048f3 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-04-21 +date: 2023-04-24 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 ddb9680520f03..1e27fc11d34a9 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-04-21 +date: 2023-04-24 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 9301c42220b3a..57c1558717fa7 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-04-21 +date: 2023-04-24 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 aca7cffe95904..1a34b399f959c 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-04-21 +date: 2023-04-24 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 f730084172123..b98c2fea8fc27 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-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana'] --- diff --git a/api_docs/deprecations_by_plugin.mdx b/api_docs/deprecations_by_plugin.mdx index cacf63a726fb4..b49d0197cbea0 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-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana'] --- diff --git a/api_docs/deprecations_by_team.mdx b/api_docs/deprecations_by_team.mdx index c210b2af4a6b3..1a29e749e4dc7 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-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana'] --- diff --git a/api_docs/dev_tools.mdx b/api_docs/dev_tools.mdx index 880a19dbd0e91..73de9827ebc22 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-04-21 +date: 2023-04-24 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 f6cb112f22cc5..cde28fb9b20e0 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-04-21 +date: 2023-04-24 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 b3793bce178b0..79aea49ce0239 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-04-21 +date: 2023-04-24 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 fb9dfb81dacae..ec0192478b19a 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-04-21 +date: 2023-04-24 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 1dca0ef9fb7d2..1de1e6f809d6b 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-04-21 +date: 2023-04-24 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 5826149eca609..24c39169fe91b 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-04-21 +date: 2023-04-24 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 bc44d093b0ff4..6004d2b66f179 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-04-21 +date: 2023-04-24 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 9ad8338b79886..68f4430bbeaae 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-04-21 +date: 2023-04-24 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 7b08e077f39e1..9e32f4ae6e75b 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-04-21 +date: 2023-04-24 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 7d9413f9555b1..77ff102f16bcb 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-04-21 +date: 2023-04-24 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 c4bf518f79333..b3075cced41d8 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; uuid?: string | undefined; version?: 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; uuid?: string | undefined; flapping?: boolean | undefined; maintenance_window_ids?: string[] | 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; namespace?: string | undefined; rel?: 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; severity?: string | number | undefined; duration?: string | number | undefined; created?: string | undefined; dataset?: string | undefined; hash?: string | undefined; ingested?: string | undefined; kind?: string | undefined; module?: string | undefined; original?: 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; uuid?: string | undefined; version?: 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; revision?: string | number | 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; uuid?: string | undefined; flapping?: boolean | undefined; maintenance_window_ids?: string[] | 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; namespace?: string | undefined; rel?: 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; severity?: string | number | undefined; duration?: string | number | undefined; created?: string | undefined; dataset?: string | undefined; hash?: string | undefined; ingested?: string | undefined; kind?: string | undefined; module?: string | undefined; original?: 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; uuid?: string | undefined; version?: 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; uuid?: string | undefined; flapping?: boolean | undefined; maintenance_window_ids?: string[] | 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; namespace?: string | undefined; rel?: 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; severity?: string | number | undefined; duration?: string | number | undefined; created?: string | undefined; dataset?: string | undefined; hash?: string | undefined; ingested?: string | undefined; kind?: string | undefined; module?: string | undefined; original?: 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; uuid?: string | undefined; version?: 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; revision?: string | number | 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; uuid?: string | undefined; flapping?: boolean | undefined; maintenance_window_ids?: string[] | 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; namespace?: string | undefined; rel?: 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; severity?: string | number | undefined; duration?: string | number | undefined; created?: string | undefined; dataset?: string | undefined; hash?: string | undefined; ingested?: string | undefined; kind?: string | undefined; module?: string | undefined; original?: 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; uuid?: string | undefined; version?: 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; uuid?: string | undefined; flapping?: boolean | undefined; maintenance_window_ids?: string[] | 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; namespace?: string | undefined; rel?: 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; severity?: string | number | undefined; duration?: string | number | undefined; created?: string | undefined; dataset?: string | undefined; hash?: string | undefined; ingested?: string | undefined; kind?: string | undefined; module?: string | undefined; original?: 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; uuid?: string | undefined; version?: 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; revision?: string | number | 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; uuid?: string | undefined; flapping?: boolean | undefined; maintenance_window_ids?: string[] | 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; namespace?: string | undefined; rel?: 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; severity?: string | number | undefined; duration?: string | number | undefined; created?: string | undefined; dataset?: string | undefined; hash?: string | undefined; ingested?: string | undefined; kind?: string | undefined; module?: string | undefined; original?: 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 bfe5735a5b5aa..5e2e0f6c10292 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-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'eventLog'] --- import eventLogObj from './event_log.devdocs.json'; diff --git a/api_docs/exploratory_view.mdx b/api_docs/exploratory_view.mdx index 2ebfc71ebc060..7315c69e13377 100644 --- a/api_docs/exploratory_view.mdx +++ b/api_docs/exploratory_view.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/exploratoryView title: "exploratoryView" image: https://source.unsplash.com/400x175/?github description: API docs for the exploratoryView plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'exploratoryView'] --- import exploratoryViewObj from './exploratory_view.devdocs.json'; diff --git a/api_docs/expression_error.mdx b/api_docs/expression_error.mdx index 5f952a34778d8..4f23479cfbfc6 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-04-21 +date: 2023-04-24 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 17b1e1a7dd0c3..3c92eace2b8e3 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-04-21 +date: 2023-04-24 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 8e23328ac3938..0a35f4158d179 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-04-21 +date: 2023-04-24 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 2a5739fbf1ce6..92cf80d1ab61a 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-04-21 +date: 2023-04-24 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 e5641b9c5fd0d..62c32bae5f5f1 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-04-21 +date: 2023-04-24 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 b8a570a50ce47..8ccde120fd543 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-04-21 +date: 2023-04-24 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 7ea3ec6b861eb..6432d4714f1e3 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-04-21 +date: 2023-04-24 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 92e6b7076b109..d8a88c5d95960 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-04-21 +date: 2023-04-24 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 5641f62c19926..a238dd8036fd1 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-04-21 +date: 2023-04-24 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 10142b9bf030a..333d1ba0e6264 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-04-21 +date: 2023-04-24 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 26f353bb29f5f..079a9d1bbced9 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-04-21 +date: 2023-04-24 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 4961a72b260d7..8af091771bd3e 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-04-21 +date: 2023-04-24 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 a70dc43040e76..1f6d6a9ba1c68 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-04-21 +date: 2023-04-24 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 823159e766547..631a20f0bcdf6 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-04-21 +date: 2023-04-24 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 711def3633bf5..6dedd249971e1 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-04-21 +date: 2023-04-24 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 0996e6259bf11..fc537a030e52b 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-04-21 +date: 2023-04-24 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 307f54008c991..624bbf01a981d 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-04-21 +date: 2023-04-24 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 b3f4dba8b7273..2e7af1d73120f 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-04-21 +date: 2023-04-24 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 bbf97bb4e042f..49c9a40ff8946 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-04-21 +date: 2023-04-24 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 f1a34342d9ed3..fe95c91406e4c 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-04-21 +date: 2023-04-24 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 e5180ea54c5f9..37e44d7a88a33 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-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'globalSearch'] --- import globalSearchObj from './global_search.devdocs.json'; diff --git a/api_docs/guided_onboarding.devdocs.json b/api_docs/guided_onboarding.devdocs.json index cd8f47f8cf277..a73976c10b794 100644 --- a/api_docs/guided_onboarding.devdocs.json +++ b/api_docs/guided_onboarding.devdocs.json @@ -690,7 +690,15 @@ "section": "def-common.GuideStepIds", "text": "GuideStepIds" }, - ") => Promise<{ pluginState: ", + ", params?: ", + { + "pluginId": "@kbn/guided-onboarding", + "scope": "common", + "docId": "kibKbnGuidedOnboardingPluginApi", + "section": "def-common.GuideParams", + "text": "GuideParams" + }, + " | undefined) => Promise<{ pluginState: ", { "pluginId": "guidedOnboarding", "scope": "common", @@ -745,6 +753,28 @@ "deprecated": false, "trackAdoption": false, "isRequired": true + }, + { + "parentPluginId": "guidedOnboarding", + "id": "def-public.GuidedOnboardingApi.completeGuideStep.$3", + "type": "Object", + "tags": [], + "label": "params", + "description": [], + "signature": [ + { + "pluginId": "@kbn/guided-onboarding", + "scope": "common", + "docId": "kibKbnGuidedOnboardingPluginApi", + "section": "def-common.GuideParams", + "text": "GuideParams" + }, + " | undefined" + ], + "path": "src/plugins/guided_onboarding/public/types.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": false } ], "returnComment": [] diff --git a/api_docs/guided_onboarding.mdx b/api_docs/guided_onboarding.mdx index d01239408c028..475a8a2c05da3 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-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'guidedOnboarding'] --- import guidedOnboardingObj from './guided_onboarding.devdocs.json'; @@ -21,7 +21,7 @@ Contact [@elastic/platform-onboarding](https://github.com/orgs/elastic/teams/pla | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 56 | 0 | 55 | 0 | +| 57 | 0 | 56 | 0 | ## Client diff --git a/api_docs/home.mdx b/api_docs/home.mdx index 9a3c60b620a1a..f62e10db50461 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-04-21 +date: 2023-04-24 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 f46b769442430..3477ece3819e0 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-04-21 +date: 2023-04-24 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 3d5fedb9c28b1..51b3707ff4cda 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-04-21 +date: 2023-04-24 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 c56e4e1ac00e1..7e62154cabf29 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-04-21 +date: 2023-04-24 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 2ef0550b3c7ec..f95130c7fda77 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-04-21 +date: 2023-04-24 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 be420e800114c..627ca307b241f 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-04-21 +date: 2023-04-24 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 77f9f7caf3d13..5794f30eaf867 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-04-21 +date: 2023-04-24 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 9d79fbd636e02..ec79dddfe1fdb 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-04-21 +date: 2023-04-24 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 d38801a007d3a..027622873bb5d 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-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/aiops-components'] --- import kbnAiopsComponentsObj from './kbn_aiops_components.devdocs.json'; diff --git a/api_docs/kbn_aiops_utils.mdx b/api_docs/kbn_aiops_utils.mdx index d5b19befd4e62..3ba110b4d641e 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-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/aiops-utils'] --- import kbnAiopsUtilsObj from './kbn_aiops_utils.devdocs.json'; diff --git a/api_docs/kbn_alerting_state_types.mdx b/api_docs/kbn_alerting_state_types.mdx index 9d8f4dce4e4e6..9209559d4c511 100644 --- a/api_docs/kbn_alerting_state_types.mdx +++ b/api_docs/kbn_alerting_state_types.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-alerting-state-types title: "@kbn/alerting-state-types" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/alerting-state-types plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/alerting-state-types'] --- import kbnAlertingStateTypesObj from './kbn_alerting_state_types.devdocs.json'; diff --git a/api_docs/kbn_alerts.mdx b/api_docs/kbn_alerts.mdx index 496c97c35fa87..fd625f04df24b 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-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/alerts'] --- import kbnAlertsObj from './kbn_alerts.devdocs.json'; diff --git a/api_docs/kbn_alerts_as_data_utils.mdx b/api_docs/kbn_alerts_as_data_utils.mdx index af3a71ef4577b..3529d77abdd16 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-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/alerts-as-data-utils'] --- import kbnAlertsAsDataUtilsObj from './kbn_alerts_as_data_utils.devdocs.json'; diff --git a/api_docs/kbn_alerts_ui_shared.mdx b/api_docs/kbn_alerts_ui_shared.mdx index 73cb3b3c1b270..c39f9c81be753 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-04-21 +date: 2023-04-24 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 c41d203f9bad1..90704b67958d8 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-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/analytics'] --- import kbnAnalyticsObj from './kbn_analytics.devdocs.json'; diff --git a/api_docs/kbn_analytics_client.devdocs.json b/api_docs/kbn_analytics_client.devdocs.json index 82163bbf3b11d..0671e818e1d3f 100644 --- a/api_docs/kbn_analytics_client.devdocs.json +++ b/api_docs/kbn_analytics_client.devdocs.json @@ -762,6 +762,14 @@ "plugin": "securitySolution", "path": "x-pack/plugins/security_solution/public/common/lib/telemetry/telemetry_client.ts" }, + { + "plugin": "securitySolution", + "path": "x-pack/plugins/security_solution/public/common/lib/telemetry/telemetry_client.ts" + }, + { + "plugin": "securitySolution", + "path": "x-pack/plugins/security_solution/public/common/lib/telemetry/telemetry_client.ts" + }, { "plugin": "@kbn/core-analytics-browser-mocks", "path": "packages/core/analytics/core-analytics-browser-mocks/src/analytics_service.mock.ts" diff --git a/api_docs/kbn_analytics_client.mdx b/api_docs/kbn_analytics_client.mdx index cdc8ea6d81beb..5bdc58d85dc6f 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-04-21 +date: 2023-04-24 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 eaf3487e3149d..98e6154aa8a60 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-04-21 +date: 2023-04-24 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 9110cc32f4c31..5cedf9017c0c3 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-04-21 +date: 2023-04-24 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 df8a64c48bd63..1900aafa94eb7 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-04-21 +date: 2023-04-24 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 05865a67ce0b7..506c2ef17b8a8 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-04-21 +date: 2023-04-24 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 f960174f73859..51e1daf9c0a4c 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-04-21 +date: 2023-04-24 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 8900077fd2ace..28663f15a680c 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-04-21 +date: 2023-04-24 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 c8432ebc4205c..1b0583f4267b7 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-04-21 +date: 2023-04-24 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.devdocs.json b/api_docs/kbn_apm_synthtrace_client.devdocs.json index 10dcb002bbdd0..fe2bbd03ef6ab 100644 --- a/api_docs/kbn_apm_synthtrace_client.devdocs.json +++ b/api_docs/kbn_apm_synthtrace_client.devdocs.json @@ -731,7 +731,7 @@ "label": "error", "description": [], "signature": [ - "({ message, type, groupingName, }: { message: string; type?: string | undefined; groupingName?: string | undefined; }) => ", + "({ message, type }: { message: string; type?: string | undefined; }) => ", "ApmError" ], "path": "packages/kbn-apm-synthtrace-client/src/lib/apm/instance.ts", @@ -743,7 +743,7 @@ "id": "def-common.Instance.error.$1", "type": "Object", "tags": [], - "label": "{\n message,\n type,\n groupingName,\n }", + "label": "{ message, type }", "description": [], "path": "packages/kbn-apm-synthtrace-client/src/lib/apm/instance.ts", "deprecated": false, @@ -773,20 +773,6 @@ "path": "packages/kbn-apm-synthtrace-client/src/lib/apm/instance.ts", "deprecated": false, "trackAdoption": false - }, - { - "parentPluginId": "@kbn/apm-synthtrace-client", - "id": "def-common.Instance.error.$1.groupingName", - "type": "string", - "tags": [], - "label": "groupingName", - "description": [], - "signature": [ - "string | undefined" - ], - "path": "packages/kbn-apm-synthtrace-client/src/lib/apm/instance.ts", - "deprecated": false, - "trackAdoption": false } ] } diff --git a/api_docs/kbn_apm_synthtrace_client.mdx b/api_docs/kbn_apm_synthtrace_client.mdx index b76de3fe835c9..080fc9faa8d69 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-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/apm-synthtrace-client'] --- import kbnApmSynthtraceClientObj from './kbn_apm_synthtrace_client.devdocs.json'; @@ -21,7 +21,7 @@ Contact [@elastic/apm-ui](https://github.com/orgs/elastic/teams/apm-ui) for ques | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 154 | 0 | 154 | 17 | +| 153 | 0 | 153 | 17 | ## Common diff --git a/api_docs/kbn_apm_utils.mdx b/api_docs/kbn_apm_utils.mdx index 61e2869b4e841..9509d2d37a5f2 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-04-21 +date: 2023-04-24 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 ac22df442633d..c9bac589f98e1 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-04-21 +date: 2023-04-24 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 738eaa012d294..9609f525002a3 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-04-21 +date: 2023-04-24 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 891ae75c684ea..936150c592f65 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-04-21 +date: 2023-04-24 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 ea357626d903d..55c5bc95f3389 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-04-21 +date: 2023-04-24 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 d47f7c20a984c..81991f4c3596a 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-04-21 +date: 2023-04-24 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 b003b37a45c17..3eeac77d30d96 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-04-21 +date: 2023-04-24 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 6d8ba1ec6ec6d..4210e48cd996c 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-04-21 +date: 2023-04-24 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 c9b004312e1fc..dd1eeae4185e8 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-04-21 +date: 2023-04-24 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 23afc0f33949d..674d7849ea278 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-04-21 +date: 2023-04-24 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 2128bc3924343..2352502cb6168 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-04-21 +date: 2023-04-24 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 2c4aa38ed970a..299b419bc62ee 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-04-21 +date: 2023-04-24 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 1f2f008178f13..d0b05022cb683 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-04-21 +date: 2023-04-24 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 0ce1b008677ff..25ba7ec1f2e1f 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-04-21 +date: 2023-04-24 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 b7e89d00b3240..c9cb8f54bf0b4 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-04-21 +date: 2023-04-24 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 81635d8b9bd51..11c17046984bd 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-04-21 +date: 2023-04-24 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 1288f3330685d..54ab8505a8c85 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-04-21 +date: 2023-04-24 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.devdocs.json b/api_docs/kbn_content_management_table_list.devdocs.json index 937dddb17c4de..8f1e0a68c22ae 100644 --- a/api_docs/kbn_content_management_table_list.devdocs.json +++ b/api_docs/kbn_content_management_table_list.devdocs.json @@ -177,7 +177,7 @@ "CoreStart contract" ], "signature": [ - "{ application: { capabilities: { advancedSettings?: { save: boolean; } | undefined; }; getUrlForApp: (app: string, options: { path: string; }) => string; currentAppId$: ", + "{ application: { capabilities: { [key: string]: Readonly>>; }; getUrlForApp: (app: string, options: { path: string; }) => string; currentAppId$: ", "Observable", "; navigateToUrl: (url: string) => void | Promise; }; notifications: { toasts: { addDanger: (notifyArgs: { title: ", { diff --git a/api_docs/kbn_content_management_table_list.mdx b/api_docs/kbn_content_management_table_list.mdx index 3dfb0e5bff18e..4c8960148995d 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-04-21 +date: 2023-04-24 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 5562e1bf3070f..61fd9c2863537 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-04-21 +date: 2023-04-24 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 d81b99987512d..8a3895b535171 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-04-21 +date: 2023-04-24 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 fb39ec52f5459..685a28bea6242 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-04-21 +date: 2023-04-24 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 5a1af3d3f7d7d..5db1b0e6e16a6 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-04-21 +date: 2023-04-24 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 111fe6d8e5318..4272953c4d763 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-04-21 +date: 2023-04-24 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 d7be4f034346a..e0082181ed949 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-04-21 +date: 2023-04-24 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 b57a29bb0ad0a..d3f1f8cfa0925 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-04-21 +date: 2023-04-24 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 00a6fa0cb815d..6ff37b3089b42 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-04-21 +date: 2023-04-24 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 e1a48c61d3b9b..02a3fd158ec19 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-04-21 +date: 2023-04-24 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 4bad226894e0c..d6a70b2b0157f 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-04-21 +date: 2023-04-24 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 5c042475509cb..d7c038b7a711f 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-04-21 +date: 2023-04-24 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 69f9212722cc9..eb6b4e1963417 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-04-21 +date: 2023-04-24 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 65e3d10226f52..1fcfd9f49e0fe 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-04-21 +date: 2023-04-24 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 86f50bf667604..7d8bb7252a866 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-04-21 +date: 2023-04-24 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 cf75d243d2b43..69cc08dec9190 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-04-21 +date: 2023-04-24 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 16fa31f01e292..eef24003ab660 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-04-21 +date: 2023-04-24 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 4ca1597fcfadc..58aff21c3f633 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-04-21 +date: 2023-04-24 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 a86cc6571c831..14f3d8712b6f3 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-04-21 +date: 2023-04-24 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 027e44a2e349d..d0b5db8e3f5b1 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-04-21 +date: 2023-04-24 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 492519d0fef3a..ced47f174e1bf 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-04-21 +date: 2023-04-24 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 a8ef5c7e24843..236d416f8d3f5 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-04-21 +date: 2023-04-24 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 cf04b0d4146b0..c286a69723583 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-04-21 +date: 2023-04-24 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 c3ba2d14d0f84..c4201d23a52f5 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-04-21 +date: 2023-04-24 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 69c93fed8bdf6..5524f5d6c7f3a 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-04-21 +date: 2023-04-24 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 3447e44fdeb14..c06544f094756 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-04-21 +date: 2023-04-24 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 512e0a4658e11..47db10d316e4b 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-04-21 +date: 2023-04-24 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 57674f1a86f54..74eb25b3e0989 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-04-21 +date: 2023-04-24 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 3271ecbc40835..2852e74f1d272 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-04-21 +date: 2023-04-24 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 947830e94c31a..6b39d83563325 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-04-21 +date: 2023-04-24 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 fed153ea6f806..efcb0ef0d682e 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-04-21 +date: 2023-04-24 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 14c84336e55d8..1b84c3249cedb 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-04-21 +date: 2023-04-24 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 f5dac83c7c859..d9183c76749eb 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-04-21 +date: 2023-04-24 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 bd675bc3f02fb..2dabde1158feb 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-04-21 +date: 2023-04-24 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 41ca6aa3b4e82..1f150624000c0 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-04-21 +date: 2023-04-24 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 5d235b665bccf..f6098418f65b8 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-04-21 +date: 2023-04-24 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 e11a7361be6fe..18f41498f2022 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-04-21 +date: 2023-04-24 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 86023b4693bda..4da7cae29afc1 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-04-21 +date: 2023-04-24 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 1b1996813ec5e..7efaa1d260ede 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-04-21 +date: 2023-04-24 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 014a6cff4fb8f..3a8b4f7733b89 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-04-21 +date: 2023-04-24 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 8a827ee08a3cb..6d30b7f184a66 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-04-21 +date: 2023-04-24 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 cf25c21fc3e7b..7e04c291cf2e5 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-04-21 +date: 2023-04-24 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 d81566d93b87b..bd6d7e5360355 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-04-21 +date: 2023-04-24 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 52c845ba3e159..108a226e77328 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-04-21 +date: 2023-04-24 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 25bdcf097fc8f..36da5cccdf92f 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-04-21 +date: 2023-04-24 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 d3e52e4498e97..a54b680f5d90d 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-04-21 +date: 2023-04-24 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 d7116ffefd9e3..22c91cf01d375 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-04-21 +date: 2023-04-24 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 8763333e3c1ca..50e70544b210b 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-04-21 +date: 2023-04-24 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 c72123128deb8..f538a5f8e0b4e 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-04-21 +date: 2023-04-24 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 2bca835abcc60..8f1461db2e66f 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-04-21 +date: 2023-04-24 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 e0133ab2eceac..77a377a7db9c4 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-04-21 +date: 2023-04-24 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 f4285539090c7..d7dcc38efe471 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-04-21 +date: 2023-04-24 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 e88005161eee4..d1ac9f6597bd6 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-04-21 +date: 2023-04-24 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 9bb759d225d18..4d4c0cdc35fd7 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-04-21 +date: 2023-04-24 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 391aab7e7c207..d81bd38aa1916 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-04-21 +date: 2023-04-24 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 b0a32a23bc8fd..05d5f2ae35765 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-04-21 +date: 2023-04-24 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 d044bcd313597..68eb012274a21 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-04-21 +date: 2023-04-24 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 3185c3568f7cd..4bdeef98bbfde 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-04-21 +date: 2023-04-24 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 6d4e823e86bc7..d94568e3ed0e6 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-04-21 +date: 2023-04-24 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 2379a6b30140a..306f52219527d 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-04-21 +date: 2023-04-24 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 3a8785ce36e04..ab7a2167ee8d3 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-04-21 +date: 2023-04-24 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 cd9f5e1f32fae..3a484f5c5bb94 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-04-21 +date: 2023-04-24 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 030701d5c5535..4c6f719e62bb6 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-04-21 +date: 2023-04-24 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 3041948b6c08e..867ab6116747a 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-04-21 +date: 2023-04-24 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 6c55bb1acc35f..ad72f4f7242bc 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-04-21 +date: 2023-04-24 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 1fc1939592f1d..8bca969b22064 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-04-21 +date: 2023-04-24 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 1ce4c26ce2a2e..408b53256f7db 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-04-21 +date: 2023-04-24 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 c4dd257493cdb..c87dfa9f9fccf 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-04-21 +date: 2023-04-24 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 7baa129a75c92..f165267f56e33 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-04-21 +date: 2023-04-24 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 9115db4edf6cc..e9e7dc64f0942 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-04-21 +date: 2023-04-24 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 152820cbb488b..f0cdaa85851dc 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-04-21 +date: 2023-04-24 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 1dc544d47e781..f38f74d167a98 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-04-21 +date: 2023-04-24 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 12f4523bca3e3..c59347545a328 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-04-21 +date: 2023-04-24 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 290dc43e0f6a4..c38585619f6c0 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-04-21 +date: 2023-04-24 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 1892f31f90652..0fe31a6d87fa1 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-04-21 +date: 2023-04-24 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 8cd6df89cee80..a9a56e81b982d 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-04-21 +date: 2023-04-24 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 71388b8df33ef..ec02bcea6b7f7 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-04-21 +date: 2023-04-24 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 b46f5cadc7bf9..1a2c897736245 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-04-21 +date: 2023-04-24 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 188df6cc9052d..3c162c9189752 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-04-21 +date: 2023-04-24 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 75b8dc9819d5a..494208c4c5914 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-04-21 +date: 2023-04-24 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 a9e58cbc2c4fb..a037d65732f72 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-04-21 +date: 2023-04-24 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 d29ee50a427bf..99067d79f0a9f 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-04-21 +date: 2023-04-24 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 a3f5c62d35e87..534bb78d4eaba 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-04-21 +date: 2023-04-24 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 336ba1167e471..81ac149482e3c 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-04-21 +date: 2023-04-24 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 ead03154da97c..26ba420c08d5c 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-04-21 +date: 2023-04-24 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 70dcc85174431..9ba0f825772fa 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-04-21 +date: 2023-04-24 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 eaaab07d66216..a960a65461e1d 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-04-21 +date: 2023-04-24 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 764140b0a5e6c..3fd09f6263c27 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-04-21 +date: 2023-04-24 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 96a65db189368..f3acbc7fc8045 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-04-21 +date: 2023-04-24 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 7edad6a24183a..c3ea743d26d84 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-04-21 +date: 2023-04-24 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 1bed7b5faea0a..71e8faae9eed4 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-04-21 +date: 2023-04-24 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 f8d4fffbfe021..2eabbc2bacc62 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-04-21 +date: 2023-04-24 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 daf89498a1cfc..f37d52033dd7a 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-04-21 +date: 2023-04-24 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 2faf2f55b3dae..a7217eaf16db7 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-04-21 +date: 2023-04-24 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 9a208c0343c78..59ea42f319e9d 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-04-21 +date: 2023-04-24 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 22a3bada69676..466745e2ce499 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-04-21 +date: 2023-04-24 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 a85a0e176e836..98986e42b5bb0 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-04-21 +date: 2023-04-24 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 0418f4ca39d60..d8b7087087930 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-04-21 +date: 2023-04-24 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 fef44b08e7f61..b45a40d461b52 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-04-21 +date: 2023-04-24 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 5b4c9e57d0c5e..c8536a941c46f 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-04-21 +date: 2023-04-24 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 1c53d4f1151a2..7146b20a88240 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-04-21 +date: 2023-04-24 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 ac5a768548470..2518700bd2a9d 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-04-21 +date: 2023-04-24 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 6538de0675581..38f32ad1bd2c1 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-04-21 +date: 2023-04-24 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 2be250444e13d..d158e2d4ff953 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-04-21 +date: 2023-04-24 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 36a17d070c6e5..e2c79208c0674 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-04-21 +date: 2023-04-24 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 7a0bd26bc2490..8d655eb267f5a 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-04-21 +date: 2023-04-24 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 94f1a573c6e08..77ea3d979a1fa 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-04-21 +date: 2023-04-24 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 20d7ec12378e1..44b22b20623c4 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-04-21 +date: 2023-04-24 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 1186e3108b3e3..c032049f2dd4f 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-04-21 +date: 2023-04-24 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 9e81829a0ef67..08eca2b502206 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-04-21 +date: 2023-04-24 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 920f32db8a28e..971dc60fbd402 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-04-21 +date: 2023-04-24 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 ff75e7da8fd40..f7cb4f618bbf9 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-04-21 +date: 2023-04-24 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 5d8f7df7ac176..91d2f9cd5b3a7 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-04-21 +date: 2023-04-24 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 fc7ce48733faa..e114fbfb6c870 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-04-21 +date: 2023-04-24 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 66f6d3135417b..12234cdb3c57c 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-04-21 +date: 2023-04-24 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.mdx b/api_docs/kbn_core_saved_objects_api_browser.mdx index 814eebd1bcbfa..2ae4ebd0c693b 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-04-21 +date: 2023-04-24 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 4c5f84aee2327..55646f1f382b6 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-04-21 +date: 2023-04-24 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 3f8241f9ab849..165d452af821e 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-04-21 +date: 2023-04-24 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 72ad46898861d..99c35f7983158 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-04-21 +date: 2023-04-24 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.mdx b/api_docs/kbn_core_saved_objects_base_server_internal.mdx index 7e7ccf32b70ab..1978b36015f7e 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-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-saved-objects-base-server-internal'] --- import kbnCoreSavedObjectsBaseServerInternalObj from './kbn_core_saved_objects_base_server_internal.devdocs.json'; 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 76f857265d07b..79c73b9220d0c 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-04-21 +date: 2023-04-24 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 395382f11c5a6..10eb5868ecf47 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-04-21 +date: 2023-04-24 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 e30fc42495448..4615be9c11ce1 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-04-21 +date: 2023-04-24 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 7797116f838b6..a37c978cfb343 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-04-21 +date: 2023-04-24 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.mdx b/api_docs/kbn_core_saved_objects_common.mdx index c7820ecbca374..706710e2cb824 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-04-21 +date: 2023-04-24 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 34d40c1a0e34b..e0122dffcacb0 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-04-21 +date: 2023-04-24 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 95f19589a3e7b..08cc43ae6a54d 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-04-21 +date: 2023-04-24 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.mdx b/api_docs/kbn_core_saved_objects_migration_server_internal.mdx index 1237af41a106a..49dcee745ce38 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-04-21 +date: 2023-04-24 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 2b1be8ab9ccf3..612f215e8d922 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-04-21 +date: 2023-04-24 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 01110761151d4..391665fd14f8c 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-04-21 +date: 2023-04-24 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 c1570d44d6a4e..8f7af4ecd077d 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-04-21 +date: 2023-04-24 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 b4d9023205b5b..0a1e5d1292b0a 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-04-21 +date: 2023-04-24 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 68054fd030ba7..b99ce47f08e8a 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-04-21 +date: 2023-04-24 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 0099c87fe71c1..de9b1e18cae5d 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-04-21 +date: 2023-04-24 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 942ceea0dddb0..301494e7b2388 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-04-21 +date: 2023-04-24 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 b9013fd6ca323..df15953abc315 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-04-21 +date: 2023-04-24 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 e9c7d05fc7a4f..075a12e1c6f24 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-04-21 +date: 2023-04-24 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 0c6549c2c40a8..d47e6f46490f1 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-04-21 +date: 2023-04-24 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 0148b222dc307..a38afc5d4cc28 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-04-21 +date: 2023-04-24 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 5c3b407e8d7e1..ef13c55e07c61 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-04-21 +date: 2023-04-24 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 9f82264d3416a..cf75dcf5478b4 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-04-21 +date: 2023-04-24 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 361d7ee4abaef..f11a869c92ee1 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-04-21 +date: 2023-04-24 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 729bf46fe8b67..484dd22728fbc 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-04-21 +date: 2023-04-24 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 59ca3b555e77c..63cd2de86d6e4 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-04-21 +date: 2023-04-24 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 d8e9d7d490f07..54a121b8df78a 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-04-21 +date: 2023-04-24 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 fcbeaaf620f5b..a21b5fa4ab454 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-04-21 +date: 2023-04-24 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 db1ea4bacbeff..e741c75cc66b0 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-04-21 +date: 2023-04-24 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 4bd232bbc6d3d..92c21046ca2bb 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-04-21 +date: 2023-04-24 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 50362b87de095..9b8babfa1fcf3 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-04-21 +date: 2023-04-24 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 955203aeab79b..b95f699d6d050 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-04-21 +date: 2023-04-24 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 5e5c93b62137f..a88c23c489891 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-04-21 +date: 2023-04-24 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 586bb305927ea..06432816c1788 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-04-21 +date: 2023-04-24 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 8bde395eb141e..e1477ab3eeb87 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-04-21 +date: 2023-04-24 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 ac875acccae8d..914a5468667b5 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-04-21 +date: 2023-04-24 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 e9e8099f4692e..b5596d8ce5bc7 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-04-21 +date: 2023-04-24 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 d98b4e73ab7f0..c5fc08f39d31e 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-04-21 +date: 2023-04-24 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 a367c613df999..ff42ee5669ec3 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-04-21 +date: 2023-04-24 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 0b4c15845ebc7..6af342c88de2c 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-04-21 +date: 2023-04-24 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 e951de4adfb35..845a8d13f6268 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-04-21 +date: 2023-04-24 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 3d864f429b0c3..3b0b33c77b227 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-04-21 +date: 2023-04-24 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 af952748b1e5a..afafe20cb2e1e 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-04-21 +date: 2023-04-24 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 b66e4a59edd0e..4de5774e12693 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-04-21 +date: 2023-04-24 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 74b88642009bd..683f4f5f7821b 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-04-21 +date: 2023-04-24 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 df18c4a7ae5e6..36602e1f31d55 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-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/dev-utils'] --- import kbnDevUtilsObj from './kbn_dev_utils.devdocs.json'; diff --git a/api_docs/kbn_doc_links.mdx b/api_docs/kbn_doc_links.mdx index 237b608f7aa52..3aaf17560db47 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-04-21 +date: 2023-04-24 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 d12f4055fc03a..b1465811e2295 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-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/docs-utils'] --- import kbnDocsUtilsObj from './kbn_docs_utils.devdocs.json'; diff --git a/api_docs/kbn_dom_drag_drop.mdx b/api_docs/kbn_dom_drag_drop.mdx index a9c6b2c6c0a28..ead5751fa35c3 100644 --- a/api_docs/kbn_dom_drag_drop.mdx +++ b/api_docs/kbn_dom_drag_drop.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-dom-drag-drop title: "@kbn/dom-drag-drop" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/dom-drag-drop plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/dom-drag-drop'] --- import kbnDomDragDropObj from './kbn_dom_drag_drop.devdocs.json'; diff --git a/api_docs/kbn_ebt_tools.mdx b/api_docs/kbn_ebt_tools.mdx index f7d904bd7bce7..17263518fb4d1 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-04-21 +date: 2023-04-24 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 a070106c4a4f4..6b880006797c0 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-04-21 +date: 2023-04-24 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 d6933f82a8a56..0b8d0d939e4ae 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-04-21 +date: 2023-04-24 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 b90987c9e70ef..2969a379e7d3c 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-04-21 +date: 2023-04-24 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 9664b87b6910b..eaf29c3077a8b 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-04-21 +date: 2023-04-24 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 8a8c25e5981a8..ab41505be21e9 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-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/es-errors'] --- import kbnEsErrorsObj from './kbn_es_errors.devdocs.json'; diff --git a/api_docs/kbn_es_query.devdocs.json b/api_docs/kbn_es_query.devdocs.json index 4aac051beec95..02a3068d88627 100644 --- a/api_docs/kbn_es_query.devdocs.json +++ b/api_docs/kbn_es_query.devdocs.json @@ -4243,8 +4243,6 @@ "FilterMetaParams", " | undefined; from?: string | number | undefined; to?: string | number | undefined; gt?: string | number | undefined; lt?: string | number | undefined; gte?: string | number | undefined; lte?: string | number | undefined; format?: string | undefined; } | { query: ", "FilterMetaParams", - " | undefined; length: number; toString(): string; toLocaleString(): string; pop(): number | undefined; push(...items: number[]): number; concat(...items: ConcatArray[]): number[]; concat(...items: (number | ConcatArray)[]): number[]; join(separator?: string | undefined): string; reverse(): number[]; shift(): number | undefined; slice(start?: number | undefined, end?: number | undefined): number[]; sort(compareFn?: ((a: number, b: number) => number) | undefined): number[]; splice(start: number, deleteCount?: number | undefined): number[]; splice(start: number, deleteCount: number, ...items: number[]): number[]; unshift(...items: number[]): number; indexOf(searchElement: number, fromIndex?: number | undefined): number; lastIndexOf(searchElement: number, fromIndex?: number | undefined): number; every(predicate: (value: number, index: number, array: number[]) => value is S, thisArg?: any): this is S[]; every(predicate: (value: number, index: number, array: number[]) => unknown, thisArg?: any): boolean; some(predicate: (value: number, index: number, array: number[]) => unknown, thisArg?: any): boolean; forEach(callbackfn: (value: number, index: number, array: number[]) => void, thisArg?: any): void; map(callbackfn: (value: number, index: number, array: number[]) => U, thisArg?: any): U[]; filter(predicate: (value: number, index: number, array: number[]) => value is S, thisArg?: any): S[]; filter(predicate: (value: number, index: number, array: number[]) => unknown, thisArg?: any): number[]; reduce(callbackfn: (previousValue: number, currentValue: number, currentIndex: number, array: number[]) => number): number; reduce(callbackfn: (previousValue: number, currentValue: number, currentIndex: number, array: number[]) => number, initialValue: number): number; reduce(callbackfn: (previousValue: U, currentValue: number, currentIndex: number, array: number[]) => U, initialValue: U): U; reduceRight(callbackfn: (previousValue: number, currentValue: number, currentIndex: number, array: number[]) => number): number; reduceRight(callbackfn: (previousValue: number, currentValue: number, currentIndex: number, array: number[]) => number, initialValue: number): number; reduceRight(callbackfn: (previousValue: U, currentValue: number, currentIndex: number, array: number[]) => U, initialValue: U): U; find(predicate: (this: void, value: number, index: number, obj: number[]) => value is S, thisArg?: any): S | undefined; find(predicate: (value: number, index: number, obj: number[]) => unknown, thisArg?: any): number | undefined; findIndex(predicate: (value: number, index: number, obj: number[]) => unknown, thisArg?: any): number; fill(value: number, start?: number | undefined, end?: number | undefined): number[]; copyWithin(target: number, start: number, end?: number | undefined): number[]; entries(): IterableIterator<[number, number]>; keys(): IterableIterator; values(): IterableIterator; includes(searchElement: number, fromIndex?: number | undefined): boolean; flatMap(callback: (this: This, value: number, index: number, array: number[]) => U | readonly U[], thisArg?: This | undefined): U[]; flat(this: A, depth?: D | undefined): FlatArray[]; [Symbol.iterator](): IterableIterator; [Symbol.unscopables](): { copyWithin: boolean; entries: boolean; fill: boolean; find: boolean; findIndex: boolean; keys: boolean; values: boolean; }; at(index: number): number | undefined; } | { query: ", - "FilterMetaParams", " | undefined; $state?: { store: ", { "pluginId": "@kbn/es-query", @@ -4263,6 +4261,8 @@ }, "; } | { query: ", "FilterMetaParams", + " | undefined; length: number; toString(): string; toLocaleString(): string; pop(): number | undefined; push(...items: number[]): number; concat(...items: ConcatArray[]): number[]; concat(...items: (number | ConcatArray)[]): number[]; join(separator?: string | undefined): string; reverse(): number[]; shift(): number | undefined; slice(start?: number | undefined, end?: number | undefined): number[]; sort(compareFn?: ((a: number, b: number) => number) | undefined): number[]; splice(start: number, deleteCount?: number | undefined): number[]; splice(start: number, deleteCount: number, ...items: number[]): number[]; unshift(...items: number[]): number; indexOf(searchElement: number, fromIndex?: number | undefined): number; lastIndexOf(searchElement: number, fromIndex?: number | undefined): number; every(predicate: (value: number, index: number, array: number[]) => value is S, thisArg?: any): this is S[]; every(predicate: (value: number, index: number, array: number[]) => unknown, thisArg?: any): boolean; some(predicate: (value: number, index: number, array: number[]) => unknown, thisArg?: any): boolean; forEach(callbackfn: (value: number, index: number, array: number[]) => void, thisArg?: any): void; map(callbackfn: (value: number, index: number, array: number[]) => U, thisArg?: any): U[]; filter(predicate: (value: number, index: number, array: number[]) => value is S, thisArg?: any): S[]; filter(predicate: (value: number, index: number, array: number[]) => unknown, thisArg?: any): number[]; reduce(callbackfn: (previousValue: number, currentValue: number, currentIndex: number, array: number[]) => number): number; reduce(callbackfn: (previousValue: number, currentValue: number, currentIndex: number, array: number[]) => number, initialValue: number): number; reduce(callbackfn: (previousValue: U, currentValue: number, currentIndex: number, array: number[]) => U, initialValue: U): U; reduceRight(callbackfn: (previousValue: number, currentValue: number, currentIndex: number, array: number[]) => number): number; reduceRight(callbackfn: (previousValue: number, currentValue: number, currentIndex: number, array: number[]) => number, initialValue: number): number; reduceRight(callbackfn: (previousValue: U, currentValue: number, currentIndex: number, array: number[]) => U, initialValue: U): U; find(predicate: (this: void, value: number, index: number, obj: number[]) => value is S, thisArg?: any): S | undefined; find(predicate: (value: number, index: number, obj: number[]) => unknown, thisArg?: any): number | undefined; findIndex(predicate: (value: number, index: number, obj: number[]) => unknown, thisArg?: any): number; fill(value: number, start?: number | undefined, end?: number | undefined): number[]; copyWithin(target: number, start: number, end?: number | undefined): number[]; entries(): IterableIterator<[number, number]>; keys(): IterableIterator; values(): IterableIterator; includes(searchElement: number, fromIndex?: number | undefined): boolean; flatMap(callback: (this: This, value: number, index: number, array: number[]) => U | readonly U[], thisArg?: This | undefined): U[]; flat(this: A, depth?: D | undefined): FlatArray[]; [Symbol.iterator](): IterableIterator; [Symbol.unscopables](): { copyWithin: boolean; entries: boolean; fill: boolean; find: boolean; findIndex: boolean; keys: boolean; values: boolean; }; at(index: number): number | undefined; } | { query: ", + "FilterMetaParams", " | undefined; alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type: \"range\"; key?: string | undefined; params?: (", "FilterMetaParams", " & ", diff --git a/api_docs/kbn_es_query.mdx b/api_docs/kbn_es_query.mdx index 56f69b7a09446..9dac7b564cfc4 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-04-21 +date: 2023-04-24 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 4de4378862f0f..9d1a75a856f82 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-04-21 +date: 2023-04-24 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 99046ee7a9a8d..cd2e3feaf7128 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-04-21 +date: 2023-04-24 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.devdocs.json b/api_docs/kbn_expandable_flyout.devdocs.json index 1590d2e5ac52d..7fcd69c291a2e 100644 --- a/api_docs/kbn_expandable_flyout.devdocs.json +++ b/api_docs/kbn_expandable_flyout.devdocs.json @@ -80,31 +80,38 @@ "\nWrap your plugin with this context for the ExpandableFlyout React component." ], "signature": [ - "({ children }: ", + "React.ForwardRefExoticComponent<", "ExpandableFlyoutProviderProps", - ") => JSX.Element" + " & React.RefAttributes<", + { + "pluginId": "@kbn/expandable-flyout", + "scope": "common", + "docId": "kibKbnExpandableFlyoutPluginApi", + "section": "def-common.ExpandableFlyoutApi", + "text": "ExpandableFlyoutApi" + }, + ">>" ], "path": "packages/kbn-expandable-flyout/src/context.tsx", "deprecated": false, "trackAdoption": false, + "returnComment": [], "children": [ { "parentPluginId": "@kbn/expandable-flyout", "id": "def-common.ExpandableFlyoutProvider.$1", - "type": "Object", + "type": "Uncategorized", "tags": [], - "label": "{ children }", + "label": "props", "description": [], "signature": [ - "ExpandableFlyoutProviderProps" + "P" ], - "path": "packages/kbn-expandable-flyout/src/context.tsx", + "path": "node_modules/@types/react/index.d.ts", "deprecated": false, - "trackAdoption": false, - "isRequired": true + "trackAdoption": false } ], - "returnComment": [], "initialIsOpen": false }, { @@ -118,7 +125,13 @@ ], "signature": [ "() => ", - "ExpandableFlyoutContext" + { + "pluginId": "@kbn/expandable-flyout", + "scope": "common", + "docId": "kibKbnExpandableFlyoutPluginApi", + "section": "def-common.ExpandableFlyoutContext", + "text": "ExpandableFlyoutContext" + } ], "path": "packages/kbn-expandable-flyout/src/context.tsx", "deprecated": false, @@ -129,6 +142,389 @@ } ], "interfaces": [ + { + "parentPluginId": "@kbn/expandable-flyout", + "id": "def-common.ExpandableFlyoutContext", + "type": "Interface", + "tags": [], + "label": "ExpandableFlyoutContext", + "description": [], + "path": "packages/kbn-expandable-flyout/src/context.tsx", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/expandable-flyout", + "id": "def-common.ExpandableFlyoutContext.panels", + "type": "Object", + "tags": [], + "label": "panels", + "description": [ + "\nRight, left and preview panels" + ], + "signature": [ + "State" + ], + "path": "packages/kbn-expandable-flyout/src/context.tsx", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/expandable-flyout", + "id": "def-common.ExpandableFlyoutContext.openFlyout", + "type": "Function", + "tags": [], + "label": "openFlyout", + "description": [ + "\nOpen the flyout with left, right and/or preview panels" + ], + "signature": [ + "(panels: { left?: ", + { + "pluginId": "@kbn/expandable-flyout", + "scope": "common", + "docId": "kibKbnExpandableFlyoutPluginApi", + "section": "def-common.FlyoutPanel", + "text": "FlyoutPanel" + }, + " | undefined; right?: ", + { + "pluginId": "@kbn/expandable-flyout", + "scope": "common", + "docId": "kibKbnExpandableFlyoutPluginApi", + "section": "def-common.FlyoutPanel", + "text": "FlyoutPanel" + }, + " | undefined; preview?: ", + { + "pluginId": "@kbn/expandable-flyout", + "scope": "common", + "docId": "kibKbnExpandableFlyoutPluginApi", + "section": "def-common.FlyoutPanel", + "text": "FlyoutPanel" + }, + " | undefined; }) => void" + ], + "path": "packages/kbn-expandable-flyout/src/context.tsx", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/expandable-flyout", + "id": "def-common.ExpandableFlyoutContext.openFlyout.$1", + "type": "Object", + "tags": [], + "label": "panels", + "description": [], + "path": "packages/kbn-expandable-flyout/src/context.tsx", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/expandable-flyout", + "id": "def-common.ExpandableFlyoutContext.openFlyout.$1.left", + "type": "Object", + "tags": [], + "label": "left", + "description": [], + "signature": [ + { + "pluginId": "@kbn/expandable-flyout", + "scope": "common", + "docId": "kibKbnExpandableFlyoutPluginApi", + "section": "def-common.FlyoutPanel", + "text": "FlyoutPanel" + }, + " | undefined" + ], + "path": "packages/kbn-expandable-flyout/src/context.tsx", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/expandable-flyout", + "id": "def-common.ExpandableFlyoutContext.openFlyout.$1.right", + "type": "Object", + "tags": [], + "label": "right", + "description": [], + "signature": [ + { + "pluginId": "@kbn/expandable-flyout", + "scope": "common", + "docId": "kibKbnExpandableFlyoutPluginApi", + "section": "def-common.FlyoutPanel", + "text": "FlyoutPanel" + }, + " | undefined" + ], + "path": "packages/kbn-expandable-flyout/src/context.tsx", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/expandable-flyout", + "id": "def-common.ExpandableFlyoutContext.openFlyout.$1.preview", + "type": "Object", + "tags": [], + "label": "preview", + "description": [], + "signature": [ + { + "pluginId": "@kbn/expandable-flyout", + "scope": "common", + "docId": "kibKbnExpandableFlyoutPluginApi", + "section": "def-common.FlyoutPanel", + "text": "FlyoutPanel" + }, + " | undefined" + ], + "path": "packages/kbn-expandable-flyout/src/context.tsx", + "deprecated": false, + "trackAdoption": false + } + ] + } + ], + "returnComment": [] + }, + { + "parentPluginId": "@kbn/expandable-flyout", + "id": "def-common.ExpandableFlyoutContext.openRightPanel", + "type": "Function", + "tags": [], + "label": "openRightPanel", + "description": [ + "\nReplaces the current right panel with a new one" + ], + "signature": [ + "(panel: ", + { + "pluginId": "@kbn/expandable-flyout", + "scope": "common", + "docId": "kibKbnExpandableFlyoutPluginApi", + "section": "def-common.FlyoutPanel", + "text": "FlyoutPanel" + }, + ") => void" + ], + "path": "packages/kbn-expandable-flyout/src/context.tsx", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/expandable-flyout", + "id": "def-common.ExpandableFlyoutContext.openRightPanel.$1", + "type": "Object", + "tags": [], + "label": "panel", + "description": [], + "signature": [ + { + "pluginId": "@kbn/expandable-flyout", + "scope": "common", + "docId": "kibKbnExpandableFlyoutPluginApi", + "section": "def-common.FlyoutPanel", + "text": "FlyoutPanel" + } + ], + "path": "packages/kbn-expandable-flyout/src/context.tsx", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + } + ], + "returnComment": [] + }, + { + "parentPluginId": "@kbn/expandable-flyout", + "id": "def-common.ExpandableFlyoutContext.openLeftPanel", + "type": "Function", + "tags": [], + "label": "openLeftPanel", + "description": [ + "\nReplaces the current left panel with a new one" + ], + "signature": [ + "(panel: ", + { + "pluginId": "@kbn/expandable-flyout", + "scope": "common", + "docId": "kibKbnExpandableFlyoutPluginApi", + "section": "def-common.FlyoutPanel", + "text": "FlyoutPanel" + }, + ") => void" + ], + "path": "packages/kbn-expandable-flyout/src/context.tsx", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/expandable-flyout", + "id": "def-common.ExpandableFlyoutContext.openLeftPanel.$1", + "type": "Object", + "tags": [], + "label": "panel", + "description": [], + "signature": [ + { + "pluginId": "@kbn/expandable-flyout", + "scope": "common", + "docId": "kibKbnExpandableFlyoutPluginApi", + "section": "def-common.FlyoutPanel", + "text": "FlyoutPanel" + } + ], + "path": "packages/kbn-expandable-flyout/src/context.tsx", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + } + ], + "returnComment": [] + }, + { + "parentPluginId": "@kbn/expandable-flyout", + "id": "def-common.ExpandableFlyoutContext.openPreviewPanel", + "type": "Function", + "tags": [], + "label": "openPreviewPanel", + "description": [ + "\nAdd a new preview panel to the list of current preview panels" + ], + "signature": [ + "(panel: ", + { + "pluginId": "@kbn/expandable-flyout", + "scope": "common", + "docId": "kibKbnExpandableFlyoutPluginApi", + "section": "def-common.FlyoutPanel", + "text": "FlyoutPanel" + }, + ") => void" + ], + "path": "packages/kbn-expandable-flyout/src/context.tsx", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/expandable-flyout", + "id": "def-common.ExpandableFlyoutContext.openPreviewPanel.$1", + "type": "Object", + "tags": [], + "label": "panel", + "description": [], + "signature": [ + { + "pluginId": "@kbn/expandable-flyout", + "scope": "common", + "docId": "kibKbnExpandableFlyoutPluginApi", + "section": "def-common.FlyoutPanel", + "text": "FlyoutPanel" + } + ], + "path": "packages/kbn-expandable-flyout/src/context.tsx", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + } + ], + "returnComment": [] + }, + { + "parentPluginId": "@kbn/expandable-flyout", + "id": "def-common.ExpandableFlyoutContext.closeRightPanel", + "type": "Function", + "tags": [], + "label": "closeRightPanel", + "description": [ + "\nCloses right panel" + ], + "signature": [ + "() => void" + ], + "path": "packages/kbn-expandable-flyout/src/context.tsx", + "deprecated": false, + "trackAdoption": false, + "children": [], + "returnComment": [] + }, + { + "parentPluginId": "@kbn/expandable-flyout", + "id": "def-common.ExpandableFlyoutContext.closeLeftPanel", + "type": "Function", + "tags": [], + "label": "closeLeftPanel", + "description": [ + "\nCloses left panel" + ], + "signature": [ + "() => void" + ], + "path": "packages/kbn-expandable-flyout/src/context.tsx", + "deprecated": false, + "trackAdoption": false, + "children": [], + "returnComment": [] + }, + { + "parentPluginId": "@kbn/expandable-flyout", + "id": "def-common.ExpandableFlyoutContext.closePreviewPanel", + "type": "Function", + "tags": [], + "label": "closePreviewPanel", + "description": [ + "\nCloses all preview panels" + ], + "signature": [ + "() => void" + ], + "path": "packages/kbn-expandable-flyout/src/context.tsx", + "deprecated": false, + "trackAdoption": false, + "children": [], + "returnComment": [] + }, + { + "parentPluginId": "@kbn/expandable-flyout", + "id": "def-common.ExpandableFlyoutContext.previousPreviewPanel", + "type": "Function", + "tags": [], + "label": "previousPreviewPanel", + "description": [ + "\nGo back to previous preview panel" + ], + "signature": [ + "() => void" + ], + "path": "packages/kbn-expandable-flyout/src/context.tsx", + "deprecated": false, + "trackAdoption": false, + "children": [], + "returnComment": [] + }, + { + "parentPluginId": "@kbn/expandable-flyout", + "id": "def-common.ExpandableFlyoutContext.closeFlyout", + "type": "Function", + "tags": [], + "label": "closeFlyout", + "description": [ + "\nClose all panels and closes flyout" + ], + "signature": [ + "() => void" + ], + "path": "packages/kbn-expandable-flyout/src/context.tsx", + "deprecated": false, + "trackAdoption": false, + "children": [], + "returnComment": [] + } + ], + "initialIsOpen": false + }, { "parentPluginId": "@kbn/expandable-flyout", "id": "def-common.ExpandableFlyoutProps", @@ -267,7 +663,57 @@ } ], "enums": [], - "misc": [], - "objects": [] + "misc": [ + { + "parentPluginId": "@kbn/expandable-flyout", + "id": "def-common.ExpandableFlyoutApi", + "type": "Type", + "tags": [], + "label": "ExpandableFlyoutApi", + "description": [], + "signature": [ + "Pick<", + { + "pluginId": "@kbn/expandable-flyout", + "scope": "common", + "docId": "kibKbnExpandableFlyoutPluginApi", + "section": "def-common.ExpandableFlyoutContext", + "text": "ExpandableFlyoutContext" + }, + ", \"openFlyout\"> & { getState: () => ", + "State", + "; }" + ], + "path": "packages/kbn-expandable-flyout/src/context.tsx", + "deprecated": false, + "trackAdoption": false, + "initialIsOpen": false + } + ], + "objects": [ + { + "parentPluginId": "@kbn/expandable-flyout", + "id": "def-common.ExpandableFlyoutContext", + "type": "Object", + "tags": [], + "label": "ExpandableFlyoutContext", + "description": [], + "signature": [ + "React.Context<", + { + "pluginId": "@kbn/expandable-flyout", + "scope": "common", + "docId": "kibKbnExpandableFlyoutPluginApi", + "section": "def-common.ExpandableFlyoutContext", + "text": "ExpandableFlyoutContext" + }, + " | undefined>" + ], + "path": "packages/kbn-expandable-flyout/src/context.tsx", + "deprecated": false, + "trackAdoption": false, + "initialIsOpen": false + } + ] } } \ No newline at end of file diff --git a/api_docs/kbn_expandable_flyout.mdx b/api_docs/kbn_expandable_flyout.mdx index 86a0743c821f5..d6f3aa5be6f6b 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-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/expandable-flyout'] --- import kbnExpandableFlyoutObj from './kbn_expandable_flyout.devdocs.json'; @@ -21,13 +21,19 @@ Contact [@elastic/security-threat-hunting-investigations](https://github.com/org | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 13 | 0 | 4 | 3 | +| 33 | 0 | 13 | 3 | ## Common +### Objects + + ### Functions ### Interfaces +### Consts, variables and types + + diff --git a/api_docs/kbn_field_types.mdx b/api_docs/kbn_field_types.mdx index d0b208fb97a8e..4d30f7635f93f 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-04-21 +date: 2023-04-24 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 46afc78d5608e..602d4b76e4b69 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-04-21 +date: 2023-04-24 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 7ae2b45126f93..512b1b3bf1c1c 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-04-21 +date: 2023-04-24 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 7d52e4ad2721e..c0b9ff1ab00b0 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-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/generate'] --- import kbnGenerateObj from './kbn_generate.devdocs.json'; diff --git a/api_docs/kbn_generate_csv.mdx b/api_docs/kbn_generate_csv.mdx index bf0035738a250..3927471f08514 100644 --- a/api_docs/kbn_generate_csv.mdx +++ b/api_docs/kbn_generate_csv.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-generate-csv title: "@kbn/generate-csv" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/generate-csv plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/generate-csv'] --- import kbnGenerateCsvObj from './kbn_generate_csv.devdocs.json'; diff --git a/api_docs/kbn_generate_csv_types.mdx b/api_docs/kbn_generate_csv_types.mdx index 62ba0711eb511..0e35a01c9f842 100644 --- a/api_docs/kbn_generate_csv_types.mdx +++ b/api_docs/kbn_generate_csv_types.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-generate-csv-types title: "@kbn/generate-csv-types" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/generate-csv-types plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/generate-csv-types'] --- import kbnGenerateCsvTypesObj from './kbn_generate_csv_types.devdocs.json'; diff --git a/api_docs/kbn_guided_onboarding.devdocs.json b/api_docs/kbn_guided_onboarding.devdocs.json index ec142f58f5b6c..80777a0b54764 100644 --- a/api_docs/kbn_guided_onboarding.devdocs.json +++ b/api_docs/kbn_guided_onboarding.devdocs.json @@ -271,6 +271,27 @@ "path": "packages/kbn-guided-onboarding/src/types.ts", "deprecated": false, "trackAdoption": false + }, + { + "parentPluginId": "@kbn/guided-onboarding", + "id": "def-common.GuideState.params", + "type": "Object", + "tags": [], + "label": "params", + "description": [], + "signature": [ + { + "pluginId": "@kbn/guided-onboarding", + "scope": "common", + "docId": "kibKbnGuidedOnboardingPluginApi", + "section": "def-common.GuideParams", + "text": "GuideParams" + }, + " | undefined" + ], + "path": "packages/kbn-guided-onboarding/src/types.ts", + "deprecated": false, + "trackAdoption": false } ], "initialIsOpen": false @@ -294,7 +315,7 @@ "label": "id", "description": [], "signature": [ - "\"rules\" | \"add_data\" | \"view_dashboard\" | \"tour_observability\" | \"alertsCases\" | \"search_experience\" | \"step1\" | \"step2\" | \"step3\"" + "\"rules\" | \"add_data\" | \"view_dashboard\" | \"tour_observability\" | \"alertsCases\" | \"search_experience\" | \"step1\" | \"step2\" | \"step3\" | \"step4\"" ], "path": "packages/kbn-guided-onboarding/src/types.ts", "deprecated": false, @@ -336,7 +357,7 @@ "label": "id", "description": [], "signature": [ - "\"rules\" | \"add_data\" | \"view_dashboard\" | \"tour_observability\" | \"alertsCases\" | \"search_experience\" | \"step1\" | \"step2\" | \"step3\"" + "\"rules\" | \"add_data\" | \"view_dashboard\" | \"tour_observability\" | \"alertsCases\" | \"search_experience\" | \"step1\" | \"step2\" | \"step3\" | \"step4\"" ], "path": "packages/kbn-guided-onboarding/src/types.ts", "deprecated": false, @@ -558,6 +579,21 @@ "trackAdoption": false, "initialIsOpen": false }, + { + "parentPluginId": "@kbn/guided-onboarding", + "id": "def-common.GuideParams", + "type": "Type", + "tags": [], + "label": "GuideParams", + "description": [], + "signature": [ + "{ [x: string]: string; }" + ], + "path": "packages/kbn-guided-onboarding/src/types.ts", + "deprecated": false, + "trackAdoption": false, + "initialIsOpen": false + }, { "parentPluginId": "@kbn/guided-onboarding", "id": "def-common.GuideStatus", @@ -583,7 +619,7 @@ "label": "GuideStepIds", "description": [], "signature": [ - "\"rules\" | \"add_data\" | \"view_dashboard\" | \"tour_observability\" | \"alertsCases\" | \"search_experience\" | \"step1\" | \"step2\" | \"step3\"" + "\"rules\" | \"add_data\" | \"view_dashboard\" | \"tour_observability\" | \"alertsCases\" | \"search_experience\" | \"step1\" | \"step2\" | \"step3\" | \"step4\"" ], "path": "packages/kbn-guided-onboarding/src/types.ts", "deprecated": false, @@ -757,7 +793,7 @@ "label": "steps", "description": [], "signature": [ - "({ id: \"step1\"; title: string; descriptionList: string[]; location: { appID: string; path: string; }; integration: string; } | { id: \"step2\"; title: string; descriptionList: (string | { descriptionText: string; linkText: string; linkUrl: string; isLinkExternal: true; })[]; location: { appID: string; path: string; }; manualCompletion: { title: string; description: string; readyToCompleteOnNavigation: true; }; } | { id: \"step3\"; title: string; description: string; manualCompletion: { title: string; description: string; }; location: { appID: string; path: string; }; })[]" + "({ id: \"step1\"; title: string; descriptionList: string[]; location: { appID: string; path: string; }; integration: string; } | { id: \"step2\"; title: string; descriptionList: (string | { descriptionText: string; linkText: string; linkUrl: string; isLinkExternal: true; })[]; location: { appID: string; path: string; }; manualCompletion: { title: string; description: string; readyToCompleteOnNavigation: true; }; } | { id: \"step3\"; title: string; description: string; manualCompletion: { title: string; description: string; }; location: { appID: string; path: string; }; } | { id: \"step4\"; title: string; description: string; location: { appID: string; path: string; }; })[]" ], "path": "packages/kbn-guided-onboarding/src/common/test_guide_config.ts", "deprecated": false, diff --git a/api_docs/kbn_guided_onboarding.mdx b/api_docs/kbn_guided_onboarding.mdx index dd29963efcb66..fa3960f635cfa 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-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/guided-onboarding'] --- import kbnGuidedOnboardingObj from './kbn_guided_onboarding.devdocs.json'; @@ -21,7 +21,7 @@ Contact [@elastic/platform-onboarding](https://github.com/orgs/elastic/teams/pla | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 52 | 0 | 50 | 3 | +| 54 | 0 | 52 | 3 | ## Common diff --git a/api_docs/kbn_handlebars.mdx b/api_docs/kbn_handlebars.mdx index 7f02b89475b3e..52466ad20426f 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-04-21 +date: 2023-04-24 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 4955b1030ea07..cd8245459b82a 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-04-21 +date: 2023-04-24 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 26528fbcb1d05..f6df58ece9340 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-04-21 +date: 2023-04-24 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 ca6f2a8e2ad02..65d82c8aa5eb7 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-04-21 +date: 2023-04-24 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 ade2366ab0e33..139ab20896b0d 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-04-21 +date: 2023-04-24 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 0a6a407c727d5..c5d491f697f42 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-04-21 +date: 2023-04-24 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 7bf9b5d9dd0c8..50c599f41ff0c 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-04-21 +date: 2023-04-24 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 fd1da1af10952..d6377597c1189 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-04-21 +date: 2023-04-24 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 3bfb04e709422..4d82a84859ef6 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-04-21 +date: 2023-04-24 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 503dbab8e3754..d739403a3224c 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-04-21 +date: 2023-04-24 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 dea3262170428..479812a7adc8e 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-04-21 +date: 2023-04-24 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 aa82431ea0e3f..66f07ded66198 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-04-21 +date: 2023-04-24 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 cb2330485201b..9ba905d696133 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-04-21 +date: 2023-04-24 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 7954d741bfcb3..9b3835108da87 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-04-21 +date: 2023-04-24 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 7ceb5757539a4..34e8d2903cb26 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-04-21 +date: 2023-04-24 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 81a30f0763d92..6c2e32868b8c7 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-04-21 +date: 2023-04-24 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 8cd4373bdcf17..c5fa54588432d 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-04-21 +date: 2023-04-24 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 f660720b2133c..a0492d6bad71a 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-04-21 +date: 2023-04-24 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 05c475dfc93c2..2cfeb2a59556c 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-04-21 +date: 2023-04-24 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.mdx b/api_docs/kbn_ml_agg_utils.mdx index d82c025dbab1e..0faf79405aec0 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-04-21 +date: 2023-04-24 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 f92ec1a980b78..6d638b542f37f 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-04-21 +date: 2023-04-24 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_error_utils.devdocs.json b/api_docs/kbn_ml_error_utils.devdocs.json new file mode 100644 index 0000000000000..8872d1df30f9a --- /dev/null +++ b/api_docs/kbn_ml_error_utils.devdocs.json @@ -0,0 +1,800 @@ +{ + "id": "@kbn/ml-error-utils", + "client": { + "classes": [], + "functions": [], + "interfaces": [], + "enums": [], + "misc": [], + "objects": [] + }, + "server": { + "classes": [], + "functions": [], + "interfaces": [], + "enums": [], + "misc": [], + "objects": [] + }, + "common": { + "classes": [ + { + "parentPluginId": "@kbn/ml-error-utils", + "id": "def-common.MLRequestFailure", + "type": "Class", + "tags": [ + "export", + "class", + "typedef", + "extends" + ], + "label": "MLRequestFailure", + "description": [ + "\nML Request Failure\n" + ], + "signature": [ + { + "pluginId": "@kbn/ml-error-utils", + "scope": "common", + "docId": "kibKbnMlErrorUtilsPluginApi", + "section": "def-common.MLRequestFailure", + "text": "MLRequestFailure" + }, + " extends Error" + ], + "path": "x-pack/packages/ml/error_utils/src/request_error.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/ml-error-utils", + "id": "def-common.MLRequestFailure.Unnamed", + "type": "Function", + "tags": [ + "constructor" + ], + "label": "Constructor", + "description": [ + "\nCreates an instance of MLRequestFailure.\n" + ], + "signature": [ + "any" + ], + "path": "x-pack/packages/ml/error_utils/src/request_error.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/ml-error-utils", + "id": "def-common.MLRequestFailure.Unnamed.$1", + "type": "Object", + "tags": [], + "label": "error", + "description": [], + "signature": [ + { + "pluginId": "@kbn/ml-error-utils", + "scope": "common", + "docId": "kibKbnMlErrorUtilsPluginApi", + "section": "def-common.MLErrorObject", + "text": "MLErrorObject" + } + ], + "path": "x-pack/packages/ml/error_utils/src/request_error.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + }, + { + "parentPluginId": "@kbn/ml-error-utils", + "id": "def-common.MLRequestFailure.Unnamed.$2", + "type": "CompoundType", + "tags": [], + "label": "resp", + "description": [], + "signature": [ + { + "pluginId": "@kbn/ml-error-utils", + "scope": "common", + "docId": "kibKbnMlErrorUtilsPluginApi", + "section": "def-common.ErrorType", + "text": "ErrorType" + } + ], + "path": "x-pack/packages/ml/error_utils/src/request_error.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": false + } + ], + "returnComment": [] + } + ], + "initialIsOpen": false + } + ], + "functions": [ + { + "parentPluginId": "@kbn/ml-error-utils", + "id": "def-common.extractErrorMessage", + "type": "Function", + "tags": [], + "label": "extractErrorMessage", + "description": [ + "\nExtract only the error message within the response error\ncoming from Kibana, Elasticsearch, and our own ML messages.\n" + ], + "signature": [ + "(error: ", + { + "pluginId": "@kbn/ml-error-utils", + "scope": "common", + "docId": "kibKbnMlErrorUtilsPluginApi", + "section": "def-common.ErrorType", + "text": "ErrorType" + }, + ") => string" + ], + "path": "x-pack/packages/ml/error_utils/src/process_errors.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/ml-error-utils", + "id": "def-common.extractErrorMessage.$1", + "type": "CompoundType", + "tags": [], + "label": "error", + "description": [], + "signature": [ + { + "pluginId": "@kbn/ml-error-utils", + "scope": "common", + "docId": "kibKbnMlErrorUtilsPluginApi", + "section": "def-common.ErrorType", + "text": "ErrorType" + } + ], + "path": "x-pack/packages/ml/error_utils/src/process_errors.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": false + } + ], + "returnComment": [], + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/ml-error-utils", + "id": "def-common.extractErrorProperties", + "type": "Function", + "tags": [], + "label": "extractErrorProperties", + "description": [ + "\nExtract properties of the error object from within the response error\ncoming from Kibana, Elasticsearch, and our own ML messages.\n" + ], + "signature": [ + "(error: ", + { + "pluginId": "@kbn/ml-error-utils", + "scope": "common", + "docId": "kibKbnMlErrorUtilsPluginApi", + "section": "def-common.ErrorType", + "text": "ErrorType" + }, + ") => ", + { + "pluginId": "@kbn/ml-error-utils", + "scope": "common", + "docId": "kibKbnMlErrorUtilsPluginApi", + "section": "def-common.MLErrorObject", + "text": "MLErrorObject" + } + ], + "path": "x-pack/packages/ml/error_utils/src/process_errors.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/ml-error-utils", + "id": "def-common.extractErrorProperties.$1", + "type": "CompoundType", + "tags": [], + "label": "error", + "description": [], + "signature": [ + { + "pluginId": "@kbn/ml-error-utils", + "scope": "common", + "docId": "kibKbnMlErrorUtilsPluginApi", + "section": "def-common.ErrorType", + "text": "ErrorType" + } + ], + "path": "x-pack/packages/ml/error_utils/src/process_errors.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": false + } + ], + "returnComment": [], + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/ml-error-utils", + "id": "def-common.isBoomError", + "type": "Function", + "tags": [ + "export" + ], + "label": "isBoomError", + "description": [ + "\nType guard to check if error is of type Boom." + ], + "signature": [ + "(error: unknown) => boolean" + ], + "path": "x-pack/packages/ml/error_utils/src/types.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/ml-error-utils", + "id": "def-common.isBoomError.$1", + "type": "Unknown", + "tags": [], + "label": "error", + "description": [], + "signature": [ + "unknown" + ], + "path": "x-pack/packages/ml/error_utils/src/types.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + } + ], + "returnComment": [], + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/ml-error-utils", + "id": "def-common.isErrorString", + "type": "Function", + "tags": [ + "export" + ], + "label": "isErrorString", + "description": [ + "\nType guard to check if error is a string." + ], + "signature": [ + "(error: unknown) => boolean" + ], + "path": "x-pack/packages/ml/error_utils/src/types.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/ml-error-utils", + "id": "def-common.isErrorString.$1", + "type": "Unknown", + "tags": [], + "label": "error", + "description": [], + "signature": [ + "unknown" + ], + "path": "x-pack/packages/ml/error_utils/src/types.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + } + ], + "returnComment": [], + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/ml-error-utils", + "id": "def-common.isEsErrorBody", + "type": "Function", + "tags": [ + "export" + ], + "label": "isEsErrorBody", + "description": [ + "\nType guard to check if error is of type EsErrorBody" + ], + "signature": [ + "(error: unknown) => boolean" + ], + "path": "x-pack/packages/ml/error_utils/src/types.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/ml-error-utils", + "id": "def-common.isEsErrorBody.$1", + "type": "Unknown", + "tags": [], + "label": "error", + "description": [], + "signature": [ + "unknown" + ], + "path": "x-pack/packages/ml/error_utils/src/types.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + } + ], + "returnComment": [], + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/ml-error-utils", + "id": "def-common.isMLResponseError", + "type": "Function", + "tags": [ + "export" + ], + "label": "isMLResponseError", + "description": [ + "\nType guard to check if error is of type MLResponseError." + ], + "signature": [ + "(error: unknown) => boolean" + ], + "path": "x-pack/packages/ml/error_utils/src/types.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/ml-error-utils", + "id": "def-common.isMLResponseError.$1", + "type": "Unknown", + "tags": [], + "label": "error", + "description": [], + "signature": [ + "unknown" + ], + "path": "x-pack/packages/ml/error_utils/src/types.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + } + ], + "returnComment": [], + "initialIsOpen": false + } + ], + "interfaces": [ + { + "parentPluginId": "@kbn/ml-error-utils", + "id": "def-common.ErrorMessage", + "type": "Interface", + "tags": [ + "export", + "interface", + "typedef" + ], + "label": "ErrorMessage", + "description": [ + "\nInterface holding error message" + ], + "path": "x-pack/packages/ml/error_utils/src/types.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/ml-error-utils", + "id": "def-common.ErrorMessage.message", + "type": "string", + "tags": [ + "type" + ], + "label": "message", + "description": [ + "\nmessage" + ], + "path": "x-pack/packages/ml/error_utils/src/types.ts", + "deprecated": false, + "trackAdoption": false + } + ], + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/ml-error-utils", + "id": "def-common.MLErrorObject", + "type": "Interface", + "tags": [ + "export", + "interface", + "typedef" + ], + "label": "MLErrorObject", + "description": [ + "\nML Error Object" + ], + "path": "x-pack/packages/ml/error_utils/src/types.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/ml-error-utils", + "id": "def-common.MLErrorObject.causedBy", + "type": "string", + "tags": [ + "type" + ], + "label": "causedBy", + "description": [ + "\nOptional causedBy" + ], + "signature": [ + "string | undefined" + ], + "path": "x-pack/packages/ml/error_utils/src/types.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/ml-error-utils", + "id": "def-common.MLErrorObject.message", + "type": "string", + "tags": [ + "type" + ], + "label": "message", + "description": [ + "\nmessage" + ], + "path": "x-pack/packages/ml/error_utils/src/types.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/ml-error-utils", + "id": "def-common.MLErrorObject.statusCode", + "type": "number", + "tags": [ + "type" + ], + "label": "statusCode", + "description": [ + "\nOptional statusCode" + ], + "signature": [ + "number | undefined" + ], + "path": "x-pack/packages/ml/error_utils/src/types.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/ml-error-utils", + "id": "def-common.MLErrorObject.fullError", + "type": "Object", + "tags": [ + "type" + ], + "label": "fullError", + "description": [ + "\nOptional fullError" + ], + "signature": [ + "ErrorResponseBase", + " | undefined" + ], + "path": "x-pack/packages/ml/error_utils/src/types.ts", + "deprecated": false, + "trackAdoption": false + } + ], + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/ml-error-utils", + "id": "def-common.MLHttpFetchErrorBase", + "type": "Interface", + "tags": [ + "export", + "interface", + "typedef", + "template", + "extends" + ], + "label": "MLHttpFetchErrorBase", + "description": [ + "\nMLHttpFetchErrorBase" + ], + "signature": [ + { + "pluginId": "@kbn/ml-error-utils", + "scope": "common", + "docId": "kibKbnMlErrorUtilsPluginApi", + "section": "def-common.MLHttpFetchErrorBase", + "text": "MLHttpFetchErrorBase" + }, + " extends ", + { + "pluginId": "@kbn/core-http-browser", + "scope": "common", + "docId": "kibKbnCoreHttpBrowserPluginApi", + "section": "def-common.IHttpFetchError", + "text": "IHttpFetchError" + }, + "" + ], + "path": "x-pack/packages/ml/error_utils/src/types.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/ml-error-utils", + "id": "def-common.MLHttpFetchErrorBase.body", + "type": "Uncategorized", + "tags": [ + "type" + ], + "label": "body", + "description": [ + "\nbody" + ], + "signature": [ + "T" + ], + "path": "x-pack/packages/ml/error_utils/src/types.ts", + "deprecated": false, + "trackAdoption": false + } + ], + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/ml-error-utils", + "id": "def-common.MLResponseError", + "type": "Interface", + "tags": [ + "export", + "interface", + "typedef" + ], + "label": "MLResponseError", + "description": [ + "\nML Response error" + ], + "path": "x-pack/packages/ml/error_utils/src/types.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/ml-error-utils", + "id": "def-common.MLResponseError.statusCode", + "type": "number", + "tags": [ + "type" + ], + "label": "statusCode", + "description": [ + "\nstatusCode" + ], + "path": "x-pack/packages/ml/error_utils/src/types.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/ml-error-utils", + "id": "def-common.MLResponseError.error", + "type": "string", + "tags": [ + "type" + ], + "label": "error", + "description": [ + "\nerror" + ], + "path": "x-pack/packages/ml/error_utils/src/types.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/ml-error-utils", + "id": "def-common.MLResponseError.message", + "type": "string", + "tags": [ + "type" + ], + "label": "message", + "description": [ + "\nmessage" + ], + "path": "x-pack/packages/ml/error_utils/src/types.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/ml-error-utils", + "id": "def-common.MLResponseError.attributes", + "type": "Object", + "tags": [ + "type" + ], + "label": "attributes", + "description": [ + "\nOptional attributes" + ], + "signature": [ + "{ body: ", + "ErrorResponseBase", + "; } | undefined" + ], + "path": "x-pack/packages/ml/error_utils/src/types.ts", + "deprecated": false, + "trackAdoption": false + } + ], + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/ml-error-utils", + "id": "def-common.QueryErrorMessage", + "type": "Interface", + "tags": [], + "label": "QueryErrorMessage", + "description": [ + "\nTo be used for client side errors related to search query bars." + ], + "signature": [ + { + "pluginId": "@kbn/ml-error-utils", + "scope": "common", + "docId": "kibKbnMlErrorUtilsPluginApi", + "section": "def-common.QueryErrorMessage", + "text": "QueryErrorMessage" + }, + " extends ", + { + "pluginId": "@kbn/ml-error-utils", + "scope": "common", + "docId": "kibKbnMlErrorUtilsPluginApi", + "section": "def-common.ErrorMessage", + "text": "ErrorMessage" + } + ], + "path": "x-pack/packages/ml/error_utils/src/types.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/ml-error-utils", + "id": "def-common.QueryErrorMessage.query", + "type": "string", + "tags": [ + "type" + ], + "label": "query", + "description": [ + "\nquery" + ], + "path": "x-pack/packages/ml/error_utils/src/types.ts", + "deprecated": false, + "trackAdoption": false + } + ], + "initialIsOpen": false + } + ], + "enums": [], + "misc": [ + { + "parentPluginId": "@kbn/ml-error-utils", + "id": "def-common.ErrorType", + "type": "Type", + "tags": [ + "export", + "typedef" + ], + "label": "ErrorType", + "description": [ + "\nUnion type of error types" + ], + "signature": [ + "string | ", + { + "pluginId": "@kbn/ml-error-utils", + "scope": "common", + "docId": "kibKbnMlErrorUtilsPluginApi", + "section": "def-common.MLHttpFetchError", + "text": "MLHttpFetchError" + }, + " | ", + "ErrorResponseBase", + " | ", + "Boom", + " | undefined" + ], + "path": "x-pack/packages/ml/error_utils/src/types.ts", + "deprecated": false, + "trackAdoption": false, + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/ml-error-utils", + "id": "def-common.EsErrorBody", + "type": "Type", + "tags": [ + "typedef" + ], + "label": "EsErrorBody", + "description": [ + "\nShort hand type of estypes.ErrorResponseBase." + ], + "signature": [ + "ErrorResponseBase" + ], + "path": "x-pack/packages/ml/error_utils/src/types.ts", + "deprecated": false, + "trackAdoption": false, + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/ml-error-utils", + "id": "def-common.EsErrorRootCause", + "type": "Type", + "tags": [ + "typedef" + ], + "label": "EsErrorRootCause", + "description": [ + "\nShort hand type of estypes.ErrorCause." + ], + "signature": [ + "ErrorCauseKeys", + " & { [property: string]: any; }" + ], + "path": "x-pack/packages/ml/error_utils/src/types.ts", + "deprecated": false, + "trackAdoption": false, + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/ml-error-utils", + "id": "def-common.MLHttpFetchError", + "type": "Type", + "tags": [ + "export", + "typedef" + ], + "label": "MLHttpFetchError", + "description": [ + "\nMLHttpFetchError" + ], + "signature": [ + { + "pluginId": "@kbn/ml-error-utils", + "scope": "common", + "docId": "kibKbnMlErrorUtilsPluginApi", + "section": "def-common.MLHttpFetchErrorBase", + "text": "MLHttpFetchErrorBase" + }, + "<", + { + "pluginId": "@kbn/ml-error-utils", + "scope": "common", + "docId": "kibKbnMlErrorUtilsPluginApi", + "section": "def-common.MLResponseError", + "text": "MLResponseError" + }, + ">" + ], + "path": "x-pack/packages/ml/error_utils/src/types.ts", + "deprecated": false, + "trackAdoption": false, + "initialIsOpen": false + } + ], + "objects": [] + } +} \ No newline at end of file diff --git a/api_docs/kbn_ml_error_utils.mdx b/api_docs/kbn_ml_error_utils.mdx new file mode 100644 index 0000000000000..39fbc8c7d9cc2 --- /dev/null +++ b/api_docs/kbn_ml_error_utils.mdx @@ -0,0 +1,39 @@ +--- +#### +#### This document is auto-generated and is meant to be viewed inside our experimental, new docs system. +#### Reach out in #docs-engineering for more info. +#### +id: kibKbnMlErrorUtilsPluginApi +slug: /kibana-dev-docs/api/kbn-ml-error-utils +title: "@kbn/ml-error-utils" +image: https://source.unsplash.com/400x175/?github +description: API docs for the @kbn/ml-error-utils plugin +date: 2023-04-24 +tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ml-error-utils'] +--- +import kbnMlErrorUtilsObj from './kbn_ml_error_utils.devdocs.json'; + + + +Contact [@elastic/ml-ui](https://github.com/orgs/elastic/teams/ml-ui) for questions regarding this plugin. + +**Code health stats** + +| Public API count | Any count | Items lacking comments | Missing exports | +|-------------------|-----------|------------------------|-----------------| +| 36 | 0 | 8 | 0 | + +## Common + +### Functions + + +### Classes + + +### Interfaces + + +### Consts, variables and types + + diff --git a/api_docs/kbn_ml_is_defined.mdx b/api_docs/kbn_ml_is_defined.mdx index 9d000095378bd..22473afe43b10 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-04-21 +date: 2023-04-24 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 40ce5731de31e..6418b8eaa65f6 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-04-21 +date: 2023-04-24 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 f14deff209d77..b6ab8338b1be1 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-04-21 +date: 2023-04-24 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 ef15635229b53..20e06ed847ebb 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-04-21 +date: 2023-04-24 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_number_utils.mdx b/api_docs/kbn_ml_number_utils.mdx index 05c5432c4f57f..e17712d9bddd9 100644 --- a/api_docs/kbn_ml_number_utils.mdx +++ b/api_docs/kbn_ml_number_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-number-utils title: "@kbn/ml-number-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-number-utils plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ml-number-utils'] --- import kbnMlNumberUtilsObj from './kbn_ml_number_utils.devdocs.json'; diff --git a/api_docs/kbn_ml_query_utils.mdx b/api_docs/kbn_ml_query_utils.mdx index 8327a7ed0b4d5..ceef878888760 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-04-21 +date: 2023-04-24 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_random_sampler_utils.mdx b/api_docs/kbn_ml_random_sampler_utils.mdx index 4998fe175ef71..e065a2d921874 100644 --- a/api_docs/kbn_ml_random_sampler_utils.mdx +++ b/api_docs/kbn_ml_random_sampler_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-random-sampler-utils title: "@kbn/ml-random-sampler-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-random-sampler-utils plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ml-random-sampler-utils'] --- import kbnMlRandomSamplerUtilsObj from './kbn_ml_random_sampler_utils.devdocs.json'; diff --git a/api_docs/kbn_ml_route_utils.mdx b/api_docs/kbn_ml_route_utils.mdx index 8e1d50e0f9364..619f44aa95904 100644 --- a/api_docs/kbn_ml_route_utils.mdx +++ b/api_docs/kbn_ml_route_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-route-utils title: "@kbn/ml-route-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-route-utils plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ml-route-utils'] --- import kbnMlRouteUtilsObj from './kbn_ml_route_utils.devdocs.json'; diff --git a/api_docs/kbn_ml_string_hash.mdx b/api_docs/kbn_ml_string_hash.mdx index f18f1327cb256..c338805fe37b6 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-04-21 +date: 2023-04-24 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_trained_models_utils.mdx b/api_docs/kbn_ml_trained_models_utils.mdx index 43aeec4bf32ca..77df8b9fc96a9 100644 --- a/api_docs/kbn_ml_trained_models_utils.mdx +++ b/api_docs/kbn_ml_trained_models_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-trained-models-utils title: "@kbn/ml-trained-models-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-trained-models-utils plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ml-trained-models-utils'] --- import kbnMlTrainedModelsUtilsObj from './kbn_ml_trained_models_utils.devdocs.json'; diff --git a/api_docs/kbn_ml_url_state.mdx b/api_docs/kbn_ml_url_state.mdx index 390009d4685e5..271d8d798bb8b 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-04-21 +date: 2023-04-24 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 605d07152c5b0..0d286dc40dd46 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-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/monaco'] --- import kbnMonacoObj from './kbn_monaco.devdocs.json'; diff --git a/api_docs/kbn_object_versioning.devdocs.json b/api_docs/kbn_object_versioning.devdocs.json index bb1210067600d..551e1feda1d0b 100644 --- a/api_docs/kbn_object_versioning.devdocs.json +++ b/api_docs/kbn_object_versioning.devdocs.json @@ -768,6 +768,28 @@ "path": "packages/kbn-object-versioning/lib/content_management_types.ts", "deprecated": false, "trackAdoption": false + }, + { + "parentPluginId": "@kbn/object-versioning", + "id": "def-common.ServicesDefinition.mSearch", + "type": "Object", + "tags": [], + "label": "mSearch", + "description": [], + "signature": [ + "{ out?: { result?: ", + { + "pluginId": "@kbn/object-versioning", + "scope": "common", + "docId": "kibKbnObjectVersioningPluginApi", + "section": "def-common.VersionableObject", + "text": "VersionableObject" + }, + " | undefined; } | undefined; } | undefined" + ], + "path": "packages/kbn-object-versioning/lib/content_management_types.ts", + "deprecated": false, + "trackAdoption": false } ], "initialIsOpen": false @@ -978,6 +1000,28 @@ "path": "packages/kbn-object-versioning/lib/content_management_types.ts", "deprecated": false, "trackAdoption": false + }, + { + "parentPluginId": "@kbn/object-versioning", + "id": "def-common.ServiceTransforms.mSearch", + "type": "Object", + "tags": [], + "label": "mSearch", + "description": [], + "signature": [ + "{ out: { result: ", + { + "pluginId": "@kbn/object-versioning", + "scope": "common", + "docId": "kibKbnObjectVersioningPluginApi", + "section": "def-common.ObjectTransforms", + "text": "ObjectTransforms" + }, + "; }; }" + ], + "path": "packages/kbn-object-versioning/lib/content_management_types.ts", + "deprecated": false, + "trackAdoption": false } ], "initialIsOpen": false diff --git a/api_docs/kbn_object_versioning.mdx b/api_docs/kbn_object_versioning.mdx index 8d3135bdd7f76..bf8dc9c9b2edb 100644 --- a/api_docs/kbn_object_versioning.mdx +++ b/api_docs/kbn_object_versioning.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-object-versioning title: "@kbn/object-versioning" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/object-versioning plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/object-versioning'] --- import kbnObjectVersioningObj from './kbn_object_versioning.devdocs.json'; @@ -21,7 +21,7 @@ Contact [@elastic/appex-sharedux](https://github.com/orgs/elastic/teams/appex-sh | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 53 | 1 | 48 | 0 | +| 55 | 1 | 50 | 0 | ## Common diff --git a/api_docs/kbn_observability_alert_details.mdx b/api_docs/kbn_observability_alert_details.mdx index 48886f59e6160..109f0b15690e7 100644 --- a/api_docs/kbn_observability_alert_details.mdx +++ b/api_docs/kbn_observability_alert_details.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-observability-alert-details title: "@kbn/observability-alert-details" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/observability-alert-details plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/observability-alert-details'] --- import kbnObservabilityAlertDetailsObj from './kbn_observability_alert_details.devdocs.json'; diff --git a/api_docs/kbn_optimizer.mdx b/api_docs/kbn_optimizer.mdx index 58204fde5d980..2be790b6675bf 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-04-21 +date: 2023-04-24 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 ac5cab95f43ec..c03883615b27e 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-04-21 +date: 2023-04-24 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 d351b9e974a46..39eafb2553e2a 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-04-21 +date: 2023-04-24 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 820b381b56e3b..fe2a28edfb759 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-04-21 +date: 2023-04-24 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 4f63a60e98d21..dfafc2995046a 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-04-21 +date: 2023-04-24 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 481b254612404..90bfd0727dc86 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-04-21 +date: 2023-04-24 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 5d88f2bb7cd87..894f6e1897cb7 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-04-21 +date: 2023-04-24 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 fb0c69e268327..9baafe7d47198 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-04-21 +date: 2023-04-24 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 fa756525922ae..0dcb15d07b2b0 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-04-21 +date: 2023-04-24 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 dc04d8563bc52..44df1cb8c7ba8 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-04-21 +date: 2023-04-24 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 af0847941070f..3725fe5986da2 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-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/repo-source-classifier'] --- import kbnRepoSourceClassifierObj from './kbn_repo_source_classifier.devdocs.json'; diff --git a/api_docs/kbn_reporting_common.mdx b/api_docs/kbn_reporting_common.mdx index 52ff2a0ac41b5..660528756fcdf 100644 --- a/api_docs/kbn_reporting_common.mdx +++ b/api_docs/kbn_reporting_common.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-reporting-common title: "@kbn/reporting-common" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/reporting-common plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/reporting-common'] --- import kbnReportingCommonObj from './kbn_reporting_common.devdocs.json'; diff --git a/api_docs/kbn_rison.mdx b/api_docs/kbn_rison.mdx index 5f654ffa57ef8..ee1fd9df0086e 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-04-21 +date: 2023-04-24 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 16eb8b268e12a..56b40b98c6111 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-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/rule-data-utils'] --- import kbnRuleDataUtilsObj from './kbn_rule_data_utils.devdocs.json'; diff --git a/api_docs/kbn_saved_objects_settings.mdx b/api_docs/kbn_saved_objects_settings.mdx index 2d5a7a078d24c..ac3326c810bdf 100644 --- a/api_docs/kbn_saved_objects_settings.mdx +++ b/api_docs/kbn_saved_objects_settings.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-saved-objects-settings title: "@kbn/saved-objects-settings" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/saved-objects-settings plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/saved-objects-settings'] --- import kbnSavedObjectsSettingsObj from './kbn_saved_objects_settings.devdocs.json'; diff --git a/api_docs/kbn_security_solution_side_nav.mdx b/api_docs/kbn_security_solution_side_nav.mdx index 476ecb69cdbcc..dbd27615a2886 100644 --- a/api_docs/kbn_security_solution_side_nav.mdx +++ b/api_docs/kbn_security_solution_side_nav.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-security-solution-side-nav title: "@kbn/security-solution-side-nav" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/security-solution-side-nav plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/security-solution-side-nav'] --- import kbnSecuritySolutionSideNavObj from './kbn_security_solution_side_nav.devdocs.json'; diff --git a/api_docs/kbn_security_solution_storybook_config.mdx b/api_docs/kbn_security_solution_storybook_config.mdx index 5bec888bb4e5a..31bc6c9d9cbc9 100644 --- a/api_docs/kbn_security_solution_storybook_config.mdx +++ b/api_docs/kbn_security_solution_storybook_config.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-security-solution-storybook-config title: "@kbn/security-solution-storybook-config" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/security-solution-storybook-config plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/security-solution-storybook-config'] --- import kbnSecuritySolutionStorybookConfigObj from './kbn_security_solution_storybook_config.devdocs.json'; diff --git a/api_docs/kbn_securitysolution_autocomplete.mdx b/api_docs/kbn_securitysolution_autocomplete.mdx index 7d0c70dbb7b39..f51ac2903abc5 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-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/securitysolution-autocomplete'] --- import kbnSecuritysolutionAutocompleteObj from './kbn_securitysolution_autocomplete.devdocs.json'; diff --git a/api_docs/kbn_securitysolution_data_table.mdx b/api_docs/kbn_securitysolution_data_table.mdx index 1c5b3548f92c4..cbb7736cb66be 100644 --- a/api_docs/kbn_securitysolution_data_table.mdx +++ b/api_docs/kbn_securitysolution_data_table.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-data-table title: "@kbn/securitysolution-data-table" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/securitysolution-data-table plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/securitysolution-data-table'] --- import kbnSecuritysolutionDataTableObj from './kbn_securitysolution_data_table.devdocs.json'; diff --git a/api_docs/kbn_securitysolution_ecs.mdx b/api_docs/kbn_securitysolution_ecs.mdx index 085a9a78a69cf..2d549e62753a9 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-04-21 +date: 2023-04-24 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 1df63d4877934..966df1d43affc 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-04-21 +date: 2023-04-24 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 35453fb0b1a31..ef163cd54d784 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-04-21 +date: 2023-04-24 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.mdx b/api_docs/kbn_securitysolution_grouping.mdx index 7276dcef35bd3..548914f17feb8 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-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/securitysolution-grouping'] --- import kbnSecuritysolutionGroupingObj from './kbn_securitysolution_grouping.devdocs.json'; diff --git a/api_docs/kbn_securitysolution_hook_utils.mdx b/api_docs/kbn_securitysolution_hook_utils.mdx index a7cb93e0ca41c..012e37a5de10c 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-04-21 +date: 2023-04-24 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.devdocs.json b/api_docs/kbn_securitysolution_io_ts_alerting_types.devdocs.json index 59129f93d4ed5..9450fd73b6671 100644 --- a/api_docs/kbn_securitysolution_io_ts_alerting_types.devdocs.json +++ b/api_docs/kbn_securitysolution_io_ts_alerting_types.devdocs.json @@ -303,7 +303,7 @@ "section": "def-common.SavedObjectAttributes", "text": "SavedObjectAttributes" }, - "; } & { uuid?: string | undefined; alerts_filter?: { query: ({ kql: string; } & { dsl?: string | undefined; }) | null; timeframe: { timezone: string; days: (2 | 7 | 6 | 5 | 4 | 3 | 1)[]; hours: { start: string; end: string; }; } | null; } | undefined; }" + "; } & { uuid?: string | undefined; alerts_filter?: { query?: ({ kql: string; filters: ({ meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; params?: any; value?: string | undefined; }; } & { $state?: { store: any; } | undefined; query?: { [x: string]: any; } | undefined; })[]; } & { dsl?: string | undefined; }) | undefined; timeframe?: { timezone: string; days: (2 | 7 | 6 | 5 | 4 | 3 | 1)[]; hours: { start: string; end: string; }; } | undefined; } | undefined; frequency?: { summary: boolean; notifyWhen: \"onActionGroupChange\" | \"onActiveAlert\" | \"onThrottleInterval\"; throttle: string | null; } | undefined; }" ], "path": "packages/kbn-securitysolution-io-ts-alerting-types/src/actions/index.ts", "deprecated": false, @@ -326,7 +326,7 @@ "section": "def-common.SavedObjectAttributes", "text": "SavedObjectAttributes" }, - "; } & { uuid?: string | undefined; alerts_filter?: { query: ({ kql: string; } & { dsl?: string | undefined; }) | null; timeframe: { timezone: string; days: (2 | 7 | 6 | 5 | 4 | 3 | 1)[]; hours: { start: string; end: string; }; } | null; } | undefined; })[]" + "; } & { uuid?: string | undefined; alerts_filter?: { query?: ({ kql: string; filters: ({ meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; params?: any; value?: string | undefined; }; } & { $state?: { store: any; } | undefined; query?: { [x: string]: any; } | undefined; })[]; } & { dsl?: string | undefined; }) | undefined; timeframe?: { timezone: string; days: (2 | 7 | 6 | 5 | 4 | 3 | 1)[]; hours: { start: string; end: string; }; } | undefined; } | undefined; frequency?: { summary: boolean; notifyWhen: \"onActionGroupChange\" | \"onActiveAlert\" | \"onThrottleInterval\"; throttle: string | null; } | undefined; })[]" ], "path": "packages/kbn-securitysolution-io-ts-alerting-types/src/actions/index.ts", "deprecated": false, @@ -349,7 +349,7 @@ "section": "def-common.SavedObjectAttributes", "text": "SavedObjectAttributes" }, - "; } & { uuid?: string | undefined; alertsFilter?: { query: ({ kql: string; } & { dsl?: string | undefined; }) | null; timeframe: { timezone: string; days: (2 | 7 | 6 | 5 | 4 | 3 | 1)[]; hours: { start: string; end: string; }; } | null; } | undefined; })[]" + "; } & { uuid?: string | undefined; alertsFilter?: { query?: ({ kql: string; filters: ({ meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; params?: any; value?: string | undefined; }; } & { $state?: { store: any; } | undefined; query?: { [x: string]: any; } | undefined; })[]; } & { dsl?: string | undefined; }) | undefined; timeframe?: { timezone: string; days: (2 | 7 | 6 | 5 | 4 | 3 | 1)[]; hours: { start: string; end: string; }; } | undefined; } | undefined; frequency?: { summary: boolean; notifyWhen: \"onActionGroupChange\" | \"onActiveAlert\" | \"onThrottleInterval\"; throttle: string | null; } | undefined; })[]" ], "path": "packages/kbn-securitysolution-io-ts-alerting-types/src/actions/index.ts", "deprecated": false, @@ -372,13 +372,30 @@ "section": "def-common.SavedObjectAttributes", "text": "SavedObjectAttributes" }, - "; } & { uuid?: string | undefined; alertsFilter?: { query: ({ kql: string; } & { dsl?: string | undefined; }) | null; timeframe: { timezone: string; days: (2 | 7 | 6 | 5 | 4 | 3 | 1)[]; hours: { start: string; end: string; }; } | null; } | undefined; }" + "; } & { uuid?: string | undefined; alertsFilter?: { query?: ({ kql: string; filters: ({ meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; params?: any; value?: string | undefined; }; } & { $state?: { store: any; } | undefined; query?: { [x: string]: any; } | undefined; })[]; } & { dsl?: string | undefined; }) | undefined; timeframe?: { timezone: string; days: (2 | 7 | 6 | 5 | 4 | 3 | 1)[]; hours: { start: string; end: string; }; } | undefined; } | undefined; frequency?: { summary: boolean; notifyWhen: \"onActionGroupChange\" | \"onActiveAlert\" | \"onThrottleInterval\"; throttle: string | null; } | undefined; }" ], "path": "packages/kbn-securitysolution-io-ts-alerting-types/src/actions/index.ts", "deprecated": false, "trackAdoption": false, "initialIsOpen": false }, + { + "parentPluginId": "@kbn/securitysolution-io-ts-alerting-types", + "id": "def-common.RuleActionFrequency", + "type": "Type", + "tags": [], + "label": "RuleActionFrequency", + "description": [ + "\nThe action frequency defines when the action runs (for example, only on rule execution or at specific time intervals)." + ], + "signature": [ + "{ summary: boolean; notifyWhen: \"onActionGroupChange\" | \"onActiveAlert\" | \"onThrottleInterval\"; throttle: string | null; }" + ], + "path": "packages/kbn-securitysolution-io-ts-alerting-types/src/frequency/index.ts", + "deprecated": false, + "trackAdoption": false, + "initialIsOpen": false + }, { "parentPluginId": "@kbn/securitysolution-io-ts-alerting-types", "id": "def-common.RuleActionGroup", @@ -409,6 +426,23 @@ "trackAdoption": false, "initialIsOpen": false }, + { + "parentPluginId": "@kbn/securitysolution-io-ts-alerting-types", + "id": "def-common.RuleActionNotifyWhen", + "type": "Type", + "tags": [], + "label": "RuleActionNotifyWhen", + "description": [ + "\nThe condition for throttling the notification: `onActionGroupChange`, `onActiveAlert`, or `onThrottleInterval`" + ], + "signature": [ + "\"onActionGroupChange\" | \"onActiveAlert\" | \"onThrottleInterval\"" + ], + "path": "packages/kbn-securitysolution-io-ts-alerting-types/src/frequency/index.ts", + "deprecated": false, + "trackAdoption": false, + "initialIsOpen": false + }, { "parentPluginId": "@kbn/securitysolution-io-ts-alerting-types", "id": "def-common.RuleActionParams", @@ -434,6 +468,23 @@ "trackAdoption": false, "initialIsOpen": false }, + { + "parentPluginId": "@kbn/securitysolution-io-ts-alerting-types", + "id": "def-common.RuleActionSummary", + "type": "Type", + "tags": [], + "label": "RuleActionSummary", + "description": [ + "\nAction summary indicates whether we will send a summary notification about all the generate alerts or notification per individual alert" + ], + "signature": [ + "boolean" + ], + "path": "packages/kbn-securitysolution-io-ts-alerting-types/src/frequency/index.ts", + "deprecated": false, + "trackAdoption": false, + "initialIsOpen": false + }, { "parentPluginId": "@kbn/securitysolution-io-ts-alerting-types", "id": "def-common.RuleActionThrottle", @@ -1013,7 +1064,7 @@ "section": "def-common.SavedObjectAttributes", "text": "SavedObjectAttributes" }, - "; } & { uuid?: string | undefined; alerts_filter?: { query: ({ kql: string; } & { dsl?: string | undefined; }) | null; timeframe: { timezone: string; days: (2 | 7 | 6 | 5 | 4 | 3 | 1)[]; hours: { start: string; end: string; }; } | null; } | undefined; })[], ({ group: string; id: string; action_type_id: string; params: ", + "; } & { uuid?: string | undefined; alerts_filter?: { query?: ({ kql: string; filters: ({ meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; params?: any; value?: string | undefined; }; } & { $state?: { store: any; } | undefined; query?: { [x: string]: any; } | undefined; })[]; } & { dsl?: string | undefined; }) | undefined; timeframe?: { timezone: string; days: (2 | 7 | 6 | 5 | 4 | 3 | 1)[]; hours: { start: string; end: string; }; } | undefined; } | undefined; frequency?: { summary: boolean; notifyWhen: \"onActionGroupChange\" | \"onActiveAlert\" | \"onThrottleInterval\"; throttle: string | null; } | undefined; })[], ({ group: string; id: string; action_type_id: string; params: ", { "pluginId": "@kbn/securitysolution-io-ts-alerting-types", "scope": "common", @@ -1021,7 +1072,7 @@ "section": "def-common.SavedObjectAttributes", "text": "SavedObjectAttributes" }, - "; } & { uuid?: string | undefined; alerts_filter?: { query: ({ kql: string; } & { dsl?: string | undefined; }) | null; timeframe: { timezone: string; days: (2 | 7 | 6 | 5 | 4 | 3 | 1)[]; hours: { start: string; end: string; }; } | null; } | undefined; })[] | undefined, unknown>" + "; } & { uuid?: string | undefined; alerts_filter?: { query?: ({ kql: string; filters: ({ meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; params?: any; value?: string | undefined; }; } & { $state?: { store: any; } | undefined; query?: { [x: string]: any; } | undefined; })[]; } & { dsl?: string | undefined; }) | undefined; timeframe?: { timezone: string; days: (2 | 7 | 6 | 5 | 4 | 3 | 1)[]; hours: { start: string; end: string; }; } | undefined; } | undefined; frequency?: { summary: boolean; notifyWhen: \"onActionGroupChange\" | \"onActiveAlert\" | \"onThrottleInterval\"; throttle: string | null; } | undefined; })[] | undefined, unknown>" ], "path": "packages/kbn-securitysolution-io-ts-alerting-types/src/default_actions_array/index.ts", "deprecated": false, @@ -1570,13 +1621,11 @@ "<{ uuid: ", "Type", "; alerts_filter: ", - "ExactC", - "<", - "TypeC", + "PartialC", "<{ query: ", "UnionC", "<[", - "NullC", + "UndefinedC", ", ", "IntersectionC", "<[", @@ -1585,14 +1634,60 @@ "TypeC", "<{ kql: ", "StringC", - "; }>>, ", + "; filters: ", + "ArrayC", + "<", + "IntersectionC", + "<[", + "TypeC", + "<{ meta: ", + "PartialC", + "<{ alias: ", + "UnionC", + "<[", + "StringC", + ", ", + "NullC", + "]>; disabled: ", + "BooleanC", + "; negate: ", + "BooleanC", + "; controlledBy: ", + "StringC", + "; group: ", + "StringC", + "; index: ", + "StringC", + "; isMultiIndex: ", + "BooleanC", + "; type: ", + "StringC", + "; key: ", + "StringC", + "; params: ", + "AnyC", + "; value: ", + "StringC", + "; }>; }>, ", + "PartialC", + "<{ $state: ", + "TypeC", + "<{ store: ", + "AnyC", + "; }>; query: ", + "RecordC", + "<", + "StringC", + ", ", + "AnyC", + ">; }>]>>; }>>, ", "PartialC", "<{ dsl: ", "StringC", "; }>]>]>; timeframe: ", "UnionC", "<[", - "NullC", + "UndefinedC", ", ", "ExactC", "<", @@ -1625,7 +1720,31 @@ "StringC", "; end: ", "StringC", - "; }>>; }>>]>; }>>; }>]>>" + "; }>>; }>>]>; }>; frequency: ", + "TypeC", + "<{ summary: ", + "BooleanC", + "; notifyWhen: ", + "UnionC", + "<[", + "LiteralC", + "<\"onActionGroupChange\">, ", + "LiteralC", + "<\"onActiveAlert\">, ", + "LiteralC", + "<\"onThrottleInterval\">]>; throttle: ", + "UnionC", + "<[", + "UnionC", + "<[", + "LiteralC", + "<\"no_actions\">, ", + "LiteralC", + "<\"rule\">, ", + "Type", + "]>, ", + "NullC", + "]>; }>; }>]>>" ], "path": "packages/kbn-securitysolution-io-ts-alerting-types/src/actions/index.ts", "deprecated": false, @@ -1640,13 +1759,11 @@ "label": "RuleActionAlertsFilter", "description": [], "signature": [ - "ExactC", - "<", - "TypeC", + "PartialC", "<{ query: ", "UnionC", "<[", - "NullC", + "UndefinedC", ", ", "IntersectionC", "<[", @@ -1655,14 +1772,60 @@ "TypeC", "<{ kql: ", "StringC", - "; }>>, ", + "; filters: ", + "ArrayC", + "<", + "IntersectionC", + "<[", + "TypeC", + "<{ meta: ", + "PartialC", + "<{ alias: ", + "UnionC", + "<[", + "StringC", + ", ", + "NullC", + "]>; disabled: ", + "BooleanC", + "; negate: ", + "BooleanC", + "; controlledBy: ", + "StringC", + "; group: ", + "StringC", + "; index: ", + "StringC", + "; isMultiIndex: ", + "BooleanC", + "; type: ", + "StringC", + "; key: ", + "StringC", + "; params: ", + "AnyC", + "; value: ", + "StringC", + "; }>; }>, ", + "PartialC", + "<{ $state: ", + "TypeC", + "<{ store: ", + "AnyC", + "; }>; query: ", + "RecordC", + "<", + "StringC", + ", ", + "AnyC", + ">; }>]>>; }>>, ", "PartialC", "<{ dsl: ", "StringC", "; }>]>]>; timeframe: ", "UnionC", "<[", - "NullC", + "UndefinedC", ", ", "ExactC", "<", @@ -1695,7 +1858,7 @@ "StringC", "; end: ", "StringC", - "; }>>; }>>]>; }>>" + "; }>>; }>>]>; }>" ], "path": "packages/kbn-securitysolution-io-ts-alerting-types/src/actions/index.ts", "deprecated": false, @@ -1746,13 +1909,11 @@ "<{ uuid: ", "Type", "; alerts_filter: ", - "ExactC", - "<", - "TypeC", + "PartialC", "<{ query: ", "UnionC", "<[", - "NullC", + "UndefinedC", ", ", "IntersectionC", "<[", @@ -1761,14 +1922,60 @@ "TypeC", "<{ kql: ", "StringC", - "; }>>, ", + "; filters: ", + "ArrayC", + "<", + "IntersectionC", + "<[", + "TypeC", + "<{ meta: ", + "PartialC", + "<{ alias: ", + "UnionC", + "<[", + "StringC", + ", ", + "NullC", + "]>; disabled: ", + "BooleanC", + "; negate: ", + "BooleanC", + "; controlledBy: ", + "StringC", + "; group: ", + "StringC", + "; index: ", + "StringC", + "; isMultiIndex: ", + "BooleanC", + "; type: ", + "StringC", + "; key: ", + "StringC", + "; params: ", + "AnyC", + "; value: ", + "StringC", + "; }>; }>, ", + "PartialC", + "<{ $state: ", + "TypeC", + "<{ store: ", + "AnyC", + "; }>; query: ", + "RecordC", + "<", + "StringC", + ", ", + "AnyC", + ">; }>]>>; }>>, ", "PartialC", "<{ dsl: ", "StringC", "; }>]>]>; timeframe: ", "UnionC", "<[", - "NullC", + "UndefinedC", ", ", "ExactC", "<", @@ -1801,7 +2008,31 @@ "StringC", "; end: ", "StringC", - "; }>>; }>>]>; }>>; }>]>>>" + "; }>>; }>>]>; }>; frequency: ", + "TypeC", + "<{ summary: ", + "BooleanC", + "; notifyWhen: ", + "UnionC", + "<[", + "LiteralC", + "<\"onActionGroupChange\">, ", + "LiteralC", + "<\"onActiveAlert\">, ", + "LiteralC", + "<\"onThrottleInterval\">]>; throttle: ", + "UnionC", + "<[", + "UnionC", + "<[", + "LiteralC", + "<\"no_actions\">, ", + "LiteralC", + "<\"rule\">, ", + "Type", + "]>, ", + "NullC", + "]>; }>; }>]>>>" ], "path": "packages/kbn-securitysolution-io-ts-alerting-types/src/actions/index.ts", "deprecated": false, @@ -1852,13 +2083,11 @@ "<{ uuid: ", "Type", "; alertsFilter: ", - "ExactC", - "<", - "TypeC", + "PartialC", "<{ query: ", "UnionC", "<[", - "NullC", + "UndefinedC", ", ", "IntersectionC", "<[", @@ -1867,14 +2096,60 @@ "TypeC", "<{ kql: ", "StringC", - "; }>>, ", + "; filters: ", + "ArrayC", + "<", + "IntersectionC", + "<[", + "TypeC", + "<{ meta: ", + "PartialC", + "<{ alias: ", + "UnionC", + "<[", + "StringC", + ", ", + "NullC", + "]>; disabled: ", + "BooleanC", + "; negate: ", + "BooleanC", + "; controlledBy: ", + "StringC", + "; group: ", + "StringC", + "; index: ", + "StringC", + "; isMultiIndex: ", + "BooleanC", + "; type: ", + "StringC", + "; key: ", + "StringC", + "; params: ", + "AnyC", + "; value: ", + "StringC", + "; }>; }>, ", + "PartialC", + "<{ $state: ", + "TypeC", + "<{ store: ", + "AnyC", + "; }>; query: ", + "RecordC", + "<", + "StringC", + ", ", + "AnyC", + ">; }>]>>; }>>, ", "PartialC", "<{ dsl: ", "StringC", "; }>]>]>; timeframe: ", "UnionC", "<[", - "NullC", + "UndefinedC", ", ", "ExactC", "<", @@ -1907,7 +2182,31 @@ "StringC", "; end: ", "StringC", - "; }>>; }>>]>; }>>; }>]>>>" + "; }>>; }>>]>; }>; frequency: ", + "TypeC", + "<{ summary: ", + "BooleanC", + "; notifyWhen: ", + "UnionC", + "<[", + "LiteralC", + "<\"onActionGroupChange\">, ", + "LiteralC", + "<\"onActiveAlert\">, ", + "LiteralC", + "<\"onThrottleInterval\">]>; throttle: ", + "UnionC", + "<[", + "UnionC", + "<[", + "LiteralC", + "<\"no_actions\">, ", + "LiteralC", + "<\"rule\">, ", + "Type", + "]>, ", + "NullC", + "]>; }>; }>]>>>" ], "path": "packages/kbn-securitysolution-io-ts-alerting-types/src/actions/index.ts", "deprecated": false, @@ -1956,13 +2255,11 @@ "<{ uuid: ", "Type", "; alertsFilter: ", - "ExactC", - "<", - "TypeC", + "PartialC", "<{ query: ", "UnionC", "<[", - "NullC", + "UndefinedC", ", ", "IntersectionC", "<[", @@ -1971,14 +2268,60 @@ "TypeC", "<{ kql: ", "StringC", - "; }>>, ", + "; filters: ", + "ArrayC", + "<", + "IntersectionC", + "<[", + "TypeC", + "<{ meta: ", + "PartialC", + "<{ alias: ", + "UnionC", + "<[", + "StringC", + ", ", + "NullC", + "]>; disabled: ", + "BooleanC", + "; negate: ", + "BooleanC", + "; controlledBy: ", + "StringC", + "; group: ", + "StringC", + "; index: ", + "StringC", + "; isMultiIndex: ", + "BooleanC", + "; type: ", + "StringC", + "; key: ", + "StringC", + "; params: ", + "AnyC", + "; value: ", + "StringC", + "; }>; }>, ", + "PartialC", + "<{ $state: ", + "TypeC", + "<{ store: ", + "AnyC", + "; }>; query: ", + "RecordC", + "<", + "StringC", + ", ", + "AnyC", + ">; }>]>>; }>>, ", "PartialC", "<{ dsl: ", "StringC", "; }>]>]>; timeframe: ", "UnionC", "<[", - "NullC", + "UndefinedC", ", ", "ExactC", "<", @@ -2011,13 +2354,75 @@ "StringC", "; end: ", "StringC", - "; }>>; }>>]>; }>>; }>]>>" + "; }>>; }>>]>; }>; frequency: ", + "TypeC", + "<{ summary: ", + "BooleanC", + "; notifyWhen: ", + "UnionC", + "<[", + "LiteralC", + "<\"onActionGroupChange\">, ", + "LiteralC", + "<\"onActiveAlert\">, ", + "LiteralC", + "<\"onThrottleInterval\">]>; throttle: ", + "UnionC", + "<[", + "UnionC", + "<[", + "LiteralC", + "<\"no_actions\">, ", + "LiteralC", + "<\"rule\">, ", + "Type", + "]>, ", + "NullC", + "]>; }>; }>]>>" ], "path": "packages/kbn-securitysolution-io-ts-alerting-types/src/actions/index.ts", "deprecated": false, "trackAdoption": false, "initialIsOpen": false }, + { + "parentPluginId": "@kbn/securitysolution-io-ts-alerting-types", + "id": "def-common.RuleActionFrequency", + "type": "Object", + "tags": [], + "label": "RuleActionFrequency", + "description": [], + "signature": [ + "TypeC", + "<{ summary: ", + "BooleanC", + "; notifyWhen: ", + "UnionC", + "<[", + "LiteralC", + "<\"onActionGroupChange\">, ", + "LiteralC", + "<\"onActiveAlert\">, ", + "LiteralC", + "<\"onThrottleInterval\">]>; throttle: ", + "UnionC", + "<[", + "UnionC", + "<[", + "LiteralC", + "<\"no_actions\">, ", + "LiteralC", + "<\"rule\">, ", + "Type", + "]>, ", + "NullC", + "]>; }>" + ], + "path": "packages/kbn-securitysolution-io-ts-alerting-types/src/frequency/index.ts", + "deprecated": false, + "trackAdoption": false, + "initialIsOpen": false + }, { "parentPluginId": "@kbn/securitysolution-io-ts-alerting-types", "id": "def-common.RuleActionGroup", @@ -2048,6 +2453,28 @@ "trackAdoption": false, "initialIsOpen": false }, + { + "parentPluginId": "@kbn/securitysolution-io-ts-alerting-types", + "id": "def-common.RuleActionNotifyWhen", + "type": "Object", + "tags": [], + "label": "RuleActionNotifyWhen", + "description": [], + "signature": [ + "UnionC", + "<[", + "LiteralC", + "<\"onActionGroupChange\">, ", + "LiteralC", + "<\"onActiveAlert\">, ", + "LiteralC", + "<\"onThrottleInterval\">]>" + ], + "path": "packages/kbn-securitysolution-io-ts-alerting-types/src/frequency/index.ts", + "deprecated": false, + "trackAdoption": false, + "initialIsOpen": false + }, { "parentPluginId": "@kbn/securitysolution-io-ts-alerting-types", "id": "def-common.RuleActionParams", @@ -2080,6 +2507,21 @@ "trackAdoption": false, "initialIsOpen": false }, + { + "parentPluginId": "@kbn/securitysolution-io-ts-alerting-types", + "id": "def-common.RuleActionSummary", + "type": "Object", + "tags": [], + "label": "RuleActionSummary", + "description": [], + "signature": [ + "BooleanC" + ], + "path": "packages/kbn-securitysolution-io-ts-alerting-types/src/frequency/index.ts", + "deprecated": false, + "trackAdoption": false, + "initialIsOpen": false + }, { "parentPluginId": "@kbn/securitysolution-io-ts-alerting-types", "id": "def-common.RuleActionThrottle", diff --git a/api_docs/kbn_securitysolution_io_ts_alerting_types.mdx b/api_docs/kbn_securitysolution_io_ts_alerting_types.mdx index 1a5fe7a8eff63..c19977b427358 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-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/securitysolution-io-ts-alerting-types'] --- import kbnSecuritysolutionIoTsAlertingTypesObj from './kbn_securitysolution_io_ts_alerting_types.devdocs.json'; @@ -21,7 +21,7 @@ Contact [@elastic/security-solution-platform](https://github.com/orgs/elastic/te | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 141 | 0 | 122 | 0 | +| 147 | 0 | 125 | 0 | ## Common diff --git a/api_docs/kbn_securitysolution_io_ts_list_types.devdocs.json b/api_docs/kbn_securitysolution_io_ts_list_types.devdocs.json index cbd27335f2f35..44e52b99ed031 100644 --- a/api_docs/kbn_securitysolution_io_ts_list_types.devdocs.json +++ b/api_docs/kbn_securitysolution_io_ts_list_types.devdocs.json @@ -106,7 +106,13 @@ "label": "http", "description": [], "signature": [ - "HttpStart" + { + "pluginId": "@kbn/core-http-browser", + "scope": "common", + "docId": "kibKbnCoreHttpBrowserPluginApi", + "section": "def-common.HttpSetup", + "text": "HttpSetup" + } ], "path": "packages/kbn-securitysolution-io-ts-list-types/src/typescript_types/index.ts", "deprecated": false, @@ -148,7 +154,13 @@ "label": "http", "description": [], "signature": [ - "HttpStart" + { + "pluginId": "@kbn/core-http-browser", + "scope": "common", + "docId": "kibKbnCoreHttpBrowserPluginApi", + "section": "def-common.HttpSetup", + "text": "HttpSetup" + } ], "path": "packages/kbn-securitysolution-io-ts-list-types/src/typescript_types/index.ts", "deprecated": false, @@ -204,7 +216,13 @@ "label": "http", "description": [], "signature": [ - "HttpStart" + { + "pluginId": "@kbn/core-http-browser", + "scope": "common", + "docId": "kibKbnCoreHttpBrowserPluginApi", + "section": "def-common.HttpSetup", + "text": "HttpSetup" + } ], "path": "packages/kbn-securitysolution-io-ts-list-types/src/typescript_types/index.ts", "deprecated": false, @@ -260,7 +278,13 @@ "label": "http", "description": [], "signature": [ - "HttpStart" + { + "pluginId": "@kbn/core-http-browser", + "scope": "common", + "docId": "kibKbnCoreHttpBrowserPluginApi", + "section": "def-common.HttpSetup", + "text": "HttpSetup" + } ], "path": "packages/kbn-securitysolution-io-ts-list-types/src/typescript_types/index.ts", "deprecated": false, @@ -327,7 +351,13 @@ "label": "http", "description": [], "signature": [ - "HttpStart" + { + "pluginId": "@kbn/core-http-browser", + "scope": "common", + "docId": "kibKbnCoreHttpBrowserPluginApi", + "section": "def-common.HttpSetup", + "text": "HttpSetup" + } ], "path": "packages/kbn-securitysolution-io-ts-list-types/src/typescript_types/index.ts", "deprecated": false, @@ -439,7 +469,13 @@ "label": "http", "description": [], "signature": [ - "HttpStart" + { + "pluginId": "@kbn/core-http-browser", + "scope": "common", + "docId": "kibKbnCoreHttpBrowserPluginApi", + "section": "def-common.HttpSetup", + "text": "HttpSetup" + } ], "path": "packages/kbn-securitysolution-io-ts-list-types/src/typescript_types/index.ts", "deprecated": false, @@ -1015,6 +1051,102 @@ ], "initialIsOpen": false }, + { + "parentPluginId": "@kbn/securitysolution-io-ts-list-types", + "id": "def-common.ApiListDuplicateProps", + "type": "Interface", + "tags": [], + "label": "ApiListDuplicateProps", + "description": [], + "signature": [ + { + "pluginId": "@kbn/securitysolution-io-ts-list-types", + "scope": "common", + "docId": "kibKbnSecuritysolutionIoTsListTypesPluginApi", + "section": "def-common.ApiListDuplicateProps", + "text": "ApiListDuplicateProps" + }, + " extends Omit<", + { + "pluginId": "@kbn/securitysolution-io-ts-list-types", + "scope": "common", + "docId": "kibKbnSecuritysolutionIoTsListTypesPluginApi", + "section": "def-common.DuplicateExceptionListProps", + "text": "DuplicateExceptionListProps" + }, + ", \"http\" | \"signal\">" + ], + "path": "packages/kbn-securitysolution-io-ts-list-types/src/typescript_types/index.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/securitysolution-io-ts-list-types", + "id": "def-common.ApiListDuplicateProps.onError", + "type": "Function", + "tags": [], + "label": "onError", + "description": [], + "signature": [ + "(err: Error) => void" + ], + "path": "packages/kbn-securitysolution-io-ts-list-types/src/typescript_types/index.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/securitysolution-io-ts-list-types", + "id": "def-common.ApiListDuplicateProps.onError.$1", + "type": "Object", + "tags": [], + "label": "err", + "description": [], + "signature": [ + "Error" + ], + "path": "packages/kbn-securitysolution-io-ts-list-types/src/typescript_types/index.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + } + ], + "returnComment": [] + }, + { + "parentPluginId": "@kbn/securitysolution-io-ts-list-types", + "id": "def-common.ApiListDuplicateProps.onSuccess", + "type": "Function", + "tags": [], + "label": "onSuccess", + "description": [], + "signature": [ + "(newList: { _version: string | undefined; created_at: string; created_by: string; description: string; id: string; immutable: boolean; list_id: string; meta: object | undefined; name: string; namespace_type: \"single\" | \"agnostic\"; os_types: (\"windows\" | \"linux\" | \"macos\")[]; tags: string[]; tie_breaker_id: string; type: \"endpoint\" | \"detection\" | \"rule_default\" | \"endpoint_trusted_apps\" | \"endpoint_events\" | \"endpoint_host_isolation_exceptions\" | \"endpoint_blocklists\"; updated_at: string; updated_by: string; version: number; }) => void" + ], + "path": "packages/kbn-securitysolution-io-ts-list-types/src/typescript_types/index.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/securitysolution-io-ts-list-types", + "id": "def-common.ApiListDuplicateProps.onSuccess.$1", + "type": "Object", + "tags": [], + "label": "newList", + "description": [], + "signature": [ + "{ _version: string | undefined; created_at: string; created_by: string; description: string; id: string; immutable: boolean; list_id: string; meta: object | undefined; name: string; namespace_type: \"single\" | \"agnostic\"; os_types: (\"windows\" | \"linux\" | \"macos\")[]; tags: string[]; tie_breaker_id: string; type: \"endpoint\" | \"detection\" | \"rule_default\" | \"endpoint_trusted_apps\" | \"endpoint_events\" | \"endpoint_host_isolation_exceptions\" | \"endpoint_blocklists\"; updated_at: string; updated_by: string; version: number; }" + ], + "path": "packages/kbn-securitysolution-io-ts-list-types/src/typescript_types/index.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + } + ], + "returnComment": [] + } + ], + "initialIsOpen": false + }, { "parentPluginId": "@kbn/securitysolution-io-ts-list-types", "id": "def-common.ApiListExportProps", @@ -1140,6 +1272,66 @@ ], "initialIsOpen": false }, + { + "parentPluginId": "@kbn/securitysolution-io-ts-list-types", + "id": "def-common.DuplicateExceptionListProps", + "type": "Interface", + "tags": [], + "label": "DuplicateExceptionListProps", + "description": [], + "signature": [ + { + "pluginId": "@kbn/securitysolution-io-ts-list-types", + "scope": "common", + "docId": "kibKbnSecuritysolutionIoTsListTypesPluginApi", + "section": "def-common.DuplicateExceptionListProps", + "text": "DuplicateExceptionListProps" + }, + " extends BaseParams" + ], + "path": "packages/kbn-securitysolution-io-ts-list-types/src/typescript_types/index.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/securitysolution-io-ts-list-types", + "id": "def-common.DuplicateExceptionListProps.listId", + "type": "string", + "tags": [], + "label": "listId", + "description": [], + "path": "packages/kbn-securitysolution-io-ts-list-types/src/typescript_types/index.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/securitysolution-io-ts-list-types", + "id": "def-common.DuplicateExceptionListProps.namespaceType", + "type": "CompoundType", + "tags": [], + "label": "namespaceType", + "description": [], + "signature": [ + "\"single\" | \"agnostic\"" + ], + "path": "packages/kbn-securitysolution-io-ts-list-types/src/typescript_types/index.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/securitysolution-io-ts-list-types", + "id": "def-common.DuplicateExceptionListProps.includeExpiredExceptions", + "type": "boolean", + "tags": [], + "label": "includeExpiredExceptions", + "description": [], + "path": "packages/kbn-securitysolution-io-ts-list-types/src/typescript_types/index.ts", + "deprecated": false, + "trackAdoption": false + } + ], + "initialIsOpen": false + }, { "parentPluginId": "@kbn/securitysolution-io-ts-list-types", "id": "def-common.ExceptionFilterResponse", @@ -1393,7 +1585,13 @@ "label": "http", "description": [], "signature": [ - "HttpStart" + { + "pluginId": "@kbn/core-http-browser", + "scope": "common", + "docId": "kibKbnCoreHttpBrowserPluginApi", + "section": "def-common.HttpSetup", + "text": "HttpSetup" + } ], "path": "packages/kbn-securitysolution-io-ts-list-types/src/typescript_types/index.ts", "deprecated": false, @@ -1538,7 +1736,13 @@ "label": "http", "description": [], "signature": [ - "HttpStart" + { + "pluginId": "@kbn/core-http-browser", + "scope": "common", + "docId": "kibKbnCoreHttpBrowserPluginApi", + "section": "def-common.HttpSetup", + "text": "HttpSetup" + } ], "path": "packages/kbn-securitysolution-io-ts-list-types/src/typescript_types/index.ts", "deprecated": false, @@ -1597,7 +1801,13 @@ "label": "http", "description": [], "signature": [ - "HttpStart" + { + "pluginId": "@kbn/core-http-browser", + "scope": "common", + "docId": "kibKbnCoreHttpBrowserPluginApi", + "section": "def-common.HttpSetup", + "text": "HttpSetup" + } ], "path": "packages/kbn-securitysolution-io-ts-list-types/src/typescript_types/index.ts", "deprecated": false, @@ -1759,7 +1969,13 @@ "label": "http", "description": [], "signature": [ - "HttpStart" + { + "pluginId": "@kbn/core-http-browser", + "scope": "common", + "docId": "kibKbnCoreHttpBrowserPluginApi", + "section": "def-common.HttpSetup", + "text": "HttpSetup" + } ], "path": "packages/kbn-securitysolution-io-ts-list-types/src/typescript_types/index.ts", "deprecated": false, @@ -1855,7 +2071,13 @@ "label": "http", "description": [], "signature": [ - "HttpStart" + { + "pluginId": "@kbn/core-http-browser", + "scope": "common", + "docId": "kibKbnCoreHttpBrowserPluginApi", + "section": "def-common.HttpSetup", + "text": "HttpSetup" + } ], "path": "packages/kbn-securitysolution-io-ts-list-types/src/typescript_types/index.ts", "deprecated": false, @@ -1911,7 +2133,13 @@ "label": "http", "description": [], "signature": [ - "HttpStart" + { + "pluginId": "@kbn/core-http-browser", + "scope": "common", + "docId": "kibKbnCoreHttpBrowserPluginApi", + "section": "def-common.HttpSetup", + "text": "HttpSetup" + } ], "path": "packages/kbn-securitysolution-io-ts-list-types/src/typescript_types/index.ts", "deprecated": false, @@ -2015,7 +2243,13 @@ "label": "http", "description": [], "signature": [ - "HttpStart" + { + "pluginId": "@kbn/core-http-browser", + "scope": "common", + "docId": "kibKbnCoreHttpBrowserPluginApi", + "section": "def-common.HttpSetup", + "text": "HttpSetup" + } ], "path": "packages/kbn-securitysolution-io-ts-list-types/src/typescript_types/index.ts", "deprecated": false, @@ -2270,7 +2504,13 @@ "label": "http", "description": [], "signature": [ - "HttpStart" + { + "pluginId": "@kbn/core-http-browser", + "scope": "common", + "docId": "kibKbnCoreHttpBrowserPluginApi", + "section": "def-common.HttpSetup", + "text": "HttpSetup" + } ], "path": "packages/kbn-securitysolution-io-ts-list-types/src/typescript_types/index.ts", "deprecated": false, @@ -2293,12 +2533,18 @@ { "parentPluginId": "@kbn/securitysolution-io-ts-list-types", "id": "def-common.UseExceptionListsProps.notifications", - "type": "Any", + "type": "Object", "tags": [], "label": "notifications", "description": [], "signature": [ - "any" + { + "pluginId": "@kbn/core-notifications-browser", + "scope": "common", + "docId": "kibKbnCoreNotificationsBrowserPluginApi", + "section": "def-common.NotificationsStart", + "text": "NotificationsStart" + } ], "path": "packages/kbn-securitysolution-io-ts-list-types/src/typescript_types/index.ts", "deprecated": false, @@ -3081,6 +3327,36 @@ "trackAdoption": false, "initialIsOpen": false }, + { + "parentPluginId": "@kbn/securitysolution-io-ts-list-types", + "id": "def-common.DuplicateExceptionListQuerySchema", + "type": "Type", + "tags": [], + "label": "DuplicateExceptionListQuerySchema", + "description": [], + "signature": [ + "{ list_id: string; namespace_type: \"single\" | \"agnostic\" | undefined; include_expired_exceptions: \"true\" | \"false\" | undefined; }" + ], + "path": "packages/kbn-securitysolution-io-ts-list-types/src/request/duplicate_exception_list_query_schema/index.ts", + "deprecated": false, + "trackAdoption": false, + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/securitysolution-io-ts-list-types", + "id": "def-common.DuplicateExceptionListQuerySchemaDecoded", + "type": "Type", + "tags": [], + "label": "DuplicateExceptionListQuerySchemaDecoded", + "description": [], + "signature": [ + "Omit<{ list_id: string; namespace_type: \"single\" | \"agnostic\"; include_expired_exceptions: \"true\" | \"false\" | undefined; }, \"namespace_type\"> & { namespace_type: \"single\" | \"agnostic\"; }" + ], + "path": "packages/kbn-securitysolution-io-ts-list-types/src/request/duplicate_exception_list_query_schema/index.ts", + "deprecated": false, + "trackAdoption": false, + "initialIsOpen": false + }, { "parentPluginId": "@kbn/securitysolution-io-ts-list-types", "id": "def-common.EndpointEntriesArray", @@ -6256,6 +6532,34 @@ "trackAdoption": false, "initialIsOpen": false }, + { + "parentPluginId": "@kbn/securitysolution-io-ts-list-types", + "id": "def-common.duplicateExceptionListQuerySchema", + "type": "Object", + "tags": [], + "label": "duplicateExceptionListQuerySchema", + "description": [], + "signature": [ + "ExactC", + "<", + "TypeC", + "<{ list_id: ", + "Type", + "; namespace_type: ", + "Type", + "<\"single\" | \"agnostic\", \"single\" | \"agnostic\" | undefined, unknown>; include_expired_exceptions: ", + "UnionC", + "<[", + "KeyofC", + "<{ true: null; false: null; }>, ", + "UndefinedC", + "]>; }>>" + ], + "path": "packages/kbn-securitysolution-io-ts-list-types/src/request/duplicate_exception_list_query_schema/index.ts", + "deprecated": false, + "trackAdoption": false, + "initialIsOpen": false + }, { "parentPluginId": "@kbn/securitysolution-io-ts-list-types", "id": "def-common.endpointEntriesArray", diff --git a/api_docs/kbn_securitysolution_io_ts_list_types.mdx b/api_docs/kbn_securitysolution_io_ts_list_types.mdx index fae1f7cdd2313..61c51d7af0af1 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-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/securitysolution-io-ts-list-types'] --- import kbnSecuritysolutionIoTsListTypesObj from './kbn_securitysolution_io_ts_list_types.devdocs.json'; @@ -21,7 +21,7 @@ Contact [@elastic/security-solution-platform](https://github.com/orgs/elastic/te | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 516 | 1 | 503 | 0 | +| 528 | 0 | 515 | 0 | ## Common diff --git a/api_docs/kbn_securitysolution_io_ts_types.mdx b/api_docs/kbn_securitysolution_io_ts_types.mdx index 360fd19200755..97859ef4b0ff6 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-04-21 +date: 2023-04-24 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 2b6f02cc7215b..087b27374d68b 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-04-21 +date: 2023-04-24 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.devdocs.json b/api_docs/kbn_securitysolution_list_api.devdocs.json index b4c5635b5b281..1a07636db6652 100644 --- a/api_docs/kbn_securitysolution_list_api.devdocs.json +++ b/api_docs/kbn_securitysolution_list_api.devdocs.json @@ -348,6 +348,57 @@ "returnComment": [], "initialIsOpen": false }, + { + "parentPluginId": "@kbn/securitysolution-list-api", + "id": "def-common.duplicateExceptionList", + "type": "Function", + "tags": [ + "throws" + ], + "label": "duplicateExceptionList", + "description": [ + "\nDuplicate an ExceptionList and its items by providing a ExceptionList list_id\n" + ], + "signature": [ + "({ http, includeExpiredExceptions, listId, namespaceType, signal, }: ", + { + "pluginId": "@kbn/securitysolution-io-ts-list-types", + "scope": "common", + "docId": "kibKbnSecuritysolutionIoTsListTypesPluginApi", + "section": "def-common.DuplicateExceptionListProps", + "text": "DuplicateExceptionListProps" + }, + ") => Promise<{ _version: string | undefined; created_at: string; created_by: string; description: string; id: string; immutable: boolean; list_id: string; meta: object | undefined; name: string; namespace_type: \"single\" | \"agnostic\"; os_types: (\"windows\" | \"linux\" | \"macos\")[]; tags: string[]; tie_breaker_id: string; type: \"endpoint\" | \"detection\" | \"rule_default\" | \"endpoint_trusted_apps\" | \"endpoint_events\" | \"endpoint_host_isolation_exceptions\" | \"endpoint_blocklists\"; updated_at: string; updated_by: string; version: number; }>" + ], + "path": "packages/kbn-securitysolution-list-api/src/api/index.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/securitysolution-list-api", + "id": "def-common.duplicateExceptionList.$1", + "type": "Object", + "tags": [], + "label": "{\n http,\n includeExpiredExceptions,\n listId,\n namespaceType,\n signal,\n}", + "description": [], + "signature": [ + { + "pluginId": "@kbn/securitysolution-io-ts-list-types", + "scope": "common", + "docId": "kibKbnSecuritysolutionIoTsListTypesPluginApi", + "section": "def-common.DuplicateExceptionListProps", + "text": "DuplicateExceptionListProps" + } + ], + "path": "packages/kbn-securitysolution-list-api/src/api/index.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + } + ], + "returnComment": [], + "initialIsOpen": false + }, { "parentPluginId": "@kbn/securitysolution-list-api", "id": "def-common.exportExceptionList", diff --git a/api_docs/kbn_securitysolution_list_api.mdx b/api_docs/kbn_securitysolution_list_api.mdx index 0255ba2088e5e..aadca05334b92 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-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/securitysolution-list-api'] --- import kbnSecuritysolutionListApiObj from './kbn_securitysolution_list_api.devdocs.json'; @@ -21,7 +21,7 @@ Contact [@elastic/security-solution-platform](https://github.com/orgs/elastic/te | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 67 | 0 | 64 | 0 | +| 69 | 0 | 65 | 0 | ## Common diff --git a/api_docs/kbn_securitysolution_list_constants.mdx b/api_docs/kbn_securitysolution_list_constants.mdx index 02b65ca6fde37..2d19d6111ef53 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-04-21 +date: 2023-04-24 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.devdocs.json b/api_docs/kbn_securitysolution_list_hooks.devdocs.json index a1741a54232f0..14c2ee5a4b122 100644 --- a/api_docs/kbn_securitysolution_list_hooks.devdocs.json +++ b/api_docs/kbn_securitysolution_list_hooks.devdocs.json @@ -255,7 +255,15 @@ "label": "useApi", "description": [], "signature": [ - "(http: HttpStart) => ", + "(http: ", + { + "pluginId": "@kbn/core-http-browser", + "scope": "common", + "docId": "kibKbnCoreHttpBrowserPluginApi", + "section": "def-common.HttpSetup", + "text": "HttpSetup" + }, + ") => ", { "pluginId": "@kbn/securitysolution-list-hooks", "scope": "common", @@ -276,7 +284,13 @@ "label": "http", "description": [], "signature": [ - "HttpStart" + { + "pluginId": "@kbn/core-http-browser", + "scope": "common", + "docId": "kibKbnCoreHttpBrowserPluginApi", + "section": "def-common.HttpSetup", + "text": "HttpSetup" + } ], "path": "packages/kbn-securitysolution-list-hooks/src/use_api/index.ts", "deprecated": false, @@ -1023,6 +1037,52 @@ ], "returnComment": [] }, + { + "parentPluginId": "@kbn/securitysolution-list-hooks", + "id": "def-common.ExceptionsApi.duplicateExceptionList", + "type": "Function", + "tags": [], + "label": "duplicateExceptionList", + "description": [], + "signature": [ + "(arg: ", + { + "pluginId": "@kbn/securitysolution-io-ts-list-types", + "scope": "common", + "docId": "kibKbnSecuritysolutionIoTsListTypesPluginApi", + "section": "def-common.ApiListDuplicateProps", + "text": "ApiListDuplicateProps" + }, + ") => Promise" + ], + "path": "packages/kbn-securitysolution-list-hooks/src/use_api/index.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/securitysolution-list-hooks", + "id": "def-common.ExceptionsApi.duplicateExceptionList.$1", + "type": "Object", + "tags": [], + "label": "arg", + "description": [], + "signature": [ + { + "pluginId": "@kbn/securitysolution-io-ts-list-types", + "scope": "common", + "docId": "kibKbnSecuritysolutionIoTsListTypesPluginApi", + "section": "def-common.ApiListDuplicateProps", + "text": "ApiListDuplicateProps" + } + ], + "path": "packages/kbn-securitysolution-list-hooks/src/use_api/index.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + } + ], + "returnComment": [] + }, { "parentPluginId": "@kbn/securitysolution-list-hooks", "id": "def-common.ExceptionsApi.getExceptionItem", diff --git a/api_docs/kbn_securitysolution_list_hooks.mdx b/api_docs/kbn_securitysolution_list_hooks.mdx index 786b822ce39de..e5f63ce69a791 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-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/securitysolution-list-hooks'] --- import kbnSecuritysolutionListHooksObj from './kbn_securitysolution_list_hooks.devdocs.json'; @@ -21,7 +21,7 @@ Contact [@elastic/security-solution-platform](https://github.com/orgs/elastic/te | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 60 | 0 | 47 | 0 | +| 62 | 0 | 49 | 0 | ## Common diff --git a/api_docs/kbn_securitysolution_list_utils.mdx b/api_docs/kbn_securitysolution_list_utils.mdx index 7f1e021d2997e..9fbc74f2281fa 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-04-21 +date: 2023-04-24 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 7b8ea7f8fa153..d99c895cfbb07 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-04-21 +date: 2023-04-24 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 bbaae18e81926..fc1a7126299da 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-04-21 +date: 2023-04-24 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 b22479ca8cb62..5b42ba79461cd 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-04-21 +date: 2023-04-24 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 e959b8f8857f1..c538168844e59 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-04-21 +date: 2023-04-24 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 4dce3d0bcc354..7c066060f57b3 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-04-21 +date: 2023-04-24 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 b3241f80861cf..57b4b0757c666 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-04-21 +date: 2023-04-24 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 7fdc62d57e0e5..0e0a7de059854 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-04-21 +date: 2023-04-24 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 74b168d876714..cbe1bbc592105 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-04-21 +date: 2023-04-24 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 c67019c024447..fd754396fe38d 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-04-21 +date: 2023-04-24 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 523f937ac0308..704bf0d55a588 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-04-21 +date: 2023-04-24 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 7e026fc462e17..d5cbbf1440008 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-04-21 +date: 2023-04-24 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 6744cc7da9aa8..6e187d6a67d6d 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-04-21 +date: 2023-04-24 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 604622664685d..f1eafa0073b75 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-04-21 +date: 2023-04-24 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 2218b967c0672..345346e831748 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-04-21 +date: 2023-04-24 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 961468e71949a..e242bd651fa0d 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-04-21 +date: 2023-04-24 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 f82ac77b98773..4e391b9d8ab27 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-04-21 +date: 2023-04-24 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 1ab3bafb4ee5f..662c634769dd1 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-04-21 +date: 2023-04-24 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 f48a321a34b54..1e22cbdd34b00 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-04-21 +date: 2023-04-24 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 cf77561da9e6e..43c10d7423ef7 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-04-21 +date: 2023-04-24 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 84e8d32366c10..7a684a3ddf9b6 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-04-21 +date: 2023-04-24 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 d79e2ec066afd..7a12cb245836a 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-04-21 +date: 2023-04-24 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 35763b229e400..dc82b967602d2 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-04-21 +date: 2023-04-24 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 714f376ffb7c5..18b157f050418 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-04-21 +date: 2023-04-24 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 f4f56efd30485..8d43b2bc2609d 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-04-21 +date: 2023-04-24 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 5d46068526cc8..6ad8500d4ffa1 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-04-21 +date: 2023-04-24 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 a703893f75dec..da16a3294719e 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-04-21 +date: 2023-04-24 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 c6146d86359c0..a79ab025d8f39 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-04-21 +date: 2023-04-24 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 2fc62d3203dab..675d8ca547baa 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-04-21 +date: 2023-04-24 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 e4a702e9d4de6..87063d4968ca9 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-04-21 +date: 2023-04-24 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 0281befb594ed..d83b54deb377f 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-04-21 +date: 2023-04-24 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 bc4addd3208ad..a915fe7ce8d2f 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-04-21 +date: 2023-04-24 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 0144374aab4fb..d386725c0f96e 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-04-21 +date: 2023-04-24 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 da46bad1d78e5..b609f61949e3a 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-04-21 +date: 2023-04-24 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 4681ccfbf18d8..3b8a6aea768c4 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-04-21 +date: 2023-04-24 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 ac7fbb18cee1d..b549d3be8ce48 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-04-21 +date: 2023-04-24 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 c5f2135a7e18e..57b7f77917dbf 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-04-21 +date: 2023-04-24 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 90f8c67503a8a..cde75adfcbd03 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-04-21 +date: 2023-04-24 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 0c4bc9ae73323..449ec14d0e89b 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-04-21 +date: 2023-04-24 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 da5e9ae1eb9a1..54e8a60fb3a71 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-04-21 +date: 2023-04-24 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 aa816570472a8..64431ebf0a9f6 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-04-21 +date: 2023-04-24 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 135cdbf742f31..8f903a6a4599b 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-04-21 +date: 2023-04-24 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 9ca85a30f64b6..aa8aeb5ed628b 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-04-21 +date: 2023-04-24 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 ce99698fd9f1b..1f594a6b27503 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-04-21 +date: 2023-04-24 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 0e8f337108e82..35030e8f65b69 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-04-21 +date: 2023-04-24 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 97a606deb5c99..b86313f3a585f 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-04-21 +date: 2023-04-24 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 d1a8eebc6a352..8a4c14deb442e 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-04-21 +date: 2023-04-24 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 0217dc9a965db..8a33d6033f83b 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-04-21 +date: 2023-04-24 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 b4b67a76d6345..8bb33b6f9fc6c 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-04-21 +date: 2023-04-24 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 fd6b8221cc8fd..0dcbe8b65314a 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-04-21 +date: 2023-04-24 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 a2a8c3a870b1a..009ae2d00a260 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-04-21 +date: 2023-04-24 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 245f8573362f4..077c288dbbd06 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-04-21 +date: 2023-04-24 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 7c8dcb3038c28..ac6e29488cd2d 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-04-21 +date: 2023-04-24 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 f4a6b85e91f0c..4b38e19d2c144 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-04-21 +date: 2023-04-24 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 2da057aa80b43..1e093e1c87a67 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-04-21 +date: 2023-04-24 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 efa271163a527..ec139fac154dc 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-04-21 +date: 2023-04-24 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 e36f3796a6a55..c335bc8053866 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-04-21 +date: 2023-04-24 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 3cd1a6aef2f77..e4f4818385008 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-04-21 +date: 2023-04-24 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 11107207bbef6..cfc3a2c905dbb 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-04-21 +date: 2023-04-24 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 a6c35e39c2b54..2306fe2c1523e 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-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ui-theme'] --- import kbnUiThemeObj from './kbn_ui_theme.devdocs.json'; diff --git a/api_docs/kbn_url_state.devdocs.json b/api_docs/kbn_url_state.devdocs.json new file mode 100644 index 0000000000000..e7659b142aa8e --- /dev/null +++ b/api_docs/kbn_url_state.devdocs.json @@ -0,0 +1,99 @@ +{ + "id": "@kbn/url-state", + "client": { + "classes": [], + "functions": [], + "interfaces": [], + "enums": [], + "misc": [], + "objects": [] + }, + "server": { + "classes": [], + "functions": [], + "interfaces": [], + "enums": [], + "misc": [], + "objects": [] + }, + "common": { + "classes": [], + "functions": [ + { + "parentPluginId": "@kbn/url-state", + "id": "def-common.useSyncToUrl", + "type": "Function", + "tags": [], + "label": "useSyncToUrl", + "description": [ + "\nSync any object with browser query string using @knb/rison" + ], + "signature": [ + "(key: string, restore: (data: TValueToSerialize) => void, cleanupOnHistoryNavigation?: boolean) => (valueToSerialize?: TValueToSerialize | undefined) => void" + ], + "path": "packages/kbn-url-state/use_sync_to_url.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/url-state", + "id": "def-common.useSyncToUrl.$1", + "type": "string", + "tags": [], + "label": "key", + "description": [ + "query string param to use" + ], + "signature": [ + "string" + ], + "path": "packages/kbn-url-state/use_sync_to_url.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + }, + { + "parentPluginId": "@kbn/url-state", + "id": "def-common.useSyncToUrl.$2", + "type": "Function", + "tags": [], + "label": "restore", + "description": [ + "use this to handle restored state" + ], + "signature": [ + "(data: TValueToSerialize) => void" + ], + "path": "packages/kbn-url-state/use_sync_to_url.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + }, + { + "parentPluginId": "@kbn/url-state", + "id": "def-common.useSyncToUrl.$3", + "type": "boolean", + "tags": [], + "label": "cleanupOnHistoryNavigation", + "description": [ + "use history events to cleanup state on back / forward naviation. true by default" + ], + "signature": [ + "boolean" + ], + "path": "packages/kbn-url-state/use_sync_to_url.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + } + ], + "returnComment": [], + "initialIsOpen": false + } + ], + "interfaces": [], + "enums": [], + "misc": [], + "objects": [] + } +} \ No newline at end of file diff --git a/api_docs/kbn_url_state.mdx b/api_docs/kbn_url_state.mdx new file mode 100644 index 0000000000000..1ecdc4cf5f6b8 --- /dev/null +++ b/api_docs/kbn_url_state.mdx @@ -0,0 +1,30 @@ +--- +#### +#### This document is auto-generated and is meant to be viewed inside our experimental, new docs system. +#### Reach out in #docs-engineering for more info. +#### +id: kibKbnUrlStatePluginApi +slug: /kibana-dev-docs/api/kbn-url-state +title: "@kbn/url-state" +image: https://source.unsplash.com/400x175/?github +description: API docs for the @kbn/url-state plugin +date: 2023-04-24 +tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/url-state'] +--- +import kbnUrlStateObj from './kbn_url_state.devdocs.json'; + + + +Contact [@elastic/security-threat-hunting-investigations](https://github.com/orgs/elastic/teams/security-threat-hunting-investigations) for questions regarding this plugin. + +**Code health stats** + +| Public API count | Any count | Items lacking comments | Missing exports | +|-------------------|-----------|------------------------|-----------------| +| 4 | 0 | 0 | 0 | + +## Common + +### Functions + + diff --git a/api_docs/kbn_user_profile_components.mdx b/api_docs/kbn_user_profile_components.mdx index 99b0de88ef5ef..9678a8f3d941e 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-04-21 +date: 2023-04-24 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 d7c2e09558b57..0847fe990eba3 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-04-21 +date: 2023-04-24 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 18348076c4a98..bd7b6dc7e7ca0 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-04-21 +date: 2023-04-24 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 a743985adbe80..3bb37ce9fb475 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-04-21 +date: 2023-04-24 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 73c55462f3c81..89bec78fbe1d0 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-04-21 +date: 2023-04-24 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 e20a39ed1d8b5..35b6ab39c77a0 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-04-21 +date: 2023-04-24 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 cb370c5b41c00..277ae838dc455 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-04-21 +date: 2023-04-24 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 e4252f4bb1d23..9710a9b016f88 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-04-21 +date: 2023-04-24 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 f13e3882eba31..e9b23fb4642c8 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-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'kubernetesSecurity'] --- import kubernetesSecurityObj from './kubernetes_security.devdocs.json'; diff --git a/api_docs/lens.mdx b/api_docs/lens.mdx index 0a8741d076772..7e35673a3ef08 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-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'lens'] --- import lensObj from './lens.devdocs.json'; diff --git a/api_docs/license_api_guard.mdx b/api_docs/license_api_guard.mdx index b9b65d3c94bc6..18e54115992df 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-04-21 +date: 2023-04-24 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 e3bb7df026945..f791025b3e353 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-04-21 +date: 2023-04-24 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 704afaaf19d4d..348e30ab776d8 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-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'licensing'] --- import licensingObj from './licensing.devdocs.json'; diff --git a/api_docs/lists.devdocs.json b/api_docs/lists.devdocs.json index cf0f553770aee..a5d1cf263468f 100644 --- a/api_docs/lists.devdocs.json +++ b/api_docs/lists.devdocs.json @@ -736,7 +736,7 @@ "\nCreate the Trusted Apps Agnostic list if it does not yet exist (`null` is returned if it does exist)" ], "signature": [ - "({ listId, namespaceType, }: ", + "({ list, namespaceType, includeExpiredExceptions, }: ", "DuplicateExceptionListOptions", ") => Promise<{ _version: string | undefined; created_at: string; created_by: string; description: string; id: string; immutable: boolean; list_id: string; meta: object | undefined; name: string; namespace_type: \"single\" | \"agnostic\"; os_types: (\"windows\" | \"linux\" | \"macos\")[]; tags: string[]; tie_breaker_id: string; type: \"endpoint\" | \"detection\" | \"rule_default\" | \"endpoint_trusted_apps\" | \"endpoint_events\" | \"endpoint_host_isolation_exceptions\" | \"endpoint_blocklists\"; updated_at: string; updated_by: string; version: number; } | null>" ], @@ -749,7 +749,7 @@ "id": "def-server.ExceptionListClient.duplicateExceptionListAndItems.$1", "type": "Object", "tags": [], - "label": "{\n listId,\n namespaceType,\n }", + "label": "{\n list,\n namespaceType,\n includeExpiredExceptions,\n }", "description": [], "signature": [ "DuplicateExceptionListOptions" diff --git a/api_docs/lists.mdx b/api_docs/lists.mdx index 6a3f788c87be3..7a4f5df515fb3 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-04-21 +date: 2023-04-24 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 f2a3ee2fecf34..e86efc700e7ea 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-04-21 +date: 2023-04-24 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 3a3a5d8d917d1..d5ef557d05116 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-04-21 +date: 2023-04-24 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 4205c9b472f0b..97034dddf8ac6 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-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'mapsEms'] --- import mapsEmsObj from './maps_ems.devdocs.json'; diff --git a/api_docs/ml.devdocs.json b/api_docs/ml.devdocs.json index a235055918f57..a714fc22ec0c9 100644 --- a/api_docs/ml.devdocs.json +++ b/api_docs/ml.devdocs.json @@ -3306,41 +3306,6 @@ "returnComment": [], "initialIsOpen": false }, - { - "parentPluginId": "ml", - "id": "def-common.extractErrorMessage", - "type": "Function", - "tags": [], - "label": "extractErrorMessage", - "description": [], - "signature": [ - "(error: ", - "ErrorType", - ") => string" - ], - "path": "x-pack/plugins/ml/common/util/errors/process_errors.ts", - "deprecated": false, - "trackAdoption": false, - "children": [ - { - "parentPluginId": "ml", - "id": "def-common.extractErrorMessage.$1", - "type": "CompoundType", - "tags": [], - "label": "error", - "description": [], - "signature": [ - "ErrorType" - ], - "path": "x-pack/plugins/ml/common/util/errors/process_errors.ts", - "deprecated": false, - "trackAdoption": false, - "isRequired": false - } - ], - "returnComment": [], - "initialIsOpen": false - }, { "parentPluginId": "ml", "id": "def-common.getDefaultCapabilities", diff --git a/api_docs/ml.mdx b/api_docs/ml.mdx index d7fab7ce5ee29..d977d47492384 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-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'ml'] --- import mlObj from './ml.devdocs.json'; @@ -21,7 +21,7 @@ Contact [@elastic/ml-ui](https://github.com/orgs/elastic/teams/ml-ui) for questi | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 259 | 9 | 83 | 40 | +| 257 | 9 | 81 | 39 | ## Client diff --git a/api_docs/monitoring.mdx b/api_docs/monitoring.mdx index fbb5d4166d7c0..90c474ac78f1a 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-04-21 +date: 2023-04-24 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 390050d8aca21..8d7ae64cb77ba 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-04-21 +date: 2023-04-24 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 c1da45703e1e0..59a2eae887fb5 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-04-21 +date: 2023-04-24 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 ea9412b022b55..f1ec49c987768 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-04-21 +date: 2023-04-24 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 45300a85184d2..bf6aade55bc14 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-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'notifications'] --- import notificationsObj from './notifications.devdocs.json'; diff --git a/api_docs/observability.mdx b/api_docs/observability.mdx index caed04a1ba9d5..01cac0e1d423a 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-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'observability'] --- import observabilityObj from './observability.devdocs.json'; diff --git a/api_docs/observability_shared.mdx b/api_docs/observability_shared.mdx index 85db14ed31dc3..321906e5ba6ff 100644 --- a/api_docs/observability_shared.mdx +++ b/api_docs/observability_shared.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/observabilityShared title: "observabilityShared" image: https://source.unsplash.com/400x175/?github description: API docs for the observabilityShared plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'observabilityShared'] --- import observabilitySharedObj from './observability_shared.devdocs.json'; diff --git a/api_docs/osquery.mdx b/api_docs/osquery.mdx index 57b293ee7b2e3..171effe7c5467 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-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'osquery'] --- import osqueryObj from './osquery.devdocs.json'; diff --git a/api_docs/plugin_directory.mdx b/api_docs/plugin_directory.mdx index dc3d7eab17a06..e0ee93073348b 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-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana'] --- @@ -15,13 +15,13 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana'] | Count | Plugins or Packages with a
public API | Number of teams | |--------------|----------|------------------------| -| 595 | 491 | 37 | +| 597 | 493 | 37 | ### Public API health stats | API Count | Any Count | Missing comments | Missing exports | |--------------|----------|-----------------|--------| -| 69056 | 526 | 59639 | 1335 | +| 69151 | 525 | 59683 | 1333 | ## Plugin Directory @@ -30,7 +30,7 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana'] | | [@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. | 39 | 0 | 24 | 0 | -| | [@elastic/response-ops](https://github.com/orgs/elastic/teams/response-ops) | - | 603 | 1 | 582 | 42 | +| | [@elastic/response-ops](https://github.com/orgs/elastic/teams/response-ops) | - | 606 | 1 | 585 | 42 | | | [@elastic/apm-ui](https://github.com/orgs/elastic/teams/apm-ui) | The user interface for Elastic APM | 43 | 0 | 43 | 110 | | | [@elastic/infra-monitoring-ui](https://github.com/orgs/elastic/teams/infra-monitoring-ui) | Asset manager plugin for entity assets (inventory, topology, etc) | 3 | 0 | 3 | 0 | | | [@elastic/appex-sharedux](https://github.com/orgs/elastic/teams/appex-sharedux) | - | 9 | 0 | 9 | 0 | @@ -48,7 +48,7 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana'] | cloudLinks | [@elastic/kibana-core](https://github.com/orgs/elastic/teams/kibana-core) | Adds the links to the Elastic Cloud console | 0 | 0 | 0 | 0 | | | [@elastic/kibana-cloud-security-posture](https://github.com/orgs/elastic/teams/kibana-cloud-security-posture) | The cloud security posture plugin | 17 | 0 | 2 | 2 | | | [@elastic/platform-deployment-management](https://github.com/orgs/elastic/teams/platform-deployment-management) | - | 13 | 0 | 13 | 1 | -| | [@elastic/appex-sharedux](https://github.com/orgs/elastic/teams/appex-sharedux) | Content management app | 143 | 0 | 124 | 7 | +| | [@elastic/appex-sharedux](https://github.com/orgs/elastic/teams/appex-sharedux) | Content management app | 149 | 0 | 126 | 6 | | | [@elastic/kibana-presentation](https://github.com/orgs/elastic/teams/kibana-presentation) | The Controls Plugin contains embeddable components intended to create a simple query interface for end users, and a powerful editing suite that allows dashboard authors to build controls | 301 | 0 | 294 | 13 | | crossClusterReplication | [@elastic/platform-deployment-management](https://github.com/orgs/elastic/teams/platform-deployment-management) | - | 0 | 0 | 0 | 0 | | customBranding | [@elastic/appex-sharedux](https://github.com/orgs/elastic/teams/appex-sharedux) | Enables customization of Kibana | 0 | 0 | 0 | 0 | @@ -99,7 +99,7 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana'] | globalSearchProviders | [@elastic/appex-sharedux](https://github.com/orgs/elastic/teams/appex-sharedux) | - | 0 | 0 | 0 | 0 | | graph | [@elastic/kibana-visualizations](https://github.com/orgs/elastic/teams/kibana-visualizations) | - | 0 | 0 | 0 | 0 | | grokdebugger | [@elastic/platform-deployment-management](https://github.com/orgs/elastic/teams/platform-deployment-management) | - | 0 | 0 | 0 | 0 | -| | [@elastic/platform-onboarding](https://github.com/orgs/elastic/teams/platform-onboarding) | Guided onboarding framework | 56 | 0 | 55 | 0 | +| | [@elastic/platform-onboarding](https://github.com/orgs/elastic/teams/platform-onboarding) | Guided onboarding framework | 57 | 0 | 56 | 0 | | | [@elastic/kibana-core](https://github.com/orgs/elastic/teams/kibana-core) | - | 143 | 0 | 104 | 0 | | | [@elastic/appex-sharedux](https://github.com/orgs/elastic/teams/appex-sharedux) | Image embeddable | 3 | 0 | 3 | 1 | | | [@elastic/platform-deployment-management](https://github.com/orgs/elastic/teams/platform-deployment-management) | - | 4 | 0 | 4 | 0 | @@ -123,7 +123,7 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana'] | | [@elastic/platform-deployment-management](https://github.com/orgs/elastic/teams/platform-deployment-management) | - | 41 | 0 | 41 | 6 | | | [@elastic/kibana-gis](https://github.com/orgs/elastic/teams/kibana-gis) | - | 269 | 0 | 268 | 29 | | | [@elastic/kibana-gis](https://github.com/orgs/elastic/teams/kibana-gis) | - | 67 | 0 | 67 | 0 | -| | [@elastic/ml-ui](https://github.com/orgs/elastic/teams/ml-ui) | This plugin provides access to the machine learning features provided by Elastic. | 259 | 9 | 83 | 40 | +| | [@elastic/ml-ui](https://github.com/orgs/elastic/teams/ml-ui) | This plugin provides access to the machine learning features provided by Elastic. | 257 | 9 | 81 | 39 | | | [@elastic/infra-monitoring-ui](https://github.com/orgs/elastic/teams/infra-monitoring-ui) | - | 15 | 3 | 13 | 1 | | | [@elastic/infra-monitoring-ui](https://github.com/orgs/elastic/teams/infra-monitoring-ui) | - | 9 | 0 | 9 | 0 | | | [@elastic/appex-sharedux](https://github.com/orgs/elastic/teams/appex-sharedux) | - | 34 | 0 | 34 | 2 | @@ -167,7 +167,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) | - | 540 | 10 | 511 | 49 | +| | [@elastic/response-ops](https://github.com/orgs/elastic/teams/response-ops) | - | 542 | 10 | 513 | 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 | 296 | 0 | 270 | 6 | @@ -214,7 +214,7 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana'] | | [@elastic/kibana-core](https://github.com/orgs/elastic/teams/kibana-core) | - | 18 | 0 | 2 | 0 | | | [@elastic/kibana-core](https://github.com/orgs/elastic/teams/kibana-core) | - | 17 | 0 | 17 | 0 | | | [@elastic/apm-ui](https://github.com/orgs/elastic/teams/apm-ui) | - | 27 | 0 | 27 | 3 | -| | [@elastic/apm-ui](https://github.com/orgs/elastic/teams/apm-ui) | - | 154 | 0 | 154 | 17 | +| | [@elastic/apm-ui](https://github.com/orgs/elastic/teams/apm-ui) | - | 153 | 0 | 153 | 17 | | | [@elastic/apm-ui](https://github.com/orgs/elastic/teams/apm-ui) | - | 11 | 0 | 11 | 0 | | | [@elastic/kibana-qa](https://github.com/orgs/elastic/teams/kibana-qa) | - | 12 | 0 | 12 | 0 | | | [@elastic/response-ops](https://github.com/orgs/elastic/teams/response-ops) | - | 19 | 0 | 17 | 0 | @@ -408,14 +408,14 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana'] | | [@elastic/kibana-data-discovery](https://github.com/orgs/elastic/teams/kibana-data-discovery) | - | 255 | 1 | 197 | 15 | | | [@elastic/kibana-core](https://github.com/orgs/elastic/teams/kibana-core) | - | 12 | 0 | 12 | 0 | | | [@elastic/kibana-operations](https://github.com/orgs/elastic/teams/kibana-operations) | - | 2 | 0 | 1 | 0 | -| | [@elastic/security-threat-hunting-investigations](https://github.com/orgs/elastic/teams/security-threat-hunting-investigations) | - | 13 | 0 | 4 | 3 | +| | [@elastic/security-threat-hunting-investigations](https://github.com/orgs/elastic/teams/security-threat-hunting-investigations) | - | 33 | 0 | 13 | 3 | | | [@elastic/kibana-data-discovery](https://github.com/orgs/elastic/teams/kibana-data-discovery) | - | 20 | 0 | 16 | 0 | | | [@elastic/kibana-operations](https://github.com/orgs/elastic/teams/kibana-operations) | - | 2 | 0 | 0 | 0 | | | [@elastic/kibana-operations](https://github.com/orgs/elastic/teams/kibana-operations) | - | 29 | 0 | 29 | 1 | | | [@elastic/kibana-operations](https://github.com/orgs/elastic/teams/kibana-operations) | - | 1 | 0 | 0 | 0 | | | [@elastic/appex-sharedux](https://github.com/orgs/elastic/teams/appex-sharedux) | - | 10 | 0 | 10 | 0 | | | [@elastic/appex-sharedux](https://github.com/orgs/elastic/teams/appex-sharedux) | - | 10 | 0 | 10 | 0 | -| | [@elastic/platform-onboarding](https://github.com/orgs/elastic/teams/platform-onboarding) | - | 52 | 0 | 50 | 3 | +| | [@elastic/platform-onboarding](https://github.com/orgs/elastic/teams/platform-onboarding) | - | 54 | 0 | 52 | 3 | | | [@elastic/kibana-security](https://github.com/orgs/elastic/teams/kibana-security) | - | 33 | 3 | 24 | 6 | | | [@elastic/kibana-core](https://github.com/orgs/elastic/teams/kibana-core) | - | 3 | 0 | 3 | 0 | | | [@elastic/kibana-core](https://github.com/orgs/elastic/teams/kibana-core) | - | 1 | 0 | 1 | 0 | @@ -437,6 +437,7 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana'] | | [@elastic/kibana-gis](https://github.com/orgs/elastic/teams/kibana-gis) | - | 534 | 1 | 1 | 0 | | | [@elastic/ml-ui](https://github.com/orgs/elastic/teams/ml-ui) | - | 93 | 2 | 61 | 0 | | | [@elastic/ml-ui](https://github.com/orgs/elastic/teams/ml-ui) | - | 52 | 0 | 4 | 0 | +| | [@elastic/ml-ui](https://github.com/orgs/elastic/teams/ml-ui) | - | 36 | 0 | 8 | 0 | | | [@elastic/ml-ui](https://github.com/orgs/elastic/teams/ml-ui) | - | 2 | 0 | 0 | 0 | | | [@elastic/ml-ui](https://github.com/orgs/elastic/teams/ml-ui) | - | 3 | 0 | 2 | 0 | | | [@elastic/ml-ui](https://github.com/orgs/elastic/teams/ml-ui) | - | 5 | 0 | 3 | 0 | @@ -449,7 +450,7 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana'] | | [@elastic/ml-ui](https://github.com/orgs/elastic/teams/ml-ui) | - | 8 | 1 | 8 | 0 | | | [@elastic/ml-ui](https://github.com/orgs/elastic/teams/ml-ui) | - | 31 | 1 | 24 | 1 | | | [@elastic/appex-sharedux](https://github.com/orgs/elastic/teams/appex-sharedux) | - | 71 | 0 | 69 | 3 | -| | [@elastic/appex-sharedux](https://github.com/orgs/elastic/teams/appex-sharedux) | - | 53 | 1 | 48 | 0 | +| | [@elastic/appex-sharedux](https://github.com/orgs/elastic/teams/appex-sharedux) | - | 55 | 1 | 50 | 0 | | | [@elastic/actionable-observability](https://github.com/orgs/elastic/teams/actionable-observability) | - | 7 | 0 | 7 | 1 | | | [@elastic/kibana-operations](https://github.com/orgs/elastic/teams/kibana-operations) | - | 45 | 0 | 45 | 10 | | | [@elastic/kibana-operations](https://github.com/orgs/elastic/teams/kibana-operations) | - | 51 | 5 | 34 | 0 | @@ -475,13 +476,13 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana'] | | [@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) | - | 20 | 0 | 15 | 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) | - | 141 | 0 | 122 | 0 | -| | [@elastic/security-solution-platform](https://github.com/orgs/elastic/teams/security-solution-platform) | - | 516 | 1 | 503 | 0 | +| | [@elastic/security-solution-platform](https://github.com/orgs/elastic/teams/security-solution-platform) | - | 147 | 0 | 125 | 0 | +| | [@elastic/security-solution-platform](https://github.com/orgs/elastic/teams/security-solution-platform) | - | 528 | 0 | 515 | 0 | | | [@elastic/security-solution-platform](https://github.com/orgs/elastic/teams/security-solution-platform) | - | 65 | 0 | 36 | 0 | | | [@elastic/security-solution-platform](https://github.com/orgs/elastic/teams/security-solution-platform) | - | 28 | 0 | 21 | 0 | -| | [@elastic/security-solution-platform](https://github.com/orgs/elastic/teams/security-solution-platform) | - | 67 | 0 | 64 | 0 | +| | [@elastic/security-solution-platform](https://github.com/orgs/elastic/teams/security-solution-platform) | - | 69 | 0 | 65 | 0 | | | [@elastic/security-solution-platform](https://github.com/orgs/elastic/teams/security-solution-platform) | - | 35 | 0 | 23 | 0 | -| | [@elastic/security-solution-platform](https://github.com/orgs/elastic/teams/security-solution-platform) | - | 60 | 0 | 47 | 0 | +| | [@elastic/security-solution-platform](https://github.com/orgs/elastic/teams/security-solution-platform) | - | 62 | 0 | 49 | 0 | | | [@elastic/security-solution-platform](https://github.com/orgs/elastic/teams/security-solution-platform) | - | 210 | 10 | 163 | 0 | | | [@elastic/security-solution-platform](https://github.com/orgs/elastic/teams/security-solution-platform) | - | 26 | 0 | 23 | 0 | | | [@elastic/security-solution-platform](https://github.com/orgs/elastic/teams/security-solution-platform) | - | 120 | 0 | 116 | 0 | @@ -542,6 +543,7 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana'] | | [@elastic/appex-sharedux](https://github.com/orgs/elastic/teams/appex-sharedux) | - | 18 | 0 | 8 | 0 | | | [@elastic/kibana-operations](https://github.com/orgs/elastic/teams/kibana-operations) | - | 45 | 0 | 36 | 0 | | | [@elastic/kibana-operations](https://github.com/orgs/elastic/teams/kibana-operations) | - | 7 | 0 | 6 | 0 | +| | [@elastic/security-threat-hunting-investigations](https://github.com/orgs/elastic/teams/security-threat-hunting-investigations) | - | 4 | 0 | 0 | 0 | | | [@elastic/kibana-security](https://github.com/orgs/elastic/teams/kibana-security) | - | 58 | 0 | 5 | 0 | | | [@elastic/kibana-core](https://github.com/orgs/elastic/teams/kibana-core) | - | 36 | 0 | 15 | 1 | | | [@elastic/kibana-operations](https://github.com/orgs/elastic/teams/kibana-operations) | - | 2 | 0 | 2 | 0 | diff --git a/api_docs/presentation_util.mdx b/api_docs/presentation_util.mdx index bd5fa373355d3..bb5dd22da4a5d 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-04-21 +date: 2023-04-24 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 94ec36d0e142d..fa7c514839f1d 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-04-21 +date: 2023-04-24 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 4d93f65d4f588..052f465958e43 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-04-21 +date: 2023-04-24 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 dc655be6ea3b1..b3e643f23c824 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-04-21 +date: 2023-04-24 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 22004d4715863..3ad92d06484ef 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-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'rollup'] --- import rollupObj from './rollup.devdocs.json'; diff --git a/api_docs/rule_registry.mdx b/api_docs/rule_registry.mdx index 7af25a7132259..2230c7e475fbf 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-04-21 +date: 2023-04-24 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 bf014f1590fc2..fbb641e9ec07b 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-04-21 +date: 2023-04-24 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 1ce68779192c7..4775f7bab3e25 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-04-21 +date: 2023-04-24 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 ba213be4dd0b6..c5cc15bc31edd 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-04-21 +date: 2023-04-24 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 b3d06632c1757..8ed4d9ce7ea8b 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-04-21 +date: 2023-04-24 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 28ee6a6fdb1e9..28d4dfa912c94 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-04-21 +date: 2023-04-24 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 50a27cc71b42c..c4e422b6d4bf5 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-04-21 +date: 2023-04-24 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 2f0a64f88e254..b41919c5f029f 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-04-21 +date: 2023-04-24 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 b87efc25bb18e..eee83610aebe7 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-04-21 +date: 2023-04-24 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 179ea1d1861aa..8b105e90157ca 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-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'screenshotting'] --- import screenshottingObj from './screenshotting.devdocs.json'; diff --git a/api_docs/security.mdx b/api_docs/security.mdx index b93687377e8a6..6a3ef50e5d725 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-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'security'] --- import securityObj from './security.devdocs.json'; diff --git a/api_docs/security_solution.mdx b/api_docs/security_solution.mdx index d193ee3ae36cf..49cd868cf6aee 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-04-21 +date: 2023-04-24 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 863bb87c48c26..89f21abe997f4 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-04-21 +date: 2023-04-24 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 2bb9768f311c2..41725af018c9e 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-04-21 +date: 2023-04-24 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 44b56f58ba474..582bfce7f3253 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-04-21 +date: 2023-04-24 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 563274dd2f750..2467dff1cd464 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-04-21 +date: 2023-04-24 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 bfd22efb5d897..f2fd4e7fff1d3 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-04-21 +date: 2023-04-24 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 7426f4bf3c2e2..12ffc30623e60 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-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'stackConnectors'] --- import stackConnectorsObj from './stack_connectors.devdocs.json'; diff --git a/api_docs/task_manager.mdx b/api_docs/task_manager.mdx index 07663807996de..ec588948cbe18 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-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'taskManager'] --- import taskManagerObj from './task_manager.devdocs.json'; diff --git a/api_docs/telemetry.mdx b/api_docs/telemetry.mdx index 507775a14db43..439a2431b4376 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-04-21 +date: 2023-04-24 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 b0d73ccf96937..a445edd5a138f 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-04-21 +date: 2023-04-24 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 3870cdba63b35..29ee2d6178438 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-04-21 +date: 2023-04-24 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 27c23a0eb88db..fbe7face22441 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-04-21 +date: 2023-04-24 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 b54750be2d606..5ca7b1d27e7d4 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-04-21 +date: 2023-04-24 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 acb04c0a79a88..41df96ec361dd 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-04-21 +date: 2023-04-24 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 a5cb1bf5bb0f1..e0a7c8eca1a70 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-04-21 +date: 2023-04-24 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 8b881a6bf3005..ae29d4e3e787a 100644 --- a/api_docs/triggers_actions_ui.devdocs.json +++ b/api_docs/triggers_actions_ui.devdocs.json @@ -2831,6 +2831,20 @@ "path": "x-pack/plugins/alerting/common/alert_summary.ts", "deprecated": false, "trackAdoption": false + }, + { + "parentPluginId": "triggersActionsUi", + "id": "def-public.AlertStatus.maintenanceWindowIds", + "type": "Array", + "tags": [], + "label": "maintenanceWindowIds", + "description": [], + "signature": [ + "string[] | undefined" + ], + "path": "x-pack/plugins/alerting/common/alert_summary.ts", + "deprecated": false, + "trackAdoption": false } ], "initialIsOpen": false @@ -3045,6 +3059,17 @@ "path": "x-pack/plugins/alerting/common/alert_summary.ts", "deprecated": false, "trackAdoption": false + }, + { + "parentPluginId": "triggersActionsUi", + "id": "def-public.AlertSummary.revision", + "type": "number", + "tags": [], + "label": "revision", + "description": [], + "path": "x-pack/plugins/alerting/common/alert_summary.ts", + "deprecated": false, + "trackAdoption": false } ], "initialIsOpen": false diff --git a/api_docs/triggers_actions_ui.mdx b/api_docs/triggers_actions_ui.mdx index fc92b4f9ff615..0dfa7fa50555b 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-04-21 +date: 2023-04-24 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 | |-------------------|-----------|------------------------|-----------------| -| 540 | 10 | 511 | 49 | +| 542 | 10 | 513 | 49 | ## Client diff --git a/api_docs/ui_actions.mdx b/api_docs/ui_actions.mdx index 2fe2650841250..ba54dfe3328c8 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-04-21 +date: 2023-04-24 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 a46c6ed8f86f6..0111328be34a0 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-04-21 +date: 2023-04-24 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 604ae78db8a3e..97fceac316e4d 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-04-21 +date: 2023-04-24 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 33f08e0514e8c..e0acc17411f17 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-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'unifiedHistogram'] --- import unifiedHistogramObj from './unified_histogram.devdocs.json'; diff --git a/api_docs/unified_search.devdocs.json b/api_docs/unified_search.devdocs.json index 05f2215a9a913..8f697ba358c0c 100644 --- a/api_docs/unified_search.devdocs.json +++ b/api_docs/unified_search.devdocs.json @@ -487,7 +487,7 @@ "OnSaveTextLanguageQueryProps", ") => void) | undefined; showSubmitButton?: boolean | undefined; submitButtonStyle?: \"full\" | \"auto\" | \"iconOnly\" | undefined; suggestionsSize?: ", "SuggestionsListSize", - " | undefined; isScreenshotMode?: boolean | undefined; onFiltersUpdated?: ((filters: ", + " | undefined; isScreenshotMode?: boolean | undefined; submitOnBlur?: boolean | undefined; onFiltersUpdated?: ((filters: ", { "pluginId": "@kbn/es-query", "scope": "common", diff --git a/api_docs/unified_search.mdx b/api_docs/unified_search.mdx index 5c23136a98a7a..9655a20a1dda9 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-04-21 +date: 2023-04-24 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 1b5beadfecf13..f70714d1e5938 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-04-21 +date: 2023-04-24 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 b6d4f177afce2..fb8870f2325d2 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-04-21 +date: 2023-04-24 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 919ee29502898..569cd4f8baf09 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-04-21 +date: 2023-04-24 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 de9da5f50c4dd..71a2488190eb8 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-04-21 +date: 2023-04-24 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 d17bcc483e1c3..aa3747f8476f0 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-04-21 +date: 2023-04-24 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 d73b04c29be18..24c99b2aa19f8 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-04-21 +date: 2023-04-24 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 dfab14fdd94de..d7649559452e8 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-04-21 +date: 2023-04-24 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 096f49cdb1541..c493a75a57076 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-04-21 +date: 2023-04-24 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 e36aeeed77738..b954ce0b719d2 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-04-21 +date: 2023-04-24 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 eba3c262b3db4..b98b43d726106 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-04-21 +date: 2023-04-24 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 504196c391a34..7a80a887ba186 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-04-21 +date: 2023-04-24 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 bc9c59bd6a49c..b760568fac4e3 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-04-21 +date: 2023-04-24 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 b8ac18365cd3c..27174661c8845 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-04-21 +date: 2023-04-24 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 409866a3315d4..f64e0c6adbde7 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-04-21 +date: 2023-04-24 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 348c6c015d348..a8c2abb735484 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-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'visualizations'] --- import visualizationsObj from './visualizations.devdocs.json'; diff --git a/docs/management/connectors/action-types/email.asciidoc b/docs/management/connectors/action-types/email.asciidoc index 65bc85a7d7133..7d6cc75282a2f 100644 --- a/docs/management/connectors/action-types/email.asciidoc +++ b/docs/management/connectors/action-types/email.asciidoc @@ -30,6 +30,7 @@ or as needed when you're creating a rule. For example: [role="screenshot"] image::management/connectors/images/email-connector.png[Email connector] +// NOTE: This is an autogenerated screenshot. Do not edit it directly. [float] [[email-connector-configuration]] @@ -182,6 +183,7 @@ as you're creating or editing the connector in {kib}. For example: [role="screenshot"] image::management/connectors/images/email-params-test.png[Email params test] +// NOTE: This is an autogenerated screenshot. Do not edit it directly. Email actions have the following configuration properties. diff --git a/docs/management/connectors/images/email-connector.png b/docs/management/connectors/images/email-connector.png index b837fa545a4d1..3cce19021937f 100644 Binary files a/docs/management/connectors/images/email-connector.png and b/docs/management/connectors/images/email-connector.png differ diff --git a/docs/management/connectors/images/email-params-test.png b/docs/management/connectors/images/email-params-test.png index 3745bcd3235e9..bc2a9706e6396 100644 Binary files a/docs/management/connectors/images/email-params-test.png and b/docs/management/connectors/images/email-params-test.png differ diff --git a/docs/user/alerting/create-and-manage-rules.asciidoc b/docs/user/alerting/create-and-manage-rules.asciidoc index 7e35cd232566c..23bd7a9ae9fd6 100644 --- a/docs/user/alerting/create-and-manage-rules.asciidoc +++ b/docs/user/alerting/create-and-manage-rules.asciidoc @@ -49,6 +49,7 @@ Each rule type provides its own way of defining the conditions to detect, but an [role="screenshot"] image::images/rule-flyout-rule-conditions.png[UI for defining rule conditions on an index threshold rule,500] +// NOTE: This is an autogenerated screenshot. Do not edit it directly. All rules must have a check interval, which defines how often to evaluate the rule conditions. Checks are queued; they run as close to the defined value as capacity allows. diff --git a/docs/user/alerting/images/rule-flyout-rule-conditions.png b/docs/user/alerting/images/rule-flyout-rule-conditions.png index ffdf870dfa001..2810ac5e9a517 100644 Binary files a/docs/user/alerting/images/rule-flyout-rule-conditions.png and b/docs/user/alerting/images/rule-flyout-rule-conditions.png differ diff --git a/examples/content_management_examples/kibana.jsonc b/examples/content_management_examples/kibana.jsonc index 24759d7abdfb6..ae7cd95e4190a 100644 --- a/examples/content_management_examples/kibana.jsonc +++ b/examples/content_management_examples/kibana.jsonc @@ -9,7 +9,9 @@ "browser": true, "requiredPlugins": [ "contentManagement", - "developerExamples" + "developerExamples", + "kibanaReact", + "savedObjectsTaggingOss" ] } } diff --git a/examples/content_management_examples/public/examples/index.tsx b/examples/content_management_examples/public/examples/index.tsx index 715f3a6809581..611bcde59d445 100644 --- a/examples/content_management_examples/public/examples/index.tsx +++ b/examples/content_management_examples/public/examples/index.tsx @@ -8,22 +8,67 @@ import React from 'react'; import ReactDOM from 'react-dom'; -import { EuiPageTemplate } from '@elastic/eui'; +// eslint-disable-next-line no-restricted-imports +import { Router, Switch, Route, Redirect } from 'react-router-dom'; +import { RedirectAppLinks } from '@kbn/shared-ux-link-redirect-app'; +import { EuiPageTemplate, EuiSideNav } from '@elastic/eui'; import { AppMountParameters, CoreStart } from '@kbn/core/public'; import { StartDeps } from '../types'; import { TodoApp } from './todos'; +import { MSearchApp } from './msearch'; export const renderApp = ( - { notifications }: CoreStart, - { contentManagement }: StartDeps, - { element }: AppMountParameters + core: CoreStart, + { contentManagement, savedObjectsTaggingOss }: StartDeps, + { element, history }: AppMountParameters ) => { ReactDOM.render( - - - - - , + + + + + + + + + + + + + + + + + + + + + , element ); diff --git a/examples/content_management_examples/public/examples/msearch/index.tsx b/examples/content_management_examples/public/examples/msearch/index.tsx new file mode 100644 index 0000000000000..7caf5d200748a --- /dev/null +++ b/examples/content_management_examples/public/examples/msearch/index.tsx @@ -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 { MSearchApp } from './msearch_app'; diff --git a/examples/content_management_examples/public/examples/msearch/msearch_app.tsx b/examples/content_management_examples/public/examples/msearch/msearch_app.tsx new file mode 100644 index 0000000000000..aa2bc6c1a8b7b --- /dev/null +++ b/examples/content_management_examples/public/examples/msearch/msearch_app.tsx @@ -0,0 +1,42 @@ +/* + * 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 { ContentClientProvider, type ContentClient } from '@kbn/content-management-plugin/public'; +import { TableListViewKibanaProvider } from '@kbn/content-management-table-list'; +import type { CoreStart } from '@kbn/core/public'; +import { toMountPoint } from '@kbn/kibana-react-plugin/public'; +import { FormattedRelative, I18nProvider } from '@kbn/i18n-react'; +import { SavedObjectTaggingOssPluginStart } from '@kbn/saved-objects-tagging-oss-plugin/public'; +import { MSearchTable } from './msearch_table'; + +export const MSearchApp = (props: { + contentClient: ContentClient; + core: CoreStart; + savedObjectsTagging: SavedObjectTaggingOssPluginStart; +}) => { + return ( + + + + + + + + ); +}; diff --git a/examples/content_management_examples/public/examples/msearch/msearch_table.tsx b/examples/content_management_examples/public/examples/msearch/msearch_table.tsx new file mode 100644 index 0000000000000..47eaab4ba602b --- /dev/null +++ b/examples/content_management_examples/public/examples/msearch/msearch_table.tsx @@ -0,0 +1,62 @@ +/* + * 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 { TableListView, UserContentCommonSchema } from '@kbn/content-management-table-list'; +import { useContentClient } from '@kbn/content-management-plugin/public'; +import React from 'react'; +import { SavedObjectsFindOptionsReference } from '@kbn/core-saved-objects-api-browser'; + +const LISTING_LIMIT = 1000; + +export const MSearchTable = () => { + const contentClient = useContentClient(); + + const findItems = async ( + searchQuery: string, + refs?: { + references?: SavedObjectsFindOptionsReference[]; + referencesToExclude?: SavedObjectsFindOptionsReference[]; + } + ) => { + const { hits, pagination } = await contentClient.mSearch({ + query: { + text: searchQuery, + limit: LISTING_LIMIT, + cursor: '1', + tags: { + included: refs?.references?.map((ref) => ref.id), + excluded: refs?.referencesToExclude?.map((ref) => ref.id), + }, + }, + contentTypes: [{ contentTypeId: 'map' }], // TODO: improve types to not require objects here? + }); + + // TODO: needs to have logic of extracting common schema from an unknown mSearch hit: hits.map(hit => cm.convertToCommonSchema(hit)) + // for now we just assume that mSearch hit satisfies UserContentCommonSchema + + return { hits, total: pagination.total }; + }; + + return ( + No data found. Try to install some sample data first.} + onClickTitle={(item) => { + alert(`Clicked item ${item.attributes.title} (${item.id})`); + }} + /> + ); +}; diff --git a/examples/content_management_examples/public/types.ts b/examples/content_management_examples/public/types.ts index 83e65c5455afd..747bfd961c434 100644 --- a/examples/content_management_examples/public/types.ts +++ b/examples/content_management_examples/public/types.ts @@ -11,6 +11,7 @@ import { ContentManagementPublicStart, } from '@kbn/content-management-plugin/public'; import { DeveloperExamplesSetup } from '@kbn/developer-examples-plugin/public'; +import { SavedObjectTaggingOssPluginStart } from '@kbn/saved-objects-tagging-oss-plugin/public'; export interface SetupDeps { contentManagement: ContentManagementPublicSetup; @@ -19,4 +20,5 @@ export interface SetupDeps { export interface StartDeps { contentManagement: ContentManagementPublicStart; + savedObjectsTaggingOss: SavedObjectTaggingOssPluginStart; } diff --git a/examples/content_management_examples/tsconfig.json b/examples/content_management_examples/tsconfig.json index f383cd9d9b33e..7f07213ce82b6 100644 --- a/examples/content_management_examples/tsconfig.json +++ b/examples/content_management_examples/tsconfig.json @@ -19,5 +19,11 @@ "@kbn/developer-examples-plugin", "@kbn/content-management-plugin", "@kbn/core-application-browser", + "@kbn/shared-ux-link-redirect-app", + "@kbn/content-management-table-list", + "@kbn/kibana-react-plugin", + "@kbn/i18n-react", + "@kbn/saved-objects-tagging-oss-plugin", + "@kbn/core-saved-objects-api-browser", ] } diff --git a/examples/guided_onboarding_example/public/components/app.tsx b/examples/guided_onboarding_example/public/components/app.tsx index 41b9f62ddc075..1a1083a10f1f3 100755 --- a/examples/guided_onboarding_example/public/components/app.tsx +++ b/examples/guided_onboarding_example/public/components/app.tsx @@ -25,6 +25,7 @@ import { GuidedOnboardingPluginStart } from '@kbn/guided-onboarding-plugin/publi import { StepTwo } from './step_two'; import { StepOne } from './step_one'; import { StepThree } from './step_three'; +import { StepFour } from './step_four'; import { Main } from './main'; interface GuidedOnboardingExampleAppDeps { @@ -65,6 +66,13 @@ export const GuidedOnboardingExampleApp = (props: GuidedOnboardingExampleAppDeps + p + ( + + )} + /> diff --git a/examples/guided_onboarding_example/public/components/main.tsx b/examples/guided_onboarding_example/public/components/main.tsx index 4e7c3843f5f34..d5f1febccfbce 100644 --- a/examples/guided_onboarding_example/public/components/main.tsx +++ b/examples/guided_onboarding_example/public/components/main.tsx @@ -345,6 +345,14 @@ export const Main = (props: MainProps) => { /> + + history.push('stepFour')}> + + + diff --git a/examples/guided_onboarding_example/public/components/step_four.tsx b/examples/guided_onboarding_example/public/components/step_four.tsx new file mode 100644 index 0000000000000..57df11d422519 --- /dev/null +++ b/examples/guided_onboarding_example/public/components/step_four.tsx @@ -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 React, { useEffect, useState } from 'react'; + +import { EuiButton, EuiSpacer, EuiText, EuiTitle } from '@elastic/eui'; + +import { GuidedOnboardingPluginStart } from '@kbn/guided-onboarding-plugin/public/types'; +import { FormattedMessage } from '@kbn/i18n-react'; +import { + EuiPageContentHeader_Deprecated as EuiPageContentHeader, + EuiPageContentBody_Deprecated as EuiPageContentBody, + EuiCode, +} from '@elastic/eui'; +import { RouteComponentProps } from 'react-router-dom'; + +interface StepFourProps { + guidedOnboarding: GuidedOnboardingPluginStart; +} + +export const StepFour = (props: StepFourProps & RouteComponentProps<{ indexName: string }>) => { + const { + guidedOnboarding: { guidedOnboardingApi }, + match: { + params: { indexName }, + }, + } = props; + + const [, setIsTourStepOpen] = useState(false); + + useEffect(() => { + const subscription = guidedOnboardingApi + ?.isGuideStepActive$('testGuide', 'step4') + .subscribe((isStepActive) => { + setIsTourStepOpen(isStepActive); + }); + return () => subscription?.unsubscribe(); + }, [guidedOnboardingApi]); + + return ( + <> + + +

+ +

+
+
+ + +

+ {indexName: {indexName}} + ), + }} + /> +

+
+ + + { + await guidedOnboardingApi?.completeGuideStep('testGuide', 'step4'); + }} + > + Complete step 4 + +
+ + ); +}; diff --git a/examples/guided_onboarding_example/public/components/step_one.tsx b/examples/guided_onboarding_example/public/components/step_one.tsx index fd5cb132b6b91..632b902e14a3e 100644 --- a/examples/guided_onboarding_example/public/components/step_one.tsx +++ b/examples/guided_onboarding_example/public/components/step_one.tsx @@ -16,6 +16,11 @@ import { EuiPageContentHeader_Deprecated as EuiPageContentHeader, EuiPageContentBody_Deprecated as EuiPageContentBody, EuiSpacer, + EuiCode, + EuiFieldText, + EuiFlexGroup, + EuiFlexItem, + EuiFormRow, } from '@elastic/eui'; import useObservable from 'react-use/lib/useObservable'; @@ -30,6 +35,7 @@ export const StepOne = ({ guidedOnboarding }: GuidedOnboardingExampleAppDeps) => const { guidedOnboardingApi } = guidedOnboarding; const [isTourStepOpen, setIsTourStepOpen] = useState(false); + const [indexName, setIndexName] = useState('test1234'); const isTourActive = useObservable( guidedOnboardingApi!.isGuideStepActive$('testGuide', 'step1'), @@ -59,30 +65,59 @@ export const StepOne = ({ guidedOnboarding }: GuidedOnboardingExampleAppDeps) => Test guide, step 1, a EUI tour will be displayed, pointing to the button below." />

+

+ indexName, + }} + /> +

- -

Click this button to complete step 1.

- - } - isStepOpen={isTourStepOpen} - minWidth={300} - onFinish={() => setIsTourStepOpen(false)} - step={1} - stepsTotal={1} - title="Step 1" - anchorPosition="rightUp" - > - { - await guidedOnboardingApi?.completeGuideStep('testGuide', 'step1'); - }} - > - Complete step 1 - -
+ + + + } + > + setIndexName(e.target.value)} /> + + + + + +

Click this button to complete step 1.

+ + } + isStepOpen={isTourStepOpen} + minWidth={300} + onFinish={() => setIsTourStepOpen(false)} + step={1} + stepsTotal={1} + title="Step 1" + anchorPosition="rightUp" + > + { + await guidedOnboardingApi?.completeGuideStep('testGuide', 'step1', { + indexName, + }); + }} + > + Complete step 1 + +
+
+
+
); diff --git a/package.json b/package.json index 58b87303d4a02..56fdffa498b05 100644 --- a/package.json +++ b/package.json @@ -470,6 +470,7 @@ "@kbn/maps-plugin": "link:x-pack/plugins/maps", "@kbn/ml-agg-utils": "link:x-pack/packages/ml/agg_utils", "@kbn/ml-date-picker": "link:x-pack/packages/ml/date_picker", + "@kbn/ml-error-utils": "link:x-pack/packages/ml/error_utils", "@kbn/ml-is-defined": "link:x-pack/packages/ml/is_defined", "@kbn/ml-is-populated-object": "link:x-pack/packages/ml/is_populated_object", "@kbn/ml-local-storage": "link:x-pack/packages/ml/local_storage", @@ -672,6 +673,7 @@ "@kbn/upgrade-assistant-plugin": "link:x-pack/plugins/upgrade_assistant", "@kbn/url-drilldown-plugin": "link:x-pack/plugins/drilldowns/url_drilldown", "@kbn/url-forwarding-plugin": "link:src/plugins/url_forwarding", + "@kbn/url-state": "link:packages/kbn-url-state", "@kbn/usage-collection-plugin": "link:src/plugins/usage_collection", "@kbn/usage-collection-test-plugin": "link:test/plugin_functional/plugins/usage_collection", "@kbn/user-profile-components": "link:packages/kbn-user-profile-components", diff --git a/packages/content-management/table_list/src/services.tsx b/packages/content-management/table_list/src/services.tsx index 5edc16d9a915d..d7752a270c86c 100644 --- a/packages/content-management/table_list/src/services.tsx +++ b/packages/content-management/table_list/src/services.tsx @@ -80,9 +80,7 @@ export interface TableListViewKibanaDependencies { core: { application: { capabilities: { - advancedSettings?: { - save: boolean; - }; + [key: string]: Readonly>>; }; getUrlForApp: (app: string, options: { path: string }) => string; currentAppId$: Observable; diff --git a/packages/kbn-apm-synthtrace-client/src/lib/apm/instance.ts b/packages/kbn-apm-synthtrace-client/src/lib/apm/instance.ts index f0766301b238e..bedae100bc933 100644 --- a/packages/kbn-apm-synthtrace-client/src/lib/apm/instance.ts +++ b/packages/kbn-apm-synthtrace-client/src/lib/apm/instance.ts @@ -6,6 +6,7 @@ * Side Public License, v 1. */ +import { createHash } from 'crypto'; import { ApmError } from './apm_error'; import { Entity } from '../entity'; import { Metricset } from './metricset'; @@ -63,19 +64,11 @@ export class Instance extends Entity { }); } - error({ - message, - type, - groupingName, - }: { - message: string; - type?: string; - groupingName?: string; - }) { + error({ message, type }: { message: string; type?: string }) { return new ApmError({ ...this.fields, 'error.exception': [{ message, ...(type ? { type } : {}) }], - 'error.grouping_name': groupingName || message, + 'error.grouping_name': getErrorGroupingKey(message), }); } @@ -97,3 +90,7 @@ export class Instance extends Entity { }); } } + +export function getErrorGroupingKey(content: string) { + return createHash('sha256').update(content).digest('hex'); +} diff --git a/packages/kbn-apm-synthtrace/src/lib/utils/get_synthtrace_environment.ts b/packages/kbn-apm-synthtrace/src/lib/utils/get_synthtrace_environment.ts index b7456be994b8b..f344c8f79fa6a 100644 --- a/packages/kbn-apm-synthtrace/src/lib/utils/get_synthtrace_environment.ts +++ b/packages/kbn-apm-synthtrace/src/lib/utils/get_synthtrace_environment.ts @@ -8,6 +8,6 @@ import path from 'path'; -export function getSynthtraceEnvironment(filename: string) { - return `Synthtrace: ${path.parse(filename).name}`; +export function getSynthtraceEnvironment(filename: string, suffix = '') { + return `Synthtrace: ${path.parse(filename).name} ${suffix}`.trim(); } diff --git a/packages/kbn-apm-synthtrace/src/scenarios/continuous_rollups.ts b/packages/kbn-apm-synthtrace/src/scenarios/continuous_rollups.ts index c4eba8b8c7d5f..0a346dcf99542 100644 --- a/packages/kbn-apm-synthtrace/src/scenarios/continuous_rollups.ts +++ b/packages/kbn-apm-synthtrace/src/scenarios/continuous_rollups.ts @@ -10,6 +10,11 @@ import { apm, ApmFields } from '@kbn/apm-synthtrace-client'; import { merge, range as lodashRange } from 'lodash'; import { Scenario } from '../cli/scenario'; import { ComponentTemplateName } from '../lib/apm/client/apm_synthtrace_es_client'; +import { getSynthtraceEnvironment } from '../lib/utils/get_synthtrace_environment'; + +const ENVIRONMENTS = ['production', 'development'].map((env) => + getSynthtraceEnvironment(__filename, env) +); const scenario: Scenario = async ({ logger, scenarioOpts }) => { const { @@ -37,7 +42,6 @@ const scenario: Scenario = async ({ logger, scenarioOpts }) => { }, generate: ({ range }) => { const TRANSACTION_TYPES = ['request', 'custom']; - const ENVIRONMENTS = ['production', 'development']; const MIN_DURATION = 10; const MAX_DURATION = 1000; diff --git a/packages/kbn-apm-synthtrace/src/scenarios/high_throughput.ts b/packages/kbn-apm-synthtrace/src/scenarios/high_throughput.ts index f03d909db835f..2e7d8c22a4696 100644 --- a/packages/kbn-apm-synthtrace/src/scenarios/high_throughput.ts +++ b/packages/kbn-apm-synthtrace/src/scenarios/high_throughput.ts @@ -9,6 +9,9 @@ import { random } from 'lodash'; import { apm, Instance, ApmFields } from '@kbn/apm-synthtrace-client'; import { Scenario } from '../cli/scenario'; +import { getSynthtraceEnvironment } from '../lib/utils/get_synthtrace_environment'; + +const ENVIRONMENT = getSynthtraceEnvironment(__filename); const scenario: Scenario = async ({ logger }) => { const languages = ['go', 'dotnet', 'java', 'python']; @@ -22,7 +25,7 @@ const scenario: Scenario = async ({ logger }) => { apm .service({ name: `${service}-${languages[index % languages.length]}`, - environment: 'production', + environment: ENVIRONMENT, agentName: languages[index % languages.length], }) .instance(`instance-${index}`) diff --git a/packages/kbn-apm-synthtrace/src/scenarios/other_bucket_group.ts b/packages/kbn-apm-synthtrace/src/scenarios/other_bucket_group.ts index 8ad994e4ff848..49fcc2f6ac47b 100644 --- a/packages/kbn-apm-synthtrace/src/scenarios/other_bucket_group.ts +++ b/packages/kbn-apm-synthtrace/src/scenarios/other_bucket_group.ts @@ -8,6 +8,11 @@ import { apm, ApmFields } from '@kbn/apm-synthtrace-client'; import { range as lodashRange } from 'lodash'; import { Scenario } from '../cli/scenario'; +import { getSynthtraceEnvironment } from '../lib/utils/get_synthtrace_environment'; + +const ENVIRONMENTS = ['production', 'development'].map((env) => + getSynthtraceEnvironment(__filename, env) +); const scenario: Scenario = async ({ logger, scenarioOpts }) => { const { services: numServices = 10, txGroups: numTxGroups = 10 } = scenarioOpts ?? {}; @@ -15,7 +20,6 @@ const scenario: Scenario = async ({ logger, scenarioOpts }) => { return { generate: ({ range }) => { const TRANSACTION_TYPES = ['request']; - const ENVIRONMENTS = ['production', 'development']; const MIN_DURATION = 10; const MAX_DURATION = 1000; diff --git a/packages/kbn-apm-synthtrace/src/scenarios/services_without_transactions.ts b/packages/kbn-apm-synthtrace/src/scenarios/services_without_transactions.ts index 94e3ea20ba787..c7d535028a1af 100644 --- a/packages/kbn-apm-synthtrace/src/scenarios/services_without_transactions.ts +++ b/packages/kbn-apm-synthtrace/src/scenarios/services_without_transactions.ts @@ -7,20 +7,23 @@ */ import { apm, ApmFields } from '@kbn/apm-synthtrace-client'; import { Scenario } from '../cli/scenario'; +import { getSynthtraceEnvironment } from '../lib/utils/get_synthtrace_environment'; + +const ENVIRONMENT = getSynthtraceEnvironment(__filename); const scenario: Scenario = async ({ logger, scenarioOpts }) => { return { generate: ({ range }) => { const withTx = apm - .service('service-with-transactions', 'production', 'java') + .service('service-with-transactions', ENVIRONMENT, 'java') .instance('instance'); const withErrorsOnly = apm - .service('service-with-errors-only', 'production', 'java') + .service('service-with-errors-only', ENVIRONMENT, 'java') .instance('instance'); const withAppMetricsOnly = apm - .service('service-with-app-metrics-only', 'production', 'java') + .service('service-with-app-metrics-only', ENVIRONMENT, 'java') .instance('instance'); return range diff --git a/packages/kbn-apm-synthtrace/src/test/scenarios/05_transactions_with_errors.test.ts b/packages/kbn-apm-synthtrace/src/test/scenarios/05_transactions_with_errors.test.ts index fdb80693bf74c..a8848c1954e40 100644 --- a/packages/kbn-apm-synthtrace/src/test/scenarios/05_transactions_with_errors.test.ts +++ b/packages/kbn-apm-synthtrace/src/test/scenarios/05_transactions_with_errors.test.ts @@ -60,7 +60,11 @@ describe('transactions with errors', () => { .errors(instance.error({ message: 'test error' }).timestamp(timestamp)) .serialize(); - expect(error['error.grouping_name']).toEqual('test error'); - expect(error['error.grouping_key']).toMatchInlineSnapshot(`"0000000000000000000000test error"`); + expect(error['error.grouping_name']).toEqual( + '4274b1899eba687801198c89f64a3fdade080a475c8a54881ba8fa10e7f45691' + ); + expect(error['error.grouping_key']).toMatchInlineSnapshot( + `"4274b1899eba687801198c89f64a3fdade080a475c8a54881ba8fa10e7f45691"` + ); }); }); diff --git a/packages/kbn-cell-actions/README.md b/packages/kbn-cell-actions/README.md index 7cacff6becda6..e1ae1a73a8369 100644 --- a/packages/kbn-cell-actions/README.md +++ b/packages/kbn-cell-actions/README.md @@ -6,7 +6,7 @@ Example: ```JSX [...] - + Hover me diff --git a/packages/kbn-cell-actions/src/__stories__/cell_actions.stories.tsx b/packages/kbn-cell-actions/src/__stories__/cell_actions.stories.tsx index 657dcc93416b8..49f812cd9005a 100644 --- a/packages/kbn-cell-actions/src/__stories__/cell_actions.stories.tsx +++ b/packages/kbn-cell-actions/src/__stories__/cell_actions.stories.tsx @@ -50,8 +50,8 @@ export const DefaultWithControls = CellActionsTemplate.bind({}); DefaultWithControls.argTypes = { mode: { - options: [CellActionsMode.HOVER, CellActionsMode.INLINE], - defaultValue: CellActionsMode.HOVER, + options: [CellActionsMode.HOVER_DOWN, CellActionsMode.INLINE], + defaultValue: CellActionsMode.HOVER_DOWN, control: { type: 'radio', }, @@ -72,8 +72,14 @@ export const CellActionInline = ({}: {}) => ( ); -export const CellActionHoverPopup = ({}: {}) => ( - +export const CellActionHoverPopoverDown = ({}: {}) => ( + + Hover me + +); + +export const CellActionHoverPopoverRight = ({}: {}) => ( + Hover me ); diff --git a/packages/kbn-cell-actions/src/components/cell_actions.test.tsx b/packages/kbn-cell-actions/src/components/cell_actions.test.tsx index ec266889fe255..36d0482ebf84d 100644 --- a/packages/kbn-cell-actions/src/components/cell_actions.test.tsx +++ b/packages/kbn-cell-actions/src/components/cell_actions.test.tsx @@ -15,6 +15,11 @@ import { CellActionsProvider } from '../context/cell_actions_context'; const TRIGGER_ID = 'test-trigger-id'; const FIELD = { name: 'name', value: '123', type: 'text' }; +jest.mock('./hover_actions_popover', () => ({ + HoverActionsPopover: jest.fn((props) => ( + {props.anchorPosition} + )), +})); describe('CellActions', () => { it('renders', async () => { const getActionsPromise = Promise.resolve([]); @@ -54,13 +59,33 @@ describe('CellActions', () => { expect(queryByTestId('inlineActions')).toBeInTheDocument(); }); - it('renders HoverActionsPopover when mode is HOVER', async () => { + it('renders HoverActionsPopover when mode is HOVER_DOWN', async () => { const getActionsPromise = Promise.resolve([]); const getActions = () => getActionsPromise; - const { queryByTestId } = render( + const { getByTestId } = render( + + + Field value + + + ); + + await act(async () => { + await getActionsPromise; + }); + + expect(getByTestId('hoverActionsPopover')).toBeInTheDocument(); + expect(getByTestId('hoverActionsPopover')).toHaveTextContent('downCenter'); + }); + + it('renders HoverActionsPopover when mode is HOVER_RIGHT', async () => { + const getActionsPromise = Promise.resolve([]); + const getActions = () => getActionsPromise; + + const { getByTestId } = render( - + Field value @@ -70,6 +95,7 @@ describe('CellActions', () => { await getActionsPromise; }); - expect(queryByTestId('hoverActionsPopover')).toBeInTheDocument(); + expect(getByTestId('hoverActionsPopover')).toBeInTheDocument(); + expect(getByTestId('hoverActionsPopover')).toHaveTextContent('rightCenter'); }); }); diff --git a/packages/kbn-cell-actions/src/components/cell_actions.tsx b/packages/kbn-cell-actions/src/components/cell_actions.tsx index f6d3288ed6f7d..df6f957575c20 100644 --- a/packages/kbn-cell-actions/src/components/cell_actions.tsx +++ b/packages/kbn-cell-actions/src/components/cell_actions.tsx @@ -36,11 +36,17 @@ export const CellActions: React.FC = ({ [field, triggerId, metadata] ); + const anchorPosition = useMemo( + () => (mode === CellActionsMode.HOVER_DOWN ? 'downCenter' : 'rightCenter'), + [mode] + ); + const dataTestSubj = `cellActions-renderContent-${field.name}`; - if (mode === CellActionsMode.HOVER) { + if (mode === CellActionsMode.HOVER_DOWN || mode === CellActionsMode.HOVER_RIGHT) { return (
= ({ {children} {}, + actions: [], + button: , +}; describe('ExtraActionsPopOver', () => { it('renders', () => { - const { queryByTestId } = render( - {}} - actions={[]} - button={} - /> - ); + const { queryByTestId } = render(); expect(queryByTestId('extraActionsPopOver')).toBeInTheDocument(); }); @@ -33,11 +33,10 @@ describe('ExtraActionsPopOver', () => { const action = { ...makeAction('test-action'), execute: executeAction }; const { getByLabelText } = render( } /> ); @@ -56,13 +55,7 @@ describe('ExtraActionsPopOverWithAnchor', () => { it('renders', () => { const { queryByTestId } = render( - {}} - actions={[]} - anchorRef={{ current: anchorElement }} - /> + ); expect(queryByTestId('extraActionsPopOverWithAnchor')).toBeInTheDocument(); @@ -74,7 +67,7 @@ describe('ExtraActionsPopOverWithAnchor', () => { const action = { ...makeAction('test-action'), execute: executeAction }; const { getByLabelText } = render( void; @@ -32,6 +33,7 @@ interface ActionsPopOverProps { } export const ExtraActionsPopOver: React.FC = ({ + anchorPosition, actions, actionContext, isOpen, @@ -43,7 +45,7 @@ export const ExtraActionsPopOver: React.FC = ({ isOpen={isOpen} closePopover={closePopOver} panelPaddingSize="xs" - anchorPosition={'downCenter'} + anchorPosition={anchorPosition} hasArrow repositionOnScroll ownFocus @@ -59,11 +61,15 @@ export const ExtraActionsPopOver: React.FC = ({ ); interface ExtraActionsPopOverWithAnchorProps - extends Pick { + extends Pick< + ActionsPopOverProps, + 'anchorPosition' | 'actionContext' | 'closePopOver' | 'isOpen' | 'actions' + > { anchorRef: React.RefObject; } export const ExtraActionsPopOverWithAnchor = ({ + anchorPosition, anchorRef, actionContext, isOpen, @@ -77,7 +83,7 @@ export const ExtraActionsPopOverWithAnchor = ({ isOpen={isOpen} closePopover={closePopOver} panelPaddingSize="xs" - anchorPosition={'downCenter'} + anchorPosition={anchorPosition} hasArrow={false} repositionOnScroll ownFocus diff --git a/packages/kbn-cell-actions/src/components/hover_actions_popover.test.tsx b/packages/kbn-cell-actions/src/components/hover_actions_popover.test.tsx index b30ca63e52ec0..de68ccd6fca51 100644 --- a/packages/kbn-cell-actions/src/components/hover_actions_popover.test.tsx +++ b/packages/kbn-cell-actions/src/components/hover_actions_popover.test.tsx @@ -13,11 +13,17 @@ import { makeAction } from '../mocks/helpers'; import { CellActionExecutionContext } from '../types'; import { HoverActionsPopover } from './hover_actions_popover'; -describe('HoverActionsPopover', () => { - const actionContext = { +const defaultProps = { + anchorPosition: 'rightCenter' as const, + disabledActionTypes: [], + visibleCellActions: 4, + actionContext: { trigger: { id: 'triggerId' }, field: { name: 'fieldName' }, - } as CellActionExecutionContext; + } as CellActionExecutionContext, + showActionTooltips: false, +}; +describe('HoverActionsPopover', () => { const TestComponent = () => ; jest.useFakeTimers(); @@ -25,13 +31,7 @@ describe('HoverActionsPopover', () => { const getActions = () => Promise.resolve([]); const { queryByTestId } = render( - + ); expect(queryByTestId('hoverActionsPopover')).toBeInTheDocument(); @@ -44,12 +44,7 @@ describe('HoverActionsPopover', () => { const { queryByLabelText, getByTestId } = render( - + @@ -70,12 +65,7 @@ describe('HoverActionsPopover', () => { const { queryByLabelText, getByTestId } = render( - + @@ -101,12 +91,7 @@ describe('HoverActionsPopover', () => { const { getByTestId } = render( - + @@ -127,12 +112,7 @@ describe('HoverActionsPopover', () => { const { getByTestId, getByLabelText } = render( - + @@ -162,12 +142,7 @@ describe('HoverActionsPopover', () => { const { getByTestId, queryByLabelText } = render( - + @@ -191,6 +166,44 @@ describe('HoverActionsPopover', () => { expect(queryByLabelText('test-action-2')).toBeInTheDocument(); expect(queryByLabelText('test-action-3')).toBeInTheDocument(); }); + it('does not add css positioning when anchorPosition = downCenter', async () => { + const getActionsPromise = Promise.resolve([makeAction('test-action')]); + const getActions = () => getActionsPromise; + + const { getByLabelText, getByTestId } = render( + + + + + + ); + + await hoverElement(getByTestId('test-component'), async () => { + await getActionsPromise; + jest.runAllTimers(); + }); + + expect(Object.values(getByLabelText('Actions').style).includes('margin-top')).toEqual(false); + }); + it('adds css positioning when anchorPosition = rightCenter', async () => { + const getActionsPromise = Promise.resolve([makeAction('test-action')]); + const getActions = () => getActionsPromise; + + const { getByLabelText, getByTestId } = render( + + + + + + ); + + await hoverElement(getByTestId('test-component'), async () => { + await getActionsPromise; + jest.runAllTimers(); + }); + + expect(Object.values(getByLabelText('Actions').style).includes('margin-top')).toEqual(true); + }); }); const hoverElement = async (element: Element, waitForChange: () => Promise) => { diff --git a/packages/kbn-cell-actions/src/components/hover_actions_popover.tsx b/packages/kbn-cell-actions/src/components/hover_actions_popover.tsx index 62ea3e766eaf7..dd087aa755307 100644 --- a/packages/kbn-cell-actions/src/components/hover_actions_popover.tsx +++ b/packages/kbn-cell-actions/src/components/hover_actions_popover.tsx @@ -36,6 +36,7 @@ const hoverContentWrapperCSS = css` const HOVER_INTENT_DELAY = 100; // ms interface Props { + anchorPosition: 'downCenter' | 'rightCenter'; children: React.ReactNode; visibleCellActions: number; actionContext: CellActionExecutionContext; @@ -44,6 +45,7 @@ interface Props { } export const HoverActionsPopover: React.FC = ({ + anchorPosition, children, visibleCellActions, actionContext, @@ -115,12 +117,17 @@ export const HoverActionsPopover: React.FC = ({ ); }, [onMouseEnter, closeExtraActions, children]); + const panelStyle = useMemo( + () => (anchorPosition === 'rightCenter' ? { marginTop: euiThemeVars.euiSizeS } : {}), + [anchorPosition] + ); + return ( <>
= ({
{ - const actionContext = { trigger: { id: 'triggerId' } } as CellActionExecutionContext; it('renders', async () => { const getActionsPromise = Promise.resolve([]); const getActions = () => getActionsPromise; const { queryByTestId } = render( - + ); @@ -47,12 +48,7 @@ describe('InlineActions', () => { const getActions = () => getActionsPromise; const { queryAllByRole } = render( - + ); diff --git a/packages/kbn-cell-actions/src/components/inline_actions.tsx b/packages/kbn-cell-actions/src/components/inline_actions.tsx index cd2cfcc88edf0..c46fb5c57af76 100644 --- a/packages/kbn-cell-actions/src/components/inline_actions.tsx +++ b/packages/kbn-cell-actions/src/components/inline_actions.tsx @@ -17,6 +17,7 @@ import { useLoadActions } from '../hooks/use_load_actions'; interface InlineActionsProps { actionContext: CellActionExecutionContext; + anchorPosition: 'rightCenter' | 'downCenter'; showActionTooltips: boolean; visibleCellActions: number; disabledActionTypes: string[]; @@ -24,6 +25,7 @@ interface InlineActionsProps { export const InlineActions: React.FC = ({ actionContext, + anchorPosition, showActionTooltips, visibleCellActions, disabledActionTypes, @@ -47,10 +49,9 @@ export const InlineActions: React.FC = ({ data-test-subj="inlineActions" className={`inlineActions ${isPopoverOpen ? 'inlineActions-popoverOpen' : ''}`} > - {visibleActions.map((action, index) => ( - + {visibleActions.map((action) => ( + = ({ & { + getState: () => State; +}; + export interface ExpandableFlyoutProviderProps { /** * React children */ children: React.ReactNode; + /** + * Triggered whenever flyout state changes. You can use it to store it's state somewhere for instance. + */ + onChanges?: (state: State) => void; + /** + * Triggered whenever flyout is closed. This is independent from the onChanges above. + */ + onClosePanels?: () => void; } /** * Wrap your plugin with this context for the ExpandableFlyout React component. */ -export const ExpandableFlyoutProvider = ({ children }: ExpandableFlyoutProviderProps) => { +export const ExpandableFlyoutProvider = React.forwardRef< + ExpandableFlyoutApi, + ExpandableFlyoutProviderProps +>(({ children, onChanges = () => {}, onClosePanels = () => {} }, ref) => { const [state, dispatch] = useReducer(reducer, initialState); + useEffect(() => { + const closed = !state.right; + if (closed) { + // manual close is singalled via separate callback + return; + } + + onChanges(state); + }, [state, onChanges]); + const openPanels = useCallback( ({ right, @@ -87,40 +120,45 @@ export const ExpandableFlyoutProvider = ({ children }: ExpandableFlyoutProviderP const openRightPanel = useCallback( (panel: FlyoutPanel) => dispatch({ type: ActionType.openRightPanel, payload: panel }), - [dispatch] + [] ); const openLeftPanel = useCallback( (panel: FlyoutPanel) => dispatch({ type: ActionType.openLeftPanel, payload: panel }), - [dispatch] + [] ); const openPreviewPanel = useCallback( (panel: FlyoutPanel) => dispatch({ type: ActionType.openPreviewPanel, payload: panel }), - [dispatch] + [] ); - const closeRightPanel = useCallback( - () => dispatch({ type: ActionType.closeRightPanel }), - [dispatch] - ); + const closeRightPanel = useCallback(() => dispatch({ type: ActionType.closeRightPanel }), []); - const closeLeftPanel = useCallback( - () => dispatch({ type: ActionType.closeLeftPanel }), - [dispatch] - ); + const closeLeftPanel = useCallback(() => dispatch({ type: ActionType.closeLeftPanel }), []); - const closePreviewPanel = useCallback( - () => dispatch({ type: ActionType.closePreviewPanel }), - [dispatch] - ); + const closePreviewPanel = useCallback(() => dispatch({ type: ActionType.closePreviewPanel }), []); const previousPreviewPanel = useCallback( () => dispatch({ type: ActionType.previousPreviewPanel }), - [dispatch] + [] ); - const closePanels = useCallback(() => dispatch({ type: ActionType.closeFlyout }), [dispatch]); + const closePanels = useCallback(() => { + dispatch({ type: ActionType.closeFlyout }); + onClosePanels(); + }, [onClosePanels]); + + useImperativeHandle( + ref, + () => { + return { + openFlyout: openPanels, + getState: () => state, + }; + }, + [openPanels, state] + ); const contextValue = useMemo( () => ({ @@ -154,7 +192,7 @@ export const ExpandableFlyoutProvider = ({ children }: ExpandableFlyoutProviderP {children} ); -}; +}); /** * Retrieve context's properties diff --git a/packages/kbn-expandable-flyout/src/reducer.ts b/packages/kbn-expandable-flyout/src/reducer.ts index 4901eccfc6bb4..bb0ff125f546e 100644 --- a/packages/kbn-expandable-flyout/src/reducer.ts +++ b/packages/kbn-expandable-flyout/src/reducer.ts @@ -105,5 +105,8 @@ export function reducer(state: State, action: Action) { preview: [], }; } + + default: + return state; } } diff --git a/packages/kbn-guided-onboarding/index.ts b/packages/kbn-guided-onboarding/index.ts index 9ccaae3901b48..a72c60ac8d25e 100644 --- a/packages/kbn-guided-onboarding/index.ts +++ b/packages/kbn-guided-onboarding/index.ts @@ -16,6 +16,7 @@ export type { GuideConfig, StepConfig, StepDescriptionWithLink, + GuideParams, } from './src/types'; export { GuideCards, GuideFilters } from './src/components/landing_page'; export type { GuideFilterValues } from './src/components/landing_page'; diff --git a/packages/kbn-guided-onboarding/src/common/test_guide_config.ts b/packages/kbn-guided-onboarding/src/common/test_guide_config.ts index a7944ef1d8bb8..875500fbb827c 100644 --- a/packages/kbn-guided-onboarding/src/common/test_guide_config.ts +++ b/packages/kbn-guided-onboarding/src/common/test_guide_config.ts @@ -75,5 +75,14 @@ export const testGuideConfig: GuideConfig = { path: 'stepThree', }, }, + { + id: 'step4', + title: 'Step 4 (dynamic url)', + description: 'This step navigates to a dynamic URL with a param indexName passed in step 1.', + location: { + appID: 'guidedOnboardingExample', + path: 'stepFour/{indexName}', + }, + }, ], }; diff --git a/packages/kbn-guided-onboarding/src/types.ts b/packages/kbn-guided-onboarding/src/types.ts index 5f6da4b83ef54..bcf658ece6d3d 100644 --- a/packages/kbn-guided-onboarding/src/types.ts +++ b/packages/kbn-guided-onboarding/src/types.ts @@ -17,15 +17,18 @@ export type GuideId = type KubernetesStepIds = 'add_data' | 'view_dashboard' | 'tour_observability'; type SiemStepIds = 'add_data' | 'rules' | 'alertsCases'; type SearchStepIds = 'add_data' | 'search_experience'; -type TestGuideIds = 'step1' | 'step2' | 'step3'; +type TestGuideIds = 'step1' | 'step2' | 'step3' | 'step4'; export type GuideStepIds = KubernetesStepIds | SiemStepIds | SearchStepIds | TestGuideIds; +export type GuideParams = Record; + export interface GuideState { guideId: GuideId; status: GuideStatus; isActive?: boolean; // Drives the current guide shown in the dropdown panel steps: GuideStep[]; + params?: GuideParams; } /** @@ -92,6 +95,16 @@ export interface StepConfig { description?: string | StepDescriptionWithLink; // description list is displayed as an unordered list, can be combined with description descriptionList?: Array; + /* + * Kibana location where the user will be redirected when starting or continuing a guide step. + * The property `path` can use dynamic parameters, for example `testPath/{indexID}/{pageID}. + * For the dynamic path to be configured correctly, the values of the parameters need to be passed to + * the api service when completing one of the previous steps. + * For example, if step 2 has a dynamic parameter `indexID` in its location path + * { appID: 'test', path: 'testPath/{indexID}', params: ['indexID'] }, + * its value needs to be passed to the api service when completing step 1. For example, + * `guidedOnboardingAPI.completeGuideStep('testGuide', 'step1', { indexID: 'testIndex' }) + */ location?: { appID: string; path: string; diff --git a/packages/kbn-object-versioning/lib/content_management_services_schemas.ts b/packages/kbn-object-versioning/lib/content_management_services_schemas.ts index 90e8400689dc2..c98a3f62ca3a0 100644 --- a/packages/kbn-object-versioning/lib/content_management_services_schemas.ts +++ b/packages/kbn-object-versioning/lib/content_management_services_schemas.ts @@ -103,6 +103,20 @@ const searchSchemas = getOptionalInOutSchemas({ ), }); +// Schema to validate the "msearch" service objects +const mSearchSchemas = schema.maybe( + schema.object({ + out: schema.maybe( + schema.object( + { + result: schema.maybe(versionableObjectSchema), + }, + { unknowns: 'forbid' } + ) + ), + }) +); + export const serviceDefinitionSchema = schema.object( { get: getSchemas, @@ -111,6 +125,7 @@ export const serviceDefinitionSchema = schema.object( update: createSchemas, delete: getSchemas, search: searchSchemas, + mSearch: mSearchSchemas, }, { unknowns: 'forbid' } ); diff --git a/packages/kbn-object-versioning/lib/content_management_services_versioning.test.ts b/packages/kbn-object-versioning/lib/content_management_services_versioning.test.ts index 49b55b0da44fc..08bd66609ab7e 100644 --- a/packages/kbn-object-versioning/lib/content_management_services_versioning.test.ts +++ b/packages/kbn-object-versioning/lib/content_management_services_versioning.test.ts @@ -240,6 +240,7 @@ describe('CM services getTransforms()', () => { 'delete.out.result', 'search.in.options', 'search.out.result', + 'mSearch.out.result', ].sort() ); }); diff --git a/packages/kbn-object-versioning/lib/content_management_services_versioning.ts b/packages/kbn-object-versioning/lib/content_management_services_versioning.ts index 5655a094f5e42..eb38de643f630 100644 --- a/packages/kbn-object-versioning/lib/content_management_services_versioning.ts +++ b/packages/kbn-object-versioning/lib/content_management_services_versioning.ts @@ -34,6 +34,7 @@ const serviceObjectPaths = [ 'delete.out.result', 'search.in.options', 'search.out.result', + 'mSearch.out.result', ]; const validateServiceDefinitions = (definitions: ServiceDefinitionVersioned) => { @@ -175,6 +176,11 @@ const getDefaultServiceTransforms = (): ServiceTransforms => ({ result: getDefaultTransforms(), }, }, + mSearch: { + out: { + result: getDefaultTransforms(), + }, + }, }); export const getTransforms = ( diff --git a/packages/kbn-object-versioning/lib/content_management_types.ts b/packages/kbn-object-versioning/lib/content_management_types.ts index 56d72013335cd..1594b450661ba 100644 --- a/packages/kbn-object-versioning/lib/content_management_types.ts +++ b/packages/kbn-object-versioning/lib/content_management_types.ts @@ -59,6 +59,11 @@ export interface ServicesDefinition { result?: VersionableObject; }; }; + mSearch?: { + out?: { + result?: VersionableObject; + }; + }; } export interface ServiceTransforms { @@ -112,6 +117,11 @@ export interface ServiceTransforms { result: ObjectTransforms; }; }; + mSearch: { + out: { + result: ObjectTransforms; + }; + }; } export interface ServiceDefinitionVersioned { diff --git a/packages/kbn-securitysolution-exception-list-components/src/list_header/__snapshots__/list_header.test.tsx.snap b/packages/kbn-securitysolution-exception-list-components/src/list_header/__snapshots__/list_header.test.tsx.snap index 871e3ea311cd6..b72c08b8d74ac 100644 --- a/packages/kbn-securitysolution-exception-list-components/src/list_header/__snapshots__/list_header.test.tsx.snap +++ b/packages/kbn-securitysolution-exception-list-components/src/list_header/__snapshots__/list_header.test.tsx.snap @@ -1876,6 +1876,27 @@ Object { data-test-subj="RightSideMenuItemsMenuActionsActionItem2" disabled="" type="button" + > + + + + Duplicate exception list + + + + + + +
+ ); +} +``` + +In this example, the count state is synced to the URL using the `useSyncToUrl` hook. +Whenever the count state changes, the hook will update the URL with the new value. +When the user copies the updated url or refreshes the page, `restore` function will be called to update the count state. \ No newline at end of file diff --git a/packages/kbn-url-state/index.test.ts b/packages/kbn-url-state/index.test.ts new file mode 100644 index 0000000000000..33dc285e100e5 --- /dev/null +++ b/packages/kbn-url-state/index.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 { renderHook, act } from '@testing-library/react-hooks'; +import { useSyncToUrl } from '.'; +import { encode } from '@kbn/rison'; + +describe('useSyncToUrl', () => { + let originalLocation: Location; + let originalHistory: History; + + beforeEach(() => { + originalLocation = window.location; + originalHistory = window.history; + delete (window as any).location; + delete (window as any).history; + + window.location = { + ...originalLocation, + search: '', + }; + window.history = { + ...originalHistory, + replaceState: jest.fn(), + }; + }); + + afterEach(() => { + window.location = originalLocation; + window.history = originalHistory; + }); + + it('should restore the value from the query string on mount', () => { + const key = 'testKey'; + const restoredValue = { test: 'value' }; + const encodedValue = encode(restoredValue); + const restore = jest.fn(); + + window.location.search = `?${key}=${encodedValue}`; + + renderHook(() => useSyncToUrl(key, restore)); + + expect(restore).toHaveBeenCalledWith(restoredValue); + }); + + it('should sync the value to the query string', () => { + const key = 'testKey'; + const valueToSerialize = { test: 'value' }; + + const { result } = renderHook(() => useSyncToUrl(key, jest.fn())); + + act(() => { + result.current(valueToSerialize); + }); + + expect(window.history.replaceState).toHaveBeenCalledWith( + { path: expect.any(String) }, + '', + '/?testKey=%28test%3Avalue%29' + ); + }); + + it('should clear the value from the query string on unmount', () => { + const key = 'testKey'; + + const { unmount } = renderHook(() => useSyncToUrl(key, jest.fn())); + + act(() => { + unmount(); + }); + + expect(window.history.replaceState).toHaveBeenCalledWith( + { path: expect.any(String) }, + '', + expect.any(String) + ); + }); + + it('should clear the value from the query string when history back or forward is pressed', () => { + const key = 'testKey'; + const restore = jest.fn(); + + renderHook(() => useSyncToUrl(key, restore, true)); + + act(() => { + window.dispatchEvent(new Event('popstate')); + }); + + expect(window.history.replaceState).toHaveBeenCalledTimes(1); + expect(window.history.replaceState).toHaveBeenCalledWith( + { path: expect.any(String) }, + '', + '/?' + ); + }); +}); diff --git a/packages/kbn-url-state/index.ts b/packages/kbn-url-state/index.ts new file mode 100644 index 0000000000000..73568222fb4c0 --- /dev/null +++ b/packages/kbn-url-state/index.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 { useSyncToUrl } from './use_sync_to_url'; diff --git a/packages/kbn-url-state/jest.config.js b/packages/kbn-url-state/jest.config.js new file mode 100644 index 0000000000000..256a51239206c --- /dev/null +++ b/packages/kbn-url-state/jest.config.js @@ -0,0 +1,13 @@ +/* + * 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 = { + preset: '@kbn/test', + rootDir: '../..', + roots: ['/packages/kbn-url-state'], +}; diff --git a/packages/kbn-url-state/kibana.jsonc b/packages/kbn-url-state/kibana.jsonc new file mode 100644 index 0000000000000..b0ab56d6af8b7 --- /dev/null +++ b/packages/kbn-url-state/kibana.jsonc @@ -0,0 +1,5 @@ +{ + "type": "shared-common", + "id": "@kbn/url-state", + "owner": "@elastic/security-threat-hunting-investigations" +} diff --git a/packages/kbn-url-state/package.json b/packages/kbn-url-state/package.json new file mode 100644 index 0000000000000..2cd753f16b872 --- /dev/null +++ b/packages/kbn-url-state/package.json @@ -0,0 +1,6 @@ +{ + "name": "@kbn/url-state", + "private": true, + "version": "1.0.0", + "license": "SSPL-1.0 OR Elastic License 2.0" +} \ No newline at end of file diff --git a/packages/kbn-url-state/tsconfig.json b/packages/kbn-url-state/tsconfig.json new file mode 100644 index 0000000000000..3bd03b7f37b84 --- /dev/null +++ b/packages/kbn-url-state/tsconfig.json @@ -0,0 +1,21 @@ +{ + "extends": "../../tsconfig.base.json", + "compilerOptions": { + "outDir": "target/types", + "types": [ + "jest", + "node", + "react" + ] + }, + "include": [ + "**/*.ts", + "**/*.tsx", + ], + "exclude": [ + "target/**/*" + ], + "kbn_references": [ + "@kbn/rison", + ] +} diff --git a/packages/kbn-url-state/use_sync_to_url.ts b/packages/kbn-url-state/use_sync_to_url.ts new file mode 100644 index 0000000000000..e38f9bc688a8b --- /dev/null +++ b/packages/kbn-url-state/use_sync_to_url.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 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 { useCallback, useEffect } from 'react'; +import { encode, decode } from '@kbn/rison'; + +// https://developer.mozilla.org/en-US/docs/Web/API/Window/popstate_event +const POPSTATE_EVENT = 'popstate' as const; + +/** + * Sync any object with browser query string using @knb/rison + * @param key query string param to use + * @param restore use this to handle restored state + * @param cleanupOnHistoryNavigation use history events to cleanup state on back / forward naviation. true by default + */ +export const useSyncToUrl = ( + key: string, + restore: (data: TValueToSerialize) => void, + cleanupOnHistoryNavigation = true +) => { + useEffect(() => { + const params = new URLSearchParams(window.location.search); + const param = params.get(key); + + if (!param) { + return; + } + + const decodedQuery = decode(param); + + if (!decodedQuery) { + return; + } + + // Only restore the value if it is not falsy + restore(decodedQuery as unknown as TValueToSerialize); + }, [key, restore]); + + /** + * Synces value with the url state, under specified key. If payload is undefined, the value will be removed from the query string althogether. + */ + const syncValueToQueryString = useCallback( + (valueToSerialize?: TValueToSerialize) => { + const searchParams = new URLSearchParams(window.location.search); + + if (valueToSerialize) { + const serializedPayload = encode(valueToSerialize); + searchParams.set(key, serializedPayload); + } else { + searchParams.delete(key); + } + + const newSearch = searchParams.toString(); + + // Update query string without unnecessary re-render + const newUrl = `${window.location.pathname}?${newSearch}`; + window.history.replaceState({ path: newUrl }, '', newUrl); + }, + [key] + ); + + // Clear remove state from the url on unmount / when history back or forward is pressed + useEffect(() => { + const clearState = () => { + syncValueToQueryString(undefined); + }; + + if (cleanupOnHistoryNavigation) { + window.addEventListener(POPSTATE_EVENT, clearState); + } + + return () => { + clearState(); + + if (cleanupOnHistoryNavigation) { + window.removeEventListener(POPSTATE_EVENT, clearState); + } + }; + }, [cleanupOnHistoryNavigation, syncValueToQueryString]); + + return syncValueToQueryString; +}; diff --git a/src/plugins/content_management/server/core/index.ts b/src/plugins/content_management/server/core/index.ts index c765076f39e94..1ac7228c6f163 100644 --- a/src/plugins/content_management/server/core/index.ts +++ b/src/plugins/content_management/server/core/index.ts @@ -17,6 +17,7 @@ export type { ContentTypeDefinition, StorageContext, StorageContextGetTransformFn, + MSearchConfig, } from './types'; export type { ContentRegistry } from './registry'; diff --git a/src/plugins/content_management/server/core/registry.ts b/src/plugins/content_management/server/core/registry.ts index 7d36fa20fad1a..00adb4b04a403 100644 --- a/src/plugins/content_management/server/core/registry.ts +++ b/src/plugins/content_management/server/core/registry.ts @@ -9,7 +9,7 @@ import { validateVersion } from '@kbn/object-versioning/lib/utils'; import { ContentType } from './content_type'; import { EventBus } from './event_bus'; -import type { ContentStorage, ContentTypeDefinition } from './types'; +import type { ContentStorage, ContentTypeDefinition, MSearchConfig } from './types'; import type { ContentCrud } from './crud'; export class ContentRegistry { @@ -23,7 +23,9 @@ export class ContentRegistry { * @param contentType The content type to register * @param config The content configuration */ - register = ContentStorage>(definition: ContentTypeDefinition) { + register> = ContentStorage>( + definition: ContentTypeDefinition + ) { if (this.types.has(definition.id)) { throw new Error(`Content [${definition.id}] is already registered`); } diff --git a/src/plugins/content_management/server/core/types.ts b/src/plugins/content_management/server/core/types.ts index c9e66c2563b61..d26c6ac72fa41 100644 --- a/src/plugins/content_management/server/core/types.ts +++ b/src/plugins/content_management/server/core/types.ts @@ -41,7 +41,11 @@ export interface StorageContext { }; } -export interface ContentStorage { +export interface ContentStorage< + T = unknown, + U = T, + TMSearchConfig extends MSearchConfig = MSearchConfig +> { /** Get a single item */ get(ctx: StorageContext, id: string, options?: object): Promise>; @@ -69,7 +73,7 @@ export interface ContentStorage { * Opt-in to multi-type search. * Can only be supported if the content type is backed by a saved object since `mSearch` is using the `savedObjects.find` API. **/ - mSearch?: MSearchConfig; + mSearch?: TMSearchConfig; } export interface ContentTypeDefinition { @@ -87,7 +91,7 @@ export interface ContentTypeDefinition { +export interface MSearchConfig { /** * The saved object type that corresponds to this content type. */ @@ -98,7 +102,7 @@ export interface MSearchConfig { */ toItemResult: ( ctx: StorageContext, - savedObject: SavedObjectsFindResult + savedObject: SavedObjectsFindResult ) => T; /** diff --git a/src/plugins/content_management/server/index.ts b/src/plugins/content_management/server/index.ts index 659b59ed6880c..cdd69cb99b296 100644 --- a/src/plugins/content_management/server/index.ts +++ b/src/plugins/content_management/server/index.ts @@ -14,4 +14,4 @@ export function plugin(initializerContext: PluginInitializerContext) { } export type { ContentManagementServerSetup, ContentManagementServerStart } from './types'; -export type { ContentStorage, StorageContext } from './core'; +export type { ContentStorage, StorageContext, MSearchConfig } from './core'; diff --git a/src/plugins/controls/public/services/options_list/options_list_service.ts b/src/plugins/controls/public/services/options_list/options_list_service.ts index 7152d190c997d..4ad0ecddb80a8 100644 --- a/src/plugins/controls/public/services/options_list/options_list_service.ts +++ b/src/plugins/controls/public/services/options_list/options_list_service.ts @@ -99,7 +99,7 @@ class OptionsListService implements ControlsOptionsListService { private cachedAllowExpensiveQueries = memoize(async () => { const { allowExpensiveQueries } = await this.http.get<{ allowExpensiveQueries: boolean; - }>('/api/kibana/controls/optionsList/getClusterSettings'); + }>('/api/kibana/controls/optionsList/getExpensiveQueriesSetting'); return allowExpensiveQueries; }); diff --git a/src/plugins/controls/server/options_list/options_list_cluster_settings_route.ts b/src/plugins/controls/server/options_list/options_list_cluster_settings_route.ts index f3b4ea598b886..c757a56950da3 100644 --- a/src/plugins/controls/server/options_list/options_list_cluster_settings_route.ts +++ b/src/plugins/controls/server/options_list/options_list_cluster_settings_route.ts @@ -8,18 +8,21 @@ import { getKbnServerError, reportServerError } from '@kbn/kibana-utils-plugin/server'; import { CoreSetup } from '@kbn/core/server'; -import { errors } from '@elastic/elasticsearch'; export const setupOptionsListClusterSettingsRoute = ({ http }: CoreSetup) => { const router = http.createRouter(); router.get( { - path: '/api/kibana/controls/optionsList/getClusterSettings', + path: '/api/kibana/controls/optionsList/getExpensiveQueriesSetting', validate: false, }, async (context, _, response) => { try { - const esClient = (await context.core).elasticsearch.client.asCurrentUser; + /** + * using internal user here because in many cases the logged in user will not have the monitor permission required + * to check cluster settings. This endpoint does not take a query, params, or a body, so there is no chance of leaking info. + */ + const esClient = (await context.core).elasticsearch.client.asInternalUser; const settings = await esClient.cluster.getSettings({ include_defaults: true, filter_path: '**.allow_expensive_queries', @@ -40,17 +43,6 @@ export const setupOptionsListClusterSettingsRoute = ({ http }: CoreSetup) => { }, }); } catch (e) { - if (e instanceof errors.ResponseError && e.body.error.type === 'security_exception') { - /** - * in cases where the user does not have the 'monitor' permission this check will fail. In these cases, we will - * fall back to assume that the allowExpensiveQueries setting is on, because it defaults to true. - */ - return response.ok({ - body: { - allowExpensiveQueries: true, - }, - }); - } const kbnErr = getKbnServerError(e); return reportServerError(response, kbnErr); } diff --git a/src/plugins/dashboard/public/dashboard_actions/replace_panel_flyout.tsx b/src/plugins/dashboard/public/dashboard_actions/replace_panel_flyout.tsx index d5d5c439a4745..4a2ac8f41d6a6 100644 --- a/src/plugins/dashboard/public/dashboard_actions/replace_panel_flyout.tsx +++ b/src/plugins/dashboard/public/dashboard_actions/replace_panel_flyout.tsx @@ -81,7 +81,6 @@ export class ReplacePanelFlyout extends React.Component { } as DashboardPanelState, }, }); - container.reload(); this.showToast(name); this.props.onClose(); diff --git a/src/plugins/dashboard/public/dashboard_app/_dashboard_app_strings.ts b/src/plugins/dashboard/public/dashboard_app/_dashboard_app_strings.ts index e87a74d428f9a..dfc5a648f50aa 100644 --- a/src/plugins/dashboard/public/dashboard_app/_dashboard_app_strings.ts +++ b/src/plugins/dashboard/public/dashboard_app/_dashboard_app_strings.ts @@ -182,6 +182,14 @@ export const topNavStrings = { defaultMessage: 'Save as a new dashboard', }), }, + resetChanges: { + label: i18n.translate('dashboard.topNave.resetChangesButtonAriaLabel', { + defaultMessage: 'Reset', + }), + description: i18n.translate('dashboard.topNave.resetChangesConfigDescription', { + defaultMessage: 'Reset changes to dashboard', + }), + }, switchToViewMode: { label: i18n.translate('dashboard.topNave.cancelButtonAriaLabel', { defaultMessage: 'Switch to view mode', diff --git a/src/plugins/dashboard/public/dashboard_app/top_nav/use_dashboard_menu_items.tsx b/src/plugins/dashboard/public/dashboard_app/top_nav/use_dashboard_menu_items.tsx index 73996719966a1..8ac32563d3e19 100644 --- a/src/plugins/dashboard/public/dashboard_app/top_nav/use_dashboard_menu_items.tsx +++ b/src/plugins/dashboard/public/dashboard_app/top_nav/use_dashboard_menu_items.tsx @@ -52,6 +52,7 @@ export const useDashboardMenuItems = ({ const hasOverlays = dashboard.select((state) => state.componentState.hasOverlays); const lastSavedId = dashboard.select((state) => state.componentState.lastSavedId); const dashboardTitle = dashboard.select((state) => state.explicitInput.title); + const viewMode = dashboard.select((state) => state.explicitInput.viewMode); /** * Show the Dashboard app's share menu @@ -109,21 +110,26 @@ export const useDashboardMenuItems = ({ }, [maybeRedirect, dashboard]); /** - * Returns to view mode. If the dashboard has unsaved changes shows a warning and resets to last saved state. + * Show the dashboard's "Confirm reset changes" modal. If confirmed: + * (1) reset the dashboard to the last saved state, and + * (2) if `switchToViewMode` is `true`, set the dashboard to view mode. */ - const returnToViewMode = useCallback(() => { - dashboard.clearOverlays(); - if (hasUnsavedChanges) { - confirmDiscardUnsavedChanges(() => { - batch(() => { - dashboard.resetToLastSavedState(); - dashboard.dispatch.setViewMode(ViewMode.VIEW); - }); - }); - return; - } - dashboard.dispatch.setViewMode(ViewMode.VIEW); - }, [dashboard, hasUnsavedChanges]); + const resetChanges = useCallback( + (switchToViewMode: boolean = false) => { + dashboard.clearOverlays(); + if (hasUnsavedChanges) { + confirmDiscardUnsavedChanges(() => { + batch(() => { + dashboard.resetToLastSavedState(); + if (switchToViewMode) dashboard.dispatch.setViewMode(ViewMode.VIEW); + }); + }, viewMode); + } else { + if (switchToViewMode) dashboard.dispatch.setViewMode(ViewMode.VIEW); + } + }, + [dashboard, hasUnsavedChanges, viewMode] + ); /** * Register all of the top nav configs that can be used by dashboard. @@ -184,7 +190,7 @@ export const useDashboardMenuItems = ({ id: 'cancel', disableButton: isSaveInProgress || !lastSavedId || hasOverlays, testId: 'dashboardViewOnlyMode', - run: () => returnToViewMode(), + run: () => resetChanges(true), } as TopNavMenuData, share: { @@ -215,9 +221,9 @@ export const useDashboardMenuItems = ({ quickSaveDashboard, hasUnsavedChanges, isSaveInProgress, - returnToViewMode, saveDashboardAs, setIsLabsShown, + resetChanges, hasOverlays, lastSavedId, isLabsShown, @@ -226,27 +232,53 @@ export const useDashboardMenuItems = ({ clone, ]); + const resetChangesMenuItem = useMemo(() => { + return { + ...topNavStrings.resetChanges, + id: 'reset', + testId: 'dashboardDiscardChangesMenuItem', + disableButton: + !hasUnsavedChanges || + hasOverlays || + (viewMode === ViewMode.EDIT && (isSaveInProgress || !lastSavedId)), + run: () => resetChanges(), + }; + }, [hasOverlays, lastSavedId, resetChanges, viewMode, isSaveInProgress, hasUnsavedChanges]); + /** * Build ordered menus for view and edit mode. */ const viewModeTopNavConfig = useMemo(() => { const labsMenuItem = isLabsEnabled ? [menuItems.labs] : []; const shareMenuItem = share ? [menuItems.share] : []; - const writePermissionsMenuItems = showWriteControls ? [menuItems.clone, menuItems.edit] : []; - return [...labsMenuItem, menuItems.fullScreen, ...shareMenuItem, ...writePermissionsMenuItems]; - }, [menuItems, share, showWriteControls, isLabsEnabled]); + const cloneMenuItem = showWriteControls ? [menuItems.clone] : []; + const editMenuItem = showWriteControls ? [menuItems.edit] : []; + return [ + ...labsMenuItem, + menuItems.fullScreen, + ...shareMenuItem, + ...cloneMenuItem, + resetChangesMenuItem, + ...editMenuItem, + ]; + }, [menuItems, share, showWriteControls, resetChangesMenuItem, isLabsEnabled]); const editModeTopNavConfig = useMemo(() => { const labsMenuItem = isLabsEnabled ? [menuItems.labs] : []; const shareMenuItem = share ? [menuItems.share] : []; const editModeItems: TopNavMenuData[] = []; if (lastSavedId) { - editModeItems.push(menuItems.saveAs, menuItems.switchToViewMode, menuItems.quickSave); + editModeItems.push( + menuItems.saveAs, + menuItems.switchToViewMode, + resetChangesMenuItem, + menuItems.quickSave + ); } else { editModeItems.push(menuItems.switchToViewMode, menuItems.saveAs); } return [...labsMenuItem, menuItems.settings, ...shareMenuItem, ...editModeItems]; - }, [lastSavedId, menuItems, share, isLabsEnabled]); + }, [lastSavedId, menuItems, share, resetChangesMenuItem, isLabsEnabled]); return { viewModeTopNavConfig, editModeTopNavConfig }; }; diff --git a/src/plugins/dashboard/public/dashboard_container/embeddable/api/panel_management.ts b/src/plugins/dashboard/public/dashboard_container/embeddable/api/panel_management.ts index ee57970a93cd4..cb2ce9af37bcd 100644 --- a/src/plugins/dashboard/public/dashboard_container/embeddable/api/panel_management.ts +++ b/src/plugins/dashboard/public/dashboard_container/embeddable/api/panel_management.ts @@ -86,11 +86,7 @@ export async function replacePanel( }; } - await this.updateInput({ - panels, - lastReloadRequestTime: new Date().getTime(), - }); - + await this.updateInput({ panels }); return panelId; } diff --git a/src/plugins/dashboard/public/dashboard_container/state/dashboard_container_reducers.ts b/src/plugins/dashboard/public/dashboard_container/state/dashboard_container_reducers.ts index dcf2167014f19..70bf3a7d65989 100644 --- a/src/plugins/dashboard/public/dashboard_container/state/dashboard_container_reducers.ts +++ b/src/plugins/dashboard/public/dashboard_container/state/dashboard_container_reducers.ts @@ -124,7 +124,10 @@ export const dashboardContainerReducers = { }, resetToLastSavedInput: (state: DashboardReduxState) => { - state.explicitInput = state.componentState.lastSavedInput; + state.explicitInput = { + ...state.componentState.lastSavedInput, + viewMode: state.explicitInput.viewMode, // keep current view mode when resetting + }; }, // ------------------------------------------------------------------------------ diff --git a/src/plugins/dashboard/public/dashboard_listing/_dashboard_listing_strings.ts b/src/plugins/dashboard/public/dashboard_listing/_dashboard_listing_strings.ts index df036a01fcf95..8887272da3d91 100644 --- a/src/plugins/dashboard/public/dashboard_listing/_dashboard_listing_strings.ts +++ b/src/plugins/dashboard/public/dashboard_listing/_dashboard_listing_strings.ts @@ -6,6 +6,7 @@ * Side Public License, v 1. */ +import { ViewMode } from '@kbn/embeddable-plugin/public'; import { i18n } from '@kbn/i18n'; export const dashboardListingErrorStrings = { @@ -91,32 +92,32 @@ export const dashboardUnsavedListingStrings = { defaultMessage: 'Continue editing', }), getDiscardAriaLabel: (title: string) => - i18n.translate('dashboard.listing.unsaved.discardAria', { - defaultMessage: 'Discard changes to {title}', + i18n.translate('dashboard.listing.unsaved.resetAria', { + defaultMessage: 'Reset changes to {title}', values: { title }, }), getDiscardTitle: () => - i18n.translate('dashboard.listing.unsaved.discardTitle', { - defaultMessage: 'Discard changes', + i18n.translate('dashboard.listing.unsaved.resetTitle', { + defaultMessage: 'Reset changes', }), }; -export const discardConfirmStrings = { - getDiscardTitle: () => - i18n.translate('dashboard.discardChangesConfirmModal.discardChangesTitle', { - defaultMessage: 'Discard changes to dashboard?', - }), - getDiscardSubtitle: () => - i18n.translate('dashboard.discardChangesConfirmModal.discardChangesDescription', { - defaultMessage: `Once you discard your changes, there's no getting them back.`, - }), - getDiscardConfirmButtonText: () => - i18n.translate('dashboard.discardChangesConfirmModal.confirmButtonLabel', { - defaultMessage: 'Discard changes', - }), - getDiscardCancelButtonText: () => - i18n.translate('dashboard.discardChangesConfirmModal.cancelButtonLabel', { - defaultMessage: 'Cancel', +export const resetConfirmStrings = { + getResetTitle: () => + i18n.translate('dashboard.resetChangesConfirmModal.resetChangesTitle', { + defaultMessage: 'Reset dashboard?', + }), + getResetSubtitle: (viewMode: ViewMode) => + viewMode === ViewMode.EDIT + ? i18n.translate('dashboard.discardChangesConfirmModal.discardChangesDescription', { + defaultMessage: `All unsaved changes will be lost.`, + }) + : i18n.translate('dashboard.resetChangesConfirmModal.resetChangesDescription', { + defaultMessage: `This dashboard will return to its last saved state. You might lose changes to filters and queries.`, + }), + getResetConfirmButtonText: () => + i18n.translate('dashboard.resetChangesConfirmModal.confirmButtonLabel', { + defaultMessage: 'Reset dashboard', }), }; diff --git a/src/plugins/dashboard/public/dashboard_listing/confirm_overlays.tsx b/src/plugins/dashboard/public/dashboard_listing/confirm_overlays.tsx index 03027cda242b9..40f4367059eff 100644 --- a/src/plugins/dashboard/public/dashboard_listing/confirm_overlays.tsx +++ b/src/plugins/dashboard/public/dashboard_listing/confirm_overlays.tsx @@ -20,24 +20,28 @@ import { EuiText, EUI_MODAL_CANCEL_BUTTON, } from '@elastic/eui'; +import { ViewMode } from '@kbn/embeddable-plugin/public'; import { toMountPoint } from '@kbn/kibana-react-plugin/public'; import { pluginServices } from '../services/plugin_services'; -import { createConfirmStrings, discardConfirmStrings } from './_dashboard_listing_strings'; +import { createConfirmStrings, resetConfirmStrings } from './_dashboard_listing_strings'; export type DiscardOrKeepSelection = 'cancel' | 'discard' | 'keep'; -export const confirmDiscardUnsavedChanges = (discardCallback: () => void) => { +export const confirmDiscardUnsavedChanges = ( + discardCallback: () => void, + viewMode: ViewMode = ViewMode.EDIT // we want to show the danger modal on the listing page +) => { const { overlays: { openConfirm }, } = pluginServices.getServices(); - openConfirm(discardConfirmStrings.getDiscardSubtitle(), { - confirmButtonText: discardConfirmStrings.getDiscardConfirmButtonText(), - cancelButtonText: discardConfirmStrings.getDiscardCancelButtonText(), - buttonColor: 'danger', + openConfirm(resetConfirmStrings.getResetSubtitle(viewMode), { + confirmButtonText: resetConfirmStrings.getResetConfirmButtonText(), + buttonColor: viewMode === ViewMode.EDIT ? 'danger' : 'primary', + maxWidth: 500, defaultFocusedButton: EUI_MODAL_CANCEL_BUTTON, - title: discardConfirmStrings.getDiscardTitle(), + title: resetConfirmStrings.getResetTitle(), }).then((isConfirmed) => { if (isConfirmed) { discardCallback(); diff --git a/src/plugins/data_view_field_editor/__jest__/client_integration/helpers/setup_environment.tsx b/src/plugins/data_view_field_editor/__jest__/client_integration/helpers/setup_environment.tsx index e356c76ddb989..e76cf6960cc23 100644 --- a/src/plugins/data_view_field_editor/__jest__/client_integration/helpers/setup_environment.tsx +++ b/src/plugins/data_view_field_editor/__jest__/client_integration/helpers/setup_environment.tsx @@ -149,7 +149,7 @@ export const WithFieldEditorDependencies = }; const mergedDependencies = merge({}, dependencies, overridingDependencies); - const previewController = new PreviewController({ dataView, search }); + const previewController = new PreviewController({ dataView, search, fieldFormats }); return ( diff --git a/src/plugins/data_view_field_editor/public/components/field_editor/form_fields/script_field.tsx b/src/plugins/data_view_field_editor/public/components/field_editor/form_fields/script_field.tsx index fddf14c864743..077471b44c237 100644 --- a/src/plugins/data_view_field_editor/public/components/field_editor/form_fields/script_field.tsx +++ b/src/plugins/data_view_field_editor/public/components/field_editor/form_fields/script_field.tsx @@ -24,7 +24,7 @@ import { } from '../../../shared_imports'; import type { RuntimeFieldPainlessError } from '../../../types'; import { painlessErrorToMonacoMarker } from '../../../lib'; -import { useFieldPreviewContext, Context } from '../../preview'; +import { useFieldPreviewContext } from '../../preview'; import { schema } from '../form_schema'; import type { FieldFormInternal } from '../field_editor'; import { useStateSelector } from '../../../state_utils'; @@ -57,6 +57,7 @@ const mapReturnTypeToPainlessContext = (runtimeType: RuntimeType): PainlessConte const currentDocumentSelector = (state: PreviewState) => state.documents[state.currentIdx]; const currentDocumentIsLoadingSelector = (state: PreviewState) => state.isLoadingDocuments; +const currentErrorSelector = (state: PreviewState) => state.previewResponse?.error; const ScriptFieldComponent = ({ existingConcreteFields, links, placeholder }: Props) => { const { @@ -66,14 +67,15 @@ const ScriptFieldComponent = ({ existingConcreteFields, links, placeholder }: Pr const editorValidationSubscription = useRef(); const fieldCurrentValue = useRef(''); - const { error, isLoadingPreview, isPreviewAvailable, controller } = useFieldPreviewContext(); + const { isLoadingPreview, isPreviewAvailable, controller } = useFieldPreviewContext(); + const error = useStateSelector(controller.state$, currentErrorSelector); const currentDocument = useStateSelector(controller.state$, currentDocumentSelector); const isFetchingDoc = useStateSelector(controller.state$, currentDocumentIsLoadingSelector); const [validationData$, nextValidationData$] = useBehaviorSubject< | { isFetchingDoc: boolean; isLoadingPreview: boolean; - error: Context['error']; + error: PreviewState['previewResponse']['error']; } | undefined >(undefined); diff --git a/src/plugins/data_view_field_editor/public/components/field_editor/form_schema.ts b/src/plugins/data_view_field_editor/public/components/field_editor/form_schema.ts index 391f54581f258..45ecac570dd99 100644 --- a/src/plugins/data_view_field_editor/public/components/field_editor/form_schema.ts +++ b/src/plugins/data_view_field_editor/public/components/field_editor/form_schema.ts @@ -10,8 +10,8 @@ import { i18n } from '@kbn/i18n'; import { EuiComboBoxOptionOption } from '@elastic/eui'; import { fieldValidators, FieldConfig, RuntimeType, ValidationFunc } from '../../shared_imports'; -import type { Context } from '../preview'; import { RUNTIME_FIELD_OPTIONS } from './constants'; +import type { PreviewState } from '../preview/types'; const { containsCharsField, emptyField, numberGreaterThanField } = fieldValidators; const i18nTexts = { @@ -25,7 +25,7 @@ const i18nTexts = { // Validate the painless **script** const painlessScriptValidator: ValidationFunc = async ({ customData: { provider } }) => { - const previewError = (await provider()) as Context['error']; + const previewError = (await provider()) as PreviewState['previewResponse']['error']; if (previewError && previewError.code === 'PAINLESS_SCRIPT_ERROR') { return { diff --git a/src/plugins/data_view_field_editor/public/components/field_editor_flyout_content_container.tsx b/src/plugins/data_view_field_editor/public/components/field_editor_flyout_content_container.tsx index 60903fae03ea1..c763d08ae470b 100644 --- a/src/plugins/data_view_field_editor/public/components/field_editor_flyout_content_container.tsx +++ b/src/plugins/data_view_field_editor/public/components/field_editor_flyout_content_container.tsx @@ -85,7 +85,7 @@ export const FieldEditorFlyoutContentContainer = ({ fieldFormats, uiSettings, }: Props) => { - const [controller] = useState(() => new PreviewController({ dataView, search })); + const [controller] = useState(() => new PreviewController({ dataView, search, fieldFormats })); const [isSaving, setIsSaving] = useState(false); const { fields } = dataView; diff --git a/src/plugins/data_view_field_editor/public/components/preview/field_preview.tsx b/src/plugins/data_view_field_editor/public/components/preview/field_preview.tsx index 672f0a747991d..005a2c634cf84 100644 --- a/src/plugins/data_view_field_editor/public/components/preview/field_preview.tsx +++ b/src/plugins/data_view_field_editor/public/components/preview/field_preview.tsx @@ -16,6 +16,7 @@ import { DocumentsNavPreview } from './documents_nav_preview'; import { FieldPreviewError } from './field_preview_error'; import { PreviewListItem } from './field_list/field_list_item'; import { PreviewFieldList } from './field_list/field_list'; +import { useStateSelector } from '../../state_utils'; import './field_preview.scss'; @@ -28,12 +29,12 @@ export const FieldPreview = () => { value: { name, script, format }, }, isLoadingPreview, - fields, - error, documents: { fetchDocError }, reset, isPreviewAvailable, + controller, } = useFieldPreviewContext(); + const { fields, error } = useStateSelector(controller.state$, (state) => state.previewResponse); // To show the preview we at least need a name to be defined, the script or the format // and an first response from the _execute API diff --git a/src/plugins/data_view_field_editor/public/components/preview/field_preview_context.tsx b/src/plugins/data_view_field_editor/public/components/preview/field_preview_context.tsx index f554025ce9f4b..c4cdfad7d4fec 100644 --- a/src/plugins/data_view_field_editor/public/components/preview/field_preview_context.tsx +++ b/src/plugins/data_view_field_editor/public/components/preview/field_preview_context.tsx @@ -20,7 +20,6 @@ import { renderToString } from 'react-dom/server'; import useDebounce from 'react-use/lib/useDebounce'; import { i18n } from '@kbn/i18n'; import { get } from 'lodash'; -import { castEsToKbnFieldTypeName } from '@kbn/field-types'; import { BehaviorSubject } from 'rxjs'; import { RuntimePrimitiveTypes } from '../../shared_imports'; import { useStateSelector } from '../../state_utils'; @@ -32,7 +31,6 @@ import type { Context, Params, EsDocument, - ScriptErrorCodes, FetchDocError, FieldPreview, PreviewState, @@ -101,17 +99,11 @@ export const FieldPreviewProvider: FunctionComponent<{ controller: PreviewContro notifications, api: { getFieldPreview }, }, - fieldFormats, fieldName$, } = useFieldEditorContext(); const fieldPreview$ = useRef(new BehaviorSubject(undefined)); - /** Response from the Painless _execute API */ - const [previewResponse, setPreviewResponse] = useState<{ - fields: Context['fields']; - error: Context['error']; - }>({ fields: [], error: null }); const [initialPreviewComplete, setInitialPreviewComplete] = useState(false); /** Possible error while fetching sample documents */ @@ -169,45 +161,6 @@ export const FieldPreviewProvider: FunctionComponent<{ controller: PreviewContro ); }, [type, script, currentDocId]); - const setPreviewError = useCallback((error: Context['error']) => { - setPreviewResponse((prev) => ({ - ...prev, - error, - })); - }, []); - - const clearPreviewError = useCallback((errorCode: ScriptErrorCodes) => { - setPreviewResponse((prev) => { - const error = prev.error === null || prev.error?.code === errorCode ? null : prev.error; - return { - ...prev, - error, - }; - }); - }, []); - - const valueFormatter = useCallback( - (value: unknown) => { - if (format?.id) { - const formatter = fieldFormats.getInstance(format.id, format.params); - if (formatter) { - return formatter.getConverterFor('html')(value) ?? JSON.stringify(value); - } - } - - if (type) { - const fieldType = castEsToKbnFieldTypeName(type); - const defaultFormatterForType = fieldFormats.getDefaultInstance(fieldType); - if (defaultFormatterForType) { - return defaultFormatterForType.getConverterFor('html')(value) ?? JSON.stringify(value); - } - } - - return defaultValueFormatter(value); - }, - [format, type, fieldFormats] - ); - const fetchSampleDocuments = useCallback( async (limit: number = 50) => { if (typeof limit !== 'number') { @@ -217,7 +170,7 @@ export const FieldPreviewProvider: FunctionComponent<{ controller: PreviewContro lastExecutePainlessRequestParams.current.documentId = undefined; setIsFetchingDocument(true); - setPreviewResponse({ fields: [], error: null }); + controller.setPreviewResponse({ fields: [], error: null }); const [response, searchError] = await search .search({ @@ -335,14 +288,14 @@ export const FieldPreviewProvider: FunctionComponent<{ controller: PreviewContro const updateSingleFieldPreview = useCallback( (fieldName: string, values: unknown[]) => { const [value] = values; - const formattedValue = valueFormatter(value); + const formattedValue = controller.valueFormatter({ value, type, format }); - setPreviewResponse({ + controller.setPreviewResponse({ fields: [{ key: fieldName, value, formattedValue }], error: null, }); }, - [valueFormatter] + [controller, type, format] ); const updateCompositeFieldPreview = useCallback( @@ -359,7 +312,7 @@ export const FieldPreviewProvider: FunctionComponent<{ controller: PreviewContro updatedFieldsInScript.push(fieldName); const [value] = values; - const formattedValue = valueFormatter(value); + const formattedValue = controller.valueFormatter({ value, type, format }); return { key: parentName @@ -375,12 +328,12 @@ export const FieldPreviewProvider: FunctionComponent<{ controller: PreviewContro .sort((a, b) => a.key.localeCompare(b.key)); fieldPreview$.current.next(fields); - setPreviewResponse({ + controller.setPreviewResponse({ fields, error: null, }); }, - [valueFormatter, parentName, name, fieldPreview$, fieldName$] + [parentName, name, fieldPreview$, fieldName$, controller, type, format] ); const updatePreview = useCallback(async () => { @@ -437,7 +390,7 @@ export const FieldPreviewProvider: FunctionComponent<{ controller: PreviewContro const { values, error } = response.data; if (error) { - setPreviewResponse({ + controller.setPreviewResponse({ fields: [ { key: name ?? '', @@ -474,6 +427,7 @@ export const FieldPreviewProvider: FunctionComponent<{ controller: PreviewContro updateSingleFieldPreview, updateCompositeFieldPreview, currentDocIndex, + controller, ]); const reset = useCallback(() => { @@ -482,7 +436,7 @@ export const FieldPreviewProvider: FunctionComponent<{ controller: PreviewContro previewCount.current = 0; controller.setDocuments([]); - setPreviewResponse({ fields: [], error: null }); + controller.setPreviewResponse({ fields: [], error: null }); setIsLoadingPreview(false); setIsFetchingDocument(false); }, [controller]); @@ -490,8 +444,6 @@ export const FieldPreviewProvider: FunctionComponent<{ controller: PreviewContro const ctx = useMemo( () => ({ controller, - fields: previewResponse.fields, - error: previewResponse.error, fieldPreview$: fieldPreview$.current, isPreviewAvailable, isLoadingPreview, @@ -521,7 +473,6 @@ export const FieldPreviewProvider: FunctionComponent<{ controller: PreviewContro [ controller, currentIdx, - previewResponse, fieldPreview$, fetchDocError, params, @@ -583,74 +534,70 @@ export const FieldPreviewProvider: FunctionComponent<{ controller: PreviewContro * Whenever the name or the format changes we immediately update the preview */ useEffect(() => { - setPreviewResponse((prev) => { - const { fields } = prev; - - let updatedFields: Context['fields'] = fields.map((field) => { - let key = name ?? ''; - - if (type === 'composite') { - // restore initial key segement (the parent name), which was not returned - const { 1: fieldName } = field.key.split('.'); - key = `${name ?? ''}.${fieldName}`; - } + const { previewResponse: prev } = controller.state$.getValue(); + const { fields } = prev; - return { - ...field, - key, - }; - }); + let updatedFields: PreviewState['previewResponse']['fields'] = fields.map((field) => { + let key = name ?? ''; - // If the user has entered a name but not yet any script we will display - // the field in the preview with just the name - if (updatedFields.length === 0 && name !== null) { - updatedFields = [ - { key: name, value: undefined, formattedValue: undefined, type: undefined }, - ]; + if (type === 'composite') { + // restore initial key segement (the parent name), which was not returned + const { 1: fieldName } = field.key.split('.'); + key = `${name ?? ''}.${fieldName}`; } return { - ...prev, - fields: updatedFields, + ...field, + key, }; }); - }, [name, type, parentName]); + + // If the user has entered a name but not yet any script we will display + // the field in the preview with just the name + if (updatedFields.length === 0 && name !== null) { + updatedFields = [{ key: name, value: undefined, formattedValue: undefined, type: undefined }]; + } + + controller.setPreviewResponse({ + ...prev, + fields: updatedFields, + }); + }, [name, type, parentName, controller]); /** * Whenever the format changes we immediately update the preview */ useEffect(() => { - setPreviewResponse((prev) => { - const { fields } = prev; + const { previewResponse: prev } = controller.state$.getValue(); + const { fields } = prev; - return { - ...prev, - fields: fields.map((field) => { - const nextValue = - script === null && Boolean(document) - ? get(document?._source, name ?? '') ?? get(document?.fields, name ?? '') // When there is no script we try to read the value from _source/fields - : field?.value; + controller.setPreviewResponse({ + ...prev, + fields: fields.map((field) => { + const nextValue = + script === null && Boolean(document) + ? get(document?._source, name ?? '') ?? get(document?.fields, name ?? '') // When there is no script we try to read the value from _source/fields + : field?.value; - const formattedValue = valueFormatter(nextValue); + const formattedValue = controller.valueFormatter({ value: nextValue, type, format }); - return { - ...field, - value: nextValue, - formattedValue, - }; - }), - }; + return { + ...field, + value: nextValue, + formattedValue, + }; + }), }); - }, [name, script, document, valueFormatter]); + }, [name, script, document, controller, type, format]); useEffect(() => { if (script?.source === undefined) { // Whenever the source is not defined ("Set value" is toggled off or the // script is empty) we clear the error and update the params cache. lastExecutePainlessRequestParams.current.script = undefined; - setPreviewError(null); + controller.setPreviewError(null); } - }, [script?.source, setPreviewError]); + }, [script?.source, controller]); // Handle the validation state coming from the Painless DiagnosticAdapter // (see @kbn-monaco/src/painless/diagnostics_adapter.ts) @@ -677,16 +624,16 @@ export const FieldPreviewProvider: FunctionComponent<{ controller: PreviewContro }), }, }; - setPreviewError(error); + controller.setPreviewError(error); // Make sure to update the lastExecutePainlessRequestParams cache so when the user updates // the script and fixes the syntax the "updatePreview()" will run lastExecutePainlessRequestParams.current.script = script?.source; } else { // Clear possible previous syntax error - clearPreviewError('PAINLESS_SYNTAX_ERROR'); + controller.clearPreviewError('PAINLESS_SYNTAX_ERROR'); } - }, [scriptEditorValidation, script?.source, setPreviewError, clearPreviewError]); + }, [scriptEditorValidation, script?.source, controller]); /** * Whenever updatePreview() changes (meaning whenever a param changes) diff --git a/src/plugins/data_view_field_editor/public/components/preview/preview_controller.ts b/src/plugins/data_view_field_editor/public/components/preview/preview_controller.tsx similarity index 60% rename from src/plugins/data_view_field_editor/public/components/preview/preview_controller.ts rename to src/plugins/data_view_field_editor/public/components/preview/preview_controller.tsx index b572827eac06d..8e9f6156c7d7b 100644 --- a/src/plugins/data_view_field_editor/public/components/preview/preview_controller.ts +++ b/src/plugins/data_view_field_editor/public/components/preview/preview_controller.tsx @@ -9,13 +9,23 @@ import type { DataView } from '@kbn/data-views-plugin/public'; import type { ISearchStart } from '@kbn/data-plugin/public'; import { BehaviorSubject } from 'rxjs'; +import { castEsToKbnFieldTypeName } from '@kbn/field-types'; +import { renderToString } from 'react-dom/server'; +import React from 'react'; import { PreviewState } from './types'; import { BehaviorObservable } from '../../state_utils'; -import { EsDocument } from './types'; +import { EsDocument, ScriptErrorCodes, Params } from './types'; +import type { FieldFormatsStart } from '../../shared_imports'; + +export const defaultValueFormatter = (value: unknown) => { + const content = typeof value === 'object' ? JSON.stringify(value) : String(value) ?? '-'; + return renderToString(<>{content}); +}; interface PreviewControllerDependencies { dataView: DataView; search: ISearchStart; + fieldFormats: FieldFormatsStart; } const previewStateDefault: PreviewState = { @@ -30,12 +40,14 @@ const previewStateDefault: PreviewState = { documentSource: 'cluster', /** Keep track if the script painless syntax is being validated and if it is valid */ scriptEditorValidation: { isValidating: false, isValid: true, message: null }, + previewResponse: { fields: [], error: null }, }; export class PreviewController { - constructor({ dataView, search }: PreviewControllerDependencies) { + constructor({ dataView, search, fieldFormats }: PreviewControllerDependencies) { this.dataView = dataView; this.search = search; + this.fieldFormats = fieldFormats; this.internalState$ = new BehaviorSubject({ ...previewStateDefault, @@ -44,10 +56,13 @@ export class PreviewController { this.state$ = this.internalState$ as BehaviorObservable; } + // dependencies // @ts-ignore private dataView: DataView; // @ts-ignore private search: ISearchStart; + private fieldFormats: FieldFormatsStart; + private internalState$: BehaviorSubject; state$: BehaviorObservable; @@ -104,4 +119,52 @@ export class PreviewController { setCustomId = (customId?: string) => { this.updateState({ customId }); }; + + setPreviewError = (error: PreviewState['previewResponse']['error']) => { + this.updateState({ + previewResponse: { ...this.internalState$.getValue().previewResponse, error }, + }); + }; + + setPreviewResponse = (previewResponse: PreviewState['previewResponse']) => { + this.updateState({ previewResponse }); + }; + + clearPreviewError = (errorCode: ScriptErrorCodes) => { + const { previewResponse: prev } = this.internalState$.getValue(); + const error = prev.error === null || prev.error?.code === errorCode ? null : prev.error; + this.updateState({ + previewResponse: { + ...prev, + error, + }, + }); + }; + + valueFormatter = ({ + value, + format, + type, + }: { + value: unknown; + format: Params['format']; + type: Params['type']; + }) => { + if (format?.id) { + const formatter = this.fieldFormats.getInstance(format.id, format.params); + if (formatter) { + return formatter.getConverterFor('html')(value) ?? JSON.stringify(value); + } + } + + if (type) { + const fieldType = castEsToKbnFieldTypeName(type); + const defaultFormatterForType = this.fieldFormats.getDefaultInstance(fieldType); + if (defaultFormatterForType) { + return defaultFormatterForType.getConverterFor('html')(value) ?? JSON.stringify(value); + } + } + + return defaultValueFormatter(value); + }; } diff --git a/src/plugins/data_view_field_editor/public/components/preview/types.ts b/src/plugins/data_view_field_editor/public/components/preview/types.ts index 347e0a709cf28..b4280f54786ac 100644 --- a/src/plugins/data_view_field_editor/public/components/preview/types.ts +++ b/src/plugins/data_view_field_editor/public/components/preview/types.ts @@ -55,6 +55,11 @@ export interface PreviewState { isValid: boolean; message: string | null; }; + /** Response from the Painless _execute API */ + previewResponse: { + fields: FieldPreview[]; + error: PreviewError | null; + }; } export interface FetchDocError { @@ -108,9 +113,7 @@ export type ChangeSet = Record; export interface Context { controller: PreviewController; - fields: FieldPreview[]; fieldPreview$: BehaviorSubject; - error: PreviewError | null; fieldTypeInfo?: FieldTypeInfo[]; initialPreviewComplete: boolean; params: { diff --git a/src/plugins/embeddable/public/lib/actions/edit_panel_action.test.tsx b/src/plugins/embeddable/public/lib/actions/edit_panel_action.test.tsx index 926b6782b5633..d2fb4e701e4b4 100644 --- a/src/plugins/embeddable/public/lib/actions/edit_panel_action.test.tsx +++ b/src/plugins/embeddable/public/lib/actions/edit_panel_action.test.tsx @@ -7,7 +7,7 @@ */ import { EditPanelAction } from './edit_panel_action'; -import { Embeddable, EmbeddableInput, SavedObjectEmbeddableInput } from '../embeddables'; +import { Embeddable, EmbeddableInput } from '../embeddables'; import { ViewMode } from '../types'; import { ContactCardEmbeddable } from '../test_samples'; import { embeddablePluginMock } from '../../mocks'; @@ -42,7 +42,7 @@ test('is compatible when edit url is available, in edit mode and editable', asyn ).toBe(true); }); -test('redirects to app using state transfer with by value mode', async () => { +test('redirects to app using state transfer', async () => { applicationMock.currentAppId$ = of('superCoolCurrentApp'); const testPath = '/test-path'; const action = new EditPanelAction( @@ -78,32 +78,6 @@ test('redirects to app using state transfer with by value mode', async () => { }); }); -test('redirects to app using state transfer without by value mode', async () => { - applicationMock.currentAppId$ = of('superCoolCurrentApp'); - const testPath = '/test-path'; - const action = new EditPanelAction( - getFactory, - applicationMock, - stateTransferMock, - () => testPath - ); - const embeddable = new EditableEmbeddable( - { id: '123', viewMode: ViewMode.EDIT, savedObjectId: '1234' } as SavedObjectEmbeddableInput, - true - ); - embeddable.getOutput = jest.fn(() => ({ editApp: 'ultraVisualize', editPath: '/123' })); - await action.execute({ embeddable }); - expect(stateTransferMock.navigateToEditor).toHaveBeenCalledWith('ultraVisualize', { - path: '/123', - state: { - originatingApp: 'superCoolCurrentApp', - embeddableId: '123', - valueInput: undefined, - originatingPath: testPath, - }, - }); -}); - test('getHref returns the edit urls', async () => { const action = new EditPanelAction(getFactory, applicationMock, stateTransferMock); expect(action.getHref).toBeDefined(); diff --git a/src/plugins/embeddable/public/lib/actions/edit_panel_action.ts b/src/plugins/embeddable/public/lib/actions/edit_panel_action.ts index 1dcecf4ac894d..ba59d92cbef60 100644 --- a/src/plugins/embeddable/public/lib/actions/edit_panel_action.ts +++ b/src/plugins/embeddable/public/lib/actions/edit_panel_action.ts @@ -17,7 +17,6 @@ import { IEmbeddable, EmbeddableEditorState, EmbeddableStateTransfer, - SavedObjectEmbeddableInput, EmbeddableInput, Container, } from '../..'; @@ -124,13 +123,11 @@ export class EditPanelAction implements Action { if (app && path) { if (this.currentAppId) { - const byValueMode = !(embeddable.getInput() as SavedObjectEmbeddableInput).savedObjectId; - const originatingPath = this.getOriginatingPath?.(); const state: EmbeddableEditorState = { originatingApp: this.currentAppId, - valueInput: byValueMode ? this.getExplicitInput({ embeddable }) : undefined, + valueInput: this.getExplicitInput({ embeddable }), embeddableId: embeddable.id, searchSessionId: embeddable.getInput().searchSessionId, originatingPath, diff --git a/src/plugins/embeddable/public/lib/panel/panel_header/panel_actions/customize_panel/customize_panel_action.tsx b/src/plugins/embeddable/public/lib/panel/panel_header/panel_actions/customize_panel/customize_panel_action.tsx index ec6c2011ca53d..fc7a30efbe256 100644 --- a/src/plugins/embeddable/public/lib/panel/panel_header/panel_actions/customize_panel/customize_panel_action.tsx +++ b/src/plugins/embeddable/public/lib/panel/panel_header/panel_actions/customize_panel/customize_panel_action.tsx @@ -129,6 +129,10 @@ export class CustomizePanelAction implements Action { size: 's', 'data-test-subj': 'customizePanel', + onClose: (overlayRef) => { + if (overlayTracker) overlayTracker.clearOverlays(); + overlayRef.close(); + }, } ); overlayTracker?.openOverlay(handle); diff --git a/src/plugins/guided_onboarding/README.md b/src/plugins/guided_onboarding/README.md index 7dbf443f7f86e..1d1db6d13098a 100755 --- a/src/plugins/guided_onboarding/README.md +++ b/src/plugins/guided_onboarding/README.md @@ -45,18 +45,18 @@ When starting Kibana with `yarn start --run-examples` the `guided_onboarding_exa The guided onboarding plugin exposes an API service from its start contract that is intended to be used by other plugins. The API service allows consumers to access the current state of the guided onboarding process and manipulate it. To use the API service in your plugin, declare the guided onboarding plugin as a dependency in the file `kibana.json` of your plugin. Add the API service to your plugin's start dependencies to rely on the provided TypeScript interface: -``` +```js export interface AppPluginStartDependencies { guidedOnboarding: GuidedOnboardingPluginStart; } ``` The API service is now available to your plugin in the setup lifecycle function of your plugin -``` +```js // startDependencies is of type AppPluginStartDependencies const [coreStart, startDependencies] = await core.getStartServices(); ``` or in the start lifecycle function of your plugin. -``` +```js public start(core: CoreStart, startDependencies: AppPluginStartDependencies) { ... } @@ -67,7 +67,7 @@ public start(core: CoreStart, startDependencies: AppPluginStartDependencies) { The API service exposes an Observable that contains a boolean value for the state of a specific guide step. For example, if your plugin needs to check if the "Add data" step of the SIEM guide is currently active, you could use the following code snippet. -``` +```js const { guidedOnboardingApi } = guidedOnboarding; const isDataStepActive = useObservable(guidedOnboardingApi!.isGuideStepActive$('siem', 'add_data')); useEffect(() => { @@ -76,7 +76,7 @@ useEffect(() => { ``` Alternatively, you can subscribe to the Observable directly. -``` +```js useEffect(() => { const subscription = guidedOnboardingApi?.isGuideStepActive$('siem', 'add_data').subscribe((isDataStepACtive) => { // do some logic depending on the step state @@ -89,7 +89,7 @@ useEffect(() => { Similar to `isGuideStepActive$`, the observable `isGuideStepReadyToComplete$` can be used to track the state of a step that is configured for manual completion. The observable broadcasts `true` when the manual completion popover is displayed and the user can mark the step "done". In this state the step is not in progress anymore but is not yet fully completed. -### completeGuideStep(guideId: GuideId, stepId: GuideStepIds): Promise\<{ pluginState: PluginState } | undefined\> +### completeGuideStep(guideId: GuideId, stepId: GuideStepIds, params?: GuideParams): Promise\<{ pluginState: PluginState } | undefined\> The API service exposes an async function to mark a guide step as completed. If the specified guide step is not currently active, the function is a noop. In that case the return value is `undefined`, otherwise an updated `PluginState` is returned. @@ -98,8 +98,20 @@ otherwise an updated `PluginState` is returned. await guidedOnboardingApi?.completeGuideStep('siem', 'add_data'); ``` +The function also accepts an optional argument `params` that will be saved in the state and later used for step URLs with dynamic parameters. For example, step 2 of the guide has a dynamic parameter `indexID` in its location path: +```js +const step2Config = { + id: 'step2', + description: 'Step with dynamic url', + location: { + appID: 'test', path: 'testPath/{indexID}' + } +}; +``` +The value of the parameter `indexID` needs to be passed to the API service when completing step 1: `completeGuideStep('testGuide', 'step1', { indexID: 'testIndex' })` + ## Guides config -To use the API service, you need to know a guide ID (currently one of `search`, `kubernetes`, `siem`) and a step ID (for example, `add_data`, `search_experience`, `rules` etc). The consumers of guided onboarding register their guide configs themselves and have therefore full control over the guide ID and step IDs used for their guide. For more details on registering a guide config, see below. +To use the API service, you need to know a guide ID (currently one of `appSearch`, `websiteSearch`, `databaseSearch`, `kubernetes`, `siem`) and a step ID (for example, `add_data`, `search_experience`, `rules` etc). The consumers of guided onboarding register their guide configs themselves and have therefore full control over the guide ID and step IDs used for their guide. For more details on registering a guide config, see below. ## Server side: register a guide config The guided onboarding exposes a function `registerGuideConfig(guideId: GuideId, guideConfig: GuideConfig)` function in its setup contract. This function allows consumers to register a guide config for a specified guide ID. The function throws an error if a config already exists for the guide ID. See code examples in following plugins: diff --git a/src/plugins/guided_onboarding/public/components/get_step_location.test.ts b/src/plugins/guided_onboarding/public/components/get_step_location.test.ts new file mode 100644 index 0000000000000..98c72fb3ae0b2 --- /dev/null +++ b/src/plugins/guided_onboarding/public/components/get_step_location.test.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 type { PluginState } from '../../common'; +import { testGuideStep4ActiveState } from '../services/api.mocks'; +import { getStepLocationPath } from './get_step_location'; + +describe('getStepLocationPath', () => { + let result: string | undefined; + const pathWithParams = 'testPath/{param1}/{param2}'; + const pathWithoutParams = 'testPath'; + const pluginStateWithoutParams: PluginState = { + status: 'in_progress', + isActivePeriod: true, + activeGuide: testGuideStep4ActiveState, + }; + + it('returns initial location path if no params passed', () => { + result = getStepLocationPath(pathWithParams, pluginStateWithoutParams); + expect(result).toBe(pathWithParams); + }); + + it('returns dynamic location path if params passed', () => { + const pluginStateWithParams: PluginState = { + status: 'in_progress', + isActivePeriod: true, + activeGuide: { ...testGuideStep4ActiveState, params: { param1: 'test1', param2: 'test2' } }, + }; + result = getStepLocationPath(pathWithParams, pluginStateWithParams); + expect(result).toBe(`testPath/test1/test2`); + }); + + it('returns initial location path if params passed but no params are used in the location', () => { + const pluginStateWithParams: PluginState = { + status: 'in_progress', + isActivePeriod: true, + activeGuide: { ...testGuideStep4ActiveState, params: { indexName: 'test1234' } }, + }; + result = getStepLocationPath(pathWithoutParams, pluginStateWithParams); + expect(result).toBe(`testPath`); + }); +}); diff --git a/src/plugins/guided_onboarding/public/components/get_step_location.ts b/src/plugins/guided_onboarding/public/components/get_step_location.ts new file mode 100644 index 0000000000000..3e68b3af47eb5 --- /dev/null +++ b/src/plugins/guided_onboarding/public/components/get_step_location.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. + */ + +import { PluginState } from '../../common'; + +// regex matches everything between an opening and a closing curly braces +// without matching the braces themselves +const paramsBetweenCurlyBraces = /(?<=\{)[^\{\}]+(?=\})/g; +export const getStepLocationPath = (path: string, pluginState: PluginState): string | undefined => { + if (pluginState.activeGuide?.params) { + let dynamicPath = path; + const matchedParams = path.match(paramsBetweenCurlyBraces); + if (matchedParams) { + for (const param of matchedParams) { + dynamicPath = dynamicPath.replace(`{${param}}`, pluginState.activeGuide?.params[param]); + } + return dynamicPath; + } + } + return path; +}; diff --git a/src/plugins/guided_onboarding/public/components/guide_panel.test.tsx b/src/plugins/guided_onboarding/public/components/guide_panel.test.tsx index 233d9c348c436..42440cd2587b2 100644 --- a/src/plugins/guided_onboarding/public/components/guide_panel.test.tsx +++ b/src/plugins/guided_onboarding/public/components/guide_panel.test.tsx @@ -457,7 +457,7 @@ describe('Guided setup', () => { expect( find('guidePanelStepDescription') - .last() + .first() .containsMatchingElement(

{testGuideConfig.steps[2].description}

) ).toBe(true); }); diff --git a/src/plugins/guided_onboarding/public/components/guide_panel.tsx b/src/plugins/guided_onboarding/public/components/guide_panel.tsx index 2d3ef51489081..a87b006eb55eb 100644 --- a/src/plugins/guided_onboarding/public/components/guide_panel.tsx +++ b/src/plugins/guided_onboarding/public/components/guide_panel.tsx @@ -24,6 +24,7 @@ import { getGuidePanelStyles } from './guide_panel.styles'; import { GuideButton } from './guide_button'; import { GuidePanelFlyout } from './guide_panel_flyout'; +import { getStepLocationPath } from './get_step_location'; interface GuidePanelProps { api: GuidedOnboardingApi; @@ -76,7 +77,7 @@ export const GuidePanel = ({ api, application, notifications, uiSettings }: Guid if (stepConfig.location) { await application.navigateToApp(stepConfig.location.appID, { - path: stepConfig.location.path, + path: getStepLocationPath(stepConfig.location.path, pluginState), }); if (stepConfig.manualCompletion?.readyToCompleteOnNavigation) { diff --git a/src/plugins/guided_onboarding/public/services/api.mocks.ts b/src/plugins/guided_onboarding/public/services/api.mocks.ts index 1d5dfc16a9145..3899eb61ff4bf 100644 --- a/src/plugins/guided_onboarding/public/services/api.mocks.ts +++ b/src/plugins/guided_onboarding/public/services/api.mocks.ts @@ -12,7 +12,7 @@ import { PluginState } from '../../common'; export const testGuideFirstStep: GuideStepIds = 'step1'; export const testGuideManualCompletionStep = 'step2'; -export const testGuideLastStep: GuideStepIds = 'step3'; +export const testGuideLastStep: GuideStepIds = 'step4'; export const testIntegration = 'testIntegration'; export const wrongIntegration = 'notTestIntegration'; @@ -33,6 +33,10 @@ export const testGuideStep1ActiveState: GuideState = { id: 'step3', status: 'inactive', }, + { + id: 'step4', + status: 'inactive', + }, ], }; @@ -45,6 +49,7 @@ export const testGuideStep1InProgressState: GuideState = { }, testGuideStep1ActiveState.steps[1], testGuideStep1ActiveState.steps[2], + testGuideStep1ActiveState.steps[3], ], }; @@ -60,6 +65,7 @@ export const testGuideStep2ActiveState: GuideState = { status: 'active', }, testGuideStep1ActiveState.steps[2], + testGuideStep1ActiveState.steps[3], ], }; @@ -75,6 +81,7 @@ export const testGuideStep2InProgressState: GuideState = { status: 'in_progress', }, testGuideStep1ActiveState.steps[2], + testGuideStep1ActiveState.steps[3], ], }; @@ -90,6 +97,7 @@ export const testGuideStep2ReadyToCompleteState: GuideState = { status: 'ready_to_complete', }, testGuideStep1ActiveState.steps[2], + testGuideStep1ActiveState.steps[3], ], }; @@ -108,6 +116,29 @@ export const testGuideStep3ActiveState: GuideState = { id: testGuideStep1ActiveState.steps[2].id, status: 'active', }, + testGuideStep1ActiveState.steps[3], + ], +}; + +export const testGuideStep4ActiveState: GuideState = { + ...testGuideStep1ActiveState, + steps: [ + { + ...testGuideStep1ActiveState.steps[0], + status: 'complete', + }, + { + id: testGuideStep1ActiveState.steps[1].id, + status: 'complete', + }, + { + id: testGuideStep1ActiveState.steps[2].id, + status: 'complete', + }, + { + id: testGuideStep1ActiveState.steps[3].id, + status: 'active', + }, ], }; @@ -126,6 +157,10 @@ export const readyToCompleteGuideState: GuideState = { ...testGuideStep1ActiveState.steps[2], status: 'complete', }, + { + ...testGuideStep1ActiveState.steps[3], + status: 'complete', + }, ], }; @@ -144,3 +179,8 @@ export const mockPluginStateInProgress: PluginState = { isActivePeriod: true, activeGuide: testGuideStep1ActiveState, }; + +export const testGuideParams = { + param1: 'test1', + param2: 'test2', +}; diff --git a/src/plugins/guided_onboarding/public/services/api.test.ts b/src/plugins/guided_onboarding/public/services/api.service.test.ts similarity index 95% rename from src/plugins/guided_onboarding/public/services/api.test.ts rename to src/plugins/guided_onboarding/public/services/api.service.test.ts index 41c6a93faf27e..e6ce000bde594 100644 --- a/src/plugins/guided_onboarding/public/services/api.test.ts +++ b/src/plugins/guided_onboarding/public/services/api.service.test.ts @@ -16,7 +16,6 @@ import { API_BASE_PATH } from '../../common'; import { ApiService } from './api.service'; import { testGuideFirstStep, - testGuideLastStep, testGuideManualCompletionStep, testGuideStep1ActiveState, testGuideStep1InProgressState, @@ -30,6 +29,7 @@ import { mockPluginStateNotStarted, testGuideStep3ActiveState, testGuideStep2ReadyToCompleteState, + testGuideParams, } from './api.mocks'; describe('GuidedOnboarding ApiService', () => { @@ -395,6 +395,21 @@ describe('GuidedOnboarding ApiService', () => { }); }); + it(`saves the params if present`, async () => { + httpClient.get.mockResolvedValue({ + pluginState: { ...mockPluginStateInProgress, activeGuide: testGuideStep1InProgressState }, + }); + apiService.setup(httpClient, true); + + await apiService.completeGuideStep(testGuideId, testGuideFirstStep, testGuideParams); + + expect(httpClient.put).toHaveBeenCalledTimes(1); + // Verify the params were sent to the endpoint + expect(httpClient.put).toHaveBeenLastCalledWith(`${API_BASE_PATH}/state`, { + body: JSON.stringify({ guide: { ...testGuideStep2ActiveState, params: testGuideParams } }), + }); + }); + it(`marks the step as 'ready_to_complete' if it's configured for manual completion`, async () => { httpClient.get.mockResolvedValueOnce({ pluginState: { ...mockPluginStateInProgress, activeGuide: testGuideStep2InProgressState }, @@ -416,6 +431,7 @@ describe('GuidedOnboarding ApiService', () => { testGuideStep2InProgressState.steps[0], { ...testGuideStep2InProgressState.steps[1], status: 'ready_to_complete' }, testGuideStep2InProgressState.steps[2], + testGuideStep2InProgressState.steps[3], ], }, }), @@ -436,11 +452,21 @@ describe('GuidedOnboarding ApiService', () => { }, }); httpClient.get.mockResolvedValueOnce({ - config: testGuideConfig, + config: { + ...testGuideConfig, + steps: [ + // remove step4 for this test to make step3 the last in the guide + testGuideConfig.steps[0], + testGuideConfig.steps[1], + testGuideConfig.steps[2], + ], + }, }); apiService.setup(httpClient, true); - await apiService.completeGuideStep(testGuideId, testGuideLastStep); + // for this test step3 is the last step + const lastStepId = testGuideConfig.steps[2].id; + await apiService.completeGuideStep(testGuideId, lastStepId); expect(httpClient.put).toHaveBeenCalledTimes(1); // Verify the guide now has a "ready_to_complete" status and the last step is "complete" @@ -479,6 +505,7 @@ describe('GuidedOnboarding ApiService', () => { testGuideStep2ActiveState.steps[0], { ...testGuideStep2ActiveState.steps[1], status: 'active' }, testGuideStep2ActiveState.steps[2], + testGuideStep2ActiveState.steps[3], ], }, }), diff --git a/src/plugins/guided_onboarding/public/services/api.service.ts b/src/plugins/guided_onboarding/public/services/api.service.ts index 949002b19ea6a..d3d20143f600d 100644 --- a/src/plugins/guided_onboarding/public/services/api.service.ts +++ b/src/plugins/guided_onboarding/public/services/api.service.ts @@ -23,6 +23,7 @@ import type { GuideStep, GuideStepIds, GuideConfig, + GuideParams, } from '@kbn/guided-onboarding'; import { API_BASE_PATH } from '../../common'; @@ -360,7 +361,8 @@ export class ApiService implements GuidedOnboardingApi { */ public async completeGuideStep( guideId: GuideId, - stepId: GuideStepIds + stepId: GuideStepIds, + params?: GuideParams ): Promise<{ pluginState: PluginState } | undefined> { const pluginState = await firstValueFrom(this.fetchPluginState$()); // For now, returning undefined if consumer attempts to complete a step for a guide that isn't active @@ -395,6 +397,7 @@ export class ApiService implements GuidedOnboardingApi { isActive: true, status, steps: updatedSteps, + params, }; return await this.updatePluginState( diff --git a/src/plugins/guided_onboarding/public/types.ts b/src/plugins/guided_onboarding/public/types.ts index 1b0ccc7d925b3..1103c2ee350da 100755 --- a/src/plugins/guided_onboarding/public/types.ts +++ b/src/plugins/guided_onboarding/public/types.ts @@ -8,7 +8,13 @@ import { Observable } from 'rxjs'; import { HttpSetup } from '@kbn/core/public'; -import type { GuideState, GuideId, GuideStepIds, GuideConfig } from '@kbn/guided-onboarding'; +import type { + GuideState, + GuideId, + GuideStepIds, + GuideConfig, + GuideParams, +} from '@kbn/guided-onboarding'; import type { CloudStart } from '@kbn/cloud-plugin/public'; import type { PluginStatus, PluginState } from '../common'; @@ -45,7 +51,8 @@ export interface GuidedOnboardingApi { ) => Promise<{ pluginState: PluginState } | undefined>; completeGuideStep: ( guideId: GuideId, - stepId: GuideStepIds + stepId: GuideStepIds, + params?: GuideParams ) => Promise<{ pluginState: PluginState } | undefined>; isGuidedOnboardingActiveForIntegration$: (integration?: string) => Observable; completeGuidedOnboardingForIntegration: ( diff --git a/src/plugins/guided_onboarding/server/routes/plugin_state_routes.ts b/src/plugins/guided_onboarding/server/routes/plugin_state_routes.ts index 997303c095098..dcd46984b166f 100644 --- a/src/plugins/guided_onboarding/server/routes/plugin_state_routes.ts +++ b/src/plugins/guided_onboarding/server/routes/plugin_state_routes.ts @@ -53,6 +53,8 @@ export const registerPutPluginStateRoute = (router: IRouter) => { id: schema.string(), }) ), + // params are dynamic values + params: schema.maybe(schema.object({}, { unknowns: 'allow' })), }) ), }), diff --git a/src/plugins/unified_histogram/public/chart/utils/field_supports_breakdown.test.ts b/src/plugins/unified_histogram/public/chart/utils/field_supports_breakdown.test.ts index b38b42cf2a249..5ec2d8a1fc638 100644 --- a/src/plugins/unified_histogram/public/chart/utils/field_supports_breakdown.test.ts +++ b/src/plugins/unified_histogram/public/chart/utils/field_supports_breakdown.test.ts @@ -31,5 +31,20 @@ describe('fieldSupportsBreakdown', () => { expect( fieldSupportsBreakdown({ aggregatable: true, scripted: false, type: 'string' } as any) ).toBe(true); + + expect( + fieldSupportsBreakdown({ aggregatable: true, scripted: false, type: 'number' } as any) + ).toBe(true); + }); + + it('should return false if field is aggregatable but it is a time series counter metric', () => { + expect( + fieldSupportsBreakdown({ + aggregatable: true, + scripted: false, + type: 'number', + timeSeriesMetric: 'counter', + } as any) + ).toBe(false); }); }); diff --git a/src/plugins/unified_histogram/public/chart/utils/field_supports_breakdown.ts b/src/plugins/unified_histogram/public/chart/utils/field_supports_breakdown.ts index 302a5950fefcb..88ec604c1462e 100644 --- a/src/plugins/unified_histogram/public/chart/utils/field_supports_breakdown.ts +++ b/src/plugins/unified_histogram/public/chart/utils/field_supports_breakdown.ts @@ -11,4 +11,7 @@ import { DataViewField } from '@kbn/data-views-plugin/public'; const supportedTypes = new Set(['string', 'boolean', 'number', 'ip']); export const fieldSupportsBreakdown = (field: DataViewField) => - supportedTypes.has(field.type) && field.aggregatable && !field.scripted; + supportedTypes.has(field.type) && + field.aggregatable && + !field.scripted && + field.timeSeriesMetric !== 'counter'; diff --git a/src/plugins/unified_search/public/query_string_input/query_bar_menu.test.tsx b/src/plugins/unified_search/public/query_string_input/query_bar_menu.test.tsx index 839983c9c6b32..d5c6377d20942 100644 --- a/src/plugins/unified_search/public/query_string_input/query_bar_menu.test.tsx +++ b/src/plugins/unified_search/public/query_string_input/query_bar_menu.test.tsx @@ -230,7 +230,7 @@ describe('Querybar Menu component', () => { expect(languageSwitcher.length).toBeTruthy(); }); - it('should render the save query quick buttons', async () => { + it('should render the save query quick button', async () => { const newProps = { ...props, openQueryBarMenu: true, @@ -254,10 +254,6 @@ describe('Querybar Menu component', () => { '[data-test-subj="saved-query-management-save-changes-button"]' ); expect(saveChangesButton.length).toBeTruthy(); - const saveChangesAsNewButton = component.find( - '[data-test-subj="saved-query-management-save-as-new-button"]' - ); - expect(saveChangesAsNewButton.length).toBeTruthy(); }); it('should render all filter panel options by default', async () => { diff --git a/src/plugins/unified_search/public/query_string_input/query_bar_menu.tsx b/src/plugins/unified_search/public/query_string_input/query_bar_menu.tsx index a9342a65ea74f..9ba28c72c47e3 100644 --- a/src/plugins/unified_search/public/query_string_input/query_bar_menu.tsx +++ b/src/plugins/unified_search/public/query_string_input/query_bar_menu.tsx @@ -32,7 +32,11 @@ import { export const strings = { getFilterSetButtonLabel: () => i18n.translate('unifiedSearch.filter.options.filterSetButtonLabel', { - defaultMessage: 'Saved query menu', + defaultMessage: 'Query menu', + }), + getSavedQueryPopoverSaveChangesButtonText: () => + i18n.translate('unifiedSearch.search.searchBar.savedQueryPopoverSaveChangesButtonText', { + defaultMessage: 'Update query', }), }; @@ -171,7 +175,10 @@ function QueryBarMenuComponent({ ); case 'saveForm': return ( - {saveFormComponent}]} /> + {saveFormComponent}]} + /> ); case 'saveAsNewForm': return ( diff --git a/src/plugins/unified_search/public/query_string_input/query_bar_menu_panels.tsx b/src/plugins/unified_search/public/query_string_input/query_bar_menu_panels.tsx index 20f155db53832..d7c8bd8b64a45 100644 --- a/src/plugins/unified_search/public/query_string_input/query_bar_menu_panels.tsx +++ b/src/plugins/unified_search/public/query_string_input/query_bar_menu_panels.tsx @@ -63,7 +63,7 @@ export const strings = { }), getLoadOtherFilterSetLabel: () => i18n.translate('unifiedSearch.filter.options.loadOtherFilterSetLabel', { - defaultMessage: 'Load a different query', + defaultMessage: 'Load query', }), getLoadCurrentFilterSetLabel: () => i18n.translate('unifiedSearch.filter.options.loadCurrentFilterSetLabel', { @@ -71,7 +71,7 @@ export const strings = { }), getSaveAsNewFilterSetLabel: () => i18n.translate('unifiedSearch.filter.options.saveAsNewFilterSetLabel', { - defaultMessage: 'Save as new', + defaultMessage: 'Save query', }), getSaveFilterSetLabel: () => i18n.translate('unifiedSearch.filter.options.saveFilterSetLabel', { @@ -88,7 +88,7 @@ export const strings = { }), getSavedQueryPopoverSaveChangesButtonText: () => i18n.translate('unifiedSearch.search.searchBar.savedQueryPopoverSaveChangesButtonText', { - defaultMessage: 'Save changes', + defaultMessage: 'Update query', }), getSavedQueryPopoverSaveAsNewButtonAriaLabel: () => i18n.translate('unifiedSearch.search.searchBar.savedQueryPopoverSaveAsNewButtonAriaLabel', { @@ -100,7 +100,7 @@ export const strings = { }), getSaveCurrentFilterSetLabel: () => i18n.translate('unifiedSearch.filter.options.saveCurrentFilterSetLabel', { - defaultMessage: 'Save current query', + defaultMessage: 'Save as new', }), getApplyAllFiltersButtonLabel: () => i18n.translate('unifiedSearch.filter.options.applyAllFiltersButtonLabel', { @@ -238,10 +238,6 @@ export function QueryBarMenuPanels({ }; }; - const handleSaveAsNew = useCallback(() => { - setRenderedComponent('saveAsNewForm'); - }, [setRenderedComponent]); - const handleSave = useCallback(() => { setRenderedComponent('saveForm'); }, [setRenderedComponent]); @@ -413,38 +409,17 @@ export function QueryBarMenuPanels({ {savedQuery && savedQueryHasChanged && Boolean(showSaveQuery) && hasFiltersOrQuery && ( - - - - {strings.getSavedQueryPopoverSaveChangesButtonText()} - - - - - {strings.getSavedQueryPopoverSaveAsNewButtonText()} - - - + {strings.getSavedQueryPopoverSaveChangesButtonText()} + )} diff --git a/src/plugins/unified_search/public/query_string_input/query_bar_top_row.tsx b/src/plugins/unified_search/public/query_string_input/query_bar_top_row.tsx index 8ca32108a2555..aecd588673e09 100644 --- a/src/plugins/unified_search/public/query_string_input/query_bar_top_row.tsx +++ b/src/plugins/unified_search/public/query_string_input/query_bar_top_row.tsx @@ -120,6 +120,7 @@ export interface QueryBarTopRowProps isScreenshotMode?: boolean; onTextLangQuerySubmit: (query?: Query | AggregateQuery) => void; onTextLangQueryChange: (query: AggregateQuery) => void; + submitOnBlur?: boolean; } export const SharingMetaFields = React.memo(function SharingMetaFields({ @@ -556,6 +557,7 @@ export const QueryBarTopRow = React.memo( size={props.suggestionsSize} isDisabled={props.isDisabled} appName={appName} + submitOnBlur={props.submitOnBlur} deps={{ unifiedSearch, data, diff --git a/src/plugins/unified_search/public/saved_query_management/saved_query_management_list.test.tsx b/src/plugins/unified_search/public/saved_query_management/saved_query_management_list.test.tsx index 01837e26bb892..c891174986510 100644 --- a/src/plugins/unified_search/public/saved_query_management/saved_query_management_list.test.tsx +++ b/src/plugins/unified_search/public/saved_query_management/saved_query_management_list.test.tsx @@ -136,7 +136,7 @@ describe('Saved query management list component', () => { .find('[data-test-subj="saved-query-management-apply-changes-button"]') .first() .text() - ).toBe('Apply query'); + ).toBe('Load query'); const newProps = { ...props, diff --git a/src/plugins/unified_search/public/saved_query_management/saved_query_management_list.tsx b/src/plugins/unified_search/public/saved_query_management/saved_query_management_list.tsx index daaf523abf68b..5ebb89bd88afc 100644 --- a/src/plugins/unified_search/public/saved_query_management/saved_query_management_list.tsx +++ b/src/plugins/unified_search/public/saved_query_management/saved_query_management_list.tsx @@ -309,22 +309,7 @@ export function SavedQueryManagementList({ )} - - {canEditSavedObjects && ( - - - Manage - - - )} + - {hasFiltersOrQuery - ? i18n.translate( - 'unifiedSearch.search.searchBar.savedQueryPopoverReplaceFilterSetLabel', - { - defaultMessage: 'Load query', - } - ) - : i18n.translate( - 'unifiedSearch.search.searchBar.savedQueryPopoverApplyFilterSetLabel', - { - defaultMessage: 'Apply query', - } - )} + {i18n.translate( + 'unifiedSearch.search.searchBar.savedQueryPopoverApplyFilterSetLabel', + { + defaultMessage: 'Load query', + } + )} + {canEditSavedObjects && ( + + + {i18n.translate('unifiedSearch.search.searchBar.savedQueryPopoverManageLabel', { + defaultMessage: 'Manage saved objects', + })} + + + )} {showDeletionConfirmationModal && toBeDeletedSavedQuery && ( diff --git a/src/plugins/unified_search/public/search_bar/search_bar.tsx b/src/plugins/unified_search/public/search_bar/search_bar.tsx index 0a83a45de1d03..3326d81e29109 100644 --- a/src/plugins/unified_search/public/search_bar/search_bar.tsx +++ b/src/plugins/unified_search/public/search_bar/search_bar.tsx @@ -105,6 +105,8 @@ export interface SearchBarOwnProps { * Disables all inputs and interactive elements, */ isDisabled?: boolean; + + submitOnBlur?: boolean; } export type SearchBarProps = SearchBarOwnProps & @@ -585,6 +587,7 @@ class SearchBarUI extends C isScreenshotMode={this.props.isScreenshotMode} onTextLangQuerySubmit={this.onTextLangQuerySubmit} onTextLangQueryChange={this.onTextLangQueryChange} + submitOnBlur={this.props.submitOnBlur} /> ); diff --git a/test/api_integration/apis/guided_onboarding/get_state.ts b/test/api_integration/apis/guided_onboarding/get_state.ts index 489bf26830585..2bb428c6b353a 100644 --- a/test/api_integration/apis/guided_onboarding/get_state.ts +++ b/test/api_integration/apis/guided_onboarding/get_state.ts @@ -11,6 +11,7 @@ import { testGuideStep1ActiveState, testGuideNotActiveState, mockPluginStateNotStarted, + testGuideParams, } from '@kbn/guided-onboarding-plugin/public/services/api.mocks'; import { guideStateSavedObjectsType, @@ -110,5 +111,19 @@ export default function testGetState({ getService }: FtrProviderContext) { expect(response.body.pluginState).not.to.be.empty(); expect(response.body.pluginState.isActivePeriod).to.eql(true); }); + + it('returns the dynamic params', async () => { + // Create an active guide + await createGuides(kibanaServer, [{ ...testGuideStep1ActiveState, params: testGuideParams }]); + + // Create a plugin state + await createPluginState(kibanaServer, { + status: 'in_progress', + creationDate: new Date().toISOString(), + }); + + const response = await supertest.get(getStatePath).expect(200); + expect(response.body.pluginState.activeGuide.params).to.eql(testGuideParams); + }); }); } diff --git a/test/api_integration/apis/guided_onboarding/put_state.ts b/test/api_integration/apis/guided_onboarding/put_state.ts index 7b287b56bb77c..d22366dc080e0 100644 --- a/test/api_integration/apis/guided_onboarding/put_state.ts +++ b/test/api_integration/apis/guided_onboarding/put_state.ts @@ -10,6 +10,9 @@ import expect from '@kbn/expect'; import { testGuideStep1ActiveState, testGuideNotActiveState, + testGuideStep1InProgressState, + testGuideStep2ActiveState, + testGuideParams, } from '@kbn/guided-onboarding-plugin/public/services/api.mocks'; import { pluginStateSavedObjectsType, @@ -161,5 +164,29 @@ export default function testPutState({ getService }: FtrProviderContext) { }); expect(kubernetesGuide.attributes.isActive).to.eql(true); }); + + it('saves dynamic params if provided', async () => { + // create a guide + await createGuides(kibanaServer, [testGuideStep1InProgressState]); + + // complete step1 with dynamic params + await supertest + .put(putStatePath) + .set('kbn-xsrf', 'true') + .send({ + guide: { + ...testGuideStep2ActiveState, + params: testGuideParams, + }, + }) + .expect(200); + + // check that params object was saved + const testGuideSO = await kibanaServer.savedObjects.get({ + type: guideStateSavedObjectsType, + id: testGuideId, + }); + expect(testGuideSO.attributes.params).to.eql(testGuideParams); + }); }); } diff --git a/test/examples/content_management/index.ts b/test/examples/content_management/index.ts index d5e3f14bfc0e6..c02b022781187 100644 --- a/test/examples/content_management/index.ts +++ b/test/examples/content_management/index.ts @@ -12,5 +12,6 @@ import { PluginFunctionalProviderContext } from '../../plugin_functional/service export default function ({ loadTestFile }: PluginFunctionalProviderContext) { describe('content management examples', function () { loadTestFile(require.resolve('./todo_app')); + loadTestFile(require.resolve('./msearch')); }); } diff --git a/test/examples/content_management/msearch.ts b/test/examples/content_management/msearch.ts new file mode 100644 index 0000000000000..0a583b455cd04 --- /dev/null +++ b/test/examples/content_management/msearch.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 { PluginFunctionalProviderContext } from '../../plugin_functional/services'; + +// eslint-disable-next-line import/no-default-export +export default function ({ getService, getPageObjects }: PluginFunctionalProviderContext) { + const testSubjects = getService('testSubjects'); + const PageObjects = getPageObjects(['common', 'home', 'header']); + const listingTable = getService('listingTable'); + + describe('MSearch demo', () => { + before(async () => { + await PageObjects.common.navigateToUrl('home', '/tutorial_directory/sampleData', { + useActualUrl: true, + }); + await PageObjects.header.waitUntilLoadingHasFinished(); + await PageObjects.home.addSampleDataSet('flights'); + }); + after(async () => { + await PageObjects.common.navigateToUrl('home', '/tutorial_directory/sampleData', { + useActualUrl: true, + }); + await PageObjects.header.waitUntilLoadingHasFinished(); + await PageObjects.home.removeSampleDataSet('flights'); + }); + + it('MSearch demo works', async () => { + const appId = 'contentManagementExamples'; + await PageObjects.common.navigateToApp(appId, { + path: 'msearch', + }); + + await listingTable.waitUntilTableIsLoaded(); + await listingTable.searchForItemWithName('Origin Time Delayed'); + + await testSubjects.existOrFail( + `cm-msearch-tableListingTitleLink-[Flights]-Origin-Time-Delayed` + ); + }); + }); +} diff --git a/test/functional/apps/dashboard/group1/dashboard_unsaved_state.ts b/test/functional/apps/dashboard/group1/dashboard_unsaved_state.ts index 7fbdc66313eba..51669b395b6f1 100644 --- a/test/functional/apps/dashboard/group1/dashboard_unsaved_state.ts +++ b/test/functional/apps/dashboard/group1/dashboard_unsaved_state.ts @@ -85,10 +85,14 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { await validateQueryAndFilter(); }); - after(async () => { - // discard changes made in view mode - await PageObjects.dashboard.switchToEditMode(); - await PageObjects.dashboard.clickCancelOutOfEditMode(); + it('can discard changes', async () => { + await PageObjects.dashboard.clickDiscardChanges(); + await PageObjects.dashboard.waitForRenderComplete(); + + const query = await queryBar.getQueryString(); + expect(query).to.eql(''); + const filterCount = await filterBar.getFilterCount(); + expect(filterCount).to.eql(0); }); }); @@ -140,8 +144,21 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { expect(currentPanelCount).to.eql(unsavedPanelCount); }); - it('resets to original panel count after discarding changes', async () => { + it('can discard changes', async () => { + unsavedPanelCount = await PageObjects.dashboard.getPanelCount(); + expect(unsavedPanelCount).to.eql(originalPanelCount + 2); + + await PageObjects.dashboard.clickDiscardChanges(); + const currentPanelCount = await PageObjects.dashboard.getPanelCount(); + expect(currentPanelCount).to.eql(originalPanelCount); + }); + + it('resets to original panel count after switching to view mode and discarding changes', async () => { + await addPanels(); await PageObjects.header.waitUntilLoadingHasFinished(); + unsavedPanelCount = await PageObjects.dashboard.getPanelCount(); + expect(unsavedPanelCount).to.eql(originalPanelCount + 2); + await PageObjects.dashboard.clickCancelOutOfEditMode(); await PageObjects.header.waitUntilLoadingHasFinished(); const currentPanelCount = await PageObjects.dashboard.getPanelCount(); diff --git a/test/functional/apps/dashboard_elements/controls/options_list/options_list_dashboard_interaction.ts b/test/functional/apps/dashboard_elements/controls/options_list/options_list_dashboard_interaction.ts index 1397de3712bb8..bda198fc16e8b 100644 --- a/test/functional/apps/dashboard_elements/controls/options_list/options_list_dashboard_interaction.ts +++ b/test/functional/apps/dashboard_elements/controls/options_list/options_list_dashboard_interaction.ts @@ -257,21 +257,36 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { await dashboard.clearUnsavedChanges(); }); - it('changes to selections can be discarded', async () => { - await dashboardControls.optionsListOpenPopover(controlId); - await dashboardControls.optionsListPopoverSelectOption('bark'); - await dashboardControls.optionsListEnsurePopoverIsClosed(controlId); - let selections = await dashboardControls.optionsListGetSelectionsString(controlId); - expect(selections).to.equal('hiss, grr, bark'); + describe('discarding changes', async () => { + describe('changes can be discarded', async () => { + let selections = ''; + + beforeEach(async () => { + await dashboardControls.optionsListOpenPopover(controlId); + await dashboardControls.optionsListPopoverSelectOption('bark'); + await dashboardControls.optionsListEnsurePopoverIsClosed(controlId); + selections = await dashboardControls.optionsListGetSelectionsString(controlId); + expect(selections).to.equal('hiss, grr, bark'); + }); - await dashboard.clickCancelOutOfEditMode(); - selections = await dashboardControls.optionsListGetSelectionsString(controlId); - expect(selections).to.equal('hiss, grr'); - }); + afterEach(async () => { + selections = await dashboardControls.optionsListGetSelectionsString(controlId); + expect(selections).to.equal('hiss, grr'); + }); - it('dashboard does not load with unsaved changes when changes are discarded', async () => { - await dashboard.switchToEditMode(); - await testSubjects.missingOrFail('dashboardUnsavedChangesBadge'); + it('by clicking the discard changes button', async () => { + await dashboard.clickDiscardChanges(); + }); + + it('by switching to view mode', async () => { + await dashboard.clickCancelOutOfEditMode(); + }); + }); + + it('dashboard does not load with unsaved changes when changes are discarded', async () => { + await dashboard.switchToEditMode(); + await testSubjects.missingOrFail('dashboardUnsavedChangesBadge'); + }); }); }); diff --git a/test/functional/page_objects/common_page.ts b/test/functional/page_objects/common_page.ts index d40e19e2526a9..3e2862bc58bce 100644 --- a/test/functional/page_objects/common_page.ts +++ b/test/functional/page_objects/common_page.ts @@ -221,6 +221,7 @@ export class CommonPageObject extends FtrService { { basePath = '', shouldLoginIfPrompted = true, + path = '', hash = '', search = '', disableWelcomePrompt = true, @@ -238,7 +239,7 @@ export class CommonPageObject extends FtrService { }); } else { appUrl = getUrl.noAuth(this.config.get('servers.kibana'), { - pathname: `${basePath}/app/${appName}`, + pathname: `${basePath}/app/${appName}` + (path ? `/${path}` : ''), hash, search, }); diff --git a/test/functional/page_objects/dashboard_page.ts b/test/functional/page_objects/dashboard_page.ts index 2fa2290a70a6c..28f30bd94aecf 100644 --- a/test/functional/page_objects/dashboard_page.ts +++ b/test/functional/page_objects/dashboard_page.ts @@ -309,6 +309,18 @@ export class DashboardPageObject extends FtrService { } } + public async clickDiscardChanges(accept = true) { + await this.retry.try(async () => { + await this.expectDiscardChangesButtonEnabled(); + this.log.debug('clickDiscardChanges'); + await this.testSubjects.click('dashboardDiscardChangesMenuItem'); + }); + await this.common.expectConfirmModalOpenState(true); + if (accept) { + await this.common.clickConfirmOnModal(); + } + } + public async clickQuickSave() { await this.retry.try(async () => { await this.expectQuickSaveButtonEnabled(); @@ -734,6 +746,15 @@ export class DashboardPageObject extends FtrService { await this.testSubjects.existOrFail('dashboardQuickSaveMenuItem'); } + public async expectDiscardChangesButtonEnabled() { + this.log.debug('expectDiscardChangesButtonEnabled'); + const quickSaveButton = await this.testSubjects.find('dashboardDiscardChangesMenuItem'); + const isDisabled = await quickSaveButton.getAttribute('disabled'); + if (isDisabled) { + throw new Error('Discard changes button disabled'); + } + } + public async expectQuickSaveButtonEnabled() { this.log.debug('expectQuickSaveButtonEnabled'); const quickSaveButton = await this.testSubjects.find('dashboardQuickSaveMenuItem'); diff --git a/test/functional/services/inspector.ts b/test/functional/services/inspector.ts index c3f357ea3875b..cee65bf1c4b52 100644 --- a/test/functional/services/inspector.ts +++ b/test/functional/services/inspector.ts @@ -238,6 +238,19 @@ export class InspectorService extends FtrService { }); } + public async getTableDataWithId(tableTestSubj: string): Promise { + const chooserDataTestId = 'inspectorTableChooser'; + if (!(await this.testSubjects.exists(chooserDataTestId))) { + return []; + } + + return await this.retry.try(async () => { + await this.testSubjects.click(chooserDataTestId); + await this.testSubjects.click(tableTestSubj); + return this.getTableData(); + }); + } + /** * Returns the selected option value from combobox */ diff --git a/tsconfig.base.json b/tsconfig.base.json index 6bede7391b146..e96e5439e9c2c 100644 --- a/tsconfig.base.json +++ b/tsconfig.base.json @@ -904,6 +904,8 @@ "@kbn/ml-agg-utils/*": ["x-pack/packages/ml/agg_utils/*"], "@kbn/ml-date-picker": ["x-pack/packages/ml/date_picker"], "@kbn/ml-date-picker/*": ["x-pack/packages/ml/date_picker/*"], + "@kbn/ml-error-utils": ["x-pack/packages/ml/error_utils"], + "@kbn/ml-error-utils/*": ["x-pack/packages/ml/error_utils/*"], "@kbn/ml-is-defined": ["x-pack/packages/ml/is_defined"], "@kbn/ml-is-defined/*": ["x-pack/packages/ml/is_defined/*"], "@kbn/ml-is-populated-object": ["x-pack/packages/ml/is_populated_object"], @@ -1360,6 +1362,8 @@ "@kbn/url-drilldown-plugin/*": ["x-pack/plugins/drilldowns/url_drilldown/*"], "@kbn/url-forwarding-plugin": ["src/plugins/url_forwarding"], "@kbn/url-forwarding-plugin/*": ["src/plugins/url_forwarding/*"], + "@kbn/url-state": ["packages/kbn-url-state"], + "@kbn/url-state/*": ["packages/kbn-url-state/*"], "@kbn/usage-collection-plugin": ["src/plugins/usage_collection"], "@kbn/usage-collection-plugin/*": ["src/plugins/usage_collection/*"], "@kbn/usage-collection-test-plugin": ["test/plugin_functional/plugins/usage_collection"], diff --git a/x-pack/packages/ml/error_utils/README.md b/x-pack/packages/ml/error_utils/README.md new file mode 100644 index 0000000000000..d8f2837dffbd3 --- /dev/null +++ b/x-pack/packages/ml/error_utils/README.md @@ -0,0 +1,3 @@ +# @kbn/ml-error-utils + +Empty package generated by @kbn/generate diff --git a/x-pack/plugins/ml/common/util/errors/index.ts b/x-pack/packages/ml/error_utils/index.ts similarity index 65% rename from x-pack/plugins/ml/common/util/errors/index.ts rename to x-pack/packages/ml/error_utils/index.ts index f6566c98490da..9eac8a4d1c70b 100644 --- a/x-pack/plugins/ml/common/util/errors/index.ts +++ b/x-pack/packages/ml/error_utils/index.ts @@ -5,8 +5,8 @@ * 2.0. */ -export { MLRequestFailure } from './request_error'; -export { extractErrorMessage, extractErrorProperties } from './process_errors'; +export { MLRequestFailure } from './src/request_error'; +export { extractErrorMessage, extractErrorProperties } from './src/process_errors'; export type { ErrorType, ErrorMessage, @@ -14,6 +14,8 @@ export type { EsErrorRootCause, MLErrorObject, MLHttpFetchError, + MLHttpFetchErrorBase, MLResponseError, -} from './types'; -export { isBoomError, isErrorString, isEsErrorBody, isMLResponseError } from './types'; + QueryErrorMessage, +} from './src/types'; +export { isBoomError, isErrorString, isEsErrorBody, isMLResponseError } from './src/types'; diff --git a/x-pack/packages/ml/error_utils/jest.config.js b/x-pack/packages/ml/error_utils/jest.config.js new file mode 100644 index 0000000000000..f5da401040575 --- /dev/null +++ b/x-pack/packages/ml/error_utils/jest.config.js @@ -0,0 +1,12 @@ +/* + * 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. + */ + +module.exports = { + preset: '@kbn/test', + rootDir: '../../../..', + roots: ['/x-pack/packages/ml/error_utils'], +}; diff --git a/x-pack/packages/ml/error_utils/kibana.jsonc b/x-pack/packages/ml/error_utils/kibana.jsonc new file mode 100644 index 0000000000000..7629766aca7a7 --- /dev/null +++ b/x-pack/packages/ml/error_utils/kibana.jsonc @@ -0,0 +1,5 @@ +{ + "type": "shared-common", + "id": "@kbn/ml-error-utils", + "owner": "@elastic/ml-ui" +} diff --git a/x-pack/packages/ml/error_utils/package.json b/x-pack/packages/ml/error_utils/package.json new file mode 100644 index 0000000000000..9f0e6c09ef578 --- /dev/null +++ b/x-pack/packages/ml/error_utils/package.json @@ -0,0 +1,6 @@ +{ + "name": "@kbn/ml-error-utils", + "private": true, + "version": "1.0.0", + "license": "Elastic License 2.0" +} \ No newline at end of file diff --git a/x-pack/plugins/ml/common/util/errors/errors.test.ts b/x-pack/packages/ml/error_utils/src/process_errors.test.ts similarity index 95% rename from x-pack/plugins/ml/common/util/errors/errors.test.ts rename to x-pack/packages/ml/error_utils/src/process_errors.test.ts index 5b1e113d06f92..5624c2f0c7477 100644 --- a/x-pack/plugins/ml/common/util/errors/errors.test.ts +++ b/x-pack/packages/ml/error_utils/src/process_errors.test.ts @@ -7,7 +7,8 @@ import Boom from '@hapi/boom'; -import { extractErrorMessage, MLHttpFetchError, EsErrorBody } from '.'; +import { extractErrorMessage } from './process_errors'; +import { type MLHttpFetchError, type EsErrorBody } from './types'; describe('ML - error message utils', () => { describe('extractErrorMessage', () => { diff --git a/x-pack/plugins/ml/common/util/errors/process_errors.ts b/x-pack/packages/ml/error_utils/src/process_errors.ts similarity index 86% rename from x-pack/plugins/ml/common/util/errors/process_errors.ts rename to x-pack/packages/ml/error_utils/src/process_errors.ts index 0da2650fa5fd6..1a50fd6ce3494 100644 --- a/x-pack/plugins/ml/common/util/errors/process_errors.ts +++ b/x-pack/packages/ml/error_utils/src/process_errors.ts @@ -16,15 +16,19 @@ import { isMLResponseError, } from './types'; +/** + * Extract properties of the error object from within the response error + * coming from Kibana, Elasticsearch, and our own ML messages. + * + * @param {ErrorType} error + * @returns {MLErrorObject} + */ export const extractErrorProperties = (error: ErrorType): MLErrorObject => { - // extract properties of the error object from within the response error - // coming from Kibana, Elasticsearch, and our own ML messages - // some responses contain raw es errors as part of a bulk response // e.g. if some jobs fail the action in a bulk request if (isEsErrorBody(error)) { return { - message: error.error.reason, + message: error.error.reason ?? '', statusCode: error.status, fullError: error, }; @@ -79,7 +83,7 @@ export const extractErrorProperties = (error: ErrorType): MLErrorObject => { typeof error.body.attributes.body.error.root_cause[0] === 'object' && isPopulatedObject(error.body.attributes.body.error.root_cause[0], ['script']) ) { - errObj.causedBy = error.body.attributes.body.error.root_cause[0].script; + errObj.causedBy = error.body.attributes.body.error.root_cause[0].script as string; errObj.message += `: '${error.body.attributes.body.error.root_cause[0].script}'`; } return errObj; @@ -103,8 +107,14 @@ export const extractErrorProperties = (error: ErrorType): MLErrorObject => { }; }; +/** + * Extract only the error message within the response error + * coming from Kibana, Elasticsearch, and our own ML messages. + * + * @param {ErrorType} error + * @returns {string} + */ export const extractErrorMessage = (error: ErrorType): string => { - // extract only the error message within the response error coming from Kibana, Elasticsearch, and our own ML messages const errorObj = extractErrorProperties(error); return errorObj.message; }; diff --git a/x-pack/plugins/ml/common/util/errors/request_error.ts b/x-pack/packages/ml/error_utils/src/request_error.ts similarity index 75% rename from x-pack/plugins/ml/common/util/errors/request_error.ts rename to x-pack/packages/ml/error_utils/src/request_error.ts index 57d63e6cf54b8..a370129871323 100644 --- a/x-pack/plugins/ml/common/util/errors/request_error.ts +++ b/x-pack/packages/ml/error_utils/src/request_error.ts @@ -7,7 +7,22 @@ import { MLErrorObject, ErrorType } from './types'; +/** + * ML Request Failure + * + * @export + * @class MLRequestFailure + * @typedef {MLRequestFailure} + * @extends {Error} + */ export class MLRequestFailure extends Error { + /** + * Creates an instance of MLRequestFailure. + * + * @constructor + * @param {MLErrorObject} error + * @param {ErrorType} resp + */ constructor(error: MLErrorObject, resp: ErrorType) { super(error.message); Object.setPrototypeOf(this, new.target.prototype); diff --git a/x-pack/packages/ml/error_utils/src/types.ts b/x-pack/packages/ml/error_utils/src/types.ts new file mode 100644 index 0000000000000..b66c960b8c8c0 --- /dev/null +++ b/x-pack/packages/ml/error_utils/src/types.ts @@ -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 type Boom from '@hapi/boom'; + +import * as estypes from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; + +import type { IHttpFetchError } from '@kbn/core-http-browser'; +import { isPopulatedObject } from '@kbn/ml-is-populated-object'; + +/** + * Short hand type of estypes.ErrorCause. + * @typedef {EsErrorRootCause} + */ +export type EsErrorRootCause = estypes.ErrorCause; + +/** + * Short hand type of estypes.ErrorResponseBase. + * @typedef {EsErrorBody} + */ +export type EsErrorBody = estypes.ErrorResponseBase; + +/** + * ML Response error + * @export + * @interface MLResponseError + * @typedef {MLResponseError} + */ +export interface MLResponseError { + /** + * statusCode + * @type {number} + */ + statusCode: number; + /** + * error + * @type {string} + */ + error: string; + /** + * message + * @type {string} + */ + message: string; + /** + * Optional attributes + * @type {?{ + body: EsErrorBody; + }} + */ + attributes?: { + body: EsErrorBody; + }; +} + +/** + * Interface holding error message + * @export + * @interface ErrorMessage + * @typedef {ErrorMessage} + */ +export interface ErrorMessage { + /** + * message + * @type {string} + */ + message: string; +} + +/** + * To be used for client side errors related to search query bars. + */ +export interface QueryErrorMessage extends ErrorMessage { + /** + * query + * @type {string} + */ + query: string; +} + +/** + * ML Error Object + * @export + * @interface MLErrorObject + * @typedef {MLErrorObject} + */ +export interface MLErrorObject { + /** + * Optional causedBy + * @type {?string} + */ + causedBy?: string; + /** + * message + * @type {string} + */ + message: string; + /** + * Optional statusCode + * @type {?number} + */ + statusCode?: number; + /** + * Optional fullError + * @type {?EsErrorBody} + */ + fullError?: EsErrorBody; +} + +/** + * MLHttpFetchErrorBase + * @export + * @interface MLHttpFetchErrorBase + * @typedef {MLHttpFetchErrorBase} + * @template T + * @extends {IHttpFetchError} + */ +export interface MLHttpFetchErrorBase extends IHttpFetchError { + /** + * body + * @type {T} + */ + body: T; +} + +/** + * MLHttpFetchError + * @export + * @typedef {MLHttpFetchError} + */ +export type MLHttpFetchError = MLHttpFetchErrorBase; + +/** + * Union type of error types + * @export + * @typedef {ErrorType} + */ +export type ErrorType = MLHttpFetchError | EsErrorBody | Boom.Boom | string | undefined; + +/** + * Type guard to check if error is of type EsErrorBody + * @export + * @param {unknown} error + * @returns {error is EsErrorBody} + */ +export function isEsErrorBody(error: unknown): error is EsErrorBody { + return isPopulatedObject(error, ['error']) && isPopulatedObject(error.error, ['reason']); +} + +/** + * Type guard to check if error is a string. + * @export + * @param {unknown} error + * @returns {error is string} + */ +export function isErrorString(error: unknown): error is string { + return typeof error === 'string'; +} + +/** + * Type guard to check if error is of type ErrorMessage. + * @export + * @param {unknown} error + * @returns {error is ErrorMessage} + */ +export function isErrorMessage(error: unknown): error is ErrorMessage { + return isPopulatedObject(error, ['message']) && typeof error.message === 'string'; +} + +/** + * Type guard to check if error is of type MLResponseError. + * @export + * @param {unknown} error + * @returns {error is MLResponseError} + */ +export function isMLResponseError(error: unknown): error is MLResponseError { + return ( + isPopulatedObject(error, ['body']) && + isPopulatedObject(error.body, ['message']) && + 'message' in error.body + ); +} + +/** + * Type guard to check if error is of type Boom. + * @export + * @param {unknown} error + * @returns {error is Boom.Boom} + */ +export function isBoomError(error: unknown): error is Boom.Boom { + return isPopulatedObject(error, ['isBoom']) && error.isBoom === true; +} diff --git a/x-pack/packages/ml/error_utils/tsconfig.json b/x-pack/packages/ml/error_utils/tsconfig.json new file mode 100644 index 0000000000000..de1c550b0e1ab --- /dev/null +++ b/x-pack/packages/ml/error_utils/tsconfig.json @@ -0,0 +1,22 @@ +{ + "extends": "../../../../tsconfig.base.json", + "compilerOptions": { + "outDir": "target/types", + "types": [ + "jest", + "node", + "react" + ] + }, + "include": [ + "**/*.ts", + "**/*.tsx", + ], + "exclude": [ + "target/**/*" + ], + "kbn_references": [ + "@kbn/ml-is-populated-object", + "@kbn/core-http-browser", + ] +} diff --git a/x-pack/plugins/aiops/public/application/utils/error_utils.ts b/x-pack/plugins/aiops/public/application/utils/error_utils.ts deleted file mode 100644 index f1f1c34dd2959..0000000000000 --- a/x-pack/plugins/aiops/public/application/utils/error_utils.ts +++ /dev/null @@ -1,193 +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. - */ - -// TODO Consolidate with duplicate error utils file in -// `x-pack/plugins/data_visualizer/public/application/index_data_visualizer/utils/error_utils.ts` - -import type { IHttpFetchError } from '@kbn/core-http-browser'; -import Boom from '@hapi/boom'; -import { isPopulatedObject } from '@kbn/ml-is-populated-object'; - -export interface WrappedError { - body: { - attributes: { - body: EsErrorBody; - }; - message: Boom.Boom; - }; - statusCode: number; -} - -export interface EsErrorRootCause { - type: string; - reason: string; - caused_by?: EsErrorRootCause; - script?: string; -} - -export interface EsErrorBody { - error: { - root_cause?: EsErrorRootCause[]; - caused_by?: EsErrorRootCause; - type: string; - reason: string; - }; - status: number; -} - -export interface AiOpsResponseError { - statusCode: number; - error: string; - message: string; - attributes?: { - body: EsErrorBody; - }; -} - -export interface ErrorMessage { - message: string; -} - -export interface AiOpsErrorObject { - causedBy?: string; - message: string; - statusCode?: number; - fullError?: EsErrorBody; -} - -export interface AiOpsHttpFetchError extends IHttpFetchError { - body: T; -} - -export type ErrorType = - | WrappedError - | AiOpsHttpFetchError - | EsErrorBody - | Boom.Boom - | string - | undefined; - -export function isEsErrorBody(error: any): error is EsErrorBody { - return error && error.error?.reason !== undefined; -} - -export function isErrorString(error: any): error is string { - return typeof error === 'string'; -} - -export function isErrorMessage(error: any): error is ErrorMessage { - return error && error.message !== undefined && typeof error.message === 'string'; -} - -export function isAiOpsResponseError(error: any): error is AiOpsResponseError { - return typeof error.body === 'object' && 'message' in error.body; -} - -export function isBoomError(error: any): error is Boom.Boom { - return error?.isBoom === true; -} - -export function isWrappedError(error: any): error is WrappedError { - return error && isBoomError(error.body?.message) === true; -} - -export const extractErrorProperties = (error: ErrorType): AiOpsErrorObject => { - // extract properties of the error object from within the response error - // coming from Kibana, Elasticsearch, and our own AiOps messages - - // some responses contain raw es errors as part of a bulk response - // e.g. if some jobs fail the action in a bulk request - - if (isEsErrorBody(error)) { - return { - message: error.error.reason, - statusCode: error.status, - fullError: error, - }; - } - - if (isErrorString(error)) { - return { - message: error, - }; - } - if (isWrappedError(error)) { - return error.body.message?.output?.payload; - } - - if (isBoomError(error)) { - return { - message: error.output.payload.message, - statusCode: error.output.payload.statusCode, - }; - } - - if (error?.body === undefined && !error?.message) { - return { - message: '', - }; - } - - if (typeof error.body === 'string') { - return { - message: error.body, - }; - } - - if (isAiOpsResponseError(error)) { - if ( - typeof error.body.attributes === 'object' && - typeof error.body.attributes.body?.error?.reason === 'string' - ) { - const errObj: AiOpsErrorObject = { - message: error.body.attributes.body.error.reason, - statusCode: error.body.statusCode, - fullError: error.body.attributes.body, - }; - if ( - typeof error.body.attributes.body.error.caused_by === 'object' && - (typeof error.body.attributes.body.error.caused_by?.reason === 'string' || - typeof error.body.attributes.body.error.caused_by?.caused_by?.reason === 'string') - ) { - errObj.causedBy = - error.body.attributes.body.error.caused_by?.caused_by?.reason || - error.body.attributes.body.error.caused_by?.reason; - } - if ( - Array.isArray(error.body.attributes.body.error.root_cause) && - typeof error.body.attributes.body.error.root_cause[0] === 'object' && - isPopulatedObject(error.body.attributes.body.error.root_cause[0], ['script']) - ) { - errObj.causedBy = error.body.attributes.body.error.root_cause[0].script; - errObj.message += `: '${error.body.attributes.body.error.root_cause[0].script}'`; - } - return errObj; - } else { - return { - message: error.body.message, - statusCode: error.body.statusCode, - }; - } - } - - if (isErrorMessage(error)) { - return { - message: error.message, - }; - } - - // If all else fail return an empty message instead of JSON.stringify - return { - message: '', - }; -}; - -export const extractErrorMessage = (error: ErrorType): string => { - // extract only the error message within the response error coming from Kibana, Elasticsearch, and our own ML messages - const errorObj = extractErrorProperties(error); - return errorObj.message; -}; diff --git a/x-pack/plugins/aiops/public/hooks/use_document_count_stats.ts b/x-pack/plugins/aiops/public/hooks/use_document_count_stats.ts index 9e4f50368a0b5..8cfaa074286d6 100644 --- a/x-pack/plugins/aiops/public/hooks/use_document_count_stats.ts +++ b/x-pack/plugins/aiops/public/hooks/use_document_count_stats.ts @@ -12,10 +12,10 @@ import { i18n } from '@kbn/i18n'; import type { ToastsStart } from '@kbn/core/public'; import { stringHash } from '@kbn/ml-string-hash'; import { createRandomSamplerWrapper } from '@kbn/ml-random-sampler-utils'; +import { extractErrorProperties } from '@kbn/ml-error-utils'; import { RANDOM_SAMPLER_SEED } from '../../common/constants'; -import { extractErrorProperties } from '../application/utils/error_utils'; import { DocumentCountStats, getDocumentCountStatsRequest, diff --git a/x-pack/plugins/aiops/tsconfig.json b/x-pack/plugins/aiops/tsconfig.json index b9a6ff5408eda..89c236ba25c99 100644 --- a/x-pack/plugins/aiops/tsconfig.json +++ b/x-pack/plugins/aiops/tsconfig.json @@ -34,7 +34,6 @@ "@kbn/ui-theme", "@kbn/i18n-react", "@kbn/rison", - "@kbn/core-http-browser", "@kbn/aiops-components", "@kbn/aiops-utils", "@kbn/licensing-plugin", @@ -51,6 +50,7 @@ "@kbn/ml-route-utils", "@kbn/unified-field-list-plugin", "@kbn/ml-random-sampler-utils", + "@kbn/ml-error-utils", ], "exclude": [ "target/**/*", diff --git a/x-pack/plugins/alerting/common/alert_summary.ts b/x-pack/plugins/alerting/common/alert_summary.ts index b1563882d4f21..ed6cf325e20b1 100644 --- a/x-pack/plugins/alerting/common/alert_summary.ts +++ b/x-pack/plugins/alerting/common/alert_summary.ts @@ -29,6 +29,7 @@ export interface AlertSummary { errorMessages: Array<{ date: string; message: string }>; alerts: Record; executionDuration: ExecutionDuration; + revision: number; } export interface AlertStatus { @@ -38,4 +39,5 @@ export interface AlertStatus { actionGroupId?: string; activeStartDate?: string; flapping: boolean; + maintenanceWindowIds?: string[]; } diff --git a/x-pack/plugins/alerting/common/execution_log_types.ts b/x-pack/plugins/alerting/common/execution_log_types.ts index 2d5e34df8f766..7a35bea0df619 100644 --- a/x-pack/plugins/alerting/common/execution_log_types.ts +++ b/x-pack/plugins/alerting/common/execution_log_types.ts @@ -63,6 +63,7 @@ export interface IExecutionLog { rule_id: string; space_ids: string[]; rule_name: string; + maintenance_window_ids: string[]; } export interface IExecutionErrors { diff --git a/x-pack/plugins/alerting/common/rule.ts b/x-pack/plugins/alerting/common/rule.ts index 0bac3fd995a27..f58324f1d09ee 100644 --- a/x-pack/plugins/alerting/common/rule.ts +++ b/x-pack/plugins/alerting/common/rule.ts @@ -10,7 +10,7 @@ import type { SavedObjectAttributes, SavedObjectsResolveResponse, } from '@kbn/core/server'; -import type { KueryNode } from '@kbn/es-query'; +import type { Filter, KueryNode } from '@kbn/es-query'; import { RuleNotifyWhenType } from './rule_notify_when_type'; import { RuleSnooze } from './rule_snooze_type'; @@ -94,11 +94,12 @@ export interface AlertsFilterTimeframe extends SavedObjectAttributes { } export interface AlertsFilter extends SavedObjectAttributes { - query: null | { + query?: { kql: string; + filters: Filter[]; dsl?: string; // This fields is generated in the code by using "kql", therefore it's not optional but defined as optional to avoid modifying a lot of files in different plugins }; - timeframe: null | AlertsFilterTimeframe; + timeframe?: AlertsFilterTimeframe; } export type RuleActionAlertsFilterProperty = AlertsFilterTimeframe | RuleActionParam; @@ -191,10 +192,11 @@ export interface Rule { } export interface SanitizedAlertsFilter extends AlertsFilter { - query: null | { + query?: { kql: string; + filters: Filter[]; }; - timeframe: null | AlertsFilterTimeframe; + timeframe?: AlertsFilterTimeframe; } export type SanitizedRuleAction = Omit & { diff --git a/x-pack/plugins/alerting/public/hooks/use_archive_maintenance_window.test.tsx b/x-pack/plugins/alerting/public/hooks/use_archive_maintenance_window.test.tsx new file mode 100644 index 0000000000000..e6bd2a4071b27 --- /dev/null +++ b/x-pack/plugins/alerting/public/hooks/use_archive_maintenance_window.test.tsx @@ -0,0 +1,116 @@ +/* + * 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 { act, renderHook } from '@testing-library/react-hooks/dom'; +import { waitFor } from '@testing-library/dom'; + +import { MaintenanceWindow } from '../pages/maintenance_windows/types'; +import { AppMockRenderer, createAppMockRenderer } from '../lib/test_utils'; +import { useArchiveMaintenanceWindow } from './use_archive_maintenance_window'; + +const mockAddDanger = jest.fn(); +const mockAddSuccess = jest.fn(); + +jest.mock('../utils/kibana_react', () => { + const originalModule = jest.requireActual('../utils/kibana_react'); + return { + ...originalModule, + useKibana: () => { + const { services } = originalModule.useKibana(); + return { + services: { + ...services, + notifications: { toasts: { addSuccess: mockAddSuccess, addDanger: mockAddDanger } }, + }, + }; + }, + }; +}); +jest.mock('../services/maintenance_windows_api/archive', () => ({ + archiveMaintenanceWindow: jest.fn(), +})); + +const { archiveMaintenanceWindow } = jest.requireMock( + '../services/maintenance_windows_api/archive' +); + +const maintenanceWindow: MaintenanceWindow = { + title: 'archive', + duration: 1, + rRule: { + dtstart: '2023-03-23T19:16:21.293Z', + tzid: 'America/New_York', + }, +}; + +let appMockRenderer: AppMockRenderer; + +describe('useArchiveMaintenanceWindow', () => { + beforeEach(() => { + jest.clearAllMocks(); + + appMockRenderer = createAppMockRenderer(); + archiveMaintenanceWindow.mockResolvedValue(maintenanceWindow); + }); + + it('should call onSuccess if api succeeds', async () => { + const { result } = renderHook(() => useArchiveMaintenanceWindow(), { + wrapper: appMockRenderer.AppWrapper, + }); + + await act(async () => { + await result.current.mutate({ maintenanceWindowId: '123', archive: true }); + }); + await waitFor(() => + expect(mockAddSuccess).toBeCalledWith("Archived maintenance window 'archive'") + ); + }); + + it('should call onError if api fails', async () => { + archiveMaintenanceWindow.mockRejectedValue(''); + + const { result } = renderHook(() => useArchiveMaintenanceWindow(), { + wrapper: appMockRenderer.AppWrapper, + }); + + await act(async () => { + await result.current.mutate({ maintenanceWindowId: '123', archive: true }); + }); + + await waitFor(() => + expect(mockAddDanger).toBeCalledWith('Failed to archive maintenance window.') + ); + }); + + it('should call onSuccess if api succeeds (unarchive)', async () => { + const { result } = renderHook(() => useArchiveMaintenanceWindow(), { + wrapper: appMockRenderer.AppWrapper, + }); + + await act(async () => { + await result.current.mutate({ maintenanceWindowId: '123', archive: false }); + }); + await waitFor(() => + expect(mockAddSuccess).toBeCalledWith("Unarchived maintenance window 'archive'") + ); + }); + + it('should call onError if api fails (unarchive)', async () => { + archiveMaintenanceWindow.mockRejectedValue(''); + + const { result } = renderHook(() => useArchiveMaintenanceWindow(), { + wrapper: appMockRenderer.AppWrapper, + }); + + await act(async () => { + await result.current.mutate({ maintenanceWindowId: '123', archive: false }); + }); + + await waitFor(() => + expect(mockAddDanger).toBeCalledWith('Failed to unarchive maintenance window.') + ); + }); +}); diff --git a/x-pack/plugins/alerting/public/hooks/use_archive_maintenance_window.ts b/x-pack/plugins/alerting/public/hooks/use_archive_maintenance_window.ts new file mode 100644 index 0000000000000..2bda74f83b9bf --- /dev/null +++ b/x-pack/plugins/alerting/public/hooks/use_archive_maintenance_window.ts @@ -0,0 +1,56 @@ +/* + * 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'; +import { useMutation } from '@tanstack/react-query'; + +import { useKibana } from '../utils/kibana_react'; +import { archiveMaintenanceWindow } from '../services/maintenance_windows_api/archive'; + +export function useArchiveMaintenanceWindow() { + const { + http, + notifications: { toasts }, + } = useKibana().services; + + const mutationFn = ({ + maintenanceWindowId, + archive, + }: { + maintenanceWindowId: string; + archive: boolean; + }) => { + return archiveMaintenanceWindow({ http, maintenanceWindowId, archive }); + }; + + return useMutation(mutationFn, { + onSuccess: (data, { archive }) => { + const archiveToast = i18n.translate('xpack.alerting.maintenanceWindowsArchiveSuccess', { + defaultMessage: "Archived maintenance window '{title}'", + values: { + title: data.title, + }, + }); + const unarchiveToast = i18n.translate('xpack.alerting.maintenanceWindowsUnarchiveSuccess', { + defaultMessage: "Unarchived maintenance window '{title}'", + values: { + title: data.title, + }, + }); + toasts.addSuccess(archive ? archiveToast : unarchiveToast); + }, + onError: (error, { archive }) => { + const archiveToast = i18n.translate('xpack.alerting.maintenanceWindowsArchiveFailure', { + defaultMessage: 'Failed to archive maintenance window.', + }); + const unarchiveToast = i18n.translate('xpack.alerting.maintenanceWindowsUnarchiveFailure', { + defaultMessage: 'Failed to unarchive maintenance window.', + }); + toasts.addDanger(archive ? archiveToast : unarchiveToast); + }, + }); +} diff --git a/x-pack/plugins/alerting/public/hooks/use_create_maintenance_window.ts b/x-pack/plugins/alerting/public/hooks/use_create_maintenance_window.ts index 08c01bb080055..e710595bc6180 100644 --- a/x-pack/plugins/alerting/public/hooks/use_create_maintenance_window.ts +++ b/x-pack/plugins/alerting/public/hooks/use_create_maintenance_window.ts @@ -23,12 +23,12 @@ export function useCreateMaintenanceWindow() { }; return useMutation(mutationFn, { - onSuccess: (variables: MaintenanceWindow) => { + onSuccess: (data) => { toasts.addSuccess( i18n.translate('xpack.alerting.maintenanceWindowsCreateSuccess', { defaultMessage: "Created maintenance window '{title}'", values: { - title: variables.title, + title: data.title, }, }) ); diff --git a/x-pack/plugins/alerting/public/hooks/use_find_maintenance_windows.ts b/x-pack/plugins/alerting/public/hooks/use_find_maintenance_windows.ts index 6a71bd9c64518..10b7f3402aca1 100644 --- a/x-pack/plugins/alerting/public/hooks/use_find_maintenance_windows.ts +++ b/x-pack/plugins/alerting/public/hooks/use_find_maintenance_windows.ts @@ -30,7 +30,11 @@ export const useFindMaintenanceWindows = () => { } }; - const { isLoading, data = [] } = useQuery({ + const { + isLoading, + data = [], + refetch, + } = useQuery({ queryKey: ['findMaintenanceWindows'], queryFn, onError: onErrorFn, @@ -42,5 +46,6 @@ export const useFindMaintenanceWindows = () => { return { maintenanceWindows: data, isLoading, + refetch, }; }; diff --git a/x-pack/plugins/alerting/public/hooks/use_finish_and_archive_maintenance_window.test.tsx b/x-pack/plugins/alerting/public/hooks/use_finish_and_archive_maintenance_window.test.tsx new file mode 100644 index 0000000000000..b80dbbae355bc --- /dev/null +++ b/x-pack/plugins/alerting/public/hooks/use_finish_and_archive_maintenance_window.test.tsx @@ -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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ +import { act, renderHook } from '@testing-library/react-hooks/dom'; +import { waitFor } from '@testing-library/dom'; + +import { MaintenanceWindow } from '../pages/maintenance_windows/types'; +import { AppMockRenderer, createAppMockRenderer } from '../lib/test_utils'; +import { useFinishAndArchiveMaintenanceWindow } from './use_finish_and_archive_maintenance_window'; + +const mockAddDanger = jest.fn(); +const mockAddSuccess = jest.fn(); + +jest.mock('../utils/kibana_react', () => { + const originalModule = jest.requireActual('../utils/kibana_react'); + return { + ...originalModule, + useKibana: () => { + const { services } = originalModule.useKibana(); + return { + services: { + ...services, + notifications: { toasts: { addSuccess: mockAddSuccess, addDanger: mockAddDanger } }, + }, + }; + }, + }; +}); +jest.mock('../services/maintenance_windows_api/finish', () => ({ + finishMaintenanceWindow: jest.fn(), +})); +jest.mock('../services/maintenance_windows_api/archive', () => ({ + archiveMaintenanceWindow: jest.fn(), +})); + +const { finishMaintenanceWindow } = jest.requireMock('../services/maintenance_windows_api/finish'); +const { archiveMaintenanceWindow } = jest.requireMock( + '../services/maintenance_windows_api/archive' +); + +const maintenanceWindow: MaintenanceWindow = { + title: 'test', + duration: 1, + rRule: { + dtstart: '2023-03-23T19:16:21.293Z', + tzid: 'America/New_York', + }, +}; + +let appMockRenderer: AppMockRenderer; + +describe('useFinishAndArchiveMaintenanceWindow', () => { + beforeEach(() => { + jest.clearAllMocks(); + + appMockRenderer = createAppMockRenderer(); + finishMaintenanceWindow.mockResolvedValue(maintenanceWindow); + archiveMaintenanceWindow.mockResolvedValue(maintenanceWindow); + }); + + it('should call onSuccess if api succeeds', async () => { + const { result } = renderHook(() => useFinishAndArchiveMaintenanceWindow(), { + wrapper: appMockRenderer.AppWrapper, + }); + + await act(async () => { + await result.current.mutate('123'); + }); + await waitFor(() => + expect(mockAddSuccess).toBeCalledWith( + "Cancelled and archived running maintenance window 'test'" + ) + ); + }); + + it('should call onError if finish api fails', async () => { + finishMaintenanceWindow.mockRejectedValue(''); + + const { result } = renderHook(() => useFinishAndArchiveMaintenanceWindow(), { + wrapper: appMockRenderer.AppWrapper, + }); + + await act(async () => { + await result.current.mutate('123'); + }); + + await waitFor(() => + expect(mockAddDanger).toBeCalledWith('Failed to cancel and archive maintenance window.') + ); + }); + + it('should call onError if archive api fails', async () => { + archiveMaintenanceWindow.mockRejectedValue(''); + + const { result } = renderHook(() => useFinishAndArchiveMaintenanceWindow(), { + wrapper: appMockRenderer.AppWrapper, + }); + + await act(async () => { + await result.current.mutate('123'); + }); + + await waitFor(() => + expect(mockAddDanger).toBeCalledWith('Failed to cancel and archive maintenance window.') + ); + }); +}); diff --git a/x-pack/plugins/alerting/public/hooks/use_finish_and_archive_maintenance_window.ts b/x-pack/plugins/alerting/public/hooks/use_finish_and_archive_maintenance_window.ts new file mode 100644 index 0000000000000..d68bf2c89e379 --- /dev/null +++ b/x-pack/plugins/alerting/public/hooks/use_finish_and_archive_maintenance_window.ts @@ -0,0 +1,45 @@ +/* + * 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'; +import { useMutation } from '@tanstack/react-query'; + +import { useKibana } from '../utils/kibana_react'; +import { finishMaintenanceWindow } from '../services/maintenance_windows_api/finish'; +import { archiveMaintenanceWindow } from '../services/maintenance_windows_api/archive'; + +export function useFinishAndArchiveMaintenanceWindow() { + const { + http, + notifications: { toasts }, + } = useKibana().services; + + const mutationFn = async (maintenanceWindowId: string) => { + await finishMaintenanceWindow({ http, maintenanceWindowId }); + return archiveMaintenanceWindow({ http, maintenanceWindowId, archive: true }); + }; + + return useMutation(mutationFn, { + onSuccess: (data) => { + toasts.addSuccess( + i18n.translate('xpack.alerting.maintenanceWindowsFinishedAndArchiveSuccess', { + defaultMessage: "Cancelled and archived running maintenance window '{title}'", + values: { + title: data.title, + }, + }) + ); + }, + onError: () => { + toasts.addDanger( + i18n.translate('xpack.alerting.maintenanceWindowsFinishedAndArchiveFailure', { + defaultMessage: 'Failed to cancel and archive maintenance window.', + }) + ); + }, + }); +} diff --git a/x-pack/plugins/alerting/public/hooks/use_finish_maintenance_window.test.tsx b/x-pack/plugins/alerting/public/hooks/use_finish_maintenance_window.test.tsx new file mode 100644 index 0000000000000..ed534cb835c8d --- /dev/null +++ b/x-pack/plugins/alerting/public/hooks/use_finish_maintenance_window.test.tsx @@ -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 { act, renderHook } from '@testing-library/react-hooks/dom'; +import { waitFor } from '@testing-library/dom'; + +import { MaintenanceWindow } from '../pages/maintenance_windows/types'; +import { AppMockRenderer, createAppMockRenderer } from '../lib/test_utils'; +import { useFinishMaintenanceWindow } from './use_finish_maintenance_window'; + +const mockAddDanger = jest.fn(); +const mockAddSuccess = jest.fn(); + +jest.mock('../utils/kibana_react', () => { + const originalModule = jest.requireActual('../utils/kibana_react'); + return { + ...originalModule, + useKibana: () => { + const { services } = originalModule.useKibana(); + return { + services: { + ...services, + notifications: { toasts: { addSuccess: mockAddSuccess, addDanger: mockAddDanger } }, + }, + }; + }, + }; +}); +jest.mock('../services/maintenance_windows_api/finish', () => ({ + finishMaintenanceWindow: jest.fn(), +})); + +const { finishMaintenanceWindow } = jest.requireMock('../services/maintenance_windows_api/finish'); + +const maintenanceWindow: MaintenanceWindow = { + title: 'cancel', + duration: 1, + rRule: { + dtstart: '2023-03-23T19:16:21.293Z', + tzid: 'America/New_York', + }, +}; + +let appMockRenderer: AppMockRenderer; + +describe('useFinishMaintenanceWindow', () => { + beforeEach(() => { + jest.clearAllMocks(); + + appMockRenderer = createAppMockRenderer(); + finishMaintenanceWindow.mockResolvedValue(maintenanceWindow); + }); + + it('should call onSuccess if api succeeds', async () => { + const { result } = renderHook(() => useFinishMaintenanceWindow(), { + wrapper: appMockRenderer.AppWrapper, + }); + + await act(async () => { + await result.current.mutate('123'); + }); + await waitFor(() => + expect(mockAddSuccess).toBeCalledWith("Cancelled running maintenance window 'cancel'") + ); + }); + + it('should call onError if api fails', async () => { + finishMaintenanceWindow.mockRejectedValue(''); + + const { result } = renderHook(() => useFinishMaintenanceWindow(), { + wrapper: appMockRenderer.AppWrapper, + }); + + await act(async () => { + await result.current.mutate('123'); + }); + + await waitFor(() => + expect(mockAddDanger).toBeCalledWith('Failed to cancel maintenance window.') + ); + }); +}); diff --git a/x-pack/plugins/alerting/public/hooks/use_finish_maintenance_window.ts b/x-pack/plugins/alerting/public/hooks/use_finish_maintenance_window.ts new file mode 100644 index 0000000000000..7e8aafa1793ad --- /dev/null +++ b/x-pack/plugins/alerting/public/hooks/use_finish_maintenance_window.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 { i18n } from '@kbn/i18n'; +import { useMutation } from '@tanstack/react-query'; + +import { useKibana } from '../utils/kibana_react'; +import { finishMaintenanceWindow } from '../services/maintenance_windows_api/finish'; + +export function useFinishMaintenanceWindow() { + const { + http, + notifications: { toasts }, + } = useKibana().services; + + const mutationFn = (maintenanceWindowId: string) => { + return finishMaintenanceWindow({ http, maintenanceWindowId }); + }; + + return useMutation(mutationFn, { + onSuccess: (data) => { + toasts.addSuccess( + i18n.translate('xpack.alerting.maintenanceWindowsFinishedSuccess', { + defaultMessage: "Cancelled running maintenance window '{title}'", + values: { + title: data.title, + }, + }) + ); + }, + onError: () => { + toasts.addDanger( + i18n.translate('xpack.alerting.maintenanceWindowsFinishedFailure', { + defaultMessage: 'Failed to cancel maintenance window.', + }) + ); + }, + }); +} diff --git a/x-pack/plugins/alerting/public/hooks/use_update_maintenance_window.test.tsx b/x-pack/plugins/alerting/public/hooks/use_update_maintenance_window.test.tsx index 67545d83aba17..897b44295d8c0 100644 --- a/x-pack/plugins/alerting/public/hooks/use_update_maintenance_window.test.tsx +++ b/x-pack/plugins/alerting/public/hooks/use_update_maintenance_window.test.tsx @@ -79,7 +79,7 @@ describe('useUpdateMaintenanceWindow', () => { }); await waitFor(() => - expect(mockAddDanger).toBeCalledWith("Failed to update maintenance window '123'") + expect(mockAddDanger).toBeCalledWith('Failed to update maintenance window.') ); }); }); diff --git a/x-pack/plugins/alerting/public/hooks/use_update_maintenance_window.ts b/x-pack/plugins/alerting/public/hooks/use_update_maintenance_window.ts index de6596b1c766d..c7dd73724b6df 100644 --- a/x-pack/plugins/alerting/public/hooks/use_update_maintenance_window.ts +++ b/x-pack/plugins/alerting/public/hooks/use_update_maintenance_window.ts @@ -39,13 +39,10 @@ export function useUpdateMaintenanceWindow() { }) ); }, - onError: (error, variables) => { + onError: () => { toasts.addDanger( i18n.translate('xpack.alerting.maintenanceWindowsUpdateFailure', { - defaultMessage: "Failed to update maintenance window '{id}'", - values: { - id: variables.maintenanceWindowId, - }, + defaultMessage: 'Failed to update maintenance window.', }) ); }, diff --git a/x-pack/plugins/alerting/public/pages/maintenance_windows/components/create_maintenance_windows_form.tsx b/x-pack/plugins/alerting/public/pages/maintenance_windows/components/create_maintenance_windows_form.tsx index b3fe479c21a88..0dda6a8890529 100644 --- a/x-pack/plugins/alerting/public/pages/maintenance_windows/components/create_maintenance_windows_form.tsx +++ b/x-pack/plugins/alerting/public/pages/maintenance_windows/components/create_maintenance_windows_form.tsx @@ -4,7 +4,7 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ -import React, { useCallback, useState } from 'react'; +import React, { useCallback, useMemo, useState } from 'react'; import moment from 'moment'; import { FIELD_TYPES, @@ -16,7 +16,10 @@ import { } from '@kbn/es-ui-shared-plugin/static/forms/hook_form_lib'; import { Field } from '@kbn/es-ui-shared-plugin/static/forms/components'; import { + EuiButton, EuiButtonEmpty, + EuiCallOut, + EuiConfirmModal, EuiFlexGroup, EuiFlexItem, EuiFormLabel, @@ -33,6 +36,7 @@ import { useCreateMaintenanceWindow } from '../../../hooks/use_create_maintenanc import { useUpdateMaintenanceWindow } from '../../../hooks/use_update_maintenance_window'; import { useUiSetting } from '../../../utils/kibana_react'; import { DatePickerRangeField } from './fields/date_picker_range_field'; +import { useArchiveMaintenanceWindow } from '../../../hooks/use_archive_maintenance_window'; const UseField = getUseField({ component: Field }); @@ -56,6 +60,7 @@ export const CreateMaintenanceWindowForm = React.memo { const [defaultStartDateValue] = useState(moment().toISOString()); const [defaultEndDateValue] = useState(moment().add(30, 'minutes').toISOString()); + const [isModalVisible, setIsModalVisible] = useState(false); const { defaultTimezone, isBrowser } = useDefaultTimezone(); const isEditMode = initialValue !== undefined && maintenanceWindowId !== undefined; @@ -63,6 +68,7 @@ export const CreateMaintenanceWindowForm = React.memo { @@ -109,6 +115,35 @@ export const CreateMaintenanceWindowForm = React.memo setIsModalVisible(false), []); + const showModal = useCallback(() => setIsModalVisible(true), []); + + const modal = useMemo(() => { + let m; + if (isModalVisible) { + m = ( + { + closeModal(); + archiveMaintenanceWindow( + { maintenanceWindowId: maintenanceWindowId!, archive: true }, + { onSuccess } + ); + }} + cancelButtonText={i18n.CANCEL} + confirmButtonText={i18n.ARCHIVE_TITLE} + defaultFocusedButton="confirm" + buttonColor="danger" + > +

{i18n.ARCHIVE_CALLOUT_SUBTITLE}

+
+ ); + } + return m; + }, [closeModal, archiveMaintenanceWindow, isModalVisible, maintenanceWindowId, onSuccess]); + return (
@@ -192,6 +227,15 @@ export const CreateMaintenanceWindowForm = React.memo : null} + {isEditMode ? ( + +

{i18n.ARCHIVE_SUBTITLE}

+ + {i18n.ARCHIVE} + + {modal} +
+ ) : null} { }); test('it renders', () => { - const result = appMockRenderer.render(); + const result = appMockRenderer.render( + {}} loading={false} items={items} /> + ); expect(result.getAllByTestId('list-item')).toHaveLength(items.length); diff --git a/x-pack/plugins/alerting/public/pages/maintenance_windows/components/maintenance_windows_list.tsx b/x-pack/plugins/alerting/public/pages/maintenance_windows/components/maintenance_windows_list.tsx index 9d4fc521c3f66..705219b9baa2a 100644 --- a/x-pack/plugins/alerting/public/pages/maintenance_windows/components/maintenance_windows_list.tsx +++ b/x-pack/plugins/alerting/public/pages/maintenance_windows/components/maintenance_windows_list.tsx @@ -5,29 +5,34 @@ * 2.0. */ -import React, { useMemo } from 'react'; +import React, { useCallback, useMemo } from 'react'; import { formatDate, EuiInMemoryTable, EuiBasicTableColumn, - EuiButton, - useEuiBackgroundColor, EuiFlexGroup, EuiFlexItem, SearchFilterConfig, + EuiBadge, + useEuiTheme, } from '@elastic/eui'; import { css } from '@emotion/react'; import { MaintenanceWindowFindResponse, SortDirection } from '../types'; import * as i18n from '../translations'; import { useEditMaintenanceWindowsNavigation } from '../../../hooks/use_navigation'; +import { STATUS_DISPLAY, STATUS_SORT } from '../constants'; import { UpcomingEventsPopover } from './upcoming_events_popover'; -import { StatusColor, STATUS_DISPLAY, STATUS_SORT } from '../constants'; import { MaintenanceWindowStatus } from '../../../../common'; import { StatusFilter } from './status_filter'; +import { TableActionsPopover } from './table_actions_popover'; +import { useFinishMaintenanceWindow } from '../../../hooks/use_finish_maintenance_window'; +import { useArchiveMaintenanceWindow } from '../../../hooks/use_archive_maintenance_window'; +import { useFinishAndArchiveMaintenanceWindow } from '../../../hooks/use_finish_and_archive_maintenance_window'; interface MaintenanceWindowsListProps { loading: boolean; items: MaintenanceWindowFindResponse[]; + refreshData: () => void; } const columns: Array> = [ @@ -39,23 +44,9 @@ const columns: Array> = [ { field: 'status', name: i18n.TABLE_STATUS, - render: (status: string) => { + render: (status: MaintenanceWindowStatus) => { return ( - {}} - > - {STATUS_DISPLAY[status].label} - + {STATUS_DISPLAY[status].label} ); }, sortable: ({ status }) => STATUS_SORT[status], @@ -108,38 +99,61 @@ const search: { filters: SearchFilterConfig[] } = { }; export const MaintenanceWindowsList = React.memo( - ({ loading, items }) => { + ({ loading, items, refreshData }) => { + const { euiTheme } = useEuiTheme(); const { navigateToEditMaintenanceWindows } = useEditMaintenanceWindowsNavigation(); - const warningBackgroundColor = useEuiBackgroundColor('warning'); - const subduedBackgroundColor = useEuiBackgroundColor('subdued'); + const onEdit = useCallback( + (id) => navigateToEditMaintenanceWindows(id), + [navigateToEditMaintenanceWindows] + ); + const { mutate: finishMaintenanceWindow, isLoading: isLoadingFinish } = + useFinishMaintenanceWindow(); + const onCancel = useCallback( + (id) => finishMaintenanceWindow(id, { onSuccess: () => refreshData() }), + [finishMaintenanceWindow, refreshData] + ); + const { mutate: archiveMaintenanceWindow, isLoading: isLoadingArchive } = + useArchiveMaintenanceWindow(); + const onArchive = useCallback( + (id: string, archive: boolean) => + archiveMaintenanceWindow( + { maintenanceWindowId: id, archive }, + { onSuccess: () => refreshData() } + ), + [archiveMaintenanceWindow, refreshData] + ); + const { mutate: finishAndArchiveMaintenanceWindow, isLoading: isLoadingFinishAndArchive } = + useFinishAndArchiveMaintenanceWindow(); + const onCancelAndArchive = useCallback( + (id: string) => finishAndArchiveMaintenanceWindow(id, { onSuccess: () => refreshData() }), + [finishAndArchiveMaintenanceWindow, refreshData] + ); + const tableCss = useMemo(() => { return css` .euiTableRow { &.running { - background-color: ${warningBackgroundColor}; - } - - &.archived { - background-color: ${subduedBackgroundColor}; + background-color: ${euiTheme.colors.highlight}; } } `; - }, [warningBackgroundColor, subduedBackgroundColor]); + }, [euiTheme.colors.highlight]); const actions: Array> = [ { name: '', - actions: [ - { - name: i18n.TABLE_ACTION_EDIT, - isPrimary: true, - description: 'Edit maintenance window', - icon: 'pencil', - type: 'icon', - onClick: (mw: MaintenanceWindowFindResponse) => navigateToEditMaintenanceWindows(mw.id), - 'data-test-subj': 'action-edit', - }, - ], + render: ({ status, id }: { status: MaintenanceWindowStatus; id: string }) => { + return ( + + ); + }, }, ]; @@ -147,7 +161,7 @@ export const MaintenanceWindowsList = React.memo( { + let appMockRenderer: AppMockRenderer; + + beforeEach(() => { + jest.clearAllMocks(); + appMockRenderer = createAppMockRenderer(); + }); + + test('it renders', () => { + const result = appMockRenderer.render( + {}} + onCancel={() => {}} + onArchive={() => {}} + onCancelAndArchive={() => {}} + /> + ); + + expect(result.getByTestId('table-actions-icon-button')).toBeInTheDocument(); + }); + + test('it shows the correct actions when a maintenance window is running', () => { + const result = appMockRenderer.render( + {}} + onCancel={() => {}} + onArchive={() => {}} + onCancelAndArchive={() => {}} + /> + ); + fireEvent.click(result.getByTestId('table-actions-icon-button')); + expect(result.getByTestId('table-actions-edit')).toBeInTheDocument(); + expect(result.getByTestId('table-actions-cancel')).toBeInTheDocument(); + expect(result.getByTestId('table-actions-cancel-and-archive')).toBeInTheDocument(); + }); + + test('it shows the correct actions when a maintenance window is upcoming', () => { + const result = appMockRenderer.render( + {}} + onCancel={() => {}} + onArchive={() => {}} + onCancelAndArchive={() => {}} + /> + ); + fireEvent.click(result.getByTestId('table-actions-icon-button')); + expect(result.getByTestId('table-actions-edit')).toBeInTheDocument(); + expect(result.getByTestId('table-actions-archive')).toBeInTheDocument(); + }); + + test('it shows the correct actions when a maintenance window is finished', () => { + const result = appMockRenderer.render( + {}} + onCancel={() => {}} + onArchive={() => {}} + onCancelAndArchive={() => {}} + /> + ); + fireEvent.click(result.getByTestId('table-actions-icon-button')); + expect(result.getByTestId('table-actions-edit')).toBeInTheDocument(); + expect(result.getByTestId('table-actions-archive')).toBeInTheDocument(); + }); + + test('it shows the correct actions when a maintenance window is archived', () => { + const result = appMockRenderer.render( + {}} + onCancel={() => {}} + onArchive={() => {}} + onCancelAndArchive={() => {}} + /> + ); + fireEvent.click(result.getByTestId('table-actions-icon-button')); + expect(result.getByTestId('table-actions-unarchive')).toBeInTheDocument(); + }); +}); diff --git a/x-pack/plugins/alerting/public/pages/maintenance_windows/components/table_actions_popover.tsx b/x-pack/plugins/alerting/public/pages/maintenance_windows/components/table_actions_popover.tsx new file mode 100644 index 0000000000000..4742ede93d53c --- /dev/null +++ b/x-pack/plugins/alerting/public/pages/maintenance_windows/components/table_actions_popover.tsx @@ -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 React, { useCallback, useMemo, useState } from 'react'; +import { + EuiButtonIcon, + EuiConfirmModal, + EuiContextMenuItem, + EuiContextMenuPanel, + EuiFlexGroup, + EuiFlexItem, + EuiPopover, +} from '@elastic/eui'; +import * as i18n from '../translations'; +import { MaintenanceWindowStatus } from '../../../../common'; + +interface TableActionsPopoverProps { + id: string; + status: MaintenanceWindowStatus; + onEdit: (id: string) => void; + onCancel: (id: string) => void; + onArchive: (id: string, archive: boolean) => void; + onCancelAndArchive: (id: string) => void; +} +type ModalType = 'cancel' | 'cancelAndArchive' | 'archive' | 'unarchive'; +type ActionType = ModalType | 'edit'; + +export const TableActionsPopover: React.FC = React.memo( + ({ id, status, onEdit, onCancel, onArchive, onCancelAndArchive }) => { + const [isPopoverOpen, setIsPopoverOpen] = useState(false); + const [isModalVisible, setIsModalVisible] = useState(false); + const [modalType, setModalType] = useState(); + + const onButtonClick = useCallback(() => { + setIsPopoverOpen((open) => !open); + }, []); + const closePopover = useCallback(() => { + setIsPopoverOpen(false); + }, []); + + const closeModal = useCallback(() => setIsModalVisible(false), []); + const showModal = useCallback((type: ModalType) => { + setModalType(type); + setIsModalVisible(true); + }, []); + + const modal = useMemo(() => { + const modals = { + cancel: { + props: { + title: i18n.CANCEL_MODAL_TITLE, + onConfirm: () => { + closeModal(); + onCancel(id); + }, + cancelButtonText: i18n.CANCEL_MODAL_BUTTON, + confirmButtonText: i18n.CANCEL_MODAL_TITLE, + }, + subtitle: i18n.CANCEL_MODAL_SUBTITLE, + }, + cancelAndArchive: { + props: { + title: i18n.CANCEL_AND_ARCHIVE_MODAL_TITLE, + onConfirm: () => { + closeModal(); + onCancelAndArchive(id); + }, + cancelButtonText: i18n.CANCEL_MODAL_BUTTON, + confirmButtonText: i18n.CANCEL_AND_ARCHIVE_MODAL_TITLE, + }, + subtitle: i18n.CANCEL_AND_ARCHIVE_MODAL_SUBTITLE, + }, + archive: { + props: { + title: i18n.ARCHIVE_TITLE, + onConfirm: () => { + closeModal(); + onArchive(id, true); + }, + cancelButtonText: i18n.CANCEL, + confirmButtonText: i18n.ARCHIVE_TITLE, + }, + subtitle: i18n.ARCHIVE_SUBTITLE, + }, + unarchive: { + props: { + title: i18n.UNARCHIVE_MODAL_TITLE, + onConfirm: () => { + closeModal(); + onArchive(id, false); + }, + cancelButtonText: i18n.CANCEL, + confirmButtonText: i18n.UNARCHIVE_MODAL_TITLE, + }, + subtitle: i18n.UNARCHIVE_MODAL_SUBTITLE, + }, + }; + let m; + if (isModalVisible && modalType) { + const modalProps = modals[modalType]; + m = ( + +

{modalProps.subtitle}

+
+ ); + } + return m; + }, [id, modalType, isModalVisible, closeModal, onArchive, onCancel, onCancelAndArchive]); + + const items = useMemo(() => { + const menuItems = { + edit: ( + { + closePopover(); + onEdit(id); + }} + > + {i18n.TABLE_ACTION_EDIT} + + ), + cancel: ( + { + closePopover(); + showModal('cancel'); + }} + > + {i18n.TABLE_ACTION_CANCEL} + + ), + cancelAndArchive: ( + { + closePopover(); + showModal('cancelAndArchive'); + }} + > + {i18n.TABLE_ACTION_CANCEL_AND_ARCHIVE} + + ), + archive: ( + { + closePopover(); + showModal('archive'); + }} + > + {i18n.ARCHIVE} + + ), + unarchive: ( + { + closePopover(); + showModal('unarchive'); + }} + > + {i18n.TABLE_ACTION_UNARCHIVE} + + ), + }; + const statusMenuItemsMap: Record = { + running: ['edit', 'cancel', 'cancelAndArchive'], + upcoming: ['edit', 'archive'], + finished: ['edit', 'archive'], + archived: ['unarchive'], + }; + return statusMenuItemsMap[status].map((type) => menuItems[type]); + }, [id, status, onEdit, closePopover, showModal]); + + const button = useMemo( + () => ( + + ), + [onButtonClick] + ); + + return ( + <> + + + + + + + + {modal} + + ); + } +); +TableActionsPopover.displayName = 'TableActionsPopover'; diff --git a/x-pack/plugins/alerting/public/pages/maintenance_windows/constants.ts b/x-pack/plugins/alerting/public/pages/maintenance_windows/constants.ts index 1aed4dac0568c..27b10e804693d 100644 --- a/x-pack/plugins/alerting/public/pages/maintenance_windows/constants.ts +++ b/x-pack/plugins/alerting/public/pages/maintenance_windows/constants.ts @@ -107,15 +107,13 @@ export const RRULE_WEEKDAYS_TO_ISO_WEEKDAYS = mapValues(invert(ISO_WEEKDAYS_TO_R Number(v) ); -export const STATUS_DISPLAY: Record = { - [MaintenanceWindowStatus.Running]: { color: 'warning', label: i18n.TABLE_STATUS_RUNNING }, +export const STATUS_DISPLAY = { + [MaintenanceWindowStatus.Running]: { color: 'primary', label: i18n.TABLE_STATUS_RUNNING }, [MaintenanceWindowStatus.Upcoming]: { color: 'warning', label: i18n.TABLE_STATUS_UPCOMING }, [MaintenanceWindowStatus.Finished]: { color: 'success', label: i18n.TABLE_STATUS_FINISHED }, - [MaintenanceWindowStatus.Archived]: { color: 'text', label: i18n.TABLE_STATUS_ARCHIVED }, + [MaintenanceWindowStatus.Archived]: { color: 'default', label: i18n.TABLE_STATUS_ARCHIVED }, }; -export type StatusColor = 'warning' | 'success' | 'text'; - export const STATUS_SORT = { [MaintenanceWindowStatus.Running]: 0, [MaintenanceWindowStatus.Upcoming]: 1, diff --git a/x-pack/plugins/alerting/public/pages/maintenance_windows/index.tsx b/x-pack/plugins/alerting/public/pages/maintenance_windows/index.tsx index ab5828f0dffa3..fa9b54122562d 100644 --- a/x-pack/plugins/alerting/public/pages/maintenance_windows/index.tsx +++ b/x-pack/plugins/alerting/public/pages/maintenance_windows/index.tsx @@ -31,7 +31,7 @@ export const MaintenanceWindowsPage = React.memo(() => { const { docLinks } = useKibana().services; const { navigateToCreateMaintenanceWindow } = useCreateMaintenanceWindowNavigation(); - const { isLoading, maintenanceWindows } = useFindMaintenanceWindows(); + const { isLoading, maintenanceWindows, refetch } = useFindMaintenanceWindows(); useBreadcrumbs(AlertingDeepLinkId.maintenanceWindows); @@ -39,6 +39,8 @@ export const MaintenanceWindowsPage = React.memo(() => { navigateToCreateMaintenanceWindow(); }, [navigateToCreateMaintenanceWindow]); + const refreshData = useCallback(() => refetch(), [refetch]); + const showEmptyPrompt = !isLoading && maintenanceWindows.length === 0; if (isLoading) { @@ -77,7 +79,11 @@ export const MaintenanceWindowsPage = React.memo(() => { ) : ( <> - + )} diff --git a/x-pack/plugins/alerting/public/pages/maintenance_windows/translations.ts b/x-pack/plugins/alerting/public/pages/maintenance_windows/translations.ts index 30b83963cc5b7..65f24411a2a1a 100644 --- a/x-pack/plugins/alerting/public/pages/maintenance_windows/translations.ts +++ b/x-pack/plugins/alerting/public/pages/maintenance_windows/translations.ts @@ -454,6 +454,102 @@ export const SAVE_MAINTENANCE_WINDOW = i18n.translate( } ); +export const TABLE_ACTION_CANCEL = i18n.translate( + 'xpack.alerting.maintenanceWindows.table.cancel', + { + defaultMessage: 'Cancel', + } +); + +export const CANCEL_MODAL_TITLE = i18n.translate( + 'xpack.alerting.maintenanceWindows.cancelModal.title', + { + defaultMessage: 'Cancel maintenance window', + } +); + +export const CANCEL_MODAL_SUBTITLE = i18n.translate( + 'xpack.alerting.maintenanceWindows.cancelModal.subtitle', + { + defaultMessage: + 'Rule notifications resume immediately. Running maintenance window events are canceled; upcoming events are unaffected.', + } +); + +export const CANCEL_MODAL_BUTTON = i18n.translate( + 'xpack.alerting.maintenanceWindows.cancelModal.button', + { + defaultMessage: 'Keep running', + } +); + +export const TABLE_ACTION_CANCEL_AND_ARCHIVE = i18n.translate( + 'xpack.alerting.maintenanceWindows.table.cancelAndArchive', + { + defaultMessage: 'Cancel and archive', + } +); + +export const CANCEL_AND_ARCHIVE_MODAL_TITLE = i18n.translate( + 'xpack.alerting.maintenanceWindows.cancelAndArchiveModal.title', + { + defaultMessage: 'Cancel and archive maintenance window', + } +); + +export const CANCEL_AND_ARCHIVE_MODAL_SUBTITLE = i18n.translate( + 'xpack.alerting.maintenanceWindows.cancelAndArchiveModal.subtitle', + { + defaultMessage: + 'Rule notifications resume immediately. All running and upcoming maintenance window events are canceled and the window is queued for deletion.', + } +); + +export const ARCHIVE = i18n.translate('xpack.alerting.maintenanceWindows.archive', { + defaultMessage: 'Archive', +}); + +export const ARCHIVE_TITLE = i18n.translate('xpack.alerting.maintenanceWindows.archive.title', { + defaultMessage: 'Archive maintenance window', +}); + +export const ARCHIVE_SUBTITLE = i18n.translate( + 'xpack.alerting.maintenanceWindows.archive.subtitle', + { + defaultMessage: + 'Upcoming maintenance window events are canceled and the window is queued for deletion.', + } +); + +export const TABLE_ACTION_UNARCHIVE = i18n.translate( + 'xpack.alerting.maintenanceWindows.table.unarchive', + { + defaultMessage: 'Unarchive', + } +); + +export const UNARCHIVE_MODAL_TITLE = i18n.translate( + 'xpack.alerting.maintenanceWindows.unarchiveModal.title', + { + defaultMessage: 'Unarchive maintenance window', + } +); + +export const UNARCHIVE_MODAL_SUBTITLE = i18n.translate( + 'xpack.alerting.maintenanceWindows.unarchiveModal.subtitle', + { + defaultMessage: 'Upcoming maintenance window events are scheduled.', + } +); + +export const ARCHIVE_CALLOUT_SUBTITLE = i18n.translate( + 'xpack.alerting.maintenanceWindows.archiveCallout.subtitle', + { + defaultMessage: + 'The changes you have made here will not be saved. Are you sure you want to discard these unsaved changes and archive this maintenance window?', + } +); + export const EXPERIMENTAL_LABEL = i18n.translate( 'xpack.alerting.maintenanceWindows.badge.experimentalLabel', { diff --git a/x-pack/plugins/alerting/public/services/maintenance_windows_api/archive.test.ts b/x-pack/plugins/alerting/public/services/maintenance_windows_api/archive.test.ts new file mode 100644 index 0000000000000..8f0e44eaf2eb9 --- /dev/null +++ b/x-pack/plugins/alerting/public/services/maintenance_windows_api/archive.test.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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { httpServiceMock } from '@kbn/core/public/mocks'; +import { MaintenanceWindow } from '../../pages/maintenance_windows/types'; +import { archiveMaintenanceWindow } from './archive'; + +const http = httpServiceMock.createStartContract(); + +beforeEach(() => jest.resetAllMocks()); + +describe('archiveMaintenanceWindow', () => { + test('should call archive maintenance window api', async () => { + const apiResponse = { + title: 'test', + duration: 1, + r_rule: { + dtstart: '2023-03-23T19:16:21.293Z', + tzid: 'America/New_York', + freq: 3, + interval: 1, + byweekday: ['TH'], + }, + }; + http.post.mockResolvedValueOnce(apiResponse); + + const maintenanceWindow: MaintenanceWindow = { + title: 'test', + duration: 1, + rRule: { + dtstart: '2023-03-23T19:16:21.293Z', + tzid: 'America/New_York', + freq: 3, + interval: 1, + byweekday: ['TH'], + }, + }; + + const result = await archiveMaintenanceWindow({ + http, + maintenanceWindowId: '123', + archive: true, + }); + expect(result).toEqual(maintenanceWindow); + expect(http.post.mock.calls[0]).toMatchInlineSnapshot(` + Array [ + "/internal/alerting/rules/maintenance_window/123/_archive", + Object { + "body": "{\\"archive\\":true}", + }, + ] + `); + }); +}); diff --git a/x-pack/plugins/alerting/public/services/maintenance_windows_api/archive.ts b/x-pack/plugins/alerting/public/services/maintenance_windows_api/archive.ts new file mode 100644 index 0000000000000..fe07ebb04b38e --- /dev/null +++ b/x-pack/plugins/alerting/public/services/maintenance_windows_api/archive.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 { HttpSetup } from '@kbn/core/public'; +import { AsApiContract, RewriteRequestCase } from '@kbn/actions-plugin/common'; + +import { MaintenanceWindow } from '../../pages/maintenance_windows/types'; +import { INTERNAL_BASE_ALERTING_API_PATH } from '../../../common'; + +const rewriteBodyRes: RewriteRequestCase = ({ r_rule: rRule, ...rest }) => ({ + ...rest, + rRule, +}); + +export async function archiveMaintenanceWindow({ + http, + maintenanceWindowId, + archive, +}: { + http: HttpSetup; + maintenanceWindowId: string; + archive: boolean; +}): Promise { + const res = await http.post>( + `${INTERNAL_BASE_ALERTING_API_PATH}/rules/maintenance_window/${encodeURIComponent( + maintenanceWindowId + )}/_archive`, + { body: JSON.stringify({ archive }) } + ); + + return rewriteBodyRes(res); +} diff --git a/x-pack/plugins/alerting/public/services/maintenance_windows_api/finish.test.ts b/x-pack/plugins/alerting/public/services/maintenance_windows_api/finish.test.ts new file mode 100644 index 0000000000000..a67b7246a64f5 --- /dev/null +++ b/x-pack/plugins/alerting/public/services/maintenance_windows_api/finish.test.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 { httpServiceMock } from '@kbn/core/public/mocks'; +import { MaintenanceWindow } from '../../pages/maintenance_windows/types'; +import { finishMaintenanceWindow } from './finish'; + +const http = httpServiceMock.createStartContract(); + +beforeEach(() => jest.resetAllMocks()); + +describe('finishMaintenanceWindow', () => { + test('should call finish maintenance window api', async () => { + const apiResponse = { + title: 'test', + duration: 1, + r_rule: { + dtstart: '2023-03-23T19:16:21.293Z', + tzid: 'America/New_York', + freq: 3, + interval: 1, + byweekday: ['TH'], + }, + }; + http.post.mockResolvedValueOnce(apiResponse); + + const maintenanceWindow: MaintenanceWindow = { + title: 'test', + duration: 1, + rRule: { + dtstart: '2023-03-23T19:16:21.293Z', + tzid: 'America/New_York', + freq: 3, + interval: 1, + byweekday: ['TH'], + }, + }; + + const result = await finishMaintenanceWindow({ + http, + maintenanceWindowId: '123', + }); + expect(result).toEqual(maintenanceWindow); + expect(http.post.mock.calls[0]).toMatchInlineSnapshot(` + Array [ + "/internal/alerting/rules/maintenance_window/123/_finish", + ] + `); + }); +}); diff --git a/x-pack/plugins/alerting/public/services/maintenance_windows_api/finish.ts b/x-pack/plugins/alerting/public/services/maintenance_windows_api/finish.ts new file mode 100644 index 0000000000000..910fad0bee1c3 --- /dev/null +++ b/x-pack/plugins/alerting/public/services/maintenance_windows_api/finish.ts @@ -0,0 +1,32 @@ +/* + * 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 { HttpSetup } from '@kbn/core/public'; +import { AsApiContract, RewriteRequestCase } from '@kbn/actions-plugin/common'; + +import { MaintenanceWindow } from '../../pages/maintenance_windows/types'; +import { INTERNAL_BASE_ALERTING_API_PATH } from '../../../common'; + +const rewriteBodyRes: RewriteRequestCase = ({ r_rule: rRule, ...rest }) => ({ + ...rest, + rRule, +}); + +export async function finishMaintenanceWindow({ + http, + maintenanceWindowId, +}: { + http: HttpSetup; + maintenanceWindowId: string; +}): Promise { + const res = await http.post>( + `${INTERNAL_BASE_ALERTING_API_PATH}/rules/maintenance_window/${encodeURIComponent( + maintenanceWindowId + )}/_finish` + ); + + return rewriteBodyRes(res); +} diff --git a/x-pack/plugins/alerting/server/alert/alert.ts b/x-pack/plugins/alerting/server/alert/alert.ts index 1fff89d527d5a..36877db2726c6 100644 --- a/x-pack/plugins/alerting/server/alert/alert.ts +++ b/x-pack/plugins/alerting/server/alert/alert.ts @@ -7,7 +7,7 @@ import { v4 as uuidV4 } from 'uuid'; import { get, isEmpty } from 'lodash'; -import { ALERT_INSTANCE_ID } from '@kbn/rule-data-utils'; +import { ALERT_INSTANCE_ID, ALERT_RULE_UUID } from '@kbn/rule-data-utils'; import { CombinedSummarizedAlerts } from '../types'; import { AlertInstanceMeta, @@ -277,7 +277,9 @@ export class Alert< } return !summarizedAlerts.all.data.some( - (alert) => get(alert, ALERT_INSTANCE_ID) === this.getId() + (alert) => + get(alert, ALERT_INSTANCE_ID) === this.getId() || + get(alert, ALERT_RULE_UUID) === this.getId() ); } } diff --git a/x-pack/plugins/alerting/server/lib/alert_summary_from_event_log.test.ts b/x-pack/plugins/alerting/server/lib/alert_summary_from_event_log.test.ts index dbcb75a7816e7..45f8d84fd4a98 100644 --- a/x-pack/plugins/alerting/server/lib/alert_summary_from_event_log.test.ts +++ b/x-pack/plugins/alerting/server/lib/alert_summary_from_event_log.test.ts @@ -42,6 +42,7 @@ describe('alertSummaryFromEventLog', () => { "lastRun": undefined, "muteAll": false, "name": "rule-name", + "revision": 0, "ruleTypeId": "123", "status": "OK", "statusEndDate": "2020-06-18T01:00:00.000Z", @@ -88,6 +89,7 @@ describe('alertSummaryFromEventLog', () => { "lastRun": undefined, "muteAll": true, "name": "rule-name-2", + "revision": 0, "ruleTypeId": "456", "status": "OK", "statusEndDate": "2020-06-18T03:00:00.000Z", diff --git a/x-pack/plugins/alerting/server/lib/alert_summary_from_event_log.ts b/x-pack/plugins/alerting/server/lib/alert_summary_from_event_log.ts index 7c3df21a3281d..d316b1b5bf01c 100644 --- a/x-pack/plugins/alerting/server/lib/alert_summary_from_event_log.ts +++ b/x-pack/plugins/alerting/server/lib/alert_summary_from_event_log.ts @@ -40,6 +40,7 @@ export function alertSummaryFromEventLog(params: AlertSummaryFromEventLogParams) average: 0, valuesWithTimestamp: {}, }, + revision: rule.revision, }; const alerts = new Map(); @@ -86,6 +87,10 @@ export function alertSummaryFromEventLog(params: AlertSummaryFromEventLogParams) status.flapping = true; } + if (event?.kibana?.alert?.maintenance_window_ids?.length) { + status.maintenanceWindowIds = event.kibana.alert.maintenance_window_ids as string[]; + } + switch (action) { case EVENT_LOG_ACTIONS.newInstance: status.activeStartDate = timeStamp; diff --git a/x-pack/plugins/alerting/server/lib/alerting_event_logger/alerting_event_logger.mock.ts b/x-pack/plugins/alerting/server/lib/alerting_event_logger/alerting_event_logger.mock.ts index ff62ddaea7ff4..00a9bf7221ef3 100644 --- a/x-pack/plugins/alerting/server/lib/alerting_event_logger/alerting_event_logger.mock.ts +++ b/x-pack/plugins/alerting/server/lib/alerting_event_logger/alerting_event_logger.mock.ts @@ -15,6 +15,7 @@ const createAlertingEventLoggerMock = () => { setRuleName: jest.fn(), setExecutionSucceeded: jest.fn(), setExecutionFailed: jest.fn(), + setMaintenanceWindowIds: jest.fn(), logTimeout: jest.fn(), logAlert: jest.fn(), logAction: jest.fn(), diff --git a/x-pack/plugins/alerting/server/lib/alerting_event_logger/alerting_event_logger.test.ts b/x-pack/plugins/alerting/server/lib/alerting_event_logger/alerting_event_logger.test.ts index 2711f921e81ec..2b6075cd49f49 100644 --- a/x-pack/plugins/alerting/server/lib/alerting_event_logger/alerting_event_logger.test.ts +++ b/x-pack/plugins/alerting/server/lib/alerting_event_logger/alerting_event_logger.test.ts @@ -55,6 +55,7 @@ const context: RuleContextOpts = { spaceId: 'test-space', executionId: 'abcd-efgh-ijklmnop', taskScheduledAt: new Date('2020-01-01T00:00:00.000Z'), + ruleRevision: 0, }; const contextWithScheduleDelay = { ...context, taskScheduleDelay: 7200000 }; @@ -284,6 +285,51 @@ describe('AlertingEventLogger', () => { }); }); + describe('setMaintenanceWindowIds()', () => { + test('should throw error if alertingEventLogger has not been initialized', () => { + expect(() => + alertingEventLogger.setMaintenanceWindowIds([]) + ).toThrowErrorMatchingInlineSnapshot(`"AlertingEventLogger not initialized"`); + }); + + test('should throw error if event is null', () => { + alertingEventLogger.initialize(context); + expect(() => + alertingEventLogger.setMaintenanceWindowIds([]) + ).toThrowErrorMatchingInlineSnapshot(`"AlertingEventLogger not initialized"`); + }); + + it('should update event maintenance window IDs correctly', () => { + alertingEventLogger.initialize(context); + alertingEventLogger.start(); + alertingEventLogger.setMaintenanceWindowIds([]); + + const event = initializeExecuteRecord(contextWithScheduleDelay); + expect(alertingEventLogger.getEvent()).toEqual({ + ...event, + kibana: { + ...event.kibana, + alert: { + ...event.kibana?.alert, + maintenance_window_ids: [], + }, + }, + }); + + alertingEventLogger.setMaintenanceWindowIds(['test-id-1', 'test-id-2']); + expect(alertingEventLogger.getEvent()).toEqual({ + ...event, + kibana: { + ...event.kibana, + alert: { + ...event.kibana?.alert, + maintenance_window_ids: ['test-id-1', 'test-id-2'], + }, + }, + }); + }); + }); + describe('logTimeout()', () => { test('should throw error if alertingEventLogger has not been initialized', () => { expect(() => alertingEventLogger.logTimeout()).toThrowErrorMatchingInlineSnapshot( diff --git a/x-pack/plugins/alerting/server/lib/alerting_event_logger/alerting_event_logger.ts b/x-pack/plugins/alerting/server/lib/alerting_event_logger/alerting_event_logger.ts index 37029b4c96703..3af760488a259 100644 --- a/x-pack/plugins/alerting/server/lib/alerting_event_logger/alerting_event_logger.ts +++ b/x-pack/plugins/alerting/server/lib/alerting_event_logger/alerting_event_logger.ts @@ -30,6 +30,7 @@ export interface RuleContextOpts { executionId: string; taskScheduledAt: Date; ruleName?: string; + ruleRevision?: number; } type RuleContext = RuleContextOpts & { @@ -138,6 +139,14 @@ export class AlertingEventLogger { updateEvent(this.event, { message, outcome: 'success', alertingOutcome: 'success' }); } + public setMaintenanceWindowIds(maintenanceWindowIds: string[]) { + if (!this.isInitialized || !this.event) { + throw new Error('AlertingEventLogger not initialized'); + } + + updateEvent(this.event, { maintenanceWindowIds }); + } + public setExecutionFailed(message: string, errorMessage: string) { if (!this.isInitialized || !this.event) { throw new Error('AlertingEventLogger not initialized'); @@ -258,6 +267,7 @@ export function createAlertRecord(context: RuleContextOpts, alert: AlertOpts) { ruleName: context.ruleName, flapping: alert.flapping, maintenanceWindowIds: alert.maintenanceWindowIds, + ruleRevision: context.ruleRevision, }); } @@ -288,6 +298,7 @@ export function createActionExecuteRecord(context: RuleContextOpts, action: Acti ], ruleName: context.ruleName, alertSummary: action.alertSummary, + ruleRevision: context.ruleRevision, }); } @@ -314,6 +325,7 @@ export function createExecuteTimeoutRecord(context: RuleContextOpts) { }, ], ruleName: context.ruleName, + ruleRevision: context.ruleRevision, }); } @@ -326,6 +338,7 @@ export function initializeExecuteRecord(context: RuleContext) { spaceId: context.spaceId, executionId: context.executionId, action: EVENT_LOG_ACTIONS.execute, + ruleRevision: context.ruleRevision, task: { scheduled: context.taskScheduledAt.toISOString(), scheduleDelay: Millis2Nanos * context.taskScheduleDelay, @@ -351,11 +364,22 @@ interface UpdateEventOpts { reason?: string; metrics?: RuleRunMetrics; timings?: TaskRunnerTimings; + maintenanceWindowIds?: string[]; } export function updateEvent(event: IEvent, opts: UpdateEventOpts) { - const { message, outcome, error, ruleName, status, reason, metrics, timings, alertingOutcome } = - opts; + const { + message, + outcome, + error, + ruleName, + status, + reason, + metrics, + timings, + alertingOutcome, + maintenanceWindowIds, + } = opts; if (!event) { throw new Error('Cannot update event because it is not initialized.'); } @@ -431,4 +455,10 @@ export function updateEvent(event: IEvent, opts: UpdateEventOpts) { ...timings, }; } + + if (maintenanceWindowIds) { + event.kibana = event.kibana || {}; + event.kibana.alert = event.kibana.alert || {}; + event.kibana.alert.maintenance_window_ids = maintenanceWindowIds; + } } diff --git a/x-pack/plugins/alerting/server/lib/create_alert_event_log_record_object.test.ts b/x-pack/plugins/alerting/server/lib/create_alert_event_log_record_object.test.ts index e62ec213cba0f..6b18d1aac93dd 100644 --- a/x-pack/plugins/alerting/server/lib/create_alert_event_log_record_object.test.ts +++ b/x-pack/plugins/alerting/server/lib/create_alert_event_log_record_object.test.ts @@ -36,6 +36,7 @@ describe('createAlertEventLogRecordObject', () => { ruleType, consumer: 'rule-consumer', action: 'execute-start', + ruleRevision: 0, timestamp: '1970-01-01T00:00:00.000Z', task: { scheduled: '1970-01-01T00:00:00.000Z', @@ -66,6 +67,7 @@ describe('createAlertEventLogRecordObject', () => { execution: { uuid: '7a7065d7-6e8b-4aae-8d20-c93613dec9fb', }, + revision: 0, rule_type_id: 'test', }, maintenance_window_ids: MAINTENANCE_WINDOW_IDS, @@ -107,6 +109,7 @@ describe('createAlertEventLogRecordObject', () => { group: 'group 1', message: 'message text here', namespace: 'default', + ruleRevision: 0, state: { start: '1970-01-01T00:00:00.000Z', end: '1970-01-01T00:05:00.000Z', @@ -139,6 +142,7 @@ describe('createAlertEventLogRecordObject', () => { execution: { uuid: '7a7065d7-6e8b-4aae-8d20-c93613dec9fb', }, + revision: 0, rule_type_id: 'test', }, maintenance_window_ids: MAINTENANCE_WINDOW_IDS, @@ -182,6 +186,7 @@ describe('createAlertEventLogRecordObject', () => { group: 'group 1', message: 'action execution start', namespace: 'default', + ruleRevision: 0, state: { start: '1970-01-01T00:00:00.000Z', end: '1970-01-01T00:05:00.000Z', @@ -224,6 +229,7 @@ describe('createAlertEventLogRecordObject', () => { execution: { uuid: '7a7065d7-6e8b-4aae-8d20-c93613dec9fb', }, + revision: 0, rule_type_id: 'test', }, maintenance_window_ids: MAINTENANCE_WINDOW_IDS, diff --git a/x-pack/plugins/alerting/server/lib/create_alert_event_log_record_object.ts b/x-pack/plugins/alerting/server/lib/create_alert_event_log_record_object.ts index 97284f0ce9f78..251df68e5267f 100644 --- a/x-pack/plugins/alerting/server/lib/create_alert_event_log_record_object.ts +++ b/x-pack/plugins/alerting/server/lib/create_alert_event_log_record_object.ts @@ -43,6 +43,7 @@ interface CreateAlertEventLogRecordParams { recovered: number; }; maintenanceWindowIds?: string[]; + ruleRevision?: number; } export function createAlertEventLogRecordObject(params: CreateAlertEventLogRecordParams): Event { @@ -62,6 +63,7 @@ export function createAlertEventLogRecordObject(params: CreateAlertEventLogRecor alertUuid, alertSummary, maintenanceWindowIds, + ruleRevision, } = params; const alerting = params.instanceId || group || alertSummary @@ -97,6 +99,7 @@ export function createAlertEventLogRecordObject(params: CreateAlertEventLogRecor ...(maintenanceWindowIds ? { maintenance_window_ids: maintenanceWindowIds } : {}), ...(alertUuid ? { uuid: alertUuid } : {}), rule: { + revision: ruleRevision, rule_type_id: ruleType.id, ...(consumer ? { consumer } : {}), ...(executionId diff --git a/x-pack/plugins/alerting/server/lib/get_execution_log_aggregation.test.ts b/x-pack/plugins/alerting/server/lib/get_execution_log_aggregation.test.ts index 6a57baaacef27..bb38fb7a98bfa 100644 --- a/x-pack/plugins/alerting/server/lib/get_execution_log_aggregation.test.ts +++ b/x-pack/plugins/alerting/server/lib/get_execution_log_aggregation.test.ts @@ -270,7 +270,7 @@ describe('getExecutionLogAggregation', () => { }, }, executionDuration: { max: { field: 'event.duration' } }, - outcomeAndMessage: { + outcomeMessageAndMaintenanceWindow: { top_hits: { size: 1, _source: { @@ -283,6 +283,7 @@ describe('getExecutionLogAggregation', () => { 'kibana.space_ids', 'rule.name', 'kibana.alerting.outcome', + 'kibana.alert.maintenance_window_ids', ], }, }, @@ -477,7 +478,7 @@ describe('getExecutionLogAggregation', () => { }, }, executionDuration: { max: { field: 'event.duration' } }, - outcomeAndMessage: { + outcomeMessageAndMaintenanceWindow: { top_hits: { size: 1, _source: { @@ -490,6 +491,7 @@ describe('getExecutionLogAggregation', () => { 'kibana.space_ids', 'rule.name', 'kibana.alerting.outcome', + 'kibana.alert.maintenance_window_ids', ], }, }, @@ -684,7 +686,7 @@ describe('getExecutionLogAggregation', () => { }, }, executionDuration: { max: { field: 'event.duration' } }, - outcomeAndMessage: { + outcomeMessageAndMaintenanceWindow: { top_hits: { size: 1, _source: { @@ -697,6 +699,7 @@ describe('getExecutionLogAggregation', () => { 'kibana.space_ids', 'rule.name', 'kibana.alerting.outcome', + 'kibana.alert.maintenance_window_ids', ], }, }, @@ -774,7 +777,7 @@ describe('formatExecutionLogResult', () => { numRecoveredAlerts: { value: 0.0, }, - outcomeAndMessage: { + outcomeMessageAndMaintenanceWindow: { hits: { total: { value: 1, @@ -861,7 +864,7 @@ describe('formatExecutionLogResult', () => { numRecoveredAlerts: { value: 5.0, }, - outcomeAndMessage: { + outcomeMessageAndMaintenanceWindow: { hits: { total: { value: 1, @@ -880,6 +883,9 @@ describe('formatExecutionLogResult', () => { }, kibana: { version: '8.2.0', + alert: { + maintenance_window_ids: ['254699b0-dfb2-11ed-bb3d-c91b918d0260'], + }, alerting: { outcome: 'success', }, @@ -958,6 +964,7 @@ describe('formatExecutionLogResult', () => { rule_id: 'a348a740-9e2c-11ec-bd64-774ed95c43ef', rule_name: 'rule_name', space_ids: [], + maintenance_window_ids: [], }, { id: '41b2755e-765a-4044-9745-b03875d5e79a', @@ -981,6 +988,7 @@ describe('formatExecutionLogResult', () => { rule_id: 'a348a740-9e2c-11ec-bd64-774ed95c43ef', rule_name: 'rule_name', space_ids: [], + maintenance_window_ids: ['254699b0-dfb2-11ed-bb3d-c91b918d0260'], }, ], }); @@ -1022,7 +1030,7 @@ describe('formatExecutionLogResult', () => { numRecoveredAlerts: { value: 0.0, }, - outcomeAndMessage: { + outcomeMessageAndMaintenanceWindow: { hits: { total: { value: 1, @@ -1112,7 +1120,7 @@ describe('formatExecutionLogResult', () => { numRecoveredAlerts: { value: 5.0, }, - outcomeAndMessage: { + outcomeMessageAndMaintenanceWindow: { hits: { total: { value: 1, @@ -1131,6 +1139,9 @@ describe('formatExecutionLogResult', () => { }, kibana: { version: '8.2.0', + alert: { + maintenance_window_ids: ['254699b0-dfb2-11ed-bb3d-c91b918d0260'], + }, alerting: { outcome: 'success', }, @@ -1209,6 +1220,7 @@ describe('formatExecutionLogResult', () => { rule_id: 'a348a740-9e2c-11ec-bd64-774ed95c43ef', rule_name: 'rule_name', space_ids: [], + maintenance_window_ids: [], }, { id: '41b2755e-765a-4044-9745-b03875d5e79a', @@ -1232,6 +1244,7 @@ describe('formatExecutionLogResult', () => { rule_id: 'a348a740-9e2c-11ec-bd64-774ed95c43ef', rule_name: 'rule_name', space_ids: [], + maintenance_window_ids: ['254699b0-dfb2-11ed-bb3d-c91b918d0260'], }, ], }); @@ -1273,7 +1286,7 @@ describe('formatExecutionLogResult', () => { numRecoveredAlerts: { value: 0.0, }, - outcomeAndMessage: { + outcomeMessageAndMaintenanceWindow: { hits: { total: { value: 1, @@ -1355,7 +1368,7 @@ describe('formatExecutionLogResult', () => { numRecoveredAlerts: { value: 5.0, }, - outcomeAndMessage: { + outcomeMessageAndMaintenanceWindow: { hits: { total: { value: 1, @@ -1374,6 +1387,9 @@ describe('formatExecutionLogResult', () => { }, kibana: { version: '8.2.0', + alert: { + maintenance_window_ids: ['254699b0-dfb2-11ed-bb3d-c91b918d0260'], + }, alerting: { outcome: 'success', }, @@ -1452,6 +1468,7 @@ describe('formatExecutionLogResult', () => { rule_id: 'a348a740-9e2c-11ec-bd64-774ed95c43ef', rule_name: 'rule_name', space_ids: [], + maintenance_window_ids: [], }, { id: '41b2755e-765a-4044-9745-b03875d5e79a', @@ -1475,6 +1492,7 @@ describe('formatExecutionLogResult', () => { rule_id: 'a348a740-9e2c-11ec-bd64-774ed95c43ef', rule_name: 'rule_name', space_ids: [], + maintenance_window_ids: ['254699b0-dfb2-11ed-bb3d-c91b918d0260'], }, ], }); @@ -1516,7 +1534,7 @@ describe('formatExecutionLogResult', () => { numRecoveredAlerts: { value: 5.0, }, - outcomeAndMessage: { + outcomeMessageAndMaintenanceWindow: { hits: { total: { value: 1, @@ -1603,7 +1621,7 @@ describe('formatExecutionLogResult', () => { numRecoveredAlerts: { value: 5.0, }, - outcomeAndMessage: { + outcomeMessageAndMaintenanceWindow: { hits: { total: { value: 1, @@ -1622,6 +1640,9 @@ describe('formatExecutionLogResult', () => { }, kibana: { version: '8.2.0', + alert: { + maintenance_window_ids: ['254699b0-dfb2-11ed-bb3d-c91b918d0260'], + }, alerting: { outcome: 'success', }, @@ -1700,6 +1721,7 @@ describe('formatExecutionLogResult', () => { rule_id: 'a348a740-9e2c-11ec-bd64-774ed95c43ef', rule_name: 'rule_name', space_ids: [], + maintenance_window_ids: [], }, { id: '61bb867b-661a-471f-bf92-23471afa10b3', @@ -1723,6 +1745,7 @@ describe('formatExecutionLogResult', () => { rule_id: 'a348a740-9e2c-11ec-bd64-774ed95c43ef', rule_name: 'rule_name', space_ids: [], + maintenance_window_ids: ['254699b0-dfb2-11ed-bb3d-c91b918d0260'], }, ], }); diff --git a/x-pack/plugins/alerting/server/lib/get_execution_log_aggregation.ts b/x-pack/plugins/alerting/server/lib/get_execution_log_aggregation.ts index b65499de20d45..44e1f7bbe98c1 100644 --- a/x-pack/plugins/alerting/server/lib/get_execution_log_aggregation.ts +++ b/x-pack/plugins/alerting/server/lib/get_execution_log_aggregation.ts @@ -41,6 +41,7 @@ const NUMBER_OF_NEW_ALERTS_FIELD = 'kibana.alert.rule.execution.metrics.alert_co const NUMBER_OF_RECOVERED_ALERTS_FIELD = 'kibana.alert.rule.execution.metrics.alert_counts.recovered'; const EXECUTION_UUID_FIELD = 'kibana.alert.rule.execution.uuid'; +const MAINTENANCE_WINDOW_IDS_FIELD = 'kibana.alert.maintenance_window_ids'; const Millis2Nanos = 1000 * 1000; @@ -82,7 +83,8 @@ interface IExecutionUuidAggBucket extends estypes.AggregationsStringTermsBucketK numActiveAlerts: estypes.AggregationsMaxAggregate; numRecoveredAlerts: estypes.AggregationsMaxAggregate; numNewAlerts: estypes.AggregationsMaxAggregate; - outcomeAndMessage: estypes.AggregationsTopHitsAggregate; + outcomeMessageAndMaintenanceWindow: estypes.AggregationsTopHitsAggregate; + maintenanceWindowIds: estypes.AggregationsTopHitsAggregate; }; actionExecution: { actionOutcomes: IActionExecution; @@ -401,7 +403,7 @@ export function getExecutionLogAggregation({ field: DURATION_FIELD, }, }, - outcomeAndMessage: { + outcomeMessageAndMaintenanceWindow: { top_hits: { size: 1, _source: { @@ -414,6 +416,7 @@ export function getExecutionLogAggregation({ SPACE_ID_FIELD, RULE_NAME_FIELD, ALERTING_OUTCOME_FIELD, + MAINTENANCE_WINDOW_IDS_FIELD, ], }, }, @@ -485,20 +488,30 @@ function formatExecutionLogAggBucket(bucket: IExecutionUuidAggBucket): IExecutio const actionExecutionError = actionExecutionOutcomes.find((subBucket) => subBucket?.key === 'failure')?.doc_count ?? 0; - const outcomeAndMessage = bucket?.ruleExecution?.outcomeAndMessage?.hits?.hits[0]?._source ?? {}; - let status = outcomeAndMessage.kibana?.alerting?.outcome ?? ''; + const outcomeMessageAndMaintenanceWindow = + bucket?.ruleExecution?.outcomeMessageAndMaintenanceWindow?.hits?.hits[0]?._source ?? {}; + let status = outcomeMessageAndMaintenanceWindow.kibana?.alerting?.outcome ?? ''; if (isEmpty(status)) { - status = outcomeAndMessage.event?.outcome ?? ''; + status = outcomeMessageAndMaintenanceWindow.event?.outcome ?? ''; } - const outcomeMessage = outcomeAndMessage.message ?? ''; - const outcomeErrorMessage = outcomeAndMessage.error?.message ?? ''; + const outcomeMessage = outcomeMessageAndMaintenanceWindow.message ?? ''; + const outcomeErrorMessage = outcomeMessageAndMaintenanceWindow.error?.message ?? ''; const message = status === 'failure' ? `${outcomeMessage} - ${outcomeErrorMessage}` : outcomeMessage; - const version = outcomeAndMessage.kibana?.version ?? ''; - - const ruleId = outcomeAndMessage ? outcomeAndMessage?.rule?.id ?? '' : ''; - const spaceIds = outcomeAndMessage ? outcomeAndMessage?.kibana?.space_ids ?? [] : []; - const ruleName = outcomeAndMessage ? outcomeAndMessage.rule?.name ?? '' : ''; + const version = outcomeMessageAndMaintenanceWindow.kibana?.version ?? ''; + + const ruleId = outcomeMessageAndMaintenanceWindow + ? outcomeMessageAndMaintenanceWindow?.rule?.id ?? '' + : ''; + const spaceIds = outcomeMessageAndMaintenanceWindow + ? outcomeMessageAndMaintenanceWindow?.kibana?.space_ids ?? [] + : []; + const maintenanceWindowIds = outcomeMessageAndMaintenanceWindow + ? outcomeMessageAndMaintenanceWindow.kibana?.alert?.maintenance_window_ids ?? [] + : []; + const ruleName = outcomeMessageAndMaintenanceWindow + ? outcomeMessageAndMaintenanceWindow.rule?.name ?? '' + : ''; return { id: bucket?.key ?? '', timestamp: bucket?.ruleExecution?.executeStartTime.value_as_string ?? '', @@ -520,6 +533,7 @@ function formatExecutionLogAggBucket(bucket: IExecutionUuidAggBucket): IExecutio rule_id: ruleId, space_ids: spaceIds, rule_name: ruleName, + maintenance_window_ids: maintenanceWindowIds, }; } diff --git a/x-pack/plugins/alerting/server/routes/bulk_edit_rules.ts b/x-pack/plugins/alerting/server/routes/bulk_edit_rules.ts index fa30b0ff8d2ed..f22d7d2055b09 100644 --- a/x-pack/plugins/alerting/server/routes/bulk_edit_rules.ts +++ b/x-pack/plugins/alerting/server/routes/bulk_edit_rules.ts @@ -19,6 +19,17 @@ const ruleActionSchema = schema.object({ id: schema.string(), params: schema.recordOf(schema.string(), schema.any(), { defaultValue: {} }), uuid: schema.maybe(schema.string()), + frequency: schema.maybe( + schema.object({ + summary: schema.boolean(), + throttle: schema.nullable(schema.string()), + notifyWhen: schema.oneOf([ + schema.literal('onActionGroupChange'), + schema.literal('onActiveAlert'), + schema.literal('onThrottleInterval'), + ]), + }) + ), }); const operationsSchema = schema.arrayOf( diff --git a/x-pack/plugins/alerting/server/routes/create_rule.test.ts b/x-pack/plugins/alerting/server/routes/create_rule.test.ts index 4285c5a986b2b..edd1d10d06236 100644 --- a/x-pack/plugins/alerting/server/routes/create_rule.test.ts +++ b/x-pack/plugins/alerting/server/routes/create_rule.test.ts @@ -56,6 +56,7 @@ describe('createRuleRoute', () => { query: { kql: 'name:test', dsl: '{"must": {"term": { "name": "test" }}}', + filters: [], }, timeframe: { days: [1], @@ -92,7 +93,7 @@ describe('createRuleRoute', () => { id: mockedAlert.actions[0].id, params: mockedAlert.actions[0].params, alerts_filter: { - query: { kql: mockedAlert.actions[0].alertsFilter!.query!.kql }, + query: { kql: mockedAlert.actions[0].alertsFilter!.query!.kql, filters: [] }, timeframe: mockedAlert.actions[0].alertsFilter?.timeframe!, }, }, @@ -167,6 +168,7 @@ describe('createRuleRoute', () => { Object { "alertsFilter": Object { "query": Object { + "filters": Array [], "kql": "name:test", }, "timeframe": Object { @@ -263,6 +265,7 @@ describe('createRuleRoute', () => { Object { "alertsFilter": Object { "query": Object { + "filters": Array [], "kql": "name:test", }, "timeframe": Object { @@ -360,6 +363,7 @@ describe('createRuleRoute', () => { Object { "alertsFilter": Object { "query": Object { + "filters": Array [], "kql": "name:test", }, "timeframe": Object { @@ -457,6 +461,7 @@ describe('createRuleRoute', () => { Object { "alertsFilter": Object { "query": Object { + "filters": Array [], "kql": "name:test", }, "timeframe": Object { diff --git a/x-pack/plugins/alerting/server/routes/get_global_execution_logs.test.ts b/x-pack/plugins/alerting/server/routes/get_global_execution_logs.test.ts index 3ee2b0d1816ba..f6e1c3417a42f 100644 --- a/x-pack/plugins/alerting/server/routes/get_global_execution_logs.test.ts +++ b/x-pack/plugins/alerting/server/routes/get_global_execution_logs.test.ts @@ -48,6 +48,7 @@ describe('getRuleExecutionLogRoute', () => { rule_id: 'a348a740-9e2c-11ec-bd64-774ed95c43ef', rule_name: 'rule-name', space_ids: ['namespace'], + maintenance_window_ids: [], }, { id: '41b2755e-765a-4044-9745-b03875d5e79a', @@ -71,6 +72,7 @@ describe('getRuleExecutionLogRoute', () => { rule_id: 'a348a740-9e2c-11ec-bd64-774ed95c43ef', rule_name: 'rule-name', space_ids: ['namespace'], + maintenance_window_ids: ['test-id-1'], }, ], }; diff --git a/x-pack/plugins/alerting/server/routes/get_rule.test.ts b/x-pack/plugins/alerting/server/routes/get_rule.test.ts index 17481e538ed86..a672de9cbf320 100644 --- a/x-pack/plugins/alerting/server/routes/get_rule.test.ts +++ b/x-pack/plugins/alerting/server/routes/get_rule.test.ts @@ -49,6 +49,7 @@ describe('getRuleRoute', () => { query: { kql: 'name:test', dsl: '{"must": {"term": { "name": "test" }}}', + filters: [], }, timeframe: { days: [1], diff --git a/x-pack/plugins/alerting/server/routes/get_rule_alert_summary.test.ts b/x-pack/plugins/alerting/server/routes/get_rule_alert_summary.test.ts index a6b0e88bbf5af..f7fe1a3406e9c 100644 --- a/x-pack/plugins/alerting/server/routes/get_rule_alert_summary.test.ts +++ b/x-pack/plugins/alerting/server/routes/get_rule_alert_summary.test.ts @@ -38,6 +38,7 @@ describe('getRuleAlertSummaryRoute', () => { status: 'OK', errorMessages: [], alerts: {}, + revision: 0, executionDuration: { average: 1, valuesWithTimestamp: { diff --git a/x-pack/plugins/alerting/server/routes/get_rule_execution_log.test.ts b/x-pack/plugins/alerting/server/routes/get_rule_execution_log.test.ts index eb22a6429809a..a0dd57da558eb 100644 --- a/x-pack/plugins/alerting/server/routes/get_rule_execution_log.test.ts +++ b/x-pack/plugins/alerting/server/routes/get_rule_execution_log.test.ts @@ -49,6 +49,7 @@ describe('getRuleExecutionLogRoute', () => { rule_id: 'a348a740-9e2c-11ec-bd64-774ed95c43ef', rule_name: 'rule_name', space_ids: ['namespace'], + maintenance_window_ids: [], }, { id: '41b2755e-765a-4044-9745-b03875d5e79a', @@ -72,6 +73,7 @@ describe('getRuleExecutionLogRoute', () => { rule_id: 'a348a740-9e2c-11ec-bd64-774ed95c43ef', rule_name: 'rule_name', space_ids: ['namespace'], + maintenance_window_ids: ['test-id-1'], }, ], }; diff --git a/x-pack/plugins/alerting/server/routes/legacy/get_alert_instance_summary.test.ts b/x-pack/plugins/alerting/server/routes/legacy/get_alert_instance_summary.test.ts index 672ccee369aac..a78fcd7f86f65 100644 --- a/x-pack/plugins/alerting/server/routes/legacy/get_alert_instance_summary.test.ts +++ b/x-pack/plugins/alerting/server/routes/legacy/get_alert_instance_summary.test.ts @@ -47,6 +47,7 @@ describe('getAlertInstanceSummaryRoute', () => { average: 0, valuesWithTimestamp: {}, }, + revision: 0, }; it('gets alert instance summary', async () => { diff --git a/x-pack/plugins/alerting/server/routes/lib/actions_schema.ts b/x-pack/plugins/alerting/server/routes/lib/actions_schema.ts index 3e561168aee48..9d6f89e070c3a 100644 --- a/x-pack/plugins/alerting/server/routes/lib/actions_schema.ts +++ b/x-pack/plugins/alerting/server/routes/lib/actions_schema.ts @@ -29,13 +29,20 @@ export const actionsSchema = schema.arrayOf( uuid: schema.maybe(schema.string()), alerts_filter: schema.maybe( schema.object({ - query: schema.nullable( + query: schema.maybe( schema.object({ kql: schema.string(), + filters: schema.arrayOf( + schema.object({ + query: schema.maybe(schema.recordOf(schema.string(), schema.any())), + meta: schema.recordOf(schema.string(), schema.any()), + state$: schema.maybe(schema.object({ store: schema.string() })), + }) + ), dsl: schema.maybe(schema.string()), }) ), - timeframe: schema.nullable( + timeframe: schema.maybe( schema.object({ days: schema.arrayOf( schema.oneOf([ diff --git a/x-pack/plugins/alerting/server/routes/lib/rewrite_actions.test.ts b/x-pack/plugins/alerting/server/routes/lib/rewrite_actions.test.ts index b79e5a91ff381..61dc9282bbfa1 100644 --- a/x-pack/plugins/alerting/server/routes/lib/rewrite_actions.test.ts +++ b/x-pack/plugins/alerting/server/routes/lib/rewrite_actions.test.ts @@ -27,6 +27,7 @@ describe('rewrite Actions', () => { query: { kql: 'test:1s', dsl: '{test:1}', + filters: [], }, timeframe: { days: [1, 2, 3], @@ -42,7 +43,7 @@ describe('rewrite Actions', () => { ).toEqual([ { alerts_filter: { - query: { dsl: '{test:1}', kql: 'test:1s' }, + query: { dsl: '{test:1}', kql: 'test:1s', filters: [] }, timeframe: { days: [1, 2, 3], hours: { end: '15:00', start: '00:00' }, @@ -77,6 +78,7 @@ describe('rewrite Actions', () => { query: { kql: 'test:1s', dsl: '{test:1}', + filters: [], }, timeframe: { days: [1, 2, 3], @@ -104,6 +106,7 @@ describe('rewrite Actions', () => { query: { kql: 'test:1s', dsl: '{test:1}', + filters: [], }, timeframe: { days: [1, 2, 3], diff --git a/x-pack/plugins/alerting/server/routes/lib/rewrite_rule.test.ts b/x-pack/plugins/alerting/server/routes/lib/rewrite_rule.test.ts index d5d0ba1739355..7a348e583ac6c 100644 --- a/x-pack/plugins/alerting/server/routes/lib/rewrite_rule.test.ts +++ b/x-pack/plugins/alerting/server/routes/lib/rewrite_rule.test.ts @@ -43,7 +43,7 @@ const sampleRule: SanitizedRule & { activeSnoozes?: string[] } = notifyWhen: 'onThrottleInterval', throttle: '1m', }, - alertsFilter: { timeframe: null, query: { kql: 'test:1', dsl: '{}' } }, + alertsFilter: { query: { kql: 'test:1', dsl: '{}', filters: [] } }, }, ], scheduledTaskId: 'xyz456', diff --git a/x-pack/plugins/alerting/server/routes/update_rule.test.ts b/x-pack/plugins/alerting/server/routes/update_rule.test.ts index 4dfe1c00145e8..5a4b3a19c0d7c 100644 --- a/x-pack/plugins/alerting/server/routes/update_rule.test.ts +++ b/x-pack/plugins/alerting/server/routes/update_rule.test.ts @@ -53,8 +53,8 @@ describe('updateRuleRoute', () => { query: { kql: 'name:test', dsl: '{"must": {"term": { "name": "test" }}}', + filters: [], }, - timeframe: null, }, }, ], @@ -123,9 +123,9 @@ describe('updateRuleRoute', () => { "alertsFilter": Object { "query": Object { "dsl": "{\\"must\\": {\\"term\\": { \\"name\\": \\"test\\" }}}", + "filters": Array [], "kql": "name:test", }, - "timeframe": null, }, "group": "default", "id": "2", diff --git a/x-pack/plugins/alerting/server/rules_client/lib/add_generated_action_values.test.ts b/x-pack/plugins/alerting/server/rules_client/lib/add_generated_action_values.test.ts index 7ee9d323c7627..6605d9c22a72b 100644 --- a/x-pack/plugins/alerting/server/rules_client/lib/add_generated_action_values.test.ts +++ b/x-pack/plugins/alerting/server/rules_client/lib/add_generated_action_values.test.ts @@ -24,7 +24,15 @@ describe('addGeneratedActionValues()', () => { throttle: null, }, alertsFilter: { - query: { kql: 'test:testValue' }, + query: { + kql: 'test:testValue', + filters: [ + { + meta: { key: 'foo', params: { query: 'bar' } }, + query: { match_phrase: { foo: 'bar ' } }, + }, + ], + }, timeframe: { days: [1, 2], hours: { start: '08:00', end: '17:00' }, @@ -41,14 +49,17 @@ describe('addGeneratedActionValues()', () => { test('adds DSL', async () => { const actionWithGeneratedValues = addGeneratedActionValues([mockAction]); expect(actionWithGeneratedValues[0].alertsFilter?.query?.dsl).toBe( - '{"bool":{"should":[{"match":{"test":"testValue"}}],"minimum_should_match":1}}' + '{"bool":{"must":[],"filter":[{"bool":{"should":[{"match":{"test":"testValue"}}],"minimum_should_match":1}},{"match_phrase":{"foo":"bar "}}],"should":[],"must_not":[]}}' ); }); test('throws error if KQL is not valid', async () => { expect(() => addGeneratedActionValues([ - { ...mockAction, alertsFilter: { query: { kql: 'foo:bar:1' }, timeframe: null } }, + { + ...mockAction, + alertsFilter: { query: { kql: 'foo:bar:1', filters: [] } }, + }, ]) ).toThrowErrorMatchingInlineSnapshot('"Error creating DSL query: invalid KQL"'); }); diff --git a/x-pack/plugins/alerting/server/rules_client/lib/add_generated_action_values.ts b/x-pack/plugins/alerting/server/rules_client/lib/add_generated_action_values.ts index c3cd721698594..71cbf01ea1a4e 100644 --- a/x-pack/plugins/alerting/server/rules_client/lib/add_generated_action_values.ts +++ b/x-pack/plugins/alerting/server/rules_client/lib/add_generated_action_values.ts @@ -6,7 +6,7 @@ */ import { v4 } from 'uuid'; -import { fromKueryExpression, toElasticsearchQuery } from '@kbn/es-query'; +import { buildEsQuery, Filter } from '@kbn/es-query'; import Boom from '@hapi/boom'; import { NormalizedAlertAction, NormalizedAlertActionWithGeneratedValues } from '..'; @@ -14,9 +14,11 @@ export function addGeneratedActionValues( actions: NormalizedAlertAction[] = [] ): NormalizedAlertActionWithGeneratedValues[] { return actions.map(({ uuid, alertsFilter, ...action }) => { - const generateDSL = (kql: string) => { + const generateDSL = (kql: string, filters: Filter[]) => { try { - return JSON.stringify(toElasticsearchQuery(fromKueryExpression(kql))); + return JSON.stringify( + buildEsQuery(undefined, [{ query: kql, language: 'kuery' }], filters) + ); } catch (e) { throw Boom.badRequest(`Error creating DSL query: invalid KQL`); } @@ -29,13 +31,12 @@ export function addGeneratedActionValues( ? { alertsFilter: { ...alertsFilter, - timeframe: alertsFilter.timeframe || null, - query: !alertsFilter.query - ? null - : { - kql: alertsFilter.query.kql, - dsl: generateDSL(alertsFilter.query.kql), - }, + query: alertsFilter.query + ? { + ...alertsFilter.query, + dsl: generateDSL(alertsFilter.query.kql, alertsFilter.query.filters), + } + : undefined, }, } : {}), diff --git a/x-pack/plugins/alerting/server/rules_client/lib/recover_rule_alerts.ts b/x-pack/plugins/alerting/server/rules_client/lib/recover_rule_alerts.ts index bf8d10a341597..9648f23dc05d2 100644 --- a/x-pack/plugins/alerting/server/rules_client/lib/recover_rule_alerts.ts +++ b/x-pack/plugins/alerting/server/rules_client/lib/recover_rule_alerts.ts @@ -43,6 +43,7 @@ export const recoverRuleAlerts = async ( const event = createAlertEventLogRecordObject({ ruleId: id, ruleName: attributes.name, + ruleRevision: attributes.revision, ruleType: context.ruleTypeRegistry.get(attributes.alertTypeId), consumer: attributes.consumer, instanceId: alertId, diff --git a/x-pack/plugins/alerting/server/rules_client/lib/siem_legacy_actions/format_legacy_actions.ts b/x-pack/plugins/alerting/server/rules_client/lib/siem_legacy_actions/format_legacy_actions.ts index 8689113ca7907..a0aa3286f1f6d 100644 --- a/x-pack/plugins/alerting/server/rules_client/lib/siem_legacy_actions/format_legacy_actions.ts +++ b/x-pack/plugins/alerting/server/rules_client/lib/siem_legacy_actions/format_legacy_actions.ts @@ -14,6 +14,7 @@ import { injectReferencesIntoActions } from '../../common'; import { transformToNotifyWhen } from './transform_to_notify_when'; import { transformFromLegacyActions } from './transform_legacy_actions'; import { LegacyIRuleActionsAttributes, legacyRuleActionsSavedObjectType } from './types'; +import { transformToAlertThrottle } from './transform_to_alert_throttle'; /** * @deprecated Once we are confident all rules relying on side-car actions SO's have been migrated to SO references we should remove this function @@ -136,7 +137,9 @@ export const formatLegacyActions = async ( return { ...rule, actions: [...rule.actions, ...legacyRuleActions], - throttle: (legacyRuleActions.length ? ruleThrottle : rule.throttle) ?? 'no_actions', + throttle: transformToAlertThrottle( + (legacyRuleActions.length ? ruleThrottle : rule.throttle) ?? 'no_actions' + ), notifyWhen: transformToNotifyWhen(ruleThrottle), // muteAll property is disregarded in further rule processing in Security Solution when legacy actions are present. // So it should be safe to set it as false, so it won't be displayed to user as w/o actions see transformFromAlertThrottle method diff --git a/x-pack/plugins/alerting/server/rules_client/lib/siem_legacy_actions/transform_legacy_actions.test.ts b/x-pack/plugins/alerting/server/rules_client/lib/siem_legacy_actions/transform_legacy_actions.test.ts index b23798294b300..cea32ce3e1913 100644 --- a/x-pack/plugins/alerting/server/rules_client/lib/siem_legacy_actions/transform_legacy_actions.test.ts +++ b/x-pack/plugins/alerting/server/rules_client/lib/siem_legacy_actions/transform_legacy_actions.test.ts @@ -67,7 +67,7 @@ describe('transformFromLegacyActions', () => { (transformToNotifyWhen as jest.Mock).mockReturnValueOnce(null); const actions = transformFromLegacyActions(legacyActionsAttr, references); - expect(actions[0].frequency?.notifyWhen).toBe('onThrottleInterval'); + expect(actions[0].frequency?.notifyWhen).toBe('onActiveAlert'); }); it('should return transformed legacy actions', () => { diff --git a/x-pack/plugins/alerting/server/rules_client/lib/siem_legacy_actions/transform_legacy_actions.ts b/x-pack/plugins/alerting/server/rules_client/lib/siem_legacy_actions/transform_legacy_actions.ts index 485a32f781695..1218e96a8dfd7 100644 --- a/x-pack/plugins/alerting/server/rules_client/lib/siem_legacy_actions/transform_legacy_actions.ts +++ b/x-pack/plugins/alerting/server/rules_client/lib/siem_legacy_actions/transform_legacy_actions.ts @@ -11,6 +11,7 @@ import type { SavedObjectReference } from '@kbn/core/server'; import { RawRuleAction } from '../../../types'; import { transformToNotifyWhen } from './transform_to_notify_when'; import { LegacyIRuleActionsAttributes } from './types'; +import { transformToAlertThrottle } from './transform_to_alert_throttle'; /** * @deprecated Once we are confident all rules relying on side-car actions SO's have been migrated to SO references we should remove this function @@ -50,8 +51,8 @@ export const transformFromLegacyActions = ( actionTypeId, frequency: { summary: true, - notifyWhen: transformToNotifyWhen(legacyActionsAttr.ruleThrottle) ?? 'onThrottleInterval', - throttle: legacyActionsAttr.ruleThrottle, + notifyWhen: transformToNotifyWhen(legacyActionsAttr.ruleThrottle) ?? 'onActiveAlert', + throttle: transformToAlertThrottle(legacyActionsAttr.ruleThrottle), }, }, ]; diff --git a/x-pack/plugins/alerting/server/rules_client/lib/siem_legacy_actions/transform_to_alert_throttle.test.ts b/x-pack/plugins/alerting/server/rules_client/lib/siem_legacy_actions/transform_to_alert_throttle.test.ts new file mode 100644 index 0000000000000..f52742009503a --- /dev/null +++ b/x-pack/plugins/alerting/server/rules_client/lib/siem_legacy_actions/transform_to_alert_throttle.test.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 { transformToAlertThrottle } from './transform_to_alert_throttle'; + +describe('transformToAlertThrottle', () => { + it('should return null when throttle is null OR no_actions', () => { + expect(transformToAlertThrottle(null)).toBeNull(); + expect(transformToAlertThrottle('rule')).toBeNull(); + expect(transformToAlertThrottle('no_actions')).toBeNull(); + }); + it('should return same value for other throttle values', () => { + expect(transformToAlertThrottle('1h')).toBe('1h'); + expect(transformToAlertThrottle('1m')).toBe('1m'); + expect(transformToAlertThrottle('1d')).toBe('1d'); + }); +}); diff --git a/x-pack/plugins/alerting/server/rules_client/lib/siem_legacy_actions/transform_to_alert_throttle.ts b/x-pack/plugins/alerting/server/rules_client/lib/siem_legacy_actions/transform_to_alert_throttle.ts new file mode 100644 index 0000000000000..7f6ecee900e2f --- /dev/null +++ b/x-pack/plugins/alerting/server/rules_client/lib/siem_legacy_actions/transform_to_alert_throttle.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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +/** + * Given a throttle from a "security_solution" rule this will transform it into an "alerting" "throttle" + * on their saved object. + * @params throttle The throttle from a "security_solution" rule + * @returns The "alerting" throttle + */ +export const transformToAlertThrottle = (throttle: string | null | undefined): string | null => { + if (throttle == null || throttle === 'rule' || throttle === 'no_actions') { + return null; + } else { + return throttle; + } +}; diff --git a/x-pack/plugins/alerting/server/rules_client/lib/validate_actions.test.ts b/x-pack/plugins/alerting/server/rules_client/lib/validate_actions.test.ts index 679f79d477c86..229c009df3eec 100644 --- a/x-pack/plugins/alerting/server/rules_client/lib/validate_actions.test.ts +++ b/x-pack/plugins/alerting/server/rules_client/lib/validate_actions.test.ts @@ -45,7 +45,7 @@ describe('validateActions', () => { throttle: null, }, alertsFilter: { - query: { kql: 'test:1' }, + query: { kql: 'test:1', filters: [] }, timeframe: { days: [1], hours: { start: '10:00', end: '17:00' }, timezone: 'UTC' }, }, }, @@ -200,7 +200,7 @@ describe('validateActions', () => { { ...data.actions[0], alertsFilter: { - query: { kql: 'test:1' }, + query: { kql: 'test:1', filters: [] }, timeframe: { days: [1], hours: { start: '30:00', end: '17:00' }, timezone: 'UTC' }, }, }, @@ -237,7 +237,7 @@ describe('validateActions', () => { { ...data.actions[0], alertsFilter: { - query: { kql: 'test:1' }, + query: { kql: 'test:1', filters: [] }, // @ts-ignore timeframe: { days: [1], hours: { start: '10:00', end: '17:00' } }, }, @@ -261,7 +261,7 @@ describe('validateActions', () => { { ...data.actions[0], alertsFilter: { - query: { kql: 'test:1' }, + query: { kql: 'test:1', filters: [] }, timeframe: { // @ts-ignore days: [0, 8], diff --git a/x-pack/plugins/alerting/server/rules_client/lib/validate_actions.ts b/x-pack/plugins/alerting/server/rules_client/lib/validate_actions.ts index 357f624a815cd..b86e30556f090 100644 --- a/x-pack/plugins/alerting/server/rules_client/lib/validate_actions.ts +++ b/x-pack/plugins/alerting/server/rules_client/lib/validate_actions.ts @@ -154,7 +154,6 @@ export async function validateActions( ) { actionWithInvalidTimeframe.push(action); } - // alertsFilter time range filter's start time can't be before end time if (alertsFilter.timeframe.hours) { if ( validateHours(alertsFilter.timeframe.hours.start) || diff --git a/x-pack/plugins/alerting/server/rules_client/methods/bulk_edit.ts b/x-pack/plugins/alerting/server/rules_client/methods/bulk_edit.ts index ce11a0cc017e3..640f3c3f324b3 100644 --- a/x-pack/plugins/alerting/server/rules_client/methods/bulk_edit.ts +++ b/x-pack/plugins/alerting/server/rules_client/methods/bulk_edit.ts @@ -7,7 +7,7 @@ import pMap from 'p-map'; import Boom from '@hapi/boom'; -import { cloneDeep, omit } from 'lodash'; +import { cloneDeep } from 'lodash'; import { AlertConsumers } from '@kbn/rule-data-utils'; import { KueryNode, nodeBuilder } from '@kbn/es-query'; import { @@ -638,19 +638,6 @@ async function getUpdatedAttributesFromOperations( isAttributesUpdateSkipped = false; } - // TODO https://github.com/elastic/kibana/issues/148414 - // If any action-level frequencies get pushed into a SIEM rule, strip their frequencies - const firstFrequency = updatedOperation.value.find( - (action) => action?.frequency - )?.frequency; - if (rule.attributes.consumer === AlertConsumers.SIEM && firstFrequency) { - ruleActions.actions = ruleActions.actions.map((action) => omit(action, 'frequency')); - if (!attributes.notifyWhen) { - attributes.notifyWhen = firstFrequency.notifyWhen; - attributes.throttle = firstFrequency.throttle; - } - } - break; } case 'snoozeSchedule': { diff --git a/x-pack/plugins/alerting/server/rules_client/methods/create.ts b/x-pack/plugins/alerting/server/rules_client/methods/create.ts index 90d6a4c49f90a..fd83a7b9b92e1 100644 --- a/x-pack/plugins/alerting/server/rules_client/methods/create.ts +++ b/x-pack/plugins/alerting/server/rules_client/methods/create.ts @@ -6,8 +6,6 @@ */ import Semver from 'semver'; import Boom from '@hapi/boom'; -import { omit } from 'lodash'; -import { AlertConsumers } from '@kbn/rule-data-utils'; import { SavedObjectsUtils } from '@kbn/core/server'; import { withSpan } from '@kbn/apm-utils'; import { parseDuration } from '../../../common/parse_duration'; @@ -111,17 +109,6 @@ export async function create( throw Boom.badRequest(`Error creating rule: could not create API key - ${error.message}`); } - // TODO https://github.com/elastic/kibana/issues/148414 - // If any action-level frequencies get pushed into a SIEM rule, strip their frequencies - const firstFrequency = data.actions.find((action) => action?.frequency)?.frequency; - if (data.consumer === AlertConsumers.SIEM && firstFrequency) { - data.actions = data.actions.map((action) => omit(action, 'frequency')); - if (!data.notifyWhen) { - data.notifyWhen = firstFrequency.notifyWhen; - data.throttle = firstFrequency.throttle; - } - } - await withSpan({ name: 'validateActions', type: 'rules' }, () => validateActions(context, ruleType, data, allowMissingConnectorSecrets) ); diff --git a/x-pack/plugins/alerting/server/rules_client/methods/update.ts b/x-pack/plugins/alerting/server/rules_client/methods/update.ts index 7c1fbedff37ce..f68b41067ddae 100644 --- a/x-pack/plugins/alerting/server/rules_client/methods/update.ts +++ b/x-pack/plugins/alerting/server/rules_client/methods/update.ts @@ -6,9 +6,8 @@ */ import Boom from '@hapi/boom'; -import { isEqual, omit } from 'lodash'; +import { isEqual } from 'lodash'; import { SavedObject } from '@kbn/core/server'; -import { AlertConsumers } from '@kbn/rule-data-utils'; import type { ShouldIncrementRevision } from './bulk_edit'; import { PartialRule, @@ -186,17 +185,6 @@ async function updateAlert( const ruleType = context.ruleTypeRegistry.get(attributes.alertTypeId); - // TODO https://github.com/elastic/kibana/issues/148414 - // If any action-level frequencies get pushed into a SIEM rule, strip their frequencies - const firstFrequency = data.actions.find((action) => action?.frequency)?.frequency; - if (attributes.consumer === AlertConsumers.SIEM && firstFrequency) { - data.actions = data.actions.map((action) => omit(action, 'frequency')); - if (!attributes.notifyWhen) { - attributes.notifyWhen = firstFrequency.notifyWhen; - attributes.throttle = firstFrequency.throttle; - } - } - // Validate const validatedAlertTypeParams = validateRuleTypeParams(data.params, ruleType.validate.params); await validateActions(context, ruleType, data, allowMissingConnectorSecrets); diff --git a/x-pack/plugins/alerting/server/rules_client/tests/bulk_edit.test.ts b/x-pack/plugins/alerting/server/rules_client/tests/bulk_edit.test.ts index 47b38cce094ba..3598ad31fd390 100644 --- a/x-pack/plugins/alerting/server/rules_client/tests/bulk_edit.test.ts +++ b/x-pack/plugins/alerting/server/rules_client/tests/bulk_edit.test.ts @@ -706,7 +706,8 @@ describe('bulkEdit()', () => { alertsFilter: { query: { kql: 'name:test', - dsl: '{"bool":{"should":[{"match":{"name":"test"}}],"minimum_should_match":1}}', + dsl: '{"bool":{"must":[],"filter":[{"bool":{"should":[{"match":{"name":"test"}}],"minimum_should_match":1}}],"should":[],"must_not":[]}}', + filters: [], }, timeframe: { days: [1], @@ -725,7 +726,7 @@ describe('bulkEdit()', () => { id: '2', params: {}, uuid: '222', - alertsFilter: { query: { kql: 'test:1', dsl: 'test' } }, + alertsFilter: { query: { kql: 'test:1', dsl: 'test', filters: [] } }, }; unsecuredSavedObjectsClient.bulkCreate.mockResolvedValue({ @@ -744,8 +745,7 @@ describe('bulkEdit()', () => { actionRef: 'action_1', uuid: '222', alertsFilter: { - query: { kql: 'test:1', dsl: 'test' }, - timeframe: null, + query: { kql: 'test:1', dsl: 'test', filters: [] }, }, }, ], @@ -802,10 +802,10 @@ describe('bulkEdit()', () => { uuid: '222', alertsFilter: { query: { - dsl: '{"bool":{"should":[{"match":{"test":"1"}}],"minimum_should_match":1}}', + dsl: '{"bool":{"must":[],"filter":[{"bool":{"should":[{"match":{"test":"1"}}],"minimum_should_match":1}}],"should":[],"must_not":[]}}', kql: 'test:1', + filters: [], }, - timeframe: null, }, }, ], @@ -835,8 +835,8 @@ describe('bulkEdit()', () => { query: { dsl: 'test', kql: 'test:1', + filters: [], }, - timeframe: null, }, }, ], diff --git a/x-pack/plugins/alerting/server/rules_client/tests/create.test.ts b/x-pack/plugins/alerting/server/rules_client/tests/create.test.ts index d44d69a6e64bf..aedf130e1f846 100644 --- a/x-pack/plugins/alerting/server/rules_client/tests/create.test.ts +++ b/x-pack/plugins/alerting/server/rules_client/tests/create.test.ts @@ -3343,7 +3343,7 @@ describe('create()', () => { throttle: null, }, alertsFilter: { - query: { kql: 'test:1' }, + query: { kql: 'test:1', filters: [] }, }, }, ], diff --git a/x-pack/plugins/alerting/server/rules_client/tests/disable.test.ts b/x-pack/plugins/alerting/server/rules_client/tests/disable.test.ts index c09b491dbbdcc..b135f9b0ee23d 100644 --- a/x-pack/plugins/alerting/server/rules_client/tests/disable.test.ts +++ b/x-pack/plugins/alerting/server/rules_client/tests/disable.test.ts @@ -100,6 +100,7 @@ describe('disable()', () => { schedule: { interval: '10s' }, alertTypeId: 'myType', enabled: true, + revision: 0, scheduledTaskId: '1', actions: [ { @@ -224,6 +225,7 @@ describe('disable()', () => { meta: { versionApiKeyLastmodified: 'v7.10.0', }, + revision: 0, scheduledTaskId: '1', apiKey: 'MTIzOmFiYw==', apiKeyOwner: 'elastic', @@ -277,6 +279,7 @@ describe('disable()', () => { }, params: { alertId: '1', + revision: 0, }, ownerId: null, }); @@ -296,6 +299,7 @@ describe('disable()', () => { meta: { versionApiKeyLastmodified: 'v7.10.0', }, + revision: 0, scheduledTaskId: '1', apiKey: 'MTIzOmFiYw==', apiKeyOwner: 'elastic', @@ -333,6 +337,7 @@ describe('disable()', () => { uuid: 'uuid-1', rule: { consumer: 'myApp', + revision: 0, rule_type_id: '123', }, }, @@ -379,6 +384,7 @@ describe('disable()', () => { meta: { versionApiKeyLastmodified: 'v7.10.0', }, + revision: 0, scheduledTaskId: '1', apiKey: 'MTIzOmFiYw==', apiKeyOwner: 'elastic', @@ -425,6 +431,7 @@ describe('disable()', () => { schedule: { interval: '10s' }, alertTypeId: 'myType', enabled: false, + revision: 0, scheduledTaskId: '1', updatedAt: '2019-02-12T21:01:22.479Z', updatedBy: 'elastic', @@ -517,6 +524,7 @@ describe('disable()', () => { schedule: { interval: '10s' }, alertTypeId: 'myType', enabled: false, + revision: 0, scheduledTaskId: null, updatedAt: '2019-02-12T21:01:22.479Z', updatedBy: 'elastic', @@ -565,6 +573,7 @@ describe('disable()', () => { schedule: { interval: '10s' }, alertTypeId: 'myType', enabled: false, + revision: 0, scheduledTaskId: null, updatedAt: '2019-02-12T21:01:22.479Z', updatedBy: 'elastic', diff --git a/x-pack/plugins/alerting/server/rules_client/tests/get_alert_summary.test.ts b/x-pack/plugins/alerting/server/rules_client/tests/get_alert_summary.test.ts index 212977a8381c2..dbfcc5f3fc017 100644 --- a/x-pack/plugins/alerting/server/rules_client/tests/get_alert_summary.test.ts +++ b/x-pack/plugins/alerting/server/rules_client/tests/get_alert_summary.test.ts @@ -189,6 +189,7 @@ describe('getAlertSummary()', () => { "lastRun": "2019-02-12T21:01:32.479Z", "muteAll": false, "name": "rule-name", + "revision": 0, "ruleTypeId": "123", "status": "Active", "statusEndDate": "2019-02-12T21:01:22.479Z", diff --git a/x-pack/plugins/alerting/server/rules_client/tests/get_execution_log.test.ts b/x-pack/plugins/alerting/server/rules_client/tests/get_execution_log.test.ts index cebe9fa963971..33fb19c40f7fd 100644 --- a/x-pack/plugins/alerting/server/rules_client/tests/get_execution_log.test.ts +++ b/x-pack/plugins/alerting/server/rules_client/tests/get_execution_log.test.ts @@ -133,7 +133,7 @@ const aggregateResults = { numRecoveredAlerts: { value: 0.0, }, - outcomeAndMessage: { + outcomeMessageAndMaintenanceWindow: { hits: { total: { value: 1, @@ -242,7 +242,7 @@ const aggregateResults = { numRecoveredAlerts: { value: 5.0, }, - outcomeAndMessage: { + outcomeMessageAndMaintenanceWindow: { hits: { total: { value: 1, @@ -261,6 +261,9 @@ const aggregateResults = { }, kibana: { version: '8.2.0', + alert: { + maintenance_window_ids: ['254699b0-dfb2-11ed-bb3d-c91b918d0260'], + }, alerting: { outcome: 'success', }, @@ -389,6 +392,7 @@ describe('getExecutionLogForRule()', () => { rule_id: 'a348a740-9e2c-11ec-bd64-774ed95c43ef', rule_name: 'rule-name', space_ids: [], + maintenance_window_ids: [], }, { id: '41b2755e-765a-4044-9745-b03875d5e79a', @@ -412,6 +416,7 @@ describe('getExecutionLogForRule()', () => { rule_id: 'a348a740-9e2c-11ec-bd64-774ed95c43ef', rule_name: 'rule-name', space_ids: [], + maintenance_window_ids: ['254699b0-dfb2-11ed-bb3d-c91b918d0260'], }, ], }); @@ -725,6 +730,7 @@ describe('getGlobalExecutionLogWithAuth()', () => { rule_id: 'a348a740-9e2c-11ec-bd64-774ed95c43ef', rule_name: 'rule-name', space_ids: [], + maintenance_window_ids: [], }, { id: '41b2755e-765a-4044-9745-b03875d5e79a', @@ -748,6 +754,7 @@ describe('getGlobalExecutionLogWithAuth()', () => { rule_id: 'a348a740-9e2c-11ec-bd64-774ed95c43ef', rule_name: 'rule-name', space_ids: [], + maintenance_window_ids: ['254699b0-dfb2-11ed-bb3d-c91b918d0260'], }, ], }); 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 3aba014226042..03e1ba5976a62 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 @@ -947,7 +947,7 @@ describe('Execution Handler', () => { message: 'New: {{alerts.new.count}} Ongoing: {{alerts.ongoing.count}} Recovered: {{alerts.recovered.count}}', }, - alertsFilter: { query: { kql: 'test:1', dsl: '{}' } }, + alertsFilter: { query: { kql: 'test:1', dsl: '{}', filters: [] } }, }, ], }, @@ -1332,7 +1332,7 @@ describe('Execution Handler', () => { 'New: {{alerts.new.count}} Ongoing: {{alerts.ongoing.count}} Recovered: {{alerts.recovered.count}}', }, alertsFilter: { - query: { kql: 'kibana.alert.rule.name:foo', dsl: '{}' }, + query: { kql: 'kibana.alert.rule.name:foo', dsl: '{}', filters: [] }, }, }, ], @@ -1351,7 +1351,7 @@ describe('Execution Handler', () => { spaceId: 'test1', excludedAlertInstanceIds: ['foo'], alertsFilter: { - query: { kql: 'kibana.alert.rule.name:foo', dsl: '{}' }, + query: { kql: 'kibana.alert.rule.name:foo', dsl: '{}', filters: [] }, }, }); expect(actionsClient.bulkEnqueueExecution).not.toHaveBeenCalled(); @@ -1392,7 +1392,7 @@ describe('Execution Handler', () => { }, params: {}, alertsFilter: { - query: { kql: 'kibana.alert.instance.id:1', dsl: '{}' }, + query: { kql: 'kibana.alert.instance.id:1', dsl: '{}', filters: [] }, }, }, ], @@ -1412,7 +1412,7 @@ describe('Execution Handler', () => { spaceId: 'test1', excludedAlertInstanceIds: ['foo'], alertsFilter: { - query: { kql: 'kibana.alert.instance.id:1', dsl: '{}' }, + query: { kql: 'kibana.alert.instance.id:1', dsl: '{}', filters: [] }, }, }); expect(actionsClient.bulkEnqueueExecution).toHaveBeenCalledWith([ diff --git a/x-pack/plugins/alerting/server/task_runner/fixtures.ts b/x-pack/plugins/alerting/server/task_runner/fixtures.ts index 0b5cd235185d0..9be9064908fcb 100644 --- a/x-pack/plugins/alerting/server/task_runner/fixtures.ts +++ b/x-pack/plugins/alerting/server/task_runner/fixtures.ts @@ -409,6 +409,7 @@ export const mockAAD = { execution: { uuid: 'c35db7cc-5bf7-46ea-b43f-b251613a5b72' }, name: 'test-rule', producer: 'infrastructure', + revision: 0, rule_type_id: 'metrics.alert.threshold', uuid: '0de91960-7643-11ed-b719-bb9db8582cb6', tags: [], 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 ea37e5d8594d6..216cf19373cfa 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 @@ -678,11 +678,14 @@ describe('Task Runner', () => { 'Updating rule task for test rule with id 1 - {"lastExecutionDate":"1970-01-01T00:00:00.000Z","status":"active"} - {"outcome":"succeeded","outcomeOrder":0,"outcomeMsg":null,"warning":null,"alertsCount":{"active":1,"new":1,"recovered":0,"ignored":0}}' ); + const maintenanceWindowIds = ['test-id-1', 'test-id-2']; + testAlertingEventLogCalls({ activeAlerts: 1, newAlerts: 1, status: 'active', logAlert: 2, + maintenanceWindowIds, }); expect(alertingEventLogger.logAlert).toHaveBeenNthCalledWith( 1, @@ -690,7 +693,7 @@ describe('Task Runner', () => { action: EVENT_LOG_ACTIONS.newInstance, group: 'default', state: { start: DATE_1970, duration: '0' }, - maintenanceWindowIds: ['test-id-1', 'test-id-2'], + maintenanceWindowIds, }) ); expect(alertingEventLogger.logAlert).toHaveBeenNthCalledWith( @@ -699,7 +702,7 @@ describe('Task Runner', () => { action: EVENT_LOG_ACTIONS.activeInstance, group: 'default', state: { start: DATE_1970, duration: '0' }, - maintenanceWindowIds: ['test-id-1', 'test-id-2'], + maintenanceWindowIds, }) ); @@ -3113,6 +3116,7 @@ describe('Task Runner', () => { errorMessage = 'GENERIC ERROR MESSAGE', executionStatus = 'succeeded', setRuleName = true, + maintenanceWindowIds, logAlert = 0, logAction = 0, hasReachedAlertLimit = false, @@ -3126,6 +3130,7 @@ describe('Task Runner', () => { generatedActions?: number; executionStatus?: 'succeeded' | 'failed' | 'not-reached'; setRuleName?: boolean; + maintenanceWindowIds?: string[]; logAlert?: number; logAction?: number; errorReason?: string; @@ -3140,6 +3145,11 @@ describe('Task Runner', () => { expect(alertingEventLogger.setRuleName).not.toHaveBeenCalled(); } expect(alertingEventLogger.getStartAndDuration).toHaveBeenCalled(); + if (maintenanceWindowIds?.length) { + expect(alertingEventLogger.setMaintenanceWindowIds).toHaveBeenCalledWith( + maintenanceWindowIds + ); + } if (status === 'error') { expect(alertingEventLogger.done).toHaveBeenCalledWith({ metrics: null, 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 41426724187e1..b4ee45eea902b 100644 --- a/x-pack/plugins/alerting/server/task_runner/task_runner.ts +++ b/x-pack/plugins/alerting/server/task_runner/task_runner.ts @@ -332,6 +332,9 @@ export class TaskRunner< const maintenanceWindowIds = activeMaintenanceWindows.map( (maintenanceWindow) => maintenanceWindow.id ); + if (maintenanceWindowIds.length) { + this.alertingEventLogger.setMaintenanceWindowIds(maintenanceWindowIds); + } const { updatedRuleTypeState } = await this.timer.runWithTimer( TaskRunnerTimerSpan.RuleTypeRun, diff --git a/x-pack/plugins/alerting/server/task_runner/transform_action_params.test.ts b/x-pack/plugins/alerting/server/task_runner/transform_action_params.test.ts index cd581afafa266..a8889bc620c83 100644 --- a/x-pack/plugins/alerting/server/task_runner/transform_action_params.test.ts +++ b/x-pack/plugins/alerting/server/task_runner/transform_action_params.test.ts @@ -182,10 +182,10 @@ describe('transformActionParams', () => { flapping: false, }); expect(result).toMatchInlineSnapshot(` - Object { - "message": "Value \\"1\\" exists", - } - `); + Object { + "message": "Value \\"1\\" exists", + } + `); }); test('alertName is passed to templates', () => { @@ -212,10 +212,10 @@ describe('transformActionParams', () => { flapping: false, }); expect(result).toMatchInlineSnapshot(` - Object { - "message": "Value \\"alert-name\\" exists", - } - `); + Object { + "message": "Value \\"alert-name\\" exists", + } + `); }); test('tags is passed to templates', () => { @@ -242,10 +242,10 @@ describe('transformActionParams', () => { flapping: false, }); expect(result).toMatchInlineSnapshot(` - Object { - "message": "Value \\"tag-A,tag-B\\" exists", - } - `); + Object { + "message": "Value \\"tag-A,tag-B\\" exists", + } + `); }); test('undefined tags is passed to templates', () => { @@ -271,10 +271,10 @@ describe('transformActionParams', () => { flapping: false, }); expect(result).toMatchInlineSnapshot(` - Object { - "message": "Value \\"\\" is undefined and renders as empty string", - } - `); + Object { + "message": "Value \\"\\" is undefined and renders as empty string", + } + `); }); test('empty tags is passed to templates', () => { @@ -301,10 +301,10 @@ describe('transformActionParams', () => { flapping: false, }); expect(result).toMatchInlineSnapshot(` - Object { - "message": "Value \\"\\" is an empty array and renders as empty string", - } - `); + Object { + "message": "Value \\"\\" is an empty array and renders as empty string", + } + `); }); test('spaceId is passed to templates', () => { @@ -331,10 +331,10 @@ describe('transformActionParams', () => { flapping: false, }); expect(result).toMatchInlineSnapshot(` - Object { - "message": "Value \\"spaceId-A\\" exists", - } - `); + Object { + "message": "Value \\"spaceId-A\\" exists", + } + `); }); test('alertInstanceId is passed to templates', () => { @@ -361,10 +361,10 @@ describe('transformActionParams', () => { flapping: false, }); expect(result).toMatchInlineSnapshot(` - Object { - "message": "Value \\"2\\" exists", - } - `); + Object { + "message": "Value \\"2\\" exists", + } + `); }); test('alertActionGroup is passed to templates', () => { @@ -391,10 +391,10 @@ describe('transformActionParams', () => { flapping: false, }); expect(result).toMatchInlineSnapshot(` - Object { - "message": "Value \\"action-group\\" exists", - } - `); + Object { + "message": "Value \\"action-group\\" exists", + } + `); }); test('alertActionGroupName is passed to templates', () => { @@ -421,10 +421,10 @@ describe('transformActionParams', () => { flapping: false, }); expect(result).toMatchInlineSnapshot(` - Object { - "message": "Value \\"Action Group\\" exists", - } - `); + Object { + "message": "Value \\"Action Group\\" exists", + } + `); }); test('rule variables are passed to templates', () => { @@ -451,10 +451,10 @@ describe('transformActionParams', () => { flapping: false, }); expect(result).toMatchInlineSnapshot(` - Object { - "message": "Value \\"1\\", \\"alert-name\\", \\"spaceId-A\\" and \\"tag-A,tag-B\\" exist", - } - `); + Object { + "message": "Value \\"1\\", \\"alert-name\\", \\"spaceId-A\\" and \\"tag-A,tag-B\\" exist", + } + `); }); test('rule alert variables are passed to templates', () => { @@ -482,10 +482,10 @@ describe('transformActionParams', () => { flapping: false, }); expect(result).toMatchInlineSnapshot(` - Object { - "message": "Value \\"2\\", \\"action-group\\", \\"uuid-1\\" and \\"Action Group\\" exist", - } - `); + Object { + "message": "Value \\"2\\", \\"action-group\\", \\"uuid-1\\" and \\"Action Group\\" exist", + } + `); }); test('date is passed to templates', () => { @@ -613,10 +613,10 @@ describe('transformActionParams', () => { flapping: true, }); expect(result).toMatchInlineSnapshot(` - Object { - "message": "Value \\"true\\" exists", - } - `); + Object { + "message": "Value \\"true\\" exists", + } + `); }); }); @@ -665,9 +665,9 @@ describe('transformSummaryActionParams', () => { const result = transformSummaryActionParams({ ...params, actionParams }); expect(result).toMatchInlineSnapshot(` - Object { - "message": "Value \\"{\\"@timestamp\\":\\"2022-12-07T15:38:43.472Z\\",\\"event\\":{\\"kind\\":\\"signal\\",\\"action\\":\\"active\\"},\\"kibana\\":{\\"version\\":\\"8.7.0\\",\\"space_ids\\":[\\"default\\"],\\"alert\\":{\\"instance\\":{\\"id\\":\\"*\\"},\\"uuid\\":\\"2d3e8fe5-3e8b-4361-916e-9eaab0bf2084\\",\\"status\\":\\"active\\",\\"workflow_status\\":\\"open\\",\\"reason\\":\\"system.cpu is 90% in the last 1 min for all hosts. Alert when > 50%.\\",\\"time_range\\":{\\"gte\\":\\"2022-01-01T12:00:00.000Z\\"},\\"start\\":\\"2022-12-07T15:23:13.488Z\\",\\"duration\\":{\\"us\\":100000},\\"flapping\\":false,\\"rule\\":{\\"category\\":\\"Metric threshold\\",\\"consumer\\":\\"alerts\\",\\"execution\\":{\\"uuid\\":\\"c35db7cc-5bf7-46ea-b43f-b251613a5b72\\"},\\"name\\":\\"test-rule\\",\\"producer\\":\\"infrastructure\\",\\"rule_type_id\\":\\"metrics.alert.threshold\\",\\"uuid\\":\\"0de91960-7643-11ed-b719-bb9db8582cb6\\",\\"tags\\":[]}}}}\\", \\"http://ruleurl\\" and \\"{\\"foo\\":\\"bar\\",\\"foo_bar\\":true,\\"name\\":\\"test-rule\\",\\"id\\":\\"1\\"}\\" exist", - } + Object { + "message": "Value \\"{\\"@timestamp\\":\\"2022-12-07T15:38:43.472Z\\",\\"event\\":{\\"kind\\":\\"signal\\",\\"action\\":\\"active\\"},\\"kibana\\":{\\"version\\":\\"8.7.0\\",\\"space_ids\\":[\\"default\\"],\\"alert\\":{\\"instance\\":{\\"id\\":\\"*\\"},\\"uuid\\":\\"2d3e8fe5-3e8b-4361-916e-9eaab0bf2084\\",\\"status\\":\\"active\\",\\"workflow_status\\":\\"open\\",\\"reason\\":\\"system.cpu is 90% in the last 1 min for all hosts. Alert when > 50%.\\",\\"time_range\\":{\\"gte\\":\\"2022-01-01T12:00:00.000Z\\"},\\"start\\":\\"2022-12-07T15:23:13.488Z\\",\\"duration\\":{\\"us\\":100000},\\"flapping\\":false,\\"rule\\":{\\"category\\":\\"Metric threshold\\",\\"consumer\\":\\"alerts\\",\\"execution\\":{\\"uuid\\":\\"c35db7cc-5bf7-46ea-b43f-b251613a5b72\\"},\\"name\\":\\"test-rule\\",\\"producer\\":\\"infrastructure\\",\\"revision\\":0,\\"rule_type_id\\":\\"metrics.alert.threshold\\",\\"uuid\\":\\"0de91960-7643-11ed-b719-bb9db8582cb6\\",\\"tags\\":[]}}}}\\", \\"http://ruleurl\\" and \\"{\\"foo\\":\\"bar\\",\\"foo_bar\\":true,\\"name\\":\\"test-rule\\",\\"id\\":\\"1\\"}\\" exist", + } `); }); diff --git a/x-pack/plugins/alerting/server/types.ts b/x-pack/plugins/alerting/server/types.ts index 21ba86dc0f25f..420025b1cd007 100644 --- a/x-pack/plugins/alerting/server/types.ts +++ b/x-pack/plugins/alerting/server/types.ts @@ -23,6 +23,7 @@ import { import type { PublicMethodsOf } from '@kbn/utility-types'; import { SharePluginStart } from '@kbn/share-plugin/server'; import { type FieldMap } from '@kbn/alerts-as-data-utils'; +import { Filter } from '@kbn/es-query'; import { RuleTypeRegistry as OrigruleTypeRegistry } from './rule_type_registry'; import { PluginSetupContract, PluginStartContract } from './plugin'; import { RulesClient } from './rules_client'; @@ -285,11 +286,12 @@ export type UntypedRuleType = RuleType< >; export interface RawAlertsFilter extends AlertsFilter { - query: null | { + query?: { kql: string; + filters: Filter[]; dsl: string; }; - timeframe: null | AlertsFilterTimeframe; + timeframe?: AlertsFilterTimeframe; } export interface RawRuleAction extends SavedObjectAttributes { diff --git a/x-pack/plugins/apm/common/dependencies.ts b/x-pack/plugins/apm/common/dependencies.ts index 6228d4b9d279d..812bb33342ba5 100644 --- a/x-pack/plugins/apm/common/dependencies.ts +++ b/x-pack/plugins/apm/common/dependencies.ts @@ -13,14 +13,14 @@ import { } from './es_fields/apm'; import { environmentQuery } from './utils/environment_query'; -export const kueryBarPlaceholder = i18n.translate( - 'xpack.apm.dependencies.kueryBarPlaceholder', +export const unifiedSearchBarPlaceholder = i18n.translate( + 'xpack.apm.dependencies.unifiedSearchBarPlaceholder', { defaultMessage: `Search dependency metrics (e.g. span.destination.service.resource:elasticsearch)`, } ); -export const getKueryBarBoolFilter = ({ +export const getSearchBarBoolFilter = ({ dependencyName, environment, }: { diff --git a/x-pack/plugins/apm/common/rules/schema.ts b/x-pack/plugins/apm/common/rules/schema.ts index 7e48ad989b606..698b4507c5b3f 100644 --- a/x-pack/plugins/apm/common/rules/schema.ts +++ b/x-pack/plugins/apm/common/rules/schema.ts @@ -15,6 +15,7 @@ export const errorCountParamsSchema = schema.object({ threshold: schema.number(), serviceName: schema.maybe(schema.string()), environment: schema.string(), + errorGroupingKey: schema.maybe(schema.string()), }); export const transactionDurationParamsSchema = schema.object({ diff --git a/x-pack/plugins/apm/ftr_e2e/cypress/e2e/power_user/storage_explorer/storage_explorer.cy.ts b/x-pack/plugins/apm/ftr_e2e/cypress/e2e/power_user/storage_explorer/storage_explorer.cy.ts index 3fdeff2e406a8..88d4408d33152 100644 --- a/x-pack/plugins/apm/ftr_e2e/cypress/e2e/power_user/storage_explorer/storage_explorer.cy.ts +++ b/x-pack/plugins/apm/ftr_e2e/cypress/e2e/power_user/storage_explorer/storage_explorer.cy.ts @@ -140,12 +140,6 @@ describe('Storage Explorer', () => { }); }); - it('when clicking the refresh button', () => { - cy.wait(mainAliasNames); - cy.contains('Refresh').click(); - cy.wait(mainAliasNames); - }); - it('when selecting a different time range and clicking the update button', () => { cy.wait(mainAliasNames); @@ -155,9 +149,6 @@ describe('Storage Explorer', () => { ); cy.contains('Update').click(); cy.wait(mainAliasNames); - - cy.contains('Refresh').click(); - cy.wait(mainAliasNames); }); it('with the correct lifecycle phase when changing the lifecycle phase', () => { diff --git a/x-pack/plugins/apm/ftr_e2e/cypress/e2e/read_only_user/deep_links.cy.ts b/x-pack/plugins/apm/ftr_e2e/cypress/e2e/read_only_user/deep_links.cy.ts index 2ae901be32fbf..5451a0df2d845 100644 --- a/x-pack/plugins/apm/ftr_e2e/cypress/e2e/read_only_user/deep_links.cy.ts +++ b/x-pack/plugins/apm/ftr_e2e/cypress/e2e/read_only_user/deep_links.cy.ts @@ -17,6 +17,8 @@ describe('APM deep links', () => { cy.contains('APM / Service groups'); cy.contains('APM / Traces'); cy.contains('APM / Service Map'); + cy.contains('APM / Dependencies'); + cy.contains('APM / Settings'); // navigates to home page // Force click because welcome screen changes @@ -43,5 +45,15 @@ describe('APM deep links', () => { // navigates to service maps cy.contains('APM / Service Map').click({ force: true }); cy.url().should('include', '/apm/service-map'); + + cy.getByTestSubj('nav-search-input').type('APM'); + // navigates to dependencies page + cy.contains('APM / Dependencies').click({ force: true }); + cy.url().should('include', '/apm/dependencies/inventory'); + + cy.getByTestSubj('nav-search-input').type('APM'); + // navigates to settings page + cy.contains('APM / Settings').click({ force: true }); + cy.url().should('include', '/apm/settings/general-settings'); }); }); diff --git a/x-pack/plugins/apm/ftr_e2e/cypress/e2e/read_only_user/errors/error_details.cy.ts b/x-pack/plugins/apm/ftr_e2e/cypress/e2e/read_only_user/errors/error_details.cy.ts index 8714fe8f07873..c09547abcf7c8 100644 --- a/x-pack/plugins/apm/ftr_e2e/cypress/e2e/read_only_user/errors/error_details.cy.ts +++ b/x-pack/plugins/apm/ftr_e2e/cypress/e2e/read_only_user/errors/error_details.cy.ts @@ -5,6 +5,7 @@ * 2.0. */ +import { getErrorGroupingKey } from '@kbn/apm-synthtrace-client/src/lib/apm/instance'; import url from 'url'; import { synthtrace } from '../../../../synthtrace'; import { checkA11y } from '../../../support/commands'; @@ -12,14 +13,6 @@ import { generateData } from './generate_data'; const start = '2021-10-10T00:00:00.000Z'; const end = '2021-10-10T00:15:00.000Z'; -const errorDetailsPageHref = url.format({ - pathname: - '/app/apm/services/opbeans-java/errors/0000000000000000000000000Error%200', - query: { - rangeFrom: start, - rangeTo: end, - }, -}); describe('Error details', () => { beforeEach(() => { @@ -41,18 +34,29 @@ describe('Error details', () => { }); it('has no detectable a11y violations on load', () => { + const errorGroupingKey = getErrorGroupingKey('Error 1'); + const errorGroupingKeyShort = errorGroupingKey.slice(0, 5); + const errorDetailsPageHref = url.format({ + pathname: `/app/apm/services/opbeans-java/errors/${errorGroupingKey}`, + query: { + rangeFrom: start, + rangeTo: end, + }, + }); + cy.visitKibana(errorDetailsPageHref); - cy.contains('Error group 00000'); + cy.contains(`Error group ${errorGroupingKeyShort}`); // set skipFailures to true to not fail the test when there are accessibility failures checkA11y({ skipFailures: true }); }); describe('when error has no occurrences', () => { it('shows zero occurrences', () => { + const errorGroupingKey = getErrorGroupingKey('Error foo bar'); + cy.visitKibana( url.format({ - pathname: - '/app/apm/services/opbeans-java/errors/0000000000000000000000000Error%201', + pathname: `/app/apm/services/opbeans-java/errors/${errorGroupingKey}`, query: { rangeFrom: start, rangeTo: end, @@ -65,9 +69,19 @@ describe('Error details', () => { }); describe('when error has data', () => { + const errorGroupingKey = getErrorGroupingKey('Error 1'); + const errorGroupingKeyShort = errorGroupingKey.slice(0, 5); + const errorDetailsPageHref = url.format({ + pathname: `/app/apm/services/opbeans-java/errors/${errorGroupingKey}`, + query: { + rangeFrom: start, + rangeTo: end, + }, + }); + it('shows errors distribution chart', () => { cy.visitKibana(errorDetailsPageHref); - cy.contains('Error group 00000'); + cy.contains(`Error group ${errorGroupingKeyShort}`); cy.getByTestSubj('errorDistribution').contains('Error occurrences'); }); @@ -89,7 +103,6 @@ describe('Error details', () => { describe('when clicking on related transaction sample', () => { it('should redirects to the transaction details page', () => { cy.visitKibana(errorDetailsPageHref); - cy.contains('Error group 00000'); cy.contains('a', 'GET /apple 🍎').click(); cy.url().should('include', 'opbeans-java/transactions/view'); }); diff --git a/x-pack/plugins/apm/ftr_e2e/cypress/e2e/read_only_user/errors/errors_page.cy.ts b/x-pack/plugins/apm/ftr_e2e/cypress/e2e/read_only_user/errors/errors_page.cy.ts index 8ac95d509d0bd..a64dc157d4037 100644 --- a/x-pack/plugins/apm/ftr_e2e/cypress/e2e/read_only_user/errors/errors_page.cy.ts +++ b/x-pack/plugins/apm/ftr_e2e/cypress/e2e/read_only_user/errors/errors_page.cy.ts @@ -30,6 +30,8 @@ describe('Errors page', () => { describe('when data is loaded', () => { before(() => { + synthtrace.clean(); + synthtrace.index( generateData({ from: new Date(start).getTime(), @@ -79,16 +81,16 @@ describe('Errors page', () => { cy.contains('div', 'Error 1'); }); - it('clicking on type adds a filter in the kuerybar', () => { + it('clicking on type adds a filter in the searchbar', () => { cy.visitKibana(javaServiceErrorsPageHref); - cy.getByTestSubj('headerFilterKuerybar') + cy.getByTestSubj('apmUnifiedSearchBar') .invoke('val') .should('be.empty'); // `force: true` because Cypress says the element is 0x0 cy.contains('exception 0').click({ force: true, }); - cy.getByTestSubj('headerFilterKuerybar') + cy.getByTestSubj('apmUnifiedSearchBar') .its('length') .should('be.gt', 0); cy.get('table') @@ -142,38 +144,28 @@ describe('Check detailed statistics API with multiple errors', () => { cy.visitKibana(`${javaServiceErrorsPageHref}&pageSize=10`); cy.wait('@errorsMainStatistics'); cy.get('.euiPagination__list').children().should('have.length', 5); + + let requestedGroupIdsPage1: string[]; + cy.wait('@errorsDetailedStatistics').then((payload) => { - expect(payload.request.body.groupIds).eql( - JSON.stringify([ - '0000000000000000000000000Error 0', - '0000000000000000000000000Error 1', - '0000000000000000000000000Error 2', - '0000000000000000000000000Error 3', - '0000000000000000000000000Error 4', - '0000000000000000000000000Error 5', - '0000000000000000000000000Error 6', - '0000000000000000000000000Error 7', - '0000000000000000000000000Error 8', - '0000000000000000000000000Error 9', - ]) - ); + cy.get('[data-test-subj="errorGroupId"]').each(($el, index) => { + const displayedGroupId = $el.text(); + requestedGroupIdsPage1 = JSON.parse(payload.request.body.groupIds); + + const requestedGroupId = requestedGroupIdsPage1[index].slice(0, 5); + expect(displayedGroupId).eq(requestedGroupId); + + expect(requestedGroupIdsPage1).to.have.length(10); + }); }); cy.getByTestSubj('pagination-button-1').click(); + + // expect that the requested groupIds on page 2 are different from page 1 cy.wait('@errorsDetailedStatistics').then((payload) => { - expect(payload.request.body.groupIds).eql( - JSON.stringify([ - '000000000000000000000000Error 10', - '000000000000000000000000Error 11', - '000000000000000000000000Error 12', - '000000000000000000000000Error 13', - '000000000000000000000000Error 14', - '000000000000000000000000Error 15', - '000000000000000000000000Error 16', - '000000000000000000000000Error 17', - '000000000000000000000000Error 18', - '000000000000000000000000Error 19', - ]) - ); + const requestedGroupIdsPage2 = JSON.parse(payload.request.body.groupIds); + + expect(requestedGroupIdsPage1[0]).not.eq(requestedGroupIdsPage2[0]); + expect(requestedGroupIdsPage2).to.have.length(10); }); }); }); diff --git a/x-pack/plugins/apm/ftr_e2e/cypress/e2e/read_only_user/service_inventory/header_filters/header_filters.cy.ts b/x-pack/plugins/apm/ftr_e2e/cypress/e2e/read_only_user/service_inventory/header_filters/header_filters.cy.ts index e689e126d4bfd..8e30f2784eb34 100644 --- a/x-pack/plugins/apm/ftr_e2e/cypress/e2e/read_only_user/service_inventory/header_filters/header_filters.cy.ts +++ b/x-pack/plugins/apm/ftr_e2e/cypress/e2e/read_only_user/service_inventory/header_filters/header_filters.cy.ts @@ -45,7 +45,7 @@ describe('Service inventory - header filters', () => { cy.contains('Services'); cy.contains('opbeans-node'); cy.contains('service 1'); - cy.getByTestSubj('headerFilterKuerybar') + cy.getByTestSubj('apmUnifiedSearchBar') .type(`service.name: "${specialServiceName}"`) .type('{enter}'); cy.contains('service 1'); diff --git a/x-pack/plugins/apm/ftr_e2e/cypress/e2e/read_only_user/service_inventory/service_inventory.cy.ts b/x-pack/plugins/apm/ftr_e2e/cypress/e2e/read_only_user/service_inventory/service_inventory.cy.ts index 2d40c690a8c92..032409ec56d40 100644 --- a/x-pack/plugins/apm/ftr_e2e/cypress/e2e/read_only_user/service_inventory/service_inventory.cy.ts +++ b/x-pack/plugins/apm/ftr_e2e/cypress/e2e/read_only_user/service_inventory/service_inventory.cy.ts @@ -103,12 +103,6 @@ describe('Service inventory', () => { }); }); - it('when clicking the refresh button', () => { - cy.wait(mainAliasNames); - cy.contains('Refresh').click(); - cy.wait(mainAliasNames); - }); - it('when selecting a different time range and clicking the update button', () => { cy.wait(mainAliasNames); @@ -118,9 +112,6 @@ describe('Service inventory', () => { ); cy.contains('Update').click(); cy.wait(mainAliasNames); - - cy.contains('Refresh').click(); - cy.wait(mainAliasNames); }); }); diff --git a/x-pack/plugins/apm/ftr_e2e/cypress/e2e/read_only_user/service_overview/errors_table.cy.ts b/x-pack/plugins/apm/ftr_e2e/cypress/e2e/read_only_user/service_overview/errors_table.cy.ts index d693148010c7e..2970aa3887e95 100644 --- a/x-pack/plugins/apm/ftr_e2e/cypress/e2e/read_only_user/service_overview/errors_table.cy.ts +++ b/x-pack/plugins/apm/ftr_e2e/cypress/e2e/read_only_user/service_overview/errors_table.cy.ts @@ -50,12 +50,12 @@ describe('Errors table', () => { it('clicking on type adds a filter in the kuerybar and navigates to errors page', () => { cy.visitKibana(serviceOverviewHref); - cy.getByTestSubj('headerFilterKuerybar').invoke('val').should('be.empty'); + cy.getByTestSubj('apmUnifiedSearchBar').invoke('val').should('be.empty'); // `force: true` because Cypress says the element is 0x0 cy.contains('Exception').click({ force: true, }); - cy.getByTestSubj('headerFilterKuerybar').its('length').should('be.gt', 0); + cy.getByTestSubj('apmUnifiedSearchBar').its('length').should('be.gt', 0); cy.get('table').find('td:contains("Exception")').should('have.length', 1); }); diff --git a/x-pack/plugins/apm/ftr_e2e/cypress/e2e/read_only_user/service_overview/header_filters.cy.ts b/x-pack/plugins/apm/ftr_e2e/cypress/e2e/read_only_user/service_overview/header_filters.cy.ts index 8a25024506696..05fe508092ff4 100644 --- a/x-pack/plugins/apm/ftr_e2e/cypress/e2e/read_only_user/service_overview/header_filters.cy.ts +++ b/x-pack/plugins/apm/ftr_e2e/cypress/e2e/read_only_user/service_overview/header_filters.cy.ts @@ -117,7 +117,7 @@ describe('Service overview - header filters', () => { }); }); - describe('Filtering by kuerybar', () => { + describe('Filtering by searchbar', () => { beforeEach(() => { cy.loginAsViewerUser(); }); @@ -129,13 +129,23 @@ describe('Service overview - header filters', () => { }) ); cy.contains('opbeans-java'); - cy.getByTestSubj('headerFilterKuerybar').type('transaction.n'); + cy.getByTestSubj('apmUnifiedSearchBar').type('transaction.n'); cy.contains('transaction.name'); - cy.getByTestSubj('suggestionContainer').find('li').first().click(); - cy.getByTestSubj('headerFilterKuerybar').type(':'); - cy.getByTestSubj('suggestionContainer').find('li').first().click(); - cy.getByTestSubj('headerFilterKuerybar').type('{enter}'); - cy.url().should('include', '&kuery=transaction.name'); + cy.getByTestSubj( + 'autocompleteSuggestion-field-transaction.name-' + ).click(); + cy.getByTestSubj('apmUnifiedSearchBar').type(':'); + cy.getByTestSubj('autoCompleteSuggestionText').should('have.length', 1); + cy.getByTestSubj( + Cypress.$.escapeSelector( + 'autocompleteSuggestion-value-"GET-/api/product"-' + ) + ).click(); + cy.getByTestSubj('apmUnifiedSearchBar').type('{enter}'); + cy.url().should( + 'include', + '&kuery=transaction.name%20:%22GET%20%2Fapi%2Fproduct%22%20' + ); }); }); }); diff --git a/x-pack/plugins/apm/ftr_e2e/cypress/e2e/read_only_user/service_overview/time_comparison.cy.ts b/x-pack/plugins/apm/ftr_e2e/cypress/e2e/read_only_user/service_overview/time_comparison.cy.ts index bce3da42d5a3f..ec511a0725aac 100644 --- a/x-pack/plugins/apm/ftr_e2e/cypress/e2e/read_only_user/service_overview/time_comparison.cy.ts +++ b/x-pack/plugins/apm/ftr_e2e/cypress/e2e/read_only_user/service_overview/time_comparison.cy.ts @@ -121,7 +121,7 @@ describe('Service overview: Time Comparison', () => { '2021-10-20T00:00:00.000Z' ); - cy.getByTestSubj('superDatePickerApplyTimeButton').click(); + cy.getByTestSubj('querySubmitButton').click(); cy.getByTestSubj('comparisonSelect').should('have.value', '864000000ms'); cy.getByTestSubj('comparisonSelect').should( diff --git a/x-pack/plugins/apm/ftr_e2e/cypress/e2e/read_only_user/transaction_details/span_links.cy.ts b/x-pack/plugins/apm/ftr_e2e/cypress/e2e/read_only_user/transaction_details/span_links.cy.ts index 60b36b10ee4a3..0ec3c16e8658b 100644 --- a/x-pack/plugins/apm/ftr_e2e/cypress/e2e/read_only_user/transaction_details/span_links.cy.ts +++ b/x-pack/plugins/apm/ftr_e2e/cypress/e2e/read_only_user/transaction_details/span_links.cy.ts @@ -52,7 +52,7 @@ describe('Span links', () => { cy.contains('2 Span links'); cy.getByTestSubj( `spanLinksBadge_${ids.producerInternalOnlyIds.spanAId}` - ).realHover(); + ).trigger('mouseover'); cy.contains('2 Span links found'); cy.contains('2 incoming'); cy.contains('0 outgoing'); @@ -66,7 +66,7 @@ describe('Span links', () => { cy.contains('2 Span links'); cy.getByTestSubj( `spanLinksBadge_${ids.producerExternalOnlyIds.spanBId}` - ).realHover(); + ).trigger('mouseover'); cy.contains('2 Span links found'); cy.contains('1 incoming'); cy.contains('1 outgoing'); @@ -80,7 +80,7 @@ describe('Span links', () => { cy.contains('2 Span links'); cy.getByTestSubj( `spanLinksBadge_${ids.producerConsumerIds.transactionCId}` - ).realHover(); + ).trigger('mouseover'); cy.contains('2 Span links found'); cy.contains('1 incoming'); cy.contains('1 outgoing'); @@ -94,7 +94,7 @@ describe('Span links', () => { cy.contains('1 Span link'); cy.getByTestSubj( `spanLinksBadge_${ids.producerConsumerIds.spanCId}` - ).realHover(); + ).trigger('mouseover'); cy.contains('1 Span link found'); cy.contains('1 incoming'); cy.contains('0 outgoing'); @@ -108,7 +108,7 @@ describe('Span links', () => { cy.contains('2 Span links'); cy.getByTestSubj( `spanLinksBadge_${ids.producerMultipleIds.transactionDId}` - ).realHover(); + ).trigger('mouseover'); cy.contains('2 Span links found'); cy.contains('0 incoming'); cy.contains('2 outgoing'); @@ -122,7 +122,7 @@ describe('Span links', () => { cy.contains('2 Span links'); cy.getByTestSubj( `spanLinksBadge_${ids.producerMultipleIds.spanEId}` - ).realHover(); + ).trigger('mouseover'); cy.contains('2 Span links found'); cy.contains('0 incoming'); cy.contains('2 outgoing'); diff --git a/x-pack/plugins/apm/public/components/alerting/rule_types/error_count_rule_type/index.tsx b/x-pack/plugins/apm/public/components/alerting/rule_types/error_count_rule_type/index.tsx index 09ca441cd26e9..f23dc6f4fb362 100644 --- a/x-pack/plugins/apm/public/components/alerting/rule_types/error_count_rule_type/index.tsx +++ b/x-pack/plugins/apm/public/components/alerting/rule_types/error_count_rule_type/index.tsx @@ -21,6 +21,7 @@ import { createCallApmApi } from '../../../../services/rest/create_call_apm_api' import { ChartPreview } from '../../ui_components/chart_preview'; import { EnvironmentField, + ErrorGroupingKeyField, IsAboveField, ServiceField, } from '../../utils/fields'; @@ -33,6 +34,7 @@ export interface RuleParams { threshold?: number; serviceName?: string; environment?: string; + errorGroupingKey?: string; } interface Props { @@ -74,6 +76,7 @@ export function ErrorCountRuleType(props: Props) { query: { environment: params.environment, serviceName: params.serviceName, + errorGroupingKey: params.errorGroupingKey, interval, start, end, @@ -88,6 +91,7 @@ export function ErrorCountRuleType(props: Props) { params.windowUnit, params.environment, params.serviceName, + params.errorGroupingKey, ] ); @@ -98,6 +102,7 @@ export function ErrorCountRuleType(props: Props) { if (value !== params.serviceName) { setRuleParams('serviceName', value); setRuleParams('environment', ENVIRONMENT_ALL.value); + setRuleParams('errorGroupingKey', undefined); } }} />, @@ -106,6 +111,12 @@ export function ErrorCountRuleType(props: Props) { onChange={(value) => setRuleParams('environment', value)} serviceName={params.serviceName} />, + setRuleParams('errorGroupingKey', value)} + serviceName={params.serviceName} + />, + void; + serviceName?: string; +}) { + const label = i18n.translate('xpack.apm.alerting.fields.error.group.id', { + defaultMessage: 'Error grouping key', + }); + return ( + + + + ); +} + export function IsAboveField({ value, unit, diff --git a/x-pack/plugins/apm/public/components/app/dependencies_inventory/index.tsx b/x-pack/plugins/apm/public/components/app/dependencies_inventory/index.tsx index b4ae13619abb0..ff569f369ba4e 100644 --- a/x-pack/plugins/apm/public/components/app/dependencies_inventory/index.tsx +++ b/x-pack/plugins/apm/public/components/app/dependencies_inventory/index.tsx @@ -8,27 +8,26 @@ import { EuiSpacer } from '@elastic/eui'; import React from 'react'; import { - getKueryBarBoolFilter, - kueryBarPlaceholder, + unifiedSearchBarPlaceholder, + getSearchBarBoolFilter, } from '../../../../common/dependencies'; -import { useApmParams } from '../../../hooks/use_apm_params'; import { SearchBar } from '../../shared/search_bar/search_bar'; import { DependenciesInventoryTable } from './dependencies_inventory_table'; +import { useApmParams } from '../../../hooks/use_apm_params'; export function DependenciesInventory() { const { query: { environment }, } = useApmParams('/dependencies/inventory'); - const kueryBarBoolFilter = getKueryBarBoolFilter({ + const searchBarBoolFilter = getSearchBarBoolFilter({ environment, }); - return ( <> diff --git a/x-pack/plugins/apm/public/components/app/error_group_overview/error_group_list/index.tsx b/x-pack/plugins/apm/public/components/app/error_group_overview/error_group_list/index.tsx index 360d753b5abec..2ea165fb63702 100644 --- a/x-pack/plugins/apm/public/components/app/error_group_overview/error_group_list/index.tsx +++ b/x-pack/plugins/apm/public/components/app/error_group_overview/error_group_list/index.tsx @@ -110,7 +110,11 @@ function ErrorGroupList({ width: `${unit * 6}px`, render: (_, { groupId }) => { return ( - + {groupId.slice(0, 5) || NOT_AVAILABLE_LABEL} ); diff --git a/x-pack/plugins/apm/public/components/app/mobile/search_bar.tsx b/x-pack/plugins/apm/public/components/app/mobile/search_bar.tsx index 3039b69d094a9..7d41b8cf3ee01 100644 --- a/x-pack/plugins/apm/public/components/app/mobile/search_bar.tsx +++ b/x-pack/plugins/apm/public/components/app/mobile/search_bar.tsx @@ -5,7 +5,6 @@ * 2.0. */ -import { QueryDslQueryContainer } from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; import { EuiFlexGroup, EuiFlexGroupProps, @@ -14,30 +13,27 @@ import { } from '@elastic/eui'; import React from 'react'; import { useBreakpoints } from '../../../hooks/use_breakpoints'; -import { ApmDatePicker } from '../../shared/date_picker/apm_date_picker'; -import { KueryBar } from '../../shared/kuery_bar'; import { TimeComparison } from '../../shared/time_comparison'; import { TransactionTypeSelect } from '../../shared/transaction_type_select'; import { MobileFilters } from './service_overview/filters'; +import { UnifiedSearchBar } from '../../shared/unified_search_bar'; interface Props { hidden?: boolean; - showKueryBar?: boolean; + showUnifiedSearchBar?: boolean; showTimeComparison?: boolean; showTransactionTypeSelector?: boolean; showMobileFilters?: boolean; - kueryBarPlaceholder?: string; - kueryBarBoolFilter?: QueryDslQueryContainer[]; + searchBarPlaceholder?: string; } export function MobileSearchBar({ hidden = false, - showKueryBar = true, + showUnifiedSearchBar = true, showTimeComparison = false, showTransactionTypeSelector = false, showMobileFilters = false, - kueryBarBoolFilter, - kueryBarPlaceholder, + searchBarPlaceholder, }: Props) { const { isSmall, isMedium, isLarge, isXl, isXXXL } = useBreakpoints(); @@ -66,17 +62,11 @@ export function MobileSearchBar({ )} - {showKueryBar && ( + {showUnifiedSearchBar && ( - + )} - - -
diff --git a/x-pack/plugins/apm/public/components/app/service_map/index.tsx b/x-pack/plugins/apm/public/components/app/service_map/index.tsx index 2b3d8c8eaf15c..f9b848b816d4c 100644 --- a/x-pack/plugins/apm/public/components/app/service_map/index.tsx +++ b/x-pack/plugins/apm/public/components/app/service_map/index.tsx @@ -40,7 +40,7 @@ import { DisabledPrompt } from './disabled_prompt'; function PromptContainer({ children }: { children: ReactNode }) { return ( <> - + - + diff --git a/x-pack/plugins/apm/public/components/routing/mobile_service_detail/index.tsx b/x-pack/plugins/apm/public/components/routing/mobile_service_detail/index.tsx index 0d7a6ade36140..52e127c63f805 100644 --- a/x-pack/plugins/apm/public/components/routing/mobile_service_detail/index.tsx +++ b/x-pack/plugins/apm/public/components/routing/mobile_service_detail/index.tsx @@ -33,7 +33,7 @@ export function page({ tabKey: React.ComponentProps['selectedTabKey']; element: React.ReactElement; searchBarOptions?: { - showKueryBar?: boolean; + showUnifiedSearchBar?: boolean; showTransactionTypeSelector?: boolean; showTimeComparison?: boolean; showMobileFilters?: boolean; diff --git a/x-pack/plugins/apm/public/components/routing/service_detail/index.tsx b/x-pack/plugins/apm/public/components/routing/service_detail/index.tsx index 6158cf85d0a84..ed2b3f477ced0 100644 --- a/x-pack/plugins/apm/public/components/routing/service_detail/index.tsx +++ b/x-pack/plugins/apm/public/components/routing/service_detail/index.tsx @@ -48,7 +48,7 @@ function page({ tab: React.ComponentProps['selectedTab']; element: React.ReactElement; searchBarOptions?: { - showKueryBar?: boolean; + showUnifiedSearchBar?: boolean; showTransactionTypeSelector?: boolean; showTimeComparison?: boolean; hidden?: boolean; @@ -320,7 +320,7 @@ export const serviceDetail = { }), element: , searchBarOptions: { - showKueryBar: false, + showUnifiedSearchBar: false, }, }), '/services/{serviceName}/infrastructure': { @@ -331,7 +331,7 @@ export const serviceDetail = { }), element: , searchBarOptions: { - showKueryBar: false, + showUnifiedSearchBar: false, }, }), params: t.partial({ diff --git a/x-pack/plugins/apm/public/components/routing/templates/dependency_detail_template.tsx b/x-pack/plugins/apm/public/components/routing/templates/dependency_detail_template.tsx index 7b57dbade2216..ecc4435f092ed 100644 --- a/x-pack/plugins/apm/public/components/routing/templates/dependency_detail_template.tsx +++ b/x-pack/plugins/apm/public/components/routing/templates/dependency_detail_template.tsx @@ -8,10 +8,7 @@ import { EuiFlexGroup, EuiFlexItem, EuiTitle } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import React from 'react'; -import { - getKueryBarBoolFilter, - kueryBarPlaceholder, -} from '../../../../common/dependencies'; +import { unifiedSearchBarPlaceholder } from '../../../../common/dependencies'; import { useApmParams } from '../../../hooks/use_apm_params'; import { useApmRouter } from '../../../hooks/use_apm_router'; import { useApmRoutePath } from '../../../hooks/use_apm_route_path'; @@ -29,7 +26,7 @@ interface Props { export function DependencyDetailTemplate({ children }: Props) { const { query, - query: { dependencyName, rangeFrom, rangeTo, environment }, + query: { dependencyName, rangeFrom, rangeTo }, } = useApmParams('/dependencies'); const router = useApmRouter(); @@ -38,11 +35,6 @@ export function DependencyDetailTemplate({ children }: Props) { const path = useApmRoutePath(); - const kueryBarBoolFilter = getKueryBarBoolFilter({ - environment, - dependencyName, - }); - const dependencyMetadataFetch = useFetcher( (callApmApi) => { if (!start || !end) { @@ -113,8 +105,7 @@ export function DependencyDetailTemplate({ children }: Props) { > {children} diff --git a/x-pack/plugins/apm/public/components/shared/kuery_bar/get_bool_filter.ts b/x-pack/plugins/apm/public/components/shared/get_bool_filter.ts similarity index 85% rename from x-pack/plugins/apm/public/components/shared/kuery_bar/get_bool_filter.ts rename to x-pack/plugins/apm/public/components/shared/get_bool_filter.ts index 92a8a9172ff9a..42575126aaab5 100644 --- a/x-pack/plugins/apm/public/components/shared/kuery_bar/get_bool_filter.ts +++ b/x-pack/plugins/apm/public/components/shared/get_bool_filter.ts @@ -12,11 +12,11 @@ import { SERVICE_NAME, TRANSACTION_NAME, TRANSACTION_TYPE, -} from '../../../../common/es_fields/apm'; -import { ENVIRONMENT_ALL } from '../../../../common/environment_filter_values'; -import { UIProcessorEvent } from '../../../../common/processor_event'; -import { environmentQuery } from '../../../../common/utils/environment_query'; -import { ApmUrlParams } from '../../../context/url_params_context/types'; +} from '../../../common/es_fields/apm'; +import { ENVIRONMENT_ALL } from '../../../common/environment_filter_values'; +import { UIProcessorEvent } from '../../../common/processor_event'; +import { environmentQuery } from '../../../common/utils/environment_query'; +import { ApmUrlParams } from '../../context/url_params_context/types'; export function getBoolFilter({ groupId, diff --git a/x-pack/plugins/apm/public/components/shared/kuery_bar/index.tsx b/x-pack/plugins/apm/public/components/shared/kuery_bar/index.tsx index 825fa8e3ef7f4..d166c7d283eda 100644 --- a/x-pack/plugins/apm/public/components/shared/kuery_bar/index.tsx +++ b/x-pack/plugins/apm/public/components/shared/kuery_bar/index.tsx @@ -18,9 +18,9 @@ import { useLegacyUrlParams } from '../../../context/url_params_context/use_url_ import { useApmParams } from '../../../hooks/use_apm_params'; import { useApmDataView } from '../../../hooks/use_apm_data_view'; import { fromQuery, toQuery } from '../links/url_helpers'; -import { getBoolFilter } from './get_bool_filter'; +import { getBoolFilter } from '../get_bool_filter'; import { Typeahead } from './typeahead'; -import { useProcessorEvent } from './use_processor_event'; +import { useProcessorEvent } from '../../../hooks/use_processor_event'; interface State { suggestions: QuerySuggestion[]; diff --git a/x-pack/plugins/apm/public/components/shared/search_bar/search_bar.test.tsx b/x-pack/plugins/apm/public/components/shared/search_bar/search_bar.test.tsx index 27eecaf573bf6..d9efbe0403b41 100644 --- a/x-pack/plugins/apm/public/components/shared/search_bar/search_bar.test.tsx +++ b/x-pack/plugins/apm/public/components/shared/search_bar/search_bar.test.tsx @@ -39,6 +39,20 @@ function setup({ const KibanaReactContext = createKibanaReactContext({ usageCollection: { reportUiCounter: () => {} }, dataViews: { get: async () => {} }, + data: { + query: { + queryString: { + setQuery: () => {}, + getQuery: () => {}, + clearQuery: () => {}, + }, + timefilter: { + timefilter: { + setTime: () => {}, + }, + }, + }, + }, } as Partial); // mock transaction types diff --git a/x-pack/plugins/apm/public/components/shared/search_bar/search_bar.tsx b/x-pack/plugins/apm/public/components/shared/search_bar/search_bar.tsx index edff90281b9ec..d2f25880ade33 100644 --- a/x-pack/plugins/apm/public/components/shared/search_bar/search_bar.tsx +++ b/x-pack/plugins/apm/public/components/shared/search_bar/search_bar.tsx @@ -4,8 +4,6 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ - -import { QueryDslQueryContainer } from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; import { EuiFlexGroup, EuiFlexGroupProps, @@ -13,30 +11,30 @@ import { EuiSpacer, } from '@elastic/eui'; import React from 'react'; +import { QueryDslQueryContainer } from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; import { isMobileAgentName } from '../../../../common/agent_name'; import { useApmServiceContext } from '../../../context/apm_service/use_apm_service_context'; import { useBreakpoints } from '../../../hooks/use_breakpoints'; -import { ApmDatePicker } from '../date_picker/apm_date_picker'; -import { KueryBar } from '../kuery_bar'; import { TimeComparison } from '../time_comparison'; import { TransactionTypeSelect } from '../transaction_type_select'; +import { UnifiedSearchBar } from '../unified_search_bar'; interface Props { hidden?: boolean; - showKueryBar?: boolean; + showUnifiedSearchBar?: boolean; showTimeComparison?: boolean; showTransactionTypeSelector?: boolean; - kueryBarPlaceholder?: string; - kueryBarBoolFilter?: QueryDslQueryContainer[]; + searchBarPlaceholder?: string; + searchBarBoolFilter?: QueryDslQueryContainer[]; } export function SearchBar({ hidden = false, - showKueryBar = true, + showUnifiedSearchBar = true, showTimeComparison = false, showTransactionTypeSelector = false, - kueryBarBoolFilter, - kueryBarPlaceholder, + searchBarPlaceholder, + searchBarBoolFilter, }: Props) { const { agentName } = useApmServiceContext(); const isMobileAgent = isMobileAgentName(agentName); @@ -69,11 +67,11 @@ export function SearchBar({ )} - {showKueryBar && ( + {showUnifiedSearchBar && ( - )} @@ -91,9 +89,6 @@ export function SearchBar({ )} - - - diff --git a/x-pack/plugins/apm/public/components/shared/unified_search_bar/index.tsx b/x-pack/plugins/apm/public/components/shared/unified_search_bar/index.tsx new file mode 100644 index 0000000000000..83e080c8c0df3 --- /dev/null +++ b/x-pack/plugins/apm/public/components/shared/unified_search_bar/index.tsx @@ -0,0 +1,248 @@ +/* + * 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, { useCallback, useEffect, useState } from 'react'; +import { i18n } from '@kbn/i18n'; +import { + Filter, + fromKueryExpression, + Query, + TimeRange, + toElasticsearchQuery, +} from '@kbn/es-query'; +import { useHistory, useLocation } from 'react-router-dom'; +import deepEqual from 'fast-deep-equal'; +import { useKibana } from '@kbn/kibana-react-plugin/public'; +import { EuiSkeletonRectangle } from '@elastic/eui'; +import qs from 'query-string'; +import { DataView, UI_SETTINGS } from '@kbn/data-plugin/common'; +import { QueryDslQueryContainer } from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; +import { UIProcessorEvent } from '../../../../common/processor_event'; +import { TimePickerTimeDefaults } from '../date_picker/typings'; +import { ApmPluginStartDeps } from '../../../plugin'; +import { useApmPluginContext } from '../../../context/apm_plugin/use_apm_plugin_context'; +import { useApmDataView } from '../../../hooks/use_apm_data_view'; +import { useProcessorEvent } from '../../../hooks/use_processor_event'; +import { fromQuery, toQuery } from '../links/url_helpers'; +import { useApmParams } from '../../../hooks/use_apm_params'; +import { getBoolFilter } from '../get_bool_filter'; +import { useLegacyUrlParams } from '../../../context/url_params_context/use_url_params'; + +function useSearchBarParams(defaultKuery?: string) { + const { path, query } = useApmParams('/*'); + const urlKuery = 'kuery' in query ? query.kuery : undefined; + const serviceName = 'serviceName' in path ? path.serviceName : undefined; + const groupId = 'groupId' in path ? path.groupId : undefined; + const environment = 'environment' in query ? query.environment : undefined; + + return { + kuery: urlKuery + ? { + query: defaultKuery || urlKuery, + language: 'kuery', + } + : undefined, + serviceName, + groupId, + environment, + }; +} + +function useUrlTimeRange(defaultTimeRange: TimeRange) { + const location = useLocation(); + const query = qs.parse(location.search); + + const isDateRangeSet = 'rangeFrom' in query && 'rangeTo' in query; + + if (isDateRangeSet) { + return { + from: query.rangeFrom, + to: query.rangeTo, + }; + } + return defaultTimeRange; +} + +function getSearchBarPlaceholder( + searchbarPlaceholder?: string, + processorEvent?: UIProcessorEvent +) { + const examples = { + transaction: 'transaction.duration.us > 300000', + error: 'http.response.status_code >= 400', + metric: 'process.pid = "1234"', + defaults: + 'transaction.duration.us > 300000 AND http.response.status_code >= 400', + }; + const example = examples[processorEvent || 'defaults']; + + return ( + searchbarPlaceholder ?? + i18n.translate('xpack.apm.unifiedSearchBar.placeholder', { + defaultMessage: `Search {event, select, + transaction {transactions} + metric {metrics} + error {errors} + other {transactions, errors and metrics} + } (E.g. {queryExample})`, + values: { + queryExample: example, + event: processorEvent, + }, + }) + ); +} + +function convertKueryToEsQuery(kuery: string, dataView: DataView) { + const ast = fromKueryExpression(kuery); + return toElasticsearchQuery(ast, dataView); +} + +export function UnifiedSearchBar({ + placeholder, + value, + showDatePicker = true, + showSubmitButton = true, + isClearable = true, + boolFilter, +}: { + placeholder?: string; + value?: string; + showDatePicker?: boolean; + showSubmitButton?: boolean; + isClearable?: boolean; + boolFilter?: QueryDslQueryContainer[]; +}) { + const { + unifiedSearch: { + ui: { SearchBar }, + }, + core, + } = useApmPluginContext(); + const { services } = useKibana(); + const { + data: { + query: { queryString: queryStringService, timefilter: timeFilterService }, + }, + } = services; + + const { kuery, serviceName, environment, groupId } = + useSearchBarParams(value); + const timePickerTimeDefaults = core.uiSettings.get( + UI_SETTINGS.TIMEPICKER_TIME_DEFAULTS + ); + const urlTimeRange = useUrlTimeRange(timePickerTimeDefaults); + const [displaySearchBar, setDisplaySearchBar] = useState(false); + + const syncSearchBarWithUrl = useCallback(() => { + // Sync Kuery params with Search Bar + if (kuery && !deepEqual(queryStringService.getQuery(), kuery)) { + queryStringService.setQuery(kuery); + } + // On page navigation the search bar persists the state where as the url is cleared, hence we need to clear the search bar + if (!kuery) { + queryStringService.clearQuery(); + } + // Sync Time Range with Search Bar + timeFilterService.timefilter.setTime(urlTimeRange as TimeRange); + }, [kuery, queryStringService, timeFilterService, urlTimeRange]); + + useEffect(() => { + syncSearchBarWithUrl(); + }, [syncSearchBarWithUrl]); + + const location = useLocation(); + const history = useHistory(); + const { dataView } = useApmDataView(); + const { urlParams } = useLegacyUrlParams(); + const processorEvent = useProcessorEvent(); + const searchbarPlaceholder = getSearchBarPlaceholder( + placeholder, + processorEvent + ); + + useEffect(() => { + if (dataView) setDisplaySearchBar(true); + }, [dataView]); + + const customFilters = + boolFilter ?? + getBoolFilter({ + groupId, + processorEvent, + serviceName, + environment, + urlParams, + }); + + const filtersForSearchBarSuggestions = customFilters.map((filter) => { + return { + query: filter, + } as Filter; + }); + const handleSubmit = (payload: { dateRange: TimeRange; query?: Query }) => { + if (dataView == null) { + return; + } + + const { dateRange, query } = payload; + const { from: rangeFrom, to: rangeTo } = dateRange; + + try { + const res = convertKueryToEsQuery( + query?.query as string, + dataView as DataView + ); + if (!res) { + return; + } + + const existingQueryParams = toQuery(location.search); + const updatedQueryWithTime = { + ...existingQueryParams, + rangeFrom, + rangeTo, + }; + history.push({ + ...location, + search: fromQuery({ + ...updatedQueryWithTime, + kuery: query?.query, + }), + }); + } catch (e) { + console.log('Invalid kuery syntax'); // eslint-disable-line no-console + } + }; + + return ( + + + + ); +} diff --git a/x-pack/plugins/apm/public/components/shared/unified_search_bar/unified_search_bar.test.tsx b/x-pack/plugins/apm/public/components/shared/unified_search_bar/unified_search_bar.test.tsx new file mode 100644 index 0000000000000..25d3ecbc41b0f --- /dev/null +++ b/x-pack/plugins/apm/public/components/shared/unified_search_bar/unified_search_bar.test.tsx @@ -0,0 +1,138 @@ +/* + * 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 { createMemoryHistory, MemoryHistory } from 'history'; +import React from 'react'; +import { Router, useLocation } from 'react-router-dom'; +import { createKibanaReactContext } from '@kbn/kibana-react-plugin/public'; +import { MockApmPluginContextWrapper } from '../../../context/apm_plugin/mock_apm_plugin_context'; +import * as useFetcherHook from '../../../hooks/use_fetcher'; +import * as useApmDataViewHook from '../../../hooks/use_apm_data_view'; +import * as useApmParamsHook from '../../../hooks/use_apm_params'; +import * as useProcessorEventHook from '../../../hooks/use_processor_event'; +import { fromQuery } from '../links/url_helpers'; +import { CoreStart } from '@kbn/core/public'; +import { UnifiedSearchBar } from '.'; +import { UrlParams } from '../../../context/url_params_context/types'; +import { mount } from 'enzyme'; + +jest.mock('react-router-dom', () => ({ + ...jest.requireActual('react-router-dom'), + useLocation: jest.fn(), +})); + +function setup({ + urlParams, + history, +}: { + urlParams: UrlParams; + history: MemoryHistory; +}) { + history.replace({ + pathname: '/services', + search: fromQuery(urlParams), + }); + + const setQuerySpy = jest.fn(); + const getQuerySpy = jest.fn(); + const clearQuerySpy = jest.fn(); + const setTimeSpy = jest.fn(); + + const KibanaReactContext = createKibanaReactContext({ + usageCollection: { + reportUiCounter: () => {}, + }, + dataViews: { + get: async () => {}, + }, + data: { + query: { + queryString: { + setQuery: setQuerySpy, + getQuery: getQuerySpy, + clearQuery: clearQuerySpy, + }, + timefilter: { + timefilter: { + setTime: setTimeSpy, + }, + }, + }, + }, + } as Partial); + + // mock transaction types + jest + .spyOn(useApmDataViewHook, 'useApmDataView') + .mockReturnValue({ dataView: undefined }); + + jest.spyOn(useFetcherHook, 'useFetcher').mockReturnValue({} as any); + + const wrapper = mount( + + + + + + + + ); + + return { wrapper, setQuerySpy, getQuerySpy, clearQuerySpy, setTimeSpy }; +} + +describe('when kuery is already present in the url, the search bar must reflect the same', () => { + let history: MemoryHistory; + beforeEach(() => { + history = createMemoryHistory(); + jest.spyOn(history, 'push'); + jest.spyOn(history, 'replace'); + }); + jest + .spyOn(useProcessorEventHook, 'useProcessorEvent') + .mockReturnValue(undefined); + + const search = '?method=json'; + const pathname = '/services'; + (useLocation as jest.Mock).mockImplementationOnce(() => ({ + search, + pathname, + })); + + it('sets the searchbar value based on URL', () => { + const expectedQuery = { + query: 'service.name:"opbeans-android"', + language: 'kuery', + }; + + const expectedTimeRange = { + from: 'now-15m', + to: 'now', + }; + + const urlParams = { + kuery: expectedQuery.query, + rangeFrom: expectedTimeRange.from, + rangeTo: expectedTimeRange.to, + environment: 'ENVIRONMENT_ALL', + comparisonEnabled: true, + serviceGroup: '', + offset: '1d', + }; + jest + .spyOn(useApmParamsHook, 'useApmParams') + .mockReturnValue({ query: urlParams, path: {} }); + + const { setQuerySpy, setTimeSpy } = setup({ + history, + urlParams, + }); + + expect(setQuerySpy).toBeCalledWith(expectedQuery); + expect(setTimeSpy).toBeCalledWith(expectedTimeRange); + }); +}); diff --git a/x-pack/plugins/apm/public/context/apm_plugin/mock_apm_plugin_context.tsx b/x-pack/plugins/apm/public/context/apm_plugin/mock_apm_plugin_context.tsx index 6924277f229c0..9ba943b79b12c 100644 --- a/x-pack/plugins/apm/public/context/apm_plugin/mock_apm_plugin_context.tsx +++ b/x-pack/plugins/apm/public/context/apm_plugin/mock_apm_plugin_context.tsx @@ -98,6 +98,12 @@ const mockCorePlugins = { data: {}, }; +const mockUnifiedSearch = { + ui: { + SearchBar: () =>
, + }, +}; + export const mockApmPluginContextValue = { appMountParameters: coreMock.createAppMountParameters('/basepath'), config: mockConfig, @@ -106,6 +112,7 @@ export const mockApmPluginContextValue = { observabilityRuleTypeRegistry: createObservabilityRuleTypeRegistryMock(), corePlugins: mockCorePlugins, deps: {}, + unifiedSearch: mockUnifiedSearch, }; export function MockApmPluginContextWrapper({ diff --git a/x-pack/plugins/apm/public/components/shared/kuery_bar/use_processor_event.ts b/x-pack/plugins/apm/public/hooks/use_processor_event.ts similarity index 94% rename from x-pack/plugins/apm/public/components/shared/kuery_bar/use_processor_event.ts rename to x-pack/plugins/apm/public/hooks/use_processor_event.ts index d8abb49e42277..deffd7eae1517 100644 --- a/x-pack/plugins/apm/public/components/shared/kuery_bar/use_processor_event.ts +++ b/x-pack/plugins/apm/public/hooks/use_processor_event.ts @@ -7,7 +7,7 @@ import { ProcessorEvent } from '@kbn/observability-plugin/common'; import { useLocation } from 'react-router-dom'; -import { UIProcessorEvent } from '../../../../common/processor_event'; +import { UIProcessorEvent } from '../../common/processor_event'; /** * Infer the processor.event to used based on the route path diff --git a/x-pack/plugins/apm/server/routes/alerts/action_variables.ts b/x-pack/plugins/apm/server/routes/alerts/action_variables.ts index 471d2ff3141b0..1bb9c41546cf8 100644 --- a/x-pack/plugins/apm/server/routes/alerts/action_variables.ts +++ b/x-pack/plugins/apm/server/routes/alerts/action_variables.ts @@ -94,4 +94,13 @@ export const apmActionVariables = { name: 'viewInAppUrl' as const, usesPublicBaseUrl: true, }, + errorGroupingKey: { + description: i18n.translate( + 'xpack.apm.alerts.action_variables.errorGroupingKey', + { + defaultMessage: 'The error grouping key the alert is created for', + } + ), + name: 'errorGroupingKey' as const, + }, }; diff --git a/x-pack/plugins/apm/server/routes/alerts/route.ts b/x-pack/plugins/apm/server/routes/alerts/route.ts index 314e05fed539a..13b790155520f 100644 --- a/x-pack/plugins/apm/server/routes/alerts/route.ts +++ b/x-pack/plugins/apm/server/routes/alerts/route.ts @@ -28,6 +28,7 @@ const alertParamsRt = t.intersection([ t.literal(AggregationType.P99), ]), serviceName: t.string, + errorGroupingKey: t.string, transactionType: t.string, transactionName: t.string, }), diff --git a/x-pack/plugins/apm/server/routes/alerts/rule_types/error_count/get_error_count_chart_preview.ts b/x-pack/plugins/apm/server/routes/alerts/rule_types/error_count/get_error_count_chart_preview.ts index 6175b2578a236..59ae52eab6a5a 100644 --- a/x-pack/plugins/apm/server/routes/alerts/rule_types/error_count/get_error_count_chart_preview.ts +++ b/x-pack/plugins/apm/server/routes/alerts/rule_types/error_count/get_error_count_chart_preview.ts @@ -7,7 +7,10 @@ import { rangeQuery, termQuery } from '@kbn/observability-plugin/server'; import { ProcessorEvent } from '@kbn/observability-plugin/common'; -import { SERVICE_NAME } from '../../../../../common/es_fields/apm'; +import { + ERROR_GROUP_ID, + SERVICE_NAME, +} from '../../../../../common/es_fields/apm'; import { AlertParams } from '../../route'; import { environmentQuery } from '../../../../../common/utils/environment_query'; import { APMEventClient } from '../../../../lib/helpers/create_es_client/create_apm_event_client'; @@ -24,12 +27,14 @@ export async function getTransactionErrorCountChartPreview({ apmEventClient: APMEventClient; alertParams: AlertParams; }): Promise { - const { serviceName, environment, interval, start, end } = alertParams; + const { serviceName, environment, errorGroupingKey, interval, start, end } = + alertParams; const query = { bool: { filter: [ ...termQuery(SERVICE_NAME, serviceName), + ...termQuery(ERROR_GROUP_ID, errorGroupingKey), ...rangeQuery(start, end), ...environmentQuery(environment), ], 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 a008dd0d23cb3..2527a11e3f001 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 @@ -25,6 +25,7 @@ import { getEnvironmentLabel, } from '../../../../../common/environment_filter_values'; import { + ERROR_GROUP_ID, PROCESSOR_EVENT, SERVICE_ENVIRONMENT, SERVICE_NAME, @@ -77,6 +78,7 @@ export function registerErrorCountRuleType({ apmActionVariables.interval, apmActionVariables.reason, apmActionVariables.serviceName, + apmActionVariables.errorGroupingKey, apmActionVariables.threshold, apmActionVariables.triggerValue, apmActionVariables.viewInAppUrl, @@ -112,6 +114,7 @@ export function registerErrorCountRuleType({ }, { term: { [PROCESSOR_EVENT]: ProcessorEvent.error } }, ...termQuery(SERVICE_NAME, ruleParams.serviceName), + ...termQuery(ERROR_GROUP_ID, ruleParams.errorGroupingKey), ...environmentQuery(ruleParams.environment), ], }, @@ -164,7 +167,12 @@ export function registerErrorCountRuleType({ windowUnit: ruleParams.windowUnit, }); - const id = [ApmRuleType.ErrorCount, serviceName, environment] + const id = [ + ApmRuleType.ErrorCount, + serviceName, + environment, + ruleParams.errorGroupingKey, + ] .filter((name) => name) .join('_'); @@ -188,6 +196,7 @@ export function registerErrorCountRuleType({ [PROCESSOR_EVENT]: ProcessorEvent.error, [ALERT_EVALUATION_VALUE]: errorCount, [ALERT_EVALUATION_THRESHOLD]: ruleParams.threshold, + [ERROR_GROUP_ID]: ruleParams.errorGroupingKey, [ALERT_REASON]: alertReason, ...sourceFields, }, @@ -201,6 +210,7 @@ export function registerErrorCountRuleType({ reason: alertReason, serviceName, threshold: ruleParams.threshold, + errorGroupingKey: ruleParams.errorGroupingKey, triggerValue: errorCount, viewInAppUrl, }); diff --git a/x-pack/plugins/cloud_security_posture/common/constants.ts b/x-pack/plugins/cloud_security_posture/common/constants.ts index 69b99bc9d58cb..0194fbf969ae3 100644 --- a/x-pack/plugins/cloud_security_posture/common/constants.ts +++ b/x-pack/plugins/cloud_security_posture/common/constants.ts @@ -75,6 +75,7 @@ export const CLOUDBEAT_VULN_MGMT_AZURE = 'cloudbeat/vuln_mgmt_azure'; export const KSPM_POLICY_TEMPLATE = 'kspm'; export const CSPM_POLICY_TEMPLATE = 'cspm'; export const VULN_MGMT_POLICY_TEMPLATE = 'vuln_mgmt'; +export const CNVM_POLICY_TEMPLATE = 'cnvm'; export const SUPPORTED_POLICY_TEMPLATES = [ KSPM_POLICY_TEMPLATE, CSPM_POLICY_TEMPLATE, diff --git a/x-pack/plugins/cloud_security_posture/public/components/fleet_extensions/policy_template_selectors.tsx b/x-pack/plugins/cloud_security_posture/public/components/fleet_extensions/policy_template_selectors.tsx index 1a3ac98536943..814e5251238a2 100644 --- a/x-pack/plugins/cloud_security_posture/public/components/fleet_extensions/policy_template_selectors.tsx +++ b/x-pack/plugins/cloud_security_posture/public/components/fleet_extensions/policy_template_selectors.tsx @@ -5,10 +5,15 @@ * 2.0. */ import React from 'react'; -import { EuiSpacer, EuiText } from '@elastic/eui'; +import { EuiCallOut, EuiSpacer, EuiText } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n-react'; import type { NewPackagePolicy } from '@kbn/fleet-plugin/common'; -import { CSPM_POLICY_TEMPLATE, KSPM_POLICY_TEMPLATE } from '../../../common/constants'; +import { + CSPM_POLICY_TEMPLATE, + KSPM_POLICY_TEMPLATE, + VULN_MGMT_POLICY_TEMPLATE, + CNVM_POLICY_TEMPLATE, +} from '../../../common/constants'; import type { PostureInput, CloudSecurityPolicyTemplate } from '../../../common/types'; import { getPolicyTemplateInputOptions, type NewPackagePolicyPostureInput } from './utils'; import { RadioGroup } from './csp_boxed_radio_group'; @@ -21,17 +26,26 @@ interface PolicyTemplateSelectorProps { disabled: boolean; } +const getPolicyTemplateLabel = (policyTemplate: CloudSecurityPolicyTemplate) => { + if (policyTemplate === VULN_MGMT_POLICY_TEMPLATE) { + return CNVM_POLICY_TEMPLATE.toUpperCase(); + } + return policyTemplate.toUpperCase(); +}; + export const PolicyTemplateSelector = ({ policy, selectedTemplate, setPolicyTemplate, disabled, }: PolicyTemplateSelectorProps) => { - const policyTemplates = new Set(policy.inputs.map((input) => input.policy_template!)); + const policyTemplates = new Set( + policy.inputs.map((input) => input.policy_template as CloudSecurityPolicyTemplate) + ); return (
- + ({ id: v, label: v.toUpperCase() }))} + options={Array.from(policyTemplates, (v) => ({ id: v, label: getPolicyTemplateLabel(v) }))} idSelected={selectedTemplate} - onChange={(id) => setPolicyTemplate(id as CloudSecurityPolicyTemplate)} + onChange={(id: CloudSecurityPolicyTemplate) => setPolicyTemplate(id)} disabled={disabled} />
@@ -69,7 +83,7 @@ interface PolicyTemplateInfoProps { } export const PolicyTemplateInfo = ({ postureType }: PolicyTemplateInfoProps) => ( - + {postureType === KSPM_POLICY_TEMPLATE && ( defaultMessage="Select the cloud service provider (CSP) you want to monitor and then fill in the name and description to help identify this integration" /> )} + {postureType === VULN_MGMT_POLICY_TEMPLATE && ( + <> + + } + > + +

+ +

+
+
+ + + + )}
); diff --git a/x-pack/plugins/cloud_security_posture/public/components/vulnerability_badges.tsx b/x-pack/plugins/cloud_security_posture/public/components/vulnerability_badges.tsx index bad8bb7f751d6..4275ed433d85e 100644 --- a/x-pack/plugins/cloud_security_posture/public/components/vulnerability_badges.tsx +++ b/x-pack/plugins/cloud_security_posture/public/components/vulnerability_badges.tsx @@ -32,11 +32,13 @@ export const CVSScoreBadge = ({ score, version }: CVSScoreBadgeProps) => { .euiBadge__text { display: flex; } + display: flex; + width: 62px; `} > {versionDisplay && ( <> - {score} + {score < 10 ? score.toFixed(1) : score}
`${Math.roun const PercentageInfo = ({ compact, postureScore, -}: CloudPostureScoreChartProps['data'] & { compact?: CloudPostureScoreChartProps['compact'] }) => { +}: ComplianceScoreChartProps['data'] & { compact?: ComplianceScoreChartProps['compact'] }) => { const { euiTheme } = useEuiTheme(); const percentage = getPostureScorePercentage(postureScore); @@ -59,6 +60,7 @@ const PercentageInfo = ({ paddingLeft: compact ? euiTheme.size.s : euiTheme.size.xs, marginBottom: compact ? euiTheme.size.s : 'none', }} + data-test-subj={DASHBOARD_COMPLIANCE_SCORE_CHART.COMPLIANCE_SCORE} >

{percentage}

@@ -140,12 +142,12 @@ const CounterLink = ({ ); }; -export const CloudPostureScoreChart = ({ +export const ComplianceScoreChart = ({ data, trend, onEvalCounterClick, compact, -}: CloudPostureScoreChartProps) => { +}: ComplianceScoreChartProps) => { const { euiTheme } = useEuiTheme(); return ( @@ -173,7 +175,7 @@ export const CloudPostureScoreChart = ({ color={statusColors.passed} onClick={() => onEvalCounterClick(RULE_PASSED)} tooltipContent={i18n.translate( - 'xpack.csp.cloudPostureScoreChart.counterLink.passedFindingsTooltip', + 'xpack.csp.complianceScoreChart.counterLink.passedFindingsTooltip', { defaultMessage: 'Passed findings' } )} /> @@ -184,7 +186,7 @@ export const CloudPostureScoreChart = ({ color={statusColors.failed} onClick={() => onEvalCounterClick(RULE_FAILED)} tooltipContent={i18n.translate( - 'xpack.csp.cloudPostureScoreChart.counterLink.failedFindingsTooltip', + 'xpack.csp.complianceScoreChart.counterLink.failedFindingsTooltip', { defaultMessage: 'Failed findings' } )} /> diff --git a/x-pack/plugins/cloud_security_posture/public/pages/compliance_dashboard/compliance_dashboard.tsx b/x-pack/plugins/cloud_security_posture/public/pages/compliance_dashboard/compliance_dashboard.tsx index 6c75891fc62af..fe4f62dbbc8f5 100644 --- a/x-pack/plugins/cloud_security_posture/public/pages/compliance_dashboard/compliance_dashboard.tsx +++ b/x-pack/plugins/cloud_security_posture/public/pages/compliance_dashboard/compliance_dashboard.tsx @@ -31,6 +31,7 @@ import { KUBERNETES_DASHBOARD_CONTAINER, KUBERNETES_DASHBOARD_TAB, CLOUD_DASHBOARD_TAB, + CLOUD_POSTURE_DASHBOARD_PAGE_HEADER, } from './test_subjects'; import { useCspmStatsApi, useKspmStatsApi } from '../../common/api/use_stats_api'; import { useCspSetupStatusApi } from '../../common/api/use_setup_status_api'; @@ -332,6 +333,7 @@ export const ComplianceDashboard = () => { return ( - @@ -151,7 +152,7 @@ export const SummarySection = ({ - ; interface FindingsSearchBarProps { setQuery(v: Partial): void; loading: boolean; + placeholder?: string; } export const FindingsSearchBar = ({ dataView, loading, setQuery, + placeholder = i18n.translate('xpack.csp.findings.searchBar.searchPlaceholder', { + defaultMessage: 'Search findings (eg. rule.section : "API Server" )', + }), }: FindingsSearchBarProps & { dataView: DataView }) => { const { euiTheme } = useEuiTheme(); const { @@ -49,9 +53,7 @@ export const FindingsSearchBar = ({ indexPatterns={[dataView]} onQuerySubmit={setQuery} onFiltersUpdated={(value: Filter[]) => setQuery({ filters: value })} - placeholder={i18n.translate('xpack.csp.findings.searchBar.searchPlaceholder', { - defaultMessage: 'Search findings (eg. rule.section : "API Server" )', - })} + placeholder={placeholder} />
); diff --git a/x-pack/plugins/cloud_security_posture/public/pages/vulnerabilities/hooks/use_latest_vulnerabilities.tsx b/x-pack/plugins/cloud_security_posture/public/pages/vulnerabilities/hooks/use_latest_vulnerabilities.tsx index 2869d8d7da321..7c73eccff12eb 100644 --- a/x-pack/plugins/cloud_security_posture/public/pages/vulnerabilities/hooks/use_latest_vulnerabilities.tsx +++ b/x-pack/plugins/cloud_security_posture/public/pages/vulnerabilities/hooks/use_latest_vulnerabilities.tsx @@ -13,6 +13,7 @@ import { LATEST_VULNERABILITIES_INDEX_PATTERN } from '../../../../common/constan import { useKibana } from '../../../common/hooks/use_kibana'; import { showErrorToast } from '../../../common/utils/show_error_toast'; import { MAX_FINDINGS_TO_LOAD } from '../../../common/constants'; +import { FindingsBaseEsQuery } from '../../../common/types'; type LatestFindingsRequest = IKibanaSearchRequest; type LatestFindingsResponse = IKibanaSearchResponse>; @@ -20,14 +21,35 @@ interface FindingsAggs { count: estypes.AggregationsMultiBucketAggregateBase; } -export const getFindingsQuery = ({ query, sort }: any) => ({ +interface VulnerabilitiesQuery extends FindingsBaseEsQuery { + sort: estypes.Sort; + enabled: boolean; +} + +export const getFindingsQuery = ({ query, sort }: VulnerabilitiesQuery) => ({ index: LATEST_VULNERABILITIES_INDEX_PATTERN, - query, + query: { + ...query, + bool: { + ...query?.bool, + filter: [ + ...(query?.bool?.filter || []), + { exists: { field: 'vulnerability.score.base' } }, + { exists: { field: 'vulnerability.score.version' } }, + { exists: { field: 'resource.name' } }, + { match_phrase: { 'vulnerability.enumeration': 'CVE' } }, + ], + must_not: [ + ...(query?.bool?.must_not || []), + { match_phrase: { 'vulnerability.severity': 'UNKNOWN' } }, + ], + }, + }, size: MAX_FINDINGS_TO_LOAD, sort, }); -export const useLatestVulnerabilities = (options: any) => { +export const useLatestVulnerabilities = (options: VulnerabilitiesQuery) => { const { data, notifications: { toasts }, diff --git a/x-pack/plugins/cloud_security_posture/public/pages/vulnerabilities/translations.ts b/x-pack/plugins/cloud_security_posture/public/pages/vulnerabilities/translations.ts new file mode 100644 index 0000000000000..b2c0ca0ca8366 --- /dev/null +++ b/x-pack/plugins/cloud_security_posture/public/pages/vulnerabilities/translations.ts @@ -0,0 +1,24 @@ +/* + * 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 FILTER_IN = i18n.translate('xpack.csp.vulnerabilities.table.filterIn', { + defaultMessage: 'Filter in', +}); +export const FILTER_OUT = i18n.translate('xpack.csp.vulnerabilities.table.filterOut', { + defaultMessage: 'Filter out', +}); +export const SEARCH_BAR_PLACEHOLDER = i18n.translate( + 'xpack.csp.vulnerabilities.searchBar.placeholder', + { + defaultMessage: 'Search vulnerabilities (eg. vulnerability.severity : "CRITICAL" )', + } +); +export const VULNERABILITIES = i18n.translate('xpack.csp.vulnerabilities', { + defaultMessage: 'Vulnerabilities', +}); diff --git a/x-pack/plugins/cloud_security_posture/public/pages/vulnerabilities/types.ts b/x-pack/plugins/cloud_security_posture/public/pages/vulnerabilities/types.ts index 8f01271677321..8d5d267f8135c 100644 --- a/x-pack/plugins/cloud_security_posture/public/pages/vulnerabilities/types.ts +++ b/x-pack/plugins/cloud_security_posture/public/pages/vulnerabilities/types.ts @@ -77,10 +77,9 @@ export interface VulnerabilityRecord { } export interface Vulnerability { - published_at: string; + published_date: string; score: { version: string; - impact: number; base: number; }; cwe: string[]; diff --git a/x-pack/plugins/cloud_security_posture/public/pages/vulnerabilities/utils/get_filters.ts b/x-pack/plugins/cloud_security_posture/public/pages/vulnerabilities/utils/get_filters.ts new file mode 100644 index 0000000000000..7f7d9ff544c62 --- /dev/null +++ b/x-pack/plugins/cloud_security_posture/public/pages/vulnerabilities/utils/get_filters.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 { + type Filter, + buildFilter, + FILTERS, + FilterStateStore, + compareFilters, + FilterCompareOptions, +} from '@kbn/es-query'; +import type { Serializable } from '@kbn/utility-types'; +import type { FindingsBaseProps } from '../../../common/types'; + +const compareOptions: FilterCompareOptions = { + negate: false, +}; + +/** + * adds a new filter to a new filters array + * removes existing filter if negated filter is added + * + * @returns {Filter[]} a new array of filters to be added back to filterManager + */ +export const getFilters = ({ + filters: existingFilters, + dataView, + field, + value, + negate, +}: { + filters: Filter[]; + dataView: FindingsBaseProps['dataView']; + field: string; + value: Serializable; + negate: boolean; +}): Filter[] => { + const dataViewField = dataView.fields.find((f) => f.spec.name === field); + if (!dataViewField) return existingFilters; + + const phraseFilter = buildFilter( + dataView, + dataViewField, + FILTERS.PHRASE, + negate, + false, + value, + null, + FilterStateStore.APP_STATE + ); + + const nextFilters = [ + ...existingFilters.filter( + // Exclude existing filters that match the newly added 'phraseFilter' + (filter) => !compareFilters(filter, phraseFilter, compareOptions) + ), + phraseFilter, + ]; + + return nextFilters; +}; diff --git a/x-pack/plugins/cloud_security_posture/public/pages/vulnerabilities/utils.ts b/x-pack/plugins/cloud_security_posture/public/pages/vulnerabilities/utils/index.ts similarity index 91% rename from x-pack/plugins/cloud_security_posture/public/pages/vulnerabilities/utils.ts rename to x-pack/plugins/cloud_security_posture/public/pages/vulnerabilities/utils/index.ts index 1e330a00b4fcf..0f7a3b6f1477b 100644 --- a/x-pack/plugins/cloud_security_posture/public/pages/vulnerabilities/utils.ts +++ b/x-pack/plugins/cloud_security_posture/public/pages/vulnerabilities/utils/index.ts @@ -5,7 +5,7 @@ * 2.0. */ -import { VectorScoreBase, Vector } from './types'; +import { VectorScoreBase, Vector } from '../types'; export const getVectorScoreList = (vectorBaseScore: VectorScoreBase) => { const result: Vector[] = []; @@ -24,7 +24,7 @@ export const getVectorScoreList = (vectorBaseScore: VectorScoreBase) => { if (v3Vector) { result.push({ - version: '2.0', + version: '3.0', vector: v3Vector, score: v3Score, }); diff --git a/x-pack/plugins/cloud_security_posture/public/pages/vulnerabilities/vulnerabilities.tsx b/x-pack/plugins/cloud_security_posture/public/pages/vulnerabilities/vulnerabilities.tsx index bc54dd08f5126..22142a5818731 100644 --- a/x-pack/plugins/cloud_security_posture/public/pages/vulnerabilities/vulnerabilities.tsx +++ b/x-pack/plugins/cloud_security_posture/public/pages/vulnerabilities/vulnerabilities.tsx @@ -9,8 +9,10 @@ import { EuiButtonIcon, EuiDataGrid, EuiDataGridCellValueElementProps, + EuiDataGridColumnCellAction, EuiProgress, EuiSpacer, + EuiToolTip, useEuiTheme, } from '@elastic/eui'; import { css } from '@emotion/react'; @@ -37,6 +39,8 @@ import { vulnerabilitiesColumns, } from './vulnerabilities_table_columns'; import { defaultLoadingRenderer, defaultNoDataRenderer } from '../../components/cloud_posture_page'; +import { getFilters } from './utils/get_filters'; +import { FILTER_IN, FILTER_OUT, SEARCH_BAR_PLACEHOLDER, VULNERABILITIES } from './translations'; const getDefaultQuery = ({ query, filters }: any): any => ({ query, @@ -77,6 +81,7 @@ const VulnerabilitiesContent = ({ dataView }: { dataView: DataView }) => { onSort, setUrlQuery, onResetFilters, + urlQuery, } = useCloudPostureTable({ dataView, defaultQuery: getDefaultQuery, @@ -118,8 +123,87 @@ const VulnerabilitiesContent = ({ dataView }: { dataView: DataView }) => { }); const columns = useMemo(() => { - return getVulnerabilitiesColumnsGrid(); - }, []); + const getColumnIdValue = (rowIndex: number, columnId: string) => { + const vulnerabilityRow = data?.page[rowIndex] as VulnerabilityRecord; + if (columnId === vulnerabilitiesColumns.vulnerability) { + return vulnerabilityRow.vulnerability.id; + } + if (columnId === vulnerabilitiesColumns.cvss) { + return vulnerabilityRow.vulnerability.score.base; + } + if (columnId === vulnerabilitiesColumns.resource) { + return vulnerabilityRow.resource?.name; + } + if (columnId === vulnerabilitiesColumns.severity) { + return vulnerabilityRow.vulnerability.severity; + } + if (columnId === vulnerabilitiesColumns.package_version) { + return vulnerabilityRow.vulnerability?.package?.name; + } + if (columnId === vulnerabilitiesColumns.fix_version) { + return vulnerabilityRow.vulnerability.package?.fixed_version; + } + }; + + const cellActions: EuiDataGridColumnCellAction[] = [ + ({ Component, rowIndex, columnId }) => { + const value = getColumnIdValue(rowIndex, columnId); + + if (!value) return null; + return ( + + { + setUrlQuery({ + pageIndex: 0, + filters: getFilters({ + filters: urlQuery.filters, + dataView, + field: columnId, + value, + negate: false, + }), + }); + }} + > + {FILTER_IN} + + + ); + }, + ({ Component, rowIndex, columnId }) => { + const value = getColumnIdValue(rowIndex, columnId); + + if (!value) return null; + return ( + + { + setUrlQuery({ + pageIndex: 0, + filters: getFilters({ + filters: urlQuery.filters, + dataView, + field: columnId, + value: getColumnIdValue(rowIndex, columnId), + negate: true, + }), + }); + }} + > + {FILTER_OUT} + + + ); + }, + ]; + + return getVulnerabilitiesColumnsGrid(cellActions); + }, [data?.page, dataView, setUrlQuery, urlQuery.filters]); const renderCellValue = useMemo(() => { return ({ rowIndex, columnId }: EuiDataGridCellValueElementProps) => { @@ -176,10 +260,13 @@ const VulnerabilitiesContent = ({ dataView }: { dataView: DataView }) => { ); } if (columnId === vulnerabilitiesColumns.fix_version) { + if (!vulnerabilityRow.vulnerability.package?.fixed_version) { + return null; + } return ( <> {vulnerabilityRow.vulnerability.package?.name}{' '} - {vulnerabilityRow.vulnerability.package?.fixed_version} + {vulnerabilityRow.vulnerability.package.fixed_version} ); } @@ -207,6 +294,7 @@ const VulnerabilitiesContent = ({ dataView }: { dataView: DataView }) => { setUrlQuery({ ...newQuery, pageIndex: 0 }); }} loading={isLoading} + placeholder={SEARCH_BAR_PLACEHOLDER} /> {!isLoading && data.page.length === 0 ? ( @@ -236,8 +324,16 @@ const VulnerabilitiesContent = ({ dataView }: { dataView: DataView }) => { & .euiDataGridRowCell { font-size: ${euiTheme.size.m}; } + & + .euiDataGridRowCell__expandActions + > [data-test-subj='euiDataGridCellExpandButton'] { + display: none; + } + & .euiDataGridRowCell__expandFlex { + align-items: center; + } `} - aria-label="Data grid styling demo" + aria-label={VULNERABILITIES} columns={columns} columnVisibility={{ visibleColumns: columns.map(({ id }) => id), diff --git a/x-pack/plugins/cloud_security_posture/public/pages/vulnerabilities/vulnerabilities_finding_flyout/vulnerability_overview_tab.tsx b/x-pack/plugins/cloud_security_posture/public/pages/vulnerabilities/vulnerabilities_finding_flyout/vulnerability_overview_tab.tsx index 547bec3c9b173..89a1232540a0d 100644 --- a/x-pack/plugins/cloud_security_posture/public/pages/vulnerabilities/vulnerabilities_finding_flyout/vulnerability_overview_tab.tsx +++ b/x-pack/plugins/cloud_security_posture/public/pages/vulnerabilities/vulnerabilities_finding_flyout/vulnerability_overview_tab.tsx @@ -114,11 +114,11 @@ const VulnerabilityOverviewTiles = ({ vulnerability }: VulnerabilityTabProps) => margin-bottom: 6px; `; - const date = moment(vulnerability?.published_at).format('LL').toString(); + const date = moment(vulnerability?.published_date).format('LL').toString(); return ( - {vulnerability?.score?.version && vulnerability?.score?.impact && ( + {vulnerability?.score?.version && vulnerability?.score?.base && (
- +
)} diff --git a/x-pack/plugins/cloud_security_posture/public/pages/vulnerabilities/vulnerabilities_table_columns.ts b/x-pack/plugins/cloud_security_posture/public/pages/vulnerabilities/vulnerabilities_table_columns.ts index 88a6a8e569c80..bfa23ac440cd6 100644 --- a/x-pack/plugins/cloud_security_posture/public/pages/vulnerabilities/vulnerabilities_table_columns.ts +++ b/x-pack/plugins/cloud_security_posture/public/pages/vulnerabilities/vulnerabilities_table_columns.ts @@ -5,7 +5,7 @@ * 2.0. */ -import { EuiDataGridColumn } from '@elastic/eui'; +import { EuiDataGridColumn, EuiDataGridColumnCellAction } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; export const vulnerabilitiesColumns = { @@ -27,7 +27,9 @@ const defaultColumnProps = () => ({ }, }); -export const getVulnerabilitiesColumnsGrid = (): EuiDataGridColumn[] => [ +export const getVulnerabilitiesColumnsGrid = ( + cellActions: EuiDataGridColumnCellAction[] +): EuiDataGridColumn[] => [ { ...defaultColumnProps(), id: vulnerabilitiesColumns.actions, @@ -36,6 +38,7 @@ export const getVulnerabilitiesColumnsGrid = (): EuiDataGridColumn[] => [ actions: false, isSortable: false, isResizable: false, + cellActions: [], }, { ...defaultColumnProps(), @@ -43,15 +46,16 @@ export const getVulnerabilitiesColumnsGrid = (): EuiDataGridColumn[] => [ displayAsText: i18n.translate('xpack.csp.vulnerabilityTable.column.vulnerability', { defaultMessage: 'Vulnerability', }), - initialWidth: 150, - isResizable: false, + initialWidth: 130, + cellActions, }, { ...defaultColumnProps(), id: vulnerabilitiesColumns.cvss, displayAsText: 'CVSS', - initialWidth: 84, + initialWidth: 80, isResizable: false, + cellActions, }, { ...defaultColumnProps(), @@ -59,6 +63,7 @@ export const getVulnerabilitiesColumnsGrid = (): EuiDataGridColumn[] => [ displayAsText: i18n.translate('xpack.csp.vulnerabilityTable.column.resource', { defaultMessage: 'Resource', }), + cellActions, }, { ...defaultColumnProps(), @@ -67,6 +72,7 @@ export const getVulnerabilitiesColumnsGrid = (): EuiDataGridColumn[] => [ defaultMessage: 'Severity', }), initialWidth: 100, + cellActions, }, { ...defaultColumnProps(), @@ -74,6 +80,7 @@ export const getVulnerabilitiesColumnsGrid = (): EuiDataGridColumn[] => [ displayAsText: i18n.translate('xpack.csp.vulnerabilityTable.column.packageAndVersion', { defaultMessage: 'Package and Version', }), + cellActions, }, { ...defaultColumnProps(), @@ -81,5 +88,6 @@ export const getVulnerabilitiesColumnsGrid = (): EuiDataGridColumn[] => [ displayAsText: i18n.translate('xpack.csp.vulnerabilityTable.column.fixVersion', { defaultMessage: 'Fix Version', }), + cellActions, }, ]; diff --git a/x-pack/plugins/data_visualizer/public/application/common/components/stats_table/components/field_data_expanded_row/error_message.tsx b/x-pack/plugins/data_visualizer/public/application/common/components/stats_table/components/field_data_expanded_row/error_message.tsx index 54cbeb28cf6f3..0f3dc78f97487 100644 --- a/x-pack/plugins/data_visualizer/public/application/common/components/stats_table/components/field_data_expanded_row/error_message.tsx +++ b/x-pack/plugins/data_visualizer/public/application/common/components/stats_table/components/field_data_expanded_row/error_message.tsx @@ -8,14 +8,14 @@ import { EuiCallOut } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n-react'; import React from 'react'; -import { DVErrorObject } from '../../../../../index_data_visualizer/utils/error_utils'; +import { MLErrorObject } from '@kbn/ml-error-utils'; export const ErrorMessageContent = ({ fieldName, error, }: { fieldName: string; - error: DVErrorObject; + error: MLErrorObject; }) => { return ( diff --git a/x-pack/plugins/data_visualizer/public/application/index_data_visualizer/hooks/use_overall_stats.ts b/x-pack/plugins/data_visualizer/public/application/index_data_visualizer/hooks/use_overall_stats.ts index ced7bf1058762..3419b1674fddc 100644 --- a/x-pack/plugins/data_visualizer/public/application/index_data_visualizer/hooks/use_overall_stats.ts +++ b/x-pack/plugins/data_visualizer/public/application/index_data_visualizer/hooks/use_overall_stats.ts @@ -16,6 +16,7 @@ import type { IKibanaSearchResponse, ISearchOptions, } from '@kbn/data-plugin/common'; +import { extractErrorProperties } from '@kbn/ml-error-utils'; import { useDataVisualizerKibana } from '../../kibana_context'; import { AggregatableFieldOverallStats, @@ -29,7 +30,6 @@ import { } from '../search_strategy/requests/overall_stats'; import type { OverallStats } from '../types/overall_stats'; import { getDefaultPageState } from '../components/index_data_visualizer_view/index_data_visualizer_view'; -import { extractErrorProperties } from '../utils/error_utils'; import { DataStatsFetchProgress, isRandomSamplingOption, diff --git a/x-pack/plugins/data_visualizer/public/application/index_data_visualizer/search_strategy/requests/get_boolean_field_stats.ts b/x-pack/plugins/data_visualizer/public/application/index_data_visualizer/search_strategy/requests/get_boolean_field_stats.ts index bf941e0952657..e394f6456d8f5 100644 --- a/x-pack/plugins/data_visualizer/public/application/index_data_visualizer/search_strategy/requests/get_boolean_field_stats.ts +++ b/x-pack/plugins/data_visualizer/public/application/index_data_visualizer/search_strategy/requests/get_boolean_field_stats.ts @@ -15,6 +15,7 @@ import type { ISearchStart, } from '@kbn/data-plugin/public'; import { isPopulatedObject } from '@kbn/ml-is-populated-object'; +import { extractErrorProperties } from '@kbn/ml-error-utils'; import { processTopValues } from './utils'; import { buildAggregationWithSamplingOption } from './build_random_sampler_agg'; @@ -25,7 +26,6 @@ import type { FieldStatsCommonRequestParams, } from '../../../../../common/types/field_stats'; import { FieldStatsError, isIKibanaSearchResponse } from '../../../../../common/types/field_stats'; -import { extractErrorProperties } from '../../utils/error_utils'; export const getBooleanFieldsStatsRequest = ( params: FieldStatsCommonRequestParams, diff --git a/x-pack/plugins/data_visualizer/public/application/index_data_visualizer/search_strategy/requests/get_date_field_stats.ts b/x-pack/plugins/data_visualizer/public/application/index_data_visualizer/search_strategy/requests/get_date_field_stats.ts index 863cd6885fe88..4bd914f3637e5 100644 --- a/x-pack/plugins/data_visualizer/public/application/index_data_visualizer/search_strategy/requests/get_date_field_stats.ts +++ b/x-pack/plugins/data_visualizer/public/application/index_data_visualizer/search_strategy/requests/get_date_field_stats.ts @@ -16,11 +16,11 @@ import type { ISearchStart, } from '@kbn/data-plugin/public'; import { isPopulatedObject } from '@kbn/ml-is-populated-object'; +import { extractErrorProperties } from '@kbn/ml-error-utils'; import { buildAggregationWithSamplingOption } from './build_random_sampler_agg'; import type { FieldStatsCommonRequestParams } from '../../../../../common/types/field_stats'; import type { Field, DateFieldStats, Aggs } from '../../../../../common/types/field_stats'; import { FieldStatsError, isIKibanaSearchResponse } from '../../../../../common/types/field_stats'; -import { extractErrorProperties } from '../../utils/error_utils'; export const getDateFieldsStatsRequest = ( params: FieldStatsCommonRequestParams, diff --git a/x-pack/plugins/data_visualizer/public/application/index_data_visualizer/search_strategy/requests/get_field_examples.ts b/x-pack/plugins/data_visualizer/public/application/index_data_visualizer/search_strategy/requests/get_field_examples.ts index df7afb16479f0..3943979d290d9 100644 --- a/x-pack/plugins/data_visualizer/public/application/index_data_visualizer/search_strategy/requests/get_field_examples.ts +++ b/x-pack/plugins/data_visualizer/public/application/index_data_visualizer/search_strategy/requests/get_field_examples.ts @@ -17,6 +17,7 @@ import type { import { isPopulatedObject } from '@kbn/ml-is-populated-object'; import type { SearchHit } from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; import { buildBaseFilterCriteria } from '@kbn/ml-query-utils'; +import { extractErrorProperties } from '@kbn/ml-error-utils'; import { getUniqGeoOrStrExamples } from '../../../common/util/example_utils'; import type { Field, @@ -24,7 +25,6 @@ import type { FieldStatsCommonRequestParams, } from '../../../../../common/types/field_stats'; import { FieldStatsError, isIKibanaSearchResponse } from '../../../../../common/types/field_stats'; -import { extractErrorProperties } from '../../utils/error_utils'; import { MAX_EXAMPLES_DEFAULT } from './constants'; export const getFieldExamplesRequest = (params: FieldStatsCommonRequestParams, field: Field) => { diff --git a/x-pack/plugins/data_visualizer/public/application/index_data_visualizer/search_strategy/requests/get_numeric_field_stats.ts b/x-pack/plugins/data_visualizer/public/application/index_data_visualizer/search_strategy/requests/get_numeric_field_stats.ts index b9dd55351781f..f7d1b39f15d3f 100644 --- a/x-pack/plugins/data_visualizer/public/application/index_data_visualizer/search_strategy/requests/get_numeric_field_stats.ts +++ b/x-pack/plugins/data_visualizer/public/application/index_data_visualizer/search_strategy/requests/get_numeric_field_stats.ts @@ -18,6 +18,7 @@ import type { import type { ISearchStart } from '@kbn/data-plugin/public'; import { isPopulatedObject } from '@kbn/ml-is-populated-object'; import { isDefined } from '@kbn/ml-is-defined'; +import { extractErrorProperties } from '@kbn/ml-error-utils'; import { processTopValues } from './utils'; import { buildAggregationWithSamplingOption } from './build_random_sampler_agg'; import { MAX_PERCENT, PERCENTILE_SPACING, SAMPLER_TOP_TERMS_THRESHOLD } from './constants'; @@ -32,7 +33,6 @@ import type { FieldStatsError, } from '../../../../../common/types/field_stats'; import { processDistributionData } from '../../utils/process_distribution_data'; -import { extractErrorProperties } from '../../utils/error_utils'; import { isIKibanaSearchResponse, isNormalSamplingOption, diff --git a/x-pack/plugins/data_visualizer/public/application/index_data_visualizer/search_strategy/requests/get_string_field_stats.ts b/x-pack/plugins/data_visualizer/public/application/index_data_visualizer/search_strategy/requests/get_string_field_stats.ts index a035842fa8767..159be48b338e4 100644 --- a/x-pack/plugins/data_visualizer/public/application/index_data_visualizer/search_strategy/requests/get_string_field_stats.ts +++ b/x-pack/plugins/data_visualizer/public/application/index_data_visualizer/search_strategy/requests/get_string_field_stats.ts @@ -16,6 +16,7 @@ import type { ISearchStart, } from '@kbn/data-plugin/public'; import { isPopulatedObject } from '@kbn/ml-is-populated-object'; +import { extractErrorProperties } from '@kbn/ml-error-utils'; import { processTopValues } from './utils'; import { buildAggregationWithSamplingOption } from './build_random_sampler_agg'; import { SAMPLER_TOP_TERMS_THRESHOLD } from './constants'; @@ -26,7 +27,6 @@ import type { StringFieldStats, } from '../../../../../common/types/field_stats'; import { FieldStatsError, isIKibanaSearchResponse } from '../../../../../common/types/field_stats'; -import { extractErrorProperties } from '../../utils/error_utils'; export const getStringFieldStatsRequest = ( params: FieldStatsCommonRequestParams, diff --git a/x-pack/plugins/data_visualizer/public/application/index_data_visualizer/utils/error_utils.ts b/x-pack/plugins/data_visualizer/public/application/index_data_visualizer/utils/error_utils.ts deleted file mode 100644 index 06a9b0b4002a6..0000000000000 --- a/x-pack/plugins/data_visualizer/public/application/index_data_visualizer/utils/error_utils.ts +++ /dev/null @@ -1,184 +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 { IHttpFetchError } from '@kbn/core-http-browser'; -import Boom from '@hapi/boom'; -import { isPopulatedObject } from '@kbn/ml-is-populated-object'; - -export interface WrappedError { - body: { - attributes: { - body: EsErrorBody; - }; - message: Boom.Boom; - }; - statusCode: number; -} - -export interface EsErrorRootCause { - type: string; - reason: string; - caused_by?: EsErrorRootCause; - script?: string; -} - -export interface EsErrorBody { - error: { - root_cause?: EsErrorRootCause[]; - caused_by?: EsErrorRootCause; - type: string; - reason: string; - }; - status: number; -} - -export interface DVResponseError { - statusCode: number; - error: string; - message: string; - attributes?: { - body: EsErrorBody; - }; -} - -export interface ErrorMessage { - message: string; -} - -export interface DVErrorObject { - causedBy?: string; - message: string; - statusCode?: number; - fullError?: EsErrorBody; -} - -export interface DVHttpFetchError extends IHttpFetchError { - body: T; -} - -export type ErrorType = - | WrappedError - | DVHttpFetchError - | EsErrorBody - | Boom.Boom - | string - | undefined; - -export function isEsErrorBody(error: any): error is EsErrorBody { - return error && error.error?.reason !== undefined; -} - -export function isErrorString(error: any): error is string { - return typeof error === 'string'; -} - -export function isErrorMessage(error: any): error is ErrorMessage { - return error && error.message !== undefined && typeof error.message === 'string'; -} - -export function isDVResponseError(error: any): error is DVResponseError { - return typeof error.body === 'object' && 'message' in error.body; -} - -export function isBoomError(error: any): error is Boom.Boom { - return error?.isBoom === true; -} - -export function isWrappedError(error: any): error is WrappedError { - return error && isBoomError(error.body?.message) === true; -} - -export const extractErrorProperties = (error: ErrorType): DVErrorObject => { - // extract properties of the error object from within the response error - // coming from Kibana, Elasticsearch, and our own DV messages - - // some responses contain raw es errors as part of a bulk response - // e.g. if some jobs fail the action in a bulk request - - if (isEsErrorBody(error)) { - return { - message: error.error.reason, - statusCode: error.status, - fullError: error, - }; - } - - if (isErrorString(error)) { - return { - message: error, - }; - } - if (isWrappedError(error)) { - return error.body.message?.output?.payload; - } - - if (isBoomError(error)) { - return { - message: error.output.payload.message, - statusCode: error.output.payload.statusCode, - }; - } - - if (error?.body === undefined && !error?.message) { - return { - message: '', - }; - } - - if (typeof error.body === 'string') { - return { - message: error.body, - }; - } - - if (isDVResponseError(error)) { - if ( - typeof error.body.attributes === 'object' && - typeof error.body.attributes.body?.error?.reason === 'string' - ) { - const errObj: DVErrorObject = { - message: error.body.attributes.body.error.reason, - statusCode: error.body.statusCode, - fullError: error.body.attributes.body, - }; - if ( - typeof error.body.attributes.body.error.caused_by === 'object' && - (typeof error.body.attributes.body.error.caused_by?.reason === 'string' || - typeof error.body.attributes.body.error.caused_by?.caused_by?.reason === 'string') - ) { - errObj.causedBy = - error.body.attributes.body.error.caused_by?.caused_by?.reason || - error.body.attributes.body.error.caused_by?.reason; - } - if ( - Array.isArray(error.body.attributes.body.error.root_cause) && - typeof error.body.attributes.body.error.root_cause[0] === 'object' && - isPopulatedObject(error.body.attributes.body.error.root_cause[0], ['script']) - ) { - errObj.causedBy = error.body.attributes.body.error.root_cause[0].script; - errObj.message += `: '${error.body.attributes.body.error.root_cause[0].script}'`; - } - return errObj; - } else { - return { - message: error.body.message, - statusCode: error.body.statusCode, - }; - } - } - - if (isErrorMessage(error)) { - return { - message: error.message, - }; - } - - // If all else fail return an empty message instead of JSON.stringify - return { - message: '', - }; -}; diff --git a/x-pack/plugins/data_visualizer/tsconfig.json b/x-pack/plugins/data_visualizer/tsconfig.json index 7f38e7b9b77cb..4609d1e9497d2 100644 --- a/x-pack/plugins/data_visualizer/tsconfig.json +++ b/x-pack/plugins/data_visualizer/tsconfig.json @@ -17,7 +17,6 @@ "@kbn/cloud-chat-plugin", "@kbn/cloud-plugin", "@kbn/core-execution-context-common", - "@kbn/core-http-browser", "@kbn/core", "@kbn/custom-integrations-plugin", "@kbn/data-plugin", @@ -60,6 +59,7 @@ "@kbn/unified-search-plugin", "@kbn/usage-collection-plugin", "@kbn/utility-types", + "@kbn/ml-error-utils", ], "exclude": [ "target/**/*", diff --git a/x-pack/plugins/enterprise_search/README.md b/x-pack/plugins/enterprise_search/README.md index 5694258001dd4..af0cdd43d97b8 100644 --- a/x-pack/plugins/enterprise_search/README.md +++ b/x-pack/plugins/enterprise_search/README.md @@ -89,29 +89,13 @@ Cypress tests can be run directly from the `x-pack/plugins/enterprise_search` fo ```bash # Basic syntax -sh cypress.sh {run|open} {suite} +sh cypress.sh {run|open|dev} # Examples -sh cypress.sh run overview # run Enterprise Search overview tests -sh cypress.sh open overview # open Enterprise Search overview tests +sh cypress.sh run # run Enterprise Search tests +sh cypress.sh open # open Enterprise Search tests +sh cypress.sh dev # run "cypress only" with Enterprise Search config -sh cypress.sh run as # run App Search tests -sh cypress.sh open as # open App Search tests - -sh cypress.sh run ws # run Workplace Search tests -sh cypress.sh open ws # open Workplace Search tests - -# Overriding env variables -sh cypress.sh open as --env username=enterprise_search password=123 - -# Overriding config settings, e.g. changing the base URL to a dev path, or enabling video recording -sh cypress.sh open as --config baseUrl=http://localhost:5601/xyz video=true - -# Only run a single specific test file -sh cypress.sh run ws --spec '**/example.spec.ts' - -# Opt to run Chrome headlessly -sh cypress.sh run ws --headless ``` There are 3 ways you can spin up the required environments to run our Cypress tests: @@ -126,13 +110,8 @@ There are 3 ways you can spin up the required environments to run our Cypress te - Enterprise Search: - Nothing extra is required to run Cypress tests, only what is already needed to run Kibana/Enterprise Search locally. 2. Running Cypress against Kibana's functional test server: - - :information_source: While we won't use the runner, we can still make use of Kibana's functional test server to help us spin up Elasticsearch and Kibana instances. - - NOTE: We recommend stopping any other local dev processes, to reduce issues with memory/performance - - From the `x-pack/` project folder, run `node scripts/functional_tests_server --config test/functional_enterprise_search/cypress.config.ts` - - Kibana: - - You will need to pass `--config baseUrl=http://localhost:5620` into your Cypress command. - - Enterprise Search: - - :warning: TODO: We _currently_ do not have a way of spinning up Enterprise Search from Kibana's FTR - for now, you can use local Enterprise Search (pointed at the FTR's `http://localhost:9220` Elasticsearch host instance) + - Make sure docker is up and running in you system + - From the `x-pack/` project folder, run `sh cypress.sh` which will spin up Kibana, Elasticsearch through functional test runners and Enterprise Search instance in Docker. 3. Running Cypress against Enterprise Search dockerized stack scripts - :warning: This is for Enterprise Search devs only, as this requires access to our closed source Enterprise Search repo - `stack_scripts/start-with-es-native-auth.sh --with-kibana` @@ -158,4 +137,4 @@ To track what Cypress is doing while running tests, you can pass in `--config vi See [our functional test runner README](../../test/functional_enterprise_search). -Our automated accessibility tests can be found in [x-pack/test/accessibility/apps](../../test/accessibility/apps/enterprise_search.ts). +Our automated accessibility tests can be found in [x-pack/test/accessibility/apps](../../test/accessibility/apps/enterprise_search.ts). \ No newline at end of file diff --git a/x-pack/plugins/enterprise_search/common/constants.ts b/x-pack/plugins/enterprise_search/common/constants.ts index bdaf569d27143..34a48c7c76ccf 100644 --- a/x-pack/plugins/enterprise_search/common/constants.ts +++ b/x-pack/plugins/enterprise_search/common/constants.ts @@ -119,8 +119,8 @@ export const SEARCH_EXPERIENCES_PLUGIN = { }; export const ENGINES_PLUGIN = { - NAV_TITLE: i18n.translate('xpack.enterpriseSearch.engines.navTitle', { - defaultMessage: 'Engines', + NAV_TITLE: i18n.translate('xpack.enterpriseSearch.applications.navTitle', { + defaultMessage: 'Applications', }), }; diff --git a/x-pack/plugins/enterprise_search/cypress.config.ts b/x-pack/plugins/enterprise_search/cypress.config.ts new file mode 100644 index 0000000000000..a08f3de4bf69b --- /dev/null +++ b/x-pack/plugins/enterprise_search/cypress.config.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 { defineCypressConfig } from '@kbn/cypress-config'; + +export default defineCypressConfig({ + defaultCommandTimeout: 60000, + execTimeout: 120000, + pageLoadTimeout: 120000, + requestTimeout: 60000, + responseTimeout: 60000, + retries: { + runMode: 2, + }, + screenshotOnRunFailure: false, + screenshotsFolder: '../../../target/kibana-fleet/cypress/screenshots', + trashAssetsBeforeRuns: false, + video: false, + videosFolder: '../../../target/kibana-fleet/cypress/videos', + viewportHeight: 900, + viewportWidth: 1440, + + // eslint-disable-next-line sort-keys + env: { + configport: '5601', + hostname: 'localhost', + protocol: 'http', + }, + // eslint-disable-next-line sort-keys + e2e: { + baseUrl: 'http://localhost:5601', + supportFile: './cypress/support/e2e.ts', + }, +}); diff --git a/x-pack/plugins/enterprise_search/cypress.sh b/x-pack/plugins/enterprise_search/cypress.sh old mode 100644 new mode 100755 index 9dbdd81ab788f..05f3ed07140fd --- a/x-pack/plugins/enterprise_search/cypress.sh +++ b/x-pack/plugins/enterprise_search/cypress.sh @@ -1,18 +1,22 @@ #! /bin/bash # Use either `cypress run` or `cypress open` - defaults to run -MODE="${1:-run}" +MODE="${1}" -# Choose which product folder to use, e.g. `yarn cypress open as` -PRODUCT="${2}" -# Provide helpful shorthands -if [ "$PRODUCT" == "as" ]; then PRODUCT='app_search'; fi -if [ "$PRODUCT" == "ws" ]; then PRODUCT='workplace_search'; fi -if [ "$PRODUCT" == "overview" ]; then PRODUCT='enterprise_search'; fi +if [ "$MODE" == "dev" ]; then + echo "Running dev mode. This will run cypress only" + node ../../../node_modules/.bin/cypress open --config-file ./cypress.config.ts ${2} +else + if ! docker info > /dev/null 2>&1; then + echo "This script needs docker to run. Start docker and try again." + echo "If you are testing against your own setup use ./cypress.sh dev" + exit 1 + fi -# Pass all remaining arguments (e.g., ...rest) from the 3rd arg onwards -# as an open-ended string. Appends onto to the end the Cypress command -# @see https://docs.cypress.io/guides/guides/command-line.html#Options -ARGS="${*:3}" + if [ "$MODE" == "open" ]; then + node ../../../scripts/functional_tests --config ../../test/functional_enterprise_search/visual_config.ts + else + node ../../../scripts/functional_tests --config ../../test/functional_enterprise_search/cli_config.ts + fi +fi -../../../node_modules/.bin/cypress "$MODE" --project "public/applications/$PRODUCT" --browser chrome $ARGS diff --git a/x-pack/plugins/enterprise_search/cypress/e2e/content/basic_crawler.cy.ts b/x-pack/plugins/enterprise_search/cypress/e2e/content/basic_crawler.cy.ts new file mode 100644 index 0000000000000..484aa6976072d --- /dev/null +++ b/x-pack/plugins/enterprise_search/cypress/e2e/content/basic_crawler.cy.ts @@ -0,0 +1,15 @@ +/* + * 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 { login } from '../../tasks/login'; + +describe('Enterprise Search Crawler', () => { + it('test', () => { + login(); + cy.visit('/app/enterprise_search/content/search_indices/new_index'); + }); +}); diff --git a/x-pack/plugins/enterprise_search/cypress/support/e2e.ts b/x-pack/plugins/enterprise_search/cypress/support/e2e.ts new file mode 100644 index 0000000000000..1e2b130d59973 --- /dev/null +++ b/x-pack/plugins/enterprise_search/cypress/support/e2e.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. + */ + +// / + +// *********************************************************** +// This example support/index.js is processed and +// loaded automatically before your test files. +// +// This is a great place to put global configuration and +// behavior that modifies Cypress. +// +// You can change the location of this file or turn off +// automatically serving support files with the +// 'supportFile' configuration option. +// +// You can read more here: +// https://on.cypress.io/configuration +// *********************************************************** + +// Enforce building this file. +export {}; + +declare global { + // eslint-disable-next-line @typescript-eslint/no-namespace + namespace Cypress { + interface Chainable { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + getBySel(value: string, ...args: any[]): Chainable; + getKibanaVersion(): Chainable; + } + } +} + +// eslint-disable-next-line @typescript-eslint/no-explicit-any +function getBySel(selector: string, ...args: any[]) { + return cy.get(`[data-test-subj="${selector}"]`, ...args); +} + +Cypress.Commands.add('getBySel', getBySel); + +Cypress.on('uncaught:exception', () => { + return false; +}); diff --git a/x-pack/plugins/enterprise_search/cypress/tasks/login.ts b/x-pack/plugins/enterprise_search/cypress/tasks/login.ts new file mode 100644 index 0000000000000..bf4ad9b836def --- /dev/null +++ b/x-pack/plugins/enterprise_search/cypress/tasks/login.ts @@ -0,0 +1,45 @@ +/* + * 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 ELASTICSEARCH_USERNAME = 'elastic'; +const ELASTICSEARCH_PASSWORD = 'changeme'; +const LOGIN_API_ENDPOINT = '/internal/security/login'; + +export const constructUrlWithUser = (route: string) => { + const url = Cypress.config().baseUrl; + const kibana = new URL(String(url)); + const hostname = kibana.hostname; + const username = ELASTICSEARCH_USERNAME; + const password = ELASTICSEARCH_PASSWORD; + const protocol = kibana.protocol.replace(':', ''); + const port = kibana.port; + + const path = `${route.startsWith('/') ? '' : '/'}${route}`; + const strUrl = `${protocol}://${username}:${password}@${hostname}:${port}${path}`; + const builtUrl = new URL(strUrl); + + cy.log(`origin: ${builtUrl.href}`); + return builtUrl.href; +}; + +export const login = () => { + cy.session('user', () => { + cy.request({ + body: { + currentURL: '/', + params: { + password: ELASTICSEARCH_PASSWORD, + username: ELASTICSEARCH_USERNAME, + }, + providerName: 'basic', + providerType: 'basic', + }, + headers: { 'kbn-xsrf': 'cypress-creds-via-config' }, + method: 'POST', + url: constructUrlWithUser(LOGIN_API_ENDPOINT), + }); + }); +}; diff --git a/x-pack/plugins/enterprise_search/cypress/tsconfig.json b/x-pack/plugins/enterprise_search/cypress/tsconfig.json new file mode 100644 index 0000000000000..06640dca03363 --- /dev/null +++ b/x-pack/plugins/enterprise_search/cypress/tsconfig.json @@ -0,0 +1,20 @@ +{ + "extends": "../../../../tsconfig.base.json", + "include": ["**/*", "../cypress.config.ts"], + "exclude": ["target/**/*"], + "compilerOptions": { + "outDir": "target/types", + "types": ["cypress", "node", "cypress-react-selector"], + "resolveJsonModule": true + }, + "kbn_references": [ + "@kbn/cypress-config", + // cypress projects that are nested inside of other ts project use code + // from the parent ts project in ways that can't be automatically deteceted + // at this time so we have to force the inclusion of this reference + { + "path": "../tsconfig.json", + "force": true + } + ] +} diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/engine/add_indices_flyout.tsx b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/engine/add_indices_flyout.tsx index 22ab72ba3a405..7d504c65bb7ac 100644 --- a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/engine/add_indices_flyout.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/engine/add_indices_flyout.tsx @@ -45,10 +45,13 @@ export const AddIndicesFlyout: React.FC = ({ onClose }) = const { selectedIndices, updateEngineStatus, updateEngineError } = useValues(AddIndicesLogic); const { setSelectedIndices, submitSelectedIndices } = useActions(AddIndicesLogic); - const selectedOptions = useMemo(() => selectedIndices.map(indexToOption), [selectedIndices]); + const selectedOptions = useMemo( + () => selectedIndices.map((index) => indexToOption(index)), + [selectedIndices] + ); const onIndicesChange = useCallback( (options: IndicesSelectComboBoxOption[]) => { - setSelectedIndices(options.map(({ value }) => value).filter(isNotNullish)); + setSelectedIndices(options.map(({ label }) => label).filter(isNotNullish)); }, [setSelectedIndices] ); diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/engine/add_indices_logic.test.ts b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/engine/add_indices_logic.test.ts index a60c6e9df756a..7b3e61940f001 100644 --- a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/engine/add_indices_logic.test.ts +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/engine/add_indices_logic.test.ts @@ -8,7 +8,6 @@ import { LogicMounter } from '../../../__mocks__/kea_logic'; import { Status } from '../../../../../common/types/api'; -import { ElasticsearchIndexWithIngestion } from '../../../../../common/types/indices'; import { AddIndicesLogic, AddIndicesLogicValues } from './add_indices_logic'; @@ -18,16 +17,6 @@ const DEFAULT_VALUES: AddIndicesLogicValues = { updateEngineStatus: Status.IDLE, }; -const makeIndexData = (name: string): ElasticsearchIndexWithIngestion => ({ - count: 0, - hidden: false, - name, - total: { - docs: { count: 0, deleted: 0 }, - store: { size_in_bytes: 'n/a' }, - }, -}); - describe('AddIndicesLogic', () => { const { mount: mountAddIndicesLogic } = new LogicMounter(AddIndicesLogic); const { mount: mountEngineIndicesLogic } = new LogicMounter(AddIndicesLogic); @@ -47,31 +36,16 @@ describe('AddIndicesLogic', () => { describe('actions', () => { describe('setSelectedIndices', () => { it('adds the indices to selectedIndices', () => { - AddIndicesLogic.actions.setSelectedIndices([ - makeIndexData('index-001'), - makeIndexData('index-002'), - ]); + AddIndicesLogic.actions.setSelectedIndices(['index-001', 'index-002']); - expect(AddIndicesLogic.values.selectedIndices).toEqual([ - makeIndexData('index-001'), - makeIndexData('index-002'), - ]); + expect(AddIndicesLogic.values.selectedIndices).toEqual(['index-001', 'index-002']); }); it('replaces any existing indices', () => { - AddIndicesLogic.actions.setSelectedIndices([ - makeIndexData('index-001'), - makeIndexData('index-002'), - ]); - AddIndicesLogic.actions.setSelectedIndices([ - makeIndexData('index-003'), - makeIndexData('index-004'), - ]); + AddIndicesLogic.actions.setSelectedIndices(['index-001', 'index-002']); + AddIndicesLogic.actions.setSelectedIndices(['index-003', 'index-004']); - expect(AddIndicesLogic.values.selectedIndices).toEqual([ - makeIndexData('index-003'), - makeIndexData('index-004'), - ]); + expect(AddIndicesLogic.values.selectedIndices).toEqual(['index-003', 'index-004']); }); }); }); @@ -103,10 +77,7 @@ describe('AddIndicesLogic', () => { it('calls addIndicesToEngine when there are selectedIndices', () => { jest.spyOn(AddIndicesLogic.actions, 'addIndicesToEngine'); - AddIndicesLogic.actions.setSelectedIndices([ - makeIndexData('index-001'), - makeIndexData('index-002'), - ]); + AddIndicesLogic.actions.setSelectedIndices(['index-001', 'index-002']); AddIndicesLogic.actions.submitSelectedIndices(); expect(AddIndicesLogic.actions.addIndicesToEngine).toHaveBeenCalledTimes(1); diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/engine/add_indices_logic.ts b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/engine/add_indices_logic.ts index 37e5cf43ebd00..add950937b30a 100644 --- a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/engine/add_indices_logic.ts +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/engine/add_indices_logic.ts @@ -7,8 +7,6 @@ import { kea, MakeLogicType } from 'kea'; -import { ElasticsearchIndexWithIngestion } from '../../../../../common/types/indices'; - import { UpdateEngineApiLogic } from '../../api/engines/update_engine_api_logic'; import { EngineIndicesLogic, EngineIndicesLogicActions } from './engine_indices_logic'; @@ -17,21 +15,21 @@ export interface AddIndicesLogicActions { addIndicesToEngine: EngineIndicesLogicActions['addIndicesToEngine']; closeAddIndicesFlyout: EngineIndicesLogicActions['closeAddIndicesFlyout']; engineUpdated: EngineIndicesLogicActions['engineUpdated']; - setSelectedIndices: (indices: ElasticsearchIndexWithIngestion[]) => { - indices: ElasticsearchIndexWithIngestion[]; + setSelectedIndices: (indices: string[]) => { + indices: string[]; }; submitSelectedIndices: () => void; } export interface AddIndicesLogicValues { - selectedIndices: ElasticsearchIndexWithIngestion[]; + selectedIndices: string[]; updateEngineError: typeof UpdateEngineApiLogic.values.error | undefined; updateEngineStatus: typeof UpdateEngineApiLogic.values.status; } export const AddIndicesLogic = kea>({ actions: { - setSelectedIndices: (indices: ElasticsearchIndexWithIngestion[]) => ({ indices }), + setSelectedIndices: (indices: string[]) => ({ indices }), submitSelectedIndices: () => true, }, connect: { @@ -46,7 +44,7 @@ export const AddIndicesLogic = kea name)); + actions.addIndicesToEngine(selectedIndices); }, }), path: ['enterprise_search', 'content', 'add_indices_logic'], diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/engine/engine_connect/engine_api_integration.tsx b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/engine/engine_connect/engine_api_integration.tsx index 778d69a5ed56b..b61614838d7a1 100644 --- a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/engine/engine_connect/engine_api_integration.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/engine/engine_connect/engine_api_integration.tsx @@ -73,7 +73,7 @@ export const EngineApiIntegrationStage: React.FC = () => {

{i18n.translate('xpack.enterpriseSearch.content.engine.api.step3.intro', { defaultMessage: - 'Learn how to integrate with your engine with the language clients maintained by Elastic to help build your search experience.', + 'Learn how to integrate with your search application with the language clients maintained by Elastic to help build your search experience.', })}

diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/engine/engine_connect/generate_engine_api_key_modal/generate_engine_api_key_modal.tsx b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/engine/engine_connect/generate_engine_api_key_modal/generate_engine_api_key_modal.tsx index c0cbcea37e93a..b4d03456ff5ec 100644 --- a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/engine/engine_connect/generate_engine_api_key_modal/generate_engine_api_key_modal.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/engine/engine_connect/generate_engine_api_key_modal/generate_engine_api_key_modal.tsx @@ -59,7 +59,7 @@ export const GenerateEngineApiKeyModal: React.FC {i18n.translate( 'xpack.enterpriseSearch.content.engine.api.generateEngineApiKeyModal.title', { - defaultMessage: 'Create Engine read-only API Key', + defaultMessage: 'Create Search application read-only API Key', } )} diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/engine/engine_connect/search_application_api.tsx b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/engine/engine_connect/search_application_api.tsx index c51654f0c557d..9d3c27895657f 100644 --- a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/engine/engine_connect/search_application_api.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/engine/engine_connect/search_application_api.tsx @@ -43,26 +43,32 @@ export const SearchApplicationAPI = () => { const steps = [ { - title: i18n.translate('xpack.enterpriseSearch.content.engine.api.step1.title', { + title: i18n.translate('xpack.enterpriseSearch.content.searchApplication.api.step1.title', { defaultMessage: 'Generate and save API key', }), children: ( <>

- {i18n.translate('xpack.enterpriseSearch.content.engine.api.step1.apiKeyWarning', { - defaultMessage: - "Elastic does not store API keys. Once generated, you'll only be able to view the key one time. Make sure you save it somewhere secure. If you lose access to it you'll need to generate a new API key from this screen.", - })}{' '} + {i18n.translate( + 'xpack.enterpriseSearch.content.searchApplication.api.step1.apiKeyWarning', + { + defaultMessage: + "Elastic does not store API keys. Once generated, you'll only be able to view the key one time. Make sure you save it somewhere secure. If you lose access to it you'll need to generate a new API key from this screen.", + } + )}{' '} - {i18n.translate('xpack.enterpriseSearch.content.engine.api.step1.learnMoreLink', { - defaultMessage: 'Learn more about API keys.', - })} + {i18n.translate( + 'xpack.enterpriseSearch.content.searchApplication.api.step1.learnMoreLink', + { + defaultMessage: 'Learn more about API keys.', + } + )}

@@ -73,10 +79,10 @@ export const SearchApplicationAPI = () => { iconSide="left" iconType="plusInCircleFilled" onClick={openGenerateModal} - data-telemetry-id="entSearchContent-engines-api-step1-createApiKeyButton" + data-telemetry-id="entSearchContent-searchApplications-api-step1-createApiKeyButton" > {i18n.translate( - 'xpack.enterpriseSearch.content.engine.api.step1.createAPIKeyButton', + 'xpack.enterpriseSearch.content.searchApplication.api.step1.createAPIKeyButton', { defaultMessage: 'Create API Key', } @@ -87,16 +93,19 @@ export const SearchApplicationAPI = () => { KibanaLogic.values.navigateToUrl('/app/management/security/api_keys', { shouldNotCreateHref: true, }) } > - {i18n.translate('xpack.enterpriseSearch.content.engine.api.step1.viewKeysButton', { - defaultMessage: 'View Keys', - })} + {i18n.translate( + 'xpack.enterpriseSearch.content.searchApplication.api.step1.viewKeysButton', + { + defaultMessage: 'View Keys', + } + )}
@@ -104,17 +113,17 @@ export const SearchApplicationAPI = () => { ), }, { - title: i18n.translate('xpack.enterpriseSearch.content.engine.api.step2.title', { - defaultMessage: "Copy your engine's endpoint", + title: i18n.translate('xpack.enterpriseSearch.content.searchApplication.api.step2.title', { + defaultMessage: "Copy your search application's endpoint", }), children: ( <>

{i18n.translate( - 'xpack.enterpriseSearch.content.engine.api.step2.copyEndpointDescription', + 'xpack.enterpriseSearch.content.searchApplication.api.step2.copyEndpointDescription', { - defaultMessage: "Use this URL to access your engine's API endpoints.", + defaultMessage: "Use this URL to access your search application's API endpoints.", } )}

@@ -131,22 +140,22 @@ export const SearchApplicationAPI = () => { ), }, { - title: i18n.translate('xpack.enterpriseSearch.content.engine.api.step3.title', { + title: i18n.translate('xpack.enterpriseSearch.content.searchApplication.api.step3.title', { defaultMessage: 'Learn how to call your endpoints', }), children: , }, { - title: i18n.translate('xpack.enterpriseSearch.content.engine.api.step4.title', { + title: i18n.translate('xpack.enterpriseSearch.content.searchApplication.api.step4.title', { defaultMessage: '(Optional) Power up your analytics', }), children: ( <>

- {i18n.translate('xpack.enterpriseSearch.content.engine.api.step4.copy', { + {i18n.translate('xpack.enterpriseSearch.content.searchApplication.api.step4.copy', { defaultMessage: - 'Your engine provides basic analytics data as part of this installation. To receive more granular and custom metrics, integrate our Behavioral Analytics script on your platform.', + 'Your search application provides basic analytics data as part of this installation. To receive more granular and custom metrics, integrate our Behavioral Analytics script on your platform.', })}

@@ -154,7 +163,7 @@ export const SearchApplicationAPI = () => { navigateToUrl( generateEncodedPath(`${ANALYTICS_PLUGIN.URL}${COLLECTION_INTEGRATE_PATH}`, { @@ -166,9 +175,12 @@ export const SearchApplicationAPI = () => { iconSide="left" iconType="popout" > - {i18n.translate('xpack.enterpriseSearch.content.engine.api.step4.learnHowLink', { - defaultMessage: 'Learn how', - })} + {i18n.translate( + 'xpack.enterpriseSearch.content.searchApplication.api.step4.learnHowLink', + { + defaultMessage: 'Learn how', + } + )} diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/engine/engine_error.test.tsx b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/engine/engine_error.test.tsx index 9c71c401fbaf0..0f47d2437e3dc 100644 --- a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/engine/engine_error.test.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/engine/engine_error.test.tsx @@ -40,7 +40,7 @@ describe('EngineError', () => { const notFound = wrapper.find(NotFoundPrompt); expect(notFound.prop('backToLink')).toEqual('/engines'); - expect(notFound.prop('backToContent')).toEqual('Back to Engines'); + expect(notFound.prop('backToContent')).toEqual('Back to Search Applications'); const telemetry = wrapper.find(SendEnterpriseSearchTelemetry); expect(telemetry.prop('action')).toEqual('error'); diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/engine/engine_error.tsx b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/engine/engine_error.tsx index b8be4c7652e1b..d36ff3e14e7a7 100644 --- a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/engine/engine_error.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/engine/engine_error.tsx @@ -27,9 +27,12 @@ export const EngineError: React.FC<{ error?: HttpError; notFound?: boolean }> = <> 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 116430e4d0017..4bcd3fcf4f215 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 @@ -64,7 +64,7 @@ export const EngineIndices: React.FC = () => { description: i18n.translate( 'xpack.enterpriseSearch.content.engine.indices.actions.removeIndex.title', { - defaultMessage: 'Remove this index from engine', + defaultMessage: 'Remove this index from search application', } ), icon: 'minusInCircle', @@ -215,7 +215,7 @@ export const EngineIndices: React.FC = () => { 'xpack.enterpriseSearch.content.engine.indices.unknownIndicesCallout.description', { defaultMessage: - 'Some data might be unreachable from this engine. Check for any pending operations or errors on affected indices, or remove those that should no longer be used by this engine.', + 'Some data might be unreachable from this search application. Check for any pending operations or errors on affected indices, or remove those that should no longer be used by this search application.', } )}

@@ -259,7 +259,7 @@ export const EngineIndices: React.FC = () => { }} title={i18n.translate( 'xpack.enterpriseSearch.content.engine.indices.removeIndexConfirm.title', - { defaultMessage: 'Remove this index from the engine' } + { defaultMessage: 'Remove this index from the Search Application' } )} buttonColor="danger" cancelButtonText={CANCEL_BUTTON_LABEL} @@ -278,7 +278,7 @@ export const EngineIndices: React.FC = () => { 'xpack.enterpriseSearch.content.engine.indices.removeIndexConfirm.description', { defaultMessage: - "This won't delete the index. You may add it back to this engine at a later time.", + "This won't delete the index. You may add it back to this search application at a later time.", } )}

diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/engine/engine_view_header_actions.tsx b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/engine/engine_view_header_actions.tsx deleted file mode 100644 index bd48ee8d6d294..0000000000000 --- a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/engine/engine_view_header_actions.tsx +++ /dev/null @@ -1,87 +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, { useState } from 'react'; - -import { useValues, useActions } from 'kea'; - -import { EuiPopover, EuiButtonIcon, EuiText, EuiContextMenu, EuiIcon } from '@elastic/eui'; - -import { i18n } from '@kbn/i18n'; - -import { TelemetryLogic } from '../../../shared/telemetry/telemetry_logic'; - -import { EngineViewLogic } from './engine_view_logic'; - -export const EngineViewHeaderActions: React.FC = () => { - const { engineData } = useValues(EngineViewLogic); - - const { openDeleteEngineModal } = useActions(EngineViewLogic); - const { sendEnterpriseSearchTelemetry } = useActions(TelemetryLogic); - - const [isActionsPopoverOpen, setIsActionsPopoverOpen] = useState(false); - const toggleActionsPopover = () => setIsActionsPopoverOpen((isPopoverOpen) => !isPopoverOpen); - const closePopover = () => setIsActionsPopoverOpen(false); - return ( - <> - - } - isOpen={isActionsPopoverOpen} - panelPaddingSize="xs" - closePopover={closePopover} - display="block" - > - , - name: ( - - {i18n.translate( - 'xpack.enterpriseSearch.content.engine.headerActions.delete', - { defaultMessage: 'Delete this engine' } - )} - - ), - onClick: () => { - if (engineData) { - openDeleteEngineModal(); - sendEnterpriseSearchTelemetry({ - action: 'clicked', - metric: 'entSearchContent-engines-engineView-deleteEngine', - }); - } - }, - size: 's', - }, - ], - }, - ]} - /> - - - ); -}; diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/engine/header_docs_action.tsx b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/engine/header_docs_action.tsx index e20fbc81689a4..4e925b50c63b2 100644 --- a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/engine/header_docs_action.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/engine/header_docs_action.tsx @@ -21,7 +21,7 @@ export const EngineHeaderDocsAction: React.FC = () => ( target="_blank" iconType="documents" > - Engine Docs + Search Application Docs diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/engines/components/indices_select_combobox.tsx b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/engines/components/indices_select_combobox.tsx index 5e6f7cebd89fc..200192d1e20a4 100644 --- a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/engines/components/indices_select_combobox.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/engines/components/indices_select_combobox.tsx @@ -47,7 +47,7 @@ export const IndicesSelectComboBox = (props: IndicesSelectComboBoxProps) => { }, [searchQuery]); const options: Array> = - data?.indices?.map(indexToOption) ?? []; + data?.indices?.map((index) => indexToOption(index.name, index)) ?? []; const renderOption = (option: EuiComboBoxOptionOption) => ( @@ -84,8 +84,9 @@ export const IndicesSelectComboBox = (props: IndicesSelectComboBoxProps) => { }; export const indexToOption = ( - index: ElasticsearchIndexWithIngestion + indexName: string, + index?: ElasticsearchIndexWithIngestion ): IndicesSelectComboBoxOption => ({ - label: index.name, + label: indexName, value: index, }); diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/engines/components/tables/engines_table.tsx b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/engines/components/tables/engines_table.tsx index f07516313f6e8..de291752639fa 100644 --- a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/engines/components/tables/engines_table.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/engines/components/tables/engines_table.tsx @@ -56,7 +56,7 @@ export const EnginesListTable: React.FC = ({ { field: 'name', name: i18n.translate('xpack.enterpriseSearch.content.enginesList.table.column.name', { - defaultMessage: 'Engine Name', + defaultMessage: 'Search Application Name', }), mobileOptions: { header: true, @@ -117,7 +117,7 @@ export const EnginesListTable: React.FC = ({ description: i18n.translate( 'xpack.enterpriseSearch.content.enginesList.table.column.actions.view.buttonDescription', { - defaultMessage: 'View this engine', + defaultMessage: 'View this search application', } ), type: 'icon', @@ -134,7 +134,7 @@ export const EnginesListTable: React.FC = ({ description: i18n.translate( 'xpack.enterpriseSearch.content.enginesList.table.column.action.delete.buttonDescription', { - defaultMessage: 'Delete this engine', + defaultMessage: 'Delete this search application', } ), type: 'icon', @@ -144,7 +144,7 @@ export const EnginesListTable: React.FC = ({ i18n.translate( 'xpack.enterpriseSearch.content.engineList.table.column.actions.deleteEngineLabel', { - defaultMessage: 'Delete this engine', + defaultMessage: 'Delete this search application', } ), onClick: (engine) => { diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/engines/create_engine_flyout.tsx b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/engines/create_engine_flyout.tsx index ae7f7423be7ce..2df12a2b9bd8f 100644 --- a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/engines/create_engine_flyout.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/engines/create_engine_flyout.tsx @@ -5,13 +5,13 @@ * 2.0. */ -import React from 'react'; +import React, { useEffect } from 'react'; + +import { useLocation } from 'react-router-dom'; import { useActions, useValues } from 'kea'; import { - EuiButton, - EuiButtonEmpty, EuiFlexGroup, EuiFlexItem, EuiFieldText, @@ -26,6 +26,8 @@ import { EuiTitle, EuiComboBoxOptionOption, EuiCallOut, + EuiButton, + EuiButtonEmpty, } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; @@ -33,14 +35,14 @@ import { FormattedMessage } from '@kbn/i18n-react'; import { Status } from '../../../../../common/types/api'; import { ElasticsearchIndexWithIngestion } from '../../../../../common/types/indices'; -import { isNotNullish } from '../../../../../common/utils/is_not_nullish'; -import { CANCEL_BUTTON_LABEL } from '../../../shared/constants'; +import { CANCEL_BUTTON_LABEL, ESINDEX_QUERY_PARAMETER } from '../../../shared/constants'; import { docLinks } from '../../../shared/doc_links'; import { getErrorsFromHttpResponse } from '../../../shared/flash_messages/handle_api_errors'; -import { indexToOption, IndicesSelectComboBox } from './components/indices_select_combobox'; +import { parseQueryParams } from '../../../shared/query_params'; +import { indexToOption, IndicesSelectComboBox } from './components/indices_select_combobox'; import { CreateEngineLogic } from './create_engine_logic'; export interface CreateEngineFlyoutProps { @@ -60,11 +62,18 @@ export const CreateEngineFlyout = ({ onClose }: CreateEngineFlyoutProps) => { selectedIndices, } = useValues(CreateEngineLogic); + const { search } = useLocation() as unknown as Location; + const { ...params } = parseQueryParams(search); + const indexName = params[ESINDEX_QUERY_PARAMETER]; + const onIndicesChange = ( selectedOptions: Array> ) => { - setSelectedIndices(selectedOptions.map((option) => option.value).filter(isNotNullish)); + setSelectedIndices(selectedOptions.map((option) => option.label)); }; + useEffect(() => { + if (indexName && typeof indexName === 'string') setSelectedIndices([indexName]); + }, []); return ( @@ -72,7 +81,7 @@ export const CreateEngineFlyout = ({ onClose }: CreateEngineFlyoutProps) => {

{i18n.translate('xpack.enterpriseSearch.content.engines.createEngine.headerTitle', { - defaultMessage: 'Create an engine', + defaultMessage: 'Create a Search Application', })}

@@ -81,7 +90,7 @@ export const CreateEngineFlyout = ({ onClose }: CreateEngineFlyoutProps) => {

{ > {i18n.translate( 'xpack.enterpriseSearch.content.engines.createEngine.header.docsLink', - { defaultMessage: 'Engines documentation' } + { defaultMessage: 'Search Application documentation' } )} ), @@ -107,7 +116,7 @@ export const CreateEngineFlyout = ({ onClose }: CreateEngineFlyoutProps) => { color="danger" title={i18n.translate( 'xpack.enterpriseSearch.content.engines.createEngine.header.createError.title', - { defaultMessage: 'Error creating engine' } + { defaultMessage: 'Error creating search application' } )} > {getErrorsFromHttpResponse(createEngineError).map((errMessage, i) => ( @@ -126,7 +135,7 @@ export const CreateEngineFlyout = ({ onClose }: CreateEngineFlyoutProps) => { fullWidth isDisabled={formDisabled} onChange={onIndicesChange} - selectedOptions={selectedIndices.map(indexToOption)} + selectedOptions={selectedIndices.map((index: string) => indexToOption(index))} /> ), status: indicesStatus, @@ -142,7 +151,7 @@ export const CreateEngineFlyout = ({ onClose }: CreateEngineFlyoutProps) => { disabled={formDisabled} placeholder={i18n.translate( 'xpack.enterpriseSearch.content.engines.createEngine.nameEngine.placeholder', - { defaultMessage: 'Engine name' } + { defaultMessage: 'Search Application name' } )} value={engineName} onChange={(e) => setEngineName(e.target.value)} @@ -151,7 +160,7 @@ export const CreateEngineFlyout = ({ onClose }: CreateEngineFlyoutProps) => { status: engineNameStatus, title: i18n.translate( 'xpack.enterpriseSearch.content.engines.createEngine.nameEngine.title', - { defaultMessage: 'Name your engine' } + { defaultMessage: 'Name your Search Application' } ), }, ]} @@ -171,7 +180,7 @@ export const CreateEngineFlyout = ({ onClose }: CreateEngineFlyoutProps) => { { }} > {i18n.translate('xpack.enterpriseSearch.content.engines.createEngine.submit', { - defaultMessage: 'Create this engine', + defaultMessage: 'Create this Search Application', })} diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/engines/create_engine_logic.test.ts b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/engines/create_engine_logic.test.ts index 0d88ca44ff87d..93bdda9c4a2d0 100644 --- a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/engines/create_engine_logic.test.ts +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/engines/create_engine_logic.test.ts @@ -8,10 +8,12 @@ import { LogicMounter } from '../../../__mocks__/kea_logic'; import { Status } from '../../../../../common/types/api'; -import { ElasticsearchIndexWithIngestion } from '../../../../../common/types/indices'; +import { KibanaLogic } from '../../../shared/kibana'; import { CreateEngineApiLogic } from '../../api/engines/create_engine_api_logic'; +import { ENGINES_PATH } from '../../routes'; + import { CreateEngineLogic, CreateEngineLogicValues } from './create_engine_logic'; const DEFAULT_VALUES: CreateEngineLogicValues = { @@ -27,7 +29,7 @@ const DEFAULT_VALUES: CreateEngineLogicValues = { const VALID_ENGINE_NAME = 'unit-test-001'; const INVALID_ENGINE_NAME = 'TEST'; -const VALID_INDICES_DATA = [{ name: 'search-index-01' }] as ElasticsearchIndexWithIngestion[]; +const VALID_INDICES_DATA = ['search-index-01']; describe('CreateEngineLogic', () => { const { mount: apiLogicMount } = new LogicMounter(CreateEngineApiLogic); @@ -59,16 +61,17 @@ describe('CreateEngineLogic', () => { indices: ['search-index-01'], }); }); - it('engineCreated is handled', () => { + it('engineCreated is handled and is navigated to Search application list page', () => { jest.spyOn(CreateEngineLogic.actions, 'fetchEngines'); - jest.spyOn(CreateEngineLogic.actions, 'closeEngineCreate'); - + jest + .spyOn(KibanaLogic.values, 'navigateToUrl') + .mockImplementationOnce(() => Promise.resolve()); CreateEngineApiLogic.actions.apiSuccess({ result: 'created', }); + expect(KibanaLogic.values.navigateToUrl).toHaveBeenCalledWith(ENGINES_PATH); expect(CreateEngineLogic.actions.fetchEngines).toHaveBeenCalledTimes(1); - expect(CreateEngineLogic.actions.closeEngineCreate).toHaveBeenCalledTimes(1); }); }); describe('selectors', () => { @@ -114,7 +117,7 @@ describe('CreateEngineLogic', () => { it('returns true while create request in progress', () => { CreateEngineApiLogic.actions.makeRequest({ engineName: VALID_ENGINE_NAME, - indices: [VALID_INDICES_DATA[0].name], + indices: [VALID_INDICES_DATA[0]], }); expect(CreateEngineLogic.values.formDisabled).toEqual(true); diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/engines/create_engine_logic.ts b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/engines/create_engine_logic.ts index 266dab05c5441..c939dda096446 100644 --- a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/engines/create_engine_logic.ts +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/engines/create_engine_logic.ts @@ -8,27 +8,27 @@ import { kea, MakeLogicType } from 'kea'; import { Status } from '../../../../../common/types/api'; -import { ElasticsearchIndexWithIngestion } from '../../../../../common/types/indices'; +import { KibanaLogic } from '../../../shared/kibana'; import { CreateEngineApiLogic, CreateEngineApiLogicActions, } from '../../api/engines/create_engine_api_logic'; +import { ENGINES_PATH } from '../../routes'; import { EnginesListLogic } from './engines_list_logic'; const NAME_VALIDATION = new RegExp(/^[a-z0-9\-]+$/); export interface CreateEngineLogicActions { - closeEngineCreate: () => void; createEngine: () => void; createEngineRequest: CreateEngineApiLogicActions['makeRequest']; engineCreateError: CreateEngineApiLogicActions['apiError']; engineCreated: CreateEngineApiLogicActions['apiSuccess']; fetchEngines: () => void; setEngineName: (engineName: string) => { engineName: string }; - setSelectedIndices: (indices: ElasticsearchIndexWithIngestion[]) => { - indices: ElasticsearchIndexWithIngestion[]; + setSelectedIndices: (indices: string[]) => { + indices: string[]; }; } @@ -40,7 +40,7 @@ export interface CreateEngineLogicValues { engineNameStatus: 'complete' | 'incomplete' | 'warning'; formDisabled: boolean; indicesStatus: 'complete' | 'incomplete'; - selectedIndices: ElasticsearchIndexWithIngestion[]; + selectedIndices: string[]; } export const CreateEngineLogic = kea< @@ -49,12 +49,12 @@ export const CreateEngineLogic = kea< actions: { createEngine: true, setEngineName: (engineName: string) => ({ engineName }), - setSelectedIndices: (indices: ElasticsearchIndexWithIngestion[]) => ({ indices }), + setSelectedIndices: (indices: string[]) => ({ indices }), }, connect: { actions: [ EnginesListLogic, - ['closeEngineCreate', 'fetchEngines'], + ['fetchEngines'], CreateEngineApiLogic, [ 'makeRequest as createEngineRequest', @@ -68,12 +68,12 @@ export const CreateEngineLogic = kea< createEngine: () => { actions.createEngineRequest({ engineName: values.engineName, - indices: values.selectedIndices.map((index) => index.name), + indices: values.selectedIndices, }); }, engineCreated: () => { actions.fetchEngines(); - actions.closeEngineCreate(); + KibanaLogic.values.navigateToUrl(ENGINES_PATH); }, }), path: ['enterprise_search', 'content', 'create_engine_logic'], diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/engines/delete_engine_modal.tsx b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/engines/delete_engine_modal.tsx index 4203969bb74a2..46d7c8586ed80 100644 --- a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/engines/delete_engine_modal.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/engines/delete_engine_modal.tsx @@ -28,7 +28,7 @@ export const DeleteEngineModal: React.FC = ({ engineName return ( { @@ -42,7 +42,7 @@ export const DeleteEngineModal: React.FC = ({ engineName confirmButtonText={i18n.translate( 'xpack.enterpriseSearch.content.engineList.deleteEngineModal.confirmButton.title', { - defaultMessage: 'Yes, delete this engine ', + defaultMessage: 'Yes, delete this search application', } )} buttonColor="danger" @@ -53,7 +53,7 @@ export const DeleteEngineModal: React.FC = ({ engineName 'xpack.enterpriseSearch.content.engineList.deleteEngineModal.delete.description', { defaultMessage: - 'Deleting your engine is not a reversible action. Your indices will not be affected. ', + 'Deleting your search application is not a reversible action. Your indices will not be affected. ', } )}

diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/engines/engines_list.tsx b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/engines/engines_list.tsx index 7617467bba64f..48dce9f524164 100644 --- a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/engines/engines_list.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/engines/engines_list.tsx @@ -31,6 +31,7 @@ import { docLinks } from '../../../shared/doc_links'; import { KibanaLogic } from '../../../shared/kibana'; import { LicensingLogic } from '../../../shared/licensing'; import { EXPLORE_PLATINUM_FEATURES_LINK } from '../../../workplace_search/constants'; +import { ENGINES_PATH, ENGINE_CREATION_PATH } from '../../routes'; import { EnterpriseSearchEnginesPageTemplate } from '../layout/engines_page_template'; import { LicensingCallout, LICENSING_FEATURE } from '../shared/licensing_callout/licensing_callout'; @@ -43,9 +44,12 @@ import { EngineListIndicesFlyout } from './engines_list_flyout'; import { EnginesListFlyoutLogic } from './engines_list_flyout_logic'; import { EnginesListLogic } from './engines_list_logic'; -export const CreateEngineButton: React.FC<{ disabled: boolean }> = ({ disabled }) => { +interface CreateEngineButtonProps { + disabled: boolean; +} +export const CreateEngineButton: React.FC = ({ disabled }) => { const [showPopover, setShowPopover] = useState(false); - const { openEngineCreate } = useActions(EnginesListLogic); + return ( = ({ disabled } iconType="plusInCircle" data-test-subj="enterprise-search-content-engines-creation-button" data-telemetry-id="entSearchContent-engines-list-createEngine" - disabled={disabled} - onClick={openEngineCreate} + isDisabled={disabled} + onClick={() => KibanaLogic.values.navigateToUrl(ENGINE_CREATION_PATH)} > - {i18n.translate('xpack.enterpriseSearch.content.engines.createEngineButtonLabel', { - defaultMessage: 'Create Search Application', - })} + {i18n.translate( + 'xpack.enterpriseSearch.content.searchApplications.createEngineButtonLabel', + { + defaultMessage: 'Create Search Application', + } + )} } > @@ -82,7 +89,7 @@ export const CreateEngineButton: React.FC<{ disabled: boolean }> = ({ disabled } @@ -94,27 +101,27 @@ export const CreateEngineButton: React.FC<{ disabled: boolean }> = ({ disabled } ); }; +interface ListProps { + createEngineFlyoutOpen?: boolean; +} -export const EnginesList: React.FC = () => { +export const EnginesList: React.FC = ({ createEngineFlyoutOpen }) => { const { closeDeleteEngineModal, - closeEngineCreate, fetchEngines, onPaginate, openDeleteEngineModal, setSearchQuery, setIsFirstRequest, } = useActions(EnginesListLogic); - const { openFetchEngineFlyout } = useActions(EnginesListFlyoutLogic); - const { isCloud } = useValues(KibanaLogic); + const { isCloud, navigateToUrl } = useValues(KibanaLogic); const { hasPlatinumLicense } = useValues(LicensingLogic); const isGated = !isCloud && !hasPlatinumLicense; const { - createEngineFlyoutOpen, deleteModalEngineName, hasNoEngines, isDeleteModalVisible, @@ -127,7 +134,7 @@ export const EnginesList: React.FC = () => { const throttledSearchQuery = useThrottle(searchQuery, INPUT_THROTTLE_DELAY_MS); useEffect(() => { - // Don't fetch engines if we don't have a valid license + // Don't fetch search applications if we don't have a valid license if (!isGated) { fetchEngines(); } @@ -148,18 +155,18 @@ export const EnginesList: React.FC = () => { ) : null} - {createEngineFlyoutOpen && } + {createEngineFlyoutOpen && navigateToUrl(ENGINES_PATH)} />} { target="_blank" data-telemetry-id="entSearchContent-engines-documentation-viewDocumentaion" > - {i18n.translate('xpack.enterpriseSearch.content.engines.documentation', { - defaultMessage: 'explore our Search Applications documentation', - })} + {i18n.translate( + 'xpack.enterpriseSearch.content.searchApplications.documentation', + { + defaultMessage: 'explore our Search Applications documentation', + } + )} ), }} /> ), - pageTitle: i18n.translate('xpack.enterpriseSearch.content.engines.title', { + pageTitle: i18n.translate('xpack.enterpriseSearch.content.searchApplications.title', { defaultMessage: 'Search Applications', }), rightSideItems: isLoading @@ -185,7 +195,7 @@ export const EnginesList: React.FC = () => { ? [] : [], }} - pageViewTelemetry="Engines" + pageViewTelemetry="Search Applications" isLoading={isLoading && !isGated} > {isGated && ( @@ -200,15 +210,15 @@ export const EnginesList: React.FC = () => { { {i18n.translate( - 'xpack.enterpriseSearch.content.engines.searchPlaceholder.description', + 'xpack.enterpriseSearch.content.searchApplications.searchPlaceholder.description', { - defaultMessage: 'Locate an engine via name or by its included indices.', + defaultMessage: + 'Locate a search application via name or by its included indices.', } )} @@ -230,7 +241,7 @@ export const EnginesList: React.FC = () => { { }); }); }); - describe('openEngineCreate', () => { - it('set createEngineFlyoutOpen to true', () => { - EnginesListLogic.actions.openEngineCreate(); - expect(EnginesListLogic.values).toEqual({ - ...DEFAULT_VALUES, - createEngineFlyoutOpen: true, - }); - }); - }); - describe('closeEngineCreate', () => { - it('set createEngineFlyoutOpen to false', () => { - EnginesListLogic.actions.closeEngineCreate(); - expect(EnginesListLogic.values).toEqual({ - ...DEFAULT_VALUES, - createEngineFlyoutOpen: false, - }); - }); - }); + describe('setSearchQuery', () => { it('set setSearchQuery to search value', () => { EnginesListLogic.actions.setSearchQuery('my-search-query'); diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/engines/engines_list_logic.ts b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/engines/engines_list_logic.ts index 082747612698a..02ac44ae16c8d 100644 --- a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/engines/engines_list_logic.ts +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/engines/engines_list_logic.ts @@ -39,7 +39,7 @@ export type EnginesListActions = Pick< 'apiError' | 'apiSuccess' | 'makeRequest' > & { closeDeleteEngineModal(): void; - closeEngineCreate(): void; + deleteEngine: DeleteEnginesApiLogicActions['makeRequest']; deleteError: DeleteEnginesApiLogicActions['apiError']; deleteSuccess: DeleteEnginesApiLogicActions['apiSuccess']; @@ -50,13 +50,11 @@ export type EnginesListActions = Pick< openDeleteEngineModal: (engine: EnterpriseSearchEngine | EnterpriseSearchEngineDetails) => { engine: EnterpriseSearchEngine; }; - openEngineCreate(): void; setIsFirstRequest(): void; setSearchQuery(searchQuery: string): { searchQuery: string }; }; interface EngineListValues { - createEngineFlyoutOpen: boolean; data: typeof FetchEnginesAPILogic.values.data; deleteModalEngine: EnterpriseSearchEngine | null; deleteModalEngineName: string; @@ -76,11 +74,9 @@ interface EngineListValues { export const EnginesListLogic = kea>({ actions: { closeDeleteEngineModal: true, - closeEngineCreate: true, fetchEngines: true, onPaginate: (args: EuiBasicTableOnChange) => ({ pageNumber: args.page.index }), openDeleteEngineModal: (engine) => ({ engine }), - openEngineCreate: true, setIsFirstRequest: true, setSearchQuery: (searchQuery: string) => ({ searchQuery }), }, @@ -111,13 +107,6 @@ export const EnginesListLogic = kea ({ - createEngineFlyoutOpen: [ - false, - { - closeEngineCreate: () => false, - openEngineCreate: () => true, - }, - ], deleteModalEngine: [ null, { diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/engines/engines_router.tsx b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/engines/engines_router.tsx index 662e1d7c21417..bcd5e5ec3bcd2 100644 --- a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/engines/engines_router.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/engines/engines_router.tsx @@ -10,7 +10,7 @@ import { Switch } from 'react-router-dom'; import { Route } from '@kbn/shared-ux-router'; -import { ENGINES_PATH, ENGINE_PATH } from '../../routes'; +import { ENGINES_PATH, ENGINE_CREATION_PATH, ENGINE_PATH } from '../../routes'; import { EngineRouter } from '../engine/engine_router'; import { NotFound } from '../not_found'; @@ -23,6 +23,9 @@ export const EnginesRouter: React.FC = () => { + + + diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/new_index/new_index_created_toast.tsx b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/new_index/new_index_created_toast.tsx index f1d120dee4e46..ac5bb69706146 100644 --- a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/new_index/new_index_created_toast.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/new_index/new_index_created_toast.tsx @@ -5,37 +5,9 @@ * 2.0. */ -import React from 'react'; - -import { EuiFlexGroup, EuiFlexItem, EuiSpacer, EuiText } from '@elastic/eui'; - import { i18n } from '@kbn/i18n'; -import { APP_SEARCH_URL } from '../../../../../common/constants'; - import { flashSuccessToast } from '../../../shared/flash_messages'; -import { EuiButtonTo } from '../../../shared/react_router_helpers'; - -const SuccessToast = ( - <> - - {i18n.translate('xpack.enterpriseSearch.content.new_index.successToast.description', { - defaultMessage: - 'You can use App Search engines to build a search experience for your new Elasticsearch index.', - })} - - - - - - {i18n.translate('xpack.enterpriseSearch.content.new_index.successToast.button.label', { - defaultMessage: 'Create an engine', - })} - - - - -); export function flashIndexCreatedToast(): void { flashSuccessToast( @@ -44,7 +16,6 @@ export function flashIndexCreatedToast(): void { }), { iconType: 'cheer', - text: SuccessToast, } ); } diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/components/header_actions/create_engine_menu_item.tsx b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/components/header_actions/create_engine_menu_item.tsx index 61f090f8e20ad..059fa91c8ac0b 100644 --- a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/components/header_actions/create_engine_menu_item.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/components/header_actions/create_engine_menu_item.tsx @@ -11,11 +11,11 @@ import { EuiContextMenuItem, EuiText, EuiFlexGroup, EuiFlexItem } from '@elastic import { i18n } from '@kbn/i18n'; -import { APP_SEARCH_PLUGIN } from '../../../../../../../common/constants'; -import { ENGINE_CREATION_PATH } from '../../../../../app_search/routes'; +import { ENTERPRISE_SEARCH_CONTENT_PLUGIN } from '../../../../../../../common/constants'; import { ESINDEX_QUERY_PARAMETER } from '../../../../../shared/constants'; import { generateEncodedPath } from '../../../../../shared/encode_path_params'; import { KibanaLogic } from '../../../../../shared/kibana'; +import { ENGINE_CREATION_PATH } from '../../../../routes'; export interface CreateEngineMenuItemProps { indexName?: string; @@ -28,12 +28,15 @@ export const CreateEngineMenuItem: React.FC = ({ ingestionMethod, isHiddenIndex, }) => { - const engineCreationPath = !indexName - ? `${APP_SEARCH_PLUGIN.URL}${ENGINE_CREATION_PATH}` - : generateEncodedPath(`${APP_SEARCH_PLUGIN.URL}${ENGINE_CREATION_PATH}?:indexKey=:indexName`, { - indexKey: ESINDEX_QUERY_PARAMETER, - indexName, - }); + const searchApplicationCreationPath = !indexName + ? `${ENTERPRISE_SEARCH_CONTENT_PLUGIN.URL}${ENGINE_CREATION_PATH}` + : generateEncodedPath( + `${ENTERPRISE_SEARCH_CONTENT_PLUGIN.URL}${ENGINE_CREATION_PATH}?:indexKey=:indexName`, + { + indexKey: ESINDEX_QUERY_PARAMETER, + indexName, + } + ); return ( @@ -43,7 +46,7 @@ export const CreateEngineMenuItem: React.FC = ({ size="s" icon="plusInCircle" onClick={() => { - KibanaLogic.values.navigateToUrl(engineCreationPath, { + KibanaLogic.values.navigateToUrl(searchApplicationCreationPath, { shouldNotCreateHref: true, }); }} @@ -51,9 +54,12 @@ export const CreateEngineMenuItem: React.FC = ({ >

- {i18n.translate('xpack.enterpriseSearch.content.index.searchEngines.createEngine', { - defaultMessage: 'Create an App Search engine', - })} + {i18n.translate( + 'xpack.enterpriseSearch.content.index.searchApplication.createSearchApplication', + { + defaultMessage: 'Create a Search Application', + } + )}

diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/components/header_actions/search_engines_popover.tsx b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/components/header_actions/search_engines_popover.tsx index 47584d3021ada..611c63c276750 100644 --- a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/components/header_actions/search_engines_popover.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/components/header_actions/search_engines_popover.tsx @@ -20,9 +20,11 @@ import { import { i18n } from '@kbn/i18n'; -import { APP_SEARCH_PLUGIN } from '../../../../../../../common/constants'; +import { ENTERPRISE_SEARCH_CONTENT_PLUGIN } from '../../../../../../../common/constants'; import { KibanaLogic } from '../../../../../shared/kibana'; +import { ENGINES_PATH } from '../../../../routes'; + import { CreateEngineMenuItem } from './create_engine_menu_item'; import { SearchEnginesPopoverLogic } from './search_engines_popover_logic'; @@ -52,7 +54,7 @@ export const SearchEnginesPopover: React.FC = ({ onClick={toggleSearchEnginesPopover} > {i18n.translate('xpack.enterpriseSearch.content.index.searchEngines.label', { - defaultMessage: 'Search engines', + defaultMessage: 'Search Applications', })} } @@ -64,15 +66,18 @@ export const SearchEnginesPopover: React.FC = ({ data-telemetry-id={`entSearchContent-${ingestionMethod}-header-searchEngines-viewEngines`} icon="eye" onClick={() => { - KibanaLogic.values.navigateToUrl(APP_SEARCH_PLUGIN.URL, { - shouldNotCreateHref: true, - }); + KibanaLogic.values.navigateToUrl( + ENTERPRISE_SEARCH_CONTENT_PLUGIN.URL + ENGINES_PATH, + { + shouldNotCreateHref: true, + } + ); }} >

{i18n.translate('xpack.enterpriseSearch.content.index.searchEngines.viewEngines', { - defaultMessage: 'View App Search engines', + defaultMessage: 'View Search Applications', })}

@@ -82,7 +87,7 @@ export const SearchEnginesPopover: React.FC = ({ content={i18n.translate( 'xpack.enterpriseSearch.content.index.searchEngines.createEngineDisabledTooltip', { - defaultMessage: 'You cannot create engines from hidden indices.', + defaultMessage: 'You cannot create search applications from hidden indices.', } )} > diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/connector/connector_configuration.tsx b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/connector/connector_configuration.tsx index 90972bcfc3c51..0abd1ba5b4afe 100644 --- a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/connector/connector_configuration.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/connector/connector_configuration.tsx @@ -124,7 +124,7 @@ export const ConnectorConfiguration: React.FC = () => { values={{ link: ( @@ -145,7 +145,7 @@ export const ConnectorConfiguration: React.FC = () => { values={{ link: ( @@ -356,7 +356,7 @@ export const ConnectorConfiguration: React.FC = () => { {i18n.translate( @@ -382,7 +382,7 @@ export const ConnectorConfiguration: React.FC = () => { {i18n.translate( diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/connector/custom_connector_configuration_config.tsx b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/connector/custom_connector_configuration_config.tsx index 58be80f2caa15..6eb991df1cdf9 100644 --- a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/connector/custom_connector_configuration_config.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/connector/custom_connector_configuration_config.tsx @@ -18,16 +18,16 @@ export const ConnectorConfigurationConfig: React.FC = () => { {i18n.translate( 'xpack.enterpriseSearch.content.indices.configurationConnector.config.connectorClientLink', - { defaultMessage: 'example connector client' } + { defaultMessage: 'connectors' } )} ), @@ -56,7 +56,7 @@ export const ConnectorConfigurationConfig: React.FC = () => { ), issuesLink: ( - + {i18n.translate( 'xpack.enterpriseSearch.content.indices.configurationConnector.config.issuesLink', { defaultMessage: 'issue' } diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/generate_api_key_panel.tsx b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/generate_api_key_panel.tsx index 40cdda3aaae32..365593c4699c3 100644 --- a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/generate_api_key_panel.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/generate_api_key_panel.tsx @@ -9,7 +9,15 @@ import React, { useState } from 'react'; import { useActions, useValues } from 'kea'; -import { EuiFlexGroup, EuiFlexItem, EuiPanel, EuiSpacer, EuiSwitch, EuiTitle } from '@elastic/eui'; +import { + EuiEmptyPrompt, + EuiFlexGroup, + EuiFlexItem, + EuiPanel, + EuiSpacer, + EuiSwitch, + EuiTitle, +} from '@elastic/eui'; import { i18n } from '@kbn/i18n'; @@ -27,7 +35,7 @@ import { OverviewLogic } from './overview.logic'; export const GenerateApiKeyPanel: React.FC = () => { const { apiKey, isGenerateModalOpen } = useValues(OverviewLogic); - const { indexName, ingestionMethod } = useValues(IndexViewLogic); + const { indexName, ingestionMethod, isHiddenIndex } = useValues(IndexViewLogic); const { closeGenerateModal } = useActions(OverviewLogic); const { defaultPipeline } = useValues(SettingsLogic); @@ -41,11 +49,29 @@ export const GenerateApiKeyPanel: React.FC = () => { - - - - - {indexName[0] !== '.' && ( + {isHiddenIndex ? ( + + {i18n.translate('xpack.enterpriseSearch.content.overview.emptyPrompt.body', { + defaultMessage: + 'We do not recommend adding documents to an externally managed index.', + })} +

+ } + title={ +

+ {i18n.translate('xpack.enterpriseSearch.content.overview.emptyPrompt.title', { + defaultMessage: 'Index managed externally', + })} +

+ } + /> + ) : ( + + + +

{i18n.translate( @@ -54,45 +80,42 @@ export const GenerateApiKeyPanel: React.FC = () => { )}

- )} -
- - setOptimizedRequest(event.target.checked)} - label={i18n.translate( - 'xpack.enterpriseSearch.content.overview.optimizedRequest.label', - { defaultMessage: 'View Enterprise Search optimized request' } - )} - checked={optimizedRequest} - /> - - - - - - - - - - - -
-
- {indexName[0] !== '.' && ( - <> - - - - - - )} -
+
+ + setOptimizedRequest(event.target.checked)} + label={i18n.translate( + 'xpack.enterpriseSearch.content.overview.optimizedRequest.label', + { defaultMessage: 'View Enterprise Search optimized request' } + )} + checked={optimizedRequest} + /> + + + + + + + + + + + +
+
+ + + + + +
+ )}
diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/index_view_logic.test.ts b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/index_view_logic.test.ts index 5e4256f92a97b..e440947cdfb5c 100644 --- a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/index_view_logic.test.ts +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/index_view_logic.test.ts @@ -51,6 +51,7 @@ const DEFAULT_VALUES = { ingestionStatus: IngestionStatus.CONNECTED, isCanceling: false, isConnectorIndex: false, + isHiddenIndex: false, isInitialLoading: false, isSyncing: false, isWaitingForSync: false, diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/index_view_logic.ts b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/index_view_logic.ts index b89e122f03c7f..cd33656062fdb 100644 --- a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/index_view_logic.ts +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/index_view_logic.ts @@ -77,6 +77,7 @@ export interface IndexViewValues { ingestionMethod: IngestionMethod; ingestionStatus: IngestionStatus; isCanceling: boolean; + isHiddenIndex: boolean; isInitialLoading: typeof CachedFetchIndexApiLogic.values.isInitialLoading; isSyncing: boolean; isWaitingForSync: boolean; @@ -234,6 +235,11 @@ export const IndexViewLogic = kea [selectors.indexData], (data: FetchIndexApiResponse | undefined) => isConnectorIndex(data), ], + isHiddenIndex: [ + () => [selectors.indexData], + (data: FetchIndexApiResponse | undefined) => + data?.hidden || (data?.name ?? '').startsWith('.'), + ], isSyncing: [ () => [selectors.indexData, selectors.syncStatus], (indexData: FetchIndexApiResponse | null, syncStatus: SyncStatus) => diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_indices/delete_index_modal.tsx b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_indices/delete_index_modal.tsx index 90e1da7c26eb2..38875821aeccf 100644 --- a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_indices/delete_index_modal.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_indices/delete_index_modal.tsx @@ -80,7 +80,7 @@ export const DeleteIndexModal: React.FC = () => { 'xpack.enterpriseSearch.content.searchIndices.deleteModal.delete.description', { defaultMessage: - 'Deleting this index will also delete all of its data and its {ingestionMethod} configuration. Any associated search engines will no longer be able to access any data stored in this index.', + 'Deleting this index will also delete all of its data and its {ingestionMethod} configuration. Any associated search applications will no longer be able to access any data stored in this index.', values: { ingestionMethod: ingestionMethodToText(ingestionMethod), }, diff --git a/x-pack/plugins/event_log/generated/mappings.json b/x-pack/plugins/event_log/generated/mappings.json index f50a1c0ef3321..e802567b2ab7f 100644 --- a/x-pack/plugins/event_log/generated/mappings.json +++ b/x-pack/plugins/event_log/generated/mappings.json @@ -398,6 +398,9 @@ } } }, + "revision": { + "type": "long" + }, "rule_type_id": { "type": "keyword", "ignore_above": 1024 diff --git a/x-pack/plugins/event_log/generated/schemas.ts b/x-pack/plugins/event_log/generated/schemas.ts index 6706db9635397..8621df23020bd 100644 --- a/x-pack/plugins/event_log/generated/schemas.ts +++ b/x-pack/plugins/event_log/generated/schemas.ts @@ -178,6 +178,7 @@ export const EventSchema = schema.maybe( ), }) ), + revision: ecsStringOrNumber(), rule_type_id: ecsString(), }) ), diff --git a/x-pack/plugins/event_log/scripts/mappings.js b/x-pack/plugins/event_log/scripts/mappings.js index 7081c321cc659..768988aa3c07b 100644 --- a/x-pack/plugins/event_log/scripts/mappings.js +++ b/x-pack/plugins/event_log/scripts/mappings.js @@ -177,6 +177,9 @@ exports.EcsCustomPropertyMappings = { }, }, }, + revision: { + type: 'long', + }, rule_type_id: { type: 'keyword', ignore_above: 1024, diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_details_page/components/actions_menu.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_details_page/components/actions_menu.tsx index 2a3d29226371c..d4710e71de9c2 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_details_page/components/actions_menu.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_details_page/components/actions_menu.tsx @@ -5,7 +5,7 @@ * 2.0. */ -import React, { memo, useState, useMemo } from 'react'; +import React, { memo, useState, useMemo, useCallback } from 'react'; import { EuiPortal, EuiContextMenuItem } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n-react'; @@ -24,6 +24,8 @@ import { isAgentUpgradeable, policyHasFleetServer } from '../../../../services'; import { AgentRequestDiagnosticsModal } from '../../components/agent_request_diagnostics_modal'; import { ExperimentalFeaturesService } from '../../../../services'; +import { AgentDetailsJsonFlyout } from './agent_details_json_flyout'; + export const AgentDetailsActionMenu: React.FunctionComponent<{ agent: Agent; agentPolicy?: AgentPolicy; @@ -37,8 +39,17 @@ export const AgentDetailsActionMenu: React.FunctionComponent<{ const [isUnenrollModalOpen, setIsUnenrollModalOpen] = useState(false); const [isUpgradeModalOpen, setIsUpgradeModalOpen] = useState(false); const [isRequestDiagnosticsModalOpen, setIsRequestDiagnosticsModalOpen] = useState(false); + const [isAgentDetailsJsonFlyoutOpen, setIsAgentDetailsJsonFlyoutOpen] = useState(false); const isUnenrolling = agent.status === 'unenrolling'; + const [isContextMenuOpen, setIsContextMenuOpen] = useState(false); + const onContextMenuChange = useCallback( + (open: boolean) => { + setIsContextMenuOpen(open); + }, + [setIsContextMenuOpen] + ); + const hasFleetServer = agentPolicy && policyHasFleetServer(agentPolicy); const { diagnosticFileUploadEnabled } = ExperimentalFeaturesService.get(); @@ -70,6 +81,7 @@ export const AgentDetailsActionMenu: React.FunctionComponent<{ onClick={() => { setIsUnenrollModalOpen(true); }} + key="unenrollAgent" > {isUnenrolling ? ( { setIsUpgradeModalOpen(true); }} + key="upgradeAgent" > , + { + setIsContextMenuOpen(false); + setIsAgentDetailsJsonFlyoutOpen(!isAgentDetailsJsonFlyoutOpen); + }} + key="agentDetailsJson" + > + + , ]; if (diagnosticFileUploadEnabled) { @@ -105,6 +131,7 @@ export const AgentDetailsActionMenu: React.FunctionComponent<{ onClick={() => { setIsRequestDiagnosticsModalOpen(true); }} + key="requestDiagnostics" > )} + {isAgentDetailsJsonFlyoutOpen && ( + + setIsAgentDetailsJsonFlyoutOpen(false)} + /> + + )} { + const agent: Agent = { + id: '123', + packages: [], + type: 'PERMANENT', + active: true, + enrolled_at: `${Date.now()}`, + user_provided_metadata: {}, + local_metadata: {}, + }; + + beforeEach(() => { + mockUseStartServices.mockReturnValue({ + docLinks: { links: { fleet: { troubleshooting: 'https://elastic.co' } } }, + }); + }); + + const renderComponent = () => { + return render(); + }; + + it('renders a title with the agent id if host name is not defined', () => { + const result = renderComponent(); + expect(result.getByText("'123' agent details")).toBeInTheDocument(); + }); + + it('renders a title with the agent host name if defined', () => { + agent.local_metadata = { + host: { + hostname: '456', + }, + }; + const result = renderComponent(); + expect(result.getByText("'456' agent details")).toBeInTheDocument(); + }); + + it('does not add a link to the page after clicking Download', () => { + const result = renderComponent(); + const downloadButton = result.getByRole('button', { name: 'Download JSON' }); + const anchorMocked = { + href: '', + click: jest.fn(), + download: '', + setAttribute: jest.fn(), + } as any; + const createElementSpyOn = jest + .spyOn(document, 'createElement') + .mockReturnValueOnce(anchorMocked); + + downloadButton.click(); + expect(createElementSpyOn).toBeCalledWith('a'); + expect(result.queryAllByRole('link')).toHaveLength(1); // The only link is the one from the flyout's description. + expect(result.getByRole('link')).toHaveAttribute('href', 'https://elastic.co'); + }); +}); diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_details_page/components/agent_details_json_flyout.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_details_page/components/agent_details_json_flyout.tsx new file mode 100644 index 0000000000000..093823f108ac6 --- /dev/null +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_details_page/components/agent_details_json_flyout.tsx @@ -0,0 +1,106 @@ +/* + * 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, { memo } from 'react'; +import { FormattedMessage } from '@kbn/i18n-react'; +import { + EuiButton, + EuiButtonEmpty, + EuiCodeBlock, + EuiFlexGroup, + EuiFlexItem, + EuiFlyout, + EuiFlyoutBody, + EuiFlyoutFooter, + EuiFlyoutHeader, + EuiLink, + EuiSpacer, + EuiText, + EuiTitle, +} from '@elastic/eui'; + +import type { Agent } from '../../../../types'; +import { useStartServices } from '../../../../hooks'; + +export const AgentDetailsJsonFlyout = memo<{ agent: Agent; onClose: () => void }>( + ({ agent, onClose }) => { + const agentToJson = JSON.stringify(agent, null, 2); + const agentName = + typeof agent.local_metadata?.host?.hostname === 'string' + ? agent.local_metadata.host.hostname + : agent.id; + + const downloadJson = () => { + const link = document.createElement('a'); + link.href = `data:text/json;charset=utf-8,${encodeURIComponent(agentToJson)}`; + link.download = `${agentName}-agent-details.json`; + link.click(); + }; + + const { docLinks } = useStartServices(); + + return ( + + + +

+ +

+
+
+ + +

+ + + + ), + }} + /> +

+
+ + {agentToJson} +
+ + + + + + + + + + + + + + +
+ ); + } +); diff --git a/x-pack/plugins/fleet/server/saved_objects/index.ts b/x-pack/plugins/fleet/server/saved_objects/index.ts index 1fdce8a2d4c80..36fb30eb97893 100644 --- a/x-pack/plugins/fleet/server/saved_objects/index.ts +++ b/x-pack/plugins/fleet/server/saved_objects/index.ts @@ -55,10 +55,8 @@ import { migrateInstallationToV860, migratePackagePolicyToV860, } from './migrations/to_v8_6_0'; -import { - migratePackagePolicyToV870, - migratePackagePolicyToV880, -} from './migrations/security_solution'; +import { migratePackagePolicyToV870 } from './migrations/security_solution'; +import { migratePackagePolicyToV880 } from './migrations/to_v8_8_0'; /* * Saved object types and mappings diff --git a/x-pack/plugins/fleet/server/saved_objects/migrations/synthetics/fixtures/8.7.0.ts b/x-pack/plugins/fleet/server/saved_objects/migrations/synthetics/fixtures/8.7.0.ts new file mode 100644 index 0000000000000..2c24678184094 --- /dev/null +++ b/x-pack/plugins/fleet/server/saved_objects/migrations/synthetics/fixtures/8.7.0.ts @@ -0,0 +1,1100 @@ +/* + * 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 { SavedObjectUnsanitizedDoc } from '@kbn/core/server'; + +import type { PackagePolicy } from '../../../../../common'; + +export const httpPolicy = { + type: 'ingest-package-policies', + id: 'ce2f0cc6-b082-4080-9ed3-82f38743a3ed-fa2e69b0-dec6-11ed-8746-c5b1a1a12ec1-default', + attributes: { + name: 'Invalid http monitor with 4 minute schedule-A private location-default', + namespace: 'default', + package: { name: 'synthetics', title: 'Elastic Synthetics', version: '0.11.8' }, + enabled: true, + policy_id: 'fa2e69b0-dec6-11ed-8746-c5b1a1a12ec1', + inputs: [ + { + type: 'synthetics/http', + policy_template: 'synthetics', + enabled: true, + streams: [ + { + enabled: true, + data_stream: { + type: 'synthetics', + dataset: 'http', + elasticsearch: { privileges: { indices: ['auto_configure', 'create_doc', 'read'] } }, + }, + vars: { + __ui: { + value: '{"is_tls_enabled":false,"is_zip_url_tls_enabled":false}', + type: 'yaml', + }, + enabled: { value: true, type: 'bool' }, + type: { value: 'http', type: 'text' }, + name: { value: 'Invalid http monitor with 4 minute schedule', type: 'text' }, + schedule: { value: '"@every 4m"', type: 'text' }, + urls: { value: 'https://elastic.co', type: 'text' }, + 'service.name': { value: '', type: 'text' }, + timeout: { value: '16s', type: 'text' }, + max_redirects: { value: '0', type: 'integer' }, + proxy_url: { value: '', type: 'text' }, + tags: { value: null, type: 'yaml' }, + username: { value: '', type: 'text' }, + password: { value: '', type: 'password' }, + 'response.include_headers': { value: true, type: 'bool' }, + 'response.include_body': { value: 'on_error', type: 'text' }, + 'check.request.method': { value: 'GET', type: 'text' }, + 'check.request.headers': { value: null, type: 'yaml' }, + 'check.request.body': { value: null, type: 'yaml' }, + 'check.response.status': { value: null, type: 'yaml' }, + 'check.response.headers': { value: null, type: 'yaml' }, + 'check.response.body.positive': { value: null, type: 'yaml' }, + 'check.response.body.negative': { value: null, type: 'yaml' }, + 'ssl.certificate_authorities': { value: null, type: 'yaml' }, + 'ssl.certificate': { value: null, type: 'yaml' }, + 'ssl.key': { value: null, type: 'yaml' }, + 'ssl.key_passphrase': { value: null, type: 'text' }, + 'ssl.verification_mode': { value: null, type: 'text' }, + 'ssl.supported_protocols': { value: null, type: 'yaml' }, + location_name: { value: 'A private location', type: 'text' }, + id: { value: 'ce2f0cc6-b082-4080-9ed3-82f38743a3ed', type: 'text' }, + config_id: { value: 'ce2f0cc6-b082-4080-9ed3-82f38743a3ed', type: 'text' }, + run_once: { value: false, type: 'bool' }, + origin: { value: 'ui', type: 'text' }, + 'monitor.project.id': { value: null, type: 'text' }, + 'monitor.project.name': { value: null, type: 'text' }, + }, + id: 'synthetics/http-http-ce2f0cc6-b082-4080-9ed3-82f38743a3ed-fa2e69b0-dec6-11ed-8746-c5b1a1a12ec1-default', + compiled_stream: { + __ui: { is_tls_enabled: false, is_zip_url_tls_enabled: false }, + type: 'http', + name: 'Invalid http monitor with 4 minute schedule', + id: 'ce2f0cc6-b082-4080-9ed3-82f38743a3ed', + origin: 'ui', + 'run_from.id': 'A private location', + 'run_from.geo.name': 'A private location', + enabled: true, + urls: 'https://elastic.co', + schedule: '@every 4m', + timeout: '16s', + max_redirects: 0, + 'response.include_headers': true, + 'response.include_body': 'on_error', + 'check.request.method': 'GET', + processors: [ + { + add_fields: { + target: '', + fields: { + 'monitor.fleet_managed': true, + config_id: 'ce2f0cc6-b082-4080-9ed3-82f38743a3ed', + }, + }, + }, + ], + }, + }, + ], + }, + { + type: 'synthetics/tcp', + policy_template: 'synthetics', + enabled: false, + streams: [ + { + enabled: false, + data_stream: { type: 'synthetics', dataset: 'tcp' }, + vars: { + __ui: { type: 'yaml' }, + enabled: { value: true, type: 'bool' }, + type: { value: 'tcp', type: 'text' }, + name: { type: 'text' }, + schedule: { value: '"@every 3m"', type: 'text' }, + hosts: { type: 'text' }, + 'service.name': { type: 'text' }, + timeout: { type: 'text' }, + proxy_url: { type: 'text' }, + proxy_use_local_resolver: { value: false, type: 'bool' }, + tags: { type: 'yaml' }, + 'check.send': { type: 'text' }, + 'check.receive': { type: 'text' }, + 'ssl.certificate_authorities': { type: 'yaml' }, + 'ssl.certificate': { type: 'yaml' }, + 'ssl.key': { type: 'yaml' }, + 'ssl.key_passphrase': { type: 'text' }, + 'ssl.verification_mode': { type: 'text' }, + 'ssl.supported_protocols': { type: 'yaml' }, + location_name: { value: 'Fleet managed', type: 'text' }, + id: { type: 'text' }, + config_id: { type: 'text' }, + run_once: { value: false, type: 'bool' }, + origin: { type: 'text' }, + 'monitor.project.id': { type: 'text' }, + 'monitor.project.name': { type: 'text' }, + }, + id: 'synthetics/tcp-tcp-ce2f0cc6-b082-4080-9ed3-82f38743a3ed-fa2e69b0-dec6-11ed-8746-c5b1a1a12ec1-default', + }, + ], + }, + { + type: 'synthetics/icmp', + policy_template: 'synthetics', + enabled: false, + streams: [ + { + enabled: false, + data_stream: { type: 'synthetics', dataset: 'icmp' }, + vars: { + __ui: { type: 'yaml' }, + enabled: { value: true, type: 'bool' }, + type: { value: 'icmp', type: 'text' }, + name: { type: 'text' }, + schedule: { value: '"@every 3m"', type: 'text' }, + wait: { value: '1s', type: 'text' }, + hosts: { type: 'text' }, + 'service.name': { type: 'text' }, + timeout: { type: 'text' }, + tags: { type: 'yaml' }, + location_name: { value: 'Fleet managed', type: 'text' }, + id: { type: 'text' }, + config_id: { type: 'text' }, + run_once: { value: false, type: 'bool' }, + origin: { type: 'text' }, + 'monitor.project.id': { type: 'text' }, + 'monitor.project.name': { type: 'text' }, + }, + id: 'synthetics/icmp-icmp-ce2f0cc6-b082-4080-9ed3-82f38743a3ed-fa2e69b0-dec6-11ed-8746-c5b1a1a12ec1-default', + }, + ], + }, + { + type: 'synthetics/browser', + policy_template: 'synthetics', + enabled: false, + streams: [ + { + enabled: true, + data_stream: { + type: 'synthetics', + dataset: 'browser', + elasticsearch: { privileges: { indices: ['auto_configure', 'create_doc', 'read'] } }, + }, + vars: { + __ui: { type: 'yaml' }, + enabled: { value: true, type: 'bool' }, + type: { value: 'browser', type: 'text' }, + name: { type: 'text' }, + schedule: { value: '"@every 3m"', type: 'text' }, + 'service.name': { type: 'text' }, + timeout: { type: 'text' }, + tags: { type: 'yaml' }, + 'source.zip_url.url': { type: 'text' }, + 'source.zip_url.username': { type: 'text' }, + 'source.zip_url.folder': { type: 'text' }, + 'source.zip_url.password': { type: 'password' }, + 'source.inline.script': { type: 'yaml' }, + 'source.project.content': { type: 'text' }, + params: { type: 'yaml' }, + playwright_options: { type: 'yaml' }, + screenshots: { type: 'text' }, + synthetics_args: { type: 'text' }, + ignore_https_errors: { type: 'bool' }, + 'throttling.config': { type: 'text' }, + 'filter_journeys.tags': { type: 'yaml' }, + 'filter_journeys.match': { type: 'text' }, + 'source.zip_url.ssl.certificate_authorities': { type: 'yaml' }, + 'source.zip_url.ssl.certificate': { type: 'yaml' }, + 'source.zip_url.ssl.key': { type: 'yaml' }, + 'source.zip_url.ssl.key_passphrase': { type: 'text' }, + 'source.zip_url.ssl.verification_mode': { type: 'text' }, + 'source.zip_url.ssl.supported_protocols': { type: 'yaml' }, + 'source.zip_url.proxy_url': { type: 'text' }, + location_name: { value: 'Fleet managed', type: 'text' }, + id: { type: 'text' }, + config_id: { type: 'text' }, + run_once: { value: false, type: 'bool' }, + origin: { type: 'text' }, + 'monitor.project.id': { type: 'text' }, + 'monitor.project.name': { type: 'text' }, + }, + id: 'synthetics/browser-browser-ce2f0cc6-b082-4080-9ed3-82f38743a3ed-fa2e69b0-dec6-11ed-8746-c5b1a1a12ec1-default', + compiled_stream: { + __ui: null, + type: 'browser', + name: null, + 'run_from.id': 'Fleet managed', + 'run_from.geo.name': 'Fleet managed', + enabled: true, + schedule: '@every 3m', + timeout: null, + throttling: null, + processors: [ + { add_fields: { target: '', fields: { 'monitor.fleet_managed': true } } }, + ], + }, + }, + { + enabled: true, + data_stream: { + type: 'synthetics', + dataset: 'browser.network', + elasticsearch: { privileges: { indices: ['auto_configure', 'create_doc', 'read'] } }, + }, + id: 'synthetics/browser-browser.network-ce2f0cc6-b082-4080-9ed3-82f38743a3ed-fa2e69b0-dec6-11ed-8746-c5b1a1a12ec1-default', + compiled_stream: { + processors: [ + { add_fields: { target: '', fields: { 'monitor.fleet_managed': true } } }, + ], + }, + }, + { + enabled: true, + data_stream: { + type: 'synthetics', + dataset: 'browser.screenshot', + elasticsearch: { privileges: { indices: ['auto_configure', 'create_doc', 'read'] } }, + }, + id: 'synthetics/browser-browser.screenshot-ce2f0cc6-b082-4080-9ed3-82f38743a3ed-fa2e69b0-dec6-11ed-8746-c5b1a1a12ec1-default', + compiled_stream: { + processors: [ + { add_fields: { target: '', fields: { 'monitor.fleet_managed': true } } }, + ], + }, + }, + ], + }, + ], + is_managed: true, + revision: 1, + created_at: '2023-04-19T15:34:51.655Z', + created_by: 'system', + updated_at: '2023-04-19T15:34:51.655Z', + updated_by: 'system', + }, + references: [], + coreMigrationVersion: '8.8.0', + updated_at: '2023-04-19T15:34:51.714Z', + created_at: '2023-04-19T15:34:51.714Z', + typeMigrationVersion: '8.7.0', +} as unknown as SavedObjectUnsanitizedDoc; +export const tcpPolicy = { + type: 'ingest-package-policies', + id: '77f25200-7cf3-450d-abfa-4f95faae1907-fa2e69b0-dec6-11ed-8746-c5b1a1a12ec1-default', + attributes: { + name: 'Invalid tcp monitor with 8 minute schedule-A private location-default', + namespace: 'default', + package: { name: 'synthetics', title: 'Elastic Synthetics', version: '0.11.8' }, + enabled: true, + policy_id: 'fa2e69b0-dec6-11ed-8746-c5b1a1a12ec1', + inputs: [ + { + type: 'synthetics/http', + policy_template: 'synthetics', + enabled: false, + streams: [ + { + enabled: false, + data_stream: { type: 'synthetics', dataset: 'http' }, + vars: { + __ui: { type: 'yaml' }, + enabled: { value: true, type: 'bool' }, + type: { value: 'http', type: 'text' }, + name: { type: 'text' }, + schedule: { value: '"@every 3m"', type: 'text' }, + urls: { type: 'text' }, + 'service.name': { type: 'text' }, + timeout: { type: 'text' }, + max_redirects: { type: 'integer' }, + proxy_url: { type: 'text' }, + tags: { type: 'yaml' }, + username: { type: 'text' }, + password: { type: 'password' }, + 'response.include_headers': { type: 'bool' }, + 'response.include_body': { type: 'text' }, + 'check.request.method': { type: 'text' }, + 'check.request.headers': { type: 'yaml' }, + 'check.request.body': { type: 'yaml' }, + 'check.response.status': { type: 'yaml' }, + 'check.response.headers': { type: 'yaml' }, + 'check.response.body.positive': { type: 'yaml' }, + 'check.response.body.negative': { type: 'yaml' }, + 'ssl.certificate_authorities': { type: 'yaml' }, + 'ssl.certificate': { type: 'yaml' }, + 'ssl.key': { type: 'yaml' }, + 'ssl.key_passphrase': { type: 'text' }, + 'ssl.verification_mode': { type: 'text' }, + 'ssl.supported_protocols': { type: 'yaml' }, + location_name: { value: 'Fleet managed', type: 'text' }, + id: { type: 'text' }, + config_id: { type: 'text' }, + run_once: { value: false, type: 'bool' }, + origin: { type: 'text' }, + 'monitor.project.id': { type: 'text' }, + 'monitor.project.name': { type: 'text' }, + }, + id: 'synthetics/http-http-77f25200-7cf3-450d-abfa-4f95faae1907-fa2e69b0-dec6-11ed-8746-c5b1a1a12ec1-default', + }, + ], + }, + { + type: 'synthetics/tcp', + policy_template: 'synthetics', + enabled: true, + streams: [ + { + enabled: true, + data_stream: { + type: 'synthetics', + dataset: 'tcp', + elasticsearch: { privileges: { indices: ['auto_configure', 'create_doc', 'read'] } }, + }, + vars: { + __ui: { + value: '{"is_tls_enabled":false,"is_zip_url_tls_enabled":false}', + type: 'yaml', + }, + enabled: { value: true, type: 'bool' }, + type: { value: 'tcp', type: 'text' }, + name: { value: 'Invalid tcp monitor with 8 minute schedule', type: 'text' }, + schedule: { value: '"@every 8m"', type: 'text' }, + hosts: { value: 'localhost:5601', type: 'text' }, + 'service.name': { value: '', type: 'text' }, + timeout: { value: '16s', type: 'text' }, + proxy_url: { value: '', type: 'text' }, + proxy_use_local_resolver: { value: false, type: 'bool' }, + tags: { value: null, type: 'yaml' }, + 'check.send': { value: '', type: 'text' }, + 'check.receive': { value: '', type: 'text' }, + 'ssl.certificate_authorities': { value: null, type: 'yaml' }, + 'ssl.certificate': { value: null, type: 'yaml' }, + 'ssl.key': { value: null, type: 'yaml' }, + 'ssl.key_passphrase': { value: null, type: 'text' }, + 'ssl.verification_mode': { value: null, type: 'text' }, + 'ssl.supported_protocols': { value: null, type: 'yaml' }, + location_name: { value: 'A private location', type: 'text' }, + id: { value: '77f25200-7cf3-450d-abfa-4f95faae1907', type: 'text' }, + config_id: { value: '77f25200-7cf3-450d-abfa-4f95faae1907', type: 'text' }, + run_once: { value: false, type: 'bool' }, + origin: { value: 'ui', type: 'text' }, + 'monitor.project.id': { value: null, type: 'text' }, + 'monitor.project.name': { value: null, type: 'text' }, + }, + id: 'synthetics/tcp-tcp-77f25200-7cf3-450d-abfa-4f95faae1907-fa2e69b0-dec6-11ed-8746-c5b1a1a12ec1-default', + compiled_stream: { + __ui: { is_tls_enabled: false, is_zip_url_tls_enabled: false }, + type: 'tcp', + name: 'Invalid tcp monitor with 8 minute schedule', + id: '77f25200-7cf3-450d-abfa-4f95faae1907', + origin: 'ui', + 'run_from.id': 'A private location', + 'run_from.geo.name': 'A private location', + enabled: true, + hosts: 'localhost:5601', + schedule: '@every 8m', + timeout: '16s', + proxy_use_local_resolver: false, + processors: [ + { + add_fields: { + target: '', + fields: { + 'monitor.fleet_managed': true, + config_id: '77f25200-7cf3-450d-abfa-4f95faae1907', + }, + }, + }, + ], + }, + }, + ], + }, + { + type: 'synthetics/icmp', + policy_template: 'synthetics', + enabled: false, + streams: [ + { + enabled: false, + data_stream: { type: 'synthetics', dataset: 'icmp' }, + vars: { + __ui: { type: 'yaml' }, + enabled: { value: true, type: 'bool' }, + type: { value: 'icmp', type: 'text' }, + name: { type: 'text' }, + schedule: { value: '"@every 3m"', type: 'text' }, + wait: { value: '1s', type: 'text' }, + hosts: { type: 'text' }, + 'service.name': { type: 'text' }, + timeout: { type: 'text' }, + tags: { type: 'yaml' }, + location_name: { value: 'Fleet managed', type: 'text' }, + id: { type: 'text' }, + config_id: { type: 'text' }, + run_once: { value: false, type: 'bool' }, + origin: { type: 'text' }, + 'monitor.project.id': { type: 'text' }, + 'monitor.project.name': { type: 'text' }, + }, + id: 'synthetics/icmp-icmp-77f25200-7cf3-450d-abfa-4f95faae1907-fa2e69b0-dec6-11ed-8746-c5b1a1a12ec1-default', + }, + ], + }, + { + type: 'synthetics/browser', + policy_template: 'synthetics', + enabled: false, + streams: [ + { + enabled: true, + data_stream: { + type: 'synthetics', + dataset: 'browser', + elasticsearch: { privileges: { indices: ['auto_configure', 'create_doc', 'read'] } }, + }, + vars: { + __ui: { type: 'yaml' }, + enabled: { value: true, type: 'bool' }, + type: { value: 'browser', type: 'text' }, + name: { type: 'text' }, + schedule: { value: '"@every 3m"', type: 'text' }, + 'service.name': { type: 'text' }, + timeout: { type: 'text' }, + tags: { type: 'yaml' }, + 'source.zip_url.url': { type: 'text' }, + 'source.zip_url.username': { type: 'text' }, + 'source.zip_url.folder': { type: 'text' }, + 'source.zip_url.password': { type: 'password' }, + 'source.inline.script': { type: 'yaml' }, + 'source.project.content': { type: 'text' }, + params: { type: 'yaml' }, + playwright_options: { type: 'yaml' }, + screenshots: { type: 'text' }, + synthetics_args: { type: 'text' }, + ignore_https_errors: { type: 'bool' }, + 'throttling.config': { type: 'text' }, + 'filter_journeys.tags': { type: 'yaml' }, + 'filter_journeys.match': { type: 'text' }, + 'source.zip_url.ssl.certificate_authorities': { type: 'yaml' }, + 'source.zip_url.ssl.certificate': { type: 'yaml' }, + 'source.zip_url.ssl.key': { type: 'yaml' }, + 'source.zip_url.ssl.key_passphrase': { type: 'text' }, + 'source.zip_url.ssl.verification_mode': { type: 'text' }, + 'source.zip_url.ssl.supported_protocols': { type: 'yaml' }, + 'source.zip_url.proxy_url': { type: 'text' }, + location_name: { value: 'Fleet managed', type: 'text' }, + id: { type: 'text' }, + config_id: { type: 'text' }, + run_once: { value: false, type: 'bool' }, + origin: { type: 'text' }, + 'monitor.project.id': { type: 'text' }, + 'monitor.project.name': { type: 'text' }, + }, + id: 'synthetics/browser-browser-77f25200-7cf3-450d-abfa-4f95faae1907-fa2e69b0-dec6-11ed-8746-c5b1a1a12ec1-default', + compiled_stream: { + __ui: null, + type: 'browser', + name: null, + 'run_from.id': 'Fleet managed', + 'run_from.geo.name': 'Fleet managed', + enabled: true, + schedule: '@every 3m', + timeout: null, + throttling: null, + processors: [ + { add_fields: { target: '', fields: { 'monitor.fleet_managed': true } } }, + ], + }, + }, + { + enabled: true, + data_stream: { + type: 'synthetics', + dataset: 'browser.network', + elasticsearch: { privileges: { indices: ['auto_configure', 'create_doc', 'read'] } }, + }, + id: 'synthetics/browser-browser.network-77f25200-7cf3-450d-abfa-4f95faae1907-fa2e69b0-dec6-11ed-8746-c5b1a1a12ec1-default', + compiled_stream: { + processors: [ + { add_fields: { target: '', fields: { 'monitor.fleet_managed': true } } }, + ], + }, + }, + { + enabled: true, + data_stream: { + type: 'synthetics', + dataset: 'browser.screenshot', + elasticsearch: { privileges: { indices: ['auto_configure', 'create_doc', 'read'] } }, + }, + id: 'synthetics/browser-browser.screenshot-77f25200-7cf3-450d-abfa-4f95faae1907-fa2e69b0-dec6-11ed-8746-c5b1a1a12ec1-default', + compiled_stream: { + processors: [ + { add_fields: { target: '', fields: { 'monitor.fleet_managed': true } } }, + ], + }, + }, + ], + }, + ], + is_managed: true, + revision: 1, + created_at: '2023-04-19T15:35:26.839Z', + created_by: 'system', + updated_at: '2023-04-19T15:35:26.839Z', + updated_by: 'system', + }, + references: [], + coreMigrationVersion: '8.8.0', + updated_at: '2023-04-19T15:35:26.884Z', + created_at: '2023-04-19T15:35:26.884Z', + typeMigrationVersion: '8.7.0', +} as unknown as SavedObjectUnsanitizedDoc; +export const icmpPolicy = { + type: 'ingest-package-policies', + id: 'e437c11c-5cb0-42d1-966f-d2ba231550fd-fa2e69b0-dec6-11ed-8746-c5b1a1a12ec1-default', + attributes: { + name: 'Invalid ICMP monitor with 11 minute schedule-A private location-default', + namespace: 'default', + package: { name: 'synthetics', title: 'Elastic Synthetics', version: '0.11.8' }, + enabled: true, + policy_id: 'fa2e69b0-dec6-11ed-8746-c5b1a1a12ec1', + inputs: [ + { + type: 'synthetics/http', + policy_template: 'synthetics', + enabled: false, + streams: [ + { + enabled: false, + data_stream: { type: 'synthetics', dataset: 'http' }, + vars: { + __ui: { type: 'yaml' }, + enabled: { value: true, type: 'bool' }, + type: { value: 'http', type: 'text' }, + name: { type: 'text' }, + schedule: { value: '"@every 3m"', type: 'text' }, + urls: { type: 'text' }, + 'service.name': { type: 'text' }, + timeout: { type: 'text' }, + max_redirects: { type: 'integer' }, + proxy_url: { type: 'text' }, + tags: { type: 'yaml' }, + username: { type: 'text' }, + password: { type: 'password' }, + 'response.include_headers': { type: 'bool' }, + 'response.include_body': { type: 'text' }, + 'check.request.method': { type: 'text' }, + 'check.request.headers': { type: 'yaml' }, + 'check.request.body': { type: 'yaml' }, + 'check.response.status': { type: 'yaml' }, + 'check.response.headers': { type: 'yaml' }, + 'check.response.body.positive': { type: 'yaml' }, + 'check.response.body.negative': { type: 'yaml' }, + 'ssl.certificate_authorities': { type: 'yaml' }, + 'ssl.certificate': { type: 'yaml' }, + 'ssl.key': { type: 'yaml' }, + 'ssl.key_passphrase': { type: 'text' }, + 'ssl.verification_mode': { type: 'text' }, + 'ssl.supported_protocols': { type: 'yaml' }, + location_name: { value: 'Fleet managed', type: 'text' }, + id: { type: 'text' }, + config_id: { type: 'text' }, + run_once: { value: false, type: 'bool' }, + origin: { type: 'text' }, + 'monitor.project.id': { type: 'text' }, + 'monitor.project.name': { type: 'text' }, + }, + id: 'synthetics/http-http-e437c11c-5cb0-42d1-966f-d2ba231550fd-fa2e69b0-dec6-11ed-8746-c5b1a1a12ec1-default', + }, + ], + }, + { + type: 'synthetics/tcp', + policy_template: 'synthetics', + enabled: false, + streams: [ + { + enabled: false, + data_stream: { type: 'synthetics', dataset: 'tcp' }, + vars: { + __ui: { type: 'yaml' }, + enabled: { value: true, type: 'bool' }, + type: { value: 'tcp', type: 'text' }, + name: { type: 'text' }, + schedule: { value: '"@every 3m"', type: 'text' }, + hosts: { type: 'text' }, + 'service.name': { type: 'text' }, + timeout: { type: 'text' }, + proxy_url: { type: 'text' }, + proxy_use_local_resolver: { value: false, type: 'bool' }, + tags: { type: 'yaml' }, + 'check.send': { type: 'text' }, + 'check.receive': { type: 'text' }, + 'ssl.certificate_authorities': { type: 'yaml' }, + 'ssl.certificate': { type: 'yaml' }, + 'ssl.key': { type: 'yaml' }, + 'ssl.key_passphrase': { type: 'text' }, + 'ssl.verification_mode': { type: 'text' }, + 'ssl.supported_protocols': { type: 'yaml' }, + location_name: { value: 'Fleet managed', type: 'text' }, + id: { type: 'text' }, + config_id: { type: 'text' }, + run_once: { value: false, type: 'bool' }, + origin: { type: 'text' }, + 'monitor.project.id': { type: 'text' }, + 'monitor.project.name': { type: 'text' }, + }, + id: 'synthetics/tcp-tcp-e437c11c-5cb0-42d1-966f-d2ba231550fd-fa2e69b0-dec6-11ed-8746-c5b1a1a12ec1-default', + }, + ], + }, + { + type: 'synthetics/icmp', + policy_template: 'synthetics', + enabled: true, + streams: [ + { + enabled: true, + data_stream: { + type: 'synthetics', + dataset: 'icmp', + elasticsearch: { privileges: { indices: ['auto_configure', 'create_doc', 'read'] } }, + }, + vars: { + __ui: { type: 'yaml' }, + enabled: { value: true, type: 'bool' }, + type: { value: 'icmp', type: 'text' }, + name: { value: 'Invalid ICMP monitor with 11 minute schedule', type: 'text' }, + schedule: { value: '"@every 16m"', type: 'text' }, + wait: { value: '1s', type: 'text' }, + hosts: { value: '1.1.1.1', type: 'text' }, + 'service.name': { value: '', type: 'text' }, + timeout: { value: '16s', type: 'text' }, + tags: { value: null, type: 'yaml' }, + location_name: { value: 'A private location', type: 'text' }, + id: { value: 'e437c11c-5cb0-42d1-966f-d2ba231550fd', type: 'text' }, + config_id: { value: 'e437c11c-5cb0-42d1-966f-d2ba231550fd', type: 'text' }, + run_once: { value: false, type: 'bool' }, + origin: { value: 'ui', type: 'text' }, + 'monitor.project.id': { value: null, type: 'text' }, + 'monitor.project.name': { value: null, type: 'text' }, + }, + id: 'synthetics/icmp-icmp-e437c11c-5cb0-42d1-966f-d2ba231550fd-fa2e69b0-dec6-11ed-8746-c5b1a1a12ec1-default', + compiled_stream: { + __ui: null, + type: 'icmp', + name: 'Invalid ICMP monitor with 11 minute schedule', + id: 'e437c11c-5cb0-42d1-966f-d2ba231550fd', + origin: 'ui', + 'run_from.id': 'A private location', + 'run_from.geo.name': 'A private location', + enabled: true, + hosts: '1.1.1.1', + schedule: '@every 16m', + wait: '1s', + timeout: '16s', + processors: [ + { + add_fields: { + target: '', + fields: { + 'monitor.fleet_managed': true, + config_id: 'e437c11c-5cb0-42d1-966f-d2ba231550fd', + }, + }, + }, + ], + }, + }, + ], + }, + { + type: 'synthetics/browser', + policy_template: 'synthetics', + enabled: false, + streams: [ + { + enabled: true, + data_stream: { + type: 'synthetics', + dataset: 'browser', + elasticsearch: { privileges: { indices: ['auto_configure', 'create_doc', 'read'] } }, + }, + vars: { + __ui: { type: 'yaml' }, + enabled: { value: true, type: 'bool' }, + type: { value: 'browser', type: 'text' }, + name: { type: 'text' }, + schedule: { value: '"@every 3m"', type: 'text' }, + 'service.name': { type: 'text' }, + timeout: { type: 'text' }, + tags: { type: 'yaml' }, + 'source.zip_url.url': { type: 'text' }, + 'source.zip_url.username': { type: 'text' }, + 'source.zip_url.folder': { type: 'text' }, + 'source.zip_url.password': { type: 'password' }, + 'source.inline.script': { type: 'yaml' }, + 'source.project.content': { type: 'text' }, + params: { type: 'yaml' }, + playwright_options: { type: 'yaml' }, + screenshots: { type: 'text' }, + synthetics_args: { type: 'text' }, + ignore_https_errors: { type: 'bool' }, + 'throttling.config': { type: 'text' }, + 'filter_journeys.tags': { type: 'yaml' }, + 'filter_journeys.match': { type: 'text' }, + 'source.zip_url.ssl.certificate_authorities': { type: 'yaml' }, + 'source.zip_url.ssl.certificate': { type: 'yaml' }, + 'source.zip_url.ssl.key': { type: 'yaml' }, + 'source.zip_url.ssl.key_passphrase': { type: 'text' }, + 'source.zip_url.ssl.verification_mode': { type: 'text' }, + 'source.zip_url.ssl.supported_protocols': { type: 'yaml' }, + 'source.zip_url.proxy_url': { type: 'text' }, + location_name: { value: 'Fleet managed', type: 'text' }, + id: { type: 'text' }, + config_id: { type: 'text' }, + run_once: { value: false, type: 'bool' }, + origin: { type: 'text' }, + 'monitor.project.id': { type: 'text' }, + 'monitor.project.name': { type: 'text' }, + }, + id: 'synthetics/browser-browser-e437c11c-5cb0-42d1-966f-d2ba231550fd-fa2e69b0-dec6-11ed-8746-c5b1a1a12ec1-default', + compiled_stream: { + __ui: null, + type: 'browser', + name: null, + 'run_from.id': 'Fleet managed', + 'run_from.geo.name': 'Fleet managed', + enabled: true, + schedule: '@every 3m', + timeout: null, + throttling: null, + processors: [ + { add_fields: { target: '', fields: { 'monitor.fleet_managed': true } } }, + ], + }, + }, + { + enabled: true, + data_stream: { + type: 'synthetics', + dataset: 'browser.network', + elasticsearch: { privileges: { indices: ['auto_configure', 'create_doc', 'read'] } }, + }, + id: 'synthetics/browser-browser.network-e437c11c-5cb0-42d1-966f-d2ba231550fd-fa2e69b0-dec6-11ed-8746-c5b1a1a12ec1-default', + compiled_stream: { + processors: [ + { add_fields: { target: '', fields: { 'monitor.fleet_managed': true } } }, + ], + }, + }, + { + enabled: true, + data_stream: { + type: 'synthetics', + dataset: 'browser.screenshot', + elasticsearch: { privileges: { indices: ['auto_configure', 'create_doc', 'read'] } }, + }, + id: 'synthetics/browser-browser.screenshot-e437c11c-5cb0-42d1-966f-d2ba231550fd-fa2e69b0-dec6-11ed-8746-c5b1a1a12ec1-default', + compiled_stream: { + processors: [ + { add_fields: { target: '', fields: { 'monitor.fleet_managed': true } } }, + ], + }, + }, + ], + }, + ], + is_managed: true, + revision: 1, + created_at: '2023-04-19T15:35:53.763Z', + created_by: 'system', + updated_at: '2023-04-19T15:35:53.763Z', + updated_by: 'system', + }, + references: [], + coreMigrationVersion: '8.8.0', + updated_at: '2023-04-19T15:35:53.793Z', + created_at: '2023-04-19T15:35:53.793Z', + typeMigrationVersion: '8.7.0', +} as unknown as SavedObjectUnsanitizedDoc; + +export const getBrowserPolicy = (throttling = '5d/3u/20l') => + ({ + type: 'ingest-package-policies', + id: '420754e9-40f2-486c-bc2e-265bafd735c5-fe200580-dee2-11ed-933e-0f85f8c5dd40-default', + attributes: { + name: 'https://elastic.co-A private location-default', + namespace: 'default', + package: { name: 'synthetics', title: 'Elastic Synthetics', version: '0.11.8' }, + enabled: true, + policy_id: 'fe200580-dee2-11ed-933e-0f85f8c5dd40', + inputs: [ + { + type: 'synthetics/http', + policy_template: 'synthetics', + enabled: false, + streams: [ + { + enabled: false, + data_stream: { type: 'synthetics', dataset: 'http' }, + vars: { + __ui: { type: 'yaml' }, + enabled: { value: true, type: 'bool' }, + type: { value: 'http', type: 'text' }, + name: { type: 'text' }, + schedule: { value: '"@every 3m"', type: 'text' }, + urls: { type: 'text' }, + 'service.name': { type: 'text' }, + timeout: { type: 'text' }, + max_redirects: { type: 'integer' }, + proxy_url: { type: 'text' }, + tags: { type: 'yaml' }, + username: { type: 'text' }, + password: { type: 'password' }, + 'response.include_headers': { type: 'bool' }, + 'response.include_body': { type: 'text' }, + 'check.request.method': { type: 'text' }, + 'check.request.headers': { type: 'yaml' }, + 'check.request.body': { type: 'yaml' }, + 'check.response.status': { type: 'yaml' }, + 'check.response.headers': { type: 'yaml' }, + 'check.response.body.positive': { type: 'yaml' }, + 'check.response.body.negative': { type: 'yaml' }, + 'ssl.certificate_authorities': { type: 'yaml' }, + 'ssl.certificate': { type: 'yaml' }, + 'ssl.key': { type: 'yaml' }, + 'ssl.key_passphrase': { type: 'text' }, + 'ssl.verification_mode': { type: 'text' }, + 'ssl.supported_protocols': { type: 'yaml' }, + location_name: { value: 'Fleet managed', type: 'text' }, + id: { type: 'text' }, + config_id: { type: 'text' }, + run_once: { value: false, type: 'bool' }, + origin: { type: 'text' }, + 'monitor.project.id': { type: 'text' }, + 'monitor.project.name': { type: 'text' }, + }, + id: 'synthetics/http-http-420754e9-40f2-486c-bc2e-265bafd735c5-fe200580-dee2-11ed-933e-0f85f8c5dd40-default', + }, + ], + }, + { + type: 'synthetics/tcp', + policy_template: 'synthetics', + enabled: false, + streams: [ + { + enabled: false, + data_stream: { type: 'synthetics', dataset: 'tcp' }, + vars: { + __ui: { type: 'yaml' }, + enabled: { value: true, type: 'bool' }, + type: { value: 'tcp', type: 'text' }, + name: { type: 'text' }, + schedule: { value: '"@every 3m"', type: 'text' }, + hosts: { type: 'text' }, + 'service.name': { type: 'text' }, + timeout: { type: 'text' }, + proxy_url: { type: 'text' }, + proxy_use_local_resolver: { value: false, type: 'bool' }, + tags: { type: 'yaml' }, + 'check.send': { type: 'text' }, + 'check.receive': { type: 'text' }, + 'ssl.certificate_authorities': { type: 'yaml' }, + 'ssl.certificate': { type: 'yaml' }, + 'ssl.key': { type: 'yaml' }, + 'ssl.key_passphrase': { type: 'text' }, + 'ssl.verification_mode': { type: 'text' }, + 'ssl.supported_protocols': { type: 'yaml' }, + location_name: { value: 'Fleet managed', type: 'text' }, + id: { type: 'text' }, + config_id: { type: 'text' }, + run_once: { value: false, type: 'bool' }, + origin: { type: 'text' }, + 'monitor.project.id': { type: 'text' }, + 'monitor.project.name': { type: 'text' }, + }, + id: 'synthetics/tcp-tcp-420754e9-40f2-486c-bc2e-265bafd735c5-fe200580-dee2-11ed-933e-0f85f8c5dd40-default', + }, + ], + }, + { + type: 'synthetics/icmp', + policy_template: 'synthetics', + enabled: false, + streams: [ + { + enabled: false, + data_stream: { type: 'synthetics', dataset: 'icmp' }, + vars: { + __ui: { type: 'yaml' }, + enabled: { value: true, type: 'bool' }, + type: { value: 'icmp', type: 'text' }, + name: { type: 'text' }, + schedule: { value: '"@every 3m"', type: 'text' }, + wait: { value: '1s', type: 'text' }, + hosts: { type: 'text' }, + 'service.name': { type: 'text' }, + timeout: { type: 'text' }, + tags: { type: 'yaml' }, + location_name: { value: 'Fleet managed', type: 'text' }, + id: { type: 'text' }, + config_id: { type: 'text' }, + run_once: { value: false, type: 'bool' }, + origin: { type: 'text' }, + 'monitor.project.id': { type: 'text' }, + 'monitor.project.name': { type: 'text' }, + }, + id: 'synthetics/icmp-icmp-420754e9-40f2-486c-bc2e-265bafd735c5-fe200580-dee2-11ed-933e-0f85f8c5dd40-default', + }, + ], + }, + { + type: 'synthetics/browser', + policy_template: 'synthetics', + enabled: true, + streams: [ + { + enabled: true, + data_stream: { + type: 'synthetics', + dataset: 'browser', + elasticsearch: { + privileges: { indices: ['auto_configure', 'create_doc', 'read'] }, + }, + }, + vars: { + __ui: { + value: + '{"script_source":{"is_generated_script":false,"file_name":""},"is_zip_url_tls_enabled":false,"is_tls_enabled":false}', + type: 'yaml', + }, + enabled: { value: true, type: 'bool' }, + type: { value: 'browser', type: 'text' }, + name: { value: 'https://elastic.co', type: 'text' }, + schedule: { value: '"@every 2m"', type: 'text' }, + 'service.name': { value: '', type: 'text' }, + timeout: { value: null, type: 'text' }, + tags: { value: null, type: 'yaml' }, + 'source.zip_url.url': { value: '', type: 'text' }, + 'source.zip_url.username': { value: '', type: 'text' }, + 'source.zip_url.folder': { value: '', type: 'text' }, + 'source.zip_url.password': { value: '', type: 'password' }, + 'source.inline.script': { + value: + "\"step('Go to https://elastic.co', async () => {\\n await page.goto('https://elastic.co');\\n});\"", + type: 'yaml', + }, + 'source.project.content': { value: '', type: 'text' }, + params: { value: '', type: 'yaml' }, + playwright_options: { value: '', type: 'yaml' }, + screenshots: { value: 'on', type: 'text' }, + synthetics_args: { value: null, type: 'text' }, + ignore_https_errors: { value: false, type: 'bool' }, + 'throttling.config': { value: throttling, type: 'text' }, + 'filter_journeys.tags': { value: null, type: 'yaml' }, + 'filter_journeys.match': { value: null, type: 'text' }, + 'source.zip_url.ssl.certificate_authorities': { value: null, type: 'yaml' }, + 'source.zip_url.ssl.certificate': { value: null, type: 'yaml' }, + 'source.zip_url.ssl.key': { value: null, type: 'yaml' }, + 'source.zip_url.ssl.key_passphrase': { value: null, type: 'text' }, + 'source.zip_url.ssl.verification_mode': { value: null, type: 'text' }, + 'source.zip_url.ssl.supported_protocols': { value: null, type: 'yaml' }, + 'source.zip_url.proxy_url': { value: '', type: 'text' }, + location_name: { value: 'A private location', type: 'text' }, + id: { value: '420754e9-40f2-486c-bc2e-265bafd735c5', type: 'text' }, + config_id: { value: '420754e9-40f2-486c-bc2e-265bafd735c5', type: 'text' }, + run_once: { value: false, type: 'bool' }, + origin: { value: 'ui', type: 'text' }, + 'monitor.project.id': { value: null, type: 'text' }, + 'monitor.project.name': { value: null, type: 'text' }, + }, + id: 'synthetics/browser-browser-420754e9-40f2-486c-bc2e-265bafd735c5-fe200580-dee2-11ed-933e-0f85f8c5dd40-default', + compiled_stream: { + __ui: { + script_source: { is_generated_script: false, file_name: '' }, + is_zip_url_tls_enabled: false, + is_tls_enabled: false, + }, + type: 'browser', + name: 'https://elastic.co', + id: '420754e9-40f2-486c-bc2e-265bafd735c5', + origin: 'ui', + 'run_from.id': 'A private location', + 'run_from.geo.name': 'A private location', + enabled: true, + schedule: '@every 2m', + timeout: null, + throttling: throttling === 'false' ? false : throttling, + 'source.inline.script': + "step('Go to https://elastic.co', async () => {\n await page.goto('https://elastic.co');\n});", + screenshots: 'on', + processors: [ + { + add_fields: { + target: '', + fields: { + 'monitor.fleet_managed': true, + config_id: '420754e9-40f2-486c-bc2e-265bafd735c5', + }, + }, + }, + ], + }, + }, + { + enabled: true, + data_stream: { + type: 'synthetics', + dataset: 'browser.network', + elasticsearch: { + privileges: { indices: ['auto_configure', 'create_doc', 'read'] }, + }, + }, + id: 'synthetics/browser-browser.network-420754e9-40f2-486c-bc2e-265bafd735c5-fe200580-dee2-11ed-933e-0f85f8c5dd40-default', + compiled_stream: { + processors: [ + { add_fields: { target: '', fields: { 'monitor.fleet_managed': true } } }, + ], + }, + }, + { + enabled: true, + data_stream: { + type: 'synthetics', + dataset: 'browser.screenshot', + elasticsearch: { + privileges: { indices: ['auto_configure', 'create_doc', 'read'] }, + }, + }, + id: 'synthetics/browser-browser.screenshot-420754e9-40f2-486c-bc2e-265bafd735c5-fe200580-dee2-11ed-933e-0f85f8c5dd40-default', + compiled_stream: { + processors: [ + { add_fields: { target: '', fields: { 'monitor.fleet_managed': true } } }, + ], + }, + }, + ], + }, + ], + is_managed: true, + revision: 1, + created_at: '2023-04-19T18:55:35.126Z', + created_by: 'system', + updated_at: '2023-04-19T18:55:35.126Z', + updated_by: 'system', + }, + references: [], + coreMigrationVersion: '8.8.0', + updated_at: '2023-04-19T18:55:35.250Z', + created_at: '2023-04-19T18:55:35.250Z', + typeMigrationVersion: '8.7.0', + } as unknown as SavedObjectUnsanitizedDoc); diff --git a/x-pack/plugins/fleet/server/saved_objects/migrations/synthetics/index.ts b/x-pack/plugins/fleet/server/saved_objects/migrations/synthetics/index.ts new file mode 100644 index 0000000000000..b38d03e10a104 --- /dev/null +++ b/x-pack/plugins/fleet/server/saved_objects/migrations/synthetics/index.ts @@ -0,0 +1,8 @@ +/* + * 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 { migratePackagePolicyToV880 } from './to_v8_8_0'; diff --git a/x-pack/plugins/fleet/server/saved_objects/migrations/synthetics/to_v8_8_0.test.ts b/x-pack/plugins/fleet/server/saved_objects/migrations/synthetics/to_v8_8_0.test.ts new file mode 100644 index 0000000000000..23d7462872c39 --- /dev/null +++ b/x-pack/plugins/fleet/server/saved_objects/migrations/synthetics/to_v8_8_0.test.ts @@ -0,0 +1,167 @@ +/* + * 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 { SavedObjectMigrationContext } from '@kbn/core/server'; + +import { getBrowserPolicy, httpPolicy, icmpPolicy, tcpPolicy } from './fixtures/8.7.0'; + +import { migratePackagePolicyToV880 as migration } from './to_v8_8_0'; + +describe('8.8.0 Synthetics Package Policy migration', () => { + describe('schedule migration', () => { + const testSchedules = [ + ['4', '3'], + ['4.5', '5'], + ['7', '5'], + ['8', '10'], + ['9.5', '10'], + ['12', '10'], + ['13', '15'], + ['16', '15'], + ['18', '20'], + ['21', '20'], + ['25', '20'], + ['26', '30'], + ['31', '30'], + ['45', '30'], + ['46', '60'], + ['61', '60'], + ['90', '60'], + ['91', '120'], + ['121', '120'], + ['195', '240'], + ['600', '240'], + ]; + + it.each(testSchedules)('handles a variety of schedules', (invalidSchedule, validSchedule) => { + const actual = migration( + { + ...httpPolicy, + attributes: { + ...httpPolicy.attributes, + inputs: [ + { + ...httpPolicy.attributes.inputs[0], + streams: [ + { + ...httpPolicy.attributes.inputs[0].streams[0], + vars: { + ...httpPolicy.attributes.inputs[0].streams[0].vars, + schedule: { + value: `"@every ${invalidSchedule}m"`, + type: 'text', + }, + }, + }, + ], + }, + ], + }, + }, + {} as SavedObjectMigrationContext + ); + expect(actual.attributes?.inputs[0]?.streams[0]?.vars?.schedule?.value).toEqual( + `"@every ${validSchedule}m"` + ); + expect(actual.attributes?.inputs[0]?.streams[0]?.compiled_stream?.schedule).toEqual( + `@every ${validSchedule}m` + ); + }); + + it('handles browserPolicy with 2 minute', () => { + const actual = migration(getBrowserPolicy(), {} as SavedObjectMigrationContext); + expect(actual.attributes?.inputs[3]?.streams[0]?.vars?.schedule?.value).toEqual( + '"@every 1m"' + ); + expect(actual.attributes?.inputs[3]?.streams[0]?.compiled_stream?.schedule).toEqual( + `@every 1m` + ); + }); + + it('handles httpPolicy with 4 minute schedule', () => { + const actual = migration(httpPolicy, {} as SavedObjectMigrationContext); + expect(actual.attributes?.inputs[0]?.streams[0]?.vars?.schedule?.value).toEqual( + '"@every 3m"' + ); + expect(actual.attributes?.inputs[0]?.streams[0]?.compiled_stream?.schedule).toEqual( + `@every 3m` + ); + }); + + it('handles tcp with 8 minute schedule', () => { + const actual = migration(tcpPolicy, {} as SavedObjectMigrationContext); + expect(actual.attributes?.inputs[1]?.streams[0]?.vars?.schedule?.value).toEqual( + '"@every 10m"' + ); + expect(actual.attributes?.inputs[1]?.streams[0]?.compiled_stream?.schedule).toEqual( + `@every 10m` + ); + }); + + it('handles icmpPolicy with 16 minute schedule', () => { + const actual = migration(icmpPolicy, {} as SavedObjectMigrationContext); + expect(actual.attributes?.inputs[2]?.streams[0]?.vars?.schedule?.value).toEqual( + '"@every 15m"' + ); + expect(actual.attributes?.inputs[2]?.streams[0]?.compiled_stream?.schedule).toEqual( + `@every 15m` + ); + }); + }); + + describe('throttling migration', () => { + it('handles throtling config for throttling: false', () => { + const actual = migration(getBrowserPolicy('false'), {} as SavedObjectMigrationContext); + expect(actual.attributes?.inputs[3]?.streams[0]?.vars?.['throttling.config']?.value).toEqual( + 'false' + ); + expect(actual.attributes?.inputs[3]?.streams[0]?.compiled_stream?.throttling).toEqual(false); + }); + + it('handles throttling config for default throttling', () => { + const actual = migration(getBrowserPolicy(), {} as SavedObjectMigrationContext); + expect(actual.attributes?.inputs[3]?.streams[0]?.vars?.['throttling.config']?.value).toEqual( + JSON.stringify({ download: 5, upload: 3, latency: 20 }) + ); + expect(actual.attributes?.inputs[3]?.streams[0]?.compiled_stream.throttling).toEqual({ + download: 5, + upload: 3, + latency: 20, + }); + }); + + it('handles throttling config for custom throttling', () => { + const actual = migration( + getBrowserPolicy('1.6d/0.75u/150l'), + {} as SavedObjectMigrationContext + ); + expect(actual.attributes?.inputs[3]?.streams[0]?.vars?.['throttling.config']?.value).toEqual( + JSON.stringify({ download: 1.6, upload: 0.75, latency: 150 }) + ); + expect(actual.attributes?.inputs[3]?.streams[0]?.compiled_stream.throttling).toEqual({ + download: 1.6, + upload: 0.75, + latency: 150, + }); + }); + + it('handles edge cases', () => { + const actual = migration( + getBrowserPolicy('not a valid value'), + {} as SavedObjectMigrationContext + ); + expect(actual.attributes?.inputs[3]?.streams[0]?.vars?.['throttling.config']?.value).toEqual( + JSON.stringify({ download: 5, upload: 3, latency: 20 }) + ); + expect(actual.attributes?.inputs[3]?.streams[0]?.compiled_stream.throttling).toEqual({ + download: 5, + upload: 3, + latency: 20, + }); + }); + }); +}); diff --git a/x-pack/plugins/fleet/server/saved_objects/migrations/synthetics/to_v8_8_0.ts b/x-pack/plugins/fleet/server/saved_objects/migrations/synthetics/to_v8_8_0.ts new file mode 100644 index 0000000000000..872313471ae9b --- /dev/null +++ b/x-pack/plugins/fleet/server/saved_objects/migrations/synthetics/to_v8_8_0.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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import type { SavedObjectMigrationFn, SavedObjectUnsanitizedDoc } from '@kbn/core/server'; + +import type { PackagePolicy } from '../../../../common'; + +export const ALLOWED_SCHEDULES_IN_MINUTES = [ + '1', + '3', + '5', + '10', + '15', + '20', + '30', + '60', + '120', + '240', +]; + +export const migratePackagePolicyToV880: SavedObjectMigrationFn = ( + packagePolicyDoc +) => { + if (packagePolicyDoc.attributes.package?.name !== 'synthetics') { + return packagePolicyDoc; + } + + const updatedPackagePolicyDoc: SavedObjectUnsanitizedDoc = packagePolicyDoc; + + const enabledInput = updatedPackagePolicyDoc.attributes.inputs.find( + (input) => input.enabled === true + ); + const enabledStream = enabledInput?.streams.find((stream) => { + return ['browser', 'http', 'icmp', 'tcp'].includes(stream.data_stream.dataset); + }); + if (!enabledStream) { + return updatedPackagePolicyDoc; + } + + if ( + enabledStream.vars && + enabledStream.vars.schedule?.value && + enabledStream.compiled_stream?.schedule + ) { + const schedule = enabledStream.vars.schedule.value.match(/\d+\.?\d*/g)?.[0]; + const updatedSchedule = getNearestSupportedSchedule(schedule); + const formattedUpdatedSchedule = `@every ${updatedSchedule}m`; + enabledStream.vars.schedule.value = `"${formattedUpdatedSchedule}"`; + enabledStream.compiled_stream.schedule = formattedUpdatedSchedule; + } + + if ( + enabledStream.data_stream.dataset === 'browser' && + enabledStream.vars?.['throttling.config'] && + enabledStream.compiled_stream?.throttling + ) { + const throttling = enabledStream.vars['throttling.config'].value; + if (throttling) { + const formattedThrottling = handleThrottling(throttling); + enabledStream.vars['throttling.config'].value = JSON.stringify(formattedThrottling); + enabledStream.compiled_stream.throttling = formattedThrottling; + } + } + + return updatedPackagePolicyDoc; +}; + +const handleThrottling = ( + throttling: string +): { download: number; upload: number; latency: number } => { + try { + const [download = 5, upload = 3, latency = 20] = throttling.match(/\d+\.?\d*/g) || []; + return { + download: Number(download), + upload: Number(upload), + latency: Number(latency), + }; + } catch { + return { + download: 5, + upload: 3, + latency: 20, + }; + } +}; + +const getNearestSupportedSchedule = (currentSchedule: string): string => { + try { + const closest = ALLOWED_SCHEDULES_IN_MINUTES.reduce(function (prev, curr) { + const supportedSchedule = parseFloat(curr); + const currSchedule = parseFloat(currentSchedule); + const prevSupportedSchedule = parseFloat(prev); + return Math.abs(supportedSchedule - currSchedule) < + Math.abs(prevSupportedSchedule - currSchedule) + ? curr + : prev; + }); + + return closest; + } catch { + return '10'; + } +}; diff --git a/x-pack/plugins/fleet/server/saved_objects/migrations/to_v8_8_0.ts b/x-pack/plugins/fleet/server/saved_objects/migrations/to_v8_8_0.ts new file mode 100644 index 0000000000000..ab68e4e1c1429 --- /dev/null +++ b/x-pack/plugins/fleet/server/saved_objects/migrations/to_v8_8_0.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 { SavedObjectMigrationFn } from '@kbn/core/server'; + +import type { PackagePolicy } from '../../../common'; + +import { migratePackagePolicyToV880 as SecSolMigratePackagePolicyToV880 } from './security_solution'; +import { migratePackagePolicyToV880 as SyntheticsMigratePackagePolicyToV880 } from './synthetics'; + +export const migratePackagePolicyToV880: SavedObjectMigrationFn = ( + packagePolicyDoc, + migrationContext +) => { + let updatedPackagePolicyDoc = packagePolicyDoc; + + // Endpoint specific migrations + if (packagePolicyDoc.attributes.package?.name === 'endpoint') { + updatedPackagePolicyDoc = SecSolMigratePackagePolicyToV880(packagePolicyDoc, migrationContext); + } + + // Synthetics specific migrations + if (packagePolicyDoc.attributes.package?.name === 'synthetics') { + updatedPackagePolicyDoc = SyntheticsMigratePackagePolicyToV880( + packagePolicyDoc, + migrationContext + ); + } + + return updatedPackagePolicyDoc; +}; diff --git a/x-pack/plugins/fleet/server/types/models/package_policy.ts b/x-pack/plugins/fleet/server/types/models/package_policy.ts index b94becf1fe449..8e60621102164 100644 --- a/x-pack/plugins/fleet/server/types/models/package_policy.ts +++ b/x-pack/plugins/fleet/server/types/models/package_policy.ts @@ -99,6 +99,7 @@ const PackagePolicyBaseSchema = { namespace: NamespaceSchema, policy_id: schema.string(), enabled: schema.boolean(), + is_managed: schema.maybe(schema.boolean()), package: schema.maybe( schema.object({ name: schema.string(), diff --git a/x-pack/plugins/infra/server/lib/alerting/common/messages.ts b/x-pack/plugins/infra/server/lib/alerting/common/messages.ts index 9a7c9d2764486..6bc48083d252b 100644 --- a/x-pack/plugins/infra/server/lib/alerting/common/messages.ts +++ b/x-pack/plugins/infra/server/lib/alerting/common/messages.ts @@ -138,7 +138,7 @@ export const buildNoDataAlertReason: (alertResult: { timeUnit: string; }) => string = ({ group, metric, timeSize, timeUnit }) => i18n.translate('xpack.infra.metrics.alerting.threshold.noDataAlertReason', { - defaultMessage: '{metric} reported no data in the last {interval} for {group}', + defaultMessage: '{metric} reported no data in the last {interval}{group}', values: { metric, interval: `${timeSize}${timeUnit}`, diff --git a/x-pack/plugins/infra/server/lib/alerting/metric_threshold/metric_threshold_executor.test.ts b/x-pack/plugins/infra/server/lib/alerting/metric_threshold/metric_threshold_executor.test.ts index 9298b7583ef3d..fb8121d224187 100644 --- a/x-pack/plugins/infra/server/lib/alerting/metric_threshold/metric_threshold_executor.test.ts +++ b/x-pack/plugins/infra/server/lib/alerting/metric_threshold/metric_threshold_executor.test.ts @@ -70,9 +70,11 @@ const logger = { get: () => logger, } as unknown as Logger; +const STARTED_AT_MOCK_DATE = new Date(); + const mockOptions = { executionId: '', - startedAt: new Date(), + startedAt: STARTED_AT_MOCK_DATE, previousStartedAt: null, state: { wrapped: initialRuleState, @@ -128,7 +130,8 @@ const setEvaluationResults = (response: Array>) => { jest.requireMock('./lib/evaluate_rule').evaluateRule.mockImplementation(() => response); }; -describe('The metric threshold alert type', () => { +// FAILING: https://github.com/elastic/kibana/issues/155534 +describe.skip('The metric threshold alert type', () => { describe('querying the entire infrastructure', () => { afterAll(() => clearInstances()); const instanceID = '*'; @@ -1326,7 +1329,9 @@ describe('The metric threshold alert type', () => { }, ]); await execute(true); - expect(mostRecentAction(instanceID)).toBeNoDataAction(); + const recentAction = mostRecentAction(instanceID); + expect(recentAction.action.reason).toEqual('test.metric.3 reported no data in the last 1m'); + expect(recentAction).toBeNoDataAction(); }); test('does not send a No Data alert when not configured to do so', async () => { setEvaluationResults([ @@ -1350,6 +1355,68 @@ describe('The metric threshold alert type', () => { }); }); + describe('alerts with NO_DATA where one condtion is an aggregation and the other is a document count', () => { + afterAll(() => clearInstances()); + const instanceID = '*'; + const execute = (alertOnNoData: boolean, sourceId: string = 'default') => + executor({ + ...mockOptions, + services, + params: { + sourceId, + criteria: [ + { + ...baseNonCountCriterion, + comparator: Comparator.GT, + threshold: [1], + metric: 'test.metric.3', + }, + { + ...baseCountCriterion, + comparator: Comparator.GT, + threshold: [30], + }, + ], + alertOnNoData, + }, + }); + test('sends a No Data alert when configured to do so', async () => { + setEvaluationResults([ + { + '*': { + ...baseNonCountCriterion, + comparator: Comparator.LT, + threshold: [1], + metric: 'test.metric.3', + currentValue: null, + timestamp: STARTED_AT_MOCK_DATE.toISOString(), + shouldFire: false, + shouldWarn: false, + isNoData: true, + bucketKey: { groupBy0: '*' }, + }, + }, + {}, + ]); + await execute(true); + const recentAction = mostRecentAction(instanceID); + expect(recentAction.action).toEqual({ + alertDetailsUrl: 'http://localhost:5601/app/observability/alerts/mock-alert-uuid', + alertState: 'NO DATA', + group: '*', + groupByKeys: undefined, + metric: { condition0: 'test.metric.3', condition1: 'count' }, + reason: 'test.metric.3 reported no data in the last 1m', + threshold: { condition0: ['1'], condition1: [30] }, + timestamp: STARTED_AT_MOCK_DATE.toISOString(), + value: { condition0: '[NO DATA]', condition1: 0 }, + viewInAppUrl: 'http://localhost:5601/app/metrics/explorer', + tags: [], + }); + expect(recentAction).toBeNoDataAction(); + }); + }); + describe('querying a groupBy alert that starts reporting no data, and then later reports data', () => { afterAll(() => clearInstances()); const instanceID = '*'; diff --git a/x-pack/plugins/infra/server/lib/alerting/metric_threshold/metric_threshold_executor.ts b/x-pack/plugins/infra/server/lib/alerting/metric_threshold/metric_threshold_executor.ts index 7037e6398fc24..46d2766777d21 100644 --- a/x-pack/plugins/infra/server/lib/alerting/metric_threshold/metric_threshold_executor.ts +++ b/x-pack/plugins/infra/server/lib/alerting/metric_threshold/metric_threshold_executor.ts @@ -269,7 +269,7 @@ export const createMetricThresholdExecutor = (libs: InfraBackendLibs) => // check to see if a No Data state has occurred if (nextState === AlertStates.NO_DATA) { reason = alertResults - .filter((result) => result[group].isNoData) + .filter((result) => result[group]?.isNoData) .map((result) => buildNoDataAlertReason({ ...result[group], group })) .join('\n'); } @@ -316,17 +316,30 @@ export const createMetricThresholdExecutor = (libs: InfraBackendLibs) => alertState: stateToAlertMessage[nextState], group, groupByKeys: groupByKeysObjectMapping[group], - metric: mapToConditionsLookup(criteria, (c) => c.metric), + metric: mapToConditionsLookup(criteria, (c) => { + if (c.aggType === 'count') { + return 'count'; + } + return c.metric; + }), reason, - threshold: mapToConditionsLookup( - alertResults, - (result) => formatAlertResult(result[group]).threshold - ), + threshold: mapToConditionsLookup(alertResults, (result, index) => { + const evaluation = result[group]; + if (!evaluation) { + return criteria[index].threshold; + } + return formatAlertResult(evaluation).threshold; + }), timestamp, - value: mapToConditionsLookup( - alertResults, - (result) => formatAlertResult(result[group]).currentValue - ), + value: mapToConditionsLookup(alertResults, (result, index) => { + const evaluation = result[group]; + if (!evaluation && criteria[index].aggType === 'count') { + return 0; + } else if (!evaluation) { + return null; + } + return formatAlertResult(evaluation).currentValue; + }), viewInAppUrl: getViewInMetricsAppUrl(libs.basePath, spaceId), ...additionalContext, }); @@ -354,7 +367,12 @@ export const createMetricThresholdExecutor = (libs: InfraBackendLibs) => alertState: stateToAlertMessage[AlertStates.OK], group: recoveredAlertId, groupByKeys: groupByKeysObjectForRecovered[recoveredAlertId], - metric: mapToConditionsLookup(criteria, (c) => c.metric), + metric: mapToConditionsLookup(criteria, (c) => { + if (criteria.aggType === 'count') { + return 'count'; + } + return c.metric; + }), timestamp: startedAt.toISOString(), threshold: mapToConditionsLookup(criteria, (c) => c.threshold), viewInAppUrl: getViewInMetricsAppUrl(libs.basePath, spaceId), diff --git a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_editor/__jest__/processors/attachment.test.tsx b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_editor/__jest__/processors/attachment.test.tsx new file mode 100644 index 0000000000000..b4cfbf3046db0 --- /dev/null +++ b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_editor/__jest__/processors/attachment.test.tsx @@ -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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { act } from 'react-dom/test-utils'; +import { setup, SetupResult, getProcessorValue, setupEnvironment } from './processor.helpers'; + +const ATTACHMENT_TYPE = 'attachment'; + +describe('Processor: Attachment', () => { + let onUpdate: jest.Mock; + let testBed: SetupResult; + const { httpSetup } = setupEnvironment(); + + beforeAll(() => { + jest.useFakeTimers({ legacyFakeTimers: true }); + }); + + afterAll(() => { + jest.useRealTimers(); + }); + + beforeEach(async () => { + onUpdate = jest.fn(); + + await act(async () => { + testBed = await setup(httpSetup, { + value: { + processors: [], + }, + onFlyoutOpen: jest.fn(), + onUpdate, + }); + }); + + const { component, actions } = testBed; + + component.update(); + + // Open flyout to add new processor + actions.addProcessor(); + // Add type (the other fields are not visible until a type is selected) + await actions.addProcessorType(ATTACHMENT_TYPE); + }); + + test('prevents form submission if required fields are not provided', async () => { + const { + actions: { saveNewProcessor }, + form, + } = testBed; + + // Click submit button with only the type defined + await saveNewProcessor(); + + // Expect form error as "field" is a required parameter + expect(form.getErrorsMessages()).toEqual([ + 'A field value is required.', // "Field" input + ]); + }); + + test('saves with default parameter values', async () => { + const { + actions: { saveNewProcessor }, + form, + } = testBed; + + // Add "field" value + form.setInputValue('fieldNameField.input', 'test_attachment_processor'); + + // Save the field + await saveNewProcessor(); + + const processors = getProcessorValue(onUpdate, ATTACHMENT_TYPE); + + expect(processors[0][ATTACHMENT_TYPE]).toEqual({ + field: 'test_attachment_processor', + }); + }); + + test('saves with optional parameter values', async () => { + const { + actions: { saveNewProcessor }, + form, + find, + component, + } = testBed; + + // Add required fields + form.setInputValue('fieldNameField.input', 'test_attachment_processor'); + + // Add optional fields + form.setInputValue('targetField.input', 'test_target'); + form.setInputValue('indexedCharsField.input', '123456'); + form.setInputValue('indexedCharsFieldField.input', 'indexed_chars_field'); + form.toggleEuiSwitch('removeBinaryField.input'); + form.setInputValue('resourceNameField.input', 'resource_name_field'); + + // Add "networkDirectionField" value (required) + await act(async () => { + find('propertiesField').simulate('change', [{ label: 'content' }]); + }); + component.update(); + + // Save the field + await saveNewProcessor(); + + const processors = getProcessorValue(onUpdate, ATTACHMENT_TYPE); + + expect(processors[0][ATTACHMENT_TYPE]).toEqual({ + field: 'test_attachment_processor', + target_field: 'test_target', + properties: ['content'], + indexed_chars: '123456', + indexed_chars_field: 'indexed_chars_field', + remove_binary: true, + resource_name: 'resource_name_field', + }); + }); +}); diff --git a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_editor/__jest__/processors/processor.helpers.tsx b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_editor/__jest__/processors/processor.helpers.tsx index 43ebf84f6ef30..125d8758ca0d0 100644 --- a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_editor/__jest__/processors/processor.helpers.tsx +++ b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_editor/__jest__/processors/processor.helpers.tsx @@ -190,6 +190,11 @@ type TestSubject = | 'droppableList.input-2' | 'prefixField.input' | 'suffixField.input' + | 'indexedCharsField.input' + | 'indexedCharsFieldField.input' + | 'removeBinaryField.input' + | 'resourceNameField.input' + | 'propertiesField' | 'tileTypeField' | 'targetFormatField' | 'parentField.input' diff --git a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_editor/components/processor_form/processors/attachment.tsx b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_editor/components/processor_form/processors/attachment.tsx new file mode 100644 index 0000000000000..57f26bdaf204f --- /dev/null +++ b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_editor/components/processor_form/processors/attachment.tsx @@ -0,0 +1,202 @@ +/* + * 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, { FunctionComponent } from 'react'; +import { i18n } from '@kbn/i18n'; +import { FormattedMessage } from '@kbn/i18n-react'; +import { EuiCode, EuiFlexGroup, EuiFlexItem, EuiSpacer } from '@elastic/eui'; + +import { + ComboBoxField, + FIELD_TYPES, + UseField, + ToggleField, + Field, +} from '../../../../../../shared_imports'; + +import { FieldNameField } from './common_fields/field_name_field'; +import { TargetField } from './common_fields/target_field'; +import { IgnoreMissingField } from './common_fields/ignore_missing_field'; +import { FieldsConfig, to, from } from './shared'; + +const propertyValues: string[] = [ + 'content', + 'title', + 'author', + 'keywords', + 'date', + 'content_type', + 'content_length', + 'language', +]; + +const fieldsConfig: FieldsConfig = { + /* Optional field configs */ + indexed_chars: { + type: FIELD_TYPES.NUMBER, + serializer: from.emptyStringToUndefined, + label: i18n.translate( + 'xpack.ingestPipelines.pipelineEditor.attachment.indexedCharsFieldLabel', + { + defaultMessage: 'Indexed chars (optional)', + } + ), + helpText: ( + {'100000'} }} + /> + ), + }, + indexed_chars_field: { + type: FIELD_TYPES.TEXT, + serializer: from.emptyStringToUndefined, + label: i18n.translate( + 'xpack.ingestPipelines.pipelineEditor.attachment.indexedCharsFieldFieldLabel', + { + defaultMessage: 'Indexed chars field (optional)', + } + ), + helpText: ( + {'null'} }} + /> + ), + }, + properties: { + type: FIELD_TYPES.COMBO_BOX, + deserializer: to.arrayOfStrings, + serializer: from.optionalArrayOfStrings, + label: i18n.translate('xpack.ingestPipelines.pipelineEditor.attachment.propertiesFieldLabel', { + defaultMessage: 'Properties (optional)', + }), + helpText: ( + {'all'} }} + /> + ), + }, + remove_binary: { + type: FIELD_TYPES.TOGGLE, + defaultValue: false, + deserializer: to.booleanOrUndef, + serializer: from.undefinedIfValue(false), + label: i18n.translate( + 'xpack.ingestPipelines.pipelineEditor.attachment.removeBinaryFieldLabel', + { + defaultMessage: 'Remove binary', + } + ), + helpText: i18n.translate( + 'xpack.ingestPipelines.pipelineEditor.attachment.removeBinaryFieldHelpText', + { + defaultMessage: 'If enabled, the binary field will be removed from the document.', + } + ), + }, + resource_name: { + type: FIELD_TYPES.TEXT, + serializer: from.emptyStringToUndefined, + label: i18n.translate( + 'xpack.ingestPipelines.pipelineEditor.attachment.resourceNameFieldLabel', + { + defaultMessage: 'Resource name (optional)', + } + ), + helpText: ( + + ), + }, +}; + +export const Attachment: FunctionComponent = () => { + return ( + <> + + + + } + /> + + + {'attachment'} }} + /> + } + /> + + + + + + + + + + + + + + + + + ({ label })), + }} + path="fields.properties" + /> + + + + + + + + ); +}; diff --git a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_editor/components/processor_form/processors/index.ts b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_editor/components/processor_form/processors/index.ts index 3512dafdd2854..210d113bd2aba 100644 --- a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_editor/components/processor_form/processors/index.ts +++ b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_editor/components/processor_form/processors/index.ts @@ -8,6 +8,7 @@ // please try to keep this list sorted by module name (e.g. './bar' before './foo') export { Append } from './append'; +export { Attachment } from './attachment'; export { Bytes } from './bytes'; export { Circle } from './circle'; export { CommunityId } from './community_id'; diff --git a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_editor/components/shared/map_processor_type_to_form.tsx b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_editor/components/shared/map_processor_type_to_form.tsx index 75bbd764097ad..60f66dbb415f3 100644 --- a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_editor/components/shared/map_processor_type_to_form.tsx +++ b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_editor/components/shared/map_processor_type_to_form.tsx @@ -14,6 +14,7 @@ import { LicenseType } from '../../../../../types'; import { Append, + Attachment, Bytes, Circle, CommunityId, @@ -100,6 +101,23 @@ export const mapProcessorTypeToDescriptor: MapProcessorTypeToDescriptor = { }, }), }, + attachment: { + FieldsComponent: Attachment, + docLinkPath: '/attachment.html', + label: i18n.translate('xpack.ingestPipelines.processors.label.attachment', { + defaultMessage: 'Attachment', + }), + typeDescription: i18n.translate('xpack.ingestPipelines.processors.description.attachment', { + defaultMessage: 'Extract file attachments in common formats (such as PPT, XLS, and PDF).', + }), + getDefaultDescription: ({ field }) => + i18n.translate('xpack.ingestPipelines.processors.defaultDescription.attachment', { + defaultMessage: 'Extracts attachment from "{field}"', + values: { + field, + }, + }), + }, bytes: { FieldsComponent: Bytes, docLinkPath: '/bytes-processor.html', diff --git a/x-pack/plugins/lens/public/datasources/form_based/operations/layer_helpers.test.ts b/x-pack/plugins/lens/public/datasources/form_based/operations/layer_helpers.test.ts index 0490f3c479297..0bdf9aa0bc795 100644 --- a/x-pack/plugins/lens/public/datasources/form_based/operations/layer_helpers.test.ts +++ b/x-pack/plugins/lens/public/datasources/form_based/operations/layer_helpers.test.ts @@ -29,10 +29,12 @@ import { getFieldByNameFactory } from '../pure_helpers'; import { generateId } from '../../../id_generator'; import { createMockedFullReference, createMockedManagedReference } from './mocks'; import { + CounterRateIndexPatternColumn, FiltersIndexPatternColumn, FormulaIndexPatternColumn, GenericIndexPatternColumn, MathIndexPatternColumn, + MaxIndexPatternColumn, MovingAverageIndexPatternColumn, OperationDefinition, } from './definitions'; @@ -1356,6 +1358,47 @@ describe('state_helpers', () => { }).columns.col1 ).toEqual(expect.objectContaining({ label: 'Average of bytes' })); }); + + it('should update default label when referenced column gets a field change', () => { + expect( + replaceColumn({ + layer: { + indexPatternId: '1', + columnOrder: ['col1'], + columns: { + col1: { + label: 'MyDefaultLabel', + dataType: 'number', + operationType: 'counter_rate', + isBucketed: false, + scale: 'ratio', + references: ['col2'], + timeScale: 's', + timeShift: '', + filter: undefined, + params: undefined, + } as CounterRateIndexPatternColumn, + col2: { + label: 'Max of bytes', + dataType: 'number', + operationType: 'max', + scale: 'ratio', + sourceField: indexPattern.fields[2].displayName, + } as MaxIndexPatternColumn, + }, + }, + indexPattern, + columnId: 'col2', + op: 'max', + field: indexPattern.fields[3], + visualizationGroups: [], + }).columns.col1 + ).toEqual( + expect.objectContaining({ + label: 'Counter rate of memory per second', + }) + ); + }); }); it('should execute adjustments for other columns', () => { 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 efa523a5bd5da..e6ac80c439083 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 @@ -836,12 +836,16 @@ export function replaceColumn({ { ...layer, columns: { ...layer.columns, [columnId]: newColumn } }, columnId ); - return adjustColumnReferencesForChangedColumn( - { - ...newLayer, - columnOrder: getColumnOrder(newLayer), - }, - columnId + + return updateDefaultLabels( + adjustColumnReferencesForChangedColumn( + { + ...newLayer, + columnOrder: getColumnOrder(newLayer), + }, + columnId + ), + indexPattern ); } else if (operationDefinition.input === 'managedReference') { // Just changing a param in a formula column should trigger diff --git a/x-pack/plugins/lists/public/exceptions/api.test.ts b/x-pack/plugins/lists/public/exceptions/api.test.ts index 95675fe96aee8..a0f2bf9c21fe4 100644 --- a/x-pack/plugins/lists/public/exceptions/api.test.ts +++ b/x-pack/plugins/lists/public/exceptions/api.test.ts @@ -11,6 +11,7 @@ import { addExceptionListItem, deleteExceptionListById, deleteExceptionListItemById, + duplicateExceptionList, exportExceptionList, fetchExceptionListById, fetchExceptionListItemById, @@ -728,4 +729,30 @@ describe('Exceptions Lists API', () => { expect(exceptionResponse).toEqual(blob); }); }); + + describe('#duplicateExceptionList', () => { + beforeEach(() => { + httpMock.fetch.mockResolvedValue(getExceptionListSchemaMock()); + }); + + test('it invokes "duplicateExceptionList" with expected url and body values', async () => { + await duplicateExceptionList({ + http: httpMock, + includeExpiredExceptions: false, + listId: 'my_list', + namespaceType: 'single', + signal: abortCtrl.signal, + }); + + expect(httpMock.fetch).toHaveBeenCalledWith('/api/exception_lists/_duplicate', { + method: 'POST', + query: { + include_expired_exceptions: false, + list_id: 'my_list', + namespace_type: 'single', + }, + signal: abortCtrl.signal, + }); + }); + }); }); diff --git a/x-pack/plugins/lists/public/exceptions/hooks/use_api.test.ts b/x-pack/plugins/lists/public/exceptions/hooks/use_api.test.ts index bf10fb57f1a5c..55c8ebde7cebd 100644 --- a/x-pack/plugins/lists/public/exceptions/hooks/use_api.test.ts +++ b/x-pack/plugins/lists/public/exceptions/hooks/use_api.test.ts @@ -12,6 +12,7 @@ import type { AddExceptionListItemProps, ApiCallByIdProps, ApiCallByListIdProps, + DuplicateExceptionListProps, UpdateExceptionListItemProps, } from '@kbn/securitysolution-io-ts-list-types'; import { coreMock } from '@kbn/core/public/mocks'; @@ -462,4 +463,61 @@ describe('useApi', () => { }); }); }); + + describe('duplicateExceptionList', () => { + test('it invokes "onSuccess" when duplication does not throw', async () => { + const onSuccessMock = jest.fn(); + const spyOnDuplicateExceptionList = jest + .spyOn(api, 'duplicateExceptionList') + .mockResolvedValue(getExceptionListSchemaMock()); + + await act(async () => { + const { result, waitForNextUpdate } = renderHook(() => + useApi(mockKibanaHttpService) + ); + await waitForNextUpdate(); + + await result.current.duplicateExceptionList({ + includeExpiredExceptions: false, + listId: 'my_list', + namespaceType: 'single', + onError: jest.fn(), + onSuccess: onSuccessMock, + }); + + const expected: DuplicateExceptionListProps = { + http: mockKibanaHttpService, + includeExpiredExceptions: false, + listId: 'my_list', + namespaceType: 'single', + signal: new AbortController().signal, + }; + + expect(spyOnDuplicateExceptionList).toHaveBeenCalledWith(expected); + expect(onSuccessMock).toHaveBeenCalled(); + }); + }); + + test('invokes "onError" callback if "duplicateExceptionList" fails', async () => { + const mockError = new Error('failed to duplicate item'); + jest.spyOn(api, 'duplicateExceptionList').mockRejectedValue(mockError); + + await act(async () => { + const { result, waitForNextUpdate } = renderHook(() => + useApi(mockKibanaHttpService) + ); + await waitForNextUpdate(); + + await result.current.duplicateExceptionList({ + includeExpiredExceptions: false, + listId: 'my_list', + namespaceType: 'single', + onError: onErrorMock, + onSuccess: jest.fn(), + }); + + expect(onErrorMock).toHaveBeenCalledWith(mockError); + }); + }); + }); }); diff --git a/x-pack/plugins/lists/server/routes/duplicate_exception_list_route.ts b/x-pack/plugins/lists/server/routes/duplicate_exception_list_route.ts new file mode 100644 index 0000000000000..2724215971aae --- /dev/null +++ b/x-pack/plugins/lists/server/routes/duplicate_exception_list_route.ts @@ -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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { transformError } from '@kbn/securitysolution-es-utils'; +import { + DuplicateExceptionListQuerySchemaDecoded, + duplicateExceptionListQuerySchema, + exceptionListSchema, +} from '@kbn/securitysolution-io-ts-list-types'; +import { validate } from '@kbn/securitysolution-io-ts-utils'; +import { EXCEPTION_LIST_URL } from '@kbn/securitysolution-list-constants'; + +import type { ListsPluginRouter } from '../types'; + +import { buildRouteValidation, buildSiemResponse, getExceptionListClient } from './utils'; + +export const duplicateExceptionsRoute = (router: ListsPluginRouter): void => { + router.post( + { + options: { + tags: ['access:lists-all'], + }, + path: `${EXCEPTION_LIST_URL}/_duplicate`, + validate: { + query: buildRouteValidation< + typeof duplicateExceptionListQuerySchema, + DuplicateExceptionListQuerySchemaDecoded + >(duplicateExceptionListQuerySchema), + }, + }, + async (context, request, response) => { + const siemResponse = buildSiemResponse(response); + + try { + const { + list_id: listId, + namespace_type: namespaceType, + include_expired_exceptions: includeExpiredExceptionsString, + } = request.query; + + const exceptionListsClient = await getExceptionListClient(context); + + // fetch list container + const listToDuplicate = await exceptionListsClient.getExceptionList({ + id: undefined, + listId, + namespaceType, + }); + + if (listToDuplicate == null) { + return siemResponse.error({ + body: `exception list id: "${listId}" does not exist`, + statusCode: 404, + }); + } + + // Defaults to including expired exceptions if query param is not present + const includeExpiredExceptions = + includeExpiredExceptionsString !== undefined + ? includeExpiredExceptionsString === 'true' + : true; + const duplicatedList = await exceptionListsClient.duplicateExceptionListAndItems({ + includeExpiredExceptions, + list: listToDuplicate, + namespaceType, + }); + + if (duplicatedList == null) { + return siemResponse.error({ + body: `unable to duplicate exception list with list_id: ${listId} - action not allowed`, + statusCode: 405, + }); + } + + const [validated, errors] = validate(duplicatedList, exceptionListSchema); + if (errors != null) { + return siemResponse.error({ body: errors, statusCode: 500 }); + } else { + return response.ok({ body: validated ?? {} }); + } + } catch (err) { + const error = transformError(err); + return siemResponse.error({ + body: error.message, + statusCode: error.statusCode, + }); + } + } + ); +}; diff --git a/x-pack/plugins/lists/server/routes/index.ts b/x-pack/plugins/lists/server/routes/index.ts index 851ed971c9b09..6347d564981bf 100644 --- a/x-pack/plugins/lists/server/routes/index.ts +++ b/x-pack/plugins/lists/server/routes/index.ts @@ -18,6 +18,7 @@ export * from './delete_exception_list_item_route'; export * from './delete_list_index_route'; export * from './delete_list_item_route'; export * from './delete_list_route'; +export * from './duplicate_exception_list_route'; export * from './export_exception_list_route'; export * from './export_list_item_route'; export * from './find_endpoint_list_item_route'; diff --git a/x-pack/plugins/lists/server/routes/init_routes.ts b/x-pack/plugins/lists/server/routes/init_routes.ts index 4649a82a8e4a1..7fd87c72765bd 100644 --- a/x-pack/plugins/lists/server/routes/init_routes.ts +++ b/x-pack/plugins/lists/server/routes/init_routes.ts @@ -22,6 +22,7 @@ import { deleteListIndexRoute, deleteListItemRoute, deleteListRoute, + duplicateExceptionsRoute, exportExceptionsRoute, exportListItemRoute, findEndpointListItemRoute, @@ -87,6 +88,7 @@ export const initRoutes = (router: ListsPluginRouter, config: ConfigType): void updateExceptionListRoute(router); deleteExceptionListRoute(router); findExceptionListRoute(router); + duplicateExceptionsRoute(router); // exception list items createExceptionListItemRoute(router); diff --git a/x-pack/plugins/lists/server/services/exception_lists/duplicate_exception_list.test.ts b/x-pack/plugins/lists/server/services/exception_lists/duplicate_exception_list.test.ts new file mode 100644 index 0000000000000..562c6ac674beb --- /dev/null +++ b/x-pack/plugins/lists/server/services/exception_lists/duplicate_exception_list.test.ts @@ -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 { savedObjectsClientMock } from '@kbn/core/server/mocks'; + +import { + getDetectionsExceptionListSchemaMock, + getTrustedAppsListSchemaMock, +} from '../../../common/schemas/response/exception_list_schema.mock'; +import { getExceptionListItemSchemaMock } from '../../../common/schemas/response/exception_list_item_schema.mock'; + +import { findExceptionListsItemPointInTimeFinder } from './find_exception_list_items_point_in_time_finder'; +import { duplicateExceptionListAndItems } from './duplicate_exception_list'; +import { getExceptionList } from './get_exception_list'; +import { createExceptionList } from './create_exception_list'; + +jest.mock('./get_exception_list'); +jest.mock('./create_exception_list'); +jest.mock('./bulk_create_exception_list_items'); +jest.mock('./find_exception_list_items_point_in_time_finder'); + +const mockCurrentTime = new Date('2023-02-01T10:20:30Z'); + +describe('duplicateExceptionListAndItems', () => { + beforeEach(() => { + jest.useFakeTimers(); + jest.setSystemTime(mockCurrentTime); + }); + afterEach(() => { + jest.resetAllMocks(); + jest.restoreAllMocks(); + }); + + test('should return null exception list is not of type "detection" or "rule_default"', async () => { + (getExceptionList as jest.Mock).mockResolvedValue(getTrustedAppsListSchemaMock()); + + const result = await duplicateExceptionListAndItems({ + includeExpiredExceptions: true, + list: getTrustedAppsListSchemaMock(), + namespaceType: 'single', + savedObjectsClient: savedObjectsClientMock.create(), + user: 'test-user', + }); + + expect(result).toBeNull(); + }); + + test('should duplicate a list with expired exceptions', async () => { + (getExceptionList as jest.Mock).mockResolvedValue(getDetectionsExceptionListSchemaMock()); + (createExceptionList as jest.Mock).mockResolvedValue({ + ...getDetectionsExceptionListSchemaMock(), + list_id: 'exception_list_id_dupe', + name: 'Test [Duplicate]', + }); + (findExceptionListsItemPointInTimeFinder as jest.Mock).mockImplementationOnce( + ({ executeFunctionOnStream }) => { + executeFunctionOnStream({ data: [getExceptionListItemSchemaMock()] }); + } + ); + + await duplicateExceptionListAndItems({ + includeExpiredExceptions: true, + list: getDetectionsExceptionListSchemaMock(), + namespaceType: 'single', + savedObjectsClient: savedObjectsClientMock.create(), + user: 'test-user', + }); + + expect(findExceptionListsItemPointInTimeFinder).toHaveBeenCalledWith({ + executeFunctionOnStream: expect.any(Function), + filter: [], + listId: ['exception_list_id'], + maxSize: 10000, + namespaceType: ['single'], + perPage: undefined, + savedObjectsClient: expect.any(Object), + sortField: undefined, + sortOrder: undefined, + }); + }); + + test('should duplicate a list without expired exceptions', async () => { + (getExceptionList as jest.Mock).mockResolvedValue(getDetectionsExceptionListSchemaMock()); + (createExceptionList as jest.Mock).mockResolvedValue({ + ...getDetectionsExceptionListSchemaMock(), + list_id: 'exception_list_id_dupe', + name: 'Test [Duplicate]', + }); + (findExceptionListsItemPointInTimeFinder as jest.Mock).mockImplementationOnce( + ({ executeFunctionOnStream }) => { + executeFunctionOnStream({ data: [getExceptionListItemSchemaMock()] }); + } + ); + + await duplicateExceptionListAndItems({ + includeExpiredExceptions: false, + list: getDetectionsExceptionListSchemaMock(), + namespaceType: 'single', + savedObjectsClient: savedObjectsClientMock.create(), + user: 'test-user', + }); + + expect(findExceptionListsItemPointInTimeFinder).toHaveBeenCalledWith({ + executeFunctionOnStream: expect.any(Function), + filter: [ + '(exception-list.attributes.expire_time > "2023-02-01T10:20:30.000Z" OR NOT exception-list.attributes.expire_time: *)', + ], + listId: ['exception_list_id'], + maxSize: 10000, + namespaceType: ['single'], + perPage: undefined, + savedObjectsClient: expect.any(Object), + sortField: undefined, + sortOrder: undefined, + }); + }); +}); diff --git a/x-pack/plugins/lists/server/services/exception_lists/duplicate_exception_list.ts b/x-pack/plugins/lists/server/services/exception_lists/duplicate_exception_list.ts index 4d4c8a07b455e..0bd17f8c39e2e 100644 --- a/x-pack/plugins/lists/server/services/exception_lists/duplicate_exception_list.ts +++ b/x-pack/plugins/lists/server/services/exception_lists/duplicate_exception_list.ts @@ -12,13 +12,12 @@ import { ExceptionListSchema, ExceptionListTypeEnum, FoundExceptionListItemSchema, - ListId, NamespaceType, } from '@kbn/securitysolution-io-ts-list-types'; +import { getSavedObjectType } from '@kbn/securitysolution-list-utils'; import { findExceptionListsItemPointInTimeFinder } from './find_exception_list_items_point_in_time_finder'; import { bulkCreateExceptionListItems } from './bulk_create_exception_list_items'; -import { getExceptionList } from './get_exception_list'; import { createExceptionList } from './create_exception_list'; const LISTS_ABLE_TO_DUPLICATE = [ @@ -26,48 +25,38 @@ const LISTS_ABLE_TO_DUPLICATE = [ ExceptionListTypeEnum.RULE_DEFAULT.toString(), ]; -interface CreateExceptionListOptions { - listId: ListId; +interface DuplicateExceptionListOptions { + list: ExceptionListSchema; savedObjectsClient: SavedObjectsClientContract; namespaceType: NamespaceType; user: string; + includeExpiredExceptions: boolean; } export const duplicateExceptionListAndItems = async ({ - listId, + includeExpiredExceptions, + list, savedObjectsClient, namespaceType, user, -}: CreateExceptionListOptions): Promise => { +}: DuplicateExceptionListOptions): Promise => { // Generate a new static listId const newListId = uuidv4(); - // fetch list container - const listToDuplicate = await getExceptionList({ - id: undefined, - listId, - namespaceType, - savedObjectsClient, - }); - - if (listToDuplicate == null) { - throw new Error(`Exception list to duplicat of list_id:${listId} not found.`); - } - - if (!LISTS_ABLE_TO_DUPLICATE.includes(listToDuplicate.type)) { - throw new Error(`Exception list of type:${listToDuplicate.type} cannot be duplicated.`); + if (!LISTS_ABLE_TO_DUPLICATE.includes(list.type)) { + return null; } const newlyCreatedList = await createExceptionList({ - description: listToDuplicate.description, - immutable: listToDuplicate.immutable, + description: list.description, + immutable: list.immutable, listId: newListId, - meta: listToDuplicate.meta, - name: listToDuplicate.name, - namespaceType: listToDuplicate.namespace_type, + meta: list.meta, + name: `${list.name} [Duplicate]`, + namespaceType: list.namespace_type, savedObjectsClient, - tags: listToDuplicate.tags, - type: listToDuplicate.type, + tags: list.tags, + type: list.type, user, version: 1, }); @@ -96,10 +85,16 @@ export const duplicateExceptionListAndItems = async ({ }); itemsToBeDuplicated = [...itemsToBeDuplicated, ...transformedItems]; }; + const savedObjectPrefix = getSavedObjectType({ namespaceType }); + const filter = includeExpiredExceptions + ? [] + : [ + `(${savedObjectPrefix}.attributes.expire_time > "${new Date().toISOString()}" OR NOT ${savedObjectPrefix}.attributes.expire_time: *)`, + ]; await findExceptionListsItemPointInTimeFinder({ executeFunctionOnStream, - filter: [], - listId: [listId], + filter, + listId: [list.list_id], maxSize: 10000, namespaceType: [namespaceType], perPage: undefined, diff --git a/x-pack/plugins/lists/server/services/exception_lists/exception_list_client.ts b/x-pack/plugins/lists/server/services/exception_lists/exception_list_client.ts index db9c62ae5b377..484353d30e346 100644 --- a/x-pack/plugins/lists/server/services/exception_lists/exception_list_client.ts +++ b/x-pack/plugins/lists/server/services/exception_lists/exception_list_client.ts @@ -317,17 +317,20 @@ export class ExceptionListClient { /** * Create the Trusted Apps Agnostic list if it does not yet exist (`null` is returned if it does exist) - * @param options.listId the "list_id" of the exception list + * @param options.list the "list" to be duplicated * @param options.namespaceType saved object namespace (single | agnostic) + * @param options.includeExpiredExceptions include or exclude expired TTL exception items * @returns The exception list schema or null if it does not exist */ public duplicateExceptionListAndItems = async ({ - listId, + list, namespaceType, + includeExpiredExceptions, }: DuplicateExceptionListOptions): Promise => { const { savedObjectsClient, user } = this; return duplicateExceptionListAndItems({ - listId, + includeExpiredExceptions, + list, namespaceType, savedObjectsClient, user, @@ -1051,6 +1054,7 @@ export class ExceptionListClient { * @param options.listId the "list_id" of an exception list * @param options.id the "id" of an exception list * @param options.namespaceType saved object namespace (single | agnostic) + * @param options.includeExpiredExceptions include or exclude expired TTL exception items * @returns the ndjson of the list and items to export or null if none exists */ public exportExceptionListAndItems = async ({ diff --git a/x-pack/plugins/lists/server/services/exception_lists/exception_list_client_types.ts b/x-pack/plugins/lists/server/services/exception_lists/exception_list_client_types.ts index b93de1413e4db..14c75b84a1367 100644 --- a/x-pack/plugins/lists/server/services/exception_lists/exception_list_client_types.ts +++ b/x-pack/plugins/lists/server/services/exception_lists/exception_list_client_types.ts @@ -19,6 +19,7 @@ import type { EntriesArray, ExceptionListItemType, ExceptionListItemTypeOrUndefined, + ExceptionListSchema, ExceptionListType, ExceptionListTypeOrUndefined, ExpireTimeOrUndefined, @@ -295,10 +296,12 @@ export interface CreateEndpointListItemOptions { * {@link ExceptionListClient.duplicateExceptionListAndItems} */ export interface DuplicateExceptionListOptions { - /** The single list id to do the search against */ - listId: ListId; + /** The list to be duplicated */ + list: ExceptionListSchema; /** saved object namespace (single | agnostic) */ namespaceType: NamespaceType; + /** determines whether exception items with an expired TTL are included in duplication */ + includeExpiredExceptions: boolean; } /** diff --git a/x-pack/plugins/maps/common/constants.ts b/x-pack/plugins/maps/common/constants.ts index 6c8f719bf2886..8f5ad4869ebe7 100644 --- a/x-pack/plugins/maps/common/constants.ts +++ b/x-pack/plugins/maps/common/constants.ts @@ -188,10 +188,6 @@ export const GEOCENTROID_AGG_NAME = 'gridCentroid'; export const TOP_TERM_PERCENTAGE_SUFFIX = '__percentage'; export const DEFAULT_PERCENTILE = 50; -export const COUNT_PROP_LABEL = i18n.translate('xpack.maps.aggs.defaultCountLabel', { - defaultMessage: 'count', -}); - export const COUNT_PROP_NAME = 'doc_count'; export enum STYLE_TYPE { @@ -341,6 +337,11 @@ export enum WIZARD_ID { TMS_LAYER = 'tmsLayer', } +export enum MASK_OPERATOR { + ABOVE = 'ABOVE', + BELOW = 'BELOW', +} + // Maplibre does not provide any feedback when rendering is complete. // Workaround is hard-coded timeout period. export const RENDER_TIMEOUT = 1000; diff --git a/x-pack/plugins/maps/common/content_management/v1/cm_services.ts b/x-pack/plugins/maps/common/content_management/v1/cm_services.ts index 0e430b56ced98..5ea8008f53225 100644 --- a/x-pack/plugins/maps/common/content_management/v1/cm_services.ts +++ b/x-pack/plugins/maps/common/content_management/v1/cm_services.ts @@ -134,4 +134,11 @@ export const serviceDefinition: ServicesDefinition = { }, }, }, + mSearch: { + out: { + result: { + schema: mapSavedObjectSchema, + }, + }, + }, }; diff --git a/x-pack/plugins/maps/common/descriptor_types/source_descriptor_types.ts b/x-pack/plugins/maps/common/descriptor_types/source_descriptor_types.ts index b862558d6a215..c76bc8f6a6e17 100644 --- a/x-pack/plugins/maps/common/descriptor_types/source_descriptor_types.ts +++ b/x-pack/plugins/maps/common/descriptor_types/source_descriptor_types.ts @@ -13,6 +13,7 @@ import { SortDirection } from '@kbn/data-plugin/common/search'; import { AGG_TYPE, GRID_RESOLUTION, + MASK_OPERATOR, RENDER_AS, SCALING_TYPES, MVT_FIELD_TYPE, @@ -49,6 +50,10 @@ export type AbstractESSourceDescriptor = AbstractSourceDescriptor & { type AbstractAggDescriptor = { type: AGG_TYPE; label?: string; + mask?: { + operator: MASK_OPERATOR; + value: number; + }; }; export type CountAggDescriptor = AbstractAggDescriptor & { diff --git a/x-pack/plugins/maps/public/classes/fields/agg/agg_field_types.ts b/x-pack/plugins/maps/public/classes/fields/agg/agg_field_types.ts index d881accb1e42f..6db0d32dbd551 100644 --- a/x-pack/plugins/maps/public/classes/fields/agg/agg_field_types.ts +++ b/x-pack/plugins/maps/public/classes/fields/agg/agg_field_types.ts @@ -9,14 +9,17 @@ import { DataView } from '@kbn/data-plugin/common'; import { IField } from '../field'; import { IESAggSource } from '../../sources/es_agg_source'; import { FIELD_ORIGIN } from '../../../../common/constants'; +import { AggDescriptor } from '../../../../common/descriptor_types'; export interface IESAggField extends IField { getValueAggDsl(indexPattern: DataView): unknown | null; getBucketCount(): number; + getMask(): AggDescriptor['mask'] | undefined; } export interface CountAggFieldParams { label?: string; source: IESAggSource; origin: FIELD_ORIGIN; + mask?: AggDescriptor['mask']; } diff --git a/x-pack/plugins/maps/public/classes/fields/agg/count_agg_field.ts b/x-pack/plugins/maps/public/classes/fields/agg/count_agg_field.ts index 03ade37c3dbec..140f605832fca 100644 --- a/x-pack/plugins/maps/public/classes/fields/agg/count_agg_field.ts +++ b/x-pack/plugins/maps/public/classes/fields/agg/count_agg_field.ts @@ -14,7 +14,7 @@ import { DataView } from '@kbn/data-plugin/common'; import { IESAggSource } from '../../sources/es_agg_source'; import { IVectorSource } from '../../sources/vector_source'; import { AGG_TYPE, FIELD_ORIGIN } from '../../../../common/constants'; -import { TileMetaFeature } from '../../../../common/descriptor_types'; +import { AggDescriptor, TileMetaFeature } from '../../../../common/descriptor_types'; import { ITooltipProperty, TooltipProperty } from '../../tooltips/tooltip_property'; import { ESAggTooltipProperty } from '../../tooltips/es_agg_tooltip_property'; import { IESAggField, CountAggFieldParams } from './agg_field_types'; @@ -25,11 +25,13 @@ export class CountAggField implements IESAggField { protected readonly _source: IESAggSource; private readonly _origin: FIELD_ORIGIN; protected readonly _label?: string; + protected readonly _mask?: AggDescriptor['mask']; - constructor({ label, source, origin }: CountAggFieldParams) { + constructor({ label, source, origin, mask }: CountAggFieldParams) { this._source = source; this._origin = origin; this._label = label; + this._mask = mask; } supportsFieldMetaFromEs(): boolean { @@ -131,4 +133,8 @@ export class CountAggField implements IESAggField { pluckRangeFromTileMetaFeature(metaFeature: TileMetaFeature) { return getAggRange(metaFeature, '_count'); } + + getMask() { + return this._mask; + } } diff --git a/x-pack/plugins/maps/public/classes/fields/agg/es_agg_factory.ts b/x-pack/plugins/maps/public/classes/fields/agg/es_agg_factory.ts index 7e43a2a63658c..9db4e481b9963 100644 --- a/x-pack/plugins/maps/public/classes/fields/agg/es_agg_factory.ts +++ b/x-pack/plugins/maps/public/classes/fields/agg/es_agg_factory.ts @@ -26,6 +26,7 @@ export function esAggFieldsFactory( label: aggDescriptor.label, source, origin, + mask: aggDescriptor.mask, }); } else if (aggDescriptor.type === AGG_TYPE.PERCENTILE) { aggField = new PercentileAggField({ @@ -40,6 +41,7 @@ export function esAggFieldsFactory( : DEFAULT_PERCENTILE, source, origin, + mask: aggDescriptor.mask, }); } else { aggField = new AggField({ @@ -51,6 +53,7 @@ export function esAggFieldsFactory( aggType: aggDescriptor.type, source, origin, + mask: aggDescriptor.mask, }); } diff --git a/x-pack/plugins/maps/public/classes/fields/agg/top_term_percentage_field.ts b/x-pack/plugins/maps/public/classes/fields/agg/top_term_percentage_field.ts index c6a19f448390a..568fad59e058b 100644 --- a/x-pack/plugins/maps/public/classes/fields/agg/top_term_percentage_field.ts +++ b/x-pack/plugins/maps/public/classes/fields/agg/top_term_percentage_field.ts @@ -105,4 +105,8 @@ export class TopTermPercentageField implements IESAggField { pluckRangeFromTileMetaFeature(metaFeature: TileMetaFeature) { return null; } + + getMask() { + return undefined; + } } diff --git a/x-pack/plugins/maps/public/classes/layers/heatmap_layer/heatmap_layer.ts b/x-pack/plugins/maps/public/classes/layers/heatmap_layer/heatmap_layer.ts index 0421fc3b087b5..69988f0e8a6cb 100644 --- a/x-pack/plugins/maps/public/classes/layers/heatmap_layer/heatmap_layer.ts +++ b/x-pack/plugins/maps/public/classes/layers/heatmap_layer/heatmap_layer.ts @@ -5,7 +5,7 @@ * 2.0. */ -import type { Map as MbMap, VectorTileSource } from '@kbn/mapbox-gl'; +import type { FilterSpecification, Map as MbMap, VectorTileSource } from '@kbn/mapbox-gl'; import { AbstractLayer } from '../layer'; import { HeatmapStyle } from '../../styles/heatmap/heatmap_style'; import { LAYER_TYPE } from '../../../../common/constants'; @@ -21,6 +21,7 @@ import { DataRequestContext } from '../../../actions'; import { buildVectorRequestMeta } from '../build_vector_request_meta'; import { IMvtVectorSource } from '../../sources/vector_source'; import { getAggsMeta } from '../../util/tile_meta_feature_utils'; +import { Mask } from '../vector_layer/mask'; export class HeatmapLayer extends AbstractLayer { private readonly _style: HeatmapStyle; @@ -186,6 +187,19 @@ export class HeatmapLayer extends AbstractLayer { this.syncVisibilityWithMb(mbMap, heatmapLayerId); mbMap.setPaintProperty(heatmapLayerId, 'heatmap-opacity', this.getAlpha()); + + // heatmap can implement mask with filter expression because + // feature-state support is not needed since heatmap layers do not support joins + const maskDescriptor = metricField.getMask(); + if (maskDescriptor) { + const mask = new Mask({ + esAggField: metricField, + isGeometrySourceMvt: true, + ...maskDescriptor, + }); + mbMap.setFilter(heatmapLayerId, mask.getMatchUnmaskedExpression() as FilterSpecification); + } + mbMap.setLayerZoomRange(heatmapLayerId, this.getMinZoom(), this.getMaxZoom()); } diff --git a/x-pack/plugins/maps/public/classes/layers/vector_layer/blended_vector_layer/blended_vector_layer.ts b/x-pack/plugins/maps/public/classes/layers/vector_layer/blended_vector_layer/blended_vector_layer.ts index ee9fdaf410abb..200c8cad24a4c 100644 --- a/x-pack/plugins/maps/public/classes/layers/vector_layer/blended_vector_layer/blended_vector_layer.ts +++ b/x-pack/plugins/maps/public/classes/layers/vector_layer/blended_vector_layer/blended_vector_layer.ts @@ -13,7 +13,6 @@ import { getDefaultDynamicProperties } from '../../../styles/vector/vector_style import { IDynamicStyleProperty } from '../../../styles/vector/properties/dynamic_style_property'; import { IStyleProperty } from '../../../styles/vector/properties/style_property'; import { - COUNT_PROP_LABEL, COUNT_PROP_NAME, GRID_RESOLUTION, LAYER_TYPE, @@ -67,7 +66,6 @@ function getClusterSource(documentSource: IESSource, documentStyle: IVectorStyle clusterSourceDescriptor.metrics = [ { type: AGG_TYPE.COUNT, - label: COUNT_PROP_LABEL, }, ...documentStyle.getDynamicPropertiesArray().map((dynamicProperty) => { return { @@ -267,9 +265,9 @@ export class BlendedVectorLayer extends GeoJsonVectorLayer implements IVectorLay return [clonedDescriptor]; } - getSource(): IVectorSource { + getSource = () => { return this._isClustered ? this._clusterSource : this._documentSource; - } + }; getSourceForEditing() { // Layer is based on this._documentSource diff --git a/x-pack/plugins/maps/public/classes/layers/vector_layer/mask.ts b/x-pack/plugins/maps/public/classes/layers/vector_layer/mask.ts new file mode 100644 index 0000000000000..0b1861fd73397 --- /dev/null +++ b/x-pack/plugins/maps/public/classes/layers/vector_layer/mask.ts @@ -0,0 +1,193 @@ +/* + * 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'; +import { MapGeoJSONFeature } from '@kbn/mapbox-gl'; +import type { IESAggSource } from '../../sources/es_agg_source'; +import type { IESAggField } from '../../fields/agg'; +import { FIELD_ORIGIN, MASK_OPERATOR, MB_LOOKUP_FUNCTION } from '../../../../common/constants'; + +export const BELOW = i18n.translate('xpack.maps.mask.belowLabel', { + defaultMessage: 'below', +}); + +export const ABOVE = i18n.translate('xpack.maps.mask.aboveLabel', { + defaultMessage: 'above', +}); + +export const BUCKETS = i18n.translate('xpack.maps.mask.genericBucketsName', { + defaultMessage: 'buckets', +}); + +const FEATURES = i18n.translate('xpack.maps.mask.genericFeaturesName', { + defaultMessage: 'features', +}); + +const VALUE = i18n.translate('xpack.maps.mask.genericAggLabel', { + defaultMessage: 'value', +}); + +const WHEN = i18n.translate('xpack.maps.mask.when', { + defaultMessage: 'when', +}); + +const WHEN_JOIN_METRIC = i18n.translate('xpack.maps.mask.whenJoinMetric', { + defaultMessage: '{whenLabel} join metric', + values: { + whenLabel: WHEN, + }, +}); + +function getOperatorLabel(operator: MASK_OPERATOR): string { + if (operator === MASK_OPERATOR.BELOW) { + return BELOW; + } + + if (operator === MASK_OPERATOR.ABOVE) { + return ABOVE; + } + + return operator as string; +} + +export function getMaskI18nValue(operator: MASK_OPERATOR, value: number): string { + return `${getOperatorLabel(operator)} ${value}`; +} + +export function getMaskI18nLabel({ + bucketsName, + isJoin, +}: { + bucketsName?: string; + isJoin: boolean; +}): string { + return i18n.translate('xpack.maps.mask.maskLabel', { + defaultMessage: 'Hide {hideNoun}', + values: { + hideNoun: isJoin ? FEATURES : bucketsName ? bucketsName : BUCKETS, + }, + }); +} + +export function getMaskI18nDescription({ + aggLabel, + bucketsName, + isJoin, +}: { + aggLabel?: string; + bucketsName?: string; + isJoin: boolean; +}): string { + return i18n.translate('xpack.maps.mask.maskDescription', { + defaultMessage: '{maskAdverb} {aggLabel} is ', + values: { + aggLabel: aggLabel ? aggLabel : VALUE, + maskAdverb: isJoin ? WHEN_JOIN_METRIC : WHEN, + }, + }); +} + +export class Mask { + private readonly _esAggField: IESAggField; + private readonly _isGeometrySourceMvt: boolean; + private readonly _operator: MASK_OPERATOR; + private readonly _value: number; + + constructor({ + esAggField, + isGeometrySourceMvt, + operator, + value, + }: { + esAggField: IESAggField; + isGeometrySourceMvt: boolean; + operator: MASK_OPERATOR; + value: number; + }) { + this._esAggField = esAggField; + this._isGeometrySourceMvt = isGeometrySourceMvt; + this._operator = operator; + this._value = value; + } + + private _isFeatureState() { + if (this._esAggField.getOrigin() === FIELD_ORIGIN.SOURCE) { + // source fields are stored in properties + return false; + } + + if (!this._isGeometrySourceMvt) { + // For geojson sources, join fields are stored in properties + return false; + } + + // For vector tile sources, it is not possible to add join fields to properties + // so join fields are stored in feature state + return true; + } + + /* + * Returns maplibre expression that matches masked features + */ + getMatchMaskedExpression() { + const comparisionOperator = this._operator === MASK_OPERATOR.BELOW ? '<' : '>'; + const lookup = this._isFeatureState() + ? MB_LOOKUP_FUNCTION.FEATURE_STATE + : MB_LOOKUP_FUNCTION.GET; + return [comparisionOperator, [lookup, this._esAggField.getMbFieldName()], this._value]; + } + + /* + * Returns maplibre expression that matches unmasked features + */ + getMatchUnmaskedExpression() { + const comparisionOperator = this._operator === MASK_OPERATOR.BELOW ? '>=' : '<='; + const lookup = this._isFeatureState() + ? MB_LOOKUP_FUNCTION.FEATURE_STATE + : MB_LOOKUP_FUNCTION.GET; + return [comparisionOperator, [lookup, this._esAggField.getMbFieldName()], this._value]; + } + + getEsAggField() { + return this._esAggField; + } + + getFieldOriginListLabel() { + const source = this._esAggField.getSource(); + const isJoin = this._esAggField.getOrigin() === FIELD_ORIGIN.JOIN; + const maskLabel = getMaskI18nLabel({ + bucketsName: + 'getBucketsName' in (source as IESAggSource) + ? (source as IESAggSource).getBucketsName() + : undefined, + isJoin, + }); + const adverb = isJoin ? WHEN_JOIN_METRIC : WHEN; + + return `${maskLabel} ${adverb}`; + } + + getOperator() { + return this._operator; + } + + getValue() { + return this._value; + } + + isFeatureMasked(feature: MapGeoJSONFeature) { + const featureValue = this._isFeatureState() + ? feature?.state[this._esAggField.getMbFieldName()] + : feature?.properties[this._esAggField.getMbFieldName()]; + if (typeof featureValue !== 'number') { + return false; + } + return this._operator === MASK_OPERATOR.BELOW + ? featureValue < this._value + : featureValue > this._value; + } +} diff --git a/x-pack/plugins/maps/public/classes/layers/vector_layer/mvt_vector_layer/mvt_vector_layer.test.tsx b/x-pack/plugins/maps/public/classes/layers/vector_layer/mvt_vector_layer/mvt_vector_layer.test.tsx index a976837ee2881..82bb15c19ffca 100644 --- a/x-pack/plugins/maps/public/classes/layers/vector_layer/mvt_vector_layer/mvt_vector_layer.test.tsx +++ b/x-pack/plugins/maps/public/classes/layers/vector_layer/mvt_vector_layer/mvt_vector_layer.test.tsx @@ -25,6 +25,7 @@ import { } from '../../../../../common/descriptor_types'; import { LAYER_TYPE, SOURCE_TYPES } from '../../../../../common/constants'; import { MvtVectorLayer } from './mvt_vector_layer'; +import { ITermJoinSource } from '../../../sources/term_join_source'; const defaultConfig = { urlTemplate: 'https://example.com/{x}/{y}/{z}.pbf', @@ -176,6 +177,9 @@ describe('isLayerLoading', () => { getSourceDataRequestId: () => { return 'join_source_a0b0da65-5e1a-4967-9dbe-74f24391afe2'; }, + getRightJoinSource: () => { + return {} as unknown as ITermJoinSource; + }, } as unknown as InnerJoin, ], layerDescriptor: { @@ -212,6 +216,9 @@ describe('isLayerLoading', () => { getSourceDataRequestId: () => { return 'join_source_a0b0da65-5e1a-4967-9dbe-74f24391afe2'; }, + getRightJoinSource: () => { + return {} as unknown as ITermJoinSource; + }, } as unknown as InnerJoin, ], layerDescriptor: { diff --git a/x-pack/plugins/maps/public/classes/layers/vector_layer/vector_layer.tsx b/x-pack/plugins/maps/public/classes/layers/vector_layer/vector_layer.tsx index dbee11b617dec..366c9cde6eee6 100644 --- a/x-pack/plugins/maps/public/classes/layers/vector_layer/vector_layer.tsx +++ b/x-pack/plugins/maps/public/classes/layers/vector_layer/vector_layer.tsx @@ -58,12 +58,14 @@ import { ITooltipProperty } from '../../tooltips/tooltip_property'; import { IDynamicStyleProperty } from '../../styles/vector/properties/dynamic_style_property'; import { IESSource } from '../../sources/es_source'; import { ITermJoinSource } from '../../sources/term_join_source'; +import type { IESAggSource } from '../../sources/es_agg_source'; import { buildVectorRequestMeta } from '../build_vector_request_meta'; import { getJoinAggKey } from '../../../../common/get_agg_key'; import { syncBoundsData } from './bounds_data'; import { JoinState } from './types'; import { canSkipSourceUpdate } from '../../util/can_skip_fetch'; import { PropertiesMap } from '../../../../common/elasticsearch_util'; +import { Mask } from './mask'; const SUPPORTS_FEATURE_EDITING_REQUEST_ID = 'SUPPORTS_FEATURE_EDITING_REQUEST_ID'; @@ -106,6 +108,7 @@ export interface IVectorLayer extends ILayer { getLeftJoinFields(): Promise; addFeature(geometry: Geometry | Position[]): Promise; deleteFeature(featureId: string): Promise; + getMasks(): Mask[]; } export const noResultsIcon = ; @@ -120,6 +123,7 @@ export class AbstractVectorLayer extends AbstractLayer implements IVectorLayer { protected readonly _style: VectorStyle; private readonly _joins: InnerJoin[]; protected readonly _descriptor: VectorLayerDescriptor; + private readonly _masks: Mask[]; static createDescriptor( options: Partial, @@ -163,6 +167,7 @@ export class AbstractVectorLayer extends AbstractLayer implements IVectorLayer { customIcons, chartsPaletteServiceGetColor ); + this._masks = this._createMasks(); } async cloneDescriptor(): Promise { @@ -692,6 +697,69 @@ export class AbstractVectorLayer extends AbstractLayer implements IVectorLayer { return undefined; } + _createMasks() { + const masks: Mask[] = []; + const source = this.getSource(); + if ('getMetricFields' in (source as IESAggSource)) { + const metricFields = (source as IESAggSource).getMetricFields(); + metricFields.forEach((metricField) => { + const maskDescriptor = metricField.getMask(); + if (maskDescriptor) { + masks.push( + new Mask({ + esAggField: metricField, + isGeometrySourceMvt: source.isMvt(), + ...maskDescriptor, + }) + ); + } + }); + } + + this.getValidJoins().forEach((join) => { + const rightSource = join.getRightJoinSource(); + if ('getMetricFields' in (rightSource as unknown as IESAggSource)) { + const metricFields = (rightSource as unknown as IESAggSource).getMetricFields(); + metricFields.forEach((metricField) => { + const maskDescriptor = metricField.getMask(); + if (maskDescriptor) { + masks.push( + new Mask({ + esAggField: metricField, + isGeometrySourceMvt: source.isMvt(), + ...maskDescriptor, + }) + ); + } + }); + } + }); + + return masks; + } + + getMasks() { + return this._masks; + } + + // feature-state is not supported in filter expressions + // https://github.com/mapbox/mapbox-gl-js/issues/8487 + // therefore, masking must be accomplished via setting opacity paint property (hack) + _getAlphaExpression() { + const maskCaseExpressions: unknown[] = []; + this.getMasks().forEach((mask) => { + // case expressions require 2 parts + // 1) condition expression + maskCaseExpressions.push(mask.getMatchMaskedExpression()); + // 2) output. 0 opacity styling "hides" feature + maskCaseExpressions.push(0); + }); + + return maskCaseExpressions.length + ? ['case', ...maskCaseExpressions, this.getAlpha()] + : this.getAlpha(); + } + _setMbPointsProperties( mbMap: MbMap, mvtSourceLayer?: string, @@ -759,13 +827,13 @@ export class AbstractVectorLayer extends AbstractLayer implements IVectorLayer { if (this.getCurrentStyle().arePointsSymbolizedAsCircles()) { this.getCurrentStyle().setMBPaintPropertiesForPoints({ - alpha: this.getAlpha(), + alpha: this._getAlphaExpression(), mbMap, pointLayerId: markerLayerId, }); } else { this.getCurrentStyle().setMBSymbolPropertiesForPoints({ - alpha: this.getAlpha(), + alpha: this._getAlphaExpression(), mbMap, symbolLayerId: markerLayerId, }); @@ -811,7 +879,7 @@ export class AbstractVectorLayer extends AbstractLayer implements IVectorLayer { } this.getCurrentStyle().setMBPaintProperties({ - alpha: this.getAlpha(), + alpha: this._getAlphaExpression(), mbMap, fillLayerId, lineLayerId, @@ -865,7 +933,7 @@ export class AbstractVectorLayer extends AbstractLayer implements IVectorLayer { } this.getCurrentStyle().setMBPropertiesForLabelText({ - alpha: this.getAlpha(), + alpha: this._getAlphaExpression(), mbMap, textLayerId: labelLayerId, }); diff --git a/x-pack/plugins/maps/public/classes/sources/es_agg_source/es_agg_source.ts b/x-pack/plugins/maps/public/classes/sources/es_agg_source/es_agg_source.ts index fa7f329beb97a..dda3026ddd4ef 100644 --- a/x-pack/plugins/maps/public/classes/sources/es_agg_source/es_agg_source.ts +++ b/x-pack/plugins/maps/public/classes/sources/es_agg_source/es_agg_source.ts @@ -11,11 +11,13 @@ import { DataView } from '@kbn/data-plugin/common'; import type { IESAggSource } from './types'; import { AbstractESSource } from '../es_source'; import { esAggFieldsFactory, IESAggField } from '../../fields/agg'; -import { AGG_TYPE, COUNT_PROP_LABEL, FIELD_ORIGIN } from '../../../../common/constants'; +import { AGG_TYPE, FIELD_ORIGIN } from '../../../../common/constants'; import { getSourceAggKey } from '../../../../common/get_agg_key'; import { AbstractESAggSourceDescriptor, AggDescriptor } from '../../../../common/descriptor_types'; import { IField } from '../../fields/field'; import { ITooltipProperty } from '../../tooltips/tooltip_property'; +import { getAggDisplayName } from './get_agg_display_name'; +import { BUCKETS } from '../../layers/vector_layer/mask'; export const DEFAULT_METRIC = { type: AGG_TYPE.COUNT }; @@ -46,6 +48,10 @@ export abstract class AbstractESAggSource extends AbstractESSource implements IE } } + getBucketsName() { + return BUCKETS; + } + getFieldByName(fieldName: string): IField | null { return this.getMetricFieldForName(fieldName); } @@ -83,14 +89,14 @@ export abstract class AbstractESAggSource extends AbstractESSource implements IE async getAggLabel(aggType: AGG_TYPE, fieldLabel: string): Promise { switch (aggType) { case AGG_TYPE.COUNT: - return COUNT_PROP_LABEL; + return getAggDisplayName(aggType); case AGG_TYPE.TERMS: return i18n.translate('xpack.maps.source.esAggSource.topTermLabel', { - defaultMessage: `Top {fieldLabel}`, + defaultMessage: `top {fieldLabel}`, values: { fieldLabel }, }); default: - return `${aggType} ${fieldLabel}`; + return `${getAggDisplayName(aggType)} ${fieldLabel}`; } } diff --git a/x-pack/plugins/maps/public/classes/sources/es_agg_source/get_agg_display_name.ts b/x-pack/plugins/maps/public/classes/sources/es_agg_source/get_agg_display_name.ts new file mode 100644 index 0000000000000..516f6448fb629 --- /dev/null +++ b/x-pack/plugins/maps/public/classes/sources/es_agg_source/get_agg_display_name.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 { i18n } from '@kbn/i18n'; +import { AGG_TYPE } from '../../../../common/constants'; + +export function getAggDisplayName(aggType: AGG_TYPE): string { + switch (aggType) { + case AGG_TYPE.AVG: + return i18n.translate('xpack.maps.aggType.averageLabel', { + defaultMessage: 'average', + }); + case AGG_TYPE.COUNT: + return i18n.translate('xpack.maps.aggType.countLabel', { + defaultMessage: 'count', + }); + case AGG_TYPE.MAX: + return i18n.translate('xpack.maps.aggType.maximumLabel', { + defaultMessage: 'max', + }); + case AGG_TYPE.MIN: + return i18n.translate('xpack.maps.aggType.minimumLabel', { + defaultMessage: 'min', + }); + case AGG_TYPE.PERCENTILE: + return i18n.translate('xpack.maps.aggType.percentileLabel', { + defaultMessage: 'percentile', + }); + case AGG_TYPE.SUM: + return i18n.translate('xpack.maps.aggType.sumLabel', { + defaultMessage: 'sum', + }); + case AGG_TYPE.TERMS: + return i18n.translate('xpack.maps.aggType.topTermLabel', { + defaultMessage: 'top term', + }); + case AGG_TYPE.UNIQUE_COUNT: + return i18n.translate('xpack.maps.aggType.cardinalityTermLabel', { + defaultMessage: 'unique count', + }); + default: + return aggType; + } +} diff --git a/x-pack/plugins/maps/public/classes/sources/es_agg_source/index.ts b/x-pack/plugins/maps/public/classes/sources/es_agg_source/index.ts index 033d937aa9391..80bc74bd4ceb4 100644 --- a/x-pack/plugins/maps/public/classes/sources/es_agg_source/index.ts +++ b/x-pack/plugins/maps/public/classes/sources/es_agg_source/index.ts @@ -7,3 +7,4 @@ export type { IESAggSource } from './types'; export { AbstractESAggSource, DEFAULT_METRIC } from './es_agg_source'; +export { getAggDisplayName } from './get_agg_display_name'; diff --git a/x-pack/plugins/maps/public/classes/sources/es_agg_source/types.ts b/x-pack/plugins/maps/public/classes/sources/es_agg_source/types.ts index d9cb6fcd95a10..697ae8a1a606d 100644 --- a/x-pack/plugins/maps/public/classes/sources/es_agg_source/types.ts +++ b/x-pack/plugins/maps/public/classes/sources/es_agg_source/types.ts @@ -13,6 +13,12 @@ import { IESAggField } from '../../fields/agg'; export interface IESAggSource extends IESSource { getAggKey(aggType: AGG_TYPE, fieldName: string): string; getAggLabel(aggType: AGG_TYPE, fieldLabel: string): Promise; + + /* + * Returns human readable name describing buckets, like "clusters" or "grids" + */ + getBucketsName(): string; + getMetricFields(): IESAggField[]; getMetricFieldForName(fieldName: string): IESAggField | null; getValueAggsDsl(indexPattern: DataView): { [key: string]: unknown }; diff --git a/x-pack/plugins/maps/public/classes/sources/es_geo_grid_source/__snapshots__/update_source_editor.test.tsx.snap b/x-pack/plugins/maps/public/classes/sources/es_geo_grid_source/__snapshots__/update_source_editor.test.tsx.snap index 79b8c4ffc9808..fe50d54ca5535 100644 --- a/x-pack/plugins/maps/public/classes/sources/es_geo_grid_source/__snapshots__/update_source_editor.test.tsx.snap +++ b/x-pack/plugins/maps/public/classes/sources/es_geo_grid_source/__snapshots__/update_source_editor.test.tsx.snap @@ -19,7 +19,9 @@ exports[`source editor geo_grid_source should not allow editing multiple metrics /> { async function onChange(...sourceChanges: OnSourceChangeArgs[]) { sourceEditorArgs.onChange(...sourceChanges); @@ -129,6 +147,7 @@ export class ESGeoGridSource extends AbstractESAggSource implements IMvtVectorSo } return ( ({ })); const defaultProps = { + bucketsName: 'clusters', currentLayerType: LAYER_TYPE.GEOJSON_VECTOR, geoFieldName: 'myLocation', indexPatternId: 'foobar', diff --git a/x-pack/plugins/maps/public/classes/sources/es_geo_grid_source/update_source_editor.tsx b/x-pack/plugins/maps/public/classes/sources/es_geo_grid_source/update_source_editor.tsx index 3268992c7f2b6..d69a97d09dd47 100644 --- a/x-pack/plugins/maps/public/classes/sources/es_geo_grid_source/update_source_editor.tsx +++ b/x-pack/plugins/maps/public/classes/sources/es_geo_grid_source/update_source_editor.tsx @@ -6,7 +6,6 @@ */ import React, { Fragment, Component } from 'react'; - import { v4 as uuidv4 } from 'uuid'; import { FormattedMessage } from '@kbn/i18n-react'; import { EuiPanel, EuiSpacer, EuiComboBoxOptionOption, EuiTitle } from '@elastic/eui'; @@ -25,6 +24,7 @@ import { clustersTitle, heatmapTitle } from './es_geo_grid_source'; import { isMvt } from './is_mvt'; interface Props { + bucketsName: string; currentLayerType?: string; geoFieldName: string; indexPatternId: string; @@ -148,6 +148,8 @@ export class UpdateSourceEditor extends Component { { ); } diff --git a/x-pack/plugins/maps/public/classes/sources/es_pew_pew_source/update_source_editor.js b/x-pack/plugins/maps/public/classes/sources/es_pew_pew_source/update_source_editor.tsx similarity index 74% rename from x-pack/plugins/maps/public/classes/sources/es_pew_pew_source/update_source_editor.js rename to x-pack/plugins/maps/public/classes/sources/es_pew_pew_source/update_source_editor.tsx index 856504c51865e..c36cc3d49089a 100644 --- a/x-pack/plugins/maps/public/classes/sources/es_pew_pew_source/update_source_editor.js +++ b/x-pack/plugins/maps/public/classes/sources/es_pew_pew_source/update_source_editor.tsx @@ -6,17 +6,31 @@ */ import React, { Component, Fragment } from 'react'; - -import { getDataViewNotFoundMessage } from '../../../../common/i18n_getters'; -import { MetricsEditor } from '../../../components/metrics_editor'; -import { getIndexPatternService } from '../../../kibana_services'; +import type { DataViewField } from '@kbn/data-plugin/common'; import { EuiPanel, EuiTitle, EuiSpacer } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n-react'; import { indexPatterns } from '@kbn/data-plugin/public'; +import { MetricsEditor } from '../../../components/metrics_editor'; +import { getIndexPatternService } from '../../../kibana_services'; +import type { AggDescriptor } from '../../../../common/descriptor_types'; +import type { OnSourceChangeArgs } from '../source'; + +interface Props { + bucketsName: string; + indexPatternId: string; + metrics: AggDescriptor[]; + onChange: (...args: OnSourceChangeArgs[]) => void; +} + +interface State { + fields: DataViewField[]; +} + +export class UpdateSourceEditor extends Component { + private _isMounted: boolean = false; -export class UpdateSourceEditor extends Component { state = { - fields: null, + fields: [], }; componentDidMount() { @@ -33,11 +47,6 @@ export class UpdateSourceEditor extends Component { try { indexPattern = await getIndexPatternService().get(this.props.indexPatternId); } catch (err) { - if (this._isMounted) { - this.setState({ - loadError: getDataViewNotFoundMessage(this.props.indexPatternId), - }); - } return; } @@ -50,7 +59,7 @@ export class UpdateSourceEditor extends Component { }); } - _onMetricsChange = (metrics) => { + _onMetricsChange = (metrics: AggDescriptor[]) => { this.props.onChange({ propName: 'metrics', value: metrics }); }; @@ -69,6 +78,8 @@ export class UpdateSourceEditor extends Component { { }); const metrics = source.getMetricFields(); expect(metrics[0].getName()).toEqual('__kbnjoin__count__1234'); - expect(await metrics[0].getLabel()).toEqual('Count of foobar'); + expect(await metrics[0].getLabel()).toEqual('count of foobar'); }); it('should override name and label of sum metric', async () => { @@ -51,7 +51,7 @@ describe('getMetricFields', () => { expect(metrics[0].getName()).toEqual('__kbnjoin__sum_of_myFieldGettingSummed__1234'); expect(await metrics[0].getLabel()).toEqual('my custom label'); expect(metrics[1].getName()).toEqual('__kbnjoin__count__1234'); - expect(await metrics[1].getLabel()).toEqual('Count of foobar'); + expect(await metrics[1].getLabel()).toEqual('count of foobar'); }); }); diff --git a/x-pack/plugins/maps/public/classes/sources/es_term_source/es_term_source.ts b/x-pack/plugins/maps/public/classes/sources/es_term_source/es_term_source.ts index 5540454702114..4c7793e1b01cb 100644 --- a/x-pack/plugins/maps/public/classes/sources/es_term_source/es_term_source.ts +++ b/x-pack/plugins/maps/public/classes/sources/es_term_source/es_term_source.ts @@ -31,6 +31,7 @@ import { import { PropertiesMap } from '../../../../common/elasticsearch_util'; import { isValidStringConfig } from '../../util/valid_string_config'; import { ITermJoinSource } from '../term_join_source'; +import type { IESAggSource } from '../es_agg_source'; import { IField } from '../../fields/field'; import { mergeExecutionContext } from '../execution_context_utils'; @@ -52,7 +53,7 @@ export function extractPropertiesMap(rawEsData: any, countPropertyName: string): return propertiesMap; } -export class ESTermSource extends AbstractESAggSource implements ITermJoinSource { +export class ESTermSource extends AbstractESAggSource implements ITermJoinSource, IESAggSource { static type = SOURCE_TYPES.ES_TERM_SOURCE; static createDescriptor(descriptor: Partial): ESTermSourceDescriptor { @@ -115,7 +116,7 @@ export class ESTermSource extends AbstractESAggSource implements ITermJoinSource } return aggType === AGG_TYPE.COUNT ? i18n.translate('xpack.maps.source.esJoin.countLabel', { - defaultMessage: `Count of {indexPatternLabel}`, + defaultMessage: `count of {indexPatternLabel}`, values: { indexPatternLabel }, }) : super.getAggLabel(aggType, fieldLabel); diff --git a/x-pack/plugins/maps/public/classes/styles/heatmap/components/legend/heatmap_legend.tsx b/x-pack/plugins/maps/public/classes/styles/heatmap/components/legend/heatmap_legend.tsx index 390e48408d747..027e4dc29c58c 100644 --- a/x-pack/plugins/maps/public/classes/styles/heatmap/components/legend/heatmap_legend.tsx +++ b/x-pack/plugins/maps/public/classes/styles/heatmap/components/legend/heatmap_legend.tsx @@ -5,13 +5,15 @@ * 2.0. */ -import React, { Component } from 'react'; +import React, { Component, ReactNode } from 'react'; import { i18n } from '@kbn/i18n'; import { ColorGradient } from './color_gradient'; import { RangedStyleLegendRow } from '../../../components/ranged_style_legend_row'; import { HEATMAP_COLOR_RAMP_LABEL } from '../heatmap_constants'; -import { IField } from '../../../../fields/field'; +import type { IField } from '../../../../fields/field'; +import type { IESAggField } from '../../../../fields/agg'; +import { MaskLegend } from '../../../vector/components/legend/mask_legend'; interface Props { colorRampName: string; @@ -47,7 +49,7 @@ export class HeatmapLegend extends Component { } render() { - return ( + const metricLegend = ( } minLabel={i18n.translate('xpack.maps.heatmapLegend.coldLabel', { @@ -61,5 +63,28 @@ export class HeatmapLegend extends Component { invert={false} /> ); + + let maskLegend: ReactNode | undefined; + if ('getMask' in (this.props.field as IESAggField)) { + const mask = (this.props.field as IESAggField).getMask(); + if (mask) { + maskLegend = ( + + ); + } + } + + return maskLegend ? ( + <> + {maskLegend} + {metricLegend} + + ) : ( + metricLegend + ); } } diff --git a/x-pack/plugins/maps/public/classes/styles/vector/components/legend/mask_legend.tsx b/x-pack/plugins/maps/public/classes/styles/vector/components/legend/mask_legend.tsx new file mode 100644 index 0000000000000..4ffb7ba5a1834 --- /dev/null +++ b/x-pack/plugins/maps/public/classes/styles/vector/components/legend/mask_legend.tsx @@ -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 React, { Component } from 'react'; +import { i18n } from '@kbn/i18n'; +import { EuiText } from '@elastic/eui'; +import { FIELD_ORIGIN, MASK_OPERATOR } from '../../../../../../common/constants'; +import type { IESAggField } from '../../../../fields/agg'; +import type { IESAggSource } from '../../../../sources/es_agg_source'; +import { + getMaskI18nDescription, + getMaskI18nLabel, + getMaskI18nValue, +} from '../../../../layers/vector_layer/mask'; + +interface Props { + esAggField: IESAggField; + onlyShowLabelAndValue?: boolean; + operator: MASK_OPERATOR; + value: number; +} + +interface State { + aggLabel?: string; +} + +export class MaskLegend extends Component { + private _isMounted = false; + + state: State = {}; + + componentDidMount() { + this._isMounted = true; + this._loadAggLabel(); + } + + componentWillUnmount() { + this._isMounted = false; + } + + componentDidUpdate() { + this._loadAggLabel(); + } + + _loadAggLabel = async () => { + const aggLabel = await this.props.esAggField.getLabel(); + if (this._isMounted && aggLabel !== this.state.aggLabel) { + this.setState({ aggLabel }); + } + }; + + _getBucketsName() { + const source = this.props.esAggField.getSource(); + return 'getBucketsName' in (source as IESAggSource) + ? (source as IESAggSource).getBucketsName() + : undefined; + } + + _getPrefix() { + if (this.props.onlyShowLabelAndValue) { + return i18n.translate('xpack.maps.maskLegend.is', { + defaultMessage: '{aggLabel} is', + values: { + aggLabel: this.state.aggLabel, + }, + }); + } + + const isJoin = this.props.esAggField.getOrigin() === FIELD_ORIGIN.JOIN; + const maskLabel = getMaskI18nLabel({ + bucketsName: this._getBucketsName(), + isJoin, + }); + const maskDescription = getMaskI18nDescription({ + aggLabel: this.state.aggLabel, + isJoin, + }); + return `${maskLabel} ${maskDescription}`; + } + + render() { + return ( + + + {`${this._getPrefix()} `} + {getMaskI18nValue(this.props.operator, this.props.value)} + + + ); + } +} diff --git a/x-pack/plugins/maps/public/classes/styles/vector/components/legend/vector_style_legend.tsx b/x-pack/plugins/maps/public/classes/styles/vector/components/legend/vector_style_legend.tsx index 2d282a4b530cb..60bcd05c9f738 100644 --- a/x-pack/plugins/maps/public/classes/styles/vector/components/legend/vector_style_legend.tsx +++ b/x-pack/plugins/maps/public/classes/styles/vector/components/legend/vector_style_legend.tsx @@ -6,17 +6,30 @@ */ import React from 'react'; +import { EuiText } from '@elastic/eui'; +import { euiThemeVars } from '@kbn/ui-theme'; +import { FIELD_ORIGIN } from '../../../../../../common/constants'; +import { Mask } from '../../../../layers/vector_layer/mask'; import { IStyleProperty } from '../../properties/style_property'; +import { MaskLegend } from './mask_legend'; interface Props { isLinesOnly: boolean; isPointsOnly: boolean; + masks: Mask[]; styles: Array>; symbolId?: string; svg?: string; } -export function VectorStyleLegend({ isLinesOnly, isPointsOnly, styles, symbolId, svg }: Props) { +export function VectorStyleLegend({ + isLinesOnly, + isPointsOnly, + masks, + styles, + symbolId, + svg, +}: Props) { const legendRows = []; for (let i = 0; i < styles.length; i++) { @@ -34,5 +47,55 @@ export function VectorStyleLegend({ isLinesOnly, isPointsOnly, styles, symbolId, ); } - return <>{legendRows}; + function renderMasksByFieldOrigin(fieldOrigin: FIELD_ORIGIN) { + const masksByFieldOrigin = masks.filter( + (mask) => mask.getEsAggField().getOrigin() === fieldOrigin + ); + if (masksByFieldOrigin.length === 0) { + return null; + } + + if (masksByFieldOrigin.length === 1) { + const mask = masksByFieldOrigin[0]; + return ( + + ); + } + + return ( + <> + + {masksByFieldOrigin[0].getFieldOriginListLabel()} + +
    + {masksByFieldOrigin.map((mask) => ( +
  • + +
  • + ))} +
+ + ); + } + + return ( + <> + {renderMasksByFieldOrigin(FIELD_ORIGIN.SOURCE)} + {renderMasksByFieldOrigin(FIELD_ORIGIN.JOIN)} + {legendRows} + + ); } diff --git a/x-pack/plugins/maps/public/classes/styles/vector/properties/dynamic_color_property.tsx b/x-pack/plugins/maps/public/classes/styles/vector/properties/dynamic_color_property.tsx index 6daa8cf84afaa..f1a55b571e27d 100644 --- a/x-pack/plugins/maps/public/classes/styles/vector/properties/dynamic_color_property.tsx +++ b/x-pack/plugins/maps/public/classes/styles/vector/properties/dynamic_color_property.tsx @@ -51,7 +51,7 @@ export class DynamicColorProperty extends DynamicStyleProperty { - syncCircleColorWithMb(mbLayerId: string, mbMap: MbMap, alpha: number) { + syncCircleColorWithMb(mbLayerId: string, mbMap: MbMap, alpha: unknown) { mbMap.setPaintProperty(mbLayerId, 'circle-color', this._options.color); mbMap.setPaintProperty(mbLayerId, 'circle-opacity', alpha); } - syncFillColorWithMb(mbLayerId: string, mbMap: MbMap, alpha: number) { + syncFillColorWithMb(mbLayerId: string, mbMap: MbMap, alpha: unknown) { mbMap.setPaintProperty(mbLayerId, 'fill-color', this._options.color); mbMap.setPaintProperty(mbLayerId, 'fill-opacity', alpha); } @@ -28,17 +28,17 @@ export class StaticColorProperty extends StaticStyleProperty mbMap.setPaintProperty(mbLayerId, 'icon-halo-color', this._options.color); } - syncLineColorWithMb(mbLayerId: string, mbMap: MbMap, alpha: number) { + syncLineColorWithMb(mbLayerId: string, mbMap: MbMap, alpha: unknown) { mbMap.setPaintProperty(mbLayerId, 'line-color', this._options.color); mbMap.setPaintProperty(mbLayerId, 'line-opacity', alpha); } - syncCircleStrokeWithMb(mbLayerId: string, mbMap: MbMap, alpha: number) { + syncCircleStrokeWithMb(mbLayerId: string, mbMap: MbMap, alpha: unknown) { mbMap.setPaintProperty(mbLayerId, 'circle-stroke-color', this._options.color); mbMap.setPaintProperty(mbLayerId, 'circle-stroke-opacity', alpha); } - syncLabelColorWithMb(mbLayerId: string, mbMap: MbMap, alpha: number) { + syncLabelColorWithMb(mbLayerId: string, mbMap: MbMap, alpha: unknown) { mbMap.setPaintProperty(mbLayerId, 'text-color', this._options.color); mbMap.setPaintProperty(mbLayerId, 'text-opacity', alpha); } diff --git a/x-pack/plugins/maps/public/classes/styles/vector/vector_style.tsx b/x-pack/plugins/maps/public/classes/styles/vector/vector_style.tsx index 3b64d0960628c..31c06728d8112 100644 --- a/x-pack/plugins/maps/public/classes/styles/vector/vector_style.tsx +++ b/x-pack/plugins/maps/public/classes/styles/vector/vector_style.tsx @@ -130,7 +130,7 @@ export interface IVectorStyle extends IStyle { fillLayerId, lineLayerId, }: { - alpha: number; + alpha: unknown; mbMap: MbMap; fillLayerId: string; lineLayerId: string; @@ -140,7 +140,7 @@ export interface IVectorStyle extends IStyle { mbMap, pointLayerId, }: { - alpha: number; + alpha: unknown; mbMap: MbMap; pointLayerId: string; }) => void; @@ -149,7 +149,7 @@ export interface IVectorStyle extends IStyle { mbMap, textLayerId, }: { - alpha: number; + alpha: unknown; mbMap: MbMap; textLayerId: string; }) => void; @@ -158,7 +158,7 @@ export interface IVectorStyle extends IStyle { symbolLayerId, alpha, }: { - alpha: number; + alpha: unknown; mbMap: MbMap; symbolLayerId: string; }) => void; @@ -730,6 +730,7 @@ export class VectorStyle implements IVectorStyle { return ( void; + onClose: () => void; +} + +interface State { + operator: MASK_OPERATOR; + value: number | string; +} + +export class MaskEditor extends Component { + constructor(props: Props) { + super(props); + this.state = { + operator: + this.props.metric.mask !== undefined + ? this.props.metric.mask.operator + : MASK_OPERATOR.BELOW, + value: this.props.metric.mask !== undefined ? this.props.metric.mask.value : '', + }; + } + + _onSet = () => { + if (this._isValueInValid()) { + return; + } + + this.props.onChange({ + ...this.props.metric, + mask: { + operator: this.state.operator, + value: this.state.value as number, + }, + }); + this.props.onClose(); + }; + + _onClear = () => { + const newMetric = { + ...this.props.metric, + }; + delete newMetric.mask; + this.props.onChange(newMetric); + this.props.onClose(); + }; + + _onOperatorChange = (e: ChangeEvent) => { + this.setState({ + operator: e.target.value as MASK_OPERATOR, + }); + }; + + _onValueChange = (evt: ChangeEvent) => { + const sanitizedValue = parseFloat(evt.target.value); + this.setState({ + value: isNaN(sanitizedValue) ? evt.target.value : sanitizedValue, + }); + }; + + _hasChanges() { + return ( + this.props.metric.mask === undefined || + this.props.metric.mask.operator !== this.state.operator || + this.props.metric.mask.value !== this.state.value + ); + } + + _isValueInValid() { + return typeof this.state.value === 'string'; + } + + _renderForm() { + return ( + + + + + + + + + + + + + ); + } + + _renderFooter() { + return ( + + + + + {panelStrings.close} + + + + + {panelStrings.clear} + + + + + {panelStrings.apply} + + + + + ); + } + + render() { + return ( + <> + {this._renderForm()} + + {this._renderFooter()} + + ); + } +} diff --git a/x-pack/plugins/maps/public/components/metrics_editor/mask_expression/mask_expression.tsx b/x-pack/plugins/maps/public/components/metrics_editor/mask_expression/mask_expression.tsx new file mode 100644 index 0000000000000..e05fdc6cdf2d5 --- /dev/null +++ b/x-pack/plugins/maps/public/components/metrics_editor/mask_expression/mask_expression.tsx @@ -0,0 +1,104 @@ +/* + * 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, { Component } from 'react'; +import { EuiExpression, EuiPopover } from '@elastic/eui'; +import { DataViewField } from '@kbn/data-views-plugin/public'; +import { AGG_TYPE } from '../../../../common/constants'; +import { AggDescriptor, FieldedAggDescriptor } from '../../../../common/descriptor_types'; +import { MaskEditor } from './mask_editor'; +import { getAggDisplayName } from '../../../classes/sources/es_agg_source'; +import { + getMaskI18nDescription, + getMaskI18nValue, +} from '../../../classes/layers/vector_layer/mask'; + +interface Props { + fields: DataViewField[]; + isJoin: boolean; + metric: AggDescriptor; + onChange: (metric: AggDescriptor) => void; +} + +interface State { + isPopoverOpen: boolean; +} + +export class MaskExpression extends Component { + state: State = { + isPopoverOpen: false, + }; + + _togglePopover = () => { + this.setState((prevState) => ({ + isPopoverOpen: !prevState.isPopoverOpen, + })); + }; + + _closePopover = () => { + this.setState({ + isPopoverOpen: false, + }); + }; + + _getMaskExpressionValue() { + return this.props.metric.mask === undefined + ? '...' + : getMaskI18nValue(this.props.metric.mask.operator, this.props.metric.mask.value); + } + + _getAggLabel() { + const aggDisplayName = getAggDisplayName(this.props.metric.type); + if (this.props.metric.type === AGG_TYPE.COUNT || this.props.metric.field === undefined) { + return aggDisplayName; + } + + const targetField = this.props.fields.find( + (field) => field.name === (this.props.metric as FieldedAggDescriptor).field + ); + const fieldDisplayName = targetField?.displayName + ? targetField?.displayName + : this.props.metric.field; + return `${aggDisplayName} ${fieldDisplayName}`; + } + + render() { + // masks only supported for numerical metrics + if (this.props.metric.type === AGG_TYPE.TERMS) { + return null; + } + + return ( + + } + isOpen={this.state.isPopoverOpen} + closePopover={this._closePopover} + panelPaddingSize="s" + anchorPosition="downCenter" + repositionOnScroll={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 26cf6d5313821..5042cb4ffa9c7 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 @@ -18,6 +18,8 @@ import { AggDescriptor } from '../../../common/descriptor_types'; import { AGG_TYPE, DEFAULT_PERCENTILE } from '../../../common/constants'; import { getTermsFields } from '../../index_pattern_util'; import { ValidatedNumberInput } from '../validated_number_input'; +import { getMaskI18nLabel } from '../../classes/layers/vector_layer/mask'; +import { MaskExpression } from './mask_expression'; function filterFieldsForAgg(fields: DataViewField[], aggType: AGG_TYPE) { if (!fields) { @@ -43,6 +45,8 @@ function filterFieldsForAgg(fields: DataViewField[], aggType: AGG_TYPE) { } interface Props { + bucketsName?: string; + isJoin: boolean; metric: AggDescriptor; fields: DataViewField[]; onChange: (metric: AggDescriptor) => void; @@ -52,7 +56,9 @@ interface Props { } export function MetricEditor({ + bucketsName, fields, + isJoin, metricsFilter, metric, onChange, @@ -64,6 +70,8 @@ export function MetricEditor({ return; } + // Intentionally not adding mask. + // Changing aggregation likely changes value range so keeping old mask does not seem relevent const descriptor = { type: metricAggregationType, label: metric.label, @@ -93,6 +101,8 @@ export function MetricEditor({ if (!fieldName || metric.type === AGG_TYPE.COUNT) { return; } + // Intentionally not adding mask. + // Changing field likely changes value range so keeping old mask does not seem relevent onChange({ label: metric.label, type: metric.type, @@ -223,6 +233,11 @@ export function MetricEditor({ {fieldSelect} {percentileSelect} {labelInput} + + + + + {removeButton} ); diff --git a/x-pack/plugins/maps/public/components/metrics_editor/metric_select.tsx b/x-pack/plugins/maps/public/components/metrics_editor/metric_select.tsx index e8dd63fe934e5..88c9b83bc54a4 100644 --- a/x-pack/plugins/maps/public/components/metrics_editor/metric_select.tsx +++ b/x-pack/plugins/maps/public/components/metrics_editor/metric_select.tsx @@ -9,54 +9,39 @@ import React from 'react'; import { i18n } from '@kbn/i18n'; import { EuiComboBox, EuiComboBoxOptionOption, EuiComboBoxProps } from '@elastic/eui'; import { AGG_TYPE } from '../../../common/constants'; +import { getAggDisplayName } from '../../classes/sources/es_agg_source'; const AGG_OPTIONS = [ { - label: i18n.translate('xpack.maps.metricSelect.averageDropDownOptionLabel', { - defaultMessage: 'Average', - }), + label: getAggDisplayName(AGG_TYPE.AVG), value: AGG_TYPE.AVG, }, { - label: i18n.translate('xpack.maps.metricSelect.countDropDownOptionLabel', { - defaultMessage: 'Count', - }), + label: getAggDisplayName(AGG_TYPE.COUNT), value: AGG_TYPE.COUNT, }, { - label: i18n.translate('xpack.maps.metricSelect.maxDropDownOptionLabel', { - defaultMessage: 'Max', - }), + label: getAggDisplayName(AGG_TYPE.MAX), value: AGG_TYPE.MAX, }, { - label: i18n.translate('xpack.maps.metricSelect.minDropDownOptionLabel', { - defaultMessage: 'Min', - }), + label: getAggDisplayName(AGG_TYPE.MIN), value: AGG_TYPE.MIN, }, { - label: i18n.translate('xpack.maps.metricSelect.percentileDropDownOptionLabel', { - defaultMessage: 'Percentile', - }), + label: getAggDisplayName(AGG_TYPE.PERCENTILE), value: AGG_TYPE.PERCENTILE, }, { - label: i18n.translate('xpack.maps.metricSelect.sumDropDownOptionLabel', { - defaultMessage: 'Sum', - }), + label: getAggDisplayName(AGG_TYPE.SUM), value: AGG_TYPE.SUM, }, { - label: i18n.translate('xpack.maps.metricSelect.termsDropDownOptionLabel', { - defaultMessage: 'Top term', - }), + label: getAggDisplayName(AGG_TYPE.TERMS), value: AGG_TYPE.TERMS, }, { - label: i18n.translate('xpack.maps.metricSelect.cardinalityDropDownOptionLabel', { - defaultMessage: 'Unique count', - }), + label: getAggDisplayName(AGG_TYPE.UNIQUE_COUNT), value: AGG_TYPE.UNIQUE_COUNT, }, ]; diff --git a/x-pack/plugins/maps/public/components/metrics_editor/metrics_editor.test.tsx b/x-pack/plugins/maps/public/components/metrics_editor/metrics_editor.test.tsx index 66fed40936b79..5aaf1369efe81 100644 --- a/x-pack/plugins/maps/public/components/metrics_editor/metrics_editor.test.tsx +++ b/x-pack/plugins/maps/public/components/metrics_editor/metrics_editor.test.tsx @@ -20,6 +20,7 @@ const defaultProps = { fields: [], onChange: () => {}, allowMultipleMetrics: true, + isJoin: false, }; test('should render metrics editor', () => { diff --git a/x-pack/plugins/maps/public/components/metrics_editor/metrics_editor.tsx b/x-pack/plugins/maps/public/components/metrics_editor/metrics_editor.tsx index b38e20b40d990..a18608b9631c2 100644 --- a/x-pack/plugins/maps/public/components/metrics_editor/metrics_editor.tsx +++ b/x-pack/plugins/maps/public/components/metrics_editor/metrics_editor.tsx @@ -22,6 +22,8 @@ export function isMetricValid(aggDescriptor: AggDescriptor) { interface Props { allowMultipleMetrics: boolean; + bucketsName?: string; + isJoin: boolean; metrics: AggDescriptor[]; fields: DataViewField[]; onChange: (metrics: AggDescriptor[]) => void; @@ -81,6 +83,8 @@ export class MetricsEditor extends Component { return (
{ metrics={this.props.metrics} onChange={this.props.onChange} allowMultipleMetrics={true} + isJoin={true} /> ); }; diff --git a/x-pack/plugins/maps/public/connected_components/edit_layer_panel/layer_settings/__snapshots__/attribution_form_row.test.tsx.snap b/x-pack/plugins/maps/public/connected_components/edit_layer_panel/layer_settings/__snapshots__/attribution_form_row.test.tsx.snap index cb496311b3d1c..46ddc7f58eb78 100644 --- a/x-pack/plugins/maps/public/connected_components/edit_layer_panel/layer_settings/__snapshots__/attribution_form_row.test.tsx.snap +++ b/x-pack/plugins/maps/public/connected_components/edit_layer_panel/layer_settings/__snapshots__/attribution_form_row.test.tsx.snap @@ -76,11 +76,7 @@ exports[`Should render edit form row when attribution not provided 1`] = ` onClick={[Function]} size="xs" > - + Clear
diff --git a/x-pack/plugins/maps/public/connected_components/edit_layer_panel/layer_settings/attribution_form_row.tsx b/x-pack/plugins/maps/public/connected_components/edit_layer_panel/layer_settings/attribution_form_row.tsx index bdab63e1029e7..38ac195904592 100644 --- a/x-pack/plugins/maps/public/connected_components/edit_layer_panel/layer_settings/attribution_form_row.tsx +++ b/x-pack/plugins/maps/public/connected_components/edit_layer_panel/layer_settings/attribution_form_row.tsx @@ -8,10 +8,10 @@ import React from 'react'; import { EuiButtonEmpty, EuiLink, EuiPanel } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; -import { FormattedMessage } from '@kbn/i18n-react'; import { Attribution } from '../../../../common/descriptor_types'; import { ILayer } from '../../../classes/layers/layer'; import { AttributionPopover } from './attribution_popover'; +import { panelStrings } from '../../panel_strings'; interface Props { layer: ILayer; @@ -65,9 +65,7 @@ export function AttributionFormRow(props: Props) { defaultMessage: 'Edit attribution', } )} - popoverButtonLabel={i18n.translate('xpack.maps.attribution.editBtnLabel', { - defaultMessage: 'Edit', - })} + popoverButtonLabel={panelStrings.edit} label={layerDescriptor.attribution.label} url={layerDescriptor.attribution.url} /> @@ -83,10 +81,7 @@ export function AttributionFormRow(props: Props) { defaultMessage: 'Clear attribution', })} > - + {panelStrings.clear} diff --git a/x-pack/plugins/maps/public/connected_components/edit_layer_panel/layer_settings/attribution_popover.tsx b/x-pack/plugins/maps/public/connected_components/edit_layer_panel/layer_settings/attribution_popover.tsx index 0371b68c85a3b..530b3cce20f00 100644 --- a/x-pack/plugins/maps/public/connected_components/edit_layer_panel/layer_settings/attribution_popover.tsx +++ b/x-pack/plugins/maps/public/connected_components/edit_layer_panel/layer_settings/attribution_popover.tsx @@ -20,6 +20,7 @@ import { import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n-react'; import { Attribution } from '../../../../common/descriptor_types'; +import { panelStrings } from '../../panel_strings'; interface Props { onChange: (attribution: Attribution) => void; @@ -128,7 +129,7 @@ export class AttributionPopover extends Component { onClick={this._onApply} size="s" > - + {panelStrings.apply} diff --git a/x-pack/plugins/maps/public/connected_components/edit_layer_panel/style_settings/style_settings.tsx b/x-pack/plugins/maps/public/connected_components/edit_layer_panel/style_settings/style_settings.tsx index 8d399f19a765c..02b9048e93b86 100644 --- a/x-pack/plugins/maps/public/connected_components/edit_layer_panel/style_settings/style_settings.tsx +++ b/x-pack/plugins/maps/public/connected_components/edit_layer_panel/style_settings/style_settings.tsx @@ -35,7 +35,7 @@ export function StyleSettings({ layer, updateStyleDescriptor, updateCustomIcons
diff --git a/x-pack/plugins/maps/public/connected_components/mb_map/mb_map.tsx b/x-pack/plugins/maps/public/connected_components/mb_map/mb_map.tsx index da4765ca094ec..71858ecb02459 100644 --- a/x-pack/plugins/maps/public/connected_components/mb_map/mb_map.tsx +++ b/x-pack/plugins/maps/public/connected_components/mb_map/mb_map.tsx @@ -283,11 +283,10 @@ export class MbMap extends Component { } _initResizerChecker() { + this.state.mbMap?.resize(); // ensure map is sized for container prior to monitoring this._checker = new ResizeChecker(this._containerRef!); this._checker.on('resize', () => { - if (this.state.mbMap) { - this.state.mbMap.resize(); - } + this.state.mbMap?.resize(); }); } diff --git a/x-pack/plugins/maps/public/connected_components/mb_map/tooltip_control/tooltip_control.test.tsx b/x-pack/plugins/maps/public/connected_components/mb_map/tooltip_control/tooltip_control.test.tsx index edd27d2d1edb1..9074fbd8fbeaf 100644 --- a/x-pack/plugins/maps/public/connected_components/mb_map/tooltip_control/tooltip_control.test.tsx +++ b/x-pack/plugins/maps/public/connected_components/mb_map/tooltip_control/tooltip_control.test.tsx @@ -70,6 +70,9 @@ const mockLayer = { }, } as unknown as IVectorSource; }, + getMasks: () => { + return []; + }, } as unknown as IVectorLayer; const mockMbMapHandlers: { [key: string]: (event?: MapMouseEvent) => void } = {}; diff --git a/x-pack/plugins/maps/public/connected_components/mb_map/tooltip_control/tooltip_control.tsx b/x-pack/plugins/maps/public/connected_components/mb_map/tooltip_control/tooltip_control.tsx index 1c14d11eb1f96..75e41464fd0f8 100644 --- a/x-pack/plugins/maps/public/connected_components/mb_map/tooltip_control/tooltip_control.tsx +++ b/x-pack/plugins/maps/public/connected_components/mb_map/tooltip_control/tooltip_control.tsx @@ -184,6 +184,15 @@ export class TooltipControl extends Component { continue; } + // masking must use paint property "opacity" to hide features in order to support feature state + // therefore, there is no way to remove masked features with queryRenderedFeatures + // masked features must be removed via manual filtering + const masks = layer.getMasks(); + const maskHiddingFeature = masks.find((mask) => mask.isFeatureMasked(mbFeature)); + if (maskHiddingFeature) { + continue; + } + const featureId = layer.getFeatureId(mbFeature); if (featureId === undefined) { continue; diff --git a/x-pack/plugins/maps/public/connected_components/panel_strings.ts b/x-pack/plugins/maps/public/connected_components/panel_strings.ts index f7f7278138e1e..f4eb5e871a3fe 100644 --- a/x-pack/plugins/maps/public/connected_components/panel_strings.ts +++ b/x-pack/plugins/maps/public/connected_components/panel_strings.ts @@ -8,12 +8,21 @@ import { i18n } from '@kbn/i18n'; export const panelStrings = { + apply: i18n.translate('xpack.maps.panel.applyLabel', { + defaultMessage: 'Apply', + }), + clear: i18n.translate('xpack.maps.panel.clearLabel', { + defaultMessage: 'Clear', + }), close: i18n.translate('xpack.maps.panel.closeLabel', { defaultMessage: 'Close', }), discardChanges: i18n.translate('xpack.maps.panel.discardChangesLabel', { defaultMessage: 'Discard changes', }), + edit: i18n.translate('xpack.maps.panel.editLabel', { + defaultMessage: 'Edit', + }), keepChanges: i18n.translate('xpack.maps.panel.keepChangesLabel', { defaultMessage: 'Keep changes', }), diff --git a/x-pack/plugins/maps/public/render_app.tsx b/x-pack/plugins/maps/public/render_app.tsx index 01eba4ee7905e..c4bddf5a71541 100644 --- a/x-pack/plugins/maps/public/render_app.tsx +++ b/x-pack/plugins/maps/public/render_app.tsx @@ -86,8 +86,7 @@ export async function renderApp( mapEmbeddableInput = { savedObjectId: routeProps.match.params.savedMapId, } as MapByReferenceInput; - } - if (valueInput) { + } else if (valueInput) { mapEmbeddableInput = valueInput as MapByValueInput; } diff --git a/x-pack/plugins/maps/public/routes/list_page/load_list_and_render.tsx b/x-pack/plugins/maps/public/routes/list_page/load_list_and_render.tsx index 26cb872006dee..f988c5b11ef98 100644 --- a/x-pack/plugins/maps/public/routes/list_page/load_list_and_render.tsx +++ b/x-pack/plugins/maps/public/routes/list_page/load_list_and_render.tsx @@ -5,12 +5,10 @@ * 2.0. */ -import React, { Component } from 'react'; -import { i18n } from '@kbn/i18n'; +import React, { useState, useEffect } from 'react'; import { Redirect } from 'react-router-dom'; import { EmbeddableStateTransfer } from '@kbn/embeddable-plugin/public'; import { ScopedHistory } from '@kbn/core/public'; -import { getToasts } from '../../kibana_services'; import { MapsListView } from './maps_list_view'; import { APP_ID } from '../../../common/constants'; import { mapsClient } from '../../content_management'; @@ -20,49 +18,39 @@ interface Props { stateTransfer: EmbeddableStateTransfer; } -export class LoadListAndRender extends Component { - _isMounted: boolean = false; - state = { - mapsLoaded: false, - hasSavedMaps: null, - }; - - componentDidMount() { - this._isMounted = true; - this.props.stateTransfer.clearEditorState(APP_ID); - this._loadMapsList(); - } - - componentWillUnmount() { - this._isMounted = false; +export function LoadListAndRender(props: Props) { + const [mapsLoaded, setMapsLoaded] = useState(false); + const [hasSavedMaps, setHasSavedMaps] = useState(true); + + useEffect(() => { + props.stateTransfer.clearEditorState(APP_ID); + + let ignore = false; + mapsClient + .search({ limit: 1 }) + .then((results) => { + if (!ignore) { + setHasSavedMaps(results.hits.length > 0); + setMapsLoaded(true); + } + }) + .catch((err) => { + if (!ignore) { + setMapsLoaded(true); + setHasSavedMaps(false); + } + }); + return () => { + ignore = true; + }; + // only run on mount + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []); + + if (!mapsLoaded) { + // do not render loading state to avoid UI flash when listing page is displayed + return null; } - async _loadMapsList() { - try { - const results = await mapsClient.search({ limit: 1 }); - if (this._isMounted) { - this.setState({ mapsLoaded: true, hasSavedMaps: !!results.hits.length }); - } - } catch (err) { - if (this._isMounted) { - this.setState({ mapsLoaded: true, hasSavedMaps: false }); - getToasts().addDanger({ - title: i18n.translate('xpack.maps.mapListing.errorAttemptingToLoadSavedMaps', { - defaultMessage: `Unable to load maps`, - }), - text: `${err}`, - }); - } - } - } - - render() { - const { mapsLoaded, hasSavedMaps } = this.state; - - if (mapsLoaded) { - return hasSavedMaps ? : ; - } else { - return null; - } - } + return hasSavedMaps ? : ; } diff --git a/x-pack/plugins/maps/public/routes/list_page/maps_list_view.tsx b/x-pack/plugins/maps/public/routes/list_page/maps_list_view.tsx index 95063f728a8fc..d98444057097d 100644 --- a/x-pack/plugins/maps/public/routes/list_page/maps_list_view.tsx +++ b/x-pack/plugins/maps/public/routes/list_page/maps_list_view.tsx @@ -5,7 +5,7 @@ * 2.0. */ -import React, { useCallback, memo } from 'react'; +import React, { useCallback, memo, useEffect } from 'react'; import type { SavedObjectsFindOptionsReference, ScopedHistory } from '@kbn/core/public'; import { METRIC_TYPE } from '@kbn/analytics'; import { i18n } from '@kbn/i18n'; @@ -72,8 +72,14 @@ function MapsListViewComp({ history }: Props) { const listingLimit = getUiSettings().get(SAVED_OBJECTS_LIMIT_SETTING); const initialPageSize = getUiSettings().get(SAVED_OBJECTS_PER_PAGE_SETTING); - getCoreChrome().docTitle.change(APP_NAME); - getCoreChrome().setBreadcrumbs([{ text: APP_NAME }]); + // TLDR; render should be side effect free + // + // setBreadcrumbs fires observables which cause state changes in ScreenReaderRouteAnnouncements. + // wrap chrome updates in useEffect to avoid potentially causing state changes in other component during render phase. + useEffect(() => { + getCoreChrome().docTitle.change(APP_NAME); + getCoreChrome().setBreadcrumbs([{ text: APP_NAME }]); + }, []); const findMaps = useCallback( async ( diff --git a/x-pack/plugins/maps/server/content_management/maps_storage.ts b/x-pack/plugins/maps/server/content_management/maps_storage.ts index 266ad81007345..5a031c675a32c 100644 --- a/x-pack/plugins/maps/server/content_management/maps_storage.ts +++ b/x-pack/plugins/maps/server/content_management/maps_storage.ts @@ -6,11 +6,16 @@ */ import Boom from '@hapi/boom'; import type { SearchQuery } from '@kbn/content-management-plugin/common'; -import type { ContentStorage, StorageContext } from '@kbn/content-management-plugin/server'; +import type { + ContentStorage, + StorageContext, + MSearchConfig, +} from '@kbn/content-management-plugin/server'; import type { SavedObject, SavedObjectReference, SavedObjectsFindOptions, + SavedObjectsFindResult, } from '@kbn/core-saved-objects-api-server'; import { CONTENT_ID } from '../../common/content_management'; @@ -86,7 +91,9 @@ function savedObjectToMapItem( const SO_TYPE: MapContentType = 'map'; -export class MapsStorage implements ContentStorage { +export class MapsStorage + implements ContentStorage> +{ constructor() {} async get(ctx: StorageContext, id: string): Promise { @@ -306,4 +313,29 @@ export class MapsStorage implements ContentStorage { return value; } + + // Configure `mSearch` to opt-in maps into the multi content type search API + mSearch = { + savedObjectType: SO_TYPE, + toItemResult: ( + ctx: StorageContext, + savedObject: SavedObjectsFindResult + ): MapItem => { + const { + utils: { getTransforms }, + } = ctx; + const transforms = getTransforms(cmServicesDefinition); + + // Validate DB response and DOWN transform to the request version + const { value, error: resultError } = transforms.mSearch.out.result.down( + savedObjectToMapItem(savedObject, false) + ); + + if (resultError) { + throw Boom.badRequest(`Invalid response. ${resultError.message}`); + } + + return value; + }, + }; } diff --git a/x-pack/plugins/ml/common/constants/search.ts b/x-pack/plugins/ml/common/constants/search.ts index 9985b502b085c..8ff9b022c274f 100644 --- a/x-pack/plugins/ml/common/constants/search.ts +++ b/x-pack/plugins/ml/common/constants/search.ts @@ -14,8 +14,3 @@ export const SEARCH_QUERY_LANGUAGE = { } as const; export type SearchQueryLanguage = typeof SEARCH_QUERY_LANGUAGE[keyof typeof SEARCH_QUERY_LANGUAGE]; - -export interface ErrorMessage { - query: string; - message: string; -} diff --git a/x-pack/plugins/ml/common/index.ts b/x-pack/plugins/ml/common/index.ts index 8d419f120a564..b540c4d3751f1 100644 --- a/x-pack/plugins/ml/common/index.ts +++ b/x-pack/plugins/ml/common/index.ts @@ -16,7 +16,6 @@ export { export { getSeverityColor, getSeverityType } from './util/anomaly_utils'; export { composeValidators, patternValidator } from './util/validators'; export { isRuntimeMappings, isRuntimeField } from './util/runtime_field_utils'; -export { extractErrorMessage } from './util/errors'; export type { RuntimeMappings } from './types/fields'; export { getDefaultCapabilities as getDefaultMlCapabilities } from './types/capabilities'; export { DATAFEED_STATE, JOB_STATE } from './constants/states'; diff --git a/x-pack/plugins/ml/common/types/data_frame_analytics.ts b/x-pack/plugins/ml/common/types/data_frame_analytics.ts index 26bdd29ac3090..cba66124bab4b 100644 --- a/x-pack/plugins/ml/common/types/data_frame_analytics.ts +++ b/x-pack/plugins/ml/common/types/data_frame_analytics.ts @@ -9,7 +9,7 @@ import Boom from '@hapi/boom'; import type * as estypes from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; import { isPopulatedObject } from '@kbn/ml-is-populated-object'; -import { EsErrorBody } from '../util/errors'; +import { EsErrorBody } from '@kbn/ml-error-utils'; import { ANALYSIS_CONFIG_TYPE } from '../constants/data_frame_analytics'; import type { UrlConfig } from './custom_urls'; diff --git a/x-pack/plugins/ml/common/types/job_service.ts b/x-pack/plugins/ml/common/types/job_service.ts index a3e1571070ffd..cf029111e0577 100644 --- a/x-pack/plugins/ml/common/types/job_service.ts +++ b/x-pack/plugins/ml/common/types/job_service.ts @@ -5,10 +5,10 @@ * 2.0. */ +import type { ErrorType } from '@kbn/ml-error-utils'; import { Job, JobStats, IndicesOptions } from './anomaly_detection_jobs'; import { RuntimeMappings } from './fields'; import { ES_AGGREGATION } from '../constants/aggregation_types'; -import { ErrorType } from '../util/errors'; export interface MlJobsResponse { jobs: Job[]; diff --git a/x-pack/plugins/ml/common/types/job_validation.ts b/x-pack/plugins/ml/common/types/job_validation.ts index 0c1db63ff3762..226166d45c956 100644 --- a/x-pack/plugins/ml/common/types/job_validation.ts +++ b/x-pack/plugins/ml/common/types/job_validation.ts @@ -5,7 +5,7 @@ * 2.0. */ -import type { ErrorType } from '../util/errors'; +import type { ErrorType } from '@kbn/ml-error-utils'; export interface DatafeedValidationResponse { valid: boolean; diff --git a/x-pack/plugins/ml/common/types/modules.ts b/x-pack/plugins/ml/common/types/modules.ts index dd9f098cabe1c..2a6cc9bbd57ce 100644 --- a/x-pack/plugins/ml/common/types/modules.ts +++ b/x-pack/plugins/ml/common/types/modules.ts @@ -6,8 +6,8 @@ */ import type { SavedObjectAttributes } from '@kbn/core/types'; +import type { ErrorType } from '@kbn/ml-error-utils'; import type { Datafeed, Job } from './anomaly_detection_jobs'; -import type { ErrorType } from '../util/errors'; export interface ModuleJob { id: string; diff --git a/x-pack/plugins/ml/common/types/results.ts b/x-pack/plugins/ml/common/types/results.ts index cf25fc6081e16..3fda97b5740b1 100644 --- a/x-pack/plugins/ml/common/types/results.ts +++ b/x-pack/plugins/ml/common/types/results.ts @@ -7,7 +7,7 @@ import type * as estypes from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; import type { LineAnnotationDatum, RectAnnotationDatum } from '@elastic/charts'; -import type { ErrorType } from '../util/errors'; +import type { ErrorType } from '@kbn/ml-error-utils'; import type { EntityField } from '../util/anomaly_utils'; import type { Datafeed, JobId, ModelSnapshot } from './anomaly_detection_jobs'; import { ES_AGGREGATION, ML_JOB_AGGREGATION } from '../constants/aggregation_types'; diff --git a/x-pack/plugins/ml/common/types/saved_objects.ts b/x-pack/plugins/ml/common/types/saved_objects.ts index ab3b97d1e614d..adaf00fd9405f 100644 --- a/x-pack/plugins/ml/common/types/saved_objects.ts +++ b/x-pack/plugins/ml/common/types/saved_objects.ts @@ -5,7 +5,7 @@ * 2.0. */ -import type { ErrorType } from '../util/errors'; +import type { ErrorType } from '@kbn/ml-error-utils'; export type JobType = 'anomaly-detector' | 'data-frame-analytics'; export type TrainedModelType = 'trained-model'; diff --git a/x-pack/plugins/ml/common/util/errors/types.ts b/x-pack/plugins/ml/common/util/errors/types.ts deleted file mode 100644 index 9f4b123e3e45d..0000000000000 --- a/x-pack/plugins/ml/common/util/errors/types.ts +++ /dev/null @@ -1,74 +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 { IHttpFetchError } from '@kbn/core-http-browser'; -import Boom from '@hapi/boom'; - -export interface EsErrorRootCause { - type: string; - reason: string; - caused_by?: EsErrorRootCause; - script?: string; -} - -export interface EsErrorBody { - error: { - root_cause?: EsErrorRootCause[]; - caused_by?: EsErrorRootCause; - type: string; - reason: string; - }; - status: number; -} - -export interface MLResponseError { - statusCode: number; - error: string; - message: string; - attributes?: { - body: EsErrorBody; - }; -} - -export interface ErrorMessage { - message: string; -} - -export interface MLErrorObject { - causedBy?: string; - message: string; - statusCode?: number; - fullError?: EsErrorBody; -} - -export interface MLHttpFetchErrorBase extends IHttpFetchError { - body: T; -} - -export type MLHttpFetchError = MLHttpFetchErrorBase; - -export type ErrorType = MLHttpFetchError | EsErrorBody | Boom.Boom | string | undefined; - -export function isEsErrorBody(error: any): error is EsErrorBody { - return error && error.error?.reason !== undefined; -} - -export function isErrorString(error: any): error is string { - return typeof error === 'string'; -} - -export function isErrorMessage(error: any): error is ErrorMessage { - return error && error.message !== undefined && typeof error.message === 'string'; -} - -export function isMLResponseError(error: any): error is MLResponseError { - return typeof error.body === 'object' && 'message' in error.body; -} - -export function isBoomError(error: any): error is Boom.Boom { - return error?.isBoom === true; -} diff --git a/x-pack/plugins/ml/public/application/components/data_grid/column_chart.scss b/x-pack/plugins/ml/public/application/components/data_grid/column_chart.scss deleted file mode 100644 index 551734bc2fcdc..0000000000000 --- a/x-pack/plugins/ml/public/application/components/data_grid/column_chart.scss +++ /dev/null @@ -1,27 +0,0 @@ -.mlDataGridChart__histogram { - width: 100%; - height: $euiSizeXL + $euiSizeXXL; -} - -.mlDataGridChart__legend { - @include euiTextTruncate; - @include euiFontSizeXS; - - color: $euiColorMediumShade; - display: block; - overflow-x: hidden; - margin: $euiSizeXS 0 0 0; - font-style: italic; - font-weight: normal; - text-align: left; -} - -.mlDataGridChart__legend--numeric { - text-align: right; -} - -.mlDataGridChart__legendBoolean { - width: 100%; - min-width: $euiButtonMinWidth; - td { text-align: center } -} diff --git a/x-pack/plugins/ml/public/application/components/data_grid/column_chart.tsx b/x-pack/plugins/ml/public/application/components/data_grid/column_chart.tsx index 06bb5a363d36a..f28d357f14869 100644 --- a/x-pack/plugins/ml/public/application/components/data_grid/column_chart.tsx +++ b/x-pack/plugins/ml/public/application/components/data_grid/column_chart.tsx @@ -5,18 +5,41 @@ * 2.0. */ -import React, { FC } from 'react'; -import classNames from 'classnames'; +import React, { type FC } from 'react'; +import { css } from '@emotion/react'; import { BarSeries, Chart, Settings, ScaleType } from '@elastic/charts'; -import { EuiDataGridColumn } from '@elastic/eui'; +import { euiTextTruncate, type EuiDataGridColumn } from '@elastic/eui'; -import './column_chart.scss'; +import { euiThemeVars } from '@kbn/ui-theme'; import { isUnsupportedChartData, ChartData } from '../../../../common/types/field_histograms'; import { useColumnChart } from './use_column_chart'; +const cssHistogram = css({ + width: '100%', + height: `calc(${euiThemeVars.euiSizeXL} + ${euiThemeVars.euiSizeXXL})`, +}); + +const cssHistogramLegend = css([ + css` + ${euiTextTruncate()} + `, + { + color: euiThemeVars.euiColorMediumShade, + display: 'block', + overflowX: 'hidden', + margin: `${euiThemeVars.euiSizeXS} 0 0 0`, + fontSize: euiThemeVars.euiFontSizeXS, + fontStyle: 'italic', + fontWeight: 'normal', + textAlign: 'left', + }, +]); + +const cssHistogramLegendNumeric = css([cssHistogramLegend, { textAlign: 'right' }]); + interface Props { chartData: ChartData; columnType: EuiDataGridColumn; @@ -41,6 +64,7 @@ const columnChartTheme = { }, scales: { barsPadding: 0.1 }, }; + export const ColumnChart: FC = ({ chartData, columnType, @@ -53,7 +77,7 @@ export const ColumnChart: FC = ({ return (
{!isUnsupportedChartData(chartData) && data.length > 0 && ( -
+
= ({
)}
{legendText} diff --git a/x-pack/plugins/ml/public/application/components/data_grid/common.ts b/x-pack/plugins/ml/public/application/components/data_grid/common.ts index 86587112f37d7..a32aafe2b0622 100644 --- a/x-pack/plugins/ml/public/application/components/data_grid/common.ts +++ b/x-pack/plugins/ml/public/application/components/data_grid/common.ts @@ -12,16 +12,14 @@ import { useMemo } from 'react'; import { EuiDataGridCellValueElementProps, EuiDataGridStyle } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; - import { CoreSetup } from '@kbn/core/public'; - import type { DataView, DataViewField } from '@kbn/data-views-plugin/common'; import { ES_FIELD_TYPES, KBN_FIELD_TYPES } from '@kbn/field-types'; import { getNestedProperty } from '@kbn/ml-nested-property'; - import { isCounterTimeSeriesMetric } from '@kbn/ml-agg-utils'; +import { extractErrorMessage } from '@kbn/ml-error-utils'; + import { DEFAULT_RESULTS_FIELD } from '../../../../common/constants/data_frame_analytics'; -import { extractErrorMessage } from '../../../../common/util/errors'; import { FeatureImportance, FeatureImportanceClassName, diff --git a/x-pack/plugins/ml/public/application/components/data_grid/data_grid.scss b/x-pack/plugins/ml/public/application/components/data_grid/data_grid.scss deleted file mode 100644 index f9cc09ef8c425..0000000000000 --- a/x-pack/plugins/ml/public/application/components/data_grid/data_grid.scss +++ /dev/null @@ -1,18 +0,0 @@ -.mlDataGrid { - - .euiDataGridRowCell--boolean { - text-transform: none; - } - - // Overrides to align the sorting arrow, actions icon and the column header when no chart is available, - // to the bottom of the cell when histogram charts are enabled. - // Note that overrides have to be used as currently it is not possible to add a custom class name - // for the EuiDataGridHeaderCell - see https://github.com/elastic/eui/issues/5106 - .euiDataGridHeaderCell { - .euiDataGridHeaderCell__sortingArrow, - .euiDataGridHeaderCell__icon, - .euiPopover { - margin-top: auto; - } - } -} diff --git a/x-pack/plugins/ml/public/application/components/data_grid/data_grid.tsx b/x-pack/plugins/ml/public/application/components/data_grid/data_grid.tsx index e641410500611..8a290ee186a98 100644 --- a/x-pack/plugins/ml/public/application/components/data_grid/data_grid.tsx +++ b/x-pack/plugins/ml/public/application/components/data_grid/data_grid.tsx @@ -7,8 +7,7 @@ import { isEqual } from 'lodash'; import React, { memo, useEffect, useCallback, useRef, FC } from 'react'; -import { i18n } from '@kbn/i18n'; -import { FormattedMessage } from '@kbn/i18n-react'; +import { css } from '@emotion/react'; import { EuiButtonEmpty, @@ -27,8 +26,11 @@ import { EuiToolTip, } from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; +import { FormattedMessage } from '@kbn/i18n-react'; import { CoreSetup } from '@kbn/core/public'; import { isPopulatedObject } from '@kbn/ml-is-populated-object'; + import { DEFAULT_SAMPLER_SHARD_SIZE } from '../../../../common/constants/field_histograms'; import { ANALYSIS_CONFIG_TYPE, INDEX_STATUS } from '../../data_frame_analytics/common'; @@ -49,10 +51,22 @@ import { import { DEFAULT_RESULTS_FIELD } from '../../../../common/constants/data_frame_analytics'; import { DataFrameAnalysisConfigType } from '../../../../common/types/data_frame_analytics'; -import './data_grid.scss'; // TODO Fix row hovering + bar highlighting // import { hoveredRow$ } from './column_chart'; +const cssOverride = css({ + '.euiDataGridRowCell--boolean': { textTransform: 'none' }, + // Overrides to align the sorting arrow, actions icon and the column header when no chart is available, + // to the bottom of the cell when histogram charts are enabled. + // Note that overrides have to be used as currently it is not possible to add a custom class name + // for the EuiDataGridHeaderCell - see https://github.com/elastic/eui/issues/5106 + '.euiDataGridHeaderCell': { + '.euiDataGridHeaderCell__sortingArrow,.euiDataGridHeaderCell__icon,.euiPopover': { + marginTop: 'auto', + }, + }, +}); + export const DataGridTitle: FC<{ title: string }> = ({ title }) => ( {title} @@ -337,7 +351,7 @@ export const DataGrid: FC = memo( onMutation={onMutation} > {(mutationRef) => ( -
+
{ diff --git a/x-pack/plugins/ml/public/application/components/data_grid/use_column_chart.tsx b/x-pack/plugins/ml/public/application/components/data_grid/use_column_chart.tsx index f4c372d379357..e60c2f5b8f3ba 100644 --- a/x-pack/plugins/ml/public/application/components/data_grid/use_column_chart.tsx +++ b/x-pack/plugins/ml/public/application/components/data_grid/use_column_chart.tsx @@ -8,13 +8,14 @@ import moment from 'moment'; import { BehaviorSubject } from 'rxjs'; import React from 'react'; +import { css } from '@emotion/react'; import useObservable from 'react-use/lib/useObservable'; -import { euiPaletteColorBlind, EuiDataGridColumn } from '@elastic/eui'; +import { euiPaletteColorBlind, type EuiDataGridColumn } from '@elastic/eui'; +import { euiThemeVars } from '@kbn/ui-theme'; import { i18n } from '@kbn/i18n'; - import { KBN_FIELD_TYPES } from '@kbn/field-types'; import { @@ -28,6 +29,18 @@ import { import { NON_AGGREGATABLE } from './common'; +const cssHistogramLegendBoolean = css({ + width: '100%', + // This was originally $euiButtonMinWidth, but that + // is no longer exported from the EUI package, + // so we're replicating it here inline. + minWidth: `calc(${euiThemeVars.euiSize} * 7)`, +}); + +const cssTextAlignCenter = css({ + textAlign: 'center', +}); + export const hoveredRow$ = new BehaviorSubject(null); export const BAR_COLOR = euiPaletteColorBlind()[0]; @@ -94,11 +107,15 @@ export const getLegendText = ( if (chartData.type === 'boolean') { return ( - +
- {chartData.data[0] !== undefined && } - {chartData.data[1] !== undefined && } + {chartData.data[0] !== undefined && ( + + )} + {chartData.data[1] !== undefined && ( + + )}
{chartData.data[0].key_as_string}{chartData.data[1].key_as_string}{chartData.data[0].key_as_string}{chartData.data[1].key_as_string}
diff --git a/x-pack/plugins/ml/public/application/components/import_export_jobs/import_jobs_flyout/import_jobs_flyout.tsx b/x-pack/plugins/ml/public/application/components/import_export_jobs/import_jobs_flyout/import_jobs_flyout.tsx index 0e786a63b222d..94effae6da28d 100644 --- a/x-pack/plugins/ml/public/application/components/import_export_jobs/import_jobs_flyout/import_jobs_flyout.tsx +++ b/x-pack/plugins/ml/public/application/components/import_export_jobs/import_jobs_flyout/import_jobs_flyout.tsx @@ -6,9 +6,7 @@ */ import React, { FC, useState, useEffect, useCallback, useMemo } from 'react'; -import { FormattedMessage } from '@kbn/i18n-react'; import useDebounce from 'react-use/lib/useDebounce'; -import { i18n } from '@kbn/i18n'; import { EuiFlyout, @@ -29,6 +27,10 @@ import { EuiFieldText, } from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; +import { FormattedMessage } from '@kbn/i18n-react'; +import { type ErrorType, extractErrorProperties } from '@kbn/ml-error-utils'; + import type { DataFrameAnalyticsConfig } from '../../../data_frame_analytics/common'; import type { JobType } from '../../../../../common/types/saved_objects'; import { useMlApiContext, useMlKibana } from '../../../contexts/kibana'; @@ -38,7 +40,6 @@ import { toastNotificationServiceProvider } from '../../../services/toast_notifi import { JobImportService } from './jobs_import_service'; import { useValidateIds } from './validate'; import type { ImportedAdJob, JobIdObject, SkippedJobs } from './jobs_import_service'; -import { ErrorType, extractErrorProperties } from '../../../../../common/util/errors'; interface Props { isDisabled: boolean; diff --git a/x-pack/plugins/ml/public/application/components/rule_editor/rule_editor_flyout.js b/x-pack/plugins/ml/public/application/components/rule_editor/rule_editor_flyout.js index ed5be09e03b42..668907c010108 100644 --- a/x-pack/plugins/ml/public/application/components/rule_editor/rule_editor_flyout.js +++ b/x-pack/plugins/ml/public/application/components/rule_editor/rule_editor_flyout.js @@ -11,8 +11,6 @@ import PropTypes from 'prop-types'; import React, { Component } from 'react'; -import { i18n } from '@kbn/i18n'; -import { FormattedMessage } from '@kbn/i18n-react'; import { EuiButton, @@ -31,6 +29,10 @@ import { EuiTitle, } from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; +import { FormattedMessage } from '@kbn/i18n-react'; +import { extractErrorMessage } from '@kbn/ml-error-utils'; + import { DetectorDescriptionList } from './components/detector_description_list'; import { ActionsSection } from './actions_section'; import { checkPermission } from '../../capabilities/check_capabilities'; @@ -54,7 +56,6 @@ import { getPartitioningFieldNames } from '../../../../common/util/job_utils'; import { withKibana } from '@kbn/kibana-react-plugin/public'; import { mlJobService } from '../../services/job_service'; import { ml } from '../../services/ml_api_service'; -import { extractErrorMessage } from '../../../../common/util/errors'; class RuleEditorFlyoutUI extends Component { static propTypes = { diff --git a/x-pack/plugins/ml/public/application/components/scatterplot_matrix/scatterplot_matrix.tsx b/x-pack/plugins/ml/public/application/components/scatterplot_matrix/scatterplot_matrix.tsx index 9a10dc2b782c9..404c897822125 100644 --- a/x-pack/plugins/ml/public/application/components/scatterplot_matrix/scatterplot_matrix.tsx +++ b/x-pack/plugins/ml/public/application/components/scatterplot_matrix/scatterplot_matrix.tsx @@ -6,9 +6,8 @@ */ import React, { useMemo, useEffect, useState, FC, useCallback } from 'react'; -import type * as estypes from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; -import rison from '@kbn/rison'; +import type * as estypes from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; import { EuiCallOut, EuiComboBox, @@ -23,16 +22,17 @@ import { EuiSwitch, } from '@elastic/eui'; +import rison from '@kbn/rison'; import { i18n } from '@kbn/i18n'; import { Query } from '@kbn/data-plugin/common/query'; - import { DataView } from '@kbn/data-views-plugin/public'; import { stringHash } from '@kbn/ml-string-hash'; -import { extractErrorMessage } from '../../../../common'; +import { extractErrorMessage } from '@kbn/ml-error-utils'; + import { isRuntimeMappings } from '../../../../common/util/runtime_field_utils'; import { RuntimeMappings } from '../../../../common/types/fields'; -import { getCombinedRuntimeMappings } from '../data_grid'; +import { getCombinedRuntimeMappings } from '../data_grid'; import { useMlApiContext, useMlKibana } from '../../contexts/kibana'; import { getProcessedFields } from '../data_grid'; diff --git a/x-pack/plugins/ml/public/application/data_frame_analytics/common/analytics.ts b/x-pack/plugins/ml/public/application/data_frame_analytics/common/analytics.ts index 4638d3824ad61..a1609c601cb72 100644 --- a/x-pack/plugins/ml/public/application/data_frame_analytics/common/analytics.ts +++ b/x-pack/plugins/ml/public/application/data_frame_analytics/common/analytics.ts @@ -9,9 +9,9 @@ import { useEffect } from 'react'; import { BehaviorSubject, Subscription } from 'rxjs'; import { distinctUntilChanged, filter } from 'rxjs/operators'; import { cloneDeep } from 'lodash'; +import { extractErrorMessage } from '@kbn/ml-error-utils'; import { ml } from '../../services/ml_api_service'; import { Dictionary } from '../../../../common/types/common'; -import { extractErrorMessage } from '../../../../common/util/errors'; import { ClassificationEvaluateResponse, EvaluateMetrics, diff --git a/x-pack/plugins/ml/public/application/data_frame_analytics/common/get_index_data.ts b/x-pack/plugins/ml/public/application/data_frame_analytics/common/get_index_data.ts index 29d76d32d8855..afd580da21ea3 100644 --- a/x-pack/plugins/ml/public/application/data_frame_analytics/common/get_index_data.ts +++ b/x-pack/plugins/ml/public/application/data_frame_analytics/common/get_index_data.ts @@ -6,7 +6,7 @@ */ import type * as estypes from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; -import { extractErrorMessage } from '../../../../common/util/errors'; +import { extractErrorMessage } from '@kbn/ml-error-utils'; import { EsSorting, UseDataGridReturnType, getProcessedFields } from '../../components/data_grid'; import { ml } from '../../services/ml_api_service'; diff --git a/x-pack/plugins/ml/public/application/data_frame_analytics/common/use_results_view_config.ts b/x-pack/plugins/ml/public/application/data_frame_analytics/common/use_results_view_config.ts index df430142a3193..1908ff9ad614c 100644 --- a/x-pack/plugins/ml/public/application/data_frame_analytics/common/use_results_view_config.ts +++ b/x-pack/plugins/ml/public/application/data_frame_analytics/common/use_results_view_config.ts @@ -10,8 +10,7 @@ import { useEffect, useState } from 'react'; import { i18n } from '@kbn/i18n'; import type { DataView } from '@kbn/data-views-plugin/public'; - -import { extractErrorMessage } from '../../../../common/util/errors'; +import { extractErrorMessage } from '@kbn/ml-error-utils'; import { getDataViewIdFromName } from '../../util/index_utils'; import { ml } from '../../services/ml_api_service'; diff --git a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_creation/components/create_analytics_advanced_editor/create_analytics_advanced_editor.tsx b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_creation/components/create_analytics_advanced_editor/create_analytics_advanced_editor.tsx index ef43f1ee0d08b..c809597ed1208 100644 --- a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_creation/components/create_analytics_advanced_editor/create_analytics_advanced_editor.tsx +++ b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_creation/components/create_analytics_advanced_editor/create_analytics_advanced_editor.tsx @@ -10,11 +10,11 @@ import { debounce } from 'lodash'; import { EuiCallOut, EuiFieldText, EuiForm, EuiFormRow, EuiSpacer } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; - import { CodeEditor } from '@kbn/kibana-react-plugin/public'; +import { extractErrorMessage } from '@kbn/ml-error-utils'; + import { useNotifications } from '../../../../../contexts/kibana'; import { ml } from '../../../../../services/ml_api_service'; -import { extractErrorMessage } from '../../../../../../../common/util/errors'; import { CreateAnalyticsFormProps } from '../../../analytics_management/hooks/use_create_analytics_form'; import { CreateStep } from '../create_step'; import { ANALYTICS_STEPS } from '../../page'; diff --git a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_creation/components/details_step/details_step_form.tsx b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_creation/components/details_step/details_step_form.tsx index 265018ec23f9f..dcf5adfba5ff6 100644 --- a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_creation/components/details_step/details_step_form.tsx +++ b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_creation/components/details_step/details_step_form.tsx @@ -9,6 +9,7 @@ import React, { FC, Fragment, useRef, useEffect, useMemo, useState } from 'react import { debounce } from 'lodash'; import { EuiFieldText, EuiFormRow, EuiLink, EuiSpacer, EuiSwitch, EuiTextArea } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; +import { extractErrorMessage } from '@kbn/ml-error-utils'; import { useMlKibana } from '../../../../../contexts/kibana'; import { CreateAnalyticsStepProps } from '../../../analytics_management/hooks/use_create_analytics_form'; @@ -16,7 +17,6 @@ import { JOB_ID_MAX_LENGTH } from '../../../../../../../common/constants/validat import { ContinueButton } from '../continue_button'; import { ANALYTICS_STEPS } from '../../page'; import { ml } from '../../../../../services/ml_api_service'; -import { extractErrorMessage } from '../../../../../../../common/util/errors'; const DEFAULT_RESULTS_FIELD = 'ml'; diff --git a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_creation/components/shared/fetch_explain_data.ts b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_creation/components/shared/fetch_explain_data.ts index ebf5d2dce2706..e0be92ebe1a26 100644 --- a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_creation/components/shared/fetch_explain_data.ts +++ b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_creation/components/shared/fetch_explain_data.ts @@ -5,8 +5,9 @@ * 2.0. */ +import { extractErrorProperties } from '@kbn/ml-error-utils'; + import { ml } from '../../../../../services/ml_api_service'; -import { extractErrorProperties } from '../../../../../../../common/util/errors'; import { DfAnalyticsExplainResponse, FieldSelectionItem, diff --git a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_creation/components/validation_step/validation_step_wrapper.tsx b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_creation/components/validation_step/validation_step_wrapper.tsx index d5b2ba8251ad4..c1256529f357f 100644 --- a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_creation/components/validation_step/validation_step_wrapper.tsx +++ b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_creation/components/validation_step/validation_step_wrapper.tsx @@ -6,9 +6,12 @@ */ import React, { FC, useEffect, useMemo, useState } from 'react'; +import { debounce } from 'lodash'; + import { EuiForm } from '@elastic/eui'; + import { i18n } from '@kbn/i18n'; -import { debounce } from 'lodash'; +import { extractErrorMessage } from '@kbn/ml-error-utils'; import { CreateAnalyticsStepProps } from '../../../analytics_management/hooks/use_create_analytics_form'; import { ValidationStep } from './validation_step'; @@ -16,7 +19,6 @@ import { ValidationStepDetails } from './validation_step_details'; import { ANALYTICS_STEPS } from '../../page'; import { useMlApiContext } from '../../../../../contexts/kibana'; import { getJobConfigFromFormState } from '../../../analytics_management/hooks/use_create_analytics_form/state'; -import { extractErrorMessage } from '../../../../../../../common/util/errors'; import { CalloutMessage, ValidateAnalyticsJobResponse, diff --git a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_creation/hooks/use_index_data.ts b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_creation/hooks/use_index_data.ts index 4f681f07ff69e..fdd338be65bb2 100644 --- a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_creation/hooks/use_index_data.ts +++ b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_creation/hooks/use_index_data.ts @@ -9,11 +9,13 @@ import { useEffect, useMemo, useState } from 'react'; import type * as estypes from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; import { EuiDataGridColumn } from '@elastic/eui'; -import { CoreSetup } from '@kbn/core/public'; +import { CoreSetup } from '@kbn/core/public'; import type { DataView } from '@kbn/data-views-plugin/public'; import { isPopulatedObject } from '@kbn/ml-is-populated-object'; import type { TimeRange as TimeRangeMs } from '@kbn/ml-date-picker'; +import { extractErrorMessage } from '@kbn/ml-error-utils'; + import { isRuntimeMappings } from '../../../../../../common/util/runtime_field_utils'; import { RuntimeMappings } from '../../../../../../common/types/fields'; import { DEFAULT_SAMPLER_SHARD_SIZE } from '../../../../../../common/constants/field_histograms'; @@ -34,7 +36,7 @@ import { getProcessedFields, getCombinedRuntimeMappings, } from '../../../../components/data_grid'; -import { extractErrorMessage } from '../../../../../../common/util/errors'; + import { INDEX_STATUS } from '../../../common/analytics'; import { ml } from '../../../../services/ml_api_service'; diff --git a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_exploration/components/exploration_query_bar/exploration_query_bar.tsx b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_exploration/components/exploration_query_bar/exploration_query_bar.tsx index 464d57ff2917a..1ac8404318385 100644 --- a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_exploration/components/exploration_query_bar/exploration_query_bar.tsx +++ b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_exploration/components/exploration_query_bar/exploration_query_bar.tsx @@ -6,17 +6,19 @@ */ import React, { FC, useEffect, useMemo, useState } from 'react'; +import { debounce } from 'lodash'; + +import type * as estypes from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; import { EuiButtonGroup, EuiCode, EuiFlexGroup, EuiFlexItem, EuiInputPopover } from '@elastic/eui'; -import { i18n } from '@kbn/i18n'; -import { debounce } from 'lodash'; +import { i18n } from '@kbn/i18n'; import { fromKueryExpression, luceneStringToDsl, toElasticsearchQuery } from '@kbn/es-query'; -import type * as estypes from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; import { DataView } from '@kbn/data-views-plugin/common'; import type { Query } from '@kbn/es-query'; import { QueryStringInput } from '@kbn/unified-search-plugin/public'; -import { Dictionary } from '../../../../../../../common/types/common'; +import { QueryErrorMessage } from '@kbn/ml-error-utils'; +import { Dictionary } from '../../../../../../../common/types/common'; import { SEARCH_QUERY_LANGUAGE, SearchQueryLanguage, @@ -25,11 +27,6 @@ import { removeFilterFromQueryString } from '../../../../../explorer/explorer_ut import { SavedSearchQuery } from '../../../../../contexts/ml'; import { useMlKibana } from '../../../../../contexts/kibana'; -interface ErrorMessage { - query: string; - message: string; -} - export interface ExplorationQueryBarProps { indexPattern: DataView; setSearchQuery: (update: { @@ -55,7 +52,9 @@ export const ExplorationQueryBar: FC = ({ // The internal state of the input query bar updated on every key stroke. const [searchInput, setSearchInput] = useState(query); const [idToSelectedMap, setIdToSelectedMap] = useState<{ [id: string]: boolean }>({}); - const [errorMessage, setErrorMessage] = useState(undefined); + const [queryErrorMessage, setQueryErrorMessage] = useState( + undefined + ); const { services } = useMlKibana(); const { @@ -119,7 +118,7 @@ export const ExplorationQueryBar: FC = ({ convertedQuery = luceneStringToDsl(query.query as string); break; default: - setErrorMessage({ + setQueryErrorMessage({ query: query.query as string, message: i18n.translate('xpack.ml.queryBar.queryLanguageNotSupported', { defaultMessage: 'Query language is not supported', @@ -133,7 +132,7 @@ export const ExplorationQueryBar: FC = ({ language: query.language, }); } catch (e) { - setErrorMessage({ query: query.query as string, message: e.message }); + setQueryErrorMessage({ query: query.query as string, message: e.message }); } // eslint-disable-next-line react-hooks/exhaustive-deps }, [query.query]); @@ -187,7 +186,7 @@ export const ExplorationQueryBar: FC = ({ return ( setErrorMessage(undefined)} + closePopover={() => setQueryErrorMessage(undefined)} input={ @@ -249,14 +248,14 @@ export const ExplorationQueryBar: FC = ({ )} } - isOpen={errorMessage?.query === searchInput.query && errorMessage?.message !== ''} + isOpen={queryErrorMessage?.query === searchInput.query && queryErrorMessage?.message !== ''} > {i18n.translate('xpack.ml.stepDefineForm.invalidQuery', { defaultMessage: 'Invalid Query', })} {': '} - {errorMessage?.message.split('\n')[0]} + {queryErrorMessage?.message.split('\n')[0]} ); diff --git a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_exploration/components/exploration_results_table/use_exploration_results.ts b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_exploration/components/exploration_results_table/use_exploration_results.ts index b52c06905792c..30749558a23a1 100644 --- a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_exploration/components/exploration_results_table/use_exploration_results.ts +++ b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_exploration/components/exploration_results_table/use_exploration_results.ts @@ -10,9 +10,10 @@ import { useCallback, useEffect, useMemo, useState } from 'react'; import { EuiDataGridColumn } from '@elastic/eui'; import { CoreSetup } from '@kbn/core/public'; - import { i18n } from '@kbn/i18n'; import type { DataView } from '@kbn/data-views-plugin/public'; +import { extractErrorMessage } from '@kbn/ml-error-utils'; + import { MlApiServices } from '../../../../../services/ml_api_service'; import { DataLoader } from '../../../../../datavisualizer/index_based/data_loader'; @@ -35,7 +36,6 @@ import { FEATURE_IMPORTANCE, TOP_CLASSES } from '../../../../common/constants'; import { DEFAULT_RESULTS_FIELD } from '../../../../../../../common/constants/data_frame_analytics'; import { sortExplorationResultsFields, ML__ID_COPY } from '../../../../common/fields'; import { isRegressionAnalysis } from '../../../../common/analytics'; -import { extractErrorMessage } from '../../../../../../../common/util/errors'; import { useTrainedModelsApiService } from '../../../../../services/ml_api_service/trained_models'; import { FeatureImportanceBaseline } from '../../../../../../../common/types/feature_importance'; import { useExplorationDataGrid } from './use_exploration_data_grid'; diff --git a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/action_clone/clone_action_name.tsx b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/action_clone/clone_action_name.tsx index 76b85e2384dfd..0ba51a7ca64da 100644 --- a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/action_clone/clone_action_name.tsx +++ b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/action_clone/clone_action_name.tsx @@ -11,6 +11,7 @@ import { cloneDeep, isEqual } from 'lodash'; import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n-react'; import { toMountPoint, wrapWithTheme } from '@kbn/kibana-react-plugin/public'; +import { extractErrorMessage } from '@kbn/ml-error-utils'; import { DeepReadonly } from '../../../../../../../common/types/common'; import { DataFrameAnalyticsConfig, isOutlierAnalysis } from '../../../../common'; import { isClassificationAnalysis, isRegressionAnalysis } from '../../../../common/analytics'; @@ -19,7 +20,6 @@ import { useMlKibana, useNavigateToPath } from '../../../../../contexts/kibana'; import { DEFAULT_NUM_TOP_FEATURE_IMPORTANCE_VALUES } from '../../hooks/use_create_analytics_form'; import { State } from '../../hooks/use_create_analytics_form/state'; import { DataFrameAnalyticsListRow } from '../analytics_list/common'; -import { extractErrorMessage } from '../../../../../../../common/util/errors'; interface PropDefinition { /** diff --git a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/action_delete/use_delete_action.tsx b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/action_delete/use_delete_action.tsx index 4d1565c1769f3..01b6e3a3f50ad 100644 --- a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/action_delete/use_delete_action.tsx +++ b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/action_delete/use_delete_action.tsx @@ -8,7 +8,7 @@ import React, { useEffect, useMemo, useState } from 'react'; import { i18n } from '@kbn/i18n'; -import { extractErrorMessage } from '../../../../../../../common/util/errors'; +import { extractErrorMessage } from '@kbn/ml-error-utils'; import { useMlKibana } from '../../../../../contexts/kibana'; import { useToastNotificationService } from '../../../../../services/toast_notification_service'; diff --git a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/hooks/use_create_analytics_form/use_create_analytics_form.ts b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/hooks/use_create_analytics_form/use_create_analytics_form.ts index cddc4fcd092dc..4a6ed2176be25 100644 --- a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/hooks/use_create_analytics_form/use_create_analytics_form.ts +++ b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/hooks/use_create_analytics_form/use_create_analytics_form.ts @@ -10,7 +10,8 @@ import { useReducer } from 'react'; import { i18n } from '@kbn/i18n'; import { DuplicateDataViewError } from '@kbn/data-plugin/public'; -import { extractErrorMessage } from '../../../../../../../common/util/errors'; +import { extractErrorMessage } from '@kbn/ml-error-utils'; + import { DeepReadonly } from '../../../../../../../common/types/common'; import { ml } from '../../../../../services/ml_api_service'; import { useMlContext } from '../../../../../contexts/ml'; diff --git a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/services/analytics_service/delete_analytics.ts b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/services/analytics_service/delete_analytics.ts index 537b2016d9af3..c11490a660f10 100644 --- a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/services/analytics_service/delete_analytics.ts +++ b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/services/analytics_service/delete_analytics.ts @@ -6,7 +6,8 @@ */ import { i18n } from '@kbn/i18n'; -import { extractErrorMessage } from '../../../../../../../common/util/errors'; +import { extractErrorMessage } from '@kbn/ml-error-utils'; + import { ml } from '../../../../../services/ml_api_service'; import { ToastNotificationService } from '../../../../../services/toast_notification_service'; import { refreshAnalyticsList$, REFRESH_ANALYTICS_LIST_STATE } from '../../../../common'; diff --git a/x-pack/plugins/ml/public/application/explorer/components/explorer_query_bar/explorer_query_bar.tsx b/x-pack/plugins/ml/public/application/explorer/components/explorer_query_bar/explorer_query_bar.tsx index bbb18697dab1c..8f4cd30a4c115 100644 --- a/x-pack/plugins/ml/public/application/explorer/components/explorer_query_bar/explorer_query_bar.tsx +++ b/x-pack/plugins/ml/public/application/explorer/components/explorer_query_bar/explorer_query_bar.tsx @@ -12,7 +12,8 @@ import { fromKueryExpression, luceneStringToDsl, toElasticsearchQuery } from '@k import type { Query } from '@kbn/es-query'; import { QueryStringInput } from '@kbn/unified-search-plugin/public'; import type { DataView } from '@kbn/data-views-plugin/common'; -import { SEARCH_QUERY_LANGUAGE, ErrorMessage } from '../../../../../common/constants/search'; +import type { QueryErrorMessage } from '@kbn/ml-error-utils'; +import { SEARCH_QUERY_LANGUAGE } from '../../../../../common/constants/search'; import { InfluencersFilterQuery } from '../../../../../common/types/es_client'; import { useAnomalyExplorerContext } from '../../anomaly_explorer_context'; import { useMlKibana } from '../../../contexts/kibana'; @@ -129,7 +130,9 @@ export const ExplorerQueryBar: FC = ({ const [searchInput, setSearchInput] = useState( getInitSearchInputState({ filterActive, queryString }) ); - const [errorMessage, setErrorMessage] = useState(undefined); + const [queryErrorMessage, setQueryErrorMessage] = useState( + undefined + ); useEffect( function updateSearchInputFromFilter() { @@ -160,14 +163,14 @@ export const ExplorerQueryBar: FC = ({ } } catch (e) { console.log('Invalid query syntax in search bar', e); // eslint-disable-line no-console - setErrorMessage({ query: query.query as string, message: e.message }); + setQueryErrorMessage({ query: query.query as string, message: e.message }); } }; return ( = ({ }} /> } - isOpen={errorMessage?.query === searchInput.query && errorMessage?.message !== ''} + isOpen={queryErrorMessage?.query === searchInput.query && queryErrorMessage?.message !== ''} > {i18n.translate('xpack.ml.explorer.invalidKuerySyntaxErrorMessageQueryBar', { defaultMessage: 'Invalid query', })} {': '} - {errorMessage?.message.split('\n')[0]} + {queryErrorMessage?.message.split('\n')[0]} ); diff --git a/x-pack/plugins/ml/public/application/explorer/explorer_utils.ts b/x-pack/plugins/ml/public/application/explorer/explorer_utils.ts index e7ad6802c4401..f0f26a3918438 100644 --- a/x-pack/plugins/ml/public/application/explorer/explorer_utils.ts +++ b/x-pack/plugins/ml/public/application/explorer/explorer_utils.ts @@ -11,19 +11,20 @@ import { get, union, uniq } from 'lodash'; import moment from 'moment-timezone'; +import { lastValueFrom } from 'rxjs'; + import { ES_FIELD_TYPES } from '@kbn/field-types'; import { asyncForEach } from '@kbn/std'; import { isPopulatedObject } from '@kbn/ml-is-populated-object'; import type { DataViewsContract } from '@kbn/data-views-plugin/public'; +import { extractErrorMessage } from '@kbn/ml-error-utils'; -import { lastValueFrom } from 'rxjs'; import { ANNOTATIONS_TABLE_DEFAULT_QUERY_SIZE, ANOMALIES_TABLE_DEFAULT_QUERY_SIZE, } from '../../../common/constants/search'; import { EntityField, getEntityFieldList } from '../../../common/util/anomaly_utils'; import { getDataViewIdFromName } from '../util/index_utils'; -import { extractErrorMessage } from '../../../common/util/errors'; import { ML_JOB_AGGREGATION } from '../../../common/constants/aggregation_types'; import { isSourceDataChartableForDetector, diff --git a/x-pack/plugins/ml/public/application/jobs/jobs_list/components/job_details/job_messages_pane.tsx b/x-pack/plugins/ml/public/application/jobs/jobs_list/components/job_details/job_messages_pane.tsx index 8b3ed99709cb5..86cd23b46383e 100644 --- a/x-pack/plugins/ml/public/application/jobs/jobs_list/components/job_details/job_messages_pane.tsx +++ b/x-pack/plugins/ml/public/application/jobs/jobs_list/components/job_details/job_messages_pane.tsx @@ -9,10 +9,10 @@ import React, { FC, useCallback, useEffect, useState } from 'react'; import { EuiButton, EuiFlexGroup, EuiFlexItem, EuiSpacer, EuiToolTip } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n-react'; +import { extractErrorMessage } from '@kbn/ml-error-utils'; import { ml } from '../../../../services/ml_api_service'; import { JobMessages } from '../../../../components/job_messages'; import { JobMessage } from '../../../../../../common/types/audit_message'; -import { extractErrorMessage } from '../../../../../../common/util/errors'; import { useToastNotificationService } from '../../../../services/toast_notification_service'; import { useMlApiContext } from '../../../../contexts/kibana'; import { checkPermission } from '../../../../capabilities/check_capabilities'; diff --git a/x-pack/plugins/ml/public/application/jobs/new_job/common/job_creator/util/model_memory_estimator.ts b/x-pack/plugins/ml/public/application/jobs/new_job/common/job_creator/util/model_memory_estimator.ts index 159f1af10e69f..52d4d77106217 100644 --- a/x-pack/plugins/ml/public/application/jobs/new_job/common/job_creator/util/model_memory_estimator.ts +++ b/x-pack/plugins/ml/public/application/jobs/new_job/common/job_creator/util/model_memory_estimator.ts @@ -5,7 +5,6 @@ * 2.0. */ -import { i18n } from '@kbn/i18n'; import { combineLatest, Observable, of, Subject, Subscription } from 'rxjs'; import { isEqual, cloneDeep } from 'lodash'; import { @@ -21,10 +20,13 @@ import { skipWhile, } from 'rxjs/operators'; import { useEffect, useMemo } from 'react'; + +import { i18n } from '@kbn/i18n'; +import { type MLHttpFetchError, extractErrorMessage } from '@kbn/ml-error-utils'; + import { DEFAULT_MODEL_MEMORY_LIMIT } from '../../../../../../../common/constants/new_job'; import { ml } from '../../../../../services/ml_api_service'; import { JobValidator, VALIDATION_DELAY_MS } from '../../job_validator/job_validator'; -import { MLHttpFetchError, extractErrorMessage } from '../../../../../../../common/util/errors'; import { useMlKibana } from '../../../../../contexts/kibana'; import { JobCreator } from '../job_creator'; diff --git a/x-pack/plugins/ml/public/application/jobs/new_job/job_from_dashboard/quick_create_job_base.ts b/x-pack/plugins/ml/public/application/jobs/new_job/job_from_dashboard/quick_create_job_base.ts index a22b6d9fd57a5..f01898fa905a1 100644 --- a/x-pack/plugins/ml/public/application/jobs/new_job/job_from_dashboard/quick_create_job_base.ts +++ b/x-pack/plugins/ml/public/application/jobs/new_job/job_from_dashboard/quick_create_job_base.ts @@ -17,9 +17,9 @@ import type { Filter, Query, DataViewBase } from '@kbn/es-query'; import { FilterStateStore } from '@kbn/es-query'; import type { Embeddable } from '@kbn/lens-plugin/public'; import type { MapEmbeddable } from '@kbn/maps-plugin/public'; +import type { ErrorType } from '@kbn/ml-error-utils'; import type { MlApiServices } from '../../../services/ml_api_service'; import { getFiltersForDSLQuery } from '../../../../../common/util/job_utils'; -import type { ErrorType } from '../../../../../common/util/errors'; import { CREATED_BY_LABEL } from '../../../../../common/constants/new_job'; import { createQueries } from '../utils/new_job_utils'; import { createDatafeedId } from '../../../../../common/util/job_utils'; diff --git a/x-pack/plugins/ml/public/application/jobs/new_job/job_from_lens/visualization_extractor.ts b/x-pack/plugins/ml/public/application/jobs/new_job/job_from_lens/visualization_extractor.ts index c7f5a02e75d5b..4552123184f5c 100644 --- a/x-pack/plugins/ml/public/application/jobs/new_job/job_from_lens/visualization_extractor.ts +++ b/x-pack/plugins/ml/public/application/jobs/new_job/job_from_lens/visualization_extractor.ts @@ -10,8 +10,8 @@ import { layerTypes } from '@kbn/lens-plugin/public'; import { i18n } from '@kbn/i18n'; +import type { ErrorType } from '@kbn/ml-error-utils'; import { JOB_TYPE } from '../../../../../common/constants/new_job'; -import { ErrorType } from '../../../../../common/util/errors'; import { getVisTypeFactory, isCompatibleLayer, diff --git a/x-pack/plugins/ml/public/application/jobs/new_job/pages/components/datafeed_step/components/data_view/change_data_view.tsx b/x-pack/plugins/ml/public/application/jobs/new_job/pages/components/datafeed_step/components/data_view/change_data_view.tsx index 71d68b895f605..9e68d9f6f8c37 100644 --- a/x-pack/plugins/ml/public/application/jobs/new_job/pages/components/datafeed_step/components/data_view/change_data_view.tsx +++ b/x-pack/plugins/ml/public/application/jobs/new_job/pages/components/datafeed_step/components/data_view/change_data_view.tsx @@ -6,7 +6,6 @@ */ import React, { FC, useState, useEffect, useCallback, useContext } from 'react'; -import { FormattedMessage } from '@kbn/i18n-react'; import { i18n } from '@kbn/i18n'; import { @@ -23,7 +22,10 @@ import { EuiModalBody, } from '@elastic/eui'; +import { FormattedMessage } from '@kbn/i18n-react'; import { SavedObjectFinder } from '@kbn/saved-objects-finder-plugin/public'; +import { extractErrorMessage } from '@kbn/ml-error-utils'; + import { JobCreatorContext } from '../../../job_creator_context'; import { AdvancedJobCreator } from '../../../../../common/job_creator'; import { resetAdvancedJob } from '../../../../../common/job_creator/util/general'; @@ -31,7 +33,6 @@ import { CombinedJob, Datafeed, } from '../../../../../../../../../common/types/anomaly_detection_jobs'; -import { extractErrorMessage } from '../../../../../../../../../common/util/errors'; import type { DatafeedValidationResponse } from '../../../../../../../../../common/types/job_validation'; import { diff --git a/x-pack/plugins/ml/public/application/jobs/new_job/pages/components/pick_fields_step/components/categorization_view/category_stopped_partitions.tsx b/x-pack/plugins/ml/public/application/jobs/new_job/pages/components/pick_fields_step/components/categorization_view/category_stopped_partitions.tsx index 8030292bd9e59..090fcf40d3f17 100644 --- a/x-pack/plugins/ml/public/application/jobs/new_job/pages/components/pick_fields_step/components/categorization_view/category_stopped_partitions.tsx +++ b/x-pack/plugins/ml/public/application/jobs/new_job/pages/components/pick_fields_step/components/categorization_view/category_stopped_partitions.tsx @@ -11,10 +11,10 @@ import { FormattedMessage } from '@kbn/i18n-react'; import { i18n } from '@kbn/i18n'; import { from } from 'rxjs'; import { switchMap, takeWhile, tap } from 'rxjs/operators'; +import { extractErrorProperties } from '@kbn/ml-error-utils'; import { JobCreatorContext } from '../../../job_creator_context'; import { CategorizationJobCreator } from '../../../../../common/job_creator'; import { ml } from '../../../../../../../services/ml_api_service'; -import { extractErrorProperties } from '../../../../../../../../../common/util/errors'; const NUMBER_OF_PREVIEW = 5; export const CategoryStoppedPartitions: FC = () => { diff --git a/x-pack/plugins/ml/public/application/jobs/new_job/pages/components/pick_fields_step/components/categorization_view/top_categories.tsx b/x-pack/plugins/ml/public/application/jobs/new_job/pages/components/pick_fields_step/components/categorization_view/top_categories.tsx index 4491dfab1abdd..b12b4261a0628 100644 --- a/x-pack/plugins/ml/public/application/jobs/new_job/pages/components/pick_fields_step/components/categorization_view/top_categories.tsx +++ b/x-pack/plugins/ml/public/application/jobs/new_job/pages/components/pick_fields_step/components/categorization_view/top_categories.tsx @@ -8,13 +8,13 @@ import React, { FC, useContext, useEffect, useState } from 'react'; import { EuiBasicTable, EuiText } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n-react'; +import { extractErrorProperties } from '@kbn/ml-error-utils'; import { JobCreatorContext } from '../../../job_creator_context'; import { CategorizationJobCreator } from '../../../../../common/job_creator'; import { Results } from '../../../../../common/results_loader'; import { ml } from '../../../../../../../services/ml_api_service'; import { useToastNotificationService } from '../../../../../../../services/toast_notification_service'; import { NUMBER_OF_CATEGORY_EXAMPLES } from '../../../../../../../../../common/constants/categorization_job'; -import { extractErrorProperties } from '../../../../../../../../../common/util/errors'; export const TopCategories: FC = () => { const { displayErrorToast } = useToastNotificationService(); diff --git a/x-pack/plugins/ml/public/application/jobs/new_job/pages/components/summary_step/components/post_save_options/post_save_options.tsx b/x-pack/plugins/ml/public/application/jobs/new_job/pages/components/summary_step/components/post_save_options/post_save_options.tsx index 7fb483b54cf5d..75bc8772e0ac6 100644 --- a/x-pack/plugins/ml/public/application/jobs/new_job/pages/components/summary_step/components/post_save_options/post_save_options.tsx +++ b/x-pack/plugins/ml/public/application/jobs/new_job/pages/components/summary_step/components/post_save_options/post_save_options.tsx @@ -6,12 +6,15 @@ */ import React, { FC, Fragment, useContext, useState } from 'react'; + import { EuiButton, EuiFlexItem } from '@elastic/eui'; + import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n-react'; +import { extractErrorMessage } from '@kbn/ml-error-utils'; + import { JobRunner } from '../../../../../common/job_runner'; import { useMlKibana } from '../../../../../../../contexts/kibana'; -import { extractErrorMessage } from '../../../../../../../../../common/util/errors'; import { JobCreatorContext } from '../../../job_creator_context'; import { DATAFEED_STATE } from '../../../../../../../../../common/constants/states'; diff --git a/x-pack/plugins/ml/public/application/jobs/new_job/recognize/components/job_item.tsx b/x-pack/plugins/ml/public/application/jobs/new_job/recognize/components/job_item.tsx index 93b6086398358..cd6bd11096c65 100644 --- a/x-pack/plugins/ml/public/application/jobs/new_job/recognize/components/job_item.tsx +++ b/x-pack/plugins/ml/public/application/jobs/new_job/recognize/components/job_item.tsx @@ -18,11 +18,11 @@ import { } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n-react'; +import { extractErrorMessage } from '@kbn/ml-error-utils'; import { ModuleJobUI } from '../page'; import { SETUP_RESULTS_WIDTH } from './module_jobs'; import { tabColor } from '../../../../../../common/util/group_color_utils'; import { JobOverride, DatafeedResponse } from '../../../../../../common/types/modules'; -import { extractErrorMessage } from '../../../../../../common/util/errors'; interface JobItemProps { job: ModuleJobUI; diff --git a/x-pack/plugins/ml/public/application/jobs/new_job/recognize/components/kibana_objects.tsx b/x-pack/plugins/ml/public/application/jobs/new_job/recognize/components/kibana_objects.tsx index 079e698c3a5c0..95ac0b4043f57 100644 --- a/x-pack/plugins/ml/public/application/jobs/new_job/recognize/components/kibana_objects.tsx +++ b/x-pack/plugins/ml/public/application/jobs/new_job/recognize/components/kibana_objects.tsx @@ -18,8 +18,8 @@ import { EuiSpacer, } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; +import { extractErrorMessage } from '@kbn/ml-error-utils'; import { KibanaObjectUi } from '../page'; -import { extractErrorMessage } from '../../../../../../common/util/errors'; export interface KibanaObjectItemProps { objectType: string; diff --git a/x-pack/plugins/ml/public/application/model_management/test_models/models/inference_base.ts b/x-pack/plugins/ml/public/application/model_management/test_models/models/inference_base.ts index 59fa138d0f29f..1e3e3ecd5dfd7 100644 --- a/x-pack/plugins/ml/public/application/model_management/test_models/models/inference_base.ts +++ b/x-pack/plugins/ml/public/application/model_management/test_models/models/inference_base.ts @@ -11,7 +11,7 @@ import { i18n } from '@kbn/i18n'; import { map } from 'rxjs/operators'; import { SupportedPytorchTasksType } from '@kbn/ml-trained-models-utils'; -import { MLHttpFetchError } from '../../../../../common/util/errors'; +import type { MLHttpFetchError } from '@kbn/ml-error-utils'; import { trainedModelsApiProvider } from '../../../services/ml_api_service/trained_models'; import { getInferenceInfoComponent } from './inference_info'; diff --git a/x-pack/plugins/ml/public/application/model_management/test_models/models/inference_input_form/index_input.tsx b/x-pack/plugins/ml/public/application/model_management/test_models/models/inference_input_form/index_input.tsx index 5060f0033fd6c..4dbe900283657 100644 --- a/x-pack/plugins/ml/public/application/model_management/test_models/models/inference_input_form/index_input.tsx +++ b/x-pack/plugins/ml/public/application/model_management/test_models/models/inference_input_form/index_input.tsx @@ -8,7 +8,7 @@ import React, { FC, useState, useMemo, useCallback, FormEventHandler } from 'react'; import useObservable from 'react-use/lib/useObservable'; -import { FormattedMessage } from '@kbn/i18n-react'; + import { EuiSpacer, EuiButton, @@ -21,8 +21,10 @@ import { EuiForm, } from '@elastic/eui'; +import { FormattedMessage } from '@kbn/i18n-react'; +import { extractErrorMessage } from '@kbn/ml-error-utils'; + import { ErrorMessage } from '../../inference_error'; -import { extractErrorMessage } from '../../../../../../common'; import type { InferrerType } from '..'; import { useIndexInput, InferenceInputFormIndexControls } from '../index_input'; import { RUNNING_STATE } from '../inference_base'; diff --git a/x-pack/plugins/ml/public/application/model_management/test_models/models/inference_input_form/text_input.tsx b/x-pack/plugins/ml/public/application/model_management/test_models/models/inference_input_form/text_input.tsx index 3446bda477b4a..fc162a305c32b 100644 --- a/x-pack/plugins/ml/public/application/model_management/test_models/models/inference_input_form/text_input.tsx +++ b/x-pack/plugins/ml/public/application/model_management/test_models/models/inference_input_form/text_input.tsx @@ -6,13 +6,14 @@ */ import React, { FC, useState, useMemo, useCallback, FormEventHandler } from 'react'; - import useObservable from 'react-use/lib/useObservable'; -import { FormattedMessage } from '@kbn/i18n-react'; + import { EuiSpacer, EuiButton, EuiTabs, EuiTab, EuiForm } from '@elastic/eui'; +import { FormattedMessage } from '@kbn/i18n-react'; +import { extractErrorMessage } from '@kbn/ml-error-utils'; + import { ErrorMessage } from '../../inference_error'; -import { extractErrorMessage } from '../../../../../../common'; import type { InferrerType } from '..'; import { OutputLoadingContent } from '../../output_loading'; import { RUNNING_STATE } from '../inference_base'; diff --git a/x-pack/plugins/ml/public/application/services/results_service/result_service_rx.ts b/x-pack/plugins/ml/public/application/services/results_service/result_service_rx.ts index 2cfcf50c56514..c24ae573a9514 100644 --- a/x-pack/plugins/ml/public/application/services/results_service/result_service_rx.ts +++ b/x-pack/plugins/ml/public/application/services/results_service/result_service_rx.ts @@ -16,6 +16,7 @@ import { Observable } from 'rxjs'; import { map } from 'rxjs/operators'; import { each, get } from 'lodash'; import { isPopulatedObject } from '@kbn/ml-is-populated-object'; +import type { ErrorType } from '@kbn/ml-error-utils'; import { Dictionary } from '../../../../common/types/common'; import { ML_MEDIAN_PERCENTS } from '../../../../common/util/job_utils'; import { Datafeed, JobId } from '../../../../common/types/anomaly_detection_jobs'; @@ -28,7 +29,6 @@ import { ES_AGGREGATION } from '../../../../common/constants/aggregation_types'; import { InfluencersFilterQuery } from '../../../../common/types/es_client'; import { RecordForInfluencer } from './results_service'; import { isRuntimeMappings } from '../../../../common'; -import { ErrorType } from '../../../../common/util/errors'; export interface ResultResponse { success: boolean; diff --git a/x-pack/plugins/ml/public/application/services/toast_notification_service/toast_notification_service.ts b/x-pack/plugins/ml/public/application/services/toast_notification_service/toast_notification_service.ts index 8bc7bc8c87e35..585b881010d7a 100644 --- a/x-pack/plugins/ml/public/application/services/toast_notification_service/toast_notification_service.ts +++ b/x-pack/plugins/ml/public/application/services/toast_notification_service/toast_notification_service.ts @@ -8,13 +8,9 @@ import { i18n } from '@kbn/i18n'; import { ToastInput, ToastOptions, ToastsStart } from '@kbn/core/public'; import { useMemo } from 'react'; +import { extractErrorProperties, type ErrorType, MLRequestFailure } from '@kbn/ml-error-utils'; import { getToastNotifications } from '../../util/dependency_cache'; import { useNotifications } from '../../contexts/kibana'; -import { - ErrorType, - extractErrorProperties, - MLRequestFailure, -} from '../../../../common/util/errors'; export type ToastNotificationService = ReturnType; diff --git a/x-pack/plugins/ml/public/application/settings/calendars/list/delete_calendars.js b/x-pack/plugins/ml/public/application/settings/calendars/list/delete_calendars.js index e97cf8e2639fc..a767d7b65e4f3 100644 --- a/x-pack/plugins/ml/public/application/settings/calendars/list/delete_calendars.js +++ b/x-pack/plugins/ml/public/application/settings/calendars/list/delete_calendars.js @@ -5,10 +5,11 @@ * 2.0. */ +import { i18n } from '@kbn/i18n'; +import { extractErrorMessage } from '@kbn/ml-error-utils'; + import { getToastNotifications } from '../../../util/dependency_cache'; import { ml } from '../../../services/ml_api_service'; -import { i18n } from '@kbn/i18n'; -import { extractErrorMessage } from '../../../../../common/util/errors'; export async function deleteCalendars(calendarsToDelete, callback) { if (calendarsToDelete === undefined || calendarsToDelete.length === 0) { diff --git a/x-pack/plugins/ml/public/application/timeseriesexplorer/components/forecasting_modal/forecasting_modal.js b/x-pack/plugins/ml/public/application/timeseriesexplorer/components/forecasting_modal/forecasting_modal.js index 64bc1eefccd79..3342680070e8b 100644 --- a/x-pack/plugins/ml/public/application/timeseriesexplorer/components/forecasting_modal/forecasting_modal.js +++ b/x-pack/plugins/ml/public/application/timeseriesexplorer/components/forecasting_modal/forecasting_modal.js @@ -16,9 +16,13 @@ import React, { Component } from 'react'; import { EuiButton, EuiToolTip } from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; +import { FormattedMessage } from '@kbn/i18n-react'; +import { withKibana } from '@kbn/kibana-react-plugin/public'; +import { extractErrorMessage } from '@kbn/ml-error-utils'; + import { FORECAST_REQUEST_STATE, JOB_STATE } from '../../../../../common/constants/states'; import { MESSAGE_LEVEL } from '../../../../../common/constants/message_levels'; -import { extractErrorMessage } from '../../../../../common/util/errors'; import { isJobVersionGte } from '../../../../../common/util/job_utils'; import { parseInterval } from '../../../../../common/util/parse_interval'; import { Modal } from './modal'; @@ -26,9 +30,6 @@ import { PROGRESS_STATES } from './progress_states'; import { ml } from '../../../services/ml_api_service'; import { mlJobService } from '../../../services/job_service'; import { mlForecastService } from '../../../services/forecast_service'; -import { i18n } from '@kbn/i18n'; -import { FormattedMessage } from '@kbn/i18n-react'; -import { withKibana } from '@kbn/kibana-react-plugin/public'; export const FORECAST_DURATION_MAX_DAYS = 3650; // Max forecast duration allowed by analytics. diff --git a/x-pack/plugins/ml/public/application/timeseriesexplorer/components/timeseries_chart/timeseries_chart_with_tooltip.tsx b/x-pack/plugins/ml/public/application/timeseriesexplorer/components/timeseries_chart/timeseries_chart_with_tooltip.tsx index c9ca8250bedd9..af42229d8ac79 100644 --- a/x-pack/plugins/ml/public/application/timeseriesexplorer/components/timeseries_chart/timeseries_chart_with_tooltip.tsx +++ b/x-pack/plugins/ml/public/application/timeseriesexplorer/components/timeseries_chart/timeseries_chart_with_tooltip.tsx @@ -7,11 +7,11 @@ import React, { FC, useEffect, useState, useCallback, useContext } from 'react'; import { i18n } from '@kbn/i18n'; +import { extractErrorMessage } from '@kbn/ml-error-utils'; import { MlTooltipComponent } from '../../../components/chart_tooltip'; import { TimeseriesChart } from './timeseries_chart'; import { CombinedJob } from '../../../../../common/types/anomaly_detection_jobs'; import { ANNOTATIONS_TABLE_DEFAULT_QUERY_SIZE } from '../../../../../common/constants/search'; -import { extractErrorMessage } from '../../../../../common/util/errors'; import { Annotation } from '../../../../../common/types/annotations'; import { useMlKibana, useNotifications } from '../../../contexts/kibana'; import { getBoundsRoundedToInterval } from '../../../util/time_buckets'; diff --git a/x-pack/plugins/ml/public/application/timeseriesexplorer/timeseriesexplorer_utils/get_focus_data.ts b/x-pack/plugins/ml/public/application/timeseriesexplorer/timeseriesexplorer_utils/get_focus_data.ts index f9dd1fa94c4f0..7a7837ba71435 100644 --- a/x-pack/plugins/ml/public/application/timeseriesexplorer/timeseriesexplorer_utils/get_focus_data.ts +++ b/x-pack/plugins/ml/public/application/timeseriesexplorer/timeseriesexplorer_utils/get_focus_data.ts @@ -7,9 +7,9 @@ import { forkJoin, Observable, of } from 'rxjs'; import { catchError, map } from 'rxjs/operators'; +import { extractErrorMessage } from '@kbn/ml-error-utils'; import { ml } from '../../services/ml_api_service'; import { ANNOTATIONS_TABLE_DEFAULT_QUERY_SIZE } from '../../../../common/constants/search'; -import { extractErrorMessage } from '../../../../common/util/errors'; import { mlTimeSeriesSearchService } from '../timeseries_search_service'; import { mlResultsService, CriteriaField } from '../../services/results_service'; import { Job } from '../../../../common/types/anomaly_detection_jobs'; diff --git a/x-pack/plugins/ml/public/embeddables/job_creation/common/job_details.tsx b/x-pack/plugins/ml/public/embeddables/job_creation/common/job_details.tsx index 7585ffe32118d..e2fff3bd286cf 100644 --- a/x-pack/plugins/ml/public/embeddables/job_creation/common/job_details.tsx +++ b/x-pack/plugins/ml/public/embeddables/job_creation/common/job_details.tsx @@ -5,14 +5,10 @@ * 2.0. */ -import { i18n } from '@kbn/i18n'; -import type * as estypes from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; import React, { FC, useState, useCallback } from 'react'; -import { FormattedMessage } from '@kbn/i18n-react'; import useDebounce from 'react-use/lib/useDebounce'; -import type { Embeddable } from '@kbn/lens-plugin/public'; -import type { MapEmbeddable } from '@kbn/maps-plugin/public'; +import type * as estypes from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; import { EuiFlexGroup, EuiFlexItem, @@ -30,11 +26,16 @@ import { EuiCallOut, } from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; +import { FormattedMessage } from '@kbn/i18n-react'; +import type { Embeddable } from '@kbn/lens-plugin/public'; +import type { MapEmbeddable } from '@kbn/maps-plugin/public'; +import { extractErrorMessage } from '@kbn/ml-error-utils'; + import { QuickLensJobCreator } from '../../../application/jobs/new_job/job_from_lens'; import type { LayerResult } from '../../../application/jobs/new_job/job_from_lens'; import type { CreateState } from '../../../application/jobs/new_job/job_from_dashboard'; import { JOB_TYPE, DEFAULT_BUCKET_SPAN } from '../../../../common/constants/new_job'; -import { extractErrorMessage } from '../../../../common/util/errors'; import { basicJobValidation } from '../../../../common/util/job_utils'; import { JOB_ID_MAX_LENGTH } from '../../../../common/constants/validation'; import { invalidTimeIntervalMessage } from '../../../application/jobs/new_job/common/job_validator/util'; diff --git a/x-pack/plugins/ml/public/embeddables/job_creation/lens/lens_vis_layer_selection_flyout/layer/incompatible_layer.tsx b/x-pack/plugins/ml/public/embeddables/job_creation/lens/lens_vis_layer_selection_flyout/layer/incompatible_layer.tsx index f012e928e6b61..5f804e209eaa3 100644 --- a/x-pack/plugins/ml/public/embeddables/job_creation/lens/lens_vis_layer_selection_flyout/layer/incompatible_layer.tsx +++ b/x-pack/plugins/ml/public/embeddables/job_creation/lens/lens_vis_layer_selection_flyout/layer/incompatible_layer.tsx @@ -6,13 +6,13 @@ */ import React, { FC } from 'react'; -import { FormattedMessage } from '@kbn/i18n-react'; import { EuiFlexGroup, EuiFlexItem, EuiIcon, EuiText } from '@elastic/eui'; -import type { LayerResult } from '../../../../../application/jobs/new_job/job_from_lens'; +import { FormattedMessage } from '@kbn/i18n-react'; +import { extractErrorMessage } from '@kbn/ml-error-utils'; -import { extractErrorMessage } from '../../../../../../common/util/errors'; +import type { LayerResult } from '../../../../../application/jobs/new_job/job_from_lens'; interface Props { layer: LayerResult; diff --git a/x-pack/plugins/ml/public/shared.ts b/x-pack/plugins/ml/public/shared.ts index f247be1004718..1d19fee5c8392 100644 --- a/x-pack/plugins/ml/public/shared.ts +++ b/x-pack/plugins/ml/public/shared.ts @@ -15,7 +15,6 @@ export * from '../common/types/modules'; export * from '../common/types/audit_message'; export * from '../common/util/anomaly_utils'; -export * from '../common/util/errors'; export * from '../common/util/validators'; export * from '../common/util/date_utils'; diff --git a/x-pack/plugins/ml/scripts/apidoc_scripts/content_page/content_page.ts b/x-pack/plugins/ml/scripts/apidoc_scripts/content_page/content_page.ts index 402a68c07e2d5..8b992817c843c 100644 --- a/x-pack/plugins/ml/scripts/apidoc_scripts/content_page/content_page.ts +++ b/x-pack/plugins/ml/scripts/apidoc_scripts/content_page/content_page.ts @@ -24,27 +24,26 @@ const getContent = (groups: Group[]) => { .join('\n'); return `--- - id: uiMlKibanaRestApi - slug: /ml-team/docs/ui/rest-api/ml-kibana-rest-api - title: Machine Learning Kibana REST API - image: https://source.unsplash.com/400x175/?Nature - description: This page contains documentation for the ML Kibana REST API. - date: ${moment().format('YYYY-MM-DD')} - tags: ['machine learning','internal docs', 'UI'] - --- +id: uiMlKibanaRestApi +slug: /ml-team/docs/ui/rest-api/ml-kibana-rest-api +title: Machine Learning Kibana REST API +image: https://source.unsplash.com/400x175/?Nature +description: This page contains documentation for the ML Kibana REST API. +date: ${moment().format('YYYY-MM-DD')} +tags: ['machine learning','internal docs', 'UI'] +--- - _Updated for ${kibanaPackageJson.version}_ +_Updated for ${kibanaPackageJson.version}_ - Some of the features of the Machine Learning (ML) Kibana plugin are provided via a REST API, which is ideal for creating an integration with the ML plugin. +Some of the features of the Machine Learning (ML) Kibana plugin are provided via a REST API, which is ideal for creating an integration with the ML plugin. - Each API is experimental and can include breaking changes in any version of the ML plugin, or might have been entirely removed from the plugin. +Each API is experimental and can include breaking changes in any version of the ML plugin, or might have been entirely removed from the plugin. - - +- - The following APIs are available: +The following APIs are available: -${groupsStr} - `; +${groupsStr}`; }; export const generateContentPage = () => { diff --git a/x-pack/plugins/ml/server/models/data_frame_analytics/validation.ts b/x-pack/plugins/ml/server/models/data_frame_analytics/validation.ts index 6d4982ecbf464..9d286738b17da 100644 --- a/x-pack/plugins/ml/server/models/data_frame_analytics/validation.ts +++ b/x-pack/plugins/ml/server/models/data_frame_analytics/validation.ts @@ -8,6 +8,7 @@ import { i18n } from '@kbn/i18n'; import type * as estypes from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; import { IScopedClusterClient } from '@kbn/core/server'; +import { extractErrorMessage } from '@kbn/ml-error-utils'; import { getAnalysisType } from '../../../common/util/analytics_utils'; import { ANALYSIS_CONFIG_TYPE } from '../../../common/constants/data_frame_analytics'; import { @@ -25,7 +26,6 @@ import { isRegressionAnalysis, isClassificationAnalysis, } from '../../../common/util/analytics_utils'; -import { extractErrorMessage } from '../../../common/util/errors'; import { AnalysisConfig, DataFrameAnalyticsConfig, diff --git a/x-pack/plugins/ml/tsconfig.json b/x-pack/plugins/ml/tsconfig.json index 6179571a28515..8288c9998fc7d 100644 --- a/x-pack/plugins/ml/tsconfig.json +++ b/x-pack/plugins/ml/tsconfig.json @@ -30,7 +30,6 @@ "@kbn/charts-plugin", "@kbn/cloud-plugin", "@kbn/config-schema", - "@kbn/core-http-browser", "@kbn/dashboard-plugin", "@kbn/data-plugin", "@kbn/data-views-plugin", @@ -90,5 +89,6 @@ "@kbn/unified-search-plugin", "@kbn/usage-collection-plugin", "@kbn/utility-types", + "@kbn/ml-error-utils", ], } diff --git a/x-pack/plugins/observability/public/components/slo/feedback_button/feedback_button.tsx b/x-pack/plugins/observability/public/components/slo/feedback_button/feedback_button.tsx new file mode 100644 index 0000000000000..f72e4b008f390 --- /dev/null +++ b/x-pack/plugins/observability/public/components/slo/feedback_button/feedback_button.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 { EuiButton } from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; +import React from 'react'; + +const SLO_FEEDBACK_LINK = 'https://ela.st/slo-feedback'; + +export function FeedbackButton() { + return ( + + {i18n.translate('xpack.observability.slo.feedbackButtonLabel', { + defaultMessage: 'Tell us what you think!', + })} + + ); +} diff --git a/x-pack/plugins/observability/public/pages/rules/rules.tsx b/x-pack/plugins/observability/public/pages/rules/rules.tsx index e7a63cfbdb81d..40e4c2192a0c0 100644 --- a/x-pack/plugins/observability/public/pages/rules/rules.tsx +++ b/x-pack/plugins/observability/public/pages/rules/rules.tsx @@ -62,14 +62,16 @@ export function RulesPage() { useHashQuery: false, }); - const { lastResponse, search, status, type } = urlStateStorage.get<{ + const { lastResponse, params, search, status, type } = urlStateStorage.get<{ lastResponse: string[]; + params: Record; search: string; status: RuleStatus[]; type: string[]; - }>('_a') || { lastResponse: [], search: '', status: [], type: [] }; + }>('_a') || { lastResponse: [], params: {}, search: '', status: [], type: [] }; const [stateLastResponse, setLastResponse] = useState(lastResponse); + const [stateParams, setParams] = useState>(params); const [stateSearch, setSearch] = useState(search); const [stateStatus, setStatus] = useState(status); const [stateType, setType] = useState(type); @@ -80,23 +82,28 @@ export function RulesPage() { const handleStatusFilterChange = (newStatus: RuleStatus[]) => { setStatus(newStatus); - urlStateStorage.set('_a', { lastResponse, search, status: newStatus, type }); + urlStateStorage.set('_a', { lastResponse, params, search, status: newStatus, type }); }; const handleLastRunOutcomeFilterChange = (newLastResponse: string[]) => { setRefresh(new Date()); setLastResponse(newLastResponse); - urlStateStorage.set('_a', { lastResponse: newLastResponse, search, status, type }); + urlStateStorage.set('_a', { lastResponse: newLastResponse, params, search, status, type }); }; const handleTypeFilterChange = (newType: string[]) => { setType(newType); - urlStateStorage.set('_a', { lastResponse, search, status, type: newType }); + urlStateStorage.set('_a', { lastResponse, params, search, status, type: newType }); }; const handleSearchFilterChange = (newSearch: string) => { setSearch(newSearch); - urlStateStorage.set('_a', { lastResponse, search: newSearch, status, type }); + urlStateStorage.set('_a', { lastResponse, params, search: newSearch, status, type }); + }; + + const handleRuleParamFilterChange = (newParams: Record) => { + setParams(newParams); + urlStateStorage.set('_a', { lastResponse, params: newParams, search, status, type }); }; return ( @@ -143,6 +150,7 @@ export function RulesPage() { refresh={stateRefresh} ruleDetailsRoute="alerts/rules/:ruleId" rulesListKey="observability_rulesListColumns" + ruleParamFilter={stateParams} showActionFilter={false} statusFilter={stateStatus} searchFilter={stateSearch} @@ -155,6 +163,7 @@ export function RulesPage() { 'ruleExecutionState', ]} onLastRunOutcomeFilterChange={handleLastRunOutcomeFilterChange} + onRuleParamFilterChange={handleRuleParamFilterChange} onSearchFilterChange={handleSearchFilterChange} onStatusFilterChange={handleStatusFilterChange} onTypeFilterChange={handleTypeFilterChange} diff --git a/x-pack/plugins/observability/public/pages/slo_details/components/wide_chart.tsx b/x-pack/plugins/observability/public/pages/slo_details/components/wide_chart.tsx index 7fc212dbd4275..dbbacc6b1be91 100644 --- a/x-pack/plugins/observability/public/pages/slo_details/components/wide_chart.tsx +++ b/x-pack/plugins/observability/public/pages/slo_details/components/wide_chart.tsx @@ -15,10 +15,11 @@ import { ScaleType, Settings, } from '@elastic/charts'; -import React from 'react'; +import React, { useRef } from 'react'; import { EuiIcon, EuiLoadingChart, useEuiTheme } from '@elastic/eui'; import numeral from '@elastic/numeral'; import moment from 'moment'; +import { useActiveCursor } from '@kbn/charts-plugin/public'; import { ChartData } from '../../../typings'; import { useKibana } from '../../../utils/kibana_react'; @@ -45,18 +46,29 @@ export function WideChart({ chart, data, id, isLoading, state }: Props) { const color = state === 'error' ? euiTheme.colors.danger : euiTheme.colors.success; const ChartComponent = chart === 'area' ? AreaSeries : LineSeries; + const chartRef = useRef(null); + const handleCursorUpdate = useActiveCursor(charts.activeCursor, chartRef, { + isDateHistogram: true, + }); + if (isLoading) { return ; } return ( - + } + onPointerUpdate={handleCursorUpdate} + externalPointerEvents={{ + tooltip: { visible: true }, + }} + pointerUpdateDebounce={0} + pointerUpdateTrigger={'x'} /> { useKibanaMock.mockReturnValue({ services: { application: { navigateToUrl: mockNavigate }, - charts: chartPluginMock.createSetupContract(), + charts: chartPluginMock.createStartContract(), http: { basePath: { prepend: mockBasePathPrepend, diff --git a/x-pack/plugins/observability/public/pages/slo_details/slo_details.tsx b/x-pack/plugins/observability/public/pages/slo_details/slo_details.tsx index bec6e6608abcc..48401ca29f94e 100644 --- a/x-pack/plugins/observability/public/pages/slo_details/slo_details.tsx +++ b/x-pack/plugins/observability/public/pages/slo_details/slo_details.tsx @@ -26,6 +26,7 @@ import { paths } from '../../config/paths'; import type { SloDetailsPathParams } from './types'; import type { ObservabilityAppServices } from '../../application/types'; import { AutoRefreshButton } from '../slos/components/auto_refresh_button'; +import { FeedbackButton } from '../../components/slo/feedback_button/feedback_button'; export function SloDetailsPage() { const { @@ -69,6 +70,7 @@ export function SloDetailsPage() { isAutoRefreshing={isAutoRefreshing} onClick={handleToggleAutoRefresh} />, + , ], bottomBorder: false, }} diff --git a/x-pack/plugins/observability/public/pages/slo_edit/slo_edit.tsx b/x-pack/plugins/observability/public/pages/slo_edit/slo_edit.tsx index 57e27810815d6..ede2d15d3f23d 100644 --- a/x-pack/plugins/observability/public/pages/slo_edit/slo_edit.tsx +++ b/x-pack/plugins/observability/public/pages/slo_edit/slo_edit.tsx @@ -16,6 +16,7 @@ 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 { FeedbackButton } from '../../components/slo/feedback_button/feedback_button'; export function SloEditPage() { const { @@ -58,7 +59,7 @@ export function SloEditPage() { : i18n.translate('xpack.observability.sloCreatePageTitle', { defaultMessage: 'Create new SLO', }), - rightSideItems: [], + rightSideItems: [], bottomBorder: false, }} data-test-subj="slosEditPage" diff --git a/x-pack/plugins/observability/public/pages/slos/components/badges/slo_rules_badge.tsx b/x-pack/plugins/observability/public/pages/slos/components/badges/slo_rules_badge.tsx index c60617152cb00..2b80e8852bdae 100644 --- a/x-pack/plugins/observability/public/pages/slos/components/badges/slo_rules_badge.tsx +++ b/x-pack/plugins/observability/public/pages/slos/components/badges/slo_rules_badge.tsx @@ -6,9 +6,10 @@ */ import React from 'react'; -import { EuiBadge, EuiIcon, EuiToolTip } from '@elastic/eui'; +import { EuiBadge, EuiToolTip } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { Rule } from '@kbn/triggers-actions-ui-plugin/public'; + import { SloRule } from '../../../../hooks/slo/use_fetch_rules_for_slo'; export interface Props { @@ -26,16 +27,9 @@ export function SloRulesBadge({ rules, onClick }: Props) { })} display="block" > - - - + + + ); } diff --git a/x-pack/plugins/observability/public/pages/slos/slos.tsx b/x-pack/plugins/observability/public/pages/slos/slos.tsx index e224158ef9c2a..b81998d8452de 100644 --- a/x-pack/plugins/observability/public/pages/slos/slos.tsx +++ b/x-pack/plugins/observability/public/pages/slos/slos.tsx @@ -21,6 +21,7 @@ import { AutoRefreshButton } from './components/auto_refresh_button'; import { paths } from '../../config/paths'; import type { ObservabilityAppServices } from '../../application/types'; import { HeaderTitle } from './components/header_title'; +import { FeedbackButton } from '../../components/slo/feedback_button/feedback_button'; export function SlosPage() { const { @@ -69,7 +70,7 @@ export function SlosPage() { rightSideItems: [ , + , ], bottomBorder: false, }} 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 5a562958cb299..a0e5a1f0e037b 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 @@ -65,6 +65,7 @@ export function KibanaReactStorybookDecorator(Story: ComponentType) { useChartsBaseTheme: () => {}, useChartsTheme: () => {}, }, + activeCursor: () => {}, }, data: {}, dataViews: { diff --git a/x-pack/plugins/rule_registry/server/utils/create_get_summarized_alerts_fn.test.ts b/x-pack/plugins/rule_registry/server/utils/create_get_summarized_alerts_fn.test.ts index 1e55fbe047ddc..3bf83993df1e8 100644 --- a/x-pack/plugins/rule_registry/server/utils/create_get_summarized_alerts_fn.test.ts +++ b/x-pack/plugins/rule_registry/server/utils/create_get_summarized_alerts_fn.test.ts @@ -2098,6 +2098,7 @@ describe('createGetSummarizedAlertsFn', () => { query: { kql: 'kibana.alert.rule.name:test', dsl: '{"bool":{"minimum_should_match":1,"should":[{"match":{"kibana.alert.rule.name":"test"}}]}}', + filters: [], }, timeframe: { days: [1, 2, 3, 4, 5], @@ -2147,11 +2148,12 @@ describe('createGetSummarizedAlertsFn', () => { script: { script: { params: { + datetimeField: '@timestamp', days: [1, 2, 3, 4, 5], timezone: 'UTC', }, source: - "params.days.contains(doc['kibana.alert.start'].value.withZoneSameInstant(ZoneId.of(params.timezone)).dayOfWeek.getValue())", + 'params.days.contains(doc[params.datetimeField].value.withZoneSameInstant(ZoneId.of(params.timezone)).dayOfWeek.getValue())', }, }, }, @@ -2159,17 +2161,18 @@ describe('createGetSummarizedAlertsFn', () => { script: { script: { params: { + datetimeField: '@timestamp', end: '17:00', start: '08:00', timezone: 'UTC', }, source: ` - def alertsDateTime = doc['kibana.alert.start'].value.withZoneSameInstant(ZoneId.of(params.timezone)); + def alertsDateTime = doc[params.datetimeField].value.withZoneSameInstant(ZoneId.of(params.timezone)); def alertsTime = LocalTime.of(alertsDateTime.getHour(), alertsDateTime.getMinute()); def start = LocalTime.parse(params.start); def end = LocalTime.parse(params.end); - if (end.isBefore(start)){ // overnight + if (end.isBefore(start) || end.equals(start)){ // overnight def dayEnd = LocalTime.parse("23:59:59"); def dayStart = LocalTime.parse("00:00:00"); if ((alertsTime.isAfter(start) && alertsTime.isBefore(dayEnd)) || (alertsTime.isAfter(dayStart) && alertsTime.isBefore(end))) { @@ -2231,11 +2234,12 @@ describe('createGetSummarizedAlertsFn', () => { script: { script: { params: { + datetimeField: '@timestamp', days: [1, 2, 3, 4, 5], timezone: 'UTC', }, source: - "params.days.contains(doc['kibana.alert.start'].value.withZoneSameInstant(ZoneId.of(params.timezone)).dayOfWeek.getValue())", + 'params.days.contains(doc[params.datetimeField].value.withZoneSameInstant(ZoneId.of(params.timezone)).dayOfWeek.getValue())', }, }, }, @@ -2243,17 +2247,18 @@ describe('createGetSummarizedAlertsFn', () => { script: { script: { params: { + datetimeField: '@timestamp', end: '17:00', start: '08:00', timezone: 'UTC', }, source: ` - def alertsDateTime = doc['kibana.alert.start'].value.withZoneSameInstant(ZoneId.of(params.timezone)); + def alertsDateTime = doc[params.datetimeField].value.withZoneSameInstant(ZoneId.of(params.timezone)); def alertsTime = LocalTime.of(alertsDateTime.getHour(), alertsDateTime.getMinute()); def start = LocalTime.parse(params.start); def end = LocalTime.parse(params.end); - if (end.isBefore(start)){ // overnight + if (end.isBefore(start) || end.equals(start)){ // overnight def dayEnd = LocalTime.parse("23:59:59"); def dayStart = LocalTime.parse("00:00:00"); if ((alertsTime.isAfter(start) && alertsTime.isBefore(dayEnd)) || (alertsTime.isAfter(dayStart) && alertsTime.isBefore(end))) { @@ -2315,11 +2320,12 @@ describe('createGetSummarizedAlertsFn', () => { script: { script: { params: { + datetimeField: '@timestamp', days: [1, 2, 3, 4, 5], timezone: 'UTC', }, source: - "params.days.contains(doc['kibana.alert.start'].value.withZoneSameInstant(ZoneId.of(params.timezone)).dayOfWeek.getValue())", + 'params.days.contains(doc[params.datetimeField].value.withZoneSameInstant(ZoneId.of(params.timezone)).dayOfWeek.getValue())', }, }, }, @@ -2327,17 +2333,18 @@ describe('createGetSummarizedAlertsFn', () => { script: { script: { params: { + datetimeField: '@timestamp', end: '17:00', start: '08:00', timezone: 'UTC', }, source: ` - def alertsDateTime = doc['kibana.alert.start'].value.withZoneSameInstant(ZoneId.of(params.timezone)); + def alertsDateTime = doc[params.datetimeField].value.withZoneSameInstant(ZoneId.of(params.timezone)); def alertsTime = LocalTime.of(alertsDateTime.getHour(), alertsDateTime.getMinute()); def start = LocalTime.parse(params.start); def end = LocalTime.parse(params.end); - if (end.isBefore(start)){ // overnight + if (end.isBefore(start) || end.equals(start)){ // overnight def dayEnd = LocalTime.parse("23:59:59"); def dayStart = LocalTime.parse("00:00:00"); if ((alertsTime.isAfter(start) && alertsTime.isBefore(dayEnd)) || (alertsTime.isAfter(dayStart) && alertsTime.isBefore(end))) { diff --git a/x-pack/plugins/rule_registry/server/utils/create_get_summarized_alerts_fn.ts b/x-pack/plugins/rule_registry/server/utils/create_get_summarized_alerts_fn.ts index 4a7a727276ad4..d4fe127b240ee 100644 --- a/x-pack/plugins/rule_registry/server/utils/create_get_summarized_alerts_fn.ts +++ b/x-pack/plugins/rule_registry/server/utils/create_get_summarized_alerts_fn.ts @@ -573,10 +573,11 @@ const generateAlertsFilterDSL = (alertsFilter: AlertsFilter): QueryDslQueryConta script: { script: { source: - "params.days.contains(doc['kibana.alert.start'].value.withZoneSameInstant(ZoneId.of(params.timezone)).dayOfWeek.getValue())", + 'params.days.contains(doc[params.datetimeField].value.withZoneSameInstant(ZoneId.of(params.timezone)).dayOfWeek.getValue())', params: { days: alertsFilter.timeframe.days, timezone: alertsFilter.timeframe.timezone, + datetimeField: TIMESTAMP, }, }, }, @@ -585,12 +586,12 @@ const generateAlertsFilterDSL = (alertsFilter: AlertsFilter): QueryDslQueryConta script: { script: { source: ` - def alertsDateTime = doc['kibana.alert.start'].value.withZoneSameInstant(ZoneId.of(params.timezone)); + def alertsDateTime = doc[params.datetimeField].value.withZoneSameInstant(ZoneId.of(params.timezone)); def alertsTime = LocalTime.of(alertsDateTime.getHour(), alertsDateTime.getMinute()); def start = LocalTime.parse(params.start); def end = LocalTime.parse(params.end); - if (end.isBefore(start)){ // overnight + if (end.isBefore(start) || end.equals(start)){ // overnight def dayEnd = LocalTime.parse("23:59:59"); def dayStart = LocalTime.parse("00:00:00"); if ((alertsTime.isAfter(start) && alertsTime.isBefore(dayEnd)) || (alertsTime.isAfter(dayStart) && alertsTime.isBefore(end))) { @@ -610,6 +611,7 @@ const generateAlertsFilterDSL = (alertsFilter: AlertsFilter): QueryDslQueryConta start: alertsFilter.timeframe.hours.start, end: alertsFilter.timeframe.hours.end, timezone: alertsFilter.timeframe.timezone, + datetimeField: TIMESTAMP, }, }, }, diff --git a/x-pack/plugins/security/public/management/role_mappings/role_mappings_api_client.ts b/x-pack/plugins/security/public/management/role_mappings/role_mappings_api_client.ts index 79e2335919b1a..5465bc24b7e31 100644 --- a/x-pack/plugins/security/public/management/role_mappings/role_mappings_api_client.ts +++ b/x-pack/plugins/security/public/management/role_mappings/role_mappings_api_client.ts @@ -9,11 +9,12 @@ import type { HttpStart } from '@kbn/core/public'; import type { RoleMapping } from '../../../common/model'; -interface CheckRoleMappingFeaturesResponse { +export interface CheckRoleMappingFeaturesResponse { canManageRoleMappings: boolean; canUseInlineScripts: boolean; canUseStoredScripts: boolean; hasCompatibleRealms: boolean; + canUseRemoteIndices: boolean; } type DeleteRoleMappingsResponse = Array<{ diff --git a/x-pack/plugins/security/public/management/roles/edit_role/edit_role_page.test.tsx b/x-pack/plugins/security/public/management/roles/edit_role/edit_role_page.test.tsx index 090c7e6854aeb..52e3d768ef07e 100644 --- a/x-pack/plugins/security/public/management/roles/edit_role/edit_role_page.test.tsx +++ b/x-pack/plugins/security/public/management/roles/edit_role/edit_role_page.test.tsx @@ -140,11 +140,13 @@ function getProps({ role, canManageSpaces = true, spacesEnabled = true, + canUseRemoteIndices = true, }: { action: 'edit' | 'clone'; role?: Role; canManageSpaces?: boolean; spacesEnabled?: boolean; + canUseRemoteIndices?: boolean; }) { const rolesAPIClient = rolesAPIClientMock.create(); rolesAPIClient.getRole.mockResolvedValue(role); @@ -171,12 +173,15 @@ function getProps({ const { fatalErrors } = coreMock.createSetup(); const { http, docLinks, notifications } = coreMock.createStart(); http.get.mockImplementation(async (path: any) => { - if (!spacesEnabled) { - throw { response: { status: 404 } }; // eslint-disable-line no-throw-literal - } if (path === '/api/spaces/space') { + if (!spacesEnabled) { + throw { response: { status: 404 } }; // eslint-disable-line no-throw-literal + } return buildSpaces(); } + if (path === '/internal/security/_check_role_mapping_features') { + return { canUseRemoteIndices }; + } }); return { @@ -265,6 +270,8 @@ describe('', () => { expect(wrapper.find(SpaceAwarePrivilegeSection)).toHaveLength(1); expect(wrapper.find('[data-test-subj="userCannotManageSpacesCallout"]')).toHaveLength(0); expect(wrapper.find('input[data-test-subj="roleFormNameInput"]').prop('disabled')).toBe(true); + expect(wrapper.find('IndexPrivileges[indexType="indices"]')).toHaveLength(1); + expect(wrapper.find('IndexPrivileges[indexType="remote_indices"]')).toHaveLength(1); expectReadOnlyFormButtons(wrapper); }); @@ -291,6 +298,8 @@ describe('', () => { expect(wrapper.find(SpaceAwarePrivilegeSection)).toHaveLength(1); expect(wrapper.find('[data-test-subj="userCannotManageSpacesCallout"]')).toHaveLength(0); expect(wrapper.find('input[data-test-subj="roleFormNameInput"]').prop('disabled')).toBe(true); + expect(wrapper.find('IndexPrivileges[indexType="indices"]')).toHaveLength(1); + expect(wrapper.find('IndexPrivileges[indexType="remote_indices"]')).toHaveLength(1); expectSaveFormButtons(wrapper); }); @@ -308,6 +317,8 @@ describe('', () => { expect(wrapper.find('input[data-test-subj="roleFormNameInput"]').prop('disabled')).toBe( false ); + expect(wrapper.find('IndexPrivileges[indexType="indices"]')).toHaveLength(1); + expect(wrapper.find('IndexPrivileges[indexType="remote_indices"]')).toHaveLength(1); expectSaveFormButtons(wrapper); }); @@ -480,6 +491,8 @@ describe('', () => { expect(wrapper.find(SimplePrivilegeSection)).toHaveLength(1); expect(wrapper.find('[data-test-subj="userCannotManageSpacesCallout"]')).toHaveLength(0); expect(wrapper.find('input[data-test-subj="roleFormNameInput"]').prop('disabled')).toBe(true); + expect(wrapper.find('IndexPrivileges[indexType="indices"]')).toHaveLength(1); + expect(wrapper.find('IndexPrivileges[indexType="remote_indices"]')).toHaveLength(1); expectReadOnlyFormButtons(wrapper); }); @@ -507,6 +520,8 @@ describe('', () => { expect(wrapper.find(SimplePrivilegeSection)).toHaveLength(1); expect(wrapper.find('[data-test-subj="userCannotManageSpacesCallout"]')).toHaveLength(0); expect(wrapper.find('input[data-test-subj="roleFormNameInput"]').prop('disabled')).toBe(true); + expect(wrapper.find('IndexPrivileges[indexType="indices"]')).toHaveLength(1); + expect(wrapper.find('IndexPrivileges[indexType="remote_indices"]')).toHaveLength(1); expectSaveFormButtons(wrapper); }); @@ -524,6 +539,8 @@ describe('', () => { expect(wrapper.find('input[data-test-subj="roleFormNameInput"]').prop('disabled')).toBe( false ); + expect(wrapper.find('IndexPrivileges[indexType="indices"]')).toHaveLength(1); + expect(wrapper.find('IndexPrivileges[indexType="remote_indices"]')).toHaveLength(1); expectSaveFormButtons(wrapper); }); @@ -612,6 +629,19 @@ describe('', () => { }); }); + it('hides remote index privileges section when not supported', async () => { + const wrapper = mountWithIntl( + + + + ); + + await waitForRender(wrapper); + + expect(wrapper.find('IndexPrivileges[indexType="indices"]')).toHaveLength(1); + expect(wrapper.find('IndexPrivileges[indexType="remote_indices"]')).toHaveLength(0); + }); + it('registers fatal error if features endpoint fails unexpectedly', async () => { const error = { response: { status: 500 } }; const getFeatures = jest.fn().mockRejectedValue(error); diff --git a/x-pack/plugins/security/public/management/roles/edit_role/edit_role_page.tsx b/x-pack/plugins/security/public/management/roles/edit_role/edit_role_page.tsx index e5e5736ca5833..9388ab92a0a76 100644 --- a/x-pack/plugins/security/public/management/roles/edit_role/edit_role_page.tsx +++ b/x-pack/plugins/security/public/management/roles/edit_role/edit_role_page.tsx @@ -21,6 +21,7 @@ import { } from '@elastic/eui'; import type { ChangeEvent, FocusEvent, FunctionComponent, HTMLProps } from 'react'; import React, { Fragment, useCallback, useEffect, useRef, useState } from 'react'; +import useAsync from 'react-use/lib/useAsync'; import type { IHttpFetchError } from '@kbn/core-http-browser'; import type { @@ -56,6 +57,7 @@ import { prepareRoleClone, } from '../../../../common/model'; import { useCapabilities } from '../../../components/use_capabilities'; +import type { CheckRoleMappingFeaturesResponse } from '../../role_mappings/role_mappings_api_client'; import type { UserAPIClient } from '../../users'; import type { IndicesAPIClient } from '../indices_api_client'; import { KibanaPrivileges } from '../model'; @@ -86,6 +88,12 @@ interface Props { spacesApiUi?: SpacesApiUi; } +function useFeatureCheck(http: HttpStart) { + return useAsync(() => + http.get('/internal/security/_check_role_mapping_features') + ); +} + function useRunAsUsers( userAPIClient: PublicMethodsOf, fatalErrors: FatalErrorsSetup @@ -311,6 +319,7 @@ export const EditRolePage: FunctionComponent = ({ const privileges = usePrivileges(privilegesAPIClient, fatalErrors); const spaces = useSpaces(http, fatalErrors); const features = useFeatures(getFeatures, fatalErrors); + const featureCheckState = useFeatureCheck(http); const [role, setRole] = useRole( rolesAPIClient, fatalErrors, @@ -329,7 +338,15 @@ export const EditRolePage: FunctionComponent = ({ } }, [hasReadOnlyPrivileges, isEditingExistingRole]); // eslint-disable-line react-hooks/exhaustive-deps - if (!role || !runAsUsers || !indexPatternsTitles || !privileges || !spaces || !features) { + if ( + !role || + !runAsUsers || + !indexPatternsTitles || + !privileges || + !spaces || + !features || + !featureCheckState.value + ) { return null; } @@ -457,6 +474,7 @@ export const EditRolePage: FunctionComponent = ({ builtinESPrivileges={builtInESPrivileges} license={license} docLinks={docLinks} + canUseRemoteIndices={featureCheckState.value?.canUseRemoteIndices} />
); diff --git a/x-pack/plugins/security/public/management/roles/edit_role/privileges/es/__snapshots__/elasticsearch_privileges.test.tsx.snap b/x-pack/plugins/security/public/management/roles/edit_role/privileges/es/__snapshots__/elasticsearch_privileges.test.tsx.snap index 988f463f49fd1..8f160c6e57abc 100644 --- a/x-pack/plugins/security/public/management/roles/edit_role/privileges/es/__snapshots__/elasticsearch_privileges.test.tsx.snap +++ b/x-pack/plugins/security/public/management/roles/edit_role/privileges/es/__snapshots__/elasticsearch_privileges.test.tsx.snap @@ -200,89 +200,5 @@ exports[`it renders without crashing 1`] = ` } } /> - - - -

- -

-
- - -

- - - - -

-
- `; diff --git a/x-pack/plugins/security/public/management/roles/edit_role/privileges/es/elasticsearch_privileges.test.tsx b/x-pack/plugins/security/public/management/roles/edit_role/privileges/es/elasticsearch_privileges.test.tsx index 13a4143890a86..1a1486f6d82e3 100644 --- a/x-pack/plugins/security/public/management/roles/edit_role/privileges/es/elasticsearch_privileges.test.tsx +++ b/x-pack/plugins/security/public/management/roles/edit_role/privileges/es/elasticsearch_privileges.test.tsx @@ -63,8 +63,13 @@ test('it renders index privileges section', () => { expect(wrapper.find('IndexPrivileges[indexType="indices"]')).toHaveLength(1); }); -test('it renders remote index privileges section', () => { +test('it does not render remote index privileges section by default', () => { const wrapper = shallowWithIntl(); + expect(wrapper.find('IndexPrivileges[indexType="remote_indices"]')).toHaveLength(0); +}); + +test('it renders remote index privileges section when `canUseRemoteIndices` is enabled', () => { + const wrapper = shallowWithIntl(); expect(wrapper.find('IndexPrivileges[indexType="remote_indices"]')).toHaveLength(1); }); diff --git a/x-pack/plugins/security/public/management/roles/edit_role/privileges/es/elasticsearch_privileges.tsx b/x-pack/plugins/security/public/management/roles/edit_role/privileges/es/elasticsearch_privileges.tsx index 88e0c953711fa..e963c4eda6d92 100644 --- a/x-pack/plugins/security/public/management/roles/edit_role/privileges/es/elasticsearch_privileges.tsx +++ b/x-pack/plugins/security/public/management/roles/edit_role/privileges/es/elasticsearch_privileges.tsx @@ -40,6 +40,7 @@ interface Props { validator: RoleValidator; builtinESPrivileges: BuiltinESPrivileges; indexPatterns: string[]; + canUseRemoteIndices?: boolean; } export class ElasticsearchPrivileges extends Component { @@ -62,6 +63,7 @@ export class ElasticsearchPrivileges extends Component { indexPatterns, license, builtinESPrivileges, + canUseRemoteIndices, } = this.props; return ( @@ -170,37 +172,42 @@ export class ElasticsearchPrivileges extends Component { availableIndexPrivileges={builtinESPrivileges.index} editable={editable} /> - - - -

- -

-
- - -

- + + + + +

+ +

+ + + +

+ + {this.learnMore(docLinks.links.security.indicesPrivileges)} +

+
+ - {this.learnMore(docLinks.links.security.indicesPrivileges)} -

-
- + + )} ); }; diff --git a/x-pack/plugins/security/server/routes/role_mapping/feature_check.test.ts b/x-pack/plugins/security/server/routes/role_mapping/feature_check.test.ts index 0efe93d21c1b2..ce0a38ef73039 100644 --- a/x-pack/plugins/security/server/routes/role_mapping/feature_check.test.ts +++ b/x-pack/plugins/security/server/routes/role_mapping/feature_check.test.ts @@ -21,6 +21,9 @@ interface TestOptions { } const defaultXpackUsageResponse = { + remote_clusters: { + size: 0, + }, security: { realms: { native: { @@ -94,6 +97,7 @@ describe('GET role mappings feature check', () => { canUseInlineScripts: true, canUseStoredScripts: true, hasCompatibleRealms: true, + canUseRemoteIndices: true, }, }, }); @@ -117,10 +121,31 @@ describe('GET role mappings feature check', () => { canUseInlineScripts: true, canUseStoredScripts: true, hasCompatibleRealms: true, + canUseRemoteIndices: true, }, }, }); + getFeatureCheckTest( + 'indicates canUseRemoteIndices=false when cluster does not support remote indices', + { + xpackUsageResponse: () => ({ + ...defaultXpackUsageResponse, + remote_clusters: undefined, + }), + asserts: { + statusCode: 200, + result: { + canManageRoleMappings: true, + canUseInlineScripts: true, + canUseStoredScripts: true, + hasCompatibleRealms: true, + canUseRemoteIndices: false, + }, + }, + } + ); + getFeatureCheckTest('disallows stored scripts when disabled', { nodeSettingsResponse: () => ({ nodes: { @@ -140,6 +165,7 @@ describe('GET role mappings feature check', () => { canUseInlineScripts: true, canUseStoredScripts: false, hasCompatibleRealms: true, + canUseRemoteIndices: true, }, }, }); @@ -163,12 +189,14 @@ describe('GET role mappings feature check', () => { canUseInlineScripts: false, canUseStoredScripts: true, hasCompatibleRealms: true, + canUseRemoteIndices: true, }, }, }); getFeatureCheckTest('indicates incompatible realms when only native and file are enabled', { xpackUsageResponse: () => ({ + ...defaultXpackUsageResponse, security: { realms: { native: { @@ -189,6 +217,7 @@ describe('GET role mappings feature check', () => { canUseInlineScripts: true, canUseStoredScripts: true, hasCompatibleRealms: false, + canUseRemoteIndices: true, }, }, }); @@ -219,6 +248,7 @@ describe('GET role mappings feature check', () => { canUseInlineScripts: true, canUseStoredScripts: true, hasCompatibleRealms: false, + canUseRemoteIndices: false, }, }, } diff --git a/x-pack/plugins/security/server/routes/role_mapping/feature_check.ts b/x-pack/plugins/security/server/routes/role_mapping/feature_check.ts index 9715f92cb5a37..309cdfbeab456 100644 --- a/x-pack/plugins/security/server/routes/role_mapping/feature_check.ts +++ b/x-pack/plugins/security/server/routes/role_mapping/feature_check.ts @@ -24,6 +24,9 @@ interface NodeSettingsResponse { } interface XPackUsageResponse { + remote_clusters?: { + size: number; + }; security: { realms: { [realmName: string]: { @@ -128,6 +131,7 @@ async function getEnabledRoleMappingsFeatures(esClient: ElasticsearchClient, log hasCompatibleRealms, canUseStoredScripts, canUseInlineScripts, + canUseRemoteIndices: !!xpackUsage.remote_clusters, }; } diff --git a/x-pack/plugins/security_solution/common/constants.ts b/x-pack/plugins/security_solution/common/constants.ts index aba27d9b617d5..2551bc34f7fa4 100644 --- a/x-pack/plugins/security_solution/common/constants.ts +++ b/x-pack/plugins/security_solution/common/constants.ts @@ -5,6 +5,8 @@ * 2.0. */ +import { RuleNotifyWhen } from '@kbn/alerting-plugin/common'; + /** * as const * @@ -377,9 +379,18 @@ export const ML_GROUP_ID = 'security' as const; export const LEGACY_ML_GROUP_ID = 'siem' as const; export const ML_GROUP_IDS = [ML_GROUP_ID, LEGACY_ML_GROUP_ID] as const; +/** + * Rule Actions + */ export const NOTIFICATION_THROTTLE_NO_ACTIONS = 'no_actions' as const; export const NOTIFICATION_THROTTLE_RULE = 'rule' as const; +export const NOTIFICATION_DEFAULT_FREQUENCY = { + notifyWhen: RuleNotifyWhen.ACTIVE, + throttle: null, + summary: true, +}; + export const showAllOthersBucket: string[] = [ 'destination.ip', 'event.action', diff --git a/x-pack/plugins/security_solution/common/detection_engine/rule_management/api/rules/bulk_actions/request_schema.test.ts b/x-pack/plugins/security_solution/common/detection_engine/rule_management/api/rules/bulk_actions/request_schema.test.ts index 3cee4c3dbe384..712312c50140d 100644 --- a/x-pack/plugins/security_solution/common/detection_engine/rule_management/api/rules/bulk_actions/request_schema.test.ts +++ b/x-pack/plugins/security_solution/common/detection_engine/rule_management/api/rules/bulk_actions/request_schema.test.ts @@ -133,7 +133,10 @@ describe('Perform bulk action request schema', () => { const payload: PerformBulkActionRequestBody = { query: 'name: test', action: BulkActionType.duplicate, - [BulkActionType.duplicate]: { include_exceptions: false }, + [BulkActionType.duplicate]: { + include_exceptions: false, + include_expired_exceptions: false, + }, }; const message = retrieveValidationMessage(payload); expect(getPaths(left(message.errors))).toEqual([]); @@ -512,28 +515,6 @@ describe('Perform bulk action request schema', () => { expect(message.schema).toEqual({}); }); - test('invalid request: missing throttle in payload', () => { - const payload = { - query: 'name: test', - action: BulkActionType.edit, - [BulkActionType.edit]: [ - { - type: BulkActionEditType.add_rule_actions, - value: { - actions: [], - }, - }, - ], - }; - - const message = retrieveValidationMessage(payload); - - expect(getPaths(left(message.errors))).toEqual( - expect.arrayContaining(['Invalid value "undefined" supplied to "edit,value,throttle"']) - ); - expect(message.schema).toEqual({}); - }); - test('invalid request: missing actions in payload', () => { const payload = { query: 'name: test', diff --git a/x-pack/plugins/security_solution/common/detection_engine/rule_management/api/rules/bulk_actions/request_schema.ts b/x-pack/plugins/security_solution/common/detection_engine/rule_management/api/rules/bulk_actions/request_schema.ts index b0860c55ebd5a..e0a392885bad9 100644 --- a/x-pack/plugins/security_solution/common/detection_engine/rule_management/api/rules/bulk_actions/request_schema.ts +++ b/x-pack/plugins/security_solution/common/detection_engine/rule_management/api/rules/bulk_actions/request_schema.ts @@ -9,6 +9,7 @@ import * as t from 'io-ts'; import { NonEmptyArray, TimeDuration } from '@kbn/securitysolution-io-ts-types'; import { + RuleActionFrequency, RuleActionGroup, RuleActionId, RuleActionParams, @@ -96,11 +97,14 @@ const BulkActionEditPayloadTimeline = t.type({ */ type NormalizedRuleAction = t.TypeOf; const NormalizedRuleAction = t.exact( - t.type({ - group: RuleActionGroup, - id: RuleActionId, - params: RuleActionParams, - }) + t.intersection([ + t.type({ + group: RuleActionGroup, + id: RuleActionId, + params: RuleActionParams, + }), + t.partial({ frequency: RuleActionFrequency }), + ]) ); export type BulkActionEditPayloadRuleActions = t.TypeOf; @@ -109,10 +113,12 @@ export const BulkActionEditPayloadRuleActions = t.type({ t.literal(BulkActionEditType.add_rule_actions), t.literal(BulkActionEditType.set_rule_actions), ]), - value: t.type({ - throttle: ThrottleForBulkActions, - actions: t.array(NormalizedRuleAction), - }), + value: t.intersection([ + t.partial({ throttle: ThrottleForBulkActions }), + t.type({ + actions: t.array(NormalizedRuleAction), + }), + ]), }); type BulkActionEditPayloadSchedule = t.TypeOf; @@ -136,6 +142,7 @@ export const BulkActionEditPayload = t.union([ const bulkActionDuplicatePayload = t.exact( t.type({ include_exceptions: t.boolean, + include_expired_exceptions: t.boolean, }) ); diff --git a/x-pack/plugins/security_solution/common/detection_engine/rule_management/constants.ts b/x-pack/plugins/security_solution/common/detection_engine/rule_management/constants.ts index 710c0b55a86f9..ba290d30b57a3 100644 --- a/x-pack/plugins/security_solution/common/detection_engine/rule_management/constants.ts +++ b/x-pack/plugins/security_solution/common/detection_engine/rule_management/constants.ts @@ -7,5 +7,6 @@ export enum DuplicateOptions { withExceptions = 'withExceptions', + withExceptionsExcludeExpiredExceptions = 'withExceptionsExcludeExpiredExceptions', withoutExceptions = 'withoutExceptions', } 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 e0b427cdcefbc..ff4bb72a5eb65 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 @@ -119,6 +119,8 @@ export const baseSchema = buildRuleSchemas({ output_index: AlertsIndex, namespace: AlertsIndexNamespace, meta: RuleMetadata, + // Throttle + throttle: RuleActionThrottle, }, defaultable: { // Main attributes @@ -134,7 +136,6 @@ export const baseSchema = buildRuleSchemas({ to: RuleIntervalTo, // Rule actions actions: RuleActionArray, - throttle: RuleActionThrottle, // Rule exceptions exceptions_list: ExceptionListArray, // Misc attributes diff --git a/x-pack/plugins/security_solution/common/detection_engine/transform_actions.ts b/x-pack/plugins/security_solution/common/detection_engine/transform_actions.ts index 1b74bcd320aad..3808837dc0df2 100644 --- a/x-pack/plugins/security_solution/common/detection_engine/transform_actions.ts +++ b/x-pack/plugins/security_solution/common/detection_engine/transform_actions.ts @@ -16,6 +16,7 @@ export const transformRuleToAlertAction = ({ action_type_id: actionTypeId, params, uuid, + frequency, alerts_filter: alertsFilter, }: RuleAlertAction): RuleAction => ({ group, @@ -24,6 +25,7 @@ export const transformRuleToAlertAction = ({ actionTypeId, ...(alertsFilter && { alertsFilter }), ...(uuid && { uuid }), + ...(frequency && { frequency }), }); export const transformAlertToRuleAction = ({ @@ -32,6 +34,7 @@ export const transformAlertToRuleAction = ({ actionTypeId, params, uuid, + frequency, alertsFilter, }: RuleAction): RuleAlertAction => ({ group, @@ -40,6 +43,7 @@ export const transformAlertToRuleAction = ({ action_type_id: actionTypeId, ...(alertsFilter && { alerts_filter: alertsFilter }), ...(uuid && { uuid }), + ...(frequency && { frequency }), }); export const transformRuleToAlertResponseAction = ({ diff --git a/x-pack/plugins/security_solution/cypress/e2e/detection_alerts/expandable_flyout/alert_details_left_panel.cy.ts b/x-pack/plugins/security_solution/cypress/e2e/detection_alerts/expandable_flyout/alert_details_left_panel.cy.ts index a5442b786040f..a15669a620d49 100644 --- a/x-pack/plugins/security_solution/cypress/e2e/detection_alerts/expandable_flyout/alert_details_left_panel.cy.ts +++ b/x-pack/plugins/security_solution/cypress/e2e/detection_alerts/expandable_flyout/alert_details_left_panel.cy.ts @@ -100,9 +100,7 @@ describe.skip('Alert details expandable flyout left panel', { testIsolation: fal it('should display content when switching buttons', () => { openVisualizeTab(); openSessionView(); - cy.get(DOCUMENT_DETAILS_FLYOUT_VISUALIZE_TAB_SESSION_VIEW_CONTENT) - .should('be.visible') - .and('have.text', 'Session view'); + cy.get(DOCUMENT_DETAILS_FLYOUT_VISUALIZE_TAB_SESSION_VIEW_CONTENT).should('be.visible'); openGraphAnalyzer(); cy.get(DOCUMENT_DETAILS_FLYOUT_VISUALIZE_TAB_GRAPH_ANALYZER_CONTENT).should('be.visible'); diff --git a/x-pack/plugins/security_solution/cypress/e2e/detection_alerts/expandable_flyout/alert_details_left_panel_session_view_tab.cy.ts b/x-pack/plugins/security_solution/cypress/e2e/detection_alerts/expandable_flyout/alert_details_left_panel_session_view_tab.cy.ts new file mode 100644 index 0000000000000..1d18b33350fd4 --- /dev/null +++ b/x-pack/plugins/security_solution/cypress/e2e/detection_alerts/expandable_flyout/alert_details_left_panel_session_view_tab.cy.ts @@ -0,0 +1,45 @@ +/* + * 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 { DOCUMENT_DETAILS_FLYOUT_VISUALIZE_TAB_SESSION_VIEW_NO_DATA } from '../../../screens/document_expandable_flyout'; +import { + expandFirstAlertExpandableFlyout, + expandDocumentDetailsExpandableFlyoutLeftSection, +} from '../../../tasks/document_expandable_flyout'; +import { cleanKibana } from '../../../tasks/common'; +import { login, visit } from '../../../tasks/login'; +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'; + +// Skipping these for now as the feature is protected behind a feature flag set to false by default +// To run the tests locally, add 'securityFlyoutEnabled' in the Cypress config.ts here https://github.com/elastic/kibana/blob/main/x-pack/test/security_solution_cypress/config.ts#L50 +describe.skip( + 'Alert details expandable flyout left panel session view', + { testIsolation: false }, + () => { + before(() => { + cleanKibana(); + login(); + createRule(getNewRule()); + visit(ALERTS_URL); + waitForAlertsToPopulate(); + expandFirstAlertExpandableFlyout(); + expandDocumentDetailsExpandableFlyoutLeftSection(); + }); + + it('should display session view no data message', () => { + cy.get(DOCUMENT_DETAILS_FLYOUT_VISUALIZE_TAB_SESSION_VIEW_NO_DATA) + .should('be.visible') + .and('contain.text', 'No data to render') + .and('contain.text', 'No process events found for this query'); + }); + + it('should display session view component', () => {}); + } +); diff --git a/x-pack/plugins/security_solution/cypress/e2e/detection_alerts/expandable_flyout/alert_details_right_panel_overview_tab.cy.ts b/x-pack/plugins/security_solution/cypress/e2e/detection_alerts/expandable_flyout/alert_details_right_panel_overview_tab.cy.ts index c98c84b800079..57ea1a2d4efdb 100644 --- a/x-pack/plugins/security_solution/cypress/e2e/detection_alerts/expandable_flyout/alert_details_right_panel_overview_tab.cy.ts +++ b/x-pack/plugins/security_solution/cypress/e2e/detection_alerts/expandable_flyout/alert_details_right_panel_overview_tab.cy.ts @@ -29,6 +29,10 @@ import { DOCUMENT_DETAILS_FLYOUT_OVERVIEW_TAB_INSIGHTS_VIEW_ALL_ENTITIES_BUTTON, DOCUMENT_DETAILS_FLYOUT_INSIGHTS_TAB_ENTITIES_CONTENT, DOCUMENT_DETAILS_FLYOUT_OVERVIEW_TAB_ANALYZER_TREE, + DOCUMENT_DETAILS_FLYOUT_OVERVIEW_TAB_INSIGHTS_THREAT_INTELLIGENCE_HEADER, + DOCUMENT_DETAILS_FLYOUT_OVERVIEW_TAB_INSIGHTS_THREAT_INTELLIGENCE_CONTENT, + DOCUMENT_DETAILS_FLYOUT_OVERVIEW_TAB_INSIGHTS_THREAT_INTELLIGENCE_VALUES, + DOCUMENT_DETAILS_FLYOUT_OVERVIEW_TAB_INSIGHTS_THREAT_INTELLIGENCE_VIEW_ALL_BUTTON, } from '../../../screens/document_expandable_flyout'; import { expandFirstAlertExpandableFlyout, @@ -180,6 +184,38 @@ describe.skip( .click(); cy.get(DOCUMENT_DETAILS_FLYOUT_INSIGHTS_TAB_ENTITIES_CONTENT).should('be.visible'); }); + + // TODO work on getting proper IoC data to make the threat intelligence section work here + it.skip('should display threat intelligence section', () => { + cy.get(DOCUMENT_DETAILS_FLYOUT_OVERVIEW_TAB_INSIGHTS_THREAT_INTELLIGENCE_HEADER) + .scrollIntoView() + .should('be.visible') + .and('have.text', 'Threat Intelligence'); + cy.get(DOCUMENT_DETAILS_FLYOUT_OVERVIEW_TAB_INSIGHTS_THREAT_INTELLIGENCE_CONTENT) + .should('be.visible') + .within(() => { + // threat match detected + cy.get(DOCUMENT_DETAILS_FLYOUT_OVERVIEW_TAB_INSIGHTS_THREAT_INTELLIGENCE_VALUES) + .eq(0) + .should('be.visible') + .and('have.text', '1 threat match detected'); // TODO + + // field with threat enrichement + cy.get(DOCUMENT_DETAILS_FLYOUT_OVERVIEW_TAB_INSIGHTS_THREAT_INTELLIGENCE_VALUES) + .eq(1) + .should('be.visible') + .and('have.text', '1 field enriched with threat intelligence'); // TODO + }); + }); + + // TODO work on getting proper IoC data to make the threat intelligence section work here + // and improve when we can navigate Threat Intelligence to sub tab directly + it.skip('should navigate to left panel, entities tab when view all fields of threat intelligence is clicked', () => { + cy.get(DOCUMENT_DETAILS_FLYOUT_OVERVIEW_TAB_INSIGHTS_THREAT_INTELLIGENCE_VIEW_ALL_BUTTON) + .should('be.visible') + .click(); + cy.get(DOCUMENT_DETAILS_FLYOUT_INSIGHTS_TAB_ENTITIES_CONTENT).should('be.visible'); + }); }); describe('visualizations section', () => { diff --git a/x-pack/plugins/security_solution/cypress/e2e/detection_alerts/expandable_flyout/alert_details_url_sync.cy.ts b/x-pack/plugins/security_solution/cypress/e2e/detection_alerts/expandable_flyout/alert_details_url_sync.cy.ts new file mode 100644 index 0000000000000..b7580642ba564 --- /dev/null +++ b/x-pack/plugins/security_solution/cypress/e2e/detection_alerts/expandable_flyout/alert_details_url_sync.cy.ts @@ -0,0 +1,55 @@ +/* + * 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 { getNewRule } from '../../../objects/rule'; +import { cleanKibana } from '../../../tasks/common'; +import { waitForAlertsToPopulate } from '../../../tasks/create_new_rule'; +import { expandFirstAlertExpandableFlyout } from '../../../tasks/document_expandable_flyout'; +import { login, visit } from '../../../tasks/login'; +import { createRule } from '../../../tasks/api_calls/rules'; +import { ALERTS_URL } from '../../../urls/navigation'; +import { + DOCUMENT_DETAILS_FLYOUT_CLOSE_BUTTON, + DOCUMENT_DETAILS_FLYOUT_HEADER_TITLE, +} from '../../../screens/document_expandable_flyout'; + +// Skipping these for now as the feature is protected behind a feature flag set to false by default +// To run the tests locally, add 'securityFlyoutEnabled' in the Cypress config.ts here https://github.com/elastic/kibana/blob/main/x-pack/test/security_solution_cypress/config.ts#L50 +describe.skip('Expandable flyout state sync', { testIsolation: false }, () => { + const rule = getNewRule(); + + before(() => { + cleanKibana(); + login(); + createRule(rule); + visit(ALERTS_URL); + waitForAlertsToPopulate(); + expandFirstAlertExpandableFlyout(); + }); + + it('should serialize its state to url', () => { + cy.url().should('include', 'eventFlyout'); + cy.get(DOCUMENT_DETAILS_FLYOUT_HEADER_TITLE).should('be.visible').and('have.text', rule.name); + }); + + it('should reopen the flyout after browser refresh', () => { + cy.reload(); + + cy.url().should('include', 'eventFlyout'); + cy.get(DOCUMENT_DETAILS_FLYOUT_HEADER_TITLE).should('be.visible').and('have.text', rule.name); + }); + + it('should clear the url state when flyout is closed', () => { + cy.reload(); + + cy.get(DOCUMENT_DETAILS_FLYOUT_HEADER_TITLE).should('be.visible').and('have.text', rule.name); + + cy.get(DOCUMENT_DETAILS_FLYOUT_CLOSE_BUTTON).click(); + + cy.url().should('not.include', 'eventFlyout'); + }); +}); diff --git a/x-pack/plugins/security_solution/cypress/e2e/detection_rules/bulk_duplicate_rules.cy.ts b/x-pack/plugins/security_solution/cypress/e2e/detection_rules/bulk_duplicate_rules.cy.ts new file mode 100644 index 0000000000000..ca25b9955bf97 --- /dev/null +++ b/x-pack/plugins/security_solution/cypress/e2e/detection_rules/bulk_duplicate_rules.cy.ts @@ -0,0 +1,132 @@ +/* + * 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 { + waitForRulesTableToBeLoaded, + goToTheRuleDetailsOf, + selectNumberOfRules, + duplicateSelectedRulesWithoutExceptions, + expectManagementTableRules, + duplicateSelectedRulesWithExceptions, + duplicateSelectedRulesWithNonExpiredExceptions, +} from '../../tasks/alerts_detection_rules'; + +import { goToExceptionsTab, viewExpiredExceptionItems } from '../../tasks/rule_details'; +import { login, visitWithoutDateRange } from '../../tasks/login'; + +import { SECURITY_DETECTIONS_RULES_URL } from '../../urls/navigation'; +import { createRule } from '../../tasks/api_calls/rules'; +import { cleanKibana, resetRulesTableState, deleteAlertsAndRules } from '../../tasks/common'; + +import { getNewRule } from '../../objects/rule'; + +import { esArchiverResetKibana } from '../../tasks/es_archiver'; + +import { createRuleExceptionItem } from '../../tasks/api_calls/exceptions'; +import { EXCEPTION_CARD_ITEM_NAME } from '../../screens/exceptions'; +import { + assertExceptionItemsExists, + assertNumberOfExceptionItemsExists, +} from '../../tasks/exceptions'; + +const RULE_NAME = 'Custom rule for bulk actions'; + +const prePopulatedIndexPatterns = ['index-1-*', 'index-2-*']; +const prePopulatedTags = ['test-default-tag-1', 'test-default-tag-2']; + +const defaultRuleData = { + index: prePopulatedIndexPatterns, + tags: prePopulatedTags, +}; + +const expiredDate = new Date(Date.now() - 1000000).toISOString(); +const futureDate = new Date(Date.now() + 1000000).toISOString(); + +const EXPIRED_EXCEPTION_ITEM_NAME = 'Sample exception item'; + +const NON_EXPIRED_EXCEPTION_ITEM_NAME = 'Sample exception item with future expiration'; + +describe('Detection rules, bulk duplicate', () => { + before(() => { + cleanKibana(); + login(); + }); + beforeEach(() => { + // Make sure persisted rules table state is cleared + resetRulesTableState(); + deleteAlertsAndRules(); + esArchiverResetKibana(); + createRule(getNewRule({ name: RULE_NAME, ...defaultRuleData, rule_id: '1' })).then( + (response) => { + createRuleExceptionItem(response.body.id, [ + { + description: 'Exception item for rule default exception list', + entries: [ + { + field: 'user.name', + operator: 'included', + type: 'match', + value: 'some value', + }, + ], + name: EXPIRED_EXCEPTION_ITEM_NAME, + type: 'simple', + expire_time: expiredDate, + }, + { + description: 'Exception item for rule default exception list', + entries: [ + { + field: 'user.name', + operator: 'included', + type: 'match', + value: 'some value', + }, + ], + name: NON_EXPIRED_EXCEPTION_ITEM_NAME, + type: 'simple', + expire_time: futureDate, + }, + ]); + } + ); + + visitWithoutDateRange(SECURITY_DETECTIONS_RULES_URL); + + waitForRulesTableToBeLoaded(); + }); + + it('Duplicates rules', () => { + selectNumberOfRules(1); + duplicateSelectedRulesWithoutExceptions(); + expectManagementTableRules([`${RULE_NAME} [Duplicate]`]); + }); + + describe('With exceptions', () => { + it('Duplicates rules with expired exceptions', () => { + selectNumberOfRules(1); + duplicateSelectedRulesWithExceptions(); + expectManagementTableRules([`${RULE_NAME} [Duplicate]`]); + goToTheRuleDetailsOf(`${RULE_NAME} [Duplicate]`); + goToExceptionsTab(); + assertExceptionItemsExists(EXCEPTION_CARD_ITEM_NAME, [NON_EXPIRED_EXCEPTION_ITEM_NAME]); + viewExpiredExceptionItems(); + assertExceptionItemsExists(EXCEPTION_CARD_ITEM_NAME, [EXPIRED_EXCEPTION_ITEM_NAME]); + }); + + it('Duplicates rules with exceptions, excluding expired exceptions', () => { + selectNumberOfRules(1); + duplicateSelectedRulesWithNonExpiredExceptions(); + expectManagementTableRules([`${RULE_NAME} [Duplicate]`]); + goToTheRuleDetailsOf(`${RULE_NAME} [Duplicate]`); + goToExceptionsTab(); + assertExceptionItemsExists(EXCEPTION_CARD_ITEM_NAME, [NON_EXPIRED_EXCEPTION_ITEM_NAME]); + viewExpiredExceptionItems(); + assertNumberOfExceptionItemsExists(0); + }); + }); +}); 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 fceecae2b1d5b..624dec7f1c955 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 @@ -5,6 +5,7 @@ * 2.0. */ +import type { RuleActionArray } from '@kbn/securitysolution-io-ts-alerting-types'; import { ROLES } from '../../../common/test'; import { @@ -15,11 +16,18 @@ import { import { actionFormSelector } from '../../screens/common/rule_actions'; import { cleanKibana, deleteAlertsAndRules, deleteConnectors } from '../../tasks/common'; +import type { RuleActionCustomFrequency } from '../../tasks/common/rule_actions'; import { addSlackRuleAction, assertSlackRuleAction, addEmailConnectorAndRuleAction, assertEmailRuleAction, + assertSelectedCustomFrequencyOption, + assertSelectedPerRuleRunFrequencyOption, + assertSelectedSummaryOfAlertsOption, + pickCustomFrequencyOption, + pickPerRuleRunFrequencyOption, + pickSummaryOfAlertsOption, } from '../../tasks/common/rule_actions'; import { waitForRulesTableToBeLoaded, @@ -32,10 +40,8 @@ import { submitBulkEditForm, checkOverwriteRuleActionsCheckbox, openBulkEditRuleActionsForm, - pickActionFrequency, openBulkActionsMenu, } from '../../tasks/rules_bulk_edit'; -import { assertSelectedActionFrequency } from '../../tasks/edit_rule'; import { login, visitWithoutDateRange } from '../../tasks/login'; import { esArchiverResetKibana } from '../../tasks/es_archiver'; @@ -75,7 +81,7 @@ describe.skip('Detection rules, bulk edit of rule actions', () => { esArchiverResetKibana(); createSlackConnector().then(({ body }) => { - const actions = [ + const actions: RuleActionArray = [ { id: body.id, action_type_id: '.slack', @@ -83,6 +89,11 @@ describe.skip('Detection rules, bulk edit of rule actions', () => { params: { message: expectedExistingSlackMessage, }, + frequency: { + summary: true, + throttle: null, + notifyWhen: 'onActiveAlert', + }, }, ]; @@ -120,7 +131,10 @@ describe.skip('Detection rules, bulk edit of rule actions', () => { }); it('Add a rule action to rules (existing connector)', () => { - const expectedActionFrequency = 'Daily'; + const expectedActionFrequency: RuleActionCustomFrequency = { + throttle: 1, + throttleUnit: 'd', + }; loadPrebuiltDetectionRulesFromHeaderBtn(); @@ -131,8 +145,9 @@ describe.skip('Detection rules, bulk edit of rule actions', () => { // ensure rule actions info callout displayed on the form cy.get(RULES_BULK_EDIT_ACTIONS_INFO).should('be.visible'); - pickActionFrequency(expectedActionFrequency); addSlackRuleAction(expectedSlackMessage); + pickSummaryOfAlertsOption(); + pickCustomFrequencyOption(expectedActionFrequency); submitBulkEditForm(); waitForBulkEditActionToFinish({ updatedCount: expectedNumberOfRulesToBeEdited }); @@ -140,7 +155,8 @@ describe.skip('Detection rules, bulk edit of rule actions', () => { // check if rule has been updated goToEditRuleActionsSettingsOf(ruleNameToAssert); - assertSelectedActionFrequency(expectedActionFrequency); + assertSelectedSummaryOfAlertsOption(); + assertSelectedCustomFrequencyOption(expectedActionFrequency, 1); assertSlackRuleAction(expectedExistingSlackMessage, 0); assertSlackRuleAction(expectedSlackMessage, 1); // ensure there is no third action @@ -148,16 +164,15 @@ describe.skip('Detection rules, bulk edit of rule actions', () => { }); it('Overwrite rule actions in rules', () => { - const expectedActionFrequency = 'On each rule execution'; - loadPrebuiltDetectionRulesFromHeaderBtn(); // select both custom and prebuilt rules selectNumberOfRules(expectedNumberOfRulesToBeEdited); openBulkEditRuleActionsForm(); - pickActionFrequency(expectedActionFrequency); addSlackRuleAction(expectedSlackMessage); + pickSummaryOfAlertsOption(); + pickPerRuleRunFrequencyOption(); // check overwrite box, ensure warning is displayed checkOverwriteRuleActionsCheckbox(); @@ -171,22 +186,27 @@ describe.skip('Detection rules, bulk edit of rule actions', () => { // check if rule has been updated goToEditRuleActionsSettingsOf(ruleNameToAssert); - assertSelectedActionFrequency(expectedActionFrequency); + assertSelectedSummaryOfAlertsOption(); + assertSelectedPerRuleRunFrequencyOption(); assertSlackRuleAction(expectedSlackMessage); // ensure existing action was overwritten cy.get(actionFormSelector(1)).should('not.exist'); }); it('Add a rule action to rules (new connector)', () => { - const expectedActionFrequency = 'Hourly'; + const expectedActionFrequency: RuleActionCustomFrequency = { + throttle: 2, + throttleUnit: 'h', + }; const expectedEmail = 'test@example.com'; const expectedSubject = 'Subject'; selectNumberOfRules(expectedNumberOfCustomRulesToBeEdited); openBulkEditRuleActionsForm(); - pickActionFrequency(expectedActionFrequency); addEmailConnectorAndRuleAction(expectedEmail, expectedSubject); + pickSummaryOfAlertsOption(); + pickCustomFrequencyOption(expectedActionFrequency); submitBulkEditForm(); waitForBulkEditActionToFinish({ updatedCount: expectedNumberOfCustomRulesToBeEdited }); @@ -194,7 +214,8 @@ describe.skip('Detection rules, bulk edit of rule actions', () => { // check if rule has been updated goToEditRuleActionsSettingsOf(ruleNameToAssert); - assertSelectedActionFrequency(expectedActionFrequency); + assertSelectedSummaryOfAlertsOption(); + assertSelectedCustomFrequencyOption(expectedActionFrequency, 1); assertEmailRuleAction(expectedEmail, expectedSubject); }); }); 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 8e2ae1b85cce7..4965072e7038d 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 @@ -19,10 +19,13 @@ import { RULE_SWITCH, SEVERITY, } from '../../screens/alerts_detection_rules'; +import { + ACTIONS_NOTIFY_WHEN_BUTTON, + ACTIONS_SUMMARY_BUTTON, +} from '../../screens/common/rule_actions'; import { ABOUT_CONTINUE_BTN, ABOUT_EDIT_BUTTON, - ACTIONS_THROTTLE_INPUT, CUSTOM_QUERY_INPUT, DEFINE_CONTINUE_BUTTON, DEFINE_EDIT_BUTTON, @@ -401,12 +404,11 @@ describe('Custom query rules', () => { goToActionsStepTab(); - cy.get(ACTIONS_THROTTLE_INPUT).invoke('val').should('eql', 'no_actions'); - - cy.get(ACTIONS_THROTTLE_INPUT).select('Weekly'); - addEmailConnectorAndRuleAction('test@example.com', 'Subject'); + cy.get(ACTIONS_SUMMARY_BUTTON).should('have.text', 'Summary of alerts'); + cy.get(ACTIONS_NOTIFY_WHEN_BUTTON).should('have.text', 'Per rule run'); + goToAboutStepTab(); cy.get(TAGS_CLEAR_BUTTON).click({ force: true }); fillAboutRule(getEditedRule()); 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 740bdcfb54dac..fbc28e8a2c671 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 @@ -58,12 +58,12 @@ import { INDICATOR_MATCH_ROW_RENDER, PROVIDER_BADGE } from '../../screens/timeli import { investigateFirstAlertInTimeline } from '../../tasks/alerts'; import { duplicateFirstRule, - duplicateSelectedRules, duplicateRuleFromMenu, goToRuleDetails, selectNumberOfRules, checkDuplicatedRule, expectNumberOfRules, + duplicateSelectedRulesWithExceptions, } from '../../tasks/alerts_detection_rules'; import { createRule } from '../../tasks/api_calls/rules'; import { loadPrepackagedTimelineTemplates } from '../../tasks/api_calls/timelines'; @@ -544,7 +544,7 @@ describe('indicator match', () => { it("Allows the rule to be duplicated from the table's bulk actions", () => { selectNumberOfRules(1); - duplicateSelectedRules(); + duplicateSelectedRulesWithExceptions(); checkDuplicatedRule(); }); 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 5ed5ef8be059a..ab458e12dca2d 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 @@ -43,7 +43,7 @@ describe('Rule actions during detection rule creation', () => { }); const rule = getSimpleCustomQueryRule(); - const actions = { throttle: 'rule', connectors: [indexConnector] }; + const actions = { connectors: [indexConnector] }; const index = actions.connectors[0].index; const initialNumberOfDocuments = 0; const expectedJson = JSON.parse(actions.connectors[0].document); 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 5964c8303ce1d..572e535d740fd 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 @@ -15,7 +15,7 @@ import { login, visitWithoutDateRange, waitForPageWithoutDateRange } from '../.. import { EXCEPTIONS_URL } from '../../../urls/navigation'; import { deleteExceptionListWithRuleReferenceByListId, - deleteExceptionListWithoutRuleReference, + deleteExceptionListWithoutRuleReferenceByListId, exportExceptionList, searchForExceptionList, waitForExceptionsTableToBeLoaded, @@ -153,7 +153,7 @@ describe('Exceptions Table', () => { // just checking number of lists shown cy.contains(EXCEPTIONS_TABLE_SHOWING_LISTS, '3'); - deleteExceptionListWithoutRuleReference(); + deleteExceptionListWithoutRuleReferenceByListId(getExceptionList1().list_id); // Using cy.contains because we do not care about the exact text, // just checking number of lists shown diff --git a/x-pack/plugins/security_solution/cypress/e2e/exceptions/shared_exception_lists_management/all_exception_lists_read_only.cy.ts b/x-pack/plugins/security_solution/cypress/e2e/exceptions/shared_exception_lists_management/all_exception_lists_read_only.cy.ts index bc6279113c8e5..d5e68594b9967 100644 --- a/x-pack/plugins/security_solution/cypress/e2e/exceptions/shared_exception_lists_management/all_exception_lists_read_only.cy.ts +++ b/x-pack/plugins/security_solution/cypress/e2e/exceptions/shared_exception_lists_management/all_exception_lists_read_only.cy.ts @@ -9,7 +9,10 @@ import { esArchiverResetKibana } from '../../../tasks/es_archiver'; import { cleanKibana } from '../../../tasks/common'; import { ROLES } from '../../../../common/test'; import { getExceptionList } from '../../../objects/exception'; -import { EXCEPTIONS_TABLE_SHOWING_LISTS } from '../../../screens/exceptions'; +import { + EXCEPTIONS_OVERFLOW_ACTIONS_BTN, + EXCEPTIONS_TABLE_SHOWING_LISTS, +} from '../../../screens/exceptions'; import { createExceptionList, deleteExceptionList } from '../../../tasks/api_calls/exceptions'; import { dismissCallOut, @@ -56,4 +59,8 @@ describe('All exception lists - read only', () => { getCallOut(MISSING_PRIVILEGES_CALLOUT).should('not.exist'); }); }); + + it('Exception list actions should be disabled', () => { + cy.get(EXCEPTIONS_OVERFLOW_ACTIONS_BTN).first().should('be.disabled'); + }); }); diff --git a/x-pack/plugins/security_solution/cypress/e2e/exceptions/shared_exception_lists_management/manage_shared_exception_list.cy.ts b/x-pack/plugins/security_solution/cypress/e2e/exceptions/shared_exception_lists_management/manage_shared_exception_list.cy.ts index f7610c3f4b5d9..bc3e732538ff0 100644 --- a/x-pack/plugins/security_solution/cypress/e2e/exceptions/shared_exception_lists_management/manage_shared_exception_list.cy.ts +++ b/x-pack/plugins/security_solution/cypress/e2e/exceptions/shared_exception_lists_management/manage_shared_exception_list.cy.ts @@ -5,7 +5,6 @@ * 2.0. */ -import { ROLES } from '../../../../common/test'; import { getExceptionList, expectedExportedExceptionList } from '../../../objects/exception'; import { getNewRule } from '../../../objects/rule'; @@ -14,8 +13,11 @@ import { login, visitWithoutDateRange, waitForPageWithoutDateRange } from '../.. import { EXCEPTIONS_URL } from '../../../urls/navigation'; import { + assertExceptionListsExists, + duplicateSharedExceptionListFromListsManagementPageByListId, + findSharedExceptionListItemsByName, + deleteExceptionListWithoutRuleReferenceByListId, deleteExceptionListWithRuleReferenceByListId, - deleteExceptionListWithoutRuleReference, exportExceptionList, waitForExceptionsTableToBeLoaded, createSharedExceptionList, @@ -24,135 +26,211 @@ import { } from '../../../tasks/exceptions_table'; import { EXCEPTIONS_LIST_MANAGEMENT_NAME, - EXCEPTIONS_OVERFLOW_ACTIONS_BTN, EXCEPTIONS_TABLE_SHOWING_LISTS, } from '../../../screens/exceptions'; -import { createExceptionList } from '../../../tasks/api_calls/exceptions'; +import { createExceptionList, createExceptionListItem } from '../../../tasks/api_calls/exceptions'; import { esArchiverResetKibana } from '../../../tasks/es_archiver'; +import { assertNumberOfExceptionItemsExists } from '../../../tasks/exceptions'; + import { TOASTER } from '../../../screens/alerts_detection_rules'; const EXCEPTION_LIST_NAME = 'My shared list'; +const EXCEPTION_LIST_TO_DUPLICATE_NAME = 'A test list 2'; +const EXCEPTION_LIST_ITEM_NAME = 'Sample Exception List Item 1'; +const EXCEPTION_LIST_ITEM_NAME_2 = 'Sample Exception List Item 2'; + const getExceptionList1 = () => ({ ...getExceptionList(), name: EXCEPTION_LIST_NAME, list_id: 'exception_list_1', }); + const getExceptionList2 = () => ({ ...getExceptionList(), - name: 'Test list 2', + name: EXCEPTION_LIST_TO_DUPLICATE_NAME, list_id: 'exception_list_2', }); +const expiredDate = new Date(Date.now() - 1000000).toISOString(); +const futureDate = new Date(Date.now() + 1000000).toISOString(); + describe('Manage shared exception list', () => { - before(() => { - esArchiverResetKibana(); - login(); - - createRule(getNewRule({ name: 'Another rule' })); - - // Create exception list associated with a rule - createExceptionList(getExceptionList2(), getExceptionList2().list_id).then((response) => - createRule( - getNewRule({ - exceptions_list: [ - { - id: response.body.id, - list_id: getExceptionList2().list_id, - type: getExceptionList2().type, - namespace_type: getExceptionList2().namespace_type, - }, - ], - }) - ) - ); - - // Create exception list not used by any rules - createExceptionList(getExceptionList1(), getExceptionList1().list_id).as( - 'exceptionListResponse' - ); - }); + describe('Create/Export/Delete', () => { + before(() => { + esArchiverResetKibana(); + login(); + + createRule(getNewRule({ name: 'Another rule' })); + + // Create exception list associated with a rule + createExceptionList(getExceptionList2(), getExceptionList2().list_id).then((response) => + createRule( + getNewRule({ + exceptions_list: [ + { + id: response.body.id, + list_id: getExceptionList2().list_id, + type: getExceptionList2().type, + namespace_type: getExceptionList2().namespace_type, + }, + ], + }) + ) + ); - beforeEach(() => { - visitWithoutDateRange(EXCEPTIONS_URL); - waitForExceptionsTableToBeLoaded(); - }); + // Create exception list not used by any rules + createExceptionList(getExceptionList1(), getExceptionList1().list_id).as( + 'exceptionListResponse' + ); + }); + + beforeEach(() => { + visitWithoutDateRange(EXCEPTIONS_URL); + waitForExceptionsTableToBeLoaded(); + }); - it('Export exception list', function () { - cy.intercept(/(\/api\/exception_lists\/_export)/).as('export'); + it('Export exception list', function () { + cy.intercept(/(\/api\/exception_lists\/_export)/).as('export'); - exportExceptionList(getExceptionList1().list_id); + exportExceptionList(getExceptionList1().list_id); - cy.wait('@export').then(({ response }) => { - cy.wrap(response?.body).should( - 'eql', - expectedExportedExceptionList(this.exceptionListResponse) - ); + cy.wait('@export').then(({ response }) => { + cy.wrap(response?.body).should( + 'eql', + expectedExportedExceptionList(this.exceptionListResponse) + ); + + cy.get(TOASTER).should( + 'have.text', + `Exception list "${EXCEPTION_LIST_NAME}" exported successfully` + ); + }); + }); + + it('Link rules to shared exception list', function () { + assertNumberLinkedRules(getExceptionList2().list_id, '1'); + linkRulesToExceptionList(getExceptionList2().list_id, 1); + assertNumberLinkedRules(getExceptionList2().list_id, '2'); + }); - cy.get(TOASTER).should( - 'have.text', - `Exception list "${EXCEPTION_LIST_NAME}" exported successfully` + it('Create exception list', function () { + createSharedExceptionList( + { name: 'Newly created list', description: 'This is my list.' }, + true ); + + // After creation - directed to list detail page + cy.get(EXCEPTIONS_LIST_MANAGEMENT_NAME).should('have.text', 'Newly created list'); }); - }); - it('Link rules to shared exception list', function () { - assertNumberLinkedRules(getExceptionList2().list_id, '1'); - linkRulesToExceptionList(getExceptionList2().list_id, 1); - assertNumberLinkedRules(getExceptionList2().list_id, '2'); - }); + it('Delete exception list without rule reference', () => { + // Using cy.contains because we do not care about the exact text, + // just checking number of lists shown + cy.contains(EXCEPTIONS_TABLE_SHOWING_LISTS, '4'); - it('Create exception list', function () { - createSharedExceptionList({ name: EXCEPTION_LIST_NAME, description: 'This is my list.' }, true); + deleteExceptionListWithoutRuleReferenceByListId(getExceptionList1().list_id); - // After creation - directed to list detail page - cy.get(EXCEPTIONS_LIST_MANAGEMENT_NAME).should('have.text', EXCEPTION_LIST_NAME); - }); + // Using cy.contains because we do not care about the exact text, + // just checking number of lists shown + cy.contains(EXCEPTIONS_TABLE_SHOWING_LISTS, '3'); + }); + + it('Deletes exception list with rule reference', () => { + waitForPageWithoutDateRange(EXCEPTIONS_URL); + waitForExceptionsTableToBeLoaded(); - it('Delete exception list without rule reference', () => { - // Using cy.contains because we do not care about the exact text, - // just checking number of lists shown - cy.contains(EXCEPTIONS_TABLE_SHOWING_LISTS, '4'); + // Using cy.contains because we do not care about the exact text, + // just checking number of lists shown + cy.contains(EXCEPTIONS_TABLE_SHOWING_LISTS, '3'); - deleteExceptionListWithoutRuleReference(); + deleteExceptionListWithRuleReferenceByListId(getExceptionList2().list_id); - // Using cy.contains because we do not care about the exact text, - // just checking number of lists shown - cy.contains(EXCEPTIONS_TABLE_SHOWING_LISTS, '3'); + // Using cy.contains because we do not care about the exact text, + // just checking number of lists shown + cy.contains(EXCEPTIONS_TABLE_SHOWING_LISTS, '2'); + }); }); - it('Deletes exception list with rule reference', () => { - waitForPageWithoutDateRange(EXCEPTIONS_URL); - waitForExceptionsTableToBeLoaded(); + describe('Duplicate', () => { + beforeEach(() => { + esArchiverResetKibana(); + login(); + + // Create exception list associated with a rule + createExceptionList(getExceptionList2(), getExceptionList2().list_id); + + createExceptionListItem(getExceptionList2().list_id, { + list_id: getExceptionList2().list_id, + item_id: 'simple_list_item_1', + tags: [], + type: 'simple', + description: 'Test exception item', + name: EXCEPTION_LIST_ITEM_NAME, + namespace_type: 'single', + entries: [ + { + field: 'host.name', + operator: 'included', + type: 'match_any', + value: ['some host', 'another host'], + }, + ], + expire_time: expiredDate, + }); + createExceptionListItem(getExceptionList2().list_id, { + list_id: getExceptionList2().list_id, + item_id: 'simple_list_item_2', + tags: [], + type: 'simple', + description: 'Test exception item', + name: EXCEPTION_LIST_ITEM_NAME_2, + namespace_type: 'single', + entries: [ + { + field: 'host.name', + operator: 'included', + type: 'match_any', + value: ['some host', 'another host'], + }, + ], + expire_time: futureDate, + }); + + visitWithoutDateRange(EXCEPTIONS_URL); + waitForExceptionsTableToBeLoaded(); + }); + + it('Duplicate exception list with expired items', function () { + duplicateSharedExceptionListFromListsManagementPageByListId( + getExceptionList2().list_id, + true + ); - // Using cy.contains because we do not care about the exact text, - // just checking number of lists shown - cy.contains(EXCEPTIONS_TABLE_SHOWING_LISTS, '3'); + // After duplication - check for new list + assertExceptionListsExists([`${EXCEPTION_LIST_TO_DUPLICATE_NAME} [Duplicate]`]); - deleteExceptionListWithRuleReferenceByListId(getExceptionList2().list_id); + findSharedExceptionListItemsByName(`${EXCEPTION_LIST_TO_DUPLICATE_NAME} [Duplicate]`, [ + EXCEPTION_LIST_ITEM_NAME, + EXCEPTION_LIST_ITEM_NAME_2, + ]); - // Using cy.contains because we do not care about the exact text, - // just checking number of lists shown - cy.contains(EXCEPTIONS_TABLE_SHOWING_LISTS, '2'); - }); -}); + assertNumberOfExceptionItemsExists(2); + }); -describe('Manage shared exception list - read only', () => { - before(() => { - // First we login as a privileged user to create exception list - esArchiverResetKibana(); - login(ROLES.platform_engineer); - visitWithoutDateRange(EXCEPTIONS_URL, ROLES.platform_engineer); - createExceptionList(getExceptionList(), getExceptionList().list_id); + it('Duplicate exception list without expired items', function () { + duplicateSharedExceptionListFromListsManagementPageByListId( + getExceptionList2().list_id, + false + ); - // Then we login as read-only user to test. - login(ROLES.reader); - visitWithoutDateRange(EXCEPTIONS_URL, ROLES.reader); - waitForExceptionsTableToBeLoaded(); + // After duplication - check for new list + assertExceptionListsExists([`${EXCEPTION_LIST_TO_DUPLICATE_NAME} [Duplicate]`]); - cy.get(EXCEPTIONS_TABLE_SHOWING_LISTS).should('have.text', `Showing 1 list`); - }); + findSharedExceptionListItemsByName(`${EXCEPTION_LIST_TO_DUPLICATE_NAME} [Duplicate]`, [ + EXCEPTION_LIST_ITEM_NAME_2, + ]); - it('Exception list actions should be disabled', () => { - cy.get(EXCEPTIONS_OVERFLOW_ACTIONS_BTN).first().should('be.disabled'); + assertNumberOfExceptionItemsExists(1); + }); }); }); diff --git a/x-pack/plugins/security_solution/cypress/e2e/timeline_templates/export.cy.ts b/x-pack/plugins/security_solution/cypress/e2e/timeline_templates/export.cy.ts index b8fa0c1f5a80f..f015e08139cea 100644 --- a/x-pack/plugins/security_solution/cypress/e2e/timeline_templates/export.cy.ts +++ b/x-pack/plugins/security_solution/cypress/e2e/timeline_templates/export.cy.ts @@ -15,7 +15,7 @@ import { import { TIMELINE_TEMPLATES_URL } from '../../urls/navigation'; import { createTimelineTemplate } from '../../tasks/api_calls/timelines'; import { cleanKibana } from '../../tasks/common'; -import { setRowsPerPageTo } from '../../tasks/table_pagination'; +import { searchByTitle } from '../../tasks/table_pagination'; describe('Export timelines', () => { before(() => { @@ -27,13 +27,14 @@ describe('Export timelines', () => { createTimelineTemplate(getTimelineTemplate()).then((response) => { cy.wrap(response).as('templateResponse'); cy.wrap(response.body.data.persistTimeline.timeline.savedObjectId).as('templateId'); + cy.wrap(response.body.data.persistTimeline.timeline.title).as('templateTitle'); }); login(); }); it('Exports a custom timeline template', function () { visitWithoutDateRange(TIMELINE_TEMPLATES_URL); - setRowsPerPageTo(20); + searchByTitle(this.templateTitle); exportTimeline(this.templateId); cy.wait('@export').then(({ response }) => { diff --git a/x-pack/plugins/security_solution/cypress/objects/exception.ts b/x-pack/plugins/security_solution/cypress/objects/exception.ts index 8427d828b6eea..d95455e34dc7e 100644 --- a/x-pack/plugins/security_solution/cypress/objects/exception.ts +++ b/x-pack/plugins/security_solution/cypress/objects/exception.ts @@ -31,6 +31,15 @@ export interface ExceptionListItem { tags: string[]; type: 'simple'; entries: Array<{ field: string; operator: string; type: string; value: string[] }>; + expire_time?: string; +} + +export interface RuleExceptionItem { + description: string; + name: string; + type: 'simple'; + entries: Array<{ field: string; operator: string; type: string; value: string[] | string }>; + expire_time: string | undefined; } export const getExceptionList = (): ExceptionList => ({ diff --git a/x-pack/plugins/security_solution/cypress/objects/rule.ts b/x-pack/plugins/security_solution/cypress/objects/rule.ts index ac630d1ee0fd9..af44aa14aa668 100644 --- a/x-pack/plugins/security_solution/cypress/objects/rule.ts +++ b/x-pack/plugins/security_solution/cypress/objects/rule.ts @@ -578,7 +578,6 @@ export const expectedExportedRule = (ruleResponse: Cypress.Response = Partial>; export interface Actions { - throttle: RuleActionThrottle; connectors: Connectors[]; } diff --git a/x-pack/plugins/security_solution/cypress/screens/alerts_detection_rules.ts b/x-pack/plugins/security_solution/cypress/screens/alerts_detection_rules.ts index 2af59e2ae67d7..66f61cc0898f1 100644 --- a/x-pack/plugins/security_solution/cypress/screens/alerts_detection_rules.ts +++ b/x-pack/plugins/security_solution/cypress/screens/alerts_detection_rules.ts @@ -38,6 +38,13 @@ export const DELETE_RULE_BULK_BTN = '[data-test-subj="deleteRuleBulk"]'; export const DUPLICATE_RULE_BULK_BTN = '[data-test-subj="duplicateRuleBulk"]'; +export const DUPLICATE_WITH_EXCEPTIONS_OPTION = '[data-test-subj="withExceptions"] label'; + +export const DUPLICATE_WITH_EXCEPTIONS_WITHOUT_EXPIRED_OPTION = + '[data-test-subj="withExceptionsExcludeExpiredExceptions"] label'; + +export const DUPLICATE_WITHOUT_EXCEPTIONS_OPTION = '[data-test-subj="withoutExceptions"] label'; + export const RULE_SEARCH_FIELD = '[data-test-subj="ruleSearchField"]'; export const EXPORT_ACTION_BTN = '[data-test-subj="exportRuleAction"]'; diff --git a/x-pack/plugins/security_solution/cypress/screens/common/rule_actions.ts b/x-pack/plugins/security_solution/cypress/screens/common/rule_actions.ts index 2fe606fc6bf64..9a1702b96d63c 100644 --- a/x-pack/plugins/security_solution/cypress/screens/common/rule_actions.ts +++ b/x-pack/plugins/security_solution/cypress/screens/common/rule_actions.ts @@ -41,3 +41,20 @@ export const INDEX_SELECTOR = "[data-test-subj='.index-siem-ActionTypeSelectOpti export const actionFormSelector = (position: number) => `[data-test-subj="alertActionAccordion-${position}"]`; + +export const ACTIONS_SUMMARY_BUTTON = '[data-test-subj="summaryOrPerRuleSelect"]'; + +export const ACTIONS_NOTIFY_WHEN_BUTTON = '[data-test-subj="notifyWhenSelect"]'; + +export const ACTIONS_NOTIFY_PER_RULE_RUN_BUTTON = '[data-test-subj="onActiveAlert"]'; + +export const ACTIONS_NOTIFY_CUSTOM_FREQUENCY_BUTTON = '[data-test-subj="onThrottleInterval"]'; + +export const ACTIONS_THROTTLE_INPUT = '[data-test-subj="throttleInput"]'; + +export const ACTIONS_THROTTLE_UNIT_INPUT = '[data-test-subj="throttleUnitInput"]'; + +export const ACTIONS_SUMMARY_ALERT_BUTTON = '[data-test-subj="actionNotifyWhen-option-summary"]'; + +export const ACTIONS_SUMMARY_FOR_EACH_ALERT_BUTTON = + '[data-test-subj="actionNotifyWhen-option-for_each"]'; 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 a0cccb508ed66..b248cf06e1a0d 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 @@ -13,9 +13,6 @@ export const ABOUT_EDIT_TAB = '[data-test-subj="edit-rule-about-tab"]'; export const ACTIONS_EDIT_TAB = '[data-test-subj="edit-rule-actions-tab"]'; -export const ACTIONS_THROTTLE_INPUT = - '[data-test-subj="stepRuleActions"] [data-test-subj="select"]'; - export const ADD_FALSE_POSITIVE_BTN = '[data-test-subj="detectionEngineStepAboutRuleFalsePositives"] .euiButtonEmpty__text'; diff --git a/x-pack/plugins/security_solution/cypress/screens/document_expandable_flyout.ts b/x-pack/plugins/security_solution/cypress/screens/document_expandable_flyout.ts index 2f07705ab18d4..dafd16932d194 100644 --- a/x-pack/plugins/security_solution/cypress/screens/document_expandable_flyout.ts +++ b/x-pack/plugins/security_solution/cypress/screens/document_expandable_flyout.ts @@ -73,6 +73,10 @@ import { ENTITIES_VIEW_ALL_BUTTON_TEST_ID, VISUALIZATIONS_SECTION_HEADER_TEST_ID, ANALYZER_TREE_TEST_ID, + INSIGHTS_THREAT_INTELLIGENCE_VALUE_TEST_ID, + INSIGHTS_THREAT_INTELLIGENCE_VIEW_ALL_BUTTON_TEST_ID, + INSIGHTS_THREAT_INTELLIGENCE_TITLE_TEST_ID, + INSIGHTS_THREAT_INTELLIGENCE_CONTENT_TEST_ID, } from '../../public/flyout/right/components/test_ids'; import { getClassSelector, @@ -135,6 +139,8 @@ export const DOCUMENT_DETAILS_FLYOUT_VISUALIZE_TAB_SESSION_VIEW_BUTTON = getData ); export const DOCUMENT_DETAILS_FLYOUT_VISUALIZE_TAB_SESSION_VIEW_CONTENT = getDataTestSubjectSelector(SESSION_VIEW_TEST_ID); +export const DOCUMENT_DETAILS_FLYOUT_VISUALIZE_TAB_SESSION_VIEW_NO_DATA = + getDataTestSubjectSelector('sessionView:sessionViewProcessEventsEmpty'); export const DOCUMENT_DETAILS_FLYOUT_VISUALIZE_TAB_GRAPH_ANALYZER_BUTTON = getDataTestSubjectSelector(VISUALIZE_TAB_GRAPH_ANALYZER_BUTTON_TEST_ID); export const DOCUMENT_DETAILS_FLYOUT_VISUALIZE_TAB_GRAPH_ANALYZER_CONTENT = @@ -301,6 +307,14 @@ export const DOCUMENT_DETAILS_FLYOUT_OVERVIEW_TAB_INSIGHTS_ENTITY_PANEL_HEADER = getDataTestSubjectSelector(ENTITY_PANEL_HEADER_TEST_ID); export const DOCUMENT_DETAILS_FLYOUT_OVERVIEW_TAB_INSIGHTS_ENTITY_PANEL_CONTENT = getDataTestSubjectSelector(ENTITY_PANEL_CONTENT_TEST_ID); +export const DOCUMENT_DETAILS_FLYOUT_OVERVIEW_TAB_INSIGHTS_THREAT_INTELLIGENCE_HEADER = + getDataTestSubjectSelector(INSIGHTS_THREAT_INTELLIGENCE_TITLE_TEST_ID); +export const DOCUMENT_DETAILS_FLYOUT_OVERVIEW_TAB_INSIGHTS_THREAT_INTELLIGENCE_CONTENT = + getDataTestSubjectSelector(INSIGHTS_THREAT_INTELLIGENCE_CONTENT_TEST_ID); +export const DOCUMENT_DETAILS_FLYOUT_OVERVIEW_TAB_INSIGHTS_THREAT_INTELLIGENCE_VALUES = + getDataTestSubjectSelector(INSIGHTS_THREAT_INTELLIGENCE_VALUE_TEST_ID); +export const DOCUMENT_DETAILS_FLYOUT_OVERVIEW_TAB_INSIGHTS_THREAT_INTELLIGENCE_VIEW_ALL_BUTTON = + getDataTestSubjectSelector(INSIGHTS_THREAT_INTELLIGENCE_VIEW_ALL_BUTTON_TEST_ID); export const DOCUMENT_DETAILS_FLYOUT_OVERVIEW_TAB_VISUALIZATIONS_SECTION_HEADER = getDataTestSubjectSelector(VISUALIZATIONS_SECTION_HEADER_TEST_ID); @@ -333,3 +347,6 @@ export const DOCUMENT_DETAILS_FLYOUT_TABLE_TAB_ROW_CELL_ADD_TO_TIMELINE = getDataTestSubjectSelector('actionItem-security-detailsFlyout-cellActions-addToTimeline'); export const DOCUMENT_DETAILS_FLYOUT_TABLE_TAB_ROW_CELL_COPY_TO_CLIPBOARD = getDataTestSubjectSelector('actionItem-security-detailsFlyout-cellActions-copyToClipboard'); + +export const DOCUMENT_DETAILS_FLYOUT_CLOSE_BUTTON = + getDataTestSubjectSelector('euiFlyoutCloseButton'); diff --git a/x-pack/plugins/security_solution/cypress/screens/exceptions.ts b/x-pack/plugins/security_solution/cypress/screens/exceptions.ts index 100ab6f91ab1d..6a58105292eff 100644 --- a/x-pack/plugins/security_solution/cypress/screens/exceptions.ts +++ b/x-pack/plugins/security_solution/cypress/screens/exceptions.ts @@ -55,7 +55,14 @@ export const EXCEPTIONS_TABLE_LINK_RULES_BTN = export const EXCEPTIONS_TABLE_EXPORT_MODAL_BTN = '[data-test-subj="sharedListOverflowCardActionItemExport"]'; -export const EXCEPTIONS_TABLE_EXPORT_CONFIRM_BTN = '[data-test-subj="confirmModalConfirmButton"]'; +export const EXCEPTIONS_TABLE_DUPLICATE_BTN = + '[data-test-subj="sharedListOverflowCardActionItemDuplicate"]'; + +export const EXCEPTIONS_TABLE_EXPIRED_EXCEPTION_ITEMS_MODAL_CONFIRM_BTN = + '[data-test-subj="confirmModalConfirmButton"]'; + +export const INCLUDE_EXPIRED_EXCEPTION_ITEMS_SWITCH = + '[data-test-subj="includeExpiredExceptionsConfirmationModalSwitch"]'; export const EXCEPTIONS_TABLE_SEARCH_CLEAR = '[data-test-subj="allExceptionListsPanel"] button.euiFormControlLayoutClearButton'; @@ -179,3 +186,5 @@ export const EXCEPTIONS_LIST_EDIT_DETAILS_SAVE_BTN = '[data-test-subj="editModal export const EXCEPTIONS_LIST_DETAILS_HEADER = '[data-test-subj="exceptionListManagementPageHeader"]'; + +export const EXCEPTION_LIST_DETAILS_CARD_ITEM_NAME = '[data-test-subj="exceptionItemCardHeader"]'; 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 5487c5be0aded..4abb1cad40d05 100644 --- a/x-pack/plugins/security_solution/cypress/screens/rule_details.ts +++ b/x-pack/plugins/security_solution/cypress/screens/rule_details.ts @@ -39,6 +39,12 @@ export const DETAILS_TITLE = '.euiDescriptionList__title'; export const EXCEPTIONS_TAB = 'a[data-test-subj="navigation-rule_exceptions"]'; +export const EXCEPTIONS_TAB_EXPIRED_FILTER = '[data-test-subj="expired"]'; + +export const EXCEPTIONS_TAB_ACTIVE_FILTER = '[data-test-subj="active"]'; + +export const EXCEPTIONS_ITEM_CONTAINER = '[data-test-subj="exceptionsContainer"]'; + export const FALSE_POSITIVES_DETAILS = 'False positive examples'; export const INDEX_PATTERNS_DETAILS = 'Index patterns'; diff --git a/x-pack/plugins/security_solution/cypress/screens/rules_bulk_edit.ts b/x-pack/plugins/security_solution/cypress/screens/rules_bulk_edit.ts index 9546b5da8ad8d..7a36c7fd7c74e 100644 --- a/x-pack/plugins/security_solution/cypress/screens/rules_bulk_edit.ts +++ b/x-pack/plugins/security_solution/cypress/screens/rules_bulk_edit.ts @@ -66,9 +66,6 @@ export const UPDATE_SCHEDULE_LOOKBACK_INPUT = export const UPDATE_SCHEDULE_TIME_UNIT_SELECT = '[data-test-subj="timeType"]'; -export const RULES_BULK_EDIT_ACTIONS_THROTTLE_INPUT = - '[data-test-subj="bulkEditRulesRuleActionThrottle"] [data-test-subj="select"]'; - export const RULES_BULK_EDIT_ACTIONS_INFO = '[data-test-subj="bulkEditRulesRuleActionInfo"]'; export const RULES_BULK_EDIT_ACTIONS_WARNING = '[data-test-subj="bulkEditRulesRuleActionsWarning"]'; diff --git a/x-pack/plugins/security_solution/cypress/screens/table_pagination.ts b/x-pack/plugins/security_solution/cypress/screens/table_pagination.ts index e52194632954b..634cd6af9f0d1 100644 --- a/x-pack/plugins/security_solution/cypress/screens/table_pagination.ts +++ b/x-pack/plugins/security_solution/cypress/screens/table_pagination.ts @@ -19,3 +19,5 @@ export const TABLE_FIRST_PAGE = tablePageSelector(1); export const TABLE_SECOND_PAGE = tablePageSelector(2); export const TABLE_SORT_COLUMN_BTN = '[data-test-subj="tableHeaderSortButton"]'; + +export const TABLE_SEARCH_BAR = '[data-test-subj="search-bar"]'; diff --git a/x-pack/plugins/security_solution/cypress/tasks/alerts_detection_rules.ts b/x-pack/plugins/security_solution/cypress/tasks/alerts_detection_rules.ts index f17fbac688f9d..3dd0f5d563597 100644 --- a/x-pack/plugins/security_solution/cypress/tasks/alerts_detection_rules.ts +++ b/x-pack/plugins/security_solution/cypress/tasks/alerts_detection_rules.ts @@ -62,6 +62,9 @@ import { DISABLED_RULES_BTN, REFRESH_RULES_TABLE_BUTTON, RULE_LAST_RUN, + DUPLICATE_WITHOUT_EXCEPTIONS_OPTION, + DUPLICATE_WITH_EXCEPTIONS_OPTION, + DUPLICATE_WITH_EXCEPTIONS_WITHOUT_EXPIRED_OPTION, } from '../screens/alerts_detection_rules'; import type { RULES_MONITORING_TABLE } from '../screens/alerts_detection_rules'; import { EUI_CHECKBOX } from '../screens/common/controls'; @@ -145,10 +148,27 @@ export const deleteRuleFromDetailsPage = () => { .should(($el) => expect($el).to.be.not.visible); }; -export const duplicateSelectedRules = () => { +export const duplicateSelectedRulesWithoutExceptions = () => { cy.log('Duplicate selected rules'); cy.get(BULK_ACTIONS_BTN).click({ force: true }); cy.get(DUPLICATE_RULE_BULK_BTN).click(); + cy.get(DUPLICATE_WITHOUT_EXCEPTIONS_OPTION).click(); + cy.get(CONFIRM_DUPLICATE_RULE).click(); +}; + +export const duplicateSelectedRulesWithExceptions = () => { + cy.log('Duplicate selected rules'); + cy.get(BULK_ACTIONS_BTN).click({ force: true }); + cy.get(DUPLICATE_RULE_BULK_BTN).click(); + cy.get(DUPLICATE_WITH_EXCEPTIONS_OPTION).click(); + cy.get(CONFIRM_DUPLICATE_RULE).click(); +}; + +export const duplicateSelectedRulesWithNonExpiredExceptions = () => { + cy.log('Duplicate selected rules'); + cy.get(BULK_ACTIONS_BTN).click({ force: true }); + cy.get(DUPLICATE_RULE_BULK_BTN).click(); + cy.get(DUPLICATE_WITH_EXCEPTIONS_WITHOUT_EXPIRED_OPTION).click(); cy.get(CONFIRM_DUPLICATE_RULE).click(); }; diff --git a/x-pack/plugins/security_solution/cypress/tasks/api_calls/exceptions.ts b/x-pack/plugins/security_solution/cypress/tasks/api_calls/exceptions.ts index fd070cfcda55e..61c5a0aa8efc3 100644 --- a/x-pack/plugins/security_solution/cypress/tasks/api_calls/exceptions.ts +++ b/x-pack/plugins/security_solution/cypress/tasks/api_calls/exceptions.ts @@ -5,7 +5,7 @@ * 2.0. */ -import type { ExceptionList, ExceptionListItem } from '../../objects/exception'; +import type { ExceptionList, ExceptionListItem, RuleExceptionItem } from '../../objects/exception'; export const createEndpointExceptionList = () => cy.request({ @@ -59,6 +59,18 @@ export const createExceptionListItem = ( value: ['some host', 'another host'], }, ], + expire_time: exceptionListItem?.expire_time, + }, + headers: { 'kbn-xsrf': 'cypress-creds' }, + failOnStatusCode: false, + }); + +export const createRuleExceptionItem = (ruleId: string, exceptionListItems: RuleExceptionItem[]) => + cy.request({ + method: 'POST', + url: `/api/detection_engine/rules/${ruleId}/exceptions`, + body: { + items: exceptionListItems, }, headers: { 'kbn-xsrf': 'cypress-creds' }, failOnStatusCode: false, diff --git a/x-pack/plugins/security_solution/cypress/tasks/common/rule_actions.ts b/x-pack/plugins/security_solution/cypress/tasks/common/rule_actions.ts index 2c289eea0f736..a9a45780567ae 100644 --- a/x-pack/plugins/security_solution/cypress/tasks/common/rule_actions.ts +++ b/x-pack/plugins/security_solution/cypress/tasks/common/rule_actions.ts @@ -22,6 +22,15 @@ import { EMAIL_CONNECTOR_PASSWORD_INPUT, FORM_VALIDATION_ERROR, JSON_EDITOR, + ACTIONS_SUMMARY_BUTTON, + ACTIONS_NOTIFY_WHEN_BUTTON, + ACTIONS_THROTTLE_INPUT, + ACTIONS_THROTTLE_UNIT_INPUT, + ACTIONS_SUMMARY_ALERT_BUTTON, + ACTIONS_SUMMARY_FOR_EACH_ALERT_BUTTON, + ACTIONS_NOTIFY_CUSTOM_FREQUENCY_BUTTON, + actionFormSelector, + ACTIONS_NOTIFY_PER_RULE_RUN_BUTTON, } from '../../screens/common/rule_actions'; import { COMBO_BOX_INPUT, COMBO_BOX_SELECTION } from '../../screens/common/controls'; import type { EmailConnector, IndexConnector } from '../../objects/connector'; @@ -84,3 +93,79 @@ export const fillIndexConnectorForm = (connector: IndexConnector = getIndexConne parseSpecialCharSequences: false, }); }; + +export interface RuleActionCustomFrequency { + throttle?: number; + throttleUnit?: 's' | 'm' | 'h' | 'd'; +} + +export const pickSummaryOfAlertsOption = (index = 0) => { + const form = cy.get(actionFormSelector(index)); + form.within(() => { + cy.get(ACTIONS_SUMMARY_BUTTON).click(); + }); + cy.get(ACTIONS_SUMMARY_ALERT_BUTTON).click(); +}; +export const pickForEachAlertOption = (index = 0) => { + const form = cy.get(actionFormSelector(index)); + form.within(() => { + cy.get(ACTIONS_SUMMARY_BUTTON).click(); + }); + cy.get(ACTIONS_SUMMARY_FOR_EACH_ALERT_BUTTON).click(); +}; + +export const pickCustomFrequencyOption = ( + { throttle = 1, throttleUnit = 'h' }: RuleActionCustomFrequency, + index = 0 +) => { + const form = cy.get(actionFormSelector(index)); + form.within(() => { + cy.get(ACTIONS_NOTIFY_WHEN_BUTTON).click(); + }); + cy.get(ACTIONS_NOTIFY_CUSTOM_FREQUENCY_BUTTON).click(); + form.within(() => { + cy.get(ACTIONS_THROTTLE_INPUT).type(`{selectAll}${throttle}`); + cy.get(ACTIONS_THROTTLE_UNIT_INPUT).select(throttleUnit); + }); +}; + +export const pickPerRuleRunFrequencyOption = (index = 0) => { + const form = cy.get(actionFormSelector(index)); + form.within(() => { + cy.get(ACTIONS_NOTIFY_WHEN_BUTTON).click(); + }); + cy.get(ACTIONS_NOTIFY_PER_RULE_RUN_BUTTON).click(); +}; + +export const assertSelectedSummaryOfAlertsOption = (index = 0) => { + const form = cy.get(actionFormSelector(index)); + form.within(() => { + cy.get(ACTIONS_SUMMARY_BUTTON).should('have.text', 'Summary of alerts'); + }); +}; + +export const assertSelectedForEachAlertOption = (index = 0) => { + const form = cy.get(actionFormSelector(index)); + form.within(() => { + cy.get(ACTIONS_SUMMARY_BUTTON).should('have.text', 'For each alert'); + }); +}; + +export const assertSelectedCustomFrequencyOption = ( + { throttle = 1, throttleUnit = 'h' }: RuleActionCustomFrequency, + index = 0 +) => { + const form = cy.get(actionFormSelector(index)); + form.within(() => { + cy.get(ACTIONS_NOTIFY_WHEN_BUTTON).should('have.text', 'Custom frequency'); + cy.get(ACTIONS_THROTTLE_INPUT).should('have.value', throttle); + cy.get(ACTIONS_THROTTLE_UNIT_INPUT).should('have.value', throttleUnit); + }); +}; + +export const assertSelectedPerRuleRunFrequencyOption = (index = 0) => { + const form = cy.get(actionFormSelector(index)); + form.within(() => { + cy.get(ACTIONS_NOTIFY_WHEN_BUTTON).should('have.text', 'Per rule run'); + }); +}; 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 b8274ed33c120..1f3f051c6f474 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 @@ -99,7 +99,6 @@ import { NEW_TERMS_HISTORY_SIZE, NEW_TERMS_HISTORY_TIME_TYPE, NEW_TERMS_INPUT_AREA, - ACTIONS_THROTTLE_INPUT, CONTINUE_BUTTON, CREATE_WITHOUT_ENABLING_BTN, RULE_INDICES, @@ -407,7 +406,6 @@ export const fillFrom = (from: RuleIntervalFrom = ruleFields.ruleIntervalFrom) = }; export const fillRuleAction = (actions: Actions) => { - cy.get(ACTIONS_THROTTLE_INPUT).select(actions.throttle); actions.connectors.forEach((connector) => { switch (connector.type) { case 'index': diff --git a/x-pack/plugins/security_solution/cypress/tasks/edit_rule.ts b/x-pack/plugins/security_solution/cypress/tasks/edit_rule.ts index a016691328ffd..42d5619c28a67 100644 --- a/x-pack/plugins/security_solution/cypress/tasks/edit_rule.ts +++ b/x-pack/plugins/security_solution/cypress/tasks/edit_rule.ts @@ -6,7 +6,6 @@ */ import { BACK_TO_RULE_DETAILS, EDIT_SUBMIT_BUTTON } from '../screens/edit_rule'; -import { ACTIONS_THROTTLE_INPUT } from '../screens/create_new_rule'; export const saveEditedRule = () => { cy.get(EDIT_SUBMIT_BUTTON).should('exist').click({ force: true }); @@ -17,7 +16,3 @@ export const goBackToRuleDetails = () => { cy.get(BACK_TO_RULE_DETAILS).should('exist').click(); cy.get(BACK_TO_RULE_DETAILS).should('not.exist'); }; - -export const assertSelectedActionFrequency = (frequency: string) => { - cy.get(ACTIONS_THROTTLE_INPUT).find('option:selected').should('have.text', frequency); -}; diff --git a/x-pack/plugins/security_solution/cypress/tasks/exceptions.ts b/x-pack/plugins/security_solution/cypress/tasks/exceptions.ts index e3e0d76ddd26a..c40176cd2eda4 100644 --- a/x-pack/plugins/security_solution/cypress/tasks/exceptions.ts +++ b/x-pack/plugins/security_solution/cypress/tasks/exceptions.ts @@ -28,8 +28,24 @@ import { EXCEPTION_FIELD_MAPPING_CONFLICTS_TOOLTIP, EXCEPTION_FIELD_MAPPING_CONFLICTS_ACCORDION_ICON, EXCEPTION_FIELD_MAPPING_CONFLICTS_DESCRIPTION, + EXCEPTION_ITEM_VIEWER_CONTAINER, } from '../screens/exceptions'; +export const assertNumberOfExceptionItemsExists = (numberOfItems: number) => { + cy.get(EXCEPTION_ITEM_VIEWER_CONTAINER).should('have.length', numberOfItems); +}; + +export const expectToContainItem = (container: string, itemName: string) => { + cy.log(`Expecting exception items table to contain '${itemName}'`); + cy.get(container).should('include.text', itemName); +}; + +export const assertExceptionItemsExists = (container: string, itemNames: string[]) => { + for (const itemName of itemNames) { + expectToContainItem(container, itemName); + } +}; + export const addExceptionEntryFieldValueOfItemX = ( field: string, itemIndex = 0, 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 0f7a8e60e8c45..c9117751a210e 100644 --- a/x-pack/plugins/security_solution/cypress/tasks/exceptions_table.ts +++ b/x-pack/plugins/security_solution/cypress/tasks/exceptions_table.ts @@ -14,7 +14,7 @@ import { EXCEPTIONS_TABLE_MODAL_CONFIRM_BTN, EXCEPTIONS_TABLE_EXPORT_MODAL_BTN, EXCEPTIONS_OVERFLOW_ACTIONS_BTN, - EXCEPTIONS_TABLE_EXPORT_CONFIRM_BTN, + EXCEPTIONS_TABLE_EXPIRED_EXCEPTION_ITEMS_MODAL_CONFIRM_BTN, MANAGE_EXCEPTION_CREATE_BUTTON_MENU, MANAGE_EXCEPTION_CREATE_LIST_BUTTON, CREATE_SHARED_EXCEPTION_LIST_NAME_INPUT, @@ -26,12 +26,17 @@ import { EXCEPTIONS_LIST_MANAGEMENT_EDIT_MODAL_DESCRIPTION_INPUT, EXCEPTIONS_LIST_EDIT_DETAILS_SAVE_BTN, EXCEPTIONS_LIST_DETAILS_HEADER, + EXCEPTIONS_TABLE_DUPLICATE_BTN, + EXCEPTIONS_TABLE_LIST_NAME, + INCLUDE_EXPIRED_EXCEPTION_ITEMS_SWITCH, + EXCEPTION_LIST_DETAILS_CARD_ITEM_NAME, exceptionsTableListManagementListContainerByListId, EXCEPTIONS_TABLE_LINK_RULES_BTN, RULE_ACTION_LINK_RULE_SWITCH, LINKED_RULES_BADGE, MANAGE_RULES_SAVE, } from '../screens/exceptions'; +import { assertExceptionItemsExists } from './exceptions'; export const clearSearchSelection = () => { cy.get(EXCEPTIONS_TABLE_SEARCH_CLEAR).first().click(); @@ -46,7 +51,7 @@ export const exportExceptionList = (listId: string) => { .find(EXCEPTIONS_OVERFLOW_ACTIONS_BTN) .click(); cy.get(EXCEPTIONS_TABLE_EXPORT_MODAL_BTN).first().click(); - cy.get(EXCEPTIONS_TABLE_EXPORT_CONFIRM_BTN).first().click(); + cy.get(EXCEPTIONS_TABLE_EXPIRED_EXCEPTION_ITEMS_MODAL_CONFIRM_BTN).first().click(); }; export const assertNumberLinkedRules = (listId: string, numberOfRulesAsString: string) => { @@ -65,8 +70,10 @@ export const linkRulesToExceptionList = (listId: string, ruleSwitch: number = 0) cy.get(MANAGE_RULES_SAVE).first().click(); }; -export const deleteExceptionListWithoutRuleReference = () => { - cy.get(EXCEPTIONS_OVERFLOW_ACTIONS_BTN).first().click(); +export const deleteExceptionListWithoutRuleReferenceByListId = (listId: string) => { + cy.get(exceptionsTableListManagementListContainerByListId(listId)) + .find(EXCEPTIONS_OVERFLOW_ACTIONS_BTN) + .click(); cy.get(EXCEPTIONS_TABLE_DELETE_BTN).first().click(); cy.get(EXCEPTIONS_TABLE_MODAL).should('exist'); cy.get(EXCEPTIONS_TABLE_MODAL_CONFIRM_BTN).first().click(); @@ -117,10 +124,42 @@ export const createSharedExceptionList = ( } }; +export const expectToContainList = (listName: string) => { + cy.log(`Expecting exception lists table to contain '${listName}'`); + cy.get(EXCEPTIONS_TABLE_LIST_NAME).should('include.text', listName); +}; + +export const assertExceptionListsExists = (listNames: string[]) => { + for (const listName of listNames) { + expectToContainList(listName); + } +}; + +export const duplicateSharedExceptionListFromListsManagementPageByListId = ( + listId: string, + includeExpired: boolean +) => { + cy.log(`Duplicating list with list_id: '${listId}'`); + cy.get(exceptionsTableListManagementListContainerByListId(listId)) + .find(EXCEPTIONS_OVERFLOW_ACTIONS_BTN) + .click(); + cy.get(EXCEPTIONS_TABLE_DUPLICATE_BTN).first().click(); + if (!includeExpired) { + cy.get(INCLUDE_EXPIRED_EXCEPTION_ITEMS_SWITCH).first().click(); + } + cy.get(EXCEPTIONS_TABLE_EXPIRED_EXCEPTION_ITEMS_MODAL_CONFIRM_BTN).first().click(); +}; + export const waitForExceptionListDetailToBeLoaded = () => { cy.get(EXCEPTIONS_LIST_DETAILS_HEADER).should('exist'); }; +export const findSharedExceptionListItemsByName = (listName: string, itemNames: string[]) => { + cy.contains(listName).click(); + waitForExceptionListDetailToBeLoaded(); + assertExceptionItemsExists(EXCEPTION_LIST_DETAILS_CARD_ITEM_NAME, itemNames); +}; + export const editExceptionLisDetails = ({ name, description, diff --git a/x-pack/plugins/security_solution/cypress/tasks/rule_details.ts b/x-pack/plugins/security_solution/cypress/tasks/rule_details.ts index d30cbaf06e17b..d82e29e493ea3 100644 --- a/x-pack/plugins/security_solution/cypress/tasks/rule_details.ts +++ b/x-pack/plugins/security_solution/cypress/tasks/rule_details.ts @@ -29,6 +29,8 @@ import { ENDPOINT_EXCEPTIONS_TAB, EDIT_RULE_SETTINGS_LINK, BACK_TO_RULES_TABLE, + EXCEPTIONS_TAB_EXPIRED_FILTER, + EXCEPTIONS_TAB_ACTIVE_FILTER, } from '../screens/rule_details'; import { addExceptionConditions, @@ -105,6 +107,11 @@ export const goToExceptionsTab = () => { cy.get(EXCEPTIONS_TAB).click(); }; +export const viewExpiredExceptionItems = () => { + cy.get(EXCEPTIONS_TAB_EXPIRED_FILTER).click(); + cy.get(EXCEPTIONS_TAB_ACTIVE_FILTER).click(); +}; + export const goToEndpointExceptionsTab = () => { cy.get(ENDPOINT_EXCEPTIONS_TAB).should('exist'); cy.get(ENDPOINT_EXCEPTIONS_TAB).click(); diff --git a/x-pack/plugins/security_solution/cypress/tasks/rules_bulk_edit.ts b/x-pack/plugins/security_solution/cypress/tasks/rules_bulk_edit.ts index b2203d1b1202a..b4bf088bafd6a 100644 --- a/x-pack/plugins/security_solution/cypress/tasks/rules_bulk_edit.ts +++ b/x-pack/plugins/security_solution/cypress/tasks/rules_bulk_edit.ts @@ -39,7 +39,6 @@ import { UPDATE_SCHEDULE_LOOKBACK_INPUT, RULES_BULK_EDIT_SCHEDULES_WARNING, RULES_BULK_EDIT_OVERWRITE_ACTIONS_CHECKBOX, - RULES_BULK_EDIT_ACTIONS_THROTTLE_INPUT, } from '../screens/rules_bulk_edit'; import { SCHEDULE_DETAILS } from '../screens/rule_details'; @@ -292,7 +291,3 @@ export const assertRuleScheduleValues = ({ interval, lookback }: RuleSchedule) = cy.get('dd').eq(1).should('contain.text', lookback); }); }; - -export const pickActionFrequency = (frequency: string) => { - cy.get(RULES_BULK_EDIT_ACTIONS_THROTTLE_INPUT).select(frequency); -}; diff --git a/x-pack/plugins/security_solution/cypress/tasks/table_pagination.ts b/x-pack/plugins/security_solution/cypress/tasks/table_pagination.ts index ae62ca290e418..2e59383b39645 100644 --- a/x-pack/plugins/security_solution/cypress/tasks/table_pagination.ts +++ b/x-pack/plugins/security_solution/cypress/tasks/table_pagination.ts @@ -10,6 +10,7 @@ import { rowsPerPageSelector, tablePageSelector, TABLE_PER_PAGE_POPOVER_BTN, + TABLE_SEARCH_BAR, TABLE_SORT_COLUMN_BTN, } from '../screens/table_pagination'; @@ -33,6 +34,14 @@ export const setRowsPerPageTo = (rowsCount: number) => { .should('not.exist'); }; +export const searchByTitle = (title: string) => { + cy.get(LOADING_SPINNER).should('not.exist'); + cy.get(TABLE_PER_PAGE_POPOVER_BTN).should('exist'); + cy.get(TABLE_SEARCH_BAR).click({ force: true }); + // EuiSearchBox needs the "search" event to be triggered, {enter} doesn't work + cy.get(TABLE_SEARCH_BAR).type(`"${title}"`).trigger('search'); +}; + export const expectRowsPerPage = (rowsCount: number) => { cy.get(TABLE_PER_PAGE_POPOVER_BTN).contains(`Rows per page: ${rowsCount}`); }; diff --git a/x-pack/plugins/security_solution/public/actions/add_to_timeline/cell_action/add_to_new_timeline.test.ts b/x-pack/plugins/security_solution/public/actions/add_to_timeline/cell_action/add_to_new_timeline.test.ts new file mode 100644 index 0000000000000..7f3ed646da749 --- /dev/null +++ b/x-pack/plugins/security_solution/public/actions/add_to_timeline/cell_action/add_to_new_timeline.test.ts @@ -0,0 +1,247 @@ +/* + * 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 { SecurityAppStore } from '../../../common/store/types'; +import type { DataProvider } from '../../../../common/types'; +import { TimelineId } from '../../../../common/types'; +import { addProvider } from '../../../timelines/store/timeline/actions'; +import { createAddToNewTimelineCellActionFactory, getToastMessage } from './add_to_new_timeline'; +import type { CellActionExecutionContext } from '@kbn/cell-actions'; +import { GEO_FIELD_TYPE } from '../../../timelines/components/timeline/body/renderers/constants'; +import { createStartServicesMock } from '../../../common/lib/kibana/kibana_react.mock'; +import { timelineActions } from '../../../timelines/store/timeline'; + +const services = createStartServicesMock(); +const mockWarningToast = services.notifications.toasts.addWarning; + +const mockDispatch = jest.fn(); +const store = { + dispatch: mockDispatch, +} as unknown as SecurityAppStore; + +const value = 'the-value'; + +const context = { + field: { name: 'user.name', value, type: 'text' }, +} as CellActionExecutionContext; + +const defaultAddProviderAction = { + type: addProvider.type, + payload: { + id: TimelineId.active, + providers: [ + { + and: [], + enabled: true, + excluded: false, + id: 'event-field-default-timeline-1-user_name-0-the-value', + kqlQuery: '', + name: 'user.name', + queryMatch: { + field: 'user.name', + operator: ':', + value: 'the-value', + }, + }, + ], + }, +}; + +describe('createAddToNewTimelineCellAction', () => { + const addToTimelineCellActionFactory = createAddToNewTimelineCellActionFactory({ + store, + services, + }); + const addToTimelineAction = addToTimelineCellActionFactory({ id: 'testAddToTimeline', order: 1 }); + + beforeEach(() => { + jest.clearAllMocks(); + }); + + it('should return display name', () => { + expect(addToTimelineAction.getDisplayName(context)).toEqual('Investigate in timeline'); + }); + + it('should return icon type', () => { + expect(addToTimelineAction.getIconType(context)).toEqual('timeline'); + }); + + describe('isCompatible', () => { + it('should return true if everything is okay', async () => { + expect(await addToTimelineAction.isCompatible(context)).toEqual(true); + }); + it('should return false if field not allowed', async () => { + expect( + await addToTimelineAction.isCompatible({ + ...context, + field: { ...context.field, name: 'signal.reason' }, + }) + ).toEqual(false); + }); + }); + + describe('execute', () => { + it('should execute normally', async () => { + await addToTimelineAction.execute(context); + expect(mockDispatch).toHaveBeenCalledWith(defaultAddProviderAction); + expect(mockWarningToast).not.toHaveBeenCalled(); + }); + + it('should show warning if no provider added', async () => { + await addToTimelineAction.execute({ + ...context, + field: { + ...context.field, + type: GEO_FIELD_TYPE, + }, + }); + expect(mockDispatch).not.toHaveBeenCalled(); + expect(mockWarningToast).toHaveBeenCalled(); + }); + + describe('should execute correctly when negateFilters is provided', () => { + it('should not exclude if negateFilters is false', async () => { + await addToTimelineAction.execute({ + ...context, + metadata: { + negateFilters: false, + }, + }); + expect(mockDispatch).toHaveBeenCalledWith(defaultAddProviderAction); + expect(mockWarningToast).not.toHaveBeenCalled(); + }); + + it('should exclude if negateFilters is true', async () => { + await addToTimelineAction.execute({ + ...context, + metadata: { + negateFilters: true, + }, + }); + expect(mockDispatch).toHaveBeenCalledWith({ + ...defaultAddProviderAction, + payload: { + ...defaultAddProviderAction.payload, + providers: [{ ...defaultAddProviderAction.payload.providers[0], excluded: true }], + }, + }); + expect(mockWarningToast).not.toHaveBeenCalled(); + }); + }); + + it('should clear the timeline', async () => { + await addToTimelineAction.execute(context); + expect(mockDispatch.mock.calls[0][0].type).toEqual(timelineActions.createTimeline.type); + }); + + it('should add the providers to the timeline', async () => { + await addToTimelineAction.execute({ + ...context, + metadata: { + andFilters: [{ field: 'kibana.alert.severity', value: 'low' }], + }, + }); + + expect(mockDispatch).toBeCalledWith({ + ...defaultAddProviderAction, + payload: { + ...defaultAddProviderAction.payload, + providers: [ + { + ...defaultAddProviderAction.payload.providers[0], + id: 'event-field-default-timeline-1-user_name-0-the-value', + queryMatch: defaultAddProviderAction.payload.providers[0].queryMatch, + and: [ + { + enabled: true, + excluded: false, + id: 'event-field-default-timeline-1-kibana_alert_severity-0-low', + kqlQuery: '', + name: 'kibana.alert.severity', + queryMatch: { + field: 'kibana.alert.severity', + operator: ':', + value: 'low', + }, + and: [], + }, + ], + }, + ], + }, + }); + }); + }); + + describe('getToastMessage', () => { + it('handles empty input', () => { + const result = getToastMessage({ queryMatch: { value: null } } as unknown as DataProvider); + expect(result).toEqual(''); + }); + it('handles array input', () => { + const result = getToastMessage({ + queryMatch: { value: ['hello', 'world'] }, + } as unknown as DataProvider); + expect(result).toEqual('hello, world alerts'); + }); + + it('handles single filter', () => { + const result = getToastMessage({ + queryMatch: { value }, + and: [{ queryMatch: { field: 'kibana.alert.severity', value: 'critical' } }], + } as unknown as DataProvider); + expect(result).toEqual(`critical severity alerts from ${value}`); + }); + + it('handles multiple filters', () => { + const result = getToastMessage({ + queryMatch: { value }, + and: [ + { + queryMatch: { field: 'kibana.alert.workflow_status', value: 'open' }, + }, + { + queryMatch: { field: 'kibana.alert.severity', value: 'critical' }, + }, + ], + } as unknown as DataProvider); + expect(result).toEqual(`open, critical severity alerts from ${value}`); + }); + + it('ignores unrelated filters', () => { + const result = getToastMessage({ + queryMatch: { value }, + and: [ + { + queryMatch: { field: 'kibana.alert.workflow_status', value: 'open' }, + }, + { + queryMatch: { field: 'kibana.alert.severity', value: 'critical' }, + }, + // currently only supporting the above fields + { + queryMatch: { field: 'user.name', value: 'something' }, + }, + ], + } as unknown as DataProvider); + expect(result).toEqual(`open, critical severity alerts from ${value}`); + }); + + it('returns entity only when unrelated filters are passed', () => { + const result = getToastMessage({ + queryMatch: { value }, + and: [{ queryMatch: { field: 'user.name', value: 'something' } }], + } as unknown as DataProvider); + expect(result).toEqual(`${value} alerts`); + }); + + it('returns entity only when no filters are passed', () => { + const result = getToastMessage({ queryMatch: { value }, and: [] } as unknown as DataProvider); + expect(result).toEqual(`${value} alerts`); + }); + }); +}); diff --git a/x-pack/plugins/security_solution/public/actions/add_to_timeline/cell_action/add_to_new_timeline.ts b/x-pack/plugins/security_solution/public/actions/add_to_timeline/cell_action/add_to_new_timeline.ts new file mode 100644 index 0000000000000..886162803bbf1 --- /dev/null +++ b/x-pack/plugins/security_solution/public/actions/add_to_timeline/cell_action/add_to_new_timeline.ts @@ -0,0 +1,117 @@ +/* + * 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 { createCellActionFactory, type CellActionTemplate } from '@kbn/cell-actions'; +import { timelineActions } from '../../../timelines/store/timeline'; +import { addProvider } from '../../../timelines/store/timeline/actions'; +import type { DataProvider } from '../../../../common/types'; +import { TimelineId } from '../../../../common/types'; +import type { SecurityAppStore } from '../../../common/store'; +import { fieldHasCellActions } from '../../utils'; +import { + ADD_TO_NEW_TIMELINE, + ADD_TO_TIMELINE_FAILED_TEXT, + ADD_TO_TIMELINE_FAILED_TITLE, + ADD_TO_TIMELINE_ICON, + ADD_TO_TIMELINE_SUCCESS_TITLE, + ALERTS_COUNT, + SEVERITY, +} from '../constants'; +import { createDataProviders, isValidDataProviderField } from '../data_provider'; +import { SecurityCellActionType } from '../../constants'; +import type { StartServices } from '../../../types'; +import type { SecurityCellAction } from '../../types'; +import { timelineDefaults } from '../../../timelines/store/timeline/defaults'; + +const severityField = 'kibana.alert.severity'; +const statusField = 'kibana.alert.workflow_status'; + +export const getToastMessage = ({ queryMatch: { value }, and = [] }: DataProvider) => { + if (value == null) { + return ''; + } + const fieldValue = Array.isArray(value) ? value.join(', ') : value.toString(); + + const descriptors = and.reduce((msg, { queryMatch }) => { + if (Array.isArray(queryMatch.value)) { + return msg; + } + if (queryMatch.field === severityField) { + msg.push(SEVERITY(queryMatch.value.toString())); + } + if (queryMatch.field === statusField) { + msg.push(queryMatch.value.toString()); + } + return msg; + }, []); + + return ALERTS_COUNT(fieldValue, descriptors.join(', ')); +}; + +export const createAddToNewTimelineCellActionFactory = createCellActionFactory( + ({ + store, + services, + }: { + store: SecurityAppStore; + services: StartServices; + }): CellActionTemplate => { + const { notifications: notificationsService } = services; + + return { + type: SecurityCellActionType.ADD_TO_TIMELINE, + getIconType: () => ADD_TO_TIMELINE_ICON, + getDisplayName: () => ADD_TO_NEW_TIMELINE, + getDisplayNameTooltip: () => ADD_TO_NEW_TIMELINE, + isCompatible: async ({ field }) => + fieldHasCellActions(field.name) && isValidDataProviderField(field.name, field.type), + execute: async ({ field, metadata }) => { + const dataProviders = + createDataProviders({ + contextId: TimelineId.active, + fieldType: field.type, + values: field.value, + field: field.name, + negate: metadata?.negateFilters === true, + }) ?? []; + + for (const andFilter of metadata?.andFilters ?? []) { + const andDataProviders = + createDataProviders({ + contextId: TimelineId.active, + field: andFilter.field, + values: andFilter.value, + }) ?? []; + if (andDataProviders) { + for (const dataProvider of dataProviders) { + dataProvider.and.push(...andDataProviders); + } + } + } + + if (dataProviders.length > 0) { + // clear timeline + store.dispatch( + timelineActions.createTimeline({ + ...timelineDefaults, + id: TimelineId.active, + }) + ); + store.dispatch(addProvider({ id: TimelineId.active, providers: dataProviders })); + notificationsService.toasts.addSuccess({ + title: ADD_TO_TIMELINE_SUCCESS_TITLE(getToastMessage(dataProviders[0])), + }); + } else { + notificationsService.toasts.addWarning({ + title: ADD_TO_TIMELINE_FAILED_TITLE, + text: ADD_TO_TIMELINE_FAILED_TEXT, + }); + } + }, + }; + } +); diff --git a/x-pack/plugins/security_solution/public/actions/add_to_timeline/constants.ts b/x-pack/plugins/security_solution/public/actions/add_to_timeline/constants.ts index c122d7e312e4d..0396cad110367 100644 --- a/x-pack/plugins/security_solution/public/actions/add_to_timeline/constants.ts +++ b/x-pack/plugins/security_solution/public/actions/add_to_timeline/constants.ts @@ -15,6 +15,29 @@ export const ADD_TO_TIMELINE = i18n.translate( defaultMessage: 'Add to timeline', } ); +export const ADD_TO_NEW_TIMELINE = i18n.translate( + 'xpack.securitySolution.actions.cellValue.addToNewTimeline.displayName', + { + defaultMessage: 'Investigate in timeline', + } +); + +export const SEVERITY = (level: string) => + i18n.translate('xpack.securitySolution.actions.addToTimeline.severityLevel', { + values: { level }, + defaultMessage: `{level} severity`, + }); + +export const ALERTS_COUNT = (entity: string, description: string) => + description !== '' + ? i18n.translate('xpack.securitySolution.actions.addToTimeline.descriptiveAlertsCountMessage', { + values: { description, entity }, + defaultMessage: '{description} alerts from {entity}', + }) + : i18n.translate('xpack.securitySolution.actions.addToTimeline.alertsCountMessage', { + values: { entity }, + defaultMessage: '{entity} alerts', + }); export const ADD_TO_TIMELINE_SUCCESS_TITLE = (value: string) => i18n.translate('xpack.securitySolution.actions.addToTimeline.addedFieldMessage', { diff --git a/x-pack/plugins/security_solution/public/actions/add_to_timeline/index.ts b/x-pack/plugins/security_solution/public/actions/add_to_timeline/index.ts index c639dde1e2337..72e6eee17e4d4 100644 --- a/x-pack/plugins/security_solution/public/actions/add_to_timeline/index.ts +++ b/x-pack/plugins/security_solution/public/actions/add_to_timeline/index.ts @@ -6,4 +6,5 @@ */ export { createAddToTimelineCellActionFactory } from './cell_action/add_to_timeline'; +export { createAddToNewTimelineCellActionFactory } from './cell_action/add_to_new_timeline'; export { createAddToTimelineLensAction } from './lens/add_to_timeline'; diff --git a/x-pack/plugins/security_solution/public/actions/constants.ts b/x-pack/plugins/security_solution/public/actions/constants.ts index 94c2601222847..eff71171fbcea 100644 --- a/x-pack/plugins/security_solution/public/actions/constants.ts +++ b/x-pack/plugins/security_solution/public/actions/constants.ts @@ -7,6 +7,7 @@ export enum SecurityCellActionsTrigger { DEFAULT = 'security-default-cellActions', DETAILS_FLYOUT = 'security-detailsFlyout-cellActions', + ALERTS_COUNT = 'security-alertsCount-cellActions', } export enum SecurityCellActionType { diff --git a/x-pack/plugins/security_solution/public/actions/register.ts b/x-pack/plugins/security_solution/public/actions/register.ts index 0fafb561d45a1..bfea8d27163c4 100644 --- a/x-pack/plugins/security_solution/public/actions/register.ts +++ b/x-pack/plugins/security_solution/public/actions/register.ts @@ -6,14 +6,14 @@ */ import { CELL_VALUE_TRIGGER } from '@kbn/embeddable-plugin/public'; -import type { UiActionsStart } from '@kbn/ui-actions-plugin/public'; import type * as H from 'history'; import type { SecurityAppStore } from '../common/store/types'; -import type { StartPlugins, StartServices } from '../types'; +import type { StartServices } from '../types'; import { createFilterInCellActionFactory, createFilterOutCellActionFactory } from './filter'; import { createAddToTimelineLensAction, createAddToTimelineCellActionFactory, + createAddToNewTimelineCellActionFactory, } from './add_to_timeline'; import { createShowTopNCellActionFactory } from './show_top_n'; import { @@ -23,18 +23,20 @@ import { import { createToggleColumnCellActionFactory } from './toggle_column'; import { SecurityCellActionsTrigger } from './constants'; import type { SecurityCellActionName, SecurityCellActions } from './types'; +import { enhanceActionWithTelemetry } from './telemetry'; export const registerUIActions = ( - { uiActions }: StartPlugins, store: SecurityAppStore, history: H.History, services: StartServices ) => { - registerLensActions(uiActions, store); - registerCellActions(uiActions, store, history, services); + registerLensActions(store, services); + registerCellActions(store, history, services); }; -const registerLensActions = (uiActions: UiActionsStart, store: SecurityAppStore) => { +const registerLensActions = (store: SecurityAppStore, services: StartServices) => { + const { uiActions } = services; + const addToTimelineAction = createAddToTimelineLensAction({ store, order: 1 }); uiActions.addTriggerAction(CELL_VALUE_TRIGGER, addToTimelineAction); @@ -43,7 +45,6 @@ const registerLensActions = (uiActions: UiActionsStart, store: SecurityAppStore) }; const registerCellActions = ( - uiActions: UiActionsStart, store: SecurityAppStore, history: H.History, services: StartServices @@ -52,42 +53,61 @@ const registerCellActions = ( filterIn: createFilterInCellActionFactory({ store, services }), filterOut: createFilterOutCellActionFactory({ store, services }), addToTimeline: createAddToTimelineCellActionFactory({ store, services }), + addToNewTimeline: createAddToNewTimelineCellActionFactory({ store, services }), showTopN: createShowTopNCellActionFactory({ store, history, services }), copyToClipboard: createCopyToClipboardCellActionFactory({ services }), toggleColumn: createToggleColumnCellActionFactory({ store }), }; - registerCellActionsTrigger(uiActions, SecurityCellActionsTrigger.DEFAULT, cellActions, [ - 'filterIn', - 'filterOut', - 'addToTimeline', - 'showTopN', - 'copyToClipboard', - ]); + registerCellActionsTrigger({ + triggerId: SecurityCellActionsTrigger.DEFAULT, + cellActions, + actionsOrder: ['filterIn', 'filterOut', 'addToTimeline', 'showTopN', 'copyToClipboard'], + services, + }); + + registerCellActionsTrigger({ + triggerId: SecurityCellActionsTrigger.DETAILS_FLYOUT, + cellActions, + actionsOrder: [ + 'filterIn', + 'filterOut', + 'addToTimeline', + 'toggleColumn', + 'showTopN', + 'copyToClipboard', + ], + services, + }); - registerCellActionsTrigger(uiActions, SecurityCellActionsTrigger.DETAILS_FLYOUT, cellActions, [ - 'filterIn', - 'filterOut', - 'addToTimeline', - 'toggleColumn', - 'showTopN', - 'copyToClipboard', - ]); + registerCellActionsTrigger({ + triggerId: SecurityCellActionsTrigger.ALERTS_COUNT, + cellActions, + actionsOrder: ['addToNewTimeline'], + services, + }); }; -const registerCellActionsTrigger = ( - uiActions: UiActionsStart, - triggerId: SecurityCellActionsTrigger, - cellActions: SecurityCellActions, - actionsOrder: SecurityCellActionName[] -) => { +const registerCellActionsTrigger = ({ + triggerId, + cellActions, + actionsOrder, + services, +}: { + triggerId: SecurityCellActionsTrigger; + cellActions: SecurityCellActions; + actionsOrder: SecurityCellActionName[]; + services: StartServices; +}) => { + const { uiActions } = services; uiActions.registerTrigger({ id: triggerId }); actionsOrder.forEach((actionName, order) => { const actionFactory = cellActions[actionName]; - uiActions.addTriggerAction( - triggerId, - actionFactory({ id: `${triggerId}-${actionName}`, order }) - ); + if (actionFactory) { + const action = actionFactory({ id: `${triggerId}-${actionName}`, order }); + + uiActions.addTriggerAction(triggerId, enhanceActionWithTelemetry(action, services)); + } }); }; diff --git a/x-pack/plugins/security_solution/public/actions/telemetry.test.ts b/x-pack/plugins/security_solution/public/actions/telemetry.test.ts new file mode 100644 index 0000000000000..82a691348a206 --- /dev/null +++ b/x-pack/plugins/security_solution/public/actions/telemetry.test.ts @@ -0,0 +1,45 @@ +/* + * 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 { StartServices } from '../types'; +import { enhanceActionWithTelemetry } from './telemetry'; +import { createAction } from '@kbn/ui-actions-plugin/public'; +import type { CellActionExecutionContext } from '@kbn/cell-actions'; + +const actionId = 'test_action_id'; +const displayName = 'test-actions'; +const fieldName = 'test.user.name'; +const fieldValue = 'test value'; +const metadata = undefined; + +const action = createAction({ + id: actionId, + execute: async () => {}, + getIconType: () => 'test-icon', + getDisplayName: () => displayName, +}); +const context = { + field: { name: fieldName, value: fieldValue, type: 'text' }, + metadata, +} as CellActionExecutionContext; + +describe('enhanceActionWithTelemetry', () => { + it('calls telemetry report when the action is executed', () => { + const telemetry = { reportCellActionClicked: jest.fn() }; + const services = { telemetry } as unknown as StartServices; + + const enhancedAction = enhanceActionWithTelemetry(action, services); + enhancedAction.execute(context); + + expect(telemetry.reportCellActionClicked).toHaveBeenCalledWith({ + displayName, + actionId, + fieldName, + metadata, + }); + }); +}); diff --git a/x-pack/plugins/security_solution/public/actions/telemetry.ts b/x-pack/plugins/security_solution/public/actions/telemetry.ts new file mode 100644 index 0000000000000..c051f11a81271 --- /dev/null +++ b/x-pack/plugins/security_solution/public/actions/telemetry.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. + */ + +import type { CellAction, CellActionExecutionContext } from '@kbn/cell-actions'; +import type { ActionExecutionContext } from '@kbn/ui-actions-plugin/public'; +import type { StartServices } from '../types'; +import type { SecurityCellActionExecutionContext } from './types'; + +export const enhanceActionWithTelemetry = ( + action: CellAction, + services: StartServices +): CellAction => { + const { telemetry } = services; + const { execute, ...rest } = action; + const enhancedExecute = ( + context: ActionExecutionContext + ): Promise => { + telemetry.reportCellActionClicked({ + actionId: rest.id, + displayName: rest.getDisplayName(context), + fieldName: context.field.name, + metadata: context.metadata, + }); + + return execute(context); + }; + + return { ...rest, execute: enhancedExecute }; +}; diff --git a/x-pack/plugins/security_solution/public/actions/types.ts b/x-pack/plugins/security_solution/public/actions/types.ts index da6b2cc3919a9..e0cd6e764f3b8 100644 --- a/x-pack/plugins/security_solution/public/actions/types.ts +++ b/x-pack/plugins/security_solution/public/actions/types.ts @@ -6,6 +6,12 @@ */ import type { CellAction, CellActionExecutionContext, CellActionFactory } from '@kbn/cell-actions'; +import type { QueryOperator } from '../../common/types'; +export interface AndFilter { + field: string; + value: string | string[]; + operator?: QueryOperator; +} export interface SecurityMetadata extends Record { /** @@ -26,6 +32,20 @@ export interface SecurityMetadata extends Record { * and we need all the filtering actions to perform the opposite (negate) operation. */ negateFilters?: boolean; + /** + * `metadata.telemetry` is used by the telemetry service to add context to the event. + */ + telemetry?: { + /** + * It defines which UI component renders the CellActions. + */ + component: string; + }; + /** + * `metadata.andFilters` is used by the addToTimelineAction to add + * an "and" query to the main data provider + */ + andFilters?: AndFilter[]; } export interface SecurityCellActionExecutionContext extends CellActionExecutionContext { @@ -33,13 +53,15 @@ export interface SecurityCellActionExecutionContext extends CellActionExecutionC } export type SecurityCellAction = CellAction; -// All security cell actions names -export type SecurityCellActionName = - | 'filterIn' - | 'filterOut' - | 'addToTimeline' - | 'showTopN' - | 'copyToClipboard' - | 'toggleColumn'; +export interface SecurityCellActions { + filterIn?: CellActionFactory; + filterOut?: CellActionFactory; + addToTimeline?: CellActionFactory; + addToNewTimeline?: CellActionFactory; + showTopN?: CellActionFactory; + copyToClipboard?: CellActionFactory; + toggleColumn?: CellActionFactory; +} -export type SecurityCellActions = Record; +// All security cell actions names +export type SecurityCellActionName = keyof SecurityCellActions; diff --git a/x-pack/plugins/security_solution/public/app/home/template_wrapper/index.tsx b/x-pack/plugins/security_solution/public/app/home/template_wrapper/index.tsx index 264df10831fb7..a70a915e6aed4 100644 --- a/x-pack/plugins/security_solution/public/app/home/template_wrapper/index.tsx +++ b/x-pack/plugins/security_solution/public/app/home/template_wrapper/index.tsx @@ -27,6 +27,7 @@ import { useShowTimeline } from '../../../common/utils/timeline/use_show_timelin import { useShowPagesWithEmptyView } from '../../../common/utils/empty_view/use_show_pages_with_empty_view'; import { useIsPolicySettingsBarVisible } from '../../../management/pages/policy/view/policy_hooks'; import { useIsGroupedNavigationEnabled } from '../../../common/components/navigation/helpers'; +import { useSyncFlyoutStateWithUrl } from '../../../flyout/url/use_sync_flyout_state_with_url'; const NO_DATA_PAGE_MAX_WIDTH = 950; @@ -75,6 +76,8 @@ export const SecuritySolutionTemplateWrapper: React.FC + )} - {}} /> + {}} + handleOnFlyoutClosed={handleFlyoutChangedOrClosed} + /> ); diff --git a/x-pack/plugins/security_solution/public/common/components/ml/__snapshots__/entity.test.tsx.snap b/x-pack/plugins/security_solution/public/common/components/ml/__snapshots__/entity.test.tsx.snap index 941078e41f917..4cc7aad784cc8 100644 --- a/x-pack/plugins/security_solution/public/common/components/ml/__snapshots__/entity.test.tsx.snap +++ b/x-pack/plugins/security_solution/public/common/components/ml/__snapshots__/entity.test.tsx.snap @@ -10,7 +10,7 @@ exports[`entity_draggable renders correctly against snapshot 1`] = ` "value": "entity-value", } } - mode="hover" + mode="hover-down" triggerId="security-default-cellActions" visibleCellActions={5} > diff --git a/x-pack/plugins/security_solution/public/common/components/ml/entity.tsx b/x-pack/plugins/security_solution/public/common/components/ml/entity.tsx index 00a8c0cdb3f39..a50ea1e1f47f5 100644 --- a/x-pack/plugins/security_solution/public/common/components/ml/entity.tsx +++ b/x-pack/plugins/security_solution/public/common/components/ml/entity.tsx @@ -23,7 +23,7 @@ export const EntityComponent: React.FC = ({ entityName, entityValue }) => aggregatable: true, }} triggerId={SecurityCellActionsTrigger.DEFAULT} - mode={CellActionsMode.HOVER} + mode={CellActionsMode.HOVER_DOWN} visibleCellActions={5} > {`${entityName}: "${entityValue}"`} diff --git a/x-pack/plugins/security_solution/public/common/components/ml/score/__snapshots__/score.test.tsx.snap b/x-pack/plugins/security_solution/public/common/components/ml/score/__snapshots__/score.test.tsx.snap index 08e1bbe2bfc80..2d9d6a69af1f0 100644 --- a/x-pack/plugins/security_solution/public/common/components/ml/score/__snapshots__/score.test.tsx.snap +++ b/x-pack/plugins/security_solution/public/common/components/ml/score/__snapshots__/score.test.tsx.snap @@ -10,7 +10,7 @@ exports[`draggable_score renders correctly against snapshot 1`] = ` "value": "du", } } - mode="hover" + mode="hover-down" triggerId="security-default-cellActions" visibleCellActions={5} > @@ -28,7 +28,7 @@ exports[`draggable_score renders correctly against snapshot when the index is no "value": "du", } } - mode="hover" + mode="hover-down" triggerId="security-default-cellActions" visibleCellActions={5} > diff --git a/x-pack/plugins/security_solution/public/common/components/ml/score/score.tsx b/x-pack/plugins/security_solution/public/common/components/ml/score/score.tsx index 3ad058dd2ab33..4e5fc8b29190b 100644 --- a/x-pack/plugins/security_solution/public/common/components/ml/score/score.tsx +++ b/x-pack/plugins/security_solution/public/common/components/ml/score/score.tsx @@ -26,7 +26,7 @@ export const ScoreComponent = ({ return ( { useSyncGlobalQueryString(); useInitSearchBarFromUrlParams(); diff --git a/x-pack/plugins/security_solution/public/common/lib/telemetry/telemetry_client.mock.ts b/x-pack/plugins/security_solution/public/common/lib/telemetry/telemetry_client.mock.ts index 4f2b2bdafe96f..9cd22c469160d 100644 --- a/x-pack/plugins/security_solution/public/common/lib/telemetry/telemetry_client.mock.ts +++ b/x-pack/plugins/security_solution/public/common/lib/telemetry/telemetry_client.mock.ts @@ -15,4 +15,6 @@ export const createTelemetryClientMock = (): jest.Mocked = reportEntityAlertsClicked: jest.fn(), reportEntityRiskFiltered: jest.fn(), reportMLJobUpdate: jest.fn(), + reportCellActionClicked: jest.fn(), + reportAnomaliesCountClicked: jest.fn(), }); diff --git a/x-pack/plugins/security_solution/public/common/lib/telemetry/telemetry_client.ts b/x-pack/plugins/security_solution/public/common/lib/telemetry/telemetry_client.ts index c95c8153261e1..edd50340e873e 100644 --- a/x-pack/plugins/security_solution/public/common/lib/telemetry/telemetry_client.ts +++ b/x-pack/plugins/security_solution/public/common/lib/telemetry/telemetry_client.ts @@ -15,6 +15,8 @@ import type { ReportEntityAlertsClickedParams, ReportEntityRiskFilteredParams, ReportMLJobUpdateParams, + ReportCellActionClickedParams, + ReportAnomaliesCountClickedParams, } from './types'; import { TelemetryEventTypes } from './types'; @@ -88,4 +90,12 @@ export class TelemetryClient implements TelemetryClientStart { public reportMLJobUpdate = (params: ReportMLJobUpdateParams) => { this.analytics.reportEvent(TelemetryEventTypes.MLJobUpdate, params); }; + + public reportCellActionClicked = (params: ReportCellActionClickedParams) => { + this.analytics.reportEvent(TelemetryEventTypes.CellActionClicked, params); + }; + + public reportAnomaliesCountClicked = (params: ReportAnomaliesCountClickedParams) => { + this.analytics.reportEvent(TelemetryEventTypes.AnomaliesCountClicked, params); + }; } diff --git a/x-pack/plugins/security_solution/public/common/lib/telemetry/telemetry_events.ts b/x-pack/plugins/security_solution/public/common/lib/telemetry/telemetry_events.ts index cb82b16658224..e69f90b57a1f0 100644 --- a/x-pack/plugins/security_solution/public/common/lib/telemetry/telemetry_events.ts +++ b/x-pack/plugins/security_solution/public/common/lib/telemetry/telemetry_events.ts @@ -182,6 +182,60 @@ const mlJobUpdateEvent: TelemetryEvent = { }, }; +const cellActionClickedEvent: TelemetryEvent = { + eventType: TelemetryEventTypes.CellActionClicked, + schema: { + fieldName: { + type: 'keyword', + _meta: { + description: 'Field Name', + optional: false, + }, + }, + actionId: { + type: 'keyword', + _meta: { + description: 'Action id', + optional: false, + }, + }, + displayName: { + type: 'keyword', + _meta: { + description: 'User friendly action name', + optional: false, + }, + }, + metadata: { + type: 'pass_through', + _meta: { + description: 'Action metadata', + optional: true, + }, + }, + }, +}; + +const anomaliesCountClickedEvent: TelemetryEvent = { + eventType: TelemetryEventTypes.AnomaliesCountClicked, + schema: { + jobId: { + type: 'keyword', + _meta: { + description: 'Job id', + optional: false, + }, + }, + count: { + type: 'integer', + _meta: { + description: 'Number of anomalies', + optional: false, + }, + }, + }, +}; + export const telemetryEvents = [ alertsGroupingToggledEvent, alertsGroupingChangedEvent, @@ -190,4 +244,6 @@ export const telemetryEvents = [ entityAlertsClickedEvent, entityRiskFilteredEvent, mlJobUpdateEvent, + cellActionClickedEvent, + anomaliesCountClickedEvent, ]; diff --git a/x-pack/plugins/security_solution/public/common/lib/telemetry/types.ts b/x-pack/plugins/security_solution/public/common/lib/telemetry/types.ts index ba084c49cc033..f9f7c84c488ed 100644 --- a/x-pack/plugins/security_solution/public/common/lib/telemetry/types.ts +++ b/x-pack/plugins/security_solution/public/common/lib/telemetry/types.ts @@ -8,6 +8,7 @@ import type { RootSchema } from '@kbn/analytics-client'; import type { AnalyticsServiceSetup } from '@kbn/core/public'; import type { RiskSeverity } from '../../../../common/search_strategy'; +import type { SecurityMetadata } from '../../../actions/types'; export interface TelemetryServiceSetupParams { analytics: AnalyticsServiceSetup; @@ -21,6 +22,8 @@ export enum TelemetryEventTypes { EntityAlertsClicked = 'Entity Alerts Clicked', EntityRiskFiltered = 'Entity Risk Filtered', MLJobUpdate = 'ML Job Update', + CellActionClicked = 'Cell Action Clicked', + AnomaliesCountClicked = 'Anomalies Count Clicked', } export interface ReportAlertsGroupingChangedParams { @@ -69,6 +72,18 @@ export interface ReportMLJobUpdateParams { errorMessage?: string; } +export interface ReportCellActionClickedParams { + metadata: SecurityMetadata | undefined; + displayName: string; + actionId: string; + fieldName: string; +} + +export interface ReportAnomaliesCountClickedParams { + jobId: string; + count: number; +} + export type TelemetryEventParams = | ReportAlertsGroupingChangedParams | ReportAlertsGroupingToggledParams @@ -76,7 +91,10 @@ export type TelemetryEventParams = | ReportEntityDetailsClickedParams | ReportEntityAlertsClickedParams | ReportEntityRiskFilteredParams - | ReportMLJobUpdateParams; + | ReportMLJobUpdateParams + | ReportCellActionClickedParams + | ReportCellActionClickedParams + | ReportAnomaliesCountClickedParams; export interface TelemetryClientStart { reportAlertsGroupingChanged(params: ReportAlertsGroupingChangedParams): void; @@ -87,6 +105,10 @@ export interface TelemetryClientStart { reportEntityAlertsClicked(params: ReportEntityAlertsClickedParams): void; reportEntityRiskFiltered(params: ReportEntityRiskFilteredParams): void; reportMLJobUpdate(params: ReportMLJobUpdateParams): void; + + reportCellActionClicked(params: ReportCellActionClickedParams): void; + + reportAnomaliesCountClicked(params: ReportAnomaliesCountClickedParams): void; } export type TelemetryEvent = @@ -117,4 +139,12 @@ export type TelemetryEvent = | { eventType: TelemetryEventTypes.MLJobUpdate; schema: RootSchema; + } + | { + eventType: TelemetryEventTypes.CellActionClicked; + schema: RootSchema; + } + | { + eventType: TelemetryEventTypes.AnomaliesCountClicked; + schema: RootSchema; }; diff --git a/x-pack/plugins/security_solution/public/dashboards/pages/details/index.tsx b/x-pack/plugins/security_solution/public/dashboards/pages/details/index.tsx index aeb7dfcf63420..f87d4b1d71f07 100644 --- a/x-pack/plugins/security_solution/public/dashboards/pages/details/index.tsx +++ b/x-pack/plugins/security_solution/public/dashboards/pages/details/index.tsx @@ -12,7 +12,8 @@ import type { DashboardAPI } from '@kbn/dashboard-plugin/public'; import type { DashboardCapabilities } from '@kbn/dashboard-plugin/common/types'; import { useParams } from 'react-router-dom'; -import { isEmpty, pick } from 'lodash/fp'; +import { pick } from 'lodash/fp'; +import { EuiLoadingSpinner } from '@elastic/eui'; import { SecurityPageName } from '../../../../common/constants'; import { SpyRoute } from '../../../common/utils/route/spy_routes'; import { useCapabilities } from '../../../common/lib/kibana'; @@ -25,12 +26,12 @@ import { FiltersGlobal } from '../../../common/components/filters_global'; import { InputsModelId } from '../../../common/store/inputs/constants'; import { useSourcererDataView } from '../../../common/containers/sourcerer'; import { HeaderPage } from '../../../common/components/header_page'; -import { DASHBOARD_PAGE_TITLE } from '../translations'; +import { DASHBOARD_NOT_FOUND_TITLE } from './translations'; import { inputsSelectors } from '../../../common/store'; import { useDeepEqualSelector } from '../../../common/hooks/use_selector'; import { EditDashboardButton } from '../../components/edit_dashboard_button'; -type DashboardDetails = Record; +type DashboardDetails = Record; const DashboardViewComponent: React.FC = () => { const { fromStr, toStr, from, to } = useDeepEqualSelector((state) => @@ -51,13 +52,20 @@ const DashboardViewComponent: React.FC = () => { const [currentState, setCurrentState] = useState( canReadDashboard ? null : DashboardViewPromptState.NoReadPermission ); - const [dashboardDetails, setDashboardDetails] = useState(); + const [dashboardDetails, setDashboardDetails] = useState(); const onDashboardContainerLoaded = useCallback((dashboard: DashboardAPI) => { - const dashboardTitle = dashboard?.getTitle().trim(); - setDashboardDetails({ dashboardTitle }); + if (dashboard) { + const title = dashboard.getTitle().trim(); + if (title) { + setDashboardDetails({ title }); + } else { + setDashboardDetails({ title: DASHBOARD_NOT_FOUND_TITLE }); + } + } }, []); + + const dashboardExists = useMemo(() => dashboardDetails != null, [dashboardDetails]); const { detailName: savedObjectId } = useParams<{ detailName?: string }>(); - const dashboardExists = !isEmpty(dashboardDetails?.dashboardTitle); useEffect(() => { if (!indicesExist) { @@ -73,7 +81,7 @@ const DashboardViewComponent: React.FC = () => { )} - + }> {showWriteControls && dashboardExists && ( { )} - + ); diff --git a/x-pack/plugins/security_solution/public/dashboards/pages/details/translations.ts b/x-pack/plugins/security_solution/public/dashboards/pages/details/translations.ts index dfd25dcbe8512..ddfa94bd75584 100644 --- a/x-pack/plugins/security_solution/public/dashboards/pages/details/translations.ts +++ b/x-pack/plugins/security_solution/public/dashboards/pages/details/translations.ts @@ -27,6 +27,13 @@ export const DASHBOARD_INDICES_NOT_FOUND_TITLE = i18n.translate( } ); +export const DASHBOARD_NOT_FOUND_TITLE = i18n.translate( + 'xpack.securitySolution.dashboards.dashboard.notFound.title', + { + defaultMessage: 'Not found', + } +); + export const EDIT_DASHBOARD_BUTTON_TITLE = i18n.translate( 'xpack.securitySolution.dashboards.dashboard.editDashboardButtonTitle', { diff --git a/x-pack/plugins/security_solution/public/dashboards/pages/translations.ts b/x-pack/plugins/security_solution/public/dashboards/pages/translations.ts index 716b5a6d3a81d..2cdcc156e2946 100644 --- a/x-pack/plugins/security_solution/public/dashboards/pages/translations.ts +++ b/x-pack/plugins/security_solution/public/dashboards/pages/translations.ts @@ -9,7 +9,3 @@ import { i18n } from '@kbn/i18n'; export const DASHBOARDS_PAGE_TITLE = i18n.translate('xpack.securitySolution.dashboards.pageTitle', { defaultMessage: 'Dashboards', }); - -export const DASHBOARD_PAGE_TITLE = i18n.translate('xpack.securitySolution.dashboard.pageTitle', { - defaultMessage: 'Dashboard', -}); diff --git a/x-pack/plugins/security_solution/public/dashboards/pages/utils.ts b/x-pack/plugins/security_solution/public/dashboards/pages/utils.ts index 8db389553c523..c71eedbec0264 100644 --- a/x-pack/plugins/security_solution/public/dashboards/pages/utils.ts +++ b/x-pack/plugins/security_solution/public/dashboards/pages/utils.ts @@ -6,21 +6,13 @@ */ import type { ChromeBreadcrumb } from '@kbn/core/public'; -import { isEmpty } from 'lodash/fp'; import type { RouteSpyState } from '../../common/utils/route/types'; export const getTrailingBreadcrumbs = (params: RouteSpyState): ChromeBreadcrumb[] => { - let breadcrumb: ChromeBreadcrumb[] = []; - - const dashboardTitle = params?.state?.dashboardTitle?.trim(); - if (params?.state?.dashboardTitle || params.detailName) { - breadcrumb = [ - ...breadcrumb, - { - text: !isEmpty(dashboardTitle) ? dashboardTitle : params.detailName, - }, - ]; + const breadcrumbName = params?.state?.dashboardName; + if (breadcrumbName) { + return [{ text: breadcrumbName }]; } - return breadcrumb; + return []; }; diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_creation_ui/pages/rule_creation/helpers.test.ts b/x-pack/plugins/security_solution/public/detection_engine/rule_creation_ui/pages/rule_creation/helpers.test.ts index af6a82af1a9d5..8b7a9c62b1541 100644 --- a/x-pack/plugins/security_solution/public/detection_engine/rule_creation_ui/pages/rule_creation/helpers.test.ts +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_creation_ui/pages/rule_creation/helpers.test.ts @@ -797,91 +797,6 @@ describe('helpers', () => { meta: { kibana_siem_app_url: 'http://localhost:5601/app/siem', }, - throttle: 'no_actions', - }; - - expect(result).toEqual(expected); - }); - - test('returns proper throttle value for no_actions', () => { - const mockStepData: ActionsStepRule = { - ...mockData, - throttle: 'no_actions', - }; - const result = formatActionsStepData(mockStepData); - const expected: ActionsStepRuleJson = { - actions: [], - enabled: false, - meta: { - kibana_siem_app_url: mockStepData.kibanaSiemAppUrl, - }, - throttle: 'no_actions', - }; - - expect(result).toEqual(expected); - }); - - test('returns proper throttle value for rule', () => { - const mockStepData: ActionsStepRule = { - ...mockData, - throttle: 'rule', - actions: [ - { - group: 'default', - id: 'id', - actionTypeId: 'actionTypeId', - params: {}, - }, - ], - }; - const result = formatActionsStepData(mockStepData); - const expected: ActionsStepRuleJson = { - actions: [ - { - group: mockStepData.actions[0].group, - id: mockStepData.actions[0].id, - action_type_id: mockStepData.actions[0].actionTypeId, - params: mockStepData.actions[0].params, - }, - ], - enabled: false, - meta: { - kibana_siem_app_url: mockStepData.kibanaSiemAppUrl, - }, - throttle: 'rule', - }; - - expect(result).toEqual(expected); - }); - - test('returns proper throttle value for interval', () => { - const mockStepData: ActionsStepRule = { - ...mockData, - throttle: '1d', - actions: [ - { - group: 'default', - id: 'id', - actionTypeId: 'actionTypeId', - params: {}, - }, - ], - }; - const result = formatActionsStepData(mockStepData); - const expected: ActionsStepRuleJson = { - actions: [ - { - group: mockStepData.actions[0].group, - id: mockStepData.actions[0].id, - action_type_id: mockStepData.actions[0].actionTypeId, - params: mockStepData.actions[0].params, - }, - ], - enabled: false, - meta: { - kibana_siem_app_url: mockStepData.kibanaSiemAppUrl, - }, - throttle: mockStepData.throttle, }; expect(result).toEqual(expected); @@ -913,7 +828,6 @@ describe('helpers', () => { meta: { kibana_siem_app_url: mockStepData.kibanaSiemAppUrl, }, - throttle: 'no_actions', }; expect(result).toEqual(expected); diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_creation_ui/pages/rule_creation/helpers.ts b/x-pack/plugins/security_solution/public/detection_engine/rule_creation_ui/pages/rule_creation/helpers.ts index 5442727561ce1..e61f55dbce4ec 100644 --- a/x-pack/plugins/security_solution/public/detection_engine/rule_creation_ui/pages/rule_creation/helpers.ts +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_creation_ui/pages/rule_creation/helpers.ts @@ -25,7 +25,6 @@ import type { Type, } from '@kbn/securitysolution-io-ts-alerting-types'; import { ENDPOINT_LIST_ID } from '@kbn/securitysolution-list-constants'; -import { NOTIFICATION_THROTTLE_NO_ACTIONS } from '../../../../../common/constants'; import { assertUnreachable } from '../../../../../common/utility_types'; import { transformAlertToRuleAction, @@ -563,19 +562,12 @@ export const formatAboutStepData = ( }; export const formatActionsStepData = (actionsStepData: ActionsStepRule): ActionsStepRuleJson => { - const { - actions = [], - responseActions, - enabled, - kibanaSiemAppUrl, - throttle = NOTIFICATION_THROTTLE_NO_ACTIONS, - } = actionsStepData; + const { actions = [], responseActions, enabled, kibanaSiemAppUrl } = actionsStepData; return { actions: actions.map(transformAlertToRuleAction), response_actions: responseActions?.map(transformAlertToRuleResponseAction), enabled, - throttle: actions.length ? throttle : NOTIFICATION_THROTTLE_NO_ACTIONS, meta: { kibana_siem_app_url: kibanaSiemAppUrl, }, diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_management_ui/components/rules_table/__mocks__/mock.ts b/x-pack/plugins/security_solution/public/detection_engine/rule_management_ui/components/rules_table/__mocks__/mock.ts index 40707a4307f27..487052fcbf2ef 100644 --- a/x-pack/plugins/security_solution/public/detection_engine/rule_management_ui/components/rules_table/__mocks__/mock.ts +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_management_ui/components/rules_table/__mocks__/mock.ts @@ -200,7 +200,6 @@ export const mockActionsStepRule = (enabled = false): ActionsStepRule => ({ actions: [], kibanaSiemAppUrl: 'http://localhost:5601/app/siem', enabled, - throttle: 'no_actions', }); export const mockDefineStepRule = (): DefineStepRule => ({ diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_management_ui/components/rules_table/bulk_actions/bulk_duplicate_exceptions_confirmation.tsx b/x-pack/plugins/security_solution/public/detection_engine/rule_management_ui/components/rules_table/bulk_actions/bulk_duplicate_exceptions_confirmation.tsx index cd8be2d925c3f..74575c327fbc8 100644 --- a/x-pack/plugins/security_solution/public/detection_engine/rule_management_ui/components/rules_table/bulk_actions/bulk_duplicate_exceptions_confirmation.tsx +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_management_ui/components/rules_table/bulk_actions/bulk_duplicate_exceptions_confirmation.tsx @@ -46,21 +46,35 @@ const BulkActionDuplicateExceptionsConfirmationComponent = ({ defaultFocusedButton="confirm" onCancel={onCancel} > - - {i18n.MODAL_TEXT(rulesCount)}{' '} - - + {i18n.MODAL_TEXT(rulesCount)} + {i18n.DUPLICATE_EXCEPTIONS_INCLUDE_EXPIRED_EXCEPTIONS_LABEL(rulesCount)} + + + ), + 'data-test-subj': DuplicateOptions.withExceptions, + }, + { + id: DuplicateOptions.withExceptionsExcludeExpiredExceptions, + label: ( + + {i18n.DUPLICATE_EXCEPTIONS_TEXT(rulesCount)} + + + ), + 'data-test-subj': DuplicateOptions.withExceptionsExcludeExpiredExceptions, }, { id: DuplicateOptions.withoutExceptions, label: i18n.DUPLICATE_WITHOUT_EXCEPTIONS_TEXT(rulesCount), + 'data-test-subj': DuplicateOptions.withoutExceptions, }, ]} idSelected={selectedDuplicateOption} diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_management_ui/components/rules_table/bulk_actions/forms/rule_actions_form.tsx b/x-pack/plugins/security_solution/public/detection_engine/rule_management_ui/components/rules_table/bulk_actions/forms/rule_actions_form.tsx index 8b791ee2aece1..ff41017ac1771 100644 --- a/x-pack/plugins/security_solution/public/detection_engine/rule_management_ui/components/rules_table/bulk_actions/forms/rule_actions_form.tsx +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_management_ui/components/rules_table/bulk_actions/forms/rule_actions_form.tsx @@ -23,21 +23,13 @@ import { Field, } from '../../../../../../shared_imports'; import { BulkActionEditType } from '../../../../../../../common/detection_engine/rule_management/api/rules/bulk_actions/request_schema'; -import type { - BulkActionEditPayload, - ThrottleForBulkActions, -} from '../../../../../../../common/detection_engine/rule_management/api/rules/bulk_actions/request_schema'; -import { NOTIFICATION_THROTTLE_RULE } from '../../../../../../../common/constants'; +import type { BulkActionEditPayload } from '../../../../../../../common/detection_engine/rule_management/api/rules/bulk_actions/request_schema'; import { BulkEditFormWrapper } from './bulk_edit_form_wrapper'; import { bulkAddRuleActions as i18n } from '../translations'; import { useKibana } from '../../../../../../common/lib/kibana'; -import { - ThrottleSelectField, - THROTTLE_OPTIONS_FOR_BULK_RULE_ACTIONS, -} from '../../../../../../detections/components/rules/throttle_select_field'; import { getAllActionMessageParams } from '../../../../../../detections/pages/detection_engine/rules/helpers'; import { RuleActionsField } from '../../../../../../detections/components/rules/rule_actions_field'; @@ -45,19 +37,16 @@ import { debouncedValidateRuleActionsField } from '../../../../../../detections/ const CommonUseField = getUseField({ component: Field }); +type BulkActionsRuleAction = RuleAction & Required>; + export interface RuleActionsFormData { - throttle: ThrottleForBulkActions; - actions: RuleAction[]; + actions: BulkActionsRuleAction[]; overwrite: boolean; } const getFormSchema = ( actionTypeRegistry: ActionTypeRegistryContract ): FormSchema => ({ - throttle: { - label: i18n.THROTTLE_LABEL, - helpText: i18n.THROTTLE_HELP_TEXT, - }, actions: { validations: [ { @@ -75,7 +64,6 @@ const getFormSchema = ( }); const defaultFormData: RuleActionsFormData = { - throttle: NOTIFICATION_THROTTLE_RULE, actions: [], overwrite: false, }; @@ -108,7 +96,7 @@ const RuleActionsFormComponent = ({ rulesCount, onClose, onConfirm }: RuleAction return; } - const { actions = [], throttle: throttleToSubmit, overwrite: overwriteValue } = data; + const { actions = [], overwrite: overwriteValue } = data; const editAction = overwriteValue ? BulkActionEditType.set_rule_actions : BulkActionEditType.add_rule_actions; @@ -117,23 +105,10 @@ const RuleActionsFormComponent = ({ rulesCount, onClose, onConfirm }: RuleAction type: editAction, value: { actions: actions.map(({ actionTypeId, ...action }) => action), - throttle: throttleToSubmit, }, }); }, [form, onConfirm]); - const throttleFieldComponentProps = useMemo( - () => ({ - idAria: 'bulkEditRulesRuleActionThrottle', - 'data-test-subj': 'bulkEditRulesRuleActionThrottle', - hasNoInitialSelection: false, - euiFieldProps: { - options: THROTTLE_OPTIONS_FOR_BULK_RULE_ACTIONS, - }, - }), - [] - ); - const messageVariables = useMemo(() => getAllActionMessageParams(), []); return ( @@ -156,24 +131,11 @@ const RuleActionsFormComponent = ({ rulesCount, onClose, onConfirm }: RuleAction } >
    -
  • - -
  • {i18n.RULE_VARIABLES_DETAIL}
- - - ( ), @@ -147,7 +132,7 @@ export const bulkDuplicateRuleActions = { MODAL_TEXT: (rulesCount: number): JSX.Element => ( ), @@ -155,7 +140,15 @@ export const bulkDuplicateRuleActions = { DUPLICATE_EXCEPTIONS_TEXT: (rulesCount: number) => ( + ), + + DUPLICATE_EXCEPTIONS_INCLUDE_EXPIRED_EXCEPTIONS_LABEL: (rulesCount: number) => ( + ), @@ -163,7 +156,7 @@ export const bulkDuplicateRuleActions = { DUPLICATE_WITHOUT_EXCEPTIONS_TEXT: (rulesCount: number) => ( ), @@ -186,7 +179,7 @@ export const bulkDuplicateRuleActions = { 'xpack.securitySolution.detectionEngine.rules.allRules.bulkActions.duplicate.exceptionsConfirmation.tooltip', { defaultMessage: - ' If you duplicate exceptions, then the shared exceptions list will be duplicated by reference and the default rule exception will be copied and created as a new one', + ' If you choose to duplicate exceptions, the shared exceptions list will be duplicated by reference and the rule exceptions will be copied and created anew', } ), }; diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_management_ui/components/rules_table/bulk_actions/use_bulk_actions.tsx b/x-pack/plugins/security_solution/public/detection_engine/rule_management_ui/components/rules_table/bulk_actions/use_bulk_actions.tsx index f9915777483a8..b1804e5c29f0a 100644 --- a/x-pack/plugins/security_solution/public/detection_engine/rule_management_ui/components/rules_table/bulk_actions/use_bulk_actions.tsx +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_management_ui/components/rules_table/bulk_actions/use_bulk_actions.tsx @@ -6,7 +6,6 @@ */ /* eslint-disable complexity */ -import { omit } from 'lodash'; import type { EuiContextMenuPanelDescriptor } from '@elastic/eui'; import { EuiButton, EuiFlexGroup, EuiFlexItem, EuiTextColor } from '@elastic/eui'; import type { Toast } from '@kbn/core/public'; @@ -137,7 +136,13 @@ export const useBulkActions = ({ type: BulkActionType.duplicate, duplicatePayload: { include_exceptions: - modalDuplicationConfirmationResult === DuplicateOptions.withExceptions, + modalDuplicationConfirmationResult === DuplicateOptions.withExceptions || + modalDuplicationConfirmationResult === + DuplicateOptions.withExceptionsExcludeExpiredExceptions, + include_expired_exceptions: !( + modalDuplicationConfirmationResult === + DuplicateOptions.withExceptionsExcludeExpiredExceptions + ), }, ...(isAllSelected ? { query: filterQuery } : { ids: selectedRuleIds }), }); @@ -222,16 +227,6 @@ export const useBulkActions = ({ return; } - // TODO: https://github.com/elastic/kibana/issues/148414 - // Strip frequency from actions to comply with Security Solution alert API - if ('actions' in editPayload.value) { - // `actions.frequency` is included in the payload from TriggersActionsUI ActionForm - // but is not included in the type definition for the editPayload, because this type - // definition comes from the Security Solution alert API - // TODO https://github.com/elastic/kibana/issues/148414 fix this discrepancy - editPayload.value.actions = editPayload.value.actions.map((a) => omit(a, 'frequency')); - } - startTransaction({ name: BULK_RULE_ACTIONS.EDIT }); const hideWarningToast = () => { diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_management_ui/components/rules_table/bulk_actions/utils/compute_dry_run_edit_payload.ts b/x-pack/plugins/security_solution/public/detection_engine/rule_management_ui/components/rules_table/bulk_actions/utils/compute_dry_run_edit_payload.ts index c8f49ebe4a6c6..f25b1331d766a 100644 --- a/x-pack/plugins/security_solution/public/detection_engine/rule_management_ui/components/rules_table/bulk_actions/utils/compute_dry_run_edit_payload.ts +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_management_ui/components/rules_table/bulk_actions/utils/compute_dry_run_edit_payload.ts @@ -50,7 +50,7 @@ export function computeDryRunEditPayload(editAction: BulkActionEditType): BulkAc return [ { type: editAction, - value: { throttle: '1h', actions: [] }, + value: { actions: [] }, }, ]; case BulkActionEditType.set_schedule: diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_management_ui/components/rules_table/use_rules_table_actions.tsx b/x-pack/plugins/security_solution/public/detection_engine/rule_management_ui/components/rules_table/use_rules_table_actions.tsx index 62af07af3ea24..b5999b926b97f 100644 --- a/x-pack/plugins/security_solution/public/detection_engine/rule_management_ui/components/rules_table/use_rules_table_actions.tsx +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_management_ui/components/rules_table/use_rules_table_actions.tsx @@ -77,7 +77,13 @@ export const useRulesTableActions = ({ ids: [rule.id], duplicatePayload: { include_exceptions: - modalDuplicationConfirmationResult === DuplicateOptions.withExceptions, + modalDuplicationConfirmationResult === DuplicateOptions.withExceptions || + modalDuplicationConfirmationResult === + DuplicateOptions.withExceptionsExcludeExpiredExceptions, + include_expired_exceptions: !( + modalDuplicationConfirmationResult === + DuplicateOptions.withExceptionsExcludeExpiredExceptions + ), }, }); const createdRules = result?.attributes.results.created; diff --git a/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_by_type_panel/columns.tsx b/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_by_type_panel/columns.tsx index 9d7812b833b46..f68f8d54a5b19 100644 --- a/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_by_type_panel/columns.tsx +++ b/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_by_type_panel/columns.tsx @@ -56,7 +56,7 @@ export const getAlertsTypeTableColumns = ( = ({ field, messageVariables }) = const setActionAlertsFilterProperty = useCallback( (key: string, value: RuleActionAlertsFilterProperty, index: number) => { + field.setValue((prevValue: RuleAction[]) => { + const updatedActions = [...prevValue]; + const { alertsFilter, ...rest } = updatedActions[index]; + const updatedAlertsFilter = { ...alertsFilter }; + + if (value) { + updatedAlertsFilter[key] = value; + } else { + delete updatedAlertsFilter[key]; + } + + updatedActions[index] = { + ...rest, + ...(!isEmpty(updatedAlertsFilter) ? { alertsFilter: updatedAlertsFilter } : {}), + }; + return updatedActions; + }); + }, + [field] + ); + + const setActionFrequency = useCallback( + (key: string, value: RuleActionParam, index: number) => { field.setValue((prevValue: RuleAction[]) => { const updatedActions = [...prevValue]; updatedActions[index] = { ...updatedActions[index], - alertsFilter: { - ...(updatedActions[index].alertsFilter ?? { query: null, timeframe: null }), + frequency: { + ...(updatedActions[index].frequency ?? NOTIFICATION_DEFAULT_FREQUENCY), [key]: value, }, }; @@ -217,22 +234,22 @@ export const RuleActionsField: React.FC = ({ field, messageVariables }) = setActionIdByIndex, setActions: setAlertActionsProperty, setActionParamsProperty, - setActionFrequencyProperty: () => {}, + setActionFrequencyProperty: setActionFrequency, setActionAlertsFilterProperty, featureId: SecurityConnectorFeatureId, defaultActionMessage: DEFAULT_ACTION_MESSAGE, defaultSummaryMessage: DEFAULT_ACTION_MESSAGE, hideActionHeader: true, - hideNotifyWhen: true, hasSummary: true, notifyWhenSelectOptions: NOTIFY_WHEN_OPTIONS, - defaultRuleFrequency: DEFAULT_FREQUENCY, + defaultRuleFrequency: NOTIFICATION_DEFAULT_FREQUENCY, showActionAlertsFilter: true, }), [ actions, getActionForm, messageVariables, + setActionFrequency, setActionIdByIndex, setActionParamsProperty, setAlertActionsProperty, diff --git a/x-pack/plugins/security_solution/public/detections/components/rules/rule_actions_overflow/index.tsx b/x-pack/plugins/security_solution/public/detections/components/rules/rule_actions_overflow/index.tsx index f5345f42f810b..aca8fe27abd0f 100644 --- a/x-pack/plugins/security_solution/public/detections/components/rules/rule_actions_overflow/index.tsx +++ b/x-pack/plugins/security_solution/public/detections/components/rules/rule_actions_overflow/index.tsx @@ -96,7 +96,13 @@ const RuleActionsOverflowComponent = ({ ids: [rule.id], duplicatePayload: { include_exceptions: - modalDuplicationConfirmationResult === DuplicateOptions.withExceptions, + modalDuplicationConfirmationResult === DuplicateOptions.withExceptions || + modalDuplicationConfirmationResult === + DuplicateOptions.withExceptionsExcludeExpiredExceptions, + include_expired_exceptions: !( + modalDuplicationConfirmationResult === + DuplicateOptions.withExceptionsExcludeExpiredExceptions + ), }, }); diff --git a/x-pack/plugins/security_solution/public/detections/components/rules/step_rule_actions/get_schema.ts b/x-pack/plugins/security_solution/public/detections/components/rules/step_rule_actions/get_schema.ts index 858578f8a5d38..f16cac0eb923a 100644 --- a/x-pack/plugins/security_solution/public/detections/components/rules/step_rule_actions/get_schema.ts +++ b/x-pack/plugins/security_solution/public/detections/components/rules/step_rule_actions/get_schema.ts @@ -5,8 +5,6 @@ * 2.0. */ -import { i18n } from '@kbn/i18n'; - import type { ActionTypeRegistryContract } from '@kbn/triggers-actions-ui-plugin/public'; import { debouncedValidateRuleActionsField } from '../../../containers/detection_engine/rules/validate_rule_actions_field'; @@ -30,12 +28,4 @@ export const getSchema = ({ responseActions: {}, enabled: {}, kibanaSiemAppUrl: {}, - throttle: { - label: i18n.translate( - 'xpack.securitySolution.detectionEngine.createRule.stepRuleActions.fieldThrottleLabel', - { - defaultMessage: 'Actions frequency', - } - ), - }, }); diff --git a/x-pack/plugins/security_solution/public/detections/components/rules/step_rule_actions/index.tsx b/x-pack/plugins/security_solution/public/detections/components/rules/step_rule_actions/index.tsx index 58dd95bcac0dd..86bbb7604add2 100644 --- a/x-pack/plugins/security_solution/public/detections/components/rules/step_rule_actions/index.tsx +++ b/x-pack/plugins/security_solution/public/detections/components/rules/step_rule_actions/index.tsx @@ -15,7 +15,6 @@ import { EuiText, EuiTitle, } from '@elastic/eui'; -import { findIndex } from 'lodash/fp'; import type { FC } from 'react'; import React, { memo, useCallback, useEffect, useMemo } from 'react'; import { FormattedMessage } from '@kbn/i18n-react'; @@ -29,20 +28,13 @@ import { ResponseActionsForm } from '../../../../detection_engine/rule_response_ import type { RuleStepProps, ActionsStepRule } from '../../../pages/detection_engine/rules/types'; import { RuleStep } from '../../../pages/detection_engine/rules/types'; import { StepRuleDescription } from '../description_step'; -import { Form, UseField, useForm, useFormData } from '../../../../shared_imports'; +import { Form, UseField, useForm } from '../../../../shared_imports'; import { StepContentWrapper } from '../step_content_wrapper'; -import { - ThrottleSelectField, - THROTTLE_OPTIONS_FOR_RULE_CREATION_AND_EDITING, - DEFAULT_THROTTLE_OPTION, -} from '../throttle_select_field'; import { RuleActionsField } from '../rule_actions_field'; import { useKibana } from '../../../../common/lib/kibana'; import { getSchema } from './get_schema'; import * as I18n from './translations'; import { APP_UI_ID } from '../../../../../common/constants'; -import { useManageCaseAction } from './use_manage_case_action'; -import { THROTTLE_FIELD_HELP_TEXT, THROTTLE_FIELD_HELP_TEXT_WHEN_QUERY } from './translations'; interface StepRuleActionsProps extends RuleStepProps { defaultValues?: ActionsStepRule | null; @@ -55,23 +47,10 @@ export const stepActionsDefaultValue: ActionsStepRule = { actions: [], responseActions: [], kibanaSiemAppUrl: '', - throttle: DEFAULT_THROTTLE_OPTION.value, }; const GhostFormField = () => <>; -const getThrottleOptions = (throttle?: string | null) => { - // Add support for throttle options set by the API - if ( - throttle && - findIndex(['value', throttle], THROTTLE_OPTIONS_FOR_RULE_CREATION_AND_EDITING) < 0 - ) { - return [...THROTTLE_OPTIONS_FOR_RULE_CREATION_AND_EDITING, { value: throttle, text: throttle }]; - } - - return THROTTLE_OPTIONS_FOR_RULE_CREATION_AND_EDITING; -}; - const DisplayActionsHeader = () => { return ( <> @@ -99,7 +78,6 @@ const StepRuleActionsComponent: FC = ({ actionMessageParams, ruleType, }) => { - const [isLoadingCaseAction] = useManageCaseAction(); const { services: { application, @@ -127,11 +105,6 @@ const StepRuleActionsComponent: FC = ({ schema, }); const { getFields, getFormData, submit } = form; - const [{ throttle: formThrottle }] = useFormData({ - form, - watch: ['throttle'], - }); - const throttle = formThrottle || initialState.throttle; const handleSubmit = useCallback( (enabled: boolean) => { @@ -163,44 +136,20 @@ const StepRuleActionsComponent: FC = ({ }; }, [getData, setForm]); - const throttleOptions = useMemo(() => { - return getThrottleOptions(throttle); - }, [throttle]); - - const throttleFieldComponentProps = useMemo( - () => ({ - idAria: 'detectionEngineStepRuleActionsThrottle', - isDisabled: isLoading, - isLoading: isLoadingCaseAction, - dataTestSubj: 'detectionEngineStepRuleActionsThrottle', - hasNoInitialSelection: false, - helpText: isQueryRule(ruleType) - ? THROTTLE_FIELD_HELP_TEXT_WHEN_QUERY - : THROTTLE_FIELD_HELP_TEXT, - euiFieldProps: { - options: throttleOptions, - }, - }), - [isLoading, isLoadingCaseAction, ruleType, throttleOptions] - ); - const displayActionsOptions = useMemo( - () => - throttle !== stepActionsDefaultValue.throttle ? ( - <> - - - - ) : ( - - ), - [throttle, actionMessageParams] + () => ( + <> + + + + ), + [actionMessageParams] ); const displayResponseActionsOptions = useMemo(() => { if (isQueryRule(ruleType)) { @@ -217,11 +166,6 @@ const StepRuleActionsComponent: FC = ({ return application.capabilities.actions.show ? ( <> - {displayActionsOptions} {responseActionsEnabled && displayResponseActionsOptions} @@ -231,14 +175,6 @@ const StepRuleActionsComponent: FC = ({ ) : ( <> {I18n.NO_ACTIONS_READ_PERMISSIONS} - - - - ); }, [ @@ -246,7 +182,6 @@ const StepRuleActionsComponent: FC = ({ displayActionsOptions, displayResponseActionsOptions, responseActionsEnabled, - throttleFieldComponentProps, ]); if (isReadOnlyView) { diff --git a/x-pack/plugins/security_solution/public/detections/components/rules/step_rule_actions/translations.tsx b/x-pack/plugins/security_solution/public/detections/components/rules/step_rule_actions/translations.tsx index 15937883fb409..d467c3af05f8f 100644 --- a/x-pack/plugins/security_solution/public/detections/components/rules/step_rule_actions/translations.tsx +++ b/x-pack/plugins/security_solution/public/detections/components/rules/step_rule_actions/translations.tsx @@ -28,19 +28,3 @@ export const NO_ACTIONS_READ_PERMISSIONS = i18n.translate( 'Cannot create rule actions. You do not have "Read" permissions for the "Actions" plugin.', } ); - -export const THROTTLE_FIELD_HELP_TEXT = i18n.translate( - 'xpack.securitySolution.detectionEngine.createRule.stepRuleActions.fieldThrottleHelpText', - { - defaultMessage: - 'Select when automated actions should be performed if a rule evaluates as true.', - } -); - -export const THROTTLE_FIELD_HELP_TEXT_WHEN_QUERY = i18n.translate( - 'xpack.securitySolution.detectionEngine.createRule.stepRuleActions.fieldThrottleHelpTextWhenQuery', - { - defaultMessage: - 'Select when automated actions should be performed if a rule evaluates as true. This frequency does not apply to Response Actions.', - } -); diff --git a/x-pack/plugins/security_solution/public/detections/components/rules/step_rule_actions/use_manage_case_action.tsx b/x-pack/plugins/security_solution/public/detections/components/rules/step_rule_actions/use_manage_case_action.tsx deleted file mode 100644 index 57dd5670dba80..0000000000000 --- a/x-pack/plugins/security_solution/public/detections/components/rules/step_rule_actions/use_manage_case_action.tsx +++ /dev/null @@ -1,66 +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 { useEffect, useRef, useState } from 'react'; -import { getAllConnectorsUrl, getCreateConnectorUrl } from '@kbn/cases-plugin/common'; -import { convertArrayToCamelCase, KibanaServices } from '../../../../common/lib/kibana'; - -interface CaseAction { - connectorTypeId: string; - id: string; - isPreconfigured: boolean; - name: string; - referencedByCount: number; -} - -const CASE_ACTION_NAME = 'Cases'; - -export const useManageCaseAction = () => { - const hasInit = useRef(true); - const [loading, setLoading] = useState(true); - const [hasError, setHasError] = useState(false); - - useEffect(() => { - const abortCtrl = new AbortController(); - const fetchActions = async () => { - try { - const actions = convertArrayToCamelCase( - await KibanaServices.get().http.fetch(getAllConnectorsUrl(), { - method: 'GET', - signal: abortCtrl.signal, - }) - ) as CaseAction[]; - - if (!actions.some((a) => a.connectorTypeId === '.case' && a.name === CASE_ACTION_NAME)) { - await KibanaServices.get().http.post(getCreateConnectorUrl(), { - method: 'POST', - body: JSON.stringify({ - connector_type_id: '.case', - config: {}, - name: CASE_ACTION_NAME, - secrets: {}, - }), - signal: abortCtrl.signal, - }); - } - setLoading(false); - } catch { - setLoading(false); - setHasError(true); - } - }; - if (hasInit.current) { - hasInit.current = false; - fetchActions(); - } - - return () => { - abortCtrl.abort(); - }; - }, []); - return [loading, hasError]; -}; diff --git a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/helpers.test.tsx b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/helpers.test.tsx index 22ba4e03dbf38..58d6e4c7c8470 100644 --- a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/helpers.test.tsx +++ b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/helpers.test.tsx @@ -35,6 +35,7 @@ import type { ActionsStepRule, } from './types'; import { getThreatMock } from '../../../../../common/detection_engine/schemas/types/threat.mock'; +import type { RuleAlertAction } from '../../../../../common/detection_engine/types'; describe('rule helpers', () => { moment.suppressDeprecationWarnings = true; @@ -146,7 +147,6 @@ describe('rule helpers', () => { const scheduleRuleStepData = { from: '0s', interval: '5m' }; const ruleActionsStepData = { enabled: true, - throttle: 'no_actions', actions: [], responseActions: undefined, }; @@ -410,16 +410,22 @@ describe('rule helpers', () => { describe('getActionsStepsData', () => { test('returns expected ActionsStepRule rule object', () => { + const actions: RuleAlertAction[] = [ + { + id: 'id', + group: 'group', + params: {}, + action_type_id: 'action_type_id', + frequency: { + summary: true, + throttle: null, + notifyWhen: 'onActiveAlert', + }, + }, + ]; const mockedRule = { ...mockRule('test-id'), - actions: [ - { - id: 'id', - group: 'group', - params: {}, - action_type_id: 'action_type_id', - }, - ], + actions, }; const result: ActionsStepRule = getActionsStepsData(mockedRule); const expected = { @@ -429,11 +435,15 @@ describe('rule helpers', () => { group: 'group', params: {}, actionTypeId: 'action_type_id', + frequency: { + summary: true, + throttle: null, + notifyWhen: 'onActiveAlert', + }, }, ], responseActions: undefined, enabled: mockedRule.enabled, - throttle: 'no_actions', }; expect(result).toEqual(expected); diff --git a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/helpers.tsx b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/helpers.tsx index a290ac92f6a66..d0cfdc8b707f3 100644 --- a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/helpers.tsx +++ b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/helpers.tsx @@ -76,12 +76,11 @@ export const getActionsStepsData = ( response_actions?: ResponseAction[]; } ): ActionsStepRule => { - const { enabled, throttle, meta, actions = [], response_actions: responseActions } = rule; + const { enabled, meta, actions = [], response_actions: responseActions } = rule; return { actions: actions?.map(transformRuleToAlertAction), responseActions: responseActions?.map(transformRuleToAlertResponseAction), - throttle, kibanaSiemAppUrl: meta?.kibana_siem_app_url, enabled, }; diff --git a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/types.ts b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/types.ts index edbb3b4ecbf97..2d2c7580ed051 100644 --- a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/types.ts +++ b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/types.ts @@ -194,7 +194,6 @@ export interface ActionsStepRule { responseActions?: RuleResponseAction[]; enabled: boolean; kibanaSiemAppUrl?: string; - throttle?: string | null; } export interface DefineStepRuleJson { diff --git a/x-pack/plugins/security_solution/public/exceptions/components/exceptions_list_card/index.test.tsx b/x-pack/plugins/security_solution/public/exceptions/components/exceptions_list_card/index.test.tsx new file mode 100644 index 0000000000000..72ba17ad86620 --- /dev/null +++ b/x-pack/plugins/security_solution/public/exceptions/components/exceptions_list_card/index.test.tsx @@ -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 { render } from '@testing-library/react'; +import React from 'react'; + +import { ExceptionsListCard } from '.'; +import { useListDetailsView } from '../../hooks'; +import { useExceptionsListCard } from '../../hooks/use_exceptions_list.card'; +import { getExceptionListSchemaMock } from '@kbn/lists-plugin/common/schemas/response/exception_list_schema.mock'; +import { getExceptionListItemSchemaMock } from '@kbn/lists-plugin/common/schemas/response/exception_list_item_schema.mock'; +import { TestProviders } from '../../../common/mock'; + +jest.mock('../../hooks'); +jest.mock('../../hooks/use_exceptions_list.card'); + +const getMockUseExceptionsListCard = () => ({ + listId: 'my-list', + listName: 'Exception list', + listType: 'detection', + createdAt: '2023-02-01T10:20:30.000Z', + createdBy: 'elastic', + exceptions: [{ ...getExceptionListItemSchemaMock() }], + pagination: { pageIndex: 0, pageSize: 5, totalItemCount: 1 }, + ruleReferences: { + 'my-list': { + name: 'Exception list', + id: '345', + referenced_rules: [], + listId: 'my-list', + }, + }, + toggleAccordion: false, + openAccordionId: '123', + menuActionItems: [ + { + key: 'Export', + icon: 'exportAction', + label: 'Export', + onClick: jest.fn(), + }, + ], + listRulesCount: '5', + listDescription: 'My exception list description', + exceptionItemsCount: jest.fn(), + onEditExceptionItem: jest.fn(), + onDeleteException: jest.fn(), + onPaginationChange: jest.fn(), + setToggleAccordion: jest.fn(), + exceptionViewerStatus: '', + showAddExceptionFlyout: false, + showEditExceptionFlyout: false, + exceptionToEdit: undefined, + onAddExceptionClick: jest.fn(), + handleConfirmExceptionFlyout: jest.fn(), + handleCancelExceptionItemFlyout: jest.fn(), + goToExceptionDetail: jest.fn(), + emptyViewerTitle: 'Empty View', + emptyViewerBody: 'This is the empty view description.', + emptyViewerButtonText: 'Take action', + handleCancelExpiredExceptionsModal: jest.fn(), + handleConfirmExpiredExceptionsModal: jest.fn(), + showIncludeExpiredExceptionsModal: false, +}); +const getMockUseListDetailsView = () => ({ + linkedRules: [], + showManageRulesFlyout: false, + showManageButtonLoader: false, + disableManageButton: false, + onManageRules: jest.fn(), + onSaveManageRules: jest.fn(), + onCancelManageRules: jest.fn(), + onRuleSelectionChange: jest.fn(), +}); + +describe('ExceptionsListCard', () => { + beforeEach(() => { + (useExceptionsListCard as jest.Mock).mockReturnValue(getMockUseExceptionsListCard()); + (useListDetailsView as jest.Mock).mockReturnValue(getMockUseListDetailsView()); + }); + afterEach(() => { + jest.resetAllMocks(); + jest.restoreAllMocks(); + }); + + it('should display expired exception confirmation modal when "showIncludeExpiredExceptionsModal" is "true"', () => { + (useExceptionsListCard as jest.Mock).mockReturnValue({ + ...getMockUseExceptionsListCard(), + showIncludeExpiredExceptionsModal: true, + }); + + const wrapper = render( + + + + ); + expect(wrapper.getByTestId('includeExpiredExceptionsConfirmationModal')).toBeTruthy(); + }); +}); diff --git a/x-pack/plugins/security_solution/public/exceptions/components/exceptions_list_card/index.tsx b/x-pack/plugins/security_solution/public/exceptions/components/exceptions_list_card/index.tsx index 95dde7239b605..44370b274cd12 100644 --- a/x-pack/plugins/security_solution/public/exceptions/components/exceptions_list_card/index.tsx +++ b/x-pack/plugins/security_solution/public/exceptions/components/exceptions_list_card/index.tsx @@ -33,7 +33,7 @@ import { ListExceptionItems } from '../list_exception_items'; import { useListDetailsView } from '../../hooks'; import { useExceptionsListCard } from '../../hooks/use_exceptions_list.card'; import { ManageRules } from '../manage_rules'; -import { ExportExceptionsListModal } from '../export_exceptions_list_modal'; +import { IncludeExpiredExceptionsModal } from '../expired_exceptions_list_items_modal'; interface ExceptionsListCardProps { exceptionsList: ExceptionListInfo; @@ -59,6 +59,17 @@ interface ExceptionsListCardProps { name: string; namespaceType: NamespaceType; }) => () => Promise; + handleDuplicate: ({ + includeExpiredExceptions, + listId, + name, + namespaceType, + }: { + includeExpiredExceptions: boolean; + listId: string; + name: string; + namespaceType: NamespaceType; + }) => () => Promise; readOnly: boolean; } const buttonCss = css` @@ -78,7 +89,7 @@ const ListHeaderContainer = styled(EuiFlexGroup)` text-align: initial; `; export const ExceptionsListCard = memo( - ({ exceptionsList, handleDelete, handleExport, readOnly }) => { + ({ exceptionsList, handleDelete, handleExport, handleDuplicate, readOnly }) => { const { linkedRules, showManageRulesFlyout, @@ -119,13 +130,14 @@ export const ExceptionsListCard = memo( emptyViewerTitle, emptyViewerBody, emptyViewerButtonText, - handleCancelExportModal, - handleConfirmExportModal, - showExportModal, + handleCancelExpiredExceptionsModal, + handleConfirmExpiredExceptionsModal, + showIncludeExpiredExceptionsModal, } = useExceptionsListCard({ exceptionsList, handleExport, handleDelete, + handleDuplicate, handleManageRules: onManageRules, }); @@ -259,10 +271,11 @@ export const ExceptionsListCard = memo( onRuleSelectionChange={onRuleSelectionChange} /> ) : null} - {showExportModal ? ( - ) : null} diff --git a/x-pack/plugins/security_solution/public/exceptions/components/expired_exceptions_list_items_modal/index.test.tsx b/x-pack/plugins/security_solution/public/exceptions/components/expired_exceptions_list_items_modal/index.test.tsx new file mode 100644 index 0000000000000..43ebd8197c8d5 --- /dev/null +++ b/x-pack/plugins/security_solution/public/exceptions/components/expired_exceptions_list_items_modal/index.test.tsx @@ -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 React from 'react'; + +import { IncludeExpiredExceptionsModal } from '.'; +import { fireEvent, render } from '@testing-library/react'; + +describe('IncludeExpiredExceptionsModal', () => { + const handleCloseModal = jest.fn(); + const onModalConfirm = jest.fn(); + + it('should call handleCloseModal on cancel click', () => { + const wrapper = render( + + ); + fireEvent.click(wrapper.getByTestId('confirmModalCancelButton')); + expect(handleCloseModal).toHaveBeenCalled(); + }); + + it('should call onModalConfirm on confirm click', () => { + const wrapper = render( + + ); + fireEvent.click(wrapper.getByTestId('confirmModalConfirmButton')); + expect(onModalConfirm).toHaveBeenCalled(); + }); +}); diff --git a/x-pack/plugins/security_solution/public/exceptions/components/expired_exceptions_list_items_modal/index.tsx b/x-pack/plugins/security_solution/public/exceptions/components/expired_exceptions_list_items_modal/index.tsx new file mode 100644 index 0000000000000..17588e6b305a7 --- /dev/null +++ b/x-pack/plugins/security_solution/public/exceptions/components/expired_exceptions_list_items_modal/index.tsx @@ -0,0 +1,75 @@ +/* + * 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, { memo, useCallback, useState } from 'react'; + +import { EuiConfirmModal, EuiSpacer, EuiSwitch, EuiText } from '@elastic/eui'; +import * as i18n from '../../translations'; + +export const CHECK_EXCEPTION_TTL_ACTION_TYPES = { + DUPLICATE: 'duplicate', + EXPORT: 'export', +} as const; + +export type CheckExceptionTtlActionTypes = + typeof CHECK_EXCEPTION_TTL_ACTION_TYPES[keyof typeof CHECK_EXCEPTION_TTL_ACTION_TYPES]; + +interface IncludeExpiredExceptionsModalProps { + handleCloseModal: () => void; + onModalConfirm: (includeExpired: boolean) => void; + action: CheckExceptionTtlActionTypes; +} + +export const IncludeExpiredExceptionsModal = memo( + ({ handleCloseModal, onModalConfirm, action }) => { + const [includeExpired, setIncludeExpired] = useState(true); + + const handleSwitchChange = useCallback(() => { + setIncludeExpired(!includeExpired); + }, [setIncludeExpired, includeExpired]); + + const handleConfirm = useCallback(() => { + onModalConfirm(includeExpired); + handleCloseModal(); + }, [includeExpired, handleCloseModal, onModalConfirm]); + + return ( + + + {action === CHECK_EXCEPTION_TTL_ACTION_TYPES.EXPORT + ? i18n.EXPIRED_EXCEPTIONS_MODAL_EXPORT_DESCRIPTION + : i18n.EXPIRED_EXCEPTIONS_MODAL_DUPLICATE_DESCRIPTION} + + + + + ); + } +); + +IncludeExpiredExceptionsModal.displayName = 'IncludeExpiredExceptionsModal'; diff --git a/x-pack/plugins/security_solution/public/exceptions/components/export_exceptions_list_modal/index.tsx b/x-pack/plugins/security_solution/public/exceptions/components/export_exceptions_list_modal/index.tsx deleted file mode 100644 index 7316925cc24cf..0000000000000 --- a/x-pack/plugins/security_solution/public/exceptions/components/export_exceptions_list_modal/index.tsx +++ /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 React, { memo, useCallback, useState } from 'react'; - -import { EuiConfirmModal, EuiSwitch } from '@elastic/eui'; -import * as i18n from '../../translations'; - -interface ExportExceptionsListModalProps { - handleCloseModal: () => void; - onModalConfirm: (includeExpired: boolean) => void; -} - -export const ExportExceptionsListModal = memo( - ({ handleCloseModal, onModalConfirm }) => { - const [exportExpired, setExportExpired] = useState(true); - - const handleSwitchChange = useCallback(() => { - setExportExpired(!exportExpired); - }, [setExportExpired, exportExpired]); - - const handleConfirm = useCallback(() => { - onModalConfirm(exportExpired); - handleCloseModal(); - }, [exportExpired, handleCloseModal, onModalConfirm]); - - return ( - - - - ); - } -); - -ExportExceptionsListModal.displayName = 'ExportExceptionsListModal'; diff --git a/x-pack/plugins/security_solution/public/exceptions/hooks/use_exceptions_list.card/index.tsx b/x-pack/plugins/security_solution/public/exceptions/hooks/use_exceptions_list.card/index.tsx index 27ce5f3604c2f..82440e478edcd 100644 --- a/x-pack/plugins/security_solution/public/exceptions/hooks/use_exceptions_list.card/index.tsx +++ b/x-pack/plugins/security_solution/public/exceptions/hooks/use_exceptions_list.card/index.tsx @@ -20,7 +20,15 @@ import type { ExceptionListInfo } from '../use_all_exception_lists'; import { useListExceptionItems } from '../use_list_exception_items'; import * as i18n from '../../translations'; import { checkIfListCannotBeEdited } from '../../utils/list.utils'; +import type { CheckExceptionTtlActionTypes } from '../../components/expired_exceptions_list_items_modal'; +import { CHECK_EXCEPTION_TTL_ACTION_TYPES } from '../../components/expired_exceptions_list_items_modal'; +interface DuplicateListAction { + listId: string; + name: string; + namespaceType: NamespaceType; + includeExpiredExceptions: boolean; +} interface ExportListAction { id: string; listId: string; @@ -37,6 +45,7 @@ export const useExceptionsListCard = ({ exceptionsList, handleExport, handleDelete, + handleDuplicate, handleManageRules, }: { exceptionsList: ExceptionListInfo; @@ -48,13 +57,20 @@ export const useExceptionsListCard = ({ includeExpiredExceptions, }: ExportListAction) => () => Promise; handleDelete: ({ id, listId, namespaceType }: ListAction) => () => Promise; + handleDuplicate: ({ + listId, + name, + namespaceType, + includeExpiredExceptions, + }: DuplicateListAction) => () => Promise; handleManageRules: () => void; }) => { const [viewerStatus, setViewerStatus] = useState(ViewerStatus.LOADING); const [exceptionToEdit, setExceptionToEdit] = useState(); const [showAddExceptionFlyout, setShowAddExceptionFlyout] = useState(false); const [showEditExceptionFlyout, setShowEditExceptionFlyout] = useState(false); - const [showExportModal, setShowExportModal] = useState(false); + const [showIncludeExpiredExceptionsModal, setShowIncludeExpiredExceptionsModal] = + useState(null); const { name: listName, @@ -135,10 +151,19 @@ export const useExceptionsListCard = ({ includeExpiredExceptions: true, })(); } else { - setShowExportModal(true); + setShowIncludeExpiredExceptionsModal(CHECK_EXCEPTION_TTL_ACTION_TYPES.EXPORT); } }, }, + { + key: 'Duplicate', + icon: 'copy', + label: i18n.DUPLICATE_EXCEPTION_LIST, + disabled: listCannotBeEdited, + onClick: (_: React.MouseEvent) => { + setShowIncludeExpiredExceptionsModal(CHECK_EXCEPTION_TTL_ACTION_TYPES.DUPLICATE); + }, + }, { key: 'Delete', icon: 'trash', @@ -163,16 +188,15 @@ export const useExceptionsListCard = ({ }, ], [ + listCannotBeEdited, + listType, + handleExport, exceptionsList.id, exceptionsList.list_id, exceptionsList.name, exceptionsList.namespace_type, handleDelete, - setShowExportModal, - listCannotBeEdited, handleManageRules, - handleExport, - listType, ] ); @@ -197,24 +221,42 @@ export const useExceptionsListCard = ({ ); const onExportListClick = useCallback(() => { - setShowExportModal(true); - }, [setShowExportModal]); + setShowIncludeExpiredExceptionsModal(CHECK_EXCEPTION_TTL_ACTION_TYPES.EXPORT); + }, [setShowIncludeExpiredExceptionsModal]); - const handleCancelExportModal = () => { - setShowExportModal(false); + const handleCancelExpiredExceptionsModal = () => { + setShowIncludeExpiredExceptionsModal(null); }; - const handleConfirmExportModal = useCallback( + const handleConfirmExpiredExceptionsModal = useCallback( (includeExpiredExceptions: boolean): void => { - handleExport({ - id: exceptionsList.id, - listId: exceptionsList.list_id, - name: exceptionsList.name, - namespaceType: exceptionsList.namespace_type, - includeExpiredExceptions, - })(); + if (showIncludeExpiredExceptionsModal === CHECK_EXCEPTION_TTL_ACTION_TYPES.EXPORT) { + handleExport({ + id: exceptionsList.id, + listId: exceptionsList.list_id, + name: exceptionsList.name, + namespaceType: exceptionsList.namespace_type, + includeExpiredExceptions, + })(); + } + if (showIncludeExpiredExceptionsModal === CHECK_EXCEPTION_TTL_ACTION_TYPES.DUPLICATE) { + handleDuplicate({ + listId: exceptionsList.list_id, + name: exceptionsList.name, + namespaceType: exceptionsList.namespace_type, + includeExpiredExceptions, + })(); + } }, - [handleExport, exceptionsList] + [ + showIncludeExpiredExceptionsModal, + handleExport, + exceptionsList.id, + exceptionsList.list_id, + exceptionsList.name, + exceptionsList.namespace_type, + handleDuplicate, + ] ); // routes to x-pack/plugins/security_solution/public/exceptions/routes.tsx @@ -255,9 +297,9 @@ export const useExceptionsListCard = ({ emptyViewerTitle, emptyViewerBody, emptyViewerButtonText, - showExportModal, + showIncludeExpiredExceptionsModal, onExportListClick, - handleCancelExportModal, - handleConfirmExportModal, + handleCancelExpiredExceptionsModal, + handleConfirmExpiredExceptionsModal, }; }; diff --git a/x-pack/plugins/security_solution/public/exceptions/hooks/use_list_detail_view/index.ts b/x-pack/plugins/security_solution/public/exceptions/hooks/use_list_detail_view/index.ts index 4c70b0dc4521b..99f3288379537 100644 --- a/x-pack/plugins/security_solution/public/exceptions/hooks/use_list_detail_view/index.ts +++ b/x-pack/plugins/security_solution/public/exceptions/hooks/use_list_detail_view/index.ts @@ -51,7 +51,7 @@ export const useListDetailsView = (exceptionListId: string) => { const { http, notifications } = services; const { navigateToApp } = services.application; - const { exportExceptionList, deleteExceptionList } = useApi(http); + const { exportExceptionList, deleteExceptionList, duplicateExceptionList } = useApi(http); const [{ loading: userInfoLoading, canUserCRUD, canUserREAD }] = useUserData(); @@ -190,6 +190,35 @@ export const useListDetailsView = (exceptionListId: string) => { [list, exportExceptionList, handleErrorStatus, toasts] ); + const onDuplicateList = useCallback( + async (includeExpiredExceptions: boolean) => { + try { + if (!list) return; + await duplicateExceptionList({ + listId: list.list_id, + includeExpiredExceptions, + namespaceType: list.namespace_type, + onError: (error: Error) => handleErrorStatus(error), + onSuccess: (newList: ExceptionListSchema) => { + toasts?.addSuccess(i18n.EXCEPTION_LIST_DUPLICATED_SUCCESSFULLY(list.name)); + navigateToApp(APP_UI_ID, { + deepLinkId: SecurityPageName.exceptions, + path: `/details/${newList.list_id}`, + }); + }, + }); + } catch (error) { + handleErrorStatus( + error, + undefined, + i18n.EXCEPTION_DUPLICATE_ERROR, + i18n.EXCEPTION_DUPLICATE_ERROR_DESCRIPTION + ); + } + }, + [list, duplicateExceptionList, handleErrorStatus, toasts, navigateToApp] + ); + const handleOnDownload = useCallback(() => { setExportedList(undefined); }, []); @@ -384,6 +413,7 @@ export const useListDetailsView = (exceptionListId: string) => { refreshExceptions, disableManageButton, handleDelete, + onDuplicateList, onEditListDetails, onExportList, onDeleteList, 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 1e5b0af5768a4..023ebe69ab2fe 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 @@ -25,7 +25,8 @@ import { AutoDownload } from '../../../common/components/auto_download/auto_down import { ListWithSearch, ManageRules, ListDetailsLinkAnchor } from '../../components'; import { useListDetailsView } from '../../hooks'; import * as i18n from '../../translations'; -import { ExportExceptionsListModal } from '../../components/export_exceptions_list_modal'; +import type { CheckExceptionTtlActionTypes } from '../../components/expired_exceptions_list_items_modal'; +import { IncludeExpiredExceptionsModal } from '../../components/expired_exceptions_list_items_modal'; export const ListsDetailViewComponent: FC = () => { const { detailName: exceptionListId } = useParams<{ @@ -52,6 +53,7 @@ export const ListsDetailViewComponent: FC = () => { refreshExceptions, disableManageButton, onEditListDetails, + onDuplicateList, onExportList, onManageRules, onSaveManageRules, @@ -62,20 +64,33 @@ export const ListsDetailViewComponent: FC = () => { handleReferenceDelete, } = useListDetailsView(exceptionListId); - const [showExportModal, setShowExportModal] = useState(false); + const [showIncludeExpiredExceptionItemsModal, setShowIncludeExpiredExceptionItemsModal] = + useState(null); - const onModalClose = useCallback(() => setShowExportModal(false), [setShowExportModal]); + const onModalClose = useCallback( + () => setShowIncludeExpiredExceptionItemsModal(null), + [setShowIncludeExpiredExceptionItemsModal] + ); - const onModalOpen = useCallback(() => setShowExportModal(true), [setShowExportModal]); + const onModalOpen = useCallback( + (actionType: CheckExceptionTtlActionTypes) => { + setShowIncludeExpiredExceptionItemsModal(actionType); + }, + [setShowIncludeExpiredExceptionItemsModal] + ); const handleExportList = useCallback(() => { if (list?.type === ExceptionListTypeEnum.ENDPOINT) { onExportList(true); } else { - onModalOpen(); + onModalOpen('export'); } }, [onModalOpen, list, onExportList]); + const handleDuplicateList = useCallback(() => { + onModalOpen('duplicate'); + }, [onModalOpen]); + const detailsViewContent = useMemo(() => { if (viewerStatus === ViewerStatus.ERROR) return ; @@ -99,6 +114,7 @@ export const ListsDetailViewComponent: FC = () => { onExportList={handleExportList} onDeleteList={handleDelete} onManageRules={onManageRules} + onDuplicateList={handleDuplicateList} dataTestSubj="exceptionListManagement" /> @@ -125,47 +141,52 @@ export const ListsDetailViewComponent: FC = () => { onRuleSelectionChange={onRuleSelectionChange} /> ) : null} - {showExportModal && ( - )} ); }, [ - canUserEditList, - disableManageButton, - exportedList, - handleOnDownload, - headerBackOptions, - invalidListId, - isLoading, + viewerStatus, isReadOnly, - linkedRules, + isLoading, + invalidListId, + listName, list, listDescription, listId, - listName, + linkedRules, + canUserEditList, + headerBackOptions, + onEditListDetails, + handleExportList, + handleDelete, + onManageRules, + handleDuplicateList, + exportedList, + handleOnDownload, + refreshExceptions, referenceModalState.contentText, referenceModalState.rulesReferences, - refreshExceptions, - showManageButtonLoader, - showManageRulesFlyout, + handleCloseReferenceErrorModal, + handleReferenceDelete, showReferenceErrorModal, - showExportModal, - viewerStatus, + showManageRulesFlyout, + showManageButtonLoader, + disableManageButton, + onSaveManageRules, onCancelManageRules, - onEditListDetails, - onExportList, - onManageRules, onRuleSelectionChange, - onSaveManageRules, - handleCloseReferenceErrorModal, - handleDelete, - handleReferenceDelete, + showIncludeExpiredExceptionItemsModal, + onExportList, + onDuplicateList, onModalClose, - handleExportList, ]); return ( <> diff --git a/x-pack/plugins/security_solution/public/exceptions/pages/shared_lists/index.tsx b/x-pack/plugins/security_solution/public/exceptions/pages/shared_lists/index.tsx index 4fe0596941294..22b41e88bfb85 100644 --- a/x-pack/plugins/security_solution/public/exceptions/pages/shared_lists/index.tsx +++ b/x-pack/plugins/security_solution/public/exceptions/pages/shared_lists/index.tsx @@ -93,7 +93,7 @@ export const SharedLists = React.memo(() => { application: { navigateToApp }, }, } = useKibana(); - const { exportExceptionList, deleteExceptionList } = useApi(http); + const { exportExceptionList, deleteExceptionList, duplicateExceptionList } = useApi(http); const [showReferenceErrorModal, setShowReferenceErrorModal] = useState(false); const [referenceModalState, setReferenceModalState] = useState( @@ -262,6 +262,45 @@ export const SharedLists = React.memo(() => { [] ); + const handleDuplicationError = useCallback( + (err: Error) => { + addError(err, { title: i18n.EXCEPTION_DUPLICATE_ERROR }); + }, + [addError] + ); + + const handleDuplicateSuccess = useCallback( + (name: string) => (): void => { + addSuccess(i18n.EXCEPTION_LIST_DUPLICATED_SUCCESSFULLY(name)); + handleRefresh(); + }, + [addSuccess, handleRefresh] + ); + + const handleDuplicate = useCallback( + ({ + listId, + name, + namespaceType, + includeExpiredExceptions, + }: { + listId: string; + name: string; + namespaceType: NamespaceType; + includeExpiredExceptions: boolean; + }) => + async () => { + await duplicateExceptionList({ + includeExpiredExceptions, + listId, + namespaceType, + onError: handleDuplicationError, + onSuccess: handleDuplicateSuccess(name), + }); + }, + [duplicateExceptionList, handleDuplicateSuccess, handleDuplicationError] + ); + const handleCloseReferenceErrorModal = useCallback((): void => { setShowReferenceErrorModal(false); setReferenceModalState({ @@ -546,6 +585,7 @@ export const SharedLists = React.memo(() => { exceptionsList={excList} handleDelete={handleDelete} handleExport={handleExport} + handleDuplicate={handleDuplicate} /> ))}
diff --git a/x-pack/plugins/security_solution/public/exceptions/translations/list_details_view.ts b/x-pack/plugins/security_solution/public/exceptions/translations/list_details_view.ts index 6d1f8c115fab3..e2688d804b3d2 100644 --- a/x-pack/plugins/security_solution/public/exceptions/translations/list_details_view.ts +++ b/x-pack/plugins/security_solution/public/exceptions/translations/list_details_view.ts @@ -167,3 +167,17 @@ export const EXCEPTION_EXPORT_ERROR_DESCRIPTION = i18n.translate( defaultMessage: 'An error occurred exporting a list', } ); + +export const DUPLICATE_EXCEPTION_LIST = i18n.translate( + 'xpack.securitySolution.exceptionsTable.duplicateExceptionList', + { + defaultMessage: 'Duplicate exception list', + } +); + +export const EXCEPTION_DUPLICATE_ERROR_DESCRIPTION = i18n.translate( + 'xpack.securitySolution.exceptionsTable.duplicateListDescription', + { + defaultMessage: 'An error occurred duplicating a list', + } +); diff --git a/x-pack/plugins/security_solution/public/exceptions/translations/shared_list.ts b/x-pack/plugins/security_solution/public/exceptions/translations/shared_list.ts index 6ab1ca6df8464..eb18283577369 100644 --- a/x-pack/plugins/security_solution/public/exceptions/translations/shared_list.ts +++ b/x-pack/plugins/security_solution/public/exceptions/translations/shared_list.ts @@ -125,6 +125,19 @@ export const EXCEPTION_EXPORT_ERROR = i18n.translate( } ); +export const EXCEPTION_LIST_DUPLICATED_SUCCESSFULLY = (listName: string) => + i18n.translate('xpack.securitySolution.exceptions.list.duplicate_success', { + values: { listName }, + defaultMessage: 'Exception list "{listName}" duplicated successfully', + }); + +export const EXCEPTION_DUPLICATE_ERROR = i18n.translate( + 'xpack.securitySolution.detectionEngine.rules.all.exceptions.duplicateError', + { + defaultMessage: 'Exception list duplication error', + } +); + export const EXCEPTION_DELETE_ERROR = i18n.translate( 'xpack.securitySolution.detectionEngine.rules.all.exceptions.deleteError', { @@ -371,29 +384,59 @@ export const SORT_BY_CREATE_AT = i18n.translate( } ); -export const EXPORT_MODAL_CANCEL_BUTTON = i18n.translate( - 'xpack.securitySolution.exceptions.exportModalCancelButton', +export const EXPIRED_EXCEPTIONS_MODAL_CANCEL_BUTTON = i18n.translate( + 'xpack.securitySolution.exceptions.expiredExceptionModalCancelButton', { defaultMessage: 'Cancel', } ); -export const EXPORT_MODAL_TITLE = i18n.translate( - 'xpack.securitySolution.exceptions.exportModalTitle', +export const EXPIRED_EXCEPTIONS_MODAL_EXPORT_TITLE = i18n.translate( + 'xpack.securitySolution.exceptions.expiredExceptionModalExportTitle', { - defaultMessage: 'Export exception list', + defaultMessage: 'Export exception list?', } ); -export const EXPORT_MODAL_INCLUDE_SWITCH_LABEL = i18n.translate( - 'xpack.securitySolution.exceptions.exportModalIncludeSwitchLabel', +export const EXPIRED_EXCEPTIONS_MODAL_DUPLICATE_TITLE = i18n.translate( + 'xpack.securitySolution.exceptions.expiredExceptionModalDuplicateTitle', + { + defaultMessage: 'Duplicate exception list?', + } +); + +export const EXPIRED_EXCEPTIONS_MODAL_DUPLICATE_DESCRIPTION = i18n.translate( + 'xpack.securitySolution.exceptions.expiredExceptionModalIncludeDuplicateDescription', + { + defaultMessage: + 'You’re duplicating an exception list. Switch the toggle off to exclude expired exceptions.', + } +); + +export const EXPIRED_EXCEPTIONS_MODAL_EXPORT_DESCRIPTION = i18n.translate( + 'xpack.securitySolution.exceptions.expiredExceptionModalIncludeExportDescription', + { + defaultMessage: + 'You’re exporting an exception list. Switch the toggle off to exclude expired exceptions.', + } +); + +export const EXPIRED_EXCEPTIONS_MODAL_INCLUDE_SWITCH_LABEL = i18n.translate( + 'xpack.securitySolution.exceptions.expiredExceptionModalIncludeSwitchLabel', { defaultMessage: 'Include expired exceptions', } ); -export const EXPORT_MODAL_CONFIRM_BUTTON = i18n.translate( - 'xpack.securitySolution.exceptions.exportModalConfirmButton', +export const EXPIRED_EXCEPTIONS_MODAL_CONFIRM_DUPLICATE_BUTTON = i18n.translate( + 'xpack.securitySolution.exceptions.expiredExceptionModalConfirmDuplicateButton', + { + defaultMessage: 'Duplicate', + } +); + +export const EXPIRED_EXCEPTIONS_MODAL_CONFIRM_EXPORT_BUTTON = i18n.translate( + 'xpack.securitySolution.exceptions.expiredExceptionModalConfirmExportButton', { defaultMessage: 'Export', } diff --git a/x-pack/plugins/security_solution/public/explore/components/risk_score/constants.ts b/x-pack/plugins/security_solution/public/explore/components/risk_score/constants.ts index 7f2a8e37b94b0..d6147328d6be2 100644 --- a/x-pack/plugins/security_solution/public/explore/components/risk_score/constants.ts +++ b/x-pack/plugins/security_solution/public/explore/components/risk_score/constants.ts @@ -7,3 +7,7 @@ export const RISKY_HOSTS_DASHBOARD_TITLE = 'Current Risk Score for Hosts'; export const RISKY_USERS_DASHBOARD_TITLE = 'Current Risk Score for Users'; + +export const CELL_ACTIONS_TELEMETRY = { + component: 'RiskScoreTable', +}; diff --git a/x-pack/plugins/security_solution/public/explore/hosts/components/host_risk_score_table/columns.tsx b/x-pack/plugins/security_solution/public/explore/hosts/components/host_risk_score_table/columns.tsx index f1a489ed83b73..d9b57d3da7c29 100644 --- a/x-pack/plugins/security_solution/public/explore/hosts/components/host_risk_score_table/columns.tsx +++ b/x-pack/plugins/security_solution/public/explore/hosts/components/host_risk_score_table/columns.tsx @@ -21,6 +21,7 @@ import type { RiskSeverity } from '../../../../../common/search_strategy'; import { RiskScoreFields, RiskScoreEntity } from '../../../../../common/search_strategy'; import { RiskScore } from '../../../components/risk_score/severity/common'; import { ENTITY_RISK_CLASSIFICATION } from '../../../components/risk_score/translations'; +import { CELL_ACTIONS_TELEMETRY } from '../../../components/risk_score/constants'; export const getHostRiskScoreColumns = ({ dispatchSeverityUpdate, @@ -37,7 +38,7 @@ export const getHostRiskScoreColumns = ({ if (hostName != null && hostName.length > 0) { return ( diff --git a/x-pack/plugins/security_solution/public/explore/hosts/components/hosts_table/columns.tsx b/x-pack/plugins/security_solution/public/explore/hosts/components/hosts_table/columns.tsx index 2c8eba08178d0..07b458c14760f 100644 --- a/x-pack/plugins/security_solution/public/explore/hosts/components/hosts_table/columns.tsx +++ b/x-pack/plugins/security_solution/public/explore/hosts/components/hosts_table/columns.tsx @@ -38,7 +38,7 @@ export const getHostsColumns = ( if (hostName != null && hostName.length > 0) { return ( [ return ( { title={ diff --git a/x-pack/plugins/security_solution/public/explore/users/components/user_risk_score_table/columns.tsx b/x-pack/plugins/security_solution/public/explore/users/components/user_risk_score_table/columns.tsx index cf122adc0c397..e6af3c9a873f7 100644 --- a/x-pack/plugins/security_solution/public/explore/users/components/user_risk_score_table/columns.tsx +++ b/x-pack/plugins/security_solution/public/explore/users/components/user_risk_score_table/columns.tsx @@ -22,6 +22,7 @@ import { RiskScoreEntity, RiskScoreFields } from '../../../../../common/search_s import { UserDetailsLink } from '../../../../common/components/links'; import { UsersTableType } from '../../store/model'; import { ENTITY_RISK_CLASSIFICATION } from '../../../components/risk_score/translations'; +import { CELL_ACTIONS_TELEMETRY } from '../../../components/risk_score/constants'; export const getUserRiskScoreColumns = ({ dispatchSeverityUpdate, @@ -40,7 +41,7 @@ export const getUserRiskScoreColumns = ({ return ( diff --git a/x-pack/plugins/security_solution/public/flyout/left/components/analyze.stories.tsx b/x-pack/plugins/security_solution/public/flyout/left/components/analyze_graph.stories.tsx similarity index 100% rename from x-pack/plugins/security_solution/public/flyout/left/components/analyze.stories.tsx rename to x-pack/plugins/security_solution/public/flyout/left/components/analyze_graph.stories.tsx diff --git a/x-pack/plugins/security_solution/public/flyout/left/components/session_view.stories.tsx b/x-pack/plugins/security_solution/public/flyout/left/components/session_view.stories.tsx new file mode 100644 index 0000000000000..40b9b7e7a5b18 --- /dev/null +++ b/x-pack/plugins/security_solution/public/flyout/left/components/session_view.stories.tsx @@ -0,0 +1,44 @@ +/* + * 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 type { Story } from '@storybook/react'; +import { SessionView } from './session_view'; +import type { LeftPanelContext } from '../context'; +import { LeftFlyoutContext } from '../context'; + +export default { + component: SessionView, + title: 'Flyout/SessionView', +}; + +// TODO to get this working, we need to spent some time getting all the foundation items for storybook +// (ReduxStoreProvider, CellActionsProvider...) similarly to how it was done for the TestProvidersComponent +// see ticket https://github.com/elastic/security-team/issues/6223 +// export const Default: Story = () => { +// const contextValue = { +// getFieldsData: () => {}, +// } as unknown as LeftPanelContext; +// +// return ( +// +// +// +// ); +// }; + +export const Error: Story = () => { + const contextValue = { + getFieldsData: () => {}, + } as unknown as LeftPanelContext; + + return ( + + + + ); +}; diff --git a/x-pack/plugins/security_solution/public/flyout/left/components/session_view.test.tsx b/x-pack/plugins/security_solution/public/flyout/left/components/session_view.test.tsx new file mode 100644 index 0000000000000..d334f60ba2eb3 --- /dev/null +++ b/x-pack/plugins/security_solution/public/flyout/left/components/session_view.test.tsx @@ -0,0 +1,63 @@ +/* + * 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 { render } from '@testing-library/react'; +import '@testing-library/jest-dom'; +import type { LeftPanelContext } from '../context'; +import { LeftFlyoutContext } from '../context'; +import { TestProviders } from '../../../common/mock'; +import { SESSION_VIEW_ERROR_TEST_ID, SESSION_VIEW_TEST_ID } from './test_ids'; +import { SessionView } from './session_view'; + +jest.mock('../../../common/lib/kibana', () => { + const originalModule = jest.requireActual('../../../common/lib/kibana'); + return { + ...originalModule, + useKibana: jest.fn().mockReturnValue({ + services: { + sessionView: { + getSessionView: jest.fn().mockReturnValue(
), + }, + }, + }), + }; +}); + +describe('', () => { + it('renders session view correctly', () => { + const contextValue = { + getFieldsData: () => 'id', + } as unknown as LeftPanelContext; + + const wrapper = render( + + + + + + ); + expect(wrapper.getByTestId(SESSION_VIEW_TEST_ID)).toBeInTheDocument(); + }); + + it('should render error message on null eventId', () => { + const contextValue = { + getFieldsData: () => {}, + } as unknown as LeftPanelContext; + + const wrapper = render( + + + + + + ); + expect(wrapper.getByTestId(SESSION_VIEW_ERROR_TEST_ID)).toBeInTheDocument(); + expect(wrapper.getByText('Unable to display session view')).toBeInTheDocument(); + expect(wrapper.getByText('There was an error displaying session view')).toBeInTheDocument(); + }); +}); diff --git a/x-pack/plugins/security_solution/public/flyout/left/components/session_view.tsx b/x-pack/plugins/security_solution/public/flyout/left/components/session_view.tsx index e62745b905640..d48afe6e3f712 100644 --- a/x-pack/plugins/security_solution/public/flyout/left/components/session_view.tsx +++ b/x-pack/plugins/security_solution/public/flyout/left/components/session_view.tsx @@ -7,16 +7,46 @@ import type { FC } from 'react'; import React from 'react'; -import { EuiText } from '@elastic/eui'; -import { SESSION_VIEW_TEST_ID } from './test_ids'; +import { EuiEmptyPrompt } from '@elastic/eui'; +import { getField } from '../../shared/utils'; +import { ERROR_MESSAGE, ERROR_TITLE } from '../../shared/translations'; +import { SESSION_VIEW_ERROR_MESSAGE } from './translations'; +import { SESSION_VIEW_ERROR_TEST_ID, SESSION_VIEW_TEST_ID } from './test_ids'; +import { useKibana } from '../../../common/lib/kibana'; +import { useLeftPanelContext } from '../context'; export const SESSION_VIEW_ID = 'session_view'; +const SESSION_ENTITY_ID = 'process.entry_leader.entity_id'; /** * Session view displayed in the document details expandable flyout left section under the Visualize tab */ export const SessionView: FC = () => { - return {'Session view'}; + const { sessionView } = useKibana().services; + const { getFieldsData } = useLeftPanelContext(); + + const sessionEntityId = getField(getFieldsData(SESSION_ENTITY_ID)); + + if (!sessionEntityId) { + return ( + {ERROR_TITLE(SESSION_VIEW_ERROR_MESSAGE)}} + body={

{ERROR_MESSAGE(SESSION_VIEW_ERROR_MESSAGE)}

} + data-test-subj={SESSION_VIEW_ERROR_TEST_ID} + /> + ); + } + + return ( +
+ {sessionView.getSessionView({ + sessionEntityId, + isFullScreen: true, + })} +
+ ); }; SessionView.displayName = 'SessionView'; diff --git a/x-pack/plugins/security_solution/public/flyout/left/components/test_ids.ts b/x-pack/plugins/security_solution/public/flyout/left/components/test_ids.ts index 8b2804fae3e9f..40cf67fddb180 100644 --- a/x-pack/plugins/security_solution/public/flyout/left/components/test_ids.ts +++ b/x-pack/plugins/security_solution/public/flyout/left/components/test_ids.ts @@ -9,6 +9,7 @@ export const ANALYZER_GRAPH_TEST_ID = 'securitySolutionDocumentDetailsFlyoutAnal export const ANALYZE_GRAPH_ERROR_TEST_ID = 'securitySolutionDocumentDetailsFlyoutAnalyzerGraphError'; export const SESSION_VIEW_TEST_ID = 'securitySolutionDocumentDetailsFlyoutSessionView'; +export const SESSION_VIEW_ERROR_TEST_ID = 'securitySolutionDocumentDetailsFlyoutSessionViewError'; export const ENTITIES_DETAILS_TEST_ID = 'securitySolutionDocumentDetailsFlyoutEntitiesDetails'; export const THREAT_INTELLIGENCE_DETAILS_TEST_ID = 'securitySolutionDocumentDetailsFlyoutThreatIntelligenceDetails'; diff --git a/x-pack/plugins/security_solution/public/flyout/left/components/translations.ts b/x-pack/plugins/security_solution/public/flyout/left/components/translations.ts index 8c59c8a101fb0..f82d34c859ddf 100644 --- a/x-pack/plugins/security_solution/public/flyout/left/components/translations.ts +++ b/x-pack/plugins/security_solution/public/flyout/left/components/translations.ts @@ -13,3 +13,10 @@ export const ANALYZER_ERROR_MESSAGE = i18n.translate( defaultMessage: 'analyzer', } ); + +export const SESSION_VIEW_ERROR_MESSAGE = i18n.translate( + 'xpack.securitySolution.flyout.sessionViewErrorTitle', + { + defaultMessage: 'session view', + } +); diff --git a/x-pack/plugins/security_solution/public/flyout/left/context.tsx b/x-pack/plugins/security_solution/public/flyout/left/context.tsx index 9b564666ea487..bf6adb5cd6a07 100644 --- a/x-pack/plugins/security_solution/public/flyout/left/context.tsx +++ b/x-pack/plugins/security_solution/public/flyout/left/context.tsx @@ -6,6 +6,16 @@ */ import React, { createContext, useContext, useMemo } from 'react'; +import { EuiFlexItem, EuiLoadingSpinner } from '@elastic/eui'; +import { css } from '@emotion/react'; +import { SecurityPageName } from '../../../common/constants'; +import { SourcererScopeName } from '../../common/store/sourcerer/model'; +import { useSourcererDataView } from '../../common/containers/sourcerer'; +import { useTimelineEventsDetails } from '../../timelines/containers/details'; +import { useGetFieldsData } from '../../common/hooks/use_get_fields_data'; +import { useRouteSpy } from '../../common/utils/route/use_route_spy'; +import { useSpaceId } from '../../common/hooks/use_space_id'; +import { getAlertIndexAlias } from '../../timelines/components/side_panel/event_details/helpers'; import type { LeftPanelProps } from '.'; export interface LeftPanelContext { @@ -17,6 +27,10 @@ export interface LeftPanelContext { * Name of the index used in the parent's page */ indexName: string; + /** + * Retrieves searchHit values for the provided field + */ + getFieldsData: (field: string) => unknown | unknown[]; } export const LeftFlyoutContext = createContext(undefined); @@ -29,11 +43,40 @@ export type LeftPanelProviderProps = { } & Partial; export const LeftPanelProvider = ({ id, indexName, children }: LeftPanelProviderProps) => { + const currentSpaceId = useSpaceId(); + const eventIndex = indexName ? getAlertIndexAlias(indexName, currentSpaceId) ?? indexName : ''; + const [{ pageName }] = useRouteSpy(); + const sourcererScope = + pageName === SecurityPageName.detections + ? SourcererScopeName.detections + : SourcererScopeName.default; + const sourcererDataView = useSourcererDataView(sourcererScope); + const [loading, _, searchHit] = useTimelineEventsDetails({ + indexName: eventIndex, + eventId: id ?? '', + runtimeMappings: sourcererDataView.runtimeMappings, + skip: !id, + }); + const getFieldsData = useGetFieldsData(searchHit?.fields); + const contextValue = useMemo( - () => (id && indexName ? { eventId: id, indexName } : undefined), - [id, indexName] + () => (id && indexName ? { eventId: id, indexName, getFieldsData } : undefined), + [id, indexName, getFieldsData] ); + if (loading) { + return ( + + + + ); + } + return {children}; }; diff --git a/x-pack/plugins/security_solution/public/flyout/right/components/highlighted_fields.test.tsx b/x-pack/plugins/security_solution/public/flyout/right/components/highlighted_fields.test.tsx index 52a85b66a3108..0045f30cecebc 100644 --- a/x-pack/plugins/security_solution/public/flyout/right/components/highlighted_fields.test.tsx +++ b/x-pack/plugins/security_solution/public/flyout/right/components/highlighted_fields.test.tsx @@ -54,7 +54,7 @@ describe('', () => { browserFields: {}, } as unknown as RightPanelContext; - const { baseElement } = render( + const { container } = render( @@ -62,11 +62,7 @@ describe('', () => { ); - expect(baseElement).toMatchInlineSnapshot(` - -
- - `); + expect(container).toBeEmptyDOMElement(); }); it('should render empty component if browserFields is null', () => { @@ -78,7 +74,7 @@ describe('', () => { browserFields: null, } as unknown as RightPanelContext; - const { baseElement } = render( + const { container } = render( @@ -86,11 +82,7 @@ describe('', () => { ); - expect(baseElement).toMatchInlineSnapshot(` - -
- - `); + expect(container).toBeEmptyDOMElement(); }); it('should render empty component if eventId is null', () => { @@ -102,7 +94,7 @@ describe('', () => { browserFields: {}, } as unknown as RightPanelContext; - const { baseElement } = render( + const { container } = render( @@ -110,10 +102,6 @@ describe('', () => { ); - expect(baseElement).toMatchInlineSnapshot(` - -
- - `); + expect(container).toBeEmptyDOMElement(); }); }); diff --git a/x-pack/plugins/security_solution/public/flyout/right/components/highlighted_fields.tsx b/x-pack/plugins/security_solution/public/flyout/right/components/highlighted_fields.tsx index af14a20788466..eaae42100ded3 100644 --- a/x-pack/plugins/security_solution/public/flyout/right/components/highlighted_fields.tsx +++ b/x-pack/plugins/security_solution/public/flyout/right/components/highlighted_fields.tsx @@ -33,7 +33,7 @@ export const HighlightedFields: FC = () => { }, [eventId, indexName, openRightPanel, scopeId]); if (!dataFormattedForFieldBrowser || !browserFields || !eventId) { - return <>; + return null; } return ( diff --git a/x-pack/plugins/security_solution/public/flyout/right/components/insights_section.tsx b/x-pack/plugins/security_solution/public/flyout/right/components/insights_section.tsx index 8409676b610b0..9efb979ba7a26 100644 --- a/x-pack/plugins/security_solution/public/flyout/right/components/insights_section.tsx +++ b/x-pack/plugins/security_solution/public/flyout/right/components/insights_section.tsx @@ -6,6 +6,7 @@ */ import React from 'react'; +import { ThreatIntelligenceOverview } from './threat_intelligence_overview'; import { INSIGHTS_TEST_ID } from './test_ids'; import { INSIGHTS_TITLE } from './translations'; import { EntitiesOverview } from './entities_overview'; @@ -25,6 +26,7 @@ export const InsightsSection: React.FC = ({ expanded = fal return ( + ); }; diff --git a/x-pack/plugins/security_solution/public/flyout/right/components/insights_subsection.stories.tsx b/x-pack/plugins/security_solution/public/flyout/right/components/insights_subsection.stories.tsx new file mode 100644 index 0000000000000..c1fc9dfe8a7f8 --- /dev/null +++ b/x-pack/plugins/security_solution/public/flyout/right/components/insights_subsection.stories.tsx @@ -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 React from 'react'; +import type { Story } from '@storybook/react'; +import { InsightsSubSection } from './insights_subsection'; + +export default { + component: InsightsSubSection, + title: 'Flyout/InsightsSubSection', +}; + +const title = 'Title'; +const children =
{'hello'}
; + +export const Basic: Story = () => { + return {children}; +}; + +export const Loading: Story = () => { + return ( + + {null} + + ); +}; + +export const NoTitle: Story = () => { + return {children}; +}; + +export const NoChildren: Story = () => { + return {null}; +}; diff --git a/x-pack/plugins/security_solution/public/flyout/right/components/insights_subsection.test.tsx b/x-pack/plugins/security_solution/public/flyout/right/components/insights_subsection.test.tsx new file mode 100644 index 0000000000000..271953c8e8105 --- /dev/null +++ b/x-pack/plugins/security_solution/public/flyout/right/components/insights_subsection.test.tsx @@ -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 React from 'react'; +import { render } from '@testing-library/react'; +import { InsightsSubSection } from './insights_subsection'; + +const title = 'Title'; +const dataTestSubj = 'test'; +const children =
{'hello'}
; + +describe('', () => { + it('should render children component', () => { + const { getByTestId } = render( + + {children} + + ); + + const titleDataTestSubj = `${dataTestSubj}Title`; + const contentDataTestSubj = `${dataTestSubj}Content`; + + expect(getByTestId(titleDataTestSubj)).toHaveTextContent(title); + expect(getByTestId(contentDataTestSubj)).toBeInTheDocument(); + }); + + it('should render loading component', () => { + const { getByTestId } = render( + + {children} + + ); + + const loadingDataTestSubj = `${dataTestSubj}Loading`; + expect(getByTestId(loadingDataTestSubj)).toBeInTheDocument(); + }); + + it('should render null if error', () => { + const { container } = render( + + {children} + + ); + + expect(container).toBeEmptyDOMElement(); + }); + + it('should render null if no title', () => { + const { container } = render({children}); + + expect(container).toBeEmptyDOMElement(); + }); + + it('should render null if no children', () => { + const { container } = render( + + {null} + + ); + + expect(container).toBeEmptyDOMElement(); + }); +}); diff --git a/x-pack/plugins/security_solution/public/flyout/right/components/insights_subsection.tsx b/x-pack/plugins/security_solution/public/flyout/right/components/insights_subsection.tsx new file mode 100644 index 0000000000000..4b5c1a541e316 --- /dev/null +++ b/x-pack/plugins/security_solution/public/flyout/right/components/insights_subsection.tsx @@ -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 React from 'react'; +import { EuiFlexGroup, EuiFlexItem, EuiLoadingSpinner, EuiSpacer, EuiTitle } from '@elastic/eui'; + +export interface InsightsSectionProps { + /** + * Renders a loading spinner if true + */ + loading?: boolean; + /** + * Returns a null component if true + */ + error?: boolean; + /** + * Title at the top of the component + */ + title: string; + /** + * Content of the component + */ + children: React.ReactNode; + /** + * Prefix data-test-subj to use for the elements + */ + ['data-test-subj']?: string; +} + +/** + * Presentational component to handle loading and error in the subsections of the Insights section. + * Should be used for Entities, Threat Intelligence, Prevalence, Correlations and Results + */ +export const InsightsSubSection: React.FC = ({ + loading = false, + error = false, + title, + 'data-test-subj': dataTestSubj, + children, +}) => { + const loadingDataTestSubj = `${dataTestSubj}Loading`; + // showing the loading in this component instead of SummaryPanel because we're hiding the entire section if no data + + if (loading) { + return ( + + + + + + ); + } + + // hide everything + if (error || !title || !children) { + return null; + } + + const titleDataTestSubj = `${dataTestSubj}Title`; + const contentDataTestSubj = `${dataTestSubj}Content`; + + return ( + <> + +
{title}
+
+ + + {children} + + + ); +}; + +InsightsSubSection.displayName = 'InsightsSubSection'; diff --git a/x-pack/plugins/security_solution/public/flyout/right/components/insights_summary_panel.stories.tsx b/x-pack/plugins/security_solution/public/flyout/right/components/insights_summary_panel.stories.tsx new file mode 100644 index 0000000000000..5637d3c036860 --- /dev/null +++ b/x-pack/plugins/security_solution/public/flyout/right/components/insights_summary_panel.stories.tsx @@ -0,0 +1,156 @@ +/* + * 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 type { Story } from '@storybook/react'; +import { css } from '@emotion/react'; +import type { InsightsSummaryPanelData } from './insights_summary_panel'; +import { InsightsSummaryPanel } from './insights_summary_panel'; + +export default { + component: InsightsSummaryPanel, + title: 'Flyout/InsightsSummaryPanel', +}; + +export const Default: Story = () => { + const data: InsightsSummaryPanelData[] = [ + { + icon: 'image', + value: 1, + text: 'this is a test for red', + color: 'rgb(189,39,30)', + }, + { + icon: 'warning', + value: 2, + text: 'this is test for orange', + color: 'rgb(255,126,98)', + }, + { + icon: 'warning', + value: 3, + text: 'this is test for yellow', + color: 'rgb(241,216,11)', + }, + ]; + + return ( +
+ +
+ ); +}; + +export const InvalidColor: Story = () => { + const data: InsightsSummaryPanelData[] = [ + { + icon: 'image', + value: 1, + text: 'this is a test for an invalid color (abc)', + color: 'abc', + }, + ]; + + return ( +
+ +
+ ); +}; + +export const NoColor: Story = () => { + const data: InsightsSummaryPanelData[] = [ + { + icon: 'image', + value: 1, + text: 'this is a test for red', + }, + { + icon: 'warning', + value: 2, + text: 'this is test for orange', + }, + { + icon: 'warning', + value: 3, + text: 'this is test for yellow', + }, + ]; + + return ( +
+ +
+ ); +}; + +export const LongText: Story = () => { + const data: InsightsSummaryPanelData[] = [ + { + icon: 'image', + value: 1, + text: 'this is an extremely long text to verify it is properly cut off and and we show three dots at the end', + color: 'abc', + }, + ]; + + return ( +
+ +
+ ); +}; +export const LongNumber: Story = () => { + const data: InsightsSummaryPanelData[] = [ + { + icon: 'image', + value: 160000, + text: 'this is an extremely long value to verify it is properly cut off and and we show three dots at the end', + color: 'abc', + }, + ]; + + return ( +
+ +
+ ); +}; + +export const NoData: Story = () => { + const data: InsightsSummaryPanelData[] = []; + + return ( +
+ +
+ ); +}; diff --git a/x-pack/plugins/security_solution/public/flyout/right/components/insights_summary_panel.test.tsx b/x-pack/plugins/security_solution/public/flyout/right/components/insights_summary_panel.test.tsx new file mode 100644 index 0000000000000..9ecbbcc7fc0a5 --- /dev/null +++ b/x-pack/plugins/security_solution/public/flyout/right/components/insights_summary_panel.test.tsx @@ -0,0 +1,90 @@ +/* + * 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 { render } from '@testing-library/react'; +import { __IntlProvider as IntlProvider } from '@kbn/i18n-react'; +import { + INSIGHTS_THREAT_INTELLIGENCE_COLOR_TEST_ID, + INSIGHTS_THREAT_INTELLIGENCE_ICON_TEST_ID, + INSIGHTS_THREAT_INTELLIGENCE_TEST_ID, + INSIGHTS_THREAT_INTELLIGENCE_VALUE_TEST_ID, +} from './test_ids'; +import type { InsightsSummaryPanelData } from './insights_summary_panel'; +import { InsightsSummaryPanel } from './insights_summary_panel'; + +describe('', () => { + it('should render by default', () => { + const data: InsightsSummaryPanelData[] = [ + { + icon: 'image', + value: 1, + text: 'this is a test for red', + color: 'rgb(189,39,30)', + }, + ]; + + const { getByTestId } = render( + + + + ); + + const iconTestId = `${INSIGHTS_THREAT_INTELLIGENCE_ICON_TEST_ID}0`; + const valueTestId = `${INSIGHTS_THREAT_INTELLIGENCE_VALUE_TEST_ID}0`; + const colorTestId = `${INSIGHTS_THREAT_INTELLIGENCE_COLOR_TEST_ID}0`; + expect(getByTestId(iconTestId)).toBeInTheDocument(); + expect(getByTestId(valueTestId)).toHaveTextContent('1 this is a test for red'); + expect(getByTestId(colorTestId)).toBeInTheDocument(); + }); + + it('should only render null when data is null', () => { + const data = null as unknown as InsightsSummaryPanelData[]; + + const { container } = render(); + + expect(container).toBeEmptyDOMElement(); + }); + + it('should handle big number in a compact notation', () => { + const data: InsightsSummaryPanelData[] = [ + { + icon: 'image', + value: 160000, + text: 'this is a test for red', + color: 'rgb(189,39,30)', + }, + ]; + + const { getByTestId } = render( + + + + ); + + const valueTestId = `${INSIGHTS_THREAT_INTELLIGENCE_VALUE_TEST_ID}0`; + expect(getByTestId(valueTestId)).toHaveTextContent('160k this is a test for red'); + }); + + it(`should not show the colored dot if color isn't provided`, () => { + const data: InsightsSummaryPanelData[] = [ + { + icon: 'image', + value: 160000, + text: 'this is a test for no color', + }, + ]; + + const { queryByTestId } = render( + + + + ); + + expect(queryByTestId(INSIGHTS_THREAT_INTELLIGENCE_COLOR_TEST_ID)).not.toBeInTheDocument(); + }); +}); diff --git a/x-pack/plugins/security_solution/public/flyout/right/components/insights_summary_panel.tsx b/x-pack/plugins/security_solution/public/flyout/right/components/insights_summary_panel.tsx new file mode 100644 index 0000000000000..306eaa101b804 --- /dev/null +++ b/x-pack/plugins/security_solution/public/flyout/right/components/insights_summary_panel.tsx @@ -0,0 +1,106 @@ +/* + * 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 { VFC } from 'react'; +import React from 'react'; +import { css } from '@emotion/react'; +import { EuiButtonIcon, EuiFlexGroup, EuiFlexItem, EuiHealth, EuiPanel } from '@elastic/eui'; +import { FormattedCount } from '../../../common/components/formatted_number'; + +export interface InsightsSummaryPanelData { + /** + * Icon to display on the left side of each row + */ + icon: string; + /** + * Number of results/entries found + */ + value: number; + /** + * Text corresponding of the number of results/entries + */ + text: string; + /** + * Optional parameter for now, will be used to display a dot on the right side + * (corresponding to some sort of severity?) + */ + color?: string; // TODO remove optional when we have guidance on what the colors will actually be +} + +export interface InsightsSummaryPanelProps { + /** + * Array of data to display in each row + */ + data: InsightsSummaryPanelData[]; + /** + * Prefix data-test-subj because this component will be used in multiple places + */ + ['data-test-subj']?: string; +} + +/** + * Panel showing summary information as an icon, a count and text as well as a severity colored dot. + * Should be used for Entities, Threat Intelligence, Prevalence, Correlations and Results components under the Insights section. + * The colored dot is currently optional but will ultimately be mandatory (waiting on PM and UIUX). + */ +export const InsightsSummaryPanel: VFC = ({ + data, + 'data-test-subj': dataTestSubj, +}) => { + if (!data || data.length === 0) { + return null; + } + + const iconDataTestSubj = `${dataTestSubj}Icon`; + const valueDataTestSubj = `${dataTestSubj}Value`; + const colorDataTestSubj = `${dataTestSubj}Color`; + + return ( + + + {data.map((row, index) => ( + + + + + + {row.text} + + {row.color && ( + + + + )} + + ))} + + + ); +}; + +InsightsSummaryPanel.displayName = 'InsightsSummaryPanel'; diff --git a/x-pack/plugins/security_solution/public/flyout/right/components/mitre_attack.test.tsx b/x-pack/plugins/security_solution/public/flyout/right/components/mitre_attack.test.tsx index f0bc9bd993f66..24e52e4ba5915 100644 --- a/x-pack/plugins/security_solution/public/flyout/right/components/mitre_attack.test.tsx +++ b/x-pack/plugins/security_solution/public/flyout/right/components/mitre_attack.test.tsx @@ -33,16 +33,12 @@ describe('', () => { }, } as unknown as RightPanelContext; - const { baseElement } = render( + const { container } = render( ); - expect(baseElement).toMatchInlineSnapshot(` - -
- - `); + expect(container).toBeEmptyDOMElement(); }); }); diff --git a/x-pack/plugins/security_solution/public/flyout/right/components/reason.test.tsx b/x-pack/plugins/security_solution/public/flyout/right/components/reason.test.tsx index d2a7c90011d68..b7050d1df0fa0 100644 --- a/x-pack/plugins/security_solution/public/flyout/right/components/reason.test.tsx +++ b/x-pack/plugins/security_solution/public/flyout/right/components/reason.test.tsx @@ -37,17 +37,13 @@ describe('', () => { dataAsNestedObject: {}, } as unknown as RightPanelContext; - const { baseElement } = render( + const { container } = render( ); - expect(baseElement).toMatchInlineSnapshot(` - -
- - `); + expect(container).toBeEmptyDOMElement(); }); it('should render null if dataAsNestedObject is null', () => { @@ -55,17 +51,13 @@ describe('', () => { dataFormattedForFieldBrowser: [], } as unknown as RightPanelContext; - const { baseElement } = render( + const { container } = render( ); - expect(baseElement).toMatchInlineSnapshot(` - -
- - `); + expect(container).toBeEmptyDOMElement(); }); it('should render null if renderer is null', () => { const panelContextValue = { @@ -73,16 +65,12 @@ describe('', () => { dataFormattedForFieldBrowser: [], } as unknown as RightPanelContext; - const { baseElement } = render( + const { container } = render( ); - expect(baseElement).toMatchInlineSnapshot(` - -
- - `); + expect(container).toBeEmptyDOMElement(); }); }); diff --git a/x-pack/plugins/security_solution/public/flyout/right/components/risk_score.test.tsx b/x-pack/plugins/security_solution/public/flyout/right/components/risk_score.test.tsx index 3b9364df1dd04..554b6c90db32a 100644 --- a/x-pack/plugins/security_solution/public/flyout/right/components/risk_score.test.tsx +++ b/x-pack/plugins/security_solution/public/flyout/right/components/risk_score.test.tsx @@ -38,17 +38,13 @@ describe('', () => { getFieldsData: jest.fn(), } as unknown as RightPanelContext; - const { baseElement } = render( + const { container } = render( ); - expect(baseElement).toMatchInlineSnapshot(` - -
- - `); + expect(container).toBeEmptyDOMElement(); }); it('should render empty component if getFieldsData is invalid', () => { @@ -56,16 +52,12 @@ describe('', () => { getFieldsData: jest.fn().mockImplementation(() => 123), } as unknown as RightPanelContext; - const { baseElement } = render( + const { container } = render( ); - expect(baseElement).toMatchInlineSnapshot(` - -
- - `); + expect(container).toBeEmptyDOMElement(); }); }); diff --git a/x-pack/plugins/security_solution/public/flyout/right/components/severity.test.tsx b/x-pack/plugins/security_solution/public/flyout/right/components/severity.test.tsx index e329459c6cbc1..92c8b0a382638 100644 --- a/x-pack/plugins/security_solution/public/flyout/right/components/severity.test.tsx +++ b/x-pack/plugins/security_solution/public/flyout/right/components/severity.test.tsx @@ -38,17 +38,13 @@ describe('', () => { getFieldsData: jest.fn(), } as unknown as RightPanelContext; - const { baseElement } = render( + const { container } = render( ); - expect(baseElement).toMatchInlineSnapshot(` - -
- - `); + expect(container).toBeEmptyDOMElement(); }); it('should render empty component if getFieldsData is invalid array', () => { @@ -56,17 +52,13 @@ describe('', () => { getFieldsData: jest.fn().mockImplementation(() => ['abc']), } as unknown as RightPanelContext; - const { baseElement } = render( + const { container } = render( ); - expect(baseElement).toMatchInlineSnapshot(` - -
- - `); + expect(container).toBeEmptyDOMElement(); }); it('should render empty component if getFieldsData is invalid string', () => { @@ -74,16 +66,12 @@ describe('', () => { getFieldsData: jest.fn().mockImplementation(() => 'abc'), } as unknown as RightPanelContext; - const { baseElement } = render( + const { container } = render( ); - expect(baseElement).toMatchInlineSnapshot(` - -
- - `); + expect(container).toBeEmptyDOMElement(); }); }); diff --git a/x-pack/plugins/security_solution/public/flyout/right/components/test_ids.ts b/x-pack/plugins/security_solution/public/flyout/right/components/test_ids.ts index e89c16b3f5f12..9ee38ba2940cb 100644 --- a/x-pack/plugins/security_solution/public/flyout/right/components/test_ids.ts +++ b/x-pack/plugins/security_solution/public/flyout/right/components/test_ids.ts @@ -84,6 +84,18 @@ export const ENTITIES_HOST_OVERVIEW_IP_TEST_ID = export const ENTITIES_HOST_OVERVIEW_RISK_LEVEL_TEST_ID = 'securitySolutionDocumentDetailsFlyoutEntitiesHostOverviewRiskLevel'; +/* Insights Threat Intelligence */ + +export const INSIGHTS_THREAT_INTELLIGENCE_TEST_ID = + 'securitySolutionDocumentDetailsFlyoutInsightsThreatIntelligence'; +export const INSIGHTS_THREAT_INTELLIGENCE_TITLE_TEST_ID = `${INSIGHTS_THREAT_INTELLIGENCE_TEST_ID}Title`; +export const INSIGHTS_THREAT_INTELLIGENCE_CONTENT_TEST_ID = `${INSIGHTS_THREAT_INTELLIGENCE_TEST_ID}Content`; +export const INSIGHTS_THREAT_INTELLIGENCE_VIEW_ALL_BUTTON_TEST_ID = `${INSIGHTS_THREAT_INTELLIGENCE_TEST_ID}ViewAllButton`; +export const INSIGHTS_THREAT_INTELLIGENCE_LOADING_TEST_ID = `${INSIGHTS_THREAT_INTELLIGENCE_TEST_ID}Loading`; +export const INSIGHTS_THREAT_INTELLIGENCE_ICON_TEST_ID = `${INSIGHTS_THREAT_INTELLIGENCE_TEST_ID}Icon`; +export const INSIGHTS_THREAT_INTELLIGENCE_VALUE_TEST_ID = `${INSIGHTS_THREAT_INTELLIGENCE_TEST_ID}Value`; +export const INSIGHTS_THREAT_INTELLIGENCE_COLOR_TEST_ID = `${INSIGHTS_THREAT_INTELLIGENCE_TEST_ID}Color`; + /* Visualizations section*/ export const VISUALIZATIONS_SECTION_TEST_ID = 'securitySolutionDocumentDetailsVisualizationsTitle'; export const VISUALIZATIONS_SECTION_HEADER_TEST_ID = diff --git a/x-pack/plugins/security_solution/public/flyout/right/components/threat_intelligence_overview.test.tsx b/x-pack/plugins/security_solution/public/flyout/right/components/threat_intelligence_overview.test.tsx new file mode 100644 index 0000000000000..ecf17f2c7e822 --- /dev/null +++ b/x-pack/plugins/security_solution/public/flyout/right/components/threat_intelligence_overview.test.tsx @@ -0,0 +1,195 @@ +/* + * 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 { render } from '@testing-library/react'; +import { ExpandableFlyoutContext } from '@kbn/expandable-flyout/src/context'; +import { RightPanelContext } from '../context'; +import { + INSIGHTS_THREAT_INTELLIGENCE_CONTENT_TEST_ID, + INSIGHTS_THREAT_INTELLIGENCE_LOADING_TEST_ID, + INSIGHTS_THREAT_INTELLIGENCE_TITLE_TEST_ID, + INSIGHTS_THREAT_INTELLIGENCE_VIEW_ALL_BUTTON_TEST_ID, +} from './test_ids'; +import { TestProviders } from '../../../common/mock'; +import { ThreatIntelligenceOverview } from './threat_intelligence_overview'; +import { LeftPanelInsightsTabPath, LeftPanelKey } from '../../left'; +import { useFetchThreatIntelligence } from '../hooks/use_fetch_threat_intelligence'; + +jest.mock('../hooks/use_fetch_threat_intelligence'); + +const panelContextValue = { + eventId: 'event id', + indexName: 'indexName', + dataFormattedForFieldBrowser: [], +} as unknown as RightPanelContext; + +const renderThreatIntelligenceOverview = (contextValue: RightPanelContext) => ( + + + + + +); + +describe('', () => { + it('should render 1 match detected and 1 field enriched', () => { + (useFetchThreatIntelligence as jest.Mock).mockReturnValue({ + loading: false, + threatMatchesCount: 1, + threatEnrichmentsCount: 1, + }); + + const { getByTestId } = render(renderThreatIntelligenceOverview(panelContextValue)); + + expect(getByTestId(INSIGHTS_THREAT_INTELLIGENCE_TITLE_TEST_ID)).toHaveTextContent( + 'Threat Intelligence' + ); + expect(getByTestId(INSIGHTS_THREAT_INTELLIGENCE_CONTENT_TEST_ID)).toHaveTextContent( + '1 threat match detected' + ); + expect(getByTestId(INSIGHTS_THREAT_INTELLIGENCE_CONTENT_TEST_ID)).toHaveTextContent( + '1 field enriched with threat intelligence' + ); + expect(getByTestId(INSIGHTS_THREAT_INTELLIGENCE_VIEW_ALL_BUTTON_TEST_ID)).toBeInTheDocument(); + }); + + it('should render 2 matches detected and 2 fields enriched', () => { + (useFetchThreatIntelligence as jest.Mock).mockReturnValue({ + loading: false, + threatMatchesCount: 2, + threatEnrichmentsCount: 2, + }); + + const { getByTestId } = render(renderThreatIntelligenceOverview(panelContextValue)); + + expect(getByTestId(INSIGHTS_THREAT_INTELLIGENCE_TITLE_TEST_ID)).toHaveTextContent( + 'Threat Intelligence' + ); + expect(getByTestId(INSIGHTS_THREAT_INTELLIGENCE_CONTENT_TEST_ID)).toHaveTextContent( + '2 threat matches detected' + ); + expect(getByTestId(INSIGHTS_THREAT_INTELLIGENCE_CONTENT_TEST_ID)).toHaveTextContent( + '2 fields enriched with threat intelligence' + ); + expect(getByTestId(INSIGHTS_THREAT_INTELLIGENCE_VIEW_ALL_BUTTON_TEST_ID)).toBeInTheDocument(); + }); + + it('should render 0 field enriched', () => { + (useFetchThreatIntelligence as jest.Mock).mockReturnValue({ + loading: false, + threatMatchesCount: 1, + threatEnrichmentsCount: 0, + }); + + const { getByTestId } = render(renderThreatIntelligenceOverview(panelContextValue)); + + expect(getByTestId(INSIGHTS_THREAT_INTELLIGENCE_CONTENT_TEST_ID)).toHaveTextContent( + '0 field enriched with threat intelligence' + ); + }); + + it('should render 0 match detected', () => { + (useFetchThreatIntelligence as jest.Mock).mockReturnValue({ + loading: false, + threatMatchesCount: 0, + threatEnrichmentsCount: 2, + }); + + const { getByTestId } = render(renderThreatIntelligenceOverview(panelContextValue)); + + expect(getByTestId(INSIGHTS_THREAT_INTELLIGENCE_CONTENT_TEST_ID)).toHaveTextContent( + '0 threat match detected' + ); + }); + + it('should render loading', () => { + (useFetchThreatIntelligence as jest.Mock).mockReturnValue({ + loading: true, + }); + + const { getByTestId } = render(renderThreatIntelligenceOverview(panelContextValue)); + + expect(getByTestId(INSIGHTS_THREAT_INTELLIGENCE_LOADING_TEST_ID)).toBeInTheDocument(); + }); + + it('should render null when eventId is null', () => { + (useFetchThreatIntelligence as jest.Mock).mockReturnValue({ + loading: false, + }); + const contextValue = { + ...panelContextValue, + eventId: null, + } as unknown as RightPanelContext; + + const { container } = render(renderThreatIntelligenceOverview(contextValue)); + + expect(container).toBeEmptyDOMElement(); + }); + + it('should render null when dataFormattedForFieldBrowser is null', () => { + (useFetchThreatIntelligence as jest.Mock).mockReturnValue({ + loading: false, + error: true, + }); + const contextValue = { + ...panelContextValue, + dataFormattedForFieldBrowser: null, + } as unknown as RightPanelContext; + + const { container } = render(renderThreatIntelligenceOverview(contextValue)); + + expect(container).toBeEmptyDOMElement(); + }); + + it('should render null when no enrichment found is null', () => { + (useFetchThreatIntelligence as jest.Mock).mockReturnValue({ + loading: false, + threatMatchesCount: 0, + threatEnrichmentsCount: 0, + }); + const contextValue = { + ...panelContextValue, + dataFormattedForFieldBrowser: [], + } as unknown as RightPanelContext; + + const { container } = render(renderThreatIntelligenceOverview(contextValue)); + + expect(container).toBeEmptyDOMElement(); + }); + + it('should navigate to left section Insights tab when clicking on button', () => { + (useFetchThreatIntelligence as jest.Mock).mockReturnValue({ + loading: false, + threatMatchesCount: 1, + threatEnrichmentsCount: 1, + }); + const flyoutContextValue = { + openLeftPanel: jest.fn(), + } as unknown as ExpandableFlyoutContext; + + const { getByTestId } = render( + + + + + + + + ); + + getByTestId(INSIGHTS_THREAT_INTELLIGENCE_VIEW_ALL_BUTTON_TEST_ID).click(); + expect(flyoutContextValue.openLeftPanel).toHaveBeenCalledWith({ + id: LeftPanelKey, + path: LeftPanelInsightsTabPath, + params: { + id: panelContextValue.eventId, + indexName: panelContextValue.indexName, + }, + }); + }); +}); diff --git a/x-pack/plugins/security_solution/public/flyout/right/components/threat_intelligence_overview.tsx b/x-pack/plugins/security_solution/public/flyout/right/components/threat_intelligence_overview.tsx new file mode 100644 index 0000000000000..63f0862a68b3a --- /dev/null +++ b/x-pack/plugins/security_solution/public/flyout/right/components/threat_intelligence_overview.tsx @@ -0,0 +1,91 @@ +/* + * 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, { useCallback } from 'react'; +import { EuiButtonEmpty } from '@elastic/eui'; +import { useExpandableFlyoutContext } from '@kbn/expandable-flyout'; +import { useFetchThreatIntelligence } from '../hooks/use_fetch_threat_intelligence'; +import { InsightsSubSection } from './insights_subsection'; +import type { InsightsSummaryPanelData } from './insights_summary_panel'; +import { InsightsSummaryPanel } from './insights_summary_panel'; +import { useRightPanelContext } from '../context'; +import { INSIGHTS_THREAT_INTELLIGENCE_TEST_ID } from './test_ids'; +import { + VIEW_ALL, + THREAT_INTELLIGENCE_TITLE, + THREAT_INTELLIGENCE_TEXT, + THREAT_MATCH_DETECTED, + THREAT_ENRICHMENT, + THREAT_MATCHES_DETECTED, + THREAT_ENRICHMENTS, +} from './translations'; +import { LeftPanelKey, LeftPanelInsightsTabPath } from '../../left'; + +/** + * Threat Intelligence section under Insights section, overview tab. + * The component fetches the necessary data, then pass it down to the InsightsSubSection component for loading and error state, + * and the SummaryPanel component for data rendering. + */ +export const ThreatIntelligenceOverview: React.FC = () => { + const { eventId, indexName, dataFormattedForFieldBrowser } = useRightPanelContext(); + const { openLeftPanel } = useExpandableFlyoutContext(); + + const goToThreatIntelligenceTab = useCallback(() => { + openLeftPanel({ + id: LeftPanelKey, + path: LeftPanelInsightsTabPath, + params: { + id: eventId, + indexName, + }, + }); + }, [eventId, openLeftPanel, indexName]); + + const { loading, threatMatchesCount, threatEnrichmentsCount } = useFetchThreatIntelligence({ + dataFormattedForFieldBrowser, + }); + + const data: InsightsSummaryPanelData[] = [ + { + icon: 'image', + value: threatMatchesCount, + text: threatMatchesCount <= 1 ? THREAT_MATCH_DETECTED : THREAT_MATCHES_DETECTED, + }, + { + icon: 'warning', + value: threatEnrichmentsCount, + text: threatMatchesCount <= 1 ? THREAT_ENRICHMENT : THREAT_ENRICHMENTS, + }, + ]; + + const error: boolean = + !eventId || + !dataFormattedForFieldBrowser || + (threatMatchesCount === 0 && threatEnrichmentsCount === 0); + + return ( + + + + {VIEW_ALL(THREAT_INTELLIGENCE_TEXT)} + + + ); +}; + +ThreatIntelligenceOverview.displayName = 'ThreatIntelligenceOverview'; diff --git a/x-pack/plugins/security_solution/public/flyout/right/components/translations.ts b/x-pack/plugins/security_solution/public/flyout/right/components/translations.ts index 1a6a2da7344c4..d5b9f0d1928b3 100644 --- a/x-pack/plugins/security_solution/public/flyout/right/components/translations.ts +++ b/x-pack/plugins/security_solution/public/flyout/right/components/translations.ts @@ -101,11 +101,18 @@ export const HIGHLIGHTED_FIELDS_TITLE = i18n.translate( { defaultMessage: 'Highlighted fields' } ); +/* Insights section */ + export const ENTITIES_TITLE = i18n.translate( 'xpack.securitySolution.flyout.documentDetails.entitiesTitle', { defaultMessage: 'Entities' } ); +export const THREAT_INTELLIGENCE_TITLE = i18n.translate( + 'xpack.securitySolution.flyout.documentDetails.threatIntelligenceTitle', + { defaultMessage: 'Threat Intelligence' } +); + export const INSIGHTS_TITLE = i18n.translate( 'xpack.securitySolution.flyout.documentDetails.insightsTitle', { defaultMessage: 'Insights' } @@ -131,6 +138,41 @@ export const ENTITIES_TEXT = i18n.translate( } ); +export const THREAT_INTELLIGENCE_TEXT = i18n.translate( + 'xpack.securitySolution.flyout.documentDetails.overviewTab.threatIntelligenceText', + { + defaultMessage: 'fields of threat intelligence', + } +); + +export const THREAT_MATCH_DETECTED = i18n.translate( + 'xpack.securitySolution.flyout.documentDetails.overviewTab.threatIntelligence.threatMatch', + { + defaultMessage: `threat match detected`, + } +); + +export const THREAT_MATCHES_DETECTED = i18n.translate( + 'xpack.securitySolution.flyout.documentDetails.overviewTab.threatIntelligence.threatMatches', + { + defaultMessage: `threat matches detected`, + } +); + +export const THREAT_ENRICHMENT = i18n.translate( + 'xpack.securitySolution.flyout.documentDetails.overviewTab.threatIntelligence.threatEnrichment', + { + defaultMessage: `field enriched with threat intelligence`, + } +); + +export const THREAT_ENRICHMENTS = i18n.translate( + 'xpack.securitySolution.flyout.documentDetails.overviewTab.threatIntelligence.threatEnrichments', + { + defaultMessage: `fields enriched with threat intelligence`, + } +); + export const VIEW_ALL = (text: string) => i18n.translate('xpack.securitySolution.flyout.documentDetails.overviewTab.viewAllButton', { values: { text }, diff --git a/x-pack/plugins/security_solution/public/flyout/right/context.tsx b/x-pack/plugins/security_solution/public/flyout/right/context.tsx index 282c224b5a15f..2da844946cbbd 100644 --- a/x-pack/plugins/security_solution/public/flyout/right/context.tsx +++ b/x-pack/plugins/security_solution/public/flyout/right/context.tsx @@ -19,6 +19,7 @@ import { SecurityPageName } from '../../../common/constants'; import { SourcererScopeName } from '../../common/store/sourcerer/model'; import { useSourcererDataView } from '../../common/containers/sourcerer'; import type { RightPanelProps } from '.'; +import type { GetFieldsData } from '../../common/hooks/use_get_fields_data'; import { useGetFieldsData } from '../../common/hooks/use_get_fields_data'; export interface RightPanelContext { @@ -57,7 +58,7 @@ export interface RightPanelContext { /** * Retrieves searchHit values for the provided field */ - getFieldsData: (field: string) => unknown | unknown[]; + getFieldsData: GetFieldsData; } export const RightPanelContext = createContext(undefined); diff --git a/x-pack/plugins/security_solution/public/flyout/right/hooks/use_fetch_threat_intelligence.test.tsx b/x-pack/plugins/security_solution/public/flyout/right/hooks/use_fetch_threat_intelligence.test.tsx new file mode 100644 index 0000000000000..ab57dcebe32af --- /dev/null +++ b/x-pack/plugins/security_solution/public/flyout/right/hooks/use_fetch_threat_intelligence.test.tsx @@ -0,0 +1,216 @@ +/* + * 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 { RenderHookResult } from '@testing-library/react-hooks'; +import { renderHook } from '@testing-library/react-hooks'; +import type { + UseThreatIntelligenceParams, + UseThreatIntelligenceValue, +} from './use_fetch_threat_intelligence'; +import { useFetchThreatIntelligence } from './use_fetch_threat_intelligence'; +import { useInvestigationTimeEnrichment } from '../../../common/containers/cti/event_enrichment'; + +jest.mock('../../../common/containers/cti/event_enrichment'); + +const dataFormattedForFieldBrowser = [ + { + category: 'kibana', + field: 'kibana.alert.rule.uuid', + isObjectArray: false, + originalValue: ['uuid'], + values: ['uuid'], + }, + { + category: 'threat', + field: 'threat.enrichments', + isObjectArray: true, + originalValue: ['{"indicator.file.hash.sha256":["sha256"]}'], + values: ['{"indicator.file.hash.sha256":["sha256"]}'], + }, + { + category: 'threat', + field: 'threat.enrichments.indicator.file.hash.sha256', + isObjectArray: false, + originalValue: ['sha256'], + values: ['sha256'], + }, +]; + +describe('useFetchThreatIntelligence', () => { + let hookResult: RenderHookResult; + + it('should render 1 match detected and 1 field enriched', () => { + (useInvestigationTimeEnrichment as jest.Mock).mockReturnValue({ + result: { + enrichments: [ + { + 'threat.indicator.file.hash.sha256': 'sha256', + 'matched.atomic': ['sha256'], + 'matched.field': ['file.hash.sha256'], + 'matched.id': ['matched.id.1'], + 'matched.type': ['indicator_match_rule'], + }, + { + 'threat.indicator.file.hash.sha256': 'sha256', + 'matched.atomic': ['sha256'], + 'matched.field': ['file.hash.sha256'], + 'matched.id': ['matched.id.2'], + 'matched.type': ['investigation_time'], + 'event.type': ['indicator'], + }, + ], + totalCount: 2, + }, + loading: false, + }); + + hookResult = renderHook(() => useFetchThreatIntelligence({ dataFormattedForFieldBrowser })); + expect(hookResult.result.current.loading).toEqual(false); + expect(hookResult.result.current.error).toEqual(false); + expect(hookResult.result.current.threatMatches).toHaveLength(1); + expect(hookResult.result.current.threatMatchesCount).toEqual(1); + expect(hookResult.result.current.threatEnrichments).toHaveLength(1); + expect(hookResult.result.current.threatEnrichmentsCount).toEqual(1); + }); + + it('should render 2 matches detected and 2 fields enriched', () => { + (useInvestigationTimeEnrichment as jest.Mock).mockReturnValue({ + result: { + enrichments: [ + { + 'threat.indicator.file.hash.sha256': 'sha256', + 'matched.atomic': ['sha256'], + 'matched.field': ['file.hash.sha256'], + 'matched.id': ['matched.id.1'], + 'matched.type': ['indicator_match_rule'], + }, + { + 'threat.indicator.file.hash.sha256': 'sha256', + 'matched.atomic': ['sha256'], + 'matched.field': ['file.hash.sha256'], + 'matched.id': ['matched.id.2'], + 'matched.type': ['investigation_time'], + 'event.type': ['indicator'], + }, + { + 'threat.indicator.file.hash.sha256': 'sha256', + 'matched.atomic': ['sha256'], + 'matched.field': ['file.hash.sha256'], + 'matched.id': ['matched.id.3'], + 'matched.type': ['indicator_match_rule'], + }, + { + 'threat.indicator.file.hash.sha256': 'sha256', + 'matched.atomic': ['sha256'], + 'matched.field': ['file.hash.sha256'], + 'matched.id': ['matched.id.4'], + 'matched.type': ['investigation_time'], + 'event.type': ['indicator'], + }, + ], + totalCount: 4, + }, + loading: false, + }); + + hookResult = renderHook(() => useFetchThreatIntelligence({ dataFormattedForFieldBrowser })); + expect(hookResult.result.current.loading).toEqual(false); + expect(hookResult.result.current.error).toEqual(false); + expect(hookResult.result.current.threatMatches).toHaveLength(2); + expect(hookResult.result.current.threatMatchesCount).toEqual(2); + expect(hookResult.result.current.threatEnrichments).toHaveLength(2); + expect(hookResult.result.current.threatEnrichmentsCount).toEqual(2); + }); + + it('should render 0 field enriched', () => { + (useInvestigationTimeEnrichment as jest.Mock).mockReturnValue({ + result: { + enrichments: [ + { + 'threat.indicator.file.hash.sha256': 'sha256', + 'matched.atomic': ['sha256'], + 'matched.field': ['file.hash.sha256'], + 'matched.id': ['matched.id.1'], + 'matched.type': ['indicator_match_rule'], + }, + ], + totalCount: 1, + }, + loading: false, + }); + + hookResult = renderHook(() => useFetchThreatIntelligence({ dataFormattedForFieldBrowser })); + expect(hookResult.result.current.loading).toEqual(false); + expect(hookResult.result.current.error).toEqual(false); + expect(hookResult.result.current.threatMatches).toHaveLength(1); + expect(hookResult.result.current.threatMatchesCount).toEqual(1); + expect(hookResult.result.current.threatEnrichments).toEqual(undefined); + expect(hookResult.result.current.threatEnrichmentsCount).toEqual(0); + }); + + it('should render 0 match detected', () => { + (useInvestigationTimeEnrichment as jest.Mock).mockReturnValue({ + result: { + enrichments: [ + { + 'threat.indicator.file.hash.sha256': 'sha256', + 'matched.atomic': ['sha256'], + 'matched.field': ['file.hash.sha256'], + 'matched.id': ['matched.id.2'], + 'matched.type': ['investigation_time'], + 'event.type': ['indicator'], + }, + ], + totalCount: 1, + }, + loading: false, + }); + + hookResult = renderHook(() => useFetchThreatIntelligence({ dataFormattedForFieldBrowser })); + expect(hookResult.result.current.loading).toEqual(false); + expect(hookResult.result.current.error).toEqual(false); + expect(hookResult.result.current.threatMatches).toEqual(undefined); + expect(hookResult.result.current.threatMatchesCount).toEqual(0); + expect(hookResult.result.current.threatEnrichments).toHaveLength(1); + expect(hookResult.result.current.threatEnrichmentsCount).toEqual(1); + }); + + it('should return loading true', () => { + (useInvestigationTimeEnrichment as jest.Mock).mockReturnValue({ + result: undefined, + loading: true, + }); + + hookResult = renderHook(() => useFetchThreatIntelligence({ dataFormattedForFieldBrowser })); + expect(hookResult.result.current.loading).toEqual(true); + expect(hookResult.result.current.error).toEqual(false); + expect(hookResult.result.current.threatMatches).toEqual(undefined); + expect(hookResult.result.current.threatMatchesCount).toEqual(0); + expect(hookResult.result.current.threatEnrichments).toEqual(undefined); + expect(hookResult.result.current.threatEnrichmentsCount).toEqual(0); + }); + + it('should return error true', () => { + (useInvestigationTimeEnrichment as jest.Mock).mockReturnValue({ + result: { + enrichments: [], + totalCount: 0, + }, + loading: false, + }); + + hookResult = renderHook(() => + useFetchThreatIntelligence({ dataFormattedForFieldBrowser: null }) + ); + expect(hookResult.result.current.loading).toEqual(false); + expect(hookResult.result.current.error).toEqual(true); + expect(hookResult.result.current.threatMatches).toEqual(undefined); + expect(hookResult.result.current.threatMatchesCount).toEqual(0); + expect(hookResult.result.current.threatEnrichments).toEqual(undefined); + expect(hookResult.result.current.threatEnrichmentsCount).toEqual(0); + }); +}); diff --git a/x-pack/plugins/security_solution/public/flyout/right/hooks/use_fetch_threat_intelligence.ts b/x-pack/plugins/security_solution/public/flyout/right/hooks/use_fetch_threat_intelligence.ts new file mode 100644 index 0000000000000..4f3d23b082664 --- /dev/null +++ b/x-pack/plugins/security_solution/public/flyout/right/hooks/use_fetch_threat_intelligence.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 { useMemo } from 'react'; +import { groupBy } from 'lodash'; +import type { TimelineEventsDetailsItem } from '@kbn/timelines-plugin/common'; +import type { CtiEnrichment } from '../../../../common/search_strategy'; +import { useBasicDataFromDetailsData } from '../../../timelines/components/side_panel/event_details/helpers'; +import { + filterDuplicateEnrichments, + getEnrichmentFields, + parseExistingEnrichments, + timelineDataToEnrichment, +} from '../../../common/components/event_details/cti_details/helpers'; +import { useInvestigationTimeEnrichment } from '../../../common/containers/cti/event_enrichment'; +import { ENRICHMENT_TYPES } from '../../../../common/cti/constants'; + +export interface UseThreatIntelligenceParams { + /** + * An array of field objects with category and value + */ + dataFormattedForFieldBrowser: TimelineEventsDetailsItem[] | null; +} + +export interface UseThreatIntelligenceValue { + /** + * Returns true while the threat intelligence data is being queried + */ + loading: boolean; + /** + * Returns true if the dataFormattedForFieldBrowser property is null + */ + error: boolean; + /** + * Threat matches (from an indicator match rule) + */ + threatMatches: CtiEnrichment[]; + /** + * Threat matches count + */ + threatMatchesCount: number; + /** + * Threat enrichments (from the real time query) + */ + threatEnrichments: CtiEnrichment[]; + /** + * Threat enrichments count + */ + threatEnrichmentsCount: number; +} + +/** + * Hook to retrieve threat intelligence data for the expandable flyout right and left sections. + */ +export const useFetchThreatIntelligence = ({ + dataFormattedForFieldBrowser, +}: UseThreatIntelligenceParams): UseThreatIntelligenceValue => { + const { isAlert } = useBasicDataFromDetailsData(dataFormattedForFieldBrowser); + + // retrieve the threat enrichment fields with value for the current document + // (see https://github.com/elastic/kibana/blob/main/x-pack/plugins/security_solution/common/cti/constants.ts#L35) + const eventFields = useMemo( + () => getEnrichmentFields(dataFormattedForFieldBrowser || []), + [dataFormattedForFieldBrowser] + ); + + // retrieve existing enrichment fields and their value + const existingEnrichments = useMemo( + () => + isAlert + ? parseExistingEnrichments(dataFormattedForFieldBrowser || []).map((enrichmentData) => + timelineDataToEnrichment(enrichmentData) + ) + : [], + [dataFormattedForFieldBrowser, isAlert] + ); + + // api call to retrieve all documents that match the eventFields + const { result: response, loading } = useInvestigationTimeEnrichment(eventFields); + + // combine existing enrichment and enrichment from the api response + // also removes the investigation-time enrichments if the exact indicator already exists + const allEnrichments = useMemo(() => { + if (loading || !response?.enrichments) { + return existingEnrichments; + } + return filterDuplicateEnrichments([...existingEnrichments, ...response.enrichments]); + }, [loading, response, existingEnrichments]); + + // separate threat matches (from indicator-match rule) from threat enrichments (realtime query) + const { + [ENRICHMENT_TYPES.IndicatorMatchRule]: threatMatches, + [ENRICHMENT_TYPES.InvestigationTime]: threatEnrichments, + } = groupBy(allEnrichments, 'matched.type'); + + return { + loading, + error: !dataFormattedForFieldBrowser, + threatMatches, + threatMatchesCount: (threatMatches || []).length, + threatEnrichments, + threatEnrichmentsCount: (threatEnrichments || []).length, + }; +}; diff --git a/x-pack/plugins/security_solution/public/flyout/url/use_sync_flyout_state_with_url.test.tsx b/x-pack/plugins/security_solution/public/flyout/url/use_sync_flyout_state_with_url.test.tsx new file mode 100644 index 0000000000000..984b2a2e223dc --- /dev/null +++ b/x-pack/plugins/security_solution/public/flyout/url/use_sync_flyout_state_with_url.test.tsx @@ -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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import type { ExpandableFlyoutApi } from '@kbn/expandable-flyout'; +import { useSyncToUrl } from '@kbn/url-state'; +import { renderHook } from '@testing-library/react-hooks'; +import { useSyncFlyoutStateWithUrl } from './use_sync_flyout_state_with_url'; + +jest.mock('@kbn/url-state'); + +describe('useSyncFlyoutStateWithUrl', () => { + it('should return an array containing flyoutApi ref and handleFlyoutChanges function', () => { + const { result } = renderHook(() => useSyncFlyoutStateWithUrl()); + const [flyoutApi, handleFlyoutChanges] = result.current; + + expect(flyoutApi.current).toBeNull(); + expect(typeof handleFlyoutChanges).toBe('function'); + }); + + it('should open flyout when relevant url state is detected in the query string', () => { + jest.useFakeTimers(); + + jest.mocked(useSyncToUrl).mockImplementation((_urlKey, callback) => { + setTimeout(() => callback({ mocked: { flyout: 'state' } }), 0); + return jest.fn(); + }); + + const { result } = renderHook(() => useSyncFlyoutStateWithUrl()); + const [flyoutApi, handleFlyoutChanges] = result.current; + + const flyoutApiMock: ExpandableFlyoutApi = { + openFlyout: jest.fn(), + getState: () => ({ left: undefined, right: undefined, preview: [] }), + }; + + expect(typeof handleFlyoutChanges).toBe('function'); + expect(flyoutApi.current).toBeNull(); + + // eslint-disable-next-line @typescript-eslint/no-explicit-any + (flyoutApi as any).current = flyoutApiMock; + + jest.runOnlyPendingTimers(); + jest.useRealTimers(); + + expect(flyoutApiMock.openFlyout).toHaveBeenCalledTimes(1); + expect(flyoutApiMock.openFlyout).toHaveBeenCalledWith({ mocked: { flyout: 'state' } }); + }); + + it('should sync flyout state to url whenever handleFlyoutChanges is called by the consumer', () => { + const syncStateToUrl = jest.fn(); + jest.mocked(useSyncToUrl).mockImplementation((_urlKey, callback) => { + setTimeout(() => callback({ mocked: { flyout: 'state' } }), 0); + return syncStateToUrl; + }); + + const { result } = renderHook(() => useSyncFlyoutStateWithUrl()); + const [_flyoutApi, handleFlyoutChanges] = result.current; + + handleFlyoutChanges(); + + expect(syncStateToUrl).toHaveBeenCalledTimes(1); + expect(syncStateToUrl).toHaveBeenLastCalledWith(undefined); + + handleFlyoutChanges({ left: undefined, right: undefined, preview: [] }); + + expect(syncStateToUrl).toHaveBeenLastCalledWith({ + left: undefined, + right: undefined, + preview: undefined, + }); + expect(syncStateToUrl).toHaveBeenCalledTimes(2); + }); +}); diff --git a/x-pack/plugins/security_solution/public/flyout/url/use_sync_flyout_state_with_url.tsx b/x-pack/plugins/security_solution/public/flyout/url/use_sync_flyout_state_with_url.tsx new file mode 100644 index 0000000000000..be1b28147f63d --- /dev/null +++ b/x-pack/plugins/security_solution/public/flyout/url/use_sync_flyout_state_with_url.tsx @@ -0,0 +1,45 @@ +/* + * 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 { useCallback, useRef } from 'react'; +import type { ExpandableFlyoutApi, ExpandableFlyoutContext } from '@kbn/expandable-flyout'; +import { useSyncToUrl } from '@kbn/url-state'; +import last from 'lodash/last'; + +const URL_KEY = 'eventFlyout' as const; + +type FlyoutState = Parameters[0]; + +/** + * Sync flyout state with the url and open it when relevant url state is detected in the query string + * @returns [ref, flyoutChangesHandler] + */ +export const useSyncFlyoutStateWithUrl = () => { + const flyoutApi = useRef(null); + + const syncStateToUrl = useSyncToUrl(URL_KEY, (data) => { + flyoutApi.current?.openFlyout(data); + }); + + // This should be bound to flyout changed and closed events. + // When flyout is closed, url state is cleared + const handleFlyoutChanges = useCallback( + (state?: ExpandableFlyoutContext['panels']) => { + if (!state) { + return syncStateToUrl(undefined); + } + + return syncStateToUrl({ + ...state, + preview: last(state.preview), + }); + }, + [syncStateToUrl] + ); + + return [flyoutApi, handleFlyoutChanges] as const; +}; diff --git a/x-pack/plugins/security_solution/public/overview/components/detection_response/host_alerts_table/host_alerts_table.tsx b/x-pack/plugins/security_solution/public/overview/components/detection_response/host_alerts_table/host_alerts_table.tsx index 26e3d866ff343..8928d3aaf312b 100644 --- a/x-pack/plugins/security_solution/public/overview/components/detection_response/host_alerts_table/host_alerts_table.tsx +++ b/x-pack/plugins/security_solution/public/overview/components/detection_response/host_alerts_table/host_alerts_table.tsx @@ -20,6 +20,7 @@ import { } from '@elastic/eui'; import { ALERT_SEVERITY } from '@kbn/rule-data-utils'; +import { CellActionsMode } from '@kbn/cell-actions'; import { useNavigateToAlertsPageWithFilters } from '../../../../common/hooks/use_navigate_to_alerts_page_with_filters'; import { FormattedCount } from '../../../../common/components/formatted_number'; import { HeaderSection } from '../../../../common/components/header_section'; @@ -33,6 +34,10 @@ import * as i18n from '../translations'; import { ITEMS_PER_PAGE, SEVERITY_COLOR } from '../utils'; import type { HostAlertsItem } from './use_host_alerts_items'; import { useHostAlertsItems } from './use_host_alerts_items'; +import { + SecurityCellActions, + SecurityCellActionsTrigger, +} from '../../../../common/components/cell_actions'; interface HostAlertsTableProps { signalIndexName: string | null; @@ -143,13 +148,27 @@ const getTableColumns: GetTableColumns = (handleClick) => [ name: i18n.ALERTS_TEXT, 'data-test-subj': 'hostSeverityAlertsTable-totalAlerts', render: (totalAlerts: number, { hostName }) => ( - handleClick({ hostName })} + - - + handleClick({ hostName })} + > + + + ), }, { @@ -157,13 +176,30 @@ const getTableColumns: GetTableColumns = (handleClick) => [ name: i18n.STATUS_CRITICAL_LABEL, render: (count: number, { hostName }) => ( - handleClick({ hostName, severity: 'critical' })} + - - + handleClick({ hostName, severity: 'critical' })} + > + + + ), }, @@ -172,9 +208,29 @@ const getTableColumns: GetTableColumns = (handleClick) => [ name: i18n.STATUS_HIGH_LABEL, render: (count: number, { hostName }) => ( - handleClick({ hostName, severity: 'high' })}> - - + + handleClick({ hostName, severity: 'high' })} + > + + + ), }, @@ -183,12 +239,29 @@ const getTableColumns: GetTableColumns = (handleClick) => [ name: i18n.STATUS_MEDIUM_LABEL, render: (count: number, { hostName }) => ( - handleClick({ hostName, severity: 'medium' })} + - - + handleClick({ hostName, severity: 'medium' })} + > + + + ), }, @@ -197,9 +270,29 @@ const getTableColumns: GetTableColumns = (handleClick) => [ name: i18n.STATUS_LOW_LABEL, render: (count: number, { hostName }) => ( - handleClick({ hostName, severity: 'low' })}> - - + + handleClick({ hostName, severity: 'low' })} + > + + + ), }, diff --git a/x-pack/plugins/security_solution/public/overview/components/detection_response/rule_alerts_table/rule_alerts_table.tsx b/x-pack/plugins/security_solution/public/overview/components/detection_response/rule_alerts_table/rule_alerts_table.tsx index 8b0f38d0e479f..6c60bfc727b46 100644 --- a/x-pack/plugins/security_solution/public/overview/components/detection_response/rule_alerts_table/rule_alerts_table.tsx +++ b/x-pack/plugins/security_solution/public/overview/components/detection_response/rule_alerts_table/rule_alerts_table.tsx @@ -21,6 +21,8 @@ import { import { FormattedRelative } from '@kbn/i18n-react'; import type { Severity } from '@kbn/securitysolution-io-ts-alerting-types'; import { ALERT_RULE_NAME } from '@kbn/rule-data-utils'; +import { CellActionsMode } from '@kbn/cell-actions'; +import { SecurityCellActionsTrigger } from '../../../../actions/constants'; import { useNavigateToAlertsPageWithFilters } from '../../../../common/hooks/use_navigate_to_alerts_page_with_filters'; import { HeaderSection } from '../../../../common/components/header_section'; @@ -36,6 +38,7 @@ import { HoverVisibilityContainer } from '../../../../common/components/hover_vi import { BUTTON_CLASS as INSPECT_BUTTON_CLASS } from '../../../../common/components/inspect'; import { LastUpdatedAt } from '../../../../common/components/last_updated_at'; import { FormattedCount } from '../../../../common/components/formatted_number'; +import { SecurityCellActions } from '../../../../common/components/cell_actions'; export interface RuleAlertsTableProps { signalIndexName: string | null; @@ -95,13 +98,27 @@ export const getTableColumns: GetTableColumns = ({ name: i18n.RULE_ALERTS_COLUMN_ALERT_COUNT, 'data-test-subj': 'severityRuleAlertsTable-alertCount', render: (alertCount: number, { name }) => ( - openRuleInAlertsPage(name)} + - - + openRuleInAlertsPage(name)} + > + + + ), }, { diff --git a/x-pack/plugins/security_solution/public/overview/components/detection_response/user_alerts_table/user_alerts_table.tsx b/x-pack/plugins/security_solution/public/overview/components/detection_response/user_alerts_table/user_alerts_table.tsx index ddaf75cba0f8a..914c3c93ff240 100644 --- a/x-pack/plugins/security_solution/public/overview/components/detection_response/user_alerts_table/user_alerts_table.tsx +++ b/x-pack/plugins/security_solution/public/overview/components/detection_response/user_alerts_table/user_alerts_table.tsx @@ -20,6 +20,8 @@ import { } from '@elastic/eui'; import { ALERT_SEVERITY } from '@kbn/rule-data-utils'; +import { CellActionsMode } from '@kbn/cell-actions'; +import { SecurityCellActionsTrigger } from '../../../../actions/constants'; import { useNavigateToAlertsPageWithFilters } from '../../../../common/hooks/use_navigate_to_alerts_page_with_filters'; import { FormattedCount } from '../../../../common/components/formatted_number'; import { HeaderSection } from '../../../../common/components/header_section'; @@ -32,6 +34,7 @@ import * as i18n from '../translations'; import { ITEMS_PER_PAGE, SEVERITY_COLOR } from '../utils'; import type { UserAlertsItem } from './use_user_alerts_items'; import { useUserAlertsItems } from './use_user_alerts_items'; +import { SecurityCellActions } from '../../../../common/components/cell_actions'; interface UserAlertsTableProps { signalIndexName: string | null; @@ -142,13 +145,27 @@ const getTableColumns: GetTableColumns = (handleClick) => [ name: i18n.ALERTS_TEXT, 'data-test-subj': 'userSeverityAlertsTable-totalAlerts', render: (totalAlerts: number, { userName }) => ( - handleClick({ userName })} + - - + handleClick({ userName })} + > + + + ), }, { @@ -156,13 +173,30 @@ const getTableColumns: GetTableColumns = (handleClick) => [ name: i18n.STATUS_CRITICAL_LABEL, render: (count: number, { userName }) => ( - handleClick({ userName, severity: 'critical' })} + - - + handleClick({ userName, severity: 'critical' })} + > + + + ), }, @@ -171,9 +205,29 @@ const getTableColumns: GetTableColumns = (handleClick) => [ name: i18n.STATUS_HIGH_LABEL, render: (count: number, { userName }) => ( - handleClick({ userName, severity: 'high' })}> - - + + handleClick({ userName, severity: 'high' })} + > + + + ), }, @@ -182,12 +236,29 @@ const getTableColumns: GetTableColumns = (handleClick) => [ name: i18n.STATUS_MEDIUM_LABEL, render: (count: number, { userName }) => ( - handleClick({ userName, severity: 'medium' })} + - - + handleClick({ userName, severity: 'medium' })} + > + + + ), }, @@ -196,9 +267,29 @@ const getTableColumns: GetTableColumns = (handleClick) => [ name: i18n.STATUS_LOW_LABEL, render: (count: number, { userName }) => ( - handleClick({ userName, severity: 'low' })}> - - + + handleClick({ userName, severity: 'low' })} + > + + + ), }, diff --git a/x-pack/plugins/security_solution/public/overview/components/entity_analytics/anomalies/anomalies_count_link.test.tsx b/x-pack/plugins/security_solution/public/overview/components/entity_analytics/anomalies/anomalies_count_link.test.tsx new file mode 100644 index 0000000000000..4ce4b6810ec98 --- /dev/null +++ b/x-pack/plugins/security_solution/public/overview/components/entity_analytics/anomalies/anomalies_count_link.test.tsx @@ -0,0 +1,42 @@ +/* + * 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 { fireEvent, render } from '@testing-library/react'; +import React from 'react'; +import { AnomalyEntity } from '../../../../common/components/ml/anomaly/use_anomalies_search'; +import { createTelemetryServiceMock } from '../../../../common/lib/telemetry/telemetry_service.mock'; +import { TestProviders } from '../../../../common/mock'; +import { AnomaliesCountLink } from './anomalies_count_link'; + +const mockedTelemetry = createTelemetryServiceMock(); +jest.mock('../../../../common/lib/kibana', () => { + const original = jest.requireActual('../../../../common/lib/kibana'); + + return { + ...original, + useKibana: () => ({ + services: { + telemetry: mockedTelemetry, + }, + }), + }; +}); + +describe('AnomaliesCountLink', () => { + it('reports telemetry when clicked', () => { + const count = 10; + const jobId = 'test-job-id'; + + const { getByRole } = render( + , + { wrapper: TestProviders } + ); + + fireEvent.click(getByRole('button')); + + expect(mockedTelemetry.reportAnomaliesCountClicked).toHaveBeenLastCalledWith({ jobId, count }); + }); +}); diff --git a/x-pack/plugins/security_solution/public/overview/components/entity_analytics/anomalies/anomalies_count_link.tsx b/x-pack/plugins/security_solution/public/overview/components/entity_analytics/anomalies/anomalies_count_link.tsx new file mode 100644 index 0000000000000..529f197c62e44 --- /dev/null +++ b/x-pack/plugins/security_solution/public/overview/components/entity_analytics/anomalies/anomalies_count_link.tsx @@ -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 { useDispatch } from 'react-redux'; +import React, { useCallback } from 'react'; +import { AnomalyEntity } from '../../../../common/components/ml/anomaly/use_anomalies_search'; +import { SecuritySolutionLinkAnchor } from '../../../../common/components/links'; +import { SecurityPageName } from '../../../../app/types'; +import { usersActions } from '../../../../explore/users/store'; +import { hostsActions } from '../../../../explore/hosts/store'; +import { HostsType } from '../../../../explore/hosts/store/model'; +import { UsersType } from '../../../../explore/users/store/model'; + +import { useKibana } from '../../../../common/lib/kibana'; + +export const AnomaliesCountLink = ({ + count, + jobId, + entity, +}: { + count: number; + jobId?: string; + entity: AnomalyEntity; +}) => { + const dispatch = useDispatch(); + const { telemetry } = useKibana().services; + + const deepLinkId = + entity === AnomalyEntity.User + ? SecurityPageName.usersAnomalies + : SecurityPageName.hostsAnomalies; + + const onClick = useCallback(() => { + if (!jobId) return; + + telemetry.reportAnomaliesCountClicked({ + jobId, + count, + }); + + if (entity === AnomalyEntity.User) { + dispatch( + usersActions.updateUsersAnomaliesJobIdFilter({ + jobIds: [jobId], + usersType: UsersType.page, + }) + ); + + dispatch( + usersActions.updateUsersAnomaliesInterval({ + interval: 'second', + usersType: UsersType.page, + }) + ); + } else { + dispatch( + hostsActions.updateHostsAnomaliesJobIdFilter({ + jobIds: [jobId], + hostsType: HostsType.page, + }) + ); + + dispatch( + hostsActions.updateHostsAnomaliesInterval({ + interval: 'second', + hostsType: HostsType.page, + }) + ); + } + }, [jobId, telemetry, count, entity, dispatch]); + + return ( + + {count} + + ); +}; diff --git a/x-pack/plugins/security_solution/public/overview/components/entity_analytics/anomalies/columns.tsx b/x-pack/plugins/security_solution/public/overview/components/entity_analytics/anomalies/columns.tsx index 92f8751782fa6..1f8556dd27a98 100644 --- a/x-pack/plugins/security_solution/public/overview/components/entity_analytics/anomalies/columns.tsx +++ b/x-pack/plugins/security_solution/public/overview/components/entity_analytics/anomalies/columns.tsx @@ -8,24 +8,16 @@ import React, { useCallback, useMemo } from 'react'; import styled from 'styled-components'; import type { EuiBasicTableColumn } from '@elastic/eui'; import { EuiIcon, EuiLoadingSpinner } from '@elastic/eui'; -import { useDispatch } from 'react-redux'; - import * as i18n from './translations'; import type { AnomaliesCount } from '../../../../common/components/ml/anomaly/use_anomalies_search'; -import { AnomalyEntity } from '../../../../common/components/ml/anomaly/use_anomalies_search'; - -import { LinkAnchor, SecuritySolutionLinkAnchor } from '../../../../common/components/links'; -import { SecurityPageName } from '../../../../app/types'; -import { usersActions } from '../../../../explore/users/store'; -import { hostsActions } from '../../../../explore/hosts/store'; -import { HostsType } from '../../../../explore/hosts/store/model'; -import { UsersType } from '../../../../explore/users/store/model'; +import { LinkAnchor } from '../../../../common/components/links'; import type { SecurityJob } from '../../../../common/components/ml_popover/types'; import { isJobFailed, isJobStarted, isJobLoading, } from '../../../../../common/machine_learning/helpers'; +import { AnomaliesCountLink } from './anomalies_count_link'; type AnomaliesColumns = Array>; @@ -76,7 +68,7 @@ export const useAnomaliesColumns = ( if (!job) return ''; if (count > 0 || isJobStarted(job.jobState, job.datafeedState)) { - return ; + return ; } else if (isJobFailed(job.jobState, job.datafeedState)) { return i18n.JOB_STATUS_FAILED; } else if (job.isCompatible) { @@ -111,60 +103,3 @@ const EnableJob = ({ ); }; - -const AnomaliesTabLink = ({ - count, - jobId, - entity, -}: { - count: number; - jobId?: string; - entity: AnomalyEntity; -}) => { - const dispatch = useDispatch(); - - const deepLinkId = - entity === AnomalyEntity.User - ? SecurityPageName.usersAnomalies - : SecurityPageName.hostsAnomalies; - - const onClick = useCallback(() => { - if (!jobId) return; - - if (entity === AnomalyEntity.User) { - dispatch( - usersActions.updateUsersAnomaliesJobIdFilter({ - jobIds: [jobId], - usersType: UsersType.page, - }) - ); - - dispatch( - usersActions.updateUsersAnomaliesInterval({ - interval: 'second', - usersType: UsersType.page, - }) - ); - } else { - dispatch( - hostsActions.updateHostsAnomaliesJobIdFilter({ - jobIds: [jobId], - hostsType: HostsType.page, - }) - ); - - dispatch( - hostsActions.updateHostsAnomaliesInterval({ - interval: 'second', - hostsType: HostsType.page, - }) - ); - } - }, [jobId, dispatch, entity]); - - return ( - - {count} - - ); -}; diff --git a/x-pack/plugins/security_solution/public/overview/components/entity_analytics/risk_score/columns.tsx b/x-pack/plugins/security_solution/public/overview/components/entity_analytics/risk_score/columns.tsx index 7cd355e967974..27b069c45de75 100644 --- a/x-pack/plugins/security_solution/public/overview/components/entity_analytics/risk_score/columns.tsx +++ b/x-pack/plugins/security_solution/public/overview/components/entity_analytics/risk_score/columns.tsx @@ -14,6 +14,7 @@ import { getEmptyTagValue } from '../../../../common/components/empty_value'; import { HostDetailsLink, UserDetailsLink } from '../../../../common/components/links'; import { HostsTableType } from '../../../../explore/hosts/store/model'; import { RiskScore } from '../../../../explore/components/risk_score/severity/common'; +import { CELL_ACTIONS_TELEMETRY } from '../../../../explore/components/risk_score/constants'; import type { HostRiskScore, RiskSeverity, @@ -64,6 +65,9 @@ export const getRiskScoreColumns = ( SecurityCellActionType.FILTER, SecurityCellActionType.SHOW_TOP_N, ]} + metadata={{ + telemetry: CELL_ACTIONS_TELEMETRY, + }} /> ) : ( @@ -136,17 +140,31 @@ export const getRiskScoreColumns = ( truncateText: false, mobileOptions: { show: true }, render: (alertCount: number, risk) => ( - - openEntityOnAlertsPage( - riskEntity === RiskScoreEntity.host ? risk.host.name : risk.user.name - ) - } + - - + + openEntityOnAlertsPage( + riskEntity === RiskScoreEntity.host ? risk.host.name : risk.user.name + ) + } + > + + + ), }, ]; diff --git a/x-pack/plugins/security_solution/public/overview/components/entity_analytics/risk_score/index.test.tsx b/x-pack/plugins/security_solution/public/overview/components/entity_analytics/risk_score/index.test.tsx index 80a750a8bce14..7869a234ccd50 100644 --- a/x-pack/plugins/security_solution/public/overview/components/entity_analytics/risk_score/index.test.tsx +++ b/x-pack/plugins/security_solution/public/overview/components/entity_analytics/risk_score/index.test.tsx @@ -5,11 +5,10 @@ * 2.0. */ -import { render, fireEvent } from '@testing-library/react'; +import { render, fireEvent, waitFor } from '@testing-library/react'; import React from 'react'; import { TestProviders } from '../../../../common/mock'; import { EntityAnalyticsRiskScores } from '.'; -import type { UserRiskScore } from '../../../../../common/search_strategy'; import { RiskScoreEntity, RiskSeverity } from '../../../../../common/search_strategy'; import type { SeverityCount } from '../../../../explore/components/risk_score/severity/types'; import { useRiskScore, useRiskScoreKpi } from '../../../../explore/containers/risk_score'; @@ -146,17 +145,17 @@ describe.each([RiskScoreEntity.host, RiskScoreEntity.user])( expect(queryByTestId('entity_analytics_content')).not.toBeInTheDocument(); }); - it('renders alerts count', () => { + it('renders alerts count', async () => { mockUseQueryToggle.mockReturnValue({ toggleStatus: true, setToggleStatus: jest.fn() }); mockUseRiskScoreKpi.mockReturnValue({ severityCount: mockSeverityCount, loading: false, }); const alertsCount = 999; - const data: UserRiskScore[] = [ + const data = [ { '@timestamp': '1234567899', - user: { + [riskEntity]: { name: 'testUsermame', risk: { rule_risks: [], @@ -176,10 +175,12 @@ describe.each([RiskScoreEntity.host, RiskScoreEntity.user])( ); - expect(queryByTestId('risk-score-alerts')).toHaveTextContent(alertsCount.toString()); + await waitFor(() => { + expect(queryByTestId('risk-score-alerts')).toHaveTextContent(alertsCount.toString()); + }); }); - it('navigates to alerts page with filters when alerts count is clicked', () => { + it('navigates to alerts page with filters when alerts count is clicked', async () => { mockUseQueryToggle.mockReturnValue({ toggleStatus: true, setToggleStatus: jest.fn() }); mockUseRiskScoreKpi.mockReturnValue({ severityCount: mockSeverityCount, @@ -211,13 +212,15 @@ describe.each([RiskScoreEntity.host, RiskScoreEntity.user])( fireEvent.click(getByTestId('risk-score-alerts')); - expect(mockOpenAlertsPageWithFilters.mock.calls[0][0]).toEqual([ - { - title: riskEntity === RiskScoreEntity.host ? 'Host' : 'User', - fieldName: riskEntity === RiskScoreEntity.host ? 'host.name' : 'user.name', - selectedOptions: [name], - }, - ]); + await waitFor(() => { + expect(mockOpenAlertsPageWithFilters.mock.calls[0][0]).toEqual([ + { + title: riskEntity === RiskScoreEntity.host ? 'Host' : 'User', + fieldName: riskEntity === RiskScoreEntity.host ? 'host.name' : 'user.name', + selectedOptions: [name], + }, + ]); + }); }); } ); diff --git a/x-pack/plugins/security_solution/public/plugin.tsx b/x-pack/plugins/security_solution/public/plugin.tsx index 9553b2695f95c..61ec29d956414 100644 --- a/x-pack/plugins/security_solution/public/plugin.tsx +++ b/x-pack/plugins/security_solution/public/plugin.tsx @@ -194,7 +194,7 @@ export class Plugin implements IPlugin( ({ severity_mapping: [], updated_by: 'elastic', tags: [], - throttle: 'no_actions', + throttle: undefined, threat: getThreatMock(), exceptions_list: getListArrayMock(), filters: [ diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/api/rules/bulk_actions/route.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/api/rules/bulk_actions/route.ts index 27b9111b0434d..974ea9cc7ecc5 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/api/rules/bulk_actions/route.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/api/rules/bulk_actions/route.ts @@ -432,8 +432,10 @@ export const performBulkActionRoute = ( } let shouldDuplicateExceptions = true; + let shouldDuplicateExpiredExceptions = true; if (body.duplicate !== undefined) { shouldDuplicateExceptions = body.duplicate.include_exceptions; + shouldDuplicateExpiredExceptions = body.duplicate.include_expired_exceptions; } const duplicateRuleToCreate = await duplicateRule({ @@ -449,6 +451,7 @@ export const performBulkActionRoute = ( ? await duplicateExceptions({ ruleId: rule.params.ruleId, exceptionLists: rule.params.exceptionsList, + includeExpiredExceptions: shouldDuplicateExpiredExceptions, exceptionsClient, }) : []; diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/actions/duplicate_exceptions.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/actions/duplicate_exceptions.test.ts new file mode 100644 index 0000000000000..aeb593ec33fff --- /dev/null +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/actions/duplicate_exceptions.test.ts @@ -0,0 +1,163 @@ +/* + * 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 { duplicateExceptions } from './duplicate_exceptions'; +import { getExceptionListClientMock } from '@kbn/lists-plugin/server/services/exception_lists/exception_list_client.mock'; +import type { List } from '@kbn/securitysolution-io-ts-list-types'; +import { ExceptionListTypeEnum } from '@kbn/securitysolution-io-ts-list-types'; +import type { ExceptionListClient } from '@kbn/lists-plugin/server'; +import { getDetectionsExceptionListSchemaMock } from '@kbn/lists-plugin/common/schemas/response/exception_list_schema.mock'; + +jest.mock('uuid', () => ({ + v4: jest.fn(), +})); + +describe('duplicateExceptions', () => { + let exceptionsClient: ExceptionListClient; + + beforeAll(() => { + exceptionsClient = getExceptionListClientMock(); + exceptionsClient.duplicateExceptionListAndItems = jest.fn(); + }); + + afterAll(() => { + jest.clearAllMocks(); + }); + + it('returns empty array if no exceptions to duplicate', async () => { + const result = await duplicateExceptions({ + ruleId: 'rule_123', + exceptionLists: [], + exceptionsClient, + includeExpiredExceptions: false, + }); + + expect(result).toEqual([]); + }); + + it('returns array referencing the same shared exception lists if no rule default exceptions included', async () => { + const sharedExceptionListReference: List = { + type: ExceptionListTypeEnum.DETECTION, + list_id: 'my_list', + namespace_type: 'single', + id: '1234', + }; + + const result = await duplicateExceptions({ + ruleId: 'rule_123', + exceptionLists: [sharedExceptionListReference], + exceptionsClient, + includeExpiredExceptions: false, + }); + + expect(exceptionsClient.duplicateExceptionListAndItems).not.toHaveBeenCalled(); + expect(result).toEqual([sharedExceptionListReference]); + }); + + it('duplicates rule default and shared exceptions', async () => { + const newDefaultRuleList = { + ...getDetectionsExceptionListSchemaMock(), + type: ExceptionListTypeEnum.RULE_DEFAULT, + list_id: 'rule_default_list_dupe', + namespace_type: 'single', + id: '123-abc', + }; + + exceptionsClient.getExceptionList = jest.fn().mockResolvedValue({ + ...getDetectionsExceptionListSchemaMock(), + type: ExceptionListTypeEnum.RULE_DEFAULT, + list_id: 'rule_default_list', + namespace_type: 'single', + id: '5678', + }); + exceptionsClient.duplicateExceptionListAndItems = jest + .fn() + .mockResolvedValue(newDefaultRuleList); + + const sharedExceptionListReference: List = { + type: ExceptionListTypeEnum.DETECTION, + list_id: 'my_list', + namespace_type: 'single', + id: '1234', + }; + + const ruleDefaultListReference: List = { + type: ExceptionListTypeEnum.RULE_DEFAULT, + list_id: 'rule_default_list', + namespace_type: 'single', + id: '5678', + }; + + const result = await duplicateExceptions({ + ruleId: 'rule_123', + exceptionLists: [sharedExceptionListReference, ruleDefaultListReference], + exceptionsClient, + includeExpiredExceptions: false, + }); + + expect(result).toEqual([ + sharedExceptionListReference, + { + type: newDefaultRuleList.type, + namespace_type: newDefaultRuleList.namespace_type, + id: newDefaultRuleList.id, + list_id: newDefaultRuleList.list_id, + }, + ]); + }); + + it('throws error if rule default list to duplicate not found', async () => { + exceptionsClient.getExceptionList = jest.fn().mockResolvedValue(null); + + const ruleDefaultListReference: List = { + type: ExceptionListTypeEnum.RULE_DEFAULT, + list_id: 'my_list', + namespace_type: 'single', + id: '1234', + }; + + await expect(() => + duplicateExceptions({ + ruleId: 'rule_123', + exceptionLists: [ruleDefaultListReference], + exceptionsClient, + includeExpiredExceptions: false, + }) + ).rejects.toMatchInlineSnapshot( + `[Error: Unable to duplicate rule default exceptions - unable to find their container with list_id: "my_list"]` + ); + }); + + it('throws error if list duplication returns null', async () => { + exceptionsClient.getExceptionList = jest.fn().mockResolvedValue({ + ...getDetectionsExceptionListSchemaMock(), + type: ExceptionListTypeEnum.RULE_DEFAULT, + list_id: 'my_list', + namespace_type: 'single', + id: '1234', + }); + exceptionsClient.duplicateExceptionListAndItems = jest.fn().mockResolvedValue(null); + + const ruleDefaultListReference: List = { + type: ExceptionListTypeEnum.RULE_DEFAULT, + list_id: 'my_list', + namespace_type: 'single', + id: '1234', + }; + + await expect(() => + duplicateExceptions({ + ruleId: 'rule_123', + exceptionLists: [ruleDefaultListReference], + exceptionsClient, + includeExpiredExceptions: false, + }) + ).rejects.toMatchInlineSnapshot( + `[Error: Unable to duplicate rule default exception items for rule_id: rule_123]` + ); + }); +}); diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/actions/duplicate_exceptions.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/actions/duplicate_exceptions.ts index 496a91ba55963..82f13adc4534c 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/actions/duplicate_exceptions.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/actions/duplicate_exceptions.ts @@ -5,23 +5,42 @@ * 2.0. */ +import { i18n } from '@kbn/i18n'; + import type { ExceptionListClient } from '@kbn/lists-plugin/server'; import { ExceptionListTypeEnum } from '@kbn/securitysolution-io-ts-list-types'; import type { RuleParams } from '../../../rule_schema'; +const ERROR_DUPLICATING = i18n.translate( + 'xpack.securitySolution.detectionEngine.rules.cloneExceptions.errorDuplicatingList', + { + defaultMessage: + 'Unable to duplicate rule default exceptions - unable to find their container with list_id:', + } +); + +const ERROR_DUPLICATING_ITEMS = i18n.translate( + 'xpack.securitySolution.detectionEngine.rules.cloneExceptions.errorDuplicatingListItems', + { + defaultMessage: 'Unable to duplicate rule default exception items for rule_id:', + } +); + interface DuplicateExceptionsParams { ruleId: RuleParams['ruleId']; exceptionLists: RuleParams['exceptionsList']; exceptionsClient: ExceptionListClient | undefined; + includeExpiredExceptions: boolean; } export const duplicateExceptions = async ({ ruleId, exceptionLists, exceptionsClient, + includeExpiredExceptions, }: DuplicateExceptionsParams): Promise => { - if (exceptionLists == null) { + if (exceptionLists == null || !exceptionLists.length) { return []; } @@ -37,24 +56,36 @@ export const duplicateExceptions = async ({ // For rule_default list (exceptions that live only on a single rule), we need // to create a new rule_default list to assign to duplicated rule if (ruleDefaultList != null && exceptionsClient != null) { - const ruleDefaultExceptionList = await exceptionsClient.duplicateExceptionListAndItems({ + // fetch list container + const listToDuplicate = await exceptionsClient.getExceptionList({ + id: undefined, listId: ruleDefaultList.list_id, namespaceType: ruleDefaultList.namespace_type, }); - if (ruleDefaultExceptionList == null) { - throw new Error(`Unable to duplicate rule default exception items for rule_id: ${ruleId}`); - } + if (listToDuplicate == null) { + throw new Error(`${ERROR_DUPLICATING} "${ruleDefaultList.list_id}"`); + } else { + const ruleDefaultExceptionList = await exceptionsClient.duplicateExceptionListAndItems({ + list: listToDuplicate, + namespaceType: ruleDefaultList.namespace_type, + includeExpiredExceptions, + }); - return [ - ...sharedLists, - { - id: ruleDefaultExceptionList.id, - list_id: ruleDefaultExceptionList.list_id, - namespace_type: ruleDefaultExceptionList.namespace_type, - type: ruleDefaultExceptionList.type, - }, - ]; + if (ruleDefaultExceptionList == null) { + throw new Error(`${ERROR_DUPLICATING_ITEMS} ${ruleId}`); + } + + return [ + ...sharedLists, + { + id: ruleDefaultExceptionList.id, + list_id: ruleDefaultExceptionList.list_id, + namespace_type: ruleDefaultExceptionList.namespace_type, + type: ruleDefaultExceptionList.type, + }, + ]; + } } // If no rule_default list exists, we can just return diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/actions/duplicate_rule.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/actions/duplicate_rule.test.ts index b441d071c21c1..08a53c007dc06 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/actions/duplicate_rule.test.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/actions/duplicate_rule.test.ts @@ -109,8 +109,6 @@ describe('duplicateRule', () => { consumer: rule.consumer, schedule: rule.schedule, actions: rule.actions, - throttle: null, // TODO: fix? - notifyWhen: null, // TODO: fix? enabled: false, // covered in a separate test }); }); diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/actions/duplicate_rule.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/actions/duplicate_rule.ts index 4a99085123b41..315517504def4 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/actions/duplicate_rule.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/actions/duplicate_rule.ts @@ -11,6 +11,7 @@ import { ruleTypeMappings } from '@kbn/securitysolution-rules'; import type { SanitizedRule } from '@kbn/alerting-plugin/common'; import { SERVER_APP_ID } from '../../../../../../common/constants'; import type { InternalRuleCreate, RuleParams } from '../../../rule_schema'; +import { transformToActionFrequency } from '../../normalization/rule_actions'; const DUPLICATE_TITLE = i18n.translate( 'xpack.securitySolution.detectionEngine.rules.cloneRule.duplicateTitle', @@ -33,6 +34,7 @@ export const duplicateRule = async ({ rule }: DuplicateRuleParams): Promise - ({ - field: 'throttle', - operation: 'set', - value: transformToAlertThrottle(throttle), - } as const); - -const getNotifyWhenOperation = (throttle: string) => - ({ - field: 'notifyWhen', - operation: 'set', - value: transformToNotifyWhen(throttle), - } as const); +import { transformToActionFrequency } from '../../normalization/rule_actions'; /** * converts bulk edit action to format of rulesClient.bulkEdit operation @@ -70,10 +55,8 @@ export const bulkEditActionToRulesClientOperation = ( { field: 'actions', operation: 'add', - value: action.value.actions, + value: transformToActionFrequency(action.value.actions, action.value.throttle), }, - getThrottleOperation(action.value.throttle), - getNotifyWhenOperation(action.value.throttle), ]; case BulkActionEditType.set_rule_actions: @@ -81,10 +64,8 @@ export const bulkEditActionToRulesClientOperation = ( { field: 'actions', operation: 'set', - value: action.value.actions, + value: transformToActionFrequency(action.value.actions, action.value.throttle), }, - getThrottleOperation(action.value.throttle), - getNotifyWhenOperation(action.value.throttle), ]; // schedule actions diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/crud/patch_rules.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/crud/patch_rules.test.ts index cfb051273255a..6e4d573a880e0 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/crud/patch_rules.test.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/crud/patch_rules.test.ts @@ -123,6 +123,7 @@ describe('patchRules', () => { message: 'Rule {{context.rule.name}} generated {{state.signals_count}} signals', }, group: 'default', + frequency: { summary: true, throttle: null, notifyWhen: 'onActiveAlert' }, }, ], }), @@ -158,6 +159,7 @@ describe('patchRules', () => { message: 'Rule {{context.rule.name}} generated {{state.signals_count}} signals', }, group: 'default', + frequency: { summary: true, throttle: null, notifyWhen: 'onActiveAlert' }, }, ], }), diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/crud/update_rules.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/crud/update_rules.ts index 1996cb1652ab6..070a81692f3d5 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/crud/update_rules.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/crud/update_rules.ts @@ -12,7 +12,7 @@ import type { RuleUpdateProps } from '../../../../../../common/detection_engine/ import { transformRuleToAlertAction } from '../../../../../../common/detection_engine/transform_actions'; import type { InternalRuleUpdate, RuleParams, RuleAlertType } from '../../../rule_schema'; -import { transformToAlertThrottle, transformToNotifyWhen } from '../../normalization/rule_actions'; +import { transformToActionFrequency } from '../../normalization/rule_actions'; import { typeSpecificSnakeToCamel } from '../../normalization/rule_converters'; export interface UpdateRulesOptions { @@ -30,6 +30,9 @@ export const updateRules = async ({ return null; } + const alertActions = ruleUpdate.actions?.map(transformRuleToAlertAction) ?? []; + const actions = transformToActionFrequency(alertActions, ruleUpdate.throttle); + const typeSpecificParams = typeSpecificSnakeToCamel(ruleUpdate); const enabled = ruleUpdate.enabled ?? true; const newInternalRule: InternalRuleUpdate = { @@ -70,9 +73,7 @@ export const updateRules = async ({ ...typeSpecificParams, }, schedule: { interval: ruleUpdate.interval ?? '5m' }, - actions: ruleUpdate.actions != null ? ruleUpdate.actions.map(transformRuleToAlertAction) : [], - throttle: transformToAlertThrottle(ruleUpdate.throttle), - notifyWhen: transformToNotifyWhen(ruleUpdate.throttle), + actions, }; const update = await rulesClient.update({ diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/export/get_export_all.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/export/get_export_all.test.ts index 038dfbc9152d6..c86387f26f50a 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/export/get_export_all.test.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/export/get_export_all.test.ts @@ -131,7 +131,6 @@ describe('getExportAll', () => { to: 'now', type: 'query', threat: getThreatMock(), - throttle: 'no_actions', note: '# Investigative notes', version: 1, exceptions_list: getListArrayMock(), @@ -275,6 +274,7 @@ describe('getExportAll', () => { message: 'Rule {{context.rule.name}} generated {{state.signals_count}} alerts', }, action_type_id: '.slack', + frequency: { summary: true, throttle: null, notifyWhen: 'onActiveAlert' }, }, ], building_block_type: 'default', @@ -313,7 +313,6 @@ describe('getExportAll', () => { to: 'now', type: 'query', threat: getThreatMock(), - throttle: 'rule', note: '# Investigative notes', version: 1, revision: 0, @@ -416,6 +415,7 @@ describe('getExportAll', () => { message: 'Rule {{context.rule.name}} generated {{state.signals_count}} alerts', }, action_type_id: '.email', + frequency: { summary: true, throttle: null, notifyWhen: 'onActiveAlert' }, }, ], }) diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/export/get_export_by_object_ids.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/export/get_export_by_object_ids.test.ts index de76ef3b23d8b..35151f1b4b1d6 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/export/get_export_by_object_ids.test.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/export/get_export_by_object_ids.test.ts @@ -128,7 +128,6 @@ describe('get_export_by_object_ids', () => { to: 'now', type: 'query', threat: getThreatMock(), - throttle: 'no_actions', note: '# Investigative notes', version: 1, exceptions_list: getListArrayMock(), @@ -284,6 +283,7 @@ describe('get_export_by_object_ids', () => { message: 'Rule {{context.rule.name}} generated {{state.signals_count}} alerts', }, action_type_id: '.slack', + frequency: { summary: true, throttle: null, notifyWhen: 'onActiveAlert' }, }, ], building_block_type: 'default', @@ -322,7 +322,6 @@ describe('get_export_by_object_ids', () => { to: 'now', type: 'query', threat: getThreatMock(), - throttle: 'rule', note: '# Investigative notes', version: 1, revision: 0, @@ -426,6 +425,7 @@ describe('get_export_by_object_ids', () => { message: 'Rule {{context.rule.name}} generated {{state.signals_count}} alerts', }, action_type_id: '.email', + frequency: { summary: true, throttle: null, notifyWhen: 'onActiveAlert' }, }, ], }) @@ -508,7 +508,7 @@ describe('get_export_by_object_ids', () => { to: 'now', type: 'query', threat: getThreatMock(), - throttle: 'no_actions', + throttle: undefined, note: '# Investigative notes', version: 1, revision: 0, diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/normalization/rule_actions.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/normalization/rule_actions.test.ts index d0d5edad970f0..33fcf0bd29c20 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/normalization/rule_actions.test.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/normalization/rule_actions.test.ts @@ -5,7 +5,9 @@ * 2.0. */ +import type { SanitizedRuleAction } from '@kbn/alerting-plugin/common'; import { + NOTIFICATION_DEFAULT_FREQUENCY, NOTIFICATION_THROTTLE_NO_ACTIONS, NOTIFICATION_THROTTLE_RULE, } from '../../../../../common/constants'; @@ -14,6 +16,7 @@ import type { RuleAlertType } from '../../rule_schema'; import { transformFromAlertThrottle, + transformToActionFrequency, transformToAlertThrottle, transformToNotifyWhen, } from './rule_actions'; @@ -151,4 +154,152 @@ describe('Rule actions normalization', () => { ).toEqual(NOTIFICATION_THROTTLE_RULE); }); }); + + describe('transformToActionFrequency', () => { + describe('actions without frequencies', () => { + const actionsWithoutFrequencies: SanitizedRuleAction[] = [ + { + group: 'group', + id: 'id-123', + actionTypeId: 'id-456', + params: {}, + }, + { + group: 'group', + id: 'id-789', + actionTypeId: 'id-012', + params: {}, + }, + ]; + + test.each([undefined, null, NOTIFICATION_THROTTLE_NO_ACTIONS, NOTIFICATION_THROTTLE_RULE])( + `it sets each action's frequency attribute to default value when 'throttle' is '%s'`, + (throttle) => { + expect(transformToActionFrequency(actionsWithoutFrequencies, throttle)).toEqual( + actionsWithoutFrequencies.map((action) => ({ + ...action, + frequency: NOTIFICATION_DEFAULT_FREQUENCY, + })) + ); + } + ); + + test.each(['47s', '10m', '3h', '101d'])( + `it correctly transforms 'throttle = %s' and sets it as a frequency of each action`, + (throttle) => { + expect(transformToActionFrequency(actionsWithoutFrequencies, throttle)).toEqual( + actionsWithoutFrequencies.map((action) => ({ + ...action, + frequency: { + summary: true, + throttle, + notifyWhen: 'onThrottleInterval', + }, + })) + ); + } + ); + }); + + describe('actions with frequencies', () => { + const actionsWithFrequencies: SanitizedRuleAction[] = [ + { + group: 'group', + id: 'id-123', + actionTypeId: 'id-456', + params: {}, + frequency: { + summary: true, + throttle: null, + notifyWhen: 'onActiveAlert', + }, + }, + { + group: 'group', + id: 'id-789', + actionTypeId: 'id-012', + params: {}, + frequency: { + summary: false, + throttle: '1s', + notifyWhen: 'onThrottleInterval', + }, + }, + ]; + + test.each([ + undefined, + null, + NOTIFICATION_THROTTLE_NO_ACTIONS, + NOTIFICATION_THROTTLE_RULE, + '1h', + '1d', + ])(`it does not change actions frequency attributes when 'throttle' is '%s'`, (throttle) => { + expect(transformToActionFrequency(actionsWithFrequencies, throttle)).toEqual( + actionsWithFrequencies + ); + }); + }); + + describe('some actions with frequencies', () => { + const someActionsWithFrequencies: SanitizedRuleAction[] = [ + { + group: 'group', + id: 'id-123', + actionTypeId: 'id-456', + params: {}, + frequency: { + summary: true, + throttle: null, + notifyWhen: 'onActiveAlert', + }, + }, + { + group: 'group', + id: 'id-789', + actionTypeId: 'id-012', + params: {}, + frequency: { + summary: false, + throttle: '1s', + notifyWhen: 'onThrottleInterval', + }, + }, + { + group: 'group', + id: 'id-345', + actionTypeId: 'id-678', + params: {}, + }, + ]; + + test.each([undefined, null, NOTIFICATION_THROTTLE_NO_ACTIONS, NOTIFICATION_THROTTLE_RULE])( + `it overrides each action's frequency attribute to default value when 'throttle' is '%s'`, + (throttle) => { + expect(transformToActionFrequency(someActionsWithFrequencies, throttle)).toEqual( + someActionsWithFrequencies.map((action) => ({ + ...action, + frequency: action.frequency ?? NOTIFICATION_DEFAULT_FREQUENCY, + })) + ); + } + ); + + test.each(['47s', '10m', '3h', '101d'])( + `it correctly transforms 'throttle = %s' and overrides frequency attribute of each action`, + (throttle) => { + expect(transformToActionFrequency(someActionsWithFrequencies, throttle)).toEqual( + someActionsWithFrequencies.map((action) => ({ + ...action, + frequency: action.frequency ?? { + summary: true, + throttle, + notifyWhen: 'onThrottleInterval', + }, + })) + ); + } + ); + }); + }); }); diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/normalization/rule_actions.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/normalization/rule_actions.ts index 51dbe9d80f986..d0e909cc5f329 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/normalization/rule_actions.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/normalization/rule_actions.ts @@ -5,7 +5,7 @@ * 2.0. */ -import type { RuleNotifyWhenType } from '@kbn/alerting-plugin/common'; +import type { RuleActionFrequency, RuleNotifyWhenType } from '@kbn/alerting-plugin/common'; import { NOTIFICATION_THROTTLE_NO_ACTIONS, @@ -14,6 +14,38 @@ import { import type { RuleAlertType } from '../../rule_schema'; +export const transformToFrequency = (throttle: string | null | undefined): RuleActionFrequency => { + return { + summary: true, + notifyWhen: transformToNotifyWhen(throttle) ?? 'onActiveAlert', + throttle: transformToAlertThrottle(throttle), + }; +}; + +interface ActionWithFrequency { + frequency?: RuleActionFrequency; +} + +/** + * The action level `frequency` attribute should always take precedence over the rule level `throttle` + * Frequency's default value is `{ summary: true, throttle: null, notifyWhen: 'onActiveAlert' }` + * + * The transformation follows the next rules: + * - Both rule level `throttle` and all actions have `frequency` are set: we will ignore rule level `throttle` + * - Rule level `throttle` set and actions don't have `frequency` set: we will transform rule level `throttle` in action level `frequency` + * - All actions have `frequency` set: do nothing + * - Neither of them is set: we will set action level `frequency` to default value + * - Rule level `throttle` and some of the actions have `frequency` set: we will transform rule level `throttle` and set it to actions without the frequency attribute + * - Only some actions have `frequency` set and there is no rule level `throttle`: we will set default `frequency` to actions without frequency attribute + */ +export const transformToActionFrequency = ( + actions: T[], + throttle: string | null | undefined +): T[] => { + const defaultFrequency = transformToFrequency(throttle); + return actions.map((action) => ({ ...action, frequency: action.frequency ?? defaultFrequency })); +}; + /** * Given a throttle from a "security_solution" rule this will transform it into an "alerting" notifyWhen * on their saved object. diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/normalization/rule_converters.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/normalization/rule_converters.ts index 7296e90bf73e7..66d873c2c0935 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/normalization/rule_converters.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/normalization/rule_converters.ts @@ -39,10 +39,10 @@ import { } from '../../../../../common/detection_engine/rule_schema'; import { + transformAlertToRuleAction, transformAlertToRuleResponseAction, transformRuleToAlertAction, transformRuleToAlertResponseAction, - transformAlertToRuleAction, } from '../../../../../common/detection_engine/transform_actions'; import { @@ -73,11 +73,7 @@ import type { NewTermsRuleParams, NewTermsSpecificRuleParams, } from '../../rule_schema'; -import { - transformFromAlertThrottle, - transformToAlertThrottle, - transformToNotifyWhen, -} from './rule_actions'; +import { transformFromAlertThrottle, transformToActionFrequency } from './rule_actions'; import { convertAlertSuppressionToCamel, convertAlertSuppressionToSnake } from '../utils/utils'; import { createRuleExecutionSummary } from '../../rule_monitoring'; @@ -399,6 +395,11 @@ export const convertPatchAPIToInternalSchema = ( ): InternalRuleUpdate => { const typeSpecificParams = patchTypeSpecificSnakeToCamel(nextParams, existingRule.params); const existingParams = existingRule.params; + + const alertActions = nextParams.actions?.map(transformRuleToAlertAction) ?? existingRule.actions; + const throttle = nextParams.throttle ?? transformFromAlertThrottle(existingRule); + const actions = transformToActionFrequency(alertActions, throttle); + return { name: nextParams.name ?? existingRule.name, tags: nextParams.tags ?? existingRule.tags, @@ -438,15 +439,7 @@ export const convertPatchAPIToInternalSchema = ( ...typeSpecificParams, }, schedule: { interval: nextParams.interval ?? existingRule.schedule.interval }, - actions: nextParams.actions - ? nextParams.actions.map(transformRuleToAlertAction) - : existingRule.actions, - throttle: nextParams.throttle - ? transformToAlertThrottle(nextParams.throttle) - : existingRule.throttle ?? null, - notifyWhen: nextParams.throttle - ? transformToNotifyWhen(nextParams.throttle) - : existingRule.notifyWhen ?? null, + actions, }; }; @@ -462,6 +455,10 @@ export const convertCreateAPIToInternalSchema = ( ): InternalRuleCreate => { const typeSpecificParams = typeSpecificSnakeToCamel(input); const newRuleId = input.rule_id ?? uuidv4(); + + const alertActions = input.actions?.map(transformRuleToAlertAction) ?? []; + const actions = transformToActionFrequency(alertActions, input.throttle); + return { name: input.name, tags: input.tags ?? [], @@ -502,9 +499,7 @@ export const convertCreateAPIToInternalSchema = ( }, schedule: { interval: input.interval ?? '5m' }, enabled: input.enabled ?? defaultEnabled, - actions: input.actions?.map(transformRuleToAlertAction) ?? [], - throttle: transformToAlertThrottle(input.throttle), - notifyWhen: transformToNotifyWhen(input.throttle), + actions, }; }; @@ -651,6 +646,10 @@ export const internalRuleToAPIResponse = ( const isResolvedRule = (obj: unknown): obj is ResolvedSanitizedRule => (obj as ResolvedSanitizedRule).outcome != null; + const alertActions = rule.actions.map(transformAlertToRuleAction); + const throttle = transformFromAlertThrottle(rule); + const actions = transformToActionFrequency(alertActions, throttle); + return { // saved object properties outcome: isResolvedRule(rule) ? rule.outcome : undefined, @@ -672,8 +671,8 @@ export const internalRuleToAPIResponse = ( // Type specific security solution rule params ...typeSpecificCamelToSnake(rule.params), // Actions - throttle: transformFromAlertThrottle(rule), - actions: rule.actions.map(transformAlertToRuleAction), + throttle: undefined, + actions, // Execution summary execution_summary: executionSummary ?? undefined, }; diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/utils/validate.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/utils/validate.test.ts index bae75363ff49a..373842fe31860 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/utils/validate.test.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/utils/validate.test.ts @@ -43,7 +43,7 @@ export const ruleOutput = (): RuleResponse => ({ tags: [], to: 'now', type: 'query', - throttle: 'no_actions', + throttle: undefined, threat: getThreatMock(), version: 1, revision: 0, diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_monitoring/logic/rule_execution_log/__mocks__/index.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_monitoring/logic/rule_execution_log/__mocks__/index.ts index c0cb0cefa234e..c68eab1baec36 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_monitoring/logic/rule_execution_log/__mocks__/index.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_monitoring/logic/rule_execution_log/__mocks__/index.ts @@ -40,6 +40,7 @@ const ruleExecutionLogForExecutorsMock = { ruleId: context.ruleId ?? 'some rule id', ruleUuid: context.ruleUuid ?? 'some rule uuid', ruleName: context.ruleName ?? 'Some rule', + ruleRevision: context.ruleRevision ?? 0, ruleType: context.ruleType ?? 'some rule type', spaceId: context.spaceId ?? 'some space id', }, diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_monitoring/logic/rule_execution_log/client_for_executors/client.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_monitoring/logic/rule_execution_log/client_for_executors/client.ts index a0cbe754d6b3c..a8d61aa7dda84 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_monitoring/logic/rule_execution_log/client_for_executors/client.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_monitoring/logic/rule_execution_log/client_for_executors/client.ts @@ -50,7 +50,7 @@ export const createClientForExecutors = ( const baseLogSuffix = baseCorrelationIds.getLogSuffix(); const baseLogMeta = baseCorrelationIds.getLogMeta(); - const { executionId, ruleId, ruleUuid, ruleName, ruleType, spaceId } = context; + const { executionId, ruleId, ruleUuid, ruleName, ruleRevision, ruleType, spaceId } = context; const client: IRuleExecutionLogForExecutors = { get context() { @@ -140,6 +140,7 @@ export const createClientForExecutors = ( ruleId, ruleUuid, ruleName, + ruleRevision, ruleType, spaceId, executionId, @@ -202,6 +203,7 @@ export const createClientForExecutors = ( ruleId, ruleUuid, ruleName, + ruleRevision, ruleType, spaceId, executionId, @@ -213,6 +215,7 @@ export const createClientForExecutors = ( ruleId, ruleUuid, ruleName, + ruleRevision, ruleType, spaceId, executionId, diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_monitoring/logic/rule_execution_log/client_for_executors/client_interface.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_monitoring/logic/rule_execution_log/client_for_executors/client_interface.ts index cb65b42d50b69..54cad1f72be07 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_monitoring/logic/rule_execution_log/client_for_executors/client_interface.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_monitoring/logic/rule_execution_log/client_for_executors/client_interface.ts @@ -92,6 +92,11 @@ export interface RuleExecutionContext { */ ruleName: string; + /** + * Current revision of the rule being execution (rule.revision) + */ + ruleRevision: number; + /** * Alerting Framework's rule type id of the rule being executed. */ diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_monitoring/logic/rule_execution_log/event_log/event_log_writer.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_monitoring/logic/rule_execution_log/event_log/event_log_writer.ts index aa1fcf36aba68..ceed2f1d3a739 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_monitoring/logic/rule_execution_log/event_log/event_log_writer.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_monitoring/logic/rule_execution_log/event_log/event_log_writer.ts @@ -31,6 +31,7 @@ export interface BaseArgs { ruleId: string; ruleUuid: string; ruleName: string; + ruleRevision: number; ruleType: string; spaceId: string; executionId: string; @@ -83,6 +84,7 @@ export const createEventLogWriter = (eventLogService: IEventLogService): IEventL execution: { uuid: args.executionId, }, + revision: args.ruleRevision, }, }, space_ids: [args.spaceId], @@ -126,6 +128,7 @@ export const createEventLogWriter = (eventLogService: IEventLogService): IEventL status: args.newStatus, status_order: ruleExecutionStatusToNumber(args.newStatus), }, + revision: args.ruleRevision, }, }, space_ids: [args.spaceId], @@ -167,6 +170,7 @@ export const createEventLogWriter = (eventLogService: IEventLogService): IEventL uuid: args.executionId, metrics: args.metrics, }, + revision: args.ruleRevision, }, }, space_ids: [args.spaceId], diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_schema/model/rule_schemas.mock.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_schema/model/rule_schemas.mock.ts index 10a213a5d8b2f..44fa44ee01892 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_schema/model/rule_schemas.mock.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_schema/model/rule_schemas.mock.ts @@ -213,10 +213,10 @@ export const getRuleConfigMock = (type: string = 'rule-type'): SanitizedRuleConf consumer: 'sample consumer', notifyWhen: null, producer: 'sample producer', + revision: 0, ruleTypeId: `${type}-id`, ruleTypeName: type, muteAll: false, - revision: 0, snoozeSchedule: [], }); diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_schema/model/rule_schemas.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_schema/model/rule_schemas.ts index e204aeb7bc50f..0fbddc2a2236c 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_schema/model/rule_schemas.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_schema/model/rule_schemas.ts @@ -14,6 +14,7 @@ import { RiskScore, RiskScoreMapping, RuleActionArrayCamel, + RuleActionNotifyWhen, RuleActionThrottle, RuleIntervalFrom, RuleIntervalTo, @@ -259,13 +260,6 @@ export interface CompleteRule { ruleConfig: SanitizedRuleConfig; } -export const notifyWhen = t.union([ - t.literal('onActionGroupChange'), - t.literal('onActiveAlert'), - t.literal('onThrottleInterval'), - t.null, -]); - export const allRuleTypes = t.union([ t.literal(SIGNALS_ID), t.literal(EQL_RULE_TYPE_ID), @@ -291,7 +285,7 @@ const internalRuleCreateRequired = t.type({ }); const internalRuleCreateOptional = t.partial({ throttle: t.union([RuleActionThrottle, t.null]), - notifyWhen, + notifyWhen: t.union([RuleActionNotifyWhen, t.null]), }); export const internalRuleCreate = t.intersection([ internalRuleCreateOptional, @@ -310,7 +304,7 @@ const internalRuleUpdateRequired = t.type({ }); const internalRuleUpdateOptional = t.partial({ throttle: t.union([RuleActionThrottle, t.null]), - notifyWhen, + notifyWhen: t.union([RuleActionNotifyWhen, t.null]), }); export const internalRuleUpdate = t.intersection([ internalRuleUpdateOptional, 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 a22a95adf307b..e2650f0a142f2 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 @@ -124,6 +124,7 @@ export const createSecurityRuleTypeWrapper: CreateSecurityRuleTypeWrapper = ruleId: rule.id, ruleUuid: params.ruleId, ruleName: rule.name, + ruleRevision: rule.revision, ruleType: rule.ruleTypeId, spaceId, }, diff --git a/x-pack/plugins/security_solution/tsconfig.json b/x-pack/plugins/security_solution/tsconfig.json index 622785a43b4e8..6b14dd7b616bf 100644 --- a/x-pack/plugins/security_solution/tsconfig.json +++ b/x-pack/plugins/security_solution/tsconfig.json @@ -23,7 +23,9 @@ ], "kbn_references": [ "@kbn/core", - { "path": "../../../src/setup_node_env/tsconfig.json" }, + { + "path": "../../../src/setup_node_env/tsconfig.json" + }, "@kbn/data-plugin", "@kbn/embeddable-plugin", "@kbn/files-plugin", @@ -153,5 +155,6 @@ "@kbn/security-solution-side-nav", "@kbn/core-lifecycle-browser", "@kbn/ecs", + "@kbn/url-state" ] } diff --git a/x-pack/plugins/snapshot_restore/__jest__/client_integration/helpers/repository_edit.helpers.ts b/x-pack/plugins/snapshot_restore/__jest__/client_integration/helpers/repository_edit.helpers.ts index 04ddabf9c1bfd..0807f8985af90 100644 --- a/x-pack/plugins/snapshot_restore/__jest__/client_integration/helpers/repository_edit.helpers.ts +++ b/x-pack/plugins/snapshot_restore/__jest__/client_integration/helpers/repository_edit.helpers.ts @@ -88,4 +88,5 @@ type TestSubjects = | 'stepTwo.submitButton' | 'stepTwo.title' | 'submitButton' + | 'codeEditor' | 'title'; diff --git a/x-pack/plugins/snapshot_restore/__jest__/client_integration/helpers/setup_environment.tsx b/x-pack/plugins/snapshot_restore/__jest__/client_integration/helpers/setup_environment.tsx index 1124a179d245d..7eef715118c95 100644 --- a/x-pack/plugins/snapshot_restore/__jest__/client_integration/helpers/setup_environment.tsx +++ b/x-pack/plugins/snapshot_restore/__jest__/client_integration/helpers/setup_environment.tsx @@ -10,6 +10,7 @@ import { i18n } from '@kbn/i18n'; import { merge } from 'lodash'; import { LocationDescriptorObject } from 'history'; +import { KibanaContextProvider } from '@kbn/kibana-react-plugin/public'; import { HttpSetup } from '@kbn/core/public'; import { coreMock, scopedHistoryMock } from '@kbn/core/public/mocks'; import { setUiMetricService, httpService } from '../../../public/application/services/http'; @@ -50,8 +51,10 @@ export const services = { setUiMetricService(services.uiMetricService); +const core = coreMock.createStart(); + const appDependencies = { - core: coreMock.createStart(), + core, services, config: { slm_ui: { enabled: true }, @@ -59,6 +62,10 @@ const appDependencies = { plugins: {}, }; +const kibanaContextDependencies = { + uiSettings: core.uiSettings, +}; + export const setupEnvironment = () => { breadcrumbService.setup(() => undefined); textService.setup(i18n); @@ -88,11 +95,13 @@ export const WithAppDependencies = - - - - - + + + + + + + ); }; diff --git a/x-pack/plugins/snapshot_restore/__jest__/client_integration/repository_edit.test.ts b/x-pack/plugins/snapshot_restore/__jest__/client_integration/repository_edit.test.tsx similarity index 93% rename from x-pack/plugins/snapshot_restore/__jest__/client_integration/repository_edit.test.ts rename to x-pack/plugins/snapshot_restore/__jest__/client_integration/repository_edit.test.tsx index 102c0d13a012b..dc05d9c09e627 100644 --- a/x-pack/plugins/snapshot_restore/__jest__/client_integration/repository_edit.test.ts +++ b/x-pack/plugins/snapshot_restore/__jest__/client_integration/repository_edit.test.tsx @@ -5,6 +5,7 @@ * 2.0. */ +import React from 'react'; import { act } from 'react-dom/test-utils'; import { setupEnvironment, pageHelpers, nextTick, TestBed, getRandomString } from './helpers'; @@ -16,6 +17,23 @@ import { REPOSITORY_EDIT, REPOSITORY_NAME } from './helpers/constant'; const { setup } = pageHelpers.repositoryEdit; const { setup: setupRepositoryAdd } = pageHelpers.repositoryAdd; +jest.mock('@kbn/kibana-react-plugin/public', () => { + const original = jest.requireActual('@kbn/kibana-react-plugin/public'); + return { + ...original, + // Mocking CodeEditor, which uses React Monaco under the hood + CodeEditor: (props: any) => ( + { + props.onChange(e.jsonContent); + }} + /> + ), + }; +}); + describe('', () => { let testBed: TestBed; let testBedRepositoryAdd: TestBed; @@ -211,8 +229,8 @@ describe('', () => { ); expect(find('readOnlyToggle').props()['aria-checked']).toBe(settings.readonly); - const codeEditor = testBed.component.find('EuiCodeEditor').at(1); - expect(JSON.parse(codeEditor.props().value as string)).toEqual({ + const codeEditorValue = testBed.find('codeEditor').props()['data-currentvalue']; + expect(JSON.parse(codeEditorValue)).toEqual({ loadDefault: true, conf1: 'foo', conf2: 'bar', diff --git a/x-pack/plugins/snapshot_restore/public/application/app_context.tsx b/x-pack/plugins/snapshot_restore/public/application/app_context.tsx index 4f36da98cfed6..85e4b3b079541 100644 --- a/x-pack/plugins/snapshot_restore/public/application/app_context.tsx +++ b/x-pack/plugins/snapshot_restore/public/application/app_context.tsx @@ -9,7 +9,7 @@ import React, { createContext, useContext } from 'react'; import { i18n } from '@kbn/i18n'; import { Observable } from 'rxjs'; -import { CoreStart, ScopedHistory, CoreTheme } from '@kbn/core/public'; +import { CoreStart, ScopedHistory, CoreTheme, IUiSettingsClient } from '@kbn/core/public'; import { ClientConfigType } from '../types'; import { HttpService, UiMetricService } from './services'; @@ -18,6 +18,7 @@ const AppContext = createContext(undefined); export interface AppDependencies { core: CoreStart; services: { + uiSettings: IUiSettingsClient; httpService: HttpService; uiMetricService: UiMetricService; i18n: typeof i18n; diff --git a/x-pack/plugins/snapshot_restore/public/application/components/repository_form/type_settings/hdfs_settings.tsx b/x-pack/plugins/snapshot_restore/public/application/components/repository_form/type_settings/hdfs_settings.tsx index 724f3a6a17a5f..6abb387e87a1a 100644 --- a/x-pack/plugins/snapshot_restore/public/application/components/repository_form/type_settings/hdfs_settings.tsx +++ b/x-pack/plugins/snapshot_restore/public/application/components/repository_form/type_settings/hdfs_settings.tsx @@ -18,8 +18,8 @@ import { EuiTitle, } from '@elastic/eui'; +import { CodeEditor } from '@kbn/kibana-react-plugin/public'; import { HDFSRepository, Repository, SourceRepository } from '../../../../../common/types'; -import { EuiCodeEditor } from '../../../../shared_imports'; import { RepositorySettingsValidation } from '../../../services/validation'; import { ChunkSizeField, MaxSnapshotsField, MaxRestoreField } from './common'; @@ -347,21 +347,16 @@ export const HDFSSettings: React.FunctionComponent = ({ /> } > - = ({ setIsConfInvalid(true); } }} - data-test-subj="codeEditor" /> diff --git a/x-pack/plugins/snapshot_restore/public/application/components/restore_snapshot_form/steps/step_review.tsx b/x-pack/plugins/snapshot_restore/public/application/components/restore_snapshot_form/steps/step_review.tsx index 2f650ee754259..09feaf61ca9e7 100644 --- a/x-pack/plugins/snapshot_restore/public/application/components/restore_snapshot_form/steps/step_review.tsx +++ b/x-pack/plugins/snapshot_restore/public/application/components/restore_snapshot_form/steps/step_review.tsx @@ -21,9 +21,9 @@ import { EuiLink, EuiIcon, EuiToolTip, + EuiCodeBlock, } from '@elastic/eui'; import { serializeRestoreSettings } from '../../../../../common/lib'; -import { EuiCodeEditor } from '../../../../shared_imports'; import { useServices } from '../../../app_context'; import { StepProps } from '.'; import { CollapsibleIndicesList } from '../../collapsible_lists'; @@ -285,18 +285,17 @@ export const RestoreSnapshotStepReview: React.FunctionComponent = ({ const renderJsonTab = () => ( - + > + {JSON.stringify(serializedRestoreSettings, null, 2)} + ); diff --git a/x-pack/plugins/snapshot_restore/public/application/components/restore_snapshot_form/steps/step_settings.tsx b/x-pack/plugins/snapshot_restore/public/application/components/restore_snapshot_form/steps/step_settings.tsx index 46ad27e9a997b..70da1ebb1e449 100644 --- a/x-pack/plugins/snapshot_restore/public/application/components/restore_snapshot_form/steps/step_settings.tsx +++ b/x-pack/plugins/snapshot_restore/public/application/components/restore_snapshot_form/steps/step_settings.tsx @@ -21,8 +21,8 @@ import { EuiLink, EuiCallOut, } from '@elastic/eui'; +import { CodeEditor } from '@kbn/kibana-react-plugin/public'; import { RestoreSettings } from '../../../../../common/types'; -import { EuiCodeEditor } from '../../../../shared_imports'; import { REMOVE_INDEX_SETTINGS_SUGGESTIONS } from '../../../constants'; import { useCore, useServices } from '../../../app_context'; import { StepProps } from '.'; @@ -190,22 +190,15 @@ export const RestoreSnapshotStepSettings: React.FunctionComponent = ( /> } > - ( export const renderApp = (elem: Element, dependencies: AppDependencies) => { render( - - - , + + + + + , elem ); diff --git a/x-pack/plugins/snapshot_restore/public/application/mount_management_section.ts b/x-pack/plugins/snapshot_restore/public/application/mount_management_section.ts index 352e7c37f7ee2..b86cfe6318c80 100644 --- a/x-pack/plugins/snapshot_restore/public/application/mount_management_section.ts +++ b/x-pack/plugins/snapshot_restore/public/application/mount_management_section.ts @@ -37,6 +37,7 @@ export async function mountManagementSection( core, config, services: { + uiSettings: coreSetup.uiSettings, httpService, uiMetricService: services.uiMetricService, i18n, diff --git a/x-pack/plugins/snapshot_restore/public/application/sections/home/policy_list/policy_details/tabs/tab_history.tsx b/x-pack/plugins/snapshot_restore/public/application/sections/home/policy_list/policy_details/tabs/tab_history.tsx index aee603d5d7f6d..b3256f2a0c6ff 100644 --- a/x-pack/plugins/snapshot_restore/public/application/sections/home/policy_list/policy_details/tabs/tab_history.tsx +++ b/x-pack/plugins/snapshot_restore/public/application/sections/home/policy_list/policy_details/tabs/tab_history.tsx @@ -19,11 +19,11 @@ import { EuiText, EuiHorizontalRule, EuiSpacer, + EuiCodeBlock, } from '@elastic/eui'; import { reactRouterNavigate } from '@kbn/kibana-react-plugin/public'; import { SlmPolicy } from '../../../../../../../common/types'; -import { EuiCodeEditor } from '../../../../../../shared_imports'; import { FormattedDateTime } from '../../../../../components'; import { linkToSnapshot } from '../../../../../services/navigation'; import { useServices } from '../../../../../app_context'; @@ -148,23 +148,10 @@ export const TabHistory: React.FunctionComponent = ({ policy }) => { - = ({ policy }) => { values: { name }, } )} - /> + > + {JSON.stringify(details, null, 2)} + diff --git a/x-pack/plugins/snapshot_restore/public/application/sections/home/repository_list/repository_details/type_details/default_details.tsx b/x-pack/plugins/snapshot_restore/public/application/sections/home/repository_list/repository_details/type_details/default_details.tsx index c1a379785ffd1..fb03299102db9 100644 --- a/x-pack/plugins/snapshot_restore/public/application/sections/home/repository_list/repository_details/type_details/default_details.tsx +++ b/x-pack/plugins/snapshot_restore/public/application/sections/home/repository_list/repository_details/type_details/default_details.tsx @@ -5,15 +5,12 @@ * 2.0. */ -import 'react-ace'; -import 'brace/theme/textmate'; import React, { Fragment } from 'react'; import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n-react'; -import { EuiSpacer, EuiTitle } from '@elastic/eui'; +import { EuiCodeBlock, EuiSpacer, EuiTitle } from '@elastic/eui'; import { Repository } from '../../../../../../../common/types'; -import { EuiCodeEditor } from '../../../../../../shared_imports'; interface Props { repository: Repository; @@ -35,22 +32,10 @@ export const DefaultDetails: React.FunctionComponent = ({ - = ({ }, } )} - /> + > + {JSON.stringify(settings, null, 2)} + ); }; diff --git a/x-pack/plugins/snapshot_restore/public/shared_imports.ts b/x-pack/plugins/snapshot_restore/public/shared_imports.ts index 749d5ed51b53b..4a3ec13727dd1 100644 --- a/x-pack/plugins/snapshot_restore/public/shared_imports.ts +++ b/x-pack/plugins/snapshot_restore/public/shared_imports.ts @@ -28,7 +28,6 @@ export { useAuthorizationContext, useRequest, WithPrivileges, - EuiCodeEditor, AuthorizationContext, GlobalFlyout, } from '@kbn/es-ui-shared-plugin/public'; diff --git a/x-pack/plugins/stack_alerts/server/rule_types/es_query/rule_type.test.ts b/x-pack/plugins/stack_alerts/server/rule_types/es_query/rule_type.test.ts index 8b1679241d9c9..fded7e8ee37b9 100644 --- a/x-pack/plugins/stack_alerts/server/rule_types/es_query/rule_type.test.ts +++ b/x-pack/plugins/stack_alerts/server/rule_types/es_query/rule_type.test.ts @@ -713,10 +713,10 @@ async function invokeExecutor({ tags: [], consumer: '', producer: '', + revision: 0, ruleTypeId: '', ruleTypeName: '', enabled: true, - revision: 0, schedule: { interval: '1h', }, diff --git a/x-pack/plugins/stack_alerts/server/rule_types/index_threshold/rule_type.test.ts b/x-pack/plugins/stack_alerts/server/rule_types/index_threshold/rule_type.test.ts index 92664d05ff9ab..b424fb742b5ab 100644 --- a/x-pack/plugins/stack_alerts/server/rule_types/index_threshold/rule_type.test.ts +++ b/x-pack/plugins/stack_alerts/server/rule_types/index_threshold/rule_type.test.ts @@ -203,10 +203,10 @@ describe('ruleType', () => { tags: [], consumer: '', producer: '', + revision: 0, ruleTypeId: '', ruleTypeName: '', enabled: true, - revision: 0, schedule: { interval: '1h', }, @@ -270,10 +270,10 @@ describe('ruleType', () => { tags: [], consumer: '', producer: '', + revision: 0, ruleTypeId: '', ruleTypeName: '', enabled: true, - revision: 0, schedule: { interval: '1h', }, @@ -337,10 +337,10 @@ describe('ruleType', () => { tags: [], consumer: '', producer: '', + revision: 0, ruleTypeId: '', ruleTypeName: '', enabled: true, - revision: 0, schedule: { interval: '1h', }, @@ -403,10 +403,10 @@ describe('ruleType', () => { tags: [], consumer: '', producer: '', + revision: 0, ruleTypeId: '', ruleTypeName: '', enabled: true, - revision: 0, schedule: { interval: '1h', }, diff --git a/x-pack/plugins/synthetics/common/constants/monitor_defaults.ts b/x-pack/plugins/synthetics/common/constants/monitor_defaults.ts index 6b9cc7913019a..adfa002e8e20a 100644 --- a/x-pack/plugins/synthetics/common/constants/monitor_defaults.ts +++ b/x-pack/plugins/synthetics/common/constants/monitor_defaults.ts @@ -48,9 +48,11 @@ export const CUSTOM_LABEL = i18n.translate('xpack.synthetics.connectionProfile.c defaultMessage: 'Custom', }); +export const DEFAULT_THROTTLING_VALUE = { download: '5', upload: '3', latency: '20' }; + export const PROFILE_VALUES: ThrottlingConfig[] = [ { - value: { download: '5', upload: '3', latency: '20' }, + value: DEFAULT_THROTTLING_VALUE, id: PROFILE_VALUES_ENUM.DEFAULT, label: i18n.translate('xpack.synthetics.connectionProfile.default', { defaultMessage: 'Default', diff --git a/x-pack/plugins/synthetics/common/formatters/browser/formatters.test.ts b/x-pack/plugins/synthetics/common/formatters/browser/formatters.test.ts new file mode 100644 index 0000000000000..7883e0aa09bca --- /dev/null +++ b/x-pack/plugins/synthetics/common/formatters/browser/formatters.test.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 { ConfigKey } from '../../runtime_types'; +import { throttlingFormatter } from './formatters'; + +describe('formatters', () => { + describe('throttling formatter', () => { + it('formats for no throttling', () => { + expect( + throttlingFormatter!( + { + [ConfigKey.THROTTLING_CONFIG]: { + value: { + download: '0', + upload: '0', + latency: '0', + }, + label: 'No throttling', + id: 'no-throttling', + }, + }, + ConfigKey.THROTTLING_CONFIG + ) + ).toEqual('false'); + }); + + it('formats for default throttling', () => { + expect( + throttlingFormatter!( + { + [ConfigKey.THROTTLING_CONFIG]: { + value: { + download: '5', + upload: '3', + latency: '20', + }, + label: 'Default', + id: 'default', + }, + }, + ConfigKey.THROTTLING_CONFIG + ) + ).toEqual(JSON.stringify({ download: 5, upload: 3, latency: 20 })); + }); + + it('formats for custom throttling', () => { + expect( + throttlingFormatter!( + { + [ConfigKey.THROTTLING_CONFIG]: { + value: { + download: '1.25', + upload: '0.75', + latency: '150', + }, + label: 'Custom', + id: 'custom', + }, + }, + ConfigKey.THROTTLING_CONFIG + ) + ).toEqual(JSON.stringify({ download: 1.25, upload: 0.75, latency: 150 })); + }); + }); +}); diff --git a/x-pack/plugins/synthetics/common/formatters/browser/formatters.ts b/x-pack/plugins/synthetics/common/formatters/browser/formatters.ts index 508464c48a5f2..9dfa027767851 100644 --- a/x-pack/plugins/synthetics/common/formatters/browser/formatters.ts +++ b/x-pack/plugins/synthetics/common/formatters/browser/formatters.ts @@ -12,6 +12,7 @@ import { objectToJsonFormatter, stringToJsonFormatter, } from '../formatting_utils'; +import { DEFAULT_THROTTLING_VALUE } from '../../constants/monitor_defaults'; import { tlsFormatters } from '../tls/formatters'; @@ -24,7 +25,11 @@ export const throttlingFormatter: Formatter = (fields) => { return 'false'; } - return `${throttling.value.download}d/${throttling.value.upload}u/${throttling.value.latency}l`; + return JSON.stringify({ + download: Number(throttling?.value?.download || DEFAULT_THROTTLING_VALUE.download), + upload: Number(throttling?.value?.upload || DEFAULT_THROTTLING_VALUE.upload), + latency: Number(throttling?.value?.latency || DEFAULT_THROTTLING_VALUE), + }); }; export const browserFormatters: BrowserFormatMap = { diff --git a/x-pack/plugins/synthetics/common/formatters/format_synthetics_policy.test.ts b/x-pack/plugins/synthetics/common/formatters/format_synthetics_policy.test.ts index 6be823ee0a208..2706972456acd 100644 --- a/x-pack/plugins/synthetics/common/formatters/format_synthetics_policy.test.ts +++ b/x-pack/plugins/synthetics/common/formatters/format_synthetics_policy.test.ts @@ -422,7 +422,7 @@ describe('formatSyntheticsPolicy', () => { }, 'throttling.config': { type: 'text', - value: '5d/3u/20l', + value: JSON.stringify({ download: 5, upload: 3, latency: 20 }), }, timeout: { type: 'text', diff --git a/x-pack/plugins/synthetics/e2e/journeys/synthetics/services/add_monitor.ts b/x-pack/plugins/synthetics/e2e/journeys/synthetics/services/add_monitor.ts index 44053aa29ed26..fb79e0ec94ff2 100644 --- a/x-pack/plugins/synthetics/e2e/journeys/synthetics/services/add_monitor.ts +++ b/x-pack/plugins/synthetics/e2e/journeys/synthetics/services/add_monitor.ts @@ -130,11 +130,15 @@ export const testDataMonitor = { 'filter_journeys.match': '', 'filter_journeys.tags': [], ignore_https_errors: false, - 'throttling.is_enabled': true, - 'throttling.download_speed': '5', - 'throttling.upload_speed': '3', - 'throttling.latency': '20', - 'throttling.config': '5d/3u/20l', + throttling: { + id: 'custom', + label: 'Custom', + value: { + download: '5', + upload: '3', + latency: '20', + }, + }, 'ssl.certificate_authorities': '', 'ssl.certificate': '', 'ssl.key': '', diff --git a/x-pack/plugins/synthetics/e2e/tasks/import_monitors.ts b/x-pack/plugins/synthetics/e2e/tasks/import_monitors.ts index f65d82a9933f4..f7143ee5b89e8 100644 --- a/x-pack/plugins/synthetics/e2e/tasks/import_monitors.ts +++ b/x-pack/plugins/synthetics/e2e/tasks/import_monitors.ts @@ -48,11 +48,15 @@ export const importMonitors = async ({ 'filter_journeys.match': '', 'filter_journeys.tags': [], ignore_https_errors: false, - 'throttling.is_enabled': true, - 'throttling.download_speed': '5', - 'throttling.upload_speed': '3', - 'throttling.latency': '20', - 'throttling.config': '5d/3u/20l', + throttling: { + id: 'custom', + label: 'Custom', + value: { + download: '5', + upload: '3', + latency: '20', + }, + }, }; const id = '1c215bd0-f580-11ec-89e5-694db461b7a5'; diff --git a/x-pack/plugins/synthetics/public/apps/synthetics/components/monitors_page/common/monitor_filters/filter_group.tsx b/x-pack/plugins/synthetics/public/apps/synthetics/components/monitors_page/common/monitor_filters/filter_group.tsx index 7e19abbf758ce..a59a26613a427 100644 --- a/x-pack/plugins/synthetics/public/apps/synthetics/components/monitors_page/common/monitor_filters/filter_group.tsx +++ b/x-pack/plugins/synthetics/public/apps/synthetics/components/monitors_page/common/monitor_filters/filter_group.tsx @@ -61,7 +61,18 @@ export const FilterGroup = ({ label: LOCATION_LABEL, field: 'locations', values: getSyntheticsFilterDisplayValues( - mixUrlValues(data.locations, urlParams.locations), + mixUrlValues( + data.locations.map((locationData) => { + const matchingLocation = locations.find( + (location) => location.id === locationData.label + ); + return { + label: matchingLocation ? matchingLocation.label : locationData.label, + count: locationData.count, + }; + }), + urlParams.locations + ), 'locations', locations ), diff --git a/x-pack/plugins/synthetics/public/apps/synthetics/components/monitors_page/common/monitor_filters/use_filters.ts b/x-pack/plugins/synthetics/public/apps/synthetics/components/monitors_page/common/monitor_filters/use_filters.ts index 014966f31bbc0..0e3fe38566f34 100644 --- a/x-pack/plugins/synthetics/public/apps/synthetics/components/monitors_page/common/monitor_filters/use_filters.ts +++ b/x-pack/plugins/synthetics/public/apps/synthetics/components/monitors_page/common/monitor_filters/use_filters.ts @@ -132,7 +132,7 @@ export const useFilters = (): FiltersList => { })) ?? [], schedules: schedules?.buckets?.map(({ key, doc_count: count }) => ({ - label: key, + label: String(key), count, })) ?? [], }; diff --git a/x-pack/plugins/synthetics/public/apps/synthetics/components/monitors_page/management/disabled_callout.tsx b/x-pack/plugins/synthetics/public/apps/synthetics/components/monitors_page/management/disabled_callout.tsx index 00c6e9b555727..0a75b9a44499b 100644 --- a/x-pack/plugins/synthetics/public/apps/synthetics/components/monitors_page/management/disabled_callout.tsx +++ b/x-pack/plugins/synthetics/public/apps/synthetics/components/monitors_page/management/disabled_callout.tsx @@ -43,7 +43,11 @@ export const DisabledCallout = ({ total }: { total: number }) => { ) : (

{labels.CALLOUT_MANAGEMENT_CONTACT_ADMIN}{' '} - + {labels.LEARN_MORE_LABEL}

diff --git a/x-pack/plugins/synthetics/public/apps/synthetics/components/monitors_page/management/invalid_api_key_callout.tsx b/x-pack/plugins/synthetics/public/apps/synthetics/components/monitors_page/management/invalid_api_key_callout.tsx index 8361df0588c04..70816a69c2188 100644 --- a/x-pack/plugins/synthetics/public/apps/synthetics/components/monitors_page/management/invalid_api_key_callout.tsx +++ b/x-pack/plugins/synthetics/public/apps/synthetics/components/monitors_page/management/invalid_api_key_callout.tsx @@ -34,7 +34,7 @@ export const InvalidApiKeyCalloutCallout = () => { {CALLOUT_MANAGEMENT_CONTACT_ADMIN}{' '} {LEARN_MORE_LABEL} diff --git a/x-pack/plugins/synthetics/public/apps/synthetics/components/monitors_page/management/synthetics_enablement/synthetics_enablement.tsx b/x-pack/plugins/synthetics/public/apps/synthetics/components/monitors_page/management/synthetics_enablement/synthetics_enablement.tsx index b1efd88590588..d6b927bbc43b3 100644 --- a/x-pack/plugins/synthetics/public/apps/synthetics/components/monitors_page/management/synthetics_enablement/synthetics_enablement.tsx +++ b/x-pack/plugins/synthetics/public/apps/synthetics/components/monitors_page/management/synthetics_enablement/synthetics_enablement.tsx @@ -91,7 +91,7 @@ export const EnablementEmptyState = () => { {labels.DOCS_LABEL} diff --git a/x-pack/plugins/synthetics/public/legacy_uptime/state/api/ml_anomaly.ts b/x-pack/plugins/synthetics/public/legacy_uptime/state/api/ml_anomaly.ts index 18ce74d9823bb..ae069537e1b26 100644 --- a/x-pack/plugins/synthetics/public/legacy_uptime/state/api/ml_anomaly.ts +++ b/x-pack/plugins/synthetics/public/legacy_uptime/state/api/ml_anomaly.ts @@ -11,7 +11,7 @@ import { JobExistResult, MlCapabilitiesResponse, } from '@kbn/ml-plugin/public'; -import { extractErrorMessage } from '@kbn/ml-plugin/common'; +import { extractErrorMessage } from '@kbn/ml-error-utils'; import { apiService } from './utils'; import { AnomalyRecords, AnomalyRecordsParams } from '../actions'; import { API_URLS, ML_MODULE_ID } from '../../../../common/constants'; diff --git a/x-pack/plugins/synthetics/server/alert_rules/status_rule/status_rule_executor.test.ts b/x-pack/plugins/synthetics/server/alert_rules/status_rule/status_rule_executor.test.ts index 367c27c4bca08..28d7018448374 100644 --- a/x-pack/plugins/synthetics/server/alert_rules/status_rule/status_rule_executor.test.ts +++ b/x-pack/plugins/synthetics/server/alert_rules/status_rule/status_rule_executor.test.ts @@ -169,11 +169,15 @@ const testMonitors = [ 'filter_journeys.match': '', 'filter_journeys.tags': [], ignore_https_errors: false, - 'throttling.is_enabled': true, - 'throttling.download_speed': '5', - 'throttling.upload_speed': '3', - 'throttling.latency': '20', - 'throttling.config': '5d/3u/20l', + throttling: { + id: 'custom', + label: 'Custom', + value: { + download: '5', + upload: '3', + latency: '20', + }, + }, 'ssl.certificate_authorities': '', 'ssl.certificate': '', 'ssl.verification_mode': 'full', diff --git a/x-pack/plugins/synthetics/server/synthetics_service/private_location/synthetics_private_location.test.ts b/x-pack/plugins/synthetics/server/synthetics_service/private_location/synthetics_private_location.test.ts index a334b6b406b3b..f1ebab30182ce 100644 --- a/x-pack/plugins/synthetics/server/synthetics_service/private_location/synthetics_private_location.test.ts +++ b/x-pack/plugins/synthetics/server/synthetics_service/private_location/synthetics_private_location.test.ts @@ -263,7 +263,7 @@ describe('SyntheticsPrivateLocation', () => { }, 'throttling.config': { type: 'text', - value: '5d/3u/20l', + value: JSON.stringify({ download: 5, upload: 3, latency: 20 }), }, timeout: { type: 'text', diff --git a/x-pack/plugins/synthetics/tsconfig.json b/x-pack/plugins/synthetics/tsconfig.json index a02e14d577b35..61b7fe8d8d19c 100644 --- a/x-pack/plugins/synthetics/tsconfig.json +++ b/x-pack/plugins/synthetics/tsconfig.json @@ -78,6 +78,7 @@ "@kbn/alerts-as-data-utils", "@kbn/exploratory-view-plugin", "@kbn/observability-shared-plugin", + "@kbn/ml-error-utils", ], "exclude": [ "target/**/*", diff --git a/x-pack/plugins/transform/public/app/hooks/use_delete_transform.tsx b/x-pack/plugins/transform/public/app/hooks/use_delete_transform.tsx index f90faf53e87b5..25318fc9e2903 100644 --- a/x-pack/plugins/transform/public/app/hooks/use_delete_transform.tsx +++ b/x-pack/plugins/transform/public/app/hooks/use_delete_transform.tsx @@ -8,6 +8,7 @@ import React, { useCallback, useEffect, useState } from 'react'; import { i18n } from '@kbn/i18n'; import { toMountPoint } from '@kbn/kibana-react-plugin/public'; +import { extractErrorMessage } from '@kbn/ml-error-utils'; import type { DeleteTransformStatus, DeleteTransformsRequestSchema, @@ -24,7 +25,6 @@ export const useDeleteIndexAndTargetIndex = (items: TransformListRow[]) => { const { http, data: { dataViews: dataViewsContract }, - ml: { extractErrorMessage }, application: { capabilities }, } = useAppDependencies(); const toastNotifications = useToastNotifications(); @@ -62,7 +62,7 @@ export const useDeleteIndexAndTargetIndex = (items: TransformListRow[]) => { ); } }, - [dataViewsContract, toastNotifications, extractErrorMessage] + [dataViewsContract, toastNotifications] ); const checkUserIndexPermission = useCallback(async () => { diff --git a/x-pack/plugins/transform/public/app/sections/create_transform/components/source_search_bar/source_search_bar.tsx b/x-pack/plugins/transform/public/app/sections/create_transform/components/source_search_bar/source_search_bar.tsx index c23d6ed475efc..e0a52978c0b4a 100644 --- a/x-pack/plugins/transform/public/app/sections/create_transform/components/source_search_bar/source_search_bar.tsx +++ b/x-pack/plugins/transform/public/app/sections/create_transform/components/source_search_bar/source_search_bar.tsx @@ -25,8 +25,8 @@ interface SourceSearchBarProps { } export const SourceSearchBar: FC = ({ dataView, searchBar }) => { const { - actions: { searchChangeHandler, searchSubmitHandler, setErrorMessage }, - state: { errorMessage, searchInput }, + actions: { searchChangeHandler, searchSubmitHandler, setQueryErrorMessage }, + state: { queryErrorMessage, searchInput }, } = searchBar; const { @@ -44,7 +44,7 @@ export const SourceSearchBar: FC = ({ dataView, searchBar return ( setErrorMessage(undefined)} + closePopover={() => setQueryErrorMessage(undefined)} input={ = ({ dataView, searchBar }} /> } - isOpen={errorMessage?.query === searchInput.query && errorMessage?.message !== ''} + isOpen={queryErrorMessage?.query === searchInput.query && queryErrorMessage?.message !== ''} > {i18n.translate('xpack.transform.stepDefineForm.invalidKuerySyntaxErrorMessageQueryBar', { - defaultMessage: 'Invalid query: {errorMessage}', + defaultMessage: 'Invalid query: {queryErrorMessage}', values: { - errorMessage: errorMessage?.message.split('\n')[0], + queryErrorMessage: queryErrorMessage?.message.split('\n')[0], }, })} diff --git a/x-pack/plugins/transform/public/app/sections/create_transform/components/step_define/common/index.ts b/x-pack/plugins/transform/public/app/sections/create_transform/components/step_define/common/index.ts index 775401decef35..157ab0051e631 100644 --- a/x-pack/plugins/transform/public/app/sections/create_transform/components/step_define/common/index.ts +++ b/x-pack/plugins/transform/public/app/sections/create_transform/components/step_define/common/index.ts @@ -13,4 +13,4 @@ export { getDefaultAggregationConfig } from './get_default_aggregation_config'; export { getDefaultGroupByConfig } from './get_default_group_by_config'; export { getDefaultStepDefineState } from './get_default_step_define_state'; export { getPivotDropdownOptions } from './get_pivot_dropdown_options'; -export type { ErrorMessage, Field, StepDefineExposedState } from './types'; +export type { Field, StepDefineExposedState } from './types'; diff --git a/x-pack/plugins/transform/public/app/sections/create_transform/components/step_define/common/types.ts b/x-pack/plugins/transform/public/app/sections/create_transform/components/step_define/common/types.ts index 682001a937381..2e66b2187a9e0 100644 --- a/x-pack/plugins/transform/public/app/sections/create_transform/components/step_define/common/types.ts +++ b/x-pack/plugins/transform/public/app/sections/create_transform/components/step_define/common/types.ts @@ -28,11 +28,6 @@ import { import { LatestFunctionConfig } from '../../../../../../../common/api_schemas/transforms'; import { RUNTIME_FIELD_TYPES } from '../../../../../../../common/shared_imports'; -export interface ErrorMessage { - query: string; - message: string; -} - export interface Field { name: EsFieldName; type: KBN_FIELD_TYPES | TIME_SERIES_METRIC_TYPES.COUNTER; diff --git a/x-pack/plugins/transform/public/app/sections/create_transform/components/step_define/hooks/use_search_bar.ts b/x-pack/plugins/transform/public/app/sections/create_transform/components/step_define/hooks/use_search_bar.ts index e8d56fc002981..62f8661f0ca49 100644 --- a/x-pack/plugins/transform/public/app/sections/create_transform/components/step_define/hooks/use_search_bar.ts +++ b/x-pack/plugins/transform/public/app/sections/create_transform/components/step_define/hooks/use_search_bar.ts @@ -9,11 +9,11 @@ import { useState } from 'react'; import { toElasticsearchQuery, fromKueryExpression, luceneStringToDsl } from '@kbn/es-query'; import type { Query } from '@kbn/es-query'; +import type { QueryErrorMessage } from '@kbn/ml-error-utils'; import { getTransformConfigQuery } from '../../../../../common'; import { - ErrorMessage, StepDefineExposedState, QUERY_LANGUAGE_KUERY, QUERY_LANGUAGE_LUCENE, @@ -43,7 +43,9 @@ export const useSearchBar = ( const [searchQuery, setSearchQuery] = useState(defaults.searchQuery); - const [errorMessage, setErrorMessage] = useState(undefined); + const [queryErrorMessage, setQueryErrorMessage] = useState( + undefined + ); const searchChangeHandler = (query: Query) => setSearchInput(query); const searchSubmitHandler = (query: Query) => { @@ -61,7 +63,7 @@ export const useSearchBar = ( return; } } catch (e) { - setErrorMessage({ query: query.query as string, message: e.message }); + setQueryErrorMessage({ query: query.query as string, message: e.message }); } }; @@ -71,14 +73,14 @@ export const useSearchBar = ( actions: { searchChangeHandler, searchSubmitHandler, - setErrorMessage, + setQueryErrorMessage, setSearchInput, setSearchLanguage, setSearchQuery, setSearchString, }, state: { - errorMessage, + queryErrorMessage, transformConfigQuery, searchInput, searchLanguage, diff --git a/x-pack/plugins/transform/server/routes/api/error_utils.ts b/x-pack/plugins/transform/server/routes/api/error_utils.ts index 6f02b1978506f..9054ee7aa3b23 100644 --- a/x-pack/plugins/transform/server/routes/api/error_utils.ts +++ b/x-pack/plugins/transform/server/routes/api/error_utils.ts @@ -142,7 +142,7 @@ export function wrapEsError(err: any, statusCodeToMessageMap: Recordエンジン名を指定", "xpack.enterpriseSearch.content.engines.createEngine.selectIndices.title": "インデックスを選択", "xpack.enterpriseSearch.content.engines.createEngine.submit": "このエンジンを削除", - "xpack.enterpriseSearch.content.engines.createEngineButtonLabel": "エンジンを作成", - "xpack.enterpriseSearch.content.engines.documentation": "エンジンドキュメントを読む", "xpack.enterpriseSearch.content.engines.enginesList.empty.description": "最初のエンジンの作成を説明します。", "xpack.enterpriseSearch.content.engines.enginesList.empty.title": "初めてのエンジンの作成", "xpack.enterpriseSearch.content.engines.indices.addIndicesFlyout.updateError.title": "エンジンの更新エラー", - "xpack.enterpriseSearch.content.engines.searchBar.ariaLabel": "検索エンジン", - "xpack.enterpriseSearch.content.engines.searchPlaceholder": "検索エンジン", - "xpack.enterpriseSearch.content.engines.searchPlaceholder.description": "名前または含まれているインデックスでエンジンを検索します。", - "xpack.enterpriseSearch.content.engines.title": "エンジン", "xpack.enterpriseSearch.content.enginesList.indicesFlyout.table.docsCount.columnTitle": "ドキュメント数", "xpack.enterpriseSearch.content.enginesList.indicesFlyout.table.health.columnTitle": "インデックス正常性", "xpack.enterpriseSearch.content.enginesList.indicesFlyout.table.name.columnTitle": "インデックス名", @@ -12338,7 +12307,6 @@ "xpack.enterpriseSearch.content.index.pipelines.settings.reduceWhitespaceDescription": "ドキュメントの不要な空白を自動的に削除", "xpack.enterpriseSearch.content.index.pipelines.settings.reduceWhitespaceLabel": "空白の削除", "xpack.enterpriseSearch.content.index.pipelines.settings.runMlInferenceDescrition": "互換性がある学習済みMLモデルを使用してデータを強化", - "xpack.enterpriseSearch.content.index.searchEngines.createEngine": "App Searchエンジンの作成", "xpack.enterpriseSearch.content.index.searchEngines.createEngineDisabledTooltip": "非表示のインデックスからエンジンを作成することはできません。", "xpack.enterpriseSearch.content.index.searchEngines.label": "検索エンジン", "xpack.enterpriseSearch.content.index.searchEngines.viewEngines": "App Searchエンジンを表示", @@ -12617,8 +12585,6 @@ "xpack.enterpriseSearch.content.nativeConnectors.mongodb.name": "MongoDB", "xpack.enterpriseSearch.content.nativeConnectors.mysql.name": "MySQL", "xpack.enterpriseSearch.content.navTitle": "コンテンツ", - "xpack.enterpriseSearch.content.new_index.successToast.button.label": "エンジンを作成", - "xpack.enterpriseSearch.content.new_index.successToast.description": "App Searchエンジンを使用して、新しいElasticsearchインデックスの検索エクスペリエンスを構築できます。", "xpack.enterpriseSearch.content.new_index.successToast.title": "インデックスが正常に作成されました", "xpack.enterpriseSearch.content.newIndex.breadcrumb": "新しい検索インデックス", "xpack.enterpriseSearch.content.newIndex.emptyState.description": "エンタープライズ サーチで追加したデータは検索インデックスと呼ばれ、App SearchとWorkplace Searchの両方で検索可能です。App SearchのコネクターとWorkplace SearchのWebクローラーを使用できます。", @@ -13039,8 +13005,6 @@ "xpack.enterpriseSearch.emailLabel": "メール", "xpack.enterpriseSearch.emptyState.description": "コンテンツはElasticsearchインデックスに保存されます。まず、Elasticsearchインデックスを作成し、インジェスチョン方法を選択します。オプションには、Elastic Webクローラー、サードパーティデータ統合、Elasticsearch APIエンドポイントの使用があります。", "xpack.enterpriseSearch.emptyState.description.line2": "App SearchまたはElasticsearchのどちらで検索エクスペリエンスを構築しても、これが最初のステップです。", - "xpack.enterpriseSearch.engines.engine.notFound.action1": "エンジンに戻る", - "xpack.enterpriseSearch.engines.navTitle": "エンジン", "xpack.enterpriseSearch.enterpriseSearch.setupGuide.description": "場所を問わず、何でも検索。組織を支える多忙なチームのために、パワフルでモダンな検索エクスペリエンスを簡単に導入できます。Webサイトやアプリ、ワークプレイスに事前調整済みの検索をすばやく追加しましょう。何でもシンプルに検索できます。", "xpack.enterpriseSearch.enterpriseSearch.setupGuide.notConfigured": "エンタープライズサーチはまだKibanaインスタンスで構成されていません。", "xpack.enterpriseSearch.enterpriseSearch.setupGuide.videoAlt": "エンタープライズ サーチの基本操作", @@ -20349,15 +20313,11 @@ "xpack.maps.addLayerPanel.addLayer": "レイヤーを追加", "xpack.maps.addLayerPanel.changeDataSourceButtonLabel": "レイヤーを変更", "xpack.maps.addLayerPanel.footer.cancelButtonLabel": "キャンセル", - "xpack.maps.aggs.defaultCountLabel": "カウント", "xpack.maps.attribution.addBtnAriaLabel": "属性を追加", "xpack.maps.attribution.addBtnLabel": "属性を追加", - "xpack.maps.attribution.applyBtnLabel": "適用", "xpack.maps.attribution.attributionFormLabel": "属性", "xpack.maps.attribution.clearBtnAriaLabel": "属性を消去", - "xpack.maps.attribution.clearBtnLabel": "クリア", "xpack.maps.attribution.editBtnAriaLabel": "属性を編集", - "xpack.maps.attribution.editBtnLabel": "編集", "xpack.maps.attribution.labelFieldLabel": "ラベル", "xpack.maps.attribution.urlLabel": "リンク", "xpack.maps.badge.readOnly.text": "読み取り専用", @@ -20594,7 +20554,6 @@ "xpack.maps.mapActions.removeFeatureError": "インデックスから機能を削除できません。", "xpack.maps.mapListing.entityName": "マップ", "xpack.maps.mapListing.entityNamePlural": "マップ", - "xpack.maps.mapListing.errorAttemptingToLoadSavedMaps": "マップを読み込めません", "xpack.maps.mapSavedObjectLabel": "マップ", "xpack.maps.mapSettingsPanel.addCustomIcon": "カスタムアイコンを追加", "xpack.maps.mapSettingsPanel.autoFitToBoundsLocationLabel": "自動的にマップをデータ境界に合わせる", @@ -20630,15 +20589,7 @@ "xpack.maps.metricsEditor.selectFieldLabel": "フィールド", "xpack.maps.metricsEditor.selectFieldPlaceholder": "フィールドを選択", "xpack.maps.metricsEditor.selectPercentileLabel": "パーセンタイル", - "xpack.maps.metricSelect.averageDropDownOptionLabel": "平均", - "xpack.maps.metricSelect.cardinalityDropDownOptionLabel": "ユニークカウント", - "xpack.maps.metricSelect.countDropDownOptionLabel": "カウント", - "xpack.maps.metricSelect.maxDropDownOptionLabel": "最高", - "xpack.maps.metricSelect.minDropDownOptionLabel": "最低", - "xpack.maps.metricSelect.percentileDropDownOptionLabel": "パーセンタイル", "xpack.maps.metricSelect.selectAggregationPlaceholder": "集約を選択", - "xpack.maps.metricSelect.sumDropDownOptionLabel": "合計", - "xpack.maps.metricSelect.termsDropDownOptionLabel": "トップ用語", "xpack.maps.mvtSource.addFieldLabel": "追加", "xpack.maps.mvtSource.fieldPlaceholderText": "フィールド名", "xpack.maps.mvtSource.numberFieldLabel": "数字", @@ -23426,7 +23377,7 @@ "xpack.ml.ruleEditor.scopeSection.noPermissionToViewFilterListsTitle": "フィルターリストを表示するパーミッションがありません", "xpack.ml.ruleEditor.scopeSection.scopeTitle": "範囲", "xpack.ml.ruleEditor.selectRuleAction.createRuleLinkText": "ルールを作成", - "xpack.ml.ruleEditor.selectRuleAction.orText": "OR ", + "xpack.ml.ruleEditor.selectRuleAction.orText": "OR ", "xpack.ml.ruleEditor.typicalAppliesTypeText": "通常", "xpack.ml.sampleDataLinkLabel": "ML ジョブ", "xpack.ml.selectDataViewLabel": "データビューを選択", @@ -27838,7 +27789,6 @@ "xpack.securitySolution.detectionEngine.rules.allRules.bulkActions.disable.successToastDescription": "{totalRules, plural, =1 {{totalRules}個のルール} other {{totalRules}個のルール}}が正常に無効にされました", "xpack.securitySolution.detectionEngine.rules.allRules.bulkActions.duplicate.errorToastDescription": "{rulesCount, plural, =1 {#個のルール} other {#個のルール}}を複製できませんでした。", "xpack.securitySolution.detectionEngine.rules.allRules.bulkActions.duplicate.exceptionsConfirmation.modalBody": "{rulesCount, plural, other {#個の選択したルール}}を複製しています。既存の例外を複製する方法を選択してください", - "xpack.securitySolution.detectionEngine.rules.allRules.bulkActions.duplicate.exceptionsConfirmation.modalTitle": "{rulesCount, plural, other {ルール}}と例外を複製しますか?", "xpack.securitySolution.detectionEngine.rules.allRules.bulkActions.duplicate.exceptionsConfirmation.with": "{rulesCount, plural, other {ルール}}と例外を複製", "xpack.securitySolution.detectionEngine.rules.allRules.bulkActions.duplicate.exceptionsConfirmation.without": "{rulesCount, plural, other {ルール}}のみを複製", "xpack.securitySolution.detectionEngine.rules.allRules.bulkActions.duplicate.successToastDescription": "{totalRules, plural, =1 {{totalRules}個のルール} other {{totalRules}個のルール}}が正常に複製されました", @@ -29028,9 +28978,6 @@ "xpack.securitySolution.detectionEngine.createRule.stepDefineRule.threatMatchingIcesHelperDescription": "脅威インデックスを選択", "xpack.securitySolution.detectionEngine.createRule.stepDefineRule.threatMatchoutputIndiceNameFieldRequiredError": "インデックスパターンが最低1つ必要です。", "xpack.securitySolution.detectionEngine.createRule.stepDefineRule.thresholdField.thresholdFieldPlaceholderText": "すべての結果", - "xpack.securitySolution.detectionEngine.createRule.stepRuleActions.fieldThrottleHelpText": "ルールが true であると評価された場合に自動アクションを実行するタイミングを選択します。", - "xpack.securitySolution.detectionEngine.createRule.stepRuleActions.fieldThrottleHelpTextWhenQuery": "ルールが true であると評価された場合に自動アクションを実行するタイミングを選択します。この頻度は対応アクションには適用されません。", - "xpack.securitySolution.detectionEngine.createRule.stepRuleActions.fieldThrottleLabel": "アクション頻度", "xpack.securitySolution.detectionEngine.createRule.stepRuleActions.noReadActionsPrivileges": "ルールアクションを作成できません。「Actions」プラグインの「読み取り」アクセス権がありません。", "xpack.securitySolution.detectionEngine.createRule.stepScheduleRule.completeWithEnablingTitle": "ルールを作成して有効にする", "xpack.securitySolution.detectionEngine.createRule.stepScheduleRule.completeWithoutEnablingTitle": "有効にせずにルールを作成", @@ -29988,12 +29935,9 @@ "xpack.securitySolution.detectionEngine.rules.allRules.bulkActions.duplicate.exceptionsConfirmation.tooltip": " 例外を複製する場合は、参照によって共有例外リストが複製されます。それから、デフォルトルール例外がコピーされ、新しい例外が作成されます", "xpack.securitySolution.detectionEngine.rules.allRules.bulkActions.duplicate.successToastTitle": "ルールが複製されました", "xpack.securitySolution.detectionEngine.rules.allRules.bulkActions.duplicateTitle": "複製", - "xpack.securitySolution.detectionEngine.rules.allRules.bulkActions.edit.addRuleActions.actionFrequencyDetail": "以下で選択したアクション頻度は、すべての選択したルールのすべてのアクション(新規と既存のアクション)に適用されます。", "xpack.securitySolution.detectionEngine.rules.allRules.bulkActions.edit.addRuleActions.formTitle": "ルールアクションを追加", "xpack.securitySolution.detectionEngine.rules.allRules.bulkActions.edit.addRuleActions.overwriteCheckboxLabel": "すべての選択したルールアクションを上書き", "xpack.securitySolution.detectionEngine.rules.allRules.bulkActions.edit.addRuleActions.ruleVariablesDetail": "ルールタイプによっては、ルール変数が選択舌一部のルールにのみ影響する場合があります(例:\\u007b\\u007bcontext.rule.threshold\\u007d\\u007dはしきい値ルールの値のみを表示します)。", - "xpack.securitySolution.detectionEngine.rules.allRules.bulkActions.edit.addRuleActions.throttleHelpText": "ルールが true であると評価された場合に自動アクションを実行するタイミングを選択します。", - "xpack.securitySolution.detectionEngine.rules.allRules.bulkActions.edit.addRuleActions.throttleLabel": "アクション頻度", "xpack.securitySolution.detectionEngine.rules.allRules.bulkActions.edit.addRuleActions.warningCalloutMessage.buttonLabel": "保存", "xpack.securitySolution.detectionEngine.rules.allRules.bulkActions.edit.applyTimelineTemplate.formTitle": "タイムラインテンプレートを適用", "xpack.securitySolution.detectionEngine.rules.allRules.bulkActions.edit.applyTimelineTemplate.templateSelectorDefaultValue": "なし", @@ -30904,10 +30848,6 @@ "xpack.securitySolution.exceptions.exceptionListsCloseImportFlyout": "閉じる", "xpack.securitySolution.exceptions.exceptionListsFilePickerPrompt": "複数のファイルを選択するかドラッグしてください", "xpack.securitySolution.exceptions.exceptionListsImportButton": "リストをインポート", - "xpack.securitySolution.exceptions.exportModalCancelButton": "キャンセル", - "xpack.securitySolution.exceptions.exportModalConfirmButton": "エクスポート", - "xpack.securitySolution.exceptions.exportModalIncludeSwitchLabel": "有効期限切れの例外を含める", - "xpack.securitySolution.exceptions.exportModalTitle": "例外リストのエクスポート", "xpack.securitySolution.exceptions.fetchError": "例外リストの取得エラー", "xpack.securitySolution.exceptions.fetchingReferencesErrorToastTitle": "例外参照の取得エラー", "xpack.securitySolution.exceptions.list.exception.item.card.delete.label": "ルール例外の削除", @@ -35584,7 +35524,7 @@ "xpack.transform.stepCreateForm.duplicateDataViewErrorMessage": "Kibanaデータビュー{dataViewName}の作成中にエラーが発生しました:データビューはすでに存在します。", "xpack.transform.stepCreateForm.startTransformErrorMessage": "変換 {transformId} の開始中にエラーが発生しました。", "xpack.transform.stepCreateForm.startTransformSuccessMessage": "変換 {transformId} の開始リクエストが受け付けられました。", - "xpack.transform.stepDefineForm.invalidKuerySyntaxErrorMessageQueryBar": "無効なクエリ:{errorMessage}", + "xpack.transform.stepDefineForm.invalidKuerySyntaxErrorMessageQueryBar": "無効なクエリ:{queryErrorMessage}", "xpack.transform.stepDefineForm.queryPlaceholderKql": "例: {example}.", "xpack.transform.stepDefineForm.queryPlaceholderLucene": "例: {example}.", "xpack.transform.stepDefineForm.runtimeFieldsListLabel": "{runtimeFields}", diff --git a/x-pack/plugins/translations/translations/zh-CN.json b/x-pack/plugins/translations/translations/zh-CN.json index 9ad3258100c35..40989bc638cc5 100644 --- a/x-pack/plugins/translations/translations/zh-CN.json +++ b/x-pack/plugins/translations/translations/zh-CN.json @@ -1090,7 +1090,6 @@ "dashboard.addPanel.newEmbeddableAddedSuccessMessageTitle": "{savedObjectName} 已添加", "dashboard.addPanel.savedObjectAddedToContainerSuccessMessageTitle": "{savedObjectName} 已添加", "dashboard.listing.createNewDashboard.newToKibanaDescription": "Kibana 新手?{sampleDataInstallLink}来试用一下。", - "dashboard.listing.unsaved.discardAria": "丢弃对 {title} 的更改", "dashboard.listing.unsaved.editAria": "继续编辑 {title}", "dashboard.listing.unsaved.unsavedChangesTitle": "在以下 {dash} 中有未保存更改:", "dashboard.loadingError.dashboardGridErrorMessage": "无法加载仪表板:{message}", @@ -1127,10 +1126,7 @@ "dashboard.dashboardPageTitle": "仪表板", "dashboard.dashboardWasSavedSuccessMessage": "仪表板“{dashTitle}”已保存", "dashboard.deleteError.toastDescription": "删除仪表板时发生错误", - "dashboard.discardChangesConfirmModal.cancelButtonLabel": "取消", - "dashboard.discardChangesConfirmModal.confirmButtonLabel": "放弃更改", "dashboard.discardChangesConfirmModal.discardChangesDescription": "放弃更改后,它们将无法恢复。", - "dashboard.discardChangesConfirmModal.discardChangesTitle": "放弃对仪表板所做的更改?", "dashboard.editingToolbar.addControlButtonTitle": "添加控件", "dashboard.editingToolbar.addTimeSliderControlButtonTitle": "添加时间滑块控件", "dashboard.editingToolbar.controlsButtonTitle": "控件", @@ -1164,7 +1160,6 @@ "dashboard.listing.readonlyNoItemsTitle": "没有可查看的仪表板", "dashboard.listing.table.entityName": "仪表板", "dashboard.listing.table.entityNamePlural": "仪表板", - "dashboard.listing.unsaved.discardTitle": "放弃更改", "dashboard.listing.unsaved.editTitle": "继续编辑", "dashboard.listing.unsaved.loading": "正在加载", "dashboard.loadingError.dashboardNotFound": "找不到请求的仪表板。", @@ -5818,7 +5813,6 @@ "unifiedSearch.search.searchBar.savedQueryPopoverApplyFilterSetLabel": "应用已保存查询", "unifiedSearch.search.searchBar.savedQueryPopoverConfirmDeletionCancelButtonText": "取消", "unifiedSearch.search.searchBar.savedQueryPopoverConfirmDeletionConfirmButtonText": "删除", - "unifiedSearch.search.searchBar.savedQueryPopoverReplaceFilterSetLabel": "替换为选定已保存查询", "unifiedSearch.search.searchBar.savedQueryPopoverSaveAsNewButtonAriaLabel": "另存为新的已保存查询", "unifiedSearch.search.searchBar.savedQueryPopoverSaveAsNewButtonText": "另存为新的", "unifiedSearch.search.searchBar.savedQueryPopoverSaveChangesButtonText": "保存更改", @@ -7546,7 +7540,6 @@ "xpack.apm.dataView.autoCreateDisabled": "已通过“autoCreateApmDataView”配置选项禁止自动创建数据视图", "xpack.apm.dataView.noApmData": "无 APM 数据", "xpack.apm.dependecyOperationDetailView.header.backLinkLabel": "所有操作", - "xpack.apm.dependencies.kueryBarPlaceholder": "搜索依赖项指标(例如,span.destination.service.resource:elasticsearch)", "xpack.apm.dependenciesInventory.dependencyTableColumn": "依赖项", "xpack.apm.dependenciesTable.columnErrorRate": "失败事务率", "xpack.apm.dependenciesTable.columnErrorRateTip": "选定服务的失败事务百分比。状态代码为 4xx 的 HTTP 服务器事务(客户端错误)不会视为失败,因为是调用方而不是服务器造成了失败。", @@ -10626,8 +10619,6 @@ "xpack.csp.cloudPosturePage.packageNotInstalledRenderer.addKspmIntegrationButtonTitle": "添加 KSPM 集成", "xpack.csp.cloudPosturePage.packageNotInstalledRenderer.learnMoreTitle": "了解有关云安全态势的详情", "xpack.csp.cloudPosturePage.packageNotInstalledRenderer.promptTitle": "在云资源中检测安全配置错误!", - "xpack.csp.cloudPostureScoreChart.counterLink.failedFindingsTooltip": "失败的结果", - "xpack.csp.cloudPostureScoreChart.counterLink.passedFindingsTooltip": "通过的结果", "xpack.csp.createPackagePolicy.customAssetsTab.dashboardViewLabel": "查看 CSP 仪表板", "xpack.csp.createPackagePolicy.customAssetsTab.findingsViewLabel": "查看 CSP 结果 ", "xpack.csp.createPackagePolicy.customAssetsTab.rulesViewLabel": "查看 CSP 规则 ", @@ -11132,8 +11123,6 @@ "xpack.enterpriseSearch.content.engine.indices.actions.viewIndex.caption": "查看索引 {indexName}", "xpack.enterpriseSearch.content.engineList.deleteEngine.successToast.title": "{engineName} 已删除", "xpack.enterpriseSearch.content.engines.createEngine.headerSubTitle": "引擎允许您的用户在索引中查询数据。请浏览我们的 {enginesDocsLink} 了解详情!", - "xpack.enterpriseSearch.content.engines.description": "引擎允许您通过一整套相关性、分析和个性化工具查询索引数据。有关引擎在 Enterprise Search 中的工作机制的详情,{documentationUrl}", - "xpack.enterpriseSearch.content.engines.enginesList.description": "正在显示第 {from}-{to} 个(共 {total} 个)", "xpack.enterpriseSearch.content.enginesList.indicesFlyout.subTitle": "查看与 {engineName} 关联的索引", "xpack.enterpriseSearch.content.enginesList.table.column.view.indices": "{indicesCount, number} 个{indicesCount, plural, other {索引}}", "xpack.enterpriseSearch.content.index.connector.syncRules.description": "添加同步规则以定制要从 {indexName} 同步哪些数据。默认包括所有内容,并根据从上到下列出的已配置索引规则集验证文档。", @@ -12226,22 +12215,9 @@ "xpack.enterpriseSearch.content.engine.api.generateEngineApiKeyModal.done": "完成", "xpack.enterpriseSearch.content.engine.api.generateEngineApiKeyModal.generateButton": "生成只读密钥", "xpack.enterpriseSearch.content.engine.api.generateEngineApiKeyModal.title": "创建引擎只读 API 密钥", - "xpack.enterpriseSearch.content.engine.api.step1.apiKeyWarning": "Elastic 不会存储 API 密钥。一旦生成,您只能查看密钥一次。请确保将其保存在某个安全位置。如果失去它的访问权限,您需要从此屏幕生成新的 API 密钥。", - "xpack.enterpriseSearch.content.engine.api.step1.createAPIKeyButton": "创建 API 密钥", - "xpack.enterpriseSearch.content.engine.api.step1.learnMoreLink": "详细了解 API 密钥。", - "xpack.enterpriseSearch.content.engine.api.step1.title": "生成并保存 API 密钥", - "xpack.enterpriseSearch.content.engine.api.step1.viewKeysButton": "查看密钥", - "xpack.enterpriseSearch.content.engine.api.step2.copyEndpointDescription": "使用此 URL 访问您引擎的 API 终端。", - "xpack.enterpriseSearch.content.engine.api.step2.title": "复制您引擎的终端", "xpack.enterpriseSearch.content.engine.api.step3.curlTitle": "cURL", "xpack.enterpriseSearch.content.engine.api.step3.intro": "了解如何将您的引擎与由 Elastic 维护的语言客户端进行集成,以帮助构建搜索体验。", "xpack.enterpriseSearch.content.engine.api.step3.searchUITitle": "搜索 UI", - "xpack.enterpriseSearch.content.engine.api.step3.title": "了解如何调用终端", - "xpack.enterpriseSearch.content.engine.api.step4.copy": "您的引擎作为此安装的一部分提供了基本分析数据。要接收更多粒度化的定制指标,请在您的平台上集成我们的行为分析脚本。", - "xpack.enterpriseSearch.content.engine.api.step4.learnHowLink": "了解操作方法", - "xpack.enterpriseSearch.content.engine.api.step4.title": "(可选)强化分析", - "xpack.enterpriseSearch.content.engine.headerActions.actionsButton.ariaLabel": "引擎操作菜单按钮", - "xpack.enterpriseSearch.content.engine.headerActions.delete": "删除此引擎", "xpack.enterpriseSearch.content.engine.indices.actions.columnTitle": "操作", "xpack.enterpriseSearch.content.engine.indices.actions.removeIndex.title": "从引擎中移除此索引", "xpack.enterpriseSearch.content.engine.indices.actions.viewIndex.title": "查看此索引", @@ -12266,7 +12242,6 @@ "xpack.enterpriseSearch.content.engineList.deleteEngineModal.delete.description": "删除引擎是不可逆操作。您的索引不会受到影响。", "xpack.enterpriseSearch.content.engineList.deleteEngineModal.title": "永久删除此引擎?", "xpack.enterpriseSearch.content.engineList.table.column.actions.deleteEngineLabel": "删除此引擎", - "xpack.enterpriseSearch.content.engines.breadcrumb": "引擎", "xpack.enterpriseSearch.content.engines.createEngine.header.createError.title": "创建引擎时出错", "xpack.enterpriseSearch.content.engines.createEngine.header.docsLink": "引擎文档", "xpack.enterpriseSearch.content.engines.createEngine.headerTitle": "创建引擎", @@ -12274,15 +12249,9 @@ "xpack.enterpriseSearch.content.engines.createEngine.nameEngine.title": "命名您的引擎", "xpack.enterpriseSearch.content.engines.createEngine.selectIndices.title": "选择索引", "xpack.enterpriseSearch.content.engines.createEngine.submit": "创建此引擎", - "xpack.enterpriseSearch.content.engines.createEngineButtonLabel": "创建引擎", - "xpack.enterpriseSearch.content.engines.documentation": "浏览我们的引擎文档", "xpack.enterpriseSearch.content.engines.enginesList.empty.description": "下面我们指导您创建首个引擎。", "xpack.enterpriseSearch.content.engines.enginesList.empty.title": "创建您的首个引擎", "xpack.enterpriseSearch.content.engines.indices.addIndicesFlyout.updateError.title": "更新引擎时出错", - "xpack.enterpriseSearch.content.engines.searchBar.ariaLabel": "搜索引擎", - "xpack.enterpriseSearch.content.engines.searchPlaceholder": "搜索引擎", - "xpack.enterpriseSearch.content.engines.searchPlaceholder.description": "通过名称或根据其包含的索引查找引擎。", - "xpack.enterpriseSearch.content.engines.title": "引擎", "xpack.enterpriseSearch.content.enginesList.indicesFlyout.table.docsCount.columnTitle": "文档计数", "xpack.enterpriseSearch.content.enginesList.indicesFlyout.table.health.columnTitle": "索引运行状况", "xpack.enterpriseSearch.content.enginesList.indicesFlyout.table.name.columnTitle": "索引名称", @@ -12339,7 +12308,6 @@ "xpack.enterpriseSearch.content.index.pipelines.settings.reduceWhitespaceDescription": "自动剪裁文档中的额外空白", "xpack.enterpriseSearch.content.index.pipelines.settings.reduceWhitespaceLabel": "减少空白", "xpack.enterpriseSearch.content.index.pipelines.settings.runMlInferenceDescrition": "使用兼容的已训练 ML 模型增强您的数据", - "xpack.enterpriseSearch.content.index.searchEngines.createEngine": "创建 App Search 引擎", "xpack.enterpriseSearch.content.index.searchEngines.createEngineDisabledTooltip": "无法从隐藏的索引中创建引擎。", "xpack.enterpriseSearch.content.index.searchEngines.label": "搜索引擎", "xpack.enterpriseSearch.content.index.searchEngines.viewEngines": "查看 App Search 引擎", @@ -12618,8 +12586,6 @@ "xpack.enterpriseSearch.content.nativeConnectors.mongodb.name": "MongoDB", "xpack.enterpriseSearch.content.nativeConnectors.mysql.name": "MySQL", "xpack.enterpriseSearch.content.navTitle": "内容", - "xpack.enterpriseSearch.content.new_index.successToast.button.label": "创建引擎", - "xpack.enterpriseSearch.content.new_index.successToast.description": "您可以使用 App Search 引擎为您的新 Elasticsearch 索引构建搜索体验。", "xpack.enterpriseSearch.content.new_index.successToast.title": "已成功创建索引", "xpack.enterpriseSearch.content.newIndex.breadcrumb": "新搜索索引", "xpack.enterpriseSearch.content.newIndex.emptyState.description": "您在 Enterprise Search 中添加的数据称为搜索索引,您可在 App Search 和 Workplace Search 中搜索这些数据。现在,您可以在 App Search 中使用连接器,在 Workplace Search 中使用网络爬虫。", @@ -13040,8 +13006,6 @@ "xpack.enterpriseSearch.emailLabel": "电子邮件", "xpack.enterpriseSearch.emptyState.description": "您的内容存储在 Elasticsearch 索引中。通过创建 Elasticsearch 索引并选择采集方法开始使用。选项包括 Elastic 网络爬虫、第三方数据集成或使用 Elasticsearch API 终端。", "xpack.enterpriseSearch.emptyState.description.line2": "无论是使用 App Search 还是 Elasticsearch 构建搜索体验,您都可以从此处立即开始。", - "xpack.enterpriseSearch.engines.engine.notFound.action1": "返回到引擎", - "xpack.enterpriseSearch.engines.navTitle": "引擎", "xpack.enterpriseSearch.enterpriseSearch.setupGuide.description": "随时随地进行全面搜索。为工作繁忙的团队轻松实现强大的现代搜索体验。将预先调整的搜索功能快速添加到您的网站、应用或工作区。全面搜索就是这么简单。", "xpack.enterpriseSearch.enterpriseSearch.setupGuide.notConfigured": "企业搜索尚未在您的 Kibana 实例中配置。", "xpack.enterpriseSearch.enterpriseSearch.setupGuide.videoAlt": "企业搜索入门", @@ -20349,15 +20313,11 @@ "xpack.maps.addLayerPanel.addLayer": "添加图层", "xpack.maps.addLayerPanel.changeDataSourceButtonLabel": "更改图层", "xpack.maps.addLayerPanel.footer.cancelButtonLabel": "取消", - "xpack.maps.aggs.defaultCountLabel": "计数", "xpack.maps.attribution.addBtnAriaLabel": "添加归因", "xpack.maps.attribution.addBtnLabel": "添加归因", - "xpack.maps.attribution.applyBtnLabel": "应用", "xpack.maps.attribution.attributionFormLabel": "归因", "xpack.maps.attribution.clearBtnAriaLabel": "清除归因", - "xpack.maps.attribution.clearBtnLabel": "清除", "xpack.maps.attribution.editBtnAriaLabel": "编辑归因", - "xpack.maps.attribution.editBtnLabel": "编辑", "xpack.maps.attribution.labelFieldLabel": "标签", "xpack.maps.attribution.urlLabel": "链接", "xpack.maps.badge.readOnly.text": "只读", @@ -20594,7 +20554,6 @@ "xpack.maps.mapActions.removeFeatureError": "无法从索引中移除特征。", "xpack.maps.mapListing.entityName": "地图", "xpack.maps.mapListing.entityNamePlural": "地图", - "xpack.maps.mapListing.errorAttemptingToLoadSavedMaps": "无法加载地图", "xpack.maps.mapSavedObjectLabel": "地图", "xpack.maps.mapSettingsPanel.addCustomIcon": "添加定制图标", "xpack.maps.mapSettingsPanel.autoFitToBoundsLocationLabel": "使地图自动适应数据边界", @@ -20630,15 +20589,7 @@ "xpack.maps.metricsEditor.selectFieldLabel": "字段", "xpack.maps.metricsEditor.selectFieldPlaceholder": "选择字段", "xpack.maps.metricsEditor.selectPercentileLabel": "百分位数", - "xpack.maps.metricSelect.averageDropDownOptionLabel": "平均值", - "xpack.maps.metricSelect.cardinalityDropDownOptionLabel": "唯一计数", - "xpack.maps.metricSelect.countDropDownOptionLabel": "计数", - "xpack.maps.metricSelect.maxDropDownOptionLabel": "最大值", - "xpack.maps.metricSelect.minDropDownOptionLabel": "最小值", - "xpack.maps.metricSelect.percentileDropDownOptionLabel": "百分位数", "xpack.maps.metricSelect.selectAggregationPlaceholder": "选择聚合", - "xpack.maps.metricSelect.sumDropDownOptionLabel": "求和", - "xpack.maps.metricSelect.termsDropDownOptionLabel": "热门词", "xpack.maps.mvtSource.addFieldLabel": "添加", "xpack.maps.mvtSource.fieldPlaceholderText": "字段名称", "xpack.maps.mvtSource.numberFieldLabel": "数字", @@ -23438,7 +23389,7 @@ "xpack.ml.ruleEditor.scopeSection.noPermissionToViewFilterListsTitle": "您无权查看筛选列表", "xpack.ml.ruleEditor.scopeSection.scopeTitle": "范围", "xpack.ml.ruleEditor.selectRuleAction.createRuleLinkText": "创建规则", - "xpack.ml.ruleEditor.selectRuleAction.orText": "或 ", + "xpack.ml.ruleEditor.selectRuleAction.orText": "或 ", "xpack.ml.ruleEditor.typicalAppliesTypeText": "典型", "xpack.ml.sampleDataLinkLabel": "ML 作业", "xpack.ml.selectDataViewLabel": "选择数据视图", @@ -27853,7 +27804,6 @@ "xpack.securitySolution.detectionEngine.rules.allRules.bulkActions.disable.successToastDescription": "已成功禁用 {totalRules, plural, other {{totalRules} 个规则}}", "xpack.securitySolution.detectionEngine.rules.allRules.bulkActions.duplicate.errorToastDescription": "无法复制 {rulesCount, plural, other {# 个规则}}。", "xpack.securitySolution.detectionEngine.rules.allRules.bulkActions.duplicate.exceptionsConfirmation.modalBody": "您正在复制 {rulesCount, plural, other {# 个选定规则}},请选择您希望如何复制现有例外", - "xpack.securitySolution.detectionEngine.rules.allRules.bulkActions.duplicate.exceptionsConfirmation.modalTitle": "复制存在例外的{rulesCount, plural, other {规则}}?", "xpack.securitySolution.detectionEngine.rules.allRules.bulkActions.duplicate.exceptionsConfirmation.with": "复制{rulesCount, plural, other {规则}}及其例外", "xpack.securitySolution.detectionEngine.rules.allRules.bulkActions.duplicate.exceptionsConfirmation.without": "仅复制{rulesCount, plural, other {规则}}", "xpack.securitySolution.detectionEngine.rules.allRules.bulkActions.duplicate.successToastDescription": "已成功复制 {totalRules, plural, other {{totalRules} 个规则}}", @@ -29044,9 +28994,6 @@ "xpack.securitySolution.detectionEngine.createRule.stepDefineRule.threatMatchingIcesHelperDescription": "选择威胁索引", "xpack.securitySolution.detectionEngine.createRule.stepDefineRule.threatMatchoutputIndiceNameFieldRequiredError": "至少需要一种索引模式。", "xpack.securitySolution.detectionEngine.createRule.stepDefineRule.thresholdField.thresholdFieldPlaceholderText": "所有结果", - "xpack.securitySolution.detectionEngine.createRule.stepRuleActions.fieldThrottleHelpText": "选择在规则评估为 true 时应执行自动操作的时间。", - "xpack.securitySolution.detectionEngine.createRule.stepRuleActions.fieldThrottleHelpTextWhenQuery": "选择在规则评估为 true 时应执行自动操作的时间。此频率不适用于响应操作。", - "xpack.securitySolution.detectionEngine.createRule.stepRuleActions.fieldThrottleLabel": "操作频率", "xpack.securitySolution.detectionEngine.createRule.stepRuleActions.noReadActionsPrivileges": "无法创建规则操作。您对“操作”插件没有“读”权限。", "xpack.securitySolution.detectionEngine.createRule.stepScheduleRule.completeWithEnablingTitle": "创建并启用规则", "xpack.securitySolution.detectionEngine.createRule.stepScheduleRule.completeWithoutEnablingTitle": "创建规则但不启用", @@ -30004,12 +29951,9 @@ "xpack.securitySolution.detectionEngine.rules.allRules.bulkActions.duplicate.exceptionsConfirmation.tooltip": " 如果您复制例外,则会通过引用复制共享例外列表,然后复制默认规则例外,并将其创建为新例外", "xpack.securitySolution.detectionEngine.rules.allRules.bulkActions.duplicate.successToastTitle": "规则已复制", "xpack.securitySolution.detectionEngine.rules.allRules.bulkActions.duplicateTitle": "复制", - "xpack.securitySolution.detectionEngine.rules.allRules.bulkActions.edit.addRuleActions.actionFrequencyDetail": "您在下面选择的操作频率将应用于所有选定规则的所有操作(新操作和现有操作)。", "xpack.securitySolution.detectionEngine.rules.allRules.bulkActions.edit.addRuleActions.formTitle": "添加规则操作", "xpack.securitySolution.detectionEngine.rules.allRules.bulkActions.edit.addRuleActions.overwriteCheckboxLabel": "覆盖所有选定规则操作", "xpack.securitySolution.detectionEngine.rules.allRules.bulkActions.edit.addRuleActions.ruleVariablesDetail": "基于规则类型,规则变量可能仅影响您选择的某些规则(例如,\\u007b\\u007bcontext.rule.threshold\\u007d\\u007d 将仅显示阈值规则的值)。", - "xpack.securitySolution.detectionEngine.rules.allRules.bulkActions.edit.addRuleActions.throttleHelpText": "选择在规则评估为 true 时应执行自动操作的时间。", - "xpack.securitySolution.detectionEngine.rules.allRules.bulkActions.edit.addRuleActions.throttleLabel": "操作频率", "xpack.securitySolution.detectionEngine.rules.allRules.bulkActions.edit.addRuleActions.warningCalloutMessage.buttonLabel": "保存", "xpack.securitySolution.detectionEngine.rules.allRules.bulkActions.edit.applyTimelineTemplate.formTitle": "应用时间线模板", "xpack.securitySolution.detectionEngine.rules.allRules.bulkActions.edit.applyTimelineTemplate.templateSelectorDefaultValue": "无", @@ -30920,10 +30864,6 @@ "xpack.securitySolution.exceptions.exceptionListsCloseImportFlyout": "关闭", "xpack.securitySolution.exceptions.exceptionListsFilePickerPrompt": "选择或拖放多个文件", "xpack.securitySolution.exceptions.exceptionListsImportButton": "导入列表", - "xpack.securitySolution.exceptions.exportModalCancelButton": "取消", - "xpack.securitySolution.exceptions.exportModalConfirmButton": "导出", - "xpack.securitySolution.exceptions.exportModalIncludeSwitchLabel": "包括已过期例外", - "xpack.securitySolution.exceptions.exportModalTitle": "导出例外列表", "xpack.securitySolution.exceptions.fetchError": "提取例外列表时出错", "xpack.securitySolution.exceptions.fetchingReferencesErrorToastTitle": "提取例外引用时出错", "xpack.securitySolution.exceptions.list.exception.item.card.delete.label": "删除规则例外", @@ -35600,7 +35540,7 @@ "xpack.transform.stepCreateForm.duplicateDataViewErrorMessage": "创建 Kibana 数据视图 {dataViewName} 时发生错误:数据视图已存在。", "xpack.transform.stepCreateForm.startTransformErrorMessage": "启动转换 {transformId} 时发生错误:", "xpack.transform.stepCreateForm.startTransformSuccessMessage": "启动转换 {transformId} 的请求已确认。", - "xpack.transform.stepDefineForm.invalidKuerySyntaxErrorMessageQueryBar": "无效查询:{errorMessage}", + "xpack.transform.stepDefineForm.invalidKuerySyntaxErrorMessageQueryBar": "无效查询:{queryErrorMessage}", "xpack.transform.stepDefineForm.queryPlaceholderKql": "例如,{example}", "xpack.transform.stepDefineForm.queryPlaceholderLucene": "例如,{example}", "xpack.transform.stepDefineForm.runtimeFieldsListLabel": "{runtimeFields}", diff --git a/x-pack/plugins/triggers_actions_ui/public/application/constants/index.ts b/x-pack/plugins/triggers_actions_ui/public/application/constants/index.ts index 19b66ff62d524..22ead5ca4d2fe 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/constants/index.ts +++ b/x-pack/plugins/triggers_actions_ui/public/application/constants/index.ts @@ -68,6 +68,7 @@ export const RULE_EXECUTION_LOG_COLUMN_IDS = [ 'es_search_duration', 'schedule_delay', 'timed_out', + 'maintenance_window_ids', ] as const; export const RULE_EXECUTION_LOG_DURATION_COLUMNS = [ diff --git a/x-pack/plugins/triggers_actions_ui/public/application/hooks/use_load_rule_aggregations.test.tsx b/x-pack/plugins/triggers_actions_ui/public/application/hooks/use_load_rule_aggregations.test.tsx index df067b43c32c7..b9f33ab40c6fc 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/hooks/use_load_rule_aggregations.test.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/hooks/use_load_rule_aggregations.test.tsx @@ -68,6 +68,7 @@ describe('useLoadRuleAggregations', () => { ruleExecutionStatuses: [], ruleLastRunOutcomes: [], ruleStatuses: [], + ruleParams: {}, tags: [], }, enabled: true, @@ -105,6 +106,7 @@ describe('useLoadRuleAggregations', () => { types: ['type1', 'type2'], actionTypes: ['action1', 'action2'], ruleExecutionStatuses: ['status1', 'status2'], + ruleParams: {}, ruleStatuses: ['enabled', 'snoozed'] as RuleStatus[], tags: ['tag1', 'tag2'], ruleLastRunOutcomes: ['outcome1', 'outcome2'], @@ -145,6 +147,7 @@ describe('useLoadRuleAggregations', () => { types: [], actionTypes: [], ruleExecutionStatuses: [], + ruleParams: {}, ruleLastRunOutcomes: [], ruleStatuses: [], tags: [], diff --git a/x-pack/plugins/triggers_actions_ui/public/application/hooks/use_load_rules.test.tsx b/x-pack/plugins/triggers_actions_ui/public/application/hooks/use_load_rules.test.tsx index ff674f677783e..1342746223889 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/hooks/use_load_rules.test.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/hooks/use_load_rules.test.tsx @@ -272,6 +272,7 @@ describe('useLoadRules', () => { types: [], actionTypes: [], ruleExecutionStatuses: [], + ruleParams: {}, ruleLastRunOutcomes: [], ruleStatuses: [], tags: [], @@ -328,6 +329,7 @@ describe('useLoadRules', () => { types: ['type1', 'type2'], actionTypes: ['action1', 'action2'], ruleExecutionStatuses: ['status1', 'status2'], + ruleParams: {}, ruleLastRunOutcomes: ['outcome1', 'outcome2'], ruleStatuses: ['enabled', 'snoozed'] as RuleStatus[], tags: ['tag1', 'tag2'], @@ -355,6 +357,7 @@ describe('useLoadRules', () => { actionTypesFilter: ['action1', 'action2'], ruleExecutionStatusesFilter: ['status1', 'status2'], ruleLastRunOutcomesFilter: ['outcome1', 'outcome2'], + ruleParamsFilter: {}, ruleStatusesFilter: ['enabled', 'snoozed'], tagsFilter: ['tag1', 'tag2'], sort: { field: 'name', direction: 'asc' }, @@ -378,6 +381,7 @@ describe('useLoadRules', () => { types: [], actionTypes: [], ruleExecutionStatuses: [], + ruleParams: {}, ruleLastRunOutcomes: [], ruleStatuses: [], tags: [], @@ -415,6 +419,7 @@ describe('useLoadRules', () => { types: [], actionTypes: [], ruleExecutionStatuses: [], + ruleParams: {}, ruleLastRunOutcomes: [], ruleStatuses: [], tags: [], @@ -444,6 +449,7 @@ describe('useLoadRules', () => { types: [], actionTypes: [], ruleExecutionStatuses: [], + ruleParams: {}, ruleLastRunOutcomes: [], ruleStatuses: [], tags: [], @@ -477,6 +483,7 @@ describe('useLoadRules', () => { types: ['some-kind-of-filter'], actionTypes: [], ruleExecutionStatuses: [], + ruleParams: {}, ruleLastRunOutcomes: [], ruleStatuses: [], tags: [], diff --git a/x-pack/plugins/triggers_actions_ui/public/application/hooks/use_load_rules_query.ts b/x-pack/plugins/triggers_actions_ui/public/application/hooks/use_load_rules_query.ts index 8ffb59a7806ad..7a30b00b94c94 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/hooks/use_load_rules_query.ts +++ b/x-pack/plugins/triggers_actions_ui/public/application/hooks/use_load_rules_query.ts @@ -44,6 +44,7 @@ export const useLoadRulesQuery = (props: UseLoadRulesQueryProps) => { filters.actionTypes, filters.ruleStatuses, filters.ruleLastRunOutcomes, + filters.ruleParams, page, sort, { @@ -59,6 +60,7 @@ export const useLoadRulesQuery = (props: UseLoadRulesQueryProps) => { actionTypesFilter: filters.actionTypes, ruleExecutionStatusesFilter: filters.ruleExecutionStatuses, ruleLastRunOutcomesFilter: filters.ruleLastRunOutcomes, + ruleParamsFilter: filters.ruleParams, ruleStatusesFilter: filters.ruleStatuses, tagsFilter: filters.tags, sort, diff --git a/x-pack/plugins/triggers_actions_ui/public/application/lib/rule_api/create.ts b/x-pack/plugins/triggers_actions_ui/public/application/lib/rule_api/create.ts index ed56bcdb3ca7d..d8b8c85ad26a5 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/lib/rule_api/create.ts +++ b/x-pack/plugins/triggers_actions_ui/public/application/lib/rule_api/create.ts @@ -36,7 +36,7 @@ const rewriteBodyRequest: RewriteResponseCase = ({ throttle: frequency!.throttle, summary: frequency!.summary, }, - alertsFilter, + alerts_filter: alertsFilter, })), }); diff --git a/x-pack/plugins/triggers_actions_ui/public/application/lib/rule_api/map_filters_to_kuery_node.ts b/x-pack/plugins/triggers_actions_ui/public/application/lib/rule_api/map_filters_to_kuery_node.ts index 8159501b3d4b4..ea704891523b8 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/lib/rule_api/map_filters_to_kuery_node.ts +++ b/x-pack/plugins/triggers_actions_ui/public/application/lib/rule_api/map_filters_to_kuery_node.ts @@ -13,6 +13,7 @@ export const mapFiltersToKueryNode = ({ actionTypesFilter, ruleExecutionStatusesFilter, ruleLastRunOutcomesFilter, + ruleParamsFilter, ruleStatusesFilter, tagsFilter, searchText, @@ -22,6 +23,7 @@ export const mapFiltersToKueryNode = ({ tagsFilter?: string[]; ruleExecutionStatusesFilter?: string[]; ruleLastRunOutcomesFilter?: string[]; + ruleParamsFilter?: Record; ruleStatusesFilter?: RuleStatus[]; searchText?: string; }): KueryNode | null => { @@ -63,6 +65,19 @@ export const mapFiltersToKueryNode = ({ ); } + if (ruleParamsFilter && Object.keys(ruleParamsFilter).length) { + filterKueryNode.push( + nodeBuilder.and( + Object.keys(ruleParamsFilter).map((ruleParam) => + nodeBuilder.is( + `alert.attributes.params.${ruleParam}`, + String(ruleParamsFilter[ruleParam]) + ) + ) + ) + ); + } + if (ruleStatusesFilter && ruleStatusesFilter.length) { const snoozedFilter = nodeBuilder.or([ fromKueryExpression('alert.attributes.muteAll: true'), diff --git a/x-pack/plugins/triggers_actions_ui/public/application/lib/rule_api/rule_summary.test.ts b/x-pack/plugins/triggers_actions_ui/public/application/lib/rule_api/rule_summary.test.ts index 43beb66b40f9e..889f2634eacc9 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/lib/rule_api/rule_summary.test.ts +++ b/x-pack/plugins/triggers_actions_ui/public/application/lib/rule_api/rule_summary.test.ts @@ -28,6 +28,7 @@ describe('loadRuleSummary', () => { lastRun: '2021-04-01T22:18:27.609Z', muteAll: false, name: 'test', + revision: 0, ruleTypeId: '.index-threshold', status: 'OK', statusEndDate: '2021-04-01T22:19:25.174Z', @@ -55,6 +56,7 @@ describe('loadRuleSummary', () => { last_run: '2021-04-01T22:18:27.609Z', mute_all: false, name: 'test', + revision: 0, rule_type_id: '.index-threshold', status: 'OK', status_end_date: '2021-04-01T22:19:25.174Z', diff --git a/x-pack/plugins/triggers_actions_ui/public/application/lib/rule_api/rules_helpers.ts b/x-pack/plugins/triggers_actions_ui/public/application/lib/rule_api/rules_helpers.ts index cce9fd3e5ac97..989bd49289dde 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/lib/rule_api/rules_helpers.ts +++ b/x-pack/plugins/triggers_actions_ui/public/application/lib/rule_api/rules_helpers.ts @@ -19,6 +19,7 @@ export interface LoadRulesProps { tagsFilter?: string[]; ruleExecutionStatusesFilter?: string[]; ruleLastRunOutcomesFilter?: string[]; + ruleParamsFilter?: Record; ruleStatusesFilter?: RuleStatus[]; sort?: Sorting; } diff --git a/x-pack/plugins/triggers_actions_ui/public/application/lib/rule_api/rules_kuery_filter.ts b/x-pack/plugins/triggers_actions_ui/public/application/lib/rule_api/rules_kuery_filter.ts index d7c56388b4e9e..f7e2bd6a8177e 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/lib/rule_api/rules_kuery_filter.ts +++ b/x-pack/plugins/triggers_actions_ui/public/application/lib/rule_api/rules_kuery_filter.ts @@ -19,6 +19,7 @@ export async function loadRulesWithKueryFilter({ actionTypesFilter, ruleExecutionStatusesFilter, ruleLastRunOutcomesFilter, + ruleParamsFilter, ruleStatusesFilter, tagsFilter, sort = { field: 'name', direction: 'asc' }, @@ -34,6 +35,7 @@ export async function loadRulesWithKueryFilter({ tagsFilter, ruleExecutionStatusesFilter, ruleLastRunOutcomesFilter, + ruleParamsFilter, ruleStatusesFilter, searchText, }); diff --git a/x-pack/plugins/triggers_actions_ui/public/application/lib/rule_api/update.ts b/x-pack/plugins/triggers_actions_ui/public/application/lib/rule_api/update.ts index 15097eb03f156..ee2418d22262b 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/lib/rule_api/update.ts +++ b/x-pack/plugins/triggers_actions_ui/public/application/lib/rule_api/update.ts @@ -26,7 +26,7 @@ const rewriteBodyRequest: RewriteResponseCase = ({ actions, ... throttle: frequency!.throttle, summary: frequency!.summary, }, - alertsFilter, + alerts_filter: alertsFilter, ...(uuid && { uuid }), })), }); diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/action_alerts_filter_query.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/action_alerts_filter_query.tsx new file mode 100644 index 0000000000000..3cd22cc70a429 --- /dev/null +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/action_alerts_filter_query.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 React, { useState, useCallback, useMemo, useEffect } from 'react'; +import { Filter } from '@kbn/es-query'; +import { i18n } from '@kbn/i18n'; +import { EuiSwitch, EuiSpacer } from '@elastic/eui'; +import { AlertsFilter } from '@kbn/alerting-plugin/common'; +import deepEqual from 'fast-deep-equal'; +import { AlertsSearchBar } from '../alerts_search_bar'; + +interface ActionAlertsFilterQueryProps { + state?: AlertsFilter['query']; + onChange: (update?: AlertsFilter['query']) => void; +} + +export const ActionAlertsFilterQuery: React.FC = ({ + state, + onChange, +}) => { + const [query, setQuery] = useState(state ?? { kql: '', filters: [] }); + + const queryEnabled = useMemo(() => Boolean(state), [state]); + + useEffect(() => { + const nextState = queryEnabled ? query : undefined; + if (!deepEqual(state, nextState)) onChange(nextState); + }, [queryEnabled, query, state, onChange]); + + const toggleQuery = useCallback( + () => onChange(state ? undefined : query), + [state, query, onChange] + ); + const updateQuery = useCallback( + (update: Partial) => { + setQuery({ + ...query, + ...update, + }); + }, + [query, setQuery] + ); + + const onQueryChange = useCallback( + ({ query: newQuery }) => updateQuery({ kql: newQuery }), + [updateQuery] + ); + + const onFiltersUpdated = useCallback( + (filters: Filter[]) => updateQuery({ filters }), + [updateQuery] + ); + + return ( + <> + + {queryEnabled && ( + <> + + + + )} + + ); +}; diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/action_alerts_filter_timeframe.test.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/action_alerts_filter_timeframe.test.tsx index a4c3edfda3344..ef73b3ab305c0 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/action_alerts_filter_timeframe.test.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/action_alerts_filter_timeframe.test.tsx @@ -17,7 +17,7 @@ jest.mock('@kbn/kibana-react-plugin/public/ui_settings/use_ui_setting', () => ({ })); describe('action_alerts_filter_timeframe', () => { - async function setup(timeframe: AlertsFilterTimeframe | null) { + async function setup(timeframe?: AlertsFilterTimeframe) { const wrapper = mountWithIntl( {}} /> ); @@ -32,7 +32,7 @@ describe('action_alerts_filter_timeframe', () => { } it('renders an unchecked switch when passed a null timeframe', async () => { - const wrapper = await setup(null); + const wrapper = await setup(); const alertsFilterTimeframeToggle = wrapper.find( '[data-test-subj="alertsFilterTimeframeToggle"]' diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/action_alerts_filter_timeframe.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/action_alerts_filter_timeframe.tsx index a8e276f81535f..05fcd8fba77a7 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/action_alerts_filter_timeframe.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/action_alerts_filter_timeframe.tsx @@ -24,8 +24,8 @@ import { AlertsFilterTimeframe, IsoWeekday } from '@kbn/alerting-plugin/common'; import { I18N_WEEKDAY_OPTIONS_DDD, ISO_WEEKDAYS } from '../../../common/constants'; interface ActionAlertsFilterTimeframeProps { - state: AlertsFilterTimeframe | null; - onChange: (update: AlertsFilterTimeframe | null) => void; + state?: AlertsFilterTimeframe; + onChange: (update?: AlertsFilterTimeframe) => void; } const TIMEZONE_OPTIONS = moment.tz?.names().map((n) => ({ label: n })) ?? [{ label: 'UTC' }]; @@ -46,14 +46,14 @@ const useDefaultTimezone = () => { return kibanaTz; }; -const useTimeframe = (initialTimeframe: AlertsFilterTimeframe | null) => { +const useTimeframe = (initialTimeframe?: AlertsFilterTimeframe) => { const timezone = useDefaultTimezone(); const DEFAULT_TIMEFRAME = { days: [], timezone, hours: { start: '00:00', - end: '24:00', + end: '23:59', }, }; return useState(initialTimeframe || DEFAULT_TIMEFRAME); @@ -79,12 +79,12 @@ export const ActionAlertsFilterTimeframe: React.FC { - const nextState = timeframeEnabled ? timeframe : null; + const nextState = timeframeEnabled ? timeframe : undefined; if (!deepEqual(state, nextState)) onChange(nextState); }, [timeframeEnabled, timeframe, state, onChange]); const toggleTimeframe = useCallback( - () => onChange(state ? null : timeframe), + () => onChange(state ? undefined : timeframe), [state, timeframe, onChange] ); const updateTimeframe = useCallback( diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/action_form.test.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/action_form.test.tsx index 18a8a577b7e0f..5be6a91698833 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/action_form.test.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/action_form.test.tsx @@ -351,7 +351,7 @@ describe('action_form', () => { (initialAlert.actions[index] = { ...initialAlert.actions[index], alertsFilter: { - ...(initialAlert.actions[index].alertsFilter ?? { query: null, timeframe: null }), + ...initialAlert.actions[index].alertsFilter, [key]: value, }, }) diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/action_type_form.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/action_type_form.tsx index 38d267d014824..1743d435b722f 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/action_type_form.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/action_type_form.tsx @@ -61,6 +61,7 @@ import { ConnectorsSelection } from './connectors_selection'; import { ActionNotifyWhen } from './action_notify_when'; import { validateParamsForWarnings } from '../../lib/validate_params_for_warnings'; import { ActionAlertsFilterTimeframe } from './action_alerts_filter_timeframe'; +import { ActionAlertsFilterQuery } from './action_alerts_filter_query'; export type ActionTypeFormProps = { actionItem: RuleAction; @@ -405,8 +406,13 @@ export const ActionTypeForm = ({ {showActionAlertsFilter && ( <> {!hideNotifyWhen && } + setActionAlertsFilterProperty('query', query, index)} + /> + setActionAlertsFilterProperty('timeframe', timeframe, index)} /> diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/encrypted_fields_callout.test.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/encrypted_fields_callout.test.tsx index 41893e785f5bf..78039c753a276 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/encrypted_fields_callout.test.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/encrypted_fields_callout.test.tsx @@ -35,14 +35,14 @@ const renderWithSecretFields = ({ describe('EncryptedFieldsCallout', () => { const isCreateTests: Array<[number, string]> = [ - [1, 'Remember value label0. You must reenter it each time you edit the connector.'], + [1, 'Remember your label0 value. You must reenter it each time you edit the connector.'], [ 2, - 'Remember values label0 and label1. You must reenter them each time you edit the connector.', + 'Remember your label0 and label1 values. You must reenter them each time you edit the connector.', ], [ 3, - 'Remember values label0, label1, and label2. You must reenter them each time you edit the connector.', + 'Remember your label0, label1, and label2 values. You must reenter them each time you edit the connector.', ], ]; diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/encrypted_fields_callout.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/encrypted_fields_callout.tsx index 7bc0fdbc0703c..35cbb473c6a3f 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/encrypted_fields_callout.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/encrypted_fields_callout.tsx @@ -104,7 +104,7 @@ const EncryptedFieldsCalloutComponent: React.FC = ( { values: { secretFieldsLabel, encryptedFieldsLength: totalSecretFields }, defaultMessage: - 'Remember value{encryptedFieldsLength, plural, one {} other {s}} {secretFieldsLabel}. You must reenter {encryptedFieldsLength, plural, one {it} other {them}} each time you edit the connector.', + 'Remember your {secretFieldsLabel} {encryptedFieldsLength, plural, one {value} other {values}}. You must reenter {encryptedFieldsLength, plural, one {it} other {them}} each time you edit the connector.', } )} dataTestSubj="create-connector-secrets-callout" diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/actions_connectors_list/components/actions_connectors_list.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/actions_connectors_list/components/actions_connectors_list.tsx index a03c34c90962e..9edde99574ca1 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/actions_connectors_list/components/actions_connectors_list.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/actions_connectors_list/components/actions_connectors_list.tsx @@ -234,6 +234,7 @@ const ActionsConnectorsList: React.FunctionComponent = () => { <> editItem(item, EditConnectorTabs.Configuration)} key={item.id} disabled={actionTypesIndex ? !actionTypesIndex[item.actionTypeId]?.enabled : true} diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_search_bar/alerts_search_bar.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_search_bar/alerts_search_bar.tsx index 6a170363d34a8..49b1f9905cd7f 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_search_bar/alerts_search_bar.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_search_bar/alerts_search_bar.tsx @@ -19,9 +19,16 @@ export function AlertsSearchBar({ appName, featureIds, query, + filters, onQueryChange, + onFiltersUpdated, rangeFrom, rangeTo, + showFilterBar = false, + showDatePicker = true, + showSubmitButton = true, + placeholder = SEARCH_BAR_PLACEHOLDER, + submitOnBlur = false, }: AlertsSearchBarProps) { const { unifiedSearch: { @@ -52,14 +59,20 @@ export function AlertsSearchBar({ ); } diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_search_bar/types.ts b/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_search_bar/types.ts index 2c94c250c168a..b7616333c199a 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_search_bar/types.ts +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_search_bar/types.ts @@ -5,6 +5,7 @@ * 2.0. */ +import { Filter } from '@kbn/es-query'; import { ValidFeatureId } from '@kbn/rule-data-utils'; export type QueryLanguageType = 'lucene' | 'kuery'; @@ -15,8 +16,15 @@ export interface AlertsSearchBarProps { rangeFrom?: string; rangeTo?: string; query?: string; + filters?: Filter[]; + showFilterBar?: boolean; + showDatePicker?: boolean; + showSubmitButton?: boolean; + placeholder?: string; + submitOnBlur?: boolean; onQueryChange: (query: { dateRange: { from: string; to: string; mode?: 'absolute' | 'relative' }; query?: string; }) => void; + onFiltersUpdated?: (filters: Filter[]) => void; } diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/common/components/event_log/event_log_list_cell_renderer.test.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/common/components/event_log/event_log_list_cell_renderer.test.tsx index 0ec383c84f6e6..32e5c0e83d297 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/common/components/event_log/event_log_list_cell_renderer.test.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/common/components/event_log/event_log_list_cell_renderer.test.tsx @@ -111,6 +111,16 @@ describe('rule_event_log_list_cell_renderer', () => { expect(wrapper.find(EuiIcon).props().color).toEqual('gray'); }); + it('renders maintenance window correctly', () => { + const wrapper = shallow( + + ); + expect(wrapper.text()).toEqual('test-id-1, test-id-2'); + }); + it('links to rules on the correct space', () => { const wrapper1 = shallow( {value ? 'true' : 'false'}; } + if (columnId === 'maintenance_window_ids') { + return <>{Array.isArray(value) ? value.join(', ') : ''}; + } + return <>{value}; }; diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/rule_details/components/rule.test.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/rule_details/components/rule.test.tsx index f3775e5de5066..821d4edf16c66 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/rule_details/components/rule.test.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/rule_details/components/rule.test.tsx @@ -481,6 +481,7 @@ function mockRuleSummary(overloads: Partial = {}): RuleSummary { throttle: '', enabled: true, errorMessages: [], + revision: 0, statusStartDate: fake2MinutesAgo.toISOString(), statusEndDate: fakeNow.toISOString(), alerts: { diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/rule_details/components/rule.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/rule_details/components/rule.tsx index 81c5e0d7d7e90..8b99cc910d8da 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/rule_details/components/rule.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/rule_details/components/rule.tsx @@ -197,6 +197,7 @@ export function alertToListItem( isMuted, sortPriority, flapping: alert.flapping, + ...(alert.maintenanceWindowIds ? { maintenanceWindowIds: alert.maintenanceWindowIds } : {}), }; } diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/rule_details/components/rule_alert_list.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/rule_details/components/rule_alert_list.tsx index 3b3142181d8e7..5a6fad3b939a2 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/rule_details/components/rule_alert_list.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/rule_details/components/rule_alert_list.tsx @@ -91,6 +91,21 @@ const alertsTableColumns = ( width: '80px', 'data-test-subj': 'alertsTableCell-duration', }, + { + field: 'maintenanceWindowIds', + width: '250px', + render: (value: string[]) => { + return Array.isArray(value) ? value.join(', ') : ''; + }, + name: i18n.translate( + 'xpack.triggersActionsUI.sections.ruleDetails.alertsList.columns.maintenanceWindowIds', + { + defaultMessage: 'Maintenance windows', + } + ), + sortable: false, + 'data-test-subj': 'alertsTableCell-maintenanceWindowIds', + }, { field: '', align: RIGHT_ALIGNMENT, diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/rule_details/components/rule_event_log_list_table.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/rule_details/components/rule_event_log_list_table.tsx index 7df89d799b2ae..7965f58fa8420 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/rule_details/components/rule_event_log_list_table.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/rule_details/components/rule_event_log_list_table.tsx @@ -610,6 +610,20 @@ export const RuleEventLogListTable = ( ), isSortable: getIsColumnSortable('timed_out'), }, + { + id: 'maintenance_window_ids', + displayAsText: i18n.translate( + 'xpack.triggersActionsUI.sections.ruleDetails.eventLogColumn.maintenanceWindowIds', + { + defaultMessage: 'Maintenance windows', + } + ), + actions: { + showSortAsc: false, + showSortDesc: false, + }, + isSortable: getIsColumnSortable('maintenance_window_ids'), + }, ], [getPaginatedRowIndex, onFlyoutOpen, onFilterChange, hasRuleNames, showFromAllSpaces, logs] ); diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/rule_details/components/rule_route.test.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/rule_details/components/rule_route.test.tsx index 117bca134f0f9..1826894ef70f9 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/rule_details/components/rule_route.test.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/rule_details/components/rule_route.test.tsx @@ -162,6 +162,7 @@ function mockRuleSummary(overloads: Partial = {}): any { throttle: null, enabled: true, errorMessages: [], + revision: 0, statusStartDate: fake2MinutesAgo.toISOString(), statusEndDate: fakeNow.toISOString(), alerts: { diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/rule_details/components/test_helpers.ts b/x-pack/plugins/triggers_actions_ui/public/application/sections/rule_details/components/test_helpers.ts index 4146ac8006325..d72e381c170ab 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/rule_details/components/test_helpers.ts +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/rule_details/components/test_helpers.ts @@ -95,6 +95,7 @@ export function mockRuleSummary(overloads: Partial = {}): RuleSumma throttle: '', enabled: true, errorMessages: [], + revision: 0, statusStartDate: '2022-03-21T07:40:46-07:00', statusEndDate: '2022-03-25T07:40:46-07:00', alerts: { diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/rule_details/components/types.ts b/x-pack/plugins/triggers_actions_ui/public/application/sections/rule_details/components/types.ts index c469b61690c3a..f66f648051715 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/rule_details/components/types.ts +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/rule_details/components/types.ts @@ -14,4 +14,5 @@ export interface AlertListItem { isMuted: boolean; sortPriority: number; flapping: boolean; + maintenanceWindowIds?: string[]; } diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/rule_form/rule_reducer.ts b/x-pack/plugins/triggers_actions_ui/public/application/sections/rule_form/rule_reducer.ts index 8ac7c38276233..54f3871928fb3 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/rule_form/rule_reducer.ts +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/rule_form/rule_reducer.ts @@ -13,6 +13,7 @@ import { IntervalSchedule, RuleActionAlertsFilterProperty, } from '@kbn/alerting-plugin/common'; +import { isEmpty } from 'lodash/fp'; import { Rule, RuleAction } from '../../../types'; import { DEFAULT_FREQUENCY } from '../../../common/constants'; @@ -237,12 +238,18 @@ export const ruleReducer = ( return state; } else { const oldAction = rule.actions.splice(index, 1)[0]; + const { alertsFilter, ...rest } = oldAction; + const updatedAlertsFilter = { ...alertsFilter }; + + if (value) { + updatedAlertsFilter[key] = value; + } else { + delete updatedAlertsFilter[key]; + } + const updatedAction = { - ...oldAction, - alertsFilter: { - ...(oldAction.alertsFilter ?? { timeframe: null, query: null }), - [key]: value, - }, + ...rest, + ...(!isEmpty(updatedAlertsFilter) ? { alertsFilter: updatedAlertsFilter } : {}), }; rule.actions.splice(index, 0, updatedAction); return { diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/rules_list/components/rules_list.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/rules_list/components/rules_list.tsx index 0bf80a0eb83cd..0fbb367e1b9ed 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/rules_list/components/rules_list.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/rules_list/components/rules_list.tsx @@ -8,7 +8,7 @@ /* eslint-disable react-hooks/exhaustive-deps */ import { i18n } from '@kbn/i18n'; -import { capitalize, isEmpty, sortBy } from 'lodash'; +import { capitalize, isEmpty, isEqual, sortBy } from 'lodash'; import { KueryNode } from '@kbn/es-query'; import { FormattedMessage } from '@kbn/i18n-react'; import React, { @@ -75,6 +75,7 @@ import './rules_list.scss'; import { CreateRuleButton } from './create_rule_button'; import { ManageLicenseModal } from './manage_license_modal'; import { getIsExperimentalFeatureEnabled } from '../../../../common/get_experimental_features'; +import { RulesListClearRuleFilterBanner } from './rules_list_clear_rule_filter_banner'; import { RulesListTable, convertRulesToTableItems } from './rules_list_table'; import { RulesListDocLink } from './rules_list_doc_link'; import { UpdateApiKeyModalConfirmation } from '../../../components/update_api_key_modal_confirmation'; @@ -107,23 +108,26 @@ const RuleEdit = lazy(() => import('../../rule_form/rule_edit')); export interface RulesListProps { filteredRuleTypes?: string[]; - showActionFilter?: boolean; + lastResponseFilter?: string[]; + lastRunOutcomeFilter?: string[]; + refresh?: Date; ruleDetailsRoute?: string; + ruleParamFilter?: Record; + rulesListKey?: string; + searchFilter?: string; + showActionFilter?: boolean; showCreateRuleButtonInPrompt?: boolean; - setHeaderActions?: (components?: React.ReactNode[]) => void; + showSearchBar?: boolean; statusFilter?: RuleStatus[]; - onStatusFilterChange?: (status: RuleStatus[]) => void; - lastResponseFilter?: string[]; + typeFilter?: string[]; + visibleColumns?: string[]; onLastResponseFilterChange?: (lastResponse: string[]) => void; - lastRunOutcomeFilter?: string[]; onLastRunOutcomeFilterChange?: (lastRunOutcome: string[]) => void; - typeFilter?: string[]; - onTypeFilterChange?: (type: string[]) => void; - searchFilter?: string; + onRuleParamFilterChange?: (ruleParams: Record) => void; onSearchFilterChange?: (search: string) => void; - refresh?: Date; - rulesListKey?: string; - visibleColumns?: string[]; + onStatusFilterChange?: (status: RuleStatus[]) => void; + onTypeFilterChange?: (type: string[]) => void; + setHeaderActions?: (components?: React.ReactNode[]) => void; } export const percentileFields = { @@ -142,47 +146,51 @@ const EMPTY_ARRAY: string[] = []; export const RulesList = ({ filteredRuleTypes = EMPTY_ARRAY, - showActionFilter = true, + lastResponseFilter, + lastRunOutcomeFilter, + refresh, ruleDetailsRoute, + ruleParamFilter, + rulesListKey, + searchFilter = '', + showActionFilter = true, showCreateRuleButtonInPrompt = false, + showSearchBar = true, statusFilter, - onStatusFilterChange, - lastResponseFilter, + typeFilter, + visibleColumns, onLastResponseFilterChange, - lastRunOutcomeFilter, onLastRunOutcomeFilterChange, - searchFilter = '', + onRuleParamFilterChange, onSearchFilterChange, - typeFilter, + onStatusFilterChange, onTypeFilterChange, setHeaderActions, - refresh, - rulesListKey, - visibleColumns, }: RulesListProps) => { const history = useHistory(); const { + actionTypeRegistry, + application: { capabilities }, http, + kibanaFeatures, notifications: { toasts }, - application: { capabilities }, ruleTypeRegistry, - actionTypeRegistry, - kibanaFeatures, } = useKibana().services; const canExecuteActions = hasExecuteActionsCapability(capabilities); const [isPerformingAction, setIsPerformingAction] = useState(false); const [page, setPage] = useState({ index: 0, size: DEFAULT_SEARCH_PAGE_SIZE }); const [inputText, setInputText] = useState(searchFilter); - const [filters, setFilters] = useState(() => ({ - searchText: searchFilter || '', - types: typeFilter || [], + const [filters, setFilters] = useState({ actionTypes: [], ruleExecutionStatuses: lastResponseFilter || [], ruleLastRunOutcomes: lastRunOutcomeFilter || [], + ruleParams: ruleParamFilter || {}, ruleStatuses: statusFilter || [], + searchText: searchFilter || '', tags: [], - })); + types: typeFilter || [], + }); const [ruleFlyoutVisible, setRuleFlyoutVisibility] = useState(false); const [editFlyoutVisible, setEditFlyoutVisibility] = useState(false); @@ -357,6 +365,9 @@ export const RulesList = ({ case 'ruleLastRunOutcomes': onLastRunOutcomeFilterChange?.(value as string[]); break; + case 'ruleParams': + onRuleParamFilterChange?.(value as Record); + break; case 'searchText': onSearchFilterChange?.(value as string); break; @@ -389,6 +400,8 @@ export const RulesList = ({ [setFilters, handleUpdateFiltersEffect] ); + const handleClearRuleParamFilter = () => updateFilters({ filter: 'ruleParams', value: {} }); + useEffect(() => { if (statusFilter) { updateFilters({ filter: 'ruleStatuses', value: statusFilter }); @@ -407,6 +420,12 @@ export const RulesList = ({ } }, [lastRunOutcomeFilter]); + useEffect(() => { + if (ruleParamFilter && !isEqual(ruleParamFilter, filters.ruleParams)) { + updateFilters({ filter: 'ruleParams', value: ruleParamFilter }); + } + }, [ruleParamFilter]); + useEffect(() => { if (typeof searchFilter === 'string') { updateFilters({ filter: 'searchText', value: searchFilter }); @@ -783,26 +802,36 @@ export const RulesList = ({ /> )} + + {showSearchBar && !isEmpty(filters.ruleParams) ? ( + + ) : null} + {showRulesList && ( <> - - + {showSearchBar ? ( + <> + + + + ) : null} + void; +} + +export const RulesListClearRuleFilterBanner = ({ + onClickClearFilter, +}: RulesListClearRuleFilterProps) => { + return ( + <> + +

+ {' '} + {' '} + + + +

+
+ + + ); +}; diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/rules_list/components/rules_list_filters_bar.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/rules_list/components/rules_list_filters_bar.tsx index 7945f8b328623..a429f3abc06c6 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/rules_list/components/rules_list_filters_bar.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/rules_list/components/rules_list_filters_bar.tsx @@ -6,16 +6,16 @@ */ import React from 'react'; -import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n-react'; +import { i18n } from '@kbn/i18n'; import { EuiFlexGroup, EuiFlexItem, EuiButton, EuiFilterGroup, - EuiFieldSearch, EuiSpacer, EuiLink, + EuiFieldSearch, } from '@elastic/eui'; import { ActionType, RulesListFilters, UpdateFiltersProps } from '../../../../types'; import { getIsExperimentalFeatureEnabled } from '../../../../common/get_experimental_features'; @@ -29,43 +29,42 @@ import { ActionTypeFilter } from './action_type_filter'; import { RuleTagFilter } from './rule_tag_filter'; import { RuleStatusFilter } from './rule_status_filter'; -const ENTER_KEY = 13; - interface RulesListFiltersBarProps { - inputText: string; - filters: RulesListFilters; - showActionFilter: boolean; - rulesStatusesTotal: Record; - rulesLastRunOutcomesTotal: Record; - tags: string[]; - filterOptions: TypeFilterProps['options']; actionTypes: ActionType[]; + filterOptions: TypeFilterProps['options']; + filters: RulesListFilters; + inputText: string; lastUpdate: string; + rulesLastRunOutcomesTotal: Record; + rulesStatusesTotal: Record; + showActionFilter: boolean; showErrors: boolean; - updateFilters: (updateFiltersProps: UpdateFiltersProps) => void; - setInputText: (text: string) => void; + tags: string[]; onClearSelection: () => void; onRefreshRules: () => void; onToggleRuleErrors: () => void; + setInputText: (text: string) => void; + updateFilters: (updateFiltersProps: UpdateFiltersProps) => void; } +const ENTER_KEY = 13; export const RulesListFiltersBar = React.memo((props: RulesListFiltersBarProps) => { const { - filters, - inputText, - showActionFilter = true, - rulesStatusesTotal, - rulesLastRunOutcomesTotal, - tags, actionTypes, filterOptions, + filters, + inputText, lastUpdate, - showErrors, - updateFilters, - setInputText, onClearSelection, onRefreshRules, onToggleRuleErrors, + rulesLastRunOutcomesTotal, + rulesStatusesTotal, + setInputText, + showActionFilter = true, + showErrors, + tags, + updateFilters, } = props; const isRuleTagFilterEnabled = getIsExperimentalFeatureEnabled('ruleTagFilter'); @@ -136,6 +135,19 @@ export const RulesListFiltersBar = React.memo((props: RulesListFiltersBarProps) ...getRuleTagFilter(), ]; + const handleChange = (e: React.ChangeEvent) => { + setInputText(e.target.value); + if (e.target.value === '') { + updateFilters({ filter: 'searchText', value: e.target.value }); + } + }; + + const handleKeyup = (e: React.KeyboardEvent) => { + if (e.keyCode === ENTER_KEY) { + updateFilters({ filter: 'searchText', value: inputText }); + } + }; + return ( <> { - setInputText(e.target.value); - if (e.target.value === '') { - updateFilters({ filter: 'searchText', value: e.target.value }); - } - }} - onKeyUp={(e) => { - if (e.keyCode === ENTER_KEY) { - updateFilters({ filter: 'searchText', value: inputText }); - } - }} placeholder={i18n.translate( 'xpack.triggersActionsUI.sections.rulesList.searchPlaceholderTitle', { defaultMessage: 'Search' } )} + value={inputText} + onChange={handleChange} + onKeyUp={handleKeyup} /> {renderRuleStatusFilter()} diff --git a/x-pack/plugins/triggers_actions_ui/public/types.ts b/x-pack/plugins/triggers_actions_ui/public/types.ts index 83f13dcbe18e5..b5278bde4e175 100644 --- a/x-pack/plugins/triggers_actions_ui/public/types.ts +++ b/x-pack/plugins/triggers_actions_ui/public/types.ts @@ -374,6 +374,11 @@ export interface RuleTypeParamsExpressionProps< unifiedSearch: UnifiedSearchPublicPluginStart; } +export type RuleParamsForRules = Record< + string, + Array<{ label: string; value: string | number | object }> +>; + export interface RuleTypeModel { id: string; description: string; @@ -688,13 +693,14 @@ export interface ConnectorServices { } export interface RulesListFilters { - searchText: string; - types: string[]; actionTypes: string[]; ruleExecutionStatuses: string[]; ruleLastRunOutcomes: string[]; + ruleParams: Record; ruleStatuses: RuleStatus[]; + searchText: string; tags: string[]; + types: string[]; } export type UpdateFiltersProps = @@ -709,6 +715,10 @@ export type UpdateFiltersProps = | { filter: 'types' | 'actionTypes' | 'ruleExecutionStatuses' | 'ruleLastRunOutcomes' | 'tags'; value: string[]; + } + | { + filter: 'ruleParams'; + value: Record; }; export interface RulesPageContainerState { diff --git a/x-pack/plugins/triggers_actions_ui/tsconfig.json b/x-pack/plugins/triggers_actions_ui/tsconfig.json index d2ff5b039022e..3222559bf2b9f 100644 --- a/x-pack/plugins/triggers_actions_ui/tsconfig.json +++ b/x-pack/plugins/triggers_actions_ui/tsconfig.json @@ -44,7 +44,6 @@ "@kbn/ui-theme", "@kbn/datemath", "@kbn/core-capabilities-common", - "@kbn/safer-lodash-set", "@kbn/shared-ux-router", "@kbn/alerts-ui-shared", "@kbn/safer-lodash-set", @@ -52,7 +51,7 @@ "@kbn/field-types", "@kbn/ecs", "@kbn/alerts-as-data-utils", - "@kbn/core-ui-settings-common" + "@kbn/core-ui-settings-common", ], "exclude": ["target/**/*"] } diff --git a/x-pack/plugins/watcher/__jest__/client_integration/helpers/setup_environment.tsx b/x-pack/plugins/watcher/__jest__/client_integration/helpers/setup_environment.tsx index 39ce0108a4d41..89c3407f83a46 100644 --- a/x-pack/plugins/watcher/__jest__/client_integration/helpers/setup_environment.tsx +++ b/x-pack/plugins/watcher/__jest__/client_integration/helpers/setup_environment.tsx @@ -7,6 +7,7 @@ import React from 'react'; import { HttpSetup } from '@kbn/core/public'; +import { KibanaContextProvider } from '@kbn/kibana-react-plugin/public'; import { init as initHttpRequests } from './http_requests'; import { mockContextValue } from './app_context.mock'; @@ -18,9 +19,11 @@ export const WithAppDependencies = setHttpClient(httpSetup); return ( - - - + + + + + ); }; diff --git a/x-pack/plugins/watcher/__jest__/client_integration/helpers/watch_edit_page.helpers.ts b/x-pack/plugins/watcher/__jest__/client_integration/helpers/watch_edit_page.helpers.ts index cbda9eb293a70..20f8389c0fec7 100644 --- a/x-pack/plugins/watcher/__jest__/client_integration/helpers/watch_edit_page.helpers.ts +++ b/x-pack/plugins/watcher/__jest__/client_integration/helpers/watch_edit_page.helpers.ts @@ -56,5 +56,6 @@ export type TestSubjects = | 'jsonWatchForm' | 'nameInput' | 'pageTitle' + | 'jsonEditor' | 'thresholdWatchForm' | 'watchTimeFieldSelect'; diff --git a/x-pack/plugins/watcher/__jest__/client_integration/watch_edit_page.test.ts b/x-pack/plugins/watcher/__jest__/client_integration/watch_edit_page.test.tsx similarity index 88% rename from x-pack/plugins/watcher/__jest__/client_integration/watch_edit_page.test.ts rename to x-pack/plugins/watcher/__jest__/client_integration/watch_edit_page.test.tsx index 0da6fa68bd856..ba38fbd5b3cc7 100644 --- a/x-pack/plugins/watcher/__jest__/client_integration/watch_edit_page.test.ts +++ b/x-pack/plugins/watcher/__jest__/client_integration/watch_edit_page.test.tsx @@ -5,6 +5,7 @@ * 2.0. */ +import React from 'react'; import { act } from 'react-dom/test-utils'; import { getWatch } from '../../__fixtures__'; @@ -16,6 +17,23 @@ import { API_BASE_PATH } from '../../common/constants'; const { setup } = pageHelpers.watchEditPage; +jest.mock('@kbn/kibana-react-plugin/public', () => { + const original = jest.requireActual('@kbn/kibana-react-plugin/public'); + return { + ...original, + // Mocking CodeEditor, which uses React Monaco under the hood + CodeEditor: (props: any) => ( + { + props.onChange(e.jsonContent); + }} + /> + ), + }; +}); + describe('', () => { const { httpSetup, httpRequestsMockHelpers } = setupEnvironment(); let testBed: WatchEditTestBed; @@ -43,14 +61,14 @@ describe('', () => { }); test('should populate the correct values', () => { - const { find, exists, component } = testBed; + const { find, exists } = testBed; const { watch } = WATCH; - const codeEditor = component.find('EuiCodeEditor').at(1); + const jsonEditorValue = testBed.find('jsonEditor').props()['data-currentvalue']; expect(exists('jsonWatchForm')).toBe(true); expect(find('nameInput').props().value).toBe(watch.name); expect(find('idInput').props().value).toBe(watch.id); - expect(JSON.parse(codeEditor.props().value as string)).toEqual(defaultWatch); + expect(JSON.parse(jsonEditorValue)).toEqual(defaultWatch); // ID should not be editable expect(find('idInput').props().readOnly).toEqual(true); diff --git a/x-pack/plugins/watcher/public/application/index.tsx b/x-pack/plugins/watcher/public/application/index.tsx index dc12832825b81..8c2edb133660d 100644 --- a/x-pack/plugins/watcher/public/application/index.tsx +++ b/x-pack/plugins/watcher/public/application/index.tsx @@ -9,6 +9,7 @@ import React from 'react'; import { render, unmountComponentAtNode } from 'react-dom'; import { Observable } from 'rxjs'; import { CoreTheme } from '@kbn/core/public'; +import { KibanaContextProvider } from '@kbn/kibana-react-plugin/public'; import { KibanaThemeProvider } from './shared_imports'; import { App, AppDeps } from './app'; @@ -27,9 +28,11 @@ export const renderApp = (bootDeps: BootDeps) => { render( - - - + + + + + , element ); diff --git a/x-pack/plugins/watcher/public/application/sections/watch_edit_page/components/json_watch_edit/json_watch_edit_form.tsx b/x-pack/plugins/watcher/public/application/sections/watch_edit_page/components/json_watch_edit/json_watch_edit_form.tsx index 95db0ab535f8e..ef71362bf22a1 100644 --- a/x-pack/plugins/watcher/public/application/sections/watch_edit_page/components/json_watch_edit/json_watch_edit_form.tsx +++ b/x-pack/plugins/watcher/public/application/sections/watch_edit_page/components/json_watch_edit/json_watch_edit_form.tsx @@ -20,18 +20,17 @@ import { } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n-react'; -import { XJsonMode } from '@kbn/ace'; +import { CodeEditor } from '@kbn/kibana-react-plugin/public'; +import { XJson } from '../../../../shared_imports'; import { serializeJsonWatch } from '../../../../../../common/lib/serialization'; import { ErrableFormRow, SectionError, Error as ServerError } from '../../../../components'; -import { XJson, EuiCodeEditor } from '../../../../shared_imports'; import { onWatchSave } from '../../watch_edit_actions'; import { WatchContext } from '../../watch_context'; import { goToWatchList } from '../../../../lib/navigation'; import { RequestFlyout } from '../request_flyout'; import { useAppContext } from '../../../../app_context'; -const xJsonMode = new XJsonMode(); const { useXJsonMode } = XJson; export const JsonWatchEditForm = () => { @@ -168,18 +167,22 @@ export const JsonWatchEditForm = () => { fullWidth errors={jsonErrors} > - { if (validationError) { setValidationError(null); diff --git a/x-pack/plugins/watcher/public/application/sections/watch_edit_page/components/json_watch_edit/json_watch_edit_simulate.tsx b/x-pack/plugins/watcher/public/application/sections/watch_edit_page/components/json_watch_edit/json_watch_edit_simulate.tsx index 9e2fa3f35b50f..bc6ecfcbc16cc 100644 --- a/x-pack/plugins/watcher/public/application/sections/watch_edit_page/components/json_watch_edit/json_watch_edit_simulate.tsx +++ b/x-pack/plugins/watcher/public/application/sections/watch_edit_page/components/json_watch_edit/json_watch_edit_simulate.tsx @@ -24,9 +24,7 @@ import { } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n-react'; -import { XJsonMode } from '@kbn/ace'; - -const xJsonMode = new XJsonMode(); +import { CodeEditor } from '@kbn/kibana-react-plugin/public'; import { WatchHistoryItem } from '../../../../models/watch_history_item'; @@ -43,7 +41,7 @@ import { SimulateWatchResultsFlyout } from './simulate_watch_results_flyout'; import { getTimeUnitLabel } from '../../../../lib/get_time_unit_label'; import { useAppContext } from '../../../../app_context'; -import { XJson, EuiCodeEditor } from '../../../../shared_imports'; +import { XJson } from '../../../../shared_imports'; const { useXJsonMode } = XJson; @@ -369,18 +367,22 @@ export const JsonWatchEditSimulate = ({ fullWidth errors={executeWatchErrors} > - { setXJson(xjson); setExecuteDetails( diff --git a/x-pack/plugins/watcher/public/application/sections/watch_edit_page/components/threshold_watch_edit/action_fields/webhook_action_fields.tsx b/x-pack/plugins/watcher/public/application/sections/watch_edit_page/components/threshold_watch_edit/action_fields/webhook_action_fields.tsx index bbe5449fa8732..e7da2c3073870 100644 --- a/x-pack/plugins/watcher/public/application/sections/watch_edit_page/components/threshold_watch_edit/action_fields/webhook_action_fields.tsx +++ b/x-pack/plugins/watcher/public/application/sections/watch_edit_page/components/threshold_watch_edit/action_fields/webhook_action_fields.tsx @@ -18,7 +18,8 @@ import { EuiSpacer, } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; -import { EuiCodeEditor } from '../../../../../shared_imports'; +import { CodeEditor } from '@kbn/kibana-react-plugin/public'; + import { ErrableFormRow } from '../../../../../components/form_errors'; import { WebhookAction } from '../../../../../../../common/types/action_types'; @@ -242,19 +243,22 @@ export const WebhookActionFields: React.FunctionComponent = ({ fullWidth errors={errors} > - { editAction({ key: 'body', value: json }); }} diff --git a/x-pack/plugins/watcher/public/application/shared_imports.ts b/x-pack/plugins/watcher/public/application/shared_imports.ts index 971182a22a7a7..2396c7783aeb1 100644 --- a/x-pack/plugins/watcher/public/application/shared_imports.ts +++ b/x-pack/plugins/watcher/public/application/shared_imports.ts @@ -11,12 +11,6 @@ export type { UseRequestConfig, } from '@kbn/es-ui-shared-plugin/public'; -export { - sendRequest, - useRequest, - XJson, - PageError, - EuiCodeEditor, -} from '@kbn/es-ui-shared-plugin/public'; +export { sendRequest, useRequest, XJson, PageError } from '@kbn/es-ui-shared-plugin/public'; export { KibanaThemeProvider, useExecutionContext } from '@kbn/kibana-react-plugin/public'; diff --git a/x-pack/plugins/watcher/tsconfig.json b/x-pack/plugins/watcher/tsconfig.json index 0762abab662de..cd4305fe86779 100644 --- a/x-pack/plugins/watcher/tsconfig.json +++ b/x-pack/plugins/watcher/tsconfig.json @@ -31,7 +31,6 @@ "@kbn/datemath", "@kbn/field-formats-plugin", "@kbn/i18n-react", - "@kbn/ace", "@kbn/shared-ux-router", "@kbn/license-management-plugin", "@kbn/share-plugin", diff --git a/x-pack/test/alerting_api_integration/security_and_spaces/group1/tests/alerting/get_alert_summary.ts b/x-pack/test/alerting_api_integration/security_and_spaces/group1/tests/alerting/get_alert_summary.ts index f0ba9dc451937..7ad1a54de2485 100644 --- a/x-pack/test/alerting_api_integration/security_and_spaces/group1/tests/alerting/get_alert_summary.ts +++ b/x-pack/test/alerting_api_integration/security_and_spaces/group1/tests/alerting/get_alert_summary.ts @@ -78,6 +78,7 @@ export default function createGetAlertSummaryTests({ getService }: FtrProviderCo expect(stableBody).to.eql({ name: 'abc', tags: ['foo'], + revision: 0, rule_type_id: 'test.noop', consumer: 'alertsFixture', status: 'OK', diff --git a/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/alerting/alerts.ts b/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/alerting/alerts.ts index 7b5f41f4448d2..410fee01c71f5 100644 --- a/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/alerting/alerts.ts +++ b/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/alerting/alerts.ts @@ -1244,8 +1244,7 @@ instanceStateValue: true throttle: null, summary: true, alertsFilter: { - timeframe: null, - query: { kql: 'kibana.alert.rule.name:abc' }, + query: { kql: 'kibana.alert.rule.name:abc', filters: [] }, }, }); @@ -1307,8 +1306,7 @@ instanceStateValue: true throttle: null, summary: true, alertsFilter: { - timeframe: null, - query: { kql: 'kibana.alert.instance.id:1' }, + query: { kql: 'kibana.alert.instance.id:1', filters: [] }, }, }); @@ -1383,7 +1381,6 @@ instanceStateValue: true timezone: 'UTC', hours: { start, end }, }, - query: null, }, }); 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 fac54e789bd30..4e3a3fb70a455 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 @@ -1293,7 +1293,12 @@ export default function eventLogTests({ getService }: FtrProviderContext) { }); }); - const actionsToCheck = ['new-instance', 'active-instance', 'recovered-instance']; + const actionsToCheck = [ + 'new-instance', + 'active-instance', + 'recovered-instance', + 'execute', + ]; events.forEach((event) => { if (actionsToCheck.includes(event?.event?.action || '')) { diff --git a/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/group1/get_alert_summary.ts b/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/group1/get_alert_summary.ts index 75fd92ec61eaa..9be9db54904aa 100644 --- a/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/group1/get_alert_summary.ts +++ b/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/group1/get_alert_summary.ts @@ -68,6 +68,7 @@ export default function createGetAlertSummaryTests({ getService }: FtrProviderCo id: createdRule.id, name: 'abc', tags: ['foo'], + revision: 0, rule_type_id: 'test.noop', consumer: 'alertsFixture', status: 'OK', @@ -106,6 +107,7 @@ export default function createGetAlertSummaryTests({ getService }: FtrProviderCo id: createdRule.id, name: 'abc', tags: ['foo'], + revision: 0, rule_type_id: 'test.noop', consumer: 'alertsFixture', status: 'OK', @@ -268,6 +270,94 @@ export default function createGetAlertSummaryTests({ getService }: FtrProviderCo expect(actualAlerts).to.eql(expectedAlerts); }); + it('handles multi-alert status during maintenance window', async () => { + // pattern of when the rule should fire + const pattern = { + alertA: [true, true, true, true], + alertB: [true, true, false, false], + alertC: [true, true, true, true], + }; + + const { body: createdRule } = await supertest + .post(`${getUrlPrefix(Spaces.space1.id)}/api/alerting/rule`) + .set('kbn-xsrf', 'foo') + .send( + getTestRuleData({ + rule_type_id: 'test.patternFiring', + params: { pattern }, + schedule: { interval: '1s' }, + }) + ) + .expect(200); + + objectRemover.add(Spaces.space1.id, createdRule.id, 'rule', 'alerting'); + + const { body: createdMaintenanceWindow } = await supertest + .post(`${getUrlPrefix(Spaces.space1.id)}/internal/alerting/rules/maintenance_window`) + .set('kbn-xsrf', 'foo') + .send({ + title: 'test-maintenance-window', + duration: 60 * 60 * 1000, // 1 hr + r_rule: { + dtstart: new Date().toISOString(), + tzid: 'UTC', + freq: 2, // weekly + }, + }); + + objectRemover.add( + Spaces.space1.id, + createdMaintenanceWindow.id, + 'rules/maintenance_window', + 'alerting', + true + ); + + await alertUtils.muteInstance(createdRule.id, 'alertC'); + await alertUtils.muteInstance(createdRule.id, 'alertD'); + await waitForEvents(createdRule.id, ['new-instance', 'recovered-instance']); + const response = await supertest.get( + `${getUrlPrefix(Spaces.space1.id)}/internal/alerting/rule/${createdRule.id}/_alert_summary` + ); + + const actualAlerts = checkAndCleanActualAlerts(response.body.alerts, [ + 'alertA', + 'alertB', + 'alertC', + ]); + + const expectedAlerts = { + alertA: { + status: 'Active', + muted: false, + actionGroupId: 'default', + activeStartDate: actualAlerts.alertA.activeStartDate, + flapping: false, + maintenanceWindowIds: [createdMaintenanceWindow.id], + }, + alertB: { + status: 'OK', + muted: false, + flapping: false, + maintenanceWindowIds: [createdMaintenanceWindow.id], + }, + alertC: { + status: 'Active', + muted: true, + actionGroupId: 'default', + activeStartDate: actualAlerts.alertC.activeStartDate, + flapping: false, + maintenanceWindowIds: [createdMaintenanceWindow.id], + }, + alertD: { + status: 'OK', + muted: true, + flapping: false, + }, + }; + expect(actualAlerts).to.eql(expectedAlerts); + }); + describe('legacy', () => { it('handles multi-alert status', async () => { // pattern of when the alert should fire diff --git a/x-pack/test/api_integration/apis/synthetics/sample_data/test_browser_policy.ts b/x-pack/test/api_integration/apis/synthetics/sample_data/test_browser_policy.ts index 0cebf231cf787..eff1026cb4486 100644 --- a/x-pack/test/api_integration/apis/synthetics/sample_data/test_browser_policy.ts +++ b/x-pack/test/api_integration/apis/synthetics/sample_data/test_browser_policy.ts @@ -185,7 +185,10 @@ export const getTestBrowserSyntheticsPolicy = ({ screenshots: { value: 'on', type: 'text' }, synthetics_args: { value: null, type: 'text' }, ignore_https_errors: { value: false, type: 'bool' }, - 'throttling.config': { value: '5d/3u/20l', type: 'text' }, + 'throttling.config': { + value: JSON.stringify({ download: 5, upload: 3, latency: 20 }), + type: 'text', + }, 'filter_journeys.tags': { value: null, type: 'yaml' }, 'filter_journeys.match': { value: null, type: 'text' }, 'source.zip_url.ssl.certificate_authorities': { type: 'yaml' }, @@ -218,7 +221,7 @@ export const getTestBrowserSyntheticsPolicy = ({ enabled: true, schedule: '@every 3m', timeout: '16s', - throttling: '5d/3u/20l', + throttling: { download: 5, upload: 3, latency: 20 }, tags: ['cookie-test', 'browser'], 'source.inline.script': 'step("Visit /users api route", async () => {\\n const response = await page.goto(\'https://nextjs-test-synthetics.vercel.app/api/users\');\\n expect(response.status()).toEqual(200);\\n});', diff --git a/x-pack/test/api_integration/apis/synthetics/sample_data/test_project_monitor_policy.ts b/x-pack/test/api_integration/apis/synthetics/sample_data/test_project_monitor_policy.ts index cd5ea22451b92..ba48a26ff50fe 100644 --- a/x-pack/test/api_integration/apis/synthetics/sample_data/test_project_monitor_policy.ts +++ b/x-pack/test/api_integration/apis/synthetics/sample_data/test_project_monitor_policy.ts @@ -213,7 +213,10 @@ export const getTestProjectSyntheticsPolicy = ( screenshots: { value: 'on', type: 'text' }, synthetics_args: { value: null, type: 'text' }, ignore_https_errors: { value: false, type: 'bool' }, - 'throttling.config': { value: '5d/3u/20l', type: 'text' }, + 'throttling.config': { + value: JSON.stringify({ download: 5, upload: 3, latency: 20 }), + type: 'text', + }, 'filter_journeys.tags': { value: null, type: 'yaml' }, 'filter_journeys.match': { value: '"check if title is present"', type: 'text' }, 'source.zip_url.ssl.certificate_authorities': { type: 'yaml' }, @@ -246,7 +249,7 @@ export const getTestProjectSyntheticsPolicy = ( 'run_from.geo.name': locationName, 'run_from.id': locationName, timeout: null, - throttling: '5d/3u/20l', + throttling: { download: 5, upload: 3, latency: 20 }, 'source.project.content': 'UEsDBBQACAAIAON5qVQAAAAAAAAAAAAAAAAfAAAAZXhhbXBsZXMvdG9kb3MvYmFzaWMuam91cm5leS50c22Q0WrDMAxF3/sVF7MHB0LMXlc6RvcN+wDPVWNviW0sdUsp/fe5SSiD7UFCWFfHujIGlpnkybwxFTZfoY/E3hsaLEtwhs9RPNWKDU12zAOxkXRIbN4tB9d9pFOJdO6EN2HMqQguWN9asFBuQVMmJ7jiWNII9fIXrbabdUYr58l9IhwhQQZCYORCTFFUC31Btj21NRc7Mq4Nds+4bDD/pNVgT9F52Jyr2Fa+g75LAPttg8yErk+S9ELpTmVotlVwnfNCuh2lepl3+JflUmSBJ3uggt1v9INW/lHNLKze9dJe1J3QJK8pSvWkm6aTtCet5puq+x63+AFQSwcIAPQ3VfcAAACcAQAAUEsBAi0DFAAIAAgA43mpVAD0N1X3AAAAnAEAAB8AAAAAAAAAAAAgAKSBAAAAAGV4YW1wbGVzL3RvZG9zL2Jhc2ljLmpvdXJuZXkudHNQSwUGAAAAAAEAAQBNAAAARAEAAAAA', playwright_options: { headless: true, chromiumSandbox: false }, diff --git a/x-pack/test/apm_api_integration/tests/alerts/chart_preview.spec.ts b/x-pack/test/apm_api_integration/tests/alerts/chart_preview.spec.ts index f070098a66661..7ec09849b7ff2 100644 --- a/x-pack/test/apm_api_integration/tests/alerts/chart_preview.spec.ts +++ b/x-pack/test/apm_api_integration/tests/alerts/chart_preview.spec.ts @@ -100,6 +100,33 @@ export default function ApiTest({ getService }: FtrProviderContext) { ).to.equal(true); }); + it('error_count with error grouping key', async () => { + const options = { + params: { + query: { + start, + end, + serviceName: 'opbeans-java', + errorGroupingKey: 'd16d39e7fa133b8943cea035430a7b4e', + environment: 'ENVIRONMENT_ALL', + interval: '5m', + }, + }, + }; + + const response = await apmApiClient.readUser({ + endpoint: 'GET /internal/apm/rule_types/error_count/chart_preview', + ...options, + }); + + expect(response.status).to.be(200); + expect(response.body.errorCountChartPreview).to.eql([ + { x: 1627974600000, y: 4 }, + { x: 1627974900000, y: 2 }, + { x: 1627975200000, y: 0 }, + ]); + }); + it('transaction_duration (with data)', async () => { const options = getOptions(); const response = await apmApiClient.readUser({ diff --git a/x-pack/test/apm_api_integration/tests/errors/group_id_samples.spec.ts b/x-pack/test/apm_api_integration/tests/errors/group_id_samples.spec.ts index 0ea2ed4f57493..14d5ba72275ea 100644 --- a/x-pack/test/apm_api_integration/tests/errors/group_id_samples.spec.ts +++ b/x-pack/test/apm_api_integration/tests/errors/group_id_samples.spec.ts @@ -5,14 +5,11 @@ * 2.0. */ import expect from '@kbn/expect'; -import { - APIClientRequestParamsOf, - APIReturnType, -} from '@kbn/apm-plugin/public/services/rest/create_call_apm_api'; -import { RecursivePartial } from '@kbn/apm-plugin/typings/common'; import { timerange } from '@kbn/apm-synthtrace-client'; import { service } from '@kbn/apm-synthtrace-client/src/lib/apm/service'; import { orderBy } from 'lodash'; +import { APIReturnType } from '@kbn/apm-plugin/public/services/rest/create_call_apm_api'; +import { getErrorGroupingKey } from '@kbn/apm-synthtrace-client/src/lib/apm/instance'; import { FtrProviderContext } from '../../common/ftr_provider_context'; import { config, generateData } from './generate_data'; @@ -31,25 +28,19 @@ export default function ApiTest({ getService }: FtrProviderContext) { const start = new Date('2021-01-01T00:00:00.000Z').getTime(); const end = new Date('2021-01-01T00:15:00.000Z').getTime() - 1; - async function callErrorGroupSamplesApi( - overrides?: RecursivePartial< - APIClientRequestParamsOf<'GET /internal/apm/services/{serviceName}/errors/{groupId}/samples'>['params'] - > - ) { + async function callErrorGroupSamplesApi({ groupId }: { groupId: string }) { const response = await apmApiClient.readUser({ endpoint: 'GET /internal/apm/services/{serviceName}/errors/{groupId}/samples', params: { path: { serviceName, - groupId: 'foo', - ...overrides?.path, + groupId, }, query: { start: new Date(start).toISOString(), end: new Date(end).toISOString(), environment: 'ENVIRONMENT_ALL', kuery: '', - ...overrides?.query, }, }, }); @@ -78,7 +69,7 @@ export default function ApiTest({ getService }: FtrProviderContext) { registry.when('when data is not loaded', { config: 'basic', archives: [] }, () => { it('handles the empty state', async () => { - const response = await callErrorGroupSamplesApi(); + const response = await callErrorGroupSamplesApi({ groupId: 'foo' }); expect(response.status).to.be(200); expect(response.body.occurrencesCount).to.be(0); }); @@ -97,7 +88,7 @@ export default function ApiTest({ getService }: FtrProviderContext) { let errorsSamplesResponse: ErrorGroupSamples; before(async () => { const response = await callErrorGroupSamplesApi({ - path: { groupId: '0000000000000000000000000Error 1' }, + groupId: '98b75903135eac35ad42419bd3b45cf8b4270c61cbd0ede0f7e8c8a9ac9fdb03', }); errorsSamplesResponse = response.body; }); @@ -124,7 +115,7 @@ export default function ApiTest({ getService }: FtrProviderContext) { let errorSampleDetailsResponse: ErrorSampleDetails; before(async () => { const errorsSamplesResponse = await callErrorGroupSamplesApi({ - path: { groupId: '0000000000000000000000000Error 1' }, + groupId: '98b75903135eac35ad42419bd3b45cf8b4270c61cbd0ede0f7e8c8a9ac9fdb03', }); const errorId = errorsSamplesResponse.body.errorSampleIds[0]; @@ -133,10 +124,13 @@ export default function ApiTest({ getService }: FtrProviderContext) { errorSampleDetailsResponse = response.body; }); - it('displays correct error sample data', () => { + it('displays correct error grouping_key', () => { expect(errorSampleDetailsResponse.error.error.grouping_key).to.equal( - '0000000000000000000000000Error 1' + '98b75903135eac35ad42419bd3b45cf8b4270c61cbd0ede0f7e8c8a9ac9fdb03' ); + }); + + it('displays correct error message', () => { expect(errorSampleDetailsResponse.error.error.exception?.[0].message).to.equal('Error 1'); }); }); @@ -147,6 +141,8 @@ export default function ApiTest({ getService }: FtrProviderContext) { before(async () => { const instance = service(serviceName, 'production', 'go').instance('a'); + const errorMessage = 'Error 1'; + const groupId = getErrorGroupingKey(errorMessage); await synthtraceEsClient.index([ timerange(start, end) @@ -160,24 +156,20 @@ export default function ApiTest({ getService }: FtrProviderContext) { .timestamp(timestamp) .sample(false) .errors( - instance.error({ message: `Error 1` }).timestamp(timestamp), - instance.error({ message: `Error 1` }).timestamp(timestamp + 1) + instance.error({ message: errorMessage }).timestamp(timestamp), + instance.error({ message: errorMessage }).timestamp(timestamp + 1) ), instance .transaction('GET /api/foo') .duration(100) .timestamp(timestamp) .sample(true) - .errors(instance.error({ message: `Error 1` }).timestamp(timestamp)), + .errors(instance.error({ message: errorMessage }).timestamp(timestamp)), ]; }), ]); - errorGroupSamplesResponse = ( - await callErrorGroupSamplesApi({ - path: { groupId: '0000000000000000000000000Error 1' }, - }) - ).body; + errorGroupSamplesResponse = (await callErrorGroupSamplesApi({ groupId })).body; }); after(() => synthtraceEsClient.clean()); @@ -187,6 +179,7 @@ export default function ApiTest({ getService }: FtrProviderContext) { // this checks whether the order of indexing is different from the order that is returned // if it is not, scoring/sorting is broken + expect(errorGroupSamplesResponse.errorSampleIds.length).to.be(3); expect(idsOfErrors).to.not.eql(orderBy(idsOfErrors)); }); }); diff --git a/x-pack/test/apm_api_integration/tests/errors/top_erroneous_transactions/generate_data.ts b/x-pack/test/apm_api_integration/tests/errors/top_erroneous_transactions/generate_data.ts index 3229eaba383c0..47c48693eaa0e 100644 --- a/x-pack/test/apm_api_integration/tests/errors/top_erroneous_transactions/generate_data.ts +++ b/x-pack/test/apm_api_integration/tests/errors/top_erroneous_transactions/generate_data.ts @@ -59,7 +59,7 @@ export async function generateData({ .transaction({ transactionName: transaction.name }) .errors( serviceGoProdInstance - .error({ message: 'Error 1', type: transaction.name, groupingName: 'Error test' }) + .error({ message: 'Error 1', type: transaction.name }) .timestamp(timestamp) ) .duration(1000) diff --git a/x-pack/test/apm_api_integration/tests/errors/top_erroneous_transactions/top_erroneous_transactions.spec.ts b/x-pack/test/apm_api_integration/tests/errors/top_erroneous_transactions/top_erroneous_transactions.spec.ts index cd01ca49afcc5..d091fb9baf0e6 100644 --- a/x-pack/test/apm_api_integration/tests/errors/top_erroneous_transactions/top_erroneous_transactions.spec.ts +++ b/x-pack/test/apm_api_integration/tests/errors/top_erroneous_transactions/top_erroneous_transactions.spec.ts @@ -26,6 +26,7 @@ export default function ApiTest({ getService }: FtrProviderContext) { const serviceName = 'synth-go'; const start = new Date('2021-01-01T00:00:00.000Z').getTime(); const end = new Date('2021-01-01T00:15:00.000Z').getTime() - 1; + const groupId = '98b75903135eac35ad42419bd3b45cf8b4270c61cbd0ede0f7e8c8a9ac9fdb03'; async function callApi( overrides?: RecursivePartial< @@ -82,7 +83,7 @@ export default function ApiTest({ getService }: FtrProviderContext) { before(async () => { const response = await callApi({ - path: { groupId: '0000000000000000000000Error test' }, + path: { groupId }, }); erroneousTransactions = response.body; }); @@ -132,7 +133,7 @@ export default function ApiTest({ getService }: FtrProviderContext) { before(async () => { const fiveMinutes = 5 * 60 * 1000; const response = await callApi({ - path: { groupId: '0000000000000000000000Error test' }, + path: { groupId }, query: { start: new Date(end - fiveMinutes).toISOString(), end: new Date(end).toISOString(), @@ -182,7 +183,7 @@ export default function ApiTest({ getService }: FtrProviderContext) { describe('when there are no data for the time period', () => { it('returns an empty array', async () => { const response = await callApi({ - path: { groupId: '0000000000000000000000Error test' }, + path: { groupId }, query: { start: '2021-01-03T00:00:00.000Z', end: '2021-01-03T00:15:00.000Z', diff --git a/x-pack/test/apm_api_integration/tests/errors/top_errors_for_transaction/top_errors_main_stats.spec.ts b/x-pack/test/apm_api_integration/tests/errors/top_errors_for_transaction/top_errors_main_stats.spec.ts index 830e5032a8de7..d7ae081a035e8 100644 --- a/x-pack/test/apm_api_integration/tests/errors/top_errors_for_transaction/top_errors_main_stats.spec.ts +++ b/x-pack/test/apm_api_integration/tests/errors/top_errors_for_transaction/top_errors_main_stats.spec.ts @@ -71,31 +71,34 @@ export default function ApiTest({ getService }: FtrProviderContext) { after(() => synthtraceEsClient.clean()); describe('returns the correct data', () => { + const NUMBER_OF_BUCKETS = 15; let errorGroups: ErrorGroups; before(async () => { const response = await callApi({ query: { transactionName: firstTransactionName } }); errorGroups = response.body.errorGroups; }); - it('returns correct number of errors and error data', () => { - const numberOfBuckets = 15; - + it('returns correct number of errors', () => { expect(errorGroups.length).to.equal(2); + }); - const firstErrorId = `Error 1 transaction ${firstTransactionName}`; + it('error 1 is correct', () => { + const firstErrorId = `b6c1d4d41b0b60b841f40232497344ba36856fcbea0692a4695562ca73e790bd`; const firstError = errorGroups.find((x) => x.groupId === firstErrorId); expect(firstError).to.not.be(undefined); expect(firstError?.groupId).to.be(firstErrorId); - expect(firstError?.name).to.be(firstErrorId); - expect(firstError?.occurrences).to.be(firstTransactionFailureRate * numberOfBuckets); + expect(firstError?.name).to.be(`Error 1 transaction GET /apple 🍎`); + expect(firstError?.occurrences).to.be(firstTransactionFailureRate * NUMBER_OF_BUCKETS); expect(firstError?.lastSeen).to.be(moment(end).startOf('minute').valueOf()); + }); - const secondErrorId = `Error 2 transaction ${firstTransactionName}`; + it('error 2 is correct', () => { + const secondErrorId = `c3f388e4f7276d4fab85aa2fad2d2a42e70637f65cd5ec9f085de28b36e69ba5`; const secondError = errorGroups.find((x) => x.groupId === secondErrorId); expect(secondError).to.not.be(undefined); expect(secondError?.groupId).to.be(secondErrorId); - expect(secondError?.name).to.be(secondErrorId); - expect(secondError?.occurrences).to.be(firstTransactionFailureRate * numberOfBuckets); + expect(secondError?.name).to.be(`Error 2 transaction GET /apple 🍎`); + expect(secondError?.occurrences).to.be(firstTransactionFailureRate * NUMBER_OF_BUCKETS); expect(secondError?.lastSeen).to.be(moment(end).startOf('minute').valueOf()); }); }); diff --git a/x-pack/test/cloud_security_posture_functional/page_objects/csp_dashboard_page.ts b/x-pack/test/cloud_security_posture_functional/page_objects/csp_dashboard_page.ts new file mode 100644 index 0000000000000..635cf88965e5f --- /dev/null +++ b/x-pack/test/cloud_security_posture_functional/page_objects/csp_dashboard_page.ts @@ -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. + */ + +import expect from '@kbn/expect'; +import type { FtrProviderContext } from '../ftr_provider_context'; + +// Defined in CSP plugin +const LATEST_FINDINGS_INDEX = 'logs-cloud_security_posture.findings_latest-default'; + +export function CspDashboardPageProvider({ getService, getPageObjects }: FtrProviderContext) { + const testSubjects = getService('testSubjects'); + const PageObjects = getPageObjects(['common']); + const retry = getService('retry'); + const es = getService('es'); + const supertest = getService('supertest'); + const log = getService('log'); + + /** + * required before indexing findings + */ + const waitForPluginInitialized = (): Promise => + retry.try(async () => { + log.debug('Check CSP plugin is initialized'); + const response = await supertest + .get('/internal/cloud_security_posture/status?check=init') + .expect(200); + expect(response.body).to.eql({ isPluginInitialized: true }); + log.debug('CSP plugin is initialized'); + }); + + const index = { + remove: () => es.indices.delete({ index: LATEST_FINDINGS_INDEX, ignore_unavailable: true }), + add: async (findingsMock: T[]) => { + await Promise.all( + findingsMock.map((finding) => + es.index({ + index: LATEST_FINDINGS_INDEX, + body: finding, + }) + ) + ); + }, + }; + + const dashboard = { + getDashboardPageHeader: () => testSubjects.find('cloud-posture-dashboard-page-header'), + + getDashboardTabs: async () => { + const dashboardPageHeader = await dashboard.getDashboardPageHeader(); + return await dashboardPageHeader.findByClassName('euiTabs'); + }, + + getCloudTab: async () => { + const tabs = await dashboard.getDashboardTabs(); + return await tabs.findByXpath(`//span[text()="Cloud"]`); + }, + + getKubernetesTab: async () => { + const tabs = await dashboard.getDashboardTabs(); + return await tabs.findByXpath(`//span[text()="Kubernetes"]`); + }, + + clickTab: async (tab: 'Cloud' | 'Kubernetes') => { + if (tab === 'Cloud') { + const cloudTab = await dashboard.getCloudTab(); + await cloudTab.click(); + } + if (tab === 'Kubernetes') { + const k8sTab = await dashboard.getKubernetesTab(); + await k8sTab.click(); + } + }, + + getIntegrationDashboardContainer: () => testSubjects.find('dashboard-container'), + + // Cloud Dashboard + + getCloudDashboard: async () => { + await dashboard.clickTab('Cloud'); + return await testSubjects.find('cloud-dashboard-container'); + }, + + getCloudSummarySection: async () => { + await dashboard.getCloudDashboard(); + return await testSubjects.find('dashboard-summary-section'); + }, + + getCloudComplianceScore: async () => { + await dashboard.getCloudSummarySection(); + return await testSubjects.find('dashboard-summary-section-compliance-score'); + }, + + // Kubernetes Dashboard + + getKubernetesDashboard: async () => { + await dashboard.clickTab('Kubernetes'); + return await testSubjects.find('kubernetes-dashboard-container'); + }, + + getKubernetesSummarySection: async () => { + await dashboard.getKubernetesDashboard(); + return await testSubjects.find('dashboard-summary-section'); + }, + + getKubernetesComplianceScore: async () => { + await dashboard.getKubernetesSummarySection(); + return await testSubjects.find('dashboard-summary-section-compliance-score'); + }, + + getKubernetesComplianceScore2: async () => { + // await dashboard.getKubernetesSummarySection(); + return await testSubjects.find('dashboard-summary-section-compliance-score'); + }, + }; + + const navigateToComplianceDashboardPage = async () => { + await PageObjects.common.navigateToUrl( + 'securitySolution', // Defined in Security Solution plugin + 'cloud_security_posture/dashboard', + { shouldUseHashForSubUrl: false } + ); + }; + + return { + waitForPluginInitialized, + navigateToComplianceDashboardPage, + dashboard, + index, + }; +} diff --git a/x-pack/test/cloud_security_posture_functional/page_objects/index.ts b/x-pack/test/cloud_security_posture_functional/page_objects/index.ts index e5738873edc51..26aacd8cca997 100644 --- a/x-pack/test/cloud_security_posture_functional/page_objects/index.ts +++ b/x-pack/test/cloud_security_posture_functional/page_objects/index.ts @@ -7,8 +7,10 @@ import { pageObjects as xpackFunctionalPageObjects } from '../../functional/page_objects'; import { FindingsPageProvider } from './findings_page'; +import { CspDashboardPageProvider } from './csp_dashboard_page'; export const pageObjects = { ...xpackFunctionalPageObjects, findings: FindingsPageProvider, + cloudPostureDashboard: CspDashboardPageProvider, }; diff --git a/x-pack/test/cloud_security_posture_functional/pages/compliance_dashboard.ts b/x-pack/test/cloud_security_posture_functional/pages/compliance_dashboard.ts new file mode 100644 index 0000000000000..cb4635899f5c3 --- /dev/null +++ b/x-pack/test/cloud_security_posture_functional/pages/compliance_dashboard.ts @@ -0,0 +1,64 @@ +/* + * 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 Chance from 'chance'; +import type { FtrProviderContext } from '../ftr_provider_context'; + +// eslint-disable-next-line import/no-default-export +export default function ({ getPageObjects, getService }: FtrProviderContext) { + const retry = getService('retry'); + const pageObjects = getPageObjects(['common', 'cloudPostureDashboard']); + const chance = new Chance(); + + const data = [ + { + '@timestamp': new Date().toISOString(), + resource: { id: chance.guid(), name: `kubelet`, sub_type: 'lower case sub type' }, + result: { evaluation: 'failed' }, + rule: { + name: 'Upper case rule name', + section: 'Upper case section', + benchmark: { + id: 'cis_k8s', + posture_type: 'kspm', + }, + }, + cluster_id: 'Upper case cluster id', + }, + ]; + + describe('Cloud Posture Dashboard Page', () => { + let cspDashboard: typeof pageObjects.cloudPostureDashboard; + let dashboard: typeof pageObjects.cloudPostureDashboard.dashboard; + + before(async () => { + cspDashboard = pageObjects.cloudPostureDashboard; + dashboard = pageObjects.cloudPostureDashboard.dashboard; + await cspDashboard.waitForPluginInitialized(); + + await cspDashboard.index.add(data); + await cspDashboard.navigateToComplianceDashboardPage(); + await retry.waitFor( + 'Cloud posture integration dashboard to be displayed', + async () => !!dashboard.getIntegrationDashboardContainer() + ); + }); + + after(async () => { + await cspDashboard.index.remove(); + }); + + describe('Kubernetes Dashboard', () => { + it('displays accurate summary compliance score', async () => { + const scoreElement = await dashboard.getKubernetesComplianceScore(); + + expect((await scoreElement.getVisibleText()) === '0%').to.be(true); + }); + }); + }); +} diff --git a/x-pack/test/cloud_security_posture_functional/pages/index.ts b/x-pack/test/cloud_security_posture_functional/pages/index.ts index 80e96b8b17ce9..7566afda0501a 100644 --- a/x-pack/test/cloud_security_posture_functional/pages/index.ts +++ b/x-pack/test/cloud_security_posture_functional/pages/index.ts @@ -11,5 +11,6 @@ import { FtrProviderContext } from '../ftr_provider_context'; export default function ({ loadTestFile }: FtrProviderContext) { describe('Cloud Security Posture', function () { loadTestFile(require.resolve('./findings')); + loadTestFile(require.resolve('./compliance_dashboard')); }); } diff --git a/x-pack/test/detection_engine_api_integration/basic/tests/create_rules.ts b/x-pack/test/detection_engine_api_integration/basic/tests/create_rules.ts index 62fa4d3786db6..2c6ae644d911c 100644 --- a/x-pack/test/detection_engine_api_integration/basic/tests/create_rules.ts +++ b/x-pack/test/detection_engine_api_integration/basic/tests/create_rules.ts @@ -99,7 +99,6 @@ export default ({ getService }: FtrProviderContext) => { to: 'now', type: 'query', threat: [], - throttle: 'no_actions', exceptions_list: [], version: 1, revision: 0, diff --git a/x-pack/test/detection_engine_api_integration/security_and_spaces/group1/create_rules.ts b/x-pack/test/detection_engine_api_integration/security_and_spaces/group1/create_rules.ts index 4346087684fd4..e6adaa069b497 100644 --- a/x-pack/test/detection_engine_api_integration/security_and_spaces/group1/create_rules.ts +++ b/x-pack/test/detection_engine_api_integration/security_and_spaces/group1/create_rules.ts @@ -7,7 +7,12 @@ import expect from '@kbn/expect'; -import { DETECTION_ENGINE_RULES_URL } from '@kbn/security-solution-plugin/common/constants'; +import { + DETECTION_ENGINE_RULES_URL, + NOTIFICATION_DEFAULT_FREQUENCY, + NOTIFICATION_THROTTLE_NO_ACTIONS, + NOTIFICATION_THROTTLE_RULE, +} from '@kbn/security-solution-plugin/common/constants'; import { RuleCreateProps } from '@kbn/security-solution-plugin/common/detection_engine/rule_schema'; import { ExceptionListTypeEnum } from '@kbn/securitysolution-io-ts-list-types'; import { ROLES } from '@kbn/security-solution-plugin/common/test'; @@ -32,8 +37,15 @@ import { waitForSignalsToBePresent, getThresholdRuleForSignalTesting, waitForRulePartialFailure, + createRule, } from '../../utils'; import { createUserAndRole, deleteUserAndRole } from '../../../common/services/security_solution'; +import { + getActionsWithFrequencies, + getActionsWithoutFrequencies, + getSomeActionsWithFrequencies, +} from '../../utils/get_rule_actions'; +import { removeUUIDFromActions } from '../../utils/remove_uuid_from_actions'; // eslint-disable-next-line import/no-default-export export default ({ getService }: FtrProviderContext) => { @@ -200,7 +212,6 @@ export default ({ getService }: FtrProviderContext) => { to: 'now', type: 'query', threat: [], - throttle: 'no_actions', exceptions_list: [], version: 1, }; @@ -566,5 +577,139 @@ export default ({ getService }: FtrProviderContext) => { expect(rule?.execution_summary?.last_execution.status).to.eql('partial failure'); }); }); + + describe('per-action frequencies', () => { + const createSingleRule = async (rule: RuleCreateProps) => { + const createdRule = await createRule(supertest, log, rule); + createdRule.actions = removeUUIDFromActions(createdRule.actions); + return createdRule; + }; + + describe('actions without frequencies', () => { + [undefined, NOTIFICATION_THROTTLE_NO_ACTIONS, NOTIFICATION_THROTTLE_RULE].forEach( + (throttle) => { + it(`it sets each action's frequency attribute to default value when 'throttle' is ${throttle}`, async () => { + const actionsWithoutFrequencies = await getActionsWithoutFrequencies(supertest); + + const simpleRule = getSimpleRule(); + simpleRule.throttle = throttle; + simpleRule.actions = actionsWithoutFrequencies; + + const createdRule = await createSingleRule(simpleRule); + + const expectedRule = getSimpleRuleOutput(); + expectedRule.actions = actionsWithoutFrequencies.map((action) => ({ + ...action, + frequency: NOTIFICATION_DEFAULT_FREQUENCY, + })); + + const rule = removeServerGeneratedProperties(createdRule); + expect(rule).to.eql(expectedRule); + }); + } + ); + + // Action throttle cannot be shorter than the schedule interval which is by default is 5m + ['300s', '5m', '3h', '4d'].forEach((throttle) => { + it(`it correctly transforms 'throttle = ${throttle}' and sets it as a frequency of each action`, async () => { + const actionsWithoutFrequencies = await getActionsWithoutFrequencies(supertest); + + const simpleRule = getSimpleRule(); + simpleRule.throttle = throttle; + simpleRule.actions = actionsWithoutFrequencies; + + const createdRule = await createSingleRule(simpleRule); + + const expectedRule = getSimpleRuleOutput(); + expectedRule.actions = actionsWithoutFrequencies.map((action) => ({ + ...action, + frequency: { summary: true, throttle, notifyWhen: 'onThrottleInterval' }, + })); + + const rule = removeServerGeneratedProperties(createdRule); + expect(rule).to.eql(expectedRule); + }); + }); + }); + + describe('actions with frequencies', () => { + [ + undefined, + NOTIFICATION_THROTTLE_NO_ACTIONS, + NOTIFICATION_THROTTLE_RULE, + '321s', + '6m', + '10h', + '2d', + ].forEach((throttle) => { + it(`it does not change actions frequency attributes when 'throttle' is '${throttle}'`, async () => { + const actionsWithFrequencies = await getActionsWithFrequencies(supertest); + + const simpleRule = getSimpleRule(); + simpleRule.throttle = throttle; + simpleRule.actions = actionsWithFrequencies; + + const createdRule = await createSingleRule(simpleRule); + + const expectedRule = getSimpleRuleOutput(); + expectedRule.actions = actionsWithFrequencies; + + const rule = removeServerGeneratedProperties(createdRule); + expect(rule).to.eql(expectedRule); + }); + }); + }); + + describe('some actions with frequencies', () => { + [undefined, NOTIFICATION_THROTTLE_NO_ACTIONS, NOTIFICATION_THROTTLE_RULE].forEach( + (throttle) => { + it(`it overrides each action's frequency attribute to default value when 'throttle' is ${throttle}`, async () => { + const someActionsWithFrequencies = await getSomeActionsWithFrequencies(supertest); + + const simpleRule = getSimpleRule(); + simpleRule.throttle = throttle; + simpleRule.actions = someActionsWithFrequencies; + + const createdRule = await createSingleRule(simpleRule); + + const expectedRule = getSimpleRuleOutput(); + expectedRule.actions = someActionsWithFrequencies.map((action) => ({ + ...action, + frequency: action.frequency ?? NOTIFICATION_DEFAULT_FREQUENCY, + })); + + const rule = removeServerGeneratedProperties(createdRule); + expect(rule).to.eql(expectedRule); + }); + } + ); + + // Action throttle cannot be shorter than the schedule interval which is by default is 5m + ['430s', '7m', '1h', '8d'].forEach((throttle) => { + it(`it correctly transforms 'throttle = ${throttle}' and overrides frequency attribute of each action`, async () => { + const someActionsWithFrequencies = await getSomeActionsWithFrequencies(supertest); + + const simpleRule = getSimpleRule(); + simpleRule.throttle = throttle; + simpleRule.actions = someActionsWithFrequencies; + + const createdRule = await createSingleRule(simpleRule); + + const expectedRule = getSimpleRuleOutput(); + expectedRule.actions = someActionsWithFrequencies.map((action) => ({ + ...action, + frequency: action.frequency ?? { + summary: true, + throttle, + notifyWhen: 'onThrottleInterval', + }, + })); + + const rule = removeServerGeneratedProperties(createdRule); + expect(rule).to.eql(expectedRule); + }); + }); + }); + }); }); }; diff --git a/x-pack/test/detection_engine_api_integration/security_and_spaces/group1/create_rules_bulk.ts b/x-pack/test/detection_engine_api_integration/security_and_spaces/group1/create_rules_bulk.ts index 6d0e79975bfd6..63d7e18367dc6 100644 --- a/x-pack/test/detection_engine_api_integration/security_and_spaces/group1/create_rules_bulk.ts +++ b/x-pack/test/detection_engine_api_integration/security_and_spaces/group1/create_rules_bulk.ts @@ -10,7 +10,11 @@ import expect from '@kbn/expect'; import { DETECTION_ENGINE_RULES_BULK_CREATE, DETECTION_ENGINE_RULES_URL, + NOTIFICATION_DEFAULT_FREQUENCY, + NOTIFICATION_THROTTLE_NO_ACTIONS, + NOTIFICATION_THROTTLE_RULE, } from '@kbn/security-solution-plugin/common/constants'; +import { RuleCreateProps } from '@kbn/security-solution-plugin/common/detection_engine/rule_schema'; import { ExceptionListTypeEnum } from '@kbn/securitysolution-io-ts-list-types'; import { FtrProviderContext } from '../../common/ftr_provider_context'; @@ -27,6 +31,12 @@ import { removeServerGeneratedPropertiesIncludingRuleId, waitForRuleSuccess, } from '../../utils'; +import { + getActionsWithFrequencies, + getActionsWithoutFrequencies, + getSomeActionsWithFrequencies, +} from '../../utils/get_rule_actions'; +import { removeUUIDFromActions } from '../../utils/remove_uuid_from_actions'; // eslint-disable-next-line import/no-default-export export default ({ getService }: FtrProviderContext): void => { @@ -276,6 +286,141 @@ export default ({ getService }: FtrProviderContext): void => { }, ]); }); + + describe('per-action frequencies', () => { + const bulkCreateSingleRule = async (rule: RuleCreateProps) => { + const { body } = await supertest + .post(DETECTION_ENGINE_RULES_BULK_CREATE) + .set('kbn-xsrf', 'true') + .send([rule]) + .expect(200); + + const createdRule = body[0]; + createdRule.actions = removeUUIDFromActions(createdRule.actions); + return removeServerGeneratedPropertiesIncludingRuleId(createdRule); + }; + + describe('actions without frequencies', () => { + [undefined, NOTIFICATION_THROTTLE_NO_ACTIONS, NOTIFICATION_THROTTLE_RULE].forEach( + (throttle) => { + it(`it sets each action's frequency attribute to default value when 'throttle' is ${throttle}`, async () => { + const actionsWithoutFrequencies = await getActionsWithoutFrequencies(supertest); + + const simpleRule = getSimpleRuleWithoutRuleId(); + simpleRule.throttle = throttle; + simpleRule.actions = actionsWithoutFrequencies; + + const createdRule = await bulkCreateSingleRule(simpleRule); + + const expectedRule = getSimpleRuleOutputWithoutRuleId(); + expectedRule.actions = actionsWithoutFrequencies.map((action) => ({ + ...action, + frequency: NOTIFICATION_DEFAULT_FREQUENCY, + })); + + expect(createdRule).to.eql(expectedRule); + }); + } + ); + + // Action throttle cannot be shorter than the schedule interval which is by default is 5m + ['300s', '5m', '3h', '4d'].forEach((throttle) => { + it(`it correctly transforms 'throttle = ${throttle}' and sets it as a frequency of each action`, async () => { + const actionsWithoutFrequencies = await getActionsWithoutFrequencies(supertest); + + const simpleRule = getSimpleRuleWithoutRuleId(); + simpleRule.throttle = throttle; + simpleRule.actions = actionsWithoutFrequencies; + + const createdRule = await bulkCreateSingleRule(simpleRule); + + const expectedRule = getSimpleRuleOutputWithoutRuleId(); + expectedRule.actions = actionsWithoutFrequencies.map((action) => ({ + ...action, + frequency: { summary: true, throttle, notifyWhen: 'onThrottleInterval' }, + })); + + expect(createdRule).to.eql(expectedRule); + }); + }); + }); + + describe('actions with frequencies', () => { + [ + undefined, + NOTIFICATION_THROTTLE_NO_ACTIONS, + NOTIFICATION_THROTTLE_RULE, + '321s', + '6m', + '10h', + '2d', + ].forEach((throttle) => { + it(`it does not change actions frequency attributes when 'throttle' is '${throttle}'`, async () => { + const actionsWithFrequencies = await getActionsWithFrequencies(supertest); + + const simpleRule = getSimpleRuleWithoutRuleId(); + simpleRule.throttle = throttle; + simpleRule.actions = actionsWithFrequencies; + + const createdRule = await bulkCreateSingleRule(simpleRule); + + const expectedRule = getSimpleRuleOutputWithoutRuleId(); + expectedRule.actions = actionsWithFrequencies; + + expect(createdRule).to.eql(expectedRule); + }); + }); + }); + + describe('some actions with frequencies', () => { + [undefined, NOTIFICATION_THROTTLE_NO_ACTIONS, NOTIFICATION_THROTTLE_RULE].forEach( + (throttle) => { + it(`it overrides each action's frequency attribute to default value when 'throttle' is ${throttle}`, async () => { + const someActionsWithFrequencies = await getSomeActionsWithFrequencies(supertest); + + const simpleRule = getSimpleRuleWithoutRuleId(); + simpleRule.throttle = throttle; + simpleRule.actions = someActionsWithFrequencies; + + const createdRule = await bulkCreateSingleRule(simpleRule); + + const expectedRule = getSimpleRuleOutputWithoutRuleId(); + expectedRule.actions = someActionsWithFrequencies.map((action) => ({ + ...action, + frequency: action.frequency ?? NOTIFICATION_DEFAULT_FREQUENCY, + })); + + expect(createdRule).to.eql(expectedRule); + }); + } + ); + + // Action throttle cannot be shorter than the schedule interval which is by default is 5m + ['430s', '7m', '1h', '8d'].forEach((throttle) => { + it(`it correctly transforms 'throttle = ${throttle}' and overrides frequency attribute of each action`, async () => { + const someActionsWithFrequencies = await getSomeActionsWithFrequencies(supertest); + + const simpleRule = getSimpleRuleWithoutRuleId(); + simpleRule.throttle = throttle; + simpleRule.actions = someActionsWithFrequencies; + + const createdRule = await bulkCreateSingleRule(simpleRule); + + const expectedRule = getSimpleRuleOutputWithoutRuleId(); + expectedRule.actions = someActionsWithFrequencies.map((action) => ({ + ...action, + frequency: action.frequency ?? { + summary: true, + throttle, + notifyWhen: 'onThrottleInterval', + }, + })); + + expect(createdRule).to.eql(expectedRule); + }); + }); + }); + }); }); }); }; diff --git a/x-pack/test/detection_engine_api_integration/security_and_spaces/group1/delete_rules.ts b/x-pack/test/detection_engine_api_integration/security_and_spaces/group1/delete_rules.ts index a394bef16cd22..e029c13aff8e5 100644 --- a/x-pack/test/detection_engine_api_integration/security_and_spaces/group1/delete_rules.ts +++ b/x-pack/test/detection_engine_api_integration/security_and_spaces/group1/delete_rules.ts @@ -176,6 +176,7 @@ export default ({ getService }: FtrProviderContext): void => { message: 'Hourly\nRule {{context.rule.name}} generated {{state.signals_count}} alerts', }, + frequency: { summary: true, throttle: '1h', notifyWhen: 'onThrottleInterval' }, }, ]); }); diff --git a/x-pack/test/detection_engine_api_integration/security_and_spaces/group1/delete_rules_bulk.ts b/x-pack/test/detection_engine_api_integration/security_and_spaces/group1/delete_rules_bulk.ts index fa83ceb9a19b1..8e0ad07a782c3 100644 --- a/x-pack/test/detection_engine_api_integration/security_and_spaces/group1/delete_rules_bulk.ts +++ b/x-pack/test/detection_engine_api_integration/security_and_spaces/group1/delete_rules_bulk.ts @@ -309,6 +309,7 @@ export default ({ getService }: FtrProviderContext): void => { message: 'Hourly\nRule {{context.rule.name}} generated {{state.signals_count}} alerts', }, + frequency: { summary: true, throttle: '1h', notifyWhen: 'onThrottleInterval' }, }, ]); }); @@ -357,6 +358,7 @@ export default ({ getService }: FtrProviderContext): void => { message: 'Hourly\nRule {{context.rule.name}} generated {{state.signals_count}} alerts', }, + frequency: { summary: true, throttle: '1h', notifyWhen: 'onThrottleInterval' }, }, ]); expect(body[1].actions).to.eql([ @@ -368,6 +370,7 @@ export default ({ getService }: FtrProviderContext): void => { message: 'Hourly\nRule {{context.rule.name}} generated {{state.signals_count}} alerts', }, + frequency: { summary: true, throttle: '1h', notifyWhen: 'onThrottleInterval' }, }, ]); }); diff --git a/x-pack/test/detection_engine_api_integration/security_and_spaces/group1/export_rules.ts b/x-pack/test/detection_engine_api_integration/security_and_spaces/group1/export_rules.ts index c9cb430e144ba..72fb3631ac0c3 100644 --- a/x-pack/test/detection_engine_api_integration/security_and_spaces/group1/export_rules.ts +++ b/x-pack/test/detection_engine_api_integration/security_and_spaces/group1/export_rules.ts @@ -8,6 +8,7 @@ import expect from 'expect'; import { DETECTION_ENGINE_RULES_URL } from '@kbn/security-solution-plugin/common/constants'; +import { RuleResponse } from '@kbn/security-solution-plugin/common/detection_engine/rule_schema'; import { FtrProviderContext } from '../../common/ftr_provider_context'; import { binaryToString, @@ -208,10 +209,17 @@ export default ({ getService }: FtrProviderContext): void => { const outputRule1: ReturnType = { ...getSimpleRuleOutput('rule-1'), actions: [ - { ...action1, uuid: firstRule.actions[0].uuid }, - { ...action2, uuid: firstRule.actions[1].uuid }, + { + ...action1, + uuid: firstRule.actions[0].uuid, + frequency: { summary: true, throttle: null, notifyWhen: 'onActiveAlert' }, + }, + { + ...action2, + uuid: firstRule.actions[1].uuid, + frequency: { summary: true, throttle: null, notifyWhen: 'onActiveAlert' }, + }, ], - throttle: 'rule', }; expect(firstRule).toEqual(outputRule1); }); @@ -258,13 +266,23 @@ export default ({ getService }: FtrProviderContext): void => { const outputRule1: ReturnType = { ...getSimpleRuleOutput('rule-2'), - actions: [{ ...action, uuid: firstRule.actions[0].uuid }], - throttle: 'rule', + actions: [ + { + ...action, + uuid: firstRule.actions[0].uuid, + frequency: { summary: true, throttle: null, notifyWhen: 'onActiveAlert' }, + }, + ], }; const outputRule2: ReturnType = { ...getSimpleRuleOutput('rule-1'), - actions: [{ ...action, uuid: secondRule.actions[0].uuid }], - throttle: 'rule', + actions: [ + { + ...action, + uuid: secondRule.actions[0].uuid, + frequency: { summary: true, throttle: null, notifyWhen: 'onActiveAlert' }, + }, + ], }; expect(firstRule).toEqual(outputRule1); expect(secondRule).toEqual(outputRule2); @@ -437,9 +455,9 @@ export default ({ getService }: FtrProviderContext): void => { message: 'Hourly\nRule {{context.rule.name}} generated {{state.signals_count}} alerts', }, + frequency: { summary: true, throttle: '1h', notifyWhen: 'onThrottleInterval' }, }, ], - throttle: '1h', }; const firstRuleParsed = JSON.parse(body.toString().split(/\n/)[0]); const firstRule = removeServerGeneratedProperties(firstRuleParsed); @@ -514,6 +532,7 @@ export default ({ getService }: FtrProviderContext): void => { message: 'Hourly\nRule {{context.rule.name}} generated {{state.signals_count}} alerts', }, + frequency: { summary: true, throttle: '1h', notifyWhen: 'onThrottleInterval' }, }, { group: 'default', @@ -523,9 +542,9 @@ export default ({ getService }: FtrProviderContext): void => { message: 'Hourly\nRule {{context.rule.name}} generated {{state.signals_count}} alerts', }, + frequency: { summary: true, throttle: '1h', notifyWhen: 'onThrottleInterval' }, }, ], - throttle: '1h', }; const firstRuleParsed = JSON.parse(body.toString().split(/\n/)[0]); const firstRule = removeServerGeneratedProperties(firstRuleParsed); @@ -631,6 +650,7 @@ export default ({ getService }: FtrProviderContext): void => { message: 'Hourly\nRule {{context.rule.name}} generated {{state.signals_count}} alerts', }, + frequency: { summary: true, throttle: '1h', notifyWhen: 'onThrottleInterval' }, }, { group: 'default', @@ -640,9 +660,9 @@ export default ({ getService }: FtrProviderContext): void => { message: 'Hourly\nRule {{context.rule.name}} generated {{state.signals_count}} alerts', }, + frequency: { summary: true, throttle: '1h', notifyWhen: 'onThrottleInterval' }, }, ], - throttle: '1h', }; const outputRule2: ReturnType = { @@ -656,6 +676,7 @@ export default ({ getService }: FtrProviderContext): void => { message: 'Hourly\nRule {{context.rule.name}} generated {{state.signals_count}} alerts', }, + frequency: { summary: true, throttle: '1h', notifyWhen: 'onThrottleInterval' }, }, { group: 'default', @@ -665,9 +686,9 @@ export default ({ getService }: FtrProviderContext): void => { message: 'Hourly\nRule {{context.rule.name}} generated {{state.signals_count}} alerts', }, + frequency: { summary: true, throttle: '1h', notifyWhen: 'onThrottleInterval' }, }, ], - throttle: '1h', }; const firstRuleParsed = JSON.parse(body.toString().split(/\n/)[0]); const secondRuleParsed = JSON.parse(body.toString().split(/\n/)[1]); @@ -682,7 +703,8 @@ export default ({ getService }: FtrProviderContext): void => { }); }; -function expectToMatchRuleSchema(obj: unknown): void { +function expectToMatchRuleSchema(obj: RuleResponse): void { + expect(obj.throttle).toBeUndefined(); expect(obj).toEqual({ id: expect.any(String), rule_id: expect.any(String), @@ -718,7 +740,6 @@ function expectToMatchRuleSchema(obj: unknown): void { language: expect.any(String), index: expect.arrayContaining([]), query: expect.any(String), - throttle: expect.any(String), actions: expect.arrayContaining([]), }); } diff --git a/x-pack/test/detection_engine_api_integration/security_and_spaces/group1/find_rules.ts b/x-pack/test/detection_engine_api_integration/security_and_spaces/group1/find_rules.ts index 7cd260697be72..348400580edd2 100644 --- a/x-pack/test/detection_engine_api_integration/security_and_spaces/group1/find_rules.ts +++ b/x-pack/test/detection_engine_api_integration/security_and_spaces/group1/find_rules.ts @@ -126,8 +126,13 @@ export default ({ getService }: FtrProviderContext): void => { const ruleWithActions: ReturnType = { ...getSimpleRuleOutput(), - actions: [{ ...action, uuid: body.data[0].actions[0].uuid }], - throttle: 'rule', + actions: [ + { + ...action, + uuid: body.data[0].actions[0].uuid, + frequency: { summary: true, throttle: null, notifyWhen: 'onActiveAlert' }, + }, + ], }; body.data = [removeServerGeneratedProperties(body.data[0])]; @@ -171,8 +176,13 @@ export default ({ getService }: FtrProviderContext): void => { const ruleWithActions: ReturnType = { ...getSimpleRuleOutput(), - actions: [{ ...action, uuid: body.data[0].actions[0].uuid }], - throttle: '1h', // <-- throttle makes this a scheduled action + actions: [ + { + ...action, + uuid: body.data[0].actions[0].uuid, + frequency: { summary: true, throttle: '1h', notifyWhen: 'onThrottleInterval' }, + }, + ], }; body.data = [removeServerGeneratedProperties(body.data[0])]; @@ -239,9 +249,9 @@ export default ({ getService }: FtrProviderContext): void => { 'Hourly\nRule {{context.rule.name}} generated {{state.signals_count}} alerts', }, action_type_id: hookAction.actionTypeId, + frequency: { summary: true, throttle: '1h', notifyWhen: 'onThrottleInterval' }, }, ], - throttle: '1h', }; body.data = [removeServerGeneratedProperties(body.data[0])]; diff --git a/x-pack/test/detection_engine_api_integration/security_and_spaces/group10/legacy_actions_migrations.ts b/x-pack/test/detection_engine_api_integration/security_and_spaces/group10/legacy_actions_migrations.ts index 6ba7871b1dbf5..9fd2542664f3b 100644 --- a/x-pack/test/detection_engine_api_integration/security_and_spaces/group10/legacy_actions_migrations.ts +++ b/x-pack/test/detection_engine_api_integration/security_and_spaces/group10/legacy_actions_migrations.ts @@ -75,8 +75,8 @@ export default ({ getService }: FtrProviderContext) => { expect(sidecarActionsSOAfterMigration.hits.hits.length).to.eql(0); expect(ruleSO?.alert.actions).to.eql([]); - expect(ruleSO?.alert.throttle).to.eql('no_actions'); - expect(ruleSO?.alert.notifyWhen).to.eql('onThrottleInterval'); + expect(ruleSO?.alert.throttle).to.eql(null); + expect(ruleSO?.alert.notifyWhen).to.eql(null); }); it('migrates legacy actions for rule with action run on every run', async () => { @@ -122,6 +122,7 @@ export default ({ getService }: FtrProviderContext) => { to: ['test@test.com'], }, uuid: ruleSO?.alert.actions[0].uuid, + frequency: { summary: true, throttle: null, notifyWhen: 'onActiveAlert' }, }, { actionRef: 'action_1', @@ -133,10 +134,11 @@ export default ({ getService }: FtrProviderContext) => { to: ['test@test.com'], }, uuid: ruleSO?.alert.actions[1].uuid, + frequency: { summary: true, throttle: null, notifyWhen: 'onActiveAlert' }, }, ]); - expect(ruleSO?.alert.throttle).to.eql('rule'); - expect(ruleSO?.alert.notifyWhen).to.eql('onActiveAlert'); + expect(ruleSO?.alert.throttle).to.eql(null); + expect(ruleSO?.alert.notifyWhen).to.eql(null); expect(ruleSO?.references).to.eql([ { id: 'c95cb100-b075-11ec-bb3f-1f063f8e06cf', @@ -197,6 +199,7 @@ export default ({ getService }: FtrProviderContext) => { actionRef: 'action_0', group: 'default', uuid: ruleSO?.alert.actions[0].uuid, + frequency: { summary: true, throttle: '1h', notifyWhen: 'onThrottleInterval' }, }, { actionTypeId: '.slack', @@ -206,10 +209,11 @@ export default ({ getService }: FtrProviderContext) => { actionRef: 'action_1', group: 'default', uuid: ruleSO?.alert.actions[1].uuid, + frequency: { summary: true, throttle: '1h', notifyWhen: 'onThrottleInterval' }, }, ]); - expect(ruleSO?.alert.throttle).to.eql('1h'); - expect(ruleSO?.alert.notifyWhen).to.eql('onThrottleInterval'); + expect(ruleSO?.alert.throttle).to.eql(undefined); + expect(ruleSO?.alert.notifyWhen).to.eql(null); expect(ruleSO?.references).to.eql([ { id: 'c95cb100-b075-11ec-bb3f-1f063f8e06cf', @@ -269,10 +273,11 @@ export default ({ getService }: FtrProviderContext) => { to: ['test@test.com'], }, uuid: ruleSO?.alert.actions[0].uuid, + frequency: { summary: true, throttle: '1d', notifyWhen: 'onThrottleInterval' }, }, ]); - expect(ruleSO?.alert.throttle).to.eql('1d'); - expect(ruleSO?.alert.notifyWhen).to.eql('onThrottleInterval'); + expect(ruleSO?.alert.throttle).to.eql(undefined); + expect(ruleSO?.alert.notifyWhen).to.eql(null); expect(ruleSO?.references).to.eql([ { id: 'c95cb100-b075-11ec-bb3f-1f063f8e06cf', @@ -327,10 +332,11 @@ export default ({ getService }: FtrProviderContext) => { to: ['test@test.com'], }, uuid: ruleSO?.alert.actions[0].uuid, + frequency: { summary: true, throttle: '7d', notifyWhen: 'onThrottleInterval' }, }, ]); - expect(ruleSO?.alert.throttle).to.eql('7d'); - expect(ruleSO?.alert.notifyWhen).to.eql('onThrottleInterval'); + expect(ruleSO?.alert.throttle).to.eql(undefined); + expect(ruleSO?.alert.notifyWhen).to.eql(null); expect(ruleSO?.references).to.eql([ { id: 'c95cb100-b075-11ec-bb3f-1f063f8e06cf', diff --git a/x-pack/test/detection_engine_api_integration/security_and_spaces/group10/patch_rules.ts b/x-pack/test/detection_engine_api_integration/security_and_spaces/group10/patch_rules.ts index 98b64726bbaca..ae434a50df98f 100644 --- a/x-pack/test/detection_engine_api_integration/security_and_spaces/group10/patch_rules.ts +++ b/x-pack/test/detection_engine_api_integration/security_and_spaces/group10/patch_rules.ts @@ -7,7 +7,13 @@ import expect from '@kbn/expect'; -import { DETECTION_ENGINE_RULES_URL } from '@kbn/security-solution-plugin/common/constants'; +import { + DETECTION_ENGINE_RULES_URL, + NOTIFICATION_DEFAULT_FREQUENCY, + NOTIFICATION_THROTTLE_NO_ACTIONS, + NOTIFICATION_THROTTLE_RULE, +} from '@kbn/security-solution-plugin/common/constants'; +import { RuleActionArray, RuleActionThrottle } from '@kbn/securitysolution-io-ts-alerting-types'; import { ExceptionListTypeEnum } from '@kbn/securitysolution-io-ts-list-types'; import { FtrProviderContext } from '../../common/ftr_provider_context'; import { @@ -24,7 +30,14 @@ import { getSimpleMlRule, createLegacyRuleAction, getLegacyActionSO, + getSimpleRuleWithoutRuleId, } from '../../utils'; +import { + getActionsWithFrequencies, + getActionsWithoutFrequencies, + getSomeActionsWithFrequencies, +} from '../../utils/get_rule_actions'; +import { removeUUIDFromActions } from '../../utils/remove_uuid_from_actions'; // eslint-disable-next-line import/no-default-export export default ({ getService }: FtrProviderContext) => { @@ -377,9 +390,9 @@ export default ({ getService }: FtrProviderContext) => { 'Hourly\nRule {{context.rule.name}} generated {{state.signals_count}} alerts', }, uuid: bodyToCompare.actions[0].uuid, + frequency: { summary: true, throttle: '1h', notifyWhen: 'onThrottleInterval' }, }, ]; - outputRule.throttle = '1h'; outputRule.revision = 1; expect(bodyToCompare).to.eql(outputRule); @@ -414,5 +427,168 @@ export default ({ getService }: FtrProviderContext) => { }); }); }); + + describe('patch per-action frequencies', () => { + const patchSingleRule = async ( + ruleId: string, + throttle: RuleActionThrottle | undefined, + actions: RuleActionArray + ) => { + const { body: patchedRule } = await supertest + .patch(DETECTION_ENGINE_RULES_URL) + .set('kbn-xsrf', 'true') + .send({ rule_id: ruleId, throttle, actions }) + .expect(200); + + patchedRule.actions = removeUUIDFromActions(patchedRule.actions); + return removeServerGeneratedPropertiesIncludingRuleId(patchedRule); + }; + + describe('actions without frequencies', () => { + [undefined, NOTIFICATION_THROTTLE_NO_ACTIONS, NOTIFICATION_THROTTLE_RULE].forEach( + (throttle) => { + it(`it sets each action's frequency attribute to default value when 'throttle' is ${throttle}`, async () => { + const actionsWithoutFrequencies = await getActionsWithoutFrequencies(supertest); + + // create simple rule + const createdRule = await createRule(supertest, log, getSimpleRuleWithoutRuleId()); + + // patch a simple rule's `throttle` and `actions` + const patchedRule = await patchSingleRule( + createdRule.rule_id, + throttle, + actionsWithoutFrequencies + ); + + const expectedRule = getSimpleRuleOutputWithoutRuleId(); + expectedRule.revision = 1; + expectedRule.actions = actionsWithoutFrequencies.map((action) => ({ + ...action, + frequency: NOTIFICATION_DEFAULT_FREQUENCY, + })); + + expect(patchedRule).to.eql(expectedRule); + }); + } + ); + + // Action throttle cannot be shorter than the schedule interval which is by default is 5m + ['300s', '5m', '3h', '4d'].forEach((throttle) => { + it(`it correctly transforms 'throttle = ${throttle}' and sets it as a frequency of each action`, async () => { + const actionsWithoutFrequencies = await getActionsWithoutFrequencies(supertest); + + // create simple rule + const createdRule = await createRule(supertest, log, getSimpleRuleWithoutRuleId()); + + // patch a simple rule's `throttle` and `actions` + const patchedRule = await patchSingleRule( + createdRule.rule_id, + throttle, + actionsWithoutFrequencies + ); + + const expectedRule = getSimpleRuleOutputWithoutRuleId(); + expectedRule.revision = 1; + expectedRule.actions = actionsWithoutFrequencies.map((action) => ({ + ...action, + frequency: { summary: true, throttle, notifyWhen: 'onThrottleInterval' }, + })); + + expect(patchedRule).to.eql(expectedRule); + }); + }); + }); + + describe('actions with frequencies', () => { + [ + undefined, + NOTIFICATION_THROTTLE_NO_ACTIONS, + NOTIFICATION_THROTTLE_RULE, + '321s', + '6m', + '10h', + '2d', + ].forEach((throttle) => { + it(`it does not change actions frequency attributes when 'throttle' is '${throttle}'`, async () => { + const actionsWithFrequencies = await getActionsWithFrequencies(supertest); + + // create simple rule + const createdRule = await createRule(supertest, log, getSimpleRuleWithoutRuleId()); + + // patch a simple rule's `throttle` and `actions` + const patchedRule = await patchSingleRule( + createdRule.rule_id, + throttle, + actionsWithFrequencies + ); + + const expectedRule = getSimpleRuleOutputWithoutRuleId(); + expectedRule.revision = 1; + expectedRule.actions = actionsWithFrequencies; + + expect(patchedRule).to.eql(expectedRule); + }); + }); + }); + + describe('some actions with frequencies', () => { + [undefined, NOTIFICATION_THROTTLE_NO_ACTIONS, NOTIFICATION_THROTTLE_RULE].forEach( + (throttle) => { + it(`it overrides each action's frequency attribute to default value when 'throttle' is ${throttle}`, async () => { + const someActionsWithFrequencies = await getSomeActionsWithFrequencies(supertest); + + // create simple rule + const createdRule = await createRule(supertest, log, getSimpleRuleWithoutRuleId()); + + // patch a simple rule's `throttle` and `actions` + const patchedRule = await patchSingleRule( + createdRule.rule_id, + throttle, + someActionsWithFrequencies + ); + + const expectedRule = getSimpleRuleOutputWithoutRuleId(); + expectedRule.revision = 1; + expectedRule.actions = someActionsWithFrequencies.map((action) => ({ + ...action, + frequency: action.frequency ?? NOTIFICATION_DEFAULT_FREQUENCY, + })); + + expect(patchedRule).to.eql(expectedRule); + }); + } + ); + + // Action throttle cannot be shorter than the schedule interval which is by default is 5m + ['430s', '7m', '1h', '8d'].forEach((throttle) => { + it(`it correctly transforms 'throttle = ${throttle}' and overrides frequency attribute of each action`, async () => { + const someActionsWithFrequencies = await getSomeActionsWithFrequencies(supertest); + + // create simple rule + const createdRule = await createRule(supertest, log, getSimpleRuleWithoutRuleId()); + + // patch a simple rule's `throttle` and `actions` + const patchedRule = await patchSingleRule( + createdRule.rule_id, + throttle, + someActionsWithFrequencies + ); + + const expectedRule = getSimpleRuleOutputWithoutRuleId(); + expectedRule.revision = 1; + expectedRule.actions = someActionsWithFrequencies.map((action) => ({ + ...action, + frequency: action.frequency ?? { + summary: true, + throttle, + notifyWhen: 'onThrottleInterval', + }, + })); + + expect(patchedRule).to.eql(expectedRule); + }); + }); + }); + }); }); }; diff --git a/x-pack/test/detection_engine_api_integration/security_and_spaces/group10/patch_rules_bulk.ts b/x-pack/test/detection_engine_api_integration/security_and_spaces/group10/patch_rules_bulk.ts index 1a26b17bca42e..2d80d17f9100e 100644 --- a/x-pack/test/detection_engine_api_integration/security_and_spaces/group10/patch_rules_bulk.ts +++ b/x-pack/test/detection_engine_api_integration/security_and_spaces/group10/patch_rules_bulk.ts @@ -207,9 +207,9 @@ export default ({ getService }: FtrProviderContext) => { 'Hourly\nRule {{context.rule.name}} generated {{state.signals_count}} alerts', }, uuid: bodyToCompare.actions[0].uuid, + frequency: { summary: true, throttle: '1h', notifyWhen: 'onThrottleInterval' }, }, ]; - outputRule.throttle = '1h'; outputRule.revision = 1; expect(bodyToCompare).to.eql(outputRule); }); diff --git a/x-pack/test/detection_engine_api_integration/security_and_spaces/group10/perform_bulk_action.ts b/x-pack/test/detection_engine_api_integration/security_and_spaces/group10/perform_bulk_action.ts index c77ac5f142f92..185d820351459 100644 --- a/x-pack/test/detection_engine_api_integration/security_and_spaces/group10/perform_bulk_action.ts +++ b/x-pack/test/detection_engine_api_integration/security_and_spaces/group10/perform_bulk_action.ts @@ -9,7 +9,6 @@ import expect from '@kbn/expect'; import { DETECTION_ENGINE_RULES_BULK_ACTION, DETECTION_ENGINE_RULES_URL, - NOTIFICATION_THROTTLE_NO_ACTIONS, NOTIFICATION_THROTTLE_RULE, } from '@kbn/security-solution-plugin/common/constants'; import type { RuleResponse } from '@kbn/security-solution-plugin/common/detection_engine/rule_schema'; @@ -17,7 +16,10 @@ import { BulkActionType, BulkActionEditType, } from '@kbn/security-solution-plugin/common/detection_engine/rule_management/api/rules/bulk_actions/request_schema'; -import { FtrProviderContext } from '../../common/ftr_provider_context'; +import { getCreateExceptionListDetectionSchemaMock } from '@kbn/lists-plugin/common/schemas/request/create_exception_list_schema.mock'; +import { EXCEPTION_LIST_ITEM_URL, EXCEPTION_LIST_URL } from '@kbn/securitysolution-list-constants'; +import { getCreateExceptionListItemMinimalSchemaMock } from '@kbn/lists-plugin/common/schemas/request/create_exception_list_item_schema.mock'; +import { deleteAllExceptions } from '../../../lists_api_integration/utils'; import { binaryToString, createLegacyRuleAction, @@ -35,6 +37,7 @@ import { removeServerGeneratedProperties, waitForRuleSuccess, } from '../../utils'; +import { FtrProviderContext } from '../../common/ftr_provider_context'; // eslint-disable-next-line import/no-default-export export default ({ getService }: FtrProviderContext): void => { @@ -168,7 +171,6 @@ export default ({ getService }: FtrProviderContext): void => { const rule = removeServerGeneratedProperties(JSON.parse(ruleJson)); expect(rule).to.eql({ ...getSimpleRuleOutput(), - throttle: 'rule', actions: [ { action_type_id: '.webhook', @@ -178,6 +180,7 @@ export default ({ getService }: FtrProviderContext): void => { body: '{"test":"a default action"}', }, uuid: rule.actions[0].uuid, + frequency: { summary: true, throttle: null, notifyWhen: 'onActiveAlert' }, }, ], }); @@ -331,6 +334,7 @@ export default ({ getService }: FtrProviderContext): void => { message: 'Hourly\nRule {{context.rule.name}} generated {{state.signals_count}} alerts', }, uuid: ruleBody.actions[0].uuid, + frequency: { summary: true, throttle: '1h', notifyWhen: 'onThrottleInterval' }, }, ]); // we want to ensure rule is executing successfully, to prevent any AAD issues related to partial update of rule SO @@ -403,6 +407,7 @@ export default ({ getService }: FtrProviderContext): void => { message: 'Hourly\nRule {{context.rule.name}} generated {{state.signals_count}} alerts', }, uuid: ruleBody.actions[0].uuid, + frequency: { summary: true, throttle: '1h', notifyWhen: 'onThrottleInterval' }, }, ]); }); @@ -416,15 +421,225 @@ export default ({ getService }: FtrProviderContext): void => { .send({ query: '', action: BulkActionType.duplicate, - duplicate: { include_exceptions: false }, + duplicate: { include_exceptions: false, include_expired_exceptions: false }, + }) + .expect(200); + + expect(body.attributes.summary).to.eql({ failed: 0, skipped: 0, succeeded: 1, total: 1 }); + + // Check that the duplicated rule is returned with the response + expect(body.attributes.results.created[0].name).to.eql(`${ruleToDuplicate.name} [Duplicate]`); + + // Check that the updates have been persisted + const { body: rulesResponse } = await supertest + .get(`${DETECTION_ENGINE_RULES_URL}/_find`) + .set('kbn-xsrf', 'true') + .expect(200); + + expect(rulesResponse.total).to.eql(2); + }); + + it('should duplicate rules with exceptions - expired exceptions included', async () => { + await deleteAllExceptions(supertest, log); + + const expiredDate = new Date(Date.now() - 1000000).toISOString(); + + // create an exception list + const { body: exceptionList } = await supertest + .post(EXCEPTION_LIST_URL) + .set('kbn-xsrf', 'true') + .send(getCreateExceptionListDetectionSchemaMock()) + .expect(200); + // create an exception list item + await supertest + .post(EXCEPTION_LIST_ITEM_URL) + .set('kbn-xsrf', 'true') + .send({ ...getCreateExceptionListItemMinimalSchemaMock(), expire_time: expiredDate }) + .expect(200); + + const ruleId = 'ruleId'; + const ruleToDuplicate = { + ...getSimpleRule(ruleId), + exceptions_list: [ + { + type: exceptionList.type, + list_id: exceptionList.list_id, + id: exceptionList.id, + namespace_type: exceptionList.namespace_type, + }, + ], + }; + const newRule = await createRule(supertest, log, ruleToDuplicate); + + // add an exception item to the rule + await supertest + .post(`${DETECTION_ENGINE_RULES_URL}/${newRule.id}/exceptions`) + .set('kbn-xsrf', 'true') + .send({ + items: [ + { + description: 'Exception item for rule default exception list', + entries: [ + { + field: 'some.not.nested.field', + operator: 'included', + type: 'match', + value: 'some value', + }, + ], + name: 'Sample exception item', + type: 'simple', + expire_time: expiredDate, + }, + ], + }) + .expect(200); + + const { body } = await postBulkAction() + .send({ + query: '', + action: BulkActionType.duplicate, + duplicate: { include_exceptions: true, include_expired_exceptions: true }, + }) + .expect(200); + + const { body: foundItems } = await supertest + .get( + `${EXCEPTION_LIST_ITEM_URL}/_find?list_id=${body.attributes.results.created[0].exceptions_list[1].list_id}` + ) + .set('kbn-xsrf', 'true') + .send() + .expect(200); + + // Item should have been duplicated, even if expired + expect(foundItems.total).to.eql(1); + + expect(body.attributes.summary).to.eql({ failed: 0, skipped: 0, succeeded: 1, total: 1 }); + + // Check that the duplicated rule is returned with the response + expect(body.attributes.results.created[0].name).to.eql(`${ruleToDuplicate.name} [Duplicate]`); + + // Check that the exceptions are duplicated + expect(body.attributes.results.created[0].exceptions_list).to.eql([ + { + type: exceptionList.type, + list_id: exceptionList.list_id, + id: exceptionList.id, + namespace_type: exceptionList.namespace_type, + }, + { + id: body.attributes.results.created[0].exceptions_list[1].id, + list_id: body.attributes.results.created[0].exceptions_list[1].list_id, + namespace_type: 'single', + type: 'rule_default', + }, + ]); + + // Check that the updates have been persisted + const { body: rulesResponse } = await supertest + .get(`${DETECTION_ENGINE_RULES_URL}/_find`) + .set('kbn-xsrf', 'true') + .expect(200); + + expect(rulesResponse.total).to.eql(2); + }); + + it('should duplicate rules with exceptions - expired exceptions excluded', async () => { + await deleteAllExceptions(supertest, log); + + const expiredDate = new Date(Date.now() - 1000000).toISOString(); + + // create an exception list + const { body: exceptionList } = await supertest + .post(EXCEPTION_LIST_URL) + .set('kbn-xsrf', 'true') + .send(getCreateExceptionListDetectionSchemaMock()) + .expect(200); + // create an exception list item + await supertest + .post(EXCEPTION_LIST_ITEM_URL) + .set('kbn-xsrf', 'true') + .send({ ...getCreateExceptionListItemMinimalSchemaMock(), expire_time: expiredDate }) + .expect(200); + + const ruleId = 'ruleId'; + const ruleToDuplicate = { + ...getSimpleRule(ruleId), + exceptions_list: [ + { + type: exceptionList.type, + list_id: exceptionList.list_id, + id: exceptionList.id, + namespace_type: exceptionList.namespace_type, + }, + ], + }; + const newRule = await createRule(supertest, log, ruleToDuplicate); + + // add an exception item to the rule + await supertest + .post(`${DETECTION_ENGINE_RULES_URL}/${newRule.id}/exceptions`) + .set('kbn-xsrf', 'true') + .send({ + items: [ + { + description: 'Exception item for rule default exception list', + entries: [ + { + field: 'some.not.nested.field', + operator: 'included', + type: 'match', + value: 'some value', + }, + ], + name: 'Sample exception item', + type: 'simple', + expire_time: expiredDate, + }, + ], + }) + .expect(200); + + const { body } = await postBulkAction() + .send({ + query: '', + action: BulkActionType.duplicate, + duplicate: { include_exceptions: true, include_expired_exceptions: false }, }) .expect(200); + const { body: foundItems } = await supertest + .get( + `${EXCEPTION_LIST_ITEM_URL}/_find?list_id=${body.attributes.results.created[0].exceptions_list[1].list_id}` + ) + .set('kbn-xsrf', 'true') + .send() + .expect(200); + + // Item should NOT have been duplicated, since it is expired + expect(foundItems.total).to.eql(0); + expect(body.attributes.summary).to.eql({ failed: 0, skipped: 0, succeeded: 1, total: 1 }); // Check that the duplicated rule is returned with the response expect(body.attributes.results.created[0].name).to.eql(`${ruleToDuplicate.name} [Duplicate]`); + // Check that the exceptions are duplicted + expect(body.attributes.results.created[0].exceptions_list).to.eql([ + { + type: exceptionList.type, + list_id: exceptionList.list_id, + id: exceptionList.id, + namespace_type: exceptionList.namespace_type, + }, + { + id: body.attributes.results.created[0].exceptions_list[1].id, + list_id: body.attributes.results.created[0].exceptions_list[1].list_id, + namespace_type: 'single', + type: 'rule_default', + }, + ]); + // Check that the updates have been persisted const { body: rulesResponse } = await supertest .get(`${DETECTION_ENGINE_RULES_URL}/_find`) @@ -462,7 +677,7 @@ export default ({ getService }: FtrProviderContext): void => { .send({ query: '', action: BulkActionType.duplicate, - duplicate: { include_exceptions: false }, + duplicate: { include_exceptions: false, include_expired_exceptions: false }, }) .expect(200); @@ -491,6 +706,7 @@ export default ({ getService }: FtrProviderContext): void => { 'Hourly\nRule {{context.rule.name}} generated {{state.signals_count}} alerts', }, ...(uuid ? { uuid } : {}), + frequency: { summary: true, throttle: '1h', notifyWhen: 'onThrottleInterval' }, }, ]); }); @@ -1120,6 +1336,7 @@ export default ({ getService }: FtrProviderContext): void => { 'Hourly\nRule {{context.rule.name}} generated {{state.signals_count}} alerts', }, uuid: setTagsRule.actions[0].uuid, + frequency: { summary: true, throttle: '1h', notifyWhen: 'onThrottleInterval' }, }, ]); }); @@ -1404,6 +1621,7 @@ export default ({ getService }: FtrProviderContext): void => { id: webHookConnector.id, action_type_id: '.webhook', uuid: body.attributes.results.updated[0].actions[0].uuid, + frequency: { summary: true, throttle: '1h', notifyWhen: 'onThrottleInterval' }, }, ]; @@ -1461,6 +1679,7 @@ export default ({ getService }: FtrProviderContext): void => { id: webHookConnector.id, action_type_id: '.webhook', uuid: body.attributes.results.updated[0].actions[0].uuid, + frequency: { summary: true, throttle: '1h', notifyWhen: 'onThrottleInterval' }, }, ]; @@ -1572,6 +1791,7 @@ export default ({ getService }: FtrProviderContext): void => { id: webHookConnector.id, action_type_id: '.webhook', uuid: body.attributes.results.updated[0].actions[0].uuid, + frequency: { summary: true, throttle: '1h', notifyWhen: 'onThrottleInterval' }, }, ]; @@ -1624,6 +1844,7 @@ export default ({ getService }: FtrProviderContext): void => { id: webHookConnector.id, action_type_id: '.webhook', uuid: body.attributes.results.updated[0].actions[0].uuid, + frequency: { summary: true, throttle: '1h', notifyWhen: 'onThrottleInterval' }, }, ]; @@ -1678,12 +1899,17 @@ export default ({ getService }: FtrProviderContext): void => { .expect(200); const expectedRuleActions = [ - { ...defaultRuleAction, uuid: body.attributes.results.updated[0].actions[0].uuid }, + { + ...defaultRuleAction, + uuid: body.attributes.results.updated[0].actions[0].uuid, + frequency: { summary: true, throttle: '1d', notifyWhen: 'onThrottleInterval' }, + }, { ...webHookActionMock, id: webHookConnector.id, action_type_id: '.webhook', uuid: body.attributes.results.updated[0].actions[1].uuid, + frequency: { summary: true, throttle: '1h', notifyWhen: 'onThrottleInterval' }, }, ]; @@ -1746,12 +1972,17 @@ export default ({ getService }: FtrProviderContext): void => { .expect(200); const expectedRuleActions = [ - { ...defaultRuleAction, uuid: body.attributes.results.updated[0].actions[0].uuid }, + { + ...defaultRuleAction, + uuid: body.attributes.results.updated[0].actions[0].uuid, + frequency: { summary: true, throttle: '1d', notifyWhen: 'onThrottleInterval' }, + }, { ...slackConnectorMockProps, id: slackConnector.id, action_type_id: '.slack', uuid: body.attributes.results.updated[0].actions[1].uuid, + frequency: { summary: true, throttle: '1h', notifyWhen: 'onThrottleInterval' }, }, ]; @@ -1800,20 +2031,22 @@ export default ({ getService }: FtrProviderContext): void => { }) .expect(200); - // Check that the updated rule is returned with the response - expect(body.attributes.results.updated[0].actions).to.eql([ - { ...defaultRuleAction, uuid: createdRule.actions[0].uuid }, - ]); + // Check that the rule is skipped and was not updated + expect(body.attributes.results.skipped[0].id).to.eql(createdRule.id); // Check that the updates have been persisted const { body: readRule } = await fetchRule(ruleId).expect(200); expect(readRule.actions).to.eql([ - { ...defaultRuleAction, uuid: createdRule.actions[0].uuid }, + { + ...defaultRuleAction, + uuid: createdRule.actions[0].uuid, + frequency: { summary: true, throttle: '1d', notifyWhen: 'onThrottleInterval' }, + }, ]); }); - it('should change throttle if actions list in payload is empty', async () => { + it('should not change throttle if actions list in payload is empty', async () => { // create a new connector const webHookConnector = await createWebHookConnector(); @@ -1849,13 +2082,14 @@ export default ({ getService }: FtrProviderContext): void => { }) .expect(200); - // Check that the updated rule is returned with the response - expect(body.attributes.results.updated[0].throttle).to.be('1h'); + // Check that the rule is skipped and was not updated + expect(body.attributes.results.skipped[0].id).to.eql(createdRule.id); // Check that the updates have been persisted const { body: readRule } = await fetchRule(ruleId).expect(200); - expect(readRule.throttle).to.eql('1h'); + expect(readRule.throttle).to.eql(undefined); + expect(readRule.actions).to.eql(createdRule.actions); }); }); @@ -1903,6 +2137,7 @@ export default ({ getService }: FtrProviderContext): void => { id: webHookConnector.id, action_type_id: '.webhook', uuid: editedRule.actions[0].uuid, + frequency: { summary: true, throttle: '1h', notifyWhen: 'onThrottleInterval' }, }, ]); // version of prebuilt rule should not change @@ -1917,6 +2152,7 @@ export default ({ getService }: FtrProviderContext): void => { id: webHookConnector.id, action_type_id: '.webhook', uuid: readRule.actions[0].uuid, + frequency: { summary: true, throttle: '1h', notifyWhen: 'onThrottleInterval' }, }, ]); expect(prebuiltRule.version).to.be(readRule.version); @@ -1993,7 +2229,7 @@ export default ({ getService }: FtrProviderContext): void => { }, ]; casesForEmptyActions.forEach(({ payloadThrottle }) => { - it(`throttle is set to NOTIFICATION_THROTTLE_NO_ACTIONS, if payload throttle="${payloadThrottle}" and actions list is empty`, async () => { + it(`should not update throttle, if payload throttle="${payloadThrottle}" and actions list is empty`, async () => { const ruleId = 'ruleId'; const createdRule = await createRule(supertest, log, { ...getSimpleRule(ruleId), @@ -2016,34 +2252,32 @@ export default ({ getService }: FtrProviderContext): void => { }) .expect(200); - // Check that the updated rule is returned with the response - expect(body.attributes.results.updated[0].throttle).to.eql( - NOTIFICATION_THROTTLE_NO_ACTIONS - ); + // Check that the rule is skipped and was not updated + expect(body.attributes.results.skipped[0].id).to.eql(createdRule.id); // Check that the updates have been persisted const { body: rule } = await fetchRule(ruleId).expect(200); - expect(rule.throttle).to.eql(NOTIFICATION_THROTTLE_NO_ACTIONS); + expect(rule.throttle).to.eql(undefined); }); }); const casesForNonEmptyActions = [ { payloadThrottle: NOTIFICATION_THROTTLE_RULE, - expectedThrottle: NOTIFICATION_THROTTLE_RULE, + expectedThrottle: undefined, }, { payloadThrottle: '1h', - expectedThrottle: '1h', + expectedThrottle: undefined, }, { payloadThrottle: '1d', - expectedThrottle: '1d', + expectedThrottle: undefined, }, { payloadThrottle: '7d', - expectedThrottle: '7d', + expectedThrottle: undefined, }, ]; [BulkActionEditType.set_rule_actions, BulkActionEditType.add_rule_actions].forEach( @@ -2081,10 +2315,23 @@ export default ({ getService }: FtrProviderContext): void => { // Check that the updated rule is returned with the response expect(body.attributes.results.updated[0].throttle).to.eql(expectedThrottle); + const expectedActions = body.attributes.results.updated[0].actions.map( + (action: any) => ({ + ...action, + frequency: { + summary: true, + throttle: payloadThrottle !== 'rule' ? payloadThrottle : null, + notifyWhen: + payloadThrottle !== 'rule' ? 'onThrottleInterval' : 'onActiveAlert', + }, + }) + ); + // Check that the updates have been persisted const { body: rule } = await fetchRule(ruleId).expect(200); expect(rule.throttle).to.eql(expectedThrottle); + expect(rule.actions).to.eql(expectedActions); }); }); } @@ -2097,11 +2344,11 @@ export default ({ getService }: FtrProviderContext): void => { const cases = [ { payload: { throttle: '1d' }, - expected: { notifyWhen: 'onThrottleInterval' }, + expected: { notifyWhen: null }, }, { payload: { throttle: NOTIFICATION_THROTTLE_RULE }, - expected: { notifyWhen: 'onActiveAlert' }, + expected: { notifyWhen: null }, }, ]; cases.forEach(({ payload, expected }) => { diff --git a/x-pack/test/detection_engine_api_integration/security_and_spaces/group10/read_rules.ts b/x-pack/test/detection_engine_api_integration/security_and_spaces/group10/read_rules.ts index d2162e02d443e..31904342e294f 100644 --- a/x-pack/test/detection_engine_api_integration/security_and_spaces/group10/read_rules.ts +++ b/x-pack/test/detection_engine_api_integration/security_and_spaces/group10/read_rules.ts @@ -135,8 +135,13 @@ export default ({ getService }: FtrProviderContext) => { const bodyToCompare = removeServerGeneratedProperties(body); const ruleWithActions: ReturnType = { ...getSimpleRuleOutput(), - actions: [{ ...action, uuid: bodyToCompare.actions[0].uuid }], - throttle: 'rule', + actions: [ + { + ...action, + uuid: bodyToCompare.actions[0].uuid, + frequency: { summary: true, throttle: null, notifyWhen: 'onActiveAlert' }, + }, + ], }; expect(bodyToCompare).to.eql(ruleWithActions); }); @@ -174,8 +179,13 @@ export default ({ getService }: FtrProviderContext) => { const bodyToCompare = removeServerGeneratedProperties(body); const ruleWithActions: ReturnType = { ...getSimpleRuleOutput(), - actions: [{ ...action, uuid: bodyToCompare.actions[0].uuid }], - throttle: '1h', // <-- throttle makes this a scheduled action + actions: [ + { + ...action, + uuid: bodyToCompare.actions[0].uuid, + frequency: { summary: true, throttle: '1h', notifyWhen: 'onThrottleInterval' }, + }, + ], }; expect(bodyToCompare).to.eql(ruleWithActions); }); @@ -236,9 +246,9 @@ export default ({ getService }: FtrProviderContext) => { 'Hourly\nRule {{context.rule.name}} generated {{state.signals_count}} alerts', }, action_type_id: hookAction.actionTypeId, + frequency: { summary: true, throttle: '1h', notifyWhen: 'onThrottleInterval' }, }, ], - throttle: '1h', }; expect(bodyToCompare).to.eql(ruleWithActions); }); diff --git a/x-pack/test/detection_engine_api_integration/security_and_spaces/group10/throttle.ts b/x-pack/test/detection_engine_api_integration/security_and_spaces/group10/throttle.ts index eedfaee3ec531..4b218d07b7613 100644 --- a/x-pack/test/detection_engine_api_integration/security_and_spaces/group10/throttle.ts +++ b/x-pack/test/detection_engine_api_integration/security_and_spaces/group10/throttle.ts @@ -56,7 +56,7 @@ export default ({ getService }: FtrProviderContext) => { }); describe('creating a rule', () => { - it('When creating a new action and attaching it to a rule, the rule should have its kibana alerting "mute_all" set to "false" and notify_when set to "onActiveAlert"', async () => { + it('When creating a new action and attaching it to a rule, the rule should have its kibana alerting "mute_all" set to "false" and notify_when set to null', async () => { // create a new action const { body: hookAction } = await supertest .post('/api/actions/action') @@ -66,10 +66,16 @@ export default ({ getService }: FtrProviderContext) => { const rule = await createRule(supertest, log, getRuleWithWebHookAction(hookAction.id)); const { - body: { mute_all: muteAll, notify_when: notifyWhen }, + body: { mute_all: muteAll, notify_when: notifyWhen, actions }, } = await supertest.get(`/api/alerting/rule/${rule.id}`); expect(muteAll).to.eql(false); - expect(notifyWhen).to.eql('onActiveAlert'); + expect(actions.length).to.eql(1); + expect(actions[0].frequency).to.eql({ + summary: true, + throttle: null, + notify_when: 'onActiveAlert', + }); + expect(notifyWhen).to.eql(null); }); it('When creating throttle with "NOTIFICATION_THROTTLE_NO_ACTIONS" set and no actions, the rule should have its kibana alerting "mute_all" set to "false" and notify_when set to null', async () => { @@ -105,7 +111,7 @@ export default ({ getService }: FtrProviderContext) => { expect(notifyWhen).to.eql(null); }); - it('When creating throttle with "NOTIFICATION_THROTTLE_RULE" set and no actions, the rule should have its kibana alerting "mute_all" set to "false" and notify_when set to "onActiveAlert"', async () => { + it('When creating throttle with "NOTIFICATION_THROTTLE_RULE" set and no actions, the rule should have its kibana alerting "mute_all" set to "false" and notify_when set to null', async () => { const ruleWithThrottle: RuleCreateProps = { ...getSimpleRule(), throttle: NOTIFICATION_THROTTLE_RULE, @@ -115,7 +121,7 @@ export default ({ getService }: FtrProviderContext) => { body: { mute_all: muteAll, notify_when: notifyWhen }, } = await supertest.get(`/api/alerting/rule/${rule.id}`); expect(muteAll).to.eql(false); - expect(notifyWhen).to.eql('onActiveAlert'); + expect(notifyWhen).to.eql(null); }); // NOTE: This shows A side effect of how we do not set data on side cars anymore where the user is told they have no actions since the array is empty. @@ -125,10 +131,10 @@ export default ({ getService }: FtrProviderContext) => { throttle: NOTIFICATION_THROTTLE_RULE, }; const rule = await createRule(supertest, log, ruleWithThrottle); - expect(rule.throttle).to.eql(NOTIFICATION_THROTTLE_NO_ACTIONS); + expect(rule.throttle).to.eql(undefined); }); - it('When creating throttle with "NOTIFICATION_THROTTLE_RULE" set and actions set, the rule should have its kibana alerting "mute_all" set to "false" and notify_when set to "onActiveAlert"', async () => { + it('When creating throttle with "NOTIFICATION_THROTTLE_RULE" set and actions set, the rule should have its kibana alerting "mute_all" set to "false" and notify_when set to null', async () => { // create a new action const { body: hookAction } = await supertest .post('/api/actions/action') @@ -142,13 +148,19 @@ export default ({ getService }: FtrProviderContext) => { }; const rule = await createRule(supertest, log, ruleWithThrottle); const { - body: { mute_all: muteAll, notify_when: notifyWhen }, + body: { mute_all: muteAll, notify_when: notifyWhen, actions }, } = await supertest.get(`/api/alerting/rule/${rule.id}`); expect(muteAll).to.eql(false); - expect(notifyWhen).to.eql('onActiveAlert'); + expect(actions.length).to.eql(1); + expect(actions[0].frequency).to.eql({ + summary: true, + throttle: null, + notify_when: 'onActiveAlert', + }); + expect(notifyWhen).to.eql(null); }); - it('When creating throttle with "1h" set and no actions, the rule should have its kibana alerting "mute_all" set to "false" and notify_when set to "onThrottleInterval"', async () => { + it('When creating throttle with "1h" set and no actions, the rule should have its kibana alerting "mute_all" set to "false" and notify_when set to null', async () => { const ruleWithThrottle: RuleCreateProps = { ...getSimpleRule(), throttle: '1h', @@ -158,10 +170,10 @@ export default ({ getService }: FtrProviderContext) => { body: { mute_all: muteAll, notify_when: notifyWhen }, } = await supertest.get(`/api/alerting/rule/${rule.id}`); expect(muteAll).to.eql(false); - expect(notifyWhen).to.eql('onThrottleInterval'); + expect(notifyWhen).to.eql(null); }); - it('When creating throttle with "1h" set and actions set, the rule should have its kibana alerting "mute_all" set to "false" and notify_when set to "onThrottleInterval"', async () => { + it('When creating throttle with "1h" set and actions set, the rule should have its kibana alerting "mute_all" set to "false" and notify_when set to null', async () => { // create a new action const { body: hookAction } = await supertest .post('/api/actions/action') @@ -175,10 +187,16 @@ export default ({ getService }: FtrProviderContext) => { }; const rule = await createRule(supertest, log, ruleWithThrottle); const { - body: { mute_all: muteAll, notify_when: notifyWhen }, + body: { mute_all: muteAll, notify_when: notifyWhen, actions }, } = await supertest.get(`/api/alerting/rule/${rule.id}`); expect(muteAll).to.eql(false); - expect(notifyWhen).to.eql('onThrottleInterval'); + expect(actions.length).to.eql(1); + expect(actions[0].frequency).to.eql({ + summary: true, + throttle: '1h', + notify_when: 'onThrottleInterval', + }); + expect(notifyWhen).to.eql(null); }); }); @@ -193,7 +211,7 @@ export default ({ getService }: FtrProviderContext) => { const rule = await createRule(supertest, log, getRuleWithWebHookAction(hookAction.id)); const readRule = await getRule(supertest, log, rule.rule_id); - expect(readRule.throttle).to.eql(NOTIFICATION_THROTTLE_RULE); + expect(readRule.throttle).to.eql(undefined); }); it('When creating throttle with "NOTIFICATION_THROTTLE_NO_ACTIONS" set and no actions, we should return "NOTIFICATION_THROTTLE_NO_ACTIONS" when doing a read', async () => { @@ -203,7 +221,7 @@ export default ({ getService }: FtrProviderContext) => { }; const rule = await createRule(supertest, log, ruleWithThrottle); const readRule = await getRule(supertest, log, rule.rule_id); - expect(readRule.throttle).to.eql(NOTIFICATION_THROTTLE_NO_ACTIONS); + expect(readRule.throttle).to.eql(undefined); }); // NOTE: This shows A side effect of how we do not set data on side cars anymore where the user is told they have no actions since the array is empty. @@ -214,7 +232,7 @@ export default ({ getService }: FtrProviderContext) => { }; const rule = await createRule(supertest, log, ruleWithThrottle); const readRule = await getRule(supertest, log, rule.rule_id); - expect(readRule.throttle).to.eql(NOTIFICATION_THROTTLE_NO_ACTIONS); + expect(readRule.throttle).to.eql(undefined); }); it('When creating a new action and attaching it to a rule, if we change the alert to a "muteAll" through the kibana alerting API, we should get back "NOTIFICATION_THROTTLE_NO_ACTIONS" ', async () => { @@ -232,7 +250,7 @@ export default ({ getService }: FtrProviderContext) => { .send() .expect(204); const readRule = await getRule(supertest, log, rule.rule_id); - expect(readRule.throttle).to.eql(NOTIFICATION_THROTTLE_NO_ACTIONS); + expect(readRule.throttle).to.eql(undefined); }); }); @@ -249,7 +267,7 @@ export default ({ getService }: FtrProviderContext) => { await createRule(supertest, log, ruleWithWebHookAction); ruleWithWebHookAction.name = 'some other name'; const updated = await updateRule(supertest, log, ruleWithWebHookAction); - expect(updated.throttle).to.eql(NOTIFICATION_THROTTLE_RULE); + expect(updated.throttle).to.eql(undefined); }); it('will not change the "muteAll" or "notifyWhen" if we update some part of the rule', async () => { @@ -268,7 +286,7 @@ export default ({ getService }: FtrProviderContext) => { body: { mute_all: muteAll, notify_when: notifyWhen }, } = await supertest.get(`/api/alerting/rule/${updated.id}`); expect(muteAll).to.eql(false); - expect(notifyWhen).to.eql('onActiveAlert'); + expect(notifyWhen).to.eql(null); }); // NOTE: This shows A side effect of how we do not set data on side cars anymore where the user is told they have no actions since the array is empty. @@ -284,7 +302,7 @@ export default ({ getService }: FtrProviderContext) => { await createRule(supertest, log, ruleWithWebHookAction); ruleWithWebHookAction.actions = []; const updated = await updateRule(supertest, log, ruleWithWebHookAction); - expect(updated.throttle).to.eql(NOTIFICATION_THROTTLE_NO_ACTIONS); + expect(updated.throttle).to.eql(undefined); }); }); @@ -306,7 +324,7 @@ export default ({ getService }: FtrProviderContext) => { .send({ rule_id: rule.rule_id, name: 'some other name' }) .expect(200); const readRule = await getRule(supertest, log, rule.rule_id); - expect(readRule.throttle).to.eql(NOTIFICATION_THROTTLE_RULE); + expect(readRule.throttle).to.eql(undefined); }); it('will not change the "muteAll" or "notifyWhen" if we patch part of the rule', async () => { @@ -329,7 +347,7 @@ export default ({ getService }: FtrProviderContext) => { body: { mute_all: muteAll, notify_when: notifyWhen }, } = await supertest.get(`/api/alerting/rule/${rule.id}`); expect(muteAll).to.eql(false); - expect(notifyWhen).to.eql('onActiveAlert'); + expect(notifyWhen).to.eql(null); }); // NOTE: This shows A side effect of how we do not set data on side cars anymore where the user is told they have no actions since the array is empty. @@ -350,7 +368,7 @@ export default ({ getService }: FtrProviderContext) => { .send({ rule_id: rule.rule_id, actions: [] }) .expect(200); const readRule = await getRule(supertest, log, rule.rule_id); - expect(readRule.throttle).to.eql(NOTIFICATION_THROTTLE_NO_ACTIONS); + expect(readRule.throttle).to.eql(undefined); }); }); }); diff --git a/x-pack/test/detection_engine_api_integration/security_and_spaces/group10/update_rules.ts b/x-pack/test/detection_engine_api_integration/security_and_spaces/group10/update_rules.ts index 8396f72a9bb08..82b23e8b381d1 100644 --- a/x-pack/test/detection_engine_api_integration/security_and_spaces/group10/update_rules.ts +++ b/x-pack/test/detection_engine_api_integration/security_and_spaces/group10/update_rules.ts @@ -7,7 +7,13 @@ import expect from '@kbn/expect'; -import { DETECTION_ENGINE_RULES_URL } from '@kbn/security-solution-plugin/common/constants'; +import { + DETECTION_ENGINE_RULES_URL, + NOTIFICATION_DEFAULT_FREQUENCY, + NOTIFICATION_THROTTLE_NO_ACTIONS, + NOTIFICATION_THROTTLE_RULE, +} from '@kbn/security-solution-plugin/common/constants'; +import { RuleActionArray, RuleActionThrottle } from '@kbn/securitysolution-io-ts-alerting-types'; import { ExceptionListTypeEnum } from '@kbn/securitysolution-io-ts-list-types'; import { FtrProviderContext } from '../../common/ftr_provider_context'; import { @@ -28,7 +34,14 @@ import { createLegacyRuleAction, getThresholdRuleForSignalTesting, getLegacyActionSO, + getSimpleRuleWithoutRuleId, } from '../../utils'; +import { + getActionsWithFrequencies, + getActionsWithoutFrequencies, + getSomeActionsWithFrequencies, +} from '../../utils/get_rule_actions'; +import { removeUUIDFromActions } from '../../utils/remove_uuid_from_actions'; // eslint-disable-next-line import/no-default-export export default ({ getService }: FtrProviderContext) => { @@ -185,8 +198,6 @@ export default ({ getService }: FtrProviderContext) => { outputRule.revision = 1; // Expect an empty array outputRule.actions = []; - // Expect "no_actions" - outputRule.throttle = 'no_actions'; const bodyToCompare = removeServerGeneratedPropertiesIncludingRuleId(body); expect(bodyToCompare).to.eql(outputRule); }); @@ -221,7 +232,7 @@ export default ({ getService }: FtrProviderContext) => { id: connector.body.id, action_type_id: connector.body.connector_type_id, params: { - message: 'Hourly\nRule {{context.rule.name}} generated {{state.signals_count}} alerts', + message: 'Rule {{context.rule.name}} generated {{state.signals_count}} alerts', }, }; // update a simple rule's name @@ -229,7 +240,6 @@ export default ({ getService }: FtrProviderContext) => { updatedRule.rule_id = createRuleBody.rule_id; updatedRule.name = 'some other name'; updatedRule.actions = [action1]; - updatedRule.throttle = '1d'; delete updatedRule.id; const { body } = await supertest @@ -249,13 +259,12 @@ export default ({ getService }: FtrProviderContext) => { group: 'default', id: connector.body.id, params: { - message: - 'Hourly\nRule {{context.rule.name}} generated {{state.signals_count}} alerts', + message: 'Rule {{context.rule.name}} generated {{state.signals_count}} alerts', }, uuid: bodyToCompare.actions![0].uuid, + frequency: { summary: true, throttle: null, notifyWhen: 'onActiveAlert' }, }, ]; - outputRule.throttle = '1d'; expect(bodyToCompare).to.eql(outputRule); @@ -662,6 +671,176 @@ export default ({ getService }: FtrProviderContext) => { expect(outputRule.saved_id).to.be(undefined); }); }); + + describe('per-action frequencies', () => { + const updateSingleRule = async ( + ruleId: string, + throttle: RuleActionThrottle | undefined, + actions: RuleActionArray + ) => { + // update a simple rule's `throttle` and `actions` + const ruleToUpdate = getSimpleRuleUpdate(); + ruleToUpdate.throttle = throttle; + ruleToUpdate.actions = actions; + ruleToUpdate.id = ruleId; + delete ruleToUpdate.rule_id; + + const { body: updatedRule } = await supertest + .put(DETECTION_ENGINE_RULES_URL) + .set('kbn-xsrf', 'true') + .send(ruleToUpdate) + .expect(200); + + updatedRule.actions = removeUUIDFromActions(updatedRule.actions); + return removeServerGeneratedPropertiesIncludingRuleId(updatedRule); + }; + + describe('actions without frequencies', () => { + [undefined, NOTIFICATION_THROTTLE_NO_ACTIONS, NOTIFICATION_THROTTLE_RULE].forEach( + (throttle) => { + it(`it sets each action's frequency attribute to default value when 'throttle' is ${throttle}`, async () => { + const actionsWithoutFrequencies = await getActionsWithoutFrequencies(supertest); + + // create simple rule + const createdRule = await createRule(supertest, log, getSimpleRuleWithoutRuleId()); + + // update a simple rule's `throttle` and `actions` + const updatedRule = await updateSingleRule( + createdRule.id, + throttle, + actionsWithoutFrequencies + ); + + const expectedRule = getSimpleRuleOutputWithoutRuleId(); + expectedRule.revision = 1; + expectedRule.actions = actionsWithoutFrequencies.map((action) => ({ + ...action, + frequency: NOTIFICATION_DEFAULT_FREQUENCY, + })); + + expect(updatedRule).to.eql(expectedRule); + }); + } + ); + + // Action throttle cannot be shorter than the schedule interval which is by default is 5m + ['300s', '5m', '3h', '4d'].forEach((throttle) => { + it(`it correctly transforms 'throttle = ${throttle}' and sets it as a frequency of each action`, async () => { + const actionsWithoutFrequencies = await getActionsWithoutFrequencies(supertest); + + // create simple rule + const createdRule = await createRule(supertest, log, getSimpleRuleWithoutRuleId()); + + // update a simple rule's `throttle` and `actions` + const updatedRule = await updateSingleRule( + createdRule.id, + throttle, + actionsWithoutFrequencies + ); + + const expectedRule = getSimpleRuleOutputWithoutRuleId(); + expectedRule.revision = 1; + expectedRule.actions = actionsWithoutFrequencies.map((action) => ({ + ...action, + frequency: { summary: true, throttle, notifyWhen: 'onThrottleInterval' }, + })); + + expect(updatedRule).to.eql(expectedRule); + }); + }); + }); + + describe('actions with frequencies', () => { + [ + undefined, + NOTIFICATION_THROTTLE_NO_ACTIONS, + NOTIFICATION_THROTTLE_RULE, + '321s', + '6m', + '10h', + '2d', + ].forEach((throttle) => { + it(`it does not change actions frequency attributes when 'throttle' is '${throttle}'`, async () => { + const actionsWithFrequencies = await getActionsWithFrequencies(supertest); + + // create simple rule + const createdRule = await createRule(supertest, log, getSimpleRuleWithoutRuleId()); + + // update a simple rule's `throttle` and `actions` + const updatedRule = await updateSingleRule( + createdRule.id, + throttle, + actionsWithFrequencies + ); + + const expectedRule = getSimpleRuleOutputWithoutRuleId(); + expectedRule.revision = 1; + expectedRule.actions = actionsWithFrequencies; + + expect(updatedRule).to.eql(expectedRule); + }); + }); + }); + + describe('some actions with frequencies', () => { + [undefined, NOTIFICATION_THROTTLE_NO_ACTIONS, NOTIFICATION_THROTTLE_RULE].forEach( + (throttle) => { + it(`it overrides each action's frequency attribute to default value when 'throttle' is ${throttle}`, async () => { + const someActionsWithFrequencies = await getSomeActionsWithFrequencies(supertest); + + // create simple rule + const createdRule = await createRule(supertest, log, getSimpleRuleWithoutRuleId()); + + // update a simple rule's `throttle` and `actions` + const updatedRule = await updateSingleRule( + createdRule.id, + throttle, + someActionsWithFrequencies + ); + + const expectedRule = getSimpleRuleOutputWithoutRuleId(); + expectedRule.revision = 1; + expectedRule.actions = someActionsWithFrequencies.map((action) => ({ + ...action, + frequency: action.frequency ?? NOTIFICATION_DEFAULT_FREQUENCY, + })); + + expect(updatedRule).to.eql(expectedRule); + }); + } + ); + + // Action throttle cannot be shorter than the schedule interval which is by default is 5m + ['430s', '7m', '1h', '8d'].forEach((throttle) => { + it(`it correctly transforms 'throttle = ${throttle}' and overrides frequency attribute of each action`, async () => { + const someActionsWithFrequencies = await getSomeActionsWithFrequencies(supertest); + + // create simple rule + const createdRule = await createRule(supertest, log, getSimpleRuleWithoutRuleId()); + + // update a simple rule's `throttle` and `actions` + const updatedRule = await updateSingleRule( + createdRule.id, + throttle, + someActionsWithFrequencies + ); + + const expectedRule = getSimpleRuleOutputWithoutRuleId(); + expectedRule.revision = 1; + expectedRule.actions = someActionsWithFrequencies.map((action) => ({ + ...action, + frequency: action.frequency ?? { + summary: true, + throttle, + notifyWhen: 'onThrottleInterval', + }, + })); + + expect(updatedRule).to.eql(expectedRule); + }); + }); + }); + }); }); }); }; diff --git a/x-pack/test/detection_engine_api_integration/security_and_spaces/group10/update_rules_bulk.ts b/x-pack/test/detection_engine_api_integration/security_and_spaces/group10/update_rules_bulk.ts index 0c8dcacd0ebbf..5da2e8f1c10cd 100644 --- a/x-pack/test/detection_engine_api_integration/security_and_spaces/group10/update_rules_bulk.ts +++ b/x-pack/test/detection_engine_api_integration/security_and_spaces/group10/update_rules_bulk.ts @@ -11,8 +11,12 @@ import { RuleResponse } from '@kbn/security-solution-plugin/common/detection_eng import { DETECTION_ENGINE_RULES_URL, DETECTION_ENGINE_RULES_BULK_UPDATE, + NOTIFICATION_THROTTLE_NO_ACTIONS, + NOTIFICATION_THROTTLE_RULE, + NOTIFICATION_DEFAULT_FREQUENCY, } from '@kbn/security-solution-plugin/common/constants'; import { ExceptionListTypeEnum } from '@kbn/securitysolution-io-ts-list-types'; +import { RuleActionArray, RuleActionThrottle } from '@kbn/securitysolution-io-ts-alerting-types'; import { FtrProviderContext } from '../../common/ftr_provider_context'; import { createSignalsIndex, @@ -25,7 +29,16 @@ import { getSimpleRule, createLegacyRuleAction, getLegacyActionSO, + removeServerGeneratedPropertiesIncludingRuleId, + getSimpleRuleWithoutRuleId, + getSimpleRuleOutputWithoutRuleId, } from '../../utils'; +import { removeUUIDFromActions } from '../../utils/remove_uuid_from_actions'; +import { + getActionsWithFrequencies, + getActionsWithoutFrequencies, + getSomeActionsWithFrequencies, +} from '../../utils/get_rule_actions'; // eslint-disable-next-line import/no-default-export export default ({ getService }: FtrProviderContext) => { @@ -138,7 +151,7 @@ export default ({ getService }: FtrProviderContext) => { id: connector.body.id, action_type_id: connector.body.connector_type_id, params: { - message: 'Hourly\nRule {{context.rule.name}} generated {{state.signals_count}} alerts', + message: 'Rule {{context.rule.name}} generated {{state.signals_count}} alerts', }, }; const [rule1, rule2] = await Promise.all([ @@ -160,12 +173,10 @@ export default ({ getService }: FtrProviderContext) => { const updatedRule1 = getSimpleRuleUpdate('rule-1'); updatedRule1.name = 'some other name'; updatedRule1.actions = [action1]; - updatedRule1.throttle = '1d'; const updatedRule2 = getSimpleRuleUpdate('rule-2'); updatedRule2.name = 'some other name'; updatedRule2.actions = [action1]; - updatedRule2.throttle = '1d'; // update both rule names const { body }: { body: RuleResponse[] } = await supertest @@ -189,13 +200,12 @@ export default ({ getService }: FtrProviderContext) => { group: 'default', id: connector.body.id, params: { - message: - 'Hourly\nRule {{context.rule.name}} generated {{state.signals_count}} alerts', + message: 'Rule {{context.rule.name}} generated {{state.signals_count}} alerts', }, uuid: bodyToCompare.actions[0].uuid, + frequency: { summary: true, throttle: null, notifyWhen: 'onActiveAlert' }, }, ]; - outputRule.throttle = '1d'; expect(bodyToCompare).to.eql(outputRule); }); @@ -247,7 +257,6 @@ export default ({ getService }: FtrProviderContext) => { outputRule.name = 'some other name'; outputRule.revision = 1; outputRule.actions = []; - outputRule.throttle = 'no_actions'; const bodyToCompare = removeServerGeneratedProperties(response); expect(bodyToCompare).to.eql(outputRule); }); @@ -603,5 +612,177 @@ export default ({ getService }: FtrProviderContext) => { ]); }); }); + + describe('bulk per-action frequencies', () => { + const bulkUpdateSingleRule = async ( + ruleId: string, + throttle: RuleActionThrottle | undefined, + actions: RuleActionArray + ) => { + // update a simple rule's `throttle` and `actions` + const ruleToUpdate = getSimpleRuleUpdate(); + ruleToUpdate.throttle = throttle; + ruleToUpdate.actions = actions; + ruleToUpdate.id = ruleId; + delete ruleToUpdate.rule_id; + + const { body } = await supertest + .put(DETECTION_ENGINE_RULES_BULK_UPDATE) + .set('kbn-xsrf', 'true') + .send([ruleToUpdate]) + .expect(200); + + const updatedRule = body[0]; + updatedRule.actions = removeUUIDFromActions(updatedRule.actions); + return removeServerGeneratedPropertiesIncludingRuleId(updatedRule); + }; + + describe('actions without frequencies', () => { + [undefined, NOTIFICATION_THROTTLE_NO_ACTIONS, NOTIFICATION_THROTTLE_RULE].forEach( + (throttle) => { + it(`it sets each action's frequency attribute to default value when 'throttle' is ${throttle}`, async () => { + const actionsWithoutFrequencies = await getActionsWithoutFrequencies(supertest); + + // create simple rule + const createdRule = await createRule(supertest, log, getSimpleRuleWithoutRuleId()); + + // update a simple rule's `throttle` and `actions` + const updatedRule = await bulkUpdateSingleRule( + createdRule.id, + throttle, + actionsWithoutFrequencies + ); + + const expectedRule = getSimpleRuleOutputWithoutRuleId(); + expectedRule.revision = 1; + expectedRule.actions = actionsWithoutFrequencies.map((action) => ({ + ...action, + frequency: NOTIFICATION_DEFAULT_FREQUENCY, + })); + + expect(updatedRule).to.eql(expectedRule); + }); + } + ); + + // Action throttle cannot be shorter than the schedule interval which is by default is 5m + ['300s', '5m', '3h', '4d'].forEach((throttle) => { + it(`it correctly transforms 'throttle = ${throttle}' and sets it as a frequency of each action`, async () => { + const actionsWithoutFrequencies = await getActionsWithoutFrequencies(supertest); + + // create simple rule + const createdRule = await createRule(supertest, log, getSimpleRuleWithoutRuleId()); + + // update a simple rule's `throttle` and `actions` + // update a simple rule's `throttle` and `actions` + const updatedRule = await bulkUpdateSingleRule( + createdRule.id, + throttle, + actionsWithoutFrequencies + ); + + const expectedRule = getSimpleRuleOutputWithoutRuleId(); + expectedRule.revision = 1; + expectedRule.actions = actionsWithoutFrequencies.map((action) => ({ + ...action, + frequency: { summary: true, throttle, notifyWhen: 'onThrottleInterval' }, + })); + + expect(updatedRule).to.eql(expectedRule); + }); + }); + }); + + describe('actions with frequencies', () => { + [ + undefined, + NOTIFICATION_THROTTLE_NO_ACTIONS, + NOTIFICATION_THROTTLE_RULE, + '321s', + '6m', + '10h', + '2d', + ].forEach((throttle) => { + it(`it does not change actions frequency attributes when 'throttle' is '${throttle}'`, async () => { + const actionsWithFrequencies = await getActionsWithFrequencies(supertest); + + // create simple rule + const createdRule = await createRule(supertest, log, getSimpleRuleWithoutRuleId()); + + // update a simple rule's `throttle` and `actions` + const updatedRule = await bulkUpdateSingleRule( + createdRule.id, + throttle, + actionsWithFrequencies + ); + + const expectedRule = getSimpleRuleOutputWithoutRuleId(); + expectedRule.revision = 1; + expectedRule.actions = actionsWithFrequencies; + + expect(updatedRule).to.eql(expectedRule); + }); + }); + }); + + describe('some actions with frequencies', () => { + [undefined, NOTIFICATION_THROTTLE_NO_ACTIONS, NOTIFICATION_THROTTLE_RULE].forEach( + (throttle) => { + it(`it overrides each action's frequency attribute to default value when 'throttle' is ${throttle}`, async () => { + const someActionsWithFrequencies = await getSomeActionsWithFrequencies(supertest); + + // create simple rule + const createdRule = await createRule(supertest, log, getSimpleRuleWithoutRuleId()); + + // update a simple rule's `throttle` and `actions` + const updatedRule = await bulkUpdateSingleRule( + createdRule.id, + throttle, + someActionsWithFrequencies + ); + + const expectedRule = getSimpleRuleOutputWithoutRuleId(); + expectedRule.revision = 1; + expectedRule.actions = someActionsWithFrequencies.map((action) => ({ + ...action, + frequency: action.frequency ?? NOTIFICATION_DEFAULT_FREQUENCY, + })); + + expect(updatedRule).to.eql(expectedRule); + }); + } + ); + + // Action throttle cannot be shorter than the schedule interval which is by default is 5m + ['430s', '7m', '1h', '8d'].forEach((throttle) => { + it(`it correctly transforms 'throttle = ${throttle}' and overrides frequency attribute of each action`, async () => { + const someActionsWithFrequencies = await getSomeActionsWithFrequencies(supertest); + + // create simple rule + const createdRule = await createRule(supertest, log, getSimpleRuleWithoutRuleId()); + + // update a simple rule's `throttle` and `actions` + const updatedRule = await bulkUpdateSingleRule( + createdRule.id, + throttle, + someActionsWithFrequencies + ); + + const expectedRule = getSimpleRuleOutputWithoutRuleId(); + expectedRule.revision = 1; + expectedRule.actions = someActionsWithFrequencies.map((action) => ({ + ...action, + frequency: action.frequency ?? { + summary: true, + throttle, + notifyWhen: 'onThrottleInterval', + }, + })); + + expect(updatedRule).to.eql(expectedRule); + }); + }); + }); + }); }); }; diff --git a/x-pack/test/detection_engine_api_integration/utils/get_complex_rule_output.ts b/x-pack/test/detection_engine_api_integration/utils/get_complex_rule_output.ts index af66123014383..31b8c4571e2f5 100644 --- a/x-pack/test/detection_engine_api_integration/utils/get_complex_rule_output.ts +++ b/x-pack/test/detection_engine_api_integration/utils/get_complex_rule_output.ts @@ -92,7 +92,6 @@ export const getComplexRuleOutput = (ruleId = 'rule-1'): Partial = 'http://www.example.com/some-article-about-attack', 'Some plain text string here explaining why this is a valid thing to look out for', ], - throttle: 'no_actions', timeline_id: 'timeline_id', timeline_title: 'timeline_title', updated_by: 'elastic', diff --git a/x-pack/test/detection_engine_api_integration/utils/get_rule_actions.ts b/x-pack/test/detection_engine_api_integration/utils/get_rule_actions.ts new file mode 100644 index 0000000000000..519cd9136c604 --- /dev/null +++ b/x-pack/test/detection_engine_api_integration/utils/get_rule_actions.ts @@ -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 SuperTest from 'supertest'; + +import { RuleActionArray } from '@kbn/securitysolution-io-ts-alerting-types'; +import { getSlackAction } from './get_slack_action'; +import { getWebHookAction } from './get_web_hook_action'; + +const createConnector = async ( + supertest: SuperTest.SuperTest, + payload: Record +) => + (await supertest.post('/api/actions/action').set('kbn-xsrf', 'true').send(payload).expect(200)) + .body; +const createWebHookConnector = (supertest: SuperTest.SuperTest) => + createConnector(supertest, getWebHookAction()); +const createSlackConnector = (supertest: SuperTest.SuperTest) => + createConnector(supertest, getSlackAction()); + +export const getActionsWithoutFrequencies = async ( + supertest: SuperTest.SuperTest +): Promise => { + const webHookAction = await createWebHookConnector(supertest); + const slackConnector = await createSlackConnector(supertest); + return [ + { + group: 'default', + id: webHookAction.id, + action_type_id: '.webhook', + params: { message: 'Email message' }, + }, + { + group: 'default', + id: slackConnector.id, + action_type_id: '.slack', + params: { message: 'Slack message' }, + }, + ]; +}; + +export const getActionsWithFrequencies = async ( + supertest: SuperTest.SuperTest +): Promise => { + const webHookAction = await createWebHookConnector(supertest); + const slackConnector = await createSlackConnector(supertest); + return [ + { + group: 'default', + id: webHookAction.id, + action_type_id: '.webhook', + params: { message: 'Email message' }, + frequency: { summary: true, throttle: null, notifyWhen: 'onActiveAlert' }, + }, + { + group: 'default', + id: slackConnector.id, + action_type_id: '.slack', + params: { message: 'Slack message' }, + frequency: { summary: false, throttle: '3d', notifyWhen: 'onThrottleInterval' }, + }, + ]; +}; + +export const getSomeActionsWithFrequencies = async ( + supertest: SuperTest.SuperTest +): Promise => { + const webHookAction = await createWebHookConnector(supertest); + const slackConnector = await createSlackConnector(supertest); + return [ + { + group: 'default', + id: webHookAction.id, + action_type_id: '.webhook', + params: { message: 'Email message' }, + frequency: { summary: true, throttle: null, notifyWhen: 'onActiveAlert' }, + }, + { + group: 'default', + id: slackConnector.id, + action_type_id: '.slack', + params: { message: 'Slack message' }, + frequency: { summary: false, throttle: '3d', notifyWhen: 'onThrottleInterval' }, + }, + { + group: 'default', + id: slackConnector.id, + action_type_id: '.slack', + params: { message: 'Slack message' }, + }, + ]; +}; diff --git a/x-pack/test/detection_engine_api_integration/utils/get_simple_rule_output.ts b/x-pack/test/detection_engine_api_integration/utils/get_simple_rule_output.ts index 9826d9a2b98b4..cdadcce39e0a8 100644 --- a/x-pack/test/detection_engine_api_integration/utils/get_simple_rule_output.ts +++ b/x-pack/test/detection_engine_api_integration/utils/get_simple_rule_output.ts @@ -40,7 +40,7 @@ export const getMockSharedResponseSchema = ( tags: [], to: 'now', threat: [], - throttle: 'no_actions', + throttle: undefined, exceptions_list: [], version: 1, revision: 0, diff --git a/x-pack/test/detection_engine_api_integration/utils/get_simple_rule_output_with_web_hook_action.ts b/x-pack/test/detection_engine_api_integration/utils/get_simple_rule_output_with_web_hook_action.ts index 25776fefd5687..7ecee679e50b3 100644 --- a/x-pack/test/detection_engine_api_integration/utils/get_simple_rule_output_with_web_hook_action.ts +++ b/x-pack/test/detection_engine_api_integration/utils/get_simple_rule_output_with_web_hook_action.ts @@ -5,6 +5,7 @@ * 2.0. */ +import { NOTIFICATION_DEFAULT_FREQUENCY } from '@kbn/security-solution-plugin/common/constants'; import { getSimpleRuleOutput } from './get_simple_rule_output'; import { RuleWithoutServerGeneratedProperties } from './remove_server_generated_properties'; @@ -13,7 +14,6 @@ export const getSimpleRuleOutputWithWebHookAction = ( uuid: string ): RuleWithoutServerGeneratedProperties => ({ ...getSimpleRuleOutput(), - throttle: 'rule', actions: [ { action_type_id: '.webhook', @@ -23,6 +23,7 @@ export const getSimpleRuleOutputWithWebHookAction = ( body: '{}', }, uuid, + frequency: NOTIFICATION_DEFAULT_FREQUENCY, }, ], }); diff --git a/x-pack/test/detection_engine_api_integration/utils/remove_uuid_from_actions.ts b/x-pack/test/detection_engine_api_integration/utils/remove_uuid_from_actions.ts new file mode 100644 index 0000000000000..08d95bc750212 --- /dev/null +++ b/x-pack/test/detection_engine_api_integration/utils/remove_uuid_from_actions.ts @@ -0,0 +1,14 @@ +/* + * 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 { RuleActionArray } from '@kbn/securitysolution-io-ts-alerting-types'; + +export const removeUUIDFromActions = (actions: RuleActionArray): RuleActionArray => { + return actions.map(({ uuid, ...restOfAction }) => ({ + ...restOfAction, + })); +}; diff --git a/x-pack/test/functional/apps/lens/group3/metric.ts b/x-pack/test/functional/apps/lens/group3/metric.ts index 843422e87c51f..f5ef312c39d2d 100644 --- a/x-pack/test/functional/apps/lens/group3/metric.ts +++ b/x-pack/test/functional/apps/lens/group3/metric.ts @@ -15,6 +15,52 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { const retry = getService('retry'); const inspector = getService('inspector'); + const inspectorTrendlineData = [ + ['2015-09-19 06:00', 'null'], + ['2015-09-19 09:00', 'null'], + ['2015-09-19 12:00', 'null'], + ['2015-09-19 15:00', 'null'], + ['2015-09-19 18:00', 'null'], + ['2015-09-19 21:00', 'null'], + ['2015-09-20 00:00', '6,011.351'], + ['2015-09-20 03:00', '5,849.901'], + ['2015-09-20 06:00', '5,722.622'], + ['2015-09-20 09:00', '5,769.092'], + ['2015-09-20 12:00', '5,740.875'], + ['2015-09-20 15:00', '5,520.429'], + ['2015-09-20 18:00', '5,153.053'], + ['2015-09-20 21:00', '6,656.581'], + ['2015-09-21 00:00', '5,139.357'], + ['2015-09-21 03:00', '5,884.891'], + ['2015-09-21 06:00', '5,683.283'], + ['2015-09-21 09:00', '5,863.661'], + ['2015-09-21 12:00', '5,657.531'], + ['2015-09-21 15:00', '5,841.935'], + ]; + + const inspectorExpectedTrenlineDataWithBreakdown = [ + ['97.220.3.248', '2015-09-19 06:00', 'null'], + ['97.220.3.248', '2015-09-19 09:00', 'null'], + ['97.220.3.248', '2015-09-19 12:00', 'null'], + ['97.220.3.248', '2015-09-19 15:00', 'null'], + ['97.220.3.248', '2015-09-19 18:00', 'null'], + ['97.220.3.248', '2015-09-19 21:00', 'null'], + ['97.220.3.248', '2015-09-20 00:00', 'null'], + ['97.220.3.248', '2015-09-20 03:00', 'null'], + ['97.220.3.248', '2015-09-20 06:00', 'null'], + ['97.220.3.248', '2015-09-20 09:00', 'null'], + ['97.220.3.248', '2015-09-20 12:00', 'null'], + ['97.220.3.248', '2015-09-20 15:00', 'null'], + ['97.220.3.248', '2015-09-20 18:00', 'null'], + ['97.220.3.248', '2015-09-20 21:00', 'null'], + ['97.220.3.248', '2015-09-21 00:00', 'null'], + ['97.220.3.248', '2015-09-21 03:00', 'null'], + ['97.220.3.248', '2015-09-21 06:00', 'null'], + ['97.220.3.248', '2015-09-21 09:00', '19,755'], + ['97.220.3.248', '2015-09-21 12:00', 'null'], + ['97.220.3.248', '2015-09-21 15:00', 'null'], + ]; + const clickMetric = async (title: string) => { const tiles = await PageObjects.lens.getMetricTiles(); for (const tile of tiles) { @@ -47,7 +93,33 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { }); expect((await PageObjects.lens.getMetricVisualizationData()).length).to.be.equal(1); + }); + + it('should enable trendlines', async () => { + // trendline data without the breakdown + await PageObjects.lens.openDimensionEditor( + 'lnsMetric_primaryMetricDimensionPanel > lns-dimensionTrigger' + ); + await testSubjects.click('lnsMetric_supporting_visualization_trendline'); + await PageObjects.lens.closeDimensionEditor(); + + await inspector.open('lnsApp_inspectButton'); + + const trendLineData = await inspector.getTableDataWithId('inspectorTableChooser1'); + expect(trendLineData).to.eql(inspectorTrendlineData); + await inspector.close(); + await PageObjects.lens.openDimensionEditor( + 'lnsMetric_primaryMetricDimensionPanel > lns-dimensionTrigger' + ); + await testSubjects.click('lnsMetric_supporting_visualization_none'); + await PageObjects.lens.closeDimensionEditor(); + + await PageObjects.lens.waitForVisualization('mtrVis'); + }); + + it('should enable metric with breakdown', async () => { + // trendline data without the breakdown await PageObjects.lens.configureDimension({ dimension: 'lnsMetric_breakdownByDimensionPanel > lns-empty-dimension', operation: 'terms', @@ -112,7 +184,9 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { showingBar: false, }, ]); + }); + it('should enable bar with max dimension', async () => { await PageObjects.lens.openDimensionEditor( 'lnsMetric_maxDimensionPanel > lns-empty-dimension' ); @@ -125,7 +199,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { await PageObjects.lens.removeDimension('lnsMetric_maxDimensionPanel'); }); - it('should enable trendlines', async () => { + it('should enable trendlines with breakdown', async () => { await PageObjects.lens.openDimensionEditor( 'lnsMetric_primaryMetricDimensionPanel > lns-dimensionTrigger' ); @@ -139,11 +213,12 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { (datum) => datum.showingTrendline ) ).to.be(true); + await PageObjects.lens.closeDimensionEditor(); await inspector.open('lnsApp_inspectButton'); - expect(await inspector.getNumberOfTables()).to.equal(2); - + const trendLineData = await inspector.getTableDataWithId('inspectorTableChooser1'); + expect(trendLineData).to.eql(inspectorExpectedTrenlineDataWithBreakdown); await inspector.close(); await PageObjects.lens.openDimensionEditor( diff --git a/x-pack/test/functional/apps/security/doc_level_security_roles.ts b/x-pack/test/functional/apps/security/doc_level_security_roles.ts index 56feb76394b61..b1af9ec3350b7 100644 --- a/x-pack/test/functional/apps/security/doc_level_security_roles.ts +++ b/x-pack/test/functional/apps/security/doc_level_security_roles.ts @@ -19,7 +19,8 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { const PageObjects = getPageObjects(['security', 'common', 'header', 'discover', 'settings']); const kibanaServer = getService('kibanaServer'); - describe('dls', function () { + // Failing: See https://github.com/elastic/kibana/issues/155447 + describe.skip('dls', function () { before('initialize tests', async () => { await kibanaServer.savedObjects.cleanStandardList(); await esArchiver.loadIfNeeded('x-pack/test/functional/es_archives/security/dlstest'); diff --git a/x-pack/test/functional_enterprise_search/apps/enterprise_search/without_host_configured/app_search/setup_guide.ts b/x-pack/test/functional_enterprise_search/apps/enterprise_search/without_host_configured/app_search/setup_guide.ts deleted file mode 100644 index 2995150d73d07..0000000000000 --- a/x-pack/test/functional_enterprise_search/apps/enterprise_search/without_host_configured/app_search/setup_guide.ts +++ /dev/null @@ -1,41 +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 { FtrProviderContext } from '../../../../ftr_provider_context'; - -export default function enterpriseSearchSetupGuideTests({ - getService, - getPageObjects, -}: FtrProviderContext) { - const browser = getService('browser'); - const retry = getService('retry'); - const kibanaServer = getService('kibanaServer'); - const PageObjects = getPageObjects(['appSearch']); - - describe('Setup Guide', function () { - before(async () => { - await kibanaServer.savedObjects.cleanStandardList(); - }); - after(async () => { - await kibanaServer.savedObjects.cleanStandardList(); - }); - - describe('when no enterpriseSearch.host is configured', () => { - it('navigating to the plugin will redirect a user to the setup guide', async () => { - await PageObjects.appSearch.navigateToPage(); - await retry.try(async function () { - const currentUrl = await browser.getCurrentUrl(); - expect(currentUrl).to.contain('/app_search/setup_guide'); - - const documentTitle = await browser.getTitle(); - expect(documentTitle).to.contain('Setup Guide - App Search - Elastic'); - }); - }); - }); - }); -} diff --git a/x-pack/test/functional_enterprise_search/apps/enterprise_search/without_host_configured/index.ts b/x-pack/test/functional_enterprise_search/apps/enterprise_search/without_host_configured/index.ts deleted file mode 100644 index dda2e20745394..0000000000000 --- a/x-pack/test/functional_enterprise_search/apps/enterprise_search/without_host_configured/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. - */ - -import { FtrProviderContext } from '../../../ftr_provider_context'; - -export default function ({ loadTestFile }: FtrProviderContext) { - describe('Enterprise Search', function () { - loadTestFile(require.resolve('./app_search/setup_guide')); - loadTestFile(require.resolve('./workplace_search/setup_guide')); - }); -} diff --git a/x-pack/test/functional_enterprise_search/apps/enterprise_search/without_host_configured/workplace_search/setup_guide.ts b/x-pack/test/functional_enterprise_search/apps/enterprise_search/without_host_configured/workplace_search/setup_guide.ts deleted file mode 100644 index 0be3156d29c02..0000000000000 --- a/x-pack/test/functional_enterprise_search/apps/enterprise_search/without_host_configured/workplace_search/setup_guide.ts +++ /dev/null @@ -1,41 +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 { FtrProviderContext } from '../../../../ftr_provider_context'; - -export default function enterpriseSearchSetupGuideTests({ - getService, - getPageObjects, -}: FtrProviderContext) { - const browser = getService('browser'); - const retry = getService('retry'); - const kibanaServer = getService('kibanaServer'); - const PageObjects = getPageObjects(['workplaceSearch']); - - describe('Setup Guide', function () { - before(async () => { - await kibanaServer.savedObjects.cleanStandardList(); - }); - after(async () => { - await kibanaServer.savedObjects.cleanStandardList(); - }); - - describe('when no enterpriseSearch.host is configured', () => { - it('navigating to the plugin will redirect a user to the setup guide', async () => { - await PageObjects.workplaceSearch.navigateToPage(); - await retry.try(async function () { - const currentUrl = await browser.getCurrentUrl(); - expect(currentUrl).to.contain('/workplace_search/setup_guide'); - - const documentTitle = await browser.getTitle(); - expect(documentTitle).to.contain('Setup Guide - Workplace Search - Elastic'); - }); - }); - }); - }); -} diff --git a/x-pack/test/functional_enterprise_search/cli_config.ts b/x-pack/test/functional_enterprise_search/cli_config.ts new file mode 100644 index 0000000000000..ec7e75f01c288 --- /dev/null +++ b/x-pack/test/functional_enterprise_search/cli_config.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 { FtrConfigProviderContext } from '@kbn/test'; +import { EnterpriseSearchCypressCliTestRunner } from './runner'; + +export default async function ({ readConfigFile }: FtrConfigProviderContext) { + const kibanaCommonTestsConfig = await readConfigFile( + require.resolve('../../../test/common/config.js') + ); + const baseConfig = await readConfigFile(require.resolve('./cypress.config')); + + return { + ...kibanaCommonTestsConfig.getAll(), + // default to the xpack functional config + ...baseConfig.getAll(), + + junit: { + reportName: 'X-Pack Enterprise Search Functional Tests with Host Configured', + }, + kbnTestServer: { + ...baseConfig.get('kbnTestServer'), + serverArgs: [ + ...baseConfig.get('kbnTestServer.serverArgs'), + '--enterpriseSearch.host=http://localhost:3022', + ], + }, + testRunner: EnterpriseSearchCypressCliTestRunner, + }; +} diff --git a/x-pack/test/functional_enterprise_search/cypress.config.ts b/x-pack/test/functional_enterprise_search/cypress.config.ts index 9a6918ab0557d..099b92c429299 100644 --- a/x-pack/test/functional_enterprise_search/cypress.config.ts +++ b/x-pack/test/functional_enterprise_search/cypress.config.ts @@ -7,9 +7,6 @@ import { FtrConfigProviderContext } from '@kbn/test'; -// TODO: If Kibana CI doesn't end up using this (e.g., uses Dockerized containers -// instead of the functional test server), we can opt to delete this file later. - export default async function ({ readConfigFile }: FtrConfigProviderContext) { const baseConfig = await readConfigFile(require.resolve('./base_config')); @@ -32,7 +29,9 @@ export default async function ({ readConfigFile }: FtrConfigProviderContext) { ...baseConfig.get('kbnTestServer.serverArgs'), '--csp.strict=false', '--csp.warnLegacyBrowsers=false', - '--enterpriseSearch.host=http://localhost:3002', + '--enterpriseSearch.host=http://localhost:3022', + '--usageCollection.uiCounters.enabled=false', + `--home.disableWelcomeScreen=true`, ], }, }; diff --git a/x-pack/test/functional_enterprise_search/enterprise_search_server.ts b/x-pack/test/functional_enterprise_search/enterprise_search_server.ts index ae588afc7de13..2f9eacff934d8 100644 --- a/x-pack/test/functional_enterprise_search/enterprise_search_server.ts +++ b/x-pack/test/functional_enterprise_search/enterprise_search_server.ts @@ -52,7 +52,7 @@ export async function setupEnterpriseSearch(logger: ToolingLog): Promise { `--name=enterprise-search-ftr`, `--rm`, `-p`, - `3002:3002`, + `3022:3022`, `-e`, `elasticsearch.host='http://host.docker.internal:9220'`, `-e`, @@ -66,9 +66,9 @@ export async function setupEnterpriseSearch(logger: ToolingLog): Promise { `-e`, `ENT_SEARCH_DEFAULT_PASSWORD=changeme`, `-e`, - `ent_search.listen_port=3002`, + `ent_search.listen_port=3022`, `-e`, - `ent_search.external_url='http://localhost:3002'`, + `ent_search.external_url='http://localhost:3022'`, `docker.elastic.co/enterprise-search/enterprise-search:${await getLatestVersion()}`, ]; diff --git a/x-pack/test/functional_enterprise_search/runner.ts b/x-pack/test/functional_enterprise_search/runner.ts index 8dfee33c5b2e7..b8ab1df8d7108 100644 --- a/x-pack/test/functional_enterprise_search/runner.ts +++ b/x-pack/test/functional_enterprise_search/runner.ts @@ -35,12 +35,8 @@ export async function runEnterpriseSearchTests( await withEnterpriseSearch(context, (runnerEnv) => withProcRunner(log, async (procs) => { await procs.run('cypress', { - cmd: 'sh', - args: [ - `${resolve(__dirname, '../../plugins/enterprise_search/cypress.sh')}`, - `${cypressCommand}`, - 'as', - ], + cmd: '../../../node_modules/.bin/cypress', + args: [cypressCommand, '--config-file', './cypress.config.ts', '--browser', 'chrome'], cwd: resolve(__dirname, '../../plugins/enterprise_search'), env: { FORCE_COLOR: '1', diff --git a/x-pack/test/functional_enterprise_search/with_host_configured.config.ts b/x-pack/test/functional_enterprise_search/visual_config.ts similarity index 95% rename from x-pack/test/functional_enterprise_search/with_host_configured.config.ts rename to x-pack/test/functional_enterprise_search/visual_config.ts index a1584b45da092..300d1b23b4191 100644 --- a/x-pack/test/functional_enterprise_search/with_host_configured.config.ts +++ b/x-pack/test/functional_enterprise_search/visual_config.ts @@ -26,7 +26,7 @@ export default async function ({ readConfigFile }: FtrConfigProviderContext) { ...baseConfig.get('kbnTestServer'), serverArgs: [ ...baseConfig.get('kbnTestServer.serverArgs'), - '--enterpriseSearch.host=http://localhost:3002', + '--enterpriseSearch.host=http://localhost:3022', ], }, testRunner: EnterpriseSearchCypressVisualTestRunner, diff --git a/x-pack/test/functional_enterprise_search/without_host_configured.config.ts b/x-pack/test/functional_enterprise_search/without_host_configured.config.ts deleted file mode 100644 index f5af2bddd8531..0000000000000 --- a/x-pack/test/functional_enterprise_search/without_host_configured.config.ts +++ /dev/null @@ -1,24 +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 { resolve } from 'path'; -import { FtrConfigProviderContext } from '@kbn/test'; - -export default async function ({ readConfigFile }: FtrConfigProviderContext) { - const baseConfig = await readConfigFile(require.resolve('./base_config')); - - return { - // default to the xpack functional config - ...baseConfig.getAll(), - - testFiles: [resolve(__dirname, './apps/enterprise_search/without_host_configured')], - - junit: { - reportName: 'X-Pack Enterprise Search Functional Tests without Host Configured', - }, - }; -} diff --git a/x-pack/test/lists_api_integration/security_and_spaces/tests/duplicate_exception_list.ts b/x-pack/test/lists_api_integration/security_and_spaces/tests/duplicate_exception_list.ts new file mode 100644 index 0000000000000..54e7a89042b02 --- /dev/null +++ b/x-pack/test/lists_api_integration/security_and_spaces/tests/duplicate_exception_list.ts @@ -0,0 +1,207 @@ +/* + * 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 { + ENDPOINT_LIST_URL, + EXCEPTION_LIST_ITEM_URL, + EXCEPTION_LIST_URL, +} from '@kbn/securitysolution-list-constants'; +import { getExceptionResponseMockWithoutAutoGeneratedValues } from '@kbn/lists-plugin/common/schemas/response/exception_list_schema.mock'; +import { getCreateExceptionListDetectionSchemaMock } from '@kbn/lists-plugin/common/schemas/request/create_exception_list_schema.mock'; +import { getCreateExceptionListItemMinimalSchemaMock } from '@kbn/lists-plugin/common/schemas/request/create_exception_list_item_schema.mock'; +import { FtrProviderContext } from '../../../common/ftr_provider_context'; + +import { deleteAllExceptions, removeExceptionListServerGeneratedProperties } from '../../utils'; + +// eslint-disable-next-line import/no-default-export +export default ({ getService }: FtrProviderContext) => { + const supertest = getService('supertest'); + const log = getService('log'); + + describe('duplicate_exception_lists', () => { + afterEach(async () => { + await deleteAllExceptions(supertest, log); + }); + + it('should duplicate a list with no exception items', async () => { + // create an exception list + await supertest + .post(EXCEPTION_LIST_URL) + .set('kbn-xsrf', 'true') + .send(getCreateExceptionListDetectionSchemaMock()) + .expect(200); + + const { body } = await supertest + .post( + `${EXCEPTION_LIST_URL}/_duplicate?list_id=${ + getCreateExceptionListDetectionSchemaMock().list_id + }&namespace_type=single&include_expired_exceptions=true` + ) + .set('kbn-xsrf', 'true') + .expect(200); + + const bodyToCompare = removeExceptionListServerGeneratedProperties(body); + expect(bodyToCompare).to.eql({ + ...getExceptionResponseMockWithoutAutoGeneratedValues(), + type: 'detection', + list_id: body.list_id, + name: `${getCreateExceptionListDetectionSchemaMock().name} [Duplicate]`, + }); + }); + + it('should duplicate a list and its items', async () => { + // create an exception list + await supertest + .post(EXCEPTION_LIST_URL) + .set('kbn-xsrf', 'true') + .send(getCreateExceptionListDetectionSchemaMock()) + .expect(200); + + await supertest + .post(EXCEPTION_LIST_ITEM_URL) + .set('kbn-xsrf', 'true') + .send({ + ...getCreateExceptionListItemMinimalSchemaMock(), + list_id: getCreateExceptionListDetectionSchemaMock().list_id, + }) + .expect(200); + + const { body: listBody } = await supertest + .post( + `${EXCEPTION_LIST_URL}/_duplicate?list_id=${ + getCreateExceptionListDetectionSchemaMock().list_id + }&namespace_type=single&include_expired_exceptions=true` + ) + .set('kbn-xsrf', 'true') + .expect(200); + + const { body } = await supertest + .get(`${EXCEPTION_LIST_ITEM_URL}/_find?list_id=${listBody.list_id}`) + .set('kbn-xsrf', 'true') + .send() + .expect(200); + + const listBodyToCompare = removeExceptionListServerGeneratedProperties(listBody); + expect(listBodyToCompare).to.eql({ + ...getExceptionResponseMockWithoutAutoGeneratedValues(), + type: 'detection', + list_id: listBody.list_id, + name: `${getCreateExceptionListDetectionSchemaMock().name} [Duplicate]`, + }); + + expect(body.total).to.eql(1); + }); + + it('should duplicate a list with expired exception items', async () => { + const expiredDate = new Date(Date.now() - 1000000).toISOString(); + + // create an exception list + await supertest + .post(EXCEPTION_LIST_URL) + .set('kbn-xsrf', 'true') + .send(getCreateExceptionListDetectionSchemaMock()) + .expect(200); + + await supertest + .post(EXCEPTION_LIST_ITEM_URL) + .set('kbn-xsrf', 'true') + .send({ ...getCreateExceptionListItemMinimalSchemaMock(), expire_time: expiredDate }) + .expect(200); + + const { body: listBody } = await supertest + .post( + `${EXCEPTION_LIST_URL}/_duplicate?list_id=${ + getCreateExceptionListDetectionSchemaMock().list_id + }&namespace_type=single&include_expired_exceptions=true` + ) + .set('kbn-xsrf', 'true') + .expect(200); + + const { body } = await supertest + .get(`${EXCEPTION_LIST_ITEM_URL}/_find?list_id=${listBody.list_id}`) + .set('kbn-xsrf', 'true') + .send() + .expect(200); + + expect(body.total).to.eql(1); + }); + + it('should duplicate a list and EXCLUDE expired exception items when "include_expired_exceptions" set to "false"', async () => { + const expiredDate = new Date(Date.now() - 1000000).toISOString(); + + // create an exception list + await supertest + .post(EXCEPTION_LIST_URL) + .set('kbn-xsrf', 'true') + .send(getCreateExceptionListDetectionSchemaMock()) + .expect(200); + + await supertest + .post(EXCEPTION_LIST_ITEM_URL) + .set('kbn-xsrf', 'true') + .send({ + ...getCreateExceptionListItemMinimalSchemaMock(), + list_id: getCreateExceptionListDetectionSchemaMock().list_id, + expire_time: expiredDate, + }) + .expect(200); + + const { body: listBody } = await supertest + .post( + `${EXCEPTION_LIST_URL}/_duplicate?list_id=${ + getCreateExceptionListDetectionSchemaMock().list_id + }&namespace_type=single&include_expired_exceptions=false` + ) + .set('kbn-xsrf', 'true') + .expect(200); + + const { body } = await supertest + .get(`${EXCEPTION_LIST_ITEM_URL}/_find?list_id=${listBody.list_id}`) + .set('kbn-xsrf', 'true') + .send() + .expect(200); + + expect(body.total).to.eql(0); + }); + + describe('error states', () => { + it('should cause a 409 if list does not exist', async () => { + const { body } = await supertest + .post( + `${EXCEPTION_LIST_URL}/_duplicate?list_id=exception_list_id&namespace_type=agnostic&include_expired_exceptions=true` + ) + .set('kbn-xsrf', 'true') + .expect(404); + + expect(body).to.eql({ + message: 'exception list id: "exception_list_id" does not exist', + status_code: 404, + }); + }); + + it('should cause a 405 if trying to duplicate a reserved exception list type', async () => { + // create an exception list + await supertest.post(ENDPOINT_LIST_URL).set('kbn-xsrf', 'true').expect(200); + + const { body } = await supertest + .post( + `${EXCEPTION_LIST_URL}/_duplicate?list_id=endpoint_list&namespace_type=agnostic&include_expired_exceptions=true` + ) + .set('kbn-xsrf', 'true') + .expect(405); + + expect(body).to.eql({ + message: + 'unable to duplicate exception list with list_id: endpoint_list - action not allowed', + status_code: 405, + }); + }); + }); + }); +}; diff --git a/x-pack/test/lists_api_integration/security_and_spaces/tests/index.ts b/x-pack/test/lists_api_integration/security_and_spaces/tests/index.ts index bfce9ac8267e7..02b63c732d229 100644 --- a/x-pack/test/lists_api_integration/security_and_spaces/tests/index.ts +++ b/x-pack/test/lists_api_integration/security_and_spaces/tests/index.ts @@ -19,6 +19,7 @@ export default ({ loadTestFile }: FtrProviderContext): void => { loadTestFile(require.resolve('./update_list_items')); loadTestFile(require.resolve('./delete_lists')); loadTestFile(require.resolve('./delete_list_items')); + loadTestFile(require.resolve('./duplicate_exception_list')); loadTestFile(require.resolve('./find_lists')); loadTestFile(require.resolve('./find_list_items')); loadTestFile(require.resolve('./find_lists_by_size')); diff --git a/x-pack/test/plugin_api_integration/test_suites/event_log/service_api_integration.ts b/x-pack/test/plugin_api_integration/test_suites/event_log/service_api_integration.ts index 2bd65ef5dcd3e..dc244a51bb183 100644 --- a/x-pack/test/plugin_api_integration/test_suites/event_log/service_api_integration.ts +++ b/x-pack/test/plugin_api_integration/test_suites/event_log/service_api_integration.ts @@ -163,6 +163,7 @@ export default function ({ getService }: FtrProviderContext) { execution_gap_duration_s: 3000, }, }, + revision: 0, }, }, alerting: { diff --git a/x-pack/test/screenshot_creation/apps/response_ops_docs/stack_alerting/list_view.ts b/x-pack/test/screenshot_creation/apps/response_ops_docs/stack_alerting/list_view.ts index ac3be7d1814a5..dd4df322e5802 100644 --- a/x-pack/test/screenshot_creation/apps/response_ops_docs/stack_alerting/list_view.ts +++ b/x-pack/test/screenshot_creation/apps/response_ops_docs/stack_alerting/list_view.ts @@ -114,5 +114,23 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { 1024 ); }); + + it('rule conditions screenshots', async () => { + await pageObjects.common.navigateToApp('triggersActions'); + await pageObjects.header.waitUntilLoadingHasFinished(); + await testSubjects.setValue('ruleSearchField', ruleName); + const actionPanel = await testSubjects.find('collapsedItemActions'); + await actionPanel.click(); + const editRuleMenu = await testSubjects.find('editRule'); + await editRuleMenu.click(); + await testSubjects.scrollIntoView('intervalInput'); + await pageObjects.header.waitUntilLoadingHasFinished(); + await commonScreenshots.takeScreenshot( + 'rule-flyout-rule-conditions', + screenshotDirectories, + 1400, + 1500 + ); + }); }); } diff --git a/x-pack/test/screenshot_creation/apps/response_ops_docs/stack_connectors/connector_types.ts b/x-pack/test/screenshot_creation/apps/response_ops_docs/stack_connectors/connector_types.ts index 247b25d2be2ca..4da22a8e807a8 100644 --- a/x-pack/test/screenshot_creation/apps/response_ops_docs/stack_connectors/connector_types.ts +++ b/x-pack/test/screenshot_creation/apps/response_ops_docs/stack_connectors/connector_types.ts @@ -12,8 +12,10 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { const screenshotDirectories = ['response_ops_docs', 'stack_connectors']; const pageObjects = getPageObjects(['common', 'header']); const actions = getService('actions'); - const testSubjects = getService('testSubjects'); + const browser = getService('browser'); const comboBox = getService('comboBox'); + const find = getService('find'); + const testSubjects = getService('testSubjects'); const testIndex = `test-index`; const indexDocument = `{\n` + @@ -21,13 +23,36 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { `"rule_name": "{{rule.name}}",\n` + `"alert_id": "{{alert.id}}",\n` + `"context_message": "{{context.message}}"\n`; + const emailConnectorName = 'my-email-connector'; describe('connector types', function () { + let emailConnectorId: string; + before(async () => { + ({ id: emailConnectorId } = await actions.api.createConnector({ + name: emailConnectorName, + config: { + service: 'other', + from: 'bob@example.com', + host: 'some.non.existent.com', + port: 25, + }, + secrets: { + user: 'bob', + password: 'supersecret', + }, + connectorTypeId: '.email', + })); + }); + beforeEach(async () => { await pageObjects.common.navigateToApp('connectors'); await pageObjects.header.waitUntilLoadingHasFinished(); }); + after(async () => { + await actions.api.deleteConnector(emailConnectorId); + }); + it('server log connector screenshots', async () => { await pageObjects.common.navigateToApp('connectors'); await pageObjects.header.waitUntilLoadingHasFinished(); @@ -57,5 +82,43 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { const flyOutCancelButton = await testSubjects.find('euiFlyoutCloseButton'); await flyOutCancelButton.click(); }); + + it('email connector screenshots', async () => { + await pageObjects.common.navigateToApp('connectors'); + await pageObjects.header.waitUntilLoadingHasFinished(); + await actions.common.openNewConnectorForm('email'); + await testSubjects.setValue('nameInput', 'Gmail connector'); + await testSubjects.setValue('emailFromInput', 'test@gmail.com'); + await testSubjects.setValue('emailServiceSelectInput', 'gmail'); + await commonScreenshots.takeScreenshot('email-connector', screenshotDirectories); + const flyOutCancelButton = await testSubjects.find('euiFlyoutCloseButton'); + await flyOutCancelButton.click(); + }); + + it('test email connector screenshots', async () => { + const searchBox = await find.byCssSelector('[data-test-subj="actionsList"] .euiFieldSearch'); + await searchBox.click(); + await searchBox.clearValue(); + await searchBox.type('my actionTypeId:(.email)'); + await searchBox.pressKeys(browser.keys.ENTER); + const connectorList = await testSubjects.find('actionsTable'); + const emailConnector = await connectorList.findByCssSelector( + `[title="${emailConnectorName}"]` + ); + await emailConnector.click(); + const testButton = await testSubjects.find('testConnectorTab'); + await testButton.click(); + await testSubjects.setValue('comboBoxSearchInput', 'elastic@gmail.com'); + await testSubjects.setValue('subjectInput', 'Test subject'); + await testSubjects.setValue('messageTextArea', 'Enter message text'); + /* timing issue sometimes happens with the combobox so we just try to set the subjectInput again */ + await testSubjects.setValue('subjectInput', 'Test subject'); + await commonScreenshots.takeScreenshot( + 'email-params-test', + screenshotDirectories, + 1400, + 1024 + ); + }); }); } diff --git a/yarn.lock b/yarn.lock index 5ab19eee75bd9..d120721650856 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4545,6 +4545,10 @@ version "0.0.0" uid "" +"@kbn/ml-error-utils@link:x-pack/packages/ml/error_utils": + version "0.0.0" + uid "" + "@kbn/ml-is-defined@link:x-pack/packages/ml/is_defined": version "0.0.0" uid "" @@ -5457,6 +5461,10 @@ version "0.0.0" uid "" +"@kbn/url-state@link:packages/kbn-url-state": + version "0.0.0" + uid "" + "@kbn/usage-collection-plugin@link:src/plugins/usage_collection": version "0.0.0" uid ""