diff --git a/.buildkite/scripts/serverless/create_deploy_tag/info_sections/build_info.ts b/.buildkite/scripts/serverless/create_deploy_tag/info_sections/build_info.ts index 0315d59ec9ca4e..cd459b1debaabb 100644 --- a/.buildkite/scripts/serverless/create_deploy_tag/info_sections/build_info.ts +++ b/.buildkite/scripts/serverless/create_deploy_tag/info_sections/build_info.ts @@ -79,8 +79,8 @@ export async function getQAFBuildContainingCommit( // Find the first build that contains this commit const build = qafBuilds - // Only search across scheduled builds, triggered builds might run with different commits - .filter((e) => e.source === 'schedule') + // Scheduled and manually tagged builds will have this variable, other builds might have non-relevant commits + .filter((e) => e.env.PRE_RELEASE_CHECK?.match(/(1|true)/i)) .find((kbBuild) => { const commitShaIndex = recentGitCommits.findIndex((c) => c.sha === commitSha); diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index e88634c4220d15..3d9ca954ecb7d6 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -21,6 +21,7 @@ x-pack/plugins/aiops @elastic/ml-ui x-pack/packages/ml/aiops_test_utils @elastic/ml-ui x-pack/test/alerting_api_integration/packages/helpers @elastic/response-ops x-pack/test/alerting_api_integration/common/plugins/alerts @elastic/response-ops +x-pack/packages/kbn-alerting-comparators @elastic/response-ops x-pack/examples/alerting_example @elastic/response-ops x-pack/test/functional_with_es_ssl/plugins/alerts @elastic/response-ops x-pack/plugins/alerting @elastic/response-ops @@ -470,6 +471,7 @@ x-pack/plugins/global_search_providers @elastic/appex-sharedux x-pack/test/plugin_functional/plugins/global_search_test @elastic/kibana-core x-pack/plugins/graph @elastic/kibana-visualizations x-pack/plugins/grokdebugger @elastic/kibana-management +packages/kbn-grouping @elastic/response-ops packages/kbn-guided-onboarding @elastic/appex-sharedux examples/guided_onboarding_example @elastic/appex-sharedux src/plugins/guided_onboarding @elastic/appex-sharedux @@ -735,7 +737,6 @@ x-pack/packages/security-solution/data_table @elastic/security-threat-hunting-in packages/kbn-securitysolution-ecs @elastic/security-threat-hunting-explore packages/kbn-securitysolution-es-utils @elastic/security-detection-engine packages/kbn-securitysolution-exception-list-components @elastic/security-detection-engine -packages/kbn-securitysolution-grouping @elastic/security-threat-hunting-explore packages/kbn-securitysolution-hook-utils @elastic/security-detection-engine packages/kbn-securitysolution-io-ts-alerting-types @elastic/security-detection-engine packages/kbn-securitysolution-io-ts-list-types @elastic/security-detection-engine diff --git a/.i18nrc.json b/.i18nrc.json index c92cfe8b323662..7854a7855351cb 100644 --- a/.i18nrc.json +++ b/.i18nrc.json @@ -58,7 +58,7 @@ "filesManagement": "src/plugins/files_management", "flot": "packages/kbn-flot-charts/lib", "generateCsv": "packages/kbn-generate-csv", - "grouping": "packages/kbn-securitysolution-grouping/src", + "grouping": "packages/kbn-grouping/src", "guidedOnboarding": "src/plugins/guided_onboarding", "guidedOnboardingPackage": "packages/kbn-guided-onboarding", "home": "src/plugins/home", diff --git a/api_docs/actions.mdx b/api_docs/actions.mdx index a8ff588b79080b..daeb42ffb8d584 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: 2024-05-24 +date: 2024-05-28 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 abd046bcf69aeb..fdaf5f3011e4fa 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: 2024-05-24 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'advancedSettings'] --- import advancedSettingsObj from './advanced_settings.devdocs.json'; diff --git a/api_docs/ai_assistant_management_selection.mdx b/api_docs/ai_assistant_management_selection.mdx index 1723ebd4ec7d86..8ee59f1660a8e0 100644 --- a/api_docs/ai_assistant_management_selection.mdx +++ b/api_docs/ai_assistant_management_selection.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/aiAssistantManagementSelection title: "aiAssistantManagementSelection" image: https://source.unsplash.com/400x175/?github description: API docs for the aiAssistantManagementSelection plugin -date: 2024-05-24 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'aiAssistantManagementSelection'] --- import aiAssistantManagementSelectionObj from './ai_assistant_management_selection.devdocs.json'; diff --git a/api_docs/aiops.mdx b/api_docs/aiops.mdx index 8d6fc993bda14e..e16bcb06ce6a8f 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: 2024-05-24 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'aiops'] --- import aiopsObj from './aiops.devdocs.json'; diff --git a/api_docs/alerting.mdx b/api_docs/alerting.mdx index eeca7cd5e0d625..f81d03f4e38b46 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: 2024-05-24 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'alerting'] --- import alertingObj from './alerting.devdocs.json'; diff --git a/api_docs/apm.mdx b/api_docs/apm.mdx index 9114300725d2b8..9969eb16c4933d 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: 2024-05-24 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'apm'] --- import apmObj from './apm.devdocs.json'; diff --git a/api_docs/apm_data_access.mdx b/api_docs/apm_data_access.mdx index 11cc1236055e82..6cd6b6cd82030a 100644 --- a/api_docs/apm_data_access.mdx +++ b/api_docs/apm_data_access.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/apmDataAccess title: "apmDataAccess" image: https://source.unsplash.com/400x175/?github description: API docs for the apmDataAccess plugin -date: 2024-05-24 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'apmDataAccess'] --- import apmDataAccessObj from './apm_data_access.devdocs.json'; diff --git a/api_docs/asset_manager.mdx b/api_docs/asset_manager.mdx index 2a4ba627e71925..187a0f5e94e2c4 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: 2024-05-24 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'assetManager'] --- import assetManagerObj from './asset_manager.devdocs.json'; diff --git a/api_docs/assets_data_access.mdx b/api_docs/assets_data_access.mdx index 23fc89452b55b8..4ac7c1efeda966 100644 --- a/api_docs/assets_data_access.mdx +++ b/api_docs/assets_data_access.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/assetsDataAccess title: "assetsDataAccess" image: https://source.unsplash.com/400x175/?github description: API docs for the assetsDataAccess plugin -date: 2024-05-24 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'assetsDataAccess'] --- import assetsDataAccessObj from './assets_data_access.devdocs.json'; diff --git a/api_docs/banners.mdx b/api_docs/banners.mdx index 685975a75bbcd4..da372079f04b6f 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: 2024-05-24 +date: 2024-05-28 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 be34ee06302794..bdb651e3ec8e63 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: 2024-05-24 +date: 2024-05-28 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 8f3e8a7e491f54..5b85e6b575e91d 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: 2024-05-24 +date: 2024-05-28 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 b80a9cc36395de..4f697e455f24ac 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: 2024-05-24 +date: 2024-05-28 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 22733b389aada4..18124d3cb59656 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: 2024-05-24 +date: 2024-05-28 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 89e65a5b24c25f..fc457027fd24b8 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: 2024-05-24 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'cloud'] --- import cloudObj from './cloud.devdocs.json'; diff --git a/api_docs/cloud_data_migration.mdx b/api_docs/cloud_data_migration.mdx index c2daae947a252c..844666847754af 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: 2024-05-24 +date: 2024-05-28 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 c730ea8fa01148..7f5a2bf255ecf2 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: 2024-05-24 +date: 2024-05-28 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 ff5b4d464bb5d4..ada95e37c760e0 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: 2024-05-24 +date: 2024-05-28 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 6c8127935cfe26..52e3785ca00822 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: 2024-05-24 +date: 2024-05-28 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 0eb5d6efff707e..a0b6f6c745f98b 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: 2024-05-24 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'console'] --- import consoleObj from './console.devdocs.json'; diff --git a/api_docs/content_management.mdx b/api_docs/content_management.mdx index e9a1cfee92e8b2..24806e0ace83b3 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: 2024-05-24 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'contentManagement'] --- import contentManagementObj from './content_management.devdocs.json'; diff --git a/api_docs/controls.mdx b/api_docs/controls.mdx index f3c433f0df8a2c..7ca2b6859aa0fd 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: 2024-05-24 +date: 2024-05-28 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 cb48517c41f977..f3b1432498c4d7 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: 2024-05-24 +date: 2024-05-28 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 c7cdd22a8702d4..6099984cecdcfb 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: 2024-05-24 +date: 2024-05-28 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 d0dc7afb1e6c0d..de74daf58a4644 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: 2024-05-24 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'dashboardEnhanced'] --- import dashboardEnhancedObj from './dashboard_enhanced.devdocs.json'; diff --git a/api_docs/data.devdocs.json b/api_docs/data.devdocs.json index 35b158fdbb2686..1a829a4a4fa1e1 100644 --- a/api_docs/data.devdocs.json +++ b/api_docs/data.devdocs.json @@ -7117,7 +7117,9 @@ "label": "createFiltersFromRangeSelectAction", "description": [], "signature": [ - "(event: RangeSelectDataContext) => Promise<", + "(event: ", + "RangeSelectDataContext", + ") => Promise<", { "pluginId": "@kbn/es-query", "scope": "common", diff --git a/api_docs/data.mdx b/api_docs/data.mdx index 16c9a91990ed00..ec0a176f64d9ce 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: 2024-05-24 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'data'] --- import dataObj from './data.devdocs.json'; @@ -21,7 +21,7 @@ Contact [@elastic/kibana-visualizations](https://github.com/orgs/elastic/teams/k | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 3182 | 31 | 2575 | 23 | +| 3183 | 31 | 2576 | 24 | ## Client diff --git a/api_docs/data_query.mdx b/api_docs/data_query.mdx index 967f035932699a..4f335417d3a7a9 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: 2024-05-24 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'data.query'] --- import dataQueryObj from './data_query.devdocs.json'; @@ -21,7 +21,7 @@ Contact [@elastic/kibana-visualizations](https://github.com/orgs/elastic/teams/k | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 3182 | 31 | 2575 | 23 | +| 3183 | 31 | 2576 | 24 | ## Client diff --git a/api_docs/data_search.devdocs.json b/api_docs/data_search.devdocs.json index d3a56c12bd53c8..21359a0e539428 100644 --- a/api_docs/data_search.devdocs.json +++ b/api_docs/data_search.devdocs.json @@ -31207,6 +31207,21 @@ "trackAdoption": false, "initialIsOpen": false }, + { + "parentPluginId": "data", + "id": "def-common.ESQL_TABLE_TYPE", + "type": "string", + "tags": [], + "label": "ESQL_TABLE_TYPE", + "description": [], + "signature": [ + "\"es_ql\"" + ], + "path": "src/plugins/data/common/search/strategies/esql_search/types.ts", + "deprecated": false, + "trackAdoption": false, + "initialIsOpen": false + }, { "parentPluginId": "data", "id": "def-common.EsqlExpressionFunctionDefinition", diff --git a/api_docs/data_search.mdx b/api_docs/data_search.mdx index 50f5ea61375135..326a538acedb31 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: 2024-05-24 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'data.search'] --- import dataSearchObj from './data_search.devdocs.json'; @@ -21,7 +21,7 @@ Contact [@elastic/kibana-visualizations](https://github.com/orgs/elastic/teams/k | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 3182 | 31 | 2575 | 23 | +| 3183 | 31 | 2576 | 24 | ## Client diff --git a/api_docs/data_view_editor.mdx b/api_docs/data_view_editor.mdx index 9b99e4b24bcd55..213bb209f2cb3b 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: 2024-05-24 +date: 2024-05-28 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 d20d90251a8e04..275fafbf5bdfec 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: 2024-05-24 +date: 2024-05-28 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 3ea3deb355dce0..2d43c365076131 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: 2024-05-24 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'dataViewManagement'] --- import dataViewManagementObj from './data_view_management.devdocs.json'; diff --git a/api_docs/data_views.devdocs.json b/api_docs/data_views.devdocs.json index bd233d83f29d4d..7dc04f6ab4d021 100644 --- a/api_docs/data_views.devdocs.json +++ b/api_docs/data_views.devdocs.json @@ -13471,7 +13471,7 @@ }, { "plugin": "infra", - "path": "x-pack/plugins/observability_solution/infra/public/hooks/use_data_view.test.ts" + "path": "x-pack/plugins/observability_solution/infra/public/pages/metrics/inventory_view/hooks/use_waffle_filters.test.ts" } ] }, diff --git a/api_docs/data_views.mdx b/api_docs/data_views.mdx index a15efa25849f94..b99e1ea88a7921 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: 2024-05-24 +date: 2024-05-28 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 4472313f069245..bd2b3af6fc7e11 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: 2024-05-24 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'dataVisualizer'] --- import dataVisualizerObj from './data_visualizer.devdocs.json'; diff --git a/api_docs/dataset_quality.mdx b/api_docs/dataset_quality.mdx index ccdd467fda0f97..8b3d2cb385cae6 100644 --- a/api_docs/dataset_quality.mdx +++ b/api_docs/dataset_quality.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/datasetQuality title: "datasetQuality" image: https://source.unsplash.com/400x175/?github description: API docs for the datasetQuality plugin -date: 2024-05-24 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'datasetQuality'] --- import datasetQualityObj from './dataset_quality.devdocs.json'; diff --git a/api_docs/deprecations_by_api.mdx b/api_docs/deprecations_by_api.mdx index 4abe92cc80da21..99fa23782fdd0d 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: 2024-05-24 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana'] --- @@ -18,8 +18,6 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana'] | ---------------|-----------|-----------| | | ml, stackAlerts | - | | | data, @kbn/search-errors, savedObjectsManagement, unifiedSearch, @kbn/unified-field-list, lens, controls, triggersActionsUi, dataVisualizer, canvas, presentationUtil, logsShared, fleet, ml, @kbn/ml-data-view-utils, enterpriseSearch, graph, visTypeTimeseries, @kbn/lens-embeddable-utils, exploratoryView, stackAlerts, infra, timelines, securitySolution, transform, upgradeAssistant, uptime, ux, maps, dataViewManagement, eventAnnotationListing, inputControlVis, visDefaultEditor, visTypeTimelion, visTypeVega | - | -| | visualizations, lens, controls, dashboard, maps, discover, ml, infra, profiling, slo, links | - | -| | lens, controls, dashboard, observabilityShared, ml | - | | | encryptedSavedObjects, actions, data, ml, logstash, securitySolution, cloudChat | - | | | actions, savedObjectsTagging, ml, enterpriseSearch | - | | | @kbn/core-saved-objects-browser-internal, @kbn/core, savedObjects, visualizations, aiops, dataVisualizer, ml, dashboardEnhanced, graph, lens, securitySolution, eventAnnotation, @kbn/core-saved-objects-browser-mocks | - | @@ -62,6 +60,7 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana'] | | @kbn/monaco, securitySolution | - | | | fleet, cloudSecurityPosture, exploratoryView, osquery, synthetics | - | | | actions, alerting | - | +| | visualizations, lens, controls, dashboard, maps, discover, infra, profiling, links | - | | | discover, @kbn/reporting-public | - | | | data, discover, imageEmbeddable, embeddable | - | | | @kbn/core-plugins-browser-internal, @kbn/core-root-browser-internal, home, savedObjects, unifiedSearch, visualizations, fileUpload, dashboardEnhanced, transform, dashboard, discover, dataVisualizer | - | @@ -106,6 +105,7 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana'] | | visualizations, graph | - | | | kubernetesSecurity, osquery, threatIntelligence | - | | | @kbn/core, lens, savedObjects | - | +| | lens, controls, dashboard, observabilityShared | - | | | dashboard, canvas | - | | | dashboard | - | | | embeddable, dashboard | - | diff --git a/api_docs/deprecations_by_plugin.mdx b/api_docs/deprecations_by_plugin.mdx index 94a7166973c1d1..0fa092962a5c53 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: 2024-05-24 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana'] --- @@ -868,7 +868,7 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana'] | Deprecated API | Reference location(s) | Remove By | | ---------------|-----------|-----------| -| | [validation_errors.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/observability_solution/infra/public/pages/logs/settings/validation_errors.ts#:~:text=title), [validation_errors.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/observability_solution/infra/public/pages/logs/settings/validation_errors.ts#:~:text=title), [validation_errors.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/observability_solution/infra/public/pages/logs/settings/validation_errors.ts#:~:text=title), [validation_errors.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/observability_solution/infra/public/pages/logs/settings/validation_errors.ts#:~:text=title), [index_patterns.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/observability_solution/infra/common/dependency_mocks/index_patterns.ts#:~:text=title), [index_patterns.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/observability_solution/infra/common/dependency_mocks/index_patterns.ts#:~:text=title), [index_patterns.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/observability_solution/infra/common/dependency_mocks/index_patterns.ts#:~:text=title), [use_data_view.test.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/observability_solution/infra/public/hooks/use_data_view.test.ts#:~:text=title) | - | +| | [validation_errors.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/observability_solution/infra/public/pages/logs/settings/validation_errors.ts#:~:text=title), [validation_errors.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/observability_solution/infra/public/pages/logs/settings/validation_errors.ts#:~:text=title), [validation_errors.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/observability_solution/infra/public/pages/logs/settings/validation_errors.ts#:~:text=title), [validation_errors.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/observability_solution/infra/public/pages/logs/settings/validation_errors.ts#:~:text=title), [index_patterns.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/observability_solution/infra/common/dependency_mocks/index_patterns.ts#:~:text=title), [index_patterns.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/observability_solution/infra/common/dependency_mocks/index_patterns.ts#:~:text=title), [index_patterns.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/observability_solution/infra/common/dependency_mocks/index_patterns.ts#:~:text=title), [use_waffle_filters.test.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/observability_solution/infra/public/pages/metrics/inventory_view/hooks/use_waffle_filters.test.ts#:~:text=title) | - | | | [plugin.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/observability_solution/infra/public/plugin.ts#:~:text=registerEmbeddableFactory) | - | | | [saved_object_type.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/observability_solution/infra/server/lib/sources/saved_object_type.ts#:~:text=migrations) | - | @@ -1028,8 +1028,6 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana'] | ---------------|-----------|-----------| | | [register_ml_alerts.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/ml/public/alerting/register_ml_alerts.ts#:~:text=registerNavigation) | - | | | [job_creator.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/ml/public/application/jobs/new_job/common/job_creator/job_creator.ts#:~:text=title), [categorization_examples_loader.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/ml/public/application/jobs/new_job/common/results_loader/categorization_examples_loader.ts#:~:text=title), [configuration_step_details.tsx](https://github.com/elastic/kibana/tree/main/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_creation/components/configuration_step/configuration_step_details.tsx#:~:text=title), [data_loader.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/ml/public/application/datavisualizer/index_based/data_loader/data_loader.ts#:~:text=title), [use_index_data.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_creation/hooks/use_index_data.ts#:~:text=title), [use_index_data.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_creation/hooks/use_index_data.ts#:~:text=title), [use_index_data.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_creation/hooks/use_index_data.ts#:~:text=title), [use_index_data.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_creation/hooks/use_index_data.ts#:~:text=title), [configuration_step_form.tsx](https://github.com/elastic/kibana/tree/main/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_creation/components/configuration_step/configuration_step_form.tsx#:~:text=title), [configuration_step_form.tsx](https://github.com/elastic/kibana/tree/main/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_creation/components/configuration_step/configuration_step_form.tsx#:~:text=title)+ 10 more | - | -| | [index.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/ml/public/embeddables/index.ts#:~:text=registerEmbeddableFactory) | - | -| | [get_embeddable_component.tsx](https://github.com/elastic/kibana/tree/main/x-pack/plugins/ml/public/embeddables/get_embeddable_component.tsx#:~:text=getEmbeddableFactory) | - | | | [plugin.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/ml/public/plugin.ts#:~:text=license%24) | 8.8.0 | | | [plugin.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/ml/server/plugin.ts#:~:text=license%24) | 8.8.0 | | | [annotations.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/ml/server/routes/annotations.ts#:~:text=authc) | - | @@ -1300,7 +1298,6 @@ migrates to using the Kibana Privilege model: https://github.com/elastic/kibana/ | Deprecated API | Reference location(s) | Remove By | | ---------------|-----------|-----------| | | [executor.test.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/observability_solution/slo/server/lib/rules/slo_burn_rate/executor.test.ts#:~:text=alertFactory) | - | -| | [plugin.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/observability_solution/slo/public/plugin.ts#:~:text=registerEmbeddableFactory) | - | | | [plugin.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/observability_solution/slo/public/plugin.ts#:~:text=license%24) | 8.8.0 | | | [slo.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/observability_solution/slo/server/saved_objects/slo.ts#:~:text=migrations) | - | diff --git a/api_docs/deprecations_by_team.mdx b/api_docs/deprecations_by_team.mdx index 7763384170ba7e..0adc251e251d74 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: 2024-05-24 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana'] --- diff --git a/api_docs/dev_tools.mdx b/api_docs/dev_tools.mdx index 8700ada6e3d09d..f68bddf9e2aba8 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: 2024-05-24 +date: 2024-05-28 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 9470b077bc0c13..911255b7e718d2 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: 2024-05-24 +date: 2024-05-28 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 5f6ede2706f29e..09ad2c2d566fd3 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: 2024-05-24 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'discoverEnhanced'] --- import discoverEnhancedObj from './discover_enhanced.devdocs.json'; diff --git a/api_docs/discover_shared.mdx b/api_docs/discover_shared.mdx index daa23f3e030031..8a8cfacafa28e8 100644 --- a/api_docs/discover_shared.mdx +++ b/api_docs/discover_shared.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/discoverShared title: "discoverShared" image: https://source.unsplash.com/400x175/?github description: API docs for the discoverShared plugin -date: 2024-05-24 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'discoverShared'] --- import discoverSharedObj from './discover_shared.devdocs.json'; diff --git a/api_docs/ecs_data_quality_dashboard.mdx b/api_docs/ecs_data_quality_dashboard.mdx index f5f1b9b6abe708..7e8e79976b7020 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: 2024-05-24 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'ecsDataQualityDashboard'] --- import ecsDataQualityDashboardObj from './ecs_data_quality_dashboard.devdocs.json'; diff --git a/api_docs/elastic_assistant.mdx b/api_docs/elastic_assistant.mdx index 7df6e9bf552a7a..15dea304c8862f 100644 --- a/api_docs/elastic_assistant.mdx +++ b/api_docs/elastic_assistant.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/elasticAssistant title: "elasticAssistant" image: https://source.unsplash.com/400x175/?github description: API docs for the elasticAssistant plugin -date: 2024-05-24 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'elasticAssistant'] --- import elasticAssistantObj from './elastic_assistant.devdocs.json'; diff --git a/api_docs/embeddable.devdocs.json b/api_docs/embeddable.devdocs.json index 1322b23c1e5daa..3cf0bf3848d46d 100644 --- a/api_docs/embeddable.devdocs.json +++ b/api_docs/embeddable.devdocs.json @@ -14210,10 +14210,6 @@ "plugin": "discover", "path": "src/plugins/discover/public/plugin.tsx" }, - { - "plugin": "ml", - "path": "x-pack/plugins/ml/public/embeddables/index.ts" - }, { "plugin": "infra", "path": "x-pack/plugins/observability_solution/infra/public/plugin.ts" @@ -14234,10 +14230,6 @@ "plugin": "profiling", "path": "x-pack/plugins/observability_solution/profiling/public/embeddables/register_embeddables.ts" }, - { - "plugin": "slo", - "path": "x-pack/plugins/observability_solution/slo/public/plugin.ts" - }, { "plugin": "links", "path": "src/plugins/links/public/plugin.ts" @@ -14640,10 +14632,6 @@ "plugin": "observabilityShared", "path": "x-pack/plugins/observability_solution/observability_shared/public/components/profiling/embeddables/embeddable_profiling_search_bar.tsx" }, - { - "plugin": "ml", - "path": "x-pack/plugins/ml/public/embeddables/get_embeddable_component.tsx" - }, { "plugin": "dashboard", "path": "src/plugins/dashboard/public/services/embeddable/embeddable.stub.ts" diff --git a/api_docs/embeddable.mdx b/api_docs/embeddable.mdx index 0f03975d8dc82e..62ccdd9cf539e3 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: 2024-05-24 +date: 2024-05-28 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 307ba79aabcc98..9d2381b6769e6b 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: 2024-05-24 +date: 2024-05-28 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 9ce88bb1ab9168..250c25cc0abb6f 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: 2024-05-24 +date: 2024-05-28 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 ebb4f006ace862..163cc5abe6205e 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: 2024-05-24 +date: 2024-05-28 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 8c07efa929150e..6b40067c667d8e 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: 2024-05-24 +date: 2024-05-28 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 6542fb00edc6cb..7d33fd6020d276 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: 2024-05-24 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'eventAnnotation'] --- import eventAnnotationObj from './event_annotation.devdocs.json'; diff --git a/api_docs/event_annotation_listing.mdx b/api_docs/event_annotation_listing.mdx index 182a708e2cba82..0f6d5d34710410 100644 --- a/api_docs/event_annotation_listing.mdx +++ b/api_docs/event_annotation_listing.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/eventAnnotationListing title: "eventAnnotationListing" image: https://source.unsplash.com/400x175/?github description: API docs for the eventAnnotationListing plugin -date: 2024-05-24 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'eventAnnotationListing'] --- import eventAnnotationListingObj from './event_annotation_listing.devdocs.json'; diff --git a/api_docs/event_log.mdx b/api_docs/event_log.mdx index c6a31ce67126a5..5dc6b884633ec9 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: 2024-05-24 +date: 2024-05-28 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 ee2d96da709cd3..f53165a935ec59 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: 2024-05-24 +date: 2024-05-28 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 a470059efb14c1..c95134c8a7a111 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: 2024-05-24 +date: 2024-05-28 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 e94808f2707a0a..9f4f57b65eb886 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: 2024-05-24 +date: 2024-05-28 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 54c6029b18238c..856552e924055d 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: 2024-05-24 +date: 2024-05-28 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 20087eed7766c9..2b3488863c77fa 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: 2024-05-24 +date: 2024-05-28 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 6c06943fc01744..66f20b9b4aac8b 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: 2024-05-24 +date: 2024-05-28 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 d1c70f7c0a86f8..08912e91fb9e1e 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: 2024-05-24 +date: 2024-05-28 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 870de6489d9c97..181e48eb1a05a0 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: 2024-05-24 +date: 2024-05-28 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 b6f87bd3764b13..991fc651180804 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: 2024-05-24 +date: 2024-05-28 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 66aceaff9ae533..294a89e433a488 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: 2024-05-24 +date: 2024-05-28 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 8dc8d7853da444..cbfeaf6c720873 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: 2024-05-24 +date: 2024-05-28 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 c1f1448bad18fa..18cab88d815d58 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: 2024-05-24 +date: 2024-05-28 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 8021fa3872b044..035a5a352481f6 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: 2024-05-24 +date: 2024-05-28 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 cd9e2a2e5d2312..c0863b8f885ef2 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: 2024-05-24 +date: 2024-05-28 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 50acf5f61871d2..8e308858e0bdd8 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: 2024-05-24 +date: 2024-05-28 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 7fb9aa8a7a3d3a..83abd100f11e09 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: 2024-05-24 +date: 2024-05-28 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 9148c75a001442..c7462f23038119 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: 2024-05-24 +date: 2024-05-28 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 ea6ee9e25f18a2..4ee2103f271294 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: 2024-05-24 +date: 2024-05-28 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 fb8d0d61c43532..e89a6a53729120 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: 2024-05-24 +date: 2024-05-28 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 1f53d43ae41374..c0b57217e0dfb4 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: 2024-05-24 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'filesManagement'] --- import filesManagementObj from './files_management.devdocs.json'; diff --git a/api_docs/fleet.devdocs.json b/api_docs/fleet.devdocs.json index cc485026a9c3e7..e7987f0be986bc 100644 --- a/api_docs/fleet.devdocs.json +++ b/api_docs/fleet.devdocs.json @@ -25114,6 +25114,20 @@ "path": "x-pack/plugins/fleet/common/types/models/epm.ts", "deprecated": false, "trackAdoption": false + }, + { + "parentPluginId": "fleet", + "id": "def-common.RegistryVarsEntry.RegistryVarsEntryKeys.hide_in_deployment_modes", + "type": "Array", + "tags": [], + "label": "[RegistryVarsEntryKeys.hide_in_deployment_modes]", + "description": [], + "signature": [ + "string[] | undefined" + ], + "path": "x-pack/plugins/fleet/common/types/models/epm.ts", + "deprecated": false, + "trackAdoption": false } ], "initialIsOpen": false diff --git a/api_docs/fleet.mdx b/api_docs/fleet.mdx index 79dedb3ee428dc..bf40464fae816e 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: 2024-05-24 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'fleet'] --- import fleetObj from './fleet.devdocs.json'; @@ -21,7 +21,7 @@ Contact [@elastic/fleet](https://github.com/orgs/elastic/teams/fleet) for questi | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 1309 | 5 | 1188 | 66 | +| 1310 | 5 | 1189 | 66 | ## Client diff --git a/api_docs/global_search.mdx b/api_docs/global_search.mdx index 027d172fdc7855..f996b5d207c4bd 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: 2024-05-24 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'globalSearch'] --- import globalSearchObj from './global_search.devdocs.json'; diff --git a/api_docs/guided_onboarding.mdx b/api_docs/guided_onboarding.mdx index 79dcb830cb11ef..292fb5a4fb1880 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: 2024-05-24 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'guidedOnboarding'] --- import guidedOnboardingObj from './guided_onboarding.devdocs.json'; diff --git a/api_docs/home.mdx b/api_docs/home.mdx index 8ba7fd344cd222..7f9f2fce5c8897 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: 2024-05-24 +date: 2024-05-28 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 b78258826330d6..c14e7ceddfe0d9 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: 2024-05-24 +date: 2024-05-28 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 41c8309475ce7d..83850b97746649 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: 2024-05-24 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'indexLifecycleManagement'] --- import indexLifecycleManagementObj from './index_lifecycle_management.devdocs.json'; diff --git a/api_docs/index_management.devdocs.json b/api_docs/index_management.devdocs.json index 3d9a1f8659c815..9af93b24493301 100644 --- a/api_docs/index_management.devdocs.json +++ b/api_docs/index_management.devdocs.json @@ -1028,7 +1028,7 @@ "label": "IndexManagementConfig", "description": [], "signature": [ - "{ readonly ui: Readonly<{} & { enabled: boolean; }>; readonly enableIndexActions: boolean; readonly enableLegacyTemplates: boolean; readonly dev: Readonly<{} & { enableIndexDetailsPage: boolean; }>; readonly enableIndexStats: boolean; readonly editableIndexSettings: \"all\" | \"limited\"; readonly enableDataStreamsStorageColumn: boolean; readonly enableMappingsSourceFieldSection: boolean; readonly enableTogglingDataRetention: boolean; }" + "{ readonly ui: Readonly<{} & { enabled: boolean; }>; readonly enableIndexActions: boolean; readonly enableLegacyTemplates: boolean; readonly dev: Readonly<{} & { enableIndexDetailsPage: boolean; enableSemanticText: boolean; }>; readonly enableIndexStats: boolean; readonly editableIndexSettings: \"all\" | \"limited\"; readonly enableDataStreamsStorageColumn: boolean; readonly enableMappingsSourceFieldSection: boolean; readonly enableTogglingDataRetention: boolean; }" ], "path": "x-pack/plugins/index_management/server/config.ts", "deprecated": false, diff --git a/api_docs/index_management.mdx b/api_docs/index_management.mdx index 9802f2a41ef1d0..84ff0c682f117b 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: 2024-05-24 +date: 2024-05-28 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 8153bc2cae01cc..1e8557c03a06a9 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: 2024-05-24 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'infra'] --- import infraObj from './infra.devdocs.json'; diff --git a/api_docs/ingest_pipelines.mdx b/api_docs/ingest_pipelines.mdx index 42f3e51e3ae359..2a751eabf476d2 100644 --- a/api_docs/ingest_pipelines.mdx +++ b/api_docs/ingest_pipelines.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/ingestPipelines title: "ingestPipelines" image: https://source.unsplash.com/400x175/?github description: API docs for the ingestPipelines plugin -date: 2024-05-24 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'ingestPipelines'] --- import ingestPipelinesObj from './ingest_pipelines.devdocs.json'; diff --git a/api_docs/inspector.mdx b/api_docs/inspector.mdx index 5d4227bb0de277..931e8521f548d7 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: 2024-05-24 +date: 2024-05-28 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 efc7a494887efd..7e3051789c0df3 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: 2024-05-24 +date: 2024-05-28 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 668f038f6e8cbe..a2646cceedd7c8 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: 2024-05-24 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ace'] --- import kbnAceObj from './kbn_ace.devdocs.json'; diff --git a/api_docs/kbn_actions_types.mdx b/api_docs/kbn_actions_types.mdx index fafd6da65d8a37..004249188e844e 100644 --- a/api_docs/kbn_actions_types.mdx +++ b/api_docs/kbn_actions_types.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-actions-types title: "@kbn/actions-types" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/actions-types plugin -date: 2024-05-24 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/actions-types'] --- import kbnActionsTypesObj from './kbn_actions_types.devdocs.json'; diff --git a/api_docs/kbn_aiops_components.mdx b/api_docs/kbn_aiops_components.mdx index ca98b2af38a198..1b350134ed297b 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: 2024-05-24 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/aiops-components'] --- import kbnAiopsComponentsObj from './kbn_aiops_components.devdocs.json'; diff --git a/api_docs/kbn_aiops_log_pattern_analysis.mdx b/api_docs/kbn_aiops_log_pattern_analysis.mdx index c06f5fbfddbfe0..8368d39b7c9538 100644 --- a/api_docs/kbn_aiops_log_pattern_analysis.mdx +++ b/api_docs/kbn_aiops_log_pattern_analysis.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-aiops-log-pattern-analysis title: "@kbn/aiops-log-pattern-analysis" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/aiops-log-pattern-analysis plugin -date: 2024-05-24 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/aiops-log-pattern-analysis'] --- import kbnAiopsLogPatternAnalysisObj from './kbn_aiops_log_pattern_analysis.devdocs.json'; diff --git a/api_docs/kbn_aiops_log_rate_analysis.mdx b/api_docs/kbn_aiops_log_rate_analysis.mdx index a1f912bb205e31..59a7dd3107edfb 100644 --- a/api_docs/kbn_aiops_log_rate_analysis.mdx +++ b/api_docs/kbn_aiops_log_rate_analysis.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-aiops-log-rate-analysis title: "@kbn/aiops-log-rate-analysis" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/aiops-log-rate-analysis plugin -date: 2024-05-24 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/aiops-log-rate-analysis'] --- import kbnAiopsLogRateAnalysisObj from './kbn_aiops_log_rate_analysis.devdocs.json'; diff --git a/api_docs/kbn_alerting_api_integration_helpers.mdx b/api_docs/kbn_alerting_api_integration_helpers.mdx index 9efbfcdb895e1c..084fcfc7cc5dc1 100644 --- a/api_docs/kbn_alerting_api_integration_helpers.mdx +++ b/api_docs/kbn_alerting_api_integration_helpers.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-alerting-api-integration-helpers title: "@kbn/alerting-api-integration-helpers" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/alerting-api-integration-helpers plugin -date: 2024-05-24 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/alerting-api-integration-helpers'] --- import kbnAlertingApiIntegrationHelpersObj from './kbn_alerting_api_integration_helpers.devdocs.json'; diff --git a/api_docs/kbn_alerting_state_types.mdx b/api_docs/kbn_alerting_state_types.mdx index a2130b301d857a..40ace1fbfcff1a 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: 2024-05-24 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/alerting-state-types'] --- import kbnAlertingStateTypesObj from './kbn_alerting_state_types.devdocs.json'; diff --git a/api_docs/kbn_alerting_types.mdx b/api_docs/kbn_alerting_types.mdx index 86ea6492e3147d..0539e3bdc61076 100644 --- a/api_docs/kbn_alerting_types.mdx +++ b/api_docs/kbn_alerting_types.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-alerting-types title: "@kbn/alerting-types" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/alerting-types plugin -date: 2024-05-24 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/alerting-types'] --- import kbnAlertingTypesObj from './kbn_alerting_types.devdocs.json'; diff --git a/api_docs/kbn_alerts_as_data_utils.mdx b/api_docs/kbn_alerts_as_data_utils.mdx index 62c6a8839ccaa0..99c561f8a5cc79 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: 2024-05-24 +date: 2024-05-28 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 aa94ecee7227e5..21994580434aac 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: 2024-05-24 +date: 2024-05-28 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 99b6fabf0450ce..23e2c6991e14d8 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: 2024-05-24 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/analytics'] --- import kbnAnalyticsObj from './kbn_analytics.devdocs.json'; diff --git a/api_docs/kbn_analytics_client.mdx b/api_docs/kbn_analytics_client.mdx index dc90fa2f92e5cd..bb88a71b7b4a24 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: 2024-05-24 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/analytics-client'] --- import kbnAnalyticsClientObj from './kbn_analytics_client.devdocs.json'; diff --git a/api_docs/kbn_analytics_collection_utils.mdx b/api_docs/kbn_analytics_collection_utils.mdx index 04f24df27c3bdd..1d8949a3e3ad84 100644 --- a/api_docs/kbn_analytics_collection_utils.mdx +++ b/api_docs/kbn_analytics_collection_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-analytics-collection-utils title: "@kbn/analytics-collection-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/analytics-collection-utils plugin -date: 2024-05-24 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/analytics-collection-utils'] --- import kbnAnalyticsCollectionUtilsObj from './kbn_analytics_collection_utils.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 9286d5486fcaf8..bc753f800aca26 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: 2024-05-24 +date: 2024-05-28 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 1ee7e47d88dd2b..d47ede91e337cf 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: 2024-05-24 +date: 2024-05-28 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 14816f0aad9e38..95066521fc445c 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: 2024-05-24 +date: 2024-05-28 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 fb20061c3457dd..d2827827055339 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: 2024-05-24 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/analytics-shippers-fullstory'] --- import kbnAnalyticsShippersFullstoryObj from './kbn_analytics_shippers_fullstory.devdocs.json'; diff --git a/api_docs/kbn_apm_config_loader.mdx b/api_docs/kbn_apm_config_loader.mdx index 11d510821d5507..ec1736f806c53c 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: 2024-05-24 +date: 2024-05-28 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_data_view.mdx b/api_docs/kbn_apm_data_view.mdx index babf1f0f51f7fe..d0274e44fa47eb 100644 --- a/api_docs/kbn_apm_data_view.mdx +++ b/api_docs/kbn_apm_data_view.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-apm-data-view title: "@kbn/apm-data-view" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/apm-data-view plugin -date: 2024-05-24 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/apm-data-view'] --- import kbnApmDataViewObj from './kbn_apm_data_view.devdocs.json'; diff --git a/api_docs/kbn_apm_synthtrace.mdx b/api_docs/kbn_apm_synthtrace.mdx index b475cdcabd2fdb..e87f5a6c618ff1 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: 2024-05-24 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/apm-synthtrace'] --- import kbnApmSynthtraceObj from './kbn_apm_synthtrace.devdocs.json'; diff --git a/api_docs/kbn_apm_synthtrace_client.mdx b/api_docs/kbn_apm_synthtrace_client.mdx index 8ecc7ab54b1b69..c7ac35f36f2a90 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: 2024-05-24 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/apm-synthtrace-client'] --- import kbnApmSynthtraceClientObj from './kbn_apm_synthtrace_client.devdocs.json'; diff --git a/api_docs/kbn_apm_utils.mdx b/api_docs/kbn_apm_utils.mdx index 621e99ccc1ff3b..336bb94e700887 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: 2024-05-24 +date: 2024-05-28 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 a189e52483f24f..0814350c88ae19 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: 2024-05-24 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/axe-config'] --- import kbnAxeConfigObj from './kbn_axe_config.devdocs.json'; diff --git a/api_docs/kbn_bfetch_error.mdx b/api_docs/kbn_bfetch_error.mdx index f93ca4aa972e12..2d9cc4c7326fde 100644 --- a/api_docs/kbn_bfetch_error.mdx +++ b/api_docs/kbn_bfetch_error.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-bfetch-error title: "@kbn/bfetch-error" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/bfetch-error plugin -date: 2024-05-24 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/bfetch-error'] --- import kbnBfetchErrorObj from './kbn_bfetch_error.devdocs.json'; diff --git a/api_docs/kbn_calculate_auto.mdx b/api_docs/kbn_calculate_auto.mdx index 554cedb736e31c..b6c21ca42f5950 100644 --- a/api_docs/kbn_calculate_auto.mdx +++ b/api_docs/kbn_calculate_auto.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-calculate-auto title: "@kbn/calculate-auto" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/calculate-auto plugin -date: 2024-05-24 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/calculate-auto'] --- import kbnCalculateAutoObj from './kbn_calculate_auto.devdocs.json'; diff --git a/api_docs/kbn_calculate_width_from_char_count.mdx b/api_docs/kbn_calculate_width_from_char_count.mdx index 2704629372fbc3..34c7e1cc991b15 100644 --- a/api_docs/kbn_calculate_width_from_char_count.mdx +++ b/api_docs/kbn_calculate_width_from_char_count.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-calculate-width-from-char-count title: "@kbn/calculate-width-from-char-count" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/calculate-width-from-char-count plugin -date: 2024-05-24 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/calculate-width-from-char-count'] --- import kbnCalculateWidthFromCharCountObj from './kbn_calculate_width_from_char_count.devdocs.json'; diff --git a/api_docs/kbn_cases_components.mdx b/api_docs/kbn_cases_components.mdx index ce0e13e859e5b5..55e675e254720b 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: 2024-05-24 +date: 2024-05-28 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 cb5980b0487040..22419c6b2741aa 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: 2024-05-24 +date: 2024-05-28 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 2f344658ac3c6b..9455324ef98c53 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: 2024-05-24 +date: 2024-05-28 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 034fd4f8b1f2cd..6a6886844a72d4 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: 2024-05-24 +date: 2024-05-28 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 68fd997506364a..cf8a6d5b537355 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: 2024-05-24 +date: 2024-05-28 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 000f8704c768ad..74d1fe06f1a5d9 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: 2024-05-24 +date: 2024-05-28 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 81b8f5bced327b..ddf4930867e47f 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: 2024-05-24 +date: 2024-05-28 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 3c0f9d26e267d6..bd25b2997d6fa7 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: 2024-05-24 +date: 2024-05-28 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 351e211e77c3b0..790106ba17a5d4 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: 2024-05-24 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/code-editor'] --- import kbnCodeEditorObj from './kbn_code_editor.devdocs.json'; diff --git a/api_docs/kbn_code_editor_mock.mdx b/api_docs/kbn_code_editor_mock.mdx index 5dc9de9c8a32c6..8d01f73edac67c 100644 --- a/api_docs/kbn_code_editor_mock.mdx +++ b/api_docs/kbn_code_editor_mock.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-code-editor-mock title: "@kbn/code-editor-mock" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/code-editor-mock plugin -date: 2024-05-24 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/code-editor-mock'] --- import kbnCodeEditorMockObj from './kbn_code_editor_mock.devdocs.json'; diff --git a/api_docs/kbn_code_owners.mdx b/api_docs/kbn_code_owners.mdx index deeca11fbacf1f..437c8e8727c79c 100644 --- a/api_docs/kbn_code_owners.mdx +++ b/api_docs/kbn_code_owners.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-code-owners title: "@kbn/code-owners" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/code-owners plugin -date: 2024-05-24 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/code-owners'] --- import kbnCodeOwnersObj from './kbn_code_owners.devdocs.json'; diff --git a/api_docs/kbn_coloring.mdx b/api_docs/kbn_coloring.mdx index c17d47e83490de..19eefa7283dd30 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: 2024-05-24 +date: 2024-05-28 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 6f9b9d080ce381..5f24a4035fb8ac 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: 2024-05-24 +date: 2024-05-28 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 f330c0aea3b2c9..b41898dfdd0f6c 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: 2024-05-24 +date: 2024-05-28 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 4da893732c1ac4..7b911342b7f3aa 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: 2024-05-24 +date: 2024-05-28 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 5780993f9b6d48..7f2467d11f11d1 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: 2024-05-24 +date: 2024-05-28 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_tabbed_table_list_view.mdx b/api_docs/kbn_content_management_tabbed_table_list_view.mdx index bba48c92f6e4e2..5a53605ee86f79 100644 --- a/api_docs/kbn_content_management_tabbed_table_list_view.mdx +++ b/api_docs/kbn_content_management_tabbed_table_list_view.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-content-management-tabbed-table-list-view title: "@kbn/content-management-tabbed-table-list-view" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/content-management-tabbed-table-list-view plugin -date: 2024-05-24 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/content-management-tabbed-table-list-view'] --- import kbnContentManagementTabbedTableListViewObj from './kbn_content_management_tabbed_table_list_view.devdocs.json'; diff --git a/api_docs/kbn_content_management_table_list_view.mdx b/api_docs/kbn_content_management_table_list_view.mdx index a16bd6c42222d9..3b993b0e92739d 100644 --- a/api_docs/kbn_content_management_table_list_view.mdx +++ b/api_docs/kbn_content_management_table_list_view.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-content-management-table-list-view title: "@kbn/content-management-table-list-view" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/content-management-table-list-view plugin -date: 2024-05-24 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/content-management-table-list-view'] --- import kbnContentManagementTableListViewObj from './kbn_content_management_table_list_view.devdocs.json'; diff --git a/api_docs/kbn_content_management_table_list_view_common.mdx b/api_docs/kbn_content_management_table_list_view_common.mdx index fe538f6d9cef8a..6a8c0d81a4a03a 100644 --- a/api_docs/kbn_content_management_table_list_view_common.mdx +++ b/api_docs/kbn_content_management_table_list_view_common.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-content-management-table-list-view-common title: "@kbn/content-management-table-list-view-common" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/content-management-table-list-view-common plugin -date: 2024-05-24 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/content-management-table-list-view-common'] --- import kbnContentManagementTableListViewCommonObj from './kbn_content_management_table_list_view_common.devdocs.json'; diff --git a/api_docs/kbn_content_management_table_list_view_table.mdx b/api_docs/kbn_content_management_table_list_view_table.mdx index 46aacf54d6be39..77738a474bcefc 100644 --- a/api_docs/kbn_content_management_table_list_view_table.mdx +++ b/api_docs/kbn_content_management_table_list_view_table.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-content-management-table-list-view-table title: "@kbn/content-management-table-list-view-table" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/content-management-table-list-view-table plugin -date: 2024-05-24 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/content-management-table-list-view-table'] --- import kbnContentManagementTableListViewTableObj from './kbn_content_management_table_list_view_table.devdocs.json'; diff --git a/api_docs/kbn_content_management_utils.mdx b/api_docs/kbn_content_management_utils.mdx index 6647818ed36414..b832326bd02cca 100644 --- a/api_docs/kbn_content_management_utils.mdx +++ b/api_docs/kbn_content_management_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-content-management-utils title: "@kbn/content-management-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/content-management-utils plugin -date: 2024-05-24 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/content-management-utils'] --- import kbnContentManagementUtilsObj from './kbn_content_management_utils.devdocs.json'; diff --git a/api_docs/kbn_core_analytics_browser.mdx b/api_docs/kbn_core_analytics_browser.mdx index 580e25611feb9a..5f75b948020b70 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: 2024-05-24 +date: 2024-05-28 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 2319e640031051..09cc4010d12ea2 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: 2024-05-24 +date: 2024-05-28 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 d91f16a400267a..b63fc93b513dc2 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: 2024-05-24 +date: 2024-05-28 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 f4ccff13933dc1..026a76f2e12f85 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: 2024-05-24 +date: 2024-05-28 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 4b70b54cdf6ee4..fe9110e11406e1 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: 2024-05-24 +date: 2024-05-28 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 3f8f143f9f20fe..ff52b7b28f84c0 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: 2024-05-24 +date: 2024-05-28 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 b8207bcc3b2017..4730af575894b3 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: 2024-05-24 +date: 2024-05-28 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 61e49c0c5dc949..584c9e5078dd59 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: 2024-05-24 +date: 2024-05-28 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 c59e682b07f0f2..fb7b24aad67de7 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: 2024-05-24 +date: 2024-05-28 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 072535098bd6fa..58ec240c0149e3 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: 2024-05-24 +date: 2024-05-28 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 f66265afc6b433..ebeb47384bd3fe 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: 2024-05-24 +date: 2024-05-28 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 909676bed50dae..d9396c9415d84b 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: 2024-05-24 +date: 2024-05-28 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 700a9d15ad2ffd..5b02120cebfca2 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: 2024-05-24 +date: 2024-05-28 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 9abc4850a5606c..ac8b2ecdb86886 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: 2024-05-24 +date: 2024-05-28 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 1626885b30fd53..577f660827c502 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: 2024-05-24 +date: 2024-05-28 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 a26682ad0cd7b0..400ffc727f1cf4 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: 2024-05-24 +date: 2024-05-28 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 7517e2f8a7103e..774ecae2af60b0 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: 2024-05-24 +date: 2024-05-28 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 aef3b62400f777..3ca608c2225c67 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: 2024-05-24 +date: 2024-05-28 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 05e14961ed4598..77805221829b84 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: 2024-05-24 +date: 2024-05-28 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 c615cd0ca2eb48..0482ca855b8487 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: 2024-05-24 +date: 2024-05-28 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 ebb4a7419971a7..cd7f9abe0542ef 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: 2024-05-24 +date: 2024-05-28 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 6db6f1c107d7bb..91dc7ffdd0a2d5 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: 2024-05-24 +date: 2024-05-28 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 8e6df37ac19dc6..356e704a4d4601 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: 2024-05-24 +date: 2024-05-28 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 4ecc6dbae3096d..036afa23222c9a 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: 2024-05-24 +date: 2024-05-28 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 0f115a7445ba79..ed2a55f16bd9f3 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: 2024-05-24 +date: 2024-05-28 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 9dc7879b039b62..089b6e2a37e53e 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: 2024-05-24 +date: 2024-05-28 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 5fedd7d2e3e573..3f71a1789c7de9 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: 2024-05-24 +date: 2024-05-28 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 634b80ac147859..1d1e99115f5510 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: 2024-05-24 +date: 2024-05-28 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 e5857583c8e0e6..bddf8998e70555 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: 2024-05-24 +date: 2024-05-28 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 506260dc916f44..c35579dbd0bad1 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: 2024-05-24 +date: 2024-05-28 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 73a4d43652894a..ed4a6a2c4c6796 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: 2024-05-24 +date: 2024-05-28 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 9870a636432e08..a1ee7844ff6117 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: 2024-05-24 +date: 2024-05-28 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 e07c71e9fc17ea..2ca1da3c9cfafb 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: 2024-05-24 +date: 2024-05-28 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 dc57562f00e24b..72041fa258b46a 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: 2024-05-24 +date: 2024-05-28 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 f905a64f99f5a5..38266ecec943da 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: 2024-05-24 +date: 2024-05-28 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 33a7e122ad9bf9..33f86b334c3123 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: 2024-05-24 +date: 2024-05-28 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 d1affa7bad6b7c..41dca57ab3dd78 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: 2024-05-24 +date: 2024-05-28 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 f1c9fe28bae6af..d8442a823ee14d 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: 2024-05-24 +date: 2024-05-28 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 7d875dcbd9a911..8ca2bfa3fa8924 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: 2024-05-24 +date: 2024-05-28 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 7e2990bb8af2d7..efc3e1da582ab6 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: 2024-05-24 +date: 2024-05-28 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 27ffa9ef4f07ba..3ecd7bbf62be5f 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: 2024-05-24 +date: 2024-05-28 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 15f5473fdd3fd4..2214efedcead94 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: 2024-05-24 +date: 2024-05-28 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 aeee55b47bb4cf..9bcd713d9769c4 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: 2024-05-24 +date: 2024-05-28 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 f08fd4db352e12..18583e84b540d7 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: 2024-05-24 +date: 2024-05-28 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 e6b28c433b143d..f27d2cce6ee6a8 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: 2024-05-24 +date: 2024-05-28 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 96b4e15963e3ca..66ca34344ffab5 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: 2024-05-24 +date: 2024-05-28 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 903caff878113f..b6c99c21a320f2 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: 2024-05-24 +date: 2024-05-28 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 8eaa477b382698..020360574945e2 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: 2024-05-24 +date: 2024-05-28 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 67943d3220a3b4..19fa71c2733a98 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: 2024-05-24 +date: 2024-05-28 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 93994d6ba4a48a..486e7f4cca0385 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: 2024-05-24 +date: 2024-05-28 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 70a4cbc2bb5c5b..86165bc4254991 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: 2024-05-24 +date: 2024-05-28 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 246ff76ea20812..200d5fc5d45e83 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: 2024-05-24 +date: 2024-05-28 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 9dd4c242b682b6..8f6d68c35189ae 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: 2024-05-24 +date: 2024-05-28 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 658f5c2012b507..ae0cec5d3f86fb 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: 2024-05-24 +date: 2024-05-28 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 3b2a4e84d9a23a..43fc1be850f3e7 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: 2024-05-24 +date: 2024-05-28 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 45dea9af5ef193..7228c249d4a460 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: 2024-05-24 +date: 2024-05-28 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 f2ab0d311c1e73..b0ba544f6e194c 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: 2024-05-24 +date: 2024-05-28 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 38a44632bd6996..486cfb0e8f1af1 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: 2024-05-24 +date: 2024-05-28 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 9387238ee777b9..bfa3397f9a2dc7 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: 2024-05-24 +date: 2024-05-28 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 29c0c7b25bbe4f..875895d2299ace 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: 2024-05-24 +date: 2024-05-28 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 058f48d70b65d2..a5429ec68afd67 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: 2024-05-24 +date: 2024-05-28 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 cde449b27df852..05df20edc56bd7 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: 2024-05-24 +date: 2024-05-28 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 9d14aaaef2f6d1..d2982d4c3901ac 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: 2024-05-24 +date: 2024-05-28 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 89cff734a61c77..7355834d0e7276 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: 2024-05-24 +date: 2024-05-28 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 c690cc8d0066ff..5368a9700a81ab 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: 2024-05-24 +date: 2024-05-28 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 d988350d76af2e..9d1725b174cc49 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: 2024-05-24 +date: 2024-05-28 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 dbb6c83e22af6f..4228467d989854 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: 2024-05-24 +date: 2024-05-28 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 b2280925e61c85..c51a84f1de3a90 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: 2024-05-24 +date: 2024-05-28 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 0f2b5014ebfbf8..d44112a7eb83cc 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: 2024-05-24 +date: 2024-05-28 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 66873d96ba67d8..7413ae637f1a94 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: 2024-05-24 +date: 2024-05-28 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 919690a73c9056..bea20d7b5a7253 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: 2024-05-24 +date: 2024-05-28 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 b91a8d0b2e51c5..44da3921a64efd 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: 2024-05-24 +date: 2024-05-28 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 db7f47c50fecc5..e68c3344b3b774 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: 2024-05-24 +date: 2024-05-28 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 e2f19cc0e16954..d44c41d9c8fd27 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: 2024-05-24 +date: 2024-05-28 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 e71a493e54c21d..760b6912c4f6ca 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: 2024-05-24 +date: 2024-05-28 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 b93b9bd01aab6b..7b6f56426f64ce 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: 2024-05-24 +date: 2024-05-28 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 b4f3cbe416c20c..e1998b0c19d27f 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: 2024-05-24 +date: 2024-05-28 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 809b786f3b0993..8c22d9754d30be 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: 2024-05-24 +date: 2024-05-28 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 c95d628631b4eb..07d3a30095b423 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: 2024-05-24 +date: 2024-05-28 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 99fe7f480f4694..73a227db7df265 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: 2024-05-24 +date: 2024-05-28 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 0acb5fa66878b9..45f855b6eb93d7 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: 2024-05-24 +date: 2024-05-28 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 5205f5723eded2..836a79d2e1c855 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: 2024-05-24 +date: 2024-05-28 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 cafd27968b47c5..617489c2c03377 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: 2024-05-24 +date: 2024-05-28 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 ee3a3ef922889f..354e4d5df4f092 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: 2024-05-24 +date: 2024-05-28 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 47afedb514a44d..481bc142f3f822 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: 2024-05-24 +date: 2024-05-28 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 39e02eaec4e28e..87ebf1669431e2 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: 2024-05-24 +date: 2024-05-28 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 d09b2c9607e908..46dd4ca1daacb5 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: 2024-05-24 +date: 2024-05-28 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 f20ae899ae115d..74237bd9f16a49 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: 2024-05-24 +date: 2024-05-28 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 bf42fdb880e923..3ed1f26b2059af 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: 2024-05-24 +date: 2024-05-28 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 033840ccaf2238..0edb895212bd32 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: 2024-05-24 +date: 2024-05-28 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 1fa374ab67fa66..e851940d172bcc 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: 2024-05-24 +date: 2024-05-28 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 c708dff320ee0c..72483fd9561aad 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: 2024-05-24 +date: 2024-05-28 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 cab71dee4f10ad..62386c15dc18d8 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: 2024-05-24 +date: 2024-05-28 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 b6391fda8d90b4..26f71ec661e8c1 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: 2024-05-24 +date: 2024-05-28 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 215cf72b17249e..6429958b1e5a49 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: 2024-05-24 +date: 2024-05-28 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 2dd46e8075f3ed..8c727e936f4ef5 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: 2024-05-24 +date: 2024-05-28 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 c4f554dc70e36c..08c08d866a3612 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: 2024-05-24 +date: 2024-05-28 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 8858d1dcaaae9c..3a397081ac1aa7 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: 2024-05-24 +date: 2024-05-28 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 cee49ecc81b182..c72e1b30f7920f 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: 2024-05-24 +date: 2024-05-28 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 615e68ba54f44e..06120017fe312d 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: 2024-05-24 +date: 2024-05-28 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 040f88ba20e87a..e5a76743fa8cee 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: 2024-05-24 +date: 2024-05-28 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 237abe52549bca..8cb2b12ec673b7 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: 2024-05-24 +date: 2024-05-28 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 113645d1d5adb2..36a4dee062c233 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: 2024-05-24 +date: 2024-05-28 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 840c3a551a3d61..ab5d046018f0bd 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: 2024-05-24 +date: 2024-05-28 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 cbab3add667fb9..70d81a6cc29c3d 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: 2024-05-24 +date: 2024-05-28 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 99fb0a7c82fd1e..e7e9709514ffe1 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: 2024-05-24 +date: 2024-05-28 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_contracts_browser.mdx b/api_docs/kbn_core_plugins_contracts_browser.mdx index d44f7fdb8e0ed8..0c29b412b38042 100644 --- a/api_docs/kbn_core_plugins_contracts_browser.mdx +++ b/api_docs/kbn_core_plugins_contracts_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-plugins-contracts-browser title: "@kbn/core-plugins-contracts-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-plugins-contracts-browser plugin -date: 2024-05-24 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-plugins-contracts-browser'] --- import kbnCorePluginsContractsBrowserObj from './kbn_core_plugins_contracts_browser.devdocs.json'; diff --git a/api_docs/kbn_core_plugins_contracts_server.mdx b/api_docs/kbn_core_plugins_contracts_server.mdx index 15203507883b88..a725580f9e4aac 100644 --- a/api_docs/kbn_core_plugins_contracts_server.mdx +++ b/api_docs/kbn_core_plugins_contracts_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-plugins-contracts-server title: "@kbn/core-plugins-contracts-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-plugins-contracts-server plugin -date: 2024-05-24 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-plugins-contracts-server'] --- import kbnCorePluginsContractsServerObj from './kbn_core_plugins_contracts_server.devdocs.json'; diff --git a/api_docs/kbn_core_plugins_server.mdx b/api_docs/kbn_core_plugins_server.mdx index 881809caceabd1..aba8f07b22426d 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: 2024-05-24 +date: 2024-05-28 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 8279126418348e..4ff9e4a5580b02 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: 2024-05-24 +date: 2024-05-28 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 ebfd1fed3cab6f..c3442c405e06be 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: 2024-05-24 +date: 2024-05-28 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 334685d46009ec..ea2be412fd269e 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: 2024-05-24 +date: 2024-05-28 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 af6cf50f805834..5747b2dc802dd1 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: 2024-05-24 +date: 2024-05-28 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 7ba99832890a67..8b6630274b334e 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: 2024-05-24 +date: 2024-05-28 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 2337263f039af3..750c6324a9f79d 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: 2024-05-24 +date: 2024-05-28 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 eb4d4e5e30b8a4..211796a69dd49a 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: 2024-05-24 +date: 2024-05-28 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 582a1f1bbb3240..558cf3c666d60a 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: 2024-05-24 +date: 2024-05-28 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 8cb8cc10831547..62f7a46948a884 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: 2024-05-24 +date: 2024-05-28 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_mocks.mdx b/api_docs/kbn_core_saved_objects_api_server_mocks.mdx index 7fa566a6bc5c26..e5743a11f333fe 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: 2024-05-24 +date: 2024-05-28 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 bdfeff8968db83..81cca043b96bdd 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: 2024-05-24 +date: 2024-05-28 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 4e9ed55bf5ddee..a94a8de143167c 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: 2024-05-24 +date: 2024-05-28 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 bdb3cde6e95ca7..76b0083ae60a77 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: 2024-05-24 +date: 2024-05-28 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 0a8106649b7f47..7479fa6df20fba 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: 2024-05-24 +date: 2024-05-28 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 c75d1534d77a78..7e49dd726c6dfe 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: 2024-05-24 +date: 2024-05-28 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 c6a4bac606d53f..e4da98e458fbf1 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: 2024-05-24 +date: 2024-05-28 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 43a64a3b8edbf5..ab10087757e45b 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: 2024-05-24 +date: 2024-05-28 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 58d157ca34e2ad..d2561f4d9458f0 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: 2024-05-24 +date: 2024-05-28 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 ae1b24ce3185d2..ad81357c9c44b3 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: 2024-05-24 +date: 2024-05-28 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 5004dc16302429..733a3c22707318 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: 2024-05-24 +date: 2024-05-28 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 3adbcde8689dff..c588bf735e9215 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: 2024-05-24 +date: 2024-05-28 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 41ce61850472cd..300a1e8f967dd6 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: 2024-05-24 +date: 2024-05-28 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 59e828b84221a8..5208c71b5864fd 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: 2024-05-24 +date: 2024-05-28 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 d27fba8941cd1a..82425597738f8c 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: 2024-05-24 +date: 2024-05-28 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_security_browser.mdx b/api_docs/kbn_core_security_browser.mdx index 484f1cc9044fd0..276a54be6f6a84 100644 --- a/api_docs/kbn_core_security_browser.mdx +++ b/api_docs/kbn_core_security_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-security-browser title: "@kbn/core-security-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-security-browser plugin -date: 2024-05-24 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-security-browser'] --- import kbnCoreSecurityBrowserObj from './kbn_core_security_browser.devdocs.json'; diff --git a/api_docs/kbn_core_security_browser_internal.mdx b/api_docs/kbn_core_security_browser_internal.mdx index afab3cec5b44d6..ed92aab474ba3b 100644 --- a/api_docs/kbn_core_security_browser_internal.mdx +++ b/api_docs/kbn_core_security_browser_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-security-browser-internal title: "@kbn/core-security-browser-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-security-browser-internal plugin -date: 2024-05-24 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-security-browser-internal'] --- import kbnCoreSecurityBrowserInternalObj from './kbn_core_security_browser_internal.devdocs.json'; diff --git a/api_docs/kbn_core_security_browser_mocks.mdx b/api_docs/kbn_core_security_browser_mocks.mdx index 24f255c1b8fb6e..fae89452a15f1e 100644 --- a/api_docs/kbn_core_security_browser_mocks.mdx +++ b/api_docs/kbn_core_security_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-security-browser-mocks title: "@kbn/core-security-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-security-browser-mocks plugin -date: 2024-05-24 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-security-browser-mocks'] --- import kbnCoreSecurityBrowserMocksObj from './kbn_core_security_browser_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_security_common.mdx b/api_docs/kbn_core_security_common.mdx index 9caf0af1fa8158..7a4b5723092af8 100644 --- a/api_docs/kbn_core_security_common.mdx +++ b/api_docs/kbn_core_security_common.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-security-common title: "@kbn/core-security-common" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-security-common plugin -date: 2024-05-24 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-security-common'] --- import kbnCoreSecurityCommonObj from './kbn_core_security_common.devdocs.json'; diff --git a/api_docs/kbn_core_security_server.mdx b/api_docs/kbn_core_security_server.mdx index ed80acfc298a24..3041aba8706460 100644 --- a/api_docs/kbn_core_security_server.mdx +++ b/api_docs/kbn_core_security_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-security-server title: "@kbn/core-security-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-security-server plugin -date: 2024-05-24 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-security-server'] --- import kbnCoreSecurityServerObj from './kbn_core_security_server.devdocs.json'; diff --git a/api_docs/kbn_core_security_server_internal.mdx b/api_docs/kbn_core_security_server_internal.mdx index 76f43f1bbec154..1de57284028280 100644 --- a/api_docs/kbn_core_security_server_internal.mdx +++ b/api_docs/kbn_core_security_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-security-server-internal title: "@kbn/core-security-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-security-server-internal plugin -date: 2024-05-24 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-security-server-internal'] --- import kbnCoreSecurityServerInternalObj from './kbn_core_security_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_security_server_mocks.mdx b/api_docs/kbn_core_security_server_mocks.mdx index 152e83d41617cc..84fff0b44e96bd 100644 --- a/api_docs/kbn_core_security_server_mocks.mdx +++ b/api_docs/kbn_core_security_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-security-server-mocks title: "@kbn/core-security-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-security-server-mocks plugin -date: 2024-05-24 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-security-server-mocks'] --- import kbnCoreSecurityServerMocksObj from './kbn_core_security_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_status_common.mdx b/api_docs/kbn_core_status_common.mdx index b35f833d7cb1de..d59f43b8b7dded 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: 2024-05-24 +date: 2024-05-28 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 ecadb62630b4c6..00a434c7bcfec9 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: 2024-05-24 +date: 2024-05-28 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 40dbe315eb6057..e85d4ff8dddcd0 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: 2024-05-24 +date: 2024-05-28 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 f3182808584533..c3614605f87d31 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: 2024-05-24 +date: 2024-05-28 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 01f361fc3f45e8..312ddbe7e6a7ea 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: 2024-05-24 +date: 2024-05-28 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 eed6e28929e8ae..79dfb4b2b5fc27 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: 2024-05-24 +date: 2024-05-28 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 a0554906da7b3c..c796585c7a0406 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: 2024-05-24 +date: 2024-05-28 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 8842ee450ac864..d1bd261befd667 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: 2024-05-24 +date: 2024-05-28 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_model_versions.mdx b/api_docs/kbn_core_test_helpers_model_versions.mdx index df902033b253dc..accdf730d79ba8 100644 --- a/api_docs/kbn_core_test_helpers_model_versions.mdx +++ b/api_docs/kbn_core_test_helpers_model_versions.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-test-helpers-model-versions title: "@kbn/core-test-helpers-model-versions" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-test-helpers-model-versions plugin -date: 2024-05-24 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-test-helpers-model-versions'] --- import kbnCoreTestHelpersModelVersionsObj from './kbn_core_test_helpers_model_versions.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 ea360da7fea078..624939b00737ca 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: 2024-05-24 +date: 2024-05-28 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 d4ebaa50554591..534dd533500570 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: 2024-05-24 +date: 2024-05-28 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 5f1705cfb81ec1..5d1c2292454ef7 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: 2024-05-24 +date: 2024-05-28 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_mocks.mdx b/api_docs/kbn_core_theme_browser_mocks.mdx index 71e27678c1b6bf..cf426aa0b6f98c 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: 2024-05-24 +date: 2024-05-28 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 f85d0acd908ab9..1ecb4f03ec7814 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: 2024-05-24 +date: 2024-05-28 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 6736e770029111..14f69f89a6479a 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: 2024-05-24 +date: 2024-05-28 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 396c012dd7bb67..b358e8b3556fc8 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: 2024-05-24 +date: 2024-05-28 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 3ed1e3f60ca3ba..73740388f54c08 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: 2024-05-24 +date: 2024-05-28 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 8313566bd60c18..86913e6d307f8f 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: 2024-05-24 +date: 2024-05-28 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 c8727822cac46f..7282231e16bc29 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: 2024-05-24 +date: 2024-05-28 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 ad6d7947fa44f5..d837aed57e42df 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: 2024-05-24 +date: 2024-05-28 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 39e96506eff7fd..062d7d9db798d3 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: 2024-05-24 +date: 2024-05-28 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 dcdcfcbac3a454..85a91a241d5a4b 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: 2024-05-24 +date: 2024-05-28 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 0ee933bf08e9e2..ef17edf87d83c5 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: 2024-05-24 +date: 2024-05-28 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_core_user_profile_browser.mdx b/api_docs/kbn_core_user_profile_browser.mdx index 1ebbc45a16cd9b..3c4ba2b2ec6ce5 100644 --- a/api_docs/kbn_core_user_profile_browser.mdx +++ b/api_docs/kbn_core_user_profile_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-user-profile-browser title: "@kbn/core-user-profile-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-user-profile-browser plugin -date: 2024-05-24 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-user-profile-browser'] --- import kbnCoreUserProfileBrowserObj from './kbn_core_user_profile_browser.devdocs.json'; diff --git a/api_docs/kbn_core_user_profile_browser_internal.mdx b/api_docs/kbn_core_user_profile_browser_internal.mdx index 6bdce20ec4569c..c4b23ae565b5b6 100644 --- a/api_docs/kbn_core_user_profile_browser_internal.mdx +++ b/api_docs/kbn_core_user_profile_browser_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-user-profile-browser-internal title: "@kbn/core-user-profile-browser-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-user-profile-browser-internal plugin -date: 2024-05-24 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-user-profile-browser-internal'] --- import kbnCoreUserProfileBrowserInternalObj from './kbn_core_user_profile_browser_internal.devdocs.json'; diff --git a/api_docs/kbn_core_user_profile_browser_mocks.mdx b/api_docs/kbn_core_user_profile_browser_mocks.mdx index 9a8c2469367b2c..891d867958c154 100644 --- a/api_docs/kbn_core_user_profile_browser_mocks.mdx +++ b/api_docs/kbn_core_user_profile_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-user-profile-browser-mocks title: "@kbn/core-user-profile-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-user-profile-browser-mocks plugin -date: 2024-05-24 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-user-profile-browser-mocks'] --- import kbnCoreUserProfileBrowserMocksObj from './kbn_core_user_profile_browser_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_user_profile_common.mdx b/api_docs/kbn_core_user_profile_common.mdx index 672b82605c7f3c..57f3416f9d8c65 100644 --- a/api_docs/kbn_core_user_profile_common.mdx +++ b/api_docs/kbn_core_user_profile_common.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-user-profile-common title: "@kbn/core-user-profile-common" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-user-profile-common plugin -date: 2024-05-24 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-user-profile-common'] --- import kbnCoreUserProfileCommonObj from './kbn_core_user_profile_common.devdocs.json'; diff --git a/api_docs/kbn_core_user_profile_server.mdx b/api_docs/kbn_core_user_profile_server.mdx index 3b47905618f9a6..501d186f61c2d6 100644 --- a/api_docs/kbn_core_user_profile_server.mdx +++ b/api_docs/kbn_core_user_profile_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-user-profile-server title: "@kbn/core-user-profile-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-user-profile-server plugin -date: 2024-05-24 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-user-profile-server'] --- import kbnCoreUserProfileServerObj from './kbn_core_user_profile_server.devdocs.json'; diff --git a/api_docs/kbn_core_user_profile_server_internal.mdx b/api_docs/kbn_core_user_profile_server_internal.mdx index 685087c866ac09..b9699e9a70bec6 100644 --- a/api_docs/kbn_core_user_profile_server_internal.mdx +++ b/api_docs/kbn_core_user_profile_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-user-profile-server-internal title: "@kbn/core-user-profile-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-user-profile-server-internal plugin -date: 2024-05-24 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-user-profile-server-internal'] --- import kbnCoreUserProfileServerInternalObj from './kbn_core_user_profile_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_user_profile_server_mocks.mdx b/api_docs/kbn_core_user_profile_server_mocks.mdx index 2f5d5d868e78e4..013da69ed415c9 100644 --- a/api_docs/kbn_core_user_profile_server_mocks.mdx +++ b/api_docs/kbn_core_user_profile_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-user-profile-server-mocks title: "@kbn/core-user-profile-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-user-profile-server-mocks plugin -date: 2024-05-24 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-user-profile-server-mocks'] --- import kbnCoreUserProfileServerMocksObj from './kbn_core_user_profile_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_user_settings_server.mdx b/api_docs/kbn_core_user_settings_server.mdx index 9feb4db9089a25..4eaf318613874b 100644 --- a/api_docs/kbn_core_user_settings_server.mdx +++ b/api_docs/kbn_core_user_settings_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-user-settings-server title: "@kbn/core-user-settings-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-user-settings-server plugin -date: 2024-05-24 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-user-settings-server'] --- import kbnCoreUserSettingsServerObj from './kbn_core_user_settings_server.devdocs.json'; diff --git a/api_docs/kbn_core_user_settings_server_mocks.mdx b/api_docs/kbn_core_user_settings_server_mocks.mdx index 6cd36c4a4e590e..91e4381580aefa 100644 --- a/api_docs/kbn_core_user_settings_server_mocks.mdx +++ b/api_docs/kbn_core_user_settings_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-user-settings-server-mocks title: "@kbn/core-user-settings-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-user-settings-server-mocks plugin -date: 2024-05-24 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-user-settings-server-mocks'] --- import kbnCoreUserSettingsServerMocksObj from './kbn_core_user_settings_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_crypto.mdx b/api_docs/kbn_crypto.mdx index 5c102083835e86..ce8e5d0f8b22d9 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: 2024-05-24 +date: 2024-05-28 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 3f972ca4645112..52695b01d67260 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: 2024-05-24 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/crypto-browser'] --- import kbnCryptoBrowserObj from './kbn_crypto_browser.devdocs.json'; diff --git a/api_docs/kbn_custom_icons.mdx b/api_docs/kbn_custom_icons.mdx index 5bc3ae6210cc6b..14cf623f7c90f2 100644 --- a/api_docs/kbn_custom_icons.mdx +++ b/api_docs/kbn_custom_icons.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-custom-icons title: "@kbn/custom-icons" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/custom-icons plugin -date: 2024-05-24 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/custom-icons'] --- import kbnCustomIconsObj from './kbn_custom_icons.devdocs.json'; diff --git a/api_docs/kbn_custom_integrations.mdx b/api_docs/kbn_custom_integrations.mdx index cee0f7c998215e..77183dcdda0e55 100644 --- a/api_docs/kbn_custom_integrations.mdx +++ b/api_docs/kbn_custom_integrations.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-custom-integrations title: "@kbn/custom-integrations" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/custom-integrations plugin -date: 2024-05-24 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/custom-integrations'] --- import kbnCustomIntegrationsObj from './kbn_custom_integrations.devdocs.json'; diff --git a/api_docs/kbn_cypress_config.mdx b/api_docs/kbn_cypress_config.mdx index fd5777f4625b48..51392e5d1c7342 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: 2024-05-24 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/cypress-config'] --- import kbnCypressConfigObj from './kbn_cypress_config.devdocs.json'; diff --git a/api_docs/kbn_data_forge.mdx b/api_docs/kbn_data_forge.mdx index ac67687e057174..92027123e10722 100644 --- a/api_docs/kbn_data_forge.mdx +++ b/api_docs/kbn_data_forge.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-data-forge title: "@kbn/data-forge" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/data-forge plugin -date: 2024-05-24 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/data-forge'] --- import kbnDataForgeObj from './kbn_data_forge.devdocs.json'; diff --git a/api_docs/kbn_data_service.mdx b/api_docs/kbn_data_service.mdx index e8a59e06302444..8d4310ca9e4b0b 100644 --- a/api_docs/kbn_data_service.mdx +++ b/api_docs/kbn_data_service.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-data-service title: "@kbn/data-service" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/data-service plugin -date: 2024-05-24 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/data-service'] --- import kbnDataServiceObj from './kbn_data_service.devdocs.json'; diff --git a/api_docs/kbn_data_stream_adapter.mdx b/api_docs/kbn_data_stream_adapter.mdx index c4af9074bcd956..b3f7ac69274401 100644 --- a/api_docs/kbn_data_stream_adapter.mdx +++ b/api_docs/kbn_data_stream_adapter.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-data-stream-adapter title: "@kbn/data-stream-adapter" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/data-stream-adapter plugin -date: 2024-05-24 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/data-stream-adapter'] --- import kbnDataStreamAdapterObj from './kbn_data_stream_adapter.devdocs.json'; diff --git a/api_docs/kbn_data_view_utils.mdx b/api_docs/kbn_data_view_utils.mdx index 2f65f4412f9cd9..30a0e9f4f6c9f0 100644 --- a/api_docs/kbn_data_view_utils.mdx +++ b/api_docs/kbn_data_view_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-data-view-utils title: "@kbn/data-view-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/data-view-utils plugin -date: 2024-05-24 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/data-view-utils'] --- import kbnDataViewUtilsObj from './kbn_data_view_utils.devdocs.json'; diff --git a/api_docs/kbn_datemath.mdx b/api_docs/kbn_datemath.mdx index e8ca952c1c61d6..dd2d7694d16b67 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: 2024-05-24 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/datemath'] --- import kbnDatemathObj from './kbn_datemath.devdocs.json'; diff --git a/api_docs/kbn_deeplinks_analytics.mdx b/api_docs/kbn_deeplinks_analytics.mdx index 36144d2ae23742..18a4f625e0b5d2 100644 --- a/api_docs/kbn_deeplinks_analytics.mdx +++ b/api_docs/kbn_deeplinks_analytics.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-deeplinks-analytics title: "@kbn/deeplinks-analytics" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/deeplinks-analytics plugin -date: 2024-05-24 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/deeplinks-analytics'] --- import kbnDeeplinksAnalyticsObj from './kbn_deeplinks_analytics.devdocs.json'; diff --git a/api_docs/kbn_deeplinks_devtools.mdx b/api_docs/kbn_deeplinks_devtools.mdx index eb56875bf539c1..b41717cb7df4db 100644 --- a/api_docs/kbn_deeplinks_devtools.mdx +++ b/api_docs/kbn_deeplinks_devtools.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-deeplinks-devtools title: "@kbn/deeplinks-devtools" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/deeplinks-devtools plugin -date: 2024-05-24 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/deeplinks-devtools'] --- import kbnDeeplinksDevtoolsObj from './kbn_deeplinks_devtools.devdocs.json'; diff --git a/api_docs/kbn_deeplinks_fleet.mdx b/api_docs/kbn_deeplinks_fleet.mdx index 151f7616afa679..e1f13f7b687443 100644 --- a/api_docs/kbn_deeplinks_fleet.mdx +++ b/api_docs/kbn_deeplinks_fleet.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-deeplinks-fleet title: "@kbn/deeplinks-fleet" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/deeplinks-fleet plugin -date: 2024-05-24 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/deeplinks-fleet'] --- import kbnDeeplinksFleetObj from './kbn_deeplinks_fleet.devdocs.json'; diff --git a/api_docs/kbn_deeplinks_management.mdx b/api_docs/kbn_deeplinks_management.mdx index 73b414f99d802d..0465ac5bc90fe5 100644 --- a/api_docs/kbn_deeplinks_management.mdx +++ b/api_docs/kbn_deeplinks_management.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-deeplinks-management title: "@kbn/deeplinks-management" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/deeplinks-management plugin -date: 2024-05-24 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/deeplinks-management'] --- import kbnDeeplinksManagementObj from './kbn_deeplinks_management.devdocs.json'; diff --git a/api_docs/kbn_deeplinks_ml.mdx b/api_docs/kbn_deeplinks_ml.mdx index cc0d5c6e96b7ee..80097e64233d9f 100644 --- a/api_docs/kbn_deeplinks_ml.mdx +++ b/api_docs/kbn_deeplinks_ml.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-deeplinks-ml title: "@kbn/deeplinks-ml" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/deeplinks-ml plugin -date: 2024-05-24 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/deeplinks-ml'] --- import kbnDeeplinksMlObj from './kbn_deeplinks_ml.devdocs.json'; diff --git a/api_docs/kbn_deeplinks_observability.mdx b/api_docs/kbn_deeplinks_observability.mdx index c4c27bece7bff2..4e8bf129e445dd 100644 --- a/api_docs/kbn_deeplinks_observability.mdx +++ b/api_docs/kbn_deeplinks_observability.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-deeplinks-observability title: "@kbn/deeplinks-observability" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/deeplinks-observability plugin -date: 2024-05-24 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/deeplinks-observability'] --- import kbnDeeplinksObservabilityObj from './kbn_deeplinks_observability.devdocs.json'; diff --git a/api_docs/kbn_deeplinks_search.mdx b/api_docs/kbn_deeplinks_search.mdx index dfae276b32bdb5..8d2d195bbc3455 100644 --- a/api_docs/kbn_deeplinks_search.mdx +++ b/api_docs/kbn_deeplinks_search.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-deeplinks-search title: "@kbn/deeplinks-search" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/deeplinks-search plugin -date: 2024-05-24 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/deeplinks-search'] --- import kbnDeeplinksSearchObj from './kbn_deeplinks_search.devdocs.json'; diff --git a/api_docs/kbn_deeplinks_security.mdx b/api_docs/kbn_deeplinks_security.mdx index 995b83f949ee13..c069d4bab551d5 100644 --- a/api_docs/kbn_deeplinks_security.mdx +++ b/api_docs/kbn_deeplinks_security.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-deeplinks-security title: "@kbn/deeplinks-security" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/deeplinks-security plugin -date: 2024-05-24 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/deeplinks-security'] --- import kbnDeeplinksSecurityObj from './kbn_deeplinks_security.devdocs.json'; diff --git a/api_docs/kbn_deeplinks_shared.mdx b/api_docs/kbn_deeplinks_shared.mdx index 79cc3b9e3a5c53..8d7b97be64fb83 100644 --- a/api_docs/kbn_deeplinks_shared.mdx +++ b/api_docs/kbn_deeplinks_shared.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-deeplinks-shared title: "@kbn/deeplinks-shared" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/deeplinks-shared plugin -date: 2024-05-24 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/deeplinks-shared'] --- import kbnDeeplinksSharedObj from './kbn_deeplinks_shared.devdocs.json'; diff --git a/api_docs/kbn_default_nav_analytics.mdx b/api_docs/kbn_default_nav_analytics.mdx index b769d08ec89178..7bbbd0c040f92d 100644 --- a/api_docs/kbn_default_nav_analytics.mdx +++ b/api_docs/kbn_default_nav_analytics.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-default-nav-analytics title: "@kbn/default-nav-analytics" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/default-nav-analytics plugin -date: 2024-05-24 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/default-nav-analytics'] --- import kbnDefaultNavAnalyticsObj from './kbn_default_nav_analytics.devdocs.json'; diff --git a/api_docs/kbn_default_nav_devtools.mdx b/api_docs/kbn_default_nav_devtools.mdx index fa2f1667f9ab71..552d71068622c0 100644 --- a/api_docs/kbn_default_nav_devtools.mdx +++ b/api_docs/kbn_default_nav_devtools.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-default-nav-devtools title: "@kbn/default-nav-devtools" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/default-nav-devtools plugin -date: 2024-05-24 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/default-nav-devtools'] --- import kbnDefaultNavDevtoolsObj from './kbn_default_nav_devtools.devdocs.json'; diff --git a/api_docs/kbn_default_nav_management.mdx b/api_docs/kbn_default_nav_management.mdx index 7e6b0c6aceef58..a2e92aca73c5f9 100644 --- a/api_docs/kbn_default_nav_management.mdx +++ b/api_docs/kbn_default_nav_management.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-default-nav-management title: "@kbn/default-nav-management" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/default-nav-management plugin -date: 2024-05-24 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/default-nav-management'] --- import kbnDefaultNavManagementObj from './kbn_default_nav_management.devdocs.json'; diff --git a/api_docs/kbn_default_nav_ml.mdx b/api_docs/kbn_default_nav_ml.mdx index 37de496d34fea0..d877823e71a79b 100644 --- a/api_docs/kbn_default_nav_ml.mdx +++ b/api_docs/kbn_default_nav_ml.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-default-nav-ml title: "@kbn/default-nav-ml" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/default-nav-ml plugin -date: 2024-05-24 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/default-nav-ml'] --- import kbnDefaultNavMlObj from './kbn_default_nav_ml.devdocs.json'; diff --git a/api_docs/kbn_dev_cli_errors.mdx b/api_docs/kbn_dev_cli_errors.mdx index 8c8ca98afc88ee..ae5cc67c6cdffe 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: 2024-05-24 +date: 2024-05-28 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 ed327603aa4513..2ae92aff98ae65 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: 2024-05-24 +date: 2024-05-28 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 1f51a030e621fb..db533b9a6762c7 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: 2024-05-24 +date: 2024-05-28 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 6893f75315f30a..60723e1b421ca7 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: 2024-05-24 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/dev-utils'] --- import kbnDevUtilsObj from './kbn_dev_utils.devdocs.json'; diff --git a/api_docs/kbn_discover_utils.mdx b/api_docs/kbn_discover_utils.mdx index ba2e201835e4a4..27defba60fdd9d 100644 --- a/api_docs/kbn_discover_utils.mdx +++ b/api_docs/kbn_discover_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-discover-utils title: "@kbn/discover-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/discover-utils plugin -date: 2024-05-24 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/discover-utils'] --- import kbnDiscoverUtilsObj from './kbn_discover_utils.devdocs.json'; diff --git a/api_docs/kbn_doc_links.mdx b/api_docs/kbn_doc_links.mdx index 1280623574e704..4bf2d4caf3e850 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: 2024-05-24 +date: 2024-05-28 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 a2f7e2d7114f68..a0700881d4663d 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: 2024-05-24 +date: 2024-05-28 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 e0a8b790a75cb9..901c42c21c1f3a 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: 2024-05-24 +date: 2024-05-28 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 e390f2fa93898e..f4805901fd8999 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: 2024-05-24 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ebt-tools'] --- import kbnEbtToolsObj from './kbn_ebt_tools.devdocs.json'; diff --git a/api_docs/kbn_ecs_data_quality_dashboard.mdx b/api_docs/kbn_ecs_data_quality_dashboard.mdx index 2875edb89ebb96..378b0bcb7b2168 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: 2024-05-24 +date: 2024-05-28 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_elastic_agent_utils.mdx b/api_docs/kbn_elastic_agent_utils.mdx index 09a97fc3212576..4fb3e7c0f2a8af 100644 --- a/api_docs/kbn_elastic_agent_utils.mdx +++ b/api_docs/kbn_elastic_agent_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-elastic-agent-utils title: "@kbn/elastic-agent-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/elastic-agent-utils plugin -date: 2024-05-24 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/elastic-agent-utils'] --- import kbnElasticAgentUtilsObj from './kbn_elastic_agent_utils.devdocs.json'; diff --git a/api_docs/kbn_elastic_assistant.mdx b/api_docs/kbn_elastic_assistant.mdx index 4398c2800491b1..e537e340c02cf0 100644 --- a/api_docs/kbn_elastic_assistant.mdx +++ b/api_docs/kbn_elastic_assistant.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-elastic-assistant title: "@kbn/elastic-assistant" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/elastic-assistant plugin -date: 2024-05-24 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/elastic-assistant'] --- import kbnElasticAssistantObj from './kbn_elastic_assistant.devdocs.json'; diff --git a/api_docs/kbn_elastic_assistant_common.mdx b/api_docs/kbn_elastic_assistant_common.mdx index 74a9d332fdcfc2..e183824b4c06d2 100644 --- a/api_docs/kbn_elastic_assistant_common.mdx +++ b/api_docs/kbn_elastic_assistant_common.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-elastic-assistant-common title: "@kbn/elastic-assistant-common" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/elastic-assistant-common plugin -date: 2024-05-24 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/elastic-assistant-common'] --- import kbnElasticAssistantCommonObj from './kbn_elastic_assistant_common.devdocs.json'; diff --git a/api_docs/kbn_entities_schema.mdx b/api_docs/kbn_entities_schema.mdx index c574bf1c4c09ae..7e2d8b6ac00e02 100644 --- a/api_docs/kbn_entities_schema.mdx +++ b/api_docs/kbn_entities_schema.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-entities-schema title: "@kbn/entities-schema" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/entities-schema plugin -date: 2024-05-24 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/entities-schema'] --- import kbnEntitiesSchemaObj from './kbn_entities_schema.devdocs.json'; diff --git a/api_docs/kbn_es.mdx b/api_docs/kbn_es.mdx index 6a10d6e71589bd..3e8d3cb9299f46 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: 2024-05-24 +date: 2024-05-28 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 b765f55d12d20b..0e51fdfc5d0423 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: 2024-05-24 +date: 2024-05-28 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 7300d6d64697e3..2ee1eb43700f7e 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: 2024-05-24 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/es-errors'] --- import kbnEsErrorsObj from './kbn_es_errors.devdocs.json'; diff --git a/api_docs/kbn_es_query.mdx b/api_docs/kbn_es_query.mdx index 0ee8c1a7f7cfac..621025f12b7ee5 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: 2024-05-24 +date: 2024-05-28 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 7d21cddb833e33..21d0720b6d29a5 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: 2024-05-24 +date: 2024-05-28 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 6c0d4507e809bf..01aa818219813c 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: 2024-05-24 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/eslint-plugin-imports'] --- import kbnEslintPluginImportsObj from './kbn_eslint_plugin_imports.devdocs.json'; diff --git a/api_docs/kbn_esql_ast.mdx b/api_docs/kbn_esql_ast.mdx index a509c6a9c3b4ce..41542b91b86694 100644 --- a/api_docs/kbn_esql_ast.mdx +++ b/api_docs/kbn_esql_ast.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-esql-ast title: "@kbn/esql-ast" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/esql-ast plugin -date: 2024-05-24 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/esql-ast'] --- import kbnEsqlAstObj from './kbn_esql_ast.devdocs.json'; diff --git a/api_docs/kbn_esql_utils.devdocs.json b/api_docs/kbn_esql_utils.devdocs.json index fc0f2cc69906f6..c24e9c4eea755a 100644 --- a/api_docs/kbn_esql_utils.devdocs.json +++ b/api_docs/kbn_esql_utils.devdocs.json @@ -1000,6 +1000,39 @@ "returnComment": [], "initialIsOpen": false }, + { + "parentPluginId": "@kbn/esql-utils", + "id": "def-common.hasTransformationalCommand", + "type": "Function", + "tags": [], + "label": "hasTransformationalCommand", + "description": [], + "signature": [ + "(esql: string | undefined) => boolean" + ], + "path": "packages/kbn-esql-utils/src/utils/query_parsing_helpers.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/esql-utils", + "id": "def-common.hasTransformationalCommand.$1", + "type": "string", + "tags": [], + "label": "esql", + "description": [], + "signature": [ + "string | undefined" + ], + "path": "packages/kbn-esql-utils/src/utils/query_parsing_helpers.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": false + } + ], + "returnComment": [], + "initialIsOpen": false + }, { "parentPluginId": "@kbn/esql-utils", "id": "def-common.removeDropCommandsFromESQLQuery", diff --git a/api_docs/kbn_esql_utils.mdx b/api_docs/kbn_esql_utils.mdx index 2f972d02212726..62d93f8c932153 100644 --- a/api_docs/kbn_esql_utils.mdx +++ b/api_docs/kbn_esql_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-esql-utils title: "@kbn/esql-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/esql-utils plugin -date: 2024-05-24 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/esql-utils'] --- import kbnEsqlUtilsObj from './kbn_esql_utils.devdocs.json'; @@ -21,7 +21,7 @@ Contact [@elastic/kibana-esql](https://github.com/orgs/elastic/teams/kibana-esql | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 52 | 0 | 50 | 0 | +| 54 | 0 | 52 | 0 | ## Common diff --git a/api_docs/kbn_esql_validation_autocomplete.devdocs.json b/api_docs/kbn_esql_validation_autocomplete.devdocs.json index 40625e66ca85da..b97b81a2eb1f55 100644 --- a/api_docs/kbn_esql_validation_autocomplete.devdocs.json +++ b/api_docs/kbn_esql_validation_autocomplete.devdocs.json @@ -1186,7 +1186,7 @@ "section": "def-common.FunctionDefinition", "text": "FunctionDefinition" }, - ", { withTypes }: { withTypes: boolean; }) => { declaration: string; examples: string[] | undefined; }[]" + ", { withTypes }: { withTypes: boolean; }) => { declaration: string; }[]" ], "path": "packages/kbn-esql-validation-autocomplete/src/definitions/helpers.ts", "deprecated": false, @@ -3235,7 +3235,21 @@ "FunctionParameterType", "; optional?: boolean | undefined; noNestingFunctions?: boolean | undefined; supportsWildcard?: boolean | undefined; constantOnly?: boolean | undefined; literalOptions?: string[] | undefined; literalSuggestions?: string[] | undefined; }[]; minParams?: number | undefined; returnType: ", "FunctionReturnType", - "; examples?: string[] | undefined; }[]" + "; }[]" + ], + "path": "packages/kbn-esql-validation-autocomplete/src/definitions/types.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/esql-validation-autocomplete", + "id": "def-common.FunctionDefinition.examples", + "type": "Array", + "tags": [], + "label": "examples", + "description": [], + "signature": [ + "string[] | undefined" ], "path": "packages/kbn-esql-validation-autocomplete/src/definitions/types.ts", "deprecated": false, diff --git a/api_docs/kbn_esql_validation_autocomplete.mdx b/api_docs/kbn_esql_validation_autocomplete.mdx index c97ed9f64cb63f..95072266816697 100644 --- a/api_docs/kbn_esql_validation_autocomplete.mdx +++ b/api_docs/kbn_esql_validation_autocomplete.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-esql-validation-autocomplete title: "@kbn/esql-validation-autocomplete" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/esql-validation-autocomplete plugin -date: 2024-05-24 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/esql-validation-autocomplete'] --- import kbnEsqlValidationAutocompleteObj from './kbn_esql_validation_autocomplete.devdocs.json'; @@ -21,7 +21,7 @@ Contact [@elastic/kibana-esql](https://github.com/orgs/elastic/teams/kibana-esql | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 193 | 0 | 183 | 10 | +| 194 | 0 | 184 | 10 | ## Common diff --git a/api_docs/kbn_event_annotation_common.mdx b/api_docs/kbn_event_annotation_common.mdx index 53a3c58c6bd0f3..9c5745d3550a97 100644 --- a/api_docs/kbn_event_annotation_common.mdx +++ b/api_docs/kbn_event_annotation_common.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-event-annotation-common title: "@kbn/event-annotation-common" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/event-annotation-common plugin -date: 2024-05-24 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/event-annotation-common'] --- import kbnEventAnnotationCommonObj from './kbn_event_annotation_common.devdocs.json'; diff --git a/api_docs/kbn_event_annotation_components.mdx b/api_docs/kbn_event_annotation_components.mdx index 69b2a23d70f538..d0bae04c9cf91b 100644 --- a/api_docs/kbn_event_annotation_components.mdx +++ b/api_docs/kbn_event_annotation_components.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-event-annotation-components title: "@kbn/event-annotation-components" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/event-annotation-components plugin -date: 2024-05-24 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/event-annotation-components'] --- import kbnEventAnnotationComponentsObj from './kbn_event_annotation_components.devdocs.json'; diff --git a/api_docs/kbn_expandable_flyout.mdx b/api_docs/kbn_expandable_flyout.mdx index f0c091e0cc7d81..eadab089c5db77 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: 2024-05-24 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/expandable-flyout'] --- import kbnExpandableFlyoutObj from './kbn_expandable_flyout.devdocs.json'; diff --git a/api_docs/kbn_field_types.mdx b/api_docs/kbn_field_types.mdx index e53515dc663750..ba2bb100fc381d 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: 2024-05-24 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/field-types'] --- import kbnFieldTypesObj from './kbn_field_types.devdocs.json'; diff --git a/api_docs/kbn_field_utils.mdx b/api_docs/kbn_field_utils.mdx index bd5f88ec95de35..c525a3fad92189 100644 --- a/api_docs/kbn_field_utils.mdx +++ b/api_docs/kbn_field_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-field-utils title: "@kbn/field-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/field-utils plugin -date: 2024-05-24 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/field-utils'] --- import kbnFieldUtilsObj from './kbn_field_utils.devdocs.json'; diff --git a/api_docs/kbn_find_used_node_modules.mdx b/api_docs/kbn_find_used_node_modules.mdx index 7282b9b37e8297..f257bf3c6e8f4c 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: 2024-05-24 +date: 2024-05-28 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_formatters.mdx b/api_docs/kbn_formatters.mdx index 5dd8c2c76fa091..0d4de92d2c6a0f 100644 --- a/api_docs/kbn_formatters.mdx +++ b/api_docs/kbn_formatters.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-formatters title: "@kbn/formatters" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/formatters plugin -date: 2024-05-24 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/formatters'] --- import kbnFormattersObj from './kbn_formatters.devdocs.json'; diff --git a/api_docs/kbn_ftr_common_functional_services.mdx b/api_docs/kbn_ftr_common_functional_services.mdx index a5859e459f2197..fb0b87445c39c0 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: 2024-05-24 +date: 2024-05-28 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_ftr_common_functional_ui_services.mdx b/api_docs/kbn_ftr_common_functional_ui_services.mdx index 779c8c4da71d85..01f3f1bce121e4 100644 --- a/api_docs/kbn_ftr_common_functional_ui_services.mdx +++ b/api_docs/kbn_ftr_common_functional_ui_services.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ftr-common-functional-ui-services title: "@kbn/ftr-common-functional-ui-services" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ftr-common-functional-ui-services plugin -date: 2024-05-24 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ftr-common-functional-ui-services'] --- import kbnFtrCommonFunctionalUiServicesObj from './kbn_ftr_common_functional_ui_services.devdocs.json'; diff --git a/api_docs/kbn_generate.mdx b/api_docs/kbn_generate.mdx index 39878417621118..66987ba2c49132 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: 2024-05-24 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/generate'] --- import kbnGenerateObj from './kbn_generate.devdocs.json'; diff --git a/api_docs/kbn_generate_console_definitions.mdx b/api_docs/kbn_generate_console_definitions.mdx index c7fa5d6528f985..098386387dfb34 100644 --- a/api_docs/kbn_generate_console_definitions.mdx +++ b/api_docs/kbn_generate_console_definitions.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-generate-console-definitions title: "@kbn/generate-console-definitions" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/generate-console-definitions plugin -date: 2024-05-24 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/generate-console-definitions'] --- import kbnGenerateConsoleDefinitionsObj from './kbn_generate_console_definitions.devdocs.json'; diff --git a/api_docs/kbn_generate_csv.mdx b/api_docs/kbn_generate_csv.mdx index 15c06958891396..e074d3526abbf3 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: 2024-05-24 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/generate-csv'] --- import kbnGenerateCsvObj from './kbn_generate_csv.devdocs.json'; diff --git a/api_docs/kbn_guided_onboarding.mdx b/api_docs/kbn_guided_onboarding.mdx index 3f0375b37859a3..39269521bead30 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: 2024-05-24 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/guided-onboarding'] --- import kbnGuidedOnboardingObj from './kbn_guided_onboarding.devdocs.json'; diff --git a/api_docs/kbn_handlebars.mdx b/api_docs/kbn_handlebars.mdx index 21c81a81c8787c..47a42b033220e5 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: 2024-05-24 +date: 2024-05-28 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 3759c8b702e094..c5a632de03c90f 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: 2024-05-24 +date: 2024-05-28 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 4dd24a81544255..979db0202f6411 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: 2024-05-24 +date: 2024-05-28 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 18e0ede1b58a69..c62d75358955b3 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: 2024-05-24 +date: 2024-05-28 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 a37376d6bf80db..0b7c5481122c83 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: 2024-05-24 +date: 2024-05-28 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 63101a6c97d067..6ca2efe6efed5d 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: 2024-05-24 +date: 2024-05-28 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 f35bf61546e584..168538970bfe83 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: 2024-05-24 +date: 2024-05-28 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 ed6f26bca7d75c..09396a77928cc9 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: 2024-05-24 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/import-resolver'] --- import kbnImportResolverObj from './kbn_import_resolver.devdocs.json'; diff --git a/api_docs/kbn_index_management.mdx b/api_docs/kbn_index_management.mdx index d3eeddca58b6c5..0a5cec326a0fdd 100644 --- a/api_docs/kbn_index_management.mdx +++ b/api_docs/kbn_index_management.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-index-management title: "@kbn/index-management" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/index-management plugin -date: 2024-05-24 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/index-management'] --- import kbnIndexManagementObj from './kbn_index_management.devdocs.json'; diff --git a/api_docs/kbn_inference_integration_flyout.mdx b/api_docs/kbn_inference_integration_flyout.mdx index cd4332ba3991e6..43f35ee662a729 100644 --- a/api_docs/kbn_inference_integration_flyout.mdx +++ b/api_docs/kbn_inference_integration_flyout.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-inference_integration_flyout title: "@kbn/inference_integration_flyout" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/inference_integration_flyout plugin -date: 2024-05-24 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/inference_integration_flyout'] --- import kbnInferenceIntegrationFlyoutObj from './kbn_inference_integration_flyout.devdocs.json'; diff --git a/api_docs/kbn_infra_forge.mdx b/api_docs/kbn_infra_forge.mdx index 1aa7b429a690ea..4a7cd386d6637a 100644 --- a/api_docs/kbn_infra_forge.mdx +++ b/api_docs/kbn_infra_forge.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-infra-forge title: "@kbn/infra-forge" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/infra-forge plugin -date: 2024-05-24 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/infra-forge'] --- import kbnInfraForgeObj from './kbn_infra_forge.devdocs.json'; diff --git a/api_docs/kbn_interpreter.mdx b/api_docs/kbn_interpreter.mdx index f43cd69b054595..d228ba81e2ae70 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: 2024-05-24 +date: 2024-05-28 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 6e0f088f1ce3d3..752154fe8205cd 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: 2024-05-24 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/io-ts-utils'] --- import kbnIoTsUtilsObj from './kbn_io_ts_utils.devdocs.json'; diff --git a/api_docs/kbn_ipynb.mdx b/api_docs/kbn_ipynb.mdx index 70f8b401985ed7..4bc857789e0359 100644 --- a/api_docs/kbn_ipynb.mdx +++ b/api_docs/kbn_ipynb.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ipynb title: "@kbn/ipynb" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ipynb plugin -date: 2024-05-24 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ipynb'] --- import kbnIpynbObj from './kbn_ipynb.devdocs.json'; diff --git a/api_docs/kbn_jest_serializers.mdx b/api_docs/kbn_jest_serializers.mdx index e9dcf100c3983e..ab93efb6d4b804 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: 2024-05-24 +date: 2024-05-28 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 82758f975d7b66..02465fea5b8b74 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: 2024-05-24 +date: 2024-05-28 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 f7869ab4f77e76..8947cb452905c8 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: 2024-05-24 +date: 2024-05-28 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 5295d7a8f15a8b..1a59adb8e9b3bd 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: 2024-05-24 +date: 2024-05-28 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 4a329327ef98f8..e7f5a88f78fd2c 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: 2024-05-24 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/language-documentation-popover'] --- import kbnLanguageDocumentationPopoverObj from './kbn_language_documentation_popover.devdocs.json'; diff --git a/api_docs/kbn_lens_embeddable_utils.mdx b/api_docs/kbn_lens_embeddable_utils.mdx index b3eff5d6863ff5..2ca1783bee27f9 100644 --- a/api_docs/kbn_lens_embeddable_utils.mdx +++ b/api_docs/kbn_lens_embeddable_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-lens-embeddable-utils title: "@kbn/lens-embeddable-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/lens-embeddable-utils plugin -date: 2024-05-24 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/lens-embeddable-utils'] --- import kbnLensEmbeddableUtilsObj from './kbn_lens_embeddable_utils.devdocs.json'; diff --git a/api_docs/kbn_lens_formula_docs.mdx b/api_docs/kbn_lens_formula_docs.mdx index bd89d7e082fced..2eb4ea4ab55eb9 100644 --- a/api_docs/kbn_lens_formula_docs.mdx +++ b/api_docs/kbn_lens_formula_docs.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-lens-formula-docs title: "@kbn/lens-formula-docs" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/lens-formula-docs plugin -date: 2024-05-24 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/lens-formula-docs'] --- import kbnLensFormulaDocsObj from './kbn_lens_formula_docs.devdocs.json'; diff --git a/api_docs/kbn_logging.mdx b/api_docs/kbn_logging.mdx index 7b672d039f3da8..3d9c2451779e1c 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: 2024-05-24 +date: 2024-05-28 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 9b386e9285e009..5a4b9876498a6f 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: 2024-05-24 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/logging-mocks'] --- import kbnLoggingMocksObj from './kbn_logging_mocks.devdocs.json'; diff --git a/api_docs/kbn_managed_content_badge.mdx b/api_docs/kbn_managed_content_badge.mdx index cb3510007411ff..186f54572c38ef 100644 --- a/api_docs/kbn_managed_content_badge.mdx +++ b/api_docs/kbn_managed_content_badge.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-managed-content-badge title: "@kbn/managed-content-badge" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/managed-content-badge plugin -date: 2024-05-24 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/managed-content-badge'] --- import kbnManagedContentBadgeObj from './kbn_managed_content_badge.devdocs.json'; diff --git a/api_docs/kbn_managed_vscode_config.mdx b/api_docs/kbn_managed_vscode_config.mdx index e3376c226ba735..0932f1a8f08003 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: 2024-05-24 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/managed-vscode-config'] --- import kbnManagedVscodeConfigObj from './kbn_managed_vscode_config.devdocs.json'; diff --git a/api_docs/kbn_management_cards_navigation.mdx b/api_docs/kbn_management_cards_navigation.mdx index 8eae7c73cb947c..013d1d0bf78956 100644 --- a/api_docs/kbn_management_cards_navigation.mdx +++ b/api_docs/kbn_management_cards_navigation.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-management-cards-navigation title: "@kbn/management-cards-navigation" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/management-cards-navigation plugin -date: 2024-05-24 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/management-cards-navigation'] --- import kbnManagementCardsNavigationObj from './kbn_management_cards_navigation.devdocs.json'; diff --git a/api_docs/kbn_management_settings_application.mdx b/api_docs/kbn_management_settings_application.mdx index 36eddd166218f1..d145246b4dee2e 100644 --- a/api_docs/kbn_management_settings_application.mdx +++ b/api_docs/kbn_management_settings_application.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-management-settings-application title: "@kbn/management-settings-application" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/management-settings-application plugin -date: 2024-05-24 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/management-settings-application'] --- import kbnManagementSettingsApplicationObj from './kbn_management_settings_application.devdocs.json'; diff --git a/api_docs/kbn_management_settings_components_field_category.mdx b/api_docs/kbn_management_settings_components_field_category.mdx index d0fff926be6907..2f5046db79835c 100644 --- a/api_docs/kbn_management_settings_components_field_category.mdx +++ b/api_docs/kbn_management_settings_components_field_category.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-management-settings-components-field-category title: "@kbn/management-settings-components-field-category" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/management-settings-components-field-category plugin -date: 2024-05-24 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/management-settings-components-field-category'] --- import kbnManagementSettingsComponentsFieldCategoryObj from './kbn_management_settings_components_field_category.devdocs.json'; diff --git a/api_docs/kbn_management_settings_components_field_input.mdx b/api_docs/kbn_management_settings_components_field_input.mdx index 6ef66604120ddb..1bd43ceccca118 100644 --- a/api_docs/kbn_management_settings_components_field_input.mdx +++ b/api_docs/kbn_management_settings_components_field_input.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-management-settings-components-field-input title: "@kbn/management-settings-components-field-input" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/management-settings-components-field-input plugin -date: 2024-05-24 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/management-settings-components-field-input'] --- import kbnManagementSettingsComponentsFieldInputObj from './kbn_management_settings_components_field_input.devdocs.json'; diff --git a/api_docs/kbn_management_settings_components_field_row.mdx b/api_docs/kbn_management_settings_components_field_row.mdx index 5fdf1c8c8387c7..c102a55ef852b3 100644 --- a/api_docs/kbn_management_settings_components_field_row.mdx +++ b/api_docs/kbn_management_settings_components_field_row.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-management-settings-components-field-row title: "@kbn/management-settings-components-field-row" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/management-settings-components-field-row plugin -date: 2024-05-24 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/management-settings-components-field-row'] --- import kbnManagementSettingsComponentsFieldRowObj from './kbn_management_settings_components_field_row.devdocs.json'; diff --git a/api_docs/kbn_management_settings_components_form.mdx b/api_docs/kbn_management_settings_components_form.mdx index 8b750ccf023e4e..98ac7da1bddc0f 100644 --- a/api_docs/kbn_management_settings_components_form.mdx +++ b/api_docs/kbn_management_settings_components_form.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-management-settings-components-form title: "@kbn/management-settings-components-form" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/management-settings-components-form plugin -date: 2024-05-24 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/management-settings-components-form'] --- import kbnManagementSettingsComponentsFormObj from './kbn_management_settings_components_form.devdocs.json'; diff --git a/api_docs/kbn_management_settings_field_definition.mdx b/api_docs/kbn_management_settings_field_definition.mdx index 7f82f349df1776..82a78cee848ee0 100644 --- a/api_docs/kbn_management_settings_field_definition.mdx +++ b/api_docs/kbn_management_settings_field_definition.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-management-settings-field-definition title: "@kbn/management-settings-field-definition" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/management-settings-field-definition plugin -date: 2024-05-24 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/management-settings-field-definition'] --- import kbnManagementSettingsFieldDefinitionObj from './kbn_management_settings_field_definition.devdocs.json'; diff --git a/api_docs/kbn_management_settings_ids.devdocs.json b/api_docs/kbn_management_settings_ids.devdocs.json index b4b24972b69899..5ddb78e79b8533 100644 --- a/api_docs/kbn_management_settings_ids.devdocs.json +++ b/api_docs/kbn_management_settings_ids.devdocs.json @@ -1344,13 +1344,13 @@ }, { "parentPluginId": "@kbn/management-settings-ids", - "id": "def-common.OBSERVABILITY_ENABLE_CONTAINER_ASSET_VIEW_ID", + "id": "def-common.OBSERVABILITY_ENABLE_INFRASTRUCTURE_ASSET_CUSTOM_DASHBOARDS_ID", "type": "string", "tags": [], - "label": "OBSERVABILITY_ENABLE_CONTAINER_ASSET_VIEW_ID", + "label": "OBSERVABILITY_ENABLE_INFRASTRUCTURE_ASSET_CUSTOM_DASHBOARDS_ID", "description": [], "signature": [ - "\"observability:enableContainerAssetView\"" + "\"observability:enableInfrastructureAssetCustomDashboards\"" ], "path": "packages/kbn-management/settings/setting_ids/index.ts", "deprecated": false, @@ -1359,13 +1359,13 @@ }, { "parentPluginId": "@kbn/management-settings-ids", - "id": "def-common.OBSERVABILITY_ENABLE_INFRASTRUCTURE_ASSET_CUSTOM_DASHBOARDS_ID", + "id": "def-common.OBSERVABILITY_ENABLE_INFRASTRUCTURE_CONTAINER_ASSET_VIEW_ID", "type": "string", "tags": [], - "label": "OBSERVABILITY_ENABLE_INFRASTRUCTURE_ASSET_CUSTOM_DASHBOARDS_ID", + "label": "OBSERVABILITY_ENABLE_INFRASTRUCTURE_CONTAINER_ASSET_VIEW_ID", "description": [], "signature": [ - "\"observability:enableInfrastructureAssetCustomDashboards\"" + "\"observability:enableInfrastructureContainerAssetView\"" ], "path": "packages/kbn-management/settings/setting_ids/index.ts", "deprecated": false, diff --git a/api_docs/kbn_management_settings_ids.mdx b/api_docs/kbn_management_settings_ids.mdx index 8e698f93fea857..11bc3a4c0cc8c4 100644 --- a/api_docs/kbn_management_settings_ids.mdx +++ b/api_docs/kbn_management_settings_ids.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-management-settings-ids title: "@kbn/management-settings-ids" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/management-settings-ids plugin -date: 2024-05-24 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/management-settings-ids'] --- import kbnManagementSettingsIdsObj from './kbn_management_settings_ids.devdocs.json'; diff --git a/api_docs/kbn_management_settings_section_registry.mdx b/api_docs/kbn_management_settings_section_registry.mdx index db0d52392d3242..81d364b74a8327 100644 --- a/api_docs/kbn_management_settings_section_registry.mdx +++ b/api_docs/kbn_management_settings_section_registry.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-management-settings-section-registry title: "@kbn/management-settings-section-registry" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/management-settings-section-registry plugin -date: 2024-05-24 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/management-settings-section-registry'] --- import kbnManagementSettingsSectionRegistryObj from './kbn_management_settings_section_registry.devdocs.json'; diff --git a/api_docs/kbn_management_settings_types.mdx b/api_docs/kbn_management_settings_types.mdx index dd737d1ba4e2bd..4d5fbb8638c099 100644 --- a/api_docs/kbn_management_settings_types.mdx +++ b/api_docs/kbn_management_settings_types.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-management-settings-types title: "@kbn/management-settings-types" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/management-settings-types plugin -date: 2024-05-24 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/management-settings-types'] --- import kbnManagementSettingsTypesObj from './kbn_management_settings_types.devdocs.json'; diff --git a/api_docs/kbn_management_settings_utilities.mdx b/api_docs/kbn_management_settings_utilities.mdx index f143006787e7b7..dba78dea4bfa25 100644 --- a/api_docs/kbn_management_settings_utilities.mdx +++ b/api_docs/kbn_management_settings_utilities.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-management-settings-utilities title: "@kbn/management-settings-utilities" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/management-settings-utilities plugin -date: 2024-05-24 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/management-settings-utilities'] --- import kbnManagementSettingsUtilitiesObj from './kbn_management_settings_utilities.devdocs.json'; diff --git a/api_docs/kbn_management_storybook_config.mdx b/api_docs/kbn_management_storybook_config.mdx index aceb187e7c7493..10872198966942 100644 --- a/api_docs/kbn_management_storybook_config.mdx +++ b/api_docs/kbn_management_storybook_config.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-management-storybook-config title: "@kbn/management-storybook-config" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/management-storybook-config plugin -date: 2024-05-24 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/management-storybook-config'] --- import kbnManagementStorybookConfigObj from './kbn_management_storybook_config.devdocs.json'; diff --git a/api_docs/kbn_mapbox_gl.mdx b/api_docs/kbn_mapbox_gl.mdx index f2ee9c2206f647..fb2289ccd48a62 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: 2024-05-24 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/mapbox-gl'] --- import kbnMapboxGlObj from './kbn_mapbox_gl.devdocs.json'; diff --git a/api_docs/kbn_maps_vector_tile_utils.mdx b/api_docs/kbn_maps_vector_tile_utils.mdx index 3868e2b15e84fc..ec38be3deacdce 100644 --- a/api_docs/kbn_maps_vector_tile_utils.mdx +++ b/api_docs/kbn_maps_vector_tile_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-maps-vector-tile-utils title: "@kbn/maps-vector-tile-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/maps-vector-tile-utils plugin -date: 2024-05-24 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/maps-vector-tile-utils'] --- import kbnMapsVectorTileUtilsObj from './kbn_maps_vector_tile_utils.devdocs.json'; diff --git a/api_docs/kbn_ml_agg_utils.mdx b/api_docs/kbn_ml_agg_utils.mdx index f93a99a72b11a1..e0097c5040bc4d 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: 2024-05-24 +date: 2024-05-28 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_anomaly_utils.mdx b/api_docs/kbn_ml_anomaly_utils.mdx index 3936d3bc3f8eb3..50d3ef624be756 100644 --- a/api_docs/kbn_ml_anomaly_utils.mdx +++ b/api_docs/kbn_ml_anomaly_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-anomaly-utils title: "@kbn/ml-anomaly-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-anomaly-utils plugin -date: 2024-05-24 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ml-anomaly-utils'] --- import kbnMlAnomalyUtilsObj from './kbn_ml_anomaly_utils.devdocs.json'; diff --git a/api_docs/kbn_ml_cancellable_search.mdx b/api_docs/kbn_ml_cancellable_search.mdx index 2d600b386a2c76..7ffccd15b65dc5 100644 --- a/api_docs/kbn_ml_cancellable_search.mdx +++ b/api_docs/kbn_ml_cancellable_search.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-cancellable-search title: "@kbn/ml-cancellable-search" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-cancellable-search plugin -date: 2024-05-24 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ml-cancellable-search'] --- import kbnMlCancellableSearchObj from './kbn_ml_cancellable_search.devdocs.json'; diff --git a/api_docs/kbn_ml_category_validator.mdx b/api_docs/kbn_ml_category_validator.mdx index 557f83248daeb1..7bbdde8f7db697 100644 --- a/api_docs/kbn_ml_category_validator.mdx +++ b/api_docs/kbn_ml_category_validator.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-category-validator title: "@kbn/ml-category-validator" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-category-validator plugin -date: 2024-05-24 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ml-category-validator'] --- import kbnMlCategoryValidatorObj from './kbn_ml_category_validator.devdocs.json'; diff --git a/api_docs/kbn_ml_chi2test.mdx b/api_docs/kbn_ml_chi2test.mdx index 013e5a4609c1af..b39998c7d5834a 100644 --- a/api_docs/kbn_ml_chi2test.mdx +++ b/api_docs/kbn_ml_chi2test.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-chi2test title: "@kbn/ml-chi2test" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-chi2test plugin -date: 2024-05-24 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ml-chi2test'] --- import kbnMlChi2testObj from './kbn_ml_chi2test.devdocs.json'; diff --git a/api_docs/kbn_ml_data_frame_analytics_utils.mdx b/api_docs/kbn_ml_data_frame_analytics_utils.mdx index dc61ea93474a60..226560e601c952 100644 --- a/api_docs/kbn_ml_data_frame_analytics_utils.mdx +++ b/api_docs/kbn_ml_data_frame_analytics_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-data-frame-analytics-utils title: "@kbn/ml-data-frame-analytics-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-data-frame-analytics-utils plugin -date: 2024-05-24 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ml-data-frame-analytics-utils'] --- import kbnMlDataFrameAnalyticsUtilsObj from './kbn_ml_data_frame_analytics_utils.devdocs.json'; diff --git a/api_docs/kbn_ml_data_grid.mdx b/api_docs/kbn_ml_data_grid.mdx index 58a6d67dd68145..09936cd82ec030 100644 --- a/api_docs/kbn_ml_data_grid.mdx +++ b/api_docs/kbn_ml_data_grid.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-data-grid title: "@kbn/ml-data-grid" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-data-grid plugin -date: 2024-05-24 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ml-data-grid'] --- import kbnMlDataGridObj from './kbn_ml_data_grid.devdocs.json'; diff --git a/api_docs/kbn_ml_date_picker.mdx b/api_docs/kbn_ml_date_picker.mdx index 436ab3d3e4d864..7e5c5ddab80a85 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: 2024-05-24 +date: 2024-05-28 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_date_utils.mdx b/api_docs/kbn_ml_date_utils.mdx index b2c686428fff0e..7c13b84e018b2e 100644 --- a/api_docs/kbn_ml_date_utils.mdx +++ b/api_docs/kbn_ml_date_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-date-utils title: "@kbn/ml-date-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-date-utils plugin -date: 2024-05-24 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ml-date-utils'] --- import kbnMlDateUtilsObj from './kbn_ml_date_utils.devdocs.json'; diff --git a/api_docs/kbn_ml_error_utils.mdx b/api_docs/kbn_ml_error_utils.mdx index 0862edca82983b..c0f79a672dd87a 100644 --- a/api_docs/kbn_ml_error_utils.mdx +++ b/api_docs/kbn_ml_error_utils.mdx @@ -8,7 +8,7 @@ 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: 2024-05-24 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ml-error-utils'] --- import kbnMlErrorUtilsObj from './kbn_ml_error_utils.devdocs.json'; diff --git a/api_docs/kbn_ml_in_memory_table.mdx b/api_docs/kbn_ml_in_memory_table.mdx index 04ab38b1aa87af..4de94ca590b79a 100644 --- a/api_docs/kbn_ml_in_memory_table.mdx +++ b/api_docs/kbn_ml_in_memory_table.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-in-memory-table title: "@kbn/ml-in-memory-table" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-in-memory-table plugin -date: 2024-05-24 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ml-in-memory-table'] --- import kbnMlInMemoryTableObj from './kbn_ml_in_memory_table.devdocs.json'; diff --git a/api_docs/kbn_ml_is_defined.mdx b/api_docs/kbn_ml_is_defined.mdx index 5d6393aa3d057d..0b39a4fbd1ee6c 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: 2024-05-24 +date: 2024-05-28 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 a55ac3dceb1394..41ce6a80ede597 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: 2024-05-24 +date: 2024-05-28 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_kibana_theme.mdx b/api_docs/kbn_ml_kibana_theme.mdx index 74a5b6db28d19c..4da6ffe435fe2c 100644 --- a/api_docs/kbn_ml_kibana_theme.mdx +++ b/api_docs/kbn_ml_kibana_theme.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-kibana-theme title: "@kbn/ml-kibana-theme" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-kibana-theme plugin -date: 2024-05-24 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ml-kibana-theme'] --- import kbnMlKibanaThemeObj from './kbn_ml_kibana_theme.devdocs.json'; diff --git a/api_docs/kbn_ml_local_storage.mdx b/api_docs/kbn_ml_local_storage.mdx index e8ff7e9b17207f..58b49f0a94dc9c 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: 2024-05-24 +date: 2024-05-28 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 7d9ddb0c9dc28c..e7a5eb64915b2b 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: 2024-05-24 +date: 2024-05-28 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 a93deac7f44804..d98f8bd6ec7c91 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: 2024-05-24 +date: 2024-05-28 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 0c60d620c0f39d..825ef529143539 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: 2024-05-24 +date: 2024-05-28 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 c89a54e896a613..886facc920c421 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: 2024-05-24 +date: 2024-05-28 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 7002a9674ccd1c..1d8f99122b69e8 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: 2024-05-24 +date: 2024-05-28 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_runtime_field_utils.mdx b/api_docs/kbn_ml_runtime_field_utils.mdx index f0caf7de0317ee..95873e82a1ed44 100644 --- a/api_docs/kbn_ml_runtime_field_utils.mdx +++ b/api_docs/kbn_ml_runtime_field_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-runtime-field-utils title: "@kbn/ml-runtime-field-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-runtime-field-utils plugin -date: 2024-05-24 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ml-runtime-field-utils'] --- import kbnMlRuntimeFieldUtilsObj from './kbn_ml_runtime_field_utils.devdocs.json'; diff --git a/api_docs/kbn_ml_string_hash.mdx b/api_docs/kbn_ml_string_hash.mdx index 021e1fed1372b9..78f9af02cbc2d4 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: 2024-05-24 +date: 2024-05-28 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_time_buckets.mdx b/api_docs/kbn_ml_time_buckets.mdx index 3ca76c27a90f7e..f553774e2645ad 100644 --- a/api_docs/kbn_ml_time_buckets.mdx +++ b/api_docs/kbn_ml_time_buckets.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-time-buckets title: "@kbn/ml-time-buckets" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-time-buckets plugin -date: 2024-05-24 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ml-time-buckets'] --- import kbnMlTimeBucketsObj from './kbn_ml_time_buckets.devdocs.json'; diff --git a/api_docs/kbn_ml_trained_models_utils.mdx b/api_docs/kbn_ml_trained_models_utils.mdx index 0d9b585620ae1a..9ed4df8f791aeb 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: 2024-05-24 +date: 2024-05-28 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_ui_actions.mdx b/api_docs/kbn_ml_ui_actions.mdx index 07c47c6e1189b6..5d26dbb64f0976 100644 --- a/api_docs/kbn_ml_ui_actions.mdx +++ b/api_docs/kbn_ml_ui_actions.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-ui-actions title: "@kbn/ml-ui-actions" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-ui-actions plugin -date: 2024-05-24 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ml-ui-actions'] --- import kbnMlUiActionsObj from './kbn_ml_ui_actions.devdocs.json'; diff --git a/api_docs/kbn_ml_url_state.mdx b/api_docs/kbn_ml_url_state.mdx index a7fe767d8bce35..a8e5fe380d59ce 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: 2024-05-24 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ml-url-state'] --- import kbnMlUrlStateObj from './kbn_ml_url_state.devdocs.json'; diff --git a/api_docs/kbn_mock_idp_utils.mdx b/api_docs/kbn_mock_idp_utils.mdx index 3c0aeaae4800ed..ee64b879984ffa 100644 --- a/api_docs/kbn_mock_idp_utils.mdx +++ b/api_docs/kbn_mock_idp_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-mock-idp-utils title: "@kbn/mock-idp-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/mock-idp-utils plugin -date: 2024-05-24 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/mock-idp-utils'] --- import kbnMockIdpUtilsObj from './kbn_mock_idp_utils.devdocs.json'; diff --git a/api_docs/kbn_monaco.mdx b/api_docs/kbn_monaco.mdx index bba132cdad00c0..932f7fe996baf5 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: 2024-05-24 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/monaco'] --- import kbnMonacoObj from './kbn_monaco.devdocs.json'; diff --git a/api_docs/kbn_object_versioning.mdx b/api_docs/kbn_object_versioning.mdx index d519d1f1c4a7c8..10f6b366aef3bc 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: 2024-05-24 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/object-versioning'] --- import kbnObjectVersioningObj from './kbn_object_versioning.devdocs.json'; diff --git a/api_docs/kbn_observability_alert_details.mdx b/api_docs/kbn_observability_alert_details.mdx index fa9fdc6f531470..2205716119ebc0 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: 2024-05-24 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/observability-alert-details'] --- import kbnObservabilityAlertDetailsObj from './kbn_observability_alert_details.devdocs.json'; diff --git a/api_docs/kbn_observability_alerting_test_data.mdx b/api_docs/kbn_observability_alerting_test_data.mdx index 06d0e9451f3ffb..0b1db4d46c1818 100644 --- a/api_docs/kbn_observability_alerting_test_data.mdx +++ b/api_docs/kbn_observability_alerting_test_data.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-observability-alerting-test-data title: "@kbn/observability-alerting-test-data" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/observability-alerting-test-data plugin -date: 2024-05-24 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/observability-alerting-test-data'] --- import kbnObservabilityAlertingTestDataObj from './kbn_observability_alerting_test_data.devdocs.json'; diff --git a/api_docs/kbn_observability_get_padded_alert_time_range_util.mdx b/api_docs/kbn_observability_get_padded_alert_time_range_util.mdx index 9f80b2c109cae1..bc45d1baf20126 100644 --- a/api_docs/kbn_observability_get_padded_alert_time_range_util.mdx +++ b/api_docs/kbn_observability_get_padded_alert_time_range_util.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-observability-get-padded-alert-time-range-util title: "@kbn/observability-get-padded-alert-time-range-util" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/observability-get-padded-alert-time-range-util plugin -date: 2024-05-24 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/observability-get-padded-alert-time-range-util'] --- import kbnObservabilityGetPaddedAlertTimeRangeUtilObj from './kbn_observability_get_padded_alert_time_range_util.devdocs.json'; diff --git a/api_docs/kbn_openapi_bundler.mdx b/api_docs/kbn_openapi_bundler.mdx index bf22b600861628..0be4fcadecdfe2 100644 --- a/api_docs/kbn_openapi_bundler.mdx +++ b/api_docs/kbn_openapi_bundler.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-openapi-bundler title: "@kbn/openapi-bundler" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/openapi-bundler plugin -date: 2024-05-24 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/openapi-bundler'] --- import kbnOpenapiBundlerObj from './kbn_openapi_bundler.devdocs.json'; diff --git a/api_docs/kbn_openapi_generator.mdx b/api_docs/kbn_openapi_generator.mdx index 43ebba64d53b15..5b798fdcc851ac 100644 --- a/api_docs/kbn_openapi_generator.mdx +++ b/api_docs/kbn_openapi_generator.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-openapi-generator title: "@kbn/openapi-generator" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/openapi-generator plugin -date: 2024-05-24 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/openapi-generator'] --- import kbnOpenapiGeneratorObj from './kbn_openapi_generator.devdocs.json'; diff --git a/api_docs/kbn_optimizer.mdx b/api_docs/kbn_optimizer.mdx index 9daaeb6ce8263e..3873899e81b852 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: 2024-05-24 +date: 2024-05-28 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 be1887dce41bae..b78d88a45304ed 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: 2024-05-24 +date: 2024-05-28 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 c3fe7661261d1d..19d83df3206cde 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: 2024-05-24 +date: 2024-05-28 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_panel_loader.mdx b/api_docs/kbn_panel_loader.mdx index d63b39598801e1..02fdcae82995b1 100644 --- a/api_docs/kbn_panel_loader.mdx +++ b/api_docs/kbn_panel_loader.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-panel-loader title: "@kbn/panel-loader" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/panel-loader plugin -date: 2024-05-24 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/panel-loader'] --- import kbnPanelLoaderObj from './kbn_panel_loader.devdocs.json'; diff --git a/api_docs/kbn_performance_testing_dataset_extractor.mdx b/api_docs/kbn_performance_testing_dataset_extractor.mdx index 808672ea8f429d..d55a8db63d72d5 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: 2024-05-24 +date: 2024-05-28 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_check.mdx b/api_docs/kbn_plugin_check.mdx index 7b74faf36e7013..4c16dd2e4454a6 100644 --- a/api_docs/kbn_plugin_check.mdx +++ b/api_docs/kbn_plugin_check.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-plugin-check title: "@kbn/plugin-check" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/plugin-check plugin -date: 2024-05-24 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/plugin-check'] --- import kbnPluginCheckObj from './kbn_plugin_check.devdocs.json'; diff --git a/api_docs/kbn_plugin_generator.mdx b/api_docs/kbn_plugin_generator.mdx index 017503c5efd9cf..7fb02a3ab2fc93 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: 2024-05-24 +date: 2024-05-28 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 c3faae2d8845a8..56f51fca315d87 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: 2024-05-24 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/plugin-helpers'] --- import kbnPluginHelpersObj from './kbn_plugin_helpers.devdocs.json'; diff --git a/api_docs/kbn_presentation_containers.mdx b/api_docs/kbn_presentation_containers.mdx index 3baf7f185582d5..8835a9b1ff3120 100644 --- a/api_docs/kbn_presentation_containers.mdx +++ b/api_docs/kbn_presentation_containers.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-presentation-containers title: "@kbn/presentation-containers" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/presentation-containers plugin -date: 2024-05-24 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/presentation-containers'] --- import kbnPresentationContainersObj from './kbn_presentation_containers.devdocs.json'; diff --git a/api_docs/kbn_presentation_publishing.devdocs.json b/api_docs/kbn_presentation_publishing.devdocs.json index fdfac45fbefb3b..93e1c1bf2fbf54 100644 --- a/api_docs/kbn_presentation_publishing.devdocs.json +++ b/api_docs/kbn_presentation_publishing.devdocs.json @@ -1984,6 +1984,46 @@ "returnComment": [], "initialIsOpen": false }, + { + "parentPluginId": "@kbn/presentation-publishing", + "id": "def-common.useFetchContext", + "type": "Function", + "tags": [], + "label": "useFetchContext", + "description": [], + "signature": [ + "(api: unknown) => ", + { + "pluginId": "@kbn/presentation-publishing", + "scope": "common", + "docId": "kibKbnPresentationPublishingPluginApi", + "section": "def-common.FetchContext", + "text": "FetchContext" + } + ], + "path": "packages/presentation/presentation_publishing/interfaces/fetch/fetch.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/presentation-publishing", + "id": "def-common.useFetchContext.$1", + "type": "Unknown", + "tags": [], + "label": "api", + "description": [], + "signature": [ + "unknown" + ], + "path": "packages/presentation/presentation_publishing/interfaces/fetch/fetch.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + } + ], + "returnComment": [], + "initialIsOpen": false + }, { "parentPluginId": "@kbn/presentation-publishing", "id": "def-common.useInheritedViewMode", diff --git a/api_docs/kbn_presentation_publishing.mdx b/api_docs/kbn_presentation_publishing.mdx index aebc887cccabd1..b5f654891df182 100644 --- a/api_docs/kbn_presentation_publishing.mdx +++ b/api_docs/kbn_presentation_publishing.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-presentation-publishing title: "@kbn/presentation-publishing" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/presentation-publishing plugin -date: 2024-05-24 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/presentation-publishing'] --- import kbnPresentationPublishingObj from './kbn_presentation_publishing.devdocs.json'; @@ -21,7 +21,7 @@ Contact [@elastic/kibana-presentation](https://github.com/orgs/elastic/teams/kib | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 202 | 0 | 167 | 5 | +| 204 | 0 | 169 | 5 | ## Common diff --git a/api_docs/kbn_profiling_utils.mdx b/api_docs/kbn_profiling_utils.mdx index 8a2e3a0b5f8cfd..2ec65ce5dd62aa 100644 --- a/api_docs/kbn_profiling_utils.mdx +++ b/api_docs/kbn_profiling_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-profiling-utils title: "@kbn/profiling-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/profiling-utils plugin -date: 2024-05-24 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/profiling-utils'] --- import kbnProfilingUtilsObj from './kbn_profiling_utils.devdocs.json'; diff --git a/api_docs/kbn_random_sampling.mdx b/api_docs/kbn_random_sampling.mdx index 359e98f0d3f0a5..c3edf4d476f54b 100644 --- a/api_docs/kbn_random_sampling.mdx +++ b/api_docs/kbn_random_sampling.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-random-sampling title: "@kbn/random-sampling" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/random-sampling plugin -date: 2024-05-24 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/random-sampling'] --- import kbnRandomSamplingObj from './kbn_random_sampling.devdocs.json'; diff --git a/api_docs/kbn_react_field.mdx b/api_docs/kbn_react_field.mdx index e3d7756cf18554..da5d4347af4b78 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: 2024-05-24 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/react-field'] --- import kbnReactFieldObj from './kbn_react_field.devdocs.json'; diff --git a/api_docs/kbn_react_hooks.mdx b/api_docs/kbn_react_hooks.mdx index 9b43a4b4f5d39d..b313d8f72e28a8 100644 --- a/api_docs/kbn_react_hooks.mdx +++ b/api_docs/kbn_react_hooks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-react-hooks title: "@kbn/react-hooks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/react-hooks plugin -date: 2024-05-24 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/react-hooks'] --- import kbnReactHooksObj from './kbn_react_hooks.devdocs.json'; diff --git a/api_docs/kbn_react_kibana_context_common.mdx b/api_docs/kbn_react_kibana_context_common.mdx index 492e042bdfb946..b0957fecad23ca 100644 --- a/api_docs/kbn_react_kibana_context_common.mdx +++ b/api_docs/kbn_react_kibana_context_common.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-react-kibana-context-common title: "@kbn/react-kibana-context-common" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/react-kibana-context-common plugin -date: 2024-05-24 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/react-kibana-context-common'] --- import kbnReactKibanaContextCommonObj from './kbn_react_kibana_context_common.devdocs.json'; diff --git a/api_docs/kbn_react_kibana_context_render.mdx b/api_docs/kbn_react_kibana_context_render.mdx index 6c5a6241435bf9..4ee18b1e8c9259 100644 --- a/api_docs/kbn_react_kibana_context_render.mdx +++ b/api_docs/kbn_react_kibana_context_render.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-react-kibana-context-render title: "@kbn/react-kibana-context-render" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/react-kibana-context-render plugin -date: 2024-05-24 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/react-kibana-context-render'] --- import kbnReactKibanaContextRenderObj from './kbn_react_kibana_context_render.devdocs.json'; diff --git a/api_docs/kbn_react_kibana_context_root.mdx b/api_docs/kbn_react_kibana_context_root.mdx index 00f3e97c7c3b25..d49269d841aca0 100644 --- a/api_docs/kbn_react_kibana_context_root.mdx +++ b/api_docs/kbn_react_kibana_context_root.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-react-kibana-context-root title: "@kbn/react-kibana-context-root" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/react-kibana-context-root plugin -date: 2024-05-24 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/react-kibana-context-root'] --- import kbnReactKibanaContextRootObj from './kbn_react_kibana_context_root.devdocs.json'; diff --git a/api_docs/kbn_react_kibana_context_styled.mdx b/api_docs/kbn_react_kibana_context_styled.mdx index 3c0dcc88052f3c..15fce585e4bccf 100644 --- a/api_docs/kbn_react_kibana_context_styled.mdx +++ b/api_docs/kbn_react_kibana_context_styled.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-react-kibana-context-styled title: "@kbn/react-kibana-context-styled" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/react-kibana-context-styled plugin -date: 2024-05-24 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/react-kibana-context-styled'] --- import kbnReactKibanaContextStyledObj from './kbn_react_kibana_context_styled.devdocs.json'; diff --git a/api_docs/kbn_react_kibana_context_theme.mdx b/api_docs/kbn_react_kibana_context_theme.mdx index 8324881a57ffcb..58524a5734a860 100644 --- a/api_docs/kbn_react_kibana_context_theme.mdx +++ b/api_docs/kbn_react_kibana_context_theme.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-react-kibana-context-theme title: "@kbn/react-kibana-context-theme" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/react-kibana-context-theme plugin -date: 2024-05-24 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/react-kibana-context-theme'] --- import kbnReactKibanaContextThemeObj from './kbn_react_kibana_context_theme.devdocs.json'; diff --git a/api_docs/kbn_react_kibana_mount.mdx b/api_docs/kbn_react_kibana_mount.mdx index 85857b57c59570..8b129cc73a56f9 100644 --- a/api_docs/kbn_react_kibana_mount.mdx +++ b/api_docs/kbn_react_kibana_mount.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-react-kibana-mount title: "@kbn/react-kibana-mount" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/react-kibana-mount plugin -date: 2024-05-24 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/react-kibana-mount'] --- import kbnReactKibanaMountObj from './kbn_react_kibana_mount.devdocs.json'; diff --git a/api_docs/kbn_repo_file_maps.mdx b/api_docs/kbn_repo_file_maps.mdx index 0ef923c25b3934..3dab8188138e30 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: 2024-05-24 +date: 2024-05-28 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 e2feb0ccbf73c9..ffbed8cc4603fe 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: 2024-05-24 +date: 2024-05-28 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 a5d401a48dfaf4..834bbb2810a539 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: 2024-05-24 +date: 2024-05-28 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 9c3551591fc3fb..0e849a9675e769 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: 2024-05-24 +date: 2024-05-28 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 c6dec769f0afb1..b9502029408dba 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: 2024-05-24 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/reporting-common'] --- import kbnReportingCommonObj from './kbn_reporting_common.devdocs.json'; diff --git a/api_docs/kbn_reporting_csv_share_panel.mdx b/api_docs/kbn_reporting_csv_share_panel.mdx index 02bac16fcf7c4d..5be23d32f8bec6 100644 --- a/api_docs/kbn_reporting_csv_share_panel.mdx +++ b/api_docs/kbn_reporting_csv_share_panel.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-reporting-csv-share-panel title: "@kbn/reporting-csv-share-panel" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/reporting-csv-share-panel plugin -date: 2024-05-24 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/reporting-csv-share-panel'] --- import kbnReportingCsvSharePanelObj from './kbn_reporting_csv_share_panel.devdocs.json'; diff --git a/api_docs/kbn_reporting_export_types_csv.mdx b/api_docs/kbn_reporting_export_types_csv.mdx index e0c4acedcbab40..2b137b778b5829 100644 --- a/api_docs/kbn_reporting_export_types_csv.mdx +++ b/api_docs/kbn_reporting_export_types_csv.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-reporting-export-types-csv title: "@kbn/reporting-export-types-csv" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/reporting-export-types-csv plugin -date: 2024-05-24 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/reporting-export-types-csv'] --- import kbnReportingExportTypesCsvObj from './kbn_reporting_export_types_csv.devdocs.json'; diff --git a/api_docs/kbn_reporting_export_types_csv_common.mdx b/api_docs/kbn_reporting_export_types_csv_common.mdx index 5b90ea32e38112..56f2a62022d436 100644 --- a/api_docs/kbn_reporting_export_types_csv_common.mdx +++ b/api_docs/kbn_reporting_export_types_csv_common.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-reporting-export-types-csv-common title: "@kbn/reporting-export-types-csv-common" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/reporting-export-types-csv-common plugin -date: 2024-05-24 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/reporting-export-types-csv-common'] --- import kbnReportingExportTypesCsvCommonObj from './kbn_reporting_export_types_csv_common.devdocs.json'; diff --git a/api_docs/kbn_reporting_export_types_pdf.mdx b/api_docs/kbn_reporting_export_types_pdf.mdx index 7dde00ad167664..9a3ce288a853f4 100644 --- a/api_docs/kbn_reporting_export_types_pdf.mdx +++ b/api_docs/kbn_reporting_export_types_pdf.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-reporting-export-types-pdf title: "@kbn/reporting-export-types-pdf" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/reporting-export-types-pdf plugin -date: 2024-05-24 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/reporting-export-types-pdf'] --- import kbnReportingExportTypesPdfObj from './kbn_reporting_export_types_pdf.devdocs.json'; diff --git a/api_docs/kbn_reporting_export_types_pdf_common.mdx b/api_docs/kbn_reporting_export_types_pdf_common.mdx index 27d583248839bd..f79a4c72af08b9 100644 --- a/api_docs/kbn_reporting_export_types_pdf_common.mdx +++ b/api_docs/kbn_reporting_export_types_pdf_common.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-reporting-export-types-pdf-common title: "@kbn/reporting-export-types-pdf-common" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/reporting-export-types-pdf-common plugin -date: 2024-05-24 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/reporting-export-types-pdf-common'] --- import kbnReportingExportTypesPdfCommonObj from './kbn_reporting_export_types_pdf_common.devdocs.json'; diff --git a/api_docs/kbn_reporting_export_types_png.mdx b/api_docs/kbn_reporting_export_types_png.mdx index 8324f251c3a20d..3e7b011609170c 100644 --- a/api_docs/kbn_reporting_export_types_png.mdx +++ b/api_docs/kbn_reporting_export_types_png.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-reporting-export-types-png title: "@kbn/reporting-export-types-png" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/reporting-export-types-png plugin -date: 2024-05-24 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/reporting-export-types-png'] --- import kbnReportingExportTypesPngObj from './kbn_reporting_export_types_png.devdocs.json'; diff --git a/api_docs/kbn_reporting_export_types_png_common.mdx b/api_docs/kbn_reporting_export_types_png_common.mdx index fb39628d4a0672..617030c9a96856 100644 --- a/api_docs/kbn_reporting_export_types_png_common.mdx +++ b/api_docs/kbn_reporting_export_types_png_common.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-reporting-export-types-png-common title: "@kbn/reporting-export-types-png-common" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/reporting-export-types-png-common plugin -date: 2024-05-24 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/reporting-export-types-png-common'] --- import kbnReportingExportTypesPngCommonObj from './kbn_reporting_export_types_png_common.devdocs.json'; diff --git a/api_docs/kbn_reporting_mocks_server.mdx b/api_docs/kbn_reporting_mocks_server.mdx index d1e92a71ec254f..205c7006a61c2d 100644 --- a/api_docs/kbn_reporting_mocks_server.mdx +++ b/api_docs/kbn_reporting_mocks_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-reporting-mocks-server title: "@kbn/reporting-mocks-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/reporting-mocks-server plugin -date: 2024-05-24 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/reporting-mocks-server'] --- import kbnReportingMocksServerObj from './kbn_reporting_mocks_server.devdocs.json'; diff --git a/api_docs/kbn_reporting_public.mdx b/api_docs/kbn_reporting_public.mdx index 93b0619d12d225..de3a4d0abe846c 100644 --- a/api_docs/kbn_reporting_public.mdx +++ b/api_docs/kbn_reporting_public.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-reporting-public title: "@kbn/reporting-public" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/reporting-public plugin -date: 2024-05-24 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/reporting-public'] --- import kbnReportingPublicObj from './kbn_reporting_public.devdocs.json'; diff --git a/api_docs/kbn_reporting_server.mdx b/api_docs/kbn_reporting_server.mdx index 88c885e867e313..bc2ae6079a63ca 100644 --- a/api_docs/kbn_reporting_server.mdx +++ b/api_docs/kbn_reporting_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-reporting-server title: "@kbn/reporting-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/reporting-server plugin -date: 2024-05-24 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/reporting-server'] --- import kbnReportingServerObj from './kbn_reporting_server.devdocs.json'; diff --git a/api_docs/kbn_resizable_layout.mdx b/api_docs/kbn_resizable_layout.mdx index 0b8d242f5fc1cc..26ed562b942ae0 100644 --- a/api_docs/kbn_resizable_layout.mdx +++ b/api_docs/kbn_resizable_layout.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-resizable-layout title: "@kbn/resizable-layout" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/resizable-layout plugin -date: 2024-05-24 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/resizable-layout'] --- import kbnResizableLayoutObj from './kbn_resizable_layout.devdocs.json'; diff --git a/api_docs/kbn_rison.mdx b/api_docs/kbn_rison.mdx index 95737aa864f53a..aa6b2ae552c613 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: 2024-05-24 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/rison'] --- import kbnRisonObj from './kbn_rison.devdocs.json'; diff --git a/api_docs/kbn_router_to_openapispec.mdx b/api_docs/kbn_router_to_openapispec.mdx index cc250e27033a92..970f72366b510e 100644 --- a/api_docs/kbn_router_to_openapispec.mdx +++ b/api_docs/kbn_router_to_openapispec.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-router-to-openapispec title: "@kbn/router-to-openapispec" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/router-to-openapispec plugin -date: 2024-05-24 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/router-to-openapispec'] --- import kbnRouterToOpenapispecObj from './kbn_router_to_openapispec.devdocs.json'; diff --git a/api_docs/kbn_router_utils.mdx b/api_docs/kbn_router_utils.mdx index 5220c45eb4ebb1..88081a11c8f498 100644 --- a/api_docs/kbn_router_utils.mdx +++ b/api_docs/kbn_router_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-router-utils title: "@kbn/router-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/router-utils plugin -date: 2024-05-24 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/router-utils'] --- import kbnRouterUtilsObj from './kbn_router_utils.devdocs.json'; diff --git a/api_docs/kbn_rrule.mdx b/api_docs/kbn_rrule.mdx index b551ad2a20c3da..a7e15e74a8e96f 100644 --- a/api_docs/kbn_rrule.mdx +++ b/api_docs/kbn_rrule.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-rrule title: "@kbn/rrule" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/rrule plugin -date: 2024-05-24 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/rrule'] --- import kbnRruleObj from './kbn_rrule.devdocs.json'; diff --git a/api_docs/kbn_rule_data_utils.mdx b/api_docs/kbn_rule_data_utils.mdx index b34232c13b5f88..02f1610f9bf464 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: 2024-05-24 +date: 2024-05-28 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 c49f1119421871..5c7bef31faa405 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: 2024-05-24 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/saved-objects-settings'] --- import kbnSavedObjectsSettingsObj from './kbn_saved_objects_settings.devdocs.json'; diff --git a/api_docs/kbn_search_api_panels.mdx b/api_docs/kbn_search_api_panels.mdx index e69df56d20bab6..78c1554c3e9e5b 100644 --- a/api_docs/kbn_search_api_panels.mdx +++ b/api_docs/kbn_search_api_panels.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-search-api-panels title: "@kbn/search-api-panels" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/search-api-panels plugin -date: 2024-05-24 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/search-api-panels'] --- import kbnSearchApiPanelsObj from './kbn_search_api_panels.devdocs.json'; diff --git a/api_docs/kbn_search_connectors.devdocs.json b/api_docs/kbn_search_connectors.devdocs.json index f7e4549263202d..669f5be7fa9c10 100644 --- a/api_docs/kbn_search_connectors.devdocs.json +++ b/api_docs/kbn_search_connectors.devdocs.json @@ -1121,7 +1121,15 @@ "section": "def-common.ElasticsearchClient", "text": "ElasticsearchClient" }, - ", connectorId?: string | undefined, from?: number, size?: number, syncJobType?: \"all\" | \"content\" | \"access_control\") => Promise<", + ", connectorId?: string | undefined, from?: number, size?: number, syncJobType?: \"all\" | \"content\" | \"access_control\", syncStatus?: ", + { + "pluginId": "@kbn/search-connectors", + "scope": "common", + "docId": "kibKbnSearchConnectorsPluginApi", + "section": "def-common.SyncStatus", + "text": "SyncStatus" + }, + " | undefined) => Promise<", { "pluginId": "@kbn/search-connectors", "scope": "common", @@ -1223,6 +1231,28 @@ "deprecated": false, "trackAdoption": false, "isRequired": true + }, + { + "parentPluginId": "@kbn/search-connectors", + "id": "def-common.fetchSyncJobs.$6", + "type": "CompoundType", + "tags": [], + "label": "syncStatus", + "description": [], + "signature": [ + { + "pluginId": "@kbn/search-connectors", + "scope": "common", + "docId": "kibKbnSearchConnectorsPluginApi", + "section": "def-common.SyncStatus", + "text": "SyncStatus" + }, + " | undefined" + ], + "path": "packages/kbn-search-connectors/lib/fetch_sync_jobs.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": false } ], "returnComment": [], @@ -41186,6 +41216,206 @@ "trackAdoption": false } ] + }, + { + "parentPluginId": "@kbn/search-connectors", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.outlook.configuration.use_document_level_security", + "type": "Object", + "tags": [], + "label": "use_document_level_security", + "description": [], + "path": "packages/kbn-search-connectors/types/native_connectors.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/search-connectors", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.outlook.configuration.use_document_level_security.default_value", + "type": "Uncategorized", + "tags": [], + "label": "default_value", + "description": [], + "signature": [ + "null" + ], + "path": "packages/kbn-search-connectors/types/native_connectors.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/search-connectors", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.outlook.configuration.use_document_level_security.depends_on", + "type": "Array", + "tags": [], + "label": "depends_on", + "description": [], + "signature": [ + "never[]" + ], + "path": "packages/kbn-search-connectors/types/native_connectors.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/search-connectors", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.outlook.configuration.use_document_level_security.display", + "type": "string", + "tags": [], + "label": "display", + "description": [], + "signature": [ + { + "pluginId": "@kbn/search-connectors", + "scope": "common", + "docId": "kibKbnSearchConnectorsPluginApi", + "section": "def-common.DisplayType", + "text": "DisplayType" + }, + ".TOGGLE" + ], + "path": "packages/kbn-search-connectors/types/native_connectors.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/search-connectors", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.outlook.configuration.use_document_level_security.label", + "type": "string", + "tags": [], + "label": "label", + "description": [], + "path": "packages/kbn-search-connectors/types/native_connectors.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/search-connectors", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.outlook.configuration.use_document_level_security.options", + "type": "Array", + "tags": [], + "label": "options", + "description": [], + "signature": [ + "never[]" + ], + "path": "packages/kbn-search-connectors/types/native_connectors.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/search-connectors", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.outlook.configuration.use_document_level_security.order", + "type": "number", + "tags": [], + "label": "order", + "description": [], + "path": "packages/kbn-search-connectors/types/native_connectors.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/search-connectors", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.outlook.configuration.use_document_level_security.required", + "type": "boolean", + "tags": [], + "label": "required", + "description": [], + "signature": [ + "true" + ], + "path": "packages/kbn-search-connectors/types/native_connectors.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/search-connectors", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.outlook.configuration.use_document_level_security.sensitive", + "type": "boolean", + "tags": [], + "label": "sensitive", + "description": [], + "signature": [ + "false" + ], + "path": "packages/kbn-search-connectors/types/native_connectors.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/search-connectors", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.outlook.configuration.use_document_level_security.tooltip", + "type": "string", + "tags": [], + "label": "tooltip", + "description": [], + "path": "packages/kbn-search-connectors/types/native_connectors.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/search-connectors", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.outlook.configuration.use_document_level_security.type", + "type": "string", + "tags": [], + "label": "type", + "description": [], + "signature": [ + { + "pluginId": "@kbn/search-connectors", + "scope": "common", + "docId": "kibKbnSearchConnectorsPluginApi", + "section": "def-common.FieldType", + "text": "FieldType" + }, + ".BOOLEAN" + ], + "path": "packages/kbn-search-connectors/types/native_connectors.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/search-connectors", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.outlook.configuration.use_document_level_security.ui_restrictions", + "type": "Array", + "tags": [], + "label": "ui_restrictions", + "description": [], + "signature": [ + "never[]" + ], + "path": "packages/kbn-search-connectors/types/native_connectors.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/search-connectors", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.outlook.configuration.use_document_level_security.validations", + "type": "Array", + "tags": [], + "label": "validations", + "description": [], + "signature": [ + "never[]" + ], + "path": "packages/kbn-search-connectors/types/native_connectors.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/search-connectors", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.outlook.configuration.use_document_level_security.value", + "type": "boolean", + "tags": [], + "label": "value", + "description": [], + "signature": [ + "false" + ], + "path": "packages/kbn-search-connectors/types/native_connectors.ts", + "deprecated": false, + "trackAdoption": false + } + ] } ] }, @@ -41267,6 +41497,33 @@ } ] }, + { + "parentPluginId": "@kbn/search-connectors", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.outlook.features.FeatureName.DOCUMENT_LEVEL_SECURITY", + "type": "Object", + "tags": [], + "label": "[FeatureName.DOCUMENT_LEVEL_SECURITY]", + "description": [], + "path": "packages/kbn-search-connectors/types/native_connectors.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/search-connectors", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.outlook.features.FeatureName.DOCUMENT_LEVEL_SECURITY.enabled", + "type": "boolean", + "tags": [], + "label": "enabled", + "description": [], + "signature": [ + "true" + ], + "path": "packages/kbn-search-connectors/types/native_connectors.ts", + "deprecated": false, + "trackAdoption": false + } + ] + }, { "parentPluginId": "@kbn/search-connectors", "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.outlook.features.FeatureName.INCREMENTAL_SYNC", diff --git a/api_docs/kbn_search_connectors.mdx b/api_docs/kbn_search_connectors.mdx index 30d0ea8326cac2..e7a2591314e5a5 100644 --- a/api_docs/kbn_search_connectors.mdx +++ b/api_docs/kbn_search_connectors.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-search-connectors title: "@kbn/search-connectors" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/search-connectors plugin -date: 2024-05-24 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/search-connectors'] --- import kbnSearchConnectorsObj from './kbn_search_connectors.devdocs.json'; @@ -21,7 +21,7 @@ Contact [@elastic/enterprise-search-frontend](https://github.com/orgs/elastic/te | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 3672 | 0 | 3672 | 0 | +| 3689 | 0 | 3689 | 0 | ## Common diff --git a/api_docs/kbn_search_errors.mdx b/api_docs/kbn_search_errors.mdx index 33f103ea761ee9..04adeccc6f87ec 100644 --- a/api_docs/kbn_search_errors.mdx +++ b/api_docs/kbn_search_errors.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-search-errors title: "@kbn/search-errors" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/search-errors plugin -date: 2024-05-24 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/search-errors'] --- import kbnSearchErrorsObj from './kbn_search_errors.devdocs.json'; diff --git a/api_docs/kbn_search_index_documents.mdx b/api_docs/kbn_search_index_documents.mdx index f2be01e7bb6e62..de2802d3fd1d68 100644 --- a/api_docs/kbn_search_index_documents.mdx +++ b/api_docs/kbn_search_index_documents.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-search-index-documents title: "@kbn/search-index-documents" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/search-index-documents plugin -date: 2024-05-24 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/search-index-documents'] --- import kbnSearchIndexDocumentsObj from './kbn_search_index_documents.devdocs.json'; diff --git a/api_docs/kbn_search_response_warnings.mdx b/api_docs/kbn_search_response_warnings.mdx index ed242946d7fed4..2eb135563f5b61 100644 --- a/api_docs/kbn_search_response_warnings.mdx +++ b/api_docs/kbn_search_response_warnings.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-search-response-warnings title: "@kbn/search-response-warnings" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/search-response-warnings plugin -date: 2024-05-24 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/search-response-warnings'] --- import kbnSearchResponseWarningsObj from './kbn_search_response_warnings.devdocs.json'; diff --git a/api_docs/kbn_search_types.mdx b/api_docs/kbn_search_types.mdx index 376859461862b0..8a9a615a1dc542 100644 --- a/api_docs/kbn_search_types.mdx +++ b/api_docs/kbn_search_types.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-search-types title: "@kbn/search-types" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/search-types plugin -date: 2024-05-24 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/search-types'] --- import kbnSearchTypesObj from './kbn_search_types.devdocs.json'; diff --git a/api_docs/kbn_security_hardening.mdx b/api_docs/kbn_security_hardening.mdx index 1d527daf9fe7b6..403ecc0c203337 100644 --- a/api_docs/kbn_security_hardening.mdx +++ b/api_docs/kbn_security_hardening.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-security-hardening title: "@kbn/security-hardening" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/security-hardening plugin -date: 2024-05-24 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/security-hardening'] --- import kbnSecurityHardeningObj from './kbn_security_hardening.devdocs.json'; diff --git a/api_docs/kbn_security_plugin_types_common.mdx b/api_docs/kbn_security_plugin_types_common.mdx index f46079d6881e3f..35fd2c9bd64374 100644 --- a/api_docs/kbn_security_plugin_types_common.mdx +++ b/api_docs/kbn_security_plugin_types_common.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-security-plugin-types-common title: "@kbn/security-plugin-types-common" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/security-plugin-types-common plugin -date: 2024-05-24 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/security-plugin-types-common'] --- import kbnSecurityPluginTypesCommonObj from './kbn_security_plugin_types_common.devdocs.json'; diff --git a/api_docs/kbn_security_plugin_types_public.mdx b/api_docs/kbn_security_plugin_types_public.mdx index b3b2827009dba1..bb25f69c106eba 100644 --- a/api_docs/kbn_security_plugin_types_public.mdx +++ b/api_docs/kbn_security_plugin_types_public.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-security-plugin-types-public title: "@kbn/security-plugin-types-public" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/security-plugin-types-public plugin -date: 2024-05-24 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/security-plugin-types-public'] --- import kbnSecurityPluginTypesPublicObj from './kbn_security_plugin_types_public.devdocs.json'; diff --git a/api_docs/kbn_security_plugin_types_server.mdx b/api_docs/kbn_security_plugin_types_server.mdx index 787add3df955ff..d10f6fd5ab4600 100644 --- a/api_docs/kbn_security_plugin_types_server.mdx +++ b/api_docs/kbn_security_plugin_types_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-security-plugin-types-server title: "@kbn/security-plugin-types-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/security-plugin-types-server plugin -date: 2024-05-24 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/security-plugin-types-server'] --- import kbnSecurityPluginTypesServerObj from './kbn_security_plugin_types_server.devdocs.json'; diff --git a/api_docs/kbn_security_solution_features.mdx b/api_docs/kbn_security_solution_features.mdx index bc769ab13d5fcf..cb6385416a3b47 100644 --- a/api_docs/kbn_security_solution_features.mdx +++ b/api_docs/kbn_security_solution_features.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-security-solution-features title: "@kbn/security-solution-features" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/security-solution-features plugin -date: 2024-05-24 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/security-solution-features'] --- import kbnSecuritySolutionFeaturesObj from './kbn_security_solution_features.devdocs.json'; diff --git a/api_docs/kbn_security_solution_navigation.mdx b/api_docs/kbn_security_solution_navigation.mdx index 271458277efa82..674da6f2e3e776 100644 --- a/api_docs/kbn_security_solution_navigation.mdx +++ b/api_docs/kbn_security_solution_navigation.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-security-solution-navigation title: "@kbn/security-solution-navigation" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/security-solution-navigation plugin -date: 2024-05-24 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/security-solution-navigation'] --- import kbnSecuritySolutionNavigationObj from './kbn_security_solution_navigation.devdocs.json'; diff --git a/api_docs/kbn_security_solution_side_nav.mdx b/api_docs/kbn_security_solution_side_nav.mdx index c5abfa2f6d863e..4e0f7253ff4733 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: 2024-05-24 +date: 2024-05-28 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 ec5ec473e51f5b..ab73f94e454c31 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: 2024-05-24 +date: 2024-05-28 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 220a067aef038e..0ba33939c6a512 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: 2024-05-24 +date: 2024-05-28 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 42aa6396bddf21..2d9e5411ae1511 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: 2024-05-24 +date: 2024-05-28 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 b90428765c0164..038c478e2bc70d 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: 2024-05-24 +date: 2024-05-28 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 93013ece932110..af97aa7a255491 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: 2024-05-24 +date: 2024-05-28 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 0551505eccd7e5..d1cbde84b37145 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: 2024-05-24 +date: 2024-05-28 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 33c14a78fffa64..63c86f5edbda95 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: 2024-05-24 +date: 2024-05-28 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 83894d1b8a33ff..92b8e544e0f4a0 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: 2024-05-24 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/securitysolution-hook-utils'] --- import kbnSecuritysolutionHookUtilsObj from './kbn_securitysolution_hook_utils.devdocs.json'; diff --git a/api_docs/kbn_securitysolution_io_ts_alerting_types.mdx b/api_docs/kbn_securitysolution_io_ts_alerting_types.mdx index 39c2ee8740c63a..110456ec9f5b62 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: 2024-05-24 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/securitysolution-io-ts-alerting-types'] --- import kbnSecuritysolutionIoTsAlertingTypesObj from './kbn_securitysolution_io_ts_alerting_types.devdocs.json'; diff --git a/api_docs/kbn_securitysolution_io_ts_list_types.mdx b/api_docs/kbn_securitysolution_io_ts_list_types.mdx index 3f190d880c181c..b65b81392d7141 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: 2024-05-24 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/securitysolution-io-ts-list-types'] --- import kbnSecuritysolutionIoTsListTypesObj from './kbn_securitysolution_io_ts_list_types.devdocs.json'; diff --git a/api_docs/kbn_securitysolution_io_ts_types.mdx b/api_docs/kbn_securitysolution_io_ts_types.mdx index 162587bf804409..68b1b87b02c193 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: 2024-05-24 +date: 2024-05-28 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 43ce6a7a89d440..beefb773a4dfe0 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: 2024-05-24 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/securitysolution-io-ts-utils'] --- import kbnSecuritysolutionIoTsUtilsObj from './kbn_securitysolution_io_ts_utils.devdocs.json'; diff --git a/api_docs/kbn_securitysolution_list_api.mdx b/api_docs/kbn_securitysolution_list_api.mdx index f7caa43f7b1b26..38ed263fb2451f 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: 2024-05-24 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/securitysolution-list-api'] --- import kbnSecuritysolutionListApiObj from './kbn_securitysolution_list_api.devdocs.json'; diff --git a/api_docs/kbn_securitysolution_list_constants.mdx b/api_docs/kbn_securitysolution_list_constants.mdx index 0250434a5220ed..4259544aadc0a0 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: 2024-05-24 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/securitysolution-list-constants'] --- import kbnSecuritysolutionListConstantsObj from './kbn_securitysolution_list_constants.devdocs.json'; diff --git a/api_docs/kbn_securitysolution_list_hooks.mdx b/api_docs/kbn_securitysolution_list_hooks.mdx index 935813883ef492..b3d37800c17611 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: 2024-05-24 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/securitysolution-list-hooks'] --- import kbnSecuritysolutionListHooksObj from './kbn_securitysolution_list_hooks.devdocs.json'; diff --git a/api_docs/kbn_securitysolution_list_utils.mdx b/api_docs/kbn_securitysolution_list_utils.mdx index c0f6b5ce45d1bc..5612f85b54a56e 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: 2024-05-24 +date: 2024-05-28 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 34eb14385efdfb..243a296d06052a 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: 2024-05-24 +date: 2024-05-28 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 4e5ee154f510a9..f60f80fc0aaab3 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: 2024-05-24 +date: 2024-05-28 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 5f690fa5c43f50..a1781cf5c36187 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: 2024-05-24 +date: 2024-05-28 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 d8b6f33e26e09d..1a53187f2582a4 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: 2024-05-24 +date: 2024-05-28 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 fb8de339bcb5f4..3578faf0350e0a 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: 2024-05-24 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/server-route-repository'] --- import kbnServerRouteRepositoryObj from './kbn_server_route_repository.devdocs.json'; diff --git a/api_docs/kbn_serverless_common_settings.mdx b/api_docs/kbn_serverless_common_settings.mdx index d25b86b434745f..fc8f65f7535c81 100644 --- a/api_docs/kbn_serverless_common_settings.mdx +++ b/api_docs/kbn_serverless_common_settings.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-serverless-common-settings title: "@kbn/serverless-common-settings" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/serverless-common-settings plugin -date: 2024-05-24 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/serverless-common-settings'] --- import kbnServerlessCommonSettingsObj from './kbn_serverless_common_settings.devdocs.json'; diff --git a/api_docs/kbn_serverless_observability_settings.mdx b/api_docs/kbn_serverless_observability_settings.mdx index 087abc9d3477e8..59e36905580f60 100644 --- a/api_docs/kbn_serverless_observability_settings.mdx +++ b/api_docs/kbn_serverless_observability_settings.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-serverless-observability-settings title: "@kbn/serverless-observability-settings" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/serverless-observability-settings plugin -date: 2024-05-24 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/serverless-observability-settings'] --- import kbnServerlessObservabilitySettingsObj from './kbn_serverless_observability_settings.devdocs.json'; diff --git a/api_docs/kbn_serverless_project_switcher.mdx b/api_docs/kbn_serverless_project_switcher.mdx index a832946fb50db2..f42c1170cb1710 100644 --- a/api_docs/kbn_serverless_project_switcher.mdx +++ b/api_docs/kbn_serverless_project_switcher.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-serverless-project-switcher title: "@kbn/serverless-project-switcher" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/serverless-project-switcher plugin -date: 2024-05-24 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/serverless-project-switcher'] --- import kbnServerlessProjectSwitcherObj from './kbn_serverless_project_switcher.devdocs.json'; diff --git a/api_docs/kbn_serverless_search_settings.mdx b/api_docs/kbn_serverless_search_settings.mdx index 2f56e88a606109..7621e02479b90a 100644 --- a/api_docs/kbn_serverless_search_settings.mdx +++ b/api_docs/kbn_serverless_search_settings.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-serverless-search-settings title: "@kbn/serverless-search-settings" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/serverless-search-settings plugin -date: 2024-05-24 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/serverless-search-settings'] --- import kbnServerlessSearchSettingsObj from './kbn_serverless_search_settings.devdocs.json'; diff --git a/api_docs/kbn_serverless_security_settings.mdx b/api_docs/kbn_serverless_security_settings.mdx index 7c1c4c3d7cc4e3..c17759f59ec6c9 100644 --- a/api_docs/kbn_serverless_security_settings.mdx +++ b/api_docs/kbn_serverless_security_settings.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-serverless-security-settings title: "@kbn/serverless-security-settings" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/serverless-security-settings plugin -date: 2024-05-24 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/serverless-security-settings'] --- import kbnServerlessSecuritySettingsObj from './kbn_serverless_security_settings.devdocs.json'; diff --git a/api_docs/kbn_serverless_storybook_config.mdx b/api_docs/kbn_serverless_storybook_config.mdx index 0df04baeebac98..791d9c3026e88a 100644 --- a/api_docs/kbn_serverless_storybook_config.mdx +++ b/api_docs/kbn_serverless_storybook_config.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-serverless-storybook-config title: "@kbn/serverless-storybook-config" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/serverless-storybook-config plugin -date: 2024-05-24 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/serverless-storybook-config'] --- import kbnServerlessStorybookConfigObj from './kbn_serverless_storybook_config.devdocs.json'; diff --git a/api_docs/kbn_shared_svg.mdx b/api_docs/kbn_shared_svg.mdx index a057e6d8f3eeae..7cd453bcbe1082 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: 2024-05-24 +date: 2024-05-28 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 d088dfde31988b..c44f66ce710a5a 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: 2024-05-24 +date: 2024-05-28 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_button_exit_full_screen.mdx b/api_docs/kbn_shared_ux_button_exit_full_screen.mdx index cead63de20888a..ed0d094f5e986e 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: 2024-05-24 +date: 2024-05-28 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_toolbar.mdx b/api_docs/kbn_shared_ux_button_toolbar.mdx index b45b143509af5c..a4db1d36c69bb1 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: 2024-05-24 +date: 2024-05-28 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 42fd1949c0e269..9a266a460f4eb3 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: 2024-05-24 +date: 2024-05-28 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 2505328c435888..440e4447d4f6f5 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: 2024-05-24 +date: 2024-05-28 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_chrome_navigation.mdx b/api_docs/kbn_shared_ux_chrome_navigation.mdx index ce10d06efc1fc2..e79489b57ec5e3 100644 --- a/api_docs/kbn_shared_ux_chrome_navigation.mdx +++ b/api_docs/kbn_shared_ux_chrome_navigation.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-chrome-navigation title: "@kbn/shared-ux-chrome-navigation" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-chrome-navigation plugin -date: 2024-05-24 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-chrome-navigation'] --- import kbnSharedUxChromeNavigationObj from './kbn_shared_ux_chrome_navigation.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_error_boundary.mdx b/api_docs/kbn_shared_ux_error_boundary.mdx index 609e77459f2197..c0d7888c590af5 100644 --- a/api_docs/kbn_shared_ux_error_boundary.mdx +++ b/api_docs/kbn_shared_ux_error_boundary.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-error-boundary title: "@kbn/shared-ux-error-boundary" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-error-boundary plugin -date: 2024-05-24 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-error-boundary'] --- import kbnSharedUxErrorBoundaryObj from './kbn_shared_ux_error_boundary.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_file_context.mdx b/api_docs/kbn_shared_ux_file_context.mdx index 7d58080ca2a126..4b9d18436e320a 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: 2024-05-24 +date: 2024-05-28 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 da1a3778f77834..a5bd19ac36b4ce 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: 2024-05-24 +date: 2024-05-28 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 0bde06967dda61..d532c361bd6b99 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: 2024-05-24 +date: 2024-05-28 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 efe59adaaf7053..22d52439d4ba89 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: 2024-05-24 +date: 2024-05-28 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 360a835c0b8130..e8aaf66f82af6a 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: 2024-05-24 +date: 2024-05-28 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 b3c93d877cb520..70ca6a220612f6 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: 2024-05-24 +date: 2024-05-28 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 f949341ad23fc7..8719bd0ed31fd3 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: 2024-05-24 +date: 2024-05-28 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 8dfbed0c2e374b..802e5538962492 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: 2024-05-24 +date: 2024-05-28 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 e805d1ccfbe445..566ff58dbbd61b 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: 2024-05-24 +date: 2024-05-28 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 191dd9b251b316..24c563ba05dc44 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: 2024-05-24 +date: 2024-05-28 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 98035d41c5afbe..7075642d734269 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: 2024-05-24 +date: 2024-05-28 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 d617301a58b375..53b9214599259b 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: 2024-05-24 +date: 2024-05-28 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 b40a969effdd4f..cbb7994d58bb56 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: 2024-05-24 +date: 2024-05-28 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 9c8c7943ce4c9b..dd6abd20abf43f 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: 2024-05-24 +date: 2024-05-28 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 8b11a0d0056709..088fc0f969e8a0 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: 2024-05-24 +date: 2024-05-28 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 a6a201e51575d9..0f8ac6460262c0 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: 2024-05-24 +date: 2024-05-28 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 f8e99283c1dc1d..f59f673e6bf08c 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: 2024-05-24 +date: 2024-05-28 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 79a6765d56864c..996f1a2f68d619 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: 2024-05-24 +date: 2024-05-28 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 2dbebdd9ab6347..66c914b5206f6d 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: 2024-05-24 +date: 2024-05-28 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 aa5b4ccb778da6..008fd27e011c08 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: 2024-05-24 +date: 2024-05-28 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 36b633bf8080b2..b0417308140e84 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: 2024-05-24 +date: 2024-05-28 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 c6d84494e67298..13b8b9b62b4f19 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: 2024-05-24 +date: 2024-05-28 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 57e577dc535d41..73e8f4567070fc 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: 2024-05-24 +date: 2024-05-28 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 302d9ed27d69e9..6dcd9943570540 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: 2024-05-24 +date: 2024-05-28 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 ded4ac6ec786d4..08ec759951b50f 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: 2024-05-24 +date: 2024-05-28 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 85f7d1e8d33b54..bc1d1ebfcf6dd5 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: 2024-05-24 +date: 2024-05-28 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 a870c501d0b99b..31b8eae527882c 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: 2024-05-24 +date: 2024-05-28 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 9cb5f05c9c9d4a..0fefa0685a833d 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: 2024-05-24 +date: 2024-05-28 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 bed979963c4568..5b4f12bfe1e59b 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: 2024-05-24 +date: 2024-05-28 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 1d5b8f048c51cc..843ad535b58395 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: 2024-05-24 +date: 2024-05-28 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_tabbed_modal.mdx b/api_docs/kbn_shared_ux_tabbed_modal.mdx index de139b756e65eb..6bd2784238bc68 100644 --- a/api_docs/kbn_shared_ux_tabbed_modal.mdx +++ b/api_docs/kbn_shared_ux_tabbed_modal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-tabbed-modal title: "@kbn/shared-ux-tabbed-modal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-tabbed-modal plugin -date: 2024-05-24 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-tabbed-modal'] --- import kbnSharedUxTabbedModalObj from './kbn_shared_ux_tabbed_modal.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_utility.mdx b/api_docs/kbn_shared_ux_utility.mdx index 866f0472e46070..fed143b514559a 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: 2024-05-24 +date: 2024-05-28 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 3d5b513953f041..89f1999273e10a 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: 2024-05-24 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/slo-schema'] --- import kbnSloSchemaObj from './kbn_slo_schema.devdocs.json'; diff --git a/api_docs/kbn_solution_nav_oblt.mdx b/api_docs/kbn_solution_nav_oblt.mdx index 5c431930553e92..7f4ad2c982dc27 100644 --- a/api_docs/kbn_solution_nav_oblt.mdx +++ b/api_docs/kbn_solution_nav_oblt.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-solution-nav-oblt title: "@kbn/solution-nav-oblt" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/solution-nav-oblt plugin -date: 2024-05-24 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/solution-nav-oblt'] --- import kbnSolutionNavObltObj from './kbn_solution_nav_oblt.devdocs.json'; diff --git a/api_docs/kbn_some_dev_log.mdx b/api_docs/kbn_some_dev_log.mdx index 836de80fbfed18..7bf1f512e774a5 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: 2024-05-24 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/some-dev-log'] --- import kbnSomeDevLogObj from './kbn_some_dev_log.devdocs.json'; diff --git a/api_docs/kbn_sort_predicates.mdx b/api_docs/kbn_sort_predicates.mdx index 338d932a755360..1a95854bb82bc7 100644 --- a/api_docs/kbn_sort_predicates.mdx +++ b/api_docs/kbn_sort_predicates.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-sort-predicates title: "@kbn/sort-predicates" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/sort-predicates plugin -date: 2024-05-24 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/sort-predicates'] --- import kbnSortPredicatesObj from './kbn_sort_predicates.devdocs.json'; diff --git a/api_docs/kbn_std.mdx b/api_docs/kbn_std.mdx index 0f7ab59b8021eb..330e06018adfc4 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: 2024-05-24 +date: 2024-05-28 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 39aec9016c7102..c040994615f209 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: 2024-05-24 +date: 2024-05-28 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 a9ac9b840a32aa..2424406498c3cb 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: 2024-05-24 +date: 2024-05-28 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 9a5534d65f9eb4..e8e021aa2acd83 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: 2024-05-24 +date: 2024-05-28 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 e09190f34ff456..69943b067832bb 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: 2024-05-24 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/test'] --- import kbnTestObj from './kbn_test.devdocs.json'; diff --git a/api_docs/kbn_test_eui_helpers.mdx b/api_docs/kbn_test_eui_helpers.mdx index 4349eb5baf3cc4..f02eaff0610a59 100644 --- a/api_docs/kbn_test_eui_helpers.mdx +++ b/api_docs/kbn_test_eui_helpers.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-test-eui-helpers title: "@kbn/test-eui-helpers" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/test-eui-helpers plugin -date: 2024-05-24 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/test-eui-helpers'] --- import kbnTestEuiHelpersObj from './kbn_test_eui_helpers.devdocs.json'; diff --git a/api_docs/kbn_test_jest_helpers.mdx b/api_docs/kbn_test_jest_helpers.mdx index 887505e269a3e0..3cab7d94ced457 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: 2024-05-24 +date: 2024-05-28 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 52221555fb1e39..be8ebc782da418 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: 2024-05-24 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/test-subj-selector'] --- import kbnTestSubjSelectorObj from './kbn_test_subj_selector.devdocs.json'; diff --git a/api_docs/kbn_text_based_editor.mdx b/api_docs/kbn_text_based_editor.mdx index daf123b33595ea..869147f36ac5c2 100644 --- a/api_docs/kbn_text_based_editor.mdx +++ b/api_docs/kbn_text_based_editor.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-text-based-editor title: "@kbn/text-based-editor" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/text-based-editor plugin -date: 2024-05-24 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/text-based-editor'] --- import kbnTextBasedEditorObj from './kbn_text_based_editor.devdocs.json'; diff --git a/api_docs/kbn_timerange.mdx b/api_docs/kbn_timerange.mdx index e427cb805626c6..0a7712464510b8 100644 --- a/api_docs/kbn_timerange.mdx +++ b/api_docs/kbn_timerange.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-timerange title: "@kbn/timerange" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/timerange plugin -date: 2024-05-24 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/timerange'] --- import kbnTimerangeObj from './kbn_timerange.devdocs.json'; diff --git a/api_docs/kbn_tooling_log.mdx b/api_docs/kbn_tooling_log.mdx index 8339cad27f4e6d..b3e36a7e7667d2 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: 2024-05-24 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/tooling-log'] --- import kbnToolingLogObj from './kbn_tooling_log.devdocs.json'; diff --git a/api_docs/kbn_triggers_actions_ui_types.mdx b/api_docs/kbn_triggers_actions_ui_types.mdx index 01cf31aa5c1e11..9e4f1729a8f682 100644 --- a/api_docs/kbn_triggers_actions_ui_types.mdx +++ b/api_docs/kbn_triggers_actions_ui_types.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-triggers-actions-ui-types title: "@kbn/triggers-actions-ui-types" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/triggers-actions-ui-types plugin -date: 2024-05-24 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/triggers-actions-ui-types'] --- import kbnTriggersActionsUiTypesObj from './kbn_triggers_actions_ui_types.devdocs.json'; diff --git a/api_docs/kbn_try_in_console.mdx b/api_docs/kbn_try_in_console.mdx index 5b1f63ef8e683a..5bf01e24d3fae1 100644 --- a/api_docs/kbn_try_in_console.mdx +++ b/api_docs/kbn_try_in_console.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-try-in-console title: "@kbn/try-in-console" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/try-in-console plugin -date: 2024-05-24 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/try-in-console'] --- import kbnTryInConsoleObj from './kbn_try_in_console.devdocs.json'; diff --git a/api_docs/kbn_ts_projects.mdx b/api_docs/kbn_ts_projects.mdx index fcf2a97f8004f8..5626d3f7007e47 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: 2024-05-24 +date: 2024-05-28 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 a6b842b84cd87f..a978ce90694bec 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: 2024-05-24 +date: 2024-05-28 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 ca61db3b994a45..322ab2745b1291 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: 2024-05-24 +date: 2024-05-28 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 1ee06fbefe3b43..0bf30d61e4f120 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: 2024-05-24 +date: 2024-05-28 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 1a2cc34462d6ec..44e431d7f7eeb9 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: 2024-05-24 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ui-theme'] --- import kbnUiThemeObj from './kbn_ui_theme.devdocs.json'; diff --git a/api_docs/kbn_unified_data_table.mdx b/api_docs/kbn_unified_data_table.mdx index df69eb97a46104..11800508cbbca1 100644 --- a/api_docs/kbn_unified_data_table.mdx +++ b/api_docs/kbn_unified_data_table.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-unified-data-table title: "@kbn/unified-data-table" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/unified-data-table plugin -date: 2024-05-24 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/unified-data-table'] --- import kbnUnifiedDataTableObj from './kbn_unified_data_table.devdocs.json'; diff --git a/api_docs/kbn_unified_doc_viewer.mdx b/api_docs/kbn_unified_doc_viewer.mdx index e88913138a1214..71dbf7ca86e182 100644 --- a/api_docs/kbn_unified_doc_viewer.mdx +++ b/api_docs/kbn_unified_doc_viewer.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-unified-doc-viewer title: "@kbn/unified-doc-viewer" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/unified-doc-viewer plugin -date: 2024-05-24 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/unified-doc-viewer'] --- import kbnUnifiedDocViewerObj from './kbn_unified_doc_viewer.devdocs.json'; diff --git a/api_docs/kbn_unified_field_list.mdx b/api_docs/kbn_unified_field_list.mdx index f2ad354d131ef8..fa09b841e159cd 100644 --- a/api_docs/kbn_unified_field_list.mdx +++ b/api_docs/kbn_unified_field_list.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-unified-field-list title: "@kbn/unified-field-list" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/unified-field-list plugin -date: 2024-05-24 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/unified-field-list'] --- import kbnUnifiedFieldListObj from './kbn_unified_field_list.devdocs.json'; diff --git a/api_docs/kbn_unsaved_changes_badge.mdx b/api_docs/kbn_unsaved_changes_badge.mdx index fa1a01746b82e2..d799dffe9d9297 100644 --- a/api_docs/kbn_unsaved_changes_badge.mdx +++ b/api_docs/kbn_unsaved_changes_badge.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-unsaved-changes-badge title: "@kbn/unsaved-changes-badge" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/unsaved-changes-badge plugin -date: 2024-05-24 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/unsaved-changes-badge'] --- import kbnUnsavedChangesBadgeObj from './kbn_unsaved_changes_badge.devdocs.json'; diff --git a/api_docs/kbn_use_tracked_promise.mdx b/api_docs/kbn_use_tracked_promise.mdx index 4f415ea754ca6b..b485c22f6a3bc8 100644 --- a/api_docs/kbn_use_tracked_promise.mdx +++ b/api_docs/kbn_use_tracked_promise.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-use-tracked-promise title: "@kbn/use-tracked-promise" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/use-tracked-promise plugin -date: 2024-05-24 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/use-tracked-promise'] --- import kbnUseTrackedPromiseObj from './kbn_use_tracked_promise.devdocs.json'; diff --git a/api_docs/kbn_user_profile_components.mdx b/api_docs/kbn_user_profile_components.mdx index a844ade5f81508..e9583f20e73dc4 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: 2024-05-24 +date: 2024-05-28 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 f44808dfd94b54..dd9ab7fbf100c0 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: 2024-05-24 +date: 2024-05-28 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 2846f9ae955bf0..de8cc6d545fdd1 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: 2024-05-24 +date: 2024-05-28 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 5a105bd1b1628b..b9aed7923aa449 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: 2024-05-24 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/utils'] --- import kbnUtilsObj from './kbn_utils.devdocs.json'; diff --git a/api_docs/kbn_visualization_ui_components.mdx b/api_docs/kbn_visualization_ui_components.mdx index dc7c3803515838..b1a58704a819fd 100644 --- a/api_docs/kbn_visualization_ui_components.mdx +++ b/api_docs/kbn_visualization_ui_components.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-visualization-ui-components title: "@kbn/visualization-ui-components" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/visualization-ui-components plugin -date: 2024-05-24 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/visualization-ui-components'] --- import kbnVisualizationUiComponentsObj from './kbn_visualization_ui_components.devdocs.json'; diff --git a/api_docs/kbn_visualization_utils.mdx b/api_docs/kbn_visualization_utils.mdx index a24dc36df0b747..5b19236b3f2516 100644 --- a/api_docs/kbn_visualization_utils.mdx +++ b/api_docs/kbn_visualization_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-visualization-utils title: "@kbn/visualization-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/visualization-utils plugin -date: 2024-05-24 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/visualization-utils'] --- import kbnVisualizationUtilsObj from './kbn_visualization_utils.devdocs.json'; diff --git a/api_docs/kbn_xstate_utils.mdx b/api_docs/kbn_xstate_utils.mdx index 95adab044f93fb..cc0897e8f1951c 100644 --- a/api_docs/kbn_xstate_utils.mdx +++ b/api_docs/kbn_xstate_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-xstate-utils title: "@kbn/xstate-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/xstate-utils plugin -date: 2024-05-24 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/xstate-utils'] --- import kbnXstateUtilsObj from './kbn_xstate_utils.devdocs.json'; diff --git a/api_docs/kbn_yarn_lock_validator.mdx b/api_docs/kbn_yarn_lock_validator.mdx index 9a69a537f997fa..0e3075d88610eb 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: 2024-05-24 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/yarn-lock-validator'] --- import kbnYarnLockValidatorObj from './kbn_yarn_lock_validator.devdocs.json'; diff --git a/api_docs/kbn_zod_helpers.mdx b/api_docs/kbn_zod_helpers.mdx index 7fdf2edf0b7243..7468ae6251191e 100644 --- a/api_docs/kbn_zod_helpers.mdx +++ b/api_docs/kbn_zod_helpers.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-zod-helpers title: "@kbn/zod-helpers" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/zod-helpers plugin -date: 2024-05-24 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/zod-helpers'] --- import kbnZodHelpersObj from './kbn_zod_helpers.devdocs.json'; diff --git a/api_docs/kibana_overview.mdx b/api_docs/kibana_overview.mdx index 80f9528c79b9cb..ce3f361c4abb9a 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: 2024-05-24 +date: 2024-05-28 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 13940fc995bb43..ab3086e5a26249 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: 2024-05-24 +date: 2024-05-28 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 890cfbde9988aa..1a9418531586fa 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: 2024-05-24 +date: 2024-05-28 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 6487e77309ea55..682e97024751a1 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: 2024-05-24 +date: 2024-05-28 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 7efbc4421f9978..9b3d47afd15ec2 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: 2024-05-24 +date: 2024-05-28 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 0e525faa403161..ee27c32789e28f 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: 2024-05-24 +date: 2024-05-28 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 f0a62c68037d3d..9eabc764edc4d0 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: 2024-05-24 +date: 2024-05-28 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 99b5540b953653..fe00f7ee3914d4 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: 2024-05-24 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'licensing'] --- import licensingObj from './licensing.devdocs.json'; diff --git a/api_docs/links.mdx b/api_docs/links.mdx index 12f5822d233439..49277ae1941e88 100644 --- a/api_docs/links.mdx +++ b/api_docs/links.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/links title: "links" image: https://source.unsplash.com/400x175/?github description: API docs for the links plugin -date: 2024-05-24 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'links'] --- import linksObj from './links.devdocs.json'; diff --git a/api_docs/lists.mdx b/api_docs/lists.mdx index 0c855e819ff000..e4e8c3fdc9b7da 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: 2024-05-24 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'lists'] --- import listsObj from './lists.devdocs.json'; diff --git a/api_docs/logs_data_access.mdx b/api_docs/logs_data_access.mdx index 728d9b057af814..6cc96122a5c49e 100644 --- a/api_docs/logs_data_access.mdx +++ b/api_docs/logs_data_access.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/logsDataAccess title: "logsDataAccess" image: https://source.unsplash.com/400x175/?github description: API docs for the logsDataAccess plugin -date: 2024-05-24 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'logsDataAccess'] --- import logsDataAccessObj from './logs_data_access.devdocs.json'; diff --git a/api_docs/logs_explorer.mdx b/api_docs/logs_explorer.mdx index 5659347920a3bc..cf8aee10ed6b1f 100644 --- a/api_docs/logs_explorer.mdx +++ b/api_docs/logs_explorer.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/logsExplorer title: "logsExplorer" image: https://source.unsplash.com/400x175/?github description: API docs for the logsExplorer plugin -date: 2024-05-24 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'logsExplorer'] --- import logsExplorerObj from './logs_explorer.devdocs.json'; diff --git a/api_docs/logs_shared.mdx b/api_docs/logs_shared.mdx index 181401aeb3496f..d83623dacfacb2 100644 --- a/api_docs/logs_shared.mdx +++ b/api_docs/logs_shared.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/logsShared title: "logsShared" image: https://source.unsplash.com/400x175/?github description: API docs for the logsShared plugin -date: 2024-05-24 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'logsShared'] --- import logsSharedObj from './logs_shared.devdocs.json'; diff --git a/api_docs/management.mdx b/api_docs/management.mdx index b0e1c9868b24ea..1ab44cb9f55840 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: 2024-05-24 +date: 2024-05-28 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 61596cfd5c529a..ef335a483b8e37 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: 2024-05-24 +date: 2024-05-28 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 36bf2cce24b12a..3802f228b5c795 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: 2024-05-24 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'mapsEms'] --- import mapsEmsObj from './maps_ems.devdocs.json'; diff --git a/api_docs/metrics_data_access.mdx b/api_docs/metrics_data_access.mdx index 00b4bbeb123357..4410248509881a 100644 --- a/api_docs/metrics_data_access.mdx +++ b/api_docs/metrics_data_access.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/metricsDataAccess title: "metricsDataAccess" image: https://source.unsplash.com/400x175/?github description: API docs for the metricsDataAccess plugin -date: 2024-05-24 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'metricsDataAccess'] --- import metricsDataAccessObj from './metrics_data_access.devdocs.json'; diff --git a/api_docs/ml.mdx b/api_docs/ml.mdx index 2e0332bc6dd401..0dc6dd749acf1c 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: 2024-05-24 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'ml'] --- import mlObj from './ml.devdocs.json'; diff --git a/api_docs/mock_idp_plugin.mdx b/api_docs/mock_idp_plugin.mdx index c6b6c104c99e3e..7f1448074e2b66 100644 --- a/api_docs/mock_idp_plugin.mdx +++ b/api_docs/mock_idp_plugin.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/mockIdpPlugin title: "mockIdpPlugin" image: https://source.unsplash.com/400x175/?github description: API docs for the mockIdpPlugin plugin -date: 2024-05-24 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'mockIdpPlugin'] --- import mockIdpPluginObj from './mock_idp_plugin.devdocs.json'; diff --git a/api_docs/monitoring.mdx b/api_docs/monitoring.mdx index c5c2764b8c04bb..caad8011283d84 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: 2024-05-24 +date: 2024-05-28 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 77e978621e5ae0..c541ecb49d9793 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: 2024-05-24 +date: 2024-05-28 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 aac1480b13c943..a7add772f62120 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: 2024-05-24 +date: 2024-05-28 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 a93cf408539393..8dab5293b29416 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: 2024-05-24 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'newsfeed'] --- import newsfeedObj from './newsfeed.devdocs.json'; diff --git a/api_docs/no_data_page.mdx b/api_docs/no_data_page.mdx index 16be9735cd09ca..fe86e649838969 100644 --- a/api_docs/no_data_page.mdx +++ b/api_docs/no_data_page.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/noDataPage title: "noDataPage" image: https://source.unsplash.com/400x175/?github description: API docs for the noDataPage plugin -date: 2024-05-24 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'noDataPage'] --- import noDataPageObj from './no_data_page.devdocs.json'; diff --git a/api_docs/notifications.mdx b/api_docs/notifications.mdx index a530caba894769..3e925f7cb8831c 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: 2024-05-24 +date: 2024-05-28 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 29edb72641acaa..00f314a846fa15 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: 2024-05-24 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'observability'] --- import observabilityObj from './observability.devdocs.json'; diff --git a/api_docs/observability_a_i_assistant.mdx b/api_docs/observability_a_i_assistant.mdx index eafefdae2bd069..64063011dc1464 100644 --- a/api_docs/observability_a_i_assistant.mdx +++ b/api_docs/observability_a_i_assistant.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/observabilityAIAssistant title: "observabilityAIAssistant" image: https://source.unsplash.com/400x175/?github description: API docs for the observabilityAIAssistant plugin -date: 2024-05-24 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'observabilityAIAssistant'] --- import observabilityAIAssistantObj from './observability_a_i_assistant.devdocs.json'; diff --git a/api_docs/observability_a_i_assistant_app.mdx b/api_docs/observability_a_i_assistant_app.mdx index 6b302b9fdaaa7c..ac34d9d1937269 100644 --- a/api_docs/observability_a_i_assistant_app.mdx +++ b/api_docs/observability_a_i_assistant_app.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/observabilityAIAssistantApp title: "observabilityAIAssistantApp" image: https://source.unsplash.com/400x175/?github description: API docs for the observabilityAIAssistantApp plugin -date: 2024-05-24 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'observabilityAIAssistantApp'] --- import observabilityAIAssistantAppObj from './observability_a_i_assistant_app.devdocs.json'; diff --git a/api_docs/observability_ai_assistant_management.mdx b/api_docs/observability_ai_assistant_management.mdx index 11c98224c454dc..595a475dbc383f 100644 --- a/api_docs/observability_ai_assistant_management.mdx +++ b/api_docs/observability_ai_assistant_management.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/observabilityAiAssistantManagement title: "observabilityAiAssistantManagement" image: https://source.unsplash.com/400x175/?github description: API docs for the observabilityAiAssistantManagement plugin -date: 2024-05-24 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'observabilityAiAssistantManagement'] --- import observabilityAiAssistantManagementObj from './observability_ai_assistant_management.devdocs.json'; diff --git a/api_docs/observability_logs_explorer.mdx b/api_docs/observability_logs_explorer.mdx index 74c27896ce0cf4..8614a4893144fb 100644 --- a/api_docs/observability_logs_explorer.mdx +++ b/api_docs/observability_logs_explorer.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/observabilityLogsExplorer title: "observabilityLogsExplorer" image: https://source.unsplash.com/400x175/?github description: API docs for the observabilityLogsExplorer plugin -date: 2024-05-24 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'observabilityLogsExplorer'] --- import observabilityLogsExplorerObj from './observability_logs_explorer.devdocs.json'; diff --git a/api_docs/observability_onboarding.mdx b/api_docs/observability_onboarding.mdx index a792a6380b5470..e1affa96de9367 100644 --- a/api_docs/observability_onboarding.mdx +++ b/api_docs/observability_onboarding.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/observabilityOnboarding title: "observabilityOnboarding" image: https://source.unsplash.com/400x175/?github description: API docs for the observabilityOnboarding plugin -date: 2024-05-24 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'observabilityOnboarding'] --- import observabilityOnboardingObj from './observability_onboarding.devdocs.json'; diff --git a/api_docs/observability_shared.mdx b/api_docs/observability_shared.mdx index 39f19d98f12aeb..f3efee31b3822c 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: 2024-05-24 +date: 2024-05-28 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 d091ccf300fa32..a0132a76a57be3 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: 2024-05-24 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'osquery'] --- import osqueryObj from './osquery.devdocs.json'; diff --git a/api_docs/painless_lab.mdx b/api_docs/painless_lab.mdx index bf4d00c4d8667d..d42533f6207c7d 100644 --- a/api_docs/painless_lab.mdx +++ b/api_docs/painless_lab.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/painlessLab title: "painlessLab" image: https://source.unsplash.com/400x175/?github description: API docs for the painlessLab plugin -date: 2024-05-24 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'painlessLab'] --- import painlessLabObj from './painless_lab.devdocs.json'; diff --git a/api_docs/plugin_directory.mdx b/api_docs/plugin_directory.mdx index c7b4c8909f99d7..192ab067b21235 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: 2024-05-24 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana'] --- @@ -21,7 +21,7 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana'] | API Count | Any Count | Missing comments | Missing exports | |--------------|----------|-----------------|--------| -| 48444 | 241 | 36942 | 1864 | +| 48471 | 241 | 36966 | 1865 | ## Plugin Directory @@ -57,7 +57,7 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana'] | | [@elastic/fleet](https://github.com/orgs/elastic/teams/fleet) | Add custom data integrations so they can be displayed in the Fleet integrations app | 271 | 0 | 252 | 1 | | | [@elastic/kibana-presentation](https://github.com/orgs/elastic/teams/kibana-presentation) | Adds the Dashboard app to Kibana | 116 | 0 | 113 | 13 | | | [@elastic/kibana-presentation](https://github.com/orgs/elastic/teams/kibana-presentation) | - | 54 | 0 | 51 | 0 | -| | [@elastic/kibana-visualizations](https://github.com/orgs/elastic/teams/kibana-visualizations) | Data services are useful for searching and querying data from Elasticsearch. Helpful utilities include: a re-usable react query bar, KQL autocomplete, async search, Data Views (Index Patterns) and field formatters. | 3182 | 31 | 2575 | 23 | +| | [@elastic/kibana-visualizations](https://github.com/orgs/elastic/teams/kibana-visualizations) | Data services are useful for searching and querying data from Elasticsearch. Helpful utilities include: a re-usable react query bar, KQL autocomplete, async search, Data Views (Index Patterns) and field formatters. | 3183 | 31 | 2576 | 24 | | | [@elastic/kibana-data-discovery](https://github.com/orgs/elastic/teams/kibana-data-discovery) | This plugin provides the ability to create data views via a modal flyout inside Kibana apps | 35 | 0 | 25 | 5 | | | [@elastic/kibana-data-discovery](https://github.com/orgs/elastic/teams/kibana-data-discovery) | Reusable data view field editor across Kibana | 72 | 0 | 33 | 0 | | | [@elastic/kibana-data-discovery](https://github.com/orgs/elastic/teams/kibana-data-discovery) | Data view management app | 2 | 0 | 2 | 0 | @@ -98,7 +98,7 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana'] | | [@elastic/kibana-gis](https://github.com/orgs/elastic/teams/kibana-gis) | The file upload plugin contains components and services for uploading a file, analyzing its data, and then importing the data into an Elasticsearch index. Supported file types include CSV, TSV, newline-delimited JSON and GeoJSON. | 84 | 0 | 84 | 8 | | | [@elastic/appex-sharedux](https://github.com/orgs/elastic/teams/appex-sharedux) | File upload, download, sharing, and serving over HTTP implementation in Kibana. | 240 | 0 | 24 | 9 | | | [@elastic/appex-sharedux](https://github.com/orgs/elastic/teams/appex-sharedux) | Simple UI for managing files in Kibana | 2 | 0 | 2 | 0 | -| | [@elastic/fleet](https://github.com/orgs/elastic/teams/fleet) | - | 1309 | 5 | 1188 | 66 | +| | [@elastic/fleet](https://github.com/orgs/elastic/teams/fleet) | - | 1310 | 5 | 1189 | 66 | | ftrApis | [@elastic/kibana-core](https://github.com/orgs/elastic/teams/kibana-core) | - | 0 | 0 | 0 | 0 | | | [@elastic/appex-sharedux](https://github.com/orgs/elastic/teams/appex-sharedux) | - | 72 | 0 | 14 | 5 | | globalSearchBar | [@elastic/appex-sharedux](https://github.com/orgs/elastic/teams/appex-sharedux) | - | 0 | 0 | 0 | 0 | @@ -183,7 +183,7 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana'] | | [@elastic/appex-sharedux](https://github.com/orgs/elastic/teams/appex-sharedux) | Adds URL Service and sharing capabilities to Kibana | 120 | 0 | 59 | 12 | | | [@elastic/obs-ux-management-team](https://github.com/orgs/elastic/teams/obs-ux-management-team) | - | 64 | 0 | 64 | 1 | | | [@elastic/kibana-management](https://github.com/orgs/elastic/teams/kibana-management) | - | 22 | 1 | 22 | 1 | -| | [@elastic/kibana-security](https://github.com/orgs/elastic/teams/kibana-security) | This plugin provides the Spaces feature, which allows saved objects to be organized into meaningful categories. | 257 | 0 | 66 | 0 | +| | [@elastic/kibana-security](https://github.com/orgs/elastic/teams/kibana-security) | This plugin provides the Spaces feature, which allows saved objects to be organized into meaningful categories. | 260 | 0 | 66 | 0 | | | [@elastic/response-ops](https://github.com/orgs/elastic/teams/response-ops) | - | 25 | 0 | 25 | 3 | | | [@elastic/response-ops](https://github.com/orgs/elastic/teams/response-ops) | - | 10 | 0 | 10 | 0 | | synthetics | [@elastic/obs-ux-infra_services-team](https://github.com/orgs/elastic/teams/obs-ux-infra_services-team) | This plugin visualizes data from Synthetics and Heartbeat, and integrates with other Observability solutions. | 0 | 0 | 0 | 0 | @@ -492,8 +492,8 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana'] | | [@elastic/kibana-core](https://github.com/orgs/elastic/teams/kibana-core) | - | 26 | 0 | 26 | 1 | | | [@elastic/kibana-operations](https://github.com/orgs/elastic/teams/kibana-operations) | - | 2 | 0 | 1 | 0 | | | [@elastic/kibana-esql](https://github.com/orgs/elastic/teams/kibana-esql) | - | 63 | 1 | 63 | 6 | -| | [@elastic/kibana-esql](https://github.com/orgs/elastic/teams/kibana-esql) | - | 52 | 0 | 50 | 0 | -| | [@elastic/kibana-esql](https://github.com/orgs/elastic/teams/kibana-esql) | - | 193 | 0 | 183 | 10 | +| | [@elastic/kibana-esql](https://github.com/orgs/elastic/teams/kibana-esql) | - | 54 | 0 | 52 | 0 | +| | [@elastic/kibana-esql](https://github.com/orgs/elastic/teams/kibana-esql) | - | 194 | 0 | 184 | 10 | | | [@elastic/kibana-visualizations](https://github.com/orgs/elastic/teams/kibana-visualizations) | - | 39 | 0 | 39 | 0 | | | [@elastic/kibana-visualizations](https://github.com/orgs/elastic/teams/kibana-visualizations) | - | 52 | 0 | 52 | 1 | | | [@elastic/security-threat-hunting-investigations](https://github.com/orgs/elastic/teams/security-threat-hunting-investigations) | - | 38 | 0 | 14 | 1 | @@ -589,7 +589,7 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana'] | | [@elastic/kibana-operations](https://github.com/orgs/elastic/teams/kibana-operations) | - | 1 | 0 | 1 | 0 | | | [@elastic/kibana-operations](https://github.com/orgs/elastic/teams/kibana-operations) | - | 1 | 0 | 1 | 0 | | | [@elastic/kibana-presentation](https://github.com/orgs/elastic/teams/kibana-presentation) | - | 70 | 0 | 64 | 1 | -| | [@elastic/kibana-presentation](https://github.com/orgs/elastic/teams/kibana-presentation) | - | 202 | 0 | 167 | 5 | +| | [@elastic/kibana-presentation](https://github.com/orgs/elastic/teams/kibana-presentation) | - | 204 | 0 | 169 | 5 | | | [@elastic/obs-ux-infra_services-team](https://github.com/orgs/elastic/teams/obs-ux-infra_services-team) | - | 168 | 0 | 55 | 0 | | | [@elastic/kibana-visualizations](https://github.com/orgs/elastic/teams/kibana-visualizations) | - | 13 | 0 | 7 | 0 | | | [@elastic/kibana-data-discovery](https://github.com/orgs/elastic/teams/kibana-data-discovery) | - | 22 | 0 | 9 | 0 | @@ -623,7 +623,7 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana'] | | [@elastic/security-detections-response](https://github.com/orgs/elastic/teams/security-detections-response) | - | 125 | 0 | 122 | 0 | | | [@elastic/appex-sharedux](https://github.com/orgs/elastic/teams/appex-sharedux) | - | 2 | 0 | 2 | 0 | | | [@elastic/enterprise-search-frontend](https://github.com/orgs/elastic/teams/enterprise-search-frontend) | - | 76 | 0 | 76 | 0 | -| | [@elastic/enterprise-search-frontend](https://github.com/orgs/elastic/teams/enterprise-search-frontend) | - | 3672 | 0 | 3672 | 0 | +| | [@elastic/enterprise-search-frontend](https://github.com/orgs/elastic/teams/enterprise-search-frontend) | - | 3689 | 0 | 3689 | 0 | | | [@elastic/kibana-data-discovery](https://github.com/orgs/elastic/teams/kibana-data-discovery) | - | 18 | 1 | 17 | 1 | | | [@elastic/enterprise-search-frontend](https://github.com/orgs/elastic/teams/enterprise-search-frontend) | - | 25 | 0 | 25 | 0 | | | [@elastic/kibana-data-discovery](https://github.com/orgs/elastic/teams/kibana-data-discovery) | - | 20 | 0 | 18 | 1 | diff --git a/api_docs/presentation_panel.mdx b/api_docs/presentation_panel.mdx index 0f2edcde88fd0d..28845de2265ef0 100644 --- a/api_docs/presentation_panel.mdx +++ b/api_docs/presentation_panel.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/presentationPanel title: "presentationPanel" image: https://source.unsplash.com/400x175/?github description: API docs for the presentationPanel plugin -date: 2024-05-24 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'presentationPanel'] --- import presentationPanelObj from './presentation_panel.devdocs.json'; diff --git a/api_docs/presentation_util.mdx b/api_docs/presentation_util.mdx index c220246f99db1f..bf6199d075bba8 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: 2024-05-24 +date: 2024-05-28 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 359322b10b9c73..9a2639b4fa879b 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: 2024-05-24 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'profiling'] --- import profilingObj from './profiling.devdocs.json'; diff --git a/api_docs/profiling_data_access.mdx b/api_docs/profiling_data_access.mdx index 427a3486dfa37d..d906a10512be93 100644 --- a/api_docs/profiling_data_access.mdx +++ b/api_docs/profiling_data_access.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/profilingDataAccess title: "profilingDataAccess" image: https://source.unsplash.com/400x175/?github description: API docs for the profilingDataAccess plugin -date: 2024-05-24 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'profilingDataAccess'] --- import profilingDataAccessObj from './profiling_data_access.devdocs.json'; diff --git a/api_docs/remote_clusters.mdx b/api_docs/remote_clusters.mdx index 5ac0c888eef188..c8362c67df1da7 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: 2024-05-24 +date: 2024-05-28 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 5de7bd244fb160..ef70c99cabe880 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: 2024-05-24 +date: 2024-05-28 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 4b5ac71d070b32..833c33a62bd55e 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: 2024-05-24 +date: 2024-05-28 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 284720f78a1f1e..47703dd4ec2bff 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: 2024-05-24 +date: 2024-05-28 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 47a059050556c0..09f79edf13d94c 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: 2024-05-24 +date: 2024-05-28 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 3be4ec99b56589..5aa4c443dc77e3 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: 2024-05-24 +date: 2024-05-28 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 3f5a6e0986623a..6a588031ad6a22 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: 2024-05-24 +date: 2024-05-28 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 8a283fd58b907e..79bf2c33083299 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: 2024-05-24 +date: 2024-05-28 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 10f1c54e86a4e4..833a0bd8db4350 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: 2024-05-24 +date: 2024-05-28 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 095f74d720395e..f54fbea82a02c8 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: 2024-05-24 +date: 2024-05-28 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 fe6d6a93f56ebf..d0bca350e0be04 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: 2024-05-24 +date: 2024-05-28 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 54aec683bc94ec..90f766692b2f74 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: 2024-05-24 +date: 2024-05-28 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 e4ac4bd9ab089d..3f38fcc654c770 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: 2024-05-24 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'screenshotting'] --- import screenshottingObj from './screenshotting.devdocs.json'; diff --git a/api_docs/search_connectors.mdx b/api_docs/search_connectors.mdx index 000c11d8dbbc78..0aa12cd1b2c736 100644 --- a/api_docs/search_connectors.mdx +++ b/api_docs/search_connectors.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/searchConnectors title: "searchConnectors" image: https://source.unsplash.com/400x175/?github description: API docs for the searchConnectors plugin -date: 2024-05-24 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'searchConnectors'] --- import searchConnectorsObj from './search_connectors.devdocs.json'; diff --git a/api_docs/search_notebooks.mdx b/api_docs/search_notebooks.mdx index 3e26e0fe9c2dea..b5583fdfce4734 100644 --- a/api_docs/search_notebooks.mdx +++ b/api_docs/search_notebooks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/searchNotebooks title: "searchNotebooks" image: https://source.unsplash.com/400x175/?github description: API docs for the searchNotebooks plugin -date: 2024-05-24 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'searchNotebooks'] --- import searchNotebooksObj from './search_notebooks.devdocs.json'; diff --git a/api_docs/search_playground.mdx b/api_docs/search_playground.mdx index 60b5d8f545af87..84e9db756dd97f 100644 --- a/api_docs/search_playground.mdx +++ b/api_docs/search_playground.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/searchPlayground title: "searchPlayground" image: https://source.unsplash.com/400x175/?github description: API docs for the searchPlayground plugin -date: 2024-05-24 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'searchPlayground'] --- import searchPlaygroundObj from './search_playground.devdocs.json'; diff --git a/api_docs/security.mdx b/api_docs/security.mdx index dc3c19666d11f1..6f7149d03dfdc3 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: 2024-05-24 +date: 2024-05-28 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 5986bb936f2dc9..d9ba075dcf8bc4 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: 2024-05-24 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'securitySolution'] --- import securitySolutionObj from './security_solution.devdocs.json'; diff --git a/api_docs/security_solution_ess.mdx b/api_docs/security_solution_ess.mdx index c3851a35558f4b..ec94888b8d92b8 100644 --- a/api_docs/security_solution_ess.mdx +++ b/api_docs/security_solution_ess.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/securitySolutionEss title: "securitySolutionEss" image: https://source.unsplash.com/400x175/?github description: API docs for the securitySolutionEss plugin -date: 2024-05-24 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'securitySolutionEss'] --- import securitySolutionEssObj from './security_solution_ess.devdocs.json'; diff --git a/api_docs/security_solution_serverless.mdx b/api_docs/security_solution_serverless.mdx index e32f42d5cf4927..b61524f0d74ee9 100644 --- a/api_docs/security_solution_serverless.mdx +++ b/api_docs/security_solution_serverless.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/securitySolutionServerless title: "securitySolutionServerless" image: https://source.unsplash.com/400x175/?github description: API docs for the securitySolutionServerless plugin -date: 2024-05-24 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'securitySolutionServerless'] --- import securitySolutionServerlessObj from './security_solution_serverless.devdocs.json'; diff --git a/api_docs/serverless.mdx b/api_docs/serverless.mdx index 28946405d1c4a5..2952453c01ba68 100644 --- a/api_docs/serverless.mdx +++ b/api_docs/serverless.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/serverless title: "serverless" image: https://source.unsplash.com/400x175/?github description: API docs for the serverless plugin -date: 2024-05-24 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'serverless'] --- import serverlessObj from './serverless.devdocs.json'; diff --git a/api_docs/serverless_observability.mdx b/api_docs/serverless_observability.mdx index 7579dba45f9d94..1861e9c636116e 100644 --- a/api_docs/serverless_observability.mdx +++ b/api_docs/serverless_observability.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/serverlessObservability title: "serverlessObservability" image: https://source.unsplash.com/400x175/?github description: API docs for the serverlessObservability plugin -date: 2024-05-24 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'serverlessObservability'] --- import serverlessObservabilityObj from './serverless_observability.devdocs.json'; diff --git a/api_docs/serverless_search.mdx b/api_docs/serverless_search.mdx index dbfcdf77d6ceed..9699504776f542 100644 --- a/api_docs/serverless_search.mdx +++ b/api_docs/serverless_search.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/serverlessSearch title: "serverlessSearch" image: https://source.unsplash.com/400x175/?github description: API docs for the serverlessSearch plugin -date: 2024-05-24 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'serverlessSearch'] --- import serverlessSearchObj from './serverless_search.devdocs.json'; diff --git a/api_docs/session_view.mdx b/api_docs/session_view.mdx index 5987b548200482..213129577c4cc9 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: 2024-05-24 +date: 2024-05-28 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 72c565c6f7e87c..260d8f7d79ff80 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: 2024-05-24 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'share'] --- import shareObj from './share.devdocs.json'; diff --git a/api_docs/slo.mdx b/api_docs/slo.mdx index 852ec8fda1a711..02c9f97ea436b9 100644 --- a/api_docs/slo.mdx +++ b/api_docs/slo.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/slo title: "slo" image: https://source.unsplash.com/400x175/?github description: API docs for the slo plugin -date: 2024-05-24 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'slo'] --- import sloObj from './slo.devdocs.json'; diff --git a/api_docs/snapshot_restore.mdx b/api_docs/snapshot_restore.mdx index 47417e4799866f..9f31baa803dfa5 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: 2024-05-24 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'snapshotRestore'] --- import snapshotRestoreObj from './snapshot_restore.devdocs.json'; diff --git a/api_docs/spaces.devdocs.json b/api_docs/spaces.devdocs.json index 186dd03eab6af1..0d6cb86b49fa46 100644 --- a/api_docs/spaces.devdocs.json +++ b/api_docs/spaces.devdocs.json @@ -1782,6 +1782,22 @@ "path": "x-pack/plugins/spaces/common/types/space/v1.ts", "deprecated": false, "trackAdoption": false + }, + { + "parentPluginId": "spaces", + "id": "def-public.Space.solution", + "type": "CompoundType", + "tags": [], + "label": "solution", + "description": [ + "\nSolution selected for this space." + ], + "signature": [ + "\"search\" | \"security\" | \"observability\" | \"classic\" | undefined" + ], + "path": "x-pack/plugins/spaces/common/types/space/v1.ts", + "deprecated": false, + "trackAdoption": false } ], "initialIsOpen": false @@ -1809,7 +1825,7 @@ "The space to represent with an avatar." ], "signature": [ - "{ id?: string | undefined; name?: string | undefined; description?: string | undefined; color?: string | undefined; initials?: string | undefined; imageUrl?: string | undefined; disabledFeatures?: string[] | undefined; _reserved?: boolean | undefined; }" + "{ id?: string | undefined; name?: string | undefined; description?: string | undefined; color?: string | undefined; initials?: string | undefined; imageUrl?: string | undefined; disabledFeatures?: string[] | undefined; _reserved?: boolean | undefined; solution?: \"search\" | \"security\" | \"observability\" | \"classic\" | undefined; }" ], "path": "x-pack/plugins/spaces/public/space_avatar/types.ts", "deprecated": false, @@ -3674,6 +3690,22 @@ "path": "x-pack/plugins/spaces/common/types/space/v1.ts", "deprecated": false, "trackAdoption": false + }, + { + "parentPluginId": "spaces", + "id": "def-server.Space.solution", + "type": "CompoundType", + "tags": [], + "label": "solution", + "description": [ + "\nSolution selected for this space." + ], + "signature": [ + "\"search\" | \"security\" | \"observability\" | \"classic\" | undefined" + ], + "path": "x-pack/plugins/spaces/common/types/space/v1.ts", + "deprecated": false, + "trackAdoption": false } ], "initialIsOpen": false @@ -4908,6 +4940,22 @@ "path": "x-pack/plugins/spaces/common/types/space/v1.ts", "deprecated": false, "trackAdoption": false + }, + { + "parentPluginId": "spaces", + "id": "def-common.Space.solution", + "type": "CompoundType", + "tags": [], + "label": "solution", + "description": [ + "\nSolution selected for this space." + ], + "signature": [ + "\"search\" | \"security\" | \"observability\" | \"classic\" | undefined" + ], + "path": "x-pack/plugins/spaces/common/types/space/v1.ts", + "deprecated": false, + "trackAdoption": false } ], "initialIsOpen": false diff --git a/api_docs/spaces.mdx b/api_docs/spaces.mdx index d02bce54d307db..8be13fa634bb76 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: 2024-05-24 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'spaces'] --- import spacesObj from './spaces.devdocs.json'; @@ -21,7 +21,7 @@ Contact [@elastic/kibana-security](https://github.com/orgs/elastic/teams/kibana- | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 257 | 0 | 66 | 0 | +| 260 | 0 | 66 | 0 | ## Client diff --git a/api_docs/stack_alerts.mdx b/api_docs/stack_alerts.mdx index 66b393185c3bb5..9809b82d487514 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: 2024-05-24 +date: 2024-05-28 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 1e60ae211a315e..468665987fc1c4 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: 2024-05-24 +date: 2024-05-28 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 6d835b73a9fab8..376912aa325212 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: 2024-05-24 +date: 2024-05-28 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 22866b6c8c7280..29f8ea43773dd1 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: 2024-05-24 +date: 2024-05-28 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 080cd42d8e793e..7bf7cb53c235d5 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: 2024-05-24 +date: 2024-05-28 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 f4e0ab3f3d72c4..8474a6f1f0439d 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: 2024-05-24 +date: 2024-05-28 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 2f0f5ea28b22de..8f633ad94e16eb 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: 2024-05-24 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'telemetryManagementSection'] --- import telemetryManagementSectionObj from './telemetry_management_section.devdocs.json'; diff --git a/api_docs/text_based_languages.mdx b/api_docs/text_based_languages.mdx index caf66bd513b2a2..0b283cbd949895 100644 --- a/api_docs/text_based_languages.mdx +++ b/api_docs/text_based_languages.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/textBasedLanguages title: "textBasedLanguages" image: https://source.unsplash.com/400x175/?github description: API docs for the textBasedLanguages plugin -date: 2024-05-24 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'textBasedLanguages'] --- import textBasedLanguagesObj from './text_based_languages.devdocs.json'; diff --git a/api_docs/threat_intelligence.mdx b/api_docs/threat_intelligence.mdx index f608b7b700ff10..b06e062c5c58e9 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: 2024-05-24 +date: 2024-05-28 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 755868cb986402..5e662347350862 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: 2024-05-24 +date: 2024-05-28 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 b1990aef57cdb5..c009ecdff62152 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: 2024-05-24 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'transform'] --- import transformObj from './transform.devdocs.json'; diff --git a/api_docs/triggers_actions_ui.mdx b/api_docs/triggers_actions_ui.mdx index eaa5e23d3c2916..831c39c801a349 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: 2024-05-24 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'triggersActionsUi'] --- import triggersActionsUiObj from './triggers_actions_ui.devdocs.json'; diff --git a/api_docs/ui_actions.mdx b/api_docs/ui_actions.mdx index 64a5cb9d40ceba..a860c242f5c8d1 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: 2024-05-24 +date: 2024-05-28 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 31877ae7e09db7..74f3b210a7106e 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: 2024-05-24 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'uiActionsEnhanced'] --- import uiActionsEnhancedObj from './ui_actions_enhanced.devdocs.json'; diff --git a/api_docs/unified_doc_viewer.mdx b/api_docs/unified_doc_viewer.mdx index d315349b0d4914..51ea5a2b8961b8 100644 --- a/api_docs/unified_doc_viewer.mdx +++ b/api_docs/unified_doc_viewer.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/unifiedDocViewer title: "unifiedDocViewer" image: https://source.unsplash.com/400x175/?github description: API docs for the unifiedDocViewer plugin -date: 2024-05-24 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'unifiedDocViewer'] --- import unifiedDocViewerObj from './unified_doc_viewer.devdocs.json'; diff --git a/api_docs/unified_histogram.mdx b/api_docs/unified_histogram.mdx index 0152f601b7c30b..e9dbda8e2d1b20 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: 2024-05-24 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'unifiedHistogram'] --- import unifiedHistogramObj from './unified_histogram.devdocs.json'; diff --git a/api_docs/unified_search.mdx b/api_docs/unified_search.mdx index 3941dac118646d..7a68e714da3078 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: 2024-05-24 +date: 2024-05-28 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 c3bbc3ce2e333c..b38c98db0cee35 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: 2024-05-24 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'unifiedSearch.autocomplete'] --- import unifiedSearchAutocompleteObj from './unified_search_autocomplete.devdocs.json'; diff --git a/api_docs/uptime.mdx b/api_docs/uptime.mdx index 00788dee334789..4dcaa079c4ea91 100644 --- a/api_docs/uptime.mdx +++ b/api_docs/uptime.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/uptime title: "uptime" image: https://source.unsplash.com/400x175/?github description: API docs for the uptime plugin -date: 2024-05-24 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'uptime'] --- import uptimeObj from './uptime.devdocs.json'; diff --git a/api_docs/url_forwarding.mdx b/api_docs/url_forwarding.mdx index 956b46ce6a3b1a..33f67b71b933b7 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: 2024-05-24 +date: 2024-05-28 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 362186a7c033bb..4bd786927cd1bf 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: 2024-05-24 +date: 2024-05-28 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 eb7d4b2f317349..d88075906fd3be 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: 2024-05-24 +date: 2024-05-28 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 4b1379baf3e52d..fd595f5b764bd3 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: 2024-05-24 +date: 2024-05-28 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 f47bb32dad01dd..1938631263e388 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: 2024-05-24 +date: 2024-05-28 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 a38b4ed0a5d163..252e062f1e769e 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: 2024-05-24 +date: 2024-05-28 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 336b5362b2209d..51b3ce29d070e5 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: 2024-05-24 +date: 2024-05-28 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 301929b876ce66..3cf0916b594d77 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: 2024-05-24 +date: 2024-05-28 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 f5bfe4363f07f6..26c974385671be 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: 2024-05-24 +date: 2024-05-28 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 fe35fa07fcaaf9..1e5bcab88790c0 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: 2024-05-24 +date: 2024-05-28 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 f944581881febf..4ecd068c6e914f 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: 2024-05-24 +date: 2024-05-28 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 1148f54c222857..99129c1890b7e0 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: 2024-05-24 +date: 2024-05-28 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 cb43d04403c35e..464c56ebf7bef6 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: 2024-05-24 +date: 2024-05-28 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 873000edb05501..83856809848f79 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: 2024-05-24 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'visualizations'] --- import visualizationsObj from './visualizations.devdocs.json'; diff --git a/config/serverless.yml b/config/serverless.yml index 055f564539c2e9..654b2a0a3a55ed 100644 --- a/config/serverless.yml +++ b/config/serverless.yml @@ -137,10 +137,12 @@ xpack.alerting.rules.maxScheduledPerMinute: 400 xpack.actions.run.maxAttempts: 10 xpack.actions.queued.max: 10000 -# Disables ESQL in advanced settings (hides it from the UI) uiSettings: overrides: + # Disables ESQL in advanced settings (hides it from the UI) enableESQL: true + # Disables `Defer loading panels below "the fold"` + labs:dashboard:deferBelowFold: false # Task Manager xpack.task_manager.allow_reading_invalid_state: false diff --git a/docs/api/spaces-management/get.asciidoc b/docs/api/spaces-management/get.asciidoc index 48245b77866040..d2dbeb0a3f4b40 100644 --- a/docs/api/spaces-management/get.asciidoc +++ b/docs/api/spaces-management/get.asciidoc @@ -31,6 +31,7 @@ The API returns the following: "color": "#aabbcc", "initials": "MK", "disabledFeatures": [], - "imageUrl": "" + "imageUrl": "", + "solution": "search" } -------------------------------------------------- diff --git a/docs/api/spaces-management/get_all.asciidoc b/docs/api/spaces-management/get_all.asciidoc index 3c95b1b9044417..0fd332c12b739e 100644 --- a/docs/api/spaces-management/get_all.asciidoc +++ b/docs/api/spaces-management/get_all.asciidoc @@ -71,7 +71,8 @@ The API returns the following: "name": "Sales", "initials": "MK", "disabledFeatures": ["discover"], - "imageUrl": "" + "imageUrl": "", + "solution": "observability" } ] -------------------------------------------------- diff --git a/docs/api/spaces-management/post.asciidoc b/docs/api/spaces-management/post.asciidoc index 035fe897da2512..b72d4df79c3d6a 100644 --- a/docs/api/spaces-management/post.asciidoc +++ b/docs/api/spaces-management/post.asciidoc @@ -36,6 +36,9 @@ experimental[] Create a {kib} space. (Optional, string) The data-URL encoded image to display in the space avatar. If specified, `initials` will not be displayed, and the `color` will be visible as the background color for transparent images. For best results, your image should be 64x64. Images will not be optimized by this API call, so care should be taken when using custom images. +`solution`:: + (Optional, string) The solution defined for the space. Can be one of `security`, `observability`, `search`, `classic` + [[spaces-api-post-response-codes]] ==== Response codes diff --git a/docs/api/spaces-management/put.asciidoc b/docs/api/spaces-management/put.asciidoc index e8f75ee089d77c..0d1c8d5f2e7797 100644 --- a/docs/api/spaces-management/put.asciidoc +++ b/docs/api/spaces-management/put.asciidoc @@ -36,6 +36,9 @@ experimental[] Update an existing {kib} space. (Optional, string) Specifies the data-url encoded image to display in the space avatar. If specified, `initials` will not be displayed, and the `color` will be visible as the background color for transparent images. For best results, your image should be 64x64. Images will not be optimized by this API call, so care should be taken when using custom images. +`solution`:: + (Optional, string) The solution defined for the space. Can be one of `security`, `observability`, `search`, `classic`. + [[spaces-api-put-response-codes]] ==== Response codes diff --git a/examples/response_stream/public/mount.tsx b/examples/response_stream/public/mount.tsx index 7309abf5e0022e..77eebfcd2233dd 100644 --- a/examples/response_stream/public/mount.tsx +++ b/examples/response_stream/public/mount.tsx @@ -10,6 +10,7 @@ import * as React from 'react'; import { render, unmountComponentAtNode } from 'react-dom'; import { CoreSetup, CoreStart, AppMountParameters } from '@kbn/core/public'; import { KibanaContextProvider } from '@kbn/kibana-react-plugin/public'; +import { KibanaRenderContextProvider } from '@kbn/react-kibana-context-render'; import { PLUGIN_NAME } from '../common/constants'; import { ResponseStreamStartPlugins } from './plugin'; import { App } from './containers/app'; @@ -41,9 +42,11 @@ export const mount = ]); const reactElement = ( - - - + + + + + ); render(reactElement, element); return () => unmountComponentAtNode(element); diff --git a/examples/response_stream/tsconfig.json b/examples/response_stream/tsconfig.json index 0de5b8c0df5a7f..7a0b99ab1eba2a 100644 --- a/examples/response_stream/tsconfig.json +++ b/examples/response_stream/tsconfig.json @@ -22,5 +22,6 @@ "@kbn/config-schema", "@kbn/shared-ux-router", "@kbn/ml-response-stream", + "@kbn/react-kibana-context-render", ] } diff --git a/package.json b/package.json index f4f03cb30722e0..8d4fc2fce5b726 100644 --- a/package.json +++ b/package.json @@ -149,6 +149,7 @@ "@kbn/aiops-plugin": "link:x-pack/plugins/aiops", "@kbn/aiops-test-utils": "link:x-pack/packages/ml/aiops_test_utils", "@kbn/alerting-api-integration-test-plugin": "link:x-pack/test/alerting_api_integration/common/plugins/alerts", + "@kbn/alerting-comparators": "link:x-pack/packages/kbn-alerting-comparators", "@kbn/alerting-example-plugin": "link:x-pack/examples/alerting_example", "@kbn/alerting-fixture-plugin": "link:x-pack/test/functional_with_es_ssl/plugins/alerts", "@kbn/alerting-plugin": "link:x-pack/plugins/alerting", @@ -503,6 +504,7 @@ "@kbn/global-search-test-plugin": "link:x-pack/test/plugin_functional/plugins/global_search_test", "@kbn/graph-plugin": "link:x-pack/plugins/graph", "@kbn/grokdebugger-plugin": "link:x-pack/plugins/grokdebugger", + "@kbn/grouping": "link:packages/kbn-grouping", "@kbn/guided-onboarding": "link:packages/kbn-guided-onboarding", "@kbn/guided-onboarding-example-plugin": "link:examples/guided_onboarding_example", "@kbn/guided-onboarding-plugin": "link:src/plugins/guided_onboarding", @@ -739,7 +741,6 @@ "@kbn/securitysolution-ecs": "link:packages/kbn-securitysolution-ecs", "@kbn/securitysolution-es-utils": "link:packages/kbn-securitysolution-es-utils", "@kbn/securitysolution-exception-list-components": "link:packages/kbn-securitysolution-exception-list-components", - "@kbn/securitysolution-grouping": "link:packages/kbn-securitysolution-grouping", "@kbn/securitysolution-hook-utils": "link:packages/kbn-securitysolution-hook-utils", "@kbn/securitysolution-io-ts-alerting-types": "link:packages/kbn-securitysolution-io-ts-alerting-types", "@kbn/securitysolution-io-ts-list-types": "link:packages/kbn-securitysolution-io-ts-list-types", 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 991e5e40b63407..4b3cfde40825ce 100644 --- a/packages/kbn-apm-synthtrace-client/src/lib/apm/instance.ts +++ b/packages/kbn-apm-synthtrace-client/src/lib/apm/instance.ts @@ -76,7 +76,6 @@ export class Instance extends Entity { return new ApmError({ ...this.fields, 'error.exception': [{ message, ...(type ? { type } : {}) }], - 'error.grouping_name': getErrorGroupingKey(message), 'error.culprit': culprit, }); } 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 a8848c1954e403..43b924d9d8a18d 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,11 +60,6 @@ describe('transactions with errors', () => { .errors(instance.error({ message: 'test error' }).timestamp(timestamp)) .serialize(); - expect(error['error.grouping_name']).toEqual( - '4274b1899eba687801198c89f64a3fdade080a475c8a54881ba8fa10e7f45691' - ); - expect(error['error.grouping_key']).toMatchInlineSnapshot( - `"4274b1899eba687801198c89f64a3fdade080a475c8a54881ba8fa10e7f45691"` - ); + expect(error['error.grouping_key']).toMatchInlineSnapshot(`"0000000000000000000000test error"`); }); }); diff --git a/packages/kbn-check-mappings-update-cli/current_fields.json b/packages/kbn-check-mappings-update-cli/current_fields.json index ad653171f27079..703730a262281c 100644 --- a/packages/kbn-check-mappings-update-cli/current_fields.json +++ b/packages/kbn-check-mappings-update-cli/current_fields.json @@ -931,7 +931,8 @@ ], "slo-settings": [], "space": [ - "name" + "name", + "solution" ], "spaces-usage-stats": [], "synthetics-monitor": [ diff --git a/packages/kbn-check-mappings-update-cli/current_mappings.json b/packages/kbn-check-mappings-update-cli/current_mappings.json index 6d54282cabafee..94f2148280f083 100644 --- a/packages/kbn-check-mappings-update-cli/current_mappings.json +++ b/packages/kbn-check-mappings-update-cli/current_mappings.json @@ -3062,6 +3062,9 @@ } }, "type": "text" + }, + "solution": { + "type": "keyword" } } }, diff --git a/packages/kbn-config-schema/README.md b/packages/kbn-config-schema/README.md index a4f2c1f6458cfa..5916f49ef28362 100644 --- a/packages/kbn-config-schema/README.md +++ b/packages/kbn-config-schema/README.md @@ -5,35 +5,38 @@ Kibana configuration entries providing developers with a fully typed model of th ## Table of Contents -- [Why `@kbn/config-schema`?](#why-kbnconfig-schema) -- [Schema building blocks](#schema-building-blocks) - - [Basic types](#basic-types) - - [`schema.string()`](#schemastring) - - [`schema.number()`](#schemanumber) - - [`schema.boolean()`](#schemaboolean) - - [`schema.literal()`](#schemaliteral) - - [`schema.buffer()`](#schemabuffer) - - [`schema.stream()`](#schemastream) - - [Composite types](#composite-types) - - [`schema.arrayOf()`](#schemaarrayof) - - [`schema.object()`](#schemaobject) - - [`schema.recordOf()`](#schemarecordof) - - [`schema.mapOf()`](#schemamapof) - - [Advanced types](#advanced-types) - - [`schema.oneOf()`](#schemaoneof) - - [`schema.any()`](#schemaany) - - [`schema.maybe()`](#schemamaybe) - - [`schema.nullable()`](#schemanullable) - - [`schema.never()`](#schemanever) - - [`schema.uri()`](#schemauri) - - [`schema.byteSize()`](#schemabytesize) - - [`schema.duration()`](#schemaduration) - - [`schema.conditional()`](#schemaconditional) - - [References](#references) - - [`schema.contextRef()`](#schemacontextref) - - [`schema.siblingRef()`](#schemasiblingref) -- [Custom validation](#custom-validation) -- [Default values](#default-values) +- [`@kbn/config-schema` — The Kibana config validation library](#kbnconfig-schema--the-kibana-config-validation-library) + - [Table of Contents](#table-of-contents) + - [Why `@kbn/config-schema`?](#why-kbnconfig-schema) + - [Schema building blocks](#schema-building-blocks) + - [Basic types](#basic-types) + - [`schema.string()`](#schemastring) + - [`schema.number()`](#schemanumber) + - [`schema.boolean()`](#schemaboolean) + - [`schema.literal()`](#schemaliteral) + - [`schema.buffer()`](#schemabuffer) + - [`schema.stream()`](#schemastream) + - [Composite types](#composite-types) + - [`schema.arrayOf()`](#schemaarrayof) + - [`schema.object()`](#schemaobject) + - [`schema.recordOf()`](#schemarecordof) + - [`schema.mapOf()`](#schemamapof) + - [Advanced types](#advanced-types) + - [`schema.oneOf()`](#schemaoneof) + - [`schema.any()`](#schemaany) + - [`schema.maybe()`](#schemamaybe) + - [`schema.nullable()`](#schemanullable) + - [`schema.never()`](#schemanever) + - [`schema.uri()`](#schemauri) + - [`schema.byteSize()`](#schemabytesize) + - [`schema.duration()`](#schemaduration) + - [`schema.conditional()`](#schemaconditional) + - [`schema.lazy()`](#schemalazy) + - [References](#references) + - [`schema.contextRef()`](#schemacontextref) + - [`schema.siblingRef()`](#schemasiblingref) + - [Custom validation](#custom-validation) + - [Default values](#default-values) ## Why `@kbn/config-schema`? @@ -44,7 +47,7 @@ There are a number of reasons why we decided to roll our own solution for the co * **Limited API surface** - having a future rich library is awesome, but it's a really hard task to audit such library and make sure everything is sane and secure enough. As everyone knows complexity is the enemy of security and hence we'd like to have a full control over what exactly we expose and commit to maintain. * **Custom error messages** - detailed validation error messages are a great help to developers, but at the same time they can contain information that's way too sensitive to expose to everyone. We'd like to control these messages and make them only as detailed as really needed. For example, we don't want validation error messages to contain the passwords for internal users to show-up in the logs. These logs are commonly ingested into Elasticsearch, and accessible to a large number of users which shouldn't have access to the internal user's password. * **Type information** - having run-time guarantees is great, but additionally having compile-time guarantees is even better. We'd like to provide developers with a fully typed model of the validated data so that it's harder to misuse it _after_ validation. -* **Upgradability** - no matter how well a validation library is implemented, it will have bugs and may need to be improved at some point anyway. Some external libraries are very well supported, some aren't or won't be in the future. It's always a risk to depend on an external party with their own release cadence when you need to quickly fix a security vulnerability in a patch version. We'd like to have a better control over lifecycle of such an important piece of our codebase. +* **Upgradability** - no matter how well a validation library is implemented, it will have bugs and may need to be improved at some point anyway. Some external libraries are very well supported, some aren't or won't be in the future. It's always a risk to depend on an external party with their own release cadence when you need to quickly fix a security vulnerability in a patch version. We'd like to have a better control over lifecycle of such an important piece of our codebase. ## Schema building blocks @@ -243,7 +246,7 @@ __Options:__ __Usage:__ ```typescript -const valueSchema = schema.object({ +const valueSchema = schema.object({ isEnabled: schema.boolean({ defaultValue: false }), name: schema.string({ minLength: 10 }), }); @@ -461,6 +464,46 @@ const valueSchema = schema.object({ __Notes:__ * Conditional schemas may be hard to read and understand and hence should be used only sparingly. +#### `schema.lazy()` + +Allows recursive runtime types to be defined. + +Takes a required generic type argument and a required string that represents the id of the schema. + +It is recommended to pick a globally unique ID for your schema. Consider creating only IDs that are prefixed with your +domain, e.g. `myPlugin_myRecursiveType`. + +IDs must be unique within a _schema ancestry_. You can use the same ID in multiple, separate schemas as long as they do not +share a common ancestor object. However, if you want to generate OAS from your schema you must ensure a globally +unique ID in order to avoid overriding schemas with the same ID. + +Note: use of `meta.id` is required to associate the schema with the ID used in the `schema.lazy()` call in order to +create a recursive type (see usage). + +__Output type:__ `T` + +__Usage:__ +```typescript +interface RecursiveType { + name: string; + self: undefined | RecursiveType; +} + +// Do not assign this ID to any other schema to avoid collisions. +const id = 'myPlugin_myRecursiveType'; +const object = schema.object( + { + name: schema.string(), + self: schema.lazy(id), + }, + { meta: { id } } +); +``` + +__Notes:__ +* Preferably use this sparingly and only to create recursive types. +* Intended to be used only as properties within `schema.object()` types. + ### References #### `schema.contextRef()` @@ -471,7 +514,7 @@ __Output type:__ `TReferenceValue` __Usage:__ ```typescript -const valueSchema = schema.object({ +const valueSchema = schema.object({ env: schema.string({ defaultValue: schema.contextRef('envName') }), }); valueSchema.validate({}, { envName: 'dev' }); @@ -479,7 +522,7 @@ valueSchema.validate({}, { envName: 'dev' }); __Notes:__ * The `@kbn/config-schema` neither validates nor coerces the "dereferenced" value and the developer is responsible for making sure that it has the appropriate type. -* The root context that Kibana provides during config validation includes lots of useful properties like `environment name` that can be used to provide a strict schema for production and more relaxed one for development. +* The root context that Kibana provides during config validation includes lots of useful properties like `environment name` that can be used to provide a strict schema for production and more relaxed one for development. #### `schema.siblingRef()` @@ -529,7 +572,7 @@ to denote the failed validation or not return anything at all (`void`) otherwise Another use case for custom validation functions is when the schema depends on some run-time data: ```typescript -const gesSchema = randomRunTimeSeed => schema.string({ +const gesSchema = randomRunTimeSeed => schema.string({ validate: value => value !== randomRunTimeSeed ? 'value is not allowed' : undefined }); diff --git a/packages/kbn-config-schema/src/helpers/offering_based_schema.ts b/packages/kbn-config-schema/src/helpers/offering_based_schema.ts index 09ddfc3a756443..b4ccaf57330f24 100644 --- a/packages/kbn-config-schema/src/helpers/offering_based_schema.ts +++ b/packages/kbn-config-schema/src/helpers/offering_based_schema.ts @@ -12,6 +12,16 @@ import { Type, TypeOptions } from '../types'; /** * Helper to apply different validations depending on whether Kibana is running the Serverless or Traditional offering. * + * @remark This utility is intended to be used for Kibana YAML-based configuration validation only! Using it in other + * contexts will lead to only `traditional` validation being used. + * + * If you want to switch schemas based on the offering in other contexts do the following: + * + * ```ts + * // env is passed to your plugin constructor + * const schema = env.packageInfo.buildFlavor === 'serverless' ? baseSchema.extend(a) : baseSchema.extend(b); + * ``` + * * @example Only allow the setting on Serverless * const config = schema.object({ * myProp: offeringBasedSchema({ serverless: schema.boolean({ defaultValue: true }) }), diff --git a/packages/kbn-config-schema/src/types/lazy.test.ts b/packages/kbn-config-schema/src/types/lazy.test.ts index 52864a9c825a87..6ff18fd730bdbf 100644 --- a/packages/kbn-config-schema/src/types/lazy.test.ts +++ b/packages/kbn-config-schema/src/types/lazy.test.ts @@ -68,4 +68,28 @@ describe('lazy', () => { expect(value).toEqual(invalidSelf); expect(error?.message).toBe('expected value of type [string] but got [number]'); }); + + it('requires a schema with a given ID to be present in the schema when validating', () => { + expect(() => + schema + .object({ + lazy: schema.lazy('unknown'), + }) + .validate({ lazy: {} }) + ).toThrow(/outside of schema boundaries/); + }); + + it('disallows duplicate ids in the same schema', () => { + const dupId = 'dupId'; + const schema1 = schema.object({ a: schema.string() }, { meta: { id: dupId } }); + const schema2 = schema.object({ b: schema.string() }, { meta: { id: dupId } }); + + expect(() => + schema.object({ + schema1, + schema2, + lazy: schema.lazy(dupId), + }) + ).toThrow(/Cannot add different schemas with the same id/); + }); }); diff --git a/packages/kbn-esql-utils/index.ts b/packages/kbn-esql-utils/index.ts index 31fa7eb3a14c89..306d187f1ef077 100644 --- a/packages/kbn-esql-utils/index.ts +++ b/packages/kbn-esql-utils/index.ts @@ -9,6 +9,7 @@ export { getESQLAdHocDataview, getIndexPatternFromESQLQuery, + hasTransformationalCommand, getLimitFromESQLQuery, removeDropCommandsFromESQLQuery, getIndexForESQLQuery, diff --git a/packages/kbn-esql-utils/src/index.ts b/packages/kbn-esql-utils/src/index.ts index a48765e8c0489a..18fc938b5eee5a 100644 --- a/packages/kbn-esql-utils/src/index.ts +++ b/packages/kbn-esql-utils/src/index.ts @@ -14,6 +14,7 @@ export { getIndexPatternFromESQLQuery, getLimitFromESQLQuery, removeDropCommandsFromESQLQuery, + hasTransformationalCommand, } from './utils/query_parsing_helpers'; export { appendToESQLQuery, appendWhereClauseToESQLQuery } from './utils/append_to_query'; export { getESQLQueryColumns, getESQLQueryColumnsRaw, getESQLResults } from './utils/run_query'; diff --git a/packages/kbn-esql-utils/src/utils/query_parsing_helpers.test.ts b/packages/kbn-esql-utils/src/utils/query_parsing_helpers.test.ts index 9e7686127af02d..3cb3c3171942da 100644 --- a/packages/kbn-esql-utils/src/utils/query_parsing_helpers.test.ts +++ b/packages/kbn-esql-utils/src/utils/query_parsing_helpers.test.ts @@ -10,6 +10,7 @@ import { getIndexPatternFromESQLQuery, getLimitFromESQLQuery, removeDropCommandsFromESQLQuery, + hasTransformationalCommand, } from './query_parsing_helpers'; describe('esql query helpers', () => { @@ -95,4 +96,25 @@ describe('esql query helpers', () => { ).toBe('from a | keep c '); }); }); + + describe('hasTransformationalCommand', () => { + it('should return false for non transformational command', () => { + expect(hasTransformationalCommand('from a | eval b = 1')).toBeFalsy(); + }); + + it('should return true for stats', () => { + expect(hasTransformationalCommand('from a | stats count() as total by a=b')).toBeTruthy(); + }); + + it('should return true for keep', () => { + expect(hasTransformationalCommand('from a | keep field1, field2')).toBeTruthy(); + }); + + it('should return false for commented out transformational command', () => { + expect( + hasTransformationalCommand(`from logstash-* + // | stats var0 = avg(bytes) by geo.dest`) + ).toBeFalsy(); + }); + }); }); diff --git a/packages/kbn-esql-utils/src/utils/query_parsing_helpers.ts b/packages/kbn-esql-utils/src/utils/query_parsing_helpers.ts index af6c931406bacc..994551930cc5ac 100644 --- a/packages/kbn-esql-utils/src/utils/query_parsing_helpers.ts +++ b/packages/kbn-esql-utils/src/utils/query_parsing_helpers.ts @@ -18,6 +18,13 @@ export function getIndexPatternFromESQLQuery(esql?: string) { return indices?.map((index) => index.text).join(','); } +// For ES|QL we consider the following commands as transformational commands +export function hasTransformationalCommand(esql?: string) { + const transformationalCommands = ['stats', 'keep', 'metrics']; + const { ast } = getAstAndSyntaxErrors(esql); + return transformationalCommands.some((command) => ast.find(({ name }) => name === command)); +} + export function getLimitFromESQLQuery(esql: string): number { const limitCommands = esql.match(new RegExp(/LIMIT\s[0-9]+/, 'ig')); if (!limitCommands) { diff --git a/packages/kbn-esql-validation-autocomplete/scripts/generate_function_validation_tests.ts b/packages/kbn-esql-validation-autocomplete/scripts/generate_function_validation_tests.ts index bcf11337117f49..bc12ea1fbfb2f7 100644 --- a/packages/kbn-esql-validation-autocomplete/scripts/generate_function_validation_tests.ts +++ b/packages/kbn-esql-validation-autocomplete/scripts/generate_function_validation_tests.ts @@ -24,6 +24,7 @@ import { isSupportedFieldType, } from '../src/definitions/types'; import { FUNCTION_DESCRIBE_BLOCK_NAME } from '../src/validation/function_describe_block_name'; +import { getMaxMinNumberOfParams } from '../src/validation/helpers'; export const fieldNameFromType = (type: SupportedFieldType) => `${camelCase(type)}Field`; @@ -51,6 +52,7 @@ function generateTestsForEvalFunction(definition: FunctionDefinition) { generateWhereCommandTestsForEvalFunction(definition, testCases); generateEvalCommandTestsForEvalFunction(definition, testCases); generateSortCommandTestsForEvalFunction(definition, testCases); + generateNullAcceptanceTestsForFunction(definition, testCases); return testCases; } @@ -60,6 +62,7 @@ function generateTestsForAggFunction(definition: FunctionDefinition) { generateSortCommandTestsForAggFunction(definition, testCases); generateWhereCommandTestsForAggFunction(definition, testCases); generateEvalCommandTestsForAggFunction(definition, testCases); + generateNullAcceptanceTestsForFunction(definition, testCases); return testCases; } @@ -67,9 +70,60 @@ function generateTestsForGroupingFunction(definition: FunctionDefinition) { const testCases: Map = new Map(); generateStatsCommandTestsForGroupingFunction(definition, testCases); generateSortCommandTestsForGroupingFunction(definition, testCases); + generateNullAcceptanceTestsForFunction(definition, testCases); return testCases; } +function generateNullAcceptanceTestsForFunction( + definition: FunctionDefinition, + testCases: Map +) { + const { max, min } = getMaxMinNumberOfParams(definition); + const numberOfArgsToTest = max === Infinity ? min : max; + const signatureWithGreatestNumberOfParams = definition.signatures.find( + (signature) => signature.params.length === numberOfArgsToTest + )!; + + const commandToTestWith = definition.supportedCommands.includes('eval') ? 'eval' : 'stats'; + + // test that the function accepts nulls + testCases.set( + `from a_index | ${commandToTestWith} ${ + getFunctionSignatures( + { + ...definition, + signatures: [ + { + ...signatureWithGreatestNumberOfParams, + params: new Array(numberOfArgsToTest).fill({ name: 'null' }), + }, + ], + }, + { withTypes: false } + )[0].declaration + }`, + [] + ); + + testCases.set( + `row nullVar = null | ${commandToTestWith} ${ + getFunctionSignatures( + { + ...definition, + signatures: [ + { + ...signatureWithGreatestNumberOfParams, + params: new Array(numberOfArgsToTest).fill({ name: 'nullVar' }), + }, + ], + }, + { withTypes: false } + )[0].declaration + }`, + [] + ); +} + function generateRowCommandTestsForEvalFunction( { name, alias, signatures, ...defRest }: FunctionDefinition, testCases: Map @@ -262,9 +316,11 @@ function generateWhereCommandTestsForAggFunction( } function generateEvalCommandTestsForEvalFunction( - { name, signatures, alias, ...defRest }: FunctionDefinition, + definition: FunctionDefinition, testCases: Map ) { + const { name, signatures, alias, ...defRest } = definition; + for (const { params, ...signRest } of signatures) { const fieldMapping = getFieldMapping(params); testCases.set( @@ -401,26 +457,10 @@ function generateEvalCommandTestsForEvalFunction( // test that additional args are spotted - const getNumberOfParams = (signature: FunctionDefinition['signatures'][number]) => ({ - all: signature.params.length, - required: signature.params.filter(({ optional }) => !optional).length, - }); - - // get the signature with the greatest number of params - const [first, ...rest] = signatures; - let signatureWithGreatestNumberOfParams = first; - let { all: maxNumberOfArgs, required: minNumberOfArgs } = getNumberOfParams(first); - - for (const signature of rest) { - const numberOfParams = signature.params.length; - if (numberOfParams > signatureWithGreatestNumberOfParams.params.length) { - signatureWithGreatestNumberOfParams = signature; - } - - maxNumberOfArgs = Math.max(maxNumberOfArgs, numberOfParams); - const numberOfRequiredParams = signature.params.filter(({ optional }) => !optional).length; - minNumberOfArgs = Math.min(minNumberOfArgs, numberOfRequiredParams); - } + const { max: maxNumberOfArgs, min: minNumberOfArgs } = getMaxMinNumberOfParams(definition); + const signatureWithGreatestNumberOfParams = signatures.find( + (signature) => signature.params.length === maxNumberOfArgs + )!; const fieldMappingWithOneExtraArg = getFieldMapping( signatureWithGreatestNumberOfParams.params diff --git a/packages/kbn-esql-validation-autocomplete/src/definitions/builtin.ts b/packages/kbn-esql-validation-autocomplete/src/definitions/builtin.ts index 67fe23bada50fc..b430e6e7665dff 100644 --- a/packages/kbn-esql-validation-autocomplete/src/definitions/builtin.ts +++ b/packages/kbn-esql-validation-autocomplete/src/definitions/builtin.ts @@ -456,27 +456,6 @@ const logicFunctions: FunctionDefinition[] = [ ], returnType: 'boolean', }, - { - params: [ - { name: 'left', type: 'null' }, - { name: 'right', type: 'boolean' }, - ], - returnType: 'boolean', - }, - { - params: [ - { name: 'left', type: 'boolean' }, - { name: 'right', type: 'null' }, - ], - returnType: 'boolean', - }, - { - params: [ - { name: 'left', type: 'null' }, - { name: 'right', type: 'null' }, - ], - returnType: 'boolean', - }, ], })); diff --git a/packages/kbn-esql-validation-autocomplete/src/definitions/helpers.ts b/packages/kbn-esql-validation-autocomplete/src/definitions/helpers.ts index 0380a07385a739..7d70d1acc96318 100644 --- a/packages/kbn-esql-validation-autocomplete/src/definitions/helpers.ts +++ b/packages/kbn-esql-validation-autocomplete/src/definitions/helpers.ts @@ -95,12 +95,10 @@ export function printArguments( name, type, optional, - reference, }: { name: string; type: string | string[]; optional?: boolean; - reference?: string; }, withTypes: boolean ): string { diff --git a/packages/kbn-esql-validation-autocomplete/src/shared/helpers.ts b/packages/kbn-esql-validation-autocomplete/src/shared/helpers.ts index e17788eba21b85..73f60dffb6336a 100644 --- a/packages/kbn-esql-validation-autocomplete/src/shared/helpers.ts +++ b/packages/kbn-esql-validation-autocomplete/src/shared/helpers.ts @@ -217,12 +217,17 @@ export function getCommandOption(optionName: CommandOptionsDefinition['name']) { } function compareLiteralType(argType: string, item: ESQLLiteral) { + if (item.literalType === 'null') { + return true; + } + if (item.literalType !== 'string') { if (argType === item.literalType) { return true; } return false; } + if (argType === 'chrono_literal') { return chronoLiterals.some(({ name }) => name === item.text); } @@ -423,7 +428,7 @@ export function isEqualType( } const wrappedTypes = Array.isArray(validHit.type) ? validHit.type : [validHit.type]; // if final type is of type any make it pass for now - return wrappedTypes.some((ct) => ct === 'any' || argType === ct); + return wrappedTypes.some((ct) => ['any', 'null'].includes(ct) || argType === ct); } } diff --git a/packages/kbn-esql-validation-autocomplete/src/validation/esql_validation_meta_tests.json b/packages/kbn-esql-validation-autocomplete/src/validation/esql_validation_meta_tests.json index 89c267dd01f2d5..623d854b41b4a6 100644 --- a/packages/kbn-esql-validation-autocomplete/src/validation/esql_validation_meta_tests.json +++ b/packages/kbn-esql-validation-autocomplete/src/validation/esql_validation_meta_tests.json @@ -9014,6 +9014,16 @@ ], "warning": [] }, + { + "query": "from a_index | eval date_diff(null, null, null)", + "error": [], + "warning": [] + }, + { + "query": "row nullVar = null | eval date_diff(nullVar, nullVar, nullVar)", + "error": [], + "warning": [] + }, { "query": "row var = abs(5)", "error": [], @@ -9120,6 +9130,16 @@ ], "warning": [] }, + { + "query": "from a_index | eval abs(null)", + "error": [], + "warning": [] + }, + { + "query": "row nullVar = null | eval abs(nullVar)", + "error": [], + "warning": [] + }, { "query": "row var = acos(5)", "error": [], @@ -9226,6 +9246,16 @@ ], "warning": [] }, + { + "query": "from a_index | eval acos(null)", + "error": [], + "warning": [] + }, + { + "query": "row nullVar = null | eval acos(nullVar)", + "error": [], + "warning": [] + }, { "query": "row var = asin(5)", "error": [], @@ -9332,6 +9362,16 @@ ], "warning": [] }, + { + "query": "from a_index | eval asin(null)", + "error": [], + "warning": [] + }, + { + "query": "row nullVar = null | eval asin(nullVar)", + "error": [], + "warning": [] + }, { "query": "row var = atan(5)", "error": [], @@ -9438,6 +9478,16 @@ ], "warning": [] }, + { + "query": "from a_index | eval atan(null)", + "error": [], + "warning": [] + }, + { + "query": "row nullVar = null | eval atan(nullVar)", + "error": [], + "warning": [] + }, { "query": "row var = atan2(5, 5)", "error": [], @@ -9543,6 +9593,16 @@ ], "warning": [] }, + { + "query": "from a_index | eval atan2(null, null)", + "error": [], + "warning": [] + }, + { + "query": "row nullVar = null | eval atan2(nullVar, nullVar)", + "error": [], + "warning": [] + }, { "query": "row var = case(true, \"a\")", "error": [], @@ -9575,6 +9635,16 @@ ], "warning": [] }, + { + "query": "from a_index | eval case(null, null)", + "error": [], + "warning": [] + }, + { + "query": "row nullVar = null | eval case(nullVar, nullVar)", + "error": [], + "warning": [] + }, { "query": "row var = ceil(5)", "error": [], @@ -9681,6 +9751,16 @@ ], "warning": [] }, + { + "query": "from a_index | eval ceil(null)", + "error": [], + "warning": [] + }, + { + "query": "row nullVar = null | eval ceil(nullVar)", + "error": [], + "warning": [] + }, { "query": "row var = cidr_match(to_ip(\"127.0.0.1\"), \"a\")", "error": [], @@ -9758,6 +9838,16 @@ ], "warning": [] }, + { + "query": "from a_index | eval cidr_match(null, null)", + "error": [], + "warning": [] + }, + { + "query": "row nullVar = null | eval cidr_match(nullVar, nullVar)", + "error": [], + "warning": [] + }, { "query": "row var = coalesce(\"a\")", "error": [], @@ -10463,6 +10553,16 @@ "error": [], "warning": [] }, + { + "query": "from a_index | eval coalesce(null)", + "error": [], + "warning": [] + }, + { + "query": "row nullVar = null | eval coalesce(nullVar)", + "error": [], + "warning": [] + }, { "query": "row var = concat(\"a\", \"a\")", "error": [], @@ -10561,6 +10661,16 @@ ], "warning": [] }, + { + "query": "from a_index | eval concat(null, null)", + "error": [], + "warning": [] + }, + { + "query": "row nullVar = null | eval concat(nullVar, nullVar)", + "error": [], + "warning": [] + }, { "query": "row var = cos(5)", "error": [], @@ -10667,6 +10777,16 @@ ], "warning": [] }, + { + "query": "from a_index | eval cos(null)", + "error": [], + "warning": [] + }, + { + "query": "row nullVar = null | eval cos(nullVar)", + "error": [], + "warning": [] + }, { "query": "row var = cosh(5)", "error": [], @@ -10773,6 +10893,16 @@ ], "warning": [] }, + { + "query": "from a_index | eval cosh(null)", + "error": [], + "warning": [] + }, + { + "query": "row nullVar = null | eval cosh(nullVar)", + "error": [], + "warning": [] + }, { "query": "row var = date_extract(\"ALIGNED_DAY_OF_WEEK_IN_MONTH\", now())", "error": [], @@ -10839,6 +10969,16 @@ ], "warning": [] }, + { + "query": "from a_index | eval date_extract(null, null)", + "error": [], + "warning": [] + }, + { + "query": "row nullVar = null | eval date_extract(nullVar, nullVar)", + "error": [], + "warning": [] + }, { "query": "row var = date_format(\"a\", now())", "error": [], @@ -10904,6 +11044,16 @@ ], "warning": [] }, + { + "query": "from a_index | eval date_format(null, null)", + "error": [], + "warning": [] + }, + { + "query": "row nullVar = null | eval date_format(nullVar, nullVar)", + "error": [], + "warning": [] + }, { "query": "row var = date_parse(\"a\", \"a\")", "error": [], @@ -10998,6 +11148,16 @@ ], "warning": [] }, + { + "query": "from a_index | eval date_parse(null, null)", + "error": [], + "warning": [] + }, + { + "query": "row nullVar = null | eval date_parse(nullVar, nullVar)", + "error": [], + "warning": [] + }, { "query": "row var = date_trunc(1 year, now())", "error": [], @@ -11089,6 +11249,16 @@ "error": [], "warning": [] }, + { + "query": "from a_index | eval date_trunc(null, null)", + "error": [], + "warning": [] + }, + { + "query": "row nullVar = null | eval date_trunc(nullVar, nullVar)", + "error": [], + "warning": [] + }, { "query": "row var = e()", "error": [], @@ -11126,6 +11296,11 @@ "error": [], "warning": [] }, + { + "query": "row nullVar = null | eval e()", + "error": [], + "warning": [] + }, { "query": "row var = ends_with(\"a\", \"a\")", "error": [], @@ -11210,6 +11385,16 @@ ], "warning": [] }, + { + "query": "from a_index | eval ends_with(null, null)", + "error": [], + "warning": [] + }, + { + "query": "row nullVar = null | eval ends_with(nullVar, nullVar)", + "error": [], + "warning": [] + }, { "query": "row var = floor(5)", "error": [], @@ -11316,6 +11501,16 @@ ], "warning": [] }, + { + "query": "from a_index | eval floor(null)", + "error": [], + "warning": [] + }, + { + "query": "row nullVar = null | eval floor(nullVar)", + "error": [], + "warning": [] + }, { "query": "row var = greatest(\"a\")", "error": [], @@ -11639,6 +11834,16 @@ "error": [], "warning": [] }, + { + "query": "from a_index | eval greatest(null)", + "error": [], + "warning": [] + }, + { + "query": "row nullVar = null | eval greatest(nullVar)", + "error": [], + "warning": [] + }, { "query": "row var = least(\"a\")", "error": [], @@ -11962,6 +12167,16 @@ "error": [], "warning": [] }, + { + "query": "from a_index | eval least(null)", + "error": [], + "warning": [] + }, + { + "query": "row nullVar = null | eval least(nullVar)", + "error": [], + "warning": [] + }, { "query": "row var = left(\"a\", 5)", "error": [], @@ -12067,6 +12282,16 @@ ], "warning": [] }, + { + "query": "from a_index | eval left(null, null)", + "error": [], + "warning": [] + }, + { + "query": "row nullVar = null | eval left(nullVar, nullVar)", + "error": [], + "warning": [] + }, { "query": "row var = length(\"a\")", "error": [], @@ -12173,6 +12398,16 @@ ], "warning": [] }, + { + "query": "from a_index | eval length(null)", + "error": [], + "warning": [] + }, + { + "query": "row nullVar = null | eval length(nullVar)", + "error": [], + "warning": [] + }, { "query": "row var = log(5, 5)", "error": [], @@ -12339,6 +12574,16 @@ "error": [], "warning": [] }, + { + "query": "from a_index | eval log(null, null)", + "error": [], + "warning": [] + }, + { + "query": "row nullVar = null | eval log(nullVar, nullVar)", + "error": [], + "warning": [] + }, { "query": "row var = log10(5)", "error": [], @@ -12445,6 +12690,16 @@ ], "warning": [] }, + { + "query": "from a_index | eval log10(null)", + "error": [], + "warning": [] + }, + { + "query": "row nullVar = null | eval log10(nullVar)", + "error": [], + "warning": [] + }, { "query": "row var = ltrim(\"a\")", "error": [], @@ -12551,6 +12806,16 @@ ], "warning": [] }, + { + "query": "from a_index | eval ltrim(null)", + "error": [], + "warning": [] + }, + { + "query": "row nullVar = null | eval ltrim(nullVar)", + "error": [], + "warning": [] + }, { "query": "row var = mv_avg(5)", "error": [], @@ -12657,6 +12922,16 @@ ], "warning": [] }, + { + "query": "from a_index | eval mv_avg(null)", + "error": [], + "warning": [] + }, + { + "query": "row nullVar = null | eval mv_avg(nullVar)", + "error": [], + "warning": [] + }, { "query": "row var = mv_concat(\"a\", \"a\")", "error": [], @@ -12762,6 +13037,16 @@ ], "warning": [] }, + { + "query": "from a_index | eval mv_concat(null, null)", + "error": [], + "warning": [] + }, + { + "query": "row nullVar = null | eval mv_concat(nullVar, nullVar)", + "error": [], + "warning": [] + }, { "query": "row var = mv_count(\"a\")", "error": [], @@ -13136,6 +13421,16 @@ "error": [], "warning": [] }, + { + "query": "from a_index | eval mv_count(null)", + "error": [], + "warning": [] + }, + { + "query": "row nullVar = null | eval mv_count(nullVar)", + "error": [], + "warning": [] + }, { "query": "row var = mv_dedupe(\"a\")", "error": [], @@ -13472,6 +13767,26 @@ "error": [], "warning": [] }, + { + "query": "row var = mv_dedupe(to_cartesianpoint(\"POINT (30 10)\"))", + "error": [], + "warning": [] + }, + { + "query": "from a_index | eval mv_dedupe(cartesianPointField)", + "error": [], + "warning": [] + }, + { + "query": "from a_index | eval mv_dedupe(null)", + "error": [], + "warning": [] + }, + { + "query": "row nullVar = null | eval mv_dedupe(nullVar)", + "error": [], + "warning": [] + }, { "query": "row var = mv_first(\"a\")", "error": [], @@ -13807,17 +14122,27 @@ "warning": [] }, { - "query": "row var = mv_last(\"a\")", + "query": "from a_index | eval mv_first(null)", "error": [], "warning": [] }, { - "query": "row mv_last(\"a\")", + "query": "row nullVar = null | eval mv_first(nullVar)", "error": [], "warning": [] }, { - "query": "from a_index | eval var = mv_last(stringField)", + "query": "row var = mv_last(\"a\")", + "error": [], + "warning": [] + }, + { + "query": "row mv_last(\"a\")", + "error": [], + "warning": [] + }, + { + "query": "from a_index | eval var = mv_last(stringField)", "error": [], "warning": [] }, @@ -14140,6 +14465,16 @@ "error": [], "warning": [] }, + { + "query": "from a_index | eval mv_last(null)", + "error": [], + "warning": [] + }, + { + "query": "row nullVar = null | eval mv_last(nullVar)", + "error": [], + "warning": [] + }, { "query": "row var = mv_max(\"a\")", "error": [], @@ -14382,6 +14717,16 @@ "error": [], "warning": [] }, + { + "query": "from a_index | eval mv_max(null)", + "error": [], + "warning": [] + }, + { + "query": "row nullVar = null | eval mv_max(nullVar)", + "error": [], + "warning": [] + }, { "query": "row var = mv_median(5)", "error": [], @@ -14488,6 +14833,16 @@ ], "warning": [] }, + { + "query": "from a_index | eval mv_median(null)", + "error": [], + "warning": [] + }, + { + "query": "row nullVar = null | eval mv_median(nullVar)", + "error": [], + "warning": [] + }, { "query": "row var = mv_min(\"a\")", "error": [], @@ -14730,6 +15085,16 @@ "error": [], "warning": [] }, + { + "query": "from a_index | eval mv_min(null)", + "error": [], + "warning": [] + }, + { + "query": "row nullVar = null | eval mv_min(nullVar)", + "error": [], + "warning": [] + }, { "query": "row var = mv_slice(\"a\", 5, 5)", "error": [], @@ -15161,6 +15526,16 @@ "error": [], "warning": [] }, + { + "query": "from a_index | eval mv_slice(null, null, null)", + "error": [], + "warning": [] + }, + { + "query": "row nullVar = null | eval mv_slice(nullVar, nullVar, nullVar)", + "error": [], + "warning": [] + }, { "query": "row var = mv_sort(\"a\", \"asc\")", "error": [], @@ -15332,6 +15707,16 @@ "error": [], "warning": [] }, + { + "query": "from a_index | eval mv_sort(null, null)", + "error": [], + "warning": [] + }, + { + "query": "row nullVar = null | eval mv_sort(nullVar, nullVar)", + "error": [], + "warning": [] + }, { "query": "row var = mv_sum(5)", "error": [], @@ -15438,6 +15823,16 @@ ], "warning": [] }, + { + "query": "from a_index | eval mv_sum(null)", + "error": [], + "warning": [] + }, + { + "query": "row nullVar = null | eval mv_sum(nullVar)", + "error": [], + "warning": [] + }, { "query": "row var = mv_zip(\"a\", \"a\", \"a\")", "error": [], @@ -15564,6 +15959,16 @@ ], "warning": [] }, + { + "query": "from a_index | eval mv_zip(null, null, null)", + "error": [], + "warning": [] + }, + { + "query": "row nullVar = null | eval mv_zip(nullVar, nullVar, nullVar)", + "error": [], + "warning": [] + }, { "query": "row var = now()", "error": [], @@ -15596,6 +16001,11 @@ "error": [], "warning": [] }, + { + "query": "row nullVar = null | eval now()", + "error": [], + "warning": [] + }, { "query": "row var = pi()", "error": [], @@ -15633,6 +16043,11 @@ "error": [], "warning": [] }, + { + "query": "row nullVar = null | eval pi()", + "error": [], + "warning": [] + }, { "query": "row var = pow(5, 5)", "error": [], @@ -15738,6 +16153,16 @@ ], "warning": [] }, + { + "query": "from a_index | eval pow(null, null)", + "error": [], + "warning": [] + }, + { + "query": "row nullVar = null | eval pow(nullVar, nullVar)", + "error": [], + "warning": [] + }, { "query": "row var = replace(\"a\", \"a\", \"a\")", "error": [], @@ -15849,6 +16274,16 @@ ], "warning": [] }, + { + "query": "from a_index | eval replace(null, null, null)", + "error": [], + "warning": [] + }, + { + "query": "row nullVar = null | eval replace(nullVar, nullVar, nullVar)", + "error": [], + "warning": [] + }, { "query": "row var = right(\"a\", 5)", "error": [], @@ -15954,6 +16389,16 @@ ], "warning": [] }, + { + "query": "from a_index | eval right(null, null)", + "error": [], + "warning": [] + }, + { + "query": "row nullVar = null | eval right(nullVar, nullVar)", + "error": [], + "warning": [] + }, { "query": "row var = round(5, 5)", "error": [], @@ -16120,6 +16565,16 @@ "error": [], "warning": [] }, + { + "query": "from a_index | eval round(null, null)", + "error": [], + "warning": [] + }, + { + "query": "row nullVar = null | eval round(nullVar, nullVar)", + "error": [], + "warning": [] + }, { "query": "row var = rtrim(\"a\")", "error": [], @@ -16226,6 +16681,16 @@ ], "warning": [] }, + { + "query": "from a_index | eval rtrim(null)", + "error": [], + "warning": [] + }, + { + "query": "row nullVar = null | eval rtrim(nullVar)", + "error": [], + "warning": [] + }, { "query": "row var = signum(5)", "error": [], @@ -16332,6 +16797,16 @@ ], "warning": [] }, + { + "query": "from a_index | eval signum(null)", + "error": [], + "warning": [] + }, + { + "query": "row nullVar = null | eval signum(nullVar)", + "error": [], + "warning": [] + }, { "query": "row var = sin(5)", "error": [], @@ -16438,6 +16913,16 @@ ], "warning": [] }, + { + "query": "from a_index | eval sin(null)", + "error": [], + "warning": [] + }, + { + "query": "row nullVar = null | eval sin(nullVar)", + "error": [], + "warning": [] + }, { "query": "row var = sinh(5)", "error": [], @@ -16544,6 +17029,16 @@ ], "warning": [] }, + { + "query": "from a_index | eval sinh(null)", + "error": [], + "warning": [] + }, + { + "query": "row nullVar = null | eval sinh(nullVar)", + "error": [], + "warning": [] + }, { "query": "row var = split(\"a\", \"a\")", "error": [], @@ -16649,6 +17144,16 @@ ], "warning": [] }, + { + "query": "from a_index | eval split(null, null)", + "error": [], + "warning": [] + }, + { + "query": "row nullVar = null | eval split(nullVar, nullVar)", + "error": [], + "warning": [] + }, { "query": "row var = sqrt(5)", "error": [], @@ -16755,6 +17260,16 @@ ], "warning": [] }, + { + "query": "from a_index | eval sqrt(null)", + "error": [], + "warning": [] + }, + { + "query": "row nullVar = null | eval sqrt(nullVar)", + "error": [], + "warning": [] + }, { "query": "row var = st_contains(to_geopoint(\"POINT (30 10)\"), to_geopoint(\"POINT (30 10)\"))", "error": [], @@ -17153,6 +17668,16 @@ "error": [], "warning": [] }, + { + "query": "from a_index | eval st_contains(null, null)", + "error": [], + "warning": [] + }, + { + "query": "row nullVar = null | eval st_contains(nullVar, nullVar)", + "error": [], + "warning": [] + }, { "query": "row var = st_disjoint(to_geopoint(\"POINT (30 10)\"), to_geopoint(\"POINT (30 10)\"))", "error": [], @@ -17551,6 +18076,16 @@ "error": [], "warning": [] }, + { + "query": "from a_index | eval st_disjoint(null, null)", + "error": [], + "warning": [] + }, + { + "query": "row nullVar = null | eval st_disjoint(nullVar, nullVar)", + "error": [], + "warning": [] + }, { "query": "row var = st_intersects(to_geopoint(\"POINT (30 10)\"), to_geopoint(\"POINT (30 10)\"))", "error": [], @@ -17949,6 +18484,16 @@ "error": [], "warning": [] }, + { + "query": "from a_index | eval st_intersects(null, null)", + "error": [], + "warning": [] + }, + { + "query": "row nullVar = null | eval st_intersects(nullVar, nullVar)", + "error": [], + "warning": [] + }, { "query": "row var = st_within(to_geopoint(\"POINT (30 10)\"), to_geopoint(\"POINT (30 10)\"))", "error": [], @@ -18347,6 +18892,16 @@ "error": [], "warning": [] }, + { + "query": "from a_index | eval st_within(null, null)", + "error": [], + "warning": [] + }, + { + "query": "row nullVar = null | eval st_within(nullVar, nullVar)", + "error": [], + "warning": [] + }, { "query": "row var = st_x(to_geopoint(\"POINT (30 10)\"))", "error": [], @@ -18486,6 +19041,16 @@ "error": [], "warning": [] }, + { + "query": "from a_index | eval st_x(null)", + "error": [], + "warning": [] + }, + { + "query": "row nullVar = null | eval st_x(nullVar)", + "error": [], + "warning": [] + }, { "query": "row var = st_y(to_geopoint(\"POINT (30 10)\"))", "error": [], @@ -18625,6 +19190,16 @@ "error": [], "warning": [] }, + { + "query": "from a_index | eval st_y(null)", + "error": [], + "warning": [] + }, + { + "query": "row nullVar = null | eval st_y(nullVar)", + "error": [], + "warning": [] + }, { "query": "row var = starts_with(\"a\", \"a\")", "error": [], @@ -18709,6 +19284,16 @@ ], "warning": [] }, + { + "query": "from a_index | eval starts_with(null, null)", + "error": [], + "warning": [] + }, + { + "query": "row nullVar = null | eval starts_with(nullVar, nullVar)", + "error": [], + "warning": [] + }, { "query": "row var = substring(\"a\", 5, 5)", "error": [], @@ -18835,6 +19420,16 @@ ], "warning": [] }, + { + "query": "from a_index | eval substring(null, null, null)", + "error": [], + "warning": [] + }, + { + "query": "row nullVar = null | eval substring(nullVar, nullVar, nullVar)", + "error": [], + "warning": [] + }, { "query": "row var = tan(5)", "error": [], @@ -18941,6 +19536,16 @@ ], "warning": [] }, + { + "query": "from a_index | eval tan(null)", + "error": [], + "warning": [] + }, + { + "query": "row nullVar = null | eval tan(nullVar)", + "error": [], + "warning": [] + }, { "query": "row var = tanh(5)", "error": [], @@ -19047,6 +19652,16 @@ ], "warning": [] }, + { + "query": "from a_index | eval tanh(null)", + "error": [], + "warning": [] + }, + { + "query": "row nullVar = null | eval tanh(nullVar)", + "error": [], + "warning": [] + }, { "query": "row var = tau()", "error": [], @@ -19084,6 +19699,11 @@ "error": [], "warning": [] }, + { + "query": "row nullVar = null | eval tau()", + "error": [], + "warning": [] + }, { "query": "row var = to_boolean(\"a\")", "error": [], @@ -19242,6 +19862,16 @@ "error": [], "warning": [] }, + { + "query": "from a_index | eval to_boolean(null)", + "error": [], + "warning": [] + }, + { + "query": "row nullVar = null | eval to_boolean(nullVar)", + "error": [], + "warning": [] + }, { "query": "row var = to_cartesianpoint(\"a\")", "error": [], @@ -19341,12 +19971,22 @@ "warning": [] }, { - "query": "row var = to_cartesianshape(\"a\")", + "query": "from a_index | eval to_cartesianpoint(null)", "error": [], "warning": [] }, { - "query": "row to_cartesianshape(\"a\")", + "query": "row nullVar = null | eval to_cartesianpoint(nullVar)", + "error": [], + "warning": [] + }, + { + "query": "row var = to_cartesianshape(\"a\")", + "error": [], + "warning": [] + }, + { + "query": "row to_cartesianshape(\"a\")", "error": [], "warning": [] }, @@ -19468,6 +20108,16 @@ "error": [], "warning": [] }, + { + "query": "from a_index | eval to_cartesianshape(null)", + "error": [], + "warning": [] + }, + { + "query": "row nullVar = null | eval to_cartesianshape(nullVar)", + "error": [], + "warning": [] + }, { "query": "row var = to_datetime(\"a\")", "error": [], @@ -19626,6 +20276,16 @@ "error": [], "warning": [] }, + { + "query": "from a_index | eval to_datetime(null)", + "error": [], + "warning": [] + }, + { + "query": "row nullVar = null | eval to_datetime(nullVar)", + "error": [], + "warning": [] + }, { "query": "row var = to_degrees(5)", "error": [], @@ -19732,6 +20392,16 @@ ], "warning": [] }, + { + "query": "from a_index | eval to_degrees(null)", + "error": [], + "warning": [] + }, + { + "query": "row nullVar = null | eval to_degrees(nullVar)", + "error": [], + "warning": [] + }, { "query": "row var = to_double(\"a\")", "error": [], @@ -19957,6 +20627,16 @@ "error": [], "warning": [] }, + { + "query": "from a_index | eval to_double(null)", + "error": [], + "warning": [] + }, + { + "query": "row nullVar = null | eval to_double(nullVar)", + "error": [], + "warning": [] + }, { "query": "row var = to_geopoint(\"a\")", "error": [], @@ -20055,6 +20735,16 @@ "error": [], "warning": [] }, + { + "query": "from a_index | eval to_geopoint(null)", + "error": [], + "warning": [] + }, + { + "query": "row nullVar = null | eval to_geopoint(nullVar)", + "error": [], + "warning": [] + }, { "query": "row var = to_geoshape(\"a\")", "error": [], @@ -20183,6 +20873,16 @@ "error": [], "warning": [] }, + { + "query": "from a_index | eval to_geoshape(null)", + "error": [], + "warning": [] + }, + { + "query": "row nullVar = null | eval to_geoshape(nullVar)", + "error": [], + "warning": [] + }, { "query": "row var = to_integer(\"a\")", "error": [], @@ -20408,6 +21108,16 @@ "error": [], "warning": [] }, + { + "query": "from a_index | eval to_integer(null)", + "error": [], + "warning": [] + }, + { + "query": "row nullVar = null | eval to_integer(nullVar)", + "error": [], + "warning": [] + }, { "query": "row var = to_ip(\"a\")", "error": [], @@ -20506,6 +21216,16 @@ "error": [], "warning": [] }, + { + "query": "from a_index | eval to_ip(null)", + "error": [], + "warning": [] + }, + { + "query": "row nullVar = null | eval to_ip(nullVar)", + "error": [], + "warning": [] + }, { "query": "row var = to_long(\"a\")", "error": [], @@ -20691,6 +21411,16 @@ "error": [], "warning": [] }, + { + "query": "from a_index | eval to_long(null)", + "error": [], + "warning": [] + }, + { + "query": "row nullVar = null | eval to_long(nullVar)", + "error": [], + "warning": [] + }, { "query": "row var = to_lower(\"a\")", "error": [], @@ -20797,6 +21527,16 @@ ], "warning": [] }, + { + "query": "from a_index | eval to_lower(null)", + "error": [], + "warning": [] + }, + { + "query": "row nullVar = null | eval to_lower(nullVar)", + "error": [], + "warning": [] + }, { "query": "row var = to_radians(5)", "error": [], @@ -20903,6 +21643,16 @@ ], "warning": [] }, + { + "query": "from a_index | eval to_radians(null)", + "error": [], + "warning": [] + }, + { + "query": "row nullVar = null | eval to_radians(nullVar)", + "error": [], + "warning": [] + }, { "query": "row var = to_string(\"a\")", "error": [], @@ -21377,6 +22127,16 @@ "error": [], "warning": [] }, + { + "query": "from a_index | eval to_string(null)", + "error": [], + "warning": [] + }, + { + "query": "row nullVar = null | eval to_string(nullVar)", + "error": [], + "warning": [] + }, { "query": "row var = to_unsigned_long(\"a\")", "error": [], @@ -21642,6 +22402,16 @@ "error": [], "warning": [] }, + { + "query": "from a_index | eval to_unsigned_long(null)", + "error": [], + "warning": [] + }, + { + "query": "row nullVar = null | eval to_unsigned_long(nullVar)", + "error": [], + "warning": [] + }, { "query": "row var = to_upper(\"a\")", "error": [], @@ -21748,6 +22518,16 @@ ], "warning": [] }, + { + "query": "from a_index | eval to_upper(null)", + "error": [], + "warning": [] + }, + { + "query": "row nullVar = null | eval to_upper(nullVar)", + "error": [], + "warning": [] + }, { "query": "row var = to_version(\"a\")", "error": [], @@ -21834,6 +22614,16 @@ ], "warning": [] }, + { + "query": "from a_index | eval to_version(null)", + "error": [], + "warning": [] + }, + { + "query": "row nullVar = null | eval to_version(nullVar)", + "error": [], + "warning": [] + }, { "query": "row var = trim(\"a\")", "error": [], @@ -21940,6 +22730,16 @@ ], "warning": [] }, + { + "query": "from a_index | eval trim(null)", + "error": [], + "warning": [] + }, + { + "query": "row nullVar = null | eval trim(nullVar)", + "error": [], + "warning": [] + }, { "query": "from a_index | stats var = avg(numberField)", "error": [], @@ -22119,6 +22919,16 @@ ], "warning": [] }, + { + "query": "from a_index | stats avg(null)", + "error": [], + "warning": [] + }, + { + "query": "row nullVar = null | stats avg(nullVar)", + "error": [], + "warning": [] + }, { "query": "from a_index | stats var = sum(numberField)", "error": [], @@ -22298,6 +23108,16 @@ ], "warning": [] }, + { + "query": "from a_index | stats sum(null)", + "error": [], + "warning": [] + }, + { + "query": "row nullVar = null | stats sum(nullVar)", + "error": [], + "warning": [] + }, { "query": "from a_index | stats var = median(numberField)", "error": [], @@ -22477,6 +23297,16 @@ ], "warning": [] }, + { + "query": "from a_index | stats median(null)", + "error": [], + "warning": [] + }, + { + "query": "row nullVar = null | stats median(nullVar)", + "error": [], + "warning": [] + }, { "query": "from a_index | stats var = median_absolute_deviation(numberField)", "error": [], @@ -22656,6 +23486,16 @@ ], "warning": [] }, + { + "query": "from a_index | stats median_absolute_deviation(null)", + "error": [], + "warning": [] + }, + { + "query": "row nullVar = null | stats median_absolute_deviation(nullVar)", + "error": [], + "warning": [] + }, { "query": "from a_index | stats var = percentile(numberField, 5)", "error": [], @@ -22835,6 +23675,18 @@ ], "warning": [] }, + { + "query": "from a_index | stats percentile(null, null)", + "error": [], + "warning": [] + }, + { + "query": "row nullVar = null | stats percentile(nullVar, nullVar)", + "error": [ + "Argument of [percentile] must be a constant, received [nullVar]" + ], + "warning": [] + }, { "query": "from a_index | stats var = max(numberField)", "error": [], @@ -23086,6 +23938,16 @@ ], "warning": [] }, + { + "query": "from a_index | stats max(null)", + "error": [], + "warning": [] + }, + { + "query": "row nullVar = null | stats max(nullVar)", + "error": [], + "warning": [] + }, { "query": "from a_index | stats var = min(numberField)", "error": [], @@ -23337,6 +24199,16 @@ ], "warning": [] }, + { + "query": "from a_index | stats min(null)", + "error": [], + "warning": [] + }, + { + "query": "row nullVar = null | stats min(nullVar)", + "error": [], + "warning": [] + }, { "query": "from a_index | stats var = count(stringField)", "error": [], @@ -23416,6 +24288,16 @@ ], "warning": [] }, + { + "query": "from a_index | stats count(null)", + "error": [], + "warning": [] + }, + { + "query": "row nullVar = null | stats count(nullVar)", + "error": [], + "warning": [] + }, { "query": "from a_index | stats var = count_distinct(stringField, numberField)", "error": [], @@ -23495,6 +24377,16 @@ ], "warning": [] }, + { + "query": "from a_index | stats count_distinct(null, null)", + "error": [], + "warning": [] + }, + { + "query": "row nullVar = null | stats count_distinct(nullVar, nullVar)", + "error": [], + "warning": [] + }, { "query": "from a_index | stats var = st_centroid_agg(cartesianPointField)", "error": [], @@ -23641,6 +24533,16 @@ ], "warning": [] }, + { + "query": "from a_index | stats st_centroid_agg(null)", + "error": [], + "warning": [] + }, + { + "query": "row nullVar = null | stats st_centroid_agg(nullVar)", + "error": [], + "warning": [] + }, { "query": "from a_index | stats var = values(stringField)", "error": [], @@ -23700,6 +24602,16 @@ ], "warning": [] }, + { + "query": "from a_index | stats values(null)", + "error": [], + "warning": [] + }, + { + "query": "row nullVar = null | stats values(nullVar)", + "error": [], + "warning": [] + }, { "query": "from a_index | stats by bucket(dateField, 1 year)", "error": [], @@ -23829,6 +24741,20 @@ ], "warning": [] }, + { + "query": "from a_index | stats bucket(null, null, null, null)", + "error": [], + "warning": [] + }, + { + "query": "row nullVar = null | stats bucket(nullVar, nullVar, nullVar, nullVar)", + "error": [ + "Argument of [bucket] must be a constant, received [nullVar]", + "Argument of [bucket] must be a constant, received [nullVar]", + "Argument of [bucket] must be a constant, received [nullVar]" + ], + "warning": [] + }, { "query": "row var = cbrt(5)", "error": [], @@ -23904,6 +24830,16 @@ "error": [], "warning": [] }, + { + "query": "from a_index | eval cbrt(null)", + "error": [], + "warning": [] + }, + { + "query": "row nullVar = null | eval cbrt(nullVar)", + "error": [], + "warning": [] + }, { "query": "row var = from_base64(\"a\")", "error": [], @@ -23979,6 +24915,16 @@ "error": [], "warning": [] }, + { + "query": "from a_index | eval from_base64(null)", + "error": [], + "warning": [] + }, + { + "query": "row nullVar = null | eval from_base64(nullVar)", + "error": [], + "warning": [] + }, { "query": "row var = locate(\"a\", \"a\")", "error": [], @@ -24104,6 +25050,16 @@ "error": [], "warning": [] }, + { + "query": "from a_index | eval locate(null, null, null)", + "error": [], + "warning": [] + }, + { + "query": "row nullVar = null | eval locate(nullVar, nullVar, nullVar)", + "error": [], + "warning": [] + }, { "query": "row var = to_base64(\"a\")", "error": [], @@ -24178,6 +25134,16 @@ "query": "from a_index | sort to_base64(stringField)", "error": [], "warning": [] + }, + { + "query": "from a_index | eval to_base64(null)", + "error": [], + "warning": [] + }, + { + "query": "row nullVar = null | eval to_base64(nullVar)", + "error": [], + "warning": [] } ] } \ No newline at end of file diff --git a/packages/kbn-esql-validation-autocomplete/src/validation/helpers.ts b/packages/kbn-esql-validation-autocomplete/src/validation/helpers.ts index 1b75dcd9655505..20ec8990e18301 100644 --- a/packages/kbn-esql-validation-autocomplete/src/validation/helpers.ts +++ b/packages/kbn-esql-validation-autocomplete/src/validation/helpers.ts @@ -45,6 +45,10 @@ export function buildQueryForFieldsForStringSources(queryString: string, ast: ES * Used for too-many, too-few arguments validation */ export function getMaxMinNumberOfParams(definition: FunctionDefinition) { + if (definition.signatures.length === 0) { + return { min: 0, max: 0 }; + } + let min = Infinity; let max = 0; definition.signatures.forEach(({ params, minParams }) => { diff --git a/packages/kbn-esql-validation-autocomplete/src/validation/validation.test.ts b/packages/kbn-esql-validation-autocomplete/src/validation/validation.test.ts index c05e6e2f287877..42879128cf6635 100644 --- a/packages/kbn-esql-validation-autocomplete/src/validation/validation.test.ts +++ b/packages/kbn-esql-validation-autocomplete/src/validation/validation.test.ts @@ -2065,6 +2065,8 @@ describe('validation logic', () => { 'Argument of [date_diff] must be [date], found value [booleanField] type [boolean]', ] ); + testErrorsAndWarnings('from a_index | eval date_diff(null, null, null)', []); + testErrorsAndWarnings('row nullVar = null | eval date_diff(nullVar, nullVar, nullVar)', []); }); describe('abs', () => { @@ -2114,6 +2116,8 @@ describe('validation logic', () => { testErrorsAndWarnings('from a_index | eval abs(booleanField)', [ 'Argument of [abs] must be [number], found value [booleanField] type [boolean]', ]); + testErrorsAndWarnings('from a_index | eval abs(null)', []); + testErrorsAndWarnings('row nullVar = null | eval abs(nullVar)', []); }); describe('acos', () => { @@ -2163,6 +2167,8 @@ describe('validation logic', () => { testErrorsAndWarnings('from a_index | eval acos(booleanField)', [ 'Argument of [acos] must be [number], found value [booleanField] type [boolean]', ]); + testErrorsAndWarnings('from a_index | eval acos(null)', []); + testErrorsAndWarnings('row nullVar = null | eval acos(nullVar)', []); }); describe('asin', () => { @@ -2212,6 +2218,8 @@ describe('validation logic', () => { testErrorsAndWarnings('from a_index | eval asin(booleanField)', [ 'Argument of [asin] must be [number], found value [booleanField] type [boolean]', ]); + testErrorsAndWarnings('from a_index | eval asin(null)', []); + testErrorsAndWarnings('row nullVar = null | eval asin(nullVar)', []); }); describe('atan', () => { @@ -2261,6 +2269,8 @@ describe('validation logic', () => { testErrorsAndWarnings('from a_index | eval atan(booleanField)', [ 'Argument of [atan] must be [number], found value [booleanField] type [boolean]', ]); + testErrorsAndWarnings('from a_index | eval atan(null)', []); + testErrorsAndWarnings('row nullVar = null | eval atan(nullVar)', []); }); describe('atan2', () => { @@ -2319,6 +2329,8 @@ describe('validation logic', () => { 'Argument of [atan2] must be [number], found value [booleanField] type [boolean]', 'Argument of [atan2] must be [number], found value [booleanField] type [boolean]', ]); + testErrorsAndWarnings('from a_index | eval atan2(null, null)', []); + testErrorsAndWarnings('row nullVar = null | eval atan2(nullVar, nullVar)', []); }); describe('case', () => { @@ -2331,6 +2343,8 @@ describe('validation logic', () => { testErrorsAndWarnings('row var = case(to_cartesianpoint("POINT (30 10)"), true)', [ 'Argument of [case] must be [boolean], found value [to_cartesianpoint("POINT (30 10)")] type [cartesian_point]', ]); + testErrorsAndWarnings('from a_index | eval case(null, null)', []); + testErrorsAndWarnings('row nullVar = null | eval case(nullVar, nullVar)', []); }); describe('ceil', () => { @@ -2380,6 +2394,8 @@ describe('validation logic', () => { testErrorsAndWarnings('from a_index | eval ceil(booleanField)', [ 'Argument of [ceil] must be [number], found value [booleanField] type [boolean]', ]); + testErrorsAndWarnings('from a_index | eval ceil(null)', []); + testErrorsAndWarnings('row nullVar = null | eval ceil(nullVar)', []); }); describe('cidr_match', () => { @@ -2425,6 +2441,8 @@ describe('validation logic', () => { 'Argument of [cidr_match] must be [ip], found value [booleanField] type [boolean]', 'Argument of [cidr_match] must be [string], found value [booleanField] type [boolean]', ]); + testErrorsAndWarnings('from a_index | eval cidr_match(null, null)', []); + testErrorsAndWarnings('row nullVar = null | eval cidr_match(nullVar, nullVar)', []); }); describe('coalesce', () => { @@ -2704,6 +2722,8 @@ describe('validation logic', () => { testErrorsAndWarnings('from a_index | sort coalesce(numberField)', []); testErrorsAndWarnings('from a_index | eval coalesce(cartesianPointField)', []); + testErrorsAndWarnings('from a_index | eval coalesce(null)', []); + testErrorsAndWarnings('row nullVar = null | eval coalesce(nullVar)', []); }); describe('concat', () => { @@ -2764,6 +2784,8 @@ describe('validation logic', () => { 'Argument of [concat] must be [string], found value [booleanField] type [boolean]', 'Argument of [concat] must be [string], found value [booleanField] type [boolean]', ]); + testErrorsAndWarnings('from a_index | eval concat(null, null)', []); + testErrorsAndWarnings('row nullVar = null | eval concat(nullVar, nullVar)', []); }); describe('cos', () => { @@ -2813,6 +2835,8 @@ describe('validation logic', () => { testErrorsAndWarnings('from a_index | eval cos(booleanField)', [ 'Argument of [cos] must be [number], found value [booleanField] type [boolean]', ]); + testErrorsAndWarnings('from a_index | eval cos(null)', []); + testErrorsAndWarnings('row nullVar = null | eval cos(nullVar)', []); }); describe('cosh', () => { @@ -2862,6 +2886,8 @@ describe('validation logic', () => { testErrorsAndWarnings('from a_index | eval cosh(booleanField)', [ 'Argument of [cosh] must be [number], found value [booleanField] type [boolean]', ]); + testErrorsAndWarnings('from a_index | eval cosh(null)', []); + testErrorsAndWarnings('row nullVar = null | eval cosh(nullVar)', []); }); describe('date_extract', () => { @@ -2912,6 +2938,8 @@ describe('validation logic', () => { 'Argument of [date_extract] must be [chrono_literal], found value [booleanField] type [boolean]', 'Argument of [date_extract] must be [date], found value [booleanField] type [boolean]', ]); + testErrorsAndWarnings('from a_index | eval date_extract(null, null)', []); + testErrorsAndWarnings('row nullVar = null | eval date_extract(nullVar, nullVar)', []); }); describe('date_format', () => { @@ -2949,6 +2977,8 @@ describe('validation logic', () => { 'Argument of [date_format] must be [string], found value [booleanField] type [boolean]', 'Argument of [date_format] must be [date], found value [booleanField] type [boolean]', ]); + testErrorsAndWarnings('from a_index | eval date_format(null, null)', []); + testErrorsAndWarnings('row nullVar = null | eval date_format(nullVar, nullVar)', []); }); describe('date_parse', () => { @@ -2998,6 +3028,8 @@ describe('validation logic', () => { 'Argument of [date_parse] must be [string], found value [booleanField] type [boolean]', 'Argument of [date_parse] must be [string], found value [booleanField] type [boolean]', ]); + testErrorsAndWarnings('from a_index | eval date_parse(null, null)', []); + testErrorsAndWarnings('row nullVar = null | eval date_parse(nullVar, nullVar)', []); }); describe('date_trunc', () => { @@ -3046,6 +3078,8 @@ describe('validation logic', () => { 'from a_index | eval var = date_trunc(to_datetime(dateField), to_datetime(dateField))', [] ); + testErrorsAndWarnings('from a_index | eval date_trunc(null, null)', []); + testErrorsAndWarnings('row nullVar = null | eval date_trunc(nullVar, nullVar)', []); }); describe('e', () => { @@ -3060,6 +3094,7 @@ describe('validation logic', () => { ]); testErrorsAndWarnings('from a_index | sort e()', []); + testErrorsAndWarnings('row nullVar = null | eval e()', []); }); describe('ends_with', () => { @@ -3106,6 +3141,8 @@ describe('validation logic', () => { 'Argument of [ends_with] must be [string], found value [booleanField] type [boolean]', 'Argument of [ends_with] must be [string], found value [booleanField] type [boolean]', ]); + testErrorsAndWarnings('from a_index | eval ends_with(null, null)', []); + testErrorsAndWarnings('row nullVar = null | eval ends_with(nullVar, nullVar)', []); }); describe('floor', () => { @@ -3155,6 +3192,8 @@ describe('validation logic', () => { testErrorsAndWarnings('from a_index | eval floor(booleanField)', [ 'Argument of [floor] must be [number], found value [booleanField] type [boolean]', ]); + testErrorsAndWarnings('from a_index | eval floor(null)', []); + testErrorsAndWarnings('row nullVar = null | eval floor(nullVar)', []); }); describe('greatest', () => { @@ -3293,6 +3332,8 @@ describe('validation logic', () => { ); testErrorsAndWarnings('from a_index | sort greatest(booleanField)', []); + testErrorsAndWarnings('from a_index | eval greatest(null)', []); + testErrorsAndWarnings('row nullVar = null | eval greatest(nullVar)', []); }); describe('least', () => { @@ -3431,6 +3472,8 @@ describe('validation logic', () => { ); testErrorsAndWarnings('from a_index | sort least(booleanField)', []); + testErrorsAndWarnings('from a_index | eval least(null)', []); + testErrorsAndWarnings('row nullVar = null | eval least(nullVar)', []); }); describe('left', () => { @@ -3492,6 +3535,8 @@ describe('validation logic', () => { 'Argument of [left] must be [string], found value [booleanField] type [boolean]', 'Argument of [left] must be [number], found value [booleanField] type [boolean]', ]); + testErrorsAndWarnings('from a_index | eval left(null, null)', []); + testErrorsAndWarnings('row nullVar = null | eval left(nullVar, nullVar)', []); }); describe('length', () => { @@ -3541,6 +3586,8 @@ describe('validation logic', () => { testErrorsAndWarnings('from a_index | eval length(booleanField)', [ 'Argument of [length] must be [string], found value [booleanField] type [boolean]', ]); + testErrorsAndWarnings('from a_index | eval length(null)', []); + testErrorsAndWarnings('row nullVar = null | eval length(nullVar)', []); }); describe('log', () => { @@ -3622,6 +3669,8 @@ describe('validation logic', () => { ]); testErrorsAndWarnings('from a_index | sort log(numberField)', []); + testErrorsAndWarnings('from a_index | eval log(null, null)', []); + testErrorsAndWarnings('row nullVar = null | eval log(nullVar, nullVar)', []); }); describe('log10', () => { @@ -3671,6 +3720,8 @@ describe('validation logic', () => { testErrorsAndWarnings('from a_index | eval log10(booleanField)', [ 'Argument of [log10] must be [number], found value [booleanField] type [boolean]', ]); + testErrorsAndWarnings('from a_index | eval log10(null)', []); + testErrorsAndWarnings('row nullVar = null | eval log10(nullVar)', []); }); describe('ltrim', () => { @@ -3720,6 +3771,8 @@ describe('validation logic', () => { testErrorsAndWarnings('from a_index | eval ltrim(booleanField)', [ 'Argument of [ltrim] must be [string], found value [booleanField] type [boolean]', ]); + testErrorsAndWarnings('from a_index | eval ltrim(null)', []); + testErrorsAndWarnings('row nullVar = null | eval ltrim(nullVar)', []); }); describe('mv_avg', () => { @@ -3769,6 +3822,8 @@ describe('validation logic', () => { testErrorsAndWarnings('from a_index | eval mv_avg(booleanField)', [ 'Argument of [mv_avg] must be [number], found value [booleanField] type [boolean]', ]); + testErrorsAndWarnings('from a_index | eval mv_avg(null)', []); + testErrorsAndWarnings('row nullVar = null | eval mv_avg(nullVar)', []); }); describe('mv_concat', () => { @@ -3836,6 +3891,8 @@ describe('validation logic', () => { 'Argument of [mv_concat] must be [string], found value [booleanField] type [boolean]', 'Argument of [mv_concat] must be [string], found value [booleanField] type [boolean]', ]); + testErrorsAndWarnings('from a_index | eval mv_concat(null, null)', []); + testErrorsAndWarnings('row nullVar = null | eval mv_concat(nullVar, nullVar)', []); }); describe('mv_count', () => { @@ -3941,6 +3998,8 @@ describe('validation logic', () => { ]); testErrorsAndWarnings('from a_index | sort mv_count(booleanField)', []); + testErrorsAndWarnings('from a_index | eval mv_count(null)', []); + testErrorsAndWarnings('row nullVar = null | eval mv_count(nullVar)', []); }); describe('mv_dedupe', () => { @@ -4052,6 +4111,10 @@ describe('validation logic', () => { ]); testErrorsAndWarnings('from a_index | sort mv_dedupe(numberField)', []); + testErrorsAndWarnings('row var = mv_dedupe(to_cartesianpoint("POINT (30 10)"))', []); + testErrorsAndWarnings('from a_index | eval mv_dedupe(cartesianPointField)', []); + testErrorsAndWarnings('from a_index | eval mv_dedupe(null)', []); + testErrorsAndWarnings('row nullVar = null | eval mv_dedupe(nullVar)', []); }); describe('mv_first', () => { @@ -4149,6 +4212,8 @@ describe('validation logic', () => { ]); testErrorsAndWarnings('from a_index | sort mv_first(booleanField)', []); + testErrorsAndWarnings('from a_index | eval mv_first(null)', []); + testErrorsAndWarnings('row nullVar = null | eval mv_first(nullVar)', []); }); describe('mv_last', () => { @@ -4246,6 +4311,8 @@ describe('validation logic', () => { ]); testErrorsAndWarnings('from a_index | sort mv_last(booleanField)', []); + testErrorsAndWarnings('from a_index | eval mv_last(null)', []); + testErrorsAndWarnings('row nullVar = null | eval mv_last(nullVar)', []); }); describe('mv_max', () => { @@ -4319,6 +4386,8 @@ describe('validation logic', () => { ]); testErrorsAndWarnings('from a_index | sort mv_max(booleanField)', []); + testErrorsAndWarnings('from a_index | eval mv_max(null)', []); + testErrorsAndWarnings('row nullVar = null | eval mv_max(nullVar)', []); }); describe('mv_median', () => { @@ -4368,6 +4437,8 @@ describe('validation logic', () => { testErrorsAndWarnings('from a_index | eval mv_median(booleanField)', [ 'Argument of [mv_median] must be [number], found value [booleanField] type [boolean]', ]); + testErrorsAndWarnings('from a_index | eval mv_median(null)', []); + testErrorsAndWarnings('row nullVar = null | eval mv_median(nullVar)', []); }); describe('mv_min', () => { @@ -4441,6 +4512,8 @@ describe('validation logic', () => { ]); testErrorsAndWarnings('from a_index | sort mv_min(booleanField)', []); + testErrorsAndWarnings('from a_index | eval mv_min(null)', []); + testErrorsAndWarnings('row nullVar = null | eval mv_min(nullVar)', []); }); describe('mv_slice', () => { @@ -4794,6 +4867,8 @@ describe('validation logic', () => { 'from a_index | sort mv_slice(booleanField, numberField, numberField)', [] ); + testErrorsAndWarnings('from a_index | eval mv_slice(null, null, null)', []); + testErrorsAndWarnings('row nullVar = null | eval mv_slice(nullVar, nullVar, nullVar)', []); }); describe('mv_sort', () => { @@ -4854,6 +4929,8 @@ describe('validation logic', () => { ]); testErrorsAndWarnings('from a_index | sort mv_sort(booleanField, "asc")', []); + testErrorsAndWarnings('from a_index | eval mv_sort(null, null)', []); + testErrorsAndWarnings('row nullVar = null | eval mv_sort(nullVar, nullVar)', []); }); describe('mv_sum', () => { @@ -4903,6 +4980,8 @@ describe('validation logic', () => { testErrorsAndWarnings('from a_index | eval mv_sum(booleanField)', [ 'Argument of [mv_sum] must be [number], found value [booleanField] type [boolean]', ]); + testErrorsAndWarnings('from a_index | eval mv_sum(null)', []); + testErrorsAndWarnings('row nullVar = null | eval mv_sum(nullVar)', []); }); describe('mv_zip', () => { @@ -5001,6 +5080,8 @@ describe('validation logic', () => { 'Argument of [mv_zip] must be [string], found value [booleanField] type [boolean]', ] ); + testErrorsAndWarnings('from a_index | eval mv_zip(null, null, null)', []); + testErrorsAndWarnings('row nullVar = null | eval mv_zip(nullVar, nullVar, nullVar)', []); }); describe('now', () => { @@ -5014,6 +5095,7 @@ describe('validation logic', () => { ]); testErrorsAndWarnings('from a_index | sort now()', []); + testErrorsAndWarnings('row nullVar = null | eval now()', []); }); describe('pi', () => { @@ -5028,6 +5110,7 @@ describe('validation logic', () => { ]); testErrorsAndWarnings('from a_index | sort pi()', []); + testErrorsAndWarnings('row nullVar = null | eval pi()', []); }); describe('pow', () => { @@ -5086,6 +5169,8 @@ describe('validation logic', () => { 'Argument of [pow] must be [number], found value [booleanField] type [boolean]', 'Argument of [pow] must be [number], found value [booleanField] type [boolean]', ]); + testErrorsAndWarnings('from a_index | eval pow(null, null)', []); + testErrorsAndWarnings('row nullVar = null | eval pow(nullVar, nullVar)', []); }); describe('replace', () => { @@ -5183,6 +5268,8 @@ describe('validation logic', () => { 'Argument of [replace] must be [string], found value [booleanField] type [boolean]', ] ); + testErrorsAndWarnings('from a_index | eval replace(null, null, null)', []); + testErrorsAndWarnings('row nullVar = null | eval replace(nullVar, nullVar, nullVar)', []); }); describe('right', () => { @@ -5247,6 +5334,8 @@ describe('validation logic', () => { 'Argument of [right] must be [string], found value [booleanField] type [boolean]', 'Argument of [right] must be [number], found value [booleanField] type [boolean]', ]); + testErrorsAndWarnings('from a_index | eval right(null, null)', []); + testErrorsAndWarnings('row nullVar = null | eval right(nullVar, nullVar)', []); }); describe('round', () => { @@ -5328,6 +5417,8 @@ describe('validation logic', () => { ]); testErrorsAndWarnings('from a_index | sort round(numberField)', []); + testErrorsAndWarnings('from a_index | eval round(null, null)', []); + testErrorsAndWarnings('row nullVar = null | eval round(nullVar, nullVar)', []); }); describe('rtrim', () => { @@ -5377,6 +5468,8 @@ describe('validation logic', () => { testErrorsAndWarnings('from a_index | eval rtrim(booleanField)', [ 'Argument of [rtrim] must be [string], found value [booleanField] type [boolean]', ]); + testErrorsAndWarnings('from a_index | eval rtrim(null)', []); + testErrorsAndWarnings('row nullVar = null | eval rtrim(nullVar)', []); }); describe('signum', () => { @@ -5426,6 +5519,8 @@ describe('validation logic', () => { testErrorsAndWarnings('from a_index | eval signum(booleanField)', [ 'Argument of [signum] must be [number], found value [booleanField] type [boolean]', ]); + testErrorsAndWarnings('from a_index | eval signum(null)', []); + testErrorsAndWarnings('row nullVar = null | eval signum(nullVar)', []); }); describe('sin', () => { @@ -5475,6 +5570,8 @@ describe('validation logic', () => { testErrorsAndWarnings('from a_index | eval sin(booleanField)', [ 'Argument of [sin] must be [number], found value [booleanField] type [boolean]', ]); + testErrorsAndWarnings('from a_index | eval sin(null)', []); + testErrorsAndWarnings('row nullVar = null | eval sin(nullVar)', []); }); describe('sinh', () => { @@ -5524,6 +5621,8 @@ describe('validation logic', () => { testErrorsAndWarnings('from a_index | eval sinh(booleanField)', [ 'Argument of [sinh] must be [number], found value [booleanField] type [boolean]', ]); + testErrorsAndWarnings('from a_index | eval sinh(null)', []); + testErrorsAndWarnings('row nullVar = null | eval sinh(nullVar)', []); }); describe('split', () => { @@ -5588,6 +5687,8 @@ describe('validation logic', () => { 'Argument of [split] must be [string], found value [booleanField] type [boolean]', 'Argument of [split] must be [string], found value [booleanField] type [boolean]', ]); + testErrorsAndWarnings('from a_index | eval split(null, null)', []); + testErrorsAndWarnings('row nullVar = null | eval split(nullVar, nullVar)', []); }); describe('sqrt', () => { @@ -5637,6 +5738,8 @@ describe('validation logic', () => { testErrorsAndWarnings('from a_index | eval sqrt(booleanField)', [ 'Argument of [sqrt] must be [number], found value [booleanField] type [boolean]', ]); + testErrorsAndWarnings('from a_index | eval sqrt(null)', []); + testErrorsAndWarnings('row nullVar = null | eval sqrt(nullVar)', []); }); describe('st_contains', () => { @@ -5987,6 +6090,8 @@ describe('validation logic', () => { 'from a_index | sort st_contains(cartesianPointField, cartesianPointField)', [] ); + testErrorsAndWarnings('from a_index | eval st_contains(null, null)', []); + testErrorsAndWarnings('row nullVar = null | eval st_contains(nullVar, nullVar)', []); }); describe('st_disjoint', () => { @@ -6337,6 +6442,8 @@ describe('validation logic', () => { 'from a_index | sort st_disjoint(cartesianPointField, cartesianPointField)', [] ); + testErrorsAndWarnings('from a_index | eval st_disjoint(null, null)', []); + testErrorsAndWarnings('row nullVar = null | eval st_disjoint(nullVar, nullVar)', []); }); describe('st_intersects', () => { @@ -6706,6 +6813,8 @@ describe('validation logic', () => { 'from a_index | sort st_intersects(cartesianPointField, cartesianPointField)', [] ); + testErrorsAndWarnings('from a_index | eval st_intersects(null, null)', []); + testErrorsAndWarnings('row nullVar = null | eval st_intersects(nullVar, nullVar)', []); }); describe('st_within', () => { @@ -7056,6 +7165,8 @@ describe('validation logic', () => { 'from a_index | sort st_within(cartesianPointField, cartesianPointField)', [] ); + testErrorsAndWarnings('from a_index | eval st_within(null, null)', []); + testErrorsAndWarnings('row nullVar = null | eval st_within(nullVar, nullVar)', []); }); describe('st_x', () => { @@ -7118,6 +7229,8 @@ describe('validation logic', () => { testErrorsAndWarnings('from a_index | eval var = st_x(to_geopoint(geoPointField))', []); testErrorsAndWarnings('from a_index | sort st_x(cartesianPointField)', []); + testErrorsAndWarnings('from a_index | eval st_x(null)', []); + testErrorsAndWarnings('row nullVar = null | eval st_x(nullVar)', []); }); describe('st_y', () => { @@ -7180,6 +7293,8 @@ describe('validation logic', () => { testErrorsAndWarnings('from a_index | eval var = st_y(to_geopoint(geoPointField))', []); testErrorsAndWarnings('from a_index | sort st_y(cartesianPointField)', []); + testErrorsAndWarnings('from a_index | eval st_y(null)', []); + testErrorsAndWarnings('row nullVar = null | eval st_y(nullVar)', []); }); describe('starts_with', () => { @@ -7230,6 +7345,8 @@ describe('validation logic', () => { 'Argument of [starts_with] must be [string], found value [booleanField] type [boolean]', 'Argument of [starts_with] must be [string], found value [booleanField] type [boolean]', ]); + testErrorsAndWarnings('from a_index | eval starts_with(null, null)', []); + testErrorsAndWarnings('row nullVar = null | eval starts_with(nullVar, nullVar)', []); }); describe('substring', () => { @@ -7331,6 +7448,8 @@ describe('validation logic', () => { 'Argument of [substring] must be [number], found value [booleanField] type [boolean]', ] ); + testErrorsAndWarnings('from a_index | eval substring(null, null, null)', []); + testErrorsAndWarnings('row nullVar = null | eval substring(nullVar, nullVar, nullVar)', []); }); describe('tan', () => { @@ -7380,6 +7499,8 @@ describe('validation logic', () => { testErrorsAndWarnings('from a_index | eval tan(booleanField)', [ 'Argument of [tan] must be [number], found value [booleanField] type [boolean]', ]); + testErrorsAndWarnings('from a_index | eval tan(null)', []); + testErrorsAndWarnings('row nullVar = null | eval tan(nullVar)', []); }); describe('tanh', () => { @@ -7429,6 +7550,8 @@ describe('validation logic', () => { testErrorsAndWarnings('from a_index | eval tanh(booleanField)', [ 'Argument of [tanh] must be [number], found value [booleanField] type [boolean]', ]); + testErrorsAndWarnings('from a_index | eval tanh(null)', []); + testErrorsAndWarnings('row nullVar = null | eval tanh(nullVar)', []); }); describe('tau', () => { @@ -7443,6 +7566,7 @@ describe('validation logic', () => { ]); testErrorsAndWarnings('from a_index | sort tau()', []); + testErrorsAndWarnings('row nullVar = null | eval tau()', []); }); describe('to_boolean', () => { @@ -7492,6 +7616,8 @@ describe('validation logic', () => { ]); testErrorsAndWarnings('from a_index | sort to_boolean(booleanField)', []); + testErrorsAndWarnings('from a_index | eval to_boolean(null)', []); + testErrorsAndWarnings('row nullVar = null | eval to_boolean(nullVar)', []); }); describe('to_cartesianpoint', () => { @@ -7548,6 +7674,8 @@ describe('validation logic', () => { ); testErrorsAndWarnings('from a_index | sort to_cartesianpoint(cartesianPointField)', []); + testErrorsAndWarnings('from a_index | eval to_cartesianpoint(null)', []); + testErrorsAndWarnings('row nullVar = null | eval to_cartesianpoint(nullVar)', []); }); describe('to_cartesianshape', () => { @@ -7626,6 +7754,8 @@ describe('validation logic', () => { ); testErrorsAndWarnings('from a_index | sort to_cartesianshape(cartesianPointField)', []); + testErrorsAndWarnings('from a_index | eval to_cartesianshape(null)', []); + testErrorsAndWarnings('row nullVar = null | eval to_cartesianshape(nullVar)', []); }); describe('to_datetime', () => { @@ -7678,6 +7808,8 @@ describe('validation logic', () => { ]); testErrorsAndWarnings('from a_index | sort to_datetime(dateField)', []); + testErrorsAndWarnings('from a_index | eval to_datetime(null)', []); + testErrorsAndWarnings('row nullVar = null | eval to_datetime(nullVar)', []); }); describe('to_degrees', () => { @@ -7727,6 +7859,8 @@ describe('validation logic', () => { testErrorsAndWarnings('from a_index | eval to_degrees(booleanField)', [ 'Argument of [to_degrees] must be [number], found value [booleanField] type [boolean]', ]); + testErrorsAndWarnings('from a_index | eval to_degrees(null)', []); + testErrorsAndWarnings('row nullVar = null | eval to_degrees(nullVar)', []); }); describe('to_double', () => { @@ -7793,6 +7927,8 @@ describe('validation logic', () => { ]); testErrorsAndWarnings('from a_index | sort to_double(booleanField)', []); + testErrorsAndWarnings('from a_index | eval to_double(null)', []); + testErrorsAndWarnings('row nullVar = null | eval to_double(nullVar)', []); }); describe('to_geopoint', () => { @@ -7836,6 +7972,8 @@ describe('validation logic', () => { ]); testErrorsAndWarnings('from a_index | sort to_geopoint(geoPointField)', []); + testErrorsAndWarnings('from a_index | eval to_geopoint(null)', []); + testErrorsAndWarnings('row nullVar = null | eval to_geopoint(nullVar)', []); }); describe('to_geoshape', () => { @@ -7891,6 +8029,8 @@ describe('validation logic', () => { ]); testErrorsAndWarnings('from a_index | sort to_geoshape(geoPointField)', []); + testErrorsAndWarnings('from a_index | eval to_geoshape(null)', []); + testErrorsAndWarnings('row nullVar = null | eval to_geoshape(nullVar)', []); }); describe('to_integer', () => { @@ -7957,6 +8097,8 @@ describe('validation logic', () => { ]); testErrorsAndWarnings('from a_index | sort to_integer(booleanField)', []); + testErrorsAndWarnings('from a_index | eval to_integer(null)', []); + testErrorsAndWarnings('row nullVar = null | eval to_integer(nullVar)', []); }); describe('to_ip', () => { @@ -7994,6 +8136,8 @@ describe('validation logic', () => { ]); testErrorsAndWarnings('from a_index | sort to_ip(ipField)', []); + testErrorsAndWarnings('from a_index | eval to_ip(null)', []); + testErrorsAndWarnings('row nullVar = null | eval to_ip(nullVar)', []); }); describe('to_long', () => { @@ -8052,6 +8196,8 @@ describe('validation logic', () => { ]); testErrorsAndWarnings('from a_index | sort to_long(booleanField)', []); + testErrorsAndWarnings('from a_index | eval to_long(null)', []); + testErrorsAndWarnings('row nullVar = null | eval to_long(nullVar)', []); }); describe('to_lower', () => { @@ -8101,6 +8247,8 @@ describe('validation logic', () => { testErrorsAndWarnings('from a_index | eval to_lower(booleanField)', [ 'Argument of [to_lower] must be [string], found value [booleanField] type [boolean]', ]); + testErrorsAndWarnings('from a_index | eval to_lower(null)', []); + testErrorsAndWarnings('row nullVar = null | eval to_lower(nullVar)', []); }); describe('to_radians', () => { @@ -8150,6 +8298,8 @@ describe('validation logic', () => { testErrorsAndWarnings('from a_index | eval to_radians(booleanField)', [ 'Argument of [to_radians] must be [number], found value [booleanField] type [boolean]', ]); + testErrorsAndWarnings('from a_index | eval to_radians(null)', []); + testErrorsAndWarnings('row nullVar = null | eval to_radians(nullVar)', []); }); describe('to_string', () => { @@ -8287,6 +8437,8 @@ describe('validation logic', () => { ]); testErrorsAndWarnings('from a_index | sort to_string(booleanField)', []); + testErrorsAndWarnings('from a_index | eval to_string(null)', []); + testErrorsAndWarnings('row nullVar = null | eval to_string(nullVar)', []); }); describe('to_unsigned_long', () => { @@ -8373,6 +8525,8 @@ describe('validation logic', () => { ]); testErrorsAndWarnings('from a_index | sort to_unsigned_long(booleanField)', []); + testErrorsAndWarnings('from a_index | eval to_unsigned_long(null)', []); + testErrorsAndWarnings('row nullVar = null | eval to_unsigned_long(nullVar)', []); }); describe('to_upper', () => { @@ -8422,6 +8576,8 @@ describe('validation logic', () => { testErrorsAndWarnings('from a_index | eval to_upper(booleanField)', [ 'Argument of [to_upper] must be [string], found value [booleanField] type [boolean]', ]); + testErrorsAndWarnings('from a_index | eval to_upper(null)', []); + testErrorsAndWarnings('row nullVar = null | eval to_upper(nullVar)', []); }); describe('to_version', () => { @@ -8452,6 +8608,8 @@ describe('validation logic', () => { testErrorsAndWarnings('from a_index | eval to_version(stringField, extraArg)', [ 'Error: [to_version] function expects exactly one argument, got 2.', ]); + testErrorsAndWarnings('from a_index | eval to_version(null)', []); + testErrorsAndWarnings('row nullVar = null | eval to_version(nullVar)', []); }); describe('trim', () => { @@ -8501,6 +8659,8 @@ describe('validation logic', () => { testErrorsAndWarnings('from a_index | eval trim(booleanField)', [ 'Argument of [trim] must be [string], found value [booleanField] type [boolean]', ]); + testErrorsAndWarnings('from a_index | eval trim(null)', []); + testErrorsAndWarnings('row nullVar = null | eval trim(nullVar)', []); }); describe('avg', () => { @@ -8605,6 +8765,8 @@ describe('validation logic', () => { testErrorsAndWarnings('from a_index | stats avg(booleanField)', [ 'Argument of [avg] must be [number], found value [booleanField] type [boolean]', ]); + testErrorsAndWarnings('from a_index | stats avg(null)', []); + testErrorsAndWarnings('row nullVar = null | stats avg(nullVar)', []); }); describe('sum', () => { @@ -8709,6 +8871,8 @@ describe('validation logic', () => { testErrorsAndWarnings('from a_index | stats sum(booleanField)', [ 'Argument of [sum] must be [number], found value [booleanField] type [boolean]', ]); + testErrorsAndWarnings('from a_index | stats sum(null)', []); + testErrorsAndWarnings('row nullVar = null | stats sum(nullVar)', []); }); describe('median', () => { @@ -8819,6 +8983,8 @@ describe('validation logic', () => { testErrorsAndWarnings('from a_index | stats median(booleanField)', [ 'Argument of [median] must be [number], found value [booleanField] type [boolean]', ]); + testErrorsAndWarnings('from a_index | stats median(null)', []); + testErrorsAndWarnings('row nullVar = null | stats median(nullVar)', []); }); describe('median_absolute_deviation', () => { @@ -8964,6 +9130,8 @@ describe('validation logic', () => { testErrorsAndWarnings('from a_index | stats median_absolute_deviation(booleanField)', [ 'Argument of [median_absolute_deviation] must be [number], found value [booleanField] type [boolean]', ]); + testErrorsAndWarnings('from a_index | stats median_absolute_deviation(null)', []); + testErrorsAndWarnings('row nullVar = null | stats median_absolute_deviation(nullVar)', []); }); describe('percentile', () => { @@ -9083,6 +9251,10 @@ describe('validation logic', () => { testErrorsAndWarnings('from a_index | stats percentile(booleanField, 5)', [ 'Argument of [percentile] must be [number], found value [booleanField] type [boolean]', ]); + testErrorsAndWarnings('from a_index | stats percentile(null, null)', []); + testErrorsAndWarnings('row nullVar = null | stats percentile(nullVar, nullVar)', [ + 'Argument of [percentile] must be a constant, received [nullVar]', + ]); }); describe('max', () => { @@ -9221,6 +9393,8 @@ describe('validation logic', () => { testErrorsAndWarnings('from a_index | stats max(booleanField)', [ 'Argument of [max] must be [number], found value [booleanField] type [boolean]', ]); + testErrorsAndWarnings('from a_index | stats max(null)', []); + testErrorsAndWarnings('row nullVar = null | stats max(nullVar)', []); }); describe('min', () => { @@ -9359,6 +9533,8 @@ describe('validation logic', () => { testErrorsAndWarnings('from a_index | stats min(booleanField)', [ 'Argument of [min] must be [number], found value [booleanField] type [boolean]', ]); + testErrorsAndWarnings('from a_index | stats min(null)', []); + testErrorsAndWarnings('row nullVar = null | stats min(nullVar)', []); }); describe('count', () => { @@ -9404,6 +9580,8 @@ describe('validation logic', () => { testErrorsAndWarnings('from a_index | eval count(stringField) > 0', [ 'EVAL does not support function count', ]); + testErrorsAndWarnings('from a_index | stats count(null)', []); + testErrorsAndWarnings('row nullVar = null | stats count(nullVar)', []); }); describe('count_distinct', () => { @@ -9462,6 +9640,8 @@ describe('validation logic', () => { testErrorsAndWarnings('from a_index | eval count_distinct(stringField, numberField) > 0', [ 'EVAL does not support function count_distinct', ]); + testErrorsAndWarnings('from a_index | stats count_distinct(null, null)', []); + testErrorsAndWarnings('row nullVar = null | stats count_distinct(nullVar, nullVar)', []); }); describe('st_centroid_agg', () => { @@ -9546,6 +9726,8 @@ describe('validation logic', () => { testErrorsAndWarnings('from a_index | stats st_centroid_agg(booleanField)', [ 'Argument of [st_centroid_agg] must be [cartesian_point], found value [booleanField] type [boolean]', ]); + testErrorsAndWarnings('from a_index | stats st_centroid_agg(null)', []); + testErrorsAndWarnings('row nullVar = null | stats st_centroid_agg(nullVar)', []); }); describe('values', () => { @@ -9579,6 +9761,8 @@ describe('validation logic', () => { testErrorsAndWarnings('from a_index | eval values(stringField) > 0', [ 'EVAL does not support function values', ]); + testErrorsAndWarnings('from a_index | stats values(null)', []); + testErrorsAndWarnings('row nullVar = null | stats values(nullVar)', []); }); describe('bucket', () => { @@ -9655,6 +9839,16 @@ describe('validation logic', () => { testErrorsAndWarnings('from a_index | sort bucket(dateField, 1 year)', [ 'SORT does not support function bucket', ]); + testErrorsAndWarnings('from a_index | stats bucket(null, null, null, null)', []); + + testErrorsAndWarnings( + 'row nullVar = null | stats bucket(nullVar, nullVar, nullVar, nullVar)', + [ + 'Argument of [bucket] must be a constant, received [nullVar]', + 'Argument of [bucket] must be a constant, received [nullVar]', + 'Argument of [bucket] must be a constant, received [nullVar]', + ] + ); }); describe('cbrt', () => { @@ -9689,6 +9883,8 @@ describe('validation logic', () => { ]); testErrorsAndWarnings('from a_index | sort cbrt(numberField)', []); + testErrorsAndWarnings('from a_index | eval cbrt(null)', []); + testErrorsAndWarnings('row nullVar = null | eval cbrt(nullVar)', []); }); describe('from_base64', () => { @@ -9723,6 +9919,8 @@ describe('validation logic', () => { ]); testErrorsAndWarnings('from a_index | sort from_base64(stringField)', []); + testErrorsAndWarnings('from a_index | eval from_base64(null)', []); + testErrorsAndWarnings('row nullVar = null | eval from_base64(nullVar)', []); }); describe('locate', () => { @@ -9806,6 +10004,8 @@ describe('validation logic', () => { ); testErrorsAndWarnings('from a_index | sort locate(stringField, stringField)', []); + testErrorsAndWarnings('from a_index | eval locate(null, null, null)', []); + testErrorsAndWarnings('row nullVar = null | eval locate(nullVar, nullVar, nullVar)', []); }); describe('to_base64', () => { @@ -9840,6 +10040,8 @@ describe('validation logic', () => { ]); testErrorsAndWarnings('from a_index | sort to_base64(stringField)', []); + testErrorsAndWarnings('from a_index | eval to_base64(null)', []); + testErrorsAndWarnings('row nullVar = null | eval to_base64(nullVar)', []); }); }); }); diff --git a/packages/kbn-securitysolution-grouping/.storybook/main.js b/packages/kbn-grouping/.storybook/main.js similarity index 100% rename from packages/kbn-securitysolution-grouping/.storybook/main.js rename to packages/kbn-grouping/.storybook/main.js diff --git a/packages/kbn-grouping/README.mdx b/packages/kbn-grouping/README.mdx new file mode 100644 index 00000000000000..1a49426a8fb354 --- /dev/null +++ b/packages/kbn-grouping/README.mdx @@ -0,0 +1,3 @@ +# @kbn/grouping + +Grouping component and query. diff --git a/packages/kbn-securitysolution-grouping/index.tsx b/packages/kbn-grouping/index.tsx similarity index 100% rename from packages/kbn-securitysolution-grouping/index.tsx rename to packages/kbn-grouping/index.tsx diff --git a/packages/kbn-grouping/jest.config.js b/packages/kbn-grouping/jest.config.js new file mode 100644 index 00000000000000..d415d4101ad868 --- /dev/null +++ b/packages/kbn-grouping/jest.config.js @@ -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. + */ + +module.exports = { + preset: '@kbn/test', + rootDir: '../..', + roots: ['/packages/kbn-grouping'], + coverageReporters: ['text', 'html'], + collectCoverageFrom: [ + '/packages/kbn-grouping/**/*.{ts,tsx}', + '!/packages/kbn-grouping/**/*.test', + '!/packages/kbn-grouping/**/types/*', + '!/packages/kbn-grouping/**/*.type', + '!/packages/kbn-grouping/**/*.styles', + '!/packages/kbn-grouping/**/mocks/*', + '!/packages/kbn-grouping/**/*.config', + '!/packages/kbn-grouping/**/translations', + '!/packages/kbn-grouping/**/types/*', + ], + setupFilesAfterEnv: ['/packages/kbn-grouping/setup_test.ts'], +}; diff --git a/packages/kbn-grouping/kibana.jsonc b/packages/kbn-grouping/kibana.jsonc new file mode 100644 index 00000000000000..ce91c91cefcdaa --- /dev/null +++ b/packages/kbn-grouping/kibana.jsonc @@ -0,0 +1,5 @@ +{ + "type": "shared-common", + "id": "@kbn/grouping", + "owner": "@elastic/response-ops" +} diff --git a/packages/kbn-securitysolution-grouping/package.json b/packages/kbn-grouping/package.json similarity index 72% rename from packages/kbn-securitysolution-grouping/package.json rename to packages/kbn-grouping/package.json index 8d545276362966..5b454dcf9f2688 100644 --- a/packages/kbn-securitysolution-grouping/package.json +++ b/packages/kbn-grouping/package.json @@ -1,5 +1,5 @@ { - "name": "@kbn/securitysolution-grouping", + "name": "@kbn/grouping", "private": true, "version": "1.0.0", "license": "SSPL-1.0 OR Elastic License 2.0", diff --git a/packages/kbn-securitysolution-grouping/setup_test.ts b/packages/kbn-grouping/setup_test.ts similarity index 100% rename from packages/kbn-securitysolution-grouping/setup_test.ts rename to packages/kbn-grouping/setup_test.ts diff --git a/packages/kbn-securitysolution-grouping/src/components/accordion_panel/group_stats.test.tsx b/packages/kbn-grouping/src/components/accordion_panel/group_stats.test.tsx similarity index 100% rename from packages/kbn-securitysolution-grouping/src/components/accordion_panel/group_stats.test.tsx rename to packages/kbn-grouping/src/components/accordion_panel/group_stats.test.tsx diff --git a/packages/kbn-securitysolution-grouping/src/components/accordion_panel/group_stats.tsx b/packages/kbn-grouping/src/components/accordion_panel/group_stats.tsx similarity index 100% rename from packages/kbn-securitysolution-grouping/src/components/accordion_panel/group_stats.tsx rename to packages/kbn-grouping/src/components/accordion_panel/group_stats.tsx diff --git a/packages/kbn-securitysolution-grouping/src/components/accordion_panel/index.test.tsx b/packages/kbn-grouping/src/components/accordion_panel/index.test.tsx similarity index 100% rename from packages/kbn-securitysolution-grouping/src/components/accordion_panel/index.test.tsx rename to packages/kbn-grouping/src/components/accordion_panel/index.test.tsx diff --git a/packages/kbn-securitysolution-grouping/src/components/accordion_panel/index.tsx b/packages/kbn-grouping/src/components/accordion_panel/index.tsx similarity index 100% rename from packages/kbn-securitysolution-grouping/src/components/accordion_panel/index.tsx rename to packages/kbn-grouping/src/components/accordion_panel/index.tsx diff --git a/packages/kbn-securitysolution-grouping/src/components/empty_results_panel.tsx b/packages/kbn-grouping/src/components/empty_results_panel.tsx similarity index 100% rename from packages/kbn-securitysolution-grouping/src/components/empty_results_panel.tsx rename to packages/kbn-grouping/src/components/empty_results_panel.tsx diff --git a/packages/kbn-securitysolution-grouping/src/components/group_selector/custom_field_panel.tsx b/packages/kbn-grouping/src/components/group_selector/custom_field_panel.tsx similarity index 100% rename from packages/kbn-securitysolution-grouping/src/components/group_selector/custom_field_panel.tsx rename to packages/kbn-grouping/src/components/group_selector/custom_field_panel.tsx diff --git a/packages/kbn-securitysolution-grouping/src/components/group_selector/index.test.tsx b/packages/kbn-grouping/src/components/group_selector/index.test.tsx similarity index 100% rename from packages/kbn-securitysolution-grouping/src/components/group_selector/index.test.tsx rename to packages/kbn-grouping/src/components/group_selector/index.test.tsx diff --git a/packages/kbn-securitysolution-grouping/src/components/group_selector/index.tsx b/packages/kbn-grouping/src/components/group_selector/index.tsx similarity index 100% rename from packages/kbn-securitysolution-grouping/src/components/group_selector/index.tsx rename to packages/kbn-grouping/src/components/group_selector/index.tsx diff --git a/packages/kbn-securitysolution-grouping/src/components/grouping.mock.tsx b/packages/kbn-grouping/src/components/grouping.mock.tsx similarity index 100% rename from packages/kbn-securitysolution-grouping/src/components/grouping.mock.tsx rename to packages/kbn-grouping/src/components/grouping.mock.tsx diff --git a/packages/kbn-securitysolution-grouping/src/components/grouping.stories.tsx b/packages/kbn-grouping/src/components/grouping.stories.tsx similarity index 100% rename from packages/kbn-securitysolution-grouping/src/components/grouping.stories.tsx rename to packages/kbn-grouping/src/components/grouping.stories.tsx diff --git a/packages/kbn-securitysolution-grouping/src/components/grouping.test.tsx b/packages/kbn-grouping/src/components/grouping.test.tsx similarity index 100% rename from packages/kbn-securitysolution-grouping/src/components/grouping.test.tsx rename to packages/kbn-grouping/src/components/grouping.test.tsx diff --git a/packages/kbn-securitysolution-grouping/src/components/grouping.tsx b/packages/kbn-grouping/src/components/grouping.tsx similarity index 100% rename from packages/kbn-securitysolution-grouping/src/components/grouping.tsx rename to packages/kbn-grouping/src/components/grouping.tsx diff --git a/packages/kbn-securitysolution-grouping/src/components/index.tsx b/packages/kbn-grouping/src/components/index.tsx similarity index 100% rename from packages/kbn-securitysolution-grouping/src/components/index.tsx rename to packages/kbn-grouping/src/components/index.tsx diff --git a/packages/kbn-securitysolution-grouping/src/components/styles.tsx b/packages/kbn-grouping/src/components/styles.tsx similarity index 100% rename from packages/kbn-securitysolution-grouping/src/components/styles.tsx rename to packages/kbn-grouping/src/components/styles.tsx diff --git a/packages/kbn-securitysolution-grouping/src/components/translations.ts b/packages/kbn-grouping/src/components/translations.ts similarity index 100% rename from packages/kbn-securitysolution-grouping/src/components/translations.ts rename to packages/kbn-grouping/src/components/translations.ts diff --git a/packages/kbn-securitysolution-grouping/src/components/types.ts b/packages/kbn-grouping/src/components/types.ts similarity index 100% rename from packages/kbn-securitysolution-grouping/src/components/types.ts rename to packages/kbn-grouping/src/components/types.ts diff --git a/packages/kbn-securitysolution-grouping/src/containers/index.ts b/packages/kbn-grouping/src/containers/index.ts similarity index 100% rename from packages/kbn-securitysolution-grouping/src/containers/index.ts rename to packages/kbn-grouping/src/containers/index.ts diff --git a/packages/kbn-securitysolution-grouping/src/containers/query/helpers.test.ts b/packages/kbn-grouping/src/containers/query/helpers.test.ts similarity index 100% rename from packages/kbn-securitysolution-grouping/src/containers/query/helpers.test.ts rename to packages/kbn-grouping/src/containers/query/helpers.test.ts diff --git a/packages/kbn-securitysolution-grouping/src/containers/query/helpers.ts b/packages/kbn-grouping/src/containers/query/helpers.ts similarity index 100% rename from packages/kbn-securitysolution-grouping/src/containers/query/helpers.ts rename to packages/kbn-grouping/src/containers/query/helpers.ts diff --git a/packages/kbn-securitysolution-grouping/src/containers/query/index.test.ts b/packages/kbn-grouping/src/containers/query/index.test.ts similarity index 100% rename from packages/kbn-securitysolution-grouping/src/containers/query/index.test.ts rename to packages/kbn-grouping/src/containers/query/index.test.ts diff --git a/packages/kbn-securitysolution-grouping/src/containers/query/index.ts b/packages/kbn-grouping/src/containers/query/index.ts similarity index 100% rename from packages/kbn-securitysolution-grouping/src/containers/query/index.ts rename to packages/kbn-grouping/src/containers/query/index.ts diff --git a/packages/kbn-securitysolution-grouping/src/containers/query/types.ts b/packages/kbn-grouping/src/containers/query/types.ts similarity index 100% rename from packages/kbn-securitysolution-grouping/src/containers/query/types.ts rename to packages/kbn-grouping/src/containers/query/types.ts diff --git a/packages/kbn-securitysolution-grouping/src/helpers.ts b/packages/kbn-grouping/src/helpers.ts similarity index 100% rename from packages/kbn-securitysolution-grouping/src/helpers.ts rename to packages/kbn-grouping/src/helpers.ts diff --git a/packages/kbn-securitysolution-grouping/src/hooks/state/actions.ts b/packages/kbn-grouping/src/hooks/state/actions.ts similarity index 100% rename from packages/kbn-securitysolution-grouping/src/hooks/state/actions.ts rename to packages/kbn-grouping/src/hooks/state/actions.ts diff --git a/packages/kbn-securitysolution-grouping/src/hooks/state/index.ts b/packages/kbn-grouping/src/hooks/state/index.ts similarity index 100% rename from packages/kbn-securitysolution-grouping/src/hooks/state/index.ts rename to packages/kbn-grouping/src/hooks/state/index.ts diff --git a/packages/kbn-securitysolution-grouping/src/hooks/state/reducer.test.ts b/packages/kbn-grouping/src/hooks/state/reducer.test.ts similarity index 100% rename from packages/kbn-securitysolution-grouping/src/hooks/state/reducer.test.ts rename to packages/kbn-grouping/src/hooks/state/reducer.test.ts diff --git a/packages/kbn-securitysolution-grouping/src/hooks/state/reducer.ts b/packages/kbn-grouping/src/hooks/state/reducer.ts similarity index 100% rename from packages/kbn-securitysolution-grouping/src/hooks/state/reducer.ts rename to packages/kbn-grouping/src/hooks/state/reducer.ts diff --git a/packages/kbn-securitysolution-grouping/src/hooks/state/selectors.ts b/packages/kbn-grouping/src/hooks/state/selectors.ts similarity index 100% rename from packages/kbn-securitysolution-grouping/src/hooks/state/selectors.ts rename to packages/kbn-grouping/src/hooks/state/selectors.ts diff --git a/packages/kbn-securitysolution-grouping/src/hooks/types.ts b/packages/kbn-grouping/src/hooks/types.ts similarity index 100% rename from packages/kbn-securitysolution-grouping/src/hooks/types.ts rename to packages/kbn-grouping/src/hooks/types.ts diff --git a/packages/kbn-securitysolution-grouping/src/hooks/use_get_group_selector.test.tsx b/packages/kbn-grouping/src/hooks/use_get_group_selector.test.tsx similarity index 100% rename from packages/kbn-securitysolution-grouping/src/hooks/use_get_group_selector.test.tsx rename to packages/kbn-grouping/src/hooks/use_get_group_selector.test.tsx diff --git a/packages/kbn-securitysolution-grouping/src/hooks/use_get_group_selector.tsx b/packages/kbn-grouping/src/hooks/use_get_group_selector.tsx similarity index 100% rename from packages/kbn-securitysolution-grouping/src/hooks/use_get_group_selector.tsx rename to packages/kbn-grouping/src/hooks/use_get_group_selector.tsx diff --git a/packages/kbn-securitysolution-grouping/src/hooks/use_grouping.test.tsx b/packages/kbn-grouping/src/hooks/use_grouping.test.tsx similarity index 100% rename from packages/kbn-securitysolution-grouping/src/hooks/use_grouping.test.tsx rename to packages/kbn-grouping/src/hooks/use_grouping.test.tsx diff --git a/packages/kbn-securitysolution-grouping/src/hooks/use_grouping.tsx b/packages/kbn-grouping/src/hooks/use_grouping.tsx similarity index 100% rename from packages/kbn-securitysolution-grouping/src/hooks/use_grouping.tsx rename to packages/kbn-grouping/src/hooks/use_grouping.tsx diff --git a/packages/kbn-securitysolution-grouping/src/index.ts b/packages/kbn-grouping/src/index.ts similarity index 100% rename from packages/kbn-securitysolution-grouping/src/index.ts rename to packages/kbn-grouping/src/index.ts diff --git a/packages/kbn-securitysolution-grouping/src/mocks.ts b/packages/kbn-grouping/src/mocks.ts similarity index 100% rename from packages/kbn-securitysolution-grouping/src/mocks.ts rename to packages/kbn-grouping/src/mocks.ts diff --git a/packages/kbn-securitysolution-grouping/src/telemetry/const.ts b/packages/kbn-grouping/src/telemetry/const.ts similarity index 100% rename from packages/kbn-securitysolution-grouping/src/telemetry/const.ts rename to packages/kbn-grouping/src/telemetry/const.ts diff --git a/packages/kbn-securitysolution-grouping/tsconfig.json b/packages/kbn-grouping/tsconfig.json similarity index 100% rename from packages/kbn-securitysolution-grouping/tsconfig.json rename to packages/kbn-grouping/tsconfig.json diff --git a/packages/kbn-lens-embeddable-utils/README.md b/packages/kbn-lens-embeddable-utils/README.md index 662ba0c92e7cf0..e41de4eaecc7d5 100644 --- a/packages/kbn-lens-embeddable-utils/README.md +++ b/packages/kbn-lens-embeddable-utils/README.md @@ -1,241 +1,10 @@ # @kbn/lens-embeddable-utils -## Lens Attributes Builder +## Lens Config Builder - The Lens Attributes Builder is a utility for creating JSON objects used to render charts with Lens. It simplifies the process of configuring and building the necessary attributes for different chart types. - -**Notes**: - -This utililty is primarily used by Infra Observability UI and not meant to be used as an official solution provided by the Lens team. - -- The tool has partial support of Lens charts, currently limited to XY and Metric charts. -- XY Bucket and Breakdown dimensions are limited respectively to Date Histogram and Top values. + The Lens Config Builder is a utility for creating JSON objects used to render charts with Lens. It simplifies the process of configuring and building the necessary attributes for different chart types. ### Usage -#### Creating a Metric Chart - -To create a metric chart, use the `MetricChart` class and provide the required configuration. Here's an example: - -```ts -const metricChart = new MetricChart({ - layers: new MetricLayer({ - data: { - label: 'Disk Read Throughput', - value: "counter_rate(max(system.diskio.read.count), kql='system.diskio.read.count: *')", - format: { - id: 'bytes', - params: { - decimals: 1, - }, - }, - }, - }), - dataView, - formulaAPI -}); -``` - -#### Creating an XY Chart - -To create an XY chart, use the `XYChart` class and provide the required configuration. Here's an example: - -```ts -const xyChart = new XYChart({ - layers: [new XYDataLayer({ - data: [{ - label: 'Normalized Load', - value: "average(system.load.1) / max(system.load.cores)", - format: { - id: 'percent', - params: { - decimals: 1, - }, - }, - }], - options: { - buckets: {type: 'date_histogram'}, - }, - })], - dataView, - formulaAPI -}); -``` - -#### Variations of the XY Chart - -XYChart has different series type variations. Here is an example of how to build a line (default) and area charts - -#### Line chart - -```ts -const xyChart = new XYChart({ - layers: [new XYDataLayer({ - data: [{ - label: 'Inbound (RX)', - value: "average(system.load.1) / max(system.load.cores)", - format: { - id: 'percent', - params: { - decimals: 1, - }, - }, - - }], - options: { - buckets: {type: 'date_histogram'}, - seriesType: 'line' // default. it doesn't need to be informed. - } - })], - dataView, - formulaAPI -}); -``` - -#### Area chart - -```ts -const xyChart = new XYChart({ - layers: [new XYDataLayer({ - data: [{ - label: 'Inbound (RX)', - value: "average(system.load.1) / max(system.load.cores)", - format: { - id: 'percent', - params: { - decimals: 1, - }, - }, - - }], - options: { - buckets: {type: 'date_histogram'}, - seriesType: 'area' - } - })], - dataView, - formulaAPI -}); -``` - -#### Adding Multiple Layers to an XY Chart - -An XY chart can have multiple layers. Here's an example of containing a Reference Line Layer: - -```ts -const xyChart = new XYChart({ - layers: [ - new XYDataLayer({ - data: [{ - label: 'Disk Read Throughput', - value: "average(system.load.1) / max(system.load.cores)", - format: { - id: 'percent', - params: { - decimals: 1, - }, - }, - }], - options: { - buckets: {type: 'date_histogram'}, - }, - }), - new XYReferenceLineLayer({ - data: [{ - - value: "1", - format: { - id: 'percent', - params: { - decimals: 1, - }, - }, - }], - }), - ], - dataView, - formulaAPI -}); -``` - -#### Adding Multiple Data Sources in the Same Layer - -In an XY chart, it's possible to define multiple data sources within the same layer. - -To configure multiple data sources in an XY data layer, simply provide an array of data to the same YXDataLayer class: - -```ts -const xyChart = new XYChart({ - layers: new YXDataLayer({ - data: [{ - label: 'RX', - value: "average(host.network.ingress.bytes) * 8 / (max(metricset.period, kql='host.network.ingress.bytes: *') / 1000)", - format: { - id: 'bits', - params: { - decimals: 1, - }, - }, - },{ - label: 'TX', - value: "(average(host.network.egresss.bytes) * 8 / (max(metricset.period, kql='host.network.egresss.bytes: *') / 1000)", - format: { - id: 'bits', - params: { - decimals: 1, - }, - }, - }], - options: { - buckets: {type: 'date_histogram'}, - }, - }), - dataView, - formulaAPI -}); -``` - -#### Building Lens Chart Attributes - -The `LensAttributesBuilder` is responsible for creating the full JSON object that combines the attributes returned by the chart classes. Here's an example: - -```ts -const builder = new LensAttributesBuilder({ visualization: xyChart }); -const attributes = builder.build(); -``` - -The `attributes` object contains the final JSON representation of the chart configuration and can be used to render the chart with Lens. - -### Usage with Lens EmbeddableComponent - -To display the charts rendered with the Lens Attributes Builder, it's recommended to use the Lens `EmbeddableComponent`. The `EmbeddableComponent` abstracts some of the chart styling and other details that would be challenging to handle directly with the Lens Attributes Builder. - -```tsx -const builder = new LensAttributesBuilder({ - visualization: new MetricChart({ - layers: new MetricLayer({ - data: { - label: 'Disk Read Throughput', - value: "counter_rate(max(system.diskio.read.count), kql='system.diskio.read.count: *')", - format: { - id: 'bytes', - params: { - decimals: 1, - }, - }, - }, - }), - dataView, - formulaAPI - }), -}); - -const lensAttributes = builder.build(); - - -``` \ No newline at end of file +Check kibana developer docs https://docs.elastic.dev/kibana-dev-docs/lens/config-builder \ No newline at end of file diff --git a/packages/kbn-lens-embeddable-utils/config_builder/config_builder.ts b/packages/kbn-lens-embeddable-utils/config_builder/config_builder.ts index a718609b3866fa..4a14e20b7aea47 100644 --- a/packages/kbn-lens-embeddable-utils/config_builder/config_builder.ts +++ b/packages/kbn-lens-embeddable-utils/config_builder/config_builder.ts @@ -7,8 +7,8 @@ */ import type { FormulaPublicApi, LensEmbeddableInput } from '@kbn/lens-plugin/public'; -import type { DataViewsPublicPluginStart } from '@kbn/data-views-plugin/public'; import { v4 as uuidv4 } from 'uuid'; +import { DataViewsService } from '@kbn/data-views-plugin/common'; import { LensAttributes, LensConfig, LensConfigOptions } from './types'; import { buildGauge, @@ -21,6 +21,8 @@ import { buildPartitionChart, } from './charts'; +export type DataViewsCommon = Pick; + export class LensConfigBuilder { private charts = { metric: buildMetric, @@ -36,10 +38,10 @@ export class LensConfigBuilder { table: buildTable, }; private formulaAPI: FormulaPublicApi | undefined; - private dataViewsAPI: DataViewsPublicPluginStart; + private dataViewsAPI: DataViewsCommon; // formulaApi is optional, as it is not necessary to use it when creating charts with ES|QL - constructor(dataViewsAPI: DataViewsPublicPluginStart, formulaAPI?: FormulaPublicApi) { + constructor(dataViewsAPI: DataViewsCommon, formulaAPI?: FormulaPublicApi) { this.formulaAPI = formulaAPI; this.dataViewsAPI = dataViewsAPI; } diff --git a/packages/kbn-lens-embeddable-utils/config_builder/types.ts b/packages/kbn-lens-embeddable-utils/config_builder/types.ts index 83595dbd7581c4..19683adb4d423c 100644 --- a/packages/kbn-lens-embeddable-utils/config_builder/types.ts +++ b/packages/kbn-lens-embeddable-utils/config_builder/types.ts @@ -8,8 +8,8 @@ import type { FormulaPublicApi, TypedLensByValueInput } from '@kbn/lens-plugin/public'; import type { Filter, Query } from '@kbn/es-query'; -import type { DataViewsPublicPluginStart } from '@kbn/data-views-plugin/public'; import type { Datatable } from '@kbn/expressions-plugin/common'; +import { DataViewsCommon } from './config_builder'; export type LensAttributes = TypedLensByValueInput['attributes']; export const DEFAULT_LAYER_ID = 'layer_0'; @@ -289,7 +289,7 @@ export interface LensXYConfigBase { yBounds?: LensYBoundsConfig; } export interface BuildDependencies { - dataViewsAPI: DataViewsPublicPluginStart; + dataViewsAPI: DataViewsCommon; formulaAPI?: FormulaPublicApi; } diff --git a/packages/kbn-lens-embeddable-utils/config_builder/utils.ts b/packages/kbn-lens-embeddable-utils/config_builder/utils.ts index 2dcb4205f96104..ffd0a40612a7ab 100644 --- a/packages/kbn-lens-embeddable-utils/config_builder/utils.ts +++ b/packages/kbn-lens-embeddable-utils/config_builder/utils.ts @@ -8,11 +8,7 @@ import { v4 as uuidv4 } from 'uuid'; import type { SavedObjectReference } from '@kbn/core-saved-objects-common/src/server_types'; -import type { - DataViewSpec, - DataView, - DataViewsPublicPluginStart, -} from '@kbn/data-views-plugin/public'; +import type { DataViewSpec, DataView } from '@kbn/data-views-plugin/public'; import type { FormBasedPersistedState, GenericIndexPatternColumn, @@ -24,6 +20,7 @@ import type { } from '@kbn/lens-plugin/public/datasources/text_based/types'; import type { AggregateQuery } from '@kbn/es-query'; import { getIndexPatternFromESQLQuery } from '@kbn/esql-utils'; +import { DataViewsCommon } from './config_builder'; import { FormulaValueConfig, LensAnnotationLayer, @@ -126,7 +123,7 @@ export function isFormulaDataset(dataset?: LensDataset) { */ export async function getDataView( index: string, - dataViewsAPI: DataViewsPublicPluginStart, + dataViewsAPI: DataViewsCommon, timeField?: string ) { let dataView: DataView; @@ -226,7 +223,7 @@ export const buildDatasourceStates = async ( dataView: DataView ) => PersistedIndexPatternLayer | FormBasedPersistedState['layers'] | undefined, getValueColumns: (config: any, i: number) => TextBasedLayerColumn[], - dataViewsAPI: DataViewsPublicPluginStart + dataViewsAPI: DataViewsCommon ) => { let layers: Partial = {}; diff --git a/packages/kbn-lens-embeddable-utils/package.json b/packages/kbn-lens-embeddable-utils/package.json index c70d63093593fa..13664652021bdc 100644 --- a/packages/kbn-lens-embeddable-utils/package.json +++ b/packages/kbn-lens-embeddable-utils/package.json @@ -2,5 +2,6 @@ "name": "@kbn/lens-embeddable-utils", "private": true, "version": "1.0.0", - "license": "SSPL-1.0 OR Elastic License 2.0" + "license": "SSPL-1.0 OR Elastic License 2.0", + "homepage": "https://docs.elastic.dev/kibana-dev-docs/lens/config-builder" } \ No newline at end of file diff --git a/packages/kbn-monaco/src/console/lexer_rules/console_editor.ts b/packages/kbn-monaco/src/console/lexer_rules/console_editor.ts index 22878e000fc6cd..97984a83b2c9f3 100644 --- a/packages/kbn-monaco/src/console/lexer_rules/console_editor.ts +++ b/packages/kbn-monaco/src/console/lexer_rules/console_editor.ts @@ -11,6 +11,7 @@ import { consoleSharedLexerRules, matchTokensWithEOL, matchToken, + matchTokens, } from './shared'; import { monaco } from '../../monaco_imports'; @@ -31,6 +32,11 @@ export const lexerRules: monaco.languages.IMonarchLanguage = { // text matchToken('text', '.+?'), ], + comments: [ + // line comment indicated by # + matchTokens(['comment.punctuation', 'comment.line'], /(#)(.*$)/), + ...consoleSharedLexerRules.tokenizer.comments, + ], method_sep: [ // protocol host with slash matchTokensWithEOL( diff --git a/packages/kbn-monaco/src/console/lexer_rules/console_output.ts b/packages/kbn-monaco/src/console/lexer_rules/console_output.ts index 02e14e35aa3a10..8209820930910b 100644 --- a/packages/kbn-monaco/src/console/lexer_rules/console_output.ts +++ b/packages/kbn-monaco/src/console/lexer_rules/console_output.ts @@ -6,7 +6,11 @@ * Side Public License, v 1. */ -import { consoleSharedLanguageConfiguration, consoleSharedLexerRules } from './shared'; +import { + consoleSharedLanguageConfiguration, + consoleSharedLexerRules, + matchTokensWithEOL, +} from './shared'; import { monaco } from '../../monaco_imports'; export const consoleOutputLanguageConfiguration: monaco.languages.LanguageConfiguration = { @@ -15,4 +19,24 @@ export const consoleOutputLanguageConfiguration: monaco.languages.LanguageConfig export const consoleOutputLexerRules: monaco.languages.IMonarchLanguage = { ...consoleSharedLexerRules, + tokenizer: { + ...consoleSharedLexerRules.tokenizer, + comments: [ + // Line comment indicated by # + // Everything after the # character is matched, stopping right before the status code and status text at the end if they are present + matchTokensWithEOL('comment', /# .+?(?=\s+\d{3}(?: \w+)*$)/, 'root', 'status'), + ...consoleSharedLexerRules.tokenizer.comments, + ], + status: [ + // Following HTTP response status codes conventions + // Informational responses (status codes 100 – 199) + matchTokensWithEOL('status.info', /\b1\d{2}(?: \w+)*$/, 'root'), + // Successful responses (status codes 200 – 299) + matchTokensWithEOL('status.success', /\b2\d{2}(?: \w+)*$/, 'root'), + // Redirection messages (status codes 300 – 399) + matchTokensWithEOL('status.redirect', /\b3\d{2}(?: \w+)*$/, 'root'), + // Client and server error responses (status codes 400 – 599) + matchTokensWithEOL('status.error', /\b[4-5]\d{2}(?: \w+)*$/, 'root'), + ], + }, }; diff --git a/packages/kbn-monaco/src/console/lexer_rules/shared.ts b/packages/kbn-monaco/src/console/lexer_rules/shared.ts index f9ceb0f9f85e9f..c46ef69af10026 100644 --- a/packages/kbn-monaco/src/console/lexer_rules/shared.ts +++ b/packages/kbn-monaco/src/console/lexer_rules/shared.ts @@ -112,8 +112,6 @@ export const consoleSharedLexerRules: monaco.languages.IMonarchLanguage = { matchToken('paren.lparen', '{', 'json_root'), ], comments: [ - // line comment indicated by # - matchTokens(['comment.punctuation', 'comment.line'], /(#)(.*$)/), // start a block comment indicated by /* matchToken('comment.punctuation', /\/\*/, 'block_comment'), // line comment indicated by // diff --git a/packages/kbn-monaco/src/console/theme.ts b/packages/kbn-monaco/src/console/theme.ts index 66e2bfab812bd3..3a90771354a53c 100644 --- a/packages/kbn-monaco/src/console/theme.ts +++ b/packages/kbn-monaco/src/console/theme.ts @@ -37,6 +37,26 @@ export const buildConsoleTheme = (): monaco.editor.IStandaloneThemeData => { ['constant.numeric'], makeHighContrastColor(euiThemeVars.euiColorAccentText)(background) ), + ...buildRuleGroup( + ['status.info'], + makeHighContrastColor(euiThemeVars.euiColorWarningText)(background), + true + ), + ...buildRuleGroup( + ['status.success'], + makeHighContrastColor(euiThemeVars.euiColorSuccessText)(background), + true + ), + ...buildRuleGroup( + ['status.redirect'], + makeHighContrastColor(euiThemeVars.euiColorWarningText)(background), + true + ), + ...buildRuleGroup( + ['status.error'], + makeHighContrastColor(euiThemeVars.euiColorDangerText)(background), + true + ), ...buildRuleGroup(['method'], makeHighContrastColor(methodTextColor)(background)), ...buildRuleGroup(['url'], makeHighContrastColor(urlTextColor)(background)), ], diff --git a/packages/kbn-search-connectors/lib/cancel_syncs.test.ts b/packages/kbn-search-connectors/lib/cancel_syncs.test.ts index 05c2f5fcd529d8..0fd769099a1555 100644 --- a/packages/kbn-search-connectors/lib/cancel_syncs.test.ts +++ b/packages/kbn-search-connectors/lib/cancel_syncs.test.ts @@ -8,84 +8,57 @@ import { ElasticsearchClient } from '@kbn/core/server'; -import { CONNECTORS_INDEX, CONNECTORS_JOBS_INDEX } from '..'; import { SyncStatus } from '../types/connectors'; import { cancelSyncs } from './cancel_syncs'; +import { fetchSyncJobs } from './fetch_sync_jobs'; + +jest.mock('./fetch_sync_jobs', () => ({ + fetchSyncJobs: jest.fn(), +})); describe('cancelSync lib function', () => { const mockClient = { - update: jest.fn(), - updateByQuery: jest.fn(), + transport: { + request: jest.fn(), + }, }; beforeEach(() => { jest.clearAllMocks(); - const now = new Date('2022-05-22T10:10:11.111Z'); - jest.spyOn(Date, 'now').mockImplementation(() => now.getTime()); }); - it('should call updateByQuery to cancel syncs', async () => { - mockClient.updateByQuery.mockImplementation(() => ({ _id: 'fakeId' })); + it('should call /_cancel endpoint to cancel syncs', async () => { + (fetchSyncJobs as jest.Mock) + .mockResolvedValueOnce({ + data: [{ id: 'job_1' }, { id: 'job_2' }], + }) + .mockResolvedValueOnce({ data: [] }) + .mockResolvedValueOnce({ data: [{ id: 'job_3' }] }); await expect( cancelSyncs(mockClient as unknown as ElasticsearchClient, 'connectorId') ).resolves.toEqual(undefined); - expect(mockClient.updateByQuery).toHaveBeenCalledTimes(2); - expect(mockClient.updateByQuery).toHaveBeenCalledWith({ - index: CONNECTORS_JOBS_INDEX, - query: { - bool: { - must: [ - { - term: { - 'connector.id': 'connectorId', - }, - }, - { - terms: { - status: [SyncStatus.PENDING, SyncStatus.SUSPENDED], - }, - }, - ], - }, - }, - script: { - lang: 'painless', - source: `ctx._source['status'] = '${SyncStatus.CANCELED}'; -ctx._source['cancelation_requested_at'] = '${new Date(Date.now()).toISOString()}'; -ctx._source['canceled_at'] = '${new Date(Date.now()).toISOString()}'; -ctx._source['completed_at'] = '${new Date(Date.now()).toISOString()}';`, - }, + expect(fetchSyncJobs).toHaveBeenCalledTimes(3); + expect(mockClient.transport.request).toHaveBeenCalledWith({ + method: 'PUT', + path: '/_connector/_sync_job/job_1/_cancel', }); - expect(mockClient.updateByQuery).toHaveBeenCalledWith({ - index: CONNECTORS_JOBS_INDEX, - query: { - bool: { - must: [ - { - term: { - 'connector.id': 'connectorId', - }, - }, - { - terms: { - status: [SyncStatus.IN_PROGRESS], - }, - }, - ], - }, - }, - script: { - lang: 'painless', - source: `ctx._source['status'] = '${SyncStatus.CANCELING}'; -ctx._source['cancelation_requested_at'] = '${new Date(Date.now()).toISOString()}';`, - }, + expect(mockClient.transport.request).toHaveBeenCalledWith({ + method: 'PUT', + path: '/_connector/_sync_job/job_2/_cancel', + }); + expect(mockClient.transport.request).toHaveBeenCalledWith({ + method: 'PUT', + path: '/_connector/_sync_job/job_3/_cancel', }); - await expect(mockClient.update).toHaveBeenCalledWith({ - doc: { last_sync_status: SyncStatus.CANCELED, sync_now: false }, - id: 'connectorId', - index: CONNECTORS_INDEX, + await expect(mockClient.transport.request).toHaveBeenCalledWith({ + method: 'PUT', + path: '/_connector/connectorId/_last_sync', + body: { + last_access_control_sync_status: SyncStatus.CANCELED, + last_sync_status: SyncStatus.CANCELED, + }, }); }); }); diff --git a/packages/kbn-search-connectors/lib/cancel_syncs.ts b/packages/kbn-search-connectors/lib/cancel_syncs.ts index c706dd7d311e3c..df66ceee5864e3 100644 --- a/packages/kbn-search-connectors/lib/cancel_syncs.ts +++ b/packages/kbn-search-connectors/lib/cancel_syncs.ts @@ -7,75 +7,31 @@ */ import { ElasticsearchClient } from '@kbn/core-elasticsearch-server'; +import { asyncForEach } from '@kbn/std'; -import { CONNECTORS_INDEX, CONNECTORS_JOBS_INDEX } from '..'; +import { fetchSyncJobs, cancelSync } from '..'; import { SyncStatus } from '../types/connectors'; -import { isIndexNotFoundException } from '../utils/identify_exceptions'; export const cancelSyncs = async ( client: ElasticsearchClient, connectorId: string ): Promise => { - try { - await client.updateByQuery({ - index: CONNECTORS_JOBS_INDEX, - query: { - bool: { - must: [ - { - term: { - 'connector.id': connectorId, - }, - }, - { - terms: { - status: [SyncStatus.PENDING, SyncStatus.SUSPENDED], - }, - }, - ], - }, - }, - script: { - lang: 'painless', - source: `ctx._source['status'] = '${SyncStatus.CANCELED}'; -ctx._source['cancelation_requested_at'] = '${new Date(Date.now()).toISOString()}'; -ctx._source['canceled_at'] = '${new Date(Date.now()).toISOString()}'; -ctx._source['completed_at'] = '${new Date(Date.now()).toISOString()}';`, - }, - }); - await client.updateByQuery({ - index: CONNECTORS_JOBS_INDEX, - query: { - bool: { - must: [ - { - term: { - 'connector.id': connectorId, - }, - }, - { - terms: { - status: [SyncStatus.IN_PROGRESS], - }, - }, - ], - }, - }, - script: { - lang: 'painless', - source: `ctx._source['status'] = '${SyncStatus.CANCELING}'; -ctx._source['cancelation_requested_at'] = '${new Date(Date.now()).toISOString()}';`, - }, - }); - await client.update({ - doc: { last_sync_status: SyncStatus.CANCELED, sync_now: false }, - id: connectorId, - index: CONNECTORS_INDEX, - }); - } catch (error) { - if (isIndexNotFoundException(error)) { - return; + await asyncForEach( + [SyncStatus.PENDING, SyncStatus.IN_PROGRESS, SyncStatus.SUSPENDED], + async (status) => { + const syncJobsToCancel = await fetchSyncJobs(client, connectorId, 0, 1000, 'all', status); + await asyncForEach(syncJobsToCancel.data, async (syncJob) => { + await cancelSync(client, syncJob.id); + }); } - throw error; - } + ); + + return await client.transport.request({ + method: 'PUT', + path: `/_connector/${connectorId}/_last_sync`, + body: { + last_access_control_sync_status: SyncStatus.CANCELED, + last_sync_status: SyncStatus.CANCELED, + }, + }); }; diff --git a/packages/kbn-search-connectors/lib/fetch_sync_jobs.test.ts b/packages/kbn-search-connectors/lib/fetch_sync_jobs.test.ts index c22b2ab19eef9b..248c163f5dadbf 100644 --- a/packages/kbn-search-connectors/lib/fetch_sync_jobs.test.ts +++ b/packages/kbn-search-connectors/lib/fetch_sync_jobs.test.ts @@ -6,6 +6,7 @@ * Side Public License, v 1. */ +import { SyncStatus } from '../types'; import { fetchSyncJobs } from './fetch_sync_jobs'; describe('fetchSyncJobs lib', () => { @@ -41,5 +42,30 @@ describe('fetchSyncJobs lib', () => { querystring: 'from=0&size=10&connector_id=id&job_type=full,incremental', }); }); + + it('should fetch sync jobs by status', async () => { + mockClient.transport.request.mockImplementationOnce(() => ({ + count: 22, + results: [], + })); + await expect( + fetchSyncJobs(mockClient as any, 'id', 0, 10, 'content', SyncStatus.IN_PROGRESS) + ).resolves.toEqual({ + _meta: { + page: { + from: 0, + has_more_hits_than_total: true, + size: 10, + total: 22, + }, + }, + data: [], + }); + expect(mockClient.transport.request).toHaveBeenCalledWith({ + method: 'GET', + path: '/_connector/_sync_job', + querystring: 'from=0&size=10&connector_id=id&job_type=full,incremental&status=in_progress', + }); + }); }); }); diff --git a/packages/kbn-search-connectors/lib/fetch_sync_jobs.ts b/packages/kbn-search-connectors/lib/fetch_sync_jobs.ts index d278c97c84f6b6..fec6be0cc7eb2d 100644 --- a/packages/kbn-search-connectors/lib/fetch_sync_jobs.ts +++ b/packages/kbn-search-connectors/lib/fetch_sync_jobs.ts @@ -9,7 +9,7 @@ import { ElasticsearchClient } from '@kbn/core-elasticsearch-server'; import { ConnectorsAPISyncJobResponse } from '..'; -import { ConnectorSyncJob } from '../types/connectors'; +import { ConnectorSyncJob, SyncStatus } from '../types/connectors'; import { Paginate } from '../types/pagination'; export const fetchSyncJobs = async ( @@ -17,13 +17,14 @@ export const fetchSyncJobs = async ( connectorId?: string, from: number = 0, size: number = 100, - syncJobType: 'content' | 'access_control' | 'all' = 'all' + syncJobType: 'content' | 'access_control' | 'all' = 'all', + syncStatus?: SyncStatus ): Promise> => { const querystring = `from=${from}&size=${size}${ connectorId ? '&connector_id=' + connectorId : '' }${syncJobType === 'content' ? '&job_type=full,incremental' : ''}${ syncJobType === 'access_control' ? '&job_type=access_control' : '' - }`; + }${syncStatus ? '&status=' + syncStatus : ''}`; const result = await client.transport.request({ method: 'GET', path: `/_connector/_sync_job`, diff --git a/packages/kbn-search-connectors/tsconfig.json b/packages/kbn-search-connectors/tsconfig.json index eb7decb3d1e00e..cb54e57748e948 100644 --- a/packages/kbn-search-connectors/tsconfig.json +++ b/packages/kbn-search-connectors/tsconfig.json @@ -24,5 +24,6 @@ "@kbn/config-schema", "@kbn/i18n-react", "@kbn/test-jest-helpers", + "@kbn/std", ] } diff --git a/packages/kbn-search-connectors/types/native_connectors.ts b/packages/kbn-search-connectors/types/native_connectors.ts index 422e31d2282be2..e8976b0f09f7bc 100644 --- a/packages/kbn-search-connectors/types/native_connectors.ts +++ b/packages/kbn-search-connectors/types/native_connectors.ts @@ -3583,12 +3583,34 @@ export const NATIVE_CONNECTOR_DEFINITIONS: Record/packages/kbn-securitysolution-grouping'], - coverageReporters: ['text', 'html'], - collectCoverageFrom: [ - '/packages/kbn-securitysolution-grouping/**/*.{ts,tsx}', - '!/packages/kbn-securitysolution-grouping/**/*.test', - '!/packages/kbn-securitysolution-grouping/**/types/*', - '!/packages/kbn-securitysolution-grouping/**/*.type', - '!/packages/kbn-securitysolution-grouping/**/*.styles', - '!/packages/kbn-securitysolution-grouping/**/mocks/*', - '!/packages/kbn-securitysolution-grouping/**/*.config', - '!/packages/kbn-securitysolution-grouping/**/translations', - '!/packages/kbn-securitysolution-grouping/**/types/*', - ], - setupFilesAfterEnv: ['/packages/kbn-securitysolution-grouping/setup_test.ts'], -}; diff --git a/packages/kbn-securitysolution-grouping/kibana.jsonc b/packages/kbn-securitysolution-grouping/kibana.jsonc deleted file mode 100644 index 532eb8f883dfc2..00000000000000 --- a/packages/kbn-securitysolution-grouping/kibana.jsonc +++ /dev/null @@ -1,5 +0,0 @@ -{ - "type": "shared-common", - "id": "@kbn/securitysolution-grouping", - "owner": "@elastic/security-threat-hunting-explore" -} diff --git a/packages/presentation/presentation_publishing/index.ts b/packages/presentation/presentation_publishing/index.ts index d1c3132e86c9fb..975f6f3863de14 100644 --- a/packages/presentation/presentation_publishing/index.ts +++ b/packages/presentation/presentation_publishing/index.ts @@ -29,7 +29,7 @@ export { useInheritedViewMode, type CanAccessViewMode, } from './interfaces/can_access_view_mode'; -export { fetch$, type FetchContext } from './interfaces/fetch/fetch'; +export { fetch$, useFetchContext, type FetchContext } from './interfaces/fetch/fetch'; export { initializeTimeRange, type SerializedTimeRange, diff --git a/packages/presentation/presentation_publishing/interfaces/fetch/fetch.ts b/packages/presentation/presentation_publishing/interfaces/fetch/fetch.ts index 8b77e366219cbd..466fd3a602a8ca 100644 --- a/packages/presentation/presentation_publishing/interfaces/fetch/fetch.ts +++ b/packages/presentation/presentation_publishing/interfaces/fetch/fetch.ts @@ -7,6 +7,7 @@ */ import { + BehaviorSubject, combineLatest, debounceTime, delay, @@ -23,6 +24,7 @@ import { tap, } from 'rxjs'; import { AggregateQuery, Filter, Query, TimeRange } from '@kbn/es-query'; +import { useMemo, useEffect } from 'react'; import { apiPublishesTimeRange, apiPublishesUnifiedSearch, @@ -32,6 +34,7 @@ import { import { apiPublishesSearchSession, PublishesSearchSession } from './publishes_search_session'; import { apiHasParentApi, HasParentApi } from '../has_parent_api'; import { apiPublishesReload } from './publishes_reload'; +import { useStateFromPublishingSubject } from '../../publishing_subject'; export interface FetchContext { isReload: boolean; @@ -145,3 +148,19 @@ export function fetch$(api: unknown): Observable { return merge(immediateChange$, batchedChanges$).pipe(startWith(getFetchContext(api, false))); } + +export const useFetchContext = (api: unknown): FetchContext => { + const context$: BehaviorSubject = useMemo(() => { + return new BehaviorSubject(getFetchContext(api, false)); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []); + + useEffect(() => { + const subsctription = fetch$(api).subscribe((nextContext) => context$.next(nextContext)); + + return () => subsctription.unsubscribe(); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []); + + return useStateFromPublishingSubject(context$); +}; diff --git a/packages/serverless/settings/common/index.ts b/packages/serverless/settings/common/index.ts index f8f608bdf64f5e..09d6b696da7f53 100644 --- a/packages/serverless/settings/common/index.ts +++ b/packages/serverless/settings/common/index.ts @@ -26,8 +26,6 @@ const GENERAL_SETTINGS = [ settings.TIMEPICKER_TIME_DEFAULTS_ID, ]; -const PRESENTATION_LABS_SETTINGS = [settings.LABS_DASHBOARD_DEFER_BELOW_FOLD_ID]; - const ACCESSIBILITY_SETTINGS = [settings.ACCESSIBILITY_DISABLE_ANIMATIONS_ID]; const BANNER_SETTINGS = [ @@ -49,7 +47,6 @@ const NOTIFICATION_SETTINGS = [ export const ALL_COMMON_SETTINGS = [ ...GENERAL_SETTINGS, - ...PRESENTATION_LABS_SETTINGS, ...ACCESSIBILITY_SETTINGS, ...BANNER_SETTINGS, ...DISCOVER_SETTINGS, diff --git a/src/core/server/integration_tests/ci_checks/saved_objects/check_registered_types.test.ts b/src/core/server/integration_tests/ci_checks/saved_objects/check_registered_types.test.ts index 08c1c039d5e790..1de50f0547b352 100644 --- a/src/core/server/integration_tests/ci_checks/saved_objects/check_registered_types.test.ts +++ b/src/core/server/integration_tests/ci_checks/saved_objects/check_registered_types.test.ts @@ -149,7 +149,7 @@ describe('checking migration metadata changes on all registered SO types', () => "siem-ui-timeline-pinned-event": "082daa3ce647b33873f6abccf340bdfa32057c8d", "slo": "9a9995e4572de1839651c43b5fc4dc8276bb5815", "slo-settings": "f6b5ed339470a6a2cda272bde1750adcf504a11b", - "space": "8de4ec513e9bbc6b2f1d635161d850be7747d38e", + "space": "d38fa4bc669b9b1d6ec86aac2983d4c6675723ed", "spaces-usage-stats": "3abca98713c52af8b30300e386c7779b3025a20e", "synthetics-monitor": "5ceb25b6249bd26902c9b34273c71c3dce06dbea", "synthetics-param": "3ebb744e5571de678b1312d5c418c8188002cf5e", diff --git a/src/core/server/integration_tests/saved_objects/migrations/group3/migration_from_older_v1.test.ts b/src/core/server/integration_tests/saved_objects/migrations/group3/migration_from_older_v1.test.ts index 8a4b584f34d846..186a90e47da718 100644 --- a/src/core/server/integration_tests/saved_objects/migrations/group3/migration_from_older_v1.test.ts +++ b/src/core/server/integration_tests/saved_objects/migrations/group3/migration_from_older_v1.test.ts @@ -15,6 +15,7 @@ import { Env } from '@kbn/config'; import { getEnvOptions } from '@kbn/config-mocks'; import { ElasticsearchClient } from '@kbn/core-elasticsearch-server'; import type { SavedObjectsRawDoc } from '@kbn/core-saved-objects-server'; +import { modelVersionToVirtualVersion } from '@kbn/core-saved-objects-base-server-internal'; import { createTestServers, createRootWithCorePlugins, @@ -125,7 +126,7 @@ describe('migrating from 7.3.0-xpack which used v1 migrations', () => { .getTypeRegistry() .getAllTypes() .reduce((versionMap, type) => { - const { name, migrations, convertToMultiNamespaceTypeVersion } = type; + const { name, migrations, convertToMultiNamespaceTypeVersion, modelVersions } = type; if (migrations || convertToMultiNamespaceTypeVersion) { const migrationsMap = typeof migrations === 'function' ? migrations() : migrations; const migrationsKeys = migrationsMap ? Object.keys(migrationsMap) : []; @@ -133,6 +134,16 @@ describe('migrating from 7.3.0-xpack which used v1 migrations', () => { // Setting this option registers a conversion migration that is reflected in the object's `typeMigrationVersions` field migrationsKeys.push(convertToMultiNamespaceTypeVersion); } + + const modelVersionCreateSchemas = + typeof modelVersions === 'function' ? modelVersions() : modelVersions ?? {}; + + Object.entries(modelVersionCreateSchemas).forEach(([key, modelVersion]) => { + if (modelVersion.schemas?.create) { + migrationsKeys.push(modelVersionToVirtualVersion(key)); + } + }); + const highestVersion = migrationsKeys.sort(Semver.compare).reverse()[0]; return { ...versionMap, diff --git a/src/dev/storybook/aliases.ts b/src/dev/storybook/aliases.ts index cc8b46c136aa29..1bec4f797e2fda 100644 --- a/src/dev/storybook/aliases.ts +++ b/src/dev/storybook/aliases.ts @@ -41,7 +41,7 @@ export const storybookAliases = { expression_shape: 'src/plugins/expression_shape/.storybook', expression_tagcloud: 'src/plugins/chart_expressions/expression_tagcloud/.storybook', fleet: 'x-pack/plugins/fleet/.storybook', - grouping: 'packages/kbn-securitysolution-grouping/.storybook', + grouping: 'packages/kbn-grouping/.storybook', home: 'src/plugins/home/.storybook', infra: 'x-pack/plugins/observability_solution/infra/.storybook', kibana_react: 'src/plugins/kibana_react/.storybook', diff --git a/src/plugins/chart_expressions/expression_xy/public/components/xy_chart.tsx b/src/plugins/chart_expressions/expression_xy/public/components/xy_chart.tsx index ed4f6ffad6a73d..660e2c95bed2f1 100644 --- a/src/plugins/chart_expressions/expression_xy/public/components/xy_chart.tsx +++ b/src/plugins/chart_expressions/expression_xy/public/components/xy_chart.tsx @@ -37,6 +37,7 @@ import { IconType } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { PaletteRegistry } from '@kbn/coloring'; import { RenderMode } from '@kbn/expressions-plugin/common'; +import { ESQL_TABLE_TYPE } from '@kbn/data-plugin/common'; import type { DataPublicPluginStart } from '@kbn/data-plugin/public'; import { EmptyPlaceholder, LegendToggle } from '@kbn/charts-plugin/public'; import { EventAnnotationServiceType } from '@kbn/event-annotation-plugin/public'; @@ -403,6 +404,7 @@ export function XYChart({ const defaultXScaleType = isTimeViz ? XScaleTypes.TIME : XScaleTypes.ORDINAL; const isHistogramViz = dataLayers.every((l) => l.isHistogram); + const isEsqlMode = dataLayers.some((l) => l.table?.meta?.type === ESQL_TABLE_TYPE); const hasBars = dataLayers.some((l) => l.seriesType === SeriesTypes.BAR); const { baseDomain: rawXDomain, extendedDomain: xDomain } = getXDomain( @@ -651,7 +653,12 @@ export function XYChart({ : undefined; const xAxisColumnIndex = table.columns.findIndex((el) => el.id === xAccessor); - const context: BrushEvent['data'] = { range: [min, max], table, column: xAxisColumnIndex }; + const context: BrushEvent['data'] = { + range: [min, max], + table, + column: xAxisColumnIndex, + ...(isEsqlMode ? { timeFieldName: table.columns[xAxisColumnIndex].name } : {}), + }; onSelectRange(context); }; @@ -779,7 +786,7 @@ export function XYChart({ formattedDatatables, xAxisFormatter, formatFactory, - interactive && !args.detailedTooltip + interactive && !args.detailedTooltip && !isEsqlMode )} customTooltip={ args.detailedTooltip @@ -855,8 +862,9 @@ export function XYChart({ allowBrushingLastHistogramBin={isTimeViz} rotation={shouldRotate ? 90 : 0} xDomain={xDomain} + // enable brushing only for time charts, for both ES|QL and DSL queries onBrushEnd={interactive ? (brushHandler as BrushEndListener) : undefined} - onElementClick={interactive ? clickHandler : undefined} + onElementClick={interactive && !isEsqlMode ? clickHandler : undefined} legendAction={ interactive ? getLegendAction( diff --git a/src/plugins/data/common/search/expressions/esql.ts b/src/plugins/data/common/search/expressions/esql.ts index 6c3929f2014583..a4d546f349f37c 100644 --- a/src/plugins/data/common/search/expressions/esql.ts +++ b/src/plugins/data/common/search/expressions/esql.ts @@ -22,7 +22,7 @@ import type { ESQLSearchReponse, ESQLSearchParams } from '@kbn/es-types'; import { ESQL_LATEST_VERSION } from '@kbn/esql-utils'; import { getEsQueryConfig } from '../../es_query'; import { getTime } from '../../query'; -import { ESQL_ASYNC_SEARCH_STRATEGY, KibanaContext } from '..'; +import { ESQL_ASYNC_SEARCH_STRATEGY, KibanaContext, ESQL_TABLE_TYPE } from '..'; import { UiSettingsCommon } from '../..'; type Input = KibanaContext | null; @@ -270,7 +270,7 @@ export const getEsqlFn = ({ getStartDependencies }: EsqlFnArguments) => { return { type: 'datatable', meta: { - type: 'es_ql', + type: ESQL_TABLE_TYPE, }, columns: allColumns, rows, diff --git a/src/plugins/data/common/search/strategies/esql_search/types.ts b/src/plugins/data/common/search/strategies/esql_search/types.ts index 7d69e8379a2504..0ceb71db9d4962 100644 --- a/src/plugins/data/common/search/strategies/esql_search/types.ts +++ b/src/plugins/data/common/search/strategies/esql_search/types.ts @@ -8,3 +8,4 @@ export const ESQL_SEARCH_STRATEGY = 'esql'; export const ESQL_ASYNC_SEARCH_STRATEGY = 'esql_async'; +export const ESQL_TABLE_TYPE = 'es_ql'; diff --git a/src/plugins/data/public/actions/filters/create_filters_from_range_select.test.ts b/src/plugins/data/public/actions/filters/create_filters_from_range_select.test.ts index bee22e07ab1443..74cdb28d7400cd 100644 --- a/src/plugins/data/public/actions/filters/create_filters_from_range_select.test.ts +++ b/src/plugins/data/public/actions/filters/create_filters_from_range_select.test.ts @@ -8,7 +8,10 @@ import moment from 'moment'; -import { createFiltersFromRangeSelectAction } from './create_filters_from_range_select'; +import { + createFiltersFromRangeSelectAction, + type RangeSelectDataContext, +} from './create_filters_from_range_select'; import { DataViewsContract } from '@kbn/data-views-plugin/common'; import { dataPluginMock } from '../../mocks'; @@ -20,12 +23,8 @@ import { RangeFilter } from '@kbn/es-query'; describe('brushEvent', () => { const DAY_IN_MS = 24 * 60 * 60 * 1000; const JAN_01_2014 = 1388559600000; - let baseEvent: { - table: any; - column: number; - range: number[]; - timeFieldName?: string; - }; + let baseEvent: RangeSelectDataContext; + let esqlEventContext: RangeSelectDataContext; const mockField = { name: 'time', @@ -82,6 +81,28 @@ describe('brushEvent', () => { }, range: [], }; + + esqlEventContext = { + column: 0, + query: { esql: 'FROM indexPatternId | limit 10' }, + table: { + type: 'datatable', + meta: { + type: 'es_ql', + }, + columns: [ + { + id: '1', + name: '1', + meta: { + type: 'date', + }, + }, + ], + rows: [], + }, + range: [], + }; }); test('should be a function', () => { @@ -197,4 +218,33 @@ describe('brushEvent', () => { } }); }); + + describe('handles an event for an ES_QL query', () => { + afterAll(() => { + esqlEventContext.range = []; + }); + + test('by ignoring the event when range does not span at least 2 values', async () => { + esqlEventContext.range = [JAN_01_2014]; + const filter = await createFiltersFromRangeSelectAction(esqlEventContext); + expect(filter).toEqual([]); + }); + + test('by creating a new filter', async () => { + const rangeBegin = JAN_01_2014; + const rangeEnd = rangeBegin + DAY_IN_MS; + esqlEventContext.range = [rangeBegin, rangeEnd]; + const filter = await createFiltersFromRangeSelectAction(esqlEventContext); + + expect(filter).toBeDefined(); + + if (filter.length) { + const rangeFilter = filter[0] as RangeFilter; + expect(rangeFilter.meta.index).toBeUndefined(); + expect(rangeFilter.query.range['1'].gte).toBe(moment(rangeBegin).toISOString()); + expect(rangeFilter.query.range['1'].lt).toBe(moment(rangeEnd).toISOString()); + expect(rangeFilter.query.range['1']).toHaveProperty('format', 'strict_date_optional_time'); + } + }); + }); }); diff --git a/src/plugins/data/public/actions/filters/create_filters_from_range_select.ts b/src/plugins/data/public/actions/filters/create_filters_from_range_select.ts index c3acc8b2a5106a..c5a6d86e20fb29 100644 --- a/src/plugins/data/public/actions/filters/create_filters_from_range_select.ts +++ b/src/plugins/data/public/actions/filters/create_filters_from_range_select.ts @@ -9,18 +9,57 @@ import { last } from 'lodash'; import moment from 'moment'; import { Datatable } from '@kbn/expressions-plugin/common'; +import { type AggregateQuery, isOfAggregateQueryType } from '@kbn/es-query'; +import { DataViewField } from '@kbn/data-views-plugin/public'; import { buildRangeFilter, DataViewFieldBase, RangeFilterParams } from '@kbn/es-query'; import { getIndexPatterns, getSearchService } from '../../services'; import { AggConfigSerialized } from '../../../common/search/aggs'; import { mapAndFlattenFilters } from '../../query'; -interface RangeSelectDataContext { +export interface RangeSelectDataContext { table: Datatable; column: number; range: number[]; timeFieldName?: string; + query?: AggregateQuery; } +const getParameters = async (event: RangeSelectDataContext) => { + const column: Record = event.table.columns[event.column]; + // Handling of the ES|QL datatable + if (isOfAggregateQueryType(event.query)) { + const field = new DataViewField({ + name: column.name, + type: column.meta?.type ?? 'unknown', + esTypes: column.meta?.esType ? ([column.meta.esType] as string[]) : undefined, + searchable: true, + aggregatable: false, + }); + + return { + field, + indexPattern: undefined, + }; + } + if (column.meta && 'sourceParams' in column.meta) { + const { indexPatternId, ...aggConfigs } = column.meta.sourceParams; + const indexPattern = await getIndexPatterns().get(indexPatternId); + const aggConfigsInstance = getSearchService().aggs.createAggConfigs(indexPattern, [ + aggConfigs as AggConfigSerialized, + ]); + const aggConfig = aggConfigsInstance.aggs[0]; + const field: DataViewFieldBase = aggConfig.params.field; + return { + field, + indexPattern, + }; + } + return { + field: undefined, + indexPattern: undefined, + }; +}; + export async function createFiltersFromRangeSelectAction(event: RangeSelectDataContext) { const column: Record = event.table.columns[event.column]; @@ -28,13 +67,7 @@ export async function createFiltersFromRangeSelectAction(event: RangeSelectDataC return []; } - const { indexPatternId, ...aggConfigs } = column.meta.sourceParams; - const indexPattern = await getIndexPatterns().get(indexPatternId); - const aggConfigsInstance = getSearchService().aggs.createAggConfigs(indexPattern, [ - aggConfigs as AggConfigSerialized, - ]); - const aggConfig = aggConfigsInstance.aggs[0]; - const field: DataViewFieldBase = aggConfig.params.field; + const { field, indexPattern } = await getParameters(event); if (!field || event.range.length <= 1) { return []; @@ -57,6 +90,5 @@ export async function createFiltersFromRangeSelectAction(event: RangeSelectDataC if (isDate) { range.format = 'strict_date_optional_time'; } - return mapAndFlattenFilters([buildRangeFilter(field, range, indexPattern)]); } diff --git a/src/plugins/data/public/actions/select_range_action.ts b/src/plugins/data/public/actions/select_range_action.ts index 921273ff4abd94..e8a65b049fd442 100644 --- a/src/plugins/data/public/actions/select_range_action.ts +++ b/src/plugins/data/public/actions/select_range_action.ts @@ -6,6 +6,7 @@ * Side Public License, v 1. */ +import type { AggregateQuery } from '@kbn/es-query'; import { Datatable } from '@kbn/expressions-plugin/public'; import { UiActionsActionDefinition, UiActionsStart } from '@kbn/ui-actions-plugin/public'; import { APPLY_FILTER_TRIGGER } from '../triggers'; @@ -20,6 +21,7 @@ export interface SelectRangeActionContext { column: number; range: number[]; timeFieldName?: string; + query?: AggregateQuery; }; } diff --git a/src/plugins/discover/public/application/main/components/layout/discover_histogram_layout.tsx b/src/plugins/discover/public/application/main/components/layout/discover_histogram_layout.tsx index 68585d7faf5c0d..1d24fdcd1aa814 100644 --- a/src/plugins/discover/public/application/main/components/layout/discover_histogram_layout.tsx +++ b/src/plugins/discover/public/application/main/components/layout/discover_histogram_layout.tsx @@ -10,6 +10,7 @@ import React, { useCallback, useMemo } from 'react'; import { UnifiedHistogramContainer } from '@kbn/unified-histogram-plugin/public'; import { css } from '@emotion/react'; import useObservable from 'react-use/lib/useObservable'; +import { ESQL_TABLE_TYPE } from '@kbn/data-plugin/common'; import type { Datatable } from '@kbn/expressions-plugin/common'; import { useDiscoverHistogram } from './use_discover_histogram'; import { type DiscoverMainContentProps, DiscoverMainContent } from './discover_main_content'; @@ -61,6 +62,9 @@ export const DiscoverHistogramLayout = ({ type: 'datatable' as 'datatable', rows: datatable.result!.map((r) => r.raw), columns: datatable.esqlQueryColumns || [], + meta: { + type: ESQL_TABLE_TYPE, + }, }; } }, [datatable, isEsqlMode]); diff --git a/src/plugins/discover/public/application/main/hooks/use_esql_mode.ts b/src/plugins/discover/public/application/main/hooks/use_esql_mode.ts index a907b1e796c873..2f3c99763fb047 100644 --- a/src/plugins/discover/public/application/main/hooks/use_esql_mode.ts +++ b/src/plugins/discover/public/application/main/hooks/use_esql_mode.ts @@ -8,6 +8,7 @@ import { isEqual } from 'lodash'; import { isOfAggregateQueryType, getAggregateQueryMode } from '@kbn/es-query'; +import { hasTransformationalCommand } from '@kbn/esql-utils'; import { useCallback, useEffect, useRef } from 'react'; import type { DataViewsContract } from '@kbn/data-views-plugin/public'; import { switchMap } from 'rxjs'; @@ -17,9 +18,6 @@ import { getValidViewMode } from '../utils/get_valid_view_mode'; import { FetchStatus } from '../../types'; const MAX_NUM_OF_COLUMNS = 50; -// For ES|QL we want in case of the following commands to display a table view, otherwise display a document view -const TRANSFORMATIONAL_COMMANDS = ['stats', 'keep']; - /** * Hook to take care of ES|QL state transformations when a new result is returned * If necessary this is setting displayed columns and selected data view @@ -71,12 +69,9 @@ export function useEsqlMode({ const hasResults = Boolean(next.result?.length); let queryHasTransformationalCommands = false; if ('esql' in query) { - TRANSFORMATIONAL_COMMANDS.forEach((command: string) => { - if (query.esql.toLowerCase().includes(command)) { - queryHasTransformationalCommands = true; - return; - } - }); + if (hasTransformationalCommand(query.esql)) { + queryHasTransformationalCommands = true; + } } if (isEsqlQuery) { diff --git a/src/plugins/kibana_utils/common/calculate_object_hash.test.ts b/src/plugins/kibana_utils/common/calculate_object_hash.test.ts new file mode 100644 index 00000000000000..3b11210b2e5665 --- /dev/null +++ b/src/plugins/kibana_utils/common/calculate_object_hash.test.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 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 { calculateObjectHash } from './calculate_object_hash'; + +describe('calculateObjectHash', () => { + test('calculates hash of the object', () => { + const object = { test: 123 }; + + expect(calculateObjectHash(object)).toEqual('5094c3dc'); + }); + + test('ignore inner props of index object expect for the value.id', () => { + const object1 = { test: 123, index: { value: { id: 'test', otherprop: 1 } } }; + const object2 = { test: 123, index: { value: { id: 'test', otherprop: 2 } } }; + + expect(calculateObjectHash(object1)).toEqual(calculateObjectHash(object2)); + }); +}); diff --git a/src/plugins/kibana_utils/common/calculate_object_hash.ts b/src/plugins/kibana_utils/common/calculate_object_hash.ts index dcd24b1f9f0743..9cf7266b1521d8 100644 --- a/src/plugins/kibana_utils/common/calculate_object_hash.ts +++ b/src/plugins/kibana_utils/common/calculate_object_hash.ts @@ -53,6 +53,9 @@ function foldValue(input: number, value: any, key: string, seen: any[]) { if (key === 'vis' && value.constructor.name === 'Vis') { return hash; } + if (key === 'index' && value.value?.id) { + return fold(hash, value.value.id); + } if (seen.indexOf(value) !== -1) { return fold(hash, '[Circular]' + key); } diff --git a/src/plugins/share/server/url_service/http/short_urls/register_create_route.ts b/src/plugins/share/server/url_service/http/short_urls/register_create_route.ts index 97594837f0720e..0848fdfbfe6051 100644 --- a/src/plugins/share/server/url_service/http/short_urls/register_create_route.ts +++ b/src/plugins/share/server/url_service/http/short_urls/register_create_route.ts @@ -15,6 +15,10 @@ export const registerCreateRoute = (router: IRouter, url: ServerUrlService) => { router.post( { path: '/api/short_url', + options: { + access: 'public', + description: `Create a short URL`, + }, validate: { body: schema.object({ locatorId: schema.string({ diff --git a/src/plugins/share/server/url_service/http/short_urls/register_delete_route.ts b/src/plugins/share/server/url_service/http/short_urls/register_delete_route.ts index ddc29117a3accf..258faff0d04a68 100644 --- a/src/plugins/share/server/url_service/http/short_urls/register_delete_route.ts +++ b/src/plugins/share/server/url_service/http/short_urls/register_delete_route.ts @@ -14,6 +14,10 @@ export const registerDeleteRoute = (router: IRouter, url: ServerUrlService) => { router.delete( { path: '/api/short_url/{id}', + options: { + access: 'public', + description: `Delete a short URL`, + }, validate: { params: schema.object({ id: schema.string({ diff --git a/src/plugins/share/server/url_service/http/short_urls/register_get_route.ts b/src/plugins/share/server/url_service/http/short_urls/register_get_route.ts index 8e783b3fcfd3de..ae108ccd11d973 100644 --- a/src/plugins/share/server/url_service/http/short_urls/register_get_route.ts +++ b/src/plugins/share/server/url_service/http/short_urls/register_get_route.ts @@ -14,6 +14,10 @@ export const registerGetRoute = (router: IRouter, url: ServerUrlService) => { router.get( { path: '/api/short_url/{id}', + options: { + access: 'public', + description: `Get a short URL`, + }, validate: { params: schema.object({ id: schema.string({ diff --git a/src/plugins/share/server/url_service/http/short_urls/register_resolve_route.ts b/src/plugins/share/server/url_service/http/short_urls/register_resolve_route.ts index 47290d5754545a..6076889945f366 100644 --- a/src/plugins/share/server/url_service/http/short_urls/register_resolve_route.ts +++ b/src/plugins/share/server/url_service/http/short_urls/register_resolve_route.ts @@ -15,6 +15,10 @@ export const registerResolveRoute = (router: IRouter, url: ServerUrlService) => router.get( { path: '/api/short_url/_slug/{slug}', + options: { + access: 'public', + description: `Resolve a short URL`, + }, validate: { params: schema.object({ slug: schema.string({ diff --git a/src/plugins/unified_histogram/public/layout/helpers.ts b/src/plugins/unified_histogram/public/layout/helpers.ts index 0f6e898163779e..d65ddb7763d280 100644 --- a/src/plugins/unified_histogram/public/layout/helpers.ts +++ b/src/plugins/unified_histogram/public/layout/helpers.ts @@ -7,9 +7,8 @@ */ import { AggregateQuery } from '@kbn/es-query'; - -const TRANSFORMATIONAL_COMMANDS = ['stats', 'keep']; +import { hasTransformationalCommand } from '@kbn/esql-utils'; export const shouldDisplayHistogram = (query: AggregateQuery) => { - return !TRANSFORMATIONAL_COMMANDS.some((command) => query.esql.toLowerCase().includes(command)); + return !hasTransformationalCommand(query.esql); }; diff --git a/test/plugin_functional/test_suites/core_plugins/rendering.ts b/test/plugin_functional/test_suites/core_plugins/rendering.ts index 62cffabc1b82c6..25388c7829edd4 100644 --- a/test/plugin_functional/test_suites/core_plugins/rendering.ts +++ b/test/plugin_functional/test_suites/core_plugins/rendering.ts @@ -293,6 +293,7 @@ export default function ({ getService }: PluginFunctionalProviderContext) { 'xpack.index_management.editableIndexSettings (any)', 'xpack.index_management.enableDataStreamsStorageColumn (any)', 'xpack.index_management.enableMappingsSourceFieldSection (any)', + 'xpack.index_management.dev.enableSemanticText (boolean)', 'xpack.license_management.ui.enabled (boolean)', 'xpack.maps.preserveDrawingBuffer (boolean)', 'xpack.maps.showMapsInspectorAdapter (boolean)', diff --git a/tsconfig.base.json b/tsconfig.base.json index 23c8774271af98..923d6dcb38c397 100644 --- a/tsconfig.base.json +++ b/tsconfig.base.json @@ -36,6 +36,8 @@ "@kbn/alerting-api-integration-helpers/*": ["x-pack/test/alerting_api_integration/packages/helpers/*"], "@kbn/alerting-api-integration-test-plugin": ["x-pack/test/alerting_api_integration/common/plugins/alerts"], "@kbn/alerting-api-integration-test-plugin/*": ["x-pack/test/alerting_api_integration/common/plugins/alerts/*"], + "@kbn/alerting-comparators": ["x-pack/packages/kbn-alerting-comparators"], + "@kbn/alerting-comparators/*": ["x-pack/packages/kbn-alerting-comparators/*"], "@kbn/alerting-example-plugin": ["x-pack/examples/alerting_example"], "@kbn/alerting-example-plugin/*": ["x-pack/examples/alerting_example/*"], "@kbn/alerting-fixture-plugin": ["x-pack/test/functional_with_es_ssl/plugins/alerts"], @@ -934,6 +936,8 @@ "@kbn/graph-plugin/*": ["x-pack/plugins/graph/*"], "@kbn/grokdebugger-plugin": ["x-pack/plugins/grokdebugger"], "@kbn/grokdebugger-plugin/*": ["x-pack/plugins/grokdebugger/*"], + "@kbn/grouping": ["packages/kbn-grouping"], + "@kbn/grouping/*": ["packages/kbn-grouping/*"], "@kbn/guided-onboarding": ["packages/kbn-guided-onboarding"], "@kbn/guided-onboarding/*": ["packages/kbn-guided-onboarding/*"], "@kbn/guided-onboarding-example-plugin": ["examples/guided_onboarding_example"], @@ -1464,8 +1468,6 @@ "@kbn/securitysolution-es-utils/*": ["packages/kbn-securitysolution-es-utils/*"], "@kbn/securitysolution-exception-list-components": ["packages/kbn-securitysolution-exception-list-components"], "@kbn/securitysolution-exception-list-components/*": ["packages/kbn-securitysolution-exception-list-components/*"], - "@kbn/securitysolution-grouping": ["packages/kbn-securitysolution-grouping"], - "@kbn/securitysolution-grouping/*": ["packages/kbn-securitysolution-grouping/*"], "@kbn/securitysolution-hook-utils": ["packages/kbn-securitysolution-hook-utils"], "@kbn/securitysolution-hook-utils/*": ["packages/kbn-securitysolution-hook-utils/*"], "@kbn/securitysolution-io-ts-alerting-types": ["packages/kbn-securitysolution-io-ts-alerting-types"], diff --git a/x-pack/packages/kbn-alerting-comparators/README.md b/x-pack/packages/kbn-alerting-comparators/README.md new file mode 100644 index 00000000000000..b3b6c0a02fe764 --- /dev/null +++ b/x-pack/packages/kbn-alerting-comparators/README.md @@ -0,0 +1,5 @@ +# @kbn/alerting-comparators + +Contains type information and enum for the alerting rule comparators. e.g. >, < + +This comparators are used extensively in Observability UI and server side. Also, in the triggers-actions-ui in some related UI like ThresholdExpression. \ No newline at end of file diff --git a/x-pack/packages/kbn-alerting-comparators/index.ts b/x-pack/packages/kbn-alerting-comparators/index.ts new file mode 100644 index 00000000000000..148e0bad18141a --- /dev/null +++ b/x-pack/packages/kbn-alerting-comparators/index.ts @@ -0,0 +1,7 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ +export * from './src/comparators'; diff --git a/x-pack/plugins/observability_solution/slo/public/embeddable/slo/alerts/index.ts b/x-pack/packages/kbn-alerting-comparators/jest.config.js similarity index 61% rename from x-pack/plugins/observability_solution/slo/public/embeddable/slo/alerts/index.ts rename to x-pack/packages/kbn-alerting-comparators/jest.config.js index 98d46e9ebfdf8a..9f1da3b8875aa0 100644 --- a/x-pack/plugins/observability_solution/slo/public/embeddable/slo/alerts/index.ts +++ b/x-pack/packages/kbn-alerting-comparators/jest.config.js @@ -5,5 +5,8 @@ * 2.0. */ -export { SloAlertsEmbeddableFactoryDefinition } from './slo_alerts_embeddable_factory'; -export type { SLOAlertsEmbeddable } from './slo_alerts_embeddable'; +module.exports = { + preset: '@kbn/test/jest_node', + rootDir: '../../..', + roots: ['/x-pack/packages/kbn-alerting-comparators'], +}; diff --git a/x-pack/packages/kbn-alerting-comparators/kibana.jsonc b/x-pack/packages/kbn-alerting-comparators/kibana.jsonc new file mode 100644 index 00000000000000..94ac1e532ab1fa --- /dev/null +++ b/x-pack/packages/kbn-alerting-comparators/kibana.jsonc @@ -0,0 +1,5 @@ +{ + "type": "shared-common", + "id": "@kbn/alerting-comparators", + "owner": "@elastic/response-ops" +} \ No newline at end of file diff --git a/x-pack/packages/kbn-alerting-comparators/package.json b/x-pack/packages/kbn-alerting-comparators/package.json new file mode 100644 index 00000000000000..d202ae5e6d75be --- /dev/null +++ b/x-pack/packages/kbn-alerting-comparators/package.json @@ -0,0 +1,6 @@ +{ + "name": "@kbn/alerting-comparators", + "private": true, + "version": "1.0.0", + "license": "Elastic License 2.0" +} diff --git a/x-pack/packages/kbn-alerting-comparators/src/comparators.ts b/x-pack/packages/kbn-alerting-comparators/src/comparators.ts new file mode 100644 index 00000000000000..0568fa204df6ce --- /dev/null +++ b/x-pack/packages/kbn-alerting-comparators/src/comparators.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. + */ + +export interface Comparator { + text: string; + value: string; + requiredValues: number; +} +export enum COMPARATORS { + GREATER_THAN = '>', + GREATER_THAN_OR_EQUALS = '>=', + BETWEEN = 'between', + LESS_THAN = '<', + LESS_THAN_OR_EQUALS = '<=', + NOT_BETWEEN = 'notBetween', +} diff --git a/x-pack/packages/kbn-alerting-comparators/tsconfig.json b/x-pack/packages/kbn-alerting-comparators/tsconfig.json new file mode 100644 index 00000000000000..196ffd23d93cd8 --- /dev/null +++ b/x-pack/packages/kbn-alerting-comparators/tsconfig.json @@ -0,0 +1,18 @@ +{ + "extends": "../../../tsconfig.base.json", + "compilerOptions": { + "outDir": "target/types", + "types": [ + "jest", + "node" + ] + }, + "include": [ + "**/*.ts", + ], + "exclude": [ + "target/**/*" + ], + "kbn_references": [ + ] +} diff --git a/x-pack/packages/kbn-slo-schema/src/schema/slo.ts b/x-pack/packages/kbn-slo-schema/src/schema/slo.ts index 692ec160503537..dcf18d0e3a82e3 100644 --- a/x-pack/packages/kbn-slo-schema/src/schema/slo.ts +++ b/x-pack/packages/kbn-slo-schema/src/schema/slo.ts @@ -29,6 +29,7 @@ const objectiveSchema = t.intersection([ const settingsSchema = t.type({ syncDelay: durationType, frequency: durationType, + preventInitialBackfill: t.boolean, }); const groupBySchema = allOrAnyStringOrArray; diff --git a/x-pack/packages/observability/alerting_test_data/src/create_custom_threshold_rule.ts b/x-pack/packages/observability/alerting_test_data/src/create_custom_threshold_rule.ts index 7ad732af36948c..1cd65cc9e74155 100644 --- a/x-pack/packages/observability/alerting_test_data/src/create_custom_threshold_rule.ts +++ b/x-pack/packages/observability/alerting_test_data/src/create_custom_threshold_rule.ts @@ -5,10 +5,8 @@ * 2.0. */ -import { - Aggregators, - Comparator, -} from '@kbn/observability-plugin/common/custom_threshold_rule/types'; +import { Aggregators } from '@kbn/observability-plugin/common/custom_threshold_rule/types'; +import { COMPARATORS } from '@kbn/alerting-comparators'; import { OBSERVABILITY_THRESHOLD_RULE_TYPE_ID } from '@kbn/rule-data-utils'; import { FIRED_ACTIONS_ID } from './constants'; import { createRule } from './create_rule'; @@ -38,7 +36,7 @@ export const createCustomThresholdRule = async ( params: { criteria: ruleParams.params?.criteria || [ { - comparator: Comparator.GT, + comparator: COMPARATORS.GREATER_THAN, threshold: [1], timeSize: 1, timeUnit: 'm', diff --git a/x-pack/packages/observability/alerting_test_data/src/scenarios/custom_threshold_log_count.ts b/x-pack/packages/observability/alerting_test_data/src/scenarios/custom_threshold_log_count.ts index be2f2e344d7a5d..da3d03218dd809 100644 --- a/x-pack/packages/observability/alerting_test_data/src/scenarios/custom_threshold_log_count.ts +++ b/x-pack/packages/observability/alerting_test_data/src/scenarios/custom_threshold_log_count.ts @@ -5,10 +5,8 @@ * 2.0. */ -import { - Aggregators, - Comparator, -} from '@kbn/observability-plugin/common/custom_threshold_rule/types'; +import { COMPARATORS } from '@kbn/alerting-comparators'; +import { Aggregators } from '@kbn/observability-plugin/common/custom_threshold_rule/types'; export const scenario1 = { dataView: { @@ -22,7 +20,7 @@ export const scenario1 = { params: { criteria: [ { - comparator: Comparator.LT, + comparator: COMPARATORS.LESS_THAN, threshold: [100], timeSize: 1, timeUnit: 'm', diff --git a/x-pack/packages/observability/alerting_test_data/src/scenarios/custom_threshold_log_count_groupby.ts b/x-pack/packages/observability/alerting_test_data/src/scenarios/custom_threshold_log_count_groupby.ts index fe8e2cfa01d326..bc8c7c5d2e3106 100644 --- a/x-pack/packages/observability/alerting_test_data/src/scenarios/custom_threshold_log_count_groupby.ts +++ b/x-pack/packages/observability/alerting_test_data/src/scenarios/custom_threshold_log_count_groupby.ts @@ -5,10 +5,8 @@ * 2.0. */ -import { - Aggregators, - Comparator, -} from '@kbn/observability-plugin/common/custom_threshold_rule/types'; +import { Aggregators } from '@kbn/observability-plugin/common/custom_threshold_rule/types'; +import { COMPARATORS } from '@kbn/alerting-comparators'; export const scenario2 = { dataView: { @@ -22,7 +20,7 @@ export const scenario2 = { params: { criteria: [ { - comparator: Comparator.LT, + comparator: COMPARATORS.LESS_THAN, threshold: [40], timeSize: 1, timeUnit: 'm', diff --git a/x-pack/packages/observability/alerting_test_data/src/scenarios/custom_threshold_log_count_nodata.ts b/x-pack/packages/observability/alerting_test_data/src/scenarios/custom_threshold_log_count_nodata.ts index bbb4d4e1c25514..3131c04a126b70 100644 --- a/x-pack/packages/observability/alerting_test_data/src/scenarios/custom_threshold_log_count_nodata.ts +++ b/x-pack/packages/observability/alerting_test_data/src/scenarios/custom_threshold_log_count_nodata.ts @@ -5,11 +5,8 @@ * 2.0. */ -import { - Aggregators, - Comparator, -} from '@kbn/observability-plugin/common/custom_threshold_rule/types'; - +import { Aggregators } from '@kbn/observability-plugin/common/custom_threshold_rule/types'; +import { COMPARATORS } from '@kbn/alerting-comparators'; export const scenario3 = { dataView: { indexPattern: 'high-cardinality-data-fake_hosts.fake_hosts-*', @@ -22,7 +19,7 @@ export const scenario3 = { params: { criteria: [ { - comparator: Comparator.LT, + comparator: COMPARATORS.LESS_THAN, threshold: [5], timeSize: 1, timeUnit: 'm', diff --git a/x-pack/packages/observability/alerting_test_data/src/scenarios/custom_threshold_metric_avg.ts b/x-pack/packages/observability/alerting_test_data/src/scenarios/custom_threshold_metric_avg.ts index dcf6048915ebf9..82cb220678ec6a 100644 --- a/x-pack/packages/observability/alerting_test_data/src/scenarios/custom_threshold_metric_avg.ts +++ b/x-pack/packages/observability/alerting_test_data/src/scenarios/custom_threshold_metric_avg.ts @@ -5,10 +5,8 @@ * 2.0. */ -import { - Aggregators, - Comparator, -} from '@kbn/observability-plugin/common/custom_threshold_rule/types'; +import { Aggregators } from '@kbn/observability-plugin/common/custom_threshold_rule/types'; +import { COMPARATORS } from '@kbn/alerting-comparators'; export const scenario4 = { dataView: { @@ -22,7 +20,7 @@ export const scenario4 = { params: { criteria: [ { - comparator: Comparator.GT, + comparator: COMPARATORS.GREATER_THAN, threshold: [80], timeSize: 1, timeUnit: 'm', diff --git a/x-pack/packages/observability/alerting_test_data/src/scenarios/custom_threshold_metric_avg_groupby.ts b/x-pack/packages/observability/alerting_test_data/src/scenarios/custom_threshold_metric_avg_groupby.ts index b42006a33a652d..5ef69a10dc654a 100644 --- a/x-pack/packages/observability/alerting_test_data/src/scenarios/custom_threshold_metric_avg_groupby.ts +++ b/x-pack/packages/observability/alerting_test_data/src/scenarios/custom_threshold_metric_avg_groupby.ts @@ -5,10 +5,8 @@ * 2.0. */ -import { - Aggregators, - Comparator, -} from '@kbn/observability-plugin/common/custom_threshold_rule/types'; +import { Aggregators } from '@kbn/observability-plugin/common/custom_threshold_rule/types'; +import { COMPARATORS } from '@kbn/alerting-comparators'; export const scenario5 = { dataView: { @@ -22,7 +20,7 @@ export const scenario5 = { params: { criteria: [ { - comparator: Comparator.GT, + comparator: COMPARATORS.GREATER_THAN, threshold: [80], timeSize: 5, timeUnit: 'm', diff --git a/x-pack/packages/observability/alerting_test_data/src/scenarios/custom_threshold_metric_avg_nodata.ts b/x-pack/packages/observability/alerting_test_data/src/scenarios/custom_threshold_metric_avg_nodata.ts index 9bea0a00eb7146..2a92d6f8440f24 100644 --- a/x-pack/packages/observability/alerting_test_data/src/scenarios/custom_threshold_metric_avg_nodata.ts +++ b/x-pack/packages/observability/alerting_test_data/src/scenarios/custom_threshold_metric_avg_nodata.ts @@ -5,10 +5,8 @@ * 2.0. */ -import { - Aggregators, - Comparator, -} from '@kbn/observability-plugin/common/custom_threshold_rule/types'; +import { Aggregators } from '@kbn/observability-plugin/common/custom_threshold_rule/types'; +import { COMPARATORS } from '@kbn/alerting-comparators'; export const scenario6 = { dataView: { @@ -22,7 +20,7 @@ export const scenario6 = { params: { criteria: [ { - comparator: Comparator.LT, + comparator: COMPARATORS.LESS_THAN, threshold: [1], timeSize: 1, timeUnit: 'm', diff --git a/x-pack/packages/observability/alerting_test_data/tsconfig.json b/x-pack/packages/observability/alerting_test_data/tsconfig.json index d2a9a55a887de5..109b0dfbcf1a48 100644 --- a/x-pack/packages/observability/alerting_test_data/tsconfig.json +++ b/x-pack/packages/observability/alerting_test_data/tsconfig.json @@ -18,5 +18,6 @@ "kbn_references": [ "@kbn/observability-plugin", "@kbn/rule-data-utils", + "@kbn/alerting-comparators", ] } 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 36b00b1d4b6ef1..82e8663bd6bf8d 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 @@ -378,7 +378,7 @@ describe('AlertingEventLogger', () => { saved_objects: [ // @ts-ignore ...event.kibana?.saved_objects, - { id: 'bbb', type: 'alert', type_id: 'test' }, + { id: 'bbb', type: 'alert', type_id: 'test', rel: 'primary' }, ], }, }); @@ -472,7 +472,7 @@ describe('AlertingEventLogger', () => { saved_objects: [ // @ts-ignore ...event.kibana?.saved_objects, - { id: 'bbb', type: 'alert', type_id: 'test' }, + { id: 'bbb', type: 'alert', type_id: 'test', rel: 'primary' }, ], }, }); @@ -567,7 +567,7 @@ describe('AlertingEventLogger', () => { saved_objects: [ // @ts-ignore ...event.kibana?.saved_objects, - { id: 'bbb', type: 'alert', type_id: 'test' }, + { id: 'bbb', type: 'alert', type_id: 'test', rel: 'primary' }, ], }, }); @@ -754,7 +754,7 @@ describe('AlertingEventLogger', () => { const event = createAlertRecord( backfillContext, ruleData, - [adHocRunSO, { id: 'bbb', type: 'alert', typeId: 'test' }], + [adHocRunSO, { id: 'bbb', type: 'alert', typeId: 'test', relation: 'primary' }], alert ); @@ -1130,7 +1130,7 @@ describe('AlertingEventLogger', () => { saved_objects: [ // @ts-ignore ...event.kibana?.saved_objects, - { id: 'bbb', type: 'alert', type_id: 'test' }, + { id: 'bbb', type: 'alert', type_id: 'test', rel: 'primary' }, ], }, }; 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 4b3a8b9d5201a1..a7f9a2e2bebaa5 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 @@ -255,6 +255,7 @@ export class AlertingEventLogger { typeId: type?.id, type: RULE_SAVED_OBJECT_TYPE, namespace: this.context?.namespace, + relation: SAVED_OBJECT_REL_PRIMARY, }); } } diff --git a/x-pack/plugins/alerting/tsconfig.json b/x-pack/plugins/alerting/tsconfig.json index 1bc43c34e5aa8e..63d1ea5768c4e7 100644 --- a/x-pack/plugins/alerting/tsconfig.json +++ b/x-pack/plugins/alerting/tsconfig.json @@ -39,7 +39,6 @@ "@kbn/data-views-plugin", "@kbn/share-plugin", "@kbn/safer-lodash-set", - "@kbn/alerting-state-types", "@kbn/alerting-types", "@kbn/alerts-as-data-utils", "@kbn/core-elasticsearch-client-server-mocks", @@ -70,7 +69,8 @@ "@kbn/core-execution-context-server-mocks", "@kbn/react-kibana-context-render", "@kbn/search-types", - "@kbn/core-security-server", + "@kbn/alerting-state-types", + "@kbn/core-security-server" ], "exclude": [ "target/**/*" diff --git a/x-pack/plugins/cases/public/components/all_cases/selector_modal/use_cases_add_to_existing_case_modal.test.tsx b/x-pack/plugins/cases/public/components/all_cases/selector_modal/use_cases_add_to_existing_case_modal.test.tsx index 05eeb2b000a3ff..1cfbc59eed3082 100644 --- a/x-pack/plugins/cases/public/components/all_cases/selector_modal/use_cases_add_to_existing_case_modal.test.tsx +++ b/x-pack/plugins/cases/public/components/all_cases/selector_modal/use_cases_add_to_existing_case_modal.test.tsx @@ -19,7 +19,7 @@ import { useCasesToast } from '../../../common/use_cases_toast'; import { alertComment } from '../../../containers/mock'; import { useCreateAttachments } from '../../../containers/use_create_attachments'; import { CasesContext } from '../../cases_context'; -import { CasesContextStoreActionsList } from '../../cases_context/cases_context_reducer'; +import { CasesContextStoreActionsList } from '../../cases_context/state/cases_context_reducer'; import { ExternalReferenceAttachmentTypeRegistry } from '../../../client/attachment_framework/external_reference_registry'; import type { AddToExistingCaseModalProps } from './use_cases_add_to_existing_case_modal'; import { useCasesAddToExistingCaseModal } from './use_cases_add_to_existing_case_modal'; diff --git a/x-pack/plugins/cases/public/components/all_cases/selector_modal/use_cases_add_to_existing_case_modal.tsx b/x-pack/plugins/cases/public/components/all_cases/selector_modal/use_cases_add_to_existing_case_modal.tsx index 0fa30647b60ac7..e9d37452e6ea7d 100644 --- a/x-pack/plugins/cases/public/components/all_cases/selector_modal/use_cases_add_to_existing_case_modal.tsx +++ b/x-pack/plugins/cases/public/components/all_cases/selector_modal/use_cases_add_to_existing_case_modal.tsx @@ -11,7 +11,7 @@ import { CaseStatuses } from '../../../../common/types/domain'; import type { AllCasesSelectorModalProps } from '.'; import { useCasesToast } from '../../../common/use_cases_toast'; import type { CaseUI } from '../../../containers/types'; -import { CasesContextStoreActionsList } from '../../cases_context/cases_context_reducer'; +import { CasesContextStoreActionsList } from '../../cases_context/state/cases_context_reducer'; import { useCasesContext } from '../../cases_context/use_cases_context'; import { useCasesAddToNewCaseFlyout } from '../../create/flyout/use_cases_add_to_new_case_flyout'; import type { CaseAttachmentsWithoutOwner } from '../../../types'; diff --git a/x-pack/plugins/cases/public/components/cases_context/cases_global_components.test.tsx b/x-pack/plugins/cases/public/components/cases_context/cases_global_components.test.tsx index 40793010c789fb..be21f3dc8f37b5 100644 --- a/x-pack/plugins/cases/public/components/cases_context/cases_global_components.test.tsx +++ b/x-pack/plugins/cases/public/components/cases_context/cases_global_components.test.tsx @@ -10,7 +10,7 @@ import { getAllCasesSelectorModalNoProviderLazy } from '../../client/ui/get_all_ import { getCreateCaseFlyoutLazyNoProvider } from '../../client/ui/get_create_case_flyout'; import type { AppMockRenderer } from '../../common/mock'; import { createAppMockRenderer } from '../../common/mock'; -import { getInitialCasesContextState } from './cases_context_reducer'; +import { getInitialCasesContextState } from './state/cases_context_reducer'; import { CasesGlobalComponents } from './cases_global_components'; jest.mock('../../client/ui/get_create_case_flyout'); diff --git a/x-pack/plugins/cases/public/components/cases_context/cases_global_components.tsx b/x-pack/plugins/cases/public/components/cases_context/cases_global_components.tsx index 2add2322b1f0b6..54dc9331139606 100644 --- a/x-pack/plugins/cases/public/components/cases_context/cases_global_components.tsx +++ b/x-pack/plugins/cases/public/components/cases_context/cases_global_components.tsx @@ -8,7 +8,7 @@ import React from 'react'; import { getAllCasesSelectorModalNoProviderLazy } from '../../client/ui/get_all_cases_selector_modal'; import { getCreateCaseFlyoutLazyNoProvider } from '../../client/ui/get_create_case_flyout'; -import type { CasesContextState } from './cases_context_reducer'; +import type { CasesContextState } from './state/cases_context_reducer'; export const CasesGlobalComponents = React.memo(({ state }: { state: CasesContextState }) => { return ( diff --git a/x-pack/plugins/cases/public/components/cases_context/index.tsx b/x-pack/plugins/cases/public/components/cases_context/index.tsx index a59a76f8adb6ac..85c267f5d05d71 100644 --- a/x-pack/plugins/cases/public/components/cases_context/index.tsx +++ b/x-pack/plugins/cases/public/components/cases_context/index.tsx @@ -15,7 +15,6 @@ import { FilesContext } from '@kbn/shared-ux-file-context'; import type { QueryClient } from '@tanstack/react-query'; import { QueryClientProvider } from '@tanstack/react-query'; -import type { CasesContextStoreAction } from './cases_context_reducer'; import type { CasesFeaturesAllRequired, CasesFeatures, @@ -29,7 +28,9 @@ import { CasesGlobalComponents } from './cases_global_components'; import { DEFAULT_FEATURES } from '../../../common/constants'; import { constructFileKindIdByOwner } from '../../../common/files'; import { DEFAULT_BASE_PATH } from '../../common/navigation'; -import { casesContextReducer, getInitialCasesContextState } from './cases_context_reducer'; +import type { CasesContextStoreAction } from './state/cases_context_reducer'; +import { casesContextReducer, getInitialCasesContextState } from './state/cases_context_reducer'; +import { CasesStateContext } from './state/cases_state_context'; import { isRegisteredOwner } from '../../files'; import { casesQueryClient } from './query_client'; @@ -152,14 +153,16 @@ export const CasesProvider: FC< return ( - - {applyFilesContext( - <> - - {children} - - )} - + + + {applyFilesContext( + <> + + {children} + + )} + + ); }; diff --git a/x-pack/plugins/cases/public/components/cases_context/cases_context_reducer.ts b/x-pack/plugins/cases/public/components/cases_context/state/cases_context_reducer.ts similarity index 93% rename from x-pack/plugins/cases/public/components/cases_context/cases_context_reducer.ts rename to x-pack/plugins/cases/public/components/cases_context/state/cases_context_reducer.ts index 1ab468e246bdd6..420ab2d38eba65 100644 --- a/x-pack/plugins/cases/public/components/cases_context/cases_context_reducer.ts +++ b/x-pack/plugins/cases/public/components/cases_context/state/cases_context_reducer.ts @@ -6,8 +6,8 @@ */ import { assertNever } from '@kbn/std'; -import type { AllCasesSelectorModalProps } from '../all_cases/selector_modal'; -import type { CreateCaseFlyoutProps } from '../create/flyout'; +import type { AllCasesSelectorModalProps } from '../../all_cases/selector_modal'; +import type { CreateCaseFlyoutProps } from '../../create/flyout'; export const getInitialCasesContextState = (): CasesContextState => { return { diff --git a/x-pack/plugins/cases/public/components/cases_context/state/cases_state_context.ts b/x-pack/plugins/cases/public/components/cases_context/state/cases_state_context.ts new file mode 100644 index 00000000000000..14a8c01bef9024 --- /dev/null +++ b/x-pack/plugins/cases/public/components/cases_context/state/cases_state_context.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 React, { useContext } from 'react'; +import type { CasesContextState } from './cases_context_reducer'; + +export const CasesStateContext = React.createContext(undefined); + +export const useCasesStateContext = () => { + const casesStateContext = useContext(CasesStateContext); + if (!casesStateContext) { + throw new Error( + 'useCasesStateContext must be used within a CasesProvider and have a defined value. See https://github.com/elastic/kibana/blob/main/x-pack/plugins/cases/README.md#cases-ui' + ); + } + return casesStateContext; +}; diff --git a/x-pack/plugins/cases/public/components/cases_context/state/use_is_add_to_case_open.test.tsx b/x-pack/plugins/cases/public/components/cases_context/state/use_is_add_to_case_open.test.tsx new file mode 100644 index 00000000000000..9974d0cb530d9e --- /dev/null +++ b/x-pack/plugins/cases/public/components/cases_context/state/use_is_add_to_case_open.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 { act, renderHook } from '@testing-library/react-hooks'; +import { useCasesAddToExistingCaseModal } from '../../all_cases/selector_modal/use_cases_add_to_existing_case_modal'; +import { createAppMockRenderer } from '../../../common/mock'; +import { useIsAddToCaseOpen } from './use_is_add_to_case_open'; +import { useCasesToast } from '../../../common/use_cases_toast'; +import { useCasesAddToNewCaseFlyout } from '../../create/flyout/use_cases_add_to_new_case_flyout'; + +jest.mock('../../../common/use_cases_toast'); +const useCasesToastMock = useCasesToast as jest.Mock; +useCasesToastMock.mockReturnValue({ + showInfoToast: jest.fn(), +}); + +const { AppWrapper } = createAppMockRenderer(); + +describe('use is add to existing case modal open hook', () => { + beforeEach(() => { + jest.clearAllMocks(); + }); + + it('should throw if called outside of a cases context', () => { + const { result } = renderHook(useIsAddToCaseOpen); + expect(result.error?.message).toContain( + 'useCasesStateContext must be used within a CasesProvider and have a defined value' + ); + }); + + it('should return false when the add to case modal and flyout are not open', async () => { + const { result } = renderHook(useIsAddToCaseOpen, { wrapper: AppWrapper }); + expect(result.current).toEqual(false); + }); + + it('should return true when the add to existing case modal opens', async () => { + const { result, rerender } = renderHook( + () => { + return { + modal: useCasesAddToExistingCaseModal(), + isOpen: useIsAddToCaseOpen(), + }; + }, + { wrapper: AppWrapper } + ); + + expect(result.current.isOpen).toEqual(false); + act(() => { + result.current.modal.open(); + }); + rerender(); + expect(result.current.isOpen).toEqual(true); + }); + + it('should return true when the add to new case flyout opens', async () => { + const { result, rerender } = renderHook( + () => { + return { + flyout: useCasesAddToNewCaseFlyout(), + isOpen: useIsAddToCaseOpen(), + }; + }, + { wrapper: AppWrapper } + ); + + expect(result.current.isOpen).toEqual(false); + act(() => { + result.current.flyout.open(); + }); + rerender(); + expect(result.current.isOpen).toEqual(true); + }); +}); diff --git a/x-pack/plugins/cases/public/components/cases_context/state/use_is_add_to_case_open.ts b/x-pack/plugins/cases/public/components/cases_context/state/use_is_add_to_case_open.ts new file mode 100644 index 00000000000000..7e385e3c6b0fee --- /dev/null +++ b/x-pack/plugins/cases/public/components/cases_context/state/use_is_add_to_case_open.ts @@ -0,0 +1,18 @@ +/* + * Copyright 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 { useCasesStateContext } from './cases_state_context'; + +export type UseIsAddToCaseOpen = () => boolean; + +/** + * This hook is to check if the "add to case" is open, either the modal or the flyout + */ +export const useIsAddToCaseOpen: UseIsAddToCaseOpen = () => { + const { selectCaseModal, createCaseFlyout } = useCasesStateContext(); + return selectCaseModal.isModalOpen || createCaseFlyout.isFlyoutOpen; +}; diff --git a/x-pack/plugins/cases/public/components/create/flyout/use_cases_add_to_new_case_flyout.test.tsx b/x-pack/plugins/cases/public/components/create/flyout/use_cases_add_to_new_case_flyout.test.tsx index a7aa2071629898..168cae0e478fcb 100644 --- a/x-pack/plugins/cases/public/components/create/flyout/use_cases_add_to_new_case_flyout.test.tsx +++ b/x-pack/plugins/cases/public/components/create/flyout/use_cases_add_to_new_case_flyout.test.tsx @@ -10,7 +10,7 @@ import { renderHook } from '@testing-library/react-hooks'; import type { FC, PropsWithChildren } from 'react'; import React from 'react'; import { CasesContext } from '../../cases_context'; -import { CasesContextStoreActionsList } from '../../cases_context/cases_context_reducer'; +import { CasesContextStoreActionsList } from '../../cases_context/state/cases_context_reducer'; import { useCasesAddToNewCaseFlyout } from './use_cases_add_to_new_case_flyout'; import { allCasesPermissions } from '../../../common/mock'; import { ExternalReferenceAttachmentTypeRegistry } from '../../../client/attachment_framework/external_reference_registry'; diff --git a/x-pack/plugins/cases/public/components/create/flyout/use_cases_add_to_new_case_flyout.tsx b/x-pack/plugins/cases/public/components/create/flyout/use_cases_add_to_new_case_flyout.tsx index ea2290bb49633f..6ee5b87bde9fc8 100644 --- a/x-pack/plugins/cases/public/components/create/flyout/use_cases_add_to_new_case_flyout.tsx +++ b/x-pack/plugins/cases/public/components/create/flyout/use_cases_add_to_new_case_flyout.tsx @@ -10,7 +10,7 @@ import { useCallback, useMemo } from 'react'; import type { CaseAttachmentsWithoutOwner } from '../../../types'; import { useCasesToast } from '../../../common/use_cases_toast'; import type { CaseUI } from '../../../containers/types'; -import { CasesContextStoreActionsList } from '../../cases_context/cases_context_reducer'; +import { CasesContextStoreActionsList } from '../../cases_context/state/cases_context_reducer'; import { useCasesContext } from '../../cases_context/use_cases_context'; import type { CreateCaseFlyoutProps } from './create_case_flyout'; diff --git a/x-pack/plugins/cases/public/mocks.ts b/x-pack/plugins/cases/public/mocks.ts index 3511beda3ccccf..e267c108a9b398 100644 --- a/x-pack/plugins/cases/public/mocks.ts +++ b/x-pack/plugins/cases/public/mocks.ts @@ -27,6 +27,7 @@ const uiMock: jest.Mocked = { export const openAddToExistingCaseModalMock = jest.fn(); export const openAddToNewCaseFlyoutMock = jest.fn(); +export const isAddToCaseOpenMock = jest.fn(); const hooksMock: jest.Mocked = { useCasesAddToNewCaseFlyout: jest.fn().mockImplementation(() => ({ @@ -35,6 +36,7 @@ const hooksMock: jest.Mocked = { useCasesAddToExistingCaseModal: jest.fn().mockImplementation(() => ({ open: openAddToExistingCaseModalMock, })), + useIsAddToCaseOpen: isAddToCaseOpenMock, }; const helpersMock: jest.Mocked = { diff --git a/x-pack/plugins/cases/public/plugin.test.ts b/x-pack/plugins/cases/public/plugin.test.ts index bfe00078a04d6e..54f9cf070df449 100644 --- a/x-pack/plugins/cases/public/plugin.test.ts +++ b/x-pack/plugins/cases/public/plugin.test.ts @@ -142,6 +142,7 @@ describe('Cases Ui Plugin', () => { hooks: { useCasesAddToExistingCaseModal: expect.any(Function), useCasesAddToNewCaseFlyout: expect.any(Function), + useIsAddToCaseOpen: expect.any(Function), }, ui: { getAllCasesSelectorModal: expect.any(Function), diff --git a/x-pack/plugins/cases/public/plugin.ts b/x-pack/plugins/cases/public/plugin.ts index 44393473767e62..12308fcc2f8b6a 100644 --- a/x-pack/plugins/cases/public/plugin.ts +++ b/x-pack/plugins/cases/public/plugin.ts @@ -16,6 +16,7 @@ import { APP_ID, APP_PATH } from '../common/constants'; import { APP_TITLE, APP_DESC } from './common/translations'; import { useCasesAddToExistingCaseModal } from './components/all_cases/selector_modal/use_cases_add_to_existing_case_modal'; import { useCasesAddToNewCaseFlyout } from './components/create/flyout/use_cases_add_to_new_case_flyout'; +import { useIsAddToCaseOpen } from './components/cases_context/state/use_is_add_to_case_open'; import { createClientAPI } from './client/api'; import { canUseCases } from './client/helpers/can_use_cases'; import { getRuleIdFromEvent } from './client/helpers/get_rule_id_from_event'; @@ -190,6 +191,7 @@ export class CasesUiPlugin hooks: { useCasesAddToNewCaseFlyout, useCasesAddToExistingCaseModal, + useIsAddToCaseOpen, }, helpers: { canUseCases: canUseCases(core.application.capabilities), diff --git a/x-pack/plugins/cases/public/types.ts b/x-pack/plugins/cases/public/types.ts index bb8790a299d126..c857446ea042c2 100644 --- a/x-pack/plugins/cases/public/types.ts +++ b/x-pack/plugins/cases/public/types.ts @@ -32,6 +32,7 @@ import type { ServerlessPluginSetup, ServerlessPluginStart } from '@kbn/serverle import type { UseCasesAddToExistingCaseModal } from './components/all_cases/selector_modal/use_cases_add_to_existing_case_modal'; import type { UseCasesAddToNewCaseFlyout } from './components/create/flyout/use_cases_add_to_new_case_flyout'; +import type { UseIsAddToCaseOpen } from './components/cases_context/state/use_is_add_to_case_open'; import type { canUseCases } from './client/helpers/can_use_cases'; import type { getRuleIdFromEvent } from './client/helpers/get_rule_id_from_event'; import type { GetCasesContextProps } from './client/ui/get_cases_context'; @@ -154,6 +155,7 @@ export interface CasesPublicStart { hooks: { useCasesAddToNewCaseFlyout: UseCasesAddToNewCaseFlyout; useCasesAddToExistingCaseModal: UseCasesAddToExistingCaseModal; + useIsAddToCaseOpen: UseIsAddToCaseOpen; }; helpers: { /** diff --git a/x-pack/plugins/cloud_security_posture/public/components/cloud_security_grouping/cloud_security_grouping.tsx b/x-pack/plugins/cloud_security_posture/public/components/cloud_security_grouping/cloud_security_grouping.tsx index 9e6e88ee0cbbb6..a95ae51f81eacc 100644 --- a/x-pack/plugins/cloud_security_posture/public/components/cloud_security_grouping/cloud_security_grouping.tsx +++ b/x-pack/plugins/cloud_security_posture/public/components/cloud_security_grouping/cloud_security_grouping.tsx @@ -4,8 +4,8 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ -import { useGrouping } from '@kbn/securitysolution-grouping'; -import { ParsedGroupingAggregation } from '@kbn/securitysolution-grouping/src'; +import { useGrouping } from '@kbn/grouping'; +import { ParsedGroupingAggregation } from '@kbn/grouping/src'; import { Filter } from '@kbn/es-query'; import React from 'react'; import { css } from '@emotion/react'; diff --git a/x-pack/plugins/cloud_security_posture/public/components/cloud_security_grouping/use_cloud_security_grouping.ts b/x-pack/plugins/cloud_security_posture/public/components/cloud_security_grouping/use_cloud_security_grouping.ts index 1b856fd343d260..ea1aee0ffcef74 100644 --- a/x-pack/plugins/cloud_security_posture/public/components/cloud_security_grouping/use_cloud_security_grouping.ts +++ b/x-pack/plugins/cloud_security_posture/public/components/cloud_security_grouping/use_cloud_security_grouping.ts @@ -5,14 +5,10 @@ * 2.0. */ import { useCallback, useEffect, useMemo, useState } from 'react'; -import { isNoneGroup, useGrouping } from '@kbn/securitysolution-grouping'; +import { isNoneGroup, useGrouping } from '@kbn/grouping'; import * as uuid from 'uuid'; import type { DataView } from '@kbn/data-views-plugin/common'; -import { - GroupOption, - GroupPanelRenderer, - GroupStatsRenderer, -} from '@kbn/securitysolution-grouping/src'; +import { GroupOption, GroupPanelRenderer, GroupStatsRenderer } from '@kbn/grouping/src'; import { useUrlQuery } from '../../common/hooks/use_url_query'; diff --git a/x-pack/plugins/cloud_security_posture/public/components/cloud_security_grouping/utils/first_non_null_value.ts b/x-pack/plugins/cloud_security_posture/public/components/cloud_security_grouping/utils/first_non_null_value.ts index a8c5da0500e8a0..2a8d36309f4431 100644 --- a/x-pack/plugins/cloud_security_posture/public/components/cloud_security_grouping/utils/first_non_null_value.ts +++ b/x-pack/plugins/cloud_security_posture/public/components/cloud_security_grouping/utils/first_non_null_value.ts @@ -5,7 +5,7 @@ * 2.0. */ -import { ECSField } from '@kbn/securitysolution-grouping/src'; +import { ECSField } from '@kbn/grouping/src'; /** * Return first non-null value. If the field contains an array, this will return the first value that isn't null. If the field isn't an array it'll be returned unless it's null. diff --git a/x-pack/plugins/cloud_security_posture/public/pages/configurations/latest_findings/constants.ts b/x-pack/plugins/cloud_security_posture/public/pages/configurations/latest_findings/constants.ts index 5ae8f4eb02ac2b..9f7ee15604f350 100644 --- a/x-pack/plugins/cloud_security_posture/public/pages/configurations/latest_findings/constants.ts +++ b/x-pack/plugins/cloud_security_posture/public/pages/configurations/latest_findings/constants.ts @@ -6,7 +6,7 @@ */ import { i18n } from '@kbn/i18n'; -import { GroupOption } from '@kbn/securitysolution-grouping'; +import { GroupOption } from '@kbn/grouping'; import { FINDINGS_GROUPING_OPTIONS } from '../../../common/constants'; import { FindingsBaseURLQuery } from '../../../common/types'; import { CloudSecurityDefaultColumn } from '../../../components/cloud_security_data_table'; diff --git a/x-pack/plugins/cloud_security_posture/public/pages/configurations/latest_findings/latest_findings_group_renderer.tsx b/x-pack/plugins/cloud_security_posture/public/pages/configurations/latest_findings/latest_findings_group_renderer.tsx index 6dbc78cbf0857a..d40037d156d3f3 100644 --- a/x-pack/plugins/cloud_security_posture/public/pages/configurations/latest_findings/latest_findings_group_renderer.tsx +++ b/x-pack/plugins/cloud_security_posture/public/pages/configurations/latest_findings/latest_findings_group_renderer.tsx @@ -14,7 +14,7 @@ import { useEuiTheme, } from '@elastic/eui'; import { css } from '@emotion/react'; -import { GroupPanelRenderer, RawBucket, StatRenderer } from '@kbn/securitysolution-grouping/src'; +import { GroupPanelRenderer, RawBucket, StatRenderer } from '@kbn/grouping/src'; import React from 'react'; import { i18n } from '@kbn/i18n'; import { FINDINGS_GROUPING_OPTIONS } from '../../../common/constants'; diff --git a/x-pack/plugins/cloud_security_posture/public/pages/configurations/latest_findings/use_grouped_findings.tsx b/x-pack/plugins/cloud_security_posture/public/pages/configurations/latest_findings/use_grouped_findings.tsx index a4dcfe5a442c25..5784e1e43bcf39 100644 --- a/x-pack/plugins/cloud_security_posture/public/pages/configurations/latest_findings/use_grouped_findings.tsx +++ b/x-pack/plugins/cloud_security_posture/public/pages/configurations/latest_findings/use_grouped_findings.tsx @@ -7,7 +7,7 @@ import { SearchResponse } from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; import type { IKibanaSearchResponse } from '@kbn/search-types'; -import { GenericBuckets, GroupingQuery, RootAggregation } from '@kbn/securitysolution-grouping/src'; +import { GenericBuckets, GroupingQuery, RootAggregation } from '@kbn/grouping/src'; import { useQuery } from '@tanstack/react-query'; import { lastValueFrom } from 'rxjs'; import { CSP_LATEST_FINDINGS_DATA_VIEW } from '../../../../common/constants'; diff --git a/x-pack/plugins/cloud_security_posture/public/pages/configurations/latest_findings/use_latest_findings_grouping.tsx b/x-pack/plugins/cloud_security_posture/public/pages/configurations/latest_findings/use_latest_findings_grouping.tsx index 17b8f0ab0f54d6..802ed52be92286 100644 --- a/x-pack/plugins/cloud_security_posture/public/pages/configurations/latest_findings/use_latest_findings_grouping.tsx +++ b/x-pack/plugins/cloud_security_posture/public/pages/configurations/latest_findings/use_latest_findings_grouping.tsx @@ -4,7 +4,7 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ -import { getGroupingQuery } from '@kbn/securitysolution-grouping'; +import { getGroupingQuery } from '@kbn/grouping'; import { GroupingAggregation, GroupPanelRenderer, @@ -12,7 +12,7 @@ import { isNoneGroup, NamedAggregation, parseGroupingQuery, -} from '@kbn/securitysolution-grouping/src'; +} from '@kbn/grouping/src'; import { useMemo } from 'react'; import { buildEsQuery, Filter } from '@kbn/es-query'; import { diff --git a/x-pack/plugins/cloud_security_posture/public/pages/vulnerabilities/constants.ts b/x-pack/plugins/cloud_security_posture/public/pages/vulnerabilities/constants.ts index 9ec8e0467d4678..f85d35968599a4 100644 --- a/x-pack/plugins/cloud_security_posture/public/pages/vulnerabilities/constants.ts +++ b/x-pack/plugins/cloud_security_posture/public/pages/vulnerabilities/constants.ts @@ -5,7 +5,7 @@ * 2.0. */ -import { GroupOption } from '@kbn/securitysolution-grouping'; +import { GroupOption } from '@kbn/grouping'; import { FindingsBaseURLQuery } from '../../common/types'; import { CloudSecurityDefaultColumn } from '../../components/cloud_security_data_table'; import { GROUPING_LABELS } from './translations'; diff --git a/x-pack/plugins/cloud_security_posture/public/pages/vulnerabilities/hooks/use_grouped_vulnerabilities.tsx b/x-pack/plugins/cloud_security_posture/public/pages/vulnerabilities/hooks/use_grouped_vulnerabilities.tsx index a39933da496951..79cb2aad7e60e4 100644 --- a/x-pack/plugins/cloud_security_posture/public/pages/vulnerabilities/hooks/use_grouped_vulnerabilities.tsx +++ b/x-pack/plugins/cloud_security_posture/public/pages/vulnerabilities/hooks/use_grouped_vulnerabilities.tsx @@ -7,7 +7,7 @@ import { SearchResponse } from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; import type { IKibanaSearchResponse } from '@kbn/search-types'; -import { GenericBuckets, GroupingQuery, RootAggregation } from '@kbn/securitysolution-grouping/src'; +import { GenericBuckets, GroupingQuery, RootAggregation } from '@kbn/grouping/src'; import { useQuery } from '@tanstack/react-query'; import { lastValueFrom } from 'rxjs'; import { LATEST_VULNERABILITIES_INDEX_PATTERN } from '../../../../common/constants'; diff --git a/x-pack/plugins/cloud_security_posture/public/pages/vulnerabilities/hooks/use_latest_vulnerabilities_grouping.tsx b/x-pack/plugins/cloud_security_posture/public/pages/vulnerabilities/hooks/use_latest_vulnerabilities_grouping.tsx index 00b5e80097ec5e..fa90d4f6209bdd 100644 --- a/x-pack/plugins/cloud_security_posture/public/pages/vulnerabilities/hooks/use_latest_vulnerabilities_grouping.tsx +++ b/x-pack/plugins/cloud_security_posture/public/pages/vulnerabilities/hooks/use_latest_vulnerabilities_grouping.tsx @@ -4,7 +4,7 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ -import { getGroupingQuery } from '@kbn/securitysolution-grouping'; +import { getGroupingQuery } from '@kbn/grouping'; import { GroupingAggregation, GroupPanelRenderer, @@ -12,7 +12,7 @@ import { isNoneGroup, NamedAggregation, parseGroupingQuery, -} from '@kbn/securitysolution-grouping/src'; +} from '@kbn/grouping/src'; import { useMemo } from 'react'; import { buildEsQuery, Filter } from '@kbn/es-query'; import { diff --git a/x-pack/plugins/cloud_security_posture/public/pages/vulnerabilities/latest_vulnerabilities_group_renderer.tsx b/x-pack/plugins/cloud_security_posture/public/pages/vulnerabilities/latest_vulnerabilities_group_renderer.tsx index b8531491c42c08..489c8ac0990f84 100644 --- a/x-pack/plugins/cloud_security_posture/public/pages/vulnerabilities/latest_vulnerabilities_group_renderer.tsx +++ b/x-pack/plugins/cloud_security_posture/public/pages/vulnerabilities/latest_vulnerabilities_group_renderer.tsx @@ -14,7 +14,7 @@ import { useEuiTheme, } from '@elastic/eui'; import { css } from '@emotion/react'; -import { GroupPanelRenderer, RawBucket, StatRenderer } from '@kbn/securitysolution-grouping/src'; +import { GroupPanelRenderer, RawBucket, StatRenderer } from '@kbn/grouping/src'; import React from 'react'; import { FormattedMessage } from '@kbn/i18n-react'; import { getCloudProviderNameFromAbbreviation } from '../../../common/utils/helpers'; diff --git a/x-pack/plugins/cloud_security_posture/server/tasks/findings_stats_task.ts b/x-pack/plugins/cloud_security_posture/server/tasks/findings_stats_task.ts index df9c1c73224c28..ed1be5f4f0bd5f 100644 --- a/x-pack/plugins/cloud_security_posture/server/tasks/findings_stats_task.ts +++ b/x-pack/plugins/cloud_security_posture/server/tasks/findings_stats_task.ts @@ -11,7 +11,7 @@ import { TaskManagerSetupContract, TaskManagerStartContract, } from '@kbn/task-manager-plugin/server'; -import { SearchRequest } from '@kbn/data-plugin/common'; +import type { SearchRequest } from '@elastic/elasticsearch/lib/api/types'; import { ElasticsearchClient } from '@kbn/core/server'; import { QueryDslQueryContainer } from '@kbn/data-views-plugin/common/types'; import type { ISavedObjectsRepository, Logger } from '@kbn/core/server'; diff --git a/x-pack/plugins/cloud_security_posture/tsconfig.json b/x-pack/plugins/cloud_security_posture/tsconfig.json index 907ff68fc38973..1febc701d66d60 100755 --- a/x-pack/plugins/cloud_security_posture/tsconfig.json +++ b/x-pack/plugins/cloud_security_posture/tsconfig.json @@ -59,7 +59,7 @@ "@kbn/core-http-server-mocks", "@kbn/field-formats-plugin", "@kbn/data-view-field-editor-plugin", - "@kbn/securitysolution-grouping", + "@kbn/grouping", "@kbn/alerting-plugin", "@kbn/code-editor", "@kbn/code-editor-mock", diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/connector_detail/connector_stats.tsx b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/connector_detail/connector_stats.tsx index 095844b4e04d48..d8725141eb5e3f 100644 --- a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/connector_detail/connector_stats.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/connector_detail/connector_stats.tsx @@ -122,10 +122,8 @@ export const ConnectorStats: React.FC = ({ connector, index - - {connectorStatusToText(connector?.status, !!connector?.index_name)} + + {connectorStatusToText(connector)} diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/connector_detail/connector_view_logic.ts b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/connector_detail/connector_view_logic.ts index f7d6f30ee7d54f..42d881300ac5d1 100644 --- a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/connector_detail/connector_view_logic.ts +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/connector_detail/connector_view_logic.ts @@ -158,7 +158,11 @@ export const ConnectorViewLogic = kea [selectors.connector], (connector) => connector?.id], error: [ () => [selectors.connector], - (connector: Connector | undefined) => connector?.error || connector?.last_sync_error || null, + (connector: Connector | undefined) => + connector?.error || + connector?.last_sync_error || + connector?.last_access_control_sync_error || + null, ], indexName: [ () => [selectors.connector], diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/connector_detail/overview.tsx b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/connector_detail/overview.tsx index a97f7b8c862fd1..1931f0f9455054 100644 --- a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/connector_detail/overview.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/connector_detail/overview.tsx @@ -186,7 +186,10 @@ export const ConnectorDetailOverview: React.FC = () => { {connector && connector.service_type !== ENTERPRISE_SEARCH_CONNECTOR_CRAWLER_SERVICE_TYPE && ( <> - + )} diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/connectors/connector_stats.tsx b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/connectors/connector_stats.tsx index 8b2ad4490a1a6b..db76e97ec719e7 100644 --- a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/connectors/connector_stats.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/connectors/connector_stats.tsx @@ -213,6 +213,7 @@ export const ConnectorStats: React.FC = ({ isCrawler }) => {}} onClickAriaLabel={getSyncJobErrorsLabel(errorCount, isCrawler)} + color={errorCount > 0 ? 'danger' : 'default'} > {getSyncJobErrorsLabel(errorCount, isCrawler)} diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/connectors/connectors_table.tsx b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/connectors/connectors_table.tsx index daf3f59eef45ed..82f258487f39ec 100644 --- a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/connectors/connectors_table.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/connectors/connectors_table.tsx @@ -135,12 +135,8 @@ export const ConnectorsTable: React.FC = ({ } ), render: (connector: ConnectorViewItem) => { - const label = connectorStatusToText(connector.status, !!connector.index_name); - return ( - - {label} - - ); + const label = connectorStatusToText(connector); + return {label}; }, truncateText: true, width: '15%', 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 57f6d8f11bfcef..597757e83534a7 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 @@ -264,7 +264,11 @@ export const IndexViewLogic = kea [selectors.connector], - (connector: Connector | undefined) => connector?.error || connector?.last_sync_error || null, + (connector: Connector | undefined) => + connector?.error || + connector?.last_sync_error || + connector?.last_access_control_sync_error || + null, ], hasAdvancedFilteringFeature: [ () => [selectors.connector], diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/overview.tsx b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/overview.tsx index b280d3fbf36b78..f8ccf71026e83b 100644 --- a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/overview.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/overview.tsx @@ -81,7 +81,11 @@ export const SearchIndexOverview: React.FC = () => { defaultMessage="Convert it to a {link}, to be self-managed on your own infrastructure. Native connectors are available only in your Elastic Cloud deployment." values={{ link: ( - + {i18n.translate( 'xpack.enterpriseSearch.content.searchIndex.nativeCloudCallout.connectorClient', { defaultMessage: 'connector client' } @@ -93,7 +97,12 @@ export const SearchIndexOverview: React.FC = () => {

- showModal()}> + showModal()} + > {i18n.translate( 'xpack.enterpriseSearch.content.indices.searchIndex.convertConnector.buttonLabel', { defaultMessage: 'Convert connector' } @@ -126,7 +135,10 @@ export const SearchIndexOverview: React.FC = () => { {isConnectorIndex(indexData) && ( <> - + )} diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/sync_jobs/sync_jobs.tsx b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/sync_jobs/sync_jobs.tsx index 2013d601df4aa8..49f49591007361 100644 --- a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/sync_jobs/sync_jobs.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/sync_jobs/sync_jobs.tsx @@ -21,7 +21,15 @@ import { IndexViewLogic } from '../index_view_logic'; import { SyncJobsViewLogic } from './sync_jobs_view_logic'; -export const SyncJobs: React.FC = () => { +export interface SyncJobsProps { + errorOnAccessSync?: boolean; + errorOnContentSync?: boolean; +} + +export const SyncJobs: React.FC = ({ + errorOnAccessSync = false, + errorOnContentSync = false, +}) => { const { hasDocumentLevelSecurityFeature } = useValues(IndexViewLogic); const { productFeatures } = useValues(KibanaLogic); const shouldShowAccessSyncs = @@ -74,6 +82,7 @@ export const SyncJobs: React.FC = () => { 'xpack.enterpriseSearch.content.syncJobs.lastSync.tableSelector.content.label', { defaultMessage: 'Content syncs' } ), + ...(errorOnContentSync ? { iconSide: 'right', iconType: 'warning' } : {}), }, { @@ -82,6 +91,7 @@ export const SyncJobs: React.FC = () => { 'xpack.enterpriseSearch.content.syncJobs.lastSync.tableSelector.accessControl.label', { defaultMessage: 'Access control syncs' } ), + ...(errorOnAccessSync ? { iconSide: 'right', iconType: 'warning' } : {}), }, ]} /> diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/utils/connector_status_helpers.ts b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/utils/connector_status_helpers.ts index ef8e84177eae10..587539498c7865 100644 --- a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/utils/connector_status_helpers.ts +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/utils/connector_status_helpers.ts @@ -6,17 +6,16 @@ */ import { i18n } from '@kbn/i18n'; -import { ConnectorStatus } from '@kbn/search-connectors'; +import { Connector, ConnectorStatus, SyncStatus } from '@kbn/search-connectors'; const incompleteText = i18n.translate( 'xpack.enterpriseSearch.content.searchIndices.ingestionStatus.incomplete.label', { defaultMessage: 'Incomplete' } ); -export function connectorStatusToText( - connectorStatus: ConnectorStatus, - hasIndexName: boolean -): string { +export function connectorStatusToText(connector: Connector): string { + const hasIndexName = !!connector.index_name; + const connectorStatus = connector.status; if ( connectorStatus === ConnectorStatus.CREATED || connectorStatus === ConnectorStatus.NEEDS_CONFIGURATION @@ -26,6 +25,16 @@ export function connectorStatusToText( { defaultMessage: 'Needs Configuration' } ); } + if ( + connector.error === SyncStatus.ERROR || + connector.last_sync_error !== null || + connector.last_access_control_sync_error !== null + ) { + return i18n.translate( + 'xpack.enterpriseSearch.content.searchIndices.connectorStatus.syncFailure.label', + { defaultMessage: 'Sync Failure' } + ); + } if (connectorStatus === ConnectorStatus.ERROR) { return i18n.translate( 'xpack.enterpriseSearch.content.searchIndices.connectorStatus.connectorFailure.label', @@ -51,18 +60,22 @@ export function connectorStatusToText( return incompleteText; } -export function connectorStatusToColor( - connectorStatus: ConnectorStatus, - hasIndexName: boolean -): 'warning' | 'danger' | 'success' { +export function connectorStatusToColor(connector: Connector): 'warning' | 'danger' | 'success' { + const hasIndexName = !!connector.index_name; + const connectorStatus = connector.status; if (!hasIndexName) { return 'warning'; } + if ( + connectorStatus === ConnectorStatus.ERROR || + connector.error === SyncStatus.ERROR || + connector.last_sync_error !== null || + connector.last_access_control_sync_error !== null + ) { + return 'danger'; + } if (connectorStatus === ConnectorStatus.CONNECTED) { return 'success'; } - if (connectorStatus === ConnectorStatus.ERROR) { - return 'danger'; - } return 'warning'; } diff --git a/x-pack/plugins/enterprise_search/server/utils/get_sync_jobs_queries.ts b/x-pack/plugins/enterprise_search/server/utils/get_sync_jobs_queries.ts index 5fe2e9b17eff73..ff6eeee4db472f 100644 --- a/x-pack/plugins/enterprise_search/server/utils/get_sync_jobs_queries.ts +++ b/x-pack/plugins/enterprise_search/server/utils/get_sync_jobs_queries.ts @@ -331,24 +331,18 @@ export const getIncompleteCountQuery = (isCrawler?: boolean) => { } return { bool: { - should: [ - { - bool: { - must_not: { - terms: { - status: [ConnectorStatus.CONNECTED, ConnectorStatus.ERROR], - }, - }, - }, + must_not: { + terms: { + status: [ConnectorStatus.CONNECTED, ConnectorStatus.ERROR], }, - { - range: { - last_seen: { - lt: moment().subtract(30, 'minutes').toISOString(), - }, + }, + must: { + range: { + last_seen: { + lt: moment().subtract(30, 'minutes').toISOString(), }, }, - ], + }, filter: [ { bool: { diff --git a/x-pack/plugins/features/server/routes/index.ts b/x-pack/plugins/features/server/routes/index.ts index bb78f07bc56ccf..621bf4a4b0e876 100644 --- a/x-pack/plugins/features/server/routes/index.ts +++ b/x-pack/plugins/features/server/routes/index.ts @@ -21,7 +21,11 @@ export function defineRoutes({ router, featureRegistry }: RouteDefinitionParams) router.get( { path: '/api/features', - options: { tags: ['access:features'] }, + options: { + tags: ['access:features'], + access: 'public', + description: `Get features`, + }, validate: { query: schema.object({ ignoreValidLicenses: schema.boolean({ defaultValue: false }) }), }, diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/single_page_layout/hooks/form.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/single_page_layout/hooks/form.tsx index 0ce292cd5cba6e..54199ef90a06fe 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/single_page_layout/hooks/form.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/single_page_layout/hooks/form.tsx @@ -262,7 +262,11 @@ export function useOnSubmit({ setFormState('INVALID'); return; } - if (agentCount !== 0 && !isAgentlessIntegration(packageInfo) && formState !== 'CONFIRM') { + if ( + agentCount !== 0 && + !(isAgentlessIntegration(packageInfo) || isAgentlessPackagePolicy(packagePolicy)) && + formState !== 'CONFIRM' + ) { setFormState('CONFIRM'); return; } @@ -297,7 +301,6 @@ export function useOnSubmit({ await sendBulkInstallPackages([...new Set(packagesToPreinstall)]); } } - createdPolicy = await createAgentPolicy({ newAgentPolicy, packagePolicy, diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/single_page_layout/index.test.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/single_page_layout/index.test.tsx index 75ef6537ce9bdd..ab87cbc8492be1 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/single_page_layout/index.test.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/single_page_layout/index.test.tsx @@ -129,6 +129,10 @@ afterAll(() => { }); describe('When on the package policy create page', () => { + afterEach(() => { + jest.clearAllMocks(); + }); + const createPageUrlPath = pagePathGetters.add_integration_to_policy({ pkgkey: 'nginx-1.3.0' })[1]; let testRenderer: TestRenderer; @@ -738,6 +742,18 @@ describe('When on the package policy create page', () => { describe('With agentless policy available', () => { beforeEach(async () => { + (useStartServices as jest.MockedFunction).mockReturnValue({ + ...useStartServices(), + cloud: { + ...useStartServices().cloud, + isServerlessEnabled: true, + }, + }); + jest.spyOn(ExperimentalFeaturesService, 'get').mockReturnValue({ agentless: true }); + (useGetPackageInfoByKeyQuery as jest.Mock).mockReturnValue( + getMockPackageInfo({ requiresRoot: false, dataStreamRequiresRoot: false }) + ); + (sendGetOneAgentPolicy as jest.MockedFunction).mockResolvedValue({ data: { item: { id: AGENTLESS_POLICY_ID, name: 'Agentless CSPM', namespace: 'default' } }, }); @@ -756,6 +772,7 @@ describe('When on the package policy create page', () => { }); test('should not force create package policy when not in serverless', async () => { + jest.spyOn(ExperimentalFeaturesService, 'get').mockReturnValue({ agentless: false }); (useStartServices as jest.MockedFunction).mockReturnValue({ ...useStartServices(), cloud: { @@ -784,15 +801,6 @@ describe('When on the package policy create page', () => { }); test('should force create package policy', async () => { - (useStartServices as jest.MockedFunction).mockReturnValue({ - ...useStartServices(), - cloud: { - ...useStartServices().cloud, - isServerlessEnabled: true, - }, - }); - jest.spyOn(ExperimentalFeaturesService, 'get').mockReturnValue({ agentless: true }); - await act(async () => { fireEvent.click(renderResult.getByText('Existing hosts')!); }); @@ -813,8 +821,7 @@ describe('When on the package policy create page', () => { }); }); - // FLAKY: https://github.com/elastic/kibana/issues/184191 - test.skip('should not show confirmation modal', async () => { + test('should not show confirmation modal', async () => { (sendGetAgentStatus as jest.MockedFunction).mockResolvedValueOnce({ data: { results: { total: 1 } }, }); @@ -845,6 +852,12 @@ const mockApiCalls = (http: MockedFleetStartServices['http']) => { if (path === '/api/fleet/outputs') { return Promise.resolve({ data: { items: [] } }); } + if (path === '/api/fleet/fleet_server_hosts') { + return Promise.resolve({ data: { items: [] } }); + } + if (path === '/api/fleet/agent_download_sources') { + return Promise.resolve({ data: { items: [] } }); + } const err = new Error(`API [GET ${path}] is not MOCKED!`); // eslint-disable-next-line no-console console.log(err); diff --git a/x-pack/plugins/index_management/__jest__/client_integration/helpers/setup_environment.tsx b/x-pack/plugins/index_management/__jest__/client_integration/helpers/setup_environment.tsx index 99fa4075d2d94a..5c448aef1790c3 100644 --- a/x-pack/plugins/index_management/__jest__/client_integration/helpers/setup_environment.tsx +++ b/x-pack/plugins/index_management/__jest__/client_integration/helpers/setup_environment.tsx @@ -86,6 +86,7 @@ const appDependencies = { enableDataStreamsStorageColumn: true, enableMappingsSourceFieldSection: true, enableTogglingDataRetention: true, + enableSemanticText: false, }, } as any; diff --git a/x-pack/plugins/index_management/public/application/app_context.tsx b/x-pack/plugins/index_management/public/application/app_context.tsx index 8ee80e2f8f55f1..964a0e098c15e8 100644 --- a/x-pack/plugins/index_management/public/application/app_context.tsx +++ b/x-pack/plugins/index_management/public/application/app_context.tsx @@ -64,6 +64,7 @@ export interface AppDependencies { enableDataStreamsStorageColumn: boolean; enableMappingsSourceFieldSection: boolean; enableTogglingDataRetention: boolean; + enableSemanticText: boolean; }; history: ScopedHistory; setBreadcrumbs: (type: IndexManagementBreadcrumb, additionalBreadcrumb?: EuiBreadcrumb) => void; diff --git a/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/select_inference_id.test.tsx b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/select_inference_id.test.tsx index ce516063e34495..4222cf493a9bf9 100644 --- a/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/select_inference_id.test.tsx +++ b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/select_inference_id.test.tsx @@ -11,6 +11,7 @@ import { SelectInferenceId } from './select_inference_id'; const onChangeMock = jest.fn(); const setValueMock = jest.fn(); +const setNewInferenceEndpointMock = jest.fn(); jest.mock('../../../../../app_context', () => ({ useAppContext: jest.fn().mockReturnValue({ @@ -21,6 +22,7 @@ jest.mock('../../../../../app_context', () => ({ mlApi: { trainedModels: { getTrainedModels: jest.fn().mockResolvedValue([]), + getTrainedModelStats: jest.fn().mockResolvedValue([]), }, }, }, @@ -38,6 +40,7 @@ describe('SelectInferenceId', () => { onChange: onChangeMock, 'data-test-subj': 'data-inference-endpoint-list', setValue: setValueMock, + setNewInferenceEndpoint: setNewInferenceEndpointMock, }, memoryRouter: { wrapComponent: false }, }); diff --git a/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/select_inference_id.tsx b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/select_inference_id.tsx index f08007f3174df5..1d09c2469c1649 100644 --- a/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/select_inference_id.tsx +++ b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/select_inference_id.tsx @@ -30,7 +30,11 @@ import { TRAINED_MODEL_TYPE, } from '@kbn/ml-trained-models-utils'; import { InferenceTaskType } from '@elastic/elasticsearch/lib/api/types'; -import { ModelConfig } from '@kbn/inference_integration_flyout/types'; +import { + ElasticsearchModelDefaultOptions, + ModelConfig, + Service, +} from '@kbn/inference_integration_flyout/types'; import { FormattedMessage } from '@kbn/i18n-react'; import { InferenceFlyoutWrapper } from '@kbn/inference_integration_flyout/components/inference_flyout_wrapper'; import { TrainedModelConfigResponse } from '@kbn/ml-plugin/common/types/trained_models'; @@ -39,15 +43,26 @@ import { getFieldConfig } from '../../../lib'; import { useAppContext } from '../../../../../app_context'; import { Form, UseField, useForm } from '../../../shared_imports'; import { useLoadInferenceModels } from '../../../../../services/api'; +import { getTrainedModelStats } from '../../../../../../hooks/use_details_page_mappings_model_management'; +import { InferenceToModelIdMap } from '../fields'; + +const inferenceServiceTypeElasticsearchModelMap: Record = + { + elser: ElasticsearchModelDefaultOptions.elser, + elasticsearch: ElasticsearchModelDefaultOptions.e5, + }; + interface Props { onChange(value: string): void; 'data-test-subj'?: string; setValue: (value: string) => void; + setNewInferenceEndpoint: (newInferenceEndpoint: InferenceToModelIdMap) => void; } export const SelectInferenceId = ({ onChange, 'data-test-subj': dataTestSubj, setValue, + setNewInferenceEndpoint, }: Props) => { const { core: { application }, @@ -135,14 +150,26 @@ export const SelectInferenceId = ({ setIsInferenceFlyoutVisible(!isInferenceFlyoutVisible); setIsCreateInferenceApiLoading(false); setInferenceAddError(undefined); + const trainedModelStats = await ml?.mlApi?.trainedModels.getTrainedModelStats(); + const defaultEndpointId = + inferenceServiceTypeElasticsearchModelMap[modelConfig.service] || ''; + const newModelId: InferenceToModelIdMap = {}; + newModelId[inferenceId] = { + trainedModelId: defaultEndpointId, + isDeployable: + modelConfig.service === Service.elser || modelConfig.service === Service.elasticsearch, + isDeployed: getTrainedModelStats(trainedModelStats)[defaultEndpointId] === 'deployed', + defaultInferenceEndpoint: false, + }; resendRequest(); + setNewInferenceEndpoint(newModelId); } catch (error) { const errorObj = extractErrorProperties(error); setInferenceAddError(errorObj.message); setIsCreateInferenceApiLoading(false); } }, - [isInferenceFlyoutVisible, resendRequest, ml] + [isInferenceFlyoutVisible, resendRequest, ml, setNewInferenceEndpoint] ); useEffect(() => { const subscription = subscribe((updateData) => { diff --git a/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/create_field/create_field.tsx b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/create_field/create_field.tsx index 7c5e978404383d..f91be7bf55fe24 100644 --- a/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/create_field/create_field.tsx +++ b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/create_field/create_field.tsx @@ -16,10 +16,10 @@ import { import { i18n } from '@kbn/i18n'; import { MlPluginStart } from '@kbn/ml-plugin/public'; import classNames from 'classnames'; -import React, { useEffect } from 'react'; +import React, { useCallback, useEffect } from 'react'; import { EUI_SIZE, TYPE_DEFINITION } from '../../../../constants'; import { fieldSerializer } from '../../../../lib'; -import { useDispatch } from '../../../../mappings_state_context'; +import { useDispatch, useMappingsState } from '../../../../mappings_state_context'; import { Form, FormDataProvider, UseField, useForm, useFormData } from '../../../../shared_imports'; import { Field, MainType, NormalizedFields } from '../../../../types'; import { NameParameter, SubTypeParameter, TypeParameter } from '../../field_parameters'; @@ -70,7 +70,6 @@ export const CreateField = React.memo(function CreateFieldComponent({ }: Props) { const { isSemanticTextEnabled, indexName, ml, setErrorsInTrainedModelDeployment } = semanticTextInfo ?? {}; - const dispatch = useDispatch(); const { form } = useForm({ @@ -315,8 +314,26 @@ interface InferenceProps { } function InferenceIdCombo({ setValue }: InferenceProps) { + const { inferenceToModelIdMap } = useMappingsState(); + const dispatch = useDispatch(); const [{ type }] = useFormData({ watch: 'type' }); + // update new inferenceEndpoint + const setNewInferenceEndpoint = useCallback( + (newInferenceEndpoint: InferenceToModelIdMap) => { + dispatch({ + type: 'inferenceToModelIdMap.update', + value: { + inferenceToModelIdMap: { + ...inferenceToModelIdMap, + ...newInferenceEndpoint, + }, + }, + }); + }, + [dispatch, inferenceToModelIdMap] + ); + if (type === undefined || type[0]?.value !== 'semantic_text') { return null; } @@ -325,7 +342,13 @@ function InferenceIdCombo({ setValue }: InferenceProps) { <> - {(field) => } + {(field) => ( + + )} ); diff --git a/x-pack/plugins/index_management/public/application/sections/home/index_list/details_page/details_page_mappings_content.tsx b/x-pack/plugins/index_management/public/application/sections/home/index_list/details_page/details_page_mappings_content.tsx index 71181796f6cbfc..b126f5b960a478 100644 --- a/x-pack/plugins/index_management/public/application/sections/home/index_list/details_page/details_page_mappings_content.tsx +++ b/x-pack/plugins/index_management/public/application/sections/home/index_list/details_page/details_page_mappings_content.tsx @@ -62,22 +62,15 @@ export const DetailsPageMappingsContent: FunctionComponent<{ showAboutMappings: boolean; jsonData: any; refetchMapping: () => void; - isSemanticTextEnabled?: boolean; -}> = ({ - index, - data, - jsonData, - refetchMapping, - showAboutMappings, - isSemanticTextEnabled = false, -}) => { +}> = ({ index, data, jsonData, refetchMapping, showAboutMappings }) => { const { services: { extensionsService }, core: { getUrlForApp }, plugins: { ml }, url, + config, } = useAppContext(); - + const { enableSemanticText: isSemanticTextEnabled } = config; const [errorsInTrainedModelDeployment, setErrorsInTrainedModelDeployment] = useState( [] ); diff --git a/x-pack/plugins/index_management/public/hooks/use_details_page_mappings_model_management.ts b/x-pack/plugins/index_management/public/hooks/use_details_page_mappings_model_management.ts index 559fb6a11f13f7..fb27053d35547b 100644 --- a/x-pack/plugins/index_management/public/hooks/use_details_page_mappings_model_management.ts +++ b/x-pack/plugins/index_management/public/hooks/use_details_page_mappings_model_management.ts @@ -5,6 +5,7 @@ * 2.0. */ +import { ElasticsearchModelDefaultOptions, Service } from '@kbn/inference_integration_flyout/types'; import { InferenceStatsResponse } from '@kbn/ml-plugin/public/application/services/ml_api_service/trained_models'; import type { InferenceAPIConfigResponse } from '@kbn/ml-trained-models-utils'; import { useCallback, useMemo } from 'react'; @@ -27,12 +28,16 @@ const getCustomInferenceIdMap = ( ) => { return models?.data.reduce((inferenceMap, model) => { const inferenceId = model.model_id; - const trainedModelId = - 'model_id' in model.service_settings ? model.service_settings.model_id : ''; + const trainedModelId = + 'model_id' in model.service_settings && + (model.service_settings.model_id === ElasticsearchModelDefaultOptions.elser || + model.service_settings.model_id === ElasticsearchModelDefaultOptions.e5) + ? model.service_settings.model_id + : ''; inferenceMap[inferenceId] = { trainedModelId, - isDeployable: model.service === 'elser' || model.service === 'elasticsearch', + isDeployable: model.service === Service.elser || model.service === Service.elasticsearch, isDeployed: deploymentStatsByModelId[trainedModelId] === 'deployed', defaultInferenceEndpoint: false, }; @@ -40,7 +45,7 @@ const getCustomInferenceIdMap = ( }, {}); }; -const getTrainedModelStats = (modelStats?: InferenceStatsResponse): DeploymentStatusType => { +export const getTrainedModelStats = (modelStats?: InferenceStatsResponse): DeploymentStatusType => { return ( modelStats?.trained_model_stats.reduce((acc, modelStat) => { if (modelStat.model_id) { diff --git a/x-pack/plugins/index_management/public/plugin.ts b/x-pack/plugins/index_management/public/plugin.ts index 4e6947b56ba9e4..a94fca4f6198fe 100644 --- a/x-pack/plugins/index_management/public/plugin.ts +++ b/x-pack/plugins/index_management/public/plugin.ts @@ -46,6 +46,7 @@ export class IndexMgmtUIPlugin isIndexManagementUiEnabled: boolean; enableMappingsSourceFieldSection: boolean; enableTogglingDataRetention: boolean; + enableSemanticText: boolean; }; constructor(ctx: PluginInitializerContext) { @@ -62,6 +63,7 @@ export class IndexMgmtUIPlugin enableDataStreamsStorageColumn, enableMappingsSourceFieldSection, enableTogglingDataRetention, + dev: { enableSemanticText }, } = ctx.config.get(); this.config = { isIndexManagementUiEnabled, @@ -72,6 +74,7 @@ export class IndexMgmtUIPlugin enableDataStreamsStorageColumn: enableDataStreamsStorageColumn ?? true, enableMappingsSourceFieldSection: enableMappingsSourceFieldSection ?? true, enableTogglingDataRetention: enableTogglingDataRetention ?? true, + enableSemanticText: enableSemanticText ?? false, }; } diff --git a/x-pack/plugins/index_management/public/types.ts b/x-pack/plugins/index_management/public/types.ts index e6a3b9a611026d..30df6157abd8bf 100644 --- a/x-pack/plugins/index_management/public/types.ts +++ b/x-pack/plugins/index_management/public/types.ts @@ -54,4 +54,7 @@ export interface ClientConfigType { enableDataStreamsStorageColumn?: boolean; enableMappingsSourceFieldSection?: boolean; enableTogglingDataRetention?: boolean; + dev: { + enableSemanticText?: boolean; + }; } diff --git a/x-pack/plugins/index_management/server/config.ts b/x-pack/plugins/index_management/server/config.ts index 4348d7ac2a7748..c213cf5b8a87f3 100644 --- a/x-pack/plugins/index_management/server/config.ts +++ b/x-pack/plugins/index_management/server/config.ts @@ -35,6 +35,8 @@ const schemaLatest = schema.object( dev: schema.object({ // deprecated as unused after index details page has been implemented enableIndexDetailsPage: schema.boolean({ defaultValue: false }), + // deprecate as unused after semantic text is enabled everywhere + enableSemanticText: schema.boolean({ defaultValue: false }), }), enableIndexStats: offeringBasedSchema({ // Index stats information is disabled in serverless; refer to the serverless.yml file as the source of truth @@ -69,6 +71,9 @@ const schemaLatest = schema.object( const configLatest: PluginConfigDescriptor = { exposeToBrowser: { ui: true, + dev: { + enableSemanticText: true, + }, enableIndexActions: true, enableLegacyTemplates: true, enableIndexStats: true, diff --git a/x-pack/plugins/lens/public/datasources/text_based/text_based_languages.test.ts b/x-pack/plugins/lens/public/datasources/text_based/text_based_languages.test.ts index d15723e074667c..8904031615bcff 100644 --- a/x-pack/plugins/lens/public/datasources/text_based/text_based_languages.test.ts +++ b/x-pack/plugins/lens/public/datasources/text_based/text_based_languages.test.ts @@ -835,6 +835,47 @@ describe('Textbased Data Source', () => { isBucketed: false, hasTimeShift: false, hasReducedTimeRange: false, + scale: 'ratio', + }); + }); + + it('should get an operation for col2', () => { + const state = { + layers: { + a: { + columns: [ + { + columnId: 'col1', + fieldName: 'Test 1', + meta: { + type: 'number', + }, + }, + { + columnId: 'col2', + fieldName: 'Test 2', + meta: { + type: 'date', + }, + }, + ], + index: 'foo', + }, + }, + } as unknown as TextBasedPrivateState; + + publicAPI = TextBasedDatasource.getPublicAPI({ + state, + layerId: 'a', + indexPatterns, + }); + expect(publicAPI.getOperationForColumnId('col2')).toEqual({ + label: 'Test 2', + dataType: 'date', + isBucketed: true, + hasTimeShift: false, + hasReducedTimeRange: false, + scale: 'interval', }); }); diff --git a/x-pack/plugins/lens/public/datasources/text_based/text_based_languages.tsx b/x-pack/plugins/lens/public/datasources/text_based/text_based_languages.tsx index 9f5b44e660bd31..85d622380695c3 100644 --- a/x-pack/plugins/lens/public/datasources/text_based/text_based_languages.tsx +++ b/x-pack/plugins/lens/public/datasources/text_based/text_based_languages.tsx @@ -30,6 +30,7 @@ import { DatasourceDimensionTriggerProps, DataSourceInfo, UserMessage, + OperationMetadata, } from '../../types'; import { generateId } from '../../id_generator'; import type { @@ -533,6 +534,18 @@ export function getTextBasedDatasource({ const layer = state.layers[layerId]; const column = layer?.columns?.find((c) => c.columnId === columnId); const columnLabelMap = TextBasedDatasource.uniqueLabels(state, indexPatterns); + let scale: OperationMetadata['scale'] = 'ordinal'; + switch (column?.meta?.type) { + case 'date': + scale = 'interval'; + break; + case 'number': + scale = 'ratio'; + break; + default: + scale = 'ordinal'; + break; + } if (column) { return { @@ -542,6 +555,7 @@ export function getTextBasedDatasource({ inMetricDimension: column.inMetricDimension, hasTimeShift: false, hasReducedTimeRange: false, + scale, }; } return null; diff --git a/x-pack/plugins/lens/public/embeddable/embeddable.tsx b/x-pack/plugins/lens/public/embeddable/embeddable.tsx index daf1d078894e98..366b631f7f54b5 100644 --- a/x-pack/plugins/lens/public/embeddable/embeddable.tsx +++ b/x-pack/plugins/lens/public/embeddable/embeddable.tsx @@ -1148,7 +1148,7 @@ export class Embeddable handleEvent={this.handleEvent} onData$={this.updateActiveData} onRender$={this.onRender} - interactive={!input.disableTriggers && !this.isTextBasedLanguage()} + interactive={!input.disableTriggers} renderMode={input.renderMode} syncColors={input.syncColors} syncTooltips={input.syncTooltips} @@ -1369,6 +1369,7 @@ export class Embeddable } else if (isLensTableRowContextMenuClickEvent(event)) { eventHandler = this.input.onTableRowClick; } + const esqlQuery = this.isTextBasedLanguage() ? this.savedVis?.state.query : undefined; eventHandler?.({ ...event.data, @@ -1384,6 +1385,7 @@ export class Embeddable ...event.data, timeFieldName: event.data.timeFieldName || inferTimeField(this.deps.data.datatableUtilities, event), + query: esqlQuery, }, embeddable: this, }); diff --git a/x-pack/plugins/ml/public/application/components/callout/callout.tsx b/x-pack/plugins/ml/public/application/components/callout/callout.tsx index b2c588377d232f..8c60f87ef24271 100644 --- a/x-pack/plugins/ml/public/application/components/callout/callout.tsx +++ b/x-pack/plugins/ml/public/application/components/callout/callout.tsx @@ -55,7 +55,7 @@ const Message: FC> = ({ text, url }) => ( export const Callout: FC = ({ heading, status, text, url }) => ( <> = ({ }, [chartsData.seriesToPlot, globalTimeRange, selectedCells, bounds, interval]); const isMaxSeriesToPlotValid = - maxSeriesToPlot >= 1 && maxSeriesToPlot <= MAX_ANOMALY_CHARTS_ALLOWED; + typeof maxSeriesToPlot === 'number' && + maxSeriesToPlot >= 1 && + maxSeriesToPlot <= MAX_ANOMALY_CHARTS_ALLOWED; const jobIds = selectedJobs.map(({ id }) => id); @@ -180,7 +182,7 @@ export const AnomalyContextMenu: FC = ({ ({ dashboardId, newTitle, newDescription }) => { const stateTransfer = embeddable!.getStateTransfer(); - const embeddableInput: Partial = { + const embeddableInput: Partial = { ...getEmbeddableInput(), title: newTitle, description: newDescription, diff --git a/x-pack/plugins/ml/public/application/explorer/explorer_charts/constants.ts b/x-pack/plugins/ml/public/application/explorer/explorer_charts/constants.ts index 37ef9bbc72898e..110c053d11a301 100644 --- a/x-pack/plugins/ml/public/application/explorer/explorer_charts/constants.ts +++ b/x-pack/plugins/ml/public/application/explorer/explorer_charts/constants.ts @@ -6,3 +6,4 @@ */ export const TRANSPARENT_BACKGROUND = 'rgba(0, 0, 0, 0)'; +export const CHART_HEIGHT = 170; diff --git a/x-pack/plugins/ml/public/application/explorer/explorer_charts/explorer_anomalies_container.tsx b/x-pack/plugins/ml/public/application/explorer/explorer_charts/explorer_anomalies_container.tsx index 6eaf0fffbf82bc..d1a43559cbc98c 100644 --- a/x-pack/plugins/ml/public/application/explorer/explorer_charts/explorer_anomalies_container.tsx +++ b/x-pack/plugins/ml/public/application/explorer/explorer_charts/explorer_anomalies_container.tsx @@ -62,7 +62,9 @@ export const ExplorerAnomaliesContainer: FC = ( timeRange, }) => { return ( - <> + // TODO: Remove data-shared-item and data-rendering-count as part of https://github.com/elastic/kibana/issues/179376 + // These attributes are temporarily needed for reporting to not have any warning +
@@ -96,9 +98,10 @@ export const ExplorerAnomaliesContainer: FC = ( tooManyBucketsCalloutMsg, showSelectedInterval, chartsService, + id, }} /> )} - +
); }; diff --git a/x-pack/plugins/ml/public/application/explorer/explorer_charts/explorer_chart_distribution.js b/x-pack/plugins/ml/public/application/explorer/explorer_charts/explorer_chart_distribution.js index f1794e812cc7e4..58052f5f35a653 100644 --- a/x-pack/plugins/ml/public/application/explorer/explorer_charts/explorer_chart_distribution.js +++ b/x-pack/plugins/ml/public/application/explorer/explorer_charts/explorer_chart_distribution.js @@ -32,13 +32,17 @@ import { numTicksForDateFormat, removeLabelOverlap, chartExtendedLimits, + LINE_CHART_ANOMALY_RADIUS, } from '../../util/chart_utils'; import { LoadingIndicator } from '../../components/loading_indicator/loading_indicator'; import { CHART_TYPE } from '../explorer_constants'; -import { TRANSPARENT_BACKGROUND } from './constants'; +import { CHART_HEIGHT, TRANSPARENT_BACKGROUND } from './constants'; +import { filter } from 'rxjs'; +import { drawCursor } from './utils/draw_anomaly_explorer_charts_cursor'; const CONTENT_WRAPPER_HEIGHT = 215; +const SCHEDULED_EVENT_MARKER_HEIGHT = 5; // If a rare/event-distribution chart has a cardinality of 10 or less, // then the chart will display the y axis labels for each lane of events. @@ -54,10 +58,32 @@ export class ExplorerChartDistribution extends React.Component { seriesConfig: PropTypes.object, severity: PropTypes.number, tooltipService: PropTypes.object.isRequired, + cursor$: PropTypes.object, }; + constructor(props) { + super(props); + this.chartScales = undefined; + this.cursorStateSubscription = undefined; + } componentDidMount() { this.renderChart(); + this.cursorStateSubscription = this.props.cursor$ + .pipe(filter((c) => c.isDateHistogram)) + .subscribe((cursor) => { + drawCursor( + cursor.cursor, + this.rootNode, + this.props.id, + this.props.seriesConfig, + this.chartScales, + this.props.chartTheme + ); + }); + } + + componentWillUnmount() { + this.cursorStateSubscription?.unsubscribe(); } componentDidUpdate() { @@ -71,8 +97,7 @@ export class ExplorerChartDistribution extends React.Component { timeBuckets, showSelectedInterval, onPointerUpdate, - chartTheme, - cursor, + id: chartId, } = this.props; const element = this.rootNode; @@ -90,10 +115,6 @@ export class ExplorerChartDistribution extends React.Component { ); let vizWidth = 0; - const chartHeight = 170; - const LINE_CHART_ANOMALY_RADIUS = 7; - const SCHEDULED_EVENT_MARKER_HEIGHT = 5; - const chartType = getChartType(config); // Left margin is adjusted later for longest y-axis label. @@ -122,11 +143,12 @@ export class ExplorerChartDistribution extends React.Component { chartElement.select('svg').remove(); const svgWidth = element.clientWidth; - const svgHeight = chartHeight + margin.top + margin.bottom; + const svgHeight = CHART_HEIGHT + margin.top + margin.bottom; const svg = chartElement .append('svg') .classed('ml-explorer-chart-svg', true) + .attr('id', 'ml-explorer-chart-svg' + chartId) .attr('width', svgWidth) .attr('height', svgHeight); @@ -168,7 +190,7 @@ export class ExplorerChartDistribution extends React.Component { lineChartYScale = d3.scale .linear() - .range([chartHeight, 0]) + .range([CHART_HEIGHT, 0]) .domain([yScaleDomainMin < 0 ? yScaleDomainMin : 0, yScaleDomainMax]) .nice(); } else if (chartType === CHART_TYPE.EVENT_DISTRIBUTION) { @@ -176,7 +198,7 @@ export class ExplorerChartDistribution extends React.Component { const rowMargin = 5; lineChartYScale = d3.scale .ordinal() - .rangePoints([rowMargin, chartHeight - rowMargin]) + .rangePoints([rowMargin, CHART_HEIGHT - rowMargin]) .domain(scaleCategories); } else { throw new Error(`chartType '${chartType}' not supported`); @@ -260,7 +282,7 @@ export class ExplorerChartDistribution extends React.Component { .append('rect') .attr('x', 0) .attr('y', 0) - .attr('height', chartHeight) + .attr('height', CHART_HEIGHT) .attr('width', vizWidth) .style('stroke', '#cccccc') .style('fill', 'none') @@ -268,17 +290,17 @@ export class ExplorerChartDistribution extends React.Component { drawRareChartAxes(); drawRareChartHighlightedSpan(); - drawSyncedCursorLine(lineChartGroup); + drawCursorListener(lineChartGroup); drawRareChartDots(data, lineChartGroup, lineChartValuesLine); drawRareChartMarkers(data); } - function drawSyncedCursorLine(lineChartGroup) { + function drawCursorListener(lineChartGroup) { lineChartGroup .append('rect') .attr('x', 0) .attr('y', 0) - .attr('height', chartHeight) + .attr('height', CHART_HEIGHT) .attr('width', vizWidth) .on('mouseout', function () { onPointerUpdate({ @@ -293,46 +315,19 @@ export class ExplorerChartDistribution extends React.Component { .on('mousemove', function () { const mouse = d3.mouse(this); - onPointerUpdate({ - chartId: 'ml-anomaly-chart-metric', - scale: 'time', - smHorizontalValue: null, - smVerticalValue: null, - type: 'Over', - unit: undefined, - x: moment(lineChartXScale.invert(mouse[0])).unix() * 1000, - }); + if (onPointerUpdate) { + onPointerUpdate({ + chartId: 'ml-anomaly-chart-metric', + scale: 'time', + smHorizontalValue: null, + smVerticalValue: null, + type: 'Over', + unit: undefined, + x: moment(lineChartXScale.invert(mouse[0])).unix() * 1000, + }); + } }) .style('fill', TRANSPARENT_BACKGROUND); - - const cursorData = - cursor && - cursor.type === 'Over' && - cursor.x >= config.plotEarliest && - cursor.x <= config.plotLatest - ? [cursor.x] - : []; - - const cursorMouseLine = lineChartGroup - .append('g') - .attr('class', 'ml-anomaly-chart-cursor') - .selectAll('.ml-anomaly-chart-cursor-line') - .data(cursorData); - - cursorMouseLine - .enter() - .append('path') - .attr('class', 'ml-anomaly-chart-cursor-line') - .attr('d', (ts) => { - const xPosition = lineChartXScale(ts); - return `M${xPosition},${chartHeight} ${xPosition},0`; - }) - // Use elastic chart's cursor line style if possible - .style('stroke', chartTheme.crosshair.line.stroke) - .style('stroke-width', `${chartTheme.crosshair.line.strokeWidth}px`) - .style('stroke-dasharray', chartTheme.crosshair.line.dash?.join(',') ?? '4,4'); - - cursorMouseLine.exit().remove(); } function drawRareChartAxes() { @@ -350,7 +345,7 @@ export class ExplorerChartDistribution extends React.Component { .axis() .scale(lineChartXScale) .orient('bottom') - .innerTickSize(-chartHeight) + .innerTickSize(-CHART_HEIGHT) .outerTickSize(0) .tickPadding(10) .tickFormat((d) => moment(d).format(xAxisTickFormat)); @@ -389,7 +384,7 @@ export class ExplorerChartDistribution extends React.Component { const gAxis = axes .append('g') .attr('class', 'x axis') - .attr('transform', 'translate(0,' + chartHeight + ')') + .attr('transform', 'translate(0,' + CHART_HEIGHT + ')') .call(xAxis); axes.append('g').attr('class', 'y axis').call(yAxis); @@ -450,7 +445,7 @@ export class ExplorerChartDistribution extends React.Component { .attr('rx', 3) .attr('ry', 3) .attr('width', rectWidth - 4) - .attr('height', chartHeight - 4); + .attr('height', CHART_HEIGHT - 4); } function drawRareChartMarkers(data) { @@ -635,6 +630,7 @@ export class ExplorerChartDistribution extends React.Component { y: LINE_CHART_ANOMALY_RADIUS * 2, }); } + this.chartScales = { lineChartXScale, margin }; } shouldComponentUpdate() { diff --git a/x-pack/plugins/ml/public/application/explorer/explorer_charts/explorer_chart_distribution.test.js b/x-pack/plugins/ml/public/application/explorer/explorer_charts/explorer_chart_distribution.test.js index ee0c2c41074e08..246e46c50e5f02 100644 --- a/x-pack/plugins/ml/public/application/explorer/explorer_charts/explorer_chart_distribution.test.js +++ b/x-pack/plugins/ml/public/application/explorer/explorer_charts/explorer_chart_distribution.test.js @@ -7,6 +7,7 @@ import { chartData as mockChartData } from './__mocks__/mock_chart_data_rare'; import seriesConfig from './__mocks__/mock_series_config_rare.json'; +import { BehaviorSubject } from 'rxjs'; import { mountWithIntl } from '@kbn/test-jest-helpers'; import { KibanaContextProvider } from '@kbn/kibana-react-plugin/public'; @@ -20,9 +21,7 @@ const utilityProps = { timeBuckets: timeBucketsMock, chartTheme: kibanaContextMock.services.charts.theme.useChartsBaseTheme(), onPointerUpdate: jest.fn(), - cursor: { - x: 10432423, - }, + cursor$: new BehaviorSubject({ isDataHistorgram: true, cursor: { x: 10432423 } }), }; describe('ExplorerChart', () => { diff --git a/x-pack/plugins/ml/public/application/explorer/explorer_charts/explorer_chart_single_metric.js b/x-pack/plugins/ml/public/application/explorer/explorer_charts/explorer_chart_single_metric.js index 3291307710cb24..77118b376e97a7 100644 --- a/x-pack/plugins/ml/public/application/explorer/explorer_charts/explorer_chart_single_metric.js +++ b/x-pack/plugins/ml/public/application/explorer/explorer_charts/explorer_chart_single_metric.js @@ -39,14 +39,15 @@ import { getMultiBucketImpactTooltipValue, } from '../../util/chart_utils'; import { LoadingIndicator } from '../../components/loading_indicator/loading_indicator'; -import { TRANSPARENT_BACKGROUND } from './constants'; +import { CHART_HEIGHT, TRANSPARENT_BACKGROUND } from './constants'; +import { filter } from 'rxjs'; +import { drawCursor } from './utils/draw_anomaly_explorer_charts_cursor'; const CONTENT_WRAPPER_HEIGHT = 215; const CONTENT_WRAPPER_CLASS = 'ml-explorer-chart-content-wrapper'; export class ExplorerChartSingleMetric extends React.Component { static contextType = context; - static propTypes = { tooManyBuckets: PropTypes.bool, seriesConfig: PropTypes.object, @@ -55,11 +56,33 @@ export class ExplorerChartSingleMetric extends React.Component { timeBuckets: PropTypes.object.isRequired, onPointerUpdate: PropTypes.func.isRequired, chartTheme: PropTypes.object.isRequired, - cursor: PropTypes.object, + cursor$: PropTypes.object, + id: PropTypes.string.isRequired, }; + constructor(props) { + super(props); + this.chartScales = undefined; + } componentDidMount() { this.renderChart(); + + this.cursorStateSubscription = this.props.cursor$ + .pipe(filter((c) => c.isDateHistogram)) + .subscribe((cursor) => { + drawCursor( + cursor.cursor, + this.rootNode, + this.props.id, + this.props.seriesConfig, + this.chartScales, + this.props.chartTheme + ); + }); + } + + componentWillUnmount() { + this.cursorStateSubscription?.unsubscribe(); } componentDidUpdate() { @@ -73,8 +96,7 @@ export class ExplorerChartSingleMetric extends React.Component { timeBuckets, showSelectedInterval, onPointerUpdate, - chartTheme, - cursor, + id: chartId, } = this.props; const element = this.rootNode; @@ -92,7 +114,6 @@ export class ExplorerChartSingleMetric extends React.Component { ); let vizWidth = 0; - const chartHeight = 170; // Left margin is adjusted later for longest y-axis label. const margin = { top: 10, right: 0, bottom: 30, left: 60 }; @@ -112,18 +133,19 @@ export class ExplorerChartSingleMetric extends React.Component { chartElement.select('svg').remove(); const svgWidth = element.clientWidth; - const svgHeight = chartHeight + margin.top + margin.bottom; + const svgHeight = CHART_HEIGHT + margin.top + margin.bottom; const svg = chartElement .append('svg') .classed('ml-explorer-chart-svg', true) + .attr('id', 'ml-explorer-chart-svg' + chartId) .attr('width', svgWidth) .attr('height', svgHeight); // Set the size of the left margin according to the width of the largest y axis tick label. lineChartYScale = d3.scale .linear() - .range([chartHeight, 0]) + .range([CHART_HEIGHT, 0]) .domain([chartLimits.min, chartLimits.max]) .nice(); @@ -188,7 +210,7 @@ export class ExplorerChartSingleMetric extends React.Component { .append('rect') .attr('x', 0) .attr('y', 0) - .attr('height', chartHeight) + .attr('height', CHART_HEIGHT) .attr('width', vizWidth) .style('stroke', '#cccccc') .style('fill', 'none') @@ -196,18 +218,18 @@ export class ExplorerChartSingleMetric extends React.Component { drawLineChartAxes(); drawLineChartHighlightedSpan(); - drawSyncedCursorLine(lineChartGroup); + drawCursorListener(lineChartGroup); drawLineChartPaths(data); drawLineChartDots(data, lineChartGroup, lineChartValuesLine); drawLineChartMarkers(data); } - function drawSyncedCursorLine(lineChartGroup) { + function drawCursorListener(lineChartGroup) { lineChartGroup .append('rect') .attr('x', 0) .attr('y', 0) - .attr('height', chartHeight) + .attr('height', CHART_HEIGHT) .attr('width', vizWidth) .on('mouseout', function () { onPointerUpdate({ @@ -235,35 +257,6 @@ export class ExplorerChartSingleMetric extends React.Component { } }) .style('fill', TRANSPARENT_BACKGROUND); - - const cursorData = - cursor && - cursor.type === 'Over' && - cursor.x >= config.plotEarliest && - cursor.x <= config.plotLatest - ? [cursor.x] - : []; - - const cursorMouseLine = lineChartGroup - .append('g') - .attr('class', 'ml-anomaly-chart-cursor') - .selectAll('.ml-anomaly-chart-cursor-line') - .data(cursorData); - - cursorMouseLine - .enter() - .append('path') - .attr('class', 'ml-anomaly-chart-cursor-line') - .attr('d', (ts) => { - const xPosition = lineChartXScale(ts); - return `M${xPosition},${chartHeight} ${xPosition},0`; - }) - // Use elastic chart's cursor line style if possible - .style('stroke', chartTheme.crosshair.line.stroke) - .style('stroke-width', `${chartTheme.crosshair.line.strokeWidth}px`) - .style('stroke-dasharray', chartTheme.crosshair.line.dash?.join(',') ?? '4,4'); - - cursorMouseLine.exit().remove(); } function drawLineChartAxes() { @@ -281,7 +274,7 @@ export class ExplorerChartSingleMetric extends React.Component { .axis() .scale(lineChartXScale) .orient('bottom') - .innerTickSize(-chartHeight) + .innerTickSize(-CHART_HEIGHT) .outerTickSize(0) .tickPadding(10) .tickFormat((d) => moment(d).format(xAxisTickFormat)); @@ -320,7 +313,7 @@ export class ExplorerChartSingleMetric extends React.Component { const gAxis = axes .append('g') .attr('class', 'x axis') - .attr('transform', 'translate(0,' + chartHeight + ')') + .attr('transform', 'translate(0,' + CHART_HEIGHT + ')') .call(xAxis); axes.append('g').attr('class', 'y axis').call(yAxis); @@ -348,7 +341,7 @@ export class ExplorerChartSingleMetric extends React.Component { .attr('rx', 3) .attr('ry', 3) .attr('width', rectWidth - 4) - .attr('height', chartHeight - 4); + .attr('height', CHART_HEIGHT - 4); } function drawLineChartPaths(data) { @@ -583,6 +576,8 @@ export class ExplorerChartSingleMetric extends React.Component { y: LINE_CHART_ANOMALY_RADIUS * 2, }); } + + this.chartScales = { lineChartXScale, margin }; } shouldComponentUpdate() { diff --git a/x-pack/plugins/ml/public/application/explorer/explorer_charts/explorer_chart_single_metric.test.js b/x-pack/plugins/ml/public/application/explorer/explorer_charts/explorer_chart_single_metric.test.js index 063762972e2689..70eb254f990724 100644 --- a/x-pack/plugins/ml/public/application/explorer/explorer_charts/explorer_chart_single_metric.test.js +++ b/x-pack/plugins/ml/public/application/explorer/explorer_charts/explorer_chart_single_metric.test.js @@ -15,14 +15,13 @@ import React from 'react'; import { ExplorerChartSingleMetric } from './explorer_chart_single_metric'; import { timeBucketsMock } from '../../util/__mocks__/time_buckets'; import { kibanaContextMock } from '../../contexts/kibana/__mocks__/kibana_context'; +import { BehaviorSubject } from 'rxjs'; const utilityProps = { timeBuckets: timeBucketsMock, chartTheme: kibanaContextMock.services.charts.theme.useChartsBaseTheme(), onPointerUpdate: jest.fn(), - cursor: { - x: 10432423, - }, + cursor$: new BehaviorSubject({ isDataHistorgram: true, cursor: { x: 10432423 } }), }; describe('ExplorerChart', () => { diff --git a/x-pack/plugins/ml/public/application/explorer/explorer_charts/explorer_charts_container.js b/x-pack/plugins/ml/public/application/explorer/explorer_charts/explorer_charts_container.js index 9e84a1f546b04f..702aeed891ebc0 100644 --- a/x-pack/plugins/ml/public/application/explorer/explorer_charts/explorer_charts_container.js +++ b/x-pack/plugins/ml/public/application/explorer/explorer_charts/explorer_charts_container.js @@ -44,7 +44,6 @@ import { addItemToRecentlyAccessed } from '../../util/recently_accessed'; import { EmbeddedMapComponentWrapper } from './explorer_chart_embedded_map'; import { useActiveCursor } from '@kbn/charts-plugin/public'; import { BarSeries, Chart, Settings, LEGACY_LIGHT_THEME } from '@elastic/charts'; -import useObservable from 'react-use/lib/useObservable'; import { escapeKueryForFieldValuePair } from '../../util/string_utils'; const textTooManyBuckets = i18n.translate('xpack.ml.explorer.charts.tooManyBucketsDescription', { @@ -78,15 +77,15 @@ export function getEntitiesQuery(series) { // create a somewhat unique ID // from charts metadata for React's key attribute -function getChartId(series) { - const { jobId, detectorLabel, entityFields } = series; - const entities = entityFields.map((ef) => `${ef.fieldName}/${ef.fieldValue}`).join(','); - const id = `${jobId}_${detectorLabel}_${entities}`; +function getChartId(series, randomId) { + const { jobId, detectorLabel } = series; + const id = `${jobId}${detectorLabel}`.replace(/[^a-zA-Z]+/g, '') + randomId; return id; } // Wrapper for a single explorer chart function ExplorerChartContainer({ + id, series, severity, tooManyBuckets, @@ -194,8 +193,6 @@ function ExplorerChartContainer({ isDateHistogram: true, }); - const cursor = useObservable(chartsService.activeCursor.activeCursor$)?.cursor; - const addToRecentlyAccessed = useCallback(() => { if (recentlyAccessed) { addItemToRecentlyAccessed( @@ -333,6 +330,7 @@ function ExplorerChartContainer({ {(tooltipService) => ( )} @@ -352,6 +350,7 @@ function ExplorerChartContainer({ {(tooltipService) => ( )} @@ -373,6 +372,7 @@ function ExplorerChartContainer({ // Flex layout wrapper for all explorer charts export const ExplorerChartsContainerUI = ({ + id: uuid, chartsPerRow, seriesToPlot, severity, @@ -422,6 +422,7 @@ export const ExplorerChartsContainerUI = ({ const chartsColumns = chartsPerRow === 1 ? 0 : chartsPerRow; const wrapLabel = seriesToUse.some((series) => isLabelLengthAboveThreshold(series)); + return ( <> @@ -431,29 +432,34 @@ export const ExplorerChartsContainerUI = ({ data-test-subj="mlExplorerChartsContainer" > {seriesToUse.length > 0 && - seriesToUse.map((series) => ( - - - - ))} + seriesToUse.map((series, idx) => { + const chartId = getChartId(series, '-' + (uuid ?? '') + idx); + return ( + + + + ); + })} ); diff --git a/x-pack/plugins/ml/public/application/explorer/explorer_charts/utils/draw_anomaly_explorer_charts_cursor.ts b/x-pack/plugins/ml/public/application/explorer/explorer_charts/utils/draw_anomaly_explorer_charts_cursor.ts new file mode 100644 index 00000000000000..dae80948e16e70 --- /dev/null +++ b/x-pack/plugins/ml/public/application/explorer/explorer_charts/utils/draw_anomaly_explorer_charts_cursor.ts @@ -0,0 +1,67 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ +import d3 from 'd3'; +import type { PartialTheme } from '@elastic/charts'; +import type { PointerEvent } from '@elastic/charts'; +import { CHART_HEIGHT } from '../constants'; +interface ChartScales { + lineChartXScale: (value: number | null | string) => number; + margin: { left: number; right: number; top: number; bottom: number }; +} +export function drawCursor( + cursor: Required, + rootNode: HTMLDivElement, + chartId: string, + config: { plotEarliest: number; plotLatest: number }, + chartScales: ChartScales, + chartTheme: PartialTheme +) { + if (!chartScales) return; + const { lineChartXScale, margin: updatedMargin } = chartScales; + + const element = rootNode; + const chartElement = d3.select(element).select('#ml-explorer-chart-svg' + chartId); + if (!chartElement || !lineChartXScale) return; + chartElement.select('.ml-anomaly-chart-cursor-line').remove(); + + const crosshairLine = chartTheme?.crosshair?.line ?? { + visible: true, + stroke: '#69707D', + strokeWidth: 1, + dash: [4, 4], + }; + const cursorData = + cursor && + cursor.type === 'Over' && + cursor.x !== null && + cursor.x >= config.plotEarliest && + cursor.x <= config.plotLatest + ? [cursor.x] + : []; + + const cursorMouseLine = chartElement + .append('g') + .attr('class', 'ml-anomaly-chart-cursor') + .selectAll('.ml-anomaly-chart-cursor-line') + .data(cursorData); + + // @ts-expect-error d3 types are not up to date + cursorMouseLine + .enter() + .append('path') + .attr('class', 'ml-anomaly-chart-cursor-line') + .attr('d', (ts) => { + const xPosition = lineChartXScale(ts); + return `M${xPosition},${CHART_HEIGHT} ${xPosition},0`; + }) + // Use elastic chart's cursor line style if possible + .style('stroke', crosshairLine.stroke) + .style('stroke-width', `${crosshairLine.strokeWidth}px`) + .style('stroke-dasharray', crosshairLine.dash?.join(',') ?? '4,4') + .attr('transform', 'translate(' + updatedMargin.left + ',' + updatedMargin.top + ')'); + cursorMouseLine.exit().remove(); +} diff --git a/x-pack/plugins/ml/public/application/jobs/new_job/common/components/time_range_picker.tsx b/x-pack/plugins/ml/public/application/jobs/new_job/common/components/time_range_picker.tsx index b9332e04144c06..c086320718a524 100644 --- a/x-pack/plugins/ml/public/application/jobs/new_job/common/components/time_range_picker.tsx +++ b/x-pack/plugins/ml/public/application/jobs/new_job/common/components/time_range_picker.tsx @@ -69,6 +69,9 @@ export const TimeRangePicker: FC = ({ setTimeRange, timeRange }) => { fullWidth={true} startDateControl={ = ({ setTimeRange, timeRange }) => { } endDateControl={ { {showCallOut && ( = ({ defaultMessage: 'Time range', }), ...createStepProps(WIZARD_STEPS.TIME_RANGE), + 'data-test-subj': 'mlJobWizardTimeRangeStep', }, { title: i18n.translate('xpack.ml.newJob.wizard.step.pickFieldsTitle', { defaultMessage: 'Choose fields', }), ...createStepProps(WIZARD_STEPS.PICK_FIELDS), + 'data-test-subj': 'mlJobWizardPickFieldsStep', }, { title: i18n.translate('xpack.ml.newJob.wizard.step.jobDetailsTitle', { defaultMessage: 'Job details', }), ...createStepProps(WIZARD_STEPS.JOB_DETAILS), + 'data-test-subj': 'mlJobWizardJobDetailsStep', }, { title: i18n.translate('xpack.ml.newJob.wizard.step.validationTitle', { defaultMessage: 'Validation', }), ...createStepProps(WIZARD_STEPS.VALIDATION), + 'data-test-subj': 'mlJobWizardValidationStep', }, { title: i18n.translate('xpack.ml.newJob.wizard.step.summaryTitle', { defaultMessage: 'Summary', }), ...createStepProps(WIZARD_STEPS.SUMMARY), + 'data-test-subj': 'mlJobWizardSummaryStep', }, ]; @@ -75,6 +80,7 @@ export const WizardHorizontalSteps: FC = ({ defaultMessage: 'Configure datafeed', }), ...createStepProps(WIZARD_STEPS.ADVANCED_CONFIGURE_DATAFEED), + 'data-test-subj': 'mlJobWizardAdvancedStep', }); } diff --git a/x-pack/plugins/ml/public/cases/anomaly_charts_attachments.tsx b/x-pack/plugins/ml/public/cases/anomaly_charts_attachments.tsx index 8b4ee9eb77b769..71b854100bd4d7 100644 --- a/x-pack/plugins/ml/public/cases/anomaly_charts_attachments.tsx +++ b/x-pack/plugins/ml/public/cases/anomaly_charts_attachments.tsx @@ -5,20 +5,94 @@ * 2.0. */ -import type { FC } from 'react'; -import React from 'react'; +import React, { useMemo } from 'react'; import { memoize } from 'lodash'; import deepEqual from 'fast-deep-equal'; - +import { KibanaRenderContextProvider } from '@kbn/react-kibana-context-render'; +import { KibanaContextProvider } from '@kbn/kibana-react-plugin/public'; +import { BehaviorSubject } from 'rxjs'; +import { css } from '@emotion/react'; import { FormattedMessage } from '@kbn/i18n-react'; import type { PersistableStateAttachmentViewProps } from '@kbn/cases-plugin/public/client/attachment_framework/types'; import { FIELD_FORMAT_IDS } from '@kbn/field-formats-plugin/common'; -import { EuiDescriptionList } from '@elastic/eui'; +import { EuiDescriptionList, htmlIdGenerator } from '@elastic/eui'; import type { FieldFormatsStart } from '@kbn/field-formats-plugin/public'; -import type { AnomalyChartsEmbeddableInput } from '../embeddables'; +import type { Filter, Query, TimeRange } from '@kbn/es-query'; +import { isPopulatedObject } from '@kbn/ml-is-populated-object'; +import { LazyAnomalyChartsContainer } from '../embeddables/anomaly_charts/lazy_anomaly_charts_container'; +import { initializeAnomalyChartsControls } from '../embeddables/anomaly_charts/initialize_anomaly_charts_controls'; +import type { + AnomalyChartsEmbeddableServices, + AnomalyChartsAttachmentState, + AnomalyChartsAttachmentApi, +} from '../embeddables'; + +interface AnomalyChartsCaseAttachmentProps extends AnomalyChartsAttachmentState { + services: AnomalyChartsEmbeddableServices; +} +const AnomalyChartsCaseAttachment = ({ + services, + ...rawState +}: AnomalyChartsCaseAttachmentProps) => { + const id = useMemo(() => htmlIdGenerator()(), []); + const [coreStartServices, pluginStartServices, mlServices] = services; + const contextServices = useMemo( + () => ({ + mlServices: { + ...mlServices, + }, + ...pluginStartServices, + ...coreStartServices, + }), + // eslint-disable-next-line react-hooks/exhaustive-deps + [] + ); + + const api = useMemo(() => { + const initialState: AnomalyChartsAttachmentState = rawState ?? {}; + const filters$ = new BehaviorSubject(initialState.filters ?? []); + const query$ = new BehaviorSubject(initialState.query ?? undefined); + const timeRange$ = new BehaviorSubject(initialState.timeRange); -export const initComponent = memoize( - (fieldFormats: FieldFormatsStart, EmbeddableComponent: FC) => { + const anomalyChartsApi = initializeAnomalyChartsControls(initialState); + const combined: AnomalyChartsAttachmentApi = { + ...anomalyChartsApi.anomalyChartsControlsApi, + ...anomalyChartsApi.dataLoadingApi, + parentApi: { filters$, query$, timeRange$ }, + }; + return combined; + // Initialize services upon first mount already, + // as state management for cases + // already handled by the initial state + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []); + + return ( +
+ + + + + +
+ ); +}; + +function isValidTimeRange(arg: unknown): arg is TimeRange { + return isPopulatedObject(arg, ['from', 'to']); +} + +export const initializeAnomalyChartsAttachment = memoize( + (fieldFormats: FieldFormatsStart, services: AnomalyChartsEmbeddableServices) => { return React.memo( (props: PersistableStateAttachmentViewProps) => { const { persistableStateAttachmentState } = props; @@ -28,46 +102,59 @@ export const initComponent = memoize( }); const inputProps = - persistableStateAttachmentState as unknown as AnomalyChartsEmbeddableInput; + persistableStateAttachmentState as unknown as AnomalyChartsAttachmentState; + + const descriptions = useMemo(() => { + const listItems = [ + { + title: ( + + ), + description: inputProps.jobIds.join(', '), + }, + ]; - const listItems = [ - { - title: ( - - ), - description: inputProps.jobIds.join(', '), - }, - { - title: ( - - ), - description: `${dataFormatter.convert( - inputProps.timeRange.from - )} - ${dataFormatter.convert(inputProps.timeRange.to)}`, - }, - ]; + if (isValidTimeRange(inputProps.timeRange)) { + listItems.push({ + title: ( + + ), + description: `${dataFormatter.convert( + inputProps.timeRange.from + )} - ${dataFormatter.convert(inputProps.timeRange.to)}`, + }); + } - if (typeof inputProps.query?.query === 'string' && inputProps.query?.query !== '') { - listItems.push({ - title: ( - - ), - description: inputProps.query?.query, - }); - } + if (typeof inputProps.query?.query === 'string' && inputProps.query?.query !== '') { + listItems.push({ + title: ( + + ), + description: inputProps.query?.query, + }); + } + return listItems; + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [ + dataFormatter, + inputProps.jobIds, + inputProps.query?.query, + inputProps.timeRange?.from, + inputProps.timeRange?.to, + ]); return ( <> - - + + ); }, diff --git a/x-pack/plugins/ml/public/cases/register_anomaly_charts_attachment.tsx b/x-pack/plugins/ml/public/cases/register_anomaly_charts_attachment.tsx index bb22df1d199346..cc5b00a35b8057 100644 --- a/x-pack/plugins/ml/public/cases/register_anomaly_charts_attachment.tsx +++ b/x-pack/plugins/ml/public/cases/register_anomaly_charts_attachment.tsx @@ -11,21 +11,15 @@ import { FormattedMessage } from '@kbn/i18n-react'; import type { CasesPublicSetup } from '@kbn/cases-plugin/public'; import type { CoreStart } from '@kbn/core/public'; import { CASE_ATTACHMENT_TYPE_ID_ANOMALY_EXPLORER_CHARTS } from '../../common/constants/cases'; -import { getEmbeddableComponent } from '../embeddables'; import type { MlStartDependencies } from '../plugin'; import { PLUGIN_ICON } from '../../common/constants/app'; +import { getAnomalyChartsServiceDependencies } from '../embeddables/anomaly_charts/get_anomaly_charts_services_dependencies'; export function registerAnomalyChartsCasesAttachment( cases: CasesPublicSetup, coreStart: CoreStart, pluginStart: MlStartDependencies ) { - const EmbeddableComponent = getEmbeddableComponent( - CASE_ATTACHMENT_TYPE_ID_ANOMALY_EXPLORER_CHARTS, - coreStart, - pluginStart - ); - cases.attachmentFramework.registerPersistableState({ id: CASE_ATTACHMENT_TYPE_ID_ANOMALY_EXPLORER_CHARTS, icon: PLUGIN_ICON, @@ -41,9 +35,11 @@ export function registerAnomalyChartsCasesAttachment( ), timelineAvatar: PLUGIN_ICON, children: React.lazy(async () => { - const { initComponent } = await import('./anomaly_charts_attachments'); + const { initializeAnomalyChartsAttachment } = await import('./anomaly_charts_attachments'); + const services = await getAnomalyChartsServiceDependencies(coreStart, pluginStart); + return { - default: initComponent(pluginStart.fieldFormats, EmbeddableComponent), + default: initializeAnomalyChartsAttachment(pluginStart.fieldFormats, services), }; }), }), diff --git a/x-pack/plugins/ml/public/embeddables/anomaly_charts/__snapshots__/embeddable_anomaly_charts_container.test.tsx.snap b/x-pack/plugins/ml/public/embeddables/anomaly_charts/__snapshots__/embeddable_anomaly_charts_container.test.tsx.snap deleted file mode 100644 index f40d239b65be3a..00000000000000 --- a/x-pack/plugins/ml/public/embeddables/anomaly_charts/__snapshots__/embeddable_anomaly_charts_container.test.tsx.snap +++ /dev/null @@ -1,64 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`EmbeddableAnomalyChartsContainer should render explorer charts with a valid embeddable input 1`] = ` -Object { - "chartsData": Object { - "chartsPerRow": 2, - "errorMessages": undefined, - "seriesToPlot": Array [], - "timeFieldName": "@timestamp", - "tooManyBuckets": false, - }, - "chartsService": undefined, - "id": "test-explorer-charts-embeddable", - "mlLocator": undefined, - "onSelectEntity": [Function], - "setSeverity": [Function], - "severity": Object { - "color": "#fe5050", - "display": "critical", - "val": 75, - }, - "showCharts": true, - "showSelectedInterval": false, - "timeBuckets": TimeBuckets { - "_fieldFormats": undefined, - "_timeBucketsConfig": Object { - "dateFormat": undefined, - "dateFormat:scaled": undefined, - "histogram:barTarget": undefined, - "histogram:maxBars": undefined, - }, - "barTarget": undefined, - "maxBars": undefined, - }, - "timeRange": undefined, - "timefilter": Object { - "calculateBounds": [MockFunction], - "createFilter": [MockFunction], - "createRelativeFilter": [MockFunction], - "disableAutoRefreshSelector": [MockFunction], - "disableTimeRangeSelector": [MockFunction], - "enableAutoRefreshSelector": [MockFunction], - "enableTimeRangeSelector": [MockFunction], - "getAbsoluteTime": [MockFunction], - "getActiveBounds": [MockFunction], - "getAutoRefreshFetch$": [MockFunction], - "getBounds": [MockFunction], - "getEnabledUpdated$": [MockFunction], - "getFetch$": [MockFunction], - "getRefreshInterval": [MockFunction], - "getRefreshIntervalDefaults": [MockFunction], - "getRefreshIntervalUpdate$": [MockFunction], - "getTime": [MockFunction], - "getTimeDefaults": [MockFunction], - "getTimeUpdate$": [MockFunction], - "isAutoRefreshSelectorEnabled": [MockFunction], - "isRefreshIntervalTouched": [MockFunction], - "isTimeRangeSelectorEnabled": [MockFunction], - "isTimeTouched": [MockFunction], - "setRefreshInterval": [MockFunction], - "setTime": [MockFunction], - }, -} -`; diff --git a/x-pack/plugins/ml/public/embeddables/anomaly_charts/anomaly_charts_embeddable.tsx b/x-pack/plugins/ml/public/embeddables/anomaly_charts/anomaly_charts_embeddable.tsx deleted file mode 100644 index 9d7b08fa935d09..00000000000000 --- a/x-pack/plugins/ml/public/embeddables/anomaly_charts/anomaly_charts_embeddable.tsx +++ /dev/null @@ -1,143 +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, { Suspense } from 'react'; -import ReactDOM from 'react-dom'; -import type { CoreStart } from '@kbn/core/public'; -import { i18n } from '@kbn/i18n'; -import { Subject, Subscription, type BehaviorSubject } from 'rxjs'; -import { KibanaContextProvider } from '@kbn/kibana-react-plugin/public'; -import { KibanaRenderContextProvider } from '@kbn/react-kibana-context-render'; -import type { IContainer } from '@kbn/embeddable-plugin/public'; -import { embeddableInputToSubject } from '@kbn/embeddable-plugin/public'; -import { embeddableOutputToSubject } from '@kbn/embeddable-plugin/public'; -import type { MlEntityField } from '@kbn/ml-anomaly-utils'; -import { EmbeddableAnomalyChartsContainer } from './embeddable_anomaly_charts_container_lazy'; -import type { JobId } from '../../../common/types/anomaly_detection_jobs'; -import type { MlDependencies } from '../../application/app'; -import type { - AnomalyChartsEmbeddableInput, - AnomalyChartsEmbeddableOutput, - AnomalyChartsServices, -} from '..'; -import { ANOMALY_EXPLORER_CHARTS_EMBEDDABLE_TYPE } from '..'; -import { EmbeddableLoading } from '../common/components/embeddable_loading_fallback'; -import { AnomalyDetectionEmbeddable } from '../common/anomaly_detection_embeddable'; - -export const getDefaultExplorerChartsPanelTitle = (jobIds: JobId[]) => - i18n.translate('xpack.ml.anomalyChartsEmbeddable.title', { - defaultMessage: 'ML anomaly charts for {jobIds}', - values: { jobIds: jobIds.join(', ') }, - }); - -export type IAnomalyChartsEmbeddable = typeof AnomalyChartsEmbeddable; - -export class AnomalyChartsEmbeddable extends AnomalyDetectionEmbeddable< - AnomalyChartsEmbeddableInput, - AnomalyChartsEmbeddableOutput -> { - private node?: HTMLElement; - private reload$ = new Subject(); - public readonly type: string = ANOMALY_EXPLORER_CHARTS_EMBEDDABLE_TYPE; - - // API - public readonly jobIds: BehaviorSubject; - public entityFields: BehaviorSubject; - - private apiSubscriptions = new Subscription(); - - constructor( - initialInput: AnomalyChartsEmbeddableInput, - public services: [CoreStart, MlDependencies, AnomalyChartsServices], - parent?: IContainer - ) { - super(initialInput, services[2].anomalyDetectorService, services[1].data.dataViews, parent); - - this.jobIds = embeddableInputToSubject( - this.apiSubscriptions, - this, - 'jobIds' - ); - - this.entityFields = embeddableOutputToSubject( - this.apiSubscriptions, - this, - 'entityFields' - ); - } - - public onLoading() { - this.renderComplete.dispatchInProgress(); - this.updateOutput({ loading: true, error: undefined }); - } - - public onError(error: Error) { - this.renderComplete.dispatchError(); - this.updateOutput({ loading: false, error: { name: error.name, message: error.message } }); - } - - public onRenderComplete() { - this.renderComplete.dispatchComplete(); - this.updateOutput({ loading: false, rendered: true, error: undefined }); - } - - public render(node: HTMLElement) { - super.render(node); - this.node = node; - - // required for the export feature to work - this.node.setAttribute('data-shared-item', ''); - - ReactDOM.render( - - - }> - - - - , - node - ); - } - - public destroy() { - super.destroy(); - - this.apiSubscriptions.unsubscribe(); - - if (this.node) { - ReactDOM.unmountComponentAtNode(this.node); - } - } - - public reload() { - this.reload$.next(); - } - - public supportedTriggers() { - return []; - } -} diff --git a/x-pack/plugins/ml/public/embeddables/anomaly_charts/anomaly_charts_embeddable_factory.test.ts b/x-pack/plugins/ml/public/embeddables/anomaly_charts/anomaly_charts_embeddable_factory.test.ts deleted file mode 100644 index 4d82ca0b430c21..00000000000000 --- a/x-pack/plugins/ml/public/embeddables/anomaly_charts/anomaly_charts_embeddable_factory.test.ts +++ /dev/null @@ -1,56 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { AnomalyChartsEmbeddableFactory } from './anomaly_charts_embeddable_factory'; -import { coreMock } from '@kbn/core/public/mocks'; -import { dataPluginMock } from '@kbn/data-plugin/public/mocks'; -import { AnomalyChartsEmbeddable } from './anomaly_charts_embeddable'; -import type { AnomalyChartsEmbeddableInput } from '..'; - -jest.mock('./anomaly_charts_embeddable', () => ({ - AnomalyChartsEmbeddable: jest.fn(), -})); - -describe('AnomalyChartsEmbeddableFactory', () => { - test('should provide required services on create', async () => { - // arrange - const pluginStartDeps = { data: dataPluginMock.createStartContract() }; - - const getStartServices = coreMock.createSetup({ - pluginStartDeps, - }).getStartServices; - - const [coreStart, pluginsStart] = await getStartServices(); - - // act - const factory = new AnomalyChartsEmbeddableFactory(getStartServices); - - await factory.create({ - jobIds: ['test-job'], - maxSeriesToPlot: 4, - } as AnomalyChartsEmbeddableInput); - - // assert - const mockCalls = (AnomalyChartsEmbeddable as unknown as jest.Mock) - .mock.calls[0]; - const input = mockCalls[0]; - const createServices = mockCalls[1]; - - expect(input).toEqual({ - jobIds: ['test-job'], - maxSeriesToPlot: 4, - }); - expect(Object.keys(createServices[0])).toEqual(Object.keys(coreStart)); - expect(createServices[1]).toMatchObject(pluginsStart); - expect(Object.keys(createServices[2])).toEqual([ - 'anomalyDetectorService', - 'anomalyExplorerService', - 'mlFieldFormatService', - 'mlResultsService', - ]); - }); -}); diff --git a/x-pack/plugins/ml/public/embeddables/anomaly_charts/anomaly_charts_embeddable_factory.ts b/x-pack/plugins/ml/public/embeddables/anomaly_charts/anomaly_charts_embeddable_factory.ts deleted file mode 100644 index 0ab25954ae7c09..00000000000000 --- a/x-pack/plugins/ml/public/embeddables/anomaly_charts/anomaly_charts_embeddable_factory.ts +++ /dev/null @@ -1,123 +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 { i18n } from '@kbn/i18n'; - -import type { StartServicesAccessor } from '@kbn/core/public'; - -import type { EmbeddableFactoryDefinition, IContainer } from '@kbn/embeddable-plugin/public'; -import { PLUGIN_ICON, PLUGIN_ID, ML_APP_NAME } from '../../../common/constants/app'; -import { HttpService } from '../../application/services/http_service'; -import type { MlPluginStart, MlStartDependencies } from '../../plugin'; -import type { MlDependencies } from '../../application/app'; -import type { AnomalyChartsEmbeddableInput, AnomalyChartsEmbeddableServices } from '..'; -import { ANOMALY_EXPLORER_CHARTS_EMBEDDABLE_TYPE } from '..'; -import { AnomalyExplorerChartsService } from '../../application/services/anomaly_explorer_charts_service'; - -export class AnomalyChartsEmbeddableFactory - implements EmbeddableFactoryDefinition -{ - public readonly type = ANOMALY_EXPLORER_CHARTS_EMBEDDABLE_TYPE; - - public readonly grouping = [ - { - id: PLUGIN_ID, - getDisplayName: () => ML_APP_NAME, - getIconType: () => PLUGIN_ICON, - }, - ]; - - constructor( - private getStartServices: StartServicesAccessor - ) {} - - public async isEditable() { - return true; - } - - public getDisplayName() { - return i18n.translate('xpack.ml.components.mlAnomalyExplorerEmbeddable.displayName', { - defaultMessage: 'Anomaly chart', - }); - } - - public getDescription() { - return i18n.translate('xpack.ml.components.mlAnomalyExplorerEmbeddable.description', { - defaultMessage: 'View anomaly detection results in a chart.', - }); - } - - public async getExplicitInput(): Promise> { - const [coreStart, deps] = await this.getServices(); - - try { - const { resolveEmbeddableAnomalyChartsUserInput } = await import( - './anomaly_charts_setup_flyout' - ); - return await resolveEmbeddableAnomalyChartsUserInput(coreStart, deps.data.dataViews); - } catch (e) { - return Promise.reject(); - } - } - - private async getServices(): Promise { - const [ - [coreStart, pluginsStart], - { AnomalyDetectorService }, - { fieldFormatServiceFactory }, - { indexServiceFactory }, - { mlApiServicesProvider }, - { mlResultsServiceProvider }, - ] = await Promise.all([ - await this.getStartServices(), - await import('../../application/services/anomaly_detector_service'), - await import('../../application/services/field_format_service_factory'), - await import('../../application/util/index_service'), - await import('../../application/services/ml_api_service'), - await import('../../application/services/results_service'), - ]); - - const httpService = new HttpService(coreStart.http); - const anomalyDetectorService = new AnomalyDetectorService(httpService); - const mlApiServices = mlApiServicesProvider(httpService); - const mlResultsService = mlResultsServiceProvider(mlApiServices); - const anomalyExplorerService = new AnomalyExplorerChartsService( - pluginsStart.data.query.timefilter.timefilter, - mlApiServices, - mlResultsService - ); - - // Note on the following services: - // - `mlIndexUtils` is just instantiated here to be passed on to `mlFieldFormatService`, - // but it's not being made available as part of global services. Since it's just - // some stateless utils `useMlIndexUtils()` should be used from within components. - // - `mlFieldFormatService` is a stateful legacy service that relied on "dependency cache", - // so because of its own state it needs to be made available as a global service. - // In the long run we should again try to get rid of it here and make it available via - // its own context or possibly without having a singleton like state at all, since the - // way this manages its own state right now doesn't consider React component lifecycles. - const mlIndexUtils = indexServiceFactory(pluginsStart.data.dataViews); - const mlFieldFormatService = fieldFormatServiceFactory(mlApiServices, mlIndexUtils); - - return [ - coreStart, - pluginsStart as MlDependencies, - { - anomalyDetectorService, - anomalyExplorerService, - mlFieldFormatService, - mlResultsService, - }, - ]; - } - - public async create(initialInput: AnomalyChartsEmbeddableInput, parent?: IContainer) { - const services = await this.getServices(); - const { AnomalyChartsEmbeddable } = await import('./anomaly_charts_embeddable'); - return new AnomalyChartsEmbeddable(initialInput, services, parent); - } -} diff --git a/x-pack/plugins/ml/public/embeddables/anomaly_charts/anomaly_charts_embeddable_factory.tsx b/x-pack/plugins/ml/public/embeddables/anomaly_charts/anomaly_charts_embeddable_factory.tsx new file mode 100644 index 00000000000000..1dc792ffc44bf2 --- /dev/null +++ b/x-pack/plugins/ml/public/embeddables/anomaly_charts/anomaly_charts_embeddable_factory.tsx @@ -0,0 +1,197 @@ +/* + * Copyright 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 { KibanaRenderContextProvider } from '@kbn/react-kibana-context-render'; +import React from 'react'; +import type { StartServicesAccessor } from '@kbn/core/public'; +import type { Observable } from 'rxjs'; +import { Subscription, map } from 'rxjs'; +import { fetch$ } from '@kbn/presentation-publishing'; +import useUnmount from 'react-use/lib/useUnmount'; +import { KibanaContextProvider } from '@kbn/kibana-react-plugin/public'; +import { + apiHasExecutionContext, + initializeTimeRange, + initializeTitles, +} from '@kbn/presentation-publishing'; +import { distinctUntilChanged } from 'rxjs'; +import fastIsEqual from 'fast-deep-equal'; +import type { ReactEmbeddableFactory } from '@kbn/embeddable-plugin/public'; +import type { TimeRange } from '@kbn/es-query'; +import { css } from '@emotion/react'; +import { useEuiTheme } from '@elastic/eui'; +import type { MlPluginStart, MlStartDependencies } from '../../plugin'; +import type { AnomalyChartsEmbeddableApi, AnomalyChartsEmbeddableState } from '..'; +import { ANOMALY_EXPLORER_CHARTS_EMBEDDABLE_TYPE } from '..'; +import { useReactEmbeddableExecutionContext } from '../common/use_embeddable_execution_context'; +import { initializeAnomalyChartsControls } from './initialize_anomaly_charts_controls'; +import { LazyAnomalyChartsContainer } from './lazy_anomaly_charts_container'; +import { getAnomalyChartsServiceDependencies } from './get_anomaly_charts_services_dependencies'; +import { buildDataViewPublishingApi } from '../common/anomaly_detection_embeddable'; + +export const getAnomalyChartsReactEmbeddableFactory = ( + getStartServices: StartServicesAccessor +) => { + const factory: ReactEmbeddableFactory = + { + type: ANOMALY_EXPLORER_CHARTS_EMBEDDABLE_TYPE, + deserializeState: (state) => state.rawState, + buildEmbeddable: async (state, buildApi, uuid, parentApi) => { + if (!apiHasExecutionContext(parentApi)) { + throw new Error('Parent API does not have execution context'); + } + const [coreStartServices, pluginsStartServices] = await getStartServices(); + const anomalyChartsDependencies = await getAnomalyChartsServiceDependencies( + coreStartServices, + pluginsStartServices + ); + + const [, , mlServices] = anomalyChartsDependencies; + + const subscriptions = new Subscription(); + + const { titlesApi, titleComparators, serializeTitles } = initializeTitles(state); + const { + api: timeRangeApi, + comparators: timeRangeComparators, + serialize: serializeTimeRange, + } = initializeTimeRange(state); + + const { + anomalyChartsControlsApi, + dataLoadingApi, + serializeAnomalyChartsState, + anomalyChartsComparators, + onAnomalyChartsDestroy, + } = initializeAnomalyChartsControls(state, titlesApi, parentApi); + + const api = buildApi( + { + isEditingEnabled: () => true, + getTypeDisplayName: () => + i18n.translate('xpack.ml.components.mlAnomalyExplorerEmbeddable.typeDisplayName', { + defaultMessage: 'anomaly charts', + }), + onEdit: async () => { + try { + const { resolveEmbeddableAnomalyChartsUserInput } = await import( + './anomaly_charts_setup_flyout' + ); + const result = await resolveEmbeddableAnomalyChartsUserInput( + coreStartServices, + pluginsStartServices, + parentApi, + uuid, + { + ...serializeTitles(), + ...serializeAnomalyChartsState(), + } + ); + anomalyChartsControlsApi.updateUserInput(result); + } catch (e) { + // eslint-disable-next-line no-console + console.error(e); + return Promise.reject(); + } + }, + ...titlesApi, + ...timeRangeApi, + ...anomalyChartsControlsApi, + ...dataLoadingApi, + dataViews: buildDataViewPublishingApi( + { + anomalyDetectorService: mlServices.anomalyDetectorService, + dataViewsService: pluginsStartServices.data.dataViews, + }, + { jobIds: anomalyChartsControlsApi.jobIds$ }, + subscriptions + ), + serializeState: () => { + return { + rawState: { + timeRange: undefined, + ...serializeTitles(), + ...serializeTimeRange(), + ...serializeAnomalyChartsState(), + }, + references: [], + }; + }, + }, + { + ...timeRangeComparators, + ...titleComparators, + ...anomalyChartsComparators, + } + ); + + const appliedTimeRange$: Observable = fetch$(api).pipe( + map((fetchContext) => fetchContext.timeRange), + distinctUntilChanged(fastIsEqual) + ); + + const { onRenderComplete, onLoading, onError } = dataLoadingApi; + const contextServices = { + mlServices: { + ...mlServices, + }, + ...coreStartServices, + ...pluginsStartServices, + }; + + return { + api, + Component: () => { + if (!apiHasExecutionContext(parentApi)) { + throw new Error('Parent API does not have execution context'); + } + + useReactEmbeddableExecutionContext( + coreStartServices.executionContext, + parentApi.executionContext, + ANOMALY_EXPLORER_CHARTS_EMBEDDABLE_TYPE, + uuid + ); + + useUnmount(() => { + onAnomalyChartsDestroy(); + subscriptions.unsubscribe(); + }); + const { euiTheme } = useEuiTheme(); + + return ( + + +
+ +
+
+
+ ); + }, + }; + }, + }; + return factory; +}; diff --git a/x-pack/plugins/ml/public/embeddables/anomaly_charts/anomaly_charts_initializer.test.tsx b/x-pack/plugins/ml/public/embeddables/anomaly_charts/anomaly_charts_initializer.test.tsx index 9bfce47aa9dc10..4c68437933175f 100644 --- a/x-pack/plugins/ml/public/embeddables/anomaly_charts/anomaly_charts_initializer.test.tsx +++ b/x-pack/plugins/ml/public/embeddables/anomaly_charts/anomaly_charts_initializer.test.tsx @@ -5,45 +5,62 @@ * 2.0. */ -import { render, screen } from '@testing-library/react'; +import { render, screen, act } from '@testing-library/react'; import userEvent from '@testing-library/user-event'; +import type { MlApiServices } from '../../application/services/ml_api_service'; import { AnomalyChartsInitializer } from './anomaly_charts_initializer'; import { I18nProvider } from '@kbn/i18n-react'; import React from 'react'; -import { getDefaultExplorerChartsPanelTitle } from './anomaly_charts_embeddable'; +import { getDefaultExplorerChartsPanelTitle } from './utils'; +import { kibanaContextMock } from '../../application/contexts/kibana/__mocks__/kibana_context'; +import { KibanaContextProvider } from '@kbn/kibana-react-plugin/public/context/context'; const defaultOptions = { wrapper: I18nProvider }; +jest.mock('../../application/services/anomaly_detector_service', () => { + return { + AnomalyDetectorService: jest.fn().mockImplementation(() => { + return { + getJobs$: jest.fn(), + }; + }), + }; +}); describe('AnomalyChartsInitializer', () => { test('should render anomaly charts initializer', async () => { const onCreate = jest.fn(); const onCancel = jest.fn(); + const adJobsApiService = jest.fn(); - const jobIds = ['test-job']; + const jobIds = ['job1', 'job2']; const defaultTitle = getDefaultExplorerChartsPanelTitle(jobIds); const input = { maxSeriesToPlot: 12, + jobIds, }; - const { getByTestId } = render( - onCreate(params)} - onCancel={onCancel} - />, + render( + + onCreate(params)} + onCancel={onCancel} + adJobsApiService={adJobsApiService as unknown as MlApiServices['jobs']} + /> + , defaultOptions ); - const confirmButton = screen.getByText(/Confirm/i).closest('button'); - expect(confirmButton).toBeDefined(); - expect(onCreate).toHaveBeenCalledTimes(0); - userEvent.click(confirmButton!); - expect(onCreate).toHaveBeenCalledWith({ - panelTitle: defaultTitle, - maxSeriesToPlot: input.maxSeriesToPlot, - }); + act(() => { + const confirmButton = screen.getByText(/Confirm/i).closest('button'); + expect(confirmButton).toBeDefined(); + expect(onCreate).toHaveBeenCalledTimes(0); - userEvent.clear(await getByTestId('panelTitleInput')); - expect(confirmButton).toHaveAttribute('disabled'); + userEvent.click(confirmButton!); + expect(onCreate).toHaveBeenCalledWith({ + jobIds: ['job1', 'job2'], + title: defaultTitle, + maxSeriesToPlot: input.maxSeriesToPlot, + }); + }); }); }); diff --git a/x-pack/plugins/ml/public/embeddables/anomaly_charts/anomaly_charts_initializer.tsx b/x-pack/plugins/ml/public/embeddables/anomaly_charts/anomaly_charts_initializer.tsx index add35ce0e19ce7..536084cc014968 100644 --- a/x-pack/plugins/ml/public/embeddables/anomaly_charts/anomaly_charts_initializer.tsx +++ b/x-pack/plugins/ml/public/embeddables/anomaly_charts/anomaly_charts_initializer.tsx @@ -6,64 +6,101 @@ */ import type { FC } from 'react'; -import React, { useState } from 'react'; +import React, { useState, useRef, useEffect } from 'react'; import { EuiButton, EuiButtonEmpty, EuiForm, EuiFormRow, - EuiModalBody, - EuiModalFooter, - EuiModalHeader, - EuiModalHeaderTitle, + EuiFlyoutBody, + EuiFlyoutFooter, + EuiFlyoutHeader, + EuiTitle, EuiFieldNumber, EuiFieldText, - EuiModal, + EuiSpacer, + EuiFlexGroup, } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n-react'; -import type { AnomalyChartsEmbeddableInput } from '..'; +import type { AnomalyChartsEmbeddableState } from '..'; import { DEFAULT_MAX_SERIES_TO_PLOT } from '../../application/services/anomaly_explorer_charts_service'; +import { JobSelectorControl } from '../../alerting/job_selector'; +import { ML_PAGES } from '../../../common/constants/locator'; +import { getDefaultExplorerChartsPanelTitle } from './utils'; +import { useMlLink } from '../../application/contexts/kibana'; +import { getJobSelectionErrors } from '../utils'; +import type { MlApiServices } from '../../application/services/ml_api_service'; export const MAX_ANOMALY_CHARTS_ALLOWED = 50; export interface AnomalyChartsInitializerProps { - defaultTitle: string; - initialInput?: Partial>; - onCreate: (props: { panelTitle: string; maxSeriesToPlot?: number }) => void; + initialInput?: Partial< + Pick + >; + onCreate: (props: { + jobIds: AnomalyChartsEmbeddableState['jobIds']; + title: string; + maxSeriesToPlot?: number; + }) => void; onCancel: () => void; + adJobsApiService: MlApiServices['jobs']; } export const AnomalyChartsInitializer: FC = ({ - defaultTitle, initialInput, onCreate, onCancel, + adJobsApiService, }) => { - const [panelTitle, setPanelTitle] = useState(defaultTitle); + const titleManuallyChanged = useRef(!!initialInput?.title); + + const [panelTitle, setPanelTitle] = useState(initialInput?.title ?? ''); const [maxSeriesToPlot, setMaxSeriesToPlot] = useState( initialInput?.maxSeriesToPlot ?? DEFAULT_MAX_SERIES_TO_PLOT ); - - const isPanelTitleValid = panelTitle.length > 0; + const isPanelTitleValid = panelTitle?.length > 0; const isMaxSeriesToPlotValid = maxSeriesToPlot >= 1 && maxSeriesToPlot <= MAX_ANOMALY_CHARTS_ALLOWED; - const isFormValid = isPanelTitleValid && isMaxSeriesToPlotValid; + const newJobUrl = useMlLink({ page: ML_PAGES.ANOMALY_DETECTION_CREATE_JOB }); + + const [jobIds, setJobIds] = useState(initialInput?.jobIds ?? []); + const jobIdsErrors = getJobSelectionErrors(jobIds); + + const isFormValid = isPanelTitleValid && isMaxSeriesToPlotValid && jobIdsErrors === undefined; + + useEffect( + function updateDefaultTitle() { + if (!titleManuallyChanged.current) { + setPanelTitle(getDefaultExplorerChartsPanelTitle(jobIds)); + } + }, + [initialInput?.title, jobIds] + ); return ( - - - - - - + <> + + +

+ +

+
+
- + + { + setJobIds([...(update?.jobIds ?? []), ...(update?.groupIds ?? [])]); + }} + errors={jobIdsErrors} + /> + = ({ id="panelTitle" name="panelTitle" value={panelTitle} - onChange={(e) => setPanelTitle(e.target.value)} + onChange={(e) => { + titleManuallyChanged.current = true; + setPanelTitle(e.target.value); + }} isInvalid={!isPanelTitleValid} /> @@ -112,31 +152,37 @@ export const AnomalyChartsInitializer: FC = ({ /> - + - - - - + + + + + - - - - -
+ + + + + + ); }; diff --git a/x-pack/plugins/ml/public/embeddables/anomaly_charts/embeddable_anomaly_charts_container.tsx b/x-pack/plugins/ml/public/embeddables/anomaly_charts/anomaly_charts_react_container.tsx similarity index 76% rename from x-pack/plugins/ml/public/embeddables/anomaly_charts/embeddable_anomaly_charts_container.tsx rename to x-pack/plugins/ml/public/embeddables/anomaly_charts/anomaly_charts_react_container.tsx index e7c5ce816b02bd..d31ee17ef0780c 100644 --- a/x-pack/plugins/ml/public/embeddables/anomaly_charts/embeddable_anomaly_charts_container.tsx +++ b/x-pack/plugins/ml/public/embeddables/anomaly_charts/anomaly_charts_react_container.tsx @@ -12,20 +12,19 @@ import type { Observable } from 'rxjs'; import { FormattedMessage } from '@kbn/i18n-react'; import { throttle } from 'lodash'; import { UI_SETTINGS } from '@kbn/data-plugin/common'; -import useObservable from 'react-use/lib/useObservable'; import { type MlEntityField, type MlEntityFieldOperation, ML_ANOMALY_THRESHOLD, } from '@kbn/ml-anomaly-utils'; import { TimeBuckets } from '@kbn/ml-time-buckets'; -import { useEmbeddableExecutionContext } from '../common/use_embeddable_execution_context'; -import { useAnomalyChartsInputResolver } from './use_anomaly_charts_input_resolver'; -import type { IAnomalyChartsEmbeddable } from './anomaly_charts_embeddable'; +import useObservable from 'react-use/lib/useObservable'; +import type { TimeRange } from '@kbn/es-query'; import type { - AnomalyChartsEmbeddableInput, - AnomalyChartsEmbeddableOutput, + AnomalyChartsEmbeddableOverridableState, AnomalyChartsEmbeddableServices, + AnomalyChartsApi, + AnomalyChartsAttachmentApi, } from '..'; import { ExplorerAnomaliesContainer } from '../../application/explorer/explorer_charts/explorer_anomalies_container'; @@ -33,52 +32,43 @@ import { ML_APP_LOCATOR } from '../../../common/constants/locator'; import { optionValueToThreshold } from '../../application/components/controls/select_severity/select_severity'; import { EXPLORER_ENTITY_FIELD_SELECTION_TRIGGER } from '../../ui_actions/triggers'; import type { MlLocatorParams } from '../../../common/types/locator'; -import { ANOMALY_EXPLORER_CHARTS_EMBEDDABLE_TYPE } from '..'; +import { useAnomalyChartsData } from './use_anomaly_charts_data'; const RESIZE_THROTTLE_TIME_MS = 500; -export interface EmbeddableAnomalyChartsContainerProps { +export interface AnomalyChartsContainerProps + extends Partial { + lastReloadRequestTime?: number; + api: AnomalyChartsApi | AnomalyChartsAttachmentApi; id: string; - embeddableContext: InstanceType; - embeddableInput: Observable; services: AnomalyChartsEmbeddableServices; - refresh: Observable; - onInputChange: (input: Partial) => void; - onOutputChange: (output: Partial) => void; + timeRange$: Observable; onRenderComplete: () => void; - onLoading: () => void; + onLoading: (v: boolean) => void; onError: (error: Error) => void; } -export const EmbeddableAnomalyChartsContainer: FC = ({ +const AnomalyChartsContainer: FC = ({ id, - embeddableContext, - embeddableInput, + timeRange$, + severityThreshold, services, - refresh, - onInputChange, - onOutputChange, onRenderComplete, onError, onLoading, + api, }) => { - useEmbeddableExecutionContext( - services[0].executionContext, - embeddableInput, - ANOMALY_EXPLORER_CHARTS_EMBEDDABLE_TYPE, - id - ); - const [chartWidth, setChartWidth] = useState(0); const [severity, setSeverity] = useState( optionValueToThreshold( - embeddableContext.getInput().severityThreshold ?? ML_ANOMALY_THRESHOLD.WARNING + severityThreshold !== undefined ? severityThreshold : ML_ANOMALY_THRESHOLD.WARNING ) ); const [selectedEntities, setSelectedEntities] = useState(); const [{ uiSettings }, { data: dataServices, share, uiActions, charts: chartsService }] = services; const { timefilter } = dataServices.query.timefilter; + const timeRange = useObservable(timeRange$); const mlLocator = useMemo( () => share.url.locators.get(ML_APP_LOCATOR)!, @@ -95,32 +85,28 @@ export const EmbeddableAnomalyChartsContainer: FC { + if (api?.updateSeverityThreshold) { + api.updateSeverityThreshold(severity.val); + } + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [severity.val, api?.updateSeverityThreshold]); useEffect(() => { - onInputChange({ - severityThreshold: severity.val, - }); - onOutputChange({ - severity: severity.val, - entityFields: selectedEntities, - }); + if (api?.updateSelectedEntities) { + api.updateSelectedEntities(selectedEntities); + } // eslint-disable-next-line react-hooks/exhaustive-deps - }, [severity, selectedEntities]); + }, [selectedEntities, api?.updateSelectedEntities]); + const renderCallbacks = useMemo(() => { + return { onRenderComplete, onError, onLoading }; + }, [onRenderComplete, onError, onLoading]); const { chartsData, isLoading: isExplorerLoading, error, - } = useAnomalyChartsInputResolver( - embeddableInput, - onInputChange, - refresh, - services, - chartWidth, - severity.val, - { onRenderComplete, onError, onLoading } - ); + } = useAnomalyChartsData(api, services, chartWidth, severity.val, renderCallbacks); // Holds the container height for previously fetched data const containerHeightRef = useRef(); @@ -176,7 +162,7 @@ export const EmbeddableAnomalyChartsContainer: FC {isExplorerLoading && ( @@ -213,7 +199,7 @@ export const EmbeddableAnomalyChartsContainer: FC )} - {chartsData !== undefined && isExplorerLoading === false && ( + {chartsData !== undefined && isExplorerLoading === false ? ( - )} + ) : null} )} @@ -237,4 +223,4 @@ export const EmbeddableAnomalyChartsContainer: FC> { + pluginStart: MlStartDependencies, + parentApi: unknown, + focusedPanelId: string, + input?: Partial> +): Promise> { const { http, overlays, ...startServices } = coreStart; - - const { getJobs } = mlApiServicesProvider(new HttpService(http)); + const adJobsApiService = jobsApiProvider(new HttpService(http)); + const mlServices = getMlGlobalServices(http, pluginStart.data.dataViews); + const overlayTracker = tracksOverlays(parentApi) ? parentApi : undefined; return new Promise(async (resolve, reject) => { try { - const { jobIds } = await resolveJobSelection(coreStart, dataViews, input?.jobIds); - const title = input?.title ?? getDefaultExplorerChartsPanelTitle(jobIds); - const { jobs } = await getJobs({ jobId: jobIds.join(',') }); - const influencers = extractInfluencers(jobs); - influencers.push(VIEW_BY_JOB_LABEL); - const modalSession = overlays.openModal( + const flyoutSession = overlays.openFlyout( toMountPoint( - { - modalSession.close(); - resolve({ - jobIds, - title: panelTitle, - maxSeriesToPlot, - }); - }} - onCancel={() => { - modalSession.close(); - reject(); - }} - />, + + { + resolve({ + jobIds, + title, + maxSeriesToPlot, + } as Pick); + flyoutSession.close(); + overlayTracker?.clearOverlays(); + }} + onCancel={() => { + reject(); + flyoutSession.close(); + overlayTracker?.clearOverlays(); + }} + adJobsApiService={adJobsApiService} + /> + , startServices - ) + ), + { + type: 'push', + ownFocus: true, + size: 's', + onClose: () => { + reject(); + flyoutSession.close(); + overlayTracker?.clearOverlays(); + }, + 'data-test-subj': 'mlAnomalyChartsEmbeddableInitializer', + } ); + if (tracksOverlays(parentApi)) { + parentApi.openOverlay(flyoutSession, { + focusedPanelId, + }); + } } catch (error) { reject(error); } diff --git a/x-pack/plugins/ml/public/embeddables/anomaly_charts/embeddable_anomaly_charts_container.test.tsx b/x-pack/plugins/ml/public/embeddables/anomaly_charts/embeddable_anomaly_charts_container.test.tsx deleted file mode 100644 index 0bca926ad68733..00000000000000 --- a/x-pack/plugins/ml/public/embeddables/anomaly_charts/embeddable_anomaly_charts_container.test.tsx +++ /dev/null @@ -1,189 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import React from 'react'; -import { render } from '@testing-library/react'; -import type { EmbeddableAnomalyChartsContainerProps } from './embeddable_anomaly_charts_container'; -import { EmbeddableAnomalyChartsContainer } from './embeddable_anomaly_charts_container'; -import type { Observable } from 'rxjs'; -import { BehaviorSubject, of } from 'rxjs'; -import { I18nProvider } from '@kbn/i18n-react'; -import type { AnomalyChartsEmbeddable } from './anomaly_charts_embeddable'; -import type { CoreStart } from '@kbn/core/public'; -import { useAnomalyChartsInputResolver } from './use_anomaly_charts_input_resolver'; -import type { MlDependencies } from '../../application/app'; -import type { TriggerContract } from '@kbn/ui-actions-plugin/public/triggers'; -import type { AnomalyChartsEmbeddableInput, AnomalyChartsServices } from '..'; -import { ExplorerAnomaliesContainer } from '../../application/explorer/explorer_charts/explorer_anomalies_container'; -import { createMlResultsServiceMock } from '../../application/services/ml_results_service'; -import { createCoreStartMock } from '../../__mocks__/core_start'; -import { createMlStartDepsMock } from '../../__mocks__/ml_start_deps'; -import { createAnomalyExplorerChartsServiceMock } from '../../application/services/__mocks__/anomaly_explorer_charts_service'; -import { createAnomalyDetectorServiceMock } from '../../application/services/__mocks__/anomaly_detector_service'; - -jest.mock('./use_anomaly_charts_input_resolver', () => ({ - useAnomalyChartsInputResolver: jest.fn(() => { - return []; - }), -})); - -jest.mock('../../application/explorer/explorer_charts/explorer_anomalies_container', () => ({ - ExplorerAnomaliesContainer: jest.fn(() => { - return null; - }), -})); - -const defaultOptions = { wrapper: I18nProvider }; - -describe('EmbeddableAnomalyChartsContainer', () => { - let embeddableInput: BehaviorSubject>; - let refresh: BehaviorSubject; - let services: jest.Mocked<[CoreStart, MlDependencies, AnomalyChartsServices]>; - let embeddableContext: jest.Mocked; - let trigger: jest.Mocked; - - const onInputChange = jest.fn(); - const onOutputChange = jest.fn(); - const onRenderComplete = jest.fn(); - const onLoading = jest.fn(); - const onError = jest.fn(); - - const mockedInput = { - viewMode: 'view', - filters: [], - hidePanelTitles: false, - query: { - language: 'lucene', - query: 'instance:i-d**', - }, - timeRange: { - from: 'now-3y', - to: 'now', - }, - refreshConfig: { - value: 0, - pause: true, - }, - id: 'b5b2f600-9c7e-4f7d-8b82-ee156fffad27', - searchSessionId: 'e8d052f8-0d9a-4d80-819d-fe18d9b314fa', - syncColors: true, - title: 'ML anomaly explorer charts for cw_multi_1', - jobIds: ['cw_multi_1'], - maxSeriesToPlot: 12, - enhancements: {}, - severity: 50, - severityThreshold: 75, - } as AnomalyChartsEmbeddableInput; - - beforeEach(() => { - // we only want to mock some of the functions needed - // @ts-ignore - embeddableContext = { - id: 'test-id', - getInput: jest.fn(), - }; - embeddableContext.getInput.mockReturnValue(mockedInput); - - embeddableInput = new BehaviorSubject({ - id: 'test-explorer-charts-embeddable', - } as Partial); - - trigger = { exec: jest.fn() } as unknown as jest.Mocked; - - const mlStartMock = createMlStartDepsMock(); - mlStartMock.uiActions.getTrigger.mockReturnValue(trigger); - - const coreStartMock = createCoreStartMock(); - const anomalyDetectorServiceMock = createAnomalyDetectorServiceMock(); - - anomalyDetectorServiceMock.getJobs$.mockImplementation((jobId: string[]) => { - if (jobId.includes('invalid-job-id')) { - throw new Error('Invalid job'); - } - return of([ - { - job_id: 'cw_multi_1', - analysis_config: { bucket_span: '15m' }, - }, - ]); - }); - - services = [ - coreStartMock, - mlStartMock, - { - anomalyDetectorService: anomalyDetectorServiceMock, - anomalyExplorerChartsService: createAnomalyExplorerChartsServiceMock(), - mlResultsService: createMlResultsServiceMock(), - }, - ] as unknown as EmbeddableAnomalyChartsContainerProps['services']; - }); - - test('should render explorer charts with a valid embeddable input', async () => { - const chartsData = { - chartsPerRow: 2, - seriesToPlot: [], - tooManyBuckets: false, - timeFieldName: '@timestamp', - errorMessages: undefined, - }; - - (useAnomalyChartsInputResolver as jest.Mock).mockReturnValueOnce({ - chartsData, - isLoading: false, - error: undefined, - }); - - render( - } - services={services} - refresh={refresh} - onInputChange={onInputChange} - onOutputChange={onOutputChange} - onLoading={onLoading} - onRenderComplete={onRenderComplete} - onError={onError} - />, - defaultOptions - ); - - const calledWith = ( - ExplorerAnomaliesContainer as unknown as jest.Mock - ).mock.calls[0][0]; - - expect(calledWith).toMatchSnapshot(); - }); - - test('should render an error in case it could not fetch the ML charts data', async () => { - (useAnomalyChartsInputResolver as jest.Mock).mockReturnValue({ - chartsData: undefined, - isLoading: false, - error: 'No anomalies', - }); - - const { findByText } = render( - } - services={services} - refresh={refresh} - onInputChange={onInputChange} - onOutputChange={onOutputChange} - onLoading={onLoading} - onRenderComplete={onRenderComplete} - onError={onError} - />, - defaultOptions - ); - const errorMessage = await findByText('Unable to load the data for the anomaly charts'); - expect(errorMessage).toBeDefined(); - }); -}); diff --git a/x-pack/plugins/ml/public/embeddables/anomaly_charts/get_anomaly_charts_services_dependencies.ts b/x-pack/plugins/ml/public/embeddables/anomaly_charts/get_anomaly_charts_services_dependencies.ts new file mode 100644 index 00000000000000..b0c365088beed7 --- /dev/null +++ b/x-pack/plugins/ml/public/embeddables/anomaly_charts/get_anomaly_charts_services_dependencies.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 type { CoreStart } from '@kbn/core/public'; +import { HttpService } from '../../application/services/http_service'; +import type { MlStartDependencies } from '../../plugin'; +import type { MlDependencies } from '../../application/app'; +import type { AnomalyChartsEmbeddableServices } from '..'; +import { AnomalyExplorerChartsService } from '../../application/services/anomaly_explorer_charts_service'; + +export const getAnomalyChartsServiceDependencies = async ( + coreStartServices: CoreStart, + pluginsStartServices: MlStartDependencies +): Promise => { + const [ + { AnomalyDetectorService }, + { fieldFormatServiceFactory }, + { indexServiceFactory }, + { mlApiServicesProvider }, + { mlResultsServiceProvider }, + ] = await Promise.all([ + await import('../../application/services/anomaly_detector_service'), + await import('../../application/services/field_format_service_factory'), + await import('../../application/util/index_service'), + await import('../../application/services/ml_api_service'), + await import('../../application/services/results_service'), + ]); + const httpService = new HttpService(coreStartServices.http); + const anomalyDetectorService = new AnomalyDetectorService(httpService); + const mlApiServices = mlApiServicesProvider(httpService); + const mlResultsService = mlResultsServiceProvider(mlApiServices); + const anomalyExplorerService = new AnomalyExplorerChartsService( + pluginsStartServices.data.query.timefilter.timefilter, + mlApiServices, + mlResultsService + ); + + // Note on the following services: + // - `mlIndexUtils` is just instantiated here to be passed on to `mlFieldFormatService`, + // but it's not being made available as part of global services. Since it's just + // some stateless utils `useMlIndexUtils()` should be used from within components. + // - `mlFieldFormatService` is a stateful legacy service that relied on "dependency cache", + // so because of its own state it needs to be made available as a global service. + // In the long run we should again try to get rid of it here and make it available via + // its own context or possibly without having a singleton like state at all, since the + // way this manages its own state right now doesn't consider React component lifecycles. + const mlIndexUtils = indexServiceFactory(pluginsStartServices.data.dataViews); + const mlFieldFormatService = fieldFormatServiceFactory(mlApiServices, mlIndexUtils); + + const anomalyChartsEmbeddableServices: AnomalyChartsEmbeddableServices = [ + coreStartServices, + pluginsStartServices as MlDependencies, + { + anomalyDetectorService, + anomalyExplorerService, + mlFieldFormatService, + mlResultsService, + }, + ]; + return anomalyChartsEmbeddableServices; +}; diff --git a/x-pack/plugins/ml/public/embeddables/anomaly_charts/index.ts b/x-pack/plugins/ml/public/embeddables/anomaly_charts/index.ts index 7ee763b8933679..71e5d9e71dd70b 100644 --- a/x-pack/plugins/ml/public/embeddables/anomaly_charts/index.ts +++ b/x-pack/plugins/ml/public/embeddables/anomaly_charts/index.ts @@ -5,4 +5,4 @@ * 2.0. */ -export { AnomalyChartsEmbeddableFactory } from './anomaly_charts_embeddable_factory'; +export { getAnomalyChartsReactEmbeddableFactory } from './anomaly_charts_embeddable_factory'; diff --git a/x-pack/plugins/ml/public/embeddables/anomaly_charts/initialize_anomaly_charts_controls.ts b/x-pack/plugins/ml/public/embeddables/anomaly_charts/initialize_anomaly_charts_controls.ts new file mode 100644 index 00000000000000..b5ed19391237bc --- /dev/null +++ b/x-pack/plugins/ml/public/embeddables/anomaly_charts/initialize_anomaly_charts_controls.ts @@ -0,0 +1,100 @@ +/* + * Copyright 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 { TitlesApi } from '@kbn/presentation-publishing/interfaces/titles/titles_api'; +import { BehaviorSubject } from 'rxjs'; +import fastIsEqual from 'fast-deep-equal'; +import type { MlEntityField } from '@kbn/ml-anomaly-utils'; +import type { StateComparators } from '@kbn/presentation-publishing'; +import type { JobId } from '../../../common/types/anomaly_detection_jobs'; +import { DEFAULT_MAX_SERIES_TO_PLOT } from '../../application/services/anomaly_explorer_charts_service'; +import type { + AnomalyChartsComponentApi, + AnomalyChartsDataLoadingApi, + AnomalyChartsEmbeddableRuntimeState, + AnomalyChartsEmbeddableState, +} from '../types'; + +export const initializeAnomalyChartsControls = ( + rawState: AnomalyChartsEmbeddableState, + titlesApi?: TitlesApi, + parentApi?: unknown +) => { + const jobIds$ = new BehaviorSubject(rawState.jobIds); + const maxSeriesToPlot$ = new BehaviorSubject( + rawState.maxSeriesToPlot ?? DEFAULT_MAX_SERIES_TO_PLOT + ); + const severityThreshold$ = new BehaviorSubject(rawState.severityThreshold); + const selectedEntities$ = new BehaviorSubject( + rawState.selectedEntities + ); + const interval$ = new BehaviorSubject(undefined); + const dataLoading$ = new BehaviorSubject(true); + const blockingError$ = new BehaviorSubject(undefined); + + const updateUserInput = (update: AnomalyChartsEmbeddableState) => { + jobIds$.next(update.jobIds); + maxSeriesToPlot$.next(update.maxSeriesToPlot); + if (titlesApi) { + titlesApi.setPanelTitle(update.title); + } + }; + + const updateSeverityThreshold = (v: number) => severityThreshold$.next(v); + const updateSelectedEntities = (v: MlEntityField[]) => selectedEntities$.next(v); + const setInterval = (v: number) => interval$.next(v); + + const serializeAnomalyChartsState = (): AnomalyChartsEmbeddableState => { + return { + jobIds: jobIds$.value, + maxSeriesToPlot: maxSeriesToPlot$.value, + severityThreshold: severityThreshold$.value, + selectedEntities: selectedEntities$.value, + }; + }; + + const anomalyChartsComparators: StateComparators = { + jobIds: [jobIds$, (arg: JobId[]) => jobIds$.next(arg), fastIsEqual], + maxSeriesToPlot: [maxSeriesToPlot$, (arg: number) => maxSeriesToPlot$.next(arg)], + severityThreshold: [severityThreshold$, (arg?: number) => severityThreshold$.next(arg)], + selectedEntities: [ + selectedEntities$, + (arg?: MlEntityField[]) => selectedEntities$.next(arg), + fastIsEqual, + ], + }; + const onRenderComplete = () => dataLoading$.next(false); + const onLoading = (v: boolean) => dataLoading$.next(v); + const onError = (error?: Error) => blockingError$.next(error); + + return { + anomalyChartsControlsApi: { + jobIds$, + maxSeriesToPlot$, + severityThreshold$, + selectedEntities$, + updateUserInput, + updateSeverityThreshold, + updateSelectedEntities, + } as AnomalyChartsComponentApi, + dataLoadingApi: { + dataLoading: dataLoading$, + setInterval, + onRenderComplete, + onLoading, + onError, + } as AnomalyChartsDataLoadingApi, + serializeAnomalyChartsState, + anomalyChartsComparators, + onAnomalyChartsDestroy: () => { + jobIds$.complete(); + maxSeriesToPlot$.complete(); + severityThreshold$.complete(); + selectedEntities$.complete(); + }, + }; +}; diff --git a/x-pack/plugins/ml/public/embeddables/anomaly_charts/lazy_anomaly_charts_container.ts b/x-pack/plugins/ml/public/embeddables/anomaly_charts/lazy_anomaly_charts_container.ts new file mode 100644 index 00000000000000..76abe473f3c9e0 --- /dev/null +++ b/x-pack/plugins/ml/public/embeddables/anomaly_charts/lazy_anomaly_charts_container.ts @@ -0,0 +1,9 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ +import { dynamic } from '@kbn/shared-ux-utility'; + +export const LazyAnomalyChartsContainer = dynamic(() => import('./anomaly_charts_react_container')); diff --git a/x-pack/plugins/ml/public/embeddables/anomaly_charts/types.ts b/x-pack/plugins/ml/public/embeddables/anomaly_charts/types.ts deleted file mode 100644 index 0c6ddf3a9e35c3..00000000000000 --- a/x-pack/plugins/ml/public/embeddables/anomaly_charts/types.ts +++ /dev/null @@ -1,20 +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 { MlEntityField } from '@kbn/ml-anomaly-utils'; -import type { PublishingSubject } from '@kbn/presentation-publishing'; -import type { JobId } from '../../../common/types/anomaly_detection_jobs'; -import type { MlEmbeddableBaseApi } from '../types'; - -export interface AnomalyChartsFieldSelectionApi { - jobIds: PublishingSubject; - entityFields: PublishingSubject; -} - -export interface AnomalyChartsEmbeddableApi - extends MlEmbeddableBaseApi, - AnomalyChartsFieldSelectionApi {} diff --git a/x-pack/plugins/ml/public/embeddables/anomaly_charts/use_anomaly_charts_data.ts b/x-pack/plugins/ml/public/embeddables/anomaly_charts/use_anomaly_charts_data.ts new file mode 100644 index 00000000000000..ee7acdba2ab553 --- /dev/null +++ b/x-pack/plugins/ml/public/embeddables/anomaly_charts/use_anomaly_charts_data.ts @@ -0,0 +1,170 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { useState, useMemo, useEffect } from 'react'; +import { combineLatest, tap, debounceTime, switchMap, skipWhile, of } from 'rxjs'; +import { Subject, catchError } from 'rxjs'; +import type { InfluencersFilterQuery } from '@kbn/ml-anomaly-utils'; +import type { CoreStart } from '@kbn/core/public'; +import { fetch$ } from '@kbn/presentation-publishing'; +import type { AnomalyChartsServices, AnomalyChartsApi } from '..'; +import { getJobsObservable } from '../common/get_jobs_observable'; +import { OVERALL_LABEL, SWIMLANE_TYPE } from '../../application/explorer/explorer_constants'; +import { processFilters } from '../common/process_filters'; +import type { AppStateSelectedCells } from '../../application/explorer/explorer_utils'; +import { + getSelectionInfluencers, + getSelectionJobIds, + getSelectionTimeRange, +} from '../../application/explorer/explorer_utils'; +import type { ExplorerChartsData } from '../../application/explorer/explorer_charts/explorer_charts_container_service'; +import type { MlStartDependencies } from '../../plugin'; + +const FETCH_RESULTS_DEBOUNCE_MS = 500; + +export function useAnomalyChartsData( + api: AnomalyChartsApi, + services: [CoreStart, MlStartDependencies, AnomalyChartsServices], + chartWidth: number, + severity: number, + renderCallbacks: { + onRenderComplete: () => void; + onLoading: (v: boolean) => void; + onError: (error: Error) => void; + } +): { + chartsData: ExplorerChartsData | undefined; + isLoading: boolean; + error: Error | null | undefined; +} { + const [, , { anomalyDetectorService, anomalyExplorerService }] = services; + + const [chartsData, setChartsData] = useState(); + const [error, setError] = useState(); + const [isLoading, setIsLoading] = useState(false); + + const chartWidth$ = useMemo(() => new Subject(), []); + const severity$ = useMemo(() => new Subject(), []); + + useEffect(() => { + const subscription = combineLatest({ + explorerJobs: getJobsObservable(api.jobIds$, anomalyDetectorService, setError), + maxSeriesToPlot: api.maxSeriesToPlot$, + chartWidth: chartWidth$.pipe(skipWhile((v) => !v)), + severityValue: severity$, + dataPublishingServices: fetch$(api), + }) + .pipe( + tap(setIsLoading.bind(null, true)), + debounceTime(FETCH_RESULTS_DEBOUNCE_MS), + tap(() => { + renderCallbacks.onLoading(true); + }), + switchMap( + ({ + explorerJobs, + maxSeriesToPlot, + chartWidth: embeddableContainerWidth, + severityValue, + dataPublishingServices, + }) => { + const { timeRange: timeRangeInput, filters, query } = dataPublishingServices; + if (!explorerJobs) { + // couldn't load the list of jobs + return of(undefined); + } + + const viewBySwimlaneFieldName = OVERALL_LABEL; + + if (timeRangeInput) { + anomalyExplorerService.setTimeRange(timeRangeInput); + } + + let influencersFilterQuery: InfluencersFilterQuery | undefined; + try { + if (filters || query) { + influencersFilterQuery = processFilters(filters, query); + } + } catch (e) { + // handle query syntax errors + setError(e); + return of(undefined); + } + + const bounds = anomalyExplorerService.getTimeBounds(); + + // Can be from input time range or from the timefilter bar + const selections: AppStateSelectedCells = { + lanes: [OVERALL_LABEL], + times: [bounds.min?.unix()!, bounds.max?.unix()!], + type: SWIMLANE_TYPE.OVERALL, + }; + + const selectionInfluencers = getSelectionInfluencers( + selections, + viewBySwimlaneFieldName + ); + + const jobIds = getSelectionJobIds(selections, explorerJobs); + + const timeRange = getSelectionTimeRange(selections, bounds); + + return anomalyExplorerService.getAnomalyData$( + jobIds, + embeddableContainerWidth, + timeRange.earliestMs, + timeRange.latestMs, + influencersFilterQuery, + selectionInfluencers, + severityValue ?? 0, + maxSeriesToPlot + ); + } + ), + catchError((e) => { + // eslint-disable-next-line no-console + console.error(`Error occured fetching anomaly charts data for embeddable\n`, e); + setError(e.body); + return of(undefined); + }) + ) + .subscribe((results) => { + if (results !== undefined) { + setError(null); + setChartsData(results); + setIsLoading(false); + renderCallbacks.onRenderComplete(); + } + }); + + return () => { + subscription?.unsubscribe(); + chartWidth$.complete(); + severity$.complete(); + }; + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []); + + useEffect(() => { + chartWidth$.next(chartWidth); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [chartWidth]); + + useEffect(() => { + severity$.next(severity); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [severity]); + + useEffect(() => { + if (error) { + renderCallbacks.onError(error); + } + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [error]); + + return { chartsData, isLoading, error }; +} diff --git a/x-pack/plugins/ml/public/embeddables/anomaly_charts/use_anomaly_charts_input_resolver.test.ts b/x-pack/plugins/ml/public/embeddables/anomaly_charts/use_anomaly_charts_input_resolver.test.ts deleted file mode 100644 index 2ddb7b75f3d6d7..00000000000000 --- a/x-pack/plugins/ml/public/embeddables/anomaly_charts/use_anomaly_charts_input_resolver.test.ts +++ /dev/null @@ -1,197 +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 { renderHook } from '@testing-library/react-hooks'; -import type { Observable } from 'rxjs'; -import { BehaviorSubject, of, Subject } from 'rxjs'; -import { fakeSchedulers } from 'rxjs-marbles/jest'; -import type { AnomalyChartsEmbeddableInput, AnomalyChartsServices } from '../types'; -import type { CoreStart } from '@kbn/core/public'; -import type { MlStartDependencies } from '../../plugin'; -import { useAnomalyChartsInputResolver } from './use_anomaly_charts_input_resolver'; -import type { EmbeddableAnomalyChartsContainerProps } from './embeddable_anomaly_charts_container'; -import moment from 'moment'; -import { createMlResultsServiceMock } from '../../application/services/ml_results_service'; -import { createCoreStartMock } from '../../__mocks__/core_start'; -import { createMlStartDepsMock } from '../../__mocks__/ml_start_deps'; -import { createAnomalyExplorerChartsServiceMock } from '../../application/services/__mocks__/anomaly_explorer_charts_service'; -import { createAnomalyDetectorServiceMock } from '../../application/services/__mocks__/anomaly_detector_service'; - -jest.mock('../common/process_filters', () => ({ - processFilters: jest.fn(), -})); - -jest.mock('../../application/explorer/explorer_utils', () => ({ - getSelectionInfluencers: jest.fn(() => { - return []; - }), - getSelectionJobIds: jest.fn(() => ['test-job']), - getSelectionTimeRange: jest.fn(() => ({ earliestMs: 1521309543000, latestMs: 1616003942999 })), -})); - -describe('useAnomalyChartsInputResolver', () => { - let embeddableInput: BehaviorSubject>; - let refresh: Subject; - let services: [CoreStart, MlStartDependencies, AnomalyChartsServices]; - let onInputChange: jest.Mock; - - const start = moment().subtract(1, 'years'); - const end = moment(); - - const renderCallbacks = { - onRenderComplete: jest.fn(), - onLoading: jest.fn(), - onError: jest.fn(), - }; - - beforeEach(() => { - jest.useFakeTimers({ legacyFakeTimers: true }); - - const jobIds = ['test-job']; - embeddableInput = new BehaviorSubject({ - id: 'test-explorer-charts-embeddable', - jobIds, - filters: [], - query: { language: 'kuery', query: '' }, - maxSeriesToPlot: 12, - timeRange: { - from: 'now-3y', - to: 'now', - }, - } as Partial); - - refresh = new Subject(); - const anomalyExplorerChartsServiceMock = createAnomalyExplorerChartsServiceMock(); - - anomalyExplorerChartsServiceMock.getTimeBounds.mockReturnValue({ - min: start, - max: end, - }); - - anomalyExplorerChartsServiceMock.getAnomalyData$.mockImplementation(() => - of({ - chartsPerRow: 2, - seriesToPlot: [], - tooManyBuckets: false, - timeFieldName: '@timestamp', - errorMessages: undefined, - }) - ); - - const coreStartMock = createCoreStartMock(); - const mlStartMock = createMlStartDepsMock(); - - const anomalyDetectorServiceMock = createAnomalyDetectorServiceMock(); - anomalyDetectorServiceMock.getJobs$.mockImplementation((jobId: string[]) => { - if (jobId.includes('invalid-job-id')) { - throw new Error('Invalid job'); - } - return of([ - { - job_id: 'cw_multi_1', - analysis_config: { bucket_span: '15m' }, - }, - ]); - }); - - services = [ - coreStartMock, - mlStartMock, - { - anomalyDetectorService: anomalyDetectorServiceMock, - anomalyExplorerService: anomalyExplorerChartsServiceMock, - mlResultsService: createMlResultsServiceMock(), - }, - ] as unknown as EmbeddableAnomalyChartsContainerProps['services']; - - onInputChange = jest.fn(); - }); - - afterEach(() => { - jest.useRealTimers(); - jest.clearAllMocks(); - }); - - test( - 'should fetch jobs only when input job ids have been changed', - fakeSchedulers(async (advance) => { - const { result } = renderHook(() => - useAnomalyChartsInputResolver( - embeddableInput as Observable, - onInputChange, - refresh, - services, - 1000, - 0, - renderCallbacks - ) - ); - - expect(result.current.chartsData).toBe(undefined); - expect(result.current.error).toBe(undefined); - expect(result.current.isLoading).toBe(true); - expect(renderCallbacks.onLoading).toHaveBeenCalledTimes(0); - - advance(501); - - expect(renderCallbacks.onLoading).toHaveBeenCalledTimes(1); - - const explorerServices = services[2]; - - expect(explorerServices.anomalyDetectorService.getJobs$).toHaveBeenCalledTimes(1); - expect(explorerServices.anomalyExplorerService.getAnomalyData$).toHaveBeenCalledTimes(1); - - expect(renderCallbacks.onRenderComplete).toHaveBeenCalledTimes(1); - - embeddableInput.next({ - id: 'test-explorer-charts-embeddable', - jobIds: ['anotherJobId'], - filters: [], - query: { language: 'kuery', query: '' }, - maxSeriesToPlot: 6, - timeRange: { - from: 'now-3y', - to: 'now', - }, - }); - advance(501); - - expect(renderCallbacks.onLoading).toHaveBeenCalledTimes(2); - - expect(explorerServices.anomalyDetectorService.getJobs$).toHaveBeenCalledTimes(2); - expect(explorerServices.anomalyExplorerService.getAnomalyData$).toHaveBeenCalledTimes(2); - - expect(renderCallbacks.onRenderComplete).toHaveBeenCalledTimes(2); - - expect(renderCallbacks.onError).toHaveBeenCalledTimes(0); - }) - ); - - test.skip('should not complete the observable on error', async () => { - const { result } = renderHook(() => - useAnomalyChartsInputResolver( - embeddableInput as Observable, - onInputChange, - refresh, - services, - 1000, - 1, - renderCallbacks - ) - ); - - embeddableInput.next({ - id: 'test-explorer-charts-embeddable', - jobIds: ['invalid-job-id'], - filters: [], - query: { language: 'kuery', query: '' }, - } as Partial); - - expect(result.current.error).toBeDefined(); - expect(renderCallbacks.onError).toHaveBeenCalledTimes(1); - }); -}); diff --git a/x-pack/plugins/ml/public/embeddables/anomaly_charts/use_anomaly_charts_input_resolver.ts b/x-pack/plugins/ml/public/embeddables/anomaly_charts/use_anomaly_charts_input_resolver.ts deleted file mode 100644 index 7af34daec557da..00000000000000 --- a/x-pack/plugins/ml/public/embeddables/anomaly_charts/use_anomaly_charts_input_resolver.ts +++ /dev/null @@ -1,161 +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, useMemo, useState } from 'react'; -import type { Observable } from 'rxjs'; -import { combineLatest, of, Subject } from 'rxjs'; -import { catchError, debounceTime, skipWhile, startWith, switchMap, tap } from 'rxjs'; -import type { CoreStart } from '@kbn/core/public'; -import type { InfluencersFilterQuery } from '@kbn/ml-anomaly-utils'; -import type { MlStartDependencies } from '../../plugin'; -import type { AppStateSelectedCells } from '../../application/explorer/explorer_utils'; -import { - getSelectionInfluencers, - getSelectionJobIds, - getSelectionTimeRange, -} from '../../application/explorer/explorer_utils'; -import { OVERALL_LABEL, SWIMLANE_TYPE } from '../../application/explorer/explorer_constants'; -import type { - AnomalyChartsEmbeddableInput, - AnomalyChartsEmbeddableOutput, - AnomalyChartsServices, -} from '..'; -import type { ExplorerChartsData } from '../../application/explorer/explorer_charts/explorer_charts_container_service'; -import { processFilters } from '../common/process_filters'; -import { getJobsObservable } from '../common/get_jobs_observable'; - -const FETCH_RESULTS_DEBOUNCE_MS = 500; - -export function useAnomalyChartsInputResolver( - embeddableInput: Observable, - onInputChange: (output: Partial) => void, - refresh: Observable, - services: [CoreStart, MlStartDependencies, AnomalyChartsServices], - chartWidth: number, - severity: number, - renderCallbacks: { - onRenderComplete: () => void; - onLoading: () => void; - onError: (error: Error) => void; - } -): { - chartsData: ExplorerChartsData | undefined; - isLoading: boolean; - error: Error | null | undefined; -} { - const [, , { anomalyDetectorService, anomalyExplorerService }] = services; - - const [chartsData, setChartsData] = useState(); - const [error, setError] = useState(); - const [isLoading, setIsLoading] = useState(false); - - const chartWidth$ = useMemo(() => new Subject(), []); - const severity$ = useMemo(() => new Subject(), []); - - useEffect(() => { - const subscription = combineLatest([ - getJobsObservable(embeddableInput, anomalyDetectorService, setError), - embeddableInput, - chartWidth$.pipe(skipWhile((v) => !v)), - severity$, - refresh.pipe(startWith(null)), - ]) - .pipe( - tap(setIsLoading.bind(null, true)), - debounceTime(FETCH_RESULTS_DEBOUNCE_MS), - tap(() => { - renderCallbacks.onLoading(); - }), - switchMap(([explorerJobs, input, embeddableContainerWidth, severityValue]) => { - if (!explorerJobs) { - // couldn't load the list of jobs - return of(undefined); - } - - const { maxSeriesToPlot, timeRange: timeRangeInput, filters, query } = input; - - const viewBySwimlaneFieldName = OVERALL_LABEL; - - anomalyExplorerService.setTimeRange(timeRangeInput); - - let influencersFilterQuery: InfluencersFilterQuery | undefined; - try { - if (filters || query) { - influencersFilterQuery = processFilters(filters, query); - } - } catch (e) { - // handle query syntax errors - setError(e); - return of(undefined); - } - - const bounds = anomalyExplorerService.getTimeBounds(); - - // Can be from input time range or from the timefilter bar - const selections: AppStateSelectedCells = { - lanes: [OVERALL_LABEL], - times: [bounds.min?.unix()!, bounds.max?.unix()!], - type: SWIMLANE_TYPE.OVERALL, - }; - - const selectionInfluencers = getSelectionInfluencers(selections, viewBySwimlaneFieldName); - - const jobIds = getSelectionJobIds(selections, explorerJobs); - - const timeRange = getSelectionTimeRange(selections, bounds); - - return anomalyExplorerService.getAnomalyData$( - jobIds, - embeddableContainerWidth, - timeRange.earliestMs, - timeRange.latestMs, - influencersFilterQuery, - selectionInfluencers, - severityValue ?? 0, - maxSeriesToPlot - ); - }), - catchError((e) => { - setError(e.body); - return of(undefined); - }) - ) - .subscribe((results) => { - if (results !== undefined) { - setError(null); - setChartsData(results); - setIsLoading(false); - - renderCallbacks.onRenderComplete(); - } - }); - - return () => { - subscription.unsubscribe(); - }; - // eslint-disable-next-line react-hooks/exhaustive-deps - }, []); - - useEffect(() => { - chartWidth$.next(chartWidth); - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [chartWidth]); - - useEffect(() => { - severity$.next(severity); - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [severity]); - - useEffect(() => { - if (error) { - renderCallbacks.onError(error); - } - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [error]); - - return { chartsData, isLoading, error }; -} diff --git a/x-pack/plugins/ml/public/embeddables/anomaly_charts/utils.ts b/x-pack/plugins/ml/public/embeddables/anomaly_charts/utils.ts new file mode 100644 index 00000000000000..82355a2ef3d71e --- /dev/null +++ b/x-pack/plugins/ml/public/embeddables/anomaly_charts/utils.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 { i18n } from '@kbn/i18n'; +import type { JobId } from '../../../common/types/anomaly_detection_jobs'; + +export const getDefaultExplorerChartsPanelTitle = (jobIds: JobId[]) => + i18n.translate('xpack.ml.anomalyChartsEmbeddable.title', { + defaultMessage: 'ML anomaly charts for {jobIds}', + values: { jobIds: jobIds.join(', ') }, + }); diff --git a/x-pack/plugins/ml/public/embeddables/anomaly_swimlane/anomaly_swimlane_embeddable_factory.tsx b/x-pack/plugins/ml/public/embeddables/anomaly_swimlane/anomaly_swimlane_embeddable_factory.tsx index 3c13a85cd6286f..c67c2ed157cda0 100644 --- a/x-pack/plugins/ml/public/embeddables/anomaly_swimlane/anomaly_swimlane_embeddable_factory.tsx +++ b/x-pack/plugins/ml/public/embeddables/anomaly_swimlane/anomaly_swimlane_embeddable_factory.tsx @@ -199,7 +199,6 @@ export const getAnomalySwimLaneEmbeddableFactory = ( ...swimLaneComparators, } ); - const appliedTimeRange$: Observable = combineLatest([ api.timeRange$, apiHasParentApi(api) && apiPublishesTimeRange(api.parentApi) diff --git a/x-pack/plugins/ml/public/embeddables/anomaly_swimlane/anomaly_swimlane_initializer.tsx b/x-pack/plugins/ml/public/embeddables/anomaly_swimlane/anomaly_swimlane_initializer.tsx index 64c0d9db7b7b77..52c84770da12c2 100644 --- a/x-pack/plugins/ml/public/embeddables/anomaly_swimlane/anomaly_swimlane_initializer.tsx +++ b/x-pack/plugins/ml/public/embeddables/anomaly_swimlane/anomaly_swimlane_initializer.tsx @@ -33,6 +33,7 @@ import type { SwimlaneType } from '../../application/explorer/explorer_constants import { SWIMLANE_TYPE, VIEW_BY_JOB_LABEL } from '../../application/explorer/explorer_constants'; import type { AnomalySwimLaneEmbeddableState, AnomalySwimlaneEmbeddableUserInput } from '..'; import { getDefaultSwimlanePanelTitle } from './anomaly_swimlane_embeddable'; +import { getJobSelectionErrors } from '../utils'; export type ExplicitInput = AnomalySwimlaneEmbeddableUserInput; @@ -45,16 +46,6 @@ export interface AnomalySwimlaneInitializerProps { adJobsApiService: MlApiServices['jobs']; } -const getJobSelectionErrors = (jobIds: string[]) => { - if (jobIds.length === 0) { - return [ - i18n.translate('xpack.ml.swimlaneEmbeddable.setupModal.jobSelectionRequiredError', { - defaultMessage: 'Job selection is required', - }), - ]; - } -}; - export const AnomalySwimlaneInitializer: FC = ({ onCreate, onCancel, diff --git a/x-pack/plugins/ml/public/embeddables/anomaly_swimlane/initialize_swim_lane_data_fetcher.ts b/x-pack/plugins/ml/public/embeddables/anomaly_swimlane/initialize_swim_lane_data_fetcher.ts index 6946215b0c4523..268a17fca4a818 100644 --- a/x-pack/plugins/ml/public/embeddables/anomaly_swimlane/initialize_swim_lane_data_fetcher.ts +++ b/x-pack/plugins/ml/public/embeddables/anomaly_swimlane/initialize_swim_lane_data_fetcher.ts @@ -53,13 +53,9 @@ export const initializeSwimLaneDataFetcher = ( const swimLaneData$ = new BehaviorSubject(undefined); - const selectedJobs$ = getJobsObservable( - swimLaneApi.jobIds.pipe(map((jobIds) => ({ jobIds }))), - anomalyDetectorService, - (error) => { - blockingError.next(error); - } - ).pipe(shareReplay(1)); + const selectedJobs$ = getJobsObservable(swimLaneApi.jobIds, anomalyDetectorService, (error) => { + blockingError.next(error); + }).pipe(shareReplay(1)); const swimLaneInput$ = combineLatest({ jobIds: swimLaneApi.jobIds, diff --git a/x-pack/plugins/ml/public/embeddables/common/get_jobs_observable.ts b/x-pack/plugins/ml/public/embeddables/common/get_jobs_observable.ts index e770106200f7b3..e1fc6a6d178c10 100644 --- a/x-pack/plugins/ml/public/embeddables/common/get_jobs_observable.ts +++ b/x-pack/plugins/ml/public/embeddables/common/get_jobs_observable.ts @@ -14,22 +14,21 @@ import type { ExplorerJob } from '../../application/explorer/explorer_utils'; import type { AnomalyDetectorService } from '../../application/services/anomaly_detector_service'; export function getJobsObservable( - embeddableInput: Observable<{ jobIds: JobId[] }>, + jobIds$: Observable, anomalyDetectorService: AnomalyDetectorService, setErrorHandler: (e: Error) => void -) { - return embeddableInput.pipe( - map((v) => v.jobIds), +): Observable { + return jobIds$.pipe( distinctUntilChanged(isEqual), - switchMap((jobsIds) => - anomalyDetectorService.getJobs$(jobsIds).pipe( + switchMap((jobsIds) => { + return anomalyDetectorService.getJobs$(jobsIds).pipe( catchError((e) => { // Catch error to prevent the observable from completing setErrorHandler(e.body ?? e); return EMPTY; }) - ) - ), + ); + }), map((jobs) => { const explorerJobs: ExplorerJob[] = jobs.map((job) => { const bucketSpan = parseInterval(job.analysis_config.bucket_span!); diff --git a/x-pack/plugins/ml/public/embeddables/common/process_filters.ts b/x-pack/plugins/ml/public/embeddables/common/process_filters.ts index d3725d59b7c66e..e4b6079bfe0eab 100644 --- a/x-pack/plugins/ml/public/embeddables/common/process_filters.ts +++ b/x-pack/plugins/ml/public/embeddables/common/process_filters.ts @@ -10,6 +10,7 @@ import type { AggregateQuery, Filter, Query } from '@kbn/es-query'; import { isOfAggregateQueryType } from '@kbn/es-query'; import { fromKueryExpression, luceneStringToDsl, toElasticsearchQuery } from '@kbn/es-query'; import { getDefaultQuery } from '@kbn/data-plugin/public'; +import { isDefined } from '@kbn/ml-is-defined'; export function processFilters( optionalFilters?: Filter[], @@ -28,7 +29,7 @@ export function processFilters( : luceneStringToDsl(query.query); } - const must = [inputQuery]; + const must = isDefined(inputQuery) ? [inputQuery] : []; const mustNot = []; for (const filter of filters) { diff --git a/x-pack/plugins/ml/public/embeddables/common/resolve_job_selection.tsx b/x-pack/plugins/ml/public/embeddables/common/resolve_job_selection.tsx deleted file mode 100644 index 99b301ee19e9f1..00000000000000 --- a/x-pack/plugins/ml/public/embeddables/common/resolve_job_selection.tsx +++ /dev/null @@ -1,107 +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 { CoreStart } from '@kbn/core/public'; -import moment from 'moment'; -import { takeUntil, distinctUntilChanged, skip } from 'rxjs'; -import { from } from 'rxjs'; -import React from 'react'; -import { KibanaContextProvider } from '@kbn/kibana-react-plugin/public'; -import { toMountPoint } from '@kbn/react-kibana-mount'; -import type { DataViewsContract } from '@kbn/data-views-plugin/public'; -import { getInitialGroupsMap } from '../../application/components/job_selector/job_selector'; -import type { JobId } from '../../../common/types/anomaly_detection_jobs'; -import { JobSelectorFlyout } from './components/job_selector_flyout'; -import { getMlGlobalServices } from '../../application/util/get_services'; - -/** - * Handles Anomaly detection jobs selection by a user. - * Intended to use independently of the ML app context, - * for instance on the dashboard for embeddables initialization. - * - * @param coreStart - * @param selectedJobIds - */ -export async function resolveJobSelection( - coreStart: CoreStart, - dataViews: DataViewsContract, - selectedJobIds?: JobId[], - singleSelection: boolean = false -): Promise<{ jobIds: string[]; groups: Array<{ groupId: string; jobIds: string[] }> }> { - const { - http, - uiSettings, - application: { currentAppId$ }, - ...startServices - } = coreStart; - - return new Promise(async (resolve, reject) => { - try { - const maps = { - groupsMap: getInitialGroupsMap([]), - jobsMap: {}, - }; - const tzConfig = uiSettings.get('dateFormat:tz'); - const dateFormatTz = tzConfig !== 'Browser' ? tzConfig : moment.tz.guess(); - - const onFlyoutClose = () => { - flyoutSession.close(); - reject(); - }; - - const onSelectionConfirmed = async ({ - jobIds, - groups, - }: { - jobIds: string[]; - groups: Array<{ - groupId: string; - jobIds: string[]; - }>; - }) => { - await flyoutSession.close(); - resolve({ - jobIds, - groups, - }); - }; - - const flyoutSession = coreStart.overlays.openFlyout( - toMountPoint( - - - , - startServices - ), - { - 'data-test-subj': 'mlFlyoutJobSelector', - ownFocus: true, - closeButtonProps: { 'aria-label': 'jobSelectorFlyout' }, - } - ); - - // Close the flyout when user navigates out of the current plugin - currentAppId$ - .pipe(skip(1), takeUntil(from(flyoutSession.onClose)), distinctUntilChanged()) - .subscribe(() => { - flyoutSession.close(); - }); - } catch (error) { - reject(error); - } - }); -} diff --git a/x-pack/plugins/ml/public/embeddables/get_embeddable_component.tsx b/x-pack/plugins/ml/public/embeddables/get_embeddable_component.tsx deleted file mode 100644 index 4fc41a65efd1cc..00000000000000 --- a/x-pack/plugins/ml/public/embeddables/get_embeddable_component.tsx +++ /dev/null @@ -1,56 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { EuiLoadingChart } from '@elastic/eui'; -import type { CoreStart } from '@kbn/core/public'; -import type { EmbeddableFactory, EmbeddableInput } from '@kbn/embeddable-plugin/public'; -import { EmbeddableRoot, useEmbeddableFactory } from '@kbn/embeddable-plugin/public'; -import React from 'react'; -import type { MlStartDependencies } from '../plugin'; -import type { AnomalyExplorerChartsEmbeddableType } from './constants'; -import type { MappedEmbeddableTypeOf } from './types'; - -/** - * Gets an instance of an embeddable component of requested type. - * @param embeddableType - * @param core - * @param plugins - */ -export function getEmbeddableComponent( - embeddableType: EmbeddableType, - core: CoreStart, - plugins: MlStartDependencies -) { - const { embeddable: embeddableStart } = plugins; - - const factory = - embeddableStart.getEmbeddableFactory>(embeddableType); - - if (!factory) { - throw new Error(`Embeddable type "${embeddableType}" has not been registered.`); - } - - return React.memo((props: MappedEmbeddableTypeOf) => { - return ; - }); -} - -interface EmbeddableRootWrapperProps { - factory: EmbeddableFactory; - input: TMlEmbeddableInput; -} - -const EmbeddableRootWrapper = ({ - factory, - input, -}: EmbeddableRootWrapperProps) => { - const [embeddable, loading, error] = useEmbeddableFactory({ factory, input }); - if (loading) { - return ; - } - return ; -}; diff --git a/x-pack/plugins/ml/public/embeddables/index.ts b/x-pack/plugins/ml/public/embeddables/index.ts index 936c76bc027036..9145bbddf58180 100644 --- a/x-pack/plugins/ml/public/embeddables/index.ts +++ b/x-pack/plugins/ml/public/embeddables/index.ts @@ -7,12 +7,13 @@ import type { EmbeddableSetup } from '@kbn/embeddable-plugin/public'; import type { MlCoreSetup } from '../plugin'; -import { AnomalyChartsEmbeddableFactory } from './anomaly_charts'; -import { ANOMALY_SINGLE_METRIC_VIEWER_EMBEDDABLE_TYPE } from './constants'; -import { ANOMALY_SWIMLANE_EMBEDDABLE_TYPE } from './constants'; +import { + ANOMALY_SINGLE_METRIC_VIEWER_EMBEDDABLE_TYPE, + ANOMALY_SWIMLANE_EMBEDDABLE_TYPE, + ANOMALY_EXPLORER_CHARTS_EMBEDDABLE_TYPE, +} from './constants'; export * from './constants'; -export { getEmbeddableComponent } from './get_embeddable_component'; export * from './types'; export function registerEmbeddables(embeddable: EmbeddableSetup, core: MlCoreSetup) { @@ -21,8 +22,12 @@ export function registerEmbeddables(embeddable: EmbeddableSetup, core: MlCoreSet return getAnomalySwimLaneEmbeddableFactory(core.getStartServices); }); - const anomalyChartsFactory = new AnomalyChartsEmbeddableFactory(core.getStartServices); - embeddable.registerEmbeddableFactory(anomalyChartsFactory.type, anomalyChartsFactory); + embeddable.registerReactEmbeddableFactory(ANOMALY_EXPLORER_CHARTS_EMBEDDABLE_TYPE, async () => { + const { getAnomalyChartsReactEmbeddableFactory } = await import( + './anomaly_charts/anomaly_charts_embeddable_factory' + ); + return getAnomalyChartsReactEmbeddableFactory(core.getStartServices); + }); embeddable.registerReactEmbeddableFactory( ANOMALY_SINGLE_METRIC_VIEWER_EMBEDDABLE_TYPE, diff --git a/x-pack/plugins/ml/public/embeddables/types.ts b/x-pack/plugins/ml/public/embeddables/types.ts index 34e0b1afb32659..94389c69c20fce 100644 --- a/x-pack/plugins/ml/public/embeddables/types.ts +++ b/x-pack/plugins/ml/public/embeddables/types.ts @@ -7,13 +7,7 @@ import type { CoreStart } from '@kbn/core/public'; import type { RefreshInterval } from '@kbn/data-plugin/common'; -import type { DataView } from '@kbn/data-views-plugin/common'; -import type { - DefaultEmbeddableApi, - EmbeddableInput, - EmbeddableOutput, - IEmbeddable, -} from '@kbn/embeddable-plugin/public'; +import type { DefaultEmbeddableApi, EmbeddableInput } from '@kbn/embeddable-plugin/public'; import type { Filter, Query, TimeRange } from '@kbn/es-query'; import type { MlEntityField } from '@kbn/ml-anomaly-utils'; import type { @@ -25,8 +19,10 @@ import type { PublishingSubject, PublishesTimeRange, PublishesWritablePanelTitle, + PublishesDataViews, SerializedTitles, } from '@kbn/presentation-publishing'; +import { type BehaviorSubject } from 'rxjs'; import type { JobId } from '../../common/types/anomaly_detection_jobs'; import type { MlDependencies } from '../application/app'; import type { MlCapabilitiesService } from '../application/capabilities/check_capabilities'; @@ -108,21 +104,74 @@ export interface SwimLaneDrilldownContext extends EditSwimlanePanelContext { } /** - * Anomaly Explorer + * Anomaly Explorer Charts */ -export interface AnomalyChartsEmbeddableCustomInput { + +export interface AnomalyChartsEmbeddableRuntimeState { jobIds: JobId[]; maxSeriesToPlot: number; - // Embeddable inputs which are not included in the default interface - filters: Filter[]; - query: Query; - refreshConfig: RefreshInterval; - timeRange: TimeRange; severityThreshold?: number; + selectedEntities?: MlEntityField[]; +} +export interface AnomalyChartsEmbeddableOverridableState + extends AnomalyChartsEmbeddableRuntimeState { + timeRange?: TimeRange; +} +export interface AnomalyChartsComponentApi { + jobIds$: PublishingSubject; + maxSeriesToPlot$: PublishingSubject; + severityThreshold$: PublishingSubject; + selectedEntities$: PublishingSubject; + updateUserInput: (input: AnomalyChartsEmbeddableOverridableState) => void; + updateSeverityThreshold: (v?: number) => void; + updateSelectedEntities: (entities?: MlEntityField[] | undefined) => void; +} +export interface AnomalyChartsDataLoadingApi { + onRenderComplete: () => void; + onLoading: (v: boolean) => void; + onError: (error?: Error) => void; } -export type AnomalyChartsEmbeddableInput = EmbeddableInput & AnomalyChartsEmbeddableCustomInput; +/** + * Persisted state for the Anomaly Charts Embeddable. + */ +export interface AnomalyChartsEmbeddableState + extends SerializedTitles, + AnomalyChartsEmbeddableOverridableState {} + +export type AnomalyChartsApi = AnomalyChartsComponentApi & AnomalyChartsDataLoadingApi; + +export type AnomalyChartsEmbeddableApi = MlEmbeddableBaseApi & + PublishesDataViews & + PublishesWritablePanelTitle & + HasEditCapabilities & + AnomalyChartsApi; + +export interface AnomalyChartsFieldSelectionApi { + jobIds: PublishingSubject; + entityFields: PublishingSubject; +} + +export interface AnomalyChartsAttachmentState extends AnomalyChartsEmbeddableState { + query?: Query; + filters?: Filter[]; +} + +export interface AnomalyChartsAttachmentApi extends AnomalyChartsApi { + parentApi: { + query$: BehaviorSubject; + filters$: BehaviorSubject; + timeRange$: BehaviorSubject; + }; +} + +/** + * Persisted state for the Anomaly Charts Embeddable. + */ +export interface AnomalyChartsEmbeddableState + extends SerializedTitles, + AnomalyChartsEmbeddableOverridableState {} /** Manual input by the user */ export interface SingleMetricViewerEmbeddableUserInput { @@ -205,14 +254,8 @@ export type SingleMetricViewerEmbeddableServices = [ MlDependencies, SingleMetricViewerServices ]; -export interface AnomalyChartsCustomOutput { - entityFields?: MlEntityField[]; - severity?: number; - indexPatterns: DataView[]; -} -export type AnomalyChartsEmbeddableOutput = EmbeddableOutput & AnomalyChartsCustomOutput; export interface EditAnomalyChartsPanelContext { - embeddable: IEmbeddable; + embeddable: AnomalyChartsEmbeddableApi; } export interface AnomalyChartsFieldSelectionContext extends EditAnomalyChartsPanelContext { @@ -224,5 +267,5 @@ export interface AnomalyChartsFieldSelectionContext extends EditAnomalyChartsPan export type MappedEmbeddableTypeOf = TEmbeddableType extends AnomalyExplorerChartsEmbeddableType - ? AnomalyChartsEmbeddableInput + ? AnomalyChartsEmbeddableState : unknown; diff --git a/x-pack/plugins/ml/public/embeddables/utils.ts b/x-pack/plugins/ml/public/embeddables/utils.ts new file mode 100644 index 00000000000000..807a0d8a7b8d28 --- /dev/null +++ b/x-pack/plugins/ml/public/embeddables/utils.ts @@ -0,0 +1,17 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ +import { i18n } from '@kbn/i18n'; + +export const getJobSelectionErrors = (jobIds: string[]) => { + if (jobIds.length === 0) { + return [ + i18n.translate('xpack.ml.swimlaneEmbeddable.setupModal.jobSelectionRequiredError', { + defaultMessage: 'Job selection is required', + }), + ]; + } +}; diff --git a/x-pack/plugins/ml/public/ui_actions/create_anomaly_chart.tsx b/x-pack/plugins/ml/public/ui_actions/create_anomaly_chart.tsx new file mode 100644 index 00000000000000..c4b88bdd433069 --- /dev/null +++ b/x-pack/plugins/ml/public/ui_actions/create_anomaly_chart.tsx @@ -0,0 +1,82 @@ +/* + * Copyright 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 type { PresentationContainer } from '@kbn/presentation-containers'; +import type { EmbeddableApiContext } from '@kbn/presentation-publishing'; +import type { UiActionsActionDefinition } from '@kbn/ui-actions-plugin/public'; +import { IncompatibleActionError } from '@kbn/ui-actions-plugin/public'; +import { ML_APP_NAME, PLUGIN_ICON, PLUGIN_ID } from '../../common/constants/app'; +import type { AnomalyChartsEmbeddableApi } from '../embeddables'; +import { ANOMALY_EXPLORER_CHARTS_EMBEDDABLE_TYPE } from '../embeddables'; +import type { MlCoreSetup } from '../plugin'; + +export const EDIT_ANOMALY_CHARTS_PANEL_ACTION = 'editAnomalyChartsPanelAction'; + +export type CreateAnomalyChartsPanelActionContext = EmbeddableApiContext & { + embeddable: AnomalyChartsEmbeddableApi; +}; + +const parentApiIsCompatible = async ( + parentApi: unknown +): Promise => { + const { apiIsPresentationContainer } = await import('@kbn/presentation-containers'); + // we cannot have an async type check, so return the casted parentApi rather than a boolean + return apiIsPresentationContainer(parentApi) ? (parentApi as PresentationContainer) : undefined; +}; + +export function createAddAnomalyChartsPanelAction( + getStartServices: MlCoreSetup['getStartServices'] +): UiActionsActionDefinition { + return { + id: 'create-anomaly-charts', + grouping: [ + { + id: PLUGIN_ID, + getDisplayName: () => ML_APP_NAME, + getIconType: () => PLUGIN_ICON, + }, + ], + getDisplayName: () => + i18n.translate('xpack.ml.components.mlAnomalyExplorerEmbeddable.displayName', { + defaultMessage: 'Anomaly chart', + }), + getDisplayNameTooltip: () => + i18n.translate('xpack.ml.components.mlAnomalyExplorerEmbeddable.description', { + defaultMessage: 'View anomaly detection results in a chart.', + }), + async isCompatible(context: EmbeddableApiContext) { + return Boolean(await parentApiIsCompatible(context.embeddable)); + }, + async execute(context) { + const presentationContainerParent = await parentApiIsCompatible(context.embeddable); + if (!presentationContainerParent) throw new IncompatibleActionError(); + + const [coreStart, pluginStart] = await getStartServices(); + try { + const { resolveEmbeddableAnomalyChartsUserInput } = await import( + '../embeddables/anomaly_charts/anomaly_charts_setup_flyout' + ); + const initialState = await resolveEmbeddableAnomalyChartsUserInput( + coreStart, + pluginStart, + context.embeddable, + context.embeddable.uuid + ); + + presentationContainerParent.addNewPanel({ + panelType: ANOMALY_EXPLORER_CHARTS_EMBEDDABLE_TYPE, + initialState, + }); + } catch (e) { + // eslint-disable-next-line no-console + console.error(e); + return Promise.reject(); + } + }, + }; +} diff --git a/x-pack/plugins/ml/public/ui_actions/edit_anomaly_charts_panel_action.tsx b/x-pack/plugins/ml/public/ui_actions/edit_anomaly_charts_panel_action.tsx deleted file mode 100644 index 68d629e3f3ceb8..00000000000000 --- a/x-pack/plugins/ml/public/ui_actions/edit_anomaly_charts_panel_action.tsx +++ /dev/null @@ -1,59 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { i18n } from '@kbn/i18n'; -import type { UiActionsActionDefinition } from '@kbn/ui-actions-plugin/public'; -import { ViewMode } from '@kbn/embeddable-plugin/public'; -import type { MlCoreSetup } from '../plugin'; -import type { EditAnomalyChartsPanelContext } from '../embeddables'; -import { ANOMALY_EXPLORER_CHARTS_EMBEDDABLE_TYPE } from '../embeddables'; - -export const EDIT_ANOMALY_CHARTS_PANEL_ACTION = 'editAnomalyChartsPanelAction'; - -export function createEditAnomalyChartsPanelAction( - getStartServices: MlCoreSetup['getStartServices'] -): UiActionsActionDefinition { - return { - id: 'edit-anomaly-charts', - type: EDIT_ANOMALY_CHARTS_PANEL_ACTION, - getIconType(context): string { - return 'pencil'; - }, - getDisplayName: () => - i18n.translate('xpack.ml.actions.editAnomalyChartsTitle', { - defaultMessage: 'Edit anomaly charts', - }), - async execute({ embeddable }) { - if (!embeddable) { - throw new Error('Not possible to execute an action without the embeddable context'); - } - - const [coreStart, deps] = await getStartServices(); - - try { - const { resolveEmbeddableAnomalyChartsUserInput } = await import( - '../embeddables/anomaly_charts/anomaly_charts_setup_flyout' - ); - - const result = await resolveEmbeddableAnomalyChartsUserInput( - coreStart, - deps.data.dataViews, - embeddable.getInput() - ); - embeddable.updateInput(result); - } catch (e) { - return Promise.reject(); - } - }, - async isCompatible({ embeddable }) { - return ( - embeddable.type === ANOMALY_EXPLORER_CHARTS_EMBEDDABLE_TYPE && - embeddable.getInput().viewMode === ViewMode.EDIT - ); - }, - }; -} diff --git a/x-pack/plugins/ml/public/ui_actions/index.ts b/x-pack/plugins/ml/public/ui_actions/index.ts index 08fda084b413a9..66dd1f0f06f34c 100644 --- a/x-pack/plugins/ml/public/ui_actions/index.ts +++ b/x-pack/plugins/ml/public/ui_actions/index.ts @@ -15,7 +15,6 @@ import { createApplyInfluencerFiltersAction } from './apply_influencer_filters_a import { createApplyTimeRangeSelectionAction } from './apply_time_range_action'; import { createClearSelectionAction } from './clear_selection_action'; import { createAddSwimlanePanelAction } from './create_swim_lane'; -import { createEditAnomalyChartsPanelAction } from './edit_anomaly_charts_panel_action'; import { createAddSingleMetricViewerPanelAction } from './create_single_metric_viewer'; import { createCategorizationADJobAction, @@ -30,6 +29,7 @@ import { SWIM_LANE_SELECTION_TRIGGER, swimLaneSelectionTrigger, } from './triggers'; +import { createAddAnomalyChartsPanelAction } from './create_anomaly_chart'; export { APPLY_INFLUENCER_FILTERS_ACTION } from './apply_influencer_filters_action'; export { APPLY_TIME_RANGE_SELECTION_ACTION } from './apply_time_range_action'; export { OPEN_IN_ANOMALY_EXPLORER_ACTION } from './open_in_anomaly_explorer_action'; @@ -55,19 +55,22 @@ export function registerMlUiActions( const applyEntityFieldFilterAction = createApplyEntityFieldFiltersAction(core.getStartServices); const applyTimeRangeSelectionAction = createApplyTimeRangeSelectionAction(core.getStartServices); const clearSelectionAction = createClearSelectionAction(core.getStartServices); - const editExplorerPanelAction = createEditAnomalyChartsPanelAction(core.getStartServices); const visToAdJobAction = createVisToADJobAction(core.getStartServices); const categorizationADJobAction = createCategorizationADJobAction(core.getStartServices); + const addAnomalyChartsPanelAction = createAddAnomalyChartsPanelAction(core.getStartServices); + // Register actions uiActions.registerAction(applyEntityFieldFilterAction); uiActions.registerAction(applyTimeRangeSelectionAction); uiActions.registerAction(categorizationADJobAction); + uiActions.registerAction(addAnomalyChartsPanelAction); // Assign triggers uiActions.addTriggerAction('ADD_PANEL_TRIGGER', addSingleMetricViewerPanelAction); uiActions.addTriggerAction('ADD_PANEL_TRIGGER', addSwimlanePanelAction); - uiActions.addTriggerAction(CONTEXT_MENU_TRIGGER, editExplorerPanelAction); + uiActions.addTriggerAction('ADD_PANEL_TRIGGER', addAnomalyChartsPanelAction); + uiActions.addTriggerAction(CONTEXT_MENU_TRIGGER, openInExplorerAction); uiActions.attachAction(CONTEXT_MENU_TRIGGER, openInSingleMetricViewerAction.id); diff --git a/x-pack/plugins/ml/public/ui_actions/open_in_anomaly_explorer_action.tsx b/x-pack/plugins/ml/public/ui_actions/open_in_anomaly_explorer_action.tsx index 426f6f32ee242e..98a44c042db58b 100644 --- a/x-pack/plugins/ml/public/ui_actions/open_in_anomaly_explorer_action.tsx +++ b/x-pack/plugins/ml/public/ui_actions/open_in_anomaly_explorer_action.tsx @@ -16,8 +16,8 @@ import type { SerializableRecord } from '@kbn/utility-types'; import { ML_APP_LOCATOR } from '../../common/constants/locator'; import type { ExplorerAppState } from '../../common/types/locator'; import type { AppStateSelectedCells } from '../application/explorer/explorer_utils'; +import type { AnomalyChartsApi, AnomalyChartsEmbeddableApi } from '../embeddables'; import { ANOMALY_EXPLORER_CHARTS_EMBEDDABLE_TYPE } from '../embeddables'; -import type { AnomalyChartsEmbeddableApi } from '../embeddables/anomaly_charts/types'; import type { AnomalySwimLaneEmbeddableApi } from '../embeddables/anomaly_swimlane/types'; import { isSwimLaneEmbeddableContext } from '../embeddables/anomaly_swimlane/types'; import type { MlCoreSetup } from '../plugin'; @@ -41,9 +41,9 @@ export interface OpenInAnomalyExplorerAnomalyChartsActionContext extends Embedda export const OPEN_IN_ANOMALY_EXPLORER_ACTION = 'openInAnomalyExplorerAction'; -export function isAnomalyChartsEmbeddableContext( - arg: unknown -): arg is OpenInAnomalyExplorerAnomalyChartsActionContext { +export function isAnomalyChartsEmbeddableContext(arg: unknown): arg is { + embeddable: AnomalyChartsApi; +} { return ( isPopulatedObject(arg, ['embeddable']) && apiIsOfType(arg.embeddable, ANOMALY_EXPLORER_CHARTS_EMBEDDABLE_TYPE) @@ -97,10 +97,12 @@ export function createOpenInExplorerAction( }); } else if (isAnomalyChartsEmbeddableContext(context)) { const { embeddable } = context; - const { jobIds, entityFields } = embeddable; + const { jobIds$, selectedEntities$ } = embeddable; + const jobIds = jobIds$?.getValue() ?? []; let mlExplorerFilter: ExplorerAppState['mlExplorerFilter'] | undefined; - const entityFieldsValue = entityFields.getValue(); + const entityFieldsValue = selectedEntities$?.getValue(); + if ( Array.isArray(entityFieldsValue) && entityFieldsValue.length === 1 && @@ -132,7 +134,7 @@ export function createOpenInExplorerAction( return locator.getUrl({ page: 'explorer', pageState: { - jobIds: jobIds.getValue(), + jobIds, timeRange: getEmbeddableTimeRange(embeddable), // @ts-ignore QueryDslQueryContainer is not compatible with SerializableRecord ...(mlExplorerFilter ? ({ mlExplorerFilter } as SerializableRecord) : {}), diff --git a/x-pack/plugins/ml/server/models/results_service/results_service.ts b/x-pack/plugins/ml/server/models/results_service/results_service.ts index aa0b075002367a..0a732cf45a94c8 100644 --- a/x-pack/plugins/ml/server/models/results_service/results_service.ts +++ b/x-pack/plugins/ml/server/models/results_service/results_service.ts @@ -157,7 +157,7 @@ export function resultsServiceProvider(mlClient: MlClient, client?: IScopedClust }); } - if (influencersFilterQuery !== undefined) { + if (influencersFilterQuery) { boolCriteria.push(influencersFilterQuery); } diff --git a/x-pack/plugins/observability_solution/apm/ftr_e2e/cypress/e2e/errors/error_details.cy.ts b/x-pack/plugins/observability_solution/apm/ftr_e2e/cypress/e2e/errors/error_details.cy.ts index d470202d9f041b..e61762629a5371 100644 --- a/x-pack/plugins/observability_solution/apm/ftr_e2e/cypress/e2e/errors/error_details.cy.ts +++ b/x-pack/plugins/observability_solution/apm/ftr_e2e/cypress/e2e/errors/error_details.cy.ts @@ -6,6 +6,8 @@ */ import { getErrorGroupingKey } from '@kbn/apm-synthtrace-client/src/lib/apm/instance'; +import { generateLongId } from '@kbn/apm-synthtrace-client/src/lib/utils/generate_id'; + import url from 'url'; import { synthtrace } from '../../../synthtrace'; import { checkA11y } from '../../support/commands'; @@ -69,7 +71,7 @@ describe('Error details', () => { }); describe('when error has data', () => { - const errorGroupingKey = getErrorGroupingKey('Error 1'); + const errorGroupingKey = generateLongId('Error 1'); const errorGroupingKeyShort = errorGroupingKey.slice(0, 5); const errorDetailsPageHref = url.format({ pathname: `/app/apm/services/opbeans-java/errors/${errorGroupingKey}`, diff --git a/x-pack/plugins/observability_solution/apm/public/components/alerting/ui_components/alert_details_app_section/view_in_apm_button.test.tsx b/x-pack/plugins/observability_solution/apm/public/components/alerting/ui_components/alert_details_app_section/view_in_apm_button.test.tsx new file mode 100644 index 00000000000000..703f4324238ba4 --- /dev/null +++ b/x-pack/plugins/observability_solution/apm/public/components/alerting/ui_components/alert_details_app_section/view_in_apm_button.test.tsx @@ -0,0 +1,49 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ +import React from 'react'; +import { render } from '@testing-library/react'; +import { ViewInAPMButton } from './view_in_apm_button'; +import * as apmContext from '../../../../context/apm_plugin/use_apm_plugin_context'; + +describe('ViewInApmButton', () => { + const config = { + serviceName: 'testService', + environment: 'testEnvironment', + transactionName: 'testTransaction', + transactionType: 'testTransactionType', + from: 'now-15m', + to: 'now', + }; + + beforeEach(() => { + jest.clearAllMocks(); + }); + + it('hides button when share plugin is not available', () => { + const { queryByText } = render(); + + expect(queryByText('View in APM')).not.toBeInTheDocument(); + }); + + it('reners correctly', () => { + jest.spyOn(apmContext, 'useApmPluginContext').mockReturnValue({ + share: { + url: { + locators: { + // @ts-ignore + get: () => ({ + navigate: jest.fn(), + }), + }, + }, + }, + }); + const { getByText } = render(); + + expect(getByText('View in APM')).toBeInTheDocument(); + }); +}); diff --git a/x-pack/plugins/observability_solution/apm/public/components/alerting/ui_components/alert_details_app_section/view_in_apm_button.tsx b/x-pack/plugins/observability_solution/apm/public/components/alerting/ui_components/alert_details_app_section/view_in_apm_button.tsx index ac67359d645688..f8935a4082ded1 100644 --- a/x-pack/plugins/observability_solution/apm/public/components/alerting/ui_components/alert_details_app_section/view_in_apm_button.tsx +++ b/x-pack/plugins/observability_solution/apm/public/components/alerting/ui_components/alert_details_app_section/view_in_apm_button.tsx @@ -4,7 +4,6 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ -/* Error Rate */ import React from 'react'; import { FormattedMessage } from '@kbn/i18n-react'; @@ -29,8 +28,8 @@ export function ViewInAPMButton({ to: string; kuery?: string; }) { - const { share } = useApmPluginContext(); - const serviceNavigator = share.url.locators.get(APM_APP_LOCATOR_ID); + const { share } = useApmPluginContext() || {}; + const serviceNavigator = share?.url?.locators?.get(APM_APP_LOCATOR_ID); if (!serviceNavigator) { return null; diff --git a/x-pack/plugins/observability_solution/apm/public/components/shared/ml_callout/index.tsx b/x-pack/plugins/observability_solution/apm/public/components/shared/ml_callout/index.tsx index 404e9dcba13b61..0a6058a2c28c6a 100644 --- a/x-pack/plugins/observability_solution/apm/public/components/shared/ml_callout/index.tsx +++ b/x-pack/plugins/observability_solution/apm/public/components/shared/ml_callout/index.tsx @@ -10,7 +10,7 @@ import { i18n } from '@kbn/i18n'; import React, { useState } from 'react'; import { AnomalyDetectionSetupState } from '../../../../common/anomaly_detection/get_anomaly_detection_setup_state'; import { useMlManageJobsHref } from '../../../hooks/use_ml_manage_jobs_href'; -import { LegacyAPMLink } from '../links/apm/apm_link'; +import { useAPMHref } from '../links/apm/apm_link'; export function shouldDisplayMlCallout(anomalyDetectionSetupState: AnomalyDetectionSetupState) { return ( @@ -35,7 +35,7 @@ export function MLCallout({ append?: React.ReactElement; }) { const [loading, setLoading] = useState(false); - + const apmGetLearnMoreHref = useAPMHref({ path: '/settings/anomaly-detection' }); const mlManageJobsHref = useMlManageJobsHref(); let properties: @@ -48,19 +48,19 @@ export function MLCallout({ } | undefined; - const getLearnMoreLink = (color: 'primary' | 'success') => ( - - { + return ( + {i18n.translate('xpack.apm.mlCallout.learnMoreButton', { defaultMessage: `Learn more`, })} - - - ); +
+ ); + }; switch (anomalyDetectionSetupState) { case AnomalyDetectionSetupState.NoJobs: diff --git a/x-pack/plugins/observability_solution/apm/server/routes/storage_explorer/indices_stats_helpers.ts b/x-pack/plugins/observability_solution/apm/server/routes/storage_explorer/indices_stats_helpers.ts index 3943f349e55e6d..85e3dbfb97ca1a 100644 --- a/x-pack/plugins/observability_solution/apm/server/routes/storage_explorer/indices_stats_helpers.ts +++ b/x-pack/plugins/observability_solution/apm/server/routes/storage_explorer/indices_stats_helpers.ts @@ -74,7 +74,7 @@ export async function getIndicesLifecycleStatus({ filter_path: 'indices.*.phase', }); - return indices; + return indices || {}; } export async function getIndicesInfo({ diff --git a/x-pack/plugins/observability_solution/infra/common/alerting/metrics/types.ts b/x-pack/plugins/observability_solution/infra/common/alerting/metrics/types.ts index 9121ec11adfab8..b5a3c084c47cb8 100644 --- a/x-pack/plugins/observability_solution/infra/common/alerting/metrics/types.ts +++ b/x-pack/plugins/observability_solution/infra/common/alerting/metrics/types.ts @@ -4,10 +4,10 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ -import * as rt from 'io-ts'; import { TimeUnitChar } from '@kbn/observability-plugin/common/utils/formatters/duration'; -import { ML_ANOMALY_THRESHOLD } from '@kbn/ml-anomaly-utils/anomaly_threshold'; import { InventoryItemType, SnapshotMetricType } from '@kbn/metrics-data-access-plugin/common'; +import { COMPARATORS } from '@kbn/alerting-comparators'; +import { LEGACY_COMPARATORS } from '@kbn/observability-plugin/common/utils/convert_legacy_outside_comparator'; import { SnapshotCustomMetricInput } from '../../http_api'; export const METRIC_THRESHOLD_ALERT_TYPE_ID = 'metrics.alert.threshold'; @@ -18,20 +18,6 @@ export enum InfraRuleType { InventoryThreshold = 'metrics.alert.inventory.threshold', } -export interface InfraRuleTypeParams { - [InfraRuleType.MetricThreshold]: MetricThresholdParams; - [InfraRuleType.InventoryThreshold]: InventoryMetricConditions; -} - -export enum Comparator { - GT = '>', - LT = '<', - GT_OR_EQ = '>=', - LT_OR_EQ = '<=', - BETWEEN = 'between', - OUTSIDE_RANGE = 'outside', -} - export enum Aggregators { COUNT = 'count', AVERAGE = 'avg', @@ -53,27 +39,6 @@ export enum AlertStates { ERROR, } -const metricAnomalyNodeTypeRT = rt.union([rt.literal('hosts'), rt.literal('k8s')]); -const metricAnomalyMetricRT = rt.union([ - rt.literal('memory_usage'), - rt.literal('network_in'), - rt.literal('network_out'), -]); -const metricAnomalyInfluencerFilterRT = rt.type({ - fieldName: rt.string, - fieldValue: rt.string, -}); - -export interface MetricAnomalyParams { - nodeType: rt.TypeOf; - metric: rt.TypeOf; - alertInterval?: string; - sourceId?: string; - spaceId?: string; - threshold: Exclude; - influencerFilter: rt.TypeOf | undefined; -} - // Types for the executor export interface InventoryMetricConditions { @@ -82,10 +47,10 @@ export interface InventoryMetricConditions { timeUnit: TimeUnitChar; sourceId?: string; threshold: number[]; - comparator: Comparator; + comparator: COMPARATORS | LEGACY_COMPARATORS; customMetric?: SnapshotCustomMetricInput; warningThreshold?: number[]; - warningComparator?: Comparator; + warningComparator?: COMPARATORS | LEGACY_COMPARATORS; } export interface InventoryMetricThresholdParams { @@ -111,8 +76,8 @@ interface BaseMetricExpressionParams { timeUnit: TimeUnitChar; sourceId?: string; threshold: number[]; - comparator: Comparator; - warningComparator?: Comparator; + comparator: COMPARATORS | LEGACY_COMPARATORS; + warningComparator?: COMPARATORS | LEGACY_COMPARATORS; warningThreshold?: number[]; } diff --git a/x-pack/plugins/observability_solution/infra/common/constants.ts b/x-pack/plugins/observability_solution/infra/common/constants.ts index e4621f63793c2d..d0f48068dda644 100644 --- a/x-pack/plugins/observability_solution/infra/common/constants.ts +++ b/x-pack/plugins/observability_solution/infra/common/constants.ts @@ -37,3 +37,8 @@ export const O11Y_AAD_FIELDS = [ export const LINK_TO_INVENTORY = '/app/metrics/link-to/inventory'; export const METRICS_EXPLORER_URL = '/app/metrics/explorer'; export const fifteenMinutesInMilliseconds = 15 * 60 * 1000; + +export const DEFAULT_METRICS_VIEW_ATTRIBUTES = { + name: 'Metrics View', + timeFieldName: TIMESTAMP_FIELD, +}; diff --git a/x-pack/plugins/observability_solution/infra/public/alerting/common/components/threshold.stories.tsx b/x-pack/plugins/observability_solution/infra/public/alerting/common/components/threshold.stories.tsx index 73361902799c63..0b253a981b1228 100644 --- a/x-pack/plugins/observability_solution/infra/public/alerting/common/components/threshold.stories.tsx +++ b/x-pack/plugins/observability_solution/infra/public/alerting/common/components/threshold.stories.tsx @@ -8,7 +8,7 @@ import React from 'react'; import { ComponentMeta } from '@storybook/react'; import { LIGHT_THEME } from '@elastic/charts'; -import { Comparator } from '../../../../common/alerting/metrics'; +import { COMPARATORS } from '@kbn/alerting-comparators'; import { Props, Threshold as Component } from './threshold'; export default { @@ -30,7 +30,7 @@ export default { const defaultProps: Props = { chartProps: { baseTheme: LIGHT_THEME }, - comparator: Comparator.GT, + comparator: COMPARATORS.GREATER_THAN, id: 'componentId', threshold: 90, title: 'Threshold breached', diff --git a/x-pack/plugins/observability_solution/infra/public/alerting/common/components/threshold.test.tsx b/x-pack/plugins/observability_solution/infra/public/alerting/common/components/threshold.test.tsx index b78216b78f60c5..3decd9cfdd9c02 100644 --- a/x-pack/plugins/observability_solution/infra/public/alerting/common/components/threshold.test.tsx +++ b/x-pack/plugins/observability_solution/infra/public/alerting/common/components/threshold.test.tsx @@ -6,7 +6,7 @@ */ import { LIGHT_THEME } from '@elastic/charts'; -import { Comparator } from '../../../../common/alerting/metrics'; +import { COMPARATORS } from '@kbn/alerting-comparators'; import { render } from '@testing-library/react'; import { Props, Threshold } from './threshold'; import React from 'react'; @@ -15,7 +15,7 @@ describe('Threshold', () => { const renderComponent = (props: Partial = {}) => { const defaultProps: Props = { chartProps: { baseTheme: LIGHT_THEME }, - comparator: Comparator.GT, + comparator: COMPARATORS.GREATER_THAN, id: 'componentId', threshold: 90, title: 'Threshold breached', diff --git a/x-pack/plugins/observability_solution/infra/public/alerting/common/components/threshold.tsx b/x-pack/plugins/observability_solution/infra/public/alerting/common/components/threshold.tsx index d9ca396f9aa332..bb710a165733bf 100644 --- a/x-pack/plugins/observability_solution/infra/public/alerting/common/components/threshold.tsx +++ b/x-pack/plugins/observability_solution/infra/public/alerting/common/components/threshold.tsx @@ -10,8 +10,7 @@ import { Chart, Metric, Settings } from '@elastic/charts'; import { EuiIcon, EuiPanel, useEuiBackgroundColor } from '@elastic/eui'; import type { PartialTheme, Theme } from '@elastic/charts'; import { i18n } from '@kbn/i18n'; -import { Comparator } from '../../../../common/alerting/metrics'; - +import { COMPARATORS } from '@kbn/alerting-comparators'; export interface ChartProps { theme?: PartialTheme; baseTheme: Theme; @@ -19,7 +18,7 @@ export interface ChartProps { export interface Props { chartProps: ChartProps; - comparator: Comparator | string; + comparator: COMPARATORS | string; id: string; threshold: number; title: string; diff --git a/x-pack/plugins/observability_solution/infra/public/alerting/common/criterion_preview_chart/threshold_annotations.test.tsx b/x-pack/plugins/observability_solution/infra/public/alerting/common/criterion_preview_chart/threshold_annotations.test.tsx index be8e474b601142..a5dd3386fc0838 100644 --- a/x-pack/plugins/observability_solution/infra/public/alerting/common/criterion_preview_chart/threshold_annotations.test.tsx +++ b/x-pack/plugins/observability_solution/infra/public/alerting/common/criterion_preview_chart/threshold_annotations.test.tsx @@ -6,7 +6,7 @@ */ import { shallow } from 'enzyme'; import React from 'react'; -import { Comparator } from '../../../../common/alerting/metrics'; +import { COMPARATORS } from '@kbn/alerting-comparators'; import { Color } from '../../../../common/color_palette'; import { ThresholdAnnotations } from './threshold_annotations'; @@ -29,7 +29,7 @@ describe('ThresholdAnnotations', () => { const defaultProps = { threshold: [20, 30], sortedThresholds: [20, 30], - comparator: Comparator.GT, + comparator: COMPARATORS.GREATER_THAN, color: Color.color0, id: 'testId', firstTimestamp: 123456789, @@ -53,7 +53,7 @@ describe('ThresholdAnnotations', () => { }); it('should render a rectangular annotation for in between thresholds', async () => { - const wrapper = await setup({ comparator: Comparator.BETWEEN }); + const wrapper = await setup({ comparator: COMPARATORS.BETWEEN }); const annotation = wrapper.find('[data-test-subj="between-rect"]'); const expectedValues = [ @@ -72,7 +72,7 @@ describe('ThresholdAnnotations', () => { }); it('should render an upper rectangular annotation for outside range thresholds', async () => { - const wrapper = await setup({ comparator: Comparator.OUTSIDE_RANGE }); + const wrapper = await setup({ comparator: COMPARATORS.NOT_BETWEEN }); const annotation = wrapper.find('[data-test-subj="outside-range-lower-rect"]'); const expectedValues = [ @@ -91,7 +91,7 @@ describe('ThresholdAnnotations', () => { }); it('should render a lower rectangular annotation for outside range thresholds', async () => { - const wrapper = await setup({ comparator: Comparator.OUTSIDE_RANGE }); + const wrapper = await setup({ comparator: COMPARATORS.NOT_BETWEEN }); const annotation = wrapper.find('[data-test-subj="outside-range-upper-rect"]'); const expectedValues = [ @@ -110,7 +110,7 @@ describe('ThresholdAnnotations', () => { }); it('should render a rectangular annotation for below thresholds', async () => { - const wrapper = await setup({ comparator: Comparator.LT }); + const wrapper = await setup({ comparator: COMPARATORS.LESS_THAN }); const annotation = wrapper.find('[data-test-subj="below-rect"]'); const expectedValues = [ @@ -129,7 +129,7 @@ describe('ThresholdAnnotations', () => { }); it('should render a rectangular annotation for above thresholds', async () => { - const wrapper = await setup({ comparator: Comparator.GT }); + const wrapper = await setup({ comparator: COMPARATORS.GREATER_THAN }); const annotation = wrapper.find('[data-test-subj="above-rect"]'); const expectedValues = [ diff --git a/x-pack/plugins/observability_solution/infra/public/alerting/common/criterion_preview_chart/threshold_annotations.tsx b/x-pack/plugins/observability_solution/infra/public/alerting/common/criterion_preview_chart/threshold_annotations.tsx index 9400537bb9d7c7..82258a493537f5 100644 --- a/x-pack/plugins/observability_solution/infra/public/alerting/common/criterion_preview_chart/threshold_annotations.tsx +++ b/x-pack/plugins/observability_solution/infra/public/alerting/common/criterion_preview_chart/threshold_annotations.tsx @@ -7,13 +7,13 @@ import { AnnotationDomainType, LineAnnotation, RectAnnotation } from '@elastic/charts'; import { first, last } from 'lodash'; import React from 'react'; -import { Comparator } from '../../../../common/alerting/metrics'; +import { COMPARATORS } from '@kbn/alerting-comparators'; import { Color, colorTransformer } from '../../../../common/color_palette'; interface ThresholdAnnotationsProps { threshold: number[]; sortedThresholds: number[]; - comparator: Comparator; + comparator: COMPARATORS; color: Color; id: string; firstTimestamp: number; @@ -34,8 +34,10 @@ export const ThresholdAnnotations = ({ domain, }: ThresholdAnnotationsProps) => { if (!comparator || !threshold) return null; - const isAbove = [Comparator.GT, Comparator.GT_OR_EQ].includes(comparator); - const isBelow = [Comparator.LT, Comparator.LT_OR_EQ].includes(comparator); + const isAbove = [COMPARATORS.GREATER_THAN, COMPARATORS.GREATER_THAN_OR_EQUALS].includes( + comparator + ); + const isBelow = [COMPARATORS.LESS_THAN, COMPARATORS.LESS_THAN_OR_EQUALS].includes(comparator); return ( <> - {sortedThresholds.length === 2 && comparator === Comparator.BETWEEN ? ( + {sortedThresholds.length === 2 && comparator === COMPARATORS.BETWEEN ? ( <> ) : null} - {sortedThresholds.length === 2 && comparator === Comparator.OUTSIDE_RANGE ? ( + {sortedThresholds.length === 2 && comparator === COMPARATORS.NOT_BETWEEN ? ( <> ({ +import { ResolvedDataView } from '../../../utils/data_view'; +import { TIMESTAMP_FIELD } from '../../../../common/constants'; +import type { SnapshotCustomMetricInput } from '../../../../common/http_api'; + +const mockDataView = { + id: 'mock-id', + title: 'mock-title', + timeFieldName: TIMESTAMP_FIELD, + fields: [ + { + name: 'some.system.field', + type: 'bzzz', + searchable: true, + aggregatable: true, + }, + ] as Partial, + isPersisted: () => false, + getName: () => 'mock-data-view', + toSpec: () => ({}), +} as jest.Mocked; + +jest.mock('../../../containers/metrics_source', () => ({ withSourceProvider: () => jest.fn, useSourceContext: () => ({ source: { id: 'default' }, - createDerivedIndexPattern: () => ({ fields: [], title: 'metricbeat-*' }), + }), + useMetricsDataViewContext: () => ({ + metricsView: { + indices: 'metricbeat-*', + timeFieldName: mockDataView.timeFieldName, + fields: mockDataView.fields, + dataViewReference: mockDataView, + } as ResolvedDataView, + loading: false, + error: undefined, }), })); @@ -28,7 +58,6 @@ jest.mock('../../../hooks/use_kibana', () => ({ services: mockCoreMock.createStart(), }), })); - const exampleCustomMetric = { id: 'this-is-an-id', field: 'some.system.field', @@ -83,7 +112,7 @@ describe('Expression', () => { expect(ruleParams.criteria).toEqual([ { metric: 'memory', - comparator: Comparator.GT, + comparator: COMPARATORS.GREATER_THAN, threshold: [], timeSize: 1, timeUnit: 'm', @@ -103,7 +132,7 @@ describe('Expression', () => { timeSize: 1, timeUnit: 'm', threshold: [10], - comparator: Comparator.GT, + comparator: COMPARATORS.GREATER_THAN, }, ], nodeType: undefined, @@ -145,7 +174,7 @@ describe('Expression', () => { expect(ruleParams.criteria).toEqual([ { metric: 'custom', - comparator: Comparator.GT, + comparator: COMPARATORS.GREATER_THAN, threshold: [], timeSize: 1, timeUnit: 'm', @@ -174,15 +203,6 @@ describe('ExpressionRow', () => { metric: [], }} expression={expression} - fields={[ - { - name: 'some.system.field', - type: 'bzzz', - searchable: true, - aggregatable: true, - displayable: true, - }, - ]} /> ); @@ -198,7 +218,7 @@ describe('ExpressionRow', () => { } const expression = { metric: 'custom', - comparator: Comparator.GT, + comparator: COMPARATORS.GREATER_THAN, threshold: [], timeSize: 1, timeUnit: 'm', diff --git a/x-pack/plugins/observability_solution/infra/public/alerting/inventory/components/expression.tsx b/x-pack/plugins/observability_solution/infra/public/alerting/inventory/components/expression.tsx index 56bc91f3613f53..b3b62cabcb9ed4 100644 --- a/x-pack/plugins/observability_solution/infra/public/alerting/inventory/components/expression.tsx +++ b/x-pack/plugins/observability_solution/infra/public/alerting/inventory/components/expression.tsx @@ -52,19 +52,20 @@ import { SnapshotMetricType, SnapshotMetricTypeRT, } from '@kbn/metrics-data-access-plugin/common'; +import { COMPARATORS } from '@kbn/alerting-comparators'; +import { convertToBuiltInComparators } from '@kbn/observability-plugin/common'; +import { + SnapshotCustomMetricInput, + SnapshotCustomMetricInputRT, +} from '../../../../common/http_api'; import { - Comparator, FilterQuery, InventoryMetricConditions, QUERY_INVALID, } from '../../../../common/alerting/metrics'; -import { - SnapshotCustomMetricInput, - SnapshotCustomMetricInputRT, -} from '../../../../common/http_api/snapshot_api'; import { toMetricOpt } from '../../../../common/snapshot_metric_i18n'; import { - DerivedIndexPattern, + useMetricsDataViewContext, useSourceContext, withSourceProvider, } from '../../../containers/metrics_source'; @@ -106,7 +107,7 @@ type Props = Omit< export const defaultExpression = { metric: 'cpu' as SnapshotMetricType, - comparator: Comparator.GT, + comparator: COMPARATORS.GREATER_THAN, threshold: [], timeSize: 1, timeUnit: 'm', @@ -120,15 +121,12 @@ export const defaultExpression = { export const Expressions: React.FC = (props) => { const { setRuleParams, ruleParams, errors, metadata } = props; - const { source, createDerivedIndexPattern } = useSourceContext(); + const { source } = useSourceContext(); const [timeSize, setTimeSize] = useState(1); const [timeUnit, setTimeUnit] = useState('m'); - const derivedIndexPattern = useMemo( - () => createDerivedIndexPattern(), - [createDerivedIndexPattern] - ); + const { metricsView } = useMetricsDataViewContext(); const updateParams = useCallback( (id, e: InventoryMetricConditions) => { @@ -166,13 +164,13 @@ export const Expressions: React.FC = (props) => { try { setRuleParams( 'filterQuery', - convertKueryToElasticSearchQuery(filter, derivedIndexPattern, false) || '' + convertKueryToElasticSearchQuery(filter, metricsView?.dataViewReference, false) || '' ); } catch (e) { setRuleParams('filterQuery', QUERY_INVALID); } }, - [derivedIndexPattern, setRuleParams] + [metricsView?.dataViewReference, setRuleParams] ); /* eslint-disable-next-line react-hooks/exhaustive-deps */ @@ -247,10 +245,10 @@ export const Expressions: React.FC = (props) => { setRuleParams('filterQueryText', md.filter); setRuleParams( 'filterQuery', - convertKueryToElasticSearchQuery(md.filter, derivedIndexPattern) || '' + convertKueryToElasticSearchQuery(md.filter, metricsView?.dataViewReference) || '' ); } - }, [metadata, derivedIndexPattern, setRuleParams]); + }, [metadata, metricsView?.dataViewReference, setRuleParams]); useEffect(() => { const md = metadata; @@ -276,7 +274,7 @@ export const Expressions: React.FC = (props) => { if (!ruleParams.sourceId) { setRuleParams('sourceId', source?.id || 'default'); } - }, [metadata, derivedIndexPattern, defaultExpression, source]); // eslint-disable-line react-hooks/exhaustive-deps + }, [metadata, metricsView?.dataViewReference, defaultExpression, source]); // eslint-disable-line react-hooks/exhaustive-deps return ( <> @@ -314,7 +312,6 @@ export const Expressions: React.FC = (props) => { setRuleParams={updateParams} errors={(errors[idx] as IErrorObject) || emptyError} expression={e || {}} - fields={derivedIndexPattern.fields} > = (props) => { > {metadata ? ( ): void; - fields: DerivedIndexPattern['fields']; } const NonCollapsibleExpressionCss = css` @@ -450,11 +445,10 @@ const StyledHealthCss = css` export const ExpressionRow: FC> = (props) => { const [isExpanded, toggle] = useToggle(true); - const { children, setRuleParams, expression, errors, expressionId, remove, canDelete, fields } = - props; + const { children, setRuleParams, expression, errors, expressionId, remove, canDelete } = props; const { metric, - comparator = Comparator.GT, + comparator = COMPARATORS.GREATER_THAN, threshold = [], customMetric, warningThreshold = [], @@ -485,14 +479,14 @@ export const ExpressionRow: FC> = (props) const updateComparator = useCallback( (c?: string) => { - setRuleParams(expressionId, { ...expression, comparator: c as Comparator | undefined }); + setRuleParams(expressionId, { ...expression, comparator: c as COMPARATORS | undefined }); }, [expressionId, expression, setRuleParams] ); const updateWarningComparator = useCallback( (c?: string) => { - setRuleParams(expressionId, { ...expression, warningComparator: c as Comparator }); + setRuleParams(expressionId, { ...expression, warningComparator: c as COMPARATORS }); }, [expressionId, expression, setRuleParams] ); @@ -619,7 +613,6 @@ export const ExpressionRow: FC> = (props) onChangeCustom={updateCustomMetric} errors={errors} customMetric={customMetric} - fields={fields} /> {!displayWarningThreshold && criticalThresholdExpression} @@ -721,7 +714,7 @@ const ThresholdElement: React.FC<{ <>
= ({ stack={false} /> = ({ /> {expression.warningComparator && expression.warningThreshold && ( void; onChangeCustom: (customMetric?: SnapshotCustomMetricInput) => void; customMetric?: SnapshotCustomMetricInput; - fields: DerivedIndexPattern['fields']; popupPosition?: | 'upCenter' | 'upLeft' @@ -80,7 +79,6 @@ export const MetricExpression = ({ metric, metrics, customMetric, - fields, errors, onChange, onChangeCustom, @@ -90,6 +88,7 @@ export const MetricExpression = ({ const [customMetricTabOpen, setCustomMetricTabOpen] = useState(metric?.value === 'custom'); const [selectedOption, setSelectedOption] = useState(metric?.value); const [fieldDisplayedCustomLabel, setFieldDisplayedCustomLabel] = useState(customMetric?.label); + const { metricsView } = useMetricsDataViewContext(); // eslint-disable-next-line react-hooks/exhaustive-deps const firstFieldOption = { @@ -101,10 +100,10 @@ export const MetricExpression = ({ const fieldOptions = useMemo( () => - fields + (metricsView?.fields ?? []) .filter((f) => f.aggregatable && f.type === 'number' && !(customMetric?.field === f.name)) .map((f) => ({ label: f.name })), - [fields, customMetric?.field] + [metricsView?.fields, customMetric?.field] ); const expressionDisplayValue = useMemo(() => { diff --git a/x-pack/plugins/observability_solution/infra/public/alerting/inventory/components/validation.tsx b/x-pack/plugins/observability_solution/infra/public/alerting/inventory/components/validation.tsx index 5a3415be429cae..ab44ab589882ff 100644 --- a/x-pack/plugins/observability_solution/infra/public/alerting/inventory/components/validation.tsx +++ b/x-pack/plugins/observability_solution/infra/public/alerting/inventory/components/validation.tsx @@ -7,8 +7,8 @@ import { i18n } from '@kbn/i18n'; import type { ValidationResult } from '@kbn/triggers-actions-ui-plugin/public'; +import { COMPARATORS } from '@kbn/alerting-comparators'; import { - Comparator, FilterQuery, InventoryMetricConditions, QUERY_INVALID, @@ -91,7 +91,7 @@ export function validateMetricThreshold({ // The Threshold component returns an empty array with a length ([empty]) because it's using delete newThreshold[i]. // We need to use [...c.threshold] to convert it to an array with an undefined value ([undefined]) so we can test each element. const { comparator, threshold, type } = props as { - comparator?: Comparator; + comparator?: COMPARATORS; threshold?: number[]; type: 'critical' | 'warning'; }; @@ -108,7 +108,7 @@ export function validateMetricThreshold({ }); } - if (comparator === Comparator.BETWEEN && (!threshold || threshold.length < 2)) { + if (comparator === COMPARATORS.BETWEEN && (!threshold || threshold.length < 2)) { errors[id][type].threshold1.push( i18n.translate('xpack.infra.metrics.alertFlyout.error.thresholdRequired', { defaultMessage: 'Threshold is required.', diff --git a/x-pack/plugins/observability_solution/infra/public/alerting/metric_threshold/components/__snapshots__/alert_details_app_section.test.tsx.snap b/x-pack/plugins/observability_solution/infra/public/alerting/metric_threshold/components/__snapshots__/alert_details_app_section.test.tsx.snap index 0d634150819106..72381f6e62d636 100644 --- a/x-pack/plugins/observability_solution/infra/public/alerting/metric_threshold/components/__snapshots__/alert_details_app_section.test.tsx.snap +++ b/x-pack/plugins/observability_solution/infra/public/alerting/metric_threshold/components/__snapshots__/alert_details_app_section.test.tsx.snap @@ -17,10 +17,6 @@ Array [ />, ], "chartType": "line", - "derivedIndexPattern": Object { - "fields": Array [], - "title": "metricbeat-*", - }, "expression": Object { "aggType": "count", "comparator": ">", @@ -38,9 +34,6 @@ Array [ "host-1", ], "hideTitle": true, - "source": Object { - "id": "default", - }, "timeRange": Object { "from": "2023-03-28T10:43:13.802Z", "to": "2023-03-29T13:14:09.581Z", diff --git a/x-pack/plugins/observability_solution/infra/public/alerting/metric_threshold/components/alert_details_app_section.test.tsx b/x-pack/plugins/observability_solution/infra/public/alerting/metric_threshold/components/alert_details_app_section.test.tsx index a9d5b2c2193fc3..f7827fcbcadbbe 100644 --- a/x-pack/plugins/observability_solution/infra/public/alerting/metric_threshold/components/alert_details_app_section.test.tsx +++ b/x-pack/plugins/observability_solution/infra/public/alerting/metric_threshold/components/alert_details_app_section.test.tsx @@ -48,14 +48,6 @@ jest.mock('../../../hooks/use_kibana', () => ({ }), })); -jest.mock('../../../containers/metrics_source/source', () => ({ - withSourceProvider: () => jest.fn, - useSourceContext: () => ({ - source: { id: 'default' }, - createDerivedIndexPattern: () => ({ fields: [], title: 'metricbeat-*' }), - }), -})); - describe('AlertDetailsAppSection', () => { const queryClient = new QueryClient(); const mockedSetAlertSummaryFields = jest.fn(); diff --git a/x-pack/plugins/observability_solution/infra/public/alerting/metric_threshold/components/alert_details_app_section.tsx b/x-pack/plugins/observability_solution/infra/public/alerting/metric_threshold/components/alert_details_app_section.tsx index 27c61aa8c18ca2..feed66500a2a16 100644 --- a/x-pack/plugins/observability_solution/infra/public/alerting/metric_threshold/components/alert_details_app_section.tsx +++ b/x-pack/plugins/observability_solution/infra/public/alerting/metric_threshold/components/alert_details_app_section.tsx @@ -7,7 +7,7 @@ import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n-react'; -import React, { useEffect, useMemo } from 'react'; +import React, { useEffect } from 'react'; import moment from 'moment'; import { EuiFlexGroup, @@ -33,7 +33,7 @@ import { getPaddedAlertTimeRange } from '@kbn/observability-get-padded-alert-tim import { metricValueFormatter } from '../../../../common/alerting/metrics/metric_value_formatter'; import { TIME_LABELS } from '../../common/criterion_preview_chart/criterion_preview_chart'; import { Threshold } from '../../common/components/threshold'; -import { useSourceContext, withSourceProvider } from '../../../containers/metrics_source'; +import { withSourceProvider } from '../../../containers/metrics_source'; import { generateUniqueKey } from '../lib/generate_unique_key'; import { MetricsExplorerChartType } from '../../../pages/metrics/metrics_explorer/hooks/use_metrics_explorer_options'; import { useKibanaContextForPlugin } from '../../../hooks/use_kibana'; @@ -80,16 +80,11 @@ export function AlertDetailsAppSection({ setAlertSummaryFields, }: AppSectionProps) { const { uiSettings, charts } = useKibanaContextForPlugin().services; - const { source, createDerivedIndexPattern } = useSourceContext(); const { euiTheme } = useEuiTheme(); const groupInstance = alert.fields[ALERT_GROUP]?.map((group: Group) => group.value); const groups = alert.fields[ALERT_GROUP]; const tags = alert.fields[TAGS]; - const derivedIndexPattern = useMemo( - () => createDerivedIndexPattern(), - [createDerivedIndexPattern] - ); const chartProps = { baseTheme: charts.theme.useChartsBaseTheme(), }; @@ -188,13 +183,11 @@ export function AlertDetailsAppSection({ diff --git a/x-pack/plugins/observability_solution/infra/public/alerting/metric_threshold/components/custom_equation/custom_equation_editor.stories.tsx b/x-pack/plugins/observability_solution/infra/public/alerting/metric_threshold/components/custom_equation/custom_equation_editor.stories.tsx index d2fd0639859a68..4cb0cc5aabb03c 100644 --- a/x-pack/plugins/observability_solution/infra/public/alerting/metric_threshold/components/custom_equation/custom_equation_editor.stories.tsx +++ b/x-pack/plugins/observability_solution/infra/public/alerting/metric_threshold/components/custom_equation/custom_equation_editor.stories.tsx @@ -9,11 +9,8 @@ import { Meta, Story } from '@storybook/react/types-6-0'; import React, { useCallback, useEffect, useState } from 'react'; import { TimeUnitChar } from '@kbn/observability-plugin/common'; import { IErrorObject } from '@kbn/triggers-actions-ui-plugin/public'; -import { - Aggregators, - Comparator, - MetricExpressionParams, -} from '../../../../../common/alerting/metrics'; +import { COMPARATORS } from '@kbn/alerting-comparators'; +import { Aggregators, MetricExpressionParams } from '../../../../../common/alerting/metrics'; import { decorateWithGlobalStorybookThemeProviders } from '../../../../test_utils/use_global_storybook_theme'; import { CustomEquationEditor, CustomEquationEditorProps } from './custom_equation_editor'; import { aggregationType } from '../expression_row'; @@ -34,24 +31,6 @@ export default { }, } as Meta; -const fakeDataView = { - title: 'metricbeat-*', - fields: [ - { - name: 'system.cpu.user.pct', - type: 'number', - }, - { - name: 'system.cpu.system.pct', - type: 'number', - }, - { - name: 'system.cpu.cores', - type: 'number', - }, - ], -}; - const CustomEquationEditorTemplate: Story = (args) => { const [expression, setExpression] = useState(args.expression); const [errors, setErrors] = useState(args.errors); @@ -78,7 +57,6 @@ const CustomEquationEditorTemplate: Story = (args) => errors={errors} expression={expression} onChange={handleExpressionChange} - dataView={fakeDataView} /> ); }; @@ -93,7 +71,7 @@ const BASE_ARGS = { timeSize: 1, timeUnit: 'm' as TimeUnitChar, threshold: [1], - comparator: Comparator.GT, + comparator: COMPARATORS.GREATER_THAN, }, fields: [ { name: 'system.cpu.user.pct', normalizedType: 'number' }, diff --git a/x-pack/plugins/observability_solution/infra/public/alerting/metric_threshold/components/custom_equation/custom_equation_editor.tsx b/x-pack/plugins/observability_solution/infra/public/alerting/metric_threshold/components/custom_equation/custom_equation_editor.tsx index 3b00563fcc0f4b..c87ec88fa3ef91 100644 --- a/x-pack/plugins/observability_solution/infra/public/alerting/metric_threshold/components/custom_equation/custom_equation_editor.tsx +++ b/x-pack/plugins/observability_solution/infra/public/alerting/metric_threshold/components/custom_equation/custom_equation_editor.tsx @@ -13,6 +13,7 @@ import { EuiSpacer, } from '@elastic/eui'; import React, { useState, useCallback, useMemo } from 'react'; +import { i18n } from '@kbn/i18n'; import { omit, range, first, xor, debounce } from 'lodash'; import { IErrorObject } from '@kbn/triggers-actions-ui-plugin/public'; import { FormattedMessage } from '@kbn/i18n-react'; @@ -40,7 +41,7 @@ export interface CustomEquationEditorProps { fields: NormalizedFields; aggregationTypes: AggregationTypes; errors: IErrorObject; - dataView: DataViewBase; + dataView?: DataViewBase; } const NEW_METRIC = { name: 'A', aggType: Aggregators.AVERAGE as CustomMetricAggTypes }; @@ -55,7 +56,6 @@ export const CustomEquationEditor = ({ fields, aggregationTypes, errors, - dataView, }: CustomEquationEditorProps) => { const [customMetrics, setCustomMetrics] = useState( expression?.customMetrics ?? [NEW_METRIC] @@ -133,7 +133,6 @@ export const CustomEquationEditor = ({ disableDelete={disableDelete} onChange={handleChange} errors={errors} - dataView={dataView} /> ); } @@ -183,7 +182,9 @@ export const CustomEquationEditor = ({ { const aggOptions = useMemo( () => @@ -95,7 +92,6 @@ export const MetricRowWithCount = ({ ({ +const mockDataView = { + id: 'mock-id', + title: 'mock-title', + timeFieldName: TIMESTAMP_FIELD, + isPersisted: () => false, + getName: () => 'mock-data-view', + toSpec: () => ({}), +} as jest.Mocked; + +jest.mock('../../../containers/metrics_source', () => ({ withSourceProvider: () => jest.fn, useSourceContext: () => ({ source: { id: 'default' }, - createDerivedIndexPattern: () => ({ fields: [], title: 'metricbeat-*' }), + }), + useMetricsDataViewContext: () => ({ + metricsView: { + indices: 'metricbeat-*', + timeFieldName: mockDataView.timeFieldName, + fields: mockDataView.fields, + dataViewReference: mockDataView, + } as ResolvedDataView, + loading: false, + error: undefined, }), })); @@ -29,8 +49,6 @@ jest.mock('../../../hooks/use_kibana', () => ({ }), })); -const dataViewMock = dataViewPluginMocks.createStartContract(); - describe('Expression', () => { async function setup(currentOptions: { metrics?: MetricsExplorerMetric[]; @@ -55,7 +73,6 @@ describe('Expression', () => { metadata={{ currentOptions, }} - dataViews={dataViewMock} /> ); @@ -85,7 +102,7 @@ describe('Expression', () => { expect(ruleParams.criteria).toEqual([ { metric: 'system.load.1', - comparator: Comparator.GT, + comparator: COMPARATORS.GREATER_THAN, threshold: [], timeSize: 1, timeUnit: 'm', @@ -93,7 +110,7 @@ describe('Expression', () => { }, { metric: 'system.cpu.user.pct', - comparator: Comparator.GT, + comparator: COMPARATORS.GREATER_THAN, threshold: [], timeSize: 1, timeUnit: 'm', diff --git a/x-pack/plugins/observability_solution/infra/public/alerting/metric_threshold/components/expression.tsx b/x-pack/plugins/observability_solution/infra/public/alerting/metric_threshold/components/expression.tsx index 814a968d9718ea..c6afc61ebf8816 100644 --- a/x-pack/plugins/observability_solution/infra/public/alerting/metric_threshold/components/expression.tsx +++ b/x-pack/plugins/observability_solution/infra/public/alerting/metric_threshold/components/expression.tsx @@ -28,8 +28,13 @@ import { RuleTypeParamsExpressionProps, } from '@kbn/triggers-actions-ui-plugin/public'; import { TimeUnitChar } from '@kbn/observability-plugin/common/utils/formatters/duration'; -import { useSourceContext, withSourceProvider } from '../../../containers/metrics_source'; -import { Aggregators, Comparator, QUERY_INVALID } from '../../../../common/alerting/metrics'; +import { COMPARATORS } from '@kbn/alerting-comparators'; +import { Aggregators, QUERY_INVALID } from '../../../../common/alerting/metrics'; +import { + useMetricsDataViewContext, + useSourceContext, + withSourceProvider, +} from '../../../containers/metrics_source'; import { useKibanaContextForPlugin } from '../../../hooks/use_kibana'; import { MetricsExplorerGroupBy } from '../../../pages/metrics/metrics_explorer/components/group_by'; import { MetricsExplorerKueryBar } from '../../../pages/metrics/metrics_explorer/components/kuery_bar'; @@ -42,12 +47,18 @@ const FILTER_TYPING_DEBOUNCE_MS = 500; type Props = Omit< RuleTypeParamsExpressionProps, - 'defaultActionGroupId' | 'actionGroups' | 'charts' | 'data' | 'unifiedSearch' | 'onChangeMetaData' + | 'defaultActionGroupId' + | 'actionGroups' + | 'charts' + | 'data' + | 'unifiedSearch' + | 'onChangeMetaData' + | 'dataViews' >; const defaultExpression = { aggType: Aggregators.AVERAGE, - comparator: Comparator.GT, + comparator: COMPARATORS.GREATER_THAN, threshold: [], timeSize: 1, timeUnit: 'm', @@ -57,14 +68,11 @@ export { defaultExpression }; export const Expressions: React.FC = (props) => { const { setRuleParams, ruleParams, errors, metadata } = props; const { docLinks } = useKibanaContextForPlugin().services; - const { source, createDerivedIndexPattern } = useSourceContext(); + const { source } = useSourceContext(); + const { metricsView } = useMetricsDataViewContext(); const [timeSize, setTimeSize] = useState(1); const [timeUnit, setTimeUnit] = useState('m'); - const derivedIndexPattern = useMemo( - () => createDerivedIndexPattern(), - [createDerivedIndexPattern] - ); const options = useMemo(() => { if (metadata?.currentOptions?.metrics) { @@ -113,13 +121,13 @@ export const Expressions: React.FC = (props) => { try { setRuleParams( 'filterQuery', - convertKueryToElasticSearchQuery(filter, derivedIndexPattern, false) || '' + convertKueryToElasticSearchQuery(filter, metricsView?.dataViewReference, false) || '' ); } catch (e) { setRuleParams('filterQuery', QUERY_INVALID); } }, - [setRuleParams, derivedIndexPattern] + [setRuleParams, metricsView?.dataViewReference] ); /* eslint-disable-next-line react-hooks/exhaustive-deps */ @@ -175,7 +183,7 @@ export const Expressions: React.FC = (props) => { 'criteria', md.currentOptions.metrics.map((metric) => ({ metric: metric.field, - comparator: Comparator.GT, + comparator: COMPARATORS.GREATER_THAN, threshold: [], timeSize, timeUnit, @@ -193,7 +201,10 @@ export const Expressions: React.FC = (props) => { setRuleParams('filterQueryText', md.currentOptions.filterQuery); setRuleParams( 'filterQuery', - convertKueryToElasticSearchQuery(md.currentOptions.filterQuery, derivedIndexPattern) || '' + convertKueryToElasticSearchQuery( + md.currentOptions.filterQuery, + metricsView?.dataViewReference + ) || '' ); } else if (md && md.currentOptions?.groupBy && md.series) { const { groupBy } = md.currentOptions; @@ -203,10 +214,10 @@ export const Expressions: React.FC = (props) => { setRuleParams('filterQueryText', filter); setRuleParams( 'filterQuery', - convertKueryToElasticSearchQuery(filter, derivedIndexPattern) || '' + convertKueryToElasticSearchQuery(filter, metricsView?.dataViewReference) || '' ); } - }, [metadata, derivedIndexPattern, setRuleParams]); + }, [metadata, metricsView?.dataViewReference, setRuleParams]); const preFillAlertGroupBy = useCallback(() => { const md = metadata; @@ -299,7 +310,6 @@ export const Expressions: React.FC = (props) => { return ( 1) || false} - fields={derivedIndexPattern.fields} remove={removeExpression} addExpression={addExpression} key={idx} // idx's don't usually make good key's but here the index has semantic meaning @@ -307,12 +317,9 @@ export const Expressions: React.FC = (props) => { setRuleParams={updateParams} errors={(errors[idx] as IErrorObject) || emptyError} expression={e || {}} - dataView={derivedIndexPattern} > @@ -395,7 +402,6 @@ export const Expressions: React.FC = (props) => { > {(metadata && ( = (props) => { > false, + getName: () => 'mock-data-view', + toSpec: () => ({}), +} as jest.Mocked; const mockStartServices = mockCoreMock.createStart(); jest.mock('../../../hooks/use_kibana', () => ({ @@ -32,6 +43,23 @@ jest.mock('../../../hooks/use_kibana', () => ({ }), })); +jest.mock('../../../containers/metrics_source', () => ({ + withSourceProvider: () => jest.fn, + useSourceContext: () => ({ + source: { id: 'default' }, + }), + useMetricsDataViewContext: () => ({ + metricsView: { + indices: 'metricbeat-*', + timeFieldName: mockDataView.timeFieldName, + fields: mockDataView.fields, + dataViewReference: mockDataView, + } as ResolvedDataView, + loading: false, + error: undefined, + }), +})); + const mockResponse = { pageInfo: { afterKey: null, @@ -51,29 +79,9 @@ describe('ExpressionChart', () => { groupBy?: string, annotations?: Array> ) { - const derivedIndexPattern: DataViewBase = { - title: 'metricbeat-*', - fields: [], - }; - - const source: MetricsSourceConfiguration = { - id: 'default', - origin: 'fallback', - configuration: { - name: 'default', - description: 'The default configuration', - metricAlias: 'metricbeat-*', - inventoryDefaultView: 'host', - metricsExplorerDefaultView: 'host', - anomalyThreshold: 20, - }, - }; - const wrapper = mountWithIntl( { timeUnit: 'm', sourceId: 'default', threshold: [1], - comparator: Comparator.GT_OR_EQ, + comparator: COMPARATORS.GREATER_THAN_OR_EQUALS, }; const { wrapper } = await setup(expression); expect(wrapper.find('[data-test-subj~="noChartData"]').exists()).toBeTruthy(); diff --git a/x-pack/plugins/observability_solution/infra/public/alerting/metric_threshold/components/expression_chart.tsx b/x-pack/plugins/observability_solution/infra/public/alerting/metric_threshold/components/expression_chart.tsx index cdd8c8389427a2..42baee346c75b8 100644 --- a/x-pack/plugins/observability_solution/infra/public/alerting/metric_threshold/components/expression_chart.tsx +++ b/x-pack/plugins/observability_solution/infra/public/alerting/metric_threshold/components/expression_chart.tsx @@ -19,12 +19,11 @@ import { import { EuiText } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n-react'; import { useActiveCursor } from '@kbn/charts-plugin/public'; -import { DataViewBase } from '@kbn/es-query'; import { first, last } from 'lodash'; import { i18n } from '@kbn/i18n'; +import { convertToBuiltInComparators } from '@kbn/observability-plugin/common'; import { useTimelineChartTheme } from '../../../utils/use_timeline_chart_theme'; -import { MetricsSourceConfiguration } from '../../../../common/metrics_sources'; import { Color } from '../../../../common/color_palette'; import { MetricsExplorerRow, MetricsExplorerAggregation } from '../../../../common/http_api'; import { MetricExplorerSeriesChart } from '../../../pages/metrics/metrics_explorer/components/series_chart'; @@ -50,41 +49,35 @@ import { CUSTOM_EQUATION } from '../i18n_strings'; interface Props { expression: MetricExpression; - derivedIndexPattern: DataViewBase; annotations?: Array>; chartType?: MetricsExplorerChartType; filterQuery?: string; groupBy?: string | string[]; groupInstance?: string | string[]; hideTitle?: boolean; - source?: MetricsSourceConfiguration; timeRange?: TimeRange; } export const ExpressionChart: React.FC = ({ expression, - derivedIndexPattern, annotations, chartType = MetricsExplorerChartType.bar, filterQuery, groupBy, groupInstance, hideTitle = false, - source, timeRange, }) => { const { charts } = useKibanaContextForPlugin().services; const chartTheme = useTimelineChartTheme(); - const { isLoading, data } = useMetricsExplorerChartData( + const { isLoading, data } = useMetricsExplorerChartData({ expression, - derivedIndexPattern, - source, filterQuery, groupBy, groupInstance, - timeRange - ); + timeRange, + }); const chartRef = useRef(null); const handleCursorUpdate = useActiveCursor(charts.activeCursor, chartRef, { @@ -163,7 +156,7 @@ export const ExpressionChart: React.FC = ({ stack={false} /> = ({ /> {expression.warningComparator && expression.warningThreshold && ( ({ +const mockDataView = { + id: 'mock-id', + title: 'mock-title', + timeFieldName: TIMESTAMP_FIELD, + fields: [ + { + name: 'system.cpu.user.pct', + type: 'test', + searchable: true, + aggregatable: true, + }, + { + name: 'system.load.1', + type: 'test', + searchable: true, + aggregatable: true, + }, + ] as Partial, + isPersisted: () => false, + getName: () => 'mock-data-view', + toSpec: () => ({}), +} as jest.Mocked; + +jest.mock('../../../containers/metrics_source', () => ({ withSourceProvider: () => jest.fn, useSourceContext: () => ({ source: { id: 'default' }, - createDerivedIndexPattern: () => ({ fields: [], title: 'metricbeat-*' }), + }), + useMetricsDataViewContext: () => ({ + metricsView: { + indices: 'metricbeat-*', + timeFieldName: mockDataView.timeFieldName, + fields: mockDataView.fields, + dataViewReference: mockDataView, + } as ResolvedDataView, + loading: false, + error: undefined, }), })); @@ -25,22 +60,6 @@ describe('ExpressionRow', () => { const wrapper = mountWithIntl( {}} addExpression={() => {}} key={1} @@ -52,7 +71,6 @@ describe('ExpressionRow', () => { timeWindowSize: [], }} expression={expression} - dataView={{ fields: [], title: 'metricbeat-*' }} /> ); @@ -70,7 +88,7 @@ describe('ExpressionRow', () => { it('should display thresholds as a percentage for pct metrics', async () => { const expression = { metric: 'system.cpu.user.pct', - comparator: Comparator.GT, + comparator: COMPARATORS.GREATER_THAN, threshold: [0.5], timeSize: 1, timeUnit: 'm', @@ -89,7 +107,7 @@ describe('ExpressionRow', () => { it('should display thresholds as a decimal for all other metrics', async () => { const expression = { metric: 'system.load.1', - comparator: Comparator.GT, + comparator: COMPARATORS.GREATER_THAN, threshold: [0.5], timeSize: 1, timeUnit: 'm', @@ -107,7 +125,7 @@ describe('ExpressionRow', () => { it('should render a helpText for the of expression', async () => { const expression = { metric: 'system.load.1', - comparator: Comparator.GT, + comparator: COMPARATORS.GREATER_THAN, threshold: [0.5], timeSize: 1, timeUnit: 'm', diff --git a/x-pack/plugins/observability_solution/infra/public/alerting/metric_threshold/components/expression_row.tsx b/x-pack/plugins/observability_solution/infra/public/alerting/metric_threshold/components/expression_row.tsx index 7804a9f15ab263..cca94d10e9539e 100644 --- a/x-pack/plugins/observability_solution/infra/public/alerting/metric_threshold/components/expression_row.tsx +++ b/x-pack/plugins/observability_solution/infra/public/alerting/metric_threshold/components/expression_row.tsx @@ -17,38 +17,26 @@ import { import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n-react'; import { omit } from 'lodash'; -import React, { useCallback, useMemo, useState, FC, PropsWithChildren } from 'react'; +import React, { PropsWithChildren, useCallback, useMemo, useState } from 'react'; import { euiStyled } from '@kbn/kibana-react-plugin/common'; import { AggregationType, - builtInComparators, IErrorObject, OfExpression, ThresholdExpression, WhenExpression, } from '@kbn/triggers-actions-ui-plugin/public'; -import { DataViewBase } from '@kbn/es-query'; import useToggle from 'react-use/lib/useToggle'; -import { Aggregators, Comparator } from '../../../../common/alerting/metrics'; +import { COMPARATORS } from '@kbn/alerting-comparators'; +import { convertToBuiltInComparators } from '@kbn/observability-plugin/common'; +import { Aggregators } from '../../../../common/alerting/metrics'; +import { useMetricsDataViewContext } from '../../../containers/metrics_source'; import { decimalToPct, pctToDecimal } from '../../../../common/utils/corrected_percent_convert'; -import { DerivedIndexPattern } from '../../../containers/metrics_source'; import { AGGREGATION_TYPES, MetricExpression } from '../types'; import { CustomEquationEditor } from './custom_equation'; import { CUSTOM_EQUATION } from '../i18n_strings'; -const customComparators = { - ...builtInComparators, - [Comparator.OUTSIDE_RANGE]: { - text: i18n.translate('xpack.infra.metrics.alertFlyout.outsideRangeLabel', { - defaultMessage: 'Is not between', - }), - value: Comparator.OUTSIDE_RANGE, - requiredValues: 2, - }, -}; - interface ExpressionRowProps { - fields: DerivedIndexPattern['fields']; expressionId: number; expression: MetricExpression; errors: IErrorObject; @@ -56,7 +44,6 @@ interface ExpressionRowProps { addExpression(): void; remove(id: number): void; setRuleParams(id: number, params: MetricExpression): void; - dataView: DataViewBase; } const NegativeHorizontalMarginDiv = euiStyled.div`margin: 0 -4px;`; @@ -69,25 +56,22 @@ const StyledHealth = euiStyled(EuiHealth)` margin-left: 4px; `; -export const ExpressionRow: FC> = (props) => { +export const ExpressionRow = ({ + children, + setRuleParams, + expression, + errors, + expressionId, + remove, + canDelete, +}: PropsWithChildren) => { const [isExpanded, toggle] = useToggle(true); - - const { - dataView, - children, - setRuleParams, - expression, - errors, - expressionId, - remove, - fields, - canDelete, - } = props; + const { metricsView } = useMetricsDataViewContext(); const { aggType = AGGREGATION_TYPES.MAX, metric, - comparator = Comparator.GT, + comparator = COMPARATORS.GREATER_THAN, threshold = [], warningThreshold = [], warningComparator, @@ -121,14 +105,14 @@ export const ExpressionRow: FC> = (props) const updateComparator = useCallback( (c?: string) => { - setRuleParams(expressionId, { ...expression, comparator: c as Comparator }); + setRuleParams(expressionId, { ...expression, comparator: c as COMPARATORS }); }, [expressionId, expression, setRuleParams] ); const updateWarningComparator = useCallback( (c?: string) => { - setRuleParams(expressionId, { ...expression, warningComparator: c as Comparator }); + setRuleParams(expressionId, { ...expression, warningComparator: c as COMPARATORS }); }, [expressionId, expression, setRuleParams] ); @@ -209,7 +193,7 @@ export const ExpressionRow: FC> = (props) /> ); - const normalizedFields = fields.map((f) => ({ + const normalizedFields = (metricsView?.fields ?? []).map((f) => ({ normalizedType: f.type, name: f.name, })); @@ -341,7 +325,6 @@ export const ExpressionRow: FC> = (props) aggregationTypes={aggregationType} onChange={handleCustomMetricChange} errors={errors} - dataView={dataView} /> @@ -380,14 +363,18 @@ const ThresholdElement: React.FC<{ if (isMetricPct) return threshold.map((v) => decimalToPct(v)); return threshold; }, [threshold, isMetricPct]); - + const thresholdComparator = useCallback(() => { + if (!comparator) return COMPARATORS.GREATER_THAN; + // Check if the rule had the legacy OUTSIDE_RANGE inside its params. + // Then, change it on-the-fly to NOT_BETWEEN + return convertToBuiltInComparators(comparator); + }, [comparator]); return ( <> |<|=|\?|\:|&|\!|\|]+/g; const isCustomMetricExpressionParams = ( @@ -118,7 +117,7 @@ export function validateMetricThreshold({ // The Threshold component returns an empty array with a length ([empty]) because it's using delete newThreshold[i]. // We need to use [...c.threshold] to convert it to an array with an undefined value ([undefined]) so we can test each element. const { comparator, threshold, type } = props as { - comparator?: Comparator; + comparator?: COMPARATORS; threshold?: number[]; type: 'critical' | 'warning'; }; @@ -135,7 +134,7 @@ export function validateMetricThreshold({ }); } - if (comparator === Comparator.BETWEEN && (!threshold || threshold.length < 2)) { + if (comparator === COMPARATORS.BETWEEN && (!threshold || threshold.length < 2)) { errors[id][type].threshold1.push( i18n.translate('xpack.infra.metrics.alertFlyout.error.thresholdRequired', { defaultMessage: 'Threshold is required.', diff --git a/x-pack/plugins/observability_solution/infra/public/alerting/metric_threshold/hooks/use_metrics_explorer_chart_data.ts b/x-pack/plugins/observability_solution/infra/public/alerting/metric_threshold/hooks/use_metrics_explorer_chart_data.ts index 79e2e4aec33da4..73bafecee10007 100644 --- a/x-pack/plugins/observability_solution/infra/public/alerting/metric_threshold/hooks/use_metrics_explorer_chart_data.ts +++ b/x-pack/plugins/observability_solution/infra/public/alerting/metric_threshold/hooks/use_metrics_explorer_chart_data.ts @@ -6,10 +6,8 @@ */ import DateMath from '@kbn/datemath'; -import { DataViewBase } from '@kbn/es-query'; import { useMemo } from 'react'; import { MetricExpressionCustomMetric } from '../../../../common/alerting/metrics'; -import { MetricsSourceConfiguration } from '../../../../common/metrics_sources'; import { MetricExpression, TimeRange } from '../types'; import { MetricsExplorerOptions, @@ -20,15 +18,19 @@ import { MetricExplorerCustomMetricAggregations } from '../../../../common/http_ const DEFAULT_TIME_RANGE = {}; -export const useMetricsExplorerChartData = ( - expression: MetricExpression, - derivedIndexPattern: DataViewBase, - source?: MetricsSourceConfiguration, - filterQuery?: string, - groupBy?: string | string[], - groupInstance?: string | string[], - timeRange: TimeRange = DEFAULT_TIME_RANGE -) => { +export const useMetricsExplorerChartData = ({ + expression, + filterQuery, + groupBy, + groupInstance, + timeRange = DEFAULT_TIME_RANGE, +}: { + expression: MetricExpression; + filterQuery?: string; + groupBy?: string | string[]; + groupInstance?: string | string[]; + timeRange?: TimeRange; +}) => { const { timeSize, timeUnit } = expression || { timeSize: 1, timeUnit: 'm' }; const options: MetricsExplorerOptions = useMemo( @@ -74,7 +76,7 @@ export const useMetricsExplorerChartData = ( }; }, [timeRange, timeSize, timeUnit]); - return useMetricsExplorerData(options, source?.configuration, derivedIndexPattern, timestamps); + return useMetricsExplorerData({ options, timestamps }); }; const mapMetricThresholdMetricToMetricsExplorerMetric = (metric: MetricExpressionCustomMetric) => { diff --git a/x-pack/plugins/observability_solution/infra/public/alerting/metric_threshold/lib/generate_unique_key.test.ts b/x-pack/plugins/observability_solution/infra/public/alerting/metric_threshold/lib/generate_unique_key.test.ts index a9460e700f38b6..a47bc517676a9b 100644 --- a/x-pack/plugins/observability_solution/infra/public/alerting/metric_threshold/lib/generate_unique_key.test.ts +++ b/x-pack/plugins/observability_solution/infra/public/alerting/metric_threshold/lib/generate_unique_key.test.ts @@ -4,8 +4,8 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ - -import { Aggregators, Comparator } from '../../../../common/alerting/metrics'; +import { COMPARATORS } from '@kbn/alerting-comparators'; +import { Aggregators } from '../../../../common/alerting/metrics'; import { MetricExpression } from '../types'; import { generateUniqueKey } from './generate_unique_key'; @@ -14,7 +14,7 @@ describe('generateUniqueKey', () => { [ { aggType: Aggregators.COUNT, - comparator: Comparator.LT, + comparator: COMPARATORS.LESS_THAN, threshold: [2000, 5000], timeSize: 15, timeUnit: 'm', @@ -24,7 +24,7 @@ describe('generateUniqueKey', () => { [ { aggType: Aggregators.CUSTOM, - comparator: Comparator.GT_OR_EQ, + comparator: COMPARATORS.GREATER_THAN_OR_EQUALS, threshold: [30], timeSize: 15, timeUnit: 'm', @@ -34,7 +34,7 @@ describe('generateUniqueKey', () => { [ { aggType: Aggregators.AVERAGE, - comparator: Comparator.LT_OR_EQ, + comparator: COMPARATORS.LESS_THAN_OR_EQUALS, threshold: [500], timeSize: 15, timeUnit: 'm', diff --git a/x-pack/plugins/observability_solution/infra/public/alerting/metric_threshold/mocks/metric_threshold_rule.ts b/x-pack/plugins/observability_solution/infra/public/alerting/metric_threshold/mocks/metric_threshold_rule.ts index ed0d4444f023a2..0ef6478ff12d7a 100644 --- a/x-pack/plugins/observability_solution/infra/public/alerting/metric_threshold/mocks/metric_threshold_rule.ts +++ b/x-pack/plugins/observability_solution/infra/public/alerting/metric_threshold/mocks/metric_threshold_rule.ts @@ -6,7 +6,8 @@ */ import { v4 as uuidv4 } from 'uuid'; -import { Aggregators, Comparator } from '../../../../common/alerting/metrics'; +import { COMPARATORS } from '@kbn/alerting-comparators'; +import { Aggregators } from '../../../../common/alerting/metrics'; import { MetricThresholdAlert, MetricThresholdRule } from '../components/alert_details_app_section'; export const buildMetricThresholdRule = ( @@ -59,24 +60,24 @@ export const buildMetricThresholdRule = ( criteria: [ { aggType: Aggregators.COUNT, - comparator: Comparator.GT, + comparator: COMPARATORS.GREATER_THAN, threshold: [2000], timeSize: 15, timeUnit: 'm', }, { aggType: Aggregators.MAX, - comparator: Comparator.GT, + comparator: COMPARATORS.GREATER_THAN, threshold: [4], timeSize: 15, timeUnit: 'm', metric: 'system.cpu.user.pct', - warningComparator: Comparator.GT, + warningComparator: COMPARATORS.GREATER_THAN, warningThreshold: [2.2], }, { aggType: Aggregators.MIN, - comparator: Comparator.GT, + comparator: COMPARATORS.GREATER_THAN, threshold: [0.8], timeSize: 15, timeUnit: 'm', @@ -131,7 +132,7 @@ export const buildMetricThresholdAlert = ( criteria: [ { aggType: Aggregators.AVERAGE, - comparator: Comparator.GT, + comparator: COMPARATORS.GREATER_THAN, threshold: [2000], timeSize: 15, timeUnit: 'm', @@ -139,12 +140,12 @@ export const buildMetricThresholdAlert = ( }, { aggType: Aggregators.MAX, - comparator: Comparator.GT, + comparator: COMPARATORS.GREATER_THAN, threshold: [4], timeSize: 15, timeUnit: 'm', metric: 'system.cpu.user.pct', - warningComparator: Comparator.GT, + warningComparator: COMPARATORS.GREATER_THAN, warningThreshold: [2.2], }, ], diff --git a/x-pack/plugins/observability_solution/infra/public/apps/metrics_app.tsx b/x-pack/plugins/observability_solution/infra/public/apps/metrics_app.tsx index 519befb57f8c0b..ab6ffcb6b52991 100644 --- a/x-pack/plugins/observability_solution/infra/public/apps/metrics_app.tsx +++ b/x-pack/plugins/observability_solution/infra/public/apps/metrics_app.tsx @@ -18,7 +18,7 @@ import { InfrastructurePage } from '../pages/metrics'; import { InfraClientStartDeps, InfraClientStartExports } from '../types'; import { CommonInfraProviders, CoreProviders } from './common_providers'; import { prepareMountElement } from './common_styles'; -import { SourceProvider } from '../containers/metrics_source'; +import { SourceProvider, MetricsDataViewProvider } from '../containers/metrics_source'; import { PluginConfigProvider } from '../containers/plugin_config_context'; import type { KibanaEnvContext } from '../hooks/use_kibana'; @@ -97,18 +97,20 @@ const MetricsApp: React.FC<{ triggersActionsUI={plugins.triggersActionsUi} > - - - - - - {uiCapabilities?.infrastructure?.show && ( - - )} - - - - + + + + + + + {uiCapabilities?.infrastructure?.show && ( + + )} + + + + + diff --git a/x-pack/plugins/observability_solution/infra/public/components/asset_details/__stories__/context/fixtures/asset_details_props.ts b/x-pack/plugins/observability_solution/infra/public/components/asset_details/__stories__/context/fixtures/asset_details_props.ts index 7e76b3fa1474b4..ab3318c7cebc7e 100644 --- a/x-pack/plugins/observability_solution/infra/public/components/asset_details/__stories__/context/fixtures/asset_details_props.ts +++ b/x-pack/plugins/observability_solution/infra/public/components/asset_details/__stories__/context/fixtures/asset_details_props.ts @@ -66,5 +66,4 @@ export const assetDetailsProps: AssetDetailsProps = { }, tabs, links, - metricAlias: 'metrics-*', }; diff --git a/x-pack/plugins/observability_solution/infra/public/components/asset_details/__stories__/decorator.tsx b/x-pack/plugins/observability_solution/infra/public/components/asset_details/__stories__/decorator.tsx index 7a7f93a7c57f50..1c09b8dabc58af 100644 --- a/x-pack/plugins/observability_solution/infra/public/components/asset_details/__stories__/decorator.tsx +++ b/x-pack/plugins/observability_solution/infra/public/components/asset_details/__stories__/decorator.tsx @@ -5,7 +5,7 @@ * 2.0. */ -import React, { JSXElementConstructor, ReactElement } from 'react'; +import React from 'react'; import { I18nProvider } from '@kbn/i18n-react'; import { KibanaContextProvider, @@ -19,15 +19,14 @@ import type { DeepPartial } from 'utility-types'; import type { LocatorPublic } from '@kbn/share-plugin/public'; import type { IKibanaSearchRequest, ISearchOptions } from '@kbn/search-types'; import type { SearchSessionState } from '@kbn/data-plugin/public'; -import { AlertSummaryWidget } from '@kbn/triggers-actions-ui-plugin/public/application/sections/alert_summary_widget/alert_summary_widget'; import type { Theme } from '@elastic/charts/dist/utils/themes/theme'; -import type { AlertSummaryWidgetProps } from '@kbn/triggers-actions-ui-plugin/public/application/sections/alert_summary_widget'; import { defaultLogViewAttributes } from '@kbn/logs-shared-plugin/common'; import { DataView, DataViewField } from '@kbn/data-views-plugin/common'; import { MemoryRouter } from 'react-router-dom'; +import { AlertPrefillProvider } from '../../../alerting/use_alert_prefill'; import { PluginConfigProvider } from '../../../containers/plugin_config_context'; import type { PluginKibanaContextValue } from '../../../hooks/use_kibana'; -import { SourceProvider } from '../../../containers/metrics_source'; +import { MetricsDataViewProvider, SourceProvider } from '../../../containers/metrics_source'; import { getHttp } from './context/http'; import { assetDetailsProps, getLogEntries } from './context/fixtures'; import { ContextProviders } from '../context_providers'; @@ -36,6 +35,7 @@ import type { InfraConfig } from '../../../../server'; const settings: Record = { 'dateFormat:scaled': [['', 'HH:mm:ss.SSS']], + 'timepicker:timeDefaults': ['now-15m', 'now'], }; const getSettings = (key: string): any => settings[key]; @@ -57,6 +57,16 @@ export const DecorateWithKibanaContext: DecoratorFn = (story) => { }, getUrlForApp: (url: string) => url, }, + chrome: { + docTitle: { + change(newTitle) { + action('chrome.docTitle.change')(newTitle); + return newTitle; + }, + }, + setBreadcrumbs: () => {}, + setBreadcrumbsAppendExtension: () => {}, + }, data: { search: { search: (request: IKibanaSearchRequest, options?: ISearchOptions) => { @@ -93,9 +103,8 @@ export const DecorateWithKibanaContext: DecoratorFn = (story) => { get: () => ({ key: 'mock', defaultOverride: undefined } as any), }, triggersActionsUi: { - getAlertSummaryWidget: AlertSummaryWidget as ( - props: AlertSummaryWidgetProps - ) => ReactElement>, + getAlertSummaryWidget: () => <>, + getAlertsStateTable: () => <>, }, charts: { theme: { @@ -125,6 +134,9 @@ export const DecorateWithKibanaContext: DecoratorFn = (story) => { navigate: async () => { return Promise.resolve(); }, + getRedirectUrl: (args: any) => { + action('share.url.locators.getRedirectUrl')(args); + }, } as unknown as LocatorPublic), }, }, @@ -154,6 +166,9 @@ export const DecorateWithKibanaContext: DecoratorFn = (story) => { reportAssetDetailsPageViewed: () => {}, reportAssetDashboardLoaded: () => {}, }, + observabilityShared: { + navigation: { PageTemplate: ({ children }: { children?: any }) => <>{children} }, + }, }; const config: InfraConfig = { @@ -195,7 +210,11 @@ export const DecorateWithKibanaContext: DecoratorFn = (story) => { - {story()} + + + {story()} + + @@ -212,7 +231,7 @@ export const DecorateWithAssetDetailsStateContext: DecoratorFn = (story) => { to: '2023-04-09T11:23:49Z', }} > - {story()} + {story()} ); }; diff --git a/x-pack/plugins/observability_solution/infra/public/components/asset_details/asset_details.tsx b/x-pack/plugins/observability_solution/infra/public/components/asset_details/asset_details.tsx index 4c7613e843c399..49a6e1a17ac6c7 100644 --- a/x-pack/plugins/observability_solution/infra/public/components/asset_details/asset_details.tsx +++ b/x-pack/plugins/observability_solution/infra/public/components/asset_details/asset_details.tsx @@ -25,17 +25,11 @@ const ContentTemplate = ({ ); }; -export const AssetDetails = ({ - tabs, - links, - renderMode, - metricAlias, - ...props -}: AssetDetailsProps) => { +export const AssetDetails = ({ tabs, links, renderMode, ...props }: AssetDetailsProps) => { return ( - + diff --git a/x-pack/plugins/observability_solution/infra/public/components/asset_details/charts/chart.tsx b/x-pack/plugins/observability_solution/infra/public/components/asset_details/charts/chart.tsx index f1ccf0ef31b2b9..e356eb009a1c48 100644 --- a/x-pack/plugins/observability_solution/infra/public/components/asset_details/charts/chart.tsx +++ b/x-pack/plugins/observability_solution/infra/public/components/asset_details/charts/chart.tsx @@ -4,11 +4,12 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ -import React, { useCallback, useMemo } from 'react'; - +import React, { useCallback } from 'react'; import type { LensConfig, LensDataviewDataset } from '@kbn/lens-embeddable-utils/config_builder'; import type { TimeRange } from '@kbn/es-query'; -import { useDataView } from '../../../hooks/use_data_view'; +import useAsync from 'react-use/lib/useAsync'; +import { resolveDataView } from '../../../utils/data_view'; +import { useKibanaContextForPlugin } from '../../../hooks/use_kibana'; import { METRIC_CHART_HEIGHT } from '../../../common/visualizations/constants'; import { buildCombinedAssetFilter } from '../../../utils/filters/build'; import { type BrushEndArgs, LensChart, type OnFilterEvent, LensChartProps } from '../../lens'; @@ -27,17 +28,24 @@ export type ChartProps = LensConfig & export const Chart = ({ id, queryField, overrides, dateRange, assetId, ...props }: ChartProps) => { const { setDateRange } = useDatePickerContext(); const { searchSessionId } = useLoadingStateContext(); - const { dataView } = useDataView({ index: (props.dataset as LensDataviewDataset)?.index }); + const { + services: { dataViews }, + } = useKibanaContextForPlugin(); + + const { value: filters = [] } = useAsync(async () => { + const resolvedDataView = await resolveDataView({ + dataViewId: (props.dataset as LensDataviewDataset)?.index, + dataViewsService: dataViews, + }); - const filters = useMemo(() => { return [ buildCombinedAssetFilter({ field: queryField, values: [assetId], - dataView, + dataView: resolvedDataView.dataViewReference, }), ]; - }, [assetId, dataView, queryField]); + }, [assetId, dataViews, props.dataset, queryField]); const handleBrushEnd = useCallback( ({ range, preventDefault }: BrushEndArgs) => { diff --git a/x-pack/plugins/observability_solution/infra/public/components/asset_details/context_providers.tsx b/x-pack/plugins/observability_solution/infra/public/components/asset_details/context_providers.tsx index d1294f1b513e6a..6cef28056d9a6b 100644 --- a/x-pack/plugins/observability_solution/infra/public/components/asset_details/context_providers.tsx +++ b/x-pack/plugins/observability_solution/infra/public/components/asset_details/context_providers.tsx @@ -15,7 +15,7 @@ import { AssetDetailsProps } from './types'; export const ContextProviders = ({ children, ...props -}: Omit & { +}: Omit & { children: React.ReactNode; }) => { const { diff --git a/x-pack/plugins/observability_solution/infra/public/components/asset_details/hooks/use_data_views.ts b/x-pack/plugins/observability_solution/infra/public/components/asset_details/hooks/use_data_views.ts index 8207343f2bc4d4..6ef5ab61f517d7 100644 --- a/x-pack/plugins/observability_solution/infra/public/components/asset_details/hooks/use_data_views.ts +++ b/x-pack/plugins/observability_solution/infra/public/components/asset_details/hooks/use_data_views.ts @@ -9,15 +9,14 @@ import useAsync from 'react-use/lib/useAsync'; import createContainer from 'constate'; import { i18n } from '@kbn/i18n'; import { findInventoryModel } from '@kbn/metrics-data-access-plugin/common'; +import { useMetricsDataViewContext } from '../../../containers/metrics_source'; import { useLogViewReference } from '../../../hooks/use_log_view_reference'; -import { useDataView } from '../../../hooks/use_data_view'; import { useAssetDetailsRenderPropsContext } from './use_asset_details_render_props'; -const useDataViews = ({ metricAlias }: { metricAlias: string }) => { +const useDataViews = () => { const { asset } = useAssetDetailsRenderPropsContext(); - const { dataView: metricsDataView, loading: metricsDataViewLoading } = useDataView({ - index: metricAlias, - }); + const { metricsView, loading: metricsDataViewLoading } = useMetricsDataViewContext(); + const { logViewReference, getLogsDataView, @@ -39,7 +38,7 @@ const useDataViews = ({ metricAlias }: { metricAlias: string }) => { ); return { - metrics: { dataView: metricsDataView, loading: metricsDataViewLoading }, + metrics: { dataView: metricsView?.dataViewReference, loading: metricsDataViewLoading }, logs: { dataView: logsDataView, reference: logViewReference, diff --git a/x-pack/plugins/observability_solution/infra/public/components/asset_details/hooks/use_process_list.ts b/x-pack/plugins/observability_solution/infra/public/components/asset_details/hooks/use_process_list.ts index 5ec0d38739ba47..6ddff690c3818b 100644 --- a/x-pack/plugins/observability_solution/infra/public/components/asset_details/hooks/use_process_list.ts +++ b/x-pack/plugins/observability_solution/infra/public/components/asset_details/hooks/use_process_list.ts @@ -14,7 +14,7 @@ import { BehaviorSubject } from 'rxjs'; import { ProcessListAPIResponse, ProcessListAPIResponseRT } from '../../../../common/http_api'; import { throwErrors, createPlainError } from '../../../../common/runtime_types'; import { useHTTPRequest } from '../../../hooks/use_http_request'; -import { useSourceContext } from '../../../containers/metrics_source'; +import { useMetricsDataViewContext } from '../../../containers/metrics_source'; export interface SortBy { name: string; @@ -28,8 +28,7 @@ export function useProcessList( searchFilter: object, request$?: BehaviorSubject<(() => Promise) | undefined> ) { - const { createDerivedIndexPattern } = useSourceContext(); - const indexPattern = createDerivedIndexPattern().title; + const { metricsView } = useMetricsDataViewContext(); const decodeResponse = (response: any) => { return pipe( @@ -51,7 +50,7 @@ export function useProcessList( 'POST', JSON.stringify({ hostTerm, - indexPattern, + indexPattern: metricsView?.indices, to, sortBy: parsedSortBy, searchFilter, @@ -80,9 +79,8 @@ export function useProcessList( function useProcessListParams(props: { hostTerm: Record; to: number }) { const { hostTerm, to } = props; - const { createDerivedIndexPattern } = useSourceContext(); - const indexPattern = createDerivedIndexPattern().title; - return { hostTerm, indexPattern, to }; + const { metricsView } = useMetricsDataViewContext(); + return { hostTerm, indexPattern: metricsView?.indices, to }; } const ProcessListContext = createContainter(useProcessListParams); export const [ProcessListContextProvider, useProcessListContext] = ProcessListContext; diff --git a/x-pack/plugins/observability_solution/infra/public/components/asset_details/types.ts b/x-pack/plugins/observability_solution/infra/public/components/asset_details/types.ts index 35eea0faab44b4..afbf4fd35756a1 100644 --- a/x-pack/plugins/observability_solution/infra/public/components/asset_details/types.ts +++ b/x-pack/plugins/observability_solution/infra/public/components/asset_details/types.ts @@ -80,9 +80,6 @@ export interface AssetDetailsProps { overrides?: OverridableTabState; renderMode: RenderMode; links?: LinkOptions[]; - // This is temporary. Once we start using the asset details in other plugins, - // It will have to retrieve the metricAlias internally rather than receive it via props - metricAlias: string; } export type TabsStateChangeFn = (state: TabState) => void; diff --git a/x-pack/plugins/observability_solution/infra/public/components/ml/anomaly_detection/anomaly_detection_flyout.tsx b/x-pack/plugins/observability_solution/infra/public/components/ml/anomaly_detection/anomaly_detection_flyout.tsx index dac65711e592f4..bdace63850f36a 100644 --- a/x-pack/plugins/observability_solution/infra/public/components/ml/anomaly_detection/anomaly_detection_flyout.tsx +++ b/x-pack/plugins/observability_solution/infra/public/components/ml/anomaly_detection/anomaly_detection_flyout.tsx @@ -8,7 +8,7 @@ import React, { useState, useCallback } from 'react'; import { EuiHeaderLink, EuiFlyout } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n-react'; -import { useSourceContext } from '../../../containers/metrics_source'; +import { useMetricsDataViewContext } from '../../../containers/metrics_source'; import { FlyoutHome } from './flyout_home'; import { JobSetupScreen } from './job_setup_screen'; import { useInfraMLCapabilities } from '../../../containers/ml/infra_ml_capabilities'; @@ -17,8 +17,8 @@ import { MetricK8sModuleProvider } from '../../../containers/ml/modules/metrics_ import { useActiveKibanaSpace } from '../../../hooks/use_kibana_space'; export const AnomalyDetectionFlyout = ({ - hideJobType, - hideSelectGroup, + hideJobType = false, + hideSelectGroup = false, }: { hideJobType?: boolean; hideSelectGroup?: boolean; @@ -27,7 +27,7 @@ export const AnomalyDetectionFlyout = ({ const [showFlyout, setShowFlyout] = useState(false); const [screenName, setScreenName] = useState<'home' | 'setup'>('home'); const [screenParams, setScreenParams] = useState(null); - const { source } = useSourceContext(); + const { metricsView } = useMetricsDataViewContext(); const { space } = useActiveKibanaSpace(); @@ -48,7 +48,7 @@ export const AnomalyDetectionFlyout = ({ setShowFlyout(false); }, []); - if (source?.configuration.metricAlias == null || space == null) { + if (!metricsView?.indices || !space) { return null; } @@ -68,12 +68,12 @@ export const AnomalyDetectionFlyout = ({ {showFlyout && ( diff --git a/x-pack/plugins/observability_solution/infra/public/components/ml/anomaly_detection/job_setup_screen.tsx b/x-pack/plugins/observability_solution/infra/public/components/ml/anomaly_detection/job_setup_screen.tsx index c17b73703fe977..52150d0f9066ab 100644 --- a/x-pack/plugins/observability_solution/infra/public/components/ml/anomaly_detection/job_setup_screen.tsx +++ b/x-pack/plugins/observability_solution/infra/public/components/ml/anomaly_detection/job_setup_screen.tsx @@ -31,7 +31,7 @@ import moment, { Moment } from 'moment'; import { i18n } from '@kbn/i18n'; import { FeatureFeedbackButton, useUiTracker } from '@kbn/observability-shared-plugin/public'; import { css } from '@emotion/react'; -import { useSourceContext } from '../../../containers/metrics_source'; +import { useMetricsDataViewContext } from '../../../containers/metrics_source'; import { useMetricHostsModuleContext } from '../../../containers/ml/modules/metrics_hosts/module'; import { useMetricK8sModuleContext } from '../../../containers/ml/modules/metrics_k8s/module'; import { FixedDatePicker } from '../../fixed_datepicker'; @@ -54,10 +54,10 @@ export const JobSetupScreen = (props: Props) => { const [partitionField, setPartitionField] = useState(null); const host = useMetricHostsModuleContext(); const kubernetes = useMetricK8sModuleContext(); + const { metricsView } = useMetricsDataViewContext(); const [filter, setFilter] = useState(''); const [filterQuery, setFilterQuery] = useState(''); const trackMetric = useUiTracker({ app: 'infra_metrics' }); - const { createDerivedIndexPattern } = useSourceContext(); const { kibanaVersion, isCloudEnv, isServerlessEnv } = useContext(KibanaEnvironmentContext); const { euiTheme } = useEuiTheme(); @@ -95,11 +95,6 @@ export const JobSetupScreen = (props: Props) => { } }, [props.jobType, kubernetes.jobSummaries, host.jobSummaries]); - const derivedIndexPattern = useMemo( - () => createDerivedIndexPattern(), - [createDerivedIndexPattern] - ); - const updateStart = useCallback((date: Moment) => { setStartDate(date); }, []); @@ -135,9 +130,9 @@ export const JobSetupScreen = (props: Props) => { const onFilterChange = useCallback( (f: string) => { setFilter(f || ''); - setFilterQuery(convertKueryToElasticSearchQuery(f, derivedIndexPattern) || ''); + setFilterQuery(convertKueryToElasticSearchQuery(f, metricsView?.dataViewReference) || ''); }, - [derivedIndexPattern] + [metricsView?.dataViewReference] ); /* eslint-disable-next-line react-hooks/exhaustive-deps */ @@ -323,7 +318,7 @@ export const JobSetupScreen = (props: Props) => { selectedOptions={ partitionField ? partitionField.map((p) => ({ label: p })) : undefined } - options={derivedIndexPattern.fields + options={(metricsView?.fields ?? []) .filter((f) => f.aggregatable && f.type === 'string') .map((f) => ({ label: f.name }))} onChange={onPartitionFieldChange} @@ -358,7 +353,6 @@ export const JobSetupScreen = (props: Props) => { } > { + const { + services: { dataViews }, + } = useKibanaContextForPlugin(); + + const { source } = useSourceContext(); + + const [state, refetch] = useAsyncFn(async () => { + const indexPattern = source?.configuration.metricAlias; + if (!indexPattern) { + return Promise.resolve(undefined); + } + + return resolveAdHocDataView({ + dataViewsService: dataViews, + dataViewId: indexPattern, + attributes: { + name: DEFAULT_METRICS_VIEW_ATTRIBUTES.name, + timeFieldName: DEFAULT_METRICS_VIEW_ATTRIBUTES.timeFieldName, + }, + }); + }, [dataViews, source?.configuration.metricAlias]); + + useEffect(() => { + refetch(); + }, [refetch]); + + const { value: metricsView, error, loading } = state; + + return { + metricsView, + loading, + error, + refetch, + }; +}; + +export const [MetricsDataViewProvider, useMetricsDataViewContext] = + createContainer(useMetricsDataView); diff --git a/x-pack/plugins/observability_solution/infra/public/containers/metrics_source/source.tsx b/x-pack/plugins/observability_solution/infra/public/containers/metrics_source/source.tsx index 336b07b6986753..77024a61655025 100644 --- a/x-pack/plugins/observability_solution/infra/public/containers/metrics_source/source.tsx +++ b/x-pack/plugins/observability_solution/infra/public/containers/metrics_source/source.tsx @@ -19,44 +19,32 @@ import type { import { useTrackedPromise } from '../../utils/use_tracked_promise'; import { MissingHttpClientException } from './source_errors'; import { useSourceNotifier } from './notifications'; +import { MetricsDataViewProvider } from './metrics_view'; -export const pickIndexPattern = ( - source: MetricsSourceConfiguration | undefined, - type: 'metrics' -) => { - if (!source) { - return 'unknown-index'; - } - if (type === 'metrics') { - return source.configuration.metricAlias; - } - return `${source.configuration.metricAlias}`; -}; +const API_URL = `/api/metrics/source`; -export const useSource = ({ sourceId }: { sourceId: string }) => { +export const useSourceFetcher = ({ sourceId }: { sourceId: string }) => { + const [source, setSource] = useState(undefined); const { services: { http, telemetry }, } = useKibanaContextForPlugin(); - const notify = useSourceNotifier(); - const fetchService = http; - const API_URL = `/api/metrics/source/${sourceId}`; - - const [source, setSource] = useState(undefined); - const [loadSourceRequest, loadSource] = useTrackedPromise( { cancelPreviousOn: 'resolution', createPromise: async () => { - if (!fetchService) { + if (!http) { throw new MissingHttpClientException(); } const start = performance.now(); - const response = await fetchService.fetch(API_URL, { - method: 'GET', - }); + const response = await http.fetch( + `${API_URL}/${sourceId}`, + { + method: 'GET', + } + ); telemetry?.reportPerformanceMetricEvent( 'infra_source_load', performance.now() - start, @@ -71,17 +59,17 @@ export const useSource = ({ sourceId }: { sourceId: string }) => { } }, }, - [fetchService, sourceId] + [http, sourceId] ); - const [createSourceConfigurationRequest, createSourceConfiguration] = useTrackedPromise( + const [persistSourceConfigurationRequest, persistSourceConfiguration] = useTrackedPromise( { createPromise: async (sourceProperties: PartialMetricsSourceConfigurationProperties) => { - if (!fetchService) { + if (!http) { throw new MissingHttpClientException(); } - return await fetchService.patch(API_URL, { + return await http.patch(`${API_URL}/${sourceId}`, { method: 'PATCH', body: JSON.stringify(sourceProperties), }); @@ -96,60 +84,60 @@ export const useSource = ({ sourceId }: { sourceId: string }) => { notify.updateFailure((error as IHttpFetchError<{ message: string }>).body?.message); }, }, - [fetchService, sourceId] + [http, sourceId] ); useEffect(() => { loadSource(); }, [loadSource, sourceId]); - const createDerivedIndexPattern = () => { - return { - fields: source?.status ? source.status.indexFields : [], - title: pickIndexPattern(source, 'metrics'), - }; + const error = loadSourceRequest.state === 'rejected' ? `${loadSourceRequest.value}` : undefined; + const isLoading = + loadSourceRequest.state === 'uninitialized' || + loadSourceRequest.state === 'pending' || + persistSourceConfigurationRequest.state === 'pending'; + + return { + error, + loadSource, + isLoading, + source, + persistSourceConfiguration, }; +}; - const hasFailedLoadingSource = loadSourceRequest.state === 'rejected'; - const isUninitialized = loadSourceRequest.state === 'uninitialized'; - const isLoadingSource = loadSourceRequest.state === 'pending'; - const isLoading = isLoadingSource || createSourceConfigurationRequest.state === 'pending'; +export const useSource = ({ sourceId }: { sourceId: string }) => { + const { persistSourceConfiguration, source, error, isLoading, loadSource } = useSourceFetcher({ + sourceId, + }); const sourceExists = source ? !!source.version : undefined; const metricIndicesExist = Boolean(source?.status?.metricIndicesExist); - const version = source?.version; - return { - createSourceConfiguration, - createDerivedIndexPattern, isLoading, - isLoadingSource, - isUninitialized, - hasFailedLoadingSource, + error, loadSource, - loadSourceRequest, - loadSourceFailureMessage: hasFailedLoadingSource ? `${loadSourceRequest.value}` : undefined, metricIndicesExist, source, sourceExists, sourceId, - updateSourceConfiguration: createSourceConfiguration, - version, + persistSourceConfiguration, }; }; export const [SourceProvider, useSourceContext] = createContainer(useSource); export const withSourceProvider = - (Component: React.FunctionComponent) => + (Component: React.FC) => (sourceId = 'default') => { return function ComponentWithSourceProvider(props: ComponentProps) { return ( - {/* @ts-expect-error upgrade typescript v4.9.5*/} - + + + ); }; diff --git a/x-pack/plugins/observability_solution/infra/public/containers/with_kuery_autocompletion.tsx b/x-pack/plugins/observability_solution/infra/public/containers/with_kuery_autocompletion.tsx index f9177a23d1e515..4ee3a5e1447274 100644 --- a/x-pack/plugins/observability_solution/infra/public/containers/with_kuery_autocompletion.tsx +++ b/x-pack/plugins/observability_solution/infra/public/containers/with_kuery_autocompletion.tsx @@ -6,7 +6,6 @@ */ import React from 'react'; -import { DataViewBase } from '@kbn/es-query'; import { withKibana, KibanaReactContextValue, @@ -24,7 +23,7 @@ interface WithKueryAutocompletionLifecycleProps { loadSuggestions: (expression: string, cursorPosition: number, maxSuggestions?: number) => void; suggestions: QuerySuggestion[]; }>; - indexPattern: DataViewBase; + dataView?: DataView; } interface WithKueryAutocompletionLifecycleState { @@ -62,7 +61,7 @@ class WithKueryAutocompletionComponent extends React.Component< maxSuggestions?: number, transformSuggestions?: (s: QuerySuggestion[]) => QuerySuggestion[] ) => { - const { indexPattern } = this.props; + const { dataView } = this.props; const language = 'kuery'; const hasQuerySuggestions = this.props.kibana.services.unifiedSearch.autocomplete.hasQuerySuggestions(language); @@ -79,15 +78,16 @@ class WithKueryAutocompletionComponent extends React.Component< suggestions: [], }); - const suggestions = - (await this.props.kibana.services.unifiedSearch.autocomplete.getQuerySuggestions({ - language, - query: expression, - selectionStart: cursorPosition, - selectionEnd: cursorPosition, - indexPatterns: [indexPattern as DataView], - boolFilter: [], - })) || []; + const suggestions = dataView + ? (await this.props.kibana.services.unifiedSearch.autocomplete.getQuerySuggestions({ + language, + query: expression, + selectionStart: cursorPosition, + selectionEnd: cursorPosition, + indexPatterns: [dataView], + boolFilter: [], + })) ?? [] + : []; const transformedSuggestions = transformSuggestions ? transformSuggestions(suggestions) diff --git a/x-pack/plugins/observability_solution/infra/public/hooks/use_data_view.test.ts b/x-pack/plugins/observability_solution/infra/public/hooks/use_data_view.test.ts deleted file mode 100644 index 157a5944982731..00000000000000 --- a/x-pack/plugins/observability_solution/infra/public/hooks/use_data_view.test.ts +++ /dev/null @@ -1,79 +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 { useDataView } from './use_data_view'; -import { renderHook } from '@testing-library/react-hooks'; -import { type KibanaReactContextValue, useKibana } from '@kbn/kibana-react-plugin/public'; -import { coreMock } from '@kbn/core/public/mocks'; -import type { DataView, DataViewsServicePublic } from '@kbn/data-views-plugin/public'; -import type { InfraClientStartDeps } from '../types'; -import { CoreStart } from '@kbn/core/public'; - -jest.mock('@kbn/kibana-react-plugin/public'); - -let dataViewMock: jest.Mocked; -const useKibanaMock = useKibana as jest.MockedFunction; - -const mockUseKibana = () => { - useKibanaMock.mockReturnValue({ - services: { - ...coreMock.createStart(), - dataViews: dataViewMock, - } as Partial & Partial, - } as unknown as KibanaReactContextValue & Partial>); -}; - -const mockDataView = { - id: 'mock-id', - title: 'mock-title', - timeFieldName: 'mock-time-field-name', - isPersisted: () => false, - getName: () => 'mock-data-view', - toSpec: () => ({}), -} as jest.Mocked; - -describe('useDataView hook', () => { - beforeEach(() => { - dataViewMock = { - create: jest.fn().mockImplementation(() => Promise.resolve(mockDataView)), - get: jest.fn().mockImplementation(() => Promise.reject(new Error('Data view not found'))), - } as Partial as jest.Mocked; - - mockUseKibana(); - }); - - it('should create a new ad-hoc data view', async () => { - const { result, waitForNextUpdate } = renderHook(() => useDataView({ index: 'test' })); - - await waitForNextUpdate(); - expect(result.current.loading).toEqual(false); - expect(result.current.error).toBeUndefined(); - expect(result.current.dataView).toEqual(mockDataView); - }); - - it('should create a dataview with unique id for metricAlias metrics', async () => { - const { waitForNextUpdate } = renderHook(() => useDataView({ index: 'metrics' })); - - await waitForNextUpdate(); - expect(dataViewMock.create).toHaveBeenCalledWith({ - id: 'infra_metrics_212933f0-c55e-5a36-8b13-e724aed2f66d', - timeFieldName: '@timestamp', - title: 'metrics', - }); - }); - - it('should create a dataview with unique id for metricAlias remote-metrics', async () => { - const { waitForNextUpdate } = renderHook(() => useDataView({ index: 'remote-metrics' })); - - await waitForNextUpdate(); - expect(dataViewMock.create).toHaveBeenCalledWith({ - id: 'infra_metrics_e40bb657-0351-548e-8e73-093851d9bb6e', - timeFieldName: '@timestamp', - title: 'remote-metrics', - }); - }); -}); diff --git a/x-pack/plugins/observability_solution/infra/public/hooks/use_data_view.ts b/x-pack/plugins/observability_solution/infra/public/hooks/use_data_view.ts deleted file mode 100644 index 5ad22e76d26229..00000000000000 --- a/x-pack/plugins/observability_solution/infra/public/hooks/use_data_view.ts +++ /dev/null @@ -1,49 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { v5 as uuidv5 } from 'uuid'; -import useAsyncRetry from 'react-use/lib/useAsyncRetry'; -import { useKibanaContextForPlugin } from './use_kibana'; - -export const TIMESTAMP_FIELD = '@timestamp'; -export const DATA_VIEW_PREFIX = 'infra_metrics'; - -export const generateDataViewId = (index: string) => { - // generates a unique but the same uuid as long as the index pattern doesn't change - return `${DATA_VIEW_PREFIX}_${uuidv5(index, uuidv5.DNS)}`; -}; - -export const useDataView = ({ index }: { index?: string }) => { - const { - services: { dataViews }, - } = useKibanaContextForPlugin(); - - const state = useAsyncRetry(async () => { - if (!index) { - return Promise.resolve(undefined); - } - - return dataViews.get(index, false).catch(() => - // if data view doesn't exist, create an ad-hoc one - dataViews.create({ - id: generateDataViewId(index), - title: index, - timeFieldName: TIMESTAMP_FIELD, - }) - ); - }, [index]); - - const { value: dataView, loading, error, retry } = state; - - return { - index, - dataView, - loading, - retry, - error, - }; -}; diff --git a/x-pack/plugins/observability_solution/infra/public/hooks/use_inventory_views.ts b/x-pack/plugins/observability_solution/infra/public/hooks/use_inventory_views.ts index 35bb5a154379d4..a8856e9742e638 100644 --- a/x-pack/plugins/observability_solution/infra/public/hooks/use_inventory_views.ts +++ b/x-pack/plugins/observability_solution/infra/public/hooks/use_inventory_views.ts @@ -47,7 +47,7 @@ export const useInventoryViews = (): UseInventoryViewsResult => { const trackMetric = useUiTracker({ app: 'infra_metrics' }); const queryClient = useQueryClient(); - const { source, updateSourceConfiguration } = useSourceContext(); + const { source, persistSourceConfiguration } = useSourceContext(); const defaultViewId = source?.configuration.inventoryDefaultView ?? '0'; @@ -93,7 +93,7 @@ export const useInventoryViews = (): UseInventoryViewsResult => { string, MutationContext >({ - mutationFn: (id) => updateSourceConfiguration({ inventoryDefaultView: id }), + mutationFn: (id) => persistSourceConfiguration({ inventoryDefaultView: id }), /** * To provide a quick feedback, we perform an optimistic update on the list * when updating the default view. diff --git a/x-pack/plugins/observability_solution/infra/public/hooks/use_metrics_explorer_views.ts b/x-pack/plugins/observability_solution/infra/public/hooks/use_metrics_explorer_views.ts index 7e0eb3bd387a1d..fc66789c22962e 100644 --- a/x-pack/plugins/observability_solution/infra/public/hooks/use_metrics_explorer_views.ts +++ b/x-pack/plugins/observability_solution/infra/public/hooks/use_metrics_explorer_views.ts @@ -52,7 +52,7 @@ export const useMetricsExplorerViews = (): UseMetricsExplorerViewsResult => { const trackMetric = useUiTracker({ app: 'infra_metrics' }); const queryClient = useQueryClient(); - const { source, updateSourceConfiguration } = useSourceContext(); + const { source, persistSourceConfiguration } = useSourceContext(); const defaultViewId = source?.configuration.metricsExplorerDefaultView ?? '0'; @@ -98,7 +98,7 @@ export const useMetricsExplorerViews = (): UseMetricsExplorerViewsResult => { string, MutationContext >({ - mutationFn: (id) => updateSourceConfiguration({ metricsExplorerDefaultView: id }), + mutationFn: (id) => persistSourceConfiguration({ metricsExplorerDefaultView: id }), /** * To provide a quick feedback, we perform an optimistic update on the list * when updating the default view. diff --git a/x-pack/plugins/observability_solution/infra/public/pages/404.tsx b/x-pack/plugins/observability_solution/infra/public/pages/404.tsx index e0a87b6af4235a..8f4333a6b6e261 100644 --- a/x-pack/plugins/observability_solution/infra/public/pages/404.tsx +++ b/x-pack/plugins/observability_solution/infra/public/pages/404.tsx @@ -7,21 +7,29 @@ import React from 'react'; import { NotFoundPrompt } from '@kbn/shared-ux-prompt-not-found'; -import { MetricsPageTemplate } from './metrics/page_template'; +import { useKibanaContextForPlugin } from '../hooks/use_kibana'; interface NotFoundPageProps { title: string; } export const NotFoundPage = ({ title }: NotFoundPageProps) => { + const { + services: { + observabilityShared: { + navigation: { PageTemplate }, + }, + }, + } = useKibanaContextForPlugin(); + return ( - - + ); }; diff --git a/x-pack/plugins/observability_solution/infra/public/pages/link_to/redirect_to_host_detail_via_ip.tsx b/x-pack/plugins/observability_solution/infra/public/pages/link_to/redirect_to_host_detail_via_ip.tsx index ad7b3e6e509fd5..b80708c91c2cd4 100644 --- a/x-pack/plugins/observability_solution/infra/public/pages/link_to/redirect_to_host_detail_via_ip.tsx +++ b/x-pack/plugins/observability_solution/infra/public/pages/link_to/redirect_to_host_detail_via_ip.tsx @@ -14,7 +14,7 @@ import { ASSET_DETAILS_LOCATOR_ID } from '@kbn/observability-shared-plugin/publi import { useHostIpToName } from './use_host_ip_to_name'; import { LoadingPage } from '../../components/loading_page'; import { Error } from '../error'; -import { useSourceContext } from '../../containers/metrics_source'; +import { useMetricsDataViewContext } from '../../containers/metrics_source'; import { useKibanaContextForPlugin } from '../../hooks/use_kibana'; import { getSearchParams } from './redirect_to_node_detail'; @@ -28,16 +28,13 @@ export const RedirectToHostDetailViaIP = ({ }, location, }: RedirectToHostDetailType) => { - const { source } = useSourceContext(); + const { metricsView } = useMetricsDataViewContext(); const { services: { share }, } = useKibanaContextForPlugin(); const baseLocator = share.url.locators.get(ASSET_DETAILS_LOCATOR_ID); - const { error, name } = useHostIpToName( - hostIp, - (source && source.configuration && source.configuration.metricAlias) || null - ); + const { error, name } = useHostIpToName(hostIp, (metricsView && metricsView.indices) || null); useEffect(() => { if (name) { diff --git a/x-pack/plugins/observability_solution/infra/public/pages/metrics/hosts/components/host_details_flyout/flyout_wrapper.tsx b/x-pack/plugins/observability_solution/infra/public/pages/metrics/hosts/components/host_details_flyout/flyout_wrapper.tsx index d7e36d950d4cad..694196cf1a0ac8 100644 --- a/x-pack/plugins/observability_solution/infra/public/pages/metrics/hosts/components/host_details_flyout/flyout_wrapper.tsx +++ b/x-pack/plugins/observability_solution/infra/public/pages/metrics/hosts/components/host_details_flyout/flyout_wrapper.tsx @@ -6,7 +6,6 @@ */ import React from 'react'; -import { useSourceContext } from '../../../../../containers/metrics_source'; import { useUnifiedSearchContext } from '../../hooks/use_unified_search'; import type { HostNodeRow } from '../../hooks/use_hosts_table'; import { AssetDetails } from '../../../../../components/asset_details'; @@ -18,10 +17,9 @@ export interface Props { } export const FlyoutWrapper = ({ node: { name }, closeFlyout }: Props) => { - const { source } = useSourceContext(); const { parsedDateRange } = useUnifiedSearchContext(); - return source ? ( + return ( { mode: 'flyout', closeFlyout, }} - metricAlias={source.configuration.metricAlias} /> - ) : null; + ); }; diff --git a/x-pack/plugins/observability_solution/infra/public/pages/metrics/hosts/components/hosts_container.tsx b/x-pack/plugins/observability_solution/infra/public/pages/metrics/hosts/components/hosts_container.tsx index 5b5fab795ff810..044592e3387892 100644 --- a/x-pack/plugins/observability_solution/infra/public/pages/metrics/hosts/components/hosts_container.tsx +++ b/x-pack/plugins/observability_solution/infra/public/pages/metrics/hosts/components/hosts_container.tsx @@ -7,45 +7,12 @@ import React from 'react'; import { EuiSpacer } from '@elastic/eui'; -import { i18n } from '@kbn/i18n'; -import { InfraLoadingPanel } from '../../../../components/loading'; -import { useMetricsDataViewContext } from '../hooks/use_metrics_data_view'; import { UnifiedSearchBar } from './search_bar/unified_search_bar'; import { HostsContent } from './hosts_content'; -import { ErrorCallout } from './error_callout'; import { UnifiedSearchProvider } from '../hooks/use_unified_search'; export const HostContainer = () => { - const { dataView, loading, error, metricAlias, retry } = useMetricsDataViewContext(); - - const isLoading = loading || !dataView; - if (isLoading && !error) { - return ( - - ); - } - - return error ? ( - - ) : ( + return ( diff --git a/x-pack/plugins/observability_solution/infra/public/pages/metrics/hosts/components/kpis/kpi_charts.tsx b/x-pack/plugins/observability_solution/infra/public/pages/metrics/hosts/components/kpis/kpi_charts.tsx index 9fd8f40fa08c70..9b349441c58764 100644 --- a/x-pack/plugins/observability_solution/infra/public/pages/metrics/hosts/components/kpis/kpi_charts.tsx +++ b/x-pack/plugins/observability_solution/infra/public/pages/metrics/hosts/components/kpis/kpi_charts.tsx @@ -12,13 +12,13 @@ import { useUnifiedSearchContext } from '../../hooks/use_unified_search'; import { useHostsViewContext } from '../../hooks/use_hosts_view'; import { useHostCountContext } from '../../hooks/use_host_count'; import { useAfterLoadedState } from '../../hooks/use_after_loaded_state'; -import { useMetricsDataViewContext } from '../../hooks/use_metrics_data_view'; +import { useMetricsDataViewContext } from '../../../../../containers/metrics_source'; export const KpiCharts = () => { const { searchCriteria } = useUnifiedSearchContext(); const { hostNodes, loading: hostsLoading, searchSessionId } = useHostsViewContext(); const { isRequestRunning: hostCountLoading, data: hostCountData } = useHostCountContext(); - const { dataView } = useMetricsDataViewContext(); + const { metricsView } = useMetricsDataViewContext(); const shouldUseSearchCriteria = hostNodes.length === 0; const loading = hostsLoading || hostCountLoading; @@ -29,7 +29,7 @@ export const KpiCharts = () => { buildCombinedAssetFilter({ field: 'host.name', values: hostNodes.map((p) => p.name), - dataView, + dataView: metricsView?.dataViewReference, }), ]; @@ -71,7 +71,7 @@ export const KpiCharts = () => { return ( { const { services: { unifiedSearch, application }, } = useKibanaContextForPlugin(); - const { dataView } = useMetricsDataViewContext(); + const { metricsView } = useMetricsDataViewContext(); const { searchCriteria, onSubmit } = useUnifiedSearchContext(); const { SearchBar } = unifiedSearch.ui; @@ -52,7 +52,7 @@ export const UnifiedSearchBar = () => { 0.5)', })} @@ -75,7 +75,7 @@ export const UnifiedSearchBar = () => { { const { searchCriteria } = useUnifiedSearchContext(); const { loading, searchSessionId } = useHostsViewContext(); const { currentPage } = useHostsTableContext(); - const { dataView } = useDataView({ index: (chartProps.dataset as LensDataviewDataset)?.index }); + const { + services: { dataViews }, + } = useKibanaContextForPlugin(); const shouldUseSearchCriteria = currentPage.length === 0; @@ -36,22 +41,27 @@ export const Chart = ({ id, ...chartProps }: ChartProps) => { searchSessionId, }); - const filters = useMemo(() => { + const { value: filters = [] } = useAsync(async () => { + const resolvedDataView = await resolveDataView({ + dataViewId: (chartProps.dataset as LensDataviewDataset)?.index, + dataViewsService: dataViews, + }); + return shouldUseSearchCriteria ? [...searchCriteria.filters, ...(searchCriteria.panelFilters ?? [])] : [ buildCombinedAssetFilter({ - field: 'host.name', + field: HOST_NAME_FIELD, values: currentPage.map((p) => p.name), - dataView, + dataView: resolvedDataView.dataViewReference, }), ]; }, [ - shouldUseSearchCriteria, + currentPage, + dataViews, + chartProps.dataset, searchCriteria.filters, searchCriteria.panelFilters, - currentPage, - dataView, ]); return ( diff --git a/x-pack/plugins/observability_solution/infra/public/pages/metrics/hosts/components/tabs/metrics/metrics_grid.tsx b/x-pack/plugins/observability_solution/infra/public/pages/metrics/hosts/components/tabs/metrics/metrics_grid.tsx index aafe961b7dc0be..49e833a74a33e8 100644 --- a/x-pack/plugins/observability_solution/infra/public/pages/metrics/hosts/components/tabs/metrics/metrics_grid.tsx +++ b/x-pack/plugins/observability_solution/infra/public/pages/metrics/hosts/components/tabs/metrics/metrics_grid.tsx @@ -10,13 +10,12 @@ import { EuiFlexGrid, EuiFlexItem, EuiText, EuiFlexGroup, EuiSpacer } from '@ela import { HostMetricsExplanationContent } from '../../../../../../components/lens'; import { Chart } from './chart'; import { Popover } from '../../common/popover'; -import { useMetricsDataViewContext } from '../../../hooks/use_metrics_data_view'; +import { useMetricsDataViewContext } from '../../../../../../containers/metrics_source'; import { useMetricsCharts } from '../../../hooks/use_metrics_charts'; export const MetricsGrid = () => { - const { dataView } = useMetricsDataViewContext(); - - const charts = useMetricsCharts({ dataViewId: dataView?.id }); + const { metricsView } = useMetricsDataViewContext(); + const charts = useMetricsCharts({ dataViewId: metricsView?.dataViewReference.id }); return ( <> diff --git a/x-pack/plugins/observability_solution/infra/public/pages/metrics/hosts/hooks/use_host_count.ts b/x-pack/plugins/observability_solution/infra/public/pages/metrics/hosts/hooks/use_host_count.ts index c68ec312abab1c..d3592cc050884f 100644 --- a/x-pack/plugins/observability_solution/infra/public/pages/metrics/hosts/hooks/use_host_count.ts +++ b/x-pack/plugins/observability_solution/infra/public/pages/metrics/hosts/hooks/use_host_count.ts @@ -12,15 +12,16 @@ import { useCallback, useEffect, useMemo } from 'react'; import { catchError, map, Observable, of, startWith, tap } from 'rxjs'; import createContainer from 'constate'; import type { QueryDslQueryContainer, SearchResponse } from '@elastic/elasticsearch/lib/api/types'; +import { HOST_NAME_FIELD, TIMESTAMP_FIELD } from '../../../../../common/constants'; import type { ITelemetryClient } from '../../../../services/telemetry'; import { useKibanaContextForPlugin } from '../../../../hooks/use_kibana'; import { decodeOrThrow } from '../../../../../common/runtime_types'; import { useDataSearch, useLatestPartialDataSearchResponse } from '../../../../utils/data_search'; -import { useMetricsDataViewContext } from './use_metrics_data_view'; +import { useMetricsDataViewContext } from '../../../../containers/metrics_source'; import { useUnifiedSearchContext } from './use_unified_search'; export const useHostCount = () => { - const { dataView, metricAlias } = useMetricsDataViewContext(); + const { metricsView } = useMetricsDataViewContext(); const { services: { telemetry }, } = useKibanaContextForPlugin(); @@ -37,12 +38,12 @@ export const useHostCount = () => { ...query.bool.filter, { exists: { - field: 'host.name', + field: HOST_NAME_FIELD, }, }, { range: { - [dataView?.timeFieldName ?? '@timestamp']: { + [metricsView?.timeFieldName ?? TIMESTAMP_FIELD]: { gte: searchCriteria.dateRange.from, lte: searchCriteria.dateRange.to, }, @@ -57,7 +58,7 @@ export const useHostCount = () => { params: { allow_no_indices: true, ignore_unavailable: true, - index: metricAlias, + index: metricsView?.indices, size: 0, track_total_hits: false, body: { @@ -65,7 +66,7 @@ export const useHostCount = () => { aggs: { count: { cardinality: { - field: 'host.name', + field: HOST_NAME_FIELD, }, }, }, @@ -76,8 +77,8 @@ export const useHostCount = () => { }; }, [ buildQuery, - dataView?.timeFieldName, - metricAlias, + metricsView?.indices, + metricsView?.timeFieldName, searchCriteria.dateRange.from, searchCriteria.dateRange.to, ]), diff --git a/x-pack/plugins/observability_solution/infra/public/pages/metrics/hosts/hooks/use_hosts_table.test.ts b/x-pack/plugins/observability_solution/infra/public/pages/metrics/hosts/hooks/use_hosts_table.test.ts index 9534406d5dcfa4..5df80a9e3634da 100644 --- a/x-pack/plugins/observability_solution/infra/public/pages/metrics/hosts/hooks/use_hosts_table.test.ts +++ b/x-pack/plugins/observability_solution/infra/public/pages/metrics/hosts/hooks/use_hosts_table.test.ts @@ -11,11 +11,13 @@ import { InfraAssetMetricsItem } from '../../../../../common/http_api'; import * as useUnifiedSearchHooks from './use_unified_search'; import * as useHostsViewHooks from './use_hosts_view'; import * as useKibanaContextForPluginHook from '../../../../hooks/use_kibana'; -import * as useMetricsDataViewHooks from './use_metrics_data_view'; +import * as useMetricsDataViewHooks from '../../../../containers/metrics_source'; +import type { DataView } from '@kbn/data-views-plugin/common'; +import { TIMESTAMP_FIELD } from '../../../../../common/constants'; jest.mock('./use_unified_search'); jest.mock('./use_hosts_view'); -jest.mock('./use_metrics_data_view'); +jest.mock('../../../../containers/metrics_source'); jest.mock('../../../../hooks/use_kibana'); const mockUseUnifiedSearchContext = @@ -138,7 +140,15 @@ describe('useHostTable hook', () => { } as ReturnType); mockUseMetricsDataViewContext.mockReturnValue({ - dataView: { id: 'default' }, + metricsView: { + indices: 'metrics-*', + fields: [], + timeFieldName: TIMESTAMP_FIELD, + dataViewReference: { id: 'default' } as DataView, + }, + error: undefined, + loading: false, + refetch: jest.fn(), } as ReturnType); mockUseKibanaContextForPlugin.mockReturnValue({ diff --git a/x-pack/plugins/observability_solution/infra/public/pages/metrics/hosts/hooks/use_hosts_table.tsx b/x-pack/plugins/observability_solution/infra/public/pages/metrics/hosts/hooks/use_hosts_table.tsx index 0cf70b4b4182bb..cdafb71784ab95 100644 --- a/x-pack/plugins/observability_solution/infra/public/pages/metrics/hosts/hooks/use_hosts_table.tsx +++ b/x-pack/plugins/observability_solution/infra/public/pages/metrics/hosts/hooks/use_hosts_table.tsx @@ -15,6 +15,7 @@ import { CloudProvider } from '@kbn/custom-icons'; import { findInventoryModel } from '@kbn/metrics-data-access-plugin/common'; import { EuiToolTip } from '@elastic/eui'; import { EuiBadge } from '@elastic/eui'; +import { HOST_NAME_FIELD } from '../../../../../common/constants'; import { useKibanaContextForPlugin } from '../../../../hooks/use_kibana'; import { createInventoryMetricFormatter } from '../../inventory_view/lib/create_inventory_metric_formatter'; import { EntryTitle } from '../components/table/entry_title'; @@ -25,7 +26,7 @@ import type { } from '../../../../../common/http_api'; import { Sorting, useHostsTableUrlState } from './use_hosts_table_url_state'; import { useHostsViewContext } from './use_hosts_view'; -import { useMetricsDataViewContext } from './use_metrics_data_view'; +import { useMetricsDataViewContext } from '../../../../containers/metrics_source'; import { ColumnHeader } from '../components/table/column_header'; import { TABLE_COLUMN_LABEL, TABLE_CONTENT_LABEL } from '../translations'; import { METRICS_TOOLTIP } from '../../../../common/visualizations'; @@ -138,7 +139,7 @@ export const useHostsTable = () => { }, }, } = useKibanaContextForPlugin(); - const { dataView } = useMetricsDataViewContext(); + const { metricsView } = useMetricsDataViewContext(); const closeFlyout = useCallback(() => setProperties({ detailsItemId: null }), [setProperties]); @@ -152,14 +153,14 @@ export const useHostsTable = () => { } const selectedHostNames = selectedItems.map(({ name }) => name); const newFilter = buildCombinedAssetFilter({ - field: 'host.name', + field: HOST_NAME_FIELD, values: selectedHostNames, - dataView, + dataView: metricsView?.dataViewReference, }); filterManagerService.addFilters(newFilter); setSelectedItems([]); - }, [dataView, filterManagerService, selectedItems]); + }, [filterManagerService, metricsView?.dataViewReference, selectedItems]); const reportHostEntryClick = useCallback( ({ name, cloudProvider }: HostNodeRow['title']) => { diff --git a/x-pack/plugins/observability_solution/infra/public/pages/metrics/hosts/hooks/use_metrics_data_view.ts b/x-pack/plugins/observability_solution/infra/public/pages/metrics/hosts/hooks/use_metrics_data_view.ts deleted file mode 100644 index 4c62bc58f00a9e..00000000000000 --- a/x-pack/plugins/observability_solution/infra/public/pages/metrics/hosts/hooks/use_metrics_data_view.ts +++ /dev/null @@ -1,26 +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 createContainer from 'constate'; -import { useDataView } from '../../../../hooks/use_data_view'; - -export const useMetricsDataView = ({ metricAlias }: { metricAlias: string }) => { - const { dataView, loading, retry, error } = useDataView({ - index: metricAlias, - }); - - return { - metricAlias, - dataView, - loading, - retry, - error, - }; -}; - -export const MetricsDataView = createContainer(useMetricsDataView); -export const [MetricsDataViewProvider, useMetricsDataViewContext] = MetricsDataView; diff --git a/x-pack/plugins/observability_solution/infra/public/pages/metrics/hosts/hooks/use_unified_search.ts b/x-pack/plugins/observability_solution/infra/public/pages/metrics/hosts/hooks/use_unified_search.ts index ac6e1d9a5f9e7e..4063db1e41727b 100644 --- a/x-pack/plugins/observability_solution/infra/public/pages/metrics/hosts/hooks/use_unified_search.ts +++ b/x-pack/plugins/observability_solution/infra/public/pages/metrics/hosts/hooks/use_unified_search.ts @@ -15,7 +15,7 @@ import { parseDateRange } from '../../../../utils/datemath'; import { useKibanaQuerySettings } from '../../../../utils/use_kibana_query_settings'; import { useKibanaContextForPlugin } from '../../../../hooks/use_kibana'; import { telemetryTimeRangeFormatter } from '../../../../../common/formatters/telemetry_time_range'; -import { useMetricsDataViewContext } from './use_metrics_data_view'; +import { useMetricsDataViewContext } from '../../../../containers/metrics_source'; import { HostsSearchPayload, useHostsUrlState, @@ -52,7 +52,7 @@ const getDefaultTimestamps = () => { export const useUnifiedSearch = () => { const [error, setError] = useState(null); const [searchCriteria, setSearch] = useHostsUrlState(); - const { dataView } = useMetricsDataViewContext(); + const { metricsView } = useMetricsDataViewContext(); const { services } = useKibanaContextForPlugin(); const kibanaQuerySettings = useKibanaQuerySettings(); @@ -116,13 +116,13 @@ export const useUnifiedSearch = () => { const buildQuery = useCallback(() => { return buildEsQuery( - dataView, + metricsView?.dataViewReference, searchCriteria.query, [...searchCriteria.filters, ...searchCriteria.panelFilters], kibanaQuerySettings ); }, [ - dataView, + metricsView?.dataViewReference, searchCriteria.query, searchCriteria.filters, searchCriteria.panelFilters, diff --git a/x-pack/plugins/observability_solution/infra/public/pages/metrics/hosts/index.tsx b/x-pack/plugins/observability_solution/infra/public/pages/metrics/hosts/index.tsx index 80dc5a3977c75d..998262283cf9a5 100644 --- a/x-pack/plugins/observability_solution/infra/public/pages/metrics/hosts/index.tsx +++ b/x-pack/plugins/observability_solution/infra/public/pages/metrics/hosts/index.tsx @@ -12,23 +12,17 @@ import { APP_WRAPPER_CLASS } from '@kbn/core/public'; import { css } from '@emotion/react'; import { i18n } from '@kbn/i18n'; import { useKibanaEnvironmentContext } from '../../../hooks/use_kibana'; -import { SourceErrorPage } from '../../../components/source_error_page'; -import { SourceLoadingPage } from '../../../components/source_loading_page'; -import { useSourceContext } from '../../../containers/metrics_source'; import { useMetricsBreadcrumbs } from '../../../hooks/use_metrics_breadcrumbs'; import { MetricsPageTemplate } from '../page_template'; import { hostsTitle } from '../../../translations'; -import { MetricsDataViewProvider } from './hooks/use_metrics_data_view'; import { fullHeightContentStyles } from '../../../page_template.styles'; import { HostContainer } from './components/hosts_container'; import { BetaBadge } from '../../../components/beta_badge'; -import { NoRemoteCluster } from '../../../components/empty_states'; const HOSTS_FEEDBACK_LINK = 'https://docs.google.com/forms/d/e/1FAIpQLScRHG8TIVb1Oq8ZhD4aks3P1TmgiM58TY123QpDCcBz83YC6w/viewform'; export const HostsPage = () => { - const { isLoading, loadSourceFailureMessage, loadSource, source } = useSourceContext(); const { kibanaVersion, isCloudEnv, isServerlessEnv } = useKibanaEnvironmentContext(); useTrackPageview({ app: 'infra_metrics', path: 'hosts' }); @@ -40,28 +34,10 @@ export const HostsPage = () => { }, ]); - const { metricIndicesExist, remoteClustersExist } = source?.status ?? {}; - - if (isLoading && !source) return ; - - if (!remoteClustersExist) { - return ; - } - - if (!metricIndicesExist) { - return ( - - ); - } - - if (loadSourceFailureMessage) - return ; - return (
{ }, }} > - {source && ( - - - - )} +
diff --git a/x-pack/plugins/observability_solution/infra/public/pages/metrics/index.tsx b/x-pack/plugins/observability_solution/infra/public/pages/metrics/index.tsx index d87706a587b8ba..8a96b1c2c6b6e1 100644 --- a/x-pack/plugins/observability_solution/infra/public/pages/metrics/index.tsx +++ b/x-pack/plugins/observability_solution/infra/public/pages/metrics/index.tsx @@ -20,22 +20,17 @@ import { import { useKibana, useUiSetting } from '@kbn/kibana-react-plugin/public'; import { HeaderMenuPortal, useLinkProps } from '@kbn/observability-shared-plugin/public'; import { enableInfrastructureHostsView } from '@kbn/observability-plugin/common'; -import { MetricsSourceConfigurationProperties } from '../../../common/metrics_sources'; import { HelpCenterContent } from '../../components/help_center_content'; import { useReadOnlyBadge } from '../../hooks/use_readonly_badge'; -import { MetricsExplorerOptionsContainer } from './metrics_explorer/hooks/use_metrics_explorer_options'; -import { WithMetricsExplorerOptionsUrlState } from '../../containers/metrics_explorer/with_metrics_explorer_options_url_state'; import { MetricsExplorerPage } from './metrics_explorer'; import { SnapshotPage } from './inventory_view'; import { NodeDetail } from './metric_detail'; import { MetricsSettingsPage } from './settings'; -import { SourceLoadingPage } from '../../components/source_loading_page'; import { MetricsAlertDropdown } from '../../alerting/common/components/metrics_alert_dropdown'; import { AlertPrefillProvider } from '../../alerting/use_alert_prefill'; import { InfraMLCapabilitiesProvider } from '../../containers/ml/infra_ml_capabilities'; import { AnomalyDetectionFlyout } from '../../components/ml/anomaly_detection/anomaly_detection_flyout'; import { HeaderActionMenuContext } from '../../utils/header_action_menu_provider'; -import { CreateDerivedIndexPattern, useSourceContext } from '../../containers/metrics_source'; import { NotFoundPage } from '../404'; import { ReactQueryProvider } from '../../containers/react_query_provider'; import { usePluginConfig } from '../../containers/plugin_config_context'; @@ -58,8 +53,6 @@ export const InfrastructurePage = () => { const kibana = useKibana(); - const { source, createDerivedIndexPattern } = useSourceContext(); - useReadOnlyBadge(!uiCapabilities?.infrastructure?.save); const settingsLinkProps = useLinkProps({ @@ -86,18 +79,14 @@ export const InfrastructurePage = () => { {settingsTabTitle} - + { - return ; - }} + path="/hosts" + render={() => } /> { - return ; - }} + path="/detail/host" + render={() => } /> {config.featureFlags.alertsAndRulesDropdownEnabled && ( @@ -120,24 +109,8 @@ export const InfrastructurePage = () => { {config.featureFlags.metricsExplorerEnabled && ( - ( - - - {source?.configuration ? ( - - ) : ( - - )} - - )} - /> + )} - {isHostsViewEnabled && } @@ -162,14 +135,3 @@ export const InfrastructurePage = () => { ); }; - -const PageContent = (props: { - configuration: MetricsSourceConfigurationProperties; - createDerivedIndexPattern: CreateDerivedIndexPattern; -}) => { - const { createDerivedIndexPattern, configuration } = props; - - return ( - - ); -}; diff --git a/x-pack/plugins/observability_solution/infra/public/pages/metrics/inventory_view/components/search_bar.tsx b/x-pack/plugins/observability_solution/infra/public/pages/metrics/inventory_view/components/search_bar.tsx index 7386322774442b..8b56c6468dc0f4 100644 --- a/x-pack/plugins/observability_solution/infra/public/pages/metrics/inventory_view/components/search_bar.tsx +++ b/x-pack/plugins/observability_solution/infra/public/pages/metrics/inventory_view/components/search_bar.tsx @@ -7,13 +7,13 @@ import React from 'react'; import { i18n } from '@kbn/i18n'; -import { useSourceContext } from '../../../../containers/metrics_source'; +import { useMetricsDataViewContext } from '../../../../containers/metrics_source'; import { AutocompleteField } from '../../../../components/autocomplete_field'; import { WithKueryAutocompletion } from '../../../../containers/with_kuery_autocompletion'; import { useWaffleFiltersContext } from '../hooks/use_waffle_filters'; export const SearchBar = () => { - const { createDerivedIndexPattern } = useSourceContext(); + const { metricsView } = useMetricsDataViewContext(); const { applyFilterQueryFromKueryExpression, filterQueryDraft, @@ -21,7 +21,7 @@ export const SearchBar = () => { setFilterQueryDraftFromKueryExpression, } = useWaffleFiltersContext(); return ( - + {({ isLoadingSuggestions, loadSuggestions, suggestions }) => ( { })} suggestions={suggestions} value={filterQueryDraft ? filterQueryDraft : ''} - autoFocus={true} + autoFocus /> )} diff --git a/x-pack/plugins/observability_solution/infra/public/pages/metrics/inventory_view/components/toolbars/metrics_and_groupby_toolbar_items.tsx b/x-pack/plugins/observability_solution/infra/public/pages/metrics/inventory_view/components/toolbars/metrics_and_groupby_toolbar_items.tsx index edf124978c093f..cdfefd037e0c7e 100644 --- a/x-pack/plugins/observability_solution/infra/public/pages/metrics/inventory_view/components/toolbars/metrics_and_groupby_toolbar_items.tsx +++ b/x-pack/plugins/observability_solution/infra/public/pages/metrics/inventory_view/components/toolbars/metrics_and_groupby_toolbar_items.tsx @@ -36,7 +36,6 @@ export const MetricsAndGroupByToolbarItems = (props: Props) => { <> { groupBy={props.groupBy} nodeType={props.nodeType} onChange={props.changeGroupBy} - fields={props.createDerivedIndexPattern().fields} onChangeCustomOptions={props.changeCustomOptions} customOptions={props.customOptions} /> diff --git a/x-pack/plugins/observability_solution/infra/public/pages/metrics/inventory_view/components/toolbars/toolbar_wrapper.tsx b/x-pack/plugins/observability_solution/infra/public/pages/metrics/inventory_view/components/toolbars/toolbar_wrapper.tsx index 1b81c3046362a5..e1ff4674d9e8df 100644 --- a/x-pack/plugins/observability_solution/infra/public/pages/metrics/inventory_view/components/toolbars/toolbar_wrapper.tsx +++ b/x-pack/plugins/observability_solution/infra/public/pages/metrics/inventory_view/components/toolbars/toolbar_wrapper.tsx @@ -8,7 +8,6 @@ import React from 'react'; import { EuiFlexItem, EuiFlexGroup } from '@elastic/eui'; import { fieldToName } from '../../lib/field_to_display_name'; -import { useSourceContext } from '../../../../../containers/metrics_source'; import { useWaffleOptionsContext } from '../../hooks/use_waffle_options'; import { WaffleInventorySwitcher } from '../waffle/waffle_inventory_switcher'; import { ToolbarProps } from './types'; @@ -37,14 +36,12 @@ export const ToolbarWrapper = (props: Props) => { customMetrics, changeCustomMetrics, } = useWaffleOptionsContext(); - const { createDerivedIndexPattern } = useSourceContext(); return ( {props.children({ - createDerivedIndexPattern, changeMetric, changeGroupBy, changeAccount, diff --git a/x-pack/plugins/observability_solution/infra/public/pages/metrics/inventory_view/components/toolbars/types.ts b/x-pack/plugins/observability_solution/infra/public/pages/metrics/inventory_view/components/toolbars/types.ts index c655cf62d503c8..7989049d2c5a88 100644 --- a/x-pack/plugins/observability_solution/infra/public/pages/metrics/inventory_view/components/toolbars/types.ts +++ b/x-pack/plugins/observability_solution/infra/public/pages/metrics/inventory_view/components/toolbars/types.ts @@ -10,12 +10,10 @@ import { SnapshotGroupBy, SnapshotMetricInput, } from '../../../../../../common/http_api/snapshot_api'; -import { CreateDerivedIndexPattern } from '../../../../../containers/metrics_source'; import { InfraGroupByOptions } from '../../../../../lib/lib'; import { WaffleOptionsState, WaffleSortOption } from '../../hooks/use_waffle_options'; export interface ToolbarProps extends Omit { - createDerivedIndexPattern: CreateDerivedIndexPattern; changeMetric: (payload: SnapshotMetricInput) => void; changeGroupBy: (payload: SnapshotGroupBy) => void; changeCustomOptions: (payload: InfraGroupByOptions[]) => void; diff --git a/x-pack/plugins/observability_solution/infra/public/pages/metrics/inventory_view/components/waffle/asset_details_flyout.tsx b/x-pack/plugins/observability_solution/infra/public/pages/metrics/inventory_view/components/waffle/asset_details_flyout.tsx index af87d5470c7d99..909ecdc8910b4f 100644 --- a/x-pack/plugins/observability_solution/infra/public/pages/metrics/inventory_view/components/waffle/asset_details_flyout.tsx +++ b/x-pack/plugins/observability_solution/infra/public/pages/metrics/inventory_view/components/waffle/asset_details_flyout.tsx @@ -9,7 +9,6 @@ import React, { useMemo } from 'react'; import type { InventoryItemType } from '@kbn/metrics-data-access-plugin/common'; import type { InfraWaffleMapOptions } from '../../../../../lib/lib'; import { AssetDetails } from '../../../../../components/asset_details'; -import { useSourceContext } from '../../../../../containers/metrics_source'; import { getAssetDetailsFlyoutTabs } from '../../../../../common/asset_details_config/asset_details_tabs'; interface Props { @@ -35,8 +34,6 @@ export const AssetDetailsFlyout = ({ refreshInterval, isAutoReloading = false, }: Props) => { - const { source } = useSourceContext(); - const dateRange = useMemo(() => { // forces relative dates when auto-refresh is active return isAutoReloading @@ -50,7 +47,7 @@ export const AssetDetailsFlyout = ({ }; }, [currentTime, isAutoReloading]); - return source ? ( + return ( - ) : null; + ); }; diff --git a/x-pack/plugins/observability_solution/infra/public/pages/metrics/inventory_view/components/waffle/custom_field_panel.tsx b/x-pack/plugins/observability_solution/infra/public/pages/metrics/inventory_view/components/waffle/custom_field_panel.tsx index 95625d3683cc43..cb4d1d57436bee 100644 --- a/x-pack/plugins/observability_solution/infra/public/pages/metrics/inventory_view/components/waffle/custom_field_panel.tsx +++ b/x-pack/plugins/observability_solution/infra/public/pages/metrics/inventory_view/components/waffle/custom_field_panel.tsx @@ -8,11 +8,10 @@ import { EuiButton, EuiComboBox, EuiForm, EuiFormRow } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import React from 'react'; +import { useMetricsDataViewContext } from '../../../../../containers/metrics_source'; import { InfraGroupByOptions } from '../../../../../lib/lib'; -import { DerivedIndexPattern } from '../../../../../containers/metrics_source'; interface Props { onSubmit: (field: string) => void; - fields: DerivedIndexPattern['fields']; currentOptions: InfraGroupByOptions[]; } @@ -20,71 +19,65 @@ interface SelectedOption { label: string; } -const initialState = { - selectedOptions: [] as SelectedOption[], -}; - -type State = Readonly; +export const CustomFieldPanel = ({ onSubmit, currentOptions }: Props) => { + const { metricsView } = useMetricsDataViewContext(); + const [selectedOptions, setSelectedOptions] = React.useState([]); -export class CustomFieldPanel extends React.PureComponent { - public static displayName = 'CustomFieldPanel'; - public readonly state: State = initialState; - public render() { - const { fields, currentOptions } = this.props; - const options = fields - .filter( - (f) => - f.aggregatable && - f.type === 'string' && - !(currentOptions && currentOptions.some((o) => o.field === f.name)) - ) - .map((f) => ({ label: f.name })); - const isSubmitDisabled = !this.state.selectedOptions.length; - return ( -
- - - - - - Add - - -
- ); - } - private handleSubmit = () => { - this.props.onSubmit(this.state.selectedOptions[0].label); + const handleSubmit = () => { + onSubmit(selectedOptions[0].label); }; - private handleFieldSelection = (selectedOptions: SelectedOption[]) => { - this.setState({ selectedOptions }); + const handleFieldSelection = (newSelection: SelectedOption[]) => { + setSelectedOptions(newSelection); }; -} + + const options = (metricsView?.fields ?? []) + .filter( + (f) => + f.aggregatable && + f.type === 'string' && + !(currentOptions && currentOptions.some((o) => o.field === f.name)) + ) + .map((f) => ({ label: f.name })); + + const isSubmitDisabled = !selectedOptions.length; + return ( +
+ + + + + + {i18n.translate('xpack.infra..addButtonLabel', { defaultMessage: 'Add' })} + + +
+ ); +}; diff --git a/x-pack/plugins/observability_solution/infra/public/pages/metrics/inventory_view/components/waffle/metric_control/custom_metric_form.tsx b/x-pack/plugins/observability_solution/infra/public/pages/metrics/inventory_view/components/waffle/metric_control/custom_metric_form.tsx index c3cd96ca5ad431..7182b6f59c4488 100644 --- a/x-pack/plugins/observability_solution/infra/public/pages/metrics/inventory_view/components/waffle/metric_control/custom_metric_form.tsx +++ b/x-pack/plugins/observability_solution/infra/public/pages/metrics/inventory_view/components/waffle/metric_control/custom_metric_form.tsx @@ -29,7 +29,7 @@ import { SNAPSHOT_CUSTOM_AGGREGATIONS, SnapshotCustomAggregationRT, } from '../../../../../../../common/http_api/snapshot_api'; -import { DerivedIndexPattern } from '../../../../../../containers/metrics_source'; +import { useMetricsDataViewContext } from '../../../../../../containers/metrics_source'; interface SelectedOption { label: string; @@ -53,202 +53,201 @@ const AGGREGATION_LABELS = { interface Props { theme: EuiTheme | undefined; metric?: SnapshotCustomMetricInput; - fields: DerivedIndexPattern['fields']; customMetrics: SnapshotCustomMetricInput[]; onChange: (metric: SnapshotCustomMetricInput) => void; onCancel: () => void; } -export const CustomMetricForm = withTheme( - ({ theme, onCancel, fields, onChange, metric }: Props) => { - const [label, setLabel] = useState(metric ? metric.label : void 0); - const [aggregation, setAggregation] = useState( - metric ? metric.aggregation : 'avg' - ); - const [field, setField] = useState(metric ? metric.field : void 0); +export const CustomMetricForm = withTheme(({ theme, onCancel, onChange, metric }: Props) => { + const { metricsView } = useMetricsDataViewContext(); + const [label, setLabel] = useState(metric ? metric.label : void 0); + const [aggregation, setAggregation] = useState( + metric ? metric.aggregation : 'avg' + ); + const [field, setField] = useState(metric ? metric.field : void 0); - const handleSubmit = useCallback(() => { - if (metric && aggregation && field) { - onChange({ - ...metric, - label, - aggregation, - field, - }); - } else if (aggregation && field) { - const newMetric: SnapshotCustomMetricInput = { - type: 'custom', - id: uuidv4(), - label, - aggregation, - field, - }; - onChange(newMetric); - } - }, [metric, aggregation, field, onChange, label]); + const handleSubmit = useCallback(() => { + if (metric && aggregation && field) { + onChange({ + ...metric, + label, + aggregation, + field, + }); + } else if (aggregation && field) { + const newMetric: SnapshotCustomMetricInput = { + type: 'custom', + id: uuidv4(), + label, + aggregation, + field, + }; + onChange(newMetric); + } + }, [metric, aggregation, field, onChange, label]); - const handleLabelChange = useCallback( - (e) => { - setLabel(e.target.value); - }, - [setLabel] - ); + const handleLabelChange = useCallback( + (e) => { + setLabel(e.target.value); + }, + [setLabel] + ); - const handleFieldChange = useCallback( - (selectedOptions: SelectedOption[]) => { - setField(selectedOptions[0].label); - }, - [setField] - ); + const handleFieldChange = useCallback( + (selectedOptions: SelectedOption[]) => { + setField(selectedOptions[0].label); + }, + [setField] + ); - const handleAggregationChange = useCallback( - (e) => { - const value = e.target.value; - const aggValue: SnapshotCustomAggregation = SnapshotCustomAggregationRT.is(value) - ? value - : 'avg'; - setAggregation(aggValue); - }, - [setAggregation] - ); + const handleAggregationChange = useCallback( + (e) => { + const value = e.target.value; + const aggValue: SnapshotCustomAggregation = SnapshotCustomAggregationRT.is(value) + ? value + : 'avg'; + setAggregation(aggValue); + }, + [setAggregation] + ); - const fieldOptions = fields - .filter((f) => f.aggregatable && f.type === 'number' && !(field && field === f.name)) - .map((f) => ({ label: f.name })); + const fieldOptions = (metricsView?.fields ?? []) + .filter((f) => f.aggregatable && f.type === 'number' && !(field && field === f.name)) + .map((f) => ({ label: f.name })); - const aggregationOptions = SNAPSHOT_CUSTOM_AGGREGATIONS.map((k) => ({ - text: AGGREGATION_LABELS[k as SnapshotCustomAggregation], - value: k, - })); + const aggregationOptions = SNAPSHOT_CUSTOM_AGGREGATIONS.map((k) => ({ + text: AGGREGATION_LABELS[k as SnapshotCustomAggregation], + value: k, + })); - const isSubmitDisabled = !field || !aggregation; + const isSubmitDisabled = !field || !aggregation; - const title = metric - ? i18n.translate('xpack.infra.waffle.customMetricPanelLabel.edit', { - defaultMessage: 'Edit custom metric', - }) - : i18n.translate('xpack.infra.waffle.customMetricPanelLabel.add', { - defaultMessage: 'Add custom metric', - }); + const title = metric + ? i18n.translate('xpack.infra.waffle.customMetricPanelLabel.edit', { + defaultMessage: 'Edit custom metric', + }) + : i18n.translate('xpack.infra.waffle.customMetricPanelLabel.add', { + defaultMessage: 'Add custom metric', + }); - const titleAriaLabel = metric - ? i18n.translate('xpack.infra.waffle.customMetricPanelLabel.editAriaLabel', { - defaultMessage: 'Back to custom metrics edit mode', - }) - : i18n.translate('xpack.infra.waffle.customMetricPanelLabel.addAriaLabel', { - defaultMessage: 'Back to metric picker', - }); + const titleAriaLabel = metric + ? i18n.translate('xpack.infra.waffle.customMetricPanelLabel.editAriaLabel', { + defaultMessage: 'Back to custom metrics edit mode', + }) + : i18n.translate('xpack.infra.waffle.customMetricPanelLabel.addAriaLabel', { + defaultMessage: 'Back to metric picker', + }); - return ( -
- - - - {title} - - -
+ + + - - - - - - - - of - - - - - - - - + +
+ + + + + + + + + {i18n.translate('xpack.infra.waffle.customMetrics.ofLabel', { + defaultMessage: 'of', + })} + + + + + + + + + + - - -
-
- - - - - - -
-
-
- ); - } -); + onChange={handleLabelChange} + /> + +
+
+ + + + + + +
+ +
+ ); +}); diff --git a/x-pack/plugins/observability_solution/infra/public/pages/metrics/inventory_view/components/waffle/metric_control/index.tsx b/x-pack/plugins/observability_solution/infra/public/pages/metrics/inventory_view/components/waffle/metric_control/index.tsx index cab778feda4df5..48ddf2c1d8305f 100644 --- a/x-pack/plugins/observability_solution/infra/public/pages/metrics/inventory_view/components/waffle/metric_control/index.tsx +++ b/x-pack/plugins/observability_solution/infra/public/pages/metrics/inventory_view/components/waffle/metric_control/index.tsx @@ -21,19 +21,16 @@ import { ModeSwitcher } from './mode_switcher'; import { MetricsEditMode } from './metrics_edit_mode'; import { CustomMetricMode } from './types'; import { DropdownButton } from '../../dropdown_button'; -import { DerivedIndexPattern } from '../../../../../../containers/metrics_source'; interface Props { options: Array<{ text: string; value: string }>; metric: SnapshotMetricInput; - fields: DerivedIndexPattern['fields']; onChange: (metric: SnapshotMetricInput) => void; onChangeCustomMetrics: (metrics: SnapshotCustomMetricInput[]) => void; customMetrics: SnapshotCustomMetricInput[]; } export const WaffleMetricControls = ({ - fields, onChange, onChangeCustomMetrics, metric, @@ -45,6 +42,7 @@ export const WaffleMetricControls = ({ const [editModeCustomMetrics, setEditModeCustomMetrics] = useState( [] ); + const [editCustomMetric, setEditCustomMetric] = useState(); const handleClose = useCallback(() => { setPopoverState(false); @@ -164,7 +162,6 @@ export const WaffleMetricControls = ({ ) : null} {mode === 'addMetric' ? ( ; nodeType: InventoryItemType; groupBy: SnapshotGroupBy; onChange: (groupBy: SnapshotGroupBy) => void; onChangeCustomOptions: (options: InfraGroupByOptions[]) => void; - fields: DerivedIndexPattern['fields']; customOptions: InfraGroupByOptions[]; } -const initialState = { - isPopoverOpen: false, -}; - -type State = Readonly; +export const WaffleGroupByControls = ({ + options, + nodeType, + groupBy, + onChange, + onChangeCustomOptions, + customOptions, +}: Props) => { + const [isPopoverOpen, { toggle: togglePopover, off: closePopover }] = useBoolean(false); -export class WaffleGroupByControls extends React.PureComponent { - public static displayName = 'WaffleGroupByControls'; - public readonly state: State = initialState; - - public render() { - const { nodeType, groupBy } = this.props; - const customOptions = this.props.customOptions.map((option) => ({ + const combinedOptions = [ + ...options, + ...customOptions.map((option) => ({ ...option, toolTipContent: option.text, - })); - const options = this.props.options.concat(customOptions); + })), + ]; - if (!options.length) { - throw Error( - i18n.translate('xpack.infra.waffle.unableToSelectGroupErrorMessage', { - defaultMessage: 'Unable to select group by options for {nodeType}', - values: { - nodeType, - }, - }) - ); - } - const isMaxGroupingsSelected = groupBy.length >= 2; - const maxGroupByTooltip = i18n.translate('xpack.infra.waffle.maxGroupByTooltip', { - defaultMessage: 'Only two groupings can be selected at a time', - }); - const panels: EuiContextMenuPanelDescriptor[] = [ - { - id: 'firstPanel', - title: i18n.translate('xpack.infra.waffle.selectTwoGroupingsTitle', { - defaultMessage: 'Select up to two groupings', - }), - items: [ - { - name: i18n.translate('xpack.infra.waffle.customGroupByOptionName', { - defaultMessage: 'Custom field', - }), - disabled: isMaxGroupingsSelected, - toolTipContent: isMaxGroupingsSelected ? maxGroupByTooltip : null, - icon: 'empty', - panel: 'customPanel', - }, - ...options.map((o) => { - const icon = groupBy.some((g) => g.field === o.field) ? 'check' : 'empty'; - const panel = { - name: o.text, - onClick: this.handleClick(o.field), - icon, - } as EuiContextMenuPanelItemDescriptor; - if (o.toolTipContent) { - panel.toolTipContent = o.toolTipContent; - } - if (isMaxGroupingsSelected && icon === 'empty') { - panel.toolTipContent = maxGroupByTooltip; - panel.disabled = true; - } - return panel; - }), - ], - }, - { - id: 'customPanel', - title: i18n.translate('xpack.infra.waffle.customGroupByPanelTitle', { - defaultMessage: 'Group By Custom Field', - }), - width: 685, - content: ( - - ), - }, - ]; - const buttonBody = - groupBy.length > 0 ? ( - groupBy - .map((g) => options.find((o) => o.field === g.field)) - .filter((o) => o != null) - // In this map the `o && o.field` is totally unnecessary but Typescript is - // too stupid to realize that the filter above prevents the next map from being null - .map((o) => ( - - {o && o.text} - - )) - ) : ( - - ); - - const button = ( - - {buttonBody} - - ); - - return ( - - - + if (!combinedOptions.length) { + throw Error( + i18n.translate('xpack.infra.waffle.unableToSelectGroupErrorMessage', { + defaultMessage: 'Unable to select group by options for {nodeType}', + values: { + nodeType, + }, + }) ); } - private handleRemove = (field: string) => () => { - const { groupBy } = this.props; - this.props.onChange(groupBy.filter((g) => g.field !== field)); - const options = this.props.customOptions.filter((g) => g.field !== field); - this.props.onChangeCustomOptions(options); + const handleRemove = (field: string) => { + onChange(groupBy.filter((g) => g.field !== field)); + onChangeCustomOptions(customOptions.filter((g) => g.field !== field)); // We need to close the panel after we rmeove the pill icon otherwise // it will remain open because the click is still captured by the EuiFilterButton - setTimeout(() => this.handleClose()); - }; - - private handleClose = () => { - this.setState({ isPopoverOpen: false }); + setTimeout(() => closePopover()); }; - private handleToggle = () => { - this.setState((state) => ({ isPopoverOpen: !state.isPopoverOpen })); - }; - - private handleCustomField = (field: string) => { - const options = [ - ...this.props.customOptions, + const handleCustomField = (field: string) => { + onChangeCustomOptions([ + ...customOptions, { text: field, field, }, - ]; - this.props.onChangeCustomOptions(options); - const fn = this.handleClick(field); + ]); + const fn = handleClick(field); fn(); }; - private handleClick = (field: string) => () => { - const { groupBy } = this.props; + const handleClick = (field: string) => () => { if (groupBy.some((g) => g.field === field)) { - this.handleRemove(field)(); - } else if (this.props.groupBy.length < 2) { - this.props.onChange([...groupBy, { field }]); + handleRemove(field); + } else if (groupBy.length < 2) { + onChange([...groupBy, { field }]); } - this.handleClose(); + closePopover(); }; -} -const StyledContextMenu = euiStyled(EuiContextMenu)` - width: 320px; - & .euiContextMenuItem__text { - overflow: hidden; - text-overflow: ellipsis; - } -`; + const isMaxGroupingsSelected = groupBy.length >= 2; + + const panels: EuiContextMenuPanelDescriptor[] = [ + { + id: 'firstPanel', + title: i18n.translate('xpack.infra.waffle.selectTwoGroupingsTitle', { + defaultMessage: 'Select up to two groupings', + }), + items: [ + { + name: i18n.translate('xpack.infra.waffle.customGroupByOptionName', { + defaultMessage: 'Custom field', + }), + disabled: isMaxGroupingsSelected, + toolTipContent: isMaxGroupingsSelected ? maxGroupByTooltip : null, + icon: 'empty', + panel: 'customPanel', + }, + ...combinedOptions.map((o) => { + const icon = groupBy.some((g) => g.field === o.field) ? 'check' : 'empty'; + const panel = { + name: o.text, + onClick: handleClick(o.field), + icon, + } as EuiContextMenuPanelItemDescriptor; + if (o.toolTipContent) { + panel.toolTipContent = o.toolTipContent; + } + if (isMaxGroupingsSelected && icon === 'empty') { + panel.toolTipContent = maxGroupByTooltip; + panel.disabled = true; + } + return panel; + }), + ], + }, + { + id: 'customPanel', + title: i18n.translate('xpack.infra.waffle.customGroupByPanelTitle', { + defaultMessage: 'Group By Custom Field', + }), + width: 685, + content: , + }, + ]; + const buttonBody = + groupBy.length > 0 ? ( + groupBy + .map((g) => combinedOptions.find((o) => o.field === g.field)) + .filter((o) => o != null) + // In this map the `o && o.field` is totally unnecessary but Typescript is + // too stupid to realize that the filter above prevents the next map from being null + .map((o) => ( + + {o && o.text} + + )) + ) : ( + + ); + + const button = ( + + {buttonBody} + + ); + + return ( + + + + ); +}; diff --git a/x-pack/plugins/observability_solution/infra/public/pages/metrics/inventory_view/hooks/use_waffle_filters.test.ts b/x-pack/plugins/observability_solution/infra/public/pages/metrics/inventory_view/hooks/use_waffle_filters.test.ts index cc1108cb91e6d7..87aaad06abc96f 100644 --- a/x-pack/plugins/observability_solution/infra/public/pages/metrics/inventory_view/hooks/use_waffle_filters.test.ts +++ b/x-pack/plugins/observability_solution/infra/public/pages/metrics/inventory_view/hooks/use_waffle_filters.test.ts @@ -6,8 +6,10 @@ */ import { renderHook, act } from '@testing-library/react-hooks'; - +import { DataView } from '@kbn/data-views-plugin/common'; import { useWaffleFilters, WaffleFiltersState } from './use_waffle_filters'; +import { TIMESTAMP_FIELD } from '../../../../../common/constants'; +import { ResolvedDataView } from '../../../../utils/data_view'; // Mock useUrlState hook jest.mock('react-router-dom', () => ({ @@ -17,9 +19,25 @@ jest.mock('react-router-dom', () => ({ }), })); +const mockDataView = { + id: 'mock-id', + title: 'mock-title', + timeFieldName: TIMESTAMP_FIELD, + isPersisted: () => false, + getName: () => 'mock-data-view', + toSpec: () => ({}), +} as jest.Mocked; + jest.mock('../../../../containers/metrics_source', () => ({ - useSourceContext: () => ({ - createDerivedIndexPattern: () => 'jestbeat-*', + useMetricsDataViewContext: () => ({ + metricsView: { + indices: 'jestbeat-*', + timeFieldName: mockDataView.timeFieldName, + fields: mockDataView.fields, + dataViewReference: mockDataView, + } as ResolvedDataView, + loading: false, + error: undefined, }), })); diff --git a/x-pack/plugins/observability_solution/infra/public/pages/metrics/inventory_view/hooks/use_waffle_filters.ts b/x-pack/plugins/observability_solution/infra/public/pages/metrics/inventory_view/hooks/use_waffle_filters.ts index 86f81333e338ec..658fb92c468e04 100644 --- a/x-pack/plugins/observability_solution/infra/public/pages/metrics/inventory_view/hooks/use_waffle_filters.ts +++ b/x-pack/plugins/observability_solution/infra/public/pages/metrics/inventory_view/hooks/use_waffle_filters.ts @@ -18,7 +18,7 @@ import { } from '../../../../../common/inventory_views'; import { useAlertPrefillContext } from '../../../../alerting/use_alert_prefill'; import { useUrlState } from '../../../../utils/use_url_state'; -import { useSourceContext } from '../../../../containers/metrics_source'; +import { useMetricsDataViewContext } from '../../../../containers/metrics_source'; import { convertKueryToElasticSearchQuery } from '../../../../utils/kuery'; const validateKuery = (expression: string) => { @@ -36,8 +36,7 @@ export const DEFAULT_WAFFLE_FILTERS_STATE: InventoryFiltersState = { }; export const useWaffleFilters = () => { - const { createDerivedIndexPattern } = useSourceContext(); - const indexPattern = createDerivedIndexPattern(); + const { metricsView } = useMetricsDataViewContext(); const [urlState, setUrlState] = useUrlState({ defaultState: DEFAULT_WAFFLE_FILTERS_STATE, @@ -53,8 +52,8 @@ export const useWaffleFilters = () => { const [filterQueryDraft, setFilterQueryDraft] = useState(urlState.expression); const filterQueryAsJson = useMemo( - () => convertKueryToElasticSearchQuery(urlState.expression, indexPattern), - [indexPattern, urlState.expression] + () => convertKueryToElasticSearchQuery(urlState.expression, metricsView?.dataViewReference), + [metricsView?.dataViewReference, urlState.expression] ); const applyFilterQueryFromKueryExpression = useCallback( diff --git a/x-pack/plugins/observability_solution/infra/public/pages/metrics/inventory_view/index.tsx b/x-pack/plugins/observability_solution/infra/public/pages/metrics/inventory_view/index.tsx index 3142a82d5e9d2f..818a71216c2721 100644 --- a/x-pack/plugins/observability_solution/infra/public/pages/metrics/inventory_view/index.tsx +++ b/x-pack/plugins/observability_solution/infra/public/pages/metrics/inventory_view/index.tsx @@ -11,9 +11,6 @@ import { useTrackPageview } from '@kbn/observability-shared-plugin/public'; import { APP_WRAPPER_CLASS } from '@kbn/core/public'; import { css } from '@emotion/react'; import { FilterBar } from './components/filter_bar'; -import { SourceErrorPage } from '../../../components/source_error_page'; -import { SourceLoadingPage } from '../../../components/source_loading_page'; -import { useSourceContext } from '../../../containers/metrics_source'; import { useMetricsBreadcrumbs } from '../../../hooks/use_metrics_breadcrumbs'; import { LayoutView } from './components/layout_view'; import { MetricsPageTemplate } from '../page_template'; @@ -22,14 +19,11 @@ import { SavedViews } from './components/saved_views'; import { SnapshotContainer } from './components/snapshot_container'; import { fullHeightContentStyles } from '../../../page_template.styles'; import { SurveySection } from './components/survey_section'; -import { NoRemoteCluster } from '../../../components/empty_states'; import { WaffleOptionsProvider } from './hooks/use_waffle_options'; import { WaffleTimeProvider } from './hooks/use_waffle_time'; import { WaffleFiltersProvider } from './hooks/use_waffle_filters'; export const SnapshotPage = () => { - const { isLoading, loadSourceFailureMessage, loadSource, source } = useSourceContext(); - useTrackPageview({ app: 'infra_metrics', path: 'inventory' }); useTrackPageview({ app: 'infra_metrics', path: 'inventory', delay: 15000 }); @@ -39,23 +33,6 @@ export const SnapshotPage = () => { }, ]); - const { metricIndicesExist, remoteClustersExist } = source?.status ?? {}; - - if (isLoading && !source) return ; - - if (!remoteClustersExist) { - return ; - } - - if (!metricIndicesExist) { - return ( - - ); - } - - if (loadSourceFailureMessage) - return ; - return ( @@ -63,7 +40,6 @@ export const SnapshotPage = () => {
, ], diff --git a/x-pack/plugins/observability_solution/infra/public/pages/metrics/metric_detail/asset_detail_page.tsx b/x-pack/plugins/observability_solution/infra/public/pages/metrics/metric_detail/asset_detail_page.tsx index 31cbe1baae31b9..74bf51277c446f 100644 --- a/x-pack/plugins/observability_solution/infra/public/pages/metrics/metric_detail/asset_detail_page.tsx +++ b/x-pack/plugins/observability_solution/infra/public/pages/metrics/metric_detail/asset_detail_page.tsx @@ -8,37 +8,14 @@ import React from 'react'; import { useRouteMatch } from 'react-router-dom'; import type { InventoryItemType } from '@kbn/metrics-data-access-plugin/common'; -import { NoRemoteCluster } from '../../../components/empty_states'; -import { SourceErrorPage } from '../../../components/source_error_page'; -import { SourceLoadingPage } from '../../../components/source_loading_page'; -import { useSourceContext } from '../../../containers/metrics_source'; import { AssetDetails } from '../../../components/asset_details'; -import { MetricsPageTemplate } from '../page_template'; import { getAssetDetailsTabs } from '../../../common/asset_details_config/asset_details_tabs'; export const AssetDetailPage = () => { - const { isLoading, loadSourceFailureMessage, loadSource, source } = useSourceContext(); const { params: { type: nodeType, node: nodeId }, } = useRouteMatch<{ type: InventoryItemType; node: string }>(); - const { metricIndicesExist, remoteClustersExist } = source?.status ?? {}; - - if (isLoading || !source) return ; - - if (!remoteClustersExist) { - return ; - } - - if (!metricIndicesExist) { - return ( - - ); - } - - if (loadSourceFailureMessage) - return ; - return ( { renderMode={{ mode: 'page', }} - metricAlias={source.configuration.metricAlias} /> ); }; diff --git a/x-pack/plugins/observability_solution/infra/public/pages/metrics/metric_detail/components/node_details_page.tsx b/x-pack/plugins/observability_solution/infra/public/pages/metrics/metric_detail/components/node_details_page.tsx index fdc274a6d06f75..940ff1f92f01d3 100644 --- a/x-pack/plugins/observability_solution/infra/public/pages/metrics/metric_detail/components/node_details_page.tsx +++ b/x-pack/plugins/observability_solution/infra/public/pages/metrics/metric_detail/components/node_details_page.tsx @@ -11,7 +11,6 @@ import moment from 'moment'; import { EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; import { InventoryMetric, InventoryItemType } from '@kbn/metrics-data-access-plugin/common'; import { useTemplateHeaderBreadcrumbs } from '../../../../components/asset_details/hooks/use_page_header'; -import { useSourceContext } from '../../../../containers/metrics_source'; import { useNodeDetails } from '../hooks/use_node_details'; import { MetricsSideNav } from './side_nav'; import { MetricsTimeControls } from './time_controls'; @@ -54,7 +53,6 @@ const parseRange = (range: MetricsTimeInput) => { }; export const NodeDetailsPage = (props: Props) => { - const { metricIndicesExist } = useSourceContext(); const { breadcrumbs } = useTemplateHeaderBreadcrumbs(); const [parsedTimeRange, setParsedTimeRange] = useState(parseRange(props.timeRange)); const { metrics, loading, makeRequest, error } = useNodeDetails( @@ -84,7 +82,6 @@ export const NodeDetailsPage = (props: Props) => { return ( { params: { type: nodeType, node: nodeId }, } = useRouteMatch<{ type: InventoryItemType; node: string }>(); const inventoryModel = findInventoryModel(nodeType); - const { sourceId, metricIndicesExist } = useSourceContext(); + const { sourceId } = useSourceContext(); const parentBreadcrumbResolver = useParentBreadcrumbResolver(); const { @@ -79,7 +79,7 @@ export const MetricDetailPage = () => { if (metadataLoading && !filteredRequiredMetrics.length) { return ( - + void; } export const MetricsExplorerChart = ({ - source, options, chartOptions, series, @@ -119,7 +116,6 @@ export const MetricsExplorerChart = ({ chartOptions={chartOptions} series={series} onFilter={onFilter} - source={source} uiCapabilities={uiCapabilities} /> @@ -132,7 +128,6 @@ export const MetricsExplorerChart = ({ options={options} chartOptions={chartOptions} series={series} - source={source} timeRange={timeRange} uiCapabilities={uiCapabilities} /> diff --git a/x-pack/plugins/observability_solution/infra/public/pages/metrics/metrics_explorer/components/chart_context_menu.test.tsx b/x-pack/plugins/observability_solution/infra/public/pages/metrics/metrics_explorer/components/chart_context_menu.test.tsx index 5a3ed5e2b32683..6ff53e99fd7d27 100644 --- a/x-pack/plugins/observability_solution/infra/public/pages/metrics/metrics_explorer/components/chart_context_menu.test.tsx +++ b/x-pack/plugins/observability_solution/infra/public/pages/metrics/metrics_explorer/components/chart_context_menu.test.tsx @@ -8,17 +8,26 @@ import React from 'react'; import { MetricsExplorerChartContextMenu, Props } from './chart_context_menu'; import { ReactWrapper, mount } from 'enzyme'; -import { - options, - source, - timeRange, - chartOptions, -} from '../../../../utils/fixtures/metrics_explorer'; +import { options, timeRange, chartOptions } from '../../../../utils/fixtures/metrics_explorer'; import { Capabilities } from '@kbn/core/public'; import { KibanaContextProvider } from '@kbn/kibana-react-plugin/public'; import { coreMock } from '@kbn/core/public/mocks'; +import { MetricsDataViewProvider, SourceProvider } from '../../../../containers/metrics_source'; +import { TIMESTAMP_FIELD } from '../../../../../common/constants'; +import { DataView } from '@kbn/data-views-plugin/common'; +import { ResolvedDataView } from '../../../../utils/data_view'; const coreStartMock = coreMock.createStart(); + +const mockDataView = { + id: 'mock-id', + title: 'mock-title', + timeFieldName: TIMESTAMP_FIELD, + isPersisted: () => false, + getName: () => 'mock-data-view', + toSpec: () => ({}), +} as jest.Mocked; + const series = { id: 'exmaple-01', rows: [], columns: [] }; const uiCapabilities: Capabilities = { navLinks: { show: false }, @@ -28,6 +37,24 @@ const uiCapabilities: Capabilities = { infrastructure: { save: true }, }; +jest.mock('../../../../containers/metrics_source', () => ({ + SourceProvider: jest.fn(({ children }) => <>{children}), + MetricsDataViewProvider: jest.fn(({ children }) => <>{children}), + useSourceContext: () => ({ + source: { id: 'default' }, + }), + useMetricsDataViewContext: () => ({ + metricsView: { + indices: 'metricbeat-*', + timeFieldName: mockDataView.timeFieldName, + fields: mockDataView.fields, + dataViewReference: mockDataView, + } as ResolvedDataView, + loading: false, + error: undefined, + }), +})); + const getTestSubject = (component: ReactWrapper, name: string) => { return component.find(`[data-test-subj="${name}"]`).hostNodes(); }; @@ -35,7 +62,11 @@ const getTestSubject = (component: ReactWrapper, name: string) => { const mountComponentWithProviders = (props: Props): ReactWrapper => { return mount( - + + + + + ); }; @@ -55,7 +86,6 @@ describe('MetricsExplorerChartContextMenu', () => { const onFilter = jest.fn().mockImplementation((query: string) => void 0); const component = mountComponentWithProviders({ timeRange, - source, series, options, onFilter, @@ -73,7 +103,6 @@ describe('MetricsExplorerChartContextMenu', () => { const onFilter = jest.fn().mockImplementation((query: string) => void 0); const component = mountComponentWithProviders({ timeRange, - source, series, options: customOptions, onFilter, @@ -87,7 +116,6 @@ describe('MetricsExplorerChartContextMenu', () => { it('should not display "Add Filter" without onFilter', async () => { const component = mountComponentWithProviders({ timeRange, - source, series, options, uiCapabilities, @@ -102,7 +130,6 @@ describe('MetricsExplorerChartContextMenu', () => { const onFilter = jest.fn().mockImplementation((query: string) => void 0); const component = mountComponentWithProviders({ timeRange, - source, series, options: customOptions, onFilter, @@ -117,7 +144,6 @@ describe('MetricsExplorerChartContextMenu', () => { const customOptions = { ...options, metrics: [] }; const component = mountComponentWithProviders({ timeRange, - source, series, options: customOptions, uiCapabilities, @@ -134,7 +160,6 @@ describe('MetricsExplorerChartContextMenu', () => { const onFilter = jest.fn().mockImplementation((query: string) => void 0); const component = mountComponentWithProviders({ timeRange, - source, series, options, onFilter, @@ -152,7 +177,6 @@ describe('MetricsExplorerChartContextMenu', () => { const customOptions = { ...options, groupBy: void 0 }; const component = mountComponentWithProviders({ timeRange, - source, series, options: customOptions, onFilter, diff --git a/x-pack/plugins/observability_solution/infra/public/pages/metrics/metrics_explorer/components/chart_context_menu.tsx b/x-pack/plugins/observability_solution/infra/public/pages/metrics/metrics_explorer/components/chart_context_menu.tsx index 0243ccacf136a6..2821428d9fa4b7 100644 --- a/x-pack/plugins/observability_solution/infra/public/pages/metrics/metrics_explorer/components/chart_context_menu.tsx +++ b/x-pack/plugins/observability_solution/infra/public/pages/metrics/metrics_explorer/components/chart_context_menu.tsx @@ -18,7 +18,7 @@ import DateMath from '@kbn/datemath'; import { Capabilities } from '@kbn/core/public'; import { useLinkProps } from '@kbn/observability-shared-plugin/public'; import { InventoryItemType } from '@kbn/metrics-data-access-plugin/common'; -import { MetricsSourceConfigurationProperties } from '../../../../../common/metrics_sources'; +import { useMetricsDataViewContext } from '../../../../containers/metrics_source'; import { AlertFlyout } from '../../../../alerting/metric_threshold/components/alert_flyout'; import { MetricsExplorerSeries } from '../../../../../common/http_api/metrics_explorer'; import { @@ -26,7 +26,7 @@ import { MetricsExplorerTimeOptions, MetricsExplorerChartOptions, } from '../hooks/use_metrics_explorer_options'; -import { createTSVBLink } from './helpers/create_tsvb_link'; +import { createTSVBLink, TSVB_WORKAROUND_INDEX_PATTERN } from './helpers/create_tsvb_link'; import { useNodeDetailsRedirect } from '../../../link_to'; import { HOST_FIELD, POD_FIELD, CONTAINER_FIELD } from '../../../../../common/constants'; @@ -34,16 +34,12 @@ export interface Props { options: MetricsExplorerOptions; onFilter?: (query: string) => void; series: MetricsExplorerSeries; - source?: MetricsSourceConfigurationProperties; timeRange: MetricsExplorerTimeOptions; uiCapabilities?: Capabilities; chartOptions: MetricsExplorerChartOptions; } -const fieldToNodeType = ( - source: MetricsSourceConfigurationProperties, - groupBy: string | string[] -): InventoryItemType | undefined => { +const fieldToNodeType = (groupBy: string | string[]): InventoryItemType | undefined => { const fields = Array.isArray(groupBy) ? groupBy : [groupBy]; if (fields.includes(HOST_FIELD)) { return 'host'; @@ -66,7 +62,6 @@ export const MetricsExplorerChartContextMenu: React.FC = ({ onFilter, options, series, - source, timeRange, uiCapabilities, chartOptions, @@ -74,6 +69,7 @@ export const MetricsExplorerChartContextMenu: React.FC = ({ const { getNodeDetailUrl } = useNodeDetailsRedirect(); const [isPopoverOpen, setPopoverState] = useState(false); const [flyoutVisible, setFlyoutVisible] = useState(false); + const { metricsView } = useMetricsDataViewContext(); const supportFiltering = options.groupBy != null && onFilter != null; const handleFilter = useCallback(() => { // onFilter needs check for Typescript even though it's @@ -104,7 +100,7 @@ export const MetricsExplorerChartContextMenu: React.FC = ({ ] : []; - const nodeType = source && options.groupBy && fieldToNodeType(source, options.groupBy); + const nodeType = options.groupBy && fieldToNodeType(options.groupBy); const nodeDetailLinkProps = nodeType ? getNodeDetailUrl({ @@ -117,7 +113,13 @@ export const MetricsExplorerChartContextMenu: React.FC = ({ }) : {}; const tsvbLinkProps = useLinkProps({ - ...createTSVBLink(source, options, series, timeRange, chartOptions), + ...createTSVBLink( + metricsView?.indices ?? TSVB_WORKAROUND_INDEX_PATTERN, + options, + series, + timeRange, + chartOptions + ), }); const viewNodeDetail = nodeType ? [ diff --git a/x-pack/plugins/observability_solution/infra/public/pages/metrics/metrics_explorer/components/charts.tsx b/x-pack/plugins/observability_solution/infra/public/pages/metrics/metrics_explorer/components/charts.tsx index 6a6728af986294..12ddb26164174c 100644 --- a/x-pack/plugins/observability_solution/infra/public/pages/metrics/metrics_explorer/components/charts.tsx +++ b/x-pack/plugins/observability_solution/infra/public/pages/metrics/metrics_explorer/components/charts.tsx @@ -10,7 +10,6 @@ import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n-react'; import React from 'react'; import { first, last, sumBy } from 'lodash'; -import { MetricsSourceConfigurationProperties } from '../../../../../common/metrics_sources'; import { MetricsExplorerResponse } from '../../../../../common/http_api/metrics_explorer'; import { MetricsExplorerOptions, @@ -32,7 +31,6 @@ interface Props { data?: { pages: MetricsExplorerResponse[]; }; - source: MetricsSourceConfigurationProperties | undefined; timeRange: MetricsExplorerTimeOptions; } export const MetricsExplorerCharts = ({ @@ -42,16 +40,14 @@ export const MetricsExplorerCharts = ({ options, chartOptions, onRefetch, - onFilter, - source, timeRange, onTimeChange, }: Props) => { if (isLoading) { return ( 1 ? 200 : 400} series={series} - source={source} timeRange={timeRange} onTimeChange={onTimeChange} /> diff --git a/x-pack/plugins/observability_solution/infra/public/pages/metrics/metrics_explorer/components/group_by.tsx b/x-pack/plugins/observability_solution/infra/public/pages/metrics/metrics_explorer/components/group_by.tsx index ce7c9926d2b40b..5cc1ad8ec467c5 100644 --- a/x-pack/plugins/observability_solution/infra/public/pages/metrics/metrics_explorer/components/group_by.tsx +++ b/x-pack/plugins/observability_solution/infra/public/pages/metrics/metrics_explorer/components/group_by.tsx @@ -7,19 +7,18 @@ import { EuiComboBox } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; - import React, { useCallback } from 'react'; +import { useMetricsDataViewContext } from '../../../../containers/metrics_source'; import { MetricsExplorerOptions } from '../hooks/use_metrics_explorer_options'; -import { DerivedIndexPattern } from '../../../../containers/metrics_source'; interface Props { options: MetricsExplorerOptions; onChange: (groupBy: string | null | string[]) => void; - fields: DerivedIndexPattern['fields']; errorOptions?: string[]; } -export const MetricsExplorerGroupBy = ({ options, onChange, fields, errorOptions }: Props) => { +export const MetricsExplorerGroupBy = ({ options, onChange, errorOptions }: Props) => { + const { metricsView } = useMetricsDataViewContext(); const handleChange = useCallback( (selectedOptions: Array<{ label: string }>) => { const groupBy = selectedOptions.map((option) => option.label); @@ -42,6 +41,10 @@ export const MetricsExplorerGroupBy = ({ options, onChange, fields, errorOptions ] : []; + const comboOptions = (metricsView?.fields ?? []) + .filter((f) => f.aggregatable && f.type === 'string') + .map((f) => ({ label: f.name })); + return ( f.aggregatable && f.type === 'string') - .map((f) => ({ label: f.name }))} + options={comboOptions} onChange={handleChange} isClearable={true} /> diff --git a/x-pack/plugins/observability_solution/infra/public/pages/metrics/metrics_explorer/components/helpers/create_tsvb_link.test.ts b/x-pack/plugins/observability_solution/infra/public/pages/metrics/metrics_explorer/components/helpers/create_tsvb_link.test.ts index ec41d0ff770d1d..4c29f8921947d0 100644 --- a/x-pack/plugins/observability_solution/infra/public/pages/metrics/metrics_explorer/components/helpers/create_tsvb_link.test.ts +++ b/x-pack/plugins/observability_solution/infra/public/pages/metrics/metrics_explorer/components/helpers/create_tsvb_link.test.ts @@ -6,26 +6,23 @@ */ import { createTSVBLink, createFilterFromOptions } from './create_tsvb_link'; -import { - source, - options, - timeRange, - chartOptions, -} from '../../../../../utils/fixtures/metrics_explorer'; +import { options, timeRange, chartOptions } from '../../../../../utils/fixtures/metrics_explorer'; import { MetricsExplorerYAxisMode, MetricsExplorerChartType, } from '../../hooks/use_metrics_explorer_options'; import { MetricsExplorerOptions } from '../../hooks/use_metrics_explorer_options'; + jest.mock('uuid', () => ({ v4: jest.fn().mockReturnValue('test-id'), })); +const indexPattern = 'metricbeat-*'; const series = { id: 'example-01', rows: [], columns: [] }; describe('createTSVBLink()', () => { it('should just work', () => { - const link = createTSVBLink(source, options, series, timeRange, chartOptions); + const link = createTSVBLink(indexPattern, options, series, timeRange, chartOptions); expect(link).toStrictEqual({ app: 'visualize', hash: '/create', @@ -42,7 +39,7 @@ describe('createTSVBLink()', () => { ...options, metrics: [{ aggregation: 'rate', field: 'host.network.egress.bytes' }], }; - const link = createTSVBLink(source, customOptions, series, timeRange, chartOptions); + const link = createTSVBLink(indexPattern, customOptions, series, timeRange, chartOptions); expect(link).toStrictEqual({ app: 'visualize', hash: '/create', @@ -55,7 +52,7 @@ describe('createTSVBLink()', () => { }); it('should work with time range', () => { const customTimeRange = { ...timeRange, from: 'now-10m', to: 'now' }; - const link = createTSVBLink(source, options, series, customTimeRange, chartOptions); + const link = createTSVBLink(indexPattern, options, series, customTimeRange, chartOptions); expect(link).toStrictEqual({ app: 'visualize', hash: '/create', @@ -66,31 +63,10 @@ describe('createTSVBLink()', () => { }, }); }); - it('should work with source', () => { - const customSource = { - ...source, - metricAlias: 'my-beats-*', - fields: { ...source.fields, timestamp: 'time' }, - }; - const link = createTSVBLink(customSource, options, series, timeRange, chartOptions); - expect(link).toStrictEqual({ - app: 'visualize', - hash: '/create', - search: { - _a: "(filters:!(),linked:!f,query:(language:kuery,query:''),uiState:(),vis:(aggs:!(),params:(axis_formatter:number,axis_min:0,axis_position:left,axis_scale:normal,default_index_pattern:'my-beats-*',filter:(language:kuery,query:'host.name : \"example-01\"'),id:test-id,index_pattern:'my-beats-*',interval:auto,series:!((axis_position:right,chart_type:line,color:#6092C0,fill:0,formatter:percent,id:test-id,label:'avg(system.cpu.user.pct)',line_width:2,metrics:!((field:system.cpu.user.pct,id:test-id,type:avg)),point_size:0,separate_axis:0,split_mode:everything,stacked:none,value_template:{{value}})),show_grid:1,show_legend:1,time_field:'@timestamp',type:timeseries),title:example-01,type:metrics))", - _g: '(refreshInterval:(pause:!t,value:0),time:(from:now-1h,to:now))', - type: 'metrics', - }, - }); - }); + it('should work with filterQuery', () => { - const customSource = { - ...source, - metricAlias: 'my-beats-*', - fields: { ...source.fields, timestamp: 'time' }, - }; const customOptions = { ...options, filterQuery: 'system.network.name:lo*' }; - const link = createTSVBLink(customSource, customOptions, series, timeRange, chartOptions); + const link = createTSVBLink('my-beats-*', customOptions, series, timeRange, chartOptions); expect(link).toStrictEqual({ app: 'visualize', hash: '/create', @@ -104,7 +80,7 @@ describe('createTSVBLink()', () => { it('should remove axis_min from link', () => { const customChartOptions = { ...chartOptions, yAxisMode: MetricsExplorerYAxisMode.auto }; - const link = createTSVBLink(source, options, series, timeRange, customChartOptions); + const link = createTSVBLink(indexPattern, options, series, timeRange, customChartOptions); expect(link).toStrictEqual({ app: 'visualize', hash: '/create', @@ -118,7 +94,7 @@ describe('createTSVBLink()', () => { it('should change series to area', () => { const customChartOptions = { ...chartOptions, type: MetricsExplorerChartType.area }; - const link = createTSVBLink(source, options, series, timeRange, customChartOptions); + const link = createTSVBLink(indexPattern, options, series, timeRange, customChartOptions); expect(link).toStrictEqual({ app: 'visualize', hash: '/create', @@ -136,7 +112,7 @@ describe('createTSVBLink()', () => { type: MetricsExplorerChartType.area, stack: true, }; - const link = createTSVBLink(source, options, series, timeRange, customChartOptions); + const link = createTSVBLink(indexPattern, options, series, timeRange, customChartOptions); expect(link).toStrictEqual({ app: 'visualize', hash: '/create', @@ -149,12 +125,7 @@ describe('createTSVBLink()', () => { }); it('should use the workaround index pattern when there are multiple listed in the source', () => { - const customSource = { - ...source, - metricAlias: 'my-beats-*,metrics-*', - fields: { ...source.fields, timestamp: 'time' }, - }; - const link = createTSVBLink(customSource, options, series, timeRange, chartOptions); + const link = createTSVBLink('my-beats-*,metrics-*', options, series, timeRange, chartOptions); expect(link).toStrictEqual({ app: 'visualize', hash: '/create', diff --git a/x-pack/plugins/observability_solution/infra/public/pages/metrics/metrics_explorer/components/helpers/create_tsvb_link.ts b/x-pack/plugins/observability_solution/infra/public/pages/metrics/metrics_explorer/components/helpers/create_tsvb_link.ts index 2725dc46d5ceaa..3fe9dfc8c5ba65 100644 --- a/x-pack/plugins/observability_solution/infra/public/pages/metrics/metrics_explorer/components/helpers/create_tsvb_link.ts +++ b/x-pack/plugins/observability_solution/infra/public/pages/metrics/metrics_explorer/components/helpers/create_tsvb_link.ts @@ -10,7 +10,6 @@ import { v4 as uuidv4 } from 'uuid'; import { set } from '@kbn/safer-lodash-set'; import { LinkDescriptor } from '@kbn/observability-shared-plugin/public'; import { TIMESTAMP_FIELD } from '../../../../../../common/constants'; -import { MetricsSourceConfigurationProperties } from '../../../../../../common/metrics_sources'; import { colorTransformer, Color } from '../../../../../../common/color_palette'; import { MetricsExplorerSeries } from '../../../../../../common/http_api/metrics_explorer'; import { @@ -31,7 +30,7 @@ import { createMetricLabel } from './create_metric_label'; the field dropdowns are not populated correctly. This index pattern is a temporary fix. See: https://github.com/elastic/kibana/issues/73987 */ -const TSVB_WORKAROUND_INDEX_PATTERN = 'metric*'; +export const TSVB_WORKAROUND_INDEX_PATTERN = 'metric*'; export const metricsExplorerMetricToTSVBMetric = (metric: MetricsExplorerOptionsMetric) => { if (metric.aggregation === 'rate') { @@ -143,15 +142,13 @@ const createTSVBIndexPattern = (alias: string) => { }; export const createTSVBLink = ( - source: MetricsSourceConfigurationProperties | undefined, + indexPattern: string, options: MetricsExplorerOptions, series: MetricsExplorerSeries, timeRange: MetricsExplorerTimeOptions, chartOptions: MetricsExplorerChartOptions ): LinkDescriptor => { - const tsvbIndexPattern = createTSVBIndexPattern( - (source && source.metricAlias) || TSVB_WORKAROUND_INDEX_PATTERN - ); + const tsvbIndexPattern = createTSVBIndexPattern(indexPattern); const appState = { filters: [], linked: false, diff --git a/x-pack/plugins/observability_solution/infra/public/pages/metrics/metrics_explorer/components/kuery_bar.tsx b/x-pack/plugins/observability_solution/infra/public/pages/metrics/metrics_explorer/components/kuery_bar.tsx index ef2ca1e842ed4c..b6918ab140df13 100644 --- a/x-pack/plugins/observability_solution/infra/public/pages/metrics/metrics_explorer/components/kuery_bar.tsx +++ b/x-pack/plugins/observability_solution/infra/public/pages/metrics/metrics_explorer/components/kuery_bar.tsx @@ -8,8 +8,8 @@ import { i18n } from '@kbn/i18n'; import { fromKueryExpression } from '@kbn/es-query'; import React, { useEffect, useState } from 'react'; -import { DataViewBase } from '@kbn/es-query'; import { QuerySuggestion } from '@kbn/unified-search-plugin/public'; +import { useMetricsDataViewContext } from '../../../../containers/metrics_source'; import { WithKueryAutocompletion } from '../../../../containers/with_kuery_autocompletion'; import { AutocompleteField } from '../../../../components/autocomplete_field'; @@ -22,7 +22,6 @@ type LoadSuggestionsFn = ( export type CurryLoadSuggestionsType = (loadSuggestions: LoadSuggestionsFn) => LoadSuggestionsFn; interface Props { - derivedIndexPattern: DataViewBase; onSubmit: (query: string) => void; onChange?: (query: string) => void; value?: string | null; @@ -41,7 +40,6 @@ function validateQuery(query: string) { } export const MetricsExplorerKueryBar = ({ - derivedIndexPattern, onSubmit, onChange, value, @@ -49,6 +47,7 @@ export const MetricsExplorerKueryBar = ({ curryLoadSuggestions = defaultCurryLoadSuggestions, compressed, }: Props) => { + const { metricsView } = useMetricsDataViewContext(); const [draftQuery, setDraftQuery] = useState(value || ''); const [isValid, setValidation] = useState(true); @@ -67,11 +66,6 @@ export const MetricsExplorerKueryBar = ({ } }; - const filteredDerivedIndexPattern = { - ...derivedIndexPattern, - fields: derivedIndexPattern.fields, - }; - const defaultPlaceholder = i18n.translate( 'xpack.infra.homePage.toolbar.kqlSearchFieldPlaceholder', { @@ -80,7 +74,7 @@ export const MetricsExplorerKueryBar = ({ ); return ( - + {({ isLoadingSuggestions, loadSuggestions, suggestions }) => ( void; - fields: DerivedIndexPattern['fields']; } interface SelectedOption { @@ -26,7 +24,8 @@ interface SelectedOption { label: string; } -export const MetricsExplorerMetrics = ({ options, onChange, fields, autoFocus = false }: Props) => { +export const MetricsExplorerMetrics = ({ options, onChange, autoFocus = false }: Props) => { + const { metricsView } = useMetricsDataViewContext(); const colors = Object.keys(Color) as Array; const [shouldFocus, setShouldFocus] = useState(autoFocus); @@ -54,7 +53,10 @@ export const MetricsExplorerMetrics = ({ options, onChange, fields, autoFocus = [onChange, options.aggregation, colors] ); - const comboOptions = fields.map((field) => ({ label: field.name, value: field.name })); + const comboOptions = (metricsView?.fields ?? []).map((field) => ({ + label: field.name, + value: field.name, + })); const selectedOptions = options.metrics .filter((m) => m.aggregation !== 'count') .map((metric) => ({ diff --git a/x-pack/plugins/observability_solution/infra/public/pages/metrics/metrics_explorer/components/toolbar.tsx b/x-pack/plugins/observability_solution/infra/public/pages/metrics/metrics_explorer/components/toolbar.tsx index 6c8b32fc83d8a6..dd866e72cbc3e6 100644 --- a/x-pack/plugins/observability_solution/infra/public/pages/metrics/metrics_explorer/components/toolbar.tsx +++ b/x-pack/plugins/observability_solution/infra/public/pages/metrics/metrics_explorer/components/toolbar.tsx @@ -25,10 +25,8 @@ import { MetricsExplorerAggregationPicker } from './aggregation'; import { MetricsExplorerChartOptions as MetricsExplorerChartOptionsComponent } from './chart_options'; import { useKibanaUiSetting } from '../../../../utils/use_kibana_ui_setting'; import { mapKibanaQuickRangesToDatePickerRanges } from '../../../../utils/map_timepicker_quickranges_to_datepicker_ranges'; -import { DerivedIndexPattern } from '../../../../containers/metrics_source'; interface Props { - derivedIndexPattern: DerivedIndexPattern; timeRange: MetricsExplorerTimeOptions; options: MetricsExplorerOptions; chartOptions: MetricsExplorerChartOptions; @@ -43,7 +41,6 @@ interface Props { export const MetricsExplorerToolbar = ({ timeRange, - derivedIndexPattern, options, onTimeChange, onRefresh, @@ -81,7 +78,6 @@ export const MetricsExplorerToolbar = ({ @@ -94,22 +90,14 @@ export const MetricsExplorerToolbar = ({ /> - + - + ({ useKibanaTimefilterTime: (defaults: { from: string; to: string }) => [() => defaults], @@ -30,8 +25,7 @@ jest.mock('../../../../alerting/use_alert_prefill', () => ({ })); const renderUseMetricsExplorerStateHook = () => - renderHook((props) => useMetricsExplorerState(props.source, props.derivedIndexPattern), { - initialProps: { source, derivedIndexPattern }, + renderHook(() => useMetricsExplorerState(), { wrapper: ({ children }) => ( {children} ), diff --git a/x-pack/plugins/observability_solution/infra/public/pages/metrics/metrics_explorer/hooks/use_metric_explorer_state.ts b/x-pack/plugins/observability_solution/infra/public/pages/metrics/metrics_explorer/hooks/use_metric_explorer_state.ts index 5c498708f94f6d..0bcb182879d1f8 100644 --- a/x-pack/plugins/observability_solution/infra/public/pages/metrics/metrics_explorer/hooks/use_metric_explorer_state.ts +++ b/x-pack/plugins/observability_solution/infra/public/pages/metrics/metrics_explorer/hooks/use_metric_explorer_state.ts @@ -7,7 +7,6 @@ import DateMath from '@kbn/datemath'; import { useCallback, useEffect } from 'react'; -import { DataViewBase } from '@kbn/es-query'; import type { MetricsExplorerChartOptions, MetricsExplorerOptions, @@ -15,7 +14,6 @@ import type { MetricsExplorerView, MetricsExplorerViewState, } from '../../../../../common/metrics_explorer_views'; -import { MetricsSourceConfigurationProperties } from '../../../../../common/metrics_sources'; import { MetricsExplorerMetric, MetricsExplorerAggregation, @@ -25,11 +23,7 @@ import { useMetricsExplorerOptionsContainerContext } from './use_metrics_explore export type { MetricsExplorerViewState }; -export const useMetricsExplorerState = ( - source: MetricsSourceConfigurationProperties, - derivedIndexPattern: DataViewBase, - enabled = true -) => { +export const useMetricsExplorerState = ({ enabled }: { enabled: boolean } = { enabled: true }) => { const { defaultViewState, options, @@ -53,13 +47,11 @@ export const useMetricsExplorerState = ( }); }, [setTimestamps, timeRange]); - const { data, error, fetchNextPage, isLoading } = useMetricsExplorerData( + const { data, error, fetchNextPage, isLoading } = useMetricsExplorerData({ options, - source, - derivedIndexPattern, timestamps, - enabled - ); + enabled, + }); useEffect(() => { refreshTimestamps(); diff --git a/x-pack/plugins/observability_solution/infra/public/pages/metrics/metrics_explorer/hooks/use_metrics_explorer_data.test.tsx b/x-pack/plugins/observability_solution/infra/public/pages/metrics/metrics_explorer/hooks/use_metrics_explorer_data.test.tsx index 27647088c9a531..202ae51990ad2f 100644 --- a/x-pack/plugins/observability_solution/infra/public/pages/metrics/metrics_explorer/hooks/use_metrics_explorer_data.test.tsx +++ b/x-pack/plugins/observability_solution/infra/public/pages/metrics/metrics_explorer/hooks/use_metrics_explorer_data.test.tsx @@ -8,7 +8,7 @@ import React, { FC, PropsWithChildren } from 'react'; import { QueryClient, QueryClientProvider } from '@tanstack/react-query'; import { useMetricsExplorerData } from './use_metrics_explorer_data'; - +import { DataView } from '@kbn/data-views-plugin/common'; import { renderHook } from '@testing-library/react-hooks'; import { KibanaContextProvider } from '@kbn/kibana-react-plugin/public'; @@ -23,6 +23,8 @@ import { import { MetricsExplorerOptions, MetricsExplorerTimestamp } from './use_metrics_explorer_options'; import { DataViewBase } from '@kbn/es-query'; import { MetricsSourceConfigurationProperties } from '../../../../../common/metrics_sources'; +import { TIMESTAMP_FIELD } from '../../../../../common/constants'; +import { ResolvedDataView } from '../../../../utils/data_view'; const mockedFetch = jest.fn(); @@ -35,6 +37,28 @@ const queryClient = new QueryClient({ }, }); +const mockDataView = { + id: 'mock-id', + title: 'mock-title', + timeFieldName: TIMESTAMP_FIELD, + isPersisted: () => false, + getName: () => 'mock-data-view', + toSpec: () => ({}), +} as jest.Mocked; + +jest.mock('../../../../containers/metrics_source', () => ({ + useMetricsDataViewContext: () => ({ + metricsView: { + indices: 'metricbeat-*', + timeFieldName: mockDataView.timeFieldName, + fields: mockDataView.fields, + dataViewReference: mockDataView, + } as ResolvedDataView, + loading: false, + error: undefined, + }), +})); + const renderUseMetricsExplorerDataHook = () => { const wrapper: FC> = ({ children }) => { const services = { @@ -55,12 +79,10 @@ const renderUseMetricsExplorerDataHook = () => { derivedIndexPattern: DataViewBase; timestamps: MetricsExplorerTimestamp; }) => - useMetricsExplorerData( - props.options, - props.source, - props.derivedIndexPattern, - props.timestamps - ), + useMetricsExplorerData({ + options: props.options, + timestamps: props.timestamps, + }), { initialProps: { options, diff --git a/x-pack/plugins/observability_solution/infra/public/pages/metrics/metrics_explorer/hooks/use_metrics_explorer_data.ts b/x-pack/plugins/observability_solution/infra/public/pages/metrics/metrics_explorer/hooks/use_metrics_explorer_data.ts index 28377c23da9362..39c219101aeb2a 100644 --- a/x-pack/plugins/observability_solution/infra/public/pages/metrics/metrics_explorer/hooks/use_metrics_explorer_data.ts +++ b/x-pack/plugins/observability_solution/infra/public/pages/metrics/metrics_explorer/hooks/use_metrics_explorer_data.ts @@ -5,11 +5,9 @@ * 2.0. */ -import { DataViewBase } from '@kbn/es-query'; import { useInfiniteQuery } from '@tanstack/react-query'; - import { useKibana } from '@kbn/kibana-react-plugin/public'; -import { MetricsSourceConfigurationProperties } from '../../../../../common/metrics_sources'; +import { useMetricsDataViewContext } from '../../../../containers/metrics_source'; import { MetricsExplorerResponse, metricsExplorerResponseRT, @@ -18,14 +16,17 @@ import { convertKueryToElasticSearchQuery } from '../../../../utils/kuery'; import { MetricsExplorerOptions, MetricsExplorerTimestamp } from './use_metrics_explorer_options'; import { decodeOrThrow } from '../../../../../common/runtime_types'; -export function useMetricsExplorerData( - options: MetricsExplorerOptions, - source: MetricsSourceConfigurationProperties | undefined, - derivedIndexPattern: DataViewBase, - { fromTimestamp, toTimestamp, interval }: MetricsExplorerTimestamp, - enabled = true -) { +export function useMetricsExplorerData({ + options, + timestamps: { fromTimestamp, toTimestamp, interval }, + enabled = true, +}: { + options: MetricsExplorerOptions; + timestamps: MetricsExplorerTimestamp; + enabled?: boolean; +}) { const { http } = useKibana().services; + const { metricsView } = useMetricsDataViewContext(); const { isLoading, data, error, refetch, fetchNextPage } = useInfiniteQuery< MetricsExplorerResponse, @@ -39,8 +40,8 @@ export function useMetricsExplorerData( if (!http) { throw new Error('HTTP service is unavailable'); } - if (!source) { - throw new Error('Source is unavailable'); + if (!metricsView?.dataViewReference) { + throw new Error('DataView is unavailable'); } const { afterKey } = pageParam; @@ -54,10 +55,13 @@ export function useMetricsExplorerData( groupInstance: options.groupInstance, afterKey, limit: options.limit, - indexPattern: source.metricAlias, + indexPattern: metricsView.indices, filterQuery: (options.filterQuery && - convertKueryToElasticSearchQuery(options.filterQuery, derivedIndexPattern)) || + convertKueryToElasticSearchQuery( + options.filterQuery, + metricsView.dataViewReference + )) || void 0, timerange: { interval, @@ -71,7 +75,7 @@ export function useMetricsExplorerData( return decodeOrThrow(metricsExplorerResponseRT)(response); }, getNextPageParam: (lastPage) => lastPage.pageInfo, - enabled: enabled && !!fromTimestamp && !!toTimestamp && !!http && !!source, + enabled: enabled && !!fromTimestamp && !!toTimestamp && !!http && !!metricsView, refetchOnWindowFocus: false, }); diff --git a/x-pack/plugins/observability_solution/infra/public/pages/metrics/metrics_explorer/index.tsx b/x-pack/plugins/observability_solution/infra/public/pages/metrics/metrics_explorer/index.tsx index b1cea81cec0526..887b6be224a458 100644 --- a/x-pack/plugins/observability_solution/infra/public/pages/metrics/metrics_explorer/index.tsx +++ b/x-pack/plugins/observability_solution/infra/public/pages/metrics/metrics_explorer/index.tsx @@ -5,33 +5,43 @@ * 2.0. */ -import { EuiErrorBoundary } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import React, { useEffect, useState } from 'react'; import { useTrackPageview, FeatureFeedbackButton } from '@kbn/observability-shared-plugin/public'; +import { WithMetricsExplorerOptionsUrlState } from '../../../containers/metrics_explorer/with_metrics_explorer_options_url_state'; import { useKibanaEnvironmentContext } from '../../../hooks/use_kibana'; -import { SourceLoadingPage } from '../../../components/source_loading_page'; import { useMetricsExplorerViews } from '../../../hooks/use_metrics_explorer_views'; -import { MetricsSourceConfigurationProperties } from '../../../../common/metrics_sources'; import { useMetricsBreadcrumbs } from '../../../hooks/use_metrics_breadcrumbs'; -import { NoData, NoRemoteCluster } from '../../../components/empty_states'; +import { NoData } from '../../../components/empty_states'; import { MetricsExplorerCharts } from './components/charts'; import { MetricsExplorerToolbar } from './components/toolbar'; import { useMetricsExplorerState } from './hooks/use_metric_explorer_state'; -import { useSourceContext } from '../../../containers/metrics_source'; import { MetricsPageTemplate } from '../page_template'; import { metricsExplorerTitle } from '../../../translations'; -import { DerivedIndexPattern } from '../../../containers/metrics_source'; import { SavedViews } from './components/saved_views'; - -interface MetricsExplorerPageProps { - source: MetricsSourceConfigurationProperties; - derivedIndexPattern: DerivedIndexPattern; -} +import { MetricsExplorerOptionsContainer } from './hooks/use_metrics_explorer_options'; const METRICS_EXPLORER_FEEDBACK_URL = 'https://ela.st/survey-infra-metricsexplorer?usp=pp_url'; -export const MetricsExplorerPage = ({ source, derivedIndexPattern }: MetricsExplorerPageProps) => { +export const MetricsExplorerPage = () => { + useTrackPageview({ app: 'infra_metrics', path: 'metrics_explorer' }); + useTrackPageview({ app: 'infra_metrics', path: 'metrics_explorer', delay: 15000 }); + + useMetricsBreadcrumbs([ + { + text: metricsExplorerTitle, + }, + ]); + + return ( + + + + + ); +}; + +const MetricsExplorerContent = () => { const [enabled, setEnabled] = useState(false); const { isLoading, @@ -49,16 +59,14 @@ export const MetricsExplorerPage = ({ source, derivedIndexPattern }: MetricsExpl handleLoadMore, onViewStateChange, refresh, - } = useMetricsExplorerState(source, derivedIndexPattern, enabled); + } = useMetricsExplorerState({ enabled }); const { currentView } = useMetricsExplorerViews(); - const { source: sourceContext, metricIndicesExist } = useSourceContext(); + const { kibanaVersion, isCloudEnv, isServerlessEnv } = useKibanaEnvironmentContext(); useTrackPageview({ app: 'infra_metrics', path: 'metrics_explorer' }); useTrackPageview({ app: 'infra_metrics', path: 'metrics_explorer', delay: 15000 }); - const { remoteClustersExist } = sourceContext?.status ?? {}; - useEffect(() => { if (currentView) { onViewStateChange(currentView); @@ -84,68 +92,57 @@ export const MetricsExplorerPage = ({ source, derivedIndexPattern }: MetricsExpl currentTimerange: timeRange, }; - if (isLoading && !sourceContext) return ; - - if (!remoteClustersExist) { - return ; - } - return ( - - , - , - ], - }} - > - , + , + ], + }} + > + + {error ? ( + + ) : ( + - {error ? ( - - ) : ( - - )} - - + )} + ); }; diff --git a/x-pack/plugins/observability_solution/infra/public/pages/metrics/page_template.tsx b/x-pack/plugins/observability_solution/infra/public/pages/metrics/page_template.tsx index 35baef037c4e2d..74128ad8eb41e5 100644 --- a/x-pack/plugins/observability_solution/infra/public/pages/metrics/page_template.tsx +++ b/x-pack/plugins/observability_solution/infra/public/pages/metrics/page_template.tsx @@ -12,16 +12,15 @@ import React, { useEffect } from 'react'; import { noMetricIndicesPromptDescription, noMetricIndicesPromptPrimaryActionTitle, + NoRemoteCluster, } from '../../components/empty_states'; -import { useSourceContext } from '../../containers/metrics_source'; +import { SourceErrorPage } from '../../components/source_error_page'; +import { SourceLoadingPage } from '../../components/source_loading_page'; +import { useMetricsDataViewContext, useSourceContext } from '../../containers/metrics_source'; import { useKibanaContextForPlugin } from '../../hooks/use_kibana'; +import { ErrorCallout } from './hosts/components/error_callout'; -interface MetricsPageTemplateProps extends LazyObservabilityPageTemplateProps { - hasData?: boolean; -} - -export const MetricsPageTemplate: React.FC = ({ - hasData = true, +export const MetricsPageTemplate: React.FC = ({ 'data-test-subj': _dataTestSubj, ...pageTemplateProps }) => { @@ -35,9 +34,11 @@ export const MetricsPageTemplate: React.FC = ({ }, } = useKibanaContextForPlugin(); - const { source } = useSourceContext(); + const { source, error: sourceError, loadSource, isLoading } = useSourceContext(); + const { error: dataViewLoadError, refetch: loadDataView } = useMetricsDataViewContext(); + const { remoteClustersExist, metricIndicesExist } = source?.status ?? {}; - const noDataConfig: NoDataConfig | undefined = hasData + const noDataConfig: NoDataConfig | undefined = metricIndicesExist ? undefined : { solution: i18n.translate('xpack.infra.metrics.noDataConfig.solutionName', { @@ -64,7 +65,7 @@ export const MetricsPageTemplate: React.FC = ({ }, ], starterPrompts: [ - ...(!hasData + ...(!metricIndicesExist ? [ { title: i18n.translate( @@ -85,11 +86,37 @@ export const MetricsPageTemplate: React.FC = ({ : []), ], }); - }, [hasData, setScreenContext, source]); + }, [metricIndicesExist, setScreenContext, source]); + + if (isLoading && !source) return ; + + if (!remoteClustersExist) { + return ; + } + + if (sourceError) { + ; + } + + if (dataViewLoadError) { + ; + } return ( diff --git a/x-pack/plugins/observability_solution/infra/public/pages/metrics/settings/source_configuration_settings.tsx b/x-pack/plugins/observability_solution/infra/public/pages/metrics/settings/source_configuration_settings.tsx index 9dd18644c30f19..de078dcb354df5 100644 --- a/x-pack/plugins/observability_solution/infra/public/pages/metrics/settings/source_configuration_settings.tsx +++ b/x-pack/plugins/observability_solution/infra/public/pages/metrics/settings/source_configuration_settings.tsx @@ -25,6 +25,7 @@ import { METRIC_INVENTORY_THRESHOLD_ALERT_TYPE_ID, METRIC_THRESHOLD_ALERT_TYPE_ID, } from '@kbn/rule-data-utils'; +import { PageTemplate } from '../../../components/page_template'; import { SourceLoadingPage } from '../../../components/source_loading_page'; import { useSourceContext } from '../../../containers/metrics_source'; import { useInfraMLCapabilitiesContext } from '../../../containers/ml/infra_ml_capabilities'; @@ -34,8 +35,6 @@ import { NameConfigurationPanel } from './name_configuration_panel'; import { useSourceConfigurationFormState } from './source_configuration_form_state'; import { useMetricsBreadcrumbs } from '../../../hooks/use_metrics_breadcrumbs'; import { settingsTitle } from '../../../translations'; - -import { MetricsPageTemplate } from '../page_template'; import { FeaturesConfigurationPanel } from './features_configuration_panel'; interface SourceConfigurationSettingsProps { shouldAllowEdit: boolean; @@ -72,12 +71,10 @@ export const SourceConfigurationSettings = ({ }, [http]); const { - createSourceConfiguration, + persistSourceConfiguration: updateSourceConfiguration, source, sourceExists, isLoading, - isUninitialized, - updateSourceConfiguration, } = useSourceContext(); const { @@ -103,9 +100,7 @@ export const SourceConfigurationSettings = ({ const persistUpdates = useCallback(async () => { await Promise.all([ - sourceExists - ? updateSourceConfiguration(formStateChanges) - : createSourceConfiguration(formState), + updateSourceConfiguration(sourceExists ? formStateChanges : formState), infraUiSettings.saveAll(), ]); resetForm(); @@ -115,7 +110,6 @@ export const SourceConfigurationSettings = ({ updateSourceConfiguration, formStateChanges, infraUiSettings, - createSourceConfiguration, formState, ]); @@ -132,12 +126,12 @@ export const SourceConfigurationSettings = ({ const { hasInfraMLCapabilities } = useInfraMLCapabilitiesContext(); - if ((isLoading || isUninitialized) && !source) { + if (isLoading && !source) { return ; } return ( - )} - + ); }; diff --git a/x-pack/plugins/observability_solution/infra/public/utils/data_view.ts b/x-pack/plugins/observability_solution/infra/public/utils/data_view.ts new file mode 100644 index 00000000000000..73d3e8c930ecbe --- /dev/null +++ b/x-pack/plugins/observability_solution/infra/public/utils/data_view.ts @@ -0,0 +1,85 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { DataView, DataViewsContract, type FieldSpec } from '@kbn/data-views-plugin/common'; +import { TIMESTAMP_FIELD } from '../../common/constants'; + +export interface ResolvedDataView { + dataViewReference: DataView; + indices: string; + fields: FieldSpec[]; + timeFieldName: string; +} + +interface PersistedDataView { + dataViewsService: DataViewsContract; + dataViewId: string; +} + +interface DataViewAttributes { + timeFieldName: string; + name?: string; +} + +export const resolveDataView = ({ + dataViewId, + dataViewsService, +}: { + dataViewId: string; + dataViewsService: DataViewsContract; +}) => { + try { + return resolvePersistedDataView({ dataViewsService, dataViewId }); + } catch { + return resolveAdHocDataView({ + dataViewsService, + dataViewId, + attributes: { + timeFieldName: TIMESTAMP_FIELD, + }, + }); + } +}; + +export const resolvePersistedDataView = async ({ + dataViewsService, + dataViewId, +}: PersistedDataView): Promise => { + const dataView = await dataViewsService.get(dataViewId, false); + + return { + indices: dataView.getIndexPattern(), + timeFieldName: dataView.timeFieldName ?? TIMESTAMP_FIELD, + fields: dataView.fields ?? [], + dataViewReference: dataView, + }; +}; + +export const resolveAdHocDataView = async ({ + dataViewsService, + dataViewId, + attributes, +}: PersistedDataView & { attributes: DataViewAttributes }): Promise => { + const { name, timeFieldName } = attributes; + const dataViewReference = await dataViewsService.create( + { + id: dataViewId, + name, + title: dataViewId, + timeFieldName, + }, + false, + false + ); + + return { + indices: dataViewId, + timeFieldName, + fields: dataViewReference.fields, + dataViewReference, + }; +}; diff --git a/x-pack/plugins/observability_solution/infra/public/utils/filters/build.ts b/x-pack/plugins/observability_solution/infra/public/utils/filters/build.ts index 0aee9c00814c67..9beef06244b630 100644 --- a/x-pack/plugins/observability_solution/infra/public/utils/filters/build.ts +++ b/x-pack/plugins/observability_solution/infra/public/utils/filters/build.ts @@ -36,8 +36,8 @@ export const buildCombinedAssetFilter = ({ meta: {}, }; } - const filtersFromValues = values.map((value) => buildPhraseFilter(indexField, value, dataView)); + const filtersFromValues = values.map((value) => buildPhraseFilter(indexField, value, dataView)); return buildCombinedFilter(BooleanRelation.OR, filtersFromValues, dataView); }; diff --git a/x-pack/plugins/observability_solution/infra/public/utils/kuery.ts b/x-pack/plugins/observability_solution/infra/public/utils/kuery.ts index aec9ec58aabaa4..b74549359a9c70 100644 --- a/x-pack/plugins/observability_solution/infra/public/utils/kuery.ts +++ b/x-pack/plugins/observability_solution/infra/public/utils/kuery.ts @@ -10,7 +10,7 @@ import { DataViewBase } from '@kbn/es-query'; export const convertKueryToElasticSearchQuery = ( kueryExpression: string, - indexPattern: DataViewBase, + indexPattern?: DataViewBase, swallowErrors: boolean = true ) => { try { diff --git a/x-pack/plugins/observability_solution/infra/server/lib/alerting/common/messages.ts b/x-pack/plugins/observability_solution/infra/server/lib/alerting/common/messages.ts index 2caf83d3098612..5060e6e0a6587e 100644 --- a/x-pack/plugins/observability_solution/infra/server/lib/alerting/common/messages.ts +++ b/x-pack/plugins/observability_solution/infra/server/lib/alerting/common/messages.ts @@ -10,7 +10,8 @@ import { formatDurationFromTimeUnitChar, TimeUnitChar, } from '@kbn/observability-plugin/common/utils/formatters/duration'; -import { AlertStates, Comparator } from '../../../../common/alerting/metrics'; +import { COMPARATORS } from '@kbn/alerting-comparators'; +import { AlertStates } from '../../../../common/alerting/metrics'; import { UNGROUPED_FACTORY_KEY } from './utils'; export const DOCUMENT_COUNT_I18N = i18n.translate( @@ -49,7 +50,7 @@ const toNumber = (value: number | string) => typeof value === 'string' ? parseFloat(value) : value; const recoveredComparatorToI18n = ( - comparator: Comparator, + comparator: COMPARATORS, threshold: number[], currentValue: number ) => { @@ -60,20 +61,49 @@ const recoveredComparatorToI18n = ( defaultMessage: 'above', }); switch (comparator) { - case Comparator.BETWEEN: + case COMPARATORS.BETWEEN: return currentValue < threshold[0] ? belowText : aboveText; - case Comparator.OUTSIDE_RANGE: + case COMPARATORS.NOT_BETWEEN: return i18n.translate('xpack.infra.metrics.alerting.threshold.betweenRecovery', { defaultMessage: 'between', }); - case Comparator.GT: - case Comparator.GT_OR_EQ: + case COMPARATORS.GREATER_THAN: + case COMPARATORS.GREATER_THAN_OR_EQUALS: return belowText; - case Comparator.LT: - case Comparator.LT_OR_EQ: + case COMPARATORS.LESS_THAN: + case COMPARATORS.LESS_THAN_OR_EQUALS: return aboveText; } }; +const alertComparatorToI18n = (comparator: COMPARATORS) => { + switch (comparator) { + case COMPARATORS.BETWEEN: + return i18n.translate('xpack.infra.customThreshold.rule.threshold.between', { + defaultMessage: 'between', + }); + case COMPARATORS.NOT_BETWEEN: + return i18n.translate('xpack.infra.customThreshold.rule.threshold.notBetween', { + defaultMessage: 'not between', + }); + case COMPARATORS.GREATER_THAN: + return i18n.translate('xpack.infra.customThreshold.rule.threshold.above', { + defaultMessage: 'above', + }); + case COMPARATORS.GREATER_THAN_OR_EQUALS: + return i18n.translate('xpack.infra.customThreshold.rule.threshold.aboveOrEqual', { + defaultMessage: 'above or equal', + }); + case COMPARATORS.LESS_THAN: + return i18n.translate('xpack.infra.customThreshold.rule.threshold.below', { + defaultMessage: 'below', + }); + + case COMPARATORS.LESS_THAN_OR_EQUALS: + return i18n.translate('xpack.infra.customThreshold.rule.threshold.belowOrEqual', { + defaultMessage: 'below or equal', + }); + } +}; const thresholdToI18n = ([a, b]: Array) => { if (typeof b === 'undefined') return a; @@ -88,7 +118,7 @@ const formatGroup = (group: string) => (group === UNGROUPED_FACTORY_KEY ? '' : ` export const buildFiredAlertReason: (alertResult: { group: string; metric: string; - comparator: Comparator; + comparator: COMPARATORS; threshold: Array; currentValue: number | string; timeSize: number; @@ -100,7 +130,7 @@ export const buildFiredAlertReason: (alertResult: { values: { group: formatGroup(group), metric, - comparator, + comparator: alertComparatorToI18n(comparator), threshold: thresholdToI18n(threshold), currentValue, duration: formatDurationFromTimeUnitChar(timeSize, timeUnit), @@ -111,7 +141,7 @@ export const buildFiredAlertReason: (alertResult: { export const buildRecoveredAlertReason: (alertResult: { group: string; metric: string; - comparator: Comparator; + comparator: COMPARATORS; threshold: Array; currentValue: number | string; }) => string = ({ group, metric, comparator, threshold, currentValue }) => diff --git a/x-pack/plugins/observability_solution/infra/server/lib/alerting/inventory_metric_threshold/inventory_metric_threshold_executor.test.ts b/x-pack/plugins/observability_solution/infra/server/lib/alerting/inventory_metric_threshold/inventory_metric_threshold_executor.test.ts index b0ffe0b354b138..d109669aa90f0e 100644 --- a/x-pack/plugins/observability_solution/infra/server/lib/alerting/inventory_metric_threshold/inventory_metric_threshold_executor.test.ts +++ b/x-pack/plugins/observability_solution/infra/server/lib/alerting/inventory_metric_threshold/inventory_metric_threshold_executor.test.ts @@ -13,12 +13,8 @@ import { RuleExecutorServicesMock, alertsMock } from '@kbn/alerting-plugin/serve import { LifecycleAlertServices } from '@kbn/rule-registry-plugin/server'; import { ruleRegistryMocks } from '@kbn/rule-registry-plugin/server/mocks'; import { createLifecycleRuleExecutorMock } from '@kbn/rule-registry-plugin/server/utils/create_lifecycle_rule_executor_mock'; -import { - Aggregators, - Comparator, - InventoryMetricConditions, -} from '../../../../common/alerting/metrics'; - +import { COMPARATORS } from '@kbn/alerting-comparators'; +import { Aggregators, InventoryMetricConditions } from '../../../../common/alerting/metrics'; import type { LogMeta, Logger } from '@kbn/logging'; import { DEFAULT_FLAPPING_SETTINGS } from '@kbn/alerting-plugin/common'; import { createInventoryMetricThresholdExecutor } from './inventory_metric_threshold_executor'; @@ -178,7 +174,7 @@ const baseCriterion = { timeSize: 1, timeUnit: 'm', threshold: [0], - comparator: Comparator.GT, + comparator: COMPARATORS.GREATER_THAN, } as InventoryMetricConditions; describe('The inventory threshold alert type', () => { @@ -187,7 +183,7 @@ describe('The inventory threshold alert type', () => { setup(); - const execute = (comparator: Comparator, threshold: number[], options?: any) => + const execute = (comparator: COMPARATORS, threshold: number[], options?: any) => executor({ ...mockOptions, services, @@ -215,7 +211,7 @@ describe('The inventory threshold alert type', () => { test('throws error when alertsClient is null', async () => { try { services.alertsClient = null; - await execute(Comparator.GT, [0.75]); + await execute(COMPARATORS.GREATER_THAN, [0.75]); } catch (e) { expect(e).toMatchInlineSnapshot( '[Error: Expected alertsClient not to be null! There may have been an issue installing alert resources.]' @@ -232,7 +228,7 @@ describe('The inventory threshold alert type', () => { timeSize: 1, timeUnit: 'm', threshold: [0.75], - comparator: Comparator.GT, + comparator: COMPARATORS.GREATER_THAN, shouldFire: true, shouldWarn: false, currentValue: 1.0, @@ -248,7 +244,7 @@ describe('The inventory threshold alert type', () => { timeSize: 1, timeUnit: 'm', threshold: [0.75], - comparator: Comparator.GT, + comparator: COMPARATORS.GREATER_THAN, shouldFire: true, shouldWarn: false, currentValue: 1.0, @@ -259,7 +255,7 @@ describe('The inventory threshold alert type', () => { }, }, }); - await execute(Comparator.GT, [0.75]); + await execute(COMPARATORS.GREATER_THAN, [0.75]); expect(mostRecentAction(instanceIdA).tags).toStrictEqual([ 'host-01_tag1', 'host-01_tag2', @@ -282,7 +278,7 @@ describe('The inventory threshold alert type', () => { timeSize: 1, timeUnit: 'm', threshold: [0.75], - comparator: Comparator.GT, + comparator: COMPARATORS.GREATER_THAN, shouldFire: true, shouldWarn: false, currentValue: 1.0, @@ -298,7 +294,7 @@ describe('The inventory threshold alert type', () => { timeSize: 1, timeUnit: 'm', threshold: [0.75], - comparator: Comparator.GT, + comparator: COMPARATORS.GREATER_THAN, shouldFire: true, shouldWarn: false, currentValue: 1.0, @@ -309,7 +305,7 @@ describe('The inventory threshold alert type', () => { }, }, }); - await execute(Comparator.GT, [0.75]); + await execute(COMPARATORS.GREATER_THAN, [0.75]); expect(mostRecentAction(instanceIdA).tags).toStrictEqual(['ruleTag1', 'ruleTag2']); expect(mostRecentAction(instanceIdB).tags).toStrictEqual(['ruleTag1', 'ruleTag2']); }); @@ -329,7 +325,7 @@ describe('The inventory threshold alert type', () => { timeSize: 1, timeUnit: 'm', threshold: [0.75], - comparator: Comparator.GT, + comparator: COMPARATORS.GREATER_THAN, shouldFire: true, shouldWarn: false, currentValue: 1.0, @@ -340,7 +336,7 @@ describe('The inventory threshold alert type', () => { }, }, }); - await execute(Comparator.GT, [0.75], options); + await execute(COMPARATORS.GREATER_THAN, [0.75], options); expect(evaluateConditionFn).toHaveBeenCalledWith( expect.objectContaining({ executionTimestamp: mockedEndDate, diff --git a/x-pack/plugins/observability_solution/infra/server/lib/alerting/inventory_metric_threshold/inventory_metric_threshold_executor.ts b/x-pack/plugins/observability_solution/infra/server/lib/alerting/inventory_metric_threshold/inventory_metric_threshold_executor.ts index 7124ce9db597fd..ad1056b65f0cc4 100644 --- a/x-pack/plugins/observability_solution/infra/server/lib/alerting/inventory_metric_threshold/inventory_metric_threshold_executor.ts +++ b/x-pack/plugins/observability_solution/infra/server/lib/alerting/inventory_metric_threshold/inventory_metric_threshold_executor.ts @@ -19,7 +19,7 @@ import { AlertInstanceState as AlertState, } from '@kbn/alerting-plugin/common'; import { AlertsClientError, RuleExecutorOptions, RuleTypeState } from '@kbn/alerting-plugin/server'; -import { getAlertUrl } from '@kbn/observability-plugin/common'; +import { convertToBuiltInComparators, getAlertUrl } from '@kbn/observability-plugin/common'; import { SnapshotMetricType } from '@kbn/metrics-data-access-plugin/common'; import { ObservabilityMetricsAlert } from '@kbn/alerts-as-data-utils'; import { getOriginalActionGroup } from '../../../utils/get_original_action_group'; @@ -128,7 +128,6 @@ export const createInventoryMetricThresholdExecutor = }); const indexedStartedAt = start ?? startedAt.toISOString(); - alertsClient.setAlertData({ id: UNGROUPED_FACTORY_KEY, payload: { @@ -403,7 +402,9 @@ const buildReasonWithVerboseMetricName = ( : resultItem.metric), currentValue: formatMetric(resultItem.metric, resultItem.currentValue), threshold: formatThreshold(resultItem.metric, thresholdToFormat), - comparator: useWarningThreshold ? resultItem.warningComparator! : resultItem.comparator, + comparator: useWarningThreshold + ? convertToBuiltInComparators(resultItem.warningComparator!) + : convertToBuiltInComparators(resultItem.comparator), }; return buildReason(resultWithVerboseMetricName); }; diff --git a/x-pack/plugins/observability_solution/infra/server/lib/alerting/inventory_metric_threshold/lib/create_bucket_selector.test.ts b/x-pack/plugins/observability_solution/infra/server/lib/alerting/inventory_metric_threshold/lib/create_bucket_selector.test.ts index 279793a4a4102b..a7ddc75eb30d60 100644 --- a/x-pack/plugins/observability_solution/infra/server/lib/alerting/inventory_metric_threshold/lib/create_bucket_selector.test.ts +++ b/x-pack/plugins/observability_solution/infra/server/lib/alerting/inventory_metric_threshold/lib/create_bucket_selector.test.ts @@ -4,8 +4,8 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ - -import { Comparator, InventoryMetricConditions } from '../../../../../common/alerting/metrics'; +import { COMPARATORS } from '@kbn/alerting-comparators'; +import { InventoryMetricConditions } from '../../../../../common/alerting/metrics'; import { createBucketSelector } from './create_bucket_selector'; describe('createBucketSelector', () => { @@ -15,9 +15,9 @@ describe('createBucketSelector', () => { timeSize: 5, timeUnit: 'm', threshold: [8], - comparator: Comparator.GT_OR_EQ, + comparator: COMPARATORS.GREATER_THAN_OR_EQUALS, warningThreshold: [16], - warningComparator: Comparator.LT_OR_EQ, + warningComparator: COMPARATORS.LESS_THAN_OR_EQUALS, }; expect(createBucketSelector('tx', inventoryMetricConditions)).toEqual({ selectedBucket: { diff --git a/x-pack/plugins/observability_solution/infra/server/lib/alerting/inventory_metric_threshold/lib/create_bucket_selector.ts b/x-pack/plugins/observability_solution/infra/server/lib/alerting/inventory_metric_threshold/lib/create_bucket_selector.ts index 7392446000db60..440e39ed147230 100644 --- a/x-pack/plugins/observability_solution/infra/server/lib/alerting/inventory_metric_threshold/lib/create_bucket_selector.ts +++ b/x-pack/plugins/observability_solution/infra/server/lib/alerting/inventory_metric_threshold/lib/create_bucket_selector.ts @@ -6,7 +6,8 @@ */ import { SnapshotMetricType } from '@kbn/metrics-data-access-plugin/common'; -import { Comparator, InventoryMetricConditions } from '../../../../../common/alerting/metrics'; +import { convertToBuiltInComparators } from '@kbn/observability-plugin/common'; +import { InventoryMetricConditions } from '../../../../../common/alerting/metrics'; import { SnapshotCustomMetricInput } from '../../../../../common/http_api'; import { createConditionScript } from './create_condition_script'; @@ -34,7 +35,7 @@ export const createBucketSelector = ( }, script: createConditionScript( condition.warningThreshold as number[], - condition.warningComparator as Comparator, + convertToBuiltInComparators(condition.warningComparator!), metric ), }, @@ -47,7 +48,11 @@ export const createBucketSelector = ( buckets_path: { value: metricId, }, - script: createConditionScript(condition.threshold, condition.comparator, metric), + script: createConditionScript( + condition.threshold, + convertToBuiltInComparators(condition.comparator), + metric + ), }, } : EMPTY_SHOULD_WARN; diff --git a/x-pack/plugins/observability_solution/infra/server/lib/alerting/inventory_metric_threshold/lib/create_condition_script.test.ts b/x-pack/plugins/observability_solution/infra/server/lib/alerting/inventory_metric_threshold/lib/create_condition_script.test.ts index f93989f2d4b9d9..fdc17388751538 100644 --- a/x-pack/plugins/observability_solution/infra/server/lib/alerting/inventory_metric_threshold/lib/create_condition_script.test.ts +++ b/x-pack/plugins/observability_solution/infra/server/lib/alerting/inventory_metric_threshold/lib/create_condition_script.test.ts @@ -4,13 +4,12 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ - -import { Comparator } from '../../../../../common/alerting/metrics'; +import { COMPARATORS } from '@kbn/alerting-comparators'; import { createConditionScript } from './create_condition_script'; describe('createConditionScript', () => { it('should convert tx threshold from bits to byte', () => { - expect(createConditionScript([8], Comparator.GT_OR_EQ, 'tx')).toEqual({ + expect(createConditionScript([8], COMPARATORS.GREATER_THAN_OR_EQUALS, 'tx')).toEqual({ params: { // Threshold has been converted from 8 bits to 1 byte threshold: 1, diff --git a/x-pack/plugins/observability_solution/infra/server/lib/alerting/inventory_metric_threshold/lib/create_condition_script.ts b/x-pack/plugins/observability_solution/infra/server/lib/alerting/inventory_metric_threshold/lib/create_condition_script.ts index a62f5d92dac060..21f84eb612475f 100644 --- a/x-pack/plugins/observability_solution/infra/server/lib/alerting/inventory_metric_threshold/lib/create_condition_script.ts +++ b/x-pack/plugins/observability_solution/infra/server/lib/alerting/inventory_metric_threshold/lib/create_condition_script.ts @@ -5,16 +5,16 @@ * 2.0. */ import { SnapshotMetricType } from '@kbn/metrics-data-access-plugin/common'; -import { Comparator } from '../../../../../common/alerting/metrics'; +import { COMPARATORS } from '@kbn/alerting-comparators'; import { convertMetricValue } from './convert_metric_value'; export const createConditionScript = ( conditionThresholds: number[], - comparator: Comparator, + comparator: COMPARATORS, metric: SnapshotMetricType ) => { const threshold = conditionThresholds.map((n) => convertMetricValue(metric, n)); - if (comparator === Comparator.BETWEEN && threshold.length === 2) { + if (comparator === COMPARATORS.BETWEEN && threshold.length === 2) { return { source: `params.value > params.threshold0 && params.value < params.threshold1 ? 1 : 0`, params: { @@ -23,9 +23,10 @@ export const createConditionScript = ( }, }; } - if (comparator === Comparator.OUTSIDE_RANGE && threshold.length === 2) { + + if (comparator === COMPARATORS.NOT_BETWEEN && threshold.length === 2) { return { - // OUTSIDE_RANGE/NOT BETWEEN is the opposite of BETWEEN. Use the BETWEEN condition and switch the 1 and 0 + // NOT BETWEEN is the opposite of BETWEEN. Use the BETWEEN condition and switch the 1 and 0 source: `params.value > params.threshold0 && params.value < params.threshold1 ? 0 : 1`, params: { threshold0: threshold[0], diff --git a/x-pack/plugins/observability_solution/infra/server/lib/alerting/inventory_metric_threshold/register_inventory_metric_threshold_rule_type.ts b/x-pack/plugins/observability_solution/infra/server/lib/alerting/inventory_metric_threshold/register_inventory_metric_threshold_rule_type.ts index b3ba3c5b184a9e..4f124b8327841f 100644 --- a/x-pack/plugins/observability_solution/infra/server/lib/alerting/inventory_metric_threshold/register_inventory_metric_threshold_rule_type.ts +++ b/x-pack/plugins/observability_solution/infra/server/lib/alerting/inventory_metric_threshold/register_inventory_metric_threshold_rule_type.ts @@ -16,15 +16,14 @@ import { SnapshotMetricType, SnapshotMetricTypeKeys, } from '@kbn/metrics-data-access-plugin/common'; -import type { InfraConfig } from '../../../../common/plugin_config_types'; -import { - Comparator, - METRIC_INVENTORY_THRESHOLD_ALERT_TYPE_ID, -} from '../../../../common/alerting/metrics'; +import { COMPARATORS } from '@kbn/alerting-comparators'; +import { LEGACY_COMPARATORS } from '@kbn/observability-plugin/common/utils/convert_legacy_outside_comparator'; import { SnapshotCustomAggregation, SNAPSHOT_CUSTOM_AGGREGATIONS, -} from '../../../../common/http_api/snapshot_api'; +} from '../../../../common/http_api'; +import type { InfraConfig } from '../../../../common/plugin_config_types'; +import { METRIC_INVENTORY_THRESHOLD_ALERT_TYPE_ID } from '../../../../common/alerting/metrics'; import { InfraBackendLibs } from '../../infra_types'; import { alertDetailUrlActionVariableDescription, @@ -54,16 +53,15 @@ import { import { MetricsRulesTypeAlertDefinition } from '../register_rule_types'; import { O11Y_AAD_FIELDS } from '../../../../common/constants'; +const comparators = Object.values({ ...COMPARATORS, ...LEGACY_COMPARATORS }); const condition = schema.object({ threshold: schema.arrayOf(schema.number()), - comparator: oneOfLiterals(Object.values(Comparator)) as Type, + comparator: oneOfLiterals(comparators) as Type, timeUnit: schema.string() as Type, timeSize: schema.number(), metric: oneOfLiterals(Object.keys(SnapshotMetricTypeKeys)) as Type, warningThreshold: schema.maybe(schema.arrayOf(schema.number())), - warningComparator: schema.maybe(oneOfLiterals(Object.values(Comparator))) as Type< - Comparator | undefined - >, + warningComparator: schema.maybe(oneOfLiterals(comparators)) as Type, customMetric: schema.maybe( schema.object({ type: schema.literal('custom'), diff --git a/x-pack/plugins/observability_solution/infra/server/lib/alerting/metric_threshold/lib/create_bucket_selector.ts b/x-pack/plugins/observability_solution/infra/server/lib/alerting/metric_threshold/lib/create_bucket_selector.ts index 25e725d7d0a628..2aa2ef6b5c8382 100644 --- a/x-pack/plugins/observability_solution/infra/server/lib/alerting/metric_threshold/lib/create_bucket_selector.ts +++ b/x-pack/plugins/observability_solution/infra/server/lib/alerting/metric_threshold/lib/create_bucket_selector.ts @@ -4,12 +4,8 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ - -import { - Aggregators, - Comparator, - MetricExpressionParams, -} from '../../../../../common/alerting/metrics'; +import { convertToBuiltInComparators } from '@kbn/observability-plugin/common'; +import { Aggregators, MetricExpressionParams } from '../../../../../common/alerting/metrics'; import { createConditionScript } from './create_condition_script'; import { createLastPeriod } from './wrap_in_period'; @@ -49,7 +45,7 @@ export const createBucketSelector = ( }, script: createConditionScript( condition.warningThreshold as number[], - condition.warningComparator as Comparator + convertToBuiltInComparators(condition.warningComparator!) ), }, } @@ -60,7 +56,10 @@ export const createBucketSelector = ( buckets_path: { value: bucketPath, }, - script: createConditionScript(condition.threshold, condition.comparator), + script: createConditionScript( + condition.threshold, + convertToBuiltInComparators(condition.comparator) + ), }, }; diff --git a/x-pack/plugins/observability_solution/infra/server/lib/alerting/metric_threshold/lib/create_condition_script.ts b/x-pack/plugins/observability_solution/infra/server/lib/alerting/metric_threshold/lib/create_condition_script.ts index 1320607685a875..9b56baa0dcba5c 100644 --- a/x-pack/plugins/observability_solution/infra/server/lib/alerting/metric_threshold/lib/create_condition_script.ts +++ b/x-pack/plugins/observability_solution/infra/server/lib/alerting/metric_threshold/lib/create_condition_script.ts @@ -4,10 +4,9 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ -import { Comparator } from '../../../../../common/alerting/metrics'; - -export const createConditionScript = (threshold: number[], comparator: Comparator) => { - if (comparator === Comparator.BETWEEN && threshold.length === 2) { +import { COMPARATORS } from '@kbn/alerting-comparators'; +export const createConditionScript = (threshold: number[], comparator: COMPARATORS) => { + if (comparator === COMPARATORS.BETWEEN && threshold.length === 2) { return { source: `params.value > params.threshold0 && params.value < params.threshold1 ? 1 : 0`, params: { @@ -16,9 +15,9 @@ export const createConditionScript = (threshold: number[], comparator: Comparato }, }; } - if (comparator === Comparator.OUTSIDE_RANGE && threshold.length === 2) { + if (comparator === COMPARATORS.NOT_BETWEEN && threshold.length === 2) { return { - // OUTSIDE_RANGE/NOT BETWEEN is the opposite of BETWEEN. Use the BETWEEN condition and switch the 1 and 0 + // NOT BETWEEN is the opposite of BETWEEN. Use the BETWEEN condition and switch the 1 and 0 source: `params.value > params.threshold0 && params.value < params.threshold1 ? 0 : 1`, params: { threshold0: threshold[0], diff --git a/x-pack/plugins/observability_solution/infra/server/lib/alerting/metric_threshold/lib/get_data.ts b/x-pack/plugins/observability_solution/infra/server/lib/alerting/metric_threshold/lib/get_data.ts index 0053d41795d42d..7e97c595496244 100644 --- a/x-pack/plugins/observability_solution/infra/server/lib/alerting/metric_threshold/lib/get_data.ts +++ b/x-pack/plugins/observability_solution/infra/server/lib/alerting/metric_threshold/lib/get_data.ts @@ -9,11 +9,9 @@ import { SearchResponse, AggregationsAggregate } from '@elastic/elasticsearch/li import { ElasticsearchClient } from '@kbn/core/server'; import type { Logger } from '@kbn/logging'; import { EcsFieldsResponse } from '@kbn/rule-registry-plugin/common/search_strategy'; -import { - Aggregators, - Comparator, - MetricExpressionParams, -} from '../../../../../common/alerting/metrics'; +import { COMPARATORS } from '@kbn/alerting-comparators'; +import { convertToBuiltInComparators } from '@kbn/observability-plugin/common'; +import { Aggregators, MetricExpressionParams } from '../../../../../common/alerting/metrics'; import { AdditionalContext, doFieldsExist, @@ -226,10 +224,16 @@ export const getData = async ( // the value will end up being ZERO, for other metrics it will be null. In this case // we need to do the evaluation in Node.js if (aggs.all && params.aggType === Aggregators.COUNT && value === 0) { - const trigger = comparatorMap[params.comparator](value, params.threshold); + const trigger = comparatorMap[convertToBuiltInComparators(params.comparator)]( + value, + params.threshold + ); const warn = params.warningThreshold && params.warningComparator - ? comparatorMap[params.warningComparator](value, params.warningThreshold) + ? comparatorMap[convertToBuiltInComparators(params.warningComparator)]( + value, + params.warningThreshold + ) : false; return { [UNGROUPED_FACTORY_KEY]: { @@ -286,13 +290,13 @@ export const getData = async ( }; const comparatorMap = { - [Comparator.BETWEEN]: (value: number, [a, b]: number[]) => + [COMPARATORS.BETWEEN]: (value: number, [a, b]: number[]) => value >= Math.min(a, b) && value <= Math.max(a, b), // `threshold` is always an array of numbers in case the BETWEEN comparator is // used; all other compartors will just destructure the first value in the array - [Comparator.GT]: (a: number, [b]: number[]) => a > b, - [Comparator.LT]: (a: number, [b]: number[]) => a < b, - [Comparator.OUTSIDE_RANGE]: (value: number, [a, b]: number[]) => value < a || value > b, - [Comparator.GT_OR_EQ]: (a: number, [b]: number[]) => a >= b, - [Comparator.LT_OR_EQ]: (a: number, [b]: number[]) => a <= b, + [COMPARATORS.GREATER_THAN]: (a: number, [b]: number[]) => a > b, + [COMPARATORS.LESS_THAN]: (a: number, [b]: number[]) => a < b, + [COMPARATORS.NOT_BETWEEN]: (value: number, [a, b]: number[]) => value < a || value > b, + [COMPARATORS.GREATER_THAN_OR_EQUALS]: (a: number, [b]: number[]) => a >= b, + [COMPARATORS.LESS_THAN_OR_EQUALS]: (a: number, [b]: number[]) => a <= b, }; diff --git a/x-pack/plugins/observability_solution/infra/server/lib/alerting/metric_threshold/lib/metric_query.test.ts b/x-pack/plugins/observability_solution/infra/server/lib/alerting/metric_threshold/lib/metric_query.test.ts index 381465fa198d95..a32c924385dbcf 100644 --- a/x-pack/plugins/observability_solution/infra/server/lib/alerting/metric_threshold/lib/metric_query.test.ts +++ b/x-pack/plugins/observability_solution/infra/server/lib/alerting/metric_threshold/lib/metric_query.test.ts @@ -6,11 +6,8 @@ */ import moment from 'moment'; -import { - Aggregators, - Comparator, - MetricExpressionParams, -} from '../../../../../common/alerting/metrics'; +import { COMPARATORS } from '@kbn/alerting-comparators'; +import { Aggregators, MetricExpressionParams } from '../../../../../common/alerting/metrics'; import { getElasticsearchMetricQuery } from './metric_query'; describe("The Metric Threshold Alert's getElasticsearchMetricQuery", () => { @@ -20,7 +17,7 @@ describe("The Metric Threshold Alert's getElasticsearchMetricQuery", () => { timeUnit: 'm', timeSize: 1, threshold: [1], - comparator: Comparator.GT, + comparator: COMPARATORS.GREATER_THAN, }; const groupBy = 'host.doggoname'; diff --git a/x-pack/plugins/observability_solution/infra/server/lib/alerting/metric_threshold/metric_threshold_executor.test.ts b/x-pack/plugins/observability_solution/infra/server/lib/alerting/metric_threshold/metric_threshold_executor.test.ts index eeb7f61b99ddfe..9cc5712ce09b60 100644 --- a/x-pack/plugins/observability_solution/infra/server/lib/alerting/metric_threshold/metric_threshold_executor.test.ts +++ b/x-pack/plugins/observability_solution/infra/server/lib/alerting/metric_threshold/metric_threshold_executor.test.ts @@ -8,9 +8,9 @@ import { alertsMock } from '@kbn/alerting-plugin/server/mocks'; import { getThresholds } from '../common/get_values'; import { set } from '@kbn/safer-lodash-set'; +import { COMPARATORS } from '@kbn/alerting-comparators'; import { Aggregators, - Comparator, CountMetricExpressionParams, NonCountMetricExpressionParams, } from '../../../../common/alerting/metrics'; @@ -109,7 +109,7 @@ describe('The metric threshold rule type', () => { afterAll(() => jest.useRealTimers()); describe('querying the entire infrastructure', () => { - const execute = (comparator: Comparator, threshold: number[], sourceId: string = 'default') => + const execute = (comparator: COMPARATORS, threshold: number[], sourceId: string = 'default') => executor({ ...mockOptions, services, @@ -125,7 +125,7 @@ describe('The metric threshold rule type', () => { }, }); const setResults = ( - comparator: Comparator, + comparator: COMPARATORS, threshold: number[], shouldFire: boolean = false, shouldWarn: boolean = false, @@ -149,8 +149,8 @@ describe('The metric threshold rule type', () => { ]); test('should report alert with the > comparator when condition is met', async () => { - setResults(Comparator.GT, [0.75], true); - await execute(Comparator.GT, [0.75]); + setResults(COMPARATORS.GREATER_THAN, [0.75], true); + await execute(COMPARATORS.GREATER_THAN, [0.75]); testNAlertsReported(1); testAlertReported(1, { id: '*', @@ -159,20 +159,20 @@ describe('The metric threshold rule type', () => { ], actionGroup: FIRED_ACTIONS.id, alertState: 'ALERT', - reason: 'test.metric.1 is 1 in the last 1 min. Alert when > 0.75.', + reason: 'test.metric.1 is 1 in the last 1 min. Alert when above 0.75.', tags: [], }); }); test('should not report any alerts with the > comparator when condition is not met', async () => { - setResults(Comparator.GT, [1.5], false); - await execute(Comparator.GT, [1.5]); + setResults(COMPARATORS.GREATER_THAN, [1.5], false); + await execute(COMPARATORS.GREATER_THAN, [1.5]); testNAlertsReported(0); }); test('should report alert with the < comparator when condition is met', async () => { - setResults(Comparator.LT, [1.5], true); - await execute(Comparator.LT, [1.5]); + setResults(COMPARATORS.LESS_THAN, [1.5], true); + await execute(COMPARATORS.LESS_THAN, [1.5]); testNAlertsReported(1); testAlertReported(1, { id: '*', @@ -181,20 +181,20 @@ describe('The metric threshold rule type', () => { ], actionGroup: FIRED_ACTIONS.id, alertState: 'ALERT', - reason: 'test.metric.1 is 1 in the last 1 min. Alert when < 1.5.', + reason: 'test.metric.1 is 1 in the last 1 min. Alert when below 1.5.', tags: [], }); }); test('should not report any alerts with the < comparator when condition is not met', async () => { - setResults(Comparator.LT, [0.75], false); - await execute(Comparator.LT, [0.75]); + setResults(COMPARATORS.LESS_THAN, [0.75], false); + await execute(COMPARATORS.LESS_THAN, [0.75]); testNAlertsReported(0); }); test('should report alert with the >= comparator when condition is met', async () => { - setResults(Comparator.GT_OR_EQ, [0.75], true); - await execute(Comparator.GT_OR_EQ, [0.75]); + setResults(COMPARATORS.GREATER_THAN_OR_EQUALS, [0.75], true); + await execute(COMPARATORS.GREATER_THAN_OR_EQUALS, [0.75]); testNAlertsReported(1); testAlertReported(1, { id: '*', @@ -203,20 +203,20 @@ describe('The metric threshold rule type', () => { ], actionGroup: FIRED_ACTIONS.id, alertState: 'ALERT', - reason: 'test.metric.1 is 1 in the last 1 min. Alert when >= 0.75.', + reason: 'test.metric.1 is 1 in the last 1 min. Alert when above or equal 0.75.', tags: [], }); }); test('should not report any alerts with the >= comparator when condition is not met', async () => { - setResults(Comparator.GT_OR_EQ, [1.5], false); - await execute(Comparator.GT_OR_EQ, [1.5]); + setResults(COMPARATORS.GREATER_THAN_OR_EQUALS, [1.5], false); + await execute(COMPARATORS.GREATER_THAN_OR_EQUALS, [1.5]); testNAlertsReported(0); }); test('should report alert with the <= comparator when condition is met', async () => { - setResults(Comparator.LT_OR_EQ, [1.5], true); - await execute(Comparator.LT_OR_EQ, [1.5]); + setResults(COMPARATORS.LESS_THAN_OR_EQUALS, [1.5], true); + await execute(COMPARATORS.LESS_THAN_OR_EQUALS, [1.5]); testNAlertsReported(1); testAlertReported(1, { id: '*', @@ -225,20 +225,20 @@ describe('The metric threshold rule type', () => { ], actionGroup: FIRED_ACTIONS.id, alertState: 'ALERT', - reason: 'test.metric.1 is 1 in the last 1 min. Alert when <= 1.5.', + reason: 'test.metric.1 is 1 in the last 1 min. Alert when below or equal 1.5.', tags: [], }); }); test('should not report any alerts with the <= comparator when condition is not met', async () => { - setResults(Comparator.LT_OR_EQ, [0.75], false); - await execute(Comparator.LT_OR_EQ, [0.75]); + setResults(COMPARATORS.LESS_THAN_OR_EQUALS, [0.75], false); + await execute(COMPARATORS.LESS_THAN_OR_EQUALS, [0.75]); testNAlertsReported(0); }); test('should report alert with the between comparator when condition is met', async () => { - setResults(Comparator.BETWEEN, [0, 1.5], true); - await execute(Comparator.BETWEEN, [0, 1.5]); + setResults(COMPARATORS.BETWEEN, [0, 1.5], true); + await execute(COMPARATORS.BETWEEN, [0, 1.5]); testNAlertsReported(1); testAlertReported(1, { id: '*', @@ -253,14 +253,14 @@ describe('The metric threshold rule type', () => { }); test('should not report any alerts with the between comparator when condition is not met', async () => { - setResults(Comparator.BETWEEN, [0, 0.75], false); - await execute(Comparator.BETWEEN, [0, 0.75]); + setResults(COMPARATORS.BETWEEN, [0, 0.75], false); + await execute(COMPARATORS.BETWEEN, [0, 0.75]); testNAlertsReported(0); }); test('should report alert with the outside range comparator when condition is met', async () => { - setResults(Comparator.OUTSIDE_RANGE, [0, 0.75], true); - await execute(Comparator.OUTSIDE_RANGE, [0, 0.75]); + setResults(COMPARATORS.NOT_BETWEEN, [0, 0.75], true); + await execute(COMPARATORS.NOT_BETWEEN, [0, 0.75]); testNAlertsReported(1); testAlertReported(1, { id: '*', @@ -269,21 +269,21 @@ describe('The metric threshold rule type', () => { ], actionGroup: FIRED_ACTIONS.id, alertState: 'ALERT', - reason: 'test.metric.1 is 1 in the last 1 min. Alert when outside 0 and 0.75.', + reason: 'test.metric.1 is 1 in the last 1 min. Alert when not between 0 and 0.75.', tags: [], }); }); test('should not report any alerts with the outside range comparator when condition is not met', async () => { - setResults(Comparator.OUTSIDE_RANGE, [0, 1.5], false); - await execute(Comparator.OUTSIDE_RANGE, [0, 1.5]); + setResults(COMPARATORS.NOT_BETWEEN, [0, 1.5], false); + await execute(COMPARATORS.NOT_BETWEEN, [0, 1.5]); testNAlertsReported(0); }); }); describe('querying with a groupBy parameter', () => { const execute = ( - comparator: Comparator, + comparator: COMPARATORS, threshold: number[], groupBy: string[] = ['something'], metric?: string, @@ -313,7 +313,7 @@ describe('The metric threshold rule type', () => { { a: { ...baseNonCountCriterion, - comparator: Comparator.GT, + comparator: COMPARATORS.GREATER_THAN, threshold: [0.75], metric: 'test.metric.1', currentValue: 1.0, @@ -325,7 +325,7 @@ describe('The metric threshold rule type', () => { }, b: { ...baseNonCountCriterion, - comparator: Comparator.GT, + comparator: COMPARATORS.GREATER_THAN, threshold: [0.75], metric: 'test.metric.1', currentValue: 1.0, @@ -337,7 +337,7 @@ describe('The metric threshold rule type', () => { }, }, ]); - await execute(Comparator.GT, [0.75]); + await execute(COMPARATORS.GREATER_THAN, [0.75]); testNAlertsReported(2); testAlertReported(1, { id: alertIdA, @@ -346,7 +346,7 @@ describe('The metric threshold rule type', () => { ], actionGroup: FIRED_ACTIONS.id, alertState: 'ALERT', - reason: 'test.metric.1 is 1 in the last 1 min for a. Alert when > 0.75.', + reason: 'test.metric.1 is 1 in the last 1 min for a. Alert when above 0.75.', tags: [], groupByKeys: { something: alertIdA }, }); @@ -357,7 +357,7 @@ describe('The metric threshold rule type', () => { ], actionGroup: FIRED_ACTIONS.id, alertState: 'ALERT', - reason: 'test.metric.1 is 1 in the last 1 min for b. Alert when > 0.75.', + reason: 'test.metric.1 is 1 in the last 1 min for b. Alert when above 0.75.', tags: [], groupByKeys: { something: alertIdB }, }); @@ -368,7 +368,7 @@ describe('The metric threshold rule type', () => { { a: { ...baseNonCountCriterion, - comparator: Comparator.LT, + comparator: COMPARATORS.LESS_THAN, threshold: [1.5], metric: 'test.metric.1', currentValue: 1.0, @@ -380,7 +380,7 @@ describe('The metric threshold rule type', () => { }, b: { ...baseNonCountCriterion, - comparator: Comparator.LT, + comparator: COMPARATORS.LESS_THAN, threshold: [1.5], metric: 'test.metric.1', currentValue: 3, @@ -392,7 +392,7 @@ describe('The metric threshold rule type', () => { }, }, ]); - await execute(Comparator.LT, [1.5]); + await execute(COMPARATORS.LESS_THAN, [1.5]); testNAlertsReported(1); testAlertReported(1, { id: alertIdA, @@ -401,7 +401,7 @@ describe('The metric threshold rule type', () => { ], actionGroup: FIRED_ACTIONS.id, alertState: 'ALERT', - reason: 'test.metric.1 is 1 in the last 1 min for a. Alert when < 1.5.', + reason: 'test.metric.1 is 1 in the last 1 min for a. Alert when below 1.5.', tags: [], groupByKeys: { something: alertIdA }, }); @@ -412,7 +412,7 @@ describe('The metric threshold rule type', () => { { a: { ...baseNonCountCriterion, - comparator: Comparator.GT, + comparator: COMPARATORS.GREATER_THAN, threshold: [5], metric: 'test.metric.1', currentValue: 1.0, @@ -424,7 +424,7 @@ describe('The metric threshold rule type', () => { }, b: { ...baseNonCountCriterion, - comparator: Comparator.GT, + comparator: COMPARATORS.GREATER_THAN, threshold: [5], metric: 'test.metric.1', currentValue: 3, @@ -436,7 +436,7 @@ describe('The metric threshold rule type', () => { }, }, ]); - await execute(Comparator.GT, [5]); + await execute(COMPARATORS.GREATER_THAN, [5]); testNAlertsReported(0); }); @@ -445,7 +445,7 @@ describe('The metric threshold rule type', () => { { a: { ...baseNonCountCriterion, - comparator: Comparator.GT, + comparator: COMPARATORS.GREATER_THAN, threshold: [0.75], metric: 'test.metric.2', currentValue: 1.0, @@ -457,7 +457,7 @@ describe('The metric threshold rule type', () => { }, b: { ...baseNonCountCriterion, - comparator: Comparator.GT, + comparator: COMPARATORS.GREATER_THAN, threshold: [0.75], metric: 'test.metric.2', currentValue: 3, @@ -469,7 +469,7 @@ describe('The metric threshold rule type', () => { }, c: { ...baseNonCountCriterion, - comparator: Comparator.GT, + comparator: COMPARATORS.GREATER_THAN, threshold: [0.75], metric: 'test.metric.2', currentValue: 3, @@ -482,7 +482,7 @@ describe('The metric threshold rule type', () => { }, ]); const { state: stateResult1 } = await execute( - Comparator.GT, + COMPARATORS.GREATER_THAN, [0.75], ['something'], 'test.metric.2' @@ -492,7 +492,7 @@ describe('The metric threshold rule type', () => { { a: { ...baseNonCountCriterion, - comparator: Comparator.GT, + comparator: COMPARATORS.GREATER_THAN, threshold: [0.75], metric: 'test.metric.1', currentValue: 1.0, @@ -504,7 +504,7 @@ describe('The metric threshold rule type', () => { }, b: { ...baseNonCountCriterion, - comparator: Comparator.GT, + comparator: COMPARATORS.GREATER_THAN, threshold: [0.75], metric: 'test.metric.1', currentValue: 3, @@ -516,7 +516,7 @@ describe('The metric threshold rule type', () => { }, c: { ...baseNonCountCriterion, - comparator: Comparator.GT, + comparator: COMPARATORS.GREATER_THAN, threshold: [0.75], metric: 'test.metric.1', currentValue: null, @@ -529,7 +529,7 @@ describe('The metric threshold rule type', () => { }, ]); const { state: stateResult2 } = await execute( - Comparator.GT, + COMPARATORS.GREATER_THAN, [0.75], ['something'], 'test.metric.1', @@ -542,7 +542,7 @@ describe('The metric threshold rule type', () => { { a: { ...baseNonCountCriterion, - comparator: Comparator.GT, + comparator: COMPARATORS.GREATER_THAN, threshold: [0.75], metric: 'test.metric.1', currentValue: 1.0, @@ -554,7 +554,7 @@ describe('The metric threshold rule type', () => { }, b: { ...baseNonCountCriterion, - comparator: Comparator.GT, + comparator: COMPARATORS.GREATER_THAN, threshold: [0.75], metric: 'test.metric.1', currentValue: 3, @@ -567,7 +567,7 @@ describe('The metric threshold rule type', () => { }, ]); const { state: stateResult3 } = await execute( - Comparator.GT, + COMPARATORS.GREATER_THAN, [0.75], ['something', 'something-else'], 'test.metric.1', @@ -577,7 +577,7 @@ describe('The metric threshold rule type', () => { }); const executeWithFilter = ( - comparator: Comparator, + comparator: COMPARATORS, threshold: number[], filterQuery: string, metric?: string, @@ -606,7 +606,7 @@ describe('The metric threshold rule type', () => { { a: { ...baseNonCountCriterion, - comparator: Comparator.GT, + comparator: COMPARATORS.GREATER_THAN, threshold: [0.75], metric: 'test.metric.2', currentValue: 1.0, @@ -618,7 +618,7 @@ describe('The metric threshold rule type', () => { }, b: { ...baseNonCountCriterion, - comparator: Comparator.GT, + comparator: COMPARATORS.GREATER_THAN, threshold: [0.75], metric: 'test.metric.2', currentValue: 3, @@ -630,7 +630,7 @@ describe('The metric threshold rule type', () => { }, c: { ...baseNonCountCriterion, - comparator: Comparator.GT, + comparator: COMPARATORS.GREATER_THAN, threshold: [0.75], metric: 'test.metric.2', currentValue: 3, @@ -643,7 +643,7 @@ describe('The metric threshold rule type', () => { }, ]); const { state: stateResult1 } = await executeWithFilter( - Comparator.GT, + COMPARATORS.GREATER_THAN, [0.75], JSON.stringify({ query: 'q' }), 'test.metric.2' @@ -653,7 +653,7 @@ describe('The metric threshold rule type', () => { { a: { ...baseNonCountCriterion, - comparator: Comparator.GT, + comparator: COMPARATORS.GREATER_THAN, threshold: [0.75], metric: 'test.metric.1', currentValue: 1.0, @@ -665,7 +665,7 @@ describe('The metric threshold rule type', () => { }, b: { ...baseNonCountCriterion, - comparator: Comparator.GT, + comparator: COMPARATORS.GREATER_THAN, threshold: [0.75], metric: 'test.metric.1', currentValue: 3, @@ -677,7 +677,7 @@ describe('The metric threshold rule type', () => { }, c: { ...baseNonCountCriterion, - comparator: Comparator.GT, + comparator: COMPARATORS.GREATER_THAN, threshold: [0.75], metric: 'test.metric.1', currentValue: null, @@ -690,7 +690,7 @@ describe('The metric threshold rule type', () => { }, ]); const { state: stateResult2 } = await executeWithFilter( - Comparator.GT, + COMPARATORS.GREATER_THAN, [0.75], JSON.stringify({ query: 'q' }), 'test.metric.1', @@ -703,7 +703,7 @@ describe('The metric threshold rule type', () => { { a: { ...baseNonCountCriterion, - comparator: Comparator.GT, + comparator: COMPARATORS.GREATER_THAN, threshold: [0.75], metric: 'test.metric.1', currentValue: 1.0, @@ -715,7 +715,7 @@ describe('The metric threshold rule type', () => { }, b: { ...baseNonCountCriterion, - comparator: Comparator.GT, + comparator: COMPARATORS.GREATER_THAN, threshold: [0.75], metric: 'test.metric.1', currentValue: 3, @@ -728,7 +728,7 @@ describe('The metric threshold rule type', () => { }, ]); const { state: stateResult3 } = await executeWithFilter( - Comparator.GT, + COMPARATORS.GREATER_THAN, [0.75], JSON.stringify({ query: 'different' }), 'test.metric.1', @@ -742,7 +742,7 @@ describe('The metric threshold rule type', () => { { a: { ...baseNonCountCriterion, - comparator: Comparator.GT, + comparator: COMPARATORS.GREATER_THAN, threshold: [0.75], metric: 'test.metric.2', currentValue: 1.0, @@ -754,7 +754,7 @@ describe('The metric threshold rule type', () => { }, b: { ...baseNonCountCriterion, - comparator: Comparator.GT, + comparator: COMPARATORS.GREATER_THAN, threshold: [0.75], metric: 'test.metric.2', currentValue: 3, @@ -766,7 +766,7 @@ describe('The metric threshold rule type', () => { }, c: { ...baseNonCountCriterion, - comparator: Comparator.GT, + comparator: COMPARATORS.GREATER_THAN, threshold: [0.75], metric: 'test.metric.2', currentValue: 3, @@ -779,7 +779,7 @@ describe('The metric threshold rule type', () => { }, ]); const { state: stateResult1 } = await executeWithFilter( - Comparator.GT, + COMPARATORS.GREATER_THAN, [0.75], JSON.stringify({ query: 'q' }), 'test.metric.2' @@ -789,7 +789,7 @@ describe('The metric threshold rule type', () => { { a: { ...baseNonCountCriterion, - comparator: Comparator.GT, + comparator: COMPARATORS.GREATER_THAN, threshold: [0.75], metric: 'test.metric.1', currentValue: 1.0, @@ -801,7 +801,7 @@ describe('The metric threshold rule type', () => { }, b: { ...baseNonCountCriterion, - comparator: Comparator.GT, + comparator: COMPARATORS.GREATER_THAN, threshold: [0.75], metric: 'test.metric.1', currentValue: null, @@ -813,7 +813,7 @@ describe('The metric threshold rule type', () => { }, c: { ...baseNonCountCriterion, - comparator: Comparator.GT, + comparator: COMPARATORS.GREATER_THAN, threshold: [0.75], metric: 'test.metric.1', currentValue: null, @@ -826,7 +826,7 @@ describe('The metric threshold rule type', () => { }, ]); const { state: stateResult2 } = await executeWithFilter( - Comparator.GT, + COMPARATORS.GREATER_THAN, [0.75], JSON.stringify({ query: 'q' }), 'test.metric.1', @@ -840,7 +840,7 @@ describe('The metric threshold rule type', () => { { a: { ...baseNonCountCriterion, - comparator: Comparator.GT, + comparator: COMPARATORS.GREATER_THAN, threshold: [0.75], metric: 'test.metric.1', currentValue: 1.0, @@ -852,7 +852,7 @@ describe('The metric threshold rule type', () => { }, b: { ...baseNonCountCriterion, - comparator: Comparator.GT, + comparator: COMPARATORS.GREATER_THAN, threshold: [0.75], metric: 'test.metric.1', currentValue: null, @@ -867,7 +867,7 @@ describe('The metric threshold rule type', () => { // Consider c as untracked services.alertsClient.isTrackedAlert.mockImplementation((id: string) => id !== 'c'); const { state: stateResult3 } = await executeWithFilter( - Comparator.GT, + COMPARATORS.GREATER_THAN, [0.75], JSON.stringify({ query: 'q' }), 'test.metric.1', @@ -882,7 +882,7 @@ describe('The metric threshold rule type', () => { describe('querying with a groupBy parameter host.name and rule tags', () => { const execute = ( - comparator: Comparator, + comparator: COMPARATORS, threshold: number[], groupBy: string[] = ['host.name'], metric?: string, @@ -916,7 +916,7 @@ describe('The metric threshold rule type', () => { { 'host-01': { ...baseNonCountCriterion, - comparator: Comparator.GT, + comparator: COMPARATORS.GREATER_THAN, threshold: [0.75], metric: 'test.metric.1', currentValue: 1.0, @@ -931,7 +931,7 @@ describe('The metric threshold rule type', () => { }, 'host-02': { ...baseNonCountCriterion, - comparator: Comparator.GT, + comparator: COMPARATORS.GREATER_THAN, threshold: [0.75], metric: 'test.metric.1', currentValue: 3, @@ -946,7 +946,7 @@ describe('The metric threshold rule type', () => { }, }, ]); - await execute(Comparator.GT, [0.75]); + await execute(COMPARATORS.GREATER_THAN, [0.75]); testNAlertsReported(2); testAlertReported(1, { id: alertIdA, @@ -955,7 +955,7 @@ describe('The metric threshold rule type', () => { ], actionGroup: FIRED_ACTIONS.id, alertState: 'ALERT', - reason: 'test.metric.1 is 1 in the last 1 min for host-01. Alert when > 0.75.', + reason: 'test.metric.1 is 1 in the last 1 min for host-01. Alert when above 0.75.', tags: ['host-01_tag1', 'host-01_tag2', 'ruleTag1', 'ruleTag2'], groupByKeys: { host: { name: alertIdA } }, group: [{ field: 'host.name', value: alertIdA }], @@ -967,7 +967,7 @@ describe('The metric threshold rule type', () => { ], actionGroup: FIRED_ACTIONS.id, alertState: 'ALERT', - reason: 'test.metric.1 is 3 in the last 1 min for host-02. Alert when > 0.75.', + reason: 'test.metric.1 is 3 in the last 1 min for host-02. Alert when above 0.75.', tags: ['host-02_tag1', 'host-02_tag2', 'ruleTag1', 'ruleTag2'], groupByKeys: { host: { name: alertIdB } }, group: [{ field: 'host.name', value: alertIdB }], @@ -977,7 +977,7 @@ describe('The metric threshold rule type', () => { describe('querying without a groupBy parameter and rule tags', () => { const execute = ( - comparator: Comparator, + comparator: COMPARATORS, threshold: number[], groupBy: string = '', metric?: string, @@ -1009,7 +1009,7 @@ describe('The metric threshold rule type', () => { { '*': { ...baseNonCountCriterion, - comparator: Comparator.GT, + comparator: COMPARATORS.GREATER_THAN, threshold: [0.75], metric: 'test.metric.1', currentValue: 1.0, @@ -1023,7 +1023,7 @@ describe('The metric threshold rule type', () => { ]); const alertID = '*'; - await execute(Comparator.GT, [0.75]); + await execute(COMPARATORS.GREATER_THAN, [0.75]); testNAlertsReported(1); testAlertReported(1, { id: alertID, @@ -1032,7 +1032,7 @@ describe('The metric threshold rule type', () => { ], actionGroup: FIRED_ACTIONS.id, alertState: 'ALERT', - reason: 'test.metric.1 is 1 in the last 1 min. Alert when > 0.75.', + reason: 'test.metric.1 is 1 in the last 1 min. Alert when above 0.75.', tags: ['ruleTag1', 'ruleTag2'], }); }); @@ -1040,7 +1040,7 @@ describe('The metric threshold rule type', () => { describe('querying with multiple criteria', () => { const execute = ( - comparator: Comparator, + comparator: COMPARATORS, thresholdA: number[], thresholdB: number[], groupBy: string = '', @@ -1073,7 +1073,7 @@ describe('The metric threshold rule type', () => { { '*': { ...baseNonCountCriterion, - comparator: Comparator.GT_OR_EQ, + comparator: COMPARATORS.GREATER_THAN_OR_EQUALS, threshold: [1.0], metric: 'test.metric.1', currentValue: 1.0, @@ -1087,7 +1087,7 @@ describe('The metric threshold rule type', () => { { '*': { ...baseNonCountCriterion, - comparator: Comparator.GT_OR_EQ, + comparator: COMPARATORS.GREATER_THAN_OR_EQUALS, threshold: [3.0], metric: 'test.metric.2', currentValue: 3.0, @@ -1100,7 +1100,7 @@ describe('The metric threshold rule type', () => { }, ]); const alertID = '*'; - await execute(Comparator.GT_OR_EQ, [1.0], [3.0]); + await execute(COMPARATORS.GREATER_THAN_OR_EQUALS, [1.0], [3.0]); testNAlertsReported(1); testAlertReported(1, { id: alertID, @@ -1111,7 +1111,7 @@ describe('The metric threshold rule type', () => { actionGroup: FIRED_ACTIONS.id, alertState: 'ALERT', reason: - 'test.metric.1 is 1 in the last 1 min. Alert when >= 1.\ntest.metric.2 is 3 in the last 1 min. Alert when >= 3.', + 'test.metric.1 is 1 in the last 1 min. Alert when above or equal 1.\ntest.metric.2 is 3 in the last 1 min. Alert when above or equal 3.', tags: [], }); }); @@ -1121,7 +1121,7 @@ describe('The metric threshold rule type', () => { { '*': { ...baseNonCountCriterion, - comparator: Comparator.LT_OR_EQ, + comparator: COMPARATORS.LESS_THAN_OR_EQUALS, threshold: [1.0], metric: 'test.metric.1', currentValue: 1.0, @@ -1134,7 +1134,7 @@ describe('The metric threshold rule type', () => { }, {}, ]); - await execute(Comparator.LT_OR_EQ, [1.0], [2.5]); + await execute(COMPARATORS.LESS_THAN_OR_EQUALS, [1.0], [2.5]); testNAlertsReported(0); }); @@ -1143,7 +1143,7 @@ describe('The metric threshold rule type', () => { { a: { ...baseNonCountCriterion, - comparator: Comparator.GT_OR_EQ, + comparator: COMPARATORS.GREATER_THAN_OR_EQUALS, threshold: [1.0], metric: 'test.metric.1', currentValue: 1.0, @@ -1155,7 +1155,7 @@ describe('The metric threshold rule type', () => { }, b: { ...baseNonCountCriterion, - comparator: Comparator.GT_OR_EQ, + comparator: COMPARATORS.GREATER_THAN_OR_EQUALS, threshold: [1.0], metric: 'test.metric.1', currentValue: 3.0, @@ -1169,7 +1169,7 @@ describe('The metric threshold rule type', () => { { a: { ...baseNonCountCriterion, - comparator: Comparator.GT_OR_EQ, + comparator: COMPARATORS.GREATER_THAN_OR_EQUALS, threshold: [3.0], metric: 'test.metric.2', currentValue: 3.0, @@ -1181,7 +1181,7 @@ describe('The metric threshold rule type', () => { }, b: { ...baseNonCountCriterion, - comparator: Comparator.GT_OR_EQ, + comparator: COMPARATORS.GREATER_THAN_OR_EQUALS, threshold: [3.0], metric: 'test.metric.2', currentValue: 1.0, @@ -1194,7 +1194,7 @@ describe('The metric threshold rule type', () => { }, ]); const alertIdA = 'a'; - await execute(Comparator.GT_OR_EQ, [1.0], [3.0], 'something'); + await execute(COMPARATORS.GREATER_THAN_OR_EQUALS, [1.0], [3.0], 'something'); testNAlertsReported(1); testAlertReported(1, { id: alertIdA, @@ -1205,7 +1205,7 @@ describe('The metric threshold rule type', () => { actionGroup: FIRED_ACTIONS.id, alertState: 'ALERT', reason: - 'test.metric.1 is 1 in the last 1 min for a. Alert when >= 1.\ntest.metric.2 is 3 in the last 1 min for a. Alert when >= 3.', + 'test.metric.1 is 1 in the last 1 min for a. Alert when above or equal 1.\ntest.metric.2 is 3 in the last 1 min for a. Alert when above or equal 3.', tags: [], groupByKeys: { something: alertIdA }, }); @@ -1214,7 +1214,7 @@ describe('The metric threshold rule type', () => { describe('querying with the count aggregator', () => { const alertID = '*'; - const execute = (comparator: Comparator, threshold: number[], sourceId: string = 'default') => + const execute = (comparator: COMPARATORS, threshold: number[], sourceId: string = 'default') => executor({ ...mockOptions, services, @@ -1235,7 +1235,7 @@ describe('The metric threshold rule type', () => { { '*': { ...baseCountCriterion, - comparator: Comparator.GT, + comparator: COMPARATORS.GREATER_THAN, threshold: [0.9], metric: 'count', currentValue: 1, @@ -1247,14 +1247,14 @@ describe('The metric threshold rule type', () => { }, }, ]); - await execute(Comparator.GT, [0.9]); + await execute(COMPARATORS.GREATER_THAN, [0.9]); testNAlertsReported(1); testAlertReported(1, { id: alertID, conditions: [{ metric: 'count', threshold: [0.9], value: '1', evaluation_value: 1 }], actionGroup: FIRED_ACTIONS.id, alertState: 'ALERT', - reason: 'count is 1 in the last 1 min. Alert when > 0.9.', + reason: 'count is 1 in the last 1 min. Alert when above 0.9.', tags: [], }); @@ -1262,7 +1262,7 @@ describe('The metric threshold rule type', () => { { '*': { ...baseCountCriterion, - comparator: Comparator.LT, + comparator: COMPARATORS.LESS_THAN, threshold: [0.5], metric: 'count', currentValue: 1, @@ -1274,14 +1274,14 @@ describe('The metric threshold rule type', () => { }, }, ]); - await execute(Comparator.LT, [0.5]); + await execute(COMPARATORS.LESS_THAN, [0.5]); // should still have only been called once testNAlertsReported(1); }); describe('with a groupBy parameter', () => { const executeGroupBy = ( - comparator: Comparator, + comparator: COMPARATORS, threshold: number[], sourceId: string = 'default', state?: any @@ -1310,7 +1310,7 @@ describe('The metric threshold rule type', () => { { a: { ...baseCountCriterion, - comparator: Comparator.LT_OR_EQ, + comparator: COMPARATORS.LESS_THAN_OR_EQUALS, threshold: [0], metric: 'count', currentValue: 1, @@ -1322,7 +1322,7 @@ describe('The metric threshold rule type', () => { }, b: { ...baseCountCriterion, - comparator: Comparator.LT_OR_EQ, + comparator: COMPARATORS.LESS_THAN_OR_EQUALS, threshold: [0], metric: 'count', currentValue: 1, @@ -1334,13 +1334,13 @@ describe('The metric threshold rule type', () => { }, }, ]); - const resultState = await executeGroupBy(Comparator.LT_OR_EQ, [0]); + const resultState = await executeGroupBy(COMPARATORS.LESS_THAN_OR_EQUALS, [0]); testNAlertsReported(0); setEvaluationResults([ { a: { ...baseCountCriterion, - comparator: Comparator.LT_OR_EQ, + comparator: COMPARATORS.LESS_THAN_OR_EQUALS, threshold: [0], metric: 'count', currentValue: 0, @@ -1352,7 +1352,7 @@ describe('The metric threshold rule type', () => { }, b: { ...baseCountCriterion, - comparator: Comparator.LT_OR_EQ, + comparator: COMPARATORS.LESS_THAN_OR_EQUALS, threshold: [0], metric: 'count', currentValue: 0, @@ -1364,14 +1364,14 @@ describe('The metric threshold rule type', () => { }, }, ]); - await executeGroupBy(Comparator.LT_OR_EQ, [0], 'empty-response', resultState); + await executeGroupBy(COMPARATORS.LESS_THAN_OR_EQUALS, [0], 'empty-response', resultState); testNAlertsReported(2); testAlertReported(1, { id: alertIdA, conditions: [{ metric: 'count', threshold: [0], value: '0', evaluation_value: 0 }], actionGroup: FIRED_ACTIONS.id, alertState: 'ALERT', - reason: 'count is 0 in the last 1 min for a. Alert when <= 0.', + reason: 'count is 0 in the last 1 min for a. Alert when below or equal 0.', tags: [], groupByKeys: { something: alertIdA }, }); @@ -1380,7 +1380,7 @@ describe('The metric threshold rule type', () => { conditions: [{ metric: 'count', threshold: [0], value: '0', evaluation_value: 0 }], actionGroup: FIRED_ACTIONS.id, alertState: 'ALERT', - reason: 'count is 0 in the last 1 min for b. Alert when <= 0.', + reason: 'count is 0 in the last 1 min for b. Alert when below or equal 0.', tags: [], groupByKeys: { something: alertIdB }, }); @@ -1389,7 +1389,7 @@ describe('The metric threshold rule type', () => { }); describe('querying with the p99 aggregator', () => { const alertID = '*'; - const execute = (comparator: Comparator, threshold: number[], sourceId: string = 'default') => + const execute = (comparator: COMPARATORS, threshold: number[], sourceId: string = 'default') => executor({ ...mockOptions, services, @@ -1411,7 +1411,7 @@ describe('The metric threshold rule type', () => { { '*': { ...baseNonCountCriterion, - comparator: Comparator.GT, + comparator: COMPARATORS.GREATER_THAN, threshold: [1], metric: 'test.metric.2', currentValue: 3, @@ -1423,14 +1423,14 @@ describe('The metric threshold rule type', () => { }, }, ]); - await execute(Comparator.GT, [1]); + await execute(COMPARATORS.GREATER_THAN, [1]); testNAlertsReported(1); testAlertReported(1, { id: alertID, conditions: [{ metric: 'test.metric.2', threshold: [1], value: '3', evaluation_value: 3 }], actionGroup: FIRED_ACTIONS.id, alertState: 'ALERT', - reason: 'test.metric.2 is 3 in the last 1 min. Alert when > 1.', + reason: 'test.metric.2 is 3 in the last 1 min. Alert when above 1.', tags: [], }); @@ -1438,7 +1438,7 @@ describe('The metric threshold rule type', () => { { '*': { ...baseNonCountCriterion, - comparator: Comparator.LT, + comparator: COMPARATORS.LESS_THAN, threshold: [1], metric: 'test.metric.2', currentValue: 3, @@ -1450,7 +1450,7 @@ describe('The metric threshold rule type', () => { }, }, ]); - await execute(Comparator.LT, [1]); + await execute(COMPARATORS.LESS_THAN, [1]); // should still only have been called once testNAlertsReported(1); }); @@ -1458,7 +1458,7 @@ describe('The metric threshold rule type', () => { describe('querying with the p95 aggregator', () => { const alertID = '*'; - const execute = (comparator: Comparator, threshold: number[], sourceId: string = 'default') => + const execute = (comparator: COMPARATORS, threshold: number[], sourceId: string = 'default') => executor({ ...mockOptions, services, @@ -1480,7 +1480,7 @@ describe('The metric threshold rule type', () => { { '*': { ...baseNonCountCriterion, - comparator: Comparator.GT, + comparator: COMPARATORS.GREATER_THAN, threshold: [0.25], metric: 'test.metric.1', currentValue: 1.0, @@ -1492,7 +1492,7 @@ describe('The metric threshold rule type', () => { }, }, ]); - await execute(Comparator.GT, [0.25]); + await execute(COMPARATORS.GREATER_THAN, [0.25]); testNAlertsReported(1); testAlertReported(1, { id: alertID, @@ -1501,7 +1501,7 @@ describe('The metric threshold rule type', () => { ], actionGroup: FIRED_ACTIONS.id, alertState: 'ALERT', - reason: 'test.metric.1 is 1 in the last 1 min. Alert when > 0.25.', + reason: 'test.metric.1 is 1 in the last 1 min. Alert when above 0.25.', tags: [], }); @@ -1509,7 +1509,7 @@ describe('The metric threshold rule type', () => { { '*': { ...baseNonCountCriterion, - comparator: Comparator.LT, + comparator: COMPARATORS.LESS_THAN, threshold: [0.95], metric: 'test.metric.1', currentValue: 1.0, @@ -1521,7 +1521,7 @@ describe('The metric threshold rule type', () => { }, }, ]); - await execute(Comparator.LT, [0.95]); + await execute(COMPARATORS.LESS_THAN, [0.95]); // should still only have been called once testNAlertsReported(1); }); @@ -1538,7 +1538,7 @@ describe('The metric threshold rule type', () => { criteria: [ { ...baseNonCountCriterion, - comparator: Comparator.GT, + comparator: COMPARATORS.GREATER_THAN, threshold: [1], metric: 'test.metric.3', }, @@ -1552,7 +1552,7 @@ describe('The metric threshold rule type', () => { { '*': { ...baseNonCountCriterion, - comparator: Comparator.LT, + comparator: COMPARATORS.LESS_THAN, threshold: [1], metric: 'test.metric.3', currentValue: null, @@ -1583,7 +1583,7 @@ describe('The metric threshold rule type', () => { { '*': { ...baseNonCountCriterion, - comparator: Comparator.LT, + comparator: COMPARATORS.LESS_THAN, threshold: [1], metric: 'test.metric.3', currentValue: null, @@ -1611,13 +1611,13 @@ describe('The metric threshold rule type', () => { criteria: [ { ...baseNonCountCriterion, - comparator: Comparator.GT, + comparator: COMPARATORS.GREATER_THAN, threshold: [1], metric: 'test.metric.3', }, { ...baseCountCriterion, - comparator: Comparator.GT, + comparator: COMPARATORS.GREATER_THAN, threshold: [30], }, ], @@ -1630,7 +1630,7 @@ describe('The metric threshold rule type', () => { { '*': { ...baseNonCountCriterion, - comparator: Comparator.LT, + comparator: COMPARATORS.LESS_THAN, threshold: [1], metric: 'test.metric.3', currentValue: null, @@ -1681,7 +1681,7 @@ describe('The metric threshold rule type', () => { criteria: [ { ...baseNonCountCriterion, - comparator: Comparator.GT, + comparator: COMPARATORS.GREATER_THAN, threshold: [0], metric, }, @@ -1706,7 +1706,7 @@ describe('The metric threshold rule type', () => { { '*': { ...baseNonCountCriterion, - comparator: Comparator.GT, + comparator: COMPARATORS.GREATER_THAN, threshold: [0], metric: 'test.metric.3', currentValue: null, @@ -1736,7 +1736,7 @@ describe('The metric threshold rule type', () => { { '*': { ...baseNonCountCriterion, - comparator: Comparator.GT, + comparator: COMPARATORS.GREATER_THAN, threshold: [0], metric: 'test.metric.3', currentValue: null, @@ -1766,7 +1766,7 @@ describe('The metric threshold rule type', () => { { a: { ...baseNonCountCriterion, - comparator: Comparator.GT, + comparator: COMPARATORS.GREATER_THAN, threshold: [0], metric: 'test.metric.1', currentValue: 1.0, @@ -1778,7 +1778,7 @@ describe('The metric threshold rule type', () => { }, b: { ...baseNonCountCriterion, - comparator: Comparator.GT, + comparator: COMPARATORS.GREATER_THAN, threshold: [0], metric: 'test.metric.1', currentValue: 3, @@ -1797,7 +1797,7 @@ describe('The metric threshold rule type', () => { conditions: [{ metric: 'test.metric.1', threshold: [0], value: '1', evaluation_value: 1 }], actionGroup: FIRED_ACTIONS.id, alertState: 'ALERT', - reason: 'test.metric.1 is 1 in the last 1 min for a. Alert when > 0.', + reason: 'test.metric.1 is 1 in the last 1 min for a. Alert when above 0.', tags: [], groupByKeys: { something: alertIdA }, }); @@ -1806,7 +1806,7 @@ describe('The metric threshold rule type', () => { conditions: [{ metric: 'test.metric.1', threshold: [0], value: '3', evaluation_value: 3 }], actionGroup: FIRED_ACTIONS.id, alertState: 'ALERT', - reason: 'test.metric.1 is 3 in the last 1 min for b. Alert when > 0.', + reason: 'test.metric.1 is 3 in the last 1 min for b. Alert when above 0.', tags: [], groupByKeys: { something: alertIdB }, }); @@ -1822,7 +1822,7 @@ describe('The metric threshold rule type', () => { { a: { ...baseNonCountCriterion, - comparator: Comparator.GT, + comparator: COMPARATORS.GREATER_THAN, threshold: [0], metric: 'test.metric.3', currentValue: null, @@ -1834,7 +1834,7 @@ describe('The metric threshold rule type', () => { }, b: { ...baseNonCountCriterion, - comparator: Comparator.GT, + comparator: COMPARATORS.GREATER_THAN, threshold: [0], metric: 'test.metric.3', currentValue: null, @@ -1877,7 +1877,7 @@ describe('The metric threshold rule type', () => { { a: { ...baseNonCountCriterion, - comparator: Comparator.GT, + comparator: COMPARATORS.GREATER_THAN, threshold: [0], metric: 'test.metric.2', currentValue: 3, @@ -1889,7 +1889,7 @@ describe('The metric threshold rule type', () => { }, b: { ...baseNonCountCriterion, - comparator: Comparator.GT, + comparator: COMPARATORS.GREATER_THAN, threshold: [0], metric: 'test.metric.2', currentValue: 1, @@ -1901,7 +1901,7 @@ describe('The metric threshold rule type', () => { }, c: { ...baseNonCountCriterion, - comparator: Comparator.GT, + comparator: COMPARATORS.GREATER_THAN, threshold: [0], metric: 'test.metric.2', currentValue: 3, @@ -1920,7 +1920,7 @@ describe('The metric threshold rule type', () => { conditions: [{ metric: 'test.metric.2', threshold: [0], value: '3', evaluation_value: 3 }], actionGroup: FIRED_ACTIONS.id, alertState: 'ALERT', - reason: 'test.metric.2 is 3 in the last 1 min for a. Alert when > 0.', + reason: 'test.metric.2 is 3 in the last 1 min for a. Alert when above 0.', tags: [], groupByKeys: { something: alertIdA }, }); @@ -1929,7 +1929,7 @@ describe('The metric threshold rule type', () => { conditions: [{ metric: 'test.metric.2', threshold: [0], value: '1', evaluation_value: 1 }], actionGroup: FIRED_ACTIONS.id, alertState: 'ALERT', - reason: 'test.metric.2 is 1 in the last 1 min for b. Alert when > 0.', + reason: 'test.metric.2 is 1 in the last 1 min for b. Alert when above 0.', tags: [], groupByKeys: { something: alertIdB }, }); @@ -1938,7 +1938,7 @@ describe('The metric threshold rule type', () => { conditions: [{ metric: 'test.metric.2', threshold: [0], value: '3', evaluation_value: 3 }], actionGroup: FIRED_ACTIONS.id, alertState: 'ALERT', - reason: 'test.metric.2 is 3 in the last 1 min for c. Alert when > 0.', + reason: 'test.metric.2 is 3 in the last 1 min for c. Alert when above 0.', tags: [], groupByKeys: { something: alertIdC }, }); @@ -1947,7 +1947,7 @@ describe('The metric threshold rule type', () => { { a: { ...baseNonCountCriterion, - comparator: Comparator.GT, + comparator: COMPARATORS.GREATER_THAN, threshold: [0], metric: 'test.metric.1', currentValue: 1, @@ -1959,7 +1959,7 @@ describe('The metric threshold rule type', () => { }, b: { ...baseNonCountCriterion, - comparator: Comparator.GT, + comparator: COMPARATORS.GREATER_THAN, threshold: [0], metric: 'test.metric.1', currentValue: 3, @@ -1978,7 +1978,7 @@ describe('The metric threshold rule type', () => { conditions: [{ metric: 'test.metric.1', threshold: [0], value: '1', evaluation_value: 1 }], actionGroup: FIRED_ACTIONS.id, alertState: 'ALERT', - reason: 'test.metric.1 is 1 in the last 1 min for a. Alert when > 0.', + reason: 'test.metric.1 is 1 in the last 1 min for a. Alert when above 0.', tags: [], groupByKeys: { something: alertIdA }, }); @@ -1987,7 +1987,7 @@ describe('The metric threshold rule type', () => { conditions: [{ metric: 'test.metric.1', threshold: [0], value: '3', evaluation_value: 3 }], actionGroup: FIRED_ACTIONS.id, alertState: 'ALERT', - reason: 'test.metric.1 is 3 in the last 1 min for b. Alert when > 0.', + reason: 'test.metric.1 is 3 in the last 1 min for b. Alert when above 0.', tags: [], groupByKeys: { something: alertIdB }, }); @@ -2004,7 +2004,7 @@ describe('The metric threshold rule type', () => { criteria: [ { ...baseNonCountCriterion, - comparator: Comparator.GT, + comparator: COMPARATORS.GREATER_THAN, threshold: [0], metric, }, @@ -2025,7 +2025,7 @@ describe('The metric threshold rule type', () => { { '*': { ...baseNonCountCriterion, - comparator: Comparator.GT, + comparator: COMPARATORS.GREATER_THAN, threshold: [0], metric: 'test.metric.3', currentValue: null, @@ -2043,7 +2043,7 @@ describe('The metric threshold rule type', () => { { '*': { ...baseNonCountCriterion, - comparator: Comparator.GT, + comparator: COMPARATORS.GREATER_THAN, threshold: [0], metric: 'test.metric.3', currentValue: null, @@ -2061,7 +2061,7 @@ describe('The metric threshold rule type', () => { { a: { ...baseNonCountCriterion, - comparator: Comparator.GT, + comparator: COMPARATORS.GREATER_THAN, threshold: [0], metric: 'test.metric.1', currentValue: 1, @@ -2073,7 +2073,7 @@ describe('The metric threshold rule type', () => { }, b: { ...baseNonCountCriterion, - comparator: Comparator.GT, + comparator: COMPARATORS.GREATER_THAN, threshold: [0], metric: 'test.metric.1', currentValue: 3, @@ -2094,7 +2094,7 @@ describe('The metric threshold rule type', () => { ], actionGroup: FIRED_ACTIONS.id, alertState: 'ALERT', - reason: 'test.metric.1 is 1 in the last 1 min for a. Alert when > 0.', + reason: 'test.metric.1 is 1 in the last 1 min for a. Alert when above 0.', tags: [], groupByKeys: { something: alertIdA }, }); @@ -2105,7 +2105,7 @@ describe('The metric threshold rule type', () => { ], actionGroup: FIRED_ACTIONS.id, alertState: 'ALERT', - reason: 'test.metric.1 is 3 in the last 1 min for b. Alert when > 0.', + reason: 'test.metric.1 is 3 in the last 1 min for b. Alert when above 0.', tags: [], groupByKeys: { something: alertIdB }, }); @@ -2119,7 +2119,7 @@ describe('The metric threshold rule type', () => { { a: { ...baseNonCountCriterion, - comparator: Comparator.GT, + comparator: COMPARATORS.GREATER_THAN, threshold: [0], metric: 'test.metric.3', currentValue: null, @@ -2131,7 +2131,7 @@ describe('The metric threshold rule type', () => { }, b: { ...baseNonCountCriterion, - comparator: Comparator.GT, + comparator: COMPARATORS.GREATER_THAN, threshold: [0], metric: 'test.metric.3', currentValue: null, @@ -2226,7 +2226,7 @@ describe('The metric threshold rule type', () => { criteria: [ { ...baseNonCountCriterion, - comparator: Comparator.GT, + comparator: COMPARATORS.GREATER_THAN, threshold: [9.999], }, ], @@ -2234,9 +2234,9 @@ describe('The metric threshold rule type', () => { }); const setResults = ({ - comparator = Comparator.GT, + comparator = COMPARATORS.GREATER_THAN, threshold = [9999], - warningComparator = Comparator.GT, + warningComparator = COMPARATORS.GREATER_THAN, warningThreshold = [2.49], metric = 'test.metric.1', currentValue = 7.59, @@ -2278,7 +2278,7 @@ describe('The metric threshold rule type', () => { ], actionGroup: WARNING_ACTIONS.id, alertState: 'WARNING', - reason: 'test.metric.1 is 2.5 in the last 1 min. Alert when > 2.49.', + reason: 'test.metric.1 is 2.5 in the last 1 min. Alert when above 2.49.', tags: [], }); @@ -2311,7 +2311,7 @@ describe('The metric threshold rule type', () => { ], actionGroup: WARNING_ACTIONS.id, alertState: 'WARNING', - reason: 'system.cpu.user.pct is 82% in the last 1 min. Alert when > 81%.', + reason: 'system.cpu.user.pct is 82% in the last 1 min. Alert when above 81%.', tags: [], }); }); @@ -2498,7 +2498,7 @@ const baseNonCountCriterion = { timeSize: 1, timeUnit: 'm', threshold: [0], - comparator: Comparator.GT, + comparator: COMPARATORS.GREATER_THAN, } as NonCountMetricExpressionParams; const baseCountCriterion = { @@ -2506,5 +2506,5 @@ const baseCountCriterion = { timeSize: 1, timeUnit: 'm', threshold: [0], - comparator: Comparator.GT, + comparator: COMPARATORS.GREATER_THAN, } as CountMetricExpressionParams; diff --git a/x-pack/plugins/observability_solution/infra/server/lib/alerting/metric_threshold/metric_threshold_executor.ts b/x-pack/plugins/observability_solution/infra/server/lib/alerting/metric_threshold/metric_threshold_executor.ts index 4ccd94f8560d29..49129e2058cc18 100644 --- a/x-pack/plugins/observability_solution/infra/server/lib/alerting/metric_threshold/metric_threshold_executor.ts +++ b/x-pack/plugins/observability_solution/infra/server/lib/alerting/metric_threshold/metric_threshold_executor.ts @@ -20,11 +20,12 @@ import { RecoveredActionGroup, } from '@kbn/alerting-plugin/common'; import { AlertsClientError, RuleExecutorOptions, RuleTypeState } from '@kbn/alerting-plugin/server'; -import type { TimeUnitChar } from '@kbn/observability-plugin/common'; -import { getAlertUrl } from '@kbn/observability-plugin/common'; +import { TimeUnitChar, getAlertUrl } from '@kbn/observability-plugin/common'; import { ObservabilityMetricsAlert } from '@kbn/alerts-as-data-utils'; +import { COMPARATORS } from '@kbn/alerting-comparators'; +import { convertToBuiltInComparators } from '@kbn/observability-plugin/common/utils/convert_legacy_outside_comparator'; import { getOriginalActionGroup } from '../../../utils/get_original_action_group'; -import { AlertStates, Comparator } from '../../../../common/alerting/metrics'; +import { AlertStates } from '../../../../common/alerting/metrics'; import { createFormatter } from '../../../../common/formatters'; import { InfraBackendLibs } from '../../infra_types'; import { @@ -296,7 +297,16 @@ export const createMetricThresholdExecutor = reason = alertResults .map((result) => buildFiredAlertReason({ - ...formatAlertResult(result[group], nextState === AlertStates.WARNING), + ...formatAlertResult( + { + ...result[group], + comparator: convertToBuiltInComparators(result[group].comparator), + warningComparator: result[group].comparator + ? convertToBuiltInComparators(result[group].comparator) + : undefined, + }, + nextState === AlertStates.WARNING + ), group, }) ) @@ -367,21 +377,33 @@ export const createMetricThresholdExecutor = }), reason, threshold: mapToConditionsLookup(alertResults, (result, index) => { - const evaluation = result[group]; + const evaluation = result[group] as Evaluation; if (!evaluation) { return criteria[index].threshold; } - return formatAlertResult(evaluation).threshold; + return formatAlertResult({ + ...evaluation, + comparator: convertToBuiltInComparators(evaluation.comparator), + warningComparator: evaluation.warningComparator + ? convertToBuiltInComparators(evaluation.warningComparator) + : undefined, + }).threshold; }), timestamp, value: mapToConditionsLookup(alertResults, (result, index) => { - const evaluation = result[group]; + const evaluation = result[group] as Evaluation; if (!evaluation && criteria[index].aggType === 'count') { return 0; } else if (!evaluation) { return null; } - return formatAlertResult(evaluation).currentValue; + return formatAlertResult({ + ...evaluation, + comparator: convertToBuiltInComparators(evaluation.comparator), + warningComparator: evaluation.warningComparator + ? convertToBuiltInComparators(evaluation.warningComparator) + : undefined, + }).currentValue; }), viewInAppUrl: getMetricsViewInAppUrlWithSpaceId({ basePath: libs.basePath, @@ -518,9 +540,9 @@ const formatAlertResult = ( metric: string; currentValue: number | null; threshold: number[]; - comparator: Comparator; + comparator: COMPARATORS; warningThreshold?: number[]; - warningComparator?: Comparator; + warningComparator?: COMPARATORS; timeSize: number; timeUnit: TimeUnitChar; } & AlertResult, @@ -544,7 +566,7 @@ const formatAlertResult = ( threshold: Array.isArray(thresholdToFormat) ? thresholdToFormat.map((v: number) => formatter(v)) : formatter(thresholdToFormat), - comparator: comparatorToUse, + comparator: convertToBuiltInComparators(comparatorToUse), }; } diff --git a/x-pack/plugins/observability_solution/infra/server/lib/alerting/metric_threshold/register_metric_threshold_rule_type.ts b/x-pack/plugins/observability_solution/infra/server/lib/alerting/metric_threshold/register_metric_threshold_rule_type.ts index 1adcd58f425925..8bd8aafcbf2074 100644 --- a/x-pack/plugins/observability_solution/infra/server/lib/alerting/metric_threshold/register_metric_threshold_rule_type.ts +++ b/x-pack/plugins/observability_solution/infra/server/lib/alerting/metric_threshold/register_metric_threshold_rule_type.ts @@ -8,15 +8,12 @@ import { DEFAULT_APP_CATEGORIES } from '@kbn/core/server'; import { schema } from '@kbn/config-schema'; import { i18n } from '@kbn/i18n'; -import { ActionGroupIdsOf } from '@kbn/alerting-plugin/common'; -import { - GetViewInAppRelativeUrlFnOpts, - PluginSetupContract, - RuleType, -} from '@kbn/alerting-plugin/server'; +import { GetViewInAppRelativeUrlFnOpts, PluginSetupContract } from '@kbn/alerting-plugin/server'; import { observabilityPaths } from '@kbn/observability-plugin/common'; +import { COMPARATORS } from '@kbn/alerting-comparators'; +import { LEGACY_COMPARATORS } from '@kbn/observability-plugin/common/utils/convert_legacy_outside_comparator'; import type { InfraConfig } from '../../../../common/plugin_config_types'; -import { Comparator, METRIC_THRESHOLD_ALERT_TYPE_ID } from '../../../../common/alerting/metrics'; +import { METRIC_THRESHOLD_ALERT_TYPE_ID } from '../../../../common/alerting/metrics'; import { METRIC_EXPLORER_AGGREGATIONS } from '../../../../common/http_api'; import { InfraBackendLibs } from '../../infra_types'; import { @@ -48,13 +45,6 @@ import { import { MetricsRulesTypeAlertDefinition } from '../register_rule_types'; import { O11Y_AAD_FIELDS } from '../../../../common/constants'; -type MetricThresholdAllowedActionGroups = ActionGroupIdsOf< - typeof FIRED_ACTIONS | typeof WARNING_ACTIONS | typeof NO_DATA_ACTIONS ->; -export type MetricThresholdAlertType = Omit & { - ActionGroupIdsOf: MetricThresholdAllowedActionGroups; -}; - export function registerMetricThresholdRuleType( alertingPlugin: PluginSetupContract, libs: InfraBackendLibs, @@ -63,14 +53,14 @@ export function registerMetricThresholdRuleType( if (!featureFlags.metricThresholdAlertRuleEnabled) { return; } - + const comparator = Object.values({ ...COMPARATORS, ...LEGACY_COMPARATORS }); const baseCriterion = { threshold: schema.arrayOf(schema.number()), - comparator: oneOfLiterals(Object.values(Comparator)), + comparator: oneOfLiterals(comparator), timeUnit: schema.string(), timeSize: schema.number(), warningThreshold: schema.maybe(schema.arrayOf(schema.number())), - warningComparator: schema.maybe(oneOfLiterals(Object.values(Comparator))), + warningComparator: schema.maybe(oneOfLiterals(comparator)), }; const nonCountCriterion = schema.object({ diff --git a/x-pack/plugins/observability_solution/infra/tsconfig.json b/x-pack/plugins/observability_solution/infra/tsconfig.json index d43ab3b5ccc463..63350d0cd97868 100644 --- a/x-pack/plugins/observability_solution/infra/tsconfig.json +++ b/x-pack/plugins/observability_solution/infra/tsconfig.json @@ -97,6 +97,8 @@ "@kbn/dashboard-plugin", "@kbn/shared-svg", "@kbn/aiops-log-rate-analysis", + "@kbn/search-types", + "@kbn/alerting-comparators", "@kbn/react-hooks", "@kbn/search-types", "@kbn/router-utils", diff --git a/x-pack/plugins/observability_solution/metrics_data_access/common/inventory_models/container/metrics/tsvb/container_disk_io_bytes.ts b/x-pack/plugins/observability_solution/metrics_data_access/common/inventory_models/container/metrics/tsvb/container_disk_io_bytes.ts index 3ba2b11740b6e5..7320349f92e8d8 100644 --- a/x-pack/plugins/observability_solution/metrics_data_access/common/inventory_models/container/metrics/tsvb/container_disk_io_bytes.ts +++ b/x-pack/plugins/observability_solution/metrics_data_access/common/inventory_models/container/metrics/tsvb/container_disk_io_bytes.ts @@ -13,7 +13,7 @@ export const containerDiskIOBytes: TSVBMetricModelCreator = ( interval ): TSVBMetricModel => ({ id: 'containerDiskIOBytes', - requires: ['docker.disk'], + requires: ['docker.diskio'], index_pattern: indexPattern, interval, time_field: timeField, diff --git a/x-pack/plugins/observability_solution/metrics_data_access/common/inventory_models/container/metrics/tsvb/container_diskio_ops.ts b/x-pack/plugins/observability_solution/metrics_data_access/common/inventory_models/container/metrics/tsvb/container_diskio_ops.ts index 772c0d6c0a33ee..a6887fb4e76380 100644 --- a/x-pack/plugins/observability_solution/metrics_data_access/common/inventory_models/container/metrics/tsvb/container_diskio_ops.ts +++ b/x-pack/plugins/observability_solution/metrics_data_access/common/inventory_models/container/metrics/tsvb/container_diskio_ops.ts @@ -13,7 +13,7 @@ export const containerDiskIOOps: TSVBMetricModelCreator = ( interval ): TSVBMetricModel => ({ id: 'containerDiskIOOps', - requires: ['docker.disk'], + requires: ['docker.diskio'], index_pattern: indexPattern, interval, time_field: timeField, diff --git a/x-pack/plugins/observability_solution/observability/common/custom_threshold_rule/types.ts b/x-pack/plugins/observability_solution/observability/common/custom_threshold_rule/types.ts index 38b7e6d90061c7..0e4a0e71670698 100644 --- a/x-pack/plugins/observability_solution/observability/common/custom_threshold_rule/types.ts +++ b/x-pack/plugins/observability_solution/observability/common/custom_threshold_rule/types.ts @@ -8,6 +8,8 @@ import * as rt from 'io-ts'; import { DataViewSpec, SerializedSearchSourceFields } from '@kbn/data-plugin/common'; import { Filter, Query } from '@kbn/es-query'; +import { COMPARATORS } from '@kbn/alerting-comparators'; +import { LEGACY_COMPARATORS } from '../utils/convert_legacy_outside_comparator'; import { TimeUnitChar } from '../utils/formatters/duration'; export const ThresholdFormatterTypeRT = rt.keyof({ @@ -20,15 +22,6 @@ export const ThresholdFormatterTypeRT = rt.keyof({ }); export type ThresholdFormatterType = rt.TypeOf; -export enum Comparator { - GT = '>', - LT = '<', - GT_OR_EQ = '>=', - LT_OR_EQ = '<=', - BETWEEN = 'between', - OUTSIDE_RANGE = 'outside', -} - export enum Aggregators { COUNT = 'count', AVERAGE = 'avg', @@ -78,9 +71,7 @@ export interface BaseMetricExpressionParams { timeUnit: TimeUnitChar; sourceId?: string; threshold: number[]; - comparator: Comparator; - warningComparator?: Comparator; - warningThreshold?: number[]; + comparator: COMPARATORS | LEGACY_COMPARATORS; } export interface CustomThresholdExpressionMetric { diff --git a/x-pack/plugins/observability_solution/observability/common/i18n.ts b/x-pack/plugins/observability_solution/observability/common/i18n.ts index 73c27e811b2d6f..3e7badbf635ccd 100644 --- a/x-pack/plugins/observability_solution/observability/common/i18n.ts +++ b/x-pack/plugins/observability_solution/observability/common/i18n.ts @@ -10,3 +10,46 @@ import { i18n } from '@kbn/i18n'; export const NOT_AVAILABLE_LABEL = i18n.translate('xpack.observability.notAvailable', { defaultMessage: 'N/A', }); + +// Comparators +export const BELOW_TEXT = i18n.translate( + 'xpack.observability.customThreshold.rule.threshold.below', + { + defaultMessage: 'below', + } +); + +export const BELOW_OR_EQ_TEXT = i18n.translate( + 'xpack.observability.customThreshold.rule.threshold.belowOrEqual', + { + defaultMessage: 'below or equal', + } +); + +export const ABOVE_TEXT = i18n.translate( + 'xpack.observability.customThreshold.rule.threshold.above', + { + defaultMessage: 'above', + } +); + +export const ABOVE_OR_EQ_TEXT = i18n.translate( + 'xpack.observability.customThreshold.rule.threshold.aboveOrEqual', + { + defaultMessage: 'above or equal', + } +); + +export const BETWEEN_TEXT = i18n.translate( + 'xpack.observability.customThreshold.rule.threshold.between', + { + defaultMessage: 'between', + } +); + +export const NOT_BETWEEN_TEXT = i18n.translate( + 'xpack.observability.customThreshold.rule.threshold.notBetween', + { + defaultMessage: 'not between', + } +); diff --git a/x-pack/plugins/observability_solution/observability/common/index.ts b/x-pack/plugins/observability_solution/observability/common/index.ts index d7d5672b95ec3b..eb8049f2182e2d 100644 --- a/x-pack/plugins/observability_solution/observability/common/index.ts +++ b/x-pack/plugins/observability_solution/observability/common/index.ts @@ -18,7 +18,7 @@ export { } from './utils/formatters'; export { getInspectResponse } from './utils/get_inspect_response'; export { getAlertDetailsUrl, getAlertUrl } from './utils/alerting/alert_url'; - +export { convertToBuiltInComparators } from './utils/convert_legacy_outside_comparator'; export { ProcessorEvent } from './processor_event'; export { diff --git a/x-pack/plugins/observability_solution/observability/common/utils/convert_legacy_outside_comparator.test.ts b/x-pack/plugins/observability_solution/observability/common/utils/convert_legacy_outside_comparator.test.ts new file mode 100644 index 00000000000000..70153c2084d6e6 --- /dev/null +++ b/x-pack/plugins/observability_solution/observability/common/utils/convert_legacy_outside_comparator.test.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 { + convertToBuiltInComparators, + LEGACY_COMPARATORS, +} from './convert_legacy_outside_comparator'; +import { COMPARATORS } from '@kbn/alerting-comparators'; + +describe('convertToBuiltInComparators', () => { + it('should return in between when passing the legacy outside', () => { + const comparator = convertToBuiltInComparators(LEGACY_COMPARATORS.OUTSIDE_RANGE); + expect(comparator).toBe(COMPARATORS.NOT_BETWEEN); + }); + + it('should return the same comparator when NOT passing the legacy outside', () => { + const comparator = convertToBuiltInComparators(COMPARATORS.GREATER_THAN); + expect(comparator).toBe(COMPARATORS.GREATER_THAN); + }); +}); diff --git a/x-pack/plugins/observability_solution/observability/common/utils/convert_legacy_outside_comparator.ts b/x-pack/plugins/observability_solution/observability/common/utils/convert_legacy_outside_comparator.ts new file mode 100644 index 00000000000000..d415e1a73187c8 --- /dev/null +++ b/x-pack/plugins/observability_solution/observability/common/utils/convert_legacy_outside_comparator.ts @@ -0,0 +1,17 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ +import { COMPARATORS } from '@kbn/alerting-comparators'; + +export enum LEGACY_COMPARATORS { + OUTSIDE_RANGE = 'outside', +} +export type LegacyComparator = COMPARATORS | LEGACY_COMPARATORS; + +export function convertToBuiltInComparators(comparator: LegacyComparator): COMPARATORS { + if (comparator === LEGACY_COMPARATORS.OUTSIDE_RANGE) return COMPARATORS.NOT_BETWEEN; + return comparator; +} diff --git a/x-pack/plugins/observability_solution/observability/public/components/alert_overview/helpers/map_rules_params_with_flyout.test.ts b/x-pack/plugins/observability_solution/observability/public/components/alert_overview/helpers/map_rules_params_with_flyout.test.ts index 706fce0c75a628..d02961e45d0019 100644 --- a/x-pack/plugins/observability_solution/observability/public/components/alert_overview/helpers/map_rules_params_with_flyout.test.ts +++ b/x-pack/plugins/observability_solution/observability/public/components/alert_overview/helpers/map_rules_params_with_flyout.test.ts @@ -103,13 +103,13 @@ describe('Map rules params with flyout', () => { excludeHitsFromPreviousRun: false, }, 'kibana.alert.evaluation.value': 100870655162.18182, - 'kibana.alert.evaluation.threshold': 1, + 'kibana.alert.evaluation.threshold': [1], }, }, results: [ { observedValue: [100870655162.18182], - threshold: [1], + threshold: '1', comparator: '>', pctAboveThreshold: ' (10087065516118.18% above the threshold)', }, @@ -149,7 +149,7 @@ describe('Map rules params with flyout', () => { observedValue: [4577], threshold: [100], comparator: 'more than', - pctAboveThreshold: ' (4477% above the threshold)', + pctAboveThreshold: ' (4477% more than the threshold)', }, ], }, @@ -215,12 +215,50 @@ describe('Map rules params with flyout', () => { results: [ { observedValue: '10.4 Mbit', - threshold: ['3 Mbit'], + threshold: '3 Mbit', comparator: '>', pctAboveThreshold: ' (247.54% above the threshold)', }, ], }, + { + ruleType: 'metrics.alert.inventory.threshold', + alert: { + fields: { + 'kibana.alert.rule.rule_type_id': 'metrics.alert.inventory.threshold', + 'kibana.alert.rule.parameters': { + nodeType: 'host', + criteria: [ + { + metric: 'rx', + comparator: '<', + threshold: [90], + timeSize: 1, + timeUnit: 'm', + customMetric: { + type: 'custom', + id: 'alert-custom-metric', + field: 'system.memory.used.pct', + aggregation: 'avg', + }, + }, + ], + sourceId: 'default', + }, + + 'kibana.alert.evaluation.value': [130.4], + 'kibana.alert.evaluation.threshold': 3000000, + }, + }, + results: [ + { + comparator: '<', + observedValue: '13,040%', + pctAboveThreshold: ' (14388.89% below the threshold)', + threshold: '9,000%', + }, + ], + }, { ruleType: 'apm.error_rate', alert: { diff --git a/x-pack/plugins/observability_solution/observability/public/components/alert_overview/helpers/map_rules_params_with_flyout.ts b/x-pack/plugins/observability_solution/observability/public/components/alert_overview/helpers/map_rules_params_with_flyout.ts index f2f58e17ea56a7..fce4bd35dbdeeb 100644 --- a/x-pack/plugins/observability_solution/observability/public/components/alert_overview/helpers/map_rules_params_with_flyout.ts +++ b/x-pack/plugins/observability_solution/observability/public/components/alert_overview/helpers/map_rules_params_with_flyout.ts @@ -19,7 +19,15 @@ import { } from '@kbn/rule-data-utils'; import { EsQueryRuleParams } from '@kbn/stack-alerts-plugin/public/rule_types/es_query/types'; import { i18n } from '@kbn/i18n'; -import { asDuration, asPercent } from '../../../../common'; +import { COMPARATORS } from '@kbn/alerting-comparators'; + +import { + ABOVE_OR_EQ_TEXT, + ABOVE_TEXT, + BELOW_OR_EQ_TEXT, + BELOW_TEXT, +} from '../../../../common/i18n'; +import { asDuration, asPercent, convertToBuiltInComparators } from '../../../../common'; import { createFormatter } from '../../../../common/custom_threshold_rule/formatters'; import { metricValueFormatter } from '../../../../common/custom_threshold_rule/metric_value_formatter'; import { METRIC_FORMATTERS } from '../../../../common/custom_threshold_rule/formatters/snapshot_metric_formats'; @@ -37,12 +45,34 @@ export interface FlyoutThresholdData { pctAboveThreshold: string; } -const getPctAboveThreshold = (observedValue?: number, threshold?: number[]): string => { +const getI18nComparator = (comparator?: COMPARATORS) => { + switch (comparator) { + case COMPARATORS.GREATER_THAN: + return ABOVE_TEXT; + case COMPARATORS.GREATER_THAN_OR_EQUALS: + return ABOVE_OR_EQ_TEXT; + case COMPARATORS.LESS_THAN: + return BELOW_TEXT; + case COMPARATORS.LESS_THAN_OR_EQUALS: + return BELOW_OR_EQ_TEXT; + default: + return comparator; + } +}; +const getPctAboveThreshold = ( + threshold: number[], + comparator: COMPARATORS, + observedValue?: number +): string => { if (!observedValue || !threshold || threshold.length > 1 || threshold[0] <= 0) return ''; + return i18n.translate('xpack.observability.alertFlyout.overview.aboveThresholdLabel', { - defaultMessage: ' ({pctValue}% above the threshold)', + defaultMessage: ' ({pctValue}% {comparator} the threshold)', values: { - pctValue: parseFloat((((observedValue - threshold[0]) * 100) / threshold[0]).toFixed(2)), + pctValue: Math.abs( + parseFloat((((observedValue - threshold[0]) * 100) / threshold[0]).toFixed(2)) + ), + comparator: getI18nComparator(convertToBuiltInComparators(comparator)), }, }); }; @@ -78,7 +108,11 @@ export const mapRuleParamsWithFlyout = (alert: TopAlert): FlyoutThresholdData[] observedValue: formattedValue, threshold: thresholdFormattedAsString, comparator, - pctAboveThreshold: getPctAboveThreshold(observedValue, threshold), + pctAboveThreshold: getPctAboveThreshold( + threshold, + convertToBuiltInComparators(comparator), + observedValue + ), } as unknown as FlyoutThresholdData; }); @@ -113,46 +147,82 @@ export const mapRuleParamsWithFlyout = (alert: TopAlert): FlyoutThresholdData[] observedValue: formattedValue, threshold: thresholdFormattedAsString, comparator, - pctAboveThreshold: getPctAboveThreshold(observedValue, threshold), + pctAboveThreshold: getPctAboveThreshold( + threshold, + convertToBuiltInComparators(comparator), + observedValue + ), } as unknown as FlyoutThresholdData; }); case METRIC_INVENTORY_THRESHOLD_ALERT_TYPE_ID: return observedValues.map((observedValue, metricIndex) => { - const criteria = ruleCriteria[metricIndex] as BaseMetricExpressionParams & { + const { threshold, customMetric, metric, comparator } = ruleCriteria[ + metricIndex + ] as BaseMetricExpressionParams & { metric: string; + customMetric: { + field: string; + }; }; - const infraType = METRIC_FORMATTERS[criteria.metric].formatter; - const formatter = createFormatter(infraType); - const comparator = criteria.comparator; - const threshold = criteria.threshold; - const formatThreshold = threshold.map((v: number) => { - if (infraType === 'percent') { - v = Number(v) / 100; + const metricField = customMetric?.field || metric; + const thresholdFormatted = threshold.map((thresholdToFormat) => { + if ( + metricField.endsWith('.pct') || + (METRIC_FORMATTERS[metric] && METRIC_FORMATTERS[metric].formatter === 'percent') + ) { + thresholdToFormat = thresholdToFormat / 100; + } else if ( + metricField.endsWith('.bytes') || + (METRIC_FORMATTERS[metric] && METRIC_FORMATTERS[metric].formatter === 'bits') + ) { + thresholdToFormat = thresholdToFormat / 8; } - if (infraType === 'bits') { - v = Number(v) / 8; - } - return v; + return thresholdToFormat; }); + let observedValueFormatted: string; + let thresholdFormattedAsString: string; + if (customMetric.field) { + observedValueFormatted = metricValueFormatter( + observedValue as number, + customMetric.field + ); + thresholdFormattedAsString = threshold + .map((thresholdToStringFormat) => + metricValueFormatter(thresholdToStringFormat, metricField) + ) + .join(' AND '); + } else { + const infraType = METRIC_FORMATTERS[metric].formatter; + const formatter = createFormatter(infraType); + observedValueFormatted = formatter(observedValue); + thresholdFormattedAsString = thresholdFormatted.map(formatter).join(' AND '); + } + return { - observedValue: formatter(observedValue), - threshold: formatThreshold.map(formatter), + observedValue: observedValueFormatted, + threshold: thresholdFormattedAsString, comparator, - pctAboveThreshold: getPctAboveThreshold(observedValue, formatThreshold), + pctAboveThreshold: getPctAboveThreshold( + thresholdFormatted, + convertToBuiltInComparators(comparator), + observedValue + ), } as unknown as FlyoutThresholdData; }); case LOG_THRESHOLD_ALERT_TYPE_ID: - const { comparator } = ruleParams?.count as { comparator: string }; + const { comparator } = ruleParams?.count as { comparator: COMPARATORS }; const flyoutMap = { observedValue: [alert.fields[ALERT_EVALUATION_VALUE]], threshold: [alert.fields[ALERT_EVALUATION_THRESHOLD]], comparator, - pctAboveThreshold: getPctAboveThreshold(alert.fields[ALERT_EVALUATION_VALUE], [ - alert.fields[ALERT_EVALUATION_THRESHOLD]!, - ]), + pctAboveThreshold: getPctAboveThreshold( + [alert.fields[ALERT_EVALUATION_THRESHOLD]!], + comparator, + alert.fields[ALERT_EVALUATION_VALUE] + ), } as unknown as FlyoutThresholdData; return [flyoutMap]; @@ -160,10 +230,12 @@ export const mapRuleParamsWithFlyout = (alert: TopAlert): FlyoutThresholdData[] const APMFlyoutMapErrorCount = { observedValue: [alert.fields[ALERT_EVALUATION_VALUE]], threshold: [alert.fields[ALERT_EVALUATION_THRESHOLD]], - comparator: '>', - pctAboveThreshold: getPctAboveThreshold(alert.fields[ALERT_EVALUATION_VALUE], [ - alert.fields[ALERT_EVALUATION_THRESHOLD]!, - ]), + comparator: COMPARATORS.GREATER_THAN, + pctAboveThreshold: getPctAboveThreshold( + [alert.fields[ALERT_EVALUATION_THRESHOLD]!], + COMPARATORS.GREATER_THAN, + alert.fields[ALERT_EVALUATION_VALUE] + ), } as unknown as FlyoutThresholdData; return [APMFlyoutMapErrorCount]; @@ -171,10 +243,12 @@ export const mapRuleParamsWithFlyout = (alert: TopAlert): FlyoutThresholdData[] const APMFlyoutMapTransactionErrorRate = { observedValue: [asPercent(alert.fields[ALERT_EVALUATION_VALUE], 100)], threshold: [asPercent(alert.fields[ALERT_EVALUATION_THRESHOLD], 100)], - comparator: '>', - pctAboveThreshold: getPctAboveThreshold(alert.fields[ALERT_EVALUATION_VALUE], [ - alert.fields[ALERT_EVALUATION_THRESHOLD]!, - ]), + comparator: COMPARATORS.GREATER_THAN, + pctAboveThreshold: getPctAboveThreshold( + [alert.fields[ALERT_EVALUATION_THRESHOLD]!], + COMPARATORS.GREATER_THAN, + alert.fields[ALERT_EVALUATION_VALUE] + ), } as unknown as FlyoutThresholdData; return [APMFlyoutMapTransactionErrorRate]; @@ -182,22 +256,26 @@ export const mapRuleParamsWithFlyout = (alert: TopAlert): FlyoutThresholdData[] const APMFlyoutMapTransactionDuration = { observedValue: [asDuration(alert.fields[ALERT_EVALUATION_VALUE])], threshold: [asDuration(alert.fields[ALERT_EVALUATION_THRESHOLD])], - comparator: '>', - pctAboveThreshold: getPctAboveThreshold(alert.fields[ALERT_EVALUATION_VALUE], [ - alert.fields[ALERT_EVALUATION_THRESHOLD]!, - ]), + comparator: COMPARATORS.GREATER_THAN, + pctAboveThreshold: getPctAboveThreshold( + [alert.fields[ALERT_EVALUATION_THRESHOLD]!], + COMPARATORS.GREATER_THAN, + alert.fields[ALERT_EVALUATION_VALUE] + ), } as unknown as FlyoutThresholdData; return [APMFlyoutMapTransactionDuration]; case '.es-query': - const { thresholdComparator } = ruleParams as EsQueryRuleParams; + const { thresholdComparator, threshold } = ruleParams as EsQueryRuleParams; const ESQueryFlyoutMap = { observedValue: [alert.fields[ALERT_EVALUATION_VALUE]], - threshold: [alert.fields[ALERT_EVALUATION_THRESHOLD]], + threshold: threshold.join(' AND '), comparator: thresholdComparator, - pctAboveThreshold: getPctAboveThreshold(alert.fields[ALERT_EVALUATION_VALUE], [ - alert.fields[ALERT_EVALUATION_THRESHOLD]!, - ]), + pctAboveThreshold: getPctAboveThreshold( + threshold, + thresholdComparator as COMPARATORS, + alert.fields[ALERT_EVALUATION_VALUE] + ), } as unknown as FlyoutThresholdData; return [ESQueryFlyoutMap]; @@ -205,10 +283,12 @@ export const mapRuleParamsWithFlyout = (alert: TopAlert): FlyoutThresholdData[] const SLOBurnRateFlyoutMap = { observedValue: [alert.fields[ALERT_EVALUATION_VALUE]], threshold: [alert.fields[ALERT_EVALUATION_THRESHOLD]], - comparator: '>', - pctAboveThreshold: getPctAboveThreshold(alert.fields[ALERT_EVALUATION_VALUE], [ - alert.fields[ALERT_EVALUATION_THRESHOLD]!, - ]), + comparator: COMPARATORS.GREATER_THAN, + pctAboveThreshold: getPctAboveThreshold( + [alert.fields[ALERT_EVALUATION_THRESHOLD]!], + COMPARATORS.GREATER_THAN, + alert.fields[ALERT_EVALUATION_VALUE] + ), } as unknown as FlyoutThresholdData; return [SLOBurnRateFlyoutMap]; default: diff --git a/x-pack/plugins/observability_solution/observability/public/components/alert_overview/overview_columns.tsx b/x-pack/plugins/observability_solution/observability/public/components/alert_overview/overview_columns.tsx index 32820cb164309f..a1581333294ea8 100644 --- a/x-pack/plugins/observability_solution/observability/public/components/alert_overview/overview_columns.tsx +++ b/x-pack/plugins/observability_solution/observability/public/components/alert_overview/overview_columns.tsx @@ -13,6 +13,8 @@ import { AlertStatus } from '@kbn/rule-data-utils'; import moment from 'moment'; import React from 'react'; import { Tooltip as CaseTooltip } from '@kbn/cases-components'; +import { COMPARATORS } from '@kbn/alerting-comparators'; +import { LEGACY_COMPARATORS } from '../../../common/utils/convert_legacy_outside_comparator'; import type { Group } from '../../../common/custom_threshold_rule/types'; import { NavigateToCaseView } from '../../hooks/use_case_view_navigation'; import { Groups } from '../custom_threshold/components/alert_details_app_section/groups'; @@ -124,11 +126,18 @@ export const overviewColumns: Array> = [ return (
{ruleCriteria.map((criteria, criticalIndex) => { - const threshold = criteria.threshold; - const comparator = criteria.comparator; + const { threshold, comparator } = criteria; + let formattedComparator = comparator.toUpperCase(); + if ( + comparator === COMPARATORS.NOT_BETWEEN || + comparator === LEGACY_COMPARATORS.OUTSIDE_RANGE + ) { + // No need for i18n as we are using the enum value, we only need a space. + formattedComparator = 'NOT BETWEEN'; + } return ( -

{`${comparator.toUpperCase()} ${threshold}`}

+

{`${formattedComparator} ${threshold}`}

); })} diff --git a/x-pack/plugins/observability_solution/observability/public/components/custom_threshold/components/alert_details_app_section/helpers/log_rate_analysis_query.test.ts b/x-pack/plugins/observability_solution/observability/public/components/custom_threshold/components/alert_details_app_section/helpers/log_rate_analysis_query.test.ts index 0e5da33f67d6c5..05a8c3eb67a35f 100644 --- a/x-pack/plugins/observability_solution/observability/public/components/custom_threshold/components/alert_details_app_section/helpers/log_rate_analysis_query.test.ts +++ b/x-pack/plugins/observability_solution/observability/public/components/custom_threshold/components/alert_details_app_section/helpers/log_rate_analysis_query.test.ts @@ -5,7 +5,8 @@ * 2.0. */ -import { Aggregators, Comparator } from '../../../../../../common/custom_threshold_rule/types'; +import { COMPARATORS } from '@kbn/alerting-comparators'; +import { Aggregators } from '../../../../../../common/custom_threshold_rule/types'; import { CustomThresholdRuleTypeParams } from '../../../types'; import { getLogRateAnalysisEQQuery } from './log_rate_analysis_query'; @@ -29,7 +30,7 @@ describe('buildEsQuery', () => { timeSize: 1, timeUnit: 'm', threshold: [90], - comparator: Comparator.GT, + comparator: COMPARATORS.GREATER_THAN, }, ], }; @@ -85,7 +86,7 @@ describe('buildEsQuery', () => { timeSize: 1, timeUnit: 'm', threshold: [90], - comparator: Comparator.GT, + comparator: COMPARATORS.GREATER_THAN, }, ], }, @@ -108,7 +109,7 @@ describe('buildEsQuery', () => { timeSize: 1, timeUnit: 'm', threshold: [90], - comparator: Comparator.GT, + comparator: COMPARATORS.GREATER_THAN, }, ], }, diff --git a/x-pack/plugins/observability_solution/observability/public/components/custom_threshold/components/criterion_preview_chart/threshold_annotations.test.tsx b/x-pack/plugins/observability_solution/observability/public/components/custom_threshold/components/criterion_preview_chart/threshold_annotations.test.tsx index a8e64ac343a0f4..6be6f983c7e3c3 100644 --- a/x-pack/plugins/observability_solution/observability/public/components/custom_threshold/components/criterion_preview_chart/threshold_annotations.test.tsx +++ b/x-pack/plugins/observability_solution/observability/public/components/custom_threshold/components/criterion_preview_chart/threshold_annotations.test.tsx @@ -5,7 +5,7 @@ * 2.0. */ import { Color } from '../../../../../common/custom_threshold_rule/color_palette'; -import { Comparator } from '../../../../../common/custom_threshold_rule/types'; +import { COMPARATORS } from '@kbn/alerting-comparators'; import { shallow } from 'enzyme'; import React from 'react'; @@ -30,7 +30,7 @@ describe('ThresholdAnnotations', () => { const defaultProps = { threshold: [20, 30], sortedThresholds: [20, 30], - comparator: Comparator.GT, + comparator: COMPARATORS.GREATER_THAN, color: Color.color0, id: 'testId', firstTimestamp: 123456789, @@ -54,7 +54,7 @@ describe('ThresholdAnnotations', () => { }); it('should render a rectangular annotation for in between thresholds', async () => { - const wrapper = await setup({ comparator: Comparator.BETWEEN }); + const wrapper = await setup({ comparator: COMPARATORS.BETWEEN }); const annotation = wrapper.find('[data-test-subj="between-rect"]'); const expectedValues = [ @@ -73,7 +73,7 @@ describe('ThresholdAnnotations', () => { }); it('should render an upper rectangular annotation for outside range thresholds', async () => { - const wrapper = await setup({ comparator: Comparator.OUTSIDE_RANGE }); + const wrapper = await setup({ comparator: COMPARATORS.NOT_BETWEEN }); const annotation = wrapper.find('[data-test-subj="outside-range-lower-rect"]'); const expectedValues = [ @@ -92,7 +92,7 @@ describe('ThresholdAnnotations', () => { }); it('should render a lower rectangular annotation for outside range thresholds', async () => { - const wrapper = await setup({ comparator: Comparator.OUTSIDE_RANGE }); + const wrapper = await setup({ comparator: COMPARATORS.NOT_BETWEEN }); const annotation = wrapper.find('[data-test-subj="outside-range-upper-rect"]'); const expectedValues = [ @@ -111,7 +111,7 @@ describe('ThresholdAnnotations', () => { }); it('should render a rectangular annotation for below thresholds', async () => { - const wrapper = await setup({ comparator: Comparator.LT }); + const wrapper = await setup({ comparator: COMPARATORS.LESS_THAN }); const annotation = wrapper.find('[data-test-subj="below-rect"]'); const expectedValues = [ @@ -130,7 +130,7 @@ describe('ThresholdAnnotations', () => { }); it('should render a rectangular annotation for above thresholds', async () => { - const wrapper = await setup({ comparator: Comparator.GT }); + const wrapper = await setup({ comparator: COMPARATORS.GREATER_THAN }); const annotation = wrapper.find('[data-test-subj="above-rect"]'); const expectedValues = [ diff --git a/x-pack/plugins/observability_solution/observability/public/components/custom_threshold/components/criterion_preview_chart/threshold_annotations.tsx b/x-pack/plugins/observability_solution/observability/public/components/custom_threshold/components/criterion_preview_chart/threshold_annotations.tsx index e911eba6ad6c84..763d43ae5eab39 100644 --- a/x-pack/plugins/observability_solution/observability/public/components/custom_threshold/components/criterion_preview_chart/threshold_annotations.tsx +++ b/x-pack/plugins/observability_solution/observability/public/components/custom_threshold/components/criterion_preview_chart/threshold_annotations.tsx @@ -7,13 +7,13 @@ import { AnnotationDomainType, LineAnnotation, RectAnnotation } from '@elastic/charts'; import { first, last } from 'lodash'; import React from 'react'; +import { COMPARATORS } from '@kbn/alerting-comparators'; import { Color, colorTransformer } from '../../../../../common/custom_threshold_rule/color_palette'; -import { Comparator } from '../../../../../common/custom_threshold_rule/types'; interface ThresholdAnnotationsProps { threshold: number[]; sortedThresholds: number[]; - comparator: Comparator; + comparator: COMPARATORS; color: Color; id: string; firstTimestamp: number; @@ -34,8 +34,10 @@ export function ThresholdAnnotations({ domain, }: ThresholdAnnotationsProps) { if (!comparator || !threshold) return null; - const isAbove = [Comparator.GT, Comparator.GT_OR_EQ].includes(comparator); - const isBelow = [Comparator.LT, Comparator.LT_OR_EQ].includes(comparator); + const isAbove = [COMPARATORS.GREATER_THAN, COMPARATORS.GREATER_THAN_OR_EQUALS].includes( + comparator + ); + const isBelow = [COMPARATORS.LESS_THAN, COMPARATORS.LESS_THAN_OR_EQUALS].includes(comparator); return ( <> - {sortedThresholds.length === 2 && comparator === Comparator.BETWEEN ? ( + {sortedThresholds.length === 2 && comparator === COMPARATORS.BETWEEN ? ( <> ) : null} - {sortedThresholds.length === 2 && comparator === Comparator.OUTSIDE_RANGE ? ( + {sortedThresholds.length === 2 && comparator === COMPARATORS.NOT_BETWEEN ? ( <> = { timeSize: 1, timeUnit: 'm' as TimeUnitChar, threshold: [1], - comparator: Comparator.GT, + comparator: COMPARATORS.GREATER_THAN, }, fields: [ { name: 'system.cpu.user.pct', normalizedType: 'number' }, @@ -128,7 +128,7 @@ CustomEquationEditorWithEquationErrors.args = { timeSize: 1, timeUnit: 'm' as TimeUnitChar, threshold: [1], - comparator: Comparator.GT, + comparator: COMPARATORS.GREATER_THAN, }, errors: { equation: diff --git a/x-pack/plugins/observability_solution/observability/public/components/custom_threshold/components/custom_threshold.stories.tsx b/x-pack/plugins/observability_solution/observability/public/components/custom_threshold/components/custom_threshold.stories.tsx index 674ce1c5797561..f471333f536613 100644 --- a/x-pack/plugins/observability_solution/observability/public/components/custom_threshold/components/custom_threshold.stories.tsx +++ b/x-pack/plugins/observability_solution/observability/public/components/custom_threshold/components/custom_threshold.stories.tsx @@ -8,8 +8,7 @@ import React from 'react'; import { ComponentMeta } from '@storybook/react'; import { LIGHT_THEME } from '@elastic/charts'; - -import { Comparator } from '../../../../common/custom_threshold_rule/types'; +import { COMPARATORS } from '@kbn/alerting-comparators'; import { Props, Threshold as Component } from './custom_threshold'; export default { @@ -31,7 +30,7 @@ export default { const defaultProps: Props = { chartProps: { baseTheme: LIGHT_THEME }, - comparator: Comparator.GT, + comparator: COMPARATORS.GREATER_THAN, id: 'componentId', threshold: [90], title: 'Threshold breached', diff --git a/x-pack/plugins/observability_solution/observability/public/components/custom_threshold/components/custom_threshold.test.tsx b/x-pack/plugins/observability_solution/observability/public/components/custom_threshold/components/custom_threshold.test.tsx index fbb728b4b81aa3..e0c83d31ddb4cd 100644 --- a/x-pack/plugins/observability_solution/observability/public/components/custom_threshold/components/custom_threshold.test.tsx +++ b/x-pack/plugins/observability_solution/observability/public/components/custom_threshold/components/custom_threshold.test.tsx @@ -10,13 +10,13 @@ import { LIGHT_THEME } from '@elastic/charts'; import { render } from '@testing-library/react'; import { Props, Threshold } from './custom_threshold'; import React from 'react'; -import { Comparator } from '../../../../common/custom_threshold_rule/types'; +import { COMPARATORS } from '@kbn/alerting-comparators'; describe('Threshold', () => { const renderComponent = (props: Partial = {}) => { const defaultProps: Props = { chartProps: { baseTheme: LIGHT_THEME }, - comparator: Comparator.GT, + comparator: COMPARATORS.GREATER_THAN, id: 'componentId', threshold: [90], title: 'Threshold breached', @@ -43,7 +43,7 @@ describe('Threshold', () => { it('shows component for between', () => { const component = renderComponent({ - comparator: Comparator.BETWEEN, + comparator: COMPARATORS.BETWEEN, threshold: [90, 95], }); expect(component.queryByTestId('thresholdRule-90-95-93')).toBeTruthy(); diff --git a/x-pack/plugins/observability_solution/observability/public/components/custom_threshold/components/custom_threshold.tsx b/x-pack/plugins/observability_solution/observability/public/components/custom_threshold/components/custom_threshold.tsx index 7e9fbfce9d682d..a82c369c577372 100644 --- a/x-pack/plugins/observability_solution/observability/public/components/custom_threshold/components/custom_threshold.tsx +++ b/x-pack/plugins/observability_solution/observability/public/components/custom_threshold/components/custom_threshold.tsx @@ -10,7 +10,7 @@ import { Chart, Metric, Settings } from '@elastic/charts'; import { EuiIcon, EuiPanel, useEuiBackgroundColor } from '@elastic/eui'; import type { PartialTheme, Theme } from '@elastic/charts'; import { i18n } from '@kbn/i18n'; -import { Comparator } from '../../../../common/custom_threshold_rule/types'; +import { COMPARATORS } from '@kbn/alerting-comparators'; export interface ChartProps { theme?: PartialTheme; @@ -19,7 +19,7 @@ export interface ChartProps { export interface Props { chartProps: ChartProps; - comparator: Comparator | string; + comparator: COMPARATORS | string; id: string; threshold: number[]; title: string; diff --git a/x-pack/plugins/observability_solution/observability/public/components/custom_threshold/components/expression_row.test.tsx b/x-pack/plugins/observability_solution/observability/public/components/custom_threshold/components/expression_row.test.tsx index 2bdd1f2def0815..b5ea26f2fb9073 100644 --- a/x-pack/plugins/observability_solution/observability/public/components/custom_threshold/components/expression_row.test.tsx +++ b/x-pack/plugins/observability_solution/observability/public/components/custom_threshold/components/expression_row.test.tsx @@ -9,9 +9,10 @@ import { mountWithIntl, nextTick } from '@kbn/test-jest-helpers'; import React from 'react'; import { act } from 'react-dom/test-utils'; -import { Aggregators, Comparator } from '../../../../common/custom_threshold_rule/types'; +import { Aggregators } from '../../../../common/custom_threshold_rule/types'; import { MetricExpression } from '../types'; import { ExpressionRow } from './expression_row'; +import { COMPARATORS } from '@kbn/alerting-comparators'; describe('ExpressionRow', () => { async function setup(expression: MetricExpression) { @@ -57,7 +58,7 @@ describe('ExpressionRow', () => { it('should display thresholds as a percentage for pct metrics', async () => { const expression: MetricExpression = { - comparator: Comparator.GT, + comparator: COMPARATORS.GREATER_THAN, metrics: [ { name: 'A', @@ -82,7 +83,7 @@ describe('ExpressionRow', () => { it('should display thresholds as a decimal for all other metrics', async () => { const expression = { - comparator: Comparator.GT, + comparator: COMPARATORS.GREATER_THAN, metrics: [ { name: 'A', diff --git a/x-pack/plugins/observability_solution/observability/public/components/custom_threshold/components/expression_row.tsx b/x-pack/plugins/observability_solution/observability/public/components/custom_threshold/components/expression_row.tsx index ffff1212ce05b9..0d4fe7ecafbb80 100644 --- a/x-pack/plugins/observability_solution/observability/public/components/custom_threshold/components/expression_row.tsx +++ b/x-pack/plugins/observability_solution/observability/public/components/custom_threshold/components/expression_row.tsx @@ -18,35 +18,20 @@ import { i18n } from '@kbn/i18n'; import React, { useCallback, useMemo, useState, ReactElement } from 'react'; import { AggregationType, - builtInComparators, - COMPARATORS, IErrorObject, ThresholdExpression, } from '@kbn/triggers-actions-ui-plugin/public'; import { DataViewBase, DataViewFieldBase } from '@kbn/es-query'; import { debounce } from 'lodash'; -import { Aggregators, Comparator } from '../../../../common/custom_threshold_rule/types'; +import { COMPARATORS } from '@kbn/alerting-comparators'; +import { convertToBuiltInComparators } from '../../../../common/utils/convert_legacy_outside_comparator'; +import { Aggregators } from '../../../../common/custom_threshold_rule/types'; import { MetricExpression } from '../types'; import { CustomEquationEditor } from './custom_equation'; import { CUSTOM_EQUATION, LABEL_HELP_MESSAGE, LABEL_LABEL } from '../i18n_strings'; import { decimalToPct, pctToDecimal } from '../helpers/corrected_percent_convert'; import { isPercent } from '../helpers/threshold_unit'; -// Create a new object with COMPARATORS.NOT_BETWEEN removed as we use OUTSIDE_RANGE -const updatedBuiltInComparators = { ...builtInComparators }; -delete updatedBuiltInComparators[COMPARATORS.NOT_BETWEEN]; - -const customComparators = { - ...updatedBuiltInComparators, - [Comparator.OUTSIDE_RANGE]: { - text: i18n.translate('xpack.observability.customThreshold.rule.alertFlyout.outsideRangeLabel', { - defaultMessage: 'Is not between', - }), - value: Comparator.OUTSIDE_RANGE, - requiredValues: 2, - }, -}; - interface ExpressionRowProps { title: ReactElement; fields: DataViewFieldBase[]; @@ -76,14 +61,13 @@ export const ExpressionRow: React.FC = (props) => { title, } = props; - const { metrics, comparator = Comparator.GT, threshold = [] } = expression; - + const { metrics, comparator = COMPARATORS.GREATER_THAN, threshold = [] } = expression; const isMetricPct = useMemo(() => isPercent(metrics), [metrics]); const [label, setLabel] = useState(expression?.label || undefined); const updateComparator = useCallback( (c?: string) => { - setRuleParams(expressionId, { ...expression, comparator: c as Comparator }); + setRuleParams(expressionId, { ...expression, comparator: c as COMPARATORS }); }, [expressionId, expression, setRuleParams] ); @@ -214,12 +198,17 @@ const ThresholdElement: React.FC<{ return threshold; }, [threshold, isMetricPct]); + const thresholdComparator = useCallback(() => { + if (!comparator) return COMPARATORS.GREATER_THAN; + // Check if the rule had a legacy OUTSIDE_RANGE inside its params. + // Then, change it on-the-fly to NOT_BETWEEN + return convertToBuiltInComparators(comparator); + }, [comparator]); return ( <> { timeUnit: 'm', sourceId: 'default', threshold: [1], - comparator: Comparator.GT_OR_EQ, + comparator: COMPARATORS.GREATER_THAN_OR_EQUALS, }; const { wrapper } = await setup(expression); expect(wrapper.find('[data-test-subj="thresholdRuleNoChartData"]').exists()).toBeTruthy(); diff --git a/x-pack/plugins/observability_solution/observability/public/components/custom_threshold/components/rule_condition_chart/rule_condition_chart.tsx b/x-pack/plugins/observability_solution/observability/public/components/custom_threshold/components/rule_condition_chart/rule_condition_chart.tsx index 08de905213f874..1e326e3fa5f6de 100644 --- a/x-pack/plugins/observability_solution/observability/public/components/custom_threshold/components/rule_condition_chart/rule_condition_chart.tsx +++ b/x-pack/plugins/observability_solution/observability/public/components/custom_threshold/components/rule_condition_chart/rule_condition_chart.tsx @@ -25,11 +25,9 @@ import { IErrorObject } from '@kbn/triggers-actions-ui-plugin/public'; import { i18n } from '@kbn/i18n'; import { TimeRange } from '@kbn/es-query'; import { EventAnnotationConfig } from '@kbn/event-annotation-common'; +import { COMPARATORS } from '@kbn/alerting-comparators'; import { EventsAsUnit } from '../../../../../common/constants'; -import { - Comparator, - CustomThresholdSearchSourceFields, -} from '../../../../../common/custom_threshold_rule/types'; +import { CustomThresholdSearchSourceFields } from '../../../../../common/custom_threshold_rule/types'; import { useKibana } from '../../../../utils/kibana_react'; import { MetricExpression } from '../../types'; import { AggMap, PainlessTinyMathParser } from './painless_tinymath_parser'; @@ -121,15 +119,15 @@ export function RuleConditionChart({ const refLayers = []; if ( - comparator === Comparator.OUTSIDE_RANGE || - (comparator === Comparator.BETWEEN && threshold.length === 2) + comparator === COMPARATORS.NOT_BETWEEN || + (comparator === COMPARATORS.BETWEEN && threshold.length === 2) ) { const refLineStart = new XYReferenceLinesLayer({ data: [ { value: (threshold[0] || 0).toString(), color: euiTheme.colors.danger, - fill: comparator === Comparator.OUTSIDE_RANGE ? 'below' : 'none', + fill: comparator === COMPARATORS.NOT_BETWEEN ? 'below' : 'none', }, ], }); @@ -138,7 +136,7 @@ export function RuleConditionChart({ { value: (threshold[1] || 0).toString(), color: euiTheme.colors.danger, - fill: comparator === Comparator.OUTSIDE_RANGE ? 'above' : 'none', + fill: comparator === COMPARATORS.NOT_BETWEEN ? 'above' : 'none', }, ], }); @@ -146,7 +144,7 @@ export function RuleConditionChart({ refLayers.push(refLineStart, refLineEnd); } else { let fill: FillStyle = 'above'; - if (comparator === Comparator.LT || comparator === Comparator.LT_OR_EQ) { + if (comparator === COMPARATORS.LESS_THAN || comparator === COMPARATORS.LESS_THAN_OR_EQUALS) { fill = 'below'; } const thresholdRefLine = new XYReferenceLinesLayer({ diff --git a/x-pack/plugins/observability_solution/observability/public/components/custom_threshold/components/validation.tsx b/x-pack/plugins/observability_solution/observability/public/components/custom_threshold/components/validation.tsx index 87cefa2742bbbf..184187c7052388 100644 --- a/x-pack/plugins/observability_solution/observability/public/components/custom_threshold/components/validation.tsx +++ b/x-pack/plugins/observability_solution/observability/public/components/custom_threshold/components/validation.tsx @@ -11,8 +11,8 @@ import { buildEsQuery, fromKueryExpression } from '@kbn/es-query'; import { i18n } from '@kbn/i18n'; import { ValidationResult } from '@kbn/triggers-actions-ui-plugin/public'; import { isEmpty } from 'lodash'; +import { COMPARATORS } from '@kbn/alerting-comparators'; import { - Comparator, CustomMetricExpressionParams, CustomThresholdSearchSourceFields, } from '../../../../common/custom_threshold_rule/types'; @@ -111,7 +111,7 @@ export function validateCustomThreshold({ // The Threshold component returns an empty array with a length ([empty]) because it's using delete newThreshold[i]. // We need to use [...c.threshold] to convert it to an array with an undefined value ([undefined]) so we can test each element. const { comparator, threshold } = { comparator: c.comparator, threshold: c.threshold } as { - comparator?: Comparator; + comparator?: COMPARATORS; threshold?: number[]; }; if (threshold && threshold.length && ![...threshold].every(isNumber)) { @@ -130,7 +130,7 @@ export function validateCustomThreshold({ }); } - if (comparator === Comparator.BETWEEN && (!threshold || threshold.length < 2)) { + if (comparator === COMPARATORS.BETWEEN && (!threshold || threshold.length < 2)) { errors[id].critical.threshold1.push( i18n.translate( 'xpack.observability.customThreshold.rule.alertFlyout.error.thresholdRequired', diff --git a/x-pack/plugins/observability_solution/observability/public/components/custom_threshold/custom_threshold_rule_expression.test.tsx b/x-pack/plugins/observability_solution/observability/public/components/custom_threshold/custom_threshold_rule_expression.test.tsx index 8f569e1d80b178..02c428ccf36984 100644 --- a/x-pack/plugins/observability_solution/observability/public/components/custom_threshold/custom_threshold_rule_expression.test.tsx +++ b/x-pack/plugins/observability_solution/observability/public/components/custom_threshold/custom_threshold_rule_expression.test.tsx @@ -13,7 +13,8 @@ import { queryClient } from '@kbn/osquery-plugin/public/query_client'; import { mountWithIntl, nextTick } from '@kbn/test-jest-helpers'; import { QueryClientProvider } from '@tanstack/react-query'; import { act } from 'react-dom/test-utils'; -import { Aggregators, Comparator } from '../../../common/custom_threshold_rule/types'; +import { COMPARATORS } from '@kbn/alerting-comparators'; +import { Aggregators } from '../../../common/custom_threshold_rule/types'; import { useKibana } from '../../utils/kibana_react'; import { kibanaStartMock } from '../../utils/kibana_react.mock'; import Expressions from './custom_threshold_rule_expression'; @@ -152,7 +153,7 @@ describe('Expression', () => { aggType: Aggregators.COUNT, }, ], - comparator: Comparator.GT, + comparator: COMPARATORS.GREATER_THAN, threshold: [100], timeSize: 1, timeUnit: 'm', @@ -178,7 +179,7 @@ describe('Expression', () => { { name: 'A', aggType: Aggregators.AVERAGE, field: 'system.load.1' }, { name: 'B', aggType: Aggregators.CARDINALITY, field: 'system.cpu.user.pct' }, ], - comparator: Comparator.LT_OR_EQ, + comparator: COMPARATORS.LESS_THAN_OR_EQUALS, equation: 'A * B', label: 'prefill label', threshold: [500], @@ -200,7 +201,7 @@ describe('Expression', () => { { name: 'A', aggType: Aggregators.AVERAGE, field: 'system.load.1' }, { name: 'B', aggType: Aggregators.CARDINALITY, field: 'system.cpu.user.pct' }, ], - comparator: Comparator.LT_OR_EQ, + comparator: COMPARATORS.LESS_THAN_OR_EQUALS, equation: 'A * B', label: 'prefill label', threshold: [500], diff --git a/x-pack/plugins/observability_solution/observability/public/components/custom_threshold/custom_threshold_rule_expression.tsx b/x-pack/plugins/observability_solution/observability/public/components/custom_threshold/custom_threshold_rule_expression.tsx index 41127b2880233d..f3aff841233cfe 100644 --- a/x-pack/plugins/observability_solution/observability/public/components/custom_threshold/custom_threshold_rule_expression.tsx +++ b/x-pack/plugins/observability_solution/observability/public/components/custom_threshold/custom_threshold_rule_expression.tsx @@ -36,8 +36,9 @@ import { RuleTypeParamsExpressionProps, } from '@kbn/triggers-actions-ui-plugin/public'; +import { COMPARATORS } from '@kbn/alerting-comparators'; import { useKibana } from '../../utils/kibana_react'; -import { Aggregators, Comparator } from '../../../common/custom_threshold_rule/types'; +import { Aggregators } from '../../../common/custom_threshold_rule/types'; import { TimeUnitChar } from '../../../common/utils/formatters/duration'; import { AlertContextMeta, AlertParams, MetricExpression } from './types'; import { ExpressionRow } from './components/expression_row'; @@ -53,7 +54,7 @@ type Props = Omit< >; export const defaultExpression: MetricExpression = { - comparator: Comparator.GT, + comparator: COMPARATORS.GREATER_THAN, metrics: [ { name: 'A', diff --git a/x-pack/plugins/observability_solution/observability/public/components/custom_threshold/mocks/custom_threshold_rule.ts b/x-pack/plugins/observability_solution/observability/public/components/custom_threshold/mocks/custom_threshold_rule.ts index 045c04219801e4..efa32473e0b445 100644 --- a/x-pack/plugins/observability_solution/observability/public/components/custom_threshold/mocks/custom_threshold_rule.ts +++ b/x-pack/plugins/observability_solution/observability/public/components/custom_threshold/mocks/custom_threshold_rule.ts @@ -7,8 +7,9 @@ import { v4 as uuidv4 } from 'uuid'; import { ParsedTechnicalFields } from '@kbn/rule-registry-plugin/common'; +import { COMPARATORS } from '@kbn/alerting-comparators'; import { CustomThresholdAlertFields } from '../types'; -import { Aggregators, Comparator } from '../../../../common/custom_threshold_rule/types'; +import { Aggregators } from '../../../../common/custom_threshold_rule/types'; import { CustomThresholdAlert, CustomThresholdRule } from '../components/types'; @@ -60,7 +61,7 @@ export const buildCustomThresholdRule = ( params: { criteria: [ { - comparator: Comparator.GT, + comparator: COMPARATORS.GREATER_THAN, metrics: [ { name: 'A', @@ -72,7 +73,7 @@ export const buildCustomThresholdRule = ( timeUnit: 'm', }, { - comparator: Comparator.GT, + comparator: COMPARATORS.GREATER_THAN, metrics: [ { name: 'B', @@ -83,11 +84,9 @@ export const buildCustomThresholdRule = ( threshold: [4], timeSize: 15, timeUnit: 'm', - warningComparator: Comparator.GT, - warningThreshold: [2.2], }, { - comparator: Comparator.GT, + comparator: COMPARATORS.GREATER_THAN, metrics: [ { name: 'C', @@ -100,7 +99,7 @@ export const buildCustomThresholdRule = ( timeUnit: 'm', }, { - comparator: Comparator.GT, + comparator: COMPARATORS.GREATER_THAN, metrics: [ { name: 'A', @@ -115,7 +114,7 @@ export const buildCustomThresholdRule = ( 'A + A + A + A + A + A + A + A + A + A + A + A + A + A + A + A + A + A + A + A + A', }, { - comparator: Comparator.GT, + comparator: COMPARATORS.GREATER_THAN, metrics: [ { name: 'C', @@ -133,7 +132,7 @@ export const buildCustomThresholdRule = ( timeUnit: 'm', }, { - comparator: Comparator.GT, + comparator: COMPARATORS.GREATER_THAN, metrics: [ { name: 'CAD', @@ -209,7 +208,7 @@ export const buildCustomThresholdAlert = ( 'kibana.alert.rule.parameters': { criteria: [ { - comparator: Comparator.GT, + comparator: COMPARATORS.GREATER_THAN, metrics: [ { name: 'A', @@ -222,7 +221,7 @@ export const buildCustomThresholdAlert = ( timeUnit: 'm', }, { - comparator: Comparator.GT, + comparator: COMPARATORS.GREATER_THAN, metrics: [ { name: 'B', @@ -233,7 +232,7 @@ export const buildCustomThresholdAlert = ( threshold: [4], timeSize: 15, timeUnit: 'm', - warningComparator: Comparator.GT, + warningComparator: COMPARATORS.GREATER_THAN, warningThreshold: [2.2], }, ], diff --git a/x-pack/plugins/observability_solution/observability/public/pages/rule_details/rule_details.tsx b/x-pack/plugins/observability_solution/observability/public/pages/rule_details/rule_details.tsx index bdaf10b04ca694..31ae9e41d05291 100644 --- a/x-pack/plugins/observability_solution/observability/public/pages/rule_details/rule_details.tsx +++ b/x-pack/plugins/observability_solution/observability/public/pages/rule_details/rule_details.tsx @@ -194,7 +194,6 @@ export function RuleDetailsPage() { : ''; if (isLoading || isRuleDeleting) return ; - if (!rule || isError) return ; return ( diff --git a/x-pack/plugins/observability_solution/observability/server/lib/rules/custom_threshold/custom_threshold_executor.test.ts b/x-pack/plugins/observability_solution/observability/server/lib/rules/custom_threshold/custom_threshold_executor.test.ts index 8f65682c68fb10..cb99f17a9db5c1 100644 --- a/x-pack/plugins/observability_solution/observability/server/lib/rules/custom_threshold/custom_threshold_executor.test.ts +++ b/x-pack/plugins/observability_solution/observability/server/lib/rules/custom_threshold/custom_threshold_executor.test.ts @@ -13,9 +13,9 @@ import { FIRED_ACTION, NO_DATA_ACTION } from './constants'; import { Evaluation } from './lib/evaluate_rule'; import type { LogMeta, Logger } from '@kbn/logging'; import { DEFAULT_FLAPPING_SETTINGS } from '@kbn/alerting-plugin/common'; +import { COMPARATORS } from '@kbn/alerting-comparators'; import { Aggregators, - Comparator, CustomMetricExpressionParams, CustomThresholdExpressionMetric, } from '../../../../common/custom_threshold_rule/types'; @@ -234,7 +234,7 @@ describe('The custom threshold alert type', () => { beforeEach(() => jest.clearAllMocks()); afterAll(() => clearInstances()); const instanceID = '*'; - const execute = (comparator: Comparator, threshold: number[], sourceId: string = 'default') => + const execute = (comparator: COMPARATORS, threshold: number[], sourceId: string = 'default') => executor({ ...mockOptions, services, @@ -251,7 +251,7 @@ describe('The custom threshold alert type', () => { }, }); const setResults = ( - comparator: Comparator, + comparator: COMPARATORS, threshold: number[], shouldFire: boolean = false, isNoData: boolean = false @@ -271,62 +271,62 @@ describe('The custom threshold alert type', () => { }, ]); test('alerts as expected with the > comparator', async () => { - setResults(Comparator.GT, [0.75], true); - await execute(Comparator.GT, [0.75]); + setResults(COMPARATORS.GREATER_THAN, [0.75], true); + await execute(COMPARATORS.GREATER_THAN, [0.75]); expect(getLastReportedAlert(instanceID)).toHaveAlertAction(); - setResults(Comparator.GT, [1.5], false); - await execute(Comparator.GT, [1.5]); + setResults(COMPARATORS.GREATER_THAN, [1.5], false); + await execute(COMPARATORS.GREATER_THAN, [1.5]); expect(getLastReportedAlert(instanceID)).toBe(undefined); }); test('alerts as expected with the < comparator', async () => { - setResults(Comparator.LT, [1.5], true); - await execute(Comparator.LT, [1.5]); + setResults(COMPARATORS.LESS_THAN, [1.5], true); + await execute(COMPARATORS.LESS_THAN, [1.5]); expect(getLastReportedAlert(instanceID)).toHaveAlertAction(); - setResults(Comparator.LT, [0.75], false); - await execute(Comparator.LT, [0.75]); + setResults(COMPARATORS.LESS_THAN, [0.75], false); + await execute(COMPARATORS.LESS_THAN, [0.75]); expect(getLastReportedAlert(instanceID)).toBe(undefined); }); test('alerts as expected with the >= comparator', async () => { - setResults(Comparator.GT_OR_EQ, [0.75], true); - await execute(Comparator.GT_OR_EQ, [0.75]); + setResults(COMPARATORS.GREATER_THAN_OR_EQUALS, [0.75], true); + await execute(COMPARATORS.GREATER_THAN_OR_EQUALS, [0.75]); expect(getLastReportedAlert(instanceID)).toHaveAlertAction(); - setResults(Comparator.GT_OR_EQ, [1.0], true); - await execute(Comparator.GT_OR_EQ, [1.0]); + setResults(COMPARATORS.GREATER_THAN_OR_EQUALS, [1.0], true); + await execute(COMPARATORS.GREATER_THAN_OR_EQUALS, [1.0]); expect(getLastReportedAlert(instanceID)).toHaveAlertAction(); - setResults(Comparator.GT_OR_EQ, [1.5], false); - await execute(Comparator.GT_OR_EQ, [1.5]); + setResults(COMPARATORS.GREATER_THAN_OR_EQUALS, [1.5], false); + await execute(COMPARATORS.GREATER_THAN_OR_EQUALS, [1.5]); expect(getLastReportedAlert(instanceID)).toBe(undefined); }); test('alerts as expected with the <= comparator', async () => { - setResults(Comparator.LT_OR_EQ, [1.5], true); - await execute(Comparator.LT_OR_EQ, [1.5]); + setResults(COMPARATORS.LESS_THAN_OR_EQUALS, [1.5], true); + await execute(COMPARATORS.LESS_THAN_OR_EQUALS, [1.5]); expect(getLastReportedAlert(instanceID)).toHaveAlertAction(); - setResults(Comparator.LT_OR_EQ, [1.0], true); - await execute(Comparator.LT_OR_EQ, [1.0]); + setResults(COMPARATORS.LESS_THAN_OR_EQUALS, [1.0], true); + await execute(COMPARATORS.LESS_THAN_OR_EQUALS, [1.0]); expect(getLastReportedAlert(instanceID)).toHaveAlertAction(); - setResults(Comparator.LT_OR_EQ, [0.75], false); - await execute(Comparator.LT_OR_EQ, [0.75]); + setResults(COMPARATORS.LESS_THAN_OR_EQUALS, [0.75], false); + await execute(COMPARATORS.LESS_THAN_OR_EQUALS, [0.75]); expect(getLastReportedAlert(instanceID)).toBe(undefined); }); test('alerts as expected with the between comparator', async () => { - setResults(Comparator.BETWEEN, [0, 1.5], true); - await execute(Comparator.BETWEEN, [0, 1.5]); + setResults(COMPARATORS.BETWEEN, [0, 1.5], true); + await execute(COMPARATORS.BETWEEN, [0, 1.5]); expect(getLastReportedAlert(instanceID)).toHaveAlertAction(); - setResults(Comparator.BETWEEN, [0, 0.75], false); - await execute(Comparator.BETWEEN, [0, 0.75]); + setResults(COMPARATORS.BETWEEN, [0, 0.75], false); + await execute(COMPARATORS.BETWEEN, [0, 0.75]); expect(getLastReportedAlert(instanceID)).toBe(undefined); }); - test('alerts as expected with the outside range comparator', async () => { - setResults(Comparator.OUTSIDE_RANGE, [0, 0.75], true); - await execute(Comparator.OUTSIDE_RANGE, [0, 0.75]); + test('alerts as expected with the not between comparator', async () => { + setResults(COMPARATORS.NOT_BETWEEN, [0, 0.75], true); + await execute(COMPARATORS.NOT_BETWEEN, [0, 0.75]); expect(getLastReportedAlert(instanceID)).toHaveAlertAction(); - setResults(Comparator.OUTSIDE_RANGE, [0, 1.5], false); - await execute(Comparator.OUTSIDE_RANGE, [0, 1.5]); + setResults(COMPARATORS.NOT_BETWEEN, [0, 1.5], false); + await execute(COMPARATORS.NOT_BETWEEN, [0, 1.5]); expect(getLastReportedAlert(instanceID)).toBe(undefined); }); test('reports expected values to the action context', async () => { - setResults(Comparator.GT, [0.75], true); - await execute(Comparator.GT, [0.75]); + setResults(COMPARATORS.GREATER_THAN, [0.75], true); + await execute(COMPARATORS.GREATER_THAN, [0.75]); const reportedAlert = getLastReportedAlert(instanceID); expect(reportedAlert?.context?.group).toBeUndefined(); expect(reportedAlert?.context?.reason).toBe( @@ -339,7 +339,7 @@ describe('The custom threshold alert type', () => { beforeEach(() => jest.clearAllMocks()); afterAll(() => clearInstances()); const execute = ( - comparator: Comparator, + comparator: COMPARATORS, threshold: number[], groupBy: string[] = ['groupByField'], metrics?: CustomThresholdExpressionMetric[], @@ -369,7 +369,7 @@ describe('The custom threshold alert type', () => { { a: { ...customThresholdNonCountCriterion, - comparator: Comparator.GT, + comparator: COMPARATORS.GREATER_THAN, threshold: [0.75], currentValue: 1.0, timestamp: new Date().toISOString(), @@ -379,7 +379,7 @@ describe('The custom threshold alert type', () => { }, b: { ...customThresholdNonCountCriterion, - comparator: Comparator.GT, + comparator: COMPARATORS.GREATER_THAN, threshold: [0.75], currentValue: 1.0, timestamp: new Date().toISOString(), @@ -389,7 +389,7 @@ describe('The custom threshold alert type', () => { }, }, ]); - await execute(Comparator.GT, [0.75]); + await execute(COMPARATORS.GREATER_THAN, [0.75]); expect(getLastReportedAlert(instanceIdA)).toHaveAlertAction(); expect(getLastReportedAlert(instanceIdB)).toHaveAlertAction(); }); @@ -398,7 +398,7 @@ describe('The custom threshold alert type', () => { { a: { ...customThresholdNonCountCriterion, - comparator: Comparator.LT, + comparator: COMPARATORS.LESS_THAN, threshold: [1.5], currentValue: 1.0, timestamp: new Date().toISOString(), @@ -408,7 +408,7 @@ describe('The custom threshold alert type', () => { }, b: { ...customThresholdNonCountCriterion, - comparator: Comparator.LT, + comparator: COMPARATORS.LESS_THAN, threshold: [1.5], currentValue: 3, timestamp: new Date().toISOString(), @@ -418,7 +418,7 @@ describe('The custom threshold alert type', () => { }, }, ]); - await execute(Comparator.LT, [1.5]); + await execute(COMPARATORS.LESS_THAN, [1.5]); expect(getLastReportedAlert(instanceIdA)).toHaveAlertAction(); expect(getLastReportedAlert(instanceIdB)).toBe(undefined); }); @@ -427,7 +427,7 @@ describe('The custom threshold alert type', () => { { a: { ...customThresholdNonCountCriterion, - comparator: Comparator.GT, + comparator: COMPARATORS.GREATER_THAN, threshold: [5], currentValue: 1.0, timestamp: new Date().toISOString(), @@ -437,7 +437,7 @@ describe('The custom threshold alert type', () => { }, b: { ...customThresholdNonCountCriterion, - comparator: Comparator.GT, + comparator: COMPARATORS.GREATER_THAN, threshold: [5], currentValue: 3, timestamp: new Date().toISOString(), @@ -447,7 +447,7 @@ describe('The custom threshold alert type', () => { }, }, ]); - await execute(Comparator.GT, [5]); + await execute(COMPARATORS.GREATER_THAN, [5]); expect(getLastReportedAlert(instanceIdA)).toBe(undefined); expect(getLastReportedAlert(instanceIdB)).toBe(undefined); }); @@ -456,7 +456,7 @@ describe('The custom threshold alert type', () => { { a: { ...customThresholdNonCountCriterion, - comparator: Comparator.GT, + comparator: COMPARATORS.GREATER_THAN, threshold: [0.75], currentValue: 1.0, timestamp: new Date().toISOString(), @@ -466,7 +466,7 @@ describe('The custom threshold alert type', () => { }, b: { ...customThresholdNonCountCriterion, - comparator: Comparator.GT, + comparator: COMPARATORS.GREATER_THAN, threshold: [0.75], currentValue: 3, timestamp: new Date().toISOString(), @@ -476,7 +476,7 @@ describe('The custom threshold alert type', () => { }, }, ]); - await execute(Comparator.GT, [0.75]); + await execute(COMPARATORS.GREATER_THAN, [0.75]); expect(getLastReportedAlert(instanceIdA)?.context?.group).toEqual([ { field: 'groupByField', value: 'a' }, ]); @@ -489,7 +489,7 @@ describe('The custom threshold alert type', () => { { a: { ...customThresholdNonCountCriterion, - comparator: Comparator.GT, + comparator: COMPARATORS.GREATER_THAN, threshold: [0.75], metrics: [ { @@ -506,7 +506,7 @@ describe('The custom threshold alert type', () => { }, b: { ...customThresholdNonCountCriterion, - comparator: Comparator.GT, + comparator: COMPARATORS.GREATER_THAN, threshold: [0.75], metrics: [ { @@ -523,7 +523,7 @@ describe('The custom threshold alert type', () => { }, c: { ...customThresholdNonCountCriterion, - comparator: Comparator.GT, + comparator: COMPARATORS.GREATER_THAN, threshold: [0.75], metrics: [ { @@ -541,7 +541,7 @@ describe('The custom threshold alert type', () => { }, ]); const { state: stateResult1 } = await execute( - Comparator.GT, + COMPARATORS.GREATER_THAN, [0.75], ['groupByField'], [ @@ -557,7 +557,7 @@ describe('The custom threshold alert type', () => { { a: { ...customThresholdNonCountCriterion, - comparator: Comparator.GT, + comparator: COMPARATORS.GREATER_THAN, threshold: [0.75], currentValue: 1.0, timestamp: new Date().toISOString(), @@ -567,7 +567,7 @@ describe('The custom threshold alert type', () => { }, b: { ...customThresholdNonCountCriterion, - comparator: Comparator.GT, + comparator: COMPARATORS.GREATER_THAN, threshold: [0.75], currentValue: 3, timestamp: new Date().toISOString(), @@ -577,7 +577,7 @@ describe('The custom threshold alert type', () => { }, c: { ...customThresholdNonCountCriterion, - comparator: Comparator.GT, + comparator: COMPARATORS.GREATER_THAN, threshold: [0.75], currentValue: null, timestamp: new Date().toISOString(), @@ -588,7 +588,7 @@ describe('The custom threshold alert type', () => { }, ]); const { state: stateResult2 } = await execute( - Comparator.GT, + COMPARATORS.GREATER_THAN, [0.75], ['groupByField'], [ @@ -607,7 +607,7 @@ describe('The custom threshold alert type', () => { { a: { ...customThresholdNonCountCriterion, - comparator: Comparator.GT, + comparator: COMPARATORS.GREATER_THAN, threshold: [0.75], currentValue: 1.0, timestamp: new Date().toISOString(), @@ -617,7 +617,7 @@ describe('The custom threshold alert type', () => { }, b: { ...customThresholdNonCountCriterion, - comparator: Comparator.GT, + comparator: COMPARATORS.GREATER_THAN, threshold: [0.75], currentValue: 3, timestamp: new Date().toISOString(), @@ -628,7 +628,7 @@ describe('The custom threshold alert type', () => { }, ]); const { state: stateResult3 } = await execute( - Comparator.GT, + COMPARATORS.GREATER_THAN, [0.75], ['groupByField', 'groupByField-else'], [ @@ -644,7 +644,7 @@ describe('The custom threshold alert type', () => { }); const executeWithFilter = ( - comparator: Comparator, + comparator: COMPARATORS, threshold: number[], filterQuery: string, metrics?: any, @@ -679,7 +679,7 @@ describe('The custom threshold alert type', () => { { a: { ...customThresholdNonCountCriterion, - comparator: Comparator.GT, + comparator: COMPARATORS.GREATER_THAN, threshold: [0.75], metrics: [ { @@ -696,7 +696,7 @@ describe('The custom threshold alert type', () => { }, b: { ...customThresholdNonCountCriterion, - comparator: Comparator.GT, + comparator: COMPARATORS.GREATER_THAN, threshold: [0.75], metrics: [ { @@ -713,7 +713,7 @@ describe('The custom threshold alert type', () => { }, c: { ...customThresholdNonCountCriterion, - comparator: Comparator.GT, + comparator: COMPARATORS.GREATER_THAN, threshold: [0.75], metrics: [ { @@ -731,7 +731,7 @@ describe('The custom threshold alert type', () => { }, ]); const { state: stateResult1 } = await executeWithFilter( - Comparator.GT, + COMPARATORS.GREATER_THAN, [0.75], JSON.stringify({ query: 'q' }), 'test.metric.2' @@ -741,7 +741,7 @@ describe('The custom threshold alert type', () => { { a: { ...customThresholdNonCountCriterion, - comparator: Comparator.GT, + comparator: COMPARATORS.GREATER_THAN, threshold: [0.75], currentValue: 1.0, timestamp: new Date().toISOString(), @@ -751,7 +751,7 @@ describe('The custom threshold alert type', () => { }, b: { ...customThresholdNonCountCriterion, - comparator: Comparator.GT, + comparator: COMPARATORS.GREATER_THAN, threshold: [0.75], currentValue: 3, timestamp: new Date().toISOString(), @@ -761,7 +761,7 @@ describe('The custom threshold alert type', () => { }, c: { ...customThresholdNonCountCriterion, - comparator: Comparator.GT, + comparator: COMPARATORS.GREATER_THAN, threshold: [0.75], currentValue: null, timestamp: new Date().toISOString(), @@ -772,7 +772,7 @@ describe('The custom threshold alert type', () => { }, ]); const { state: stateResult2 } = await executeWithFilter( - Comparator.GT, + COMPARATORS.GREATER_THAN, [0.75], JSON.stringify({ query: 'q' }), 'test.metric.1', @@ -785,7 +785,7 @@ describe('The custom threshold alert type', () => { { a: { ...customThresholdNonCountCriterion, - comparator: Comparator.GT, + comparator: COMPARATORS.GREATER_THAN, threshold: [0.75], currentValue: 1.0, timestamp: new Date().toISOString(), @@ -795,7 +795,7 @@ describe('The custom threshold alert type', () => { }, b: { ...customThresholdNonCountCriterion, - comparator: Comparator.GT, + comparator: COMPARATORS.GREATER_THAN, threshold: [0.75], currentValue: 3, timestamp: new Date().toISOString(), @@ -806,7 +806,7 @@ describe('The custom threshold alert type', () => { }, ]); const { state: stateResult3 } = await executeWithFilter( - Comparator.GT, + COMPARATORS.GREATER_THAN, [0.75], JSON.stringify({ query: 'different' }), [ @@ -825,7 +825,7 @@ describe('The custom threshold alert type', () => { { a: { ...customThresholdNonCountCriterion, - comparator: Comparator.GT, + comparator: COMPARATORS.GREATER_THAN, threshold: [0.75], metrics: [ { @@ -842,7 +842,7 @@ describe('The custom threshold alert type', () => { }, b: { ...customThresholdNonCountCriterion, - comparator: Comparator.GT, + comparator: COMPARATORS.GREATER_THAN, threshold: [0.75], metrics: [ { @@ -859,7 +859,7 @@ describe('The custom threshold alert type', () => { }, c: { ...customThresholdNonCountCriterion, - comparator: Comparator.GT, + comparator: COMPARATORS.GREATER_THAN, threshold: [0.75], metrics: [ { @@ -877,7 +877,7 @@ describe('The custom threshold alert type', () => { }, ]); const { state: stateResult1 } = await executeWithFilter( - Comparator.GT, + COMPARATORS.GREATER_THAN, [0.75], JSON.stringify({ query: 'q' }), 'test.metric.2' @@ -887,7 +887,7 @@ describe('The custom threshold alert type', () => { { a: { ...customThresholdNonCountCriterion, - comparator: Comparator.GT, + comparator: COMPARATORS.GREATER_THAN, threshold: [0.75], currentValue: 1.0, timestamp: new Date().toISOString(), @@ -897,7 +897,7 @@ describe('The custom threshold alert type', () => { }, b: { ...customThresholdNonCountCriterion, - comparator: Comparator.GT, + comparator: COMPARATORS.GREATER_THAN, threshold: [0.75], currentValue: null, timestamp: new Date().toISOString(), @@ -907,7 +907,7 @@ describe('The custom threshold alert type', () => { }, c: { ...customThresholdNonCountCriterion, - comparator: Comparator.GT, + comparator: COMPARATORS.GREATER_THAN, threshold: [0.75], currentValue: null, timestamp: new Date().toISOString(), @@ -918,7 +918,7 @@ describe('The custom threshold alert type', () => { }, ]); const { state: stateResult2 } = await executeWithFilter( - Comparator.GT, + COMPARATORS.GREATER_THAN, [0.75], JSON.stringify({ query: 'q' }), 'test.metric.1', @@ -934,7 +934,7 @@ describe('The custom threshold alert type', () => { { a: { ...customThresholdNonCountCriterion, - comparator: Comparator.GT, + comparator: COMPARATORS.GREATER_THAN, threshold: [0.75], currentValue: 1.0, timestamp: new Date().toISOString(), @@ -944,7 +944,7 @@ describe('The custom threshold alert type', () => { }, b: { ...customThresholdNonCountCriterion, - comparator: Comparator.GT, + comparator: COMPARATORS.GREATER_THAN, threshold: [0.75], currentValue: null, timestamp: new Date().toISOString(), @@ -957,7 +957,7 @@ describe('The custom threshold alert type', () => { // Consider c as untracked services.alertsClient.isTrackedAlert.mockImplementation((id: string) => id !== 'c'); const { state: stateResult3 } = await executeWithFilter( - Comparator.GT, + COMPARATORS.GREATER_THAN, [0.75], JSON.stringify({ query: 'q' }), 'test.metric.1', @@ -973,7 +973,7 @@ describe('The custom threshold alert type', () => { describe('querying with a groupBy parameter host.name and rule tags', () => { afterAll(() => clearInstances()); const execute = ( - comparator: Comparator, + comparator: COMPARATORS, threshold: number[], groupBy: string[] = ['host.name'], metrics?: any, @@ -1008,7 +1008,7 @@ describe('The custom threshold alert type', () => { { 'host-01': { ...customThresholdNonCountCriterion, - comparator: Comparator.GT, + comparator: COMPARATORS.GREATER_THAN, threshold: [0.75], currentValue: 1.0, timestamp: new Date().toISOString(), @@ -1021,7 +1021,7 @@ describe('The custom threshold alert type', () => { }, 'host-02': { ...customThresholdNonCountCriterion, - comparator: Comparator.GT, + comparator: COMPARATORS.GREATER_THAN, threshold: [0.75], currentValue: 3, timestamp: new Date().toISOString(), @@ -1034,7 +1034,7 @@ describe('The custom threshold alert type', () => { }, }, ]); - await execute(Comparator.GT, [0.75]); + await execute(COMPARATORS.GREATER_THAN, [0.75]); expect(getLastReportedAlert(instanceIdA)?.context?.tags).toStrictEqual([ 'host-01_tag1', 'host-01_tag2', @@ -1053,7 +1053,7 @@ describe('The custom threshold alert type', () => { describe('querying without a groupBy parameter and rule tags', () => { afterAll(() => clearInstances()); const execute = ( - comparator: Comparator, + comparator: COMPARATORS, threshold: number[], groupBy: string = '', metrics?: any, @@ -1086,7 +1086,7 @@ describe('The custom threshold alert type', () => { { '*': { ...customThresholdNonCountCriterion, - comparator: Comparator.GT, + comparator: COMPARATORS.GREATER_THAN, threshold: [0.75], currentValue: 1.0, timestamp: new Date().toISOString(), @@ -1098,7 +1098,7 @@ describe('The custom threshold alert type', () => { ]); const instanceID = '*'; - await execute(Comparator.GT, [0.75]); + await execute(COMPARATORS.GREATER_THAN, [0.75]); expect(getLastReportedAlert(instanceID)?.context?.tags).toStrictEqual([ 'ruleTag1', 'ruleTag2', @@ -1109,7 +1109,7 @@ describe('The custom threshold alert type', () => { describe('querying with multiple criteria', () => { afterAll(() => clearInstances()); const execute = ( - comparator: Comparator, + comparator: COMPARATORS, thresholdA: number[], thresholdB: number[], groupBy: string = '', @@ -1148,7 +1148,7 @@ describe('The custom threshold alert type', () => { { '*': { ...customThresholdNonCountCriterion, - comparator: Comparator.GT_OR_EQ, + comparator: COMPARATORS.GREATER_THAN_OR_EQUALS, threshold: [1.0], currentValue: 1.0, timestamp: new Date().toISOString(), @@ -1160,7 +1160,7 @@ describe('The custom threshold alert type', () => { { '*': { ...customThresholdNonCountCriterion, - comparator: Comparator.GT_OR_EQ, + comparator: COMPARATORS.GREATER_THAN_OR_EQUALS, threshold: [3.0], currentValue: 3.0, timestamp: new Date().toISOString(), @@ -1171,7 +1171,7 @@ describe('The custom threshold alert type', () => { }, ]); const instanceID = '*'; - await execute(Comparator.GT_OR_EQ, [1.0], [3.0]); + await execute(COMPARATORS.GREATER_THAN_OR_EQUALS, [1.0], [3.0]); expect(getLastReportedAlert(instanceID)).toHaveAlertAction(); }); test('sends no alert when some, but not all, criteria cross the threshold', async () => { @@ -1179,7 +1179,7 @@ describe('The custom threshold alert type', () => { { '*': { ...customThresholdNonCountCriterion, - comparator: Comparator.LT_OR_EQ, + comparator: COMPARATORS.LESS_THAN_OR_EQUALS, threshold: [1.0], currentValue: 1.0, timestamp: new Date().toISOString(), @@ -1191,7 +1191,7 @@ describe('The custom threshold alert type', () => { {}, ]); const instanceID = '*'; - await execute(Comparator.LT_OR_EQ, [1.0], [2.5]); + await execute(COMPARATORS.LESS_THAN_OR_EQUALS, [1.0], [2.5]); expect(getLastReportedAlert(instanceID)).toBe(undefined); }); test('alerts only on groups that meet all criteria when querying with a groupBy parameter', async () => { @@ -1199,7 +1199,7 @@ describe('The custom threshold alert type', () => { { a: { ...customThresholdNonCountCriterion, - comparator: Comparator.GT_OR_EQ, + comparator: COMPARATORS.GREATER_THAN_OR_EQUALS, threshold: [1.0], currentValue: 1.0, timestamp: new Date().toISOString(), @@ -1209,7 +1209,7 @@ describe('The custom threshold alert type', () => { }, b: { ...customThresholdNonCountCriterion, - comparator: Comparator.GT_OR_EQ, + comparator: COMPARATORS.GREATER_THAN_OR_EQUALS, threshold: [1.0], currentValue: 3.0, timestamp: new Date().toISOString(), @@ -1221,7 +1221,7 @@ describe('The custom threshold alert type', () => { { a: { ...customThresholdNonCountCriterion, - comparator: Comparator.GT_OR_EQ, + comparator: COMPARATORS.GREATER_THAN_OR_EQUALS, threshold: [3.0], metrics: [ { @@ -1238,7 +1238,7 @@ describe('The custom threshold alert type', () => { }, b: { ...customThresholdNonCountCriterion, - comparator: Comparator.GT_OR_EQ, + comparator: COMPARATORS.GREATER_THAN_OR_EQUALS, threshold: [3.0], metrics: [ { @@ -1257,7 +1257,7 @@ describe('The custom threshold alert type', () => { ]); const instanceIdA = 'a'; const instanceIdB = 'b'; - await execute(Comparator.GT_OR_EQ, [1.0], [3.0], 'groupByField'); + await execute(COMPARATORS.GREATER_THAN_OR_EQUALS, [1.0], [3.0], 'groupByField'); expect(getLastReportedAlert(instanceIdA)).toHaveAlertAction(); expect(getLastReportedAlert(instanceIdB)).toBe(undefined); }); @@ -1266,7 +1266,7 @@ describe('The custom threshold alert type', () => { { '*': { ...customThresholdNonCountCriterion, - comparator: Comparator.GT_OR_EQ, + comparator: COMPARATORS.GREATER_THAN_OR_EQUALS, threshold: [1.0], currentValue: 1.0, timestamp: new Date().toISOString(), @@ -1278,7 +1278,7 @@ describe('The custom threshold alert type', () => { { '*': { ...customThresholdNonCountCriterion, - comparator: Comparator.GT_OR_EQ, + comparator: COMPARATORS.GREATER_THAN_OR_EQUALS, threshold: [3.0], metrics: [ { @@ -1296,7 +1296,7 @@ describe('The custom threshold alert type', () => { }, ]); const instanceID = '*'; - await execute(Comparator.GT_OR_EQ, [1.0], [3.0]); + await execute(COMPARATORS.GREATER_THAN_OR_EQUALS, [1.0], [3.0]); const reportedAlert = getLastReportedAlert(instanceID); const reasons = reportedAlert?.context?.reason; expect(reasons).toBe( @@ -1308,7 +1308,7 @@ describe('The custom threshold alert type', () => { describe('querying with the count aggregator', () => { afterAll(() => clearInstances()); const instanceID = '*'; - const execute = (comparator: Comparator, threshold: number[], sourceId: string = 'default') => + const execute = (comparator: COMPARATORS, threshold: number[], sourceId: string = 'default') => executor({ ...mockOptions, services, @@ -1329,7 +1329,7 @@ describe('The custom threshold alert type', () => { { '*': { ...customThresholdCountCriterion, - comparator: Comparator.GT, + comparator: COMPARATORS.GREATER_THAN, threshold: [0.9], currentValue: 1, timestamp: new Date().toISOString(), @@ -1339,13 +1339,13 @@ describe('The custom threshold alert type', () => { }, }, ]); - await execute(Comparator.GT, [0.9]); + await execute(COMPARATORS.GREATER_THAN, [0.9]); expect(getLastReportedAlert(instanceID)).toHaveAlertAction(); setEvaluationResults([ { '*': { ...customThresholdCountCriterion, - comparator: Comparator.LT, + comparator: COMPARATORS.LESS_THAN, threshold: [0.5], currentValue: 1, timestamp: new Date().toISOString(), @@ -1355,12 +1355,12 @@ describe('The custom threshold alert type', () => { }, }, ]); - await execute(Comparator.LT, [0.5]); + await execute(COMPARATORS.LESS_THAN, [0.5]); expect(getLastReportedAlert(instanceID)).toBe(undefined); }); describe('with a groupBy parameter', () => { const executeGroupBy = ( - comparator: Comparator, + comparator: COMPARATORS, threshold: number[], sourceId: string = 'default', state?: any @@ -1390,7 +1390,7 @@ describe('The custom threshold alert type', () => { { a: { ...customThresholdCountCriterion, - comparator: Comparator.LT_OR_EQ, + comparator: COMPARATORS.LESS_THAN_OR_EQUALS, threshold: [0], currentValue: 1, timestamp: new Date().toISOString(), @@ -1400,7 +1400,7 @@ describe('The custom threshold alert type', () => { }, b: { ...customThresholdCountCriterion, - comparator: Comparator.LT_OR_EQ, + comparator: COMPARATORS.LESS_THAN_OR_EQUALS, threshold: [0], currentValue: 1, timestamp: new Date().toISOString(), @@ -1410,14 +1410,14 @@ describe('The custom threshold alert type', () => { }, }, ]); - const resultState = await executeGroupBy(Comparator.LT_OR_EQ, [0]); + const resultState = await executeGroupBy(COMPARATORS.LESS_THAN_OR_EQUALS, [0]); expect(getLastReportedAlert(instanceIdA)).toBe(undefined); expect(getLastReportedAlert(instanceIdB)).toBe(undefined); setEvaluationResults([ { a: { ...customThresholdCountCriterion, - comparator: Comparator.LT_OR_EQ, + comparator: COMPARATORS.LESS_THAN_OR_EQUALS, threshold: [0], currentValue: 0, timestamp: new Date().toISOString(), @@ -1427,7 +1427,7 @@ describe('The custom threshold alert type', () => { }, b: { ...customThresholdCountCriterion, - comparator: Comparator.LT_OR_EQ, + comparator: COMPARATORS.LESS_THAN_OR_EQUALS, threshold: [0], currentValue: 0, timestamp: new Date().toISOString(), @@ -1437,7 +1437,7 @@ describe('The custom threshold alert type', () => { }, }, ]); - await executeGroupBy(Comparator.LT_OR_EQ, [0], 'empty-response', resultState); + await executeGroupBy(COMPARATORS.LESS_THAN_OR_EQUALS, [0], 'empty-response', resultState); expect(getLastReportedAlert(instanceIdA)).toHaveAlertAction(); expect(getLastReportedAlert(instanceIdB)).toHaveAlertAction(); }); @@ -1446,7 +1446,7 @@ describe('The custom threshold alert type', () => { describe('querying recovered alert with a count aggregator', () => { afterAll(() => clearInstances()); - const execute = (comparator: Comparator, threshold: number[], sourceId: string = 'default') => + const execute = (comparator: COMPARATORS, threshold: number[], sourceId: string = 'default') => executor({ ...mockOptions, services, @@ -1467,7 +1467,7 @@ describe('The custom threshold alert type', () => { { '*': { ...customThresholdCountCriterion, - comparator: Comparator.GT, + comparator: COMPARATORS.GREATER_THAN, threshold: [0.9], currentValue: 1, timestamp: new Date().toISOString(), @@ -1488,7 +1488,7 @@ describe('The custom threshold alert type', () => { ]), }; }); - await execute(Comparator.GT, [0.9]); + await execute(COMPARATORS.GREATER_THAN, [0.9]); const ISO_DATE_REGEX = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{3}Z$/; expect(getViewInAppUrl).toBeCalledWith({ dataViewId: 'c34a7c79-a88b-4b4a-ad19-72f6d24104e4', @@ -1519,7 +1519,7 @@ describe('The custom threshold alert type', () => { criteria: [ { ...customThresholdNonCountCriterion, - comparator: Comparator.GT, + comparator: COMPARATORS.GREATER_THAN, threshold: [1], metrics: [ { @@ -1538,7 +1538,7 @@ describe('The custom threshold alert type', () => { { '*': { ...customThresholdNonCountCriterion, - comparator: Comparator.LT, + comparator: COMPARATORS.LESS_THAN, threshold: [1], metrics: [ { @@ -1567,7 +1567,7 @@ describe('The custom threshold alert type', () => { { '*': { ...customThresholdNonCountCriterion, - comparator: Comparator.LT, + comparator: COMPARATORS.LESS_THAN, threshold: [1], metrics: [ { @@ -1602,7 +1602,7 @@ describe('The custom threshold alert type', () => { criteria: [ { ...customThresholdNonCountCriterion, - comparator: Comparator.GT, + comparator: COMPARATORS.GREATER_THAN, threshold: [1], metrics: [ { @@ -1614,7 +1614,7 @@ describe('The custom threshold alert type', () => { }, { ...customThresholdCountCriterion, - comparator: Comparator.GT, + comparator: COMPARATORS.GREATER_THAN, threshold: [30], }, ], @@ -1626,7 +1626,7 @@ describe('The custom threshold alert type', () => { { '*': { ...customThresholdNonCountCriterion, - comparator: Comparator.LT, + comparator: COMPARATORS.LESS_THAN, threshold: [1], metrics: [ { @@ -1675,7 +1675,7 @@ describe('The custom threshold alert type', () => { criteria: [ { ...customThresholdNonCountCriterion, - comparator: Comparator.GT, + comparator: COMPARATORS.GREATER_THAN, threshold: [0], metrics, }, @@ -1700,7 +1700,7 @@ describe('The custom threshold alert type', () => { { '*': { ...customThresholdNonCountCriterion, - comparator: Comparator.GT, + comparator: COMPARATORS.GREATER_THAN, threshold: [0], metrics: [ { @@ -1723,7 +1723,7 @@ describe('The custom threshold alert type', () => { { '*': { ...customThresholdNonCountCriterion, - comparator: Comparator.GT, + comparator: COMPARATORS.GREATER_THAN, threshold: [0], metrics: [ { @@ -1746,7 +1746,7 @@ describe('The custom threshold alert type', () => { { a: { ...customThresholdNonCountCriterion, - comparator: Comparator.GT, + comparator: COMPARATORS.GREATER_THAN, threshold: [0], currentValue: 1.0, timestamp: new Date().toISOString(), @@ -1756,7 +1756,7 @@ describe('The custom threshold alert type', () => { }, b: { ...customThresholdNonCountCriterion, - comparator: Comparator.GT, + comparator: COMPARATORS.GREATER_THAN, threshold: [0], currentValue: 3, timestamp: new Date().toISOString(), @@ -1780,7 +1780,7 @@ describe('The custom threshold alert type', () => { { a: { ...customThresholdNonCountCriterion, - comparator: Comparator.GT, + comparator: COMPARATORS.GREATER_THAN, threshold: [0], metrics: [ { @@ -1797,7 +1797,7 @@ describe('The custom threshold alert type', () => { }, b: { ...customThresholdNonCountCriterion, - comparator: Comparator.GT, + comparator: COMPARATORS.GREATER_THAN, threshold: [0], metrics: [ { @@ -1824,7 +1824,7 @@ describe('The custom threshold alert type', () => { { a: { ...customThresholdNonCountCriterion, - comparator: Comparator.GT, + comparator: COMPARATORS.GREATER_THAN, threshold: [0], metrics: [ { @@ -1841,7 +1841,7 @@ describe('The custom threshold alert type', () => { }, b: { ...customThresholdNonCountCriterion, - comparator: Comparator.GT, + comparator: COMPARATORS.GREATER_THAN, threshold: [0], metrics: [ { @@ -1858,7 +1858,7 @@ describe('The custom threshold alert type', () => { }, c: { ...customThresholdNonCountCriterion, - comparator: Comparator.GT, + comparator: COMPARATORS.GREATER_THAN, threshold: [0], metrics: [ { @@ -1884,7 +1884,7 @@ describe('The custom threshold alert type', () => { { a: { ...customThresholdNonCountCriterion, - comparator: Comparator.GT, + comparator: COMPARATORS.GREATER_THAN, threshold: [0], currentValue: 1, timestamp: new Date().toISOString(), @@ -1894,7 +1894,7 @@ describe('The custom threshold alert type', () => { }, b: { ...customThresholdNonCountCriterion, - comparator: Comparator.GT, + comparator: COMPARATORS.GREATER_THAN, threshold: [0], currentValue: 3, timestamp: new Date().toISOString(), @@ -1923,7 +1923,7 @@ describe('The custom threshold alert type', () => { criteria: [ { ...customThresholdNonCountCriterion, - comparator: Comparator.GT, + comparator: COMPARATORS.GREATER_THAN, threshold: [0], metrics, }, @@ -1944,7 +1944,7 @@ describe('The custom threshold alert type', () => { { '*': { ...customThresholdNonCountCriterion, - comparator: Comparator.GT, + comparator: COMPARATORS.GREATER_THAN, threshold: [0], metrics: [ { @@ -1967,7 +1967,7 @@ describe('The custom threshold alert type', () => { { '*': { ...customThresholdNonCountCriterion, - comparator: Comparator.GT, + comparator: COMPARATORS.GREATER_THAN, threshold: [0], metrics: [ { @@ -1990,7 +1990,7 @@ describe('The custom threshold alert type', () => { { a: { ...customThresholdNonCountCriterion, - comparator: Comparator.GT, + comparator: COMPARATORS.GREATER_THAN, threshold: [0], currentValue: 1, timestamp: new Date().toISOString(), @@ -2000,7 +2000,7 @@ describe('The custom threshold alert type', () => { }, b: { ...customThresholdNonCountCriterion, - comparator: Comparator.GT, + comparator: COMPARATORS.GREATER_THAN, threshold: [0], currentValue: 3, timestamp: new Date().toISOString(), @@ -2022,7 +2022,7 @@ describe('The custom threshold alert type', () => { { a: { ...customThresholdNonCountCriterion, - comparator: Comparator.GT, + comparator: COMPARATORS.GREATER_THAN, threshold: [0], metrics: [ { @@ -2039,7 +2039,7 @@ describe('The custom threshold alert type', () => { }, b: { ...customThresholdNonCountCriterion, - comparator: Comparator.GT, + comparator: COMPARATORS.GREATER_THAN, threshold: [0], metrics: [ { @@ -2116,7 +2116,7 @@ declare global { } const customThresholdNonCountCriterion: CustomMetricExpressionParams = { - comparator: Comparator.GT, + comparator: COMPARATORS.GREATER_THAN, metrics: [ { aggType: Aggregators.AVERAGE, @@ -2131,7 +2131,7 @@ const customThresholdNonCountCriterion: CustomMetricExpressionParams = { const mockedCountFilter = 'mockedCountFilter'; const customThresholdCountCriterion: CustomMetricExpressionParams = { - comparator: Comparator.GT, + comparator: COMPARATORS.GREATER_THAN, metrics: [ { aggType: Aggregators.COUNT, diff --git a/x-pack/plugins/observability_solution/observability/server/lib/rules/custom_threshold/custom_threshold_executor.ts b/x-pack/plugins/observability_solution/observability/server/lib/rules/custom_threshold/custom_threshold_executor.ts index 21ee4358b595a4..94be15415abb8a 100644 --- a/x-pack/plugins/observability_solution/observability/server/lib/rules/custom_threshold/custom_threshold_executor.ts +++ b/x-pack/plugins/observability_solution/observability/server/lib/rules/custom_threshold/custom_threshold_executor.ts @@ -33,11 +33,7 @@ import { CustomThresholdActionGroup, CustomThresholdAlert, } from './types'; -import { - buildFiredAlertReason, - buildNoDataAlertReason, - // buildRecoveredAlertReason, -} from './messages'; +import { buildFiredAlertReason, buildNoDataAlertReason } from './messages'; import { createScopedLogger, hasAdditionalContext, @@ -91,6 +87,7 @@ export const createCustomThresholdExecutor = ({ } = options; const { criteria } = params; + if (criteria.length === 0) throw new Error('Cannot execute an alert with 0 conditions'); const thresholdLogger = createScopedLogger(logger, 'thresholdRule', { alertId: ruleId, diff --git a/x-pack/plugins/observability_solution/observability/server/lib/rules/custom_threshold/lib/create_bucket_selector.ts b/x-pack/plugins/observability_solution/observability/server/lib/rules/custom_threshold/lib/create_bucket_selector.ts index 9a1dde442983c7..48d355ee4ce33d 100644 --- a/x-pack/plugins/observability_solution/observability/server/lib/rules/custom_threshold/lib/create_bucket_selector.ts +++ b/x-pack/plugins/observability_solution/observability/server/lib/rules/custom_threshold/lib/create_bucket_selector.ts @@ -5,6 +5,7 @@ * 2.0. */ +import { convertToBuiltInComparators } from '../../../../../common'; import { CustomMetricExpressionParams } from '../../../../../common/custom_threshold_rule/types'; import { createConditionScript } from './create_condition_script'; import { createLastPeriod } from './wrap_in_period'; @@ -24,7 +25,10 @@ export const createBucketSelector = ( buckets_path: { value: bucketPath, }, - script: createConditionScript(condition.threshold, condition.comparator), + script: createConditionScript( + condition.threshold, + convertToBuiltInComparators(condition.comparator) + ), }, }; diff --git a/x-pack/plugins/observability_solution/observability/server/lib/rules/custom_threshold/lib/create_condition_script.ts b/x-pack/plugins/observability_solution/observability/server/lib/rules/custom_threshold/lib/create_condition_script.ts index 2e5eda9fa32b4e..48a105d3700be3 100644 --- a/x-pack/plugins/observability_solution/observability/server/lib/rules/custom_threshold/lib/create_condition_script.ts +++ b/x-pack/plugins/observability_solution/observability/server/lib/rules/custom_threshold/lib/create_condition_script.ts @@ -5,10 +5,10 @@ * 2.0. */ -import { Comparator } from '../../../../../common/custom_threshold_rule/types'; +import { COMPARATORS } from '@kbn/alerting-comparators'; -export const createConditionScript = (threshold: number[], comparator: Comparator) => { - if (comparator === Comparator.BETWEEN && threshold.length === 2) { +export const createConditionScript = (threshold: number[], comparator: COMPARATORS) => { + if (comparator === COMPARATORS.BETWEEN && threshold.length === 2) { return { source: `params.value > params.threshold0 && params.value < params.threshold1 ? 1 : 0`, params: { @@ -17,9 +17,9 @@ export const createConditionScript = (threshold: number[], comparator: Comparato }, }; } - if (comparator === Comparator.OUTSIDE_RANGE && threshold.length === 2) { + if (comparator === COMPARATORS.NOT_BETWEEN && threshold.length === 2) { return { - // OUTSIDE_RANGE/NOT BETWEEN is the opposite of BETWEEN. Use the BETWEEN condition and switch the 1 and 0 + // NOT BETWEEN is the opposite of BETWEEN. Use the BETWEEN condition and switch the 1 and 0 source: `params.value > params.threshold0 && params.value < params.threshold1 ? 0 : 1`, params: { threshold0: threshold[0], diff --git a/x-pack/plugins/observability_solution/observability/server/lib/rules/custom_threshold/lib/get_data.ts b/x-pack/plugins/observability_solution/observability/server/lib/rules/custom_threshold/lib/get_data.ts index ac55b7f76f3836..c3c0d8b2eee949 100644 --- a/x-pack/plugins/observability_solution/observability/server/lib/rules/custom_threshold/lib/get_data.ts +++ b/x-pack/plugins/observability_solution/observability/server/lib/rules/custom_threshold/lib/get_data.ts @@ -194,7 +194,6 @@ export const getData = async ( const fieldsExisted = groupBy?.includes(KUBERNETES_POD_UID) ? await doFieldsExist(esClient, [CONTAINER_ID], index) : null; - const request = { index, allow_no_indices: true, diff --git a/x-pack/plugins/observability_solution/observability/server/lib/rules/custom_threshold/lib/metric_query.test.ts b/x-pack/plugins/observability_solution/observability/server/lib/rules/custom_threshold/lib/metric_query.test.ts index b63e72e19fd291..e10a4f4cae9be2 100644 --- a/x-pack/plugins/observability_solution/observability/server/lib/rules/custom_threshold/lib/metric_query.test.ts +++ b/x-pack/plugins/observability_solution/observability/server/lib/rules/custom_threshold/lib/metric_query.test.ts @@ -7,12 +7,12 @@ import moment from 'moment'; import { - Comparator, Aggregators, CustomMetricExpressionParams, SearchConfigurationType, } from '../../../../../common/custom_threshold_rule/types'; import { getElasticsearchMetricQuery } from './metric_query'; +import { COMPARATORS } from '@kbn/alerting-comparators'; describe("The Metric Threshold Alert's getElasticsearchMetricQuery", () => { const expressionParams: CustomMetricExpressionParams = { @@ -26,7 +26,7 @@ describe("The Metric Threshold Alert's getElasticsearchMetricQuery", () => { timeUnit: 'm', timeSize: 1, threshold: [1], - comparator: Comparator.GT, + comparator: COMPARATORS.GREATER_THAN, }; const searchConfiguration: SearchConfigurationType = { index: { diff --git a/x-pack/plugins/observability_solution/observability/server/lib/rules/custom_threshold/messages.ts b/x-pack/plugins/observability_solution/observability/server/lib/rules/custom_threshold/messages.ts index ca160d06b65739..046b7b58790265 100644 --- a/x-pack/plugins/observability_solution/observability/server/lib/rules/custom_threshold/messages.ts +++ b/x-pack/plugins/observability_solution/observability/server/lib/rules/custom_threshold/messages.ts @@ -6,58 +6,58 @@ */ import { i18n } from '@kbn/i18n'; -import { Comparator } from '../../../../common/custom_threshold_rule/types'; -import { formatDurationFromTimeUnitChar } from '../../../../common'; -import { Evaluation } from './lib/evaluate_rule'; -import { formatAlertResult, FormattedEvaluation } from './lib/format_alert_result'; +import { COMPARATORS } from '@kbn/alerting-comparators'; import { - BELOW_TEXT, ABOVE_TEXT, - BETWEEN_TEXT, - NOT_BETWEEN_TEXT, - CUSTOM_EQUATION_I18N, ABOVE_OR_EQ_TEXT, + BELOW_TEXT, BELOW_OR_EQ_TEXT, -} from './translations'; + BETWEEN_TEXT, + NOT_BETWEEN_TEXT, +} from '../../../../common/i18n'; +import { convertToBuiltInComparators, formatDurationFromTimeUnitChar } from '../../../../common'; +import { Evaluation } from './lib/evaluate_rule'; +import { formatAlertResult, FormattedEvaluation } from './lib/format_alert_result'; +import { CUSTOM_EQUATION_I18N } from './translations'; import { UNGROUPED_FACTORY_KEY } from './constants'; const toNumber = (value: number | string) => typeof value === 'string' ? parseFloat(value) : value; const recoveredComparatorToI18n = ( - comparator: Comparator, + comparator: COMPARATORS, threshold: number[], currentValue: number ) => { switch (comparator) { - case Comparator.BETWEEN: + case COMPARATORS.BETWEEN: return currentValue < threshold[0] ? BELOW_TEXT : ABOVE_TEXT; - case Comparator.OUTSIDE_RANGE: + case COMPARATORS.NOT_BETWEEN: return BETWEEN_TEXT; - case Comparator.GT: + case COMPARATORS.GREATER_THAN: return ABOVE_TEXT; - case Comparator.GT_OR_EQ: + case COMPARATORS.GREATER_THAN_OR_EQUALS: return ABOVE_OR_EQ_TEXT; - case Comparator.LT: + case COMPARATORS.LESS_THAN: return BELOW_TEXT; - case Comparator.LT_OR_EQ: + case COMPARATORS.LESS_THAN_OR_EQUALS: return BELOW_OR_EQ_TEXT; } }; -const alertComparatorToI18n = (comparator: Comparator) => { +const alertComparatorToI18n = (comparator: COMPARATORS) => { switch (comparator) { - case Comparator.BETWEEN: + case COMPARATORS.BETWEEN: return BETWEEN_TEXT; - case Comparator.OUTSIDE_RANGE: + case COMPARATORS.NOT_BETWEEN: return NOT_BETWEEN_TEXT; - case Comparator.GT: + case COMPARATORS.GREATER_THAN: return ABOVE_TEXT; - case Comparator.GT_OR_EQ: + case COMPARATORS.GREATER_THAN_OR_EQUALS: return ABOVE_OR_EQ_TEXT; - case Comparator.LT: + case COMPARATORS.LESS_THAN: return BELOW_TEXT; - case Comparator.LT_OR_EQ: + case COMPARATORS.LESS_THAN_OR_EQUALS: return BELOW_OR_EQ_TEXT; } }; @@ -124,7 +124,7 @@ const buildAggregationReason: (evaluation: FormattedEvaluation) => string = ({ defaultMessage: '{label} is {currentValue}, {comparator} the threshold of {threshold}', values: { label, - comparator: alertComparatorToI18n(comparator), + comparator: alertComparatorToI18n(convertToBuiltInComparators(comparator)), threshold: thresholdToI18n(threshold), currentValue, }, @@ -134,7 +134,7 @@ const buildAggregationReason: (evaluation: FormattedEvaluation) => string = ({ export const buildRecoveredAlertReason: (alertResult: { group: string; label?: string; - comparator: Comparator; + comparator: COMPARATORS; threshold: Array; currentValue: number | string; }) => string = ({ group, label = CUSTOM_EQUATION_I18N, comparator, threshold, currentValue }) => diff --git a/x-pack/plugins/observability_solution/observability/server/lib/rules/custom_threshold/mocks/custom_threshold_alert_result.ts b/x-pack/plugins/observability_solution/observability/server/lib/rules/custom_threshold/mocks/custom_threshold_alert_result.ts index b8ab169b7a90d1..08c6e602989391 100644 --- a/x-pack/plugins/observability_solution/observability/server/lib/rules/custom_threshold/mocks/custom_threshold_alert_result.ts +++ b/x-pack/plugins/observability_solution/observability/server/lib/rules/custom_threshold/mocks/custom_threshold_alert_result.ts @@ -5,15 +5,15 @@ * 2.0. */ +import { COMPARATORS } from '@kbn/alerting-comparators'; import { Aggregators, - Comparator, CustomMetricExpressionParams, } from '../../../../../common/custom_threshold_rule/types'; import { Evaluation } from '../lib/evaluate_rule'; const customThresholdNonCountCriterion: CustomMetricExpressionParams = { - comparator: Comparator.GT, + comparator: COMPARATORS.GREATER_THAN, metrics: [ { aggType: Aggregators.AVERAGE, @@ -30,7 +30,7 @@ export const alertResultsMultipleConditions: Array> = { '*': { ...customThresholdNonCountCriterion, - comparator: Comparator.GT, + comparator: COMPARATORS.GREATER_THAN, threshold: [0.75], currentValue: 1.0, timestamp: new Date().toISOString(), @@ -42,7 +42,7 @@ export const alertResultsMultipleConditions: Array> = { '*': { ...customThresholdNonCountCriterion, - comparator: Comparator.GT, + comparator: COMPARATORS.GREATER_THAN, threshold: [0.75], currentValue: 3.0, timestamp: new Date().toISOString(), diff --git a/x-pack/plugins/observability_solution/observability/server/lib/rules/custom_threshold/mocks/custom_threshold_metric_params.ts b/x-pack/plugins/observability_solution/observability/server/lib/rules/custom_threshold/mocks/custom_threshold_metric_params.ts index 6c3eb7557302c8..09e202fcac7d93 100644 --- a/x-pack/plugins/observability_solution/observability/server/lib/rules/custom_threshold/mocks/custom_threshold_metric_params.ts +++ b/x-pack/plugins/observability_solution/observability/server/lib/rules/custom_threshold/mocks/custom_threshold_metric_params.ts @@ -5,9 +5,9 @@ * 2.0. */ +import { COMPARATORS } from '@kbn/alerting-comparators'; import { Aggregators, - Comparator, CustomMetricExpressionParams, } from '../../../../../common/custom_threshold_rule/types'; @@ -28,7 +28,7 @@ export const criteriaMultipleConditions: CustomMetricExpressionParams[] = [ timeUnit: 'm', timeSize: 1, threshold: [1], - comparator: Comparator.GT, + comparator: COMPARATORS.GREATER_THAN, }, { metrics: [ @@ -46,7 +46,7 @@ export const criteriaMultipleConditions: CustomMetricExpressionParams[] = [ timeUnit: 'm', timeSize: 1, threshold: [4], - comparator: Comparator.GT, + comparator: COMPARATORS.GREATER_THAN, }, ]; @@ -67,7 +67,7 @@ export const criteriaMultipleConditionsWithIsBetween: CustomMetricExpressionPara timeUnit: 'm', timeSize: 1, threshold: [1, 2], - comparator: Comparator.BETWEEN, + comparator: COMPARATORS.BETWEEN, }, { metrics: [ @@ -85,6 +85,6 @@ export const criteriaMultipleConditionsWithIsBetween: CustomMetricExpressionPara timeUnit: 'm', timeSize: 1, threshold: [4], - comparator: Comparator.GT, + comparator: COMPARATORS.GREATER_THAN, }, ]; diff --git a/x-pack/plugins/observability_solution/observability/server/lib/rules/custom_threshold/register_custom_threshold_rule_type.ts b/x-pack/plugins/observability_solution/observability/server/lib/rules/custom_threshold/register_custom_threshold_rule_type.ts index 8d3a968d85a64d..f198a6c7079686 100644 --- a/x-pack/plugins/observability_solution/observability/server/lib/rules/custom_threshold/register_custom_threshold_rule_type.ts +++ b/x-pack/plugins/observability_solution/observability/server/lib/rules/custom_threshold/register_custom_threshold_rule_type.ts @@ -15,9 +15,11 @@ import { IBasePath, Logger } from '@kbn/core/server'; import { legacyExperimentalFieldMap } from '@kbn/alerts-as-data-utils'; import { OBSERVABILITY_THRESHOLD_RULE_TYPE_ID } from '@kbn/rule-data-utils'; import { LicenseType } from '@kbn/licensing-plugin/server'; +import { COMPARATORS } from '@kbn/alerting-comparators'; import { EsQueryRuleParamsExtractedParams } from '@kbn/stack-alerts-plugin/server/rule_types/es_query/rule_type_params'; +import { LEGACY_COMPARATORS } from '../../../../common/utils/convert_legacy_outside_comparator'; import { observabilityFeatureId, observabilityPaths } from '../../../../common'; -import { Aggregators, Comparator } from '../../../../common/custom_threshold_rule/types'; +import { Aggregators } from '../../../../common/custom_threshold_rule/types'; import { THRESHOLD_RULE_REGISTRATION_CONTEXT } from '../../../common/constants'; import { @@ -75,9 +77,10 @@ export function thresholdRuleType( logger: Logger, locators: CustomThresholdLocators ) { + const comparators = Object.values({ ...COMPARATORS, ...LEGACY_COMPARATORS }); const baseCriterion = { threshold: schema.arrayOf(schema.number()), - comparator: oneOfLiterals(Object.values(Comparator)), + comparator: oneOfLiterals(comparators), timeUnit: schema.string(), timeSize: schema.number(), }; diff --git a/x-pack/plugins/observability_solution/observability/server/lib/rules/custom_threshold/translations.ts b/x-pack/plugins/observability_solution/observability/server/lib/rules/custom_threshold/translations.ts index 020229c27a4ad6..ebae07d124ebbf 100644 --- a/x-pack/plugins/observability_solution/observability/server/lib/rules/custom_threshold/translations.ts +++ b/x-pack/plugins/observability_solution/observability/server/lib/rules/custom_threshold/translations.ts @@ -85,50 +85,6 @@ export const CUSTOM_EQUATION_I18N = i18n.translate( } ); -// Comparators - -export const BELOW_TEXT = i18n.translate( - 'xpack.observability.customThreshold.rule.threshold.below', - { - defaultMessage: 'below', - } -); - -export const BELOW_OR_EQ_TEXT = i18n.translate( - 'xpack.observability.customThreshold.rule.threshold.belowOrEqual', - { - defaultMessage: 'below or equal', - } -); - -export const ABOVE_TEXT = i18n.translate( - 'xpack.observability.customThreshold.rule.threshold.above', - { - defaultMessage: 'above', - } -); - -export const ABOVE_OR_EQ_TEXT = i18n.translate( - 'xpack.observability.customThreshold.rule.threshold.aboveOrEqual', - { - defaultMessage: 'above or equal', - } -); - -export const BETWEEN_TEXT = i18n.translate( - 'xpack.observability.customThreshold.rule.threshold.between', - { - defaultMessage: 'between', - } -); - -export const NOT_BETWEEN_TEXT = i18n.translate( - 'xpack.observability.customThreshold.rule.threshold.notBetween', - { - defaultMessage: 'not between', - } -); - // Action variable descriptions export const groupByKeysActionVariableDescription = i18n.translate( diff --git a/x-pack/plugins/observability_solution/observability/tsconfig.json b/x-pack/plugins/observability_solution/observability/tsconfig.json index b4f617079dc58d..5a0ddfd8678c09 100644 --- a/x-pack/plugins/observability_solution/observability/tsconfig.json +++ b/x-pack/plugins/observability_solution/observability/tsconfig.json @@ -98,6 +98,7 @@ "@kbn/data-view-field-editor-plugin", "@kbn/cases-components", "@kbn/aiops-log-rate-analysis", + "@kbn/alerting-comparators", "@kbn/react-kibana-context-render", "@kbn/react-kibana-mount", ], diff --git a/x-pack/plugins/observability_solution/observability_ai_assistant_app/scripts/evaluation/alert_templates/templates.ts b/x-pack/plugins/observability_solution/observability_ai_assistant_app/scripts/evaluation/alert_templates/templates.ts index 42d63dced13fd7..e2571795a6e3b7 100644 --- a/x-pack/plugins/observability_solution/observability_ai_assistant_app/scripts/evaluation/alert_templates/templates.ts +++ b/x-pack/plugins/observability_solution/observability_ai_assistant_app/scripts/evaluation/alert_templates/templates.ts @@ -5,14 +5,7 @@ * 2.0. */ -export enum Comparator { - GT = '>', - LT = '<', - GT_OR_EQ = '>=', - LT_OR_EQ = '<=', - BETWEEN = 'between', - OUTSIDE_RANGE = 'outside', -} +import { COMPARATORS } from '@kbn/alerting-comparators'; export enum Aggregators { COUNT = 'count', @@ -49,7 +42,7 @@ export const customThresholdAIAssistantLogCount = { params: { criteria: [ { - comparator: Comparator.GT, + comparator: COMPARATORS.GREATER_THAN, threshold: [10], timeSize: 2, timeUnit: 'h', @@ -82,7 +75,7 @@ export const customThresholdAIAssistantMetricAvg = { params: { criteria: [ { - comparator: Comparator.GT, + comparator: COMPARATORS.GREATER_THAN, threshold: [0.5], timeSize: 2, timeUnit: 'h', diff --git a/x-pack/plugins/observability_solution/observability_ai_assistant_app/tsconfig.json b/x-pack/plugins/observability_solution/observability_ai_assistant_app/tsconfig.json index 90c4f4d4151428..ce98c24be2a256 100644 --- a/x-pack/plugins/observability_solution/observability_ai_assistant_app/tsconfig.json +++ b/x-pack/plugins/observability_solution/observability_ai_assistant_app/tsconfig.json @@ -68,7 +68,8 @@ "@kbn/serverless", "@kbn/task-manager-plugin", "@kbn/cloud-plugin", - "@kbn/observability-plugin" + "@kbn/observability-plugin", + "@kbn/alerting-comparators" ], "exclude": ["target/**/*"] } diff --git a/x-pack/plugins/observability_solution/slo/docs/openapi/slo/bundled.json b/x-pack/plugins/observability_solution/slo/docs/openapi/slo/bundled.json index 11bf01c8a7328d..90f3e26fa4ed86 100644 --- a/x-pack/plugins/observability_solution/slo/docs/openapi/slo/bundled.json +++ b/x-pack/plugins/observability_solution/slo/docs/openapi/slo/bundled.json @@ -190,6 +190,14 @@ "default": "asc" }, "example": "asc" + }, + { + "name": "hideStale", + "in": "query", + "description": "Hide stale SLOs from the list as defined by stale SLO threshold in SLO settings", + "schema": { + "type": "boolean" + } } ], "responses": { @@ -1768,12 +1776,20 @@ "syncDelay": { "description": "The synch delay to apply to the transform. Default 1m", "type": "string", + "default": "1m", "example": "5m" }, "frequency": { "description": "Configure how often the transform runs, default 1m", "type": "string", + "default": "1m", "example": "5m" + }, + "preventInitialBackfill": { + "description": "Prevents the transform from backfilling data when it starts.", + "type": "boolean", + "default": false, + "example": true } } }, diff --git a/x-pack/plugins/observability_solution/slo/docs/openapi/slo/bundled.yaml b/x-pack/plugins/observability_solution/slo/docs/openapi/slo/bundled.yaml index d099474448eae6..6f826fb8f2e10b 100644 --- a/x-pack/plugins/observability_solution/slo/docs/openapi/slo/bundled.yaml +++ b/x-pack/plugins/observability_solution/slo/docs/openapi/slo/bundled.yaml @@ -121,6 +121,11 @@ paths: - desc default: asc example: asc + - name: hideStale + in: query + description: Hide stale SLOs from the list as defined by stale SLO threshold in SLO settings + schema: + type: boolean responses: '200': description: Successful request @@ -1210,11 +1215,18 @@ components: syncDelay: description: The synch delay to apply to the transform. Default 1m type: string + default: 1m example: 5m frequency: description: Configure how often the transform runs, default 1m type: string + default: 1m example: 5m + preventInitialBackfill: + description: Prevents the transform from backfilling data when it starts. + type: boolean + default: false + example: true summary_status: title: summary status type: string diff --git a/x-pack/plugins/observability_solution/slo/docs/openapi/slo/components/schemas/settings.yaml b/x-pack/plugins/observability_solution/slo/docs/openapi/slo/components/schemas/settings.yaml index 2f4ff275f0d0e8..a50ce0c28c136b 100644 --- a/x-pack/plugins/observability_solution/slo/docs/openapi/slo/components/schemas/settings.yaml +++ b/x-pack/plugins/observability_solution/slo/docs/openapi/slo/components/schemas/settings.yaml @@ -5,8 +5,15 @@ properties: syncDelay: description: The synch delay to apply to the transform. Default 1m type: string + default: 1m example: 5m frequency: description: Configure how often the transform runs, default 1m type: string + default: 1m example: 5m + preventInitialBackfill: + description: Prevents the transform from backfilling data when it starts. + type: boolean + default: false + example: true diff --git a/x-pack/plugins/observability_solution/slo/public/data/slo/slo.ts b/x-pack/plugins/observability_solution/slo/public/data/slo/slo.ts index 31ec29c44a31dd..02541ac8a17c72 100644 --- a/x-pack/plugins/observability_solution/slo/public/data/slo/slo.ts +++ b/x-pack/plugins/observability_solution/slo/public/data/slo/slo.ts @@ -51,6 +51,7 @@ const baseSlo: Omit = { settings: { syncDelay: '1m', frequency: '1m', + preventInitialBackfill: false, }, summary: { status: 'HEALTHY', diff --git a/x-pack/plugins/observability_solution/slo/public/embeddable/slo/alerts/components/slo_alerts_summary.tsx b/x-pack/plugins/observability_solution/slo/public/embeddable/slo/alerts/components/slo_alerts_summary.tsx index da41a5938baa01..968d1f80a08248 100644 --- a/x-pack/plugins/observability_solution/slo/public/embeddable/slo/alerts/components/slo_alerts_summary.tsx +++ b/x-pack/plugins/observability_solution/slo/public/embeddable/slo/alerts/components/slo_alerts_summary.tsx @@ -12,7 +12,7 @@ import { calculateTimeRangeBucketSize } from '@kbn/observability-plugin/public'; import { observabilityAlertFeatureIds } from '@kbn/observability-plugin/common'; import { useSloAlertsQuery } from './slo_alerts_table'; -import { SloEmbeddableDeps } from '../slo_alerts_embeddable'; +import { SloEmbeddableDeps } from '../types'; import { SloItem } from '../types'; const DEFAULT_INTERVAL = '60s'; @@ -50,17 +50,13 @@ export function SloAlertsSummary({ ), [timeRange.from, timeRange.to, timeBuckets] ); - const alertSummaryTimeRange = useMemo( - () => - getAlertSummaryTimeRange( - { - from: timeRange.from, - to: timeRange.to, - }, - bucketSize?.intervalString ?? DEFAULT_INTERVAL, - bucketSize?.dateFormat ?? DEFAULT_DATE_FORMAT - ), - [timeRange.from, timeRange.to, bucketSize] + const alertSummaryTimeRange = getAlertSummaryTimeRange( + { + from: timeRange.from, + to: timeRange.to, + }, + bucketSize?.intervalString ?? DEFAULT_INTERVAL, + bucketSize?.dateFormat ?? DEFAULT_DATE_FORMAT ); return ( diff --git a/x-pack/plugins/observability_solution/slo/public/embeddable/slo/alerts/components/slo_alerts_table.tsx b/x-pack/plugins/observability_solution/slo/public/embeddable/slo/alerts/components/slo_alerts_table.tsx index 4642e315e9c541..c53f7c7c73d204 100644 --- a/x-pack/plugins/observability_solution/slo/public/embeddable/slo/alerts/components/slo_alerts_table.tsx +++ b/x-pack/plugins/observability_solution/slo/public/embeddable/slo/alerts/components/slo_alerts_table.tsx @@ -9,7 +9,7 @@ import { AlertConsumers } from '@kbn/rule-data-utils'; import type { TimeRange } from '@kbn/es-query'; import { ALL_VALUE } from '@kbn/slo-schema'; import { AlertsTableStateProps } from '@kbn/triggers-actions-ui-plugin/public/application/sections/alerts_table/alerts_table_state'; -import { SloEmbeddableDeps } from '../slo_alerts_embeddable'; +import { SloEmbeddableDeps } from '../types'; import type { SloItem } from '../types'; import { SLO_ALERTS_TABLE_CONFIG_ID } from '../../constants'; @@ -99,7 +99,6 @@ export function SloAlertsTable({ const { triggersActionsUi: { alertsTableConfigurationRegistry, getAlertsStateTable: AlertsStateTable }, } = deps; - return ( DerivedIndexPattern; +export const SLO_ALERTS_EMBEDDABLE_ID = 'SLO_ALERTS_EMBEDDABLE'; +export const ADD_SLO_ALERTS_ACTION_ID = 'CREATE_SLO_ALERTS_EMBEDDABLE'; diff --git a/x-pack/plugins/observability_solution/slo/public/embeddable/slo/alerts/slo_alerts_embeddable.tsx b/x-pack/plugins/observability_solution/slo/public/embeddable/slo/alerts/slo_alerts_embeddable.tsx deleted file mode 100644 index 94581361d91b8b..00000000000000 --- a/x-pack/plugins/observability_solution/slo/public/embeddable/slo/alerts/slo_alerts_embeddable.tsx +++ /dev/null @@ -1,165 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ -import React from 'react'; -import ReactDOM from 'react-dom'; -import { QueryClient, QueryClientProvider } from '@tanstack/react-query'; - -import { i18n } from '@kbn/i18n'; -import { Storage } from '@kbn/kibana-utils-plugin/public'; -import { createBrowserHistory } from 'history'; -import { - Embeddable as AbstractEmbeddable, - EmbeddableOutput, - IContainer, -} from '@kbn/embeddable-plugin/public'; -import { KibanaContextProvider } from '@kbn/kibana-react-plugin/public'; -import { CasesPublicStart } from '@kbn/cases-plugin/public'; - -import { - type CoreStart, - IUiSettingsClient, - ApplicationStart, - NotificationsStart, -} from '@kbn/core/public'; -import { TriggersAndActionsUIPublicPluginStart } from '@kbn/triggers-actions-ui-plugin/public'; -import type { DataPublicPluginStart } from '@kbn/data-plugin/public'; -import { Router } from '@kbn/shared-ux-router'; -import { SettingsStart } from '@kbn/core-ui-settings-browser'; -import { SecurityPluginStart } from '@kbn/security-plugin/public'; -import type { ChartsPluginStart } from '@kbn/charts-plugin/public'; -import type { UiActionsStart } from '@kbn/ui-actions-plugin/public'; -import { ServerlessPluginStart } from '@kbn/serverless/public'; - -import { Subject, Subscription } from 'rxjs'; -import { SloAlertsWrapper } from './slo_alerts_wrapper'; -import type { SloAlertsEmbeddableInput } from './types'; -export const SLO_ALERTS_EMBEDDABLE = 'SLO_ALERTS_EMBEDDABLE'; - -const history = createBrowserHistory(); - -export interface SloEmbeddableDeps { - uiSettings: IUiSettingsClient; - http: CoreStart['http']; - i18n: CoreStart['i18n']; - application: ApplicationStart; - triggersActionsUi: TriggersAndActionsUIPublicPluginStart; - data: DataPublicPluginStart; - notifications: NotificationsStart; - cases: CasesPublicStart; - settings: SettingsStart; - security: SecurityPluginStart; - charts: ChartsPluginStart; - uiActions: UiActionsStart; - serverless?: ServerlessPluginStart; -} - -export class SLOAlertsEmbeddable extends AbstractEmbeddable< - SloAlertsEmbeddableInput, - EmbeddableOutput -> { - public readonly type = SLO_ALERTS_EMBEDDABLE; - private reloadSubject: Subject; - private node?: HTMLElement; - kibanaVersion: string; - private subscription: Subscription; - - constructor( - private readonly deps: SloEmbeddableDeps, - initialInput: SloAlertsEmbeddableInput, - kibanaVersion: string, - parent?: IContainer - ) { - super(initialInput, {}, parent); - this.deps = deps; - this.kibanaVersion = kibanaVersion; - this.reloadSubject = new Subject(); - - this.subscription = this.getInput$().subscribe((input) => { - this.reloadSubject.next(input); - }); - - this.setTitle( - this.input.title || - i18n.translate('xpack.slo.sloAlertsEmbeddable.displayTitle', { - defaultMessage: 'SLO Alerts', - }) - ); - } - public onRenderComplete() { - this.renderComplete.dispatchComplete(); - } - - public getSloAlertsConfig() { - return this.getInput(); - } - - public updateSloAlertsConfig(next: SloAlertsEmbeddableInput) { - this.updateInput(next); - } - - setTitle(title: string) { - this.updateInput({ title }); - } - - public render(node: HTMLElement) { - super.render(node); - this.node = node; - // required for the export feature to work - this.node.setAttribute('data-shared-item', ''); - - const queryClient = new QueryClient(); - - const I18nContext = this.deps.i18n.Context; - const { - slos, - timeRange = { from: 'now-15m/m', to: 'now' }, - showAllGroupByInstances, - } = this.getInput(); - - const deps = this.deps; - const kibanaVersion = this.kibanaVersion; - ReactDOM.render( - - - - - this.onRenderComplete()} - embeddable={this} - deps={deps} - slos={slos} - timeRange={timeRange} - reloadSubject={this.reloadSubject} - showAllGroupByInstances={showAllGroupByInstances} - /> - - - - , - node - ); - } - - public reload() { - this.reloadSubject?.next(undefined); - } - - public destroy() { - super.destroy(); - this.subscription.unsubscribe(); - if (this.node) { - ReactDOM.unmountComponentAtNode(this.node); - } - } -} diff --git a/x-pack/plugins/observability_solution/slo/public/embeddable/slo/alerts/slo_alerts_embeddable_factory.ts b/x-pack/plugins/observability_solution/slo/public/embeddable/slo/alerts/slo_alerts_embeddable_factory.ts deleted file mode 100644 index 64d0a0755a0e76..00000000000000 --- a/x-pack/plugins/observability_solution/slo/public/embeddable/slo/alerts/slo_alerts_embeddable_factory.ts +++ /dev/null @@ -1,71 +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 { i18n } from '@kbn/i18n'; -import type { CoreSetup } from '@kbn/core/public'; -import { - EmbeddableFactory, - EmbeddableFactoryDefinition, - ErrorEmbeddable, - IContainer, -} from '@kbn/embeddable-plugin/public'; -import { COMMON_SLO_GROUPING } from '../common/constants'; -import { SLO_ALERTS_EMBEDDABLE, SLOAlertsEmbeddable } from './slo_alerts_embeddable'; -import { SloPublicPluginsStart, SloPublicStart } from '../../..'; -import { SloAlertsEmbeddableInput } from './types'; - -export type SloAlertsEmbeddableFactory = EmbeddableFactory; -export class SloAlertsEmbeddableFactoryDefinition implements EmbeddableFactoryDefinition { - public readonly type = SLO_ALERTS_EMBEDDABLE; - - public readonly grouping = COMMON_SLO_GROUPING; - - constructor( - private getStartServices: CoreSetup['getStartServices'], - private kibanaVersion: string - ) {} - - public async isEditable() { - return true; - } - - public async getExplicitInput(): Promise> { - const [coreStart, pluginStart] = await this.getStartServices(); - try { - const { resolveEmbeddableSloUserInput } = await import('./handle_explicit_input'); - return await resolveEmbeddableSloUserInput(coreStart, pluginStart); - } catch (e) { - return Promise.reject(); - } - } - - public async create(initialInput: SloAlertsEmbeddableInput, parent?: IContainer) { - try { - const [coreStart, pluginsStart] = await this.getStartServices(); - const deps = { ...coreStart, ...pluginsStart }; - return new SLOAlertsEmbeddable(deps, initialInput, this.kibanaVersion, parent); - } catch (e) { - return new ErrorEmbeddable(e, initialInput, parent); - } - } - - public getDescription() { - return i18n.translate('xpack.slo.sloAlertsEmbeddable.description', { - defaultMessage: 'Get an overview of your SLO alerts', - }); - } - - public getDisplayName() { - return i18n.translate('xpack.slo.sloAlertsEmbeddable.displayName', { - defaultMessage: 'SLO Alerts', - }); - } - - public getIconType() { - return 'alert'; - } -} diff --git a/x-pack/plugins/observability_solution/slo/public/embeddable/slo/alerts/slo_alerts_embeddable_factory.tsx b/x-pack/plugins/observability_solution/slo/public/embeddable/slo/alerts/slo_alerts_embeddable_factory.tsx new file mode 100644 index 00000000000000..7472c43253454b --- /dev/null +++ b/x-pack/plugins/observability_solution/slo/public/embeddable/slo/alerts/slo_alerts_embeddable_factory.tsx @@ -0,0 +1,133 @@ +/* + * Copyright 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 React, { useEffect } from 'react'; +import { Router } from '@kbn/shared-ux-router'; +import { BehaviorSubject, Subject } from 'rxjs'; +import { ReactEmbeddableFactory } from '@kbn/embeddable-plugin/public'; +import { + initializeTitles, + useBatchedPublishingSubjects, + fetch$, + FetchContext, + useFetchContext, +} from '@kbn/presentation-publishing'; +import { KibanaContextProvider } from '@kbn/kibana-react-plugin/public'; +import { QueryClient, QueryClientProvider } from '@tanstack/react-query'; +import { createBrowserHistory } from 'history'; +import { Storage } from '@kbn/kibana-utils-plugin/public'; +import { SLO_ALERTS_EMBEDDABLE_ID } from './constants'; +import { SloEmbeddableDeps, SloAlertsEmbeddableState, SloAlertsApi } from './types'; +import { SloAlertsWrapper } from './slo_alerts_wrapper'; +const history = createBrowserHistory(); +const queryClient = new QueryClient(); + +export const getAlertsPanelTitle = () => + i18n.translate('xpack.slo.sloAlertsEmbeddable.displayTitle', { + defaultMessage: 'SLO Alerts', + }); + +export function getAlertsEmbeddableFactory(deps: SloEmbeddableDeps, kibanaVersion: string) { + const factory: ReactEmbeddableFactory = { + type: SLO_ALERTS_EMBEDDABLE_ID, + deserializeState: (state) => { + return state.rawState as SloAlertsEmbeddableState; + }, + buildEmbeddable: async (state, buildApi, uuid, parentApi) => { + const { titlesApi, titleComparators, serializeTitles } = initializeTitles(state); + const defaultTitle$ = new BehaviorSubject(getAlertsPanelTitle()); + const slos$ = new BehaviorSubject(state.slos); + const showAllGroupByInstances$ = new BehaviorSubject(state.showAllGroupByInstances); + const reload$ = new Subject(); + const api = buildApi( + { + ...titlesApi, + defaultPanelTitle: defaultTitle$, + serializeState: () => { + return { + rawState: { + ...serializeTitles(), + slos: slos$.getValue(), + showAllGroupByInstances: showAllGroupByInstances$.getValue(), + }, + }; + }, + getSloAlertsConfig: () => { + return { + slos: slos$.getValue(), + showAllGroupByInstances: showAllGroupByInstances$.getValue(), + }; + }, + updateSloAlertsConfig: (update) => { + slos$.next(update.slos); + showAllGroupByInstances$.next(update.showAllGroupByInstances); + }, + }, + { + slos: [slos$, (value) => slos$.next(value)], + showAllGroupByInstances: [ + showAllGroupByInstances$, + (value) => showAllGroupByInstances$.next(value), + ], + ...titleComparators, + } + ); + + const fetchSubscription = fetch$(api) + .pipe() + .subscribe((next) => { + reload$.next(next); + }); + + return { + api, + Component: () => { + const [slos, showAllGroupByInstances] = useBatchedPublishingSubjects( + slos$, + showAllGroupByInstances$ + ); + const fetchContext = useFetchContext(api); + const I18nContext = deps.i18n.Context; + + useEffect(() => { + return () => { + fetchSubscription.unsubscribe(); + }; + }, []); + return ( + + + + + + + + + + ); + }, + }; + }, + }; + + return factory; +} diff --git a/x-pack/plugins/observability_solution/slo/public/embeddable/slo/alerts/handle_explicit_input.tsx b/x-pack/plugins/observability_solution/slo/public/embeddable/slo/alerts/slo_alerts_open_configuration.tsx similarity index 82% rename from x-pack/plugins/observability_solution/slo/public/embeddable/slo/alerts/handle_explicit_input.tsx rename to x-pack/plugins/observability_solution/slo/public/embeddable/slo/alerts/slo_alerts_open_configuration.tsx index 54af69acbe13d3..69529270b23b0d 100644 --- a/x-pack/plugins/observability_solution/slo/public/embeddable/slo/alerts/handle_explicit_input.tsx +++ b/x-pack/plugins/observability_solution/slo/public/embeddable/slo/alerts/slo_alerts_open_configuration.tsx @@ -4,21 +4,18 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ - import React from 'react'; -import { toMountPoint } from '@kbn/react-kibana-mount'; - import type { CoreStart } from '@kbn/core/public'; -import { KibanaContextProvider } from '@kbn/kibana-react-plugin/public'; import { QueryClient, QueryClientProvider } from '@tanstack/react-query'; -import type { EmbeddableSloProps, SloAlertsEmbeddableInput } from './types'; - -import { SloPublicPluginsStart } from '../../../types'; +import { toMountPoint } from '@kbn/react-kibana-mount'; +import { KibanaContextProvider } from '@kbn/kibana-react-plugin/public'; +import { SloPublicPluginsStart } from '../../..'; import { SloConfiguration } from './slo_configuration'; -export async function resolveEmbeddableSloUserInput( +import type { EmbeddableSloProps } from './types'; +export async function openSloConfiguration( coreStart: CoreStart, pluginStart: SloPublicPluginsStart, - input?: SloAlertsEmbeddableInput + initialState?: EmbeddableSloProps ): Promise { const { overlays } = coreStart; const queryClient = new QueryClient(); @@ -34,15 +31,14 @@ export async function resolveEmbeddableSloUserInput( > { modalSession.close(); resolve(update); }} onCancel={() => { modalSession.close(); - // @ts-expect-error - resolve(undefined); + reject(); }} /> diff --git a/x-pack/plugins/observability_solution/slo/public/embeddable/slo/alerts/slo_alerts_wrapper.tsx b/x-pack/plugins/observability_solution/slo/public/embeddable/slo/alerts/slo_alerts_wrapper.tsx index 413f92418d2cb3..9a56bcf0ae0bd2 100644 --- a/x-pack/plugins/observability_solution/slo/public/embeddable/slo/alerts/slo_alerts_wrapper.tsx +++ b/x-pack/plugins/observability_solution/slo/public/embeddable/slo/alerts/slo_alerts_wrapper.tsx @@ -10,37 +10,35 @@ import { i18n } from '@kbn/i18n'; import { EuiFlexGroup, EuiFlexItem, EuiLink } from '@elastic/eui'; import type { TimeRange } from '@kbn/es-query'; import { CONTEXT_MENU_TRIGGER } from '@kbn/embeddable-plugin/public'; -import { IEmbeddable, EmbeddableOutput } from '@kbn/embeddable-plugin/public'; import { ActionExecutionContext } from '@kbn/ui-actions-plugin/public'; import { Subject } from 'rxjs'; import styled from 'styled-components'; import { observabilityPaths } from '@kbn/observability-plugin/common'; +import { FetchContext } from '@kbn/presentation-publishing'; import { SloIncludedCount } from './components/slo_included_count'; import { SloAlertsSummary } from './components/slo_alerts_summary'; import { SloAlertsTable } from './components/slo_alerts_table'; -import type { SloItem } from './types'; -import { SloEmbeddableDeps } from './slo_alerts_embeddable'; -import { SloAlertsEmbeddableInput } from './types'; +import type { SloItem, SloEmbeddableDeps } from './types'; import { EDIT_SLO_ALERTS_ACTION } from '../../../ui_actions/edit_slo_alerts_panel'; interface Props { deps: SloEmbeddableDeps; slos: SloItem[]; timeRange: TimeRange; - embeddable: IEmbeddable; + embeddable: any; onRenderComplete?: () => void; - reloadSubject: Subject; + reloadSubject: Subject; showAllGroupByInstances?: boolean; } export function SloAlertsWrapper({ embeddable, - slos: initialSlos, + slos, deps, timeRange: initialTimeRange, onRenderComplete, reloadSubject, - showAllGroupByInstances: initialShowAllGroupByInstances, + showAllGroupByInstances, }: Props) { const { application: { navigateToUrl }, @@ -48,24 +46,15 @@ export function SloAlertsWrapper({ } = deps; const [timeRange, setTimeRange] = useState(initialTimeRange); - const [slos, setSlos] = useState(initialSlos); - const [showAllGroupByInstances, setShowAllGroupByInstances] = useState( - initialShowAllGroupByInstances - ); - const [lastRefreshTime, setLastRefreshTime] = useState(undefined); useEffect(() => { const subs = reloadSubject?.subscribe((input) => { if (input) { - const { timeRange: nTimeRange, slos: nSlos } = input; - - setSlos(nSlos); - + const { timeRange: nTimeRange } = input; if (nTimeRange && (nTimeRange.from !== timeRange.from || nTimeRange.to !== timeRange.to)) { setTimeRange(nTimeRange); } - setShowAllGroupByInstances(input.showAllGroupByInstances); } setLastRefreshTime(Date.now()); }); @@ -100,10 +89,10 @@ export function SloAlertsWrapper({ },rangeTo:${timeRange.to})` ); }; - return ( ; + initialInput?: EmbeddableSloProps; onCreate: (props: EmbeddableSloProps) => void; onCancel: () => void; } diff --git a/x-pack/plugins/observability_solution/slo/public/embeddable/slo/alerts/types.ts b/x-pack/plugins/observability_solution/slo/public/embeddable/slo/alerts/types.ts index 1483dc4898610e..8b660442ca7b66 100644 --- a/x-pack/plugins/observability_solution/slo/public/embeddable/slo/alerts/types.ts +++ b/x-pack/plugins/observability_solution/slo/public/embeddable/slo/alerts/types.ts @@ -4,8 +4,27 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ -import { EmbeddableInput } from '@kbn/embeddable-plugin/public'; -import type { TimeRange } from '@kbn/es-query'; +import { DefaultEmbeddableApi, EmbeddableInput } from '@kbn/embeddable-plugin/public'; +import { + type CoreStart, + IUiSettingsClient, + ApplicationStart, + NotificationsStart, +} from '@kbn/core/public'; +import { TriggersAndActionsUIPublicPluginStart } from '@kbn/triggers-actions-ui-plugin/public'; +import type { DataPublicPluginStart } from '@kbn/data-plugin/public'; +import { CasesPublicStart } from '@kbn/cases-plugin/public'; +import { SettingsStart } from '@kbn/core-ui-settings-browser'; +import { SecurityPluginStart } from '@kbn/security-plugin/public'; +import type { ChartsPluginStart } from '@kbn/charts-plugin/public'; +import type { UiActionsStart } from '@kbn/ui-actions-plugin/public'; +import { ServerlessPluginStart } from '@kbn/serverless/public'; +import { + SerializedTitles, + PublishesWritablePanelTitle, + PublishesPanelTitle, + EmbeddableApiContext, +} from '@kbn/presentation-publishing'; export interface SloItem { id: string; @@ -16,16 +35,21 @@ export interface SloItem { export interface EmbeddableSloProps { slos: SloItem[]; - timeRange?: TimeRange; - lastReloadRequestTime?: number | undefined; showAllGroupByInstances?: boolean; } export type SloAlertsEmbeddableInput = EmbeddableInput & EmbeddableSloProps; +export type SloAlertsEmbeddableState = SerializedTitles & EmbeddableSloProps; + +export type SloAlertsApi = DefaultEmbeddableApi & + PublishesWritablePanelTitle & + PublishesPanelTitle & + HasSloAlertsConfig; + export interface HasSloAlertsConfig { - getSloAlertsConfig: () => SloAlertsEmbeddableInput; - updateSloAlertsConfig: (next: SloAlertsEmbeddableInput) => void; + getSloAlertsConfig: () => EmbeddableSloProps; + updateSloAlertsConfig: (next: EmbeddableSloProps) => void; } export const apiHasSloAlertsConfig = (api: unknown | null): api is HasSloAlertsConfig => { @@ -35,3 +59,23 @@ export const apiHasSloAlertsConfig = (api: unknown | null): api is HasSloAlertsC typeof (api as HasSloAlertsConfig).updateSloAlertsConfig === 'function' ); }; + +export type SloAlertsEmbeddableActionContext = EmbeddableApiContext & { + embeddable: SloAlertsApi; +}; + +export interface SloEmbeddableDeps { + uiSettings: IUiSettingsClient; + http: CoreStart['http']; + i18n: CoreStart['i18n']; + application: ApplicationStart; + triggersActionsUi: TriggersAndActionsUIPublicPluginStart; + data: DataPublicPluginStart; + notifications: NotificationsStart; + cases: CasesPublicStart; + settings: SettingsStart; + security: SecurityPluginStart; + charts: ChartsPluginStart; + uiActions: UiActionsStart; + serverless?: ServerlessPluginStart; +} diff --git a/x-pack/plugins/observability_solution/slo/public/locators/slo_edit.test.ts b/x-pack/plugins/observability_solution/slo/public/locators/slo_edit.test.ts index 5ed997bcec129d..ea85e584796360 100644 --- a/x-pack/plugins/observability_solution/slo/public/locators/slo_edit.test.ts +++ b/x-pack/plugins/observability_solution/slo/public/locators/slo_edit.test.ts @@ -20,7 +20,7 @@ describe('SloEditLocator', () => { it('should return correct url when slo is provided', async () => { const location = await locator.getLocation(buildSlo({ id: 'foo' })); expect(location.path).toEqual( - "/edit/foo?_a=(budgetingMethod:occurrences,createdAt:'2022-12-29T10:11:12.000Z',description:'some%20description%20useful',enabled:!t,groupBy:'*',groupings:(),id:foo,indicator:(params:(filter:'baz:%20foo%20and%20bar%20%3E%202',good:'http_status:%202xx',index:some-index,timestampField:custom_timestamp,total:'a%20query'),type:sli.kql.custom),instanceId:'*',meta:(),name:'super%20important%20level%20service',objective:(target:0.98),revision:1,settings:(frequency:'1m',syncDelay:'1m'),summary:(errorBudget:(consumed:0.064,initial:0.02,isEstimated:!f,remaining:0.936),sliValue:0.99872,status:HEALTHY),tags:!(k8s,production,critical),timeWindow:(duration:'30d',type:rolling),updatedAt:'2022-12-29T10:11:12.000Z',version:2)" + "/edit/foo?_a=(budgetingMethod:occurrences,createdAt:'2022-12-29T10:11:12.000Z',description:'some%20description%20useful',enabled:!t,groupBy:'*',groupings:(),id:foo,indicator:(params:(filter:'baz:%20foo%20and%20bar%20%3E%202',good:'http_status:%202xx',index:some-index,timestampField:custom_timestamp,total:'a%20query'),type:sli.kql.custom),instanceId:'*',meta:(),name:'super%20important%20level%20service',objective:(target:0.98),revision:1,settings:(frequency:'1m',preventInitialBackfill:!f,syncDelay:'1m'),summary:(errorBudget:(consumed:0.064,initial:0.02,isEstimated:!f,remaining:0.936),sliValue:0.99872,status:HEALTHY),tags:!(k8s,production,critical),timeWindow:(duration:'30d',type:rolling),updatedAt:'2022-12-29T10:11:12.000Z',version:2)" ); }); }); diff --git a/x-pack/plugins/observability_solution/slo/public/pages/slo_edit/components/slo_edit_form_objective_section.tsx b/x-pack/plugins/observability_solution/slo/public/pages/slo_edit/components/slo_edit_form_objective_section.tsx index ebb82504937486..15c51b1b86ce4f 100644 --- a/x-pack/plugins/observability_solution/slo/public/pages/slo_edit/components/slo_edit_form_objective_section.tsx +++ b/x-pack/plugins/observability_solution/slo/public/pages/slo_edit/components/slo_edit_form_objective_section.tsx @@ -7,6 +7,7 @@ import { EuiCallOut, + EuiCheckbox, EuiFieldNumber, EuiFlexGrid, EuiFlexItem, @@ -18,10 +19,10 @@ import { useGeneratedHtmlId, } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; +import { FormattedMessage } from '@kbn/i18n-react'; import { TimeWindowType } from '@kbn/slo-schema'; import React, { useEffect, useState } from 'react'; import { Controller, useFormContext } from 'react-hook-form'; -import { FormattedMessage } from '@kbn/i18n-react'; import { BUDGETING_METHOD_OPTIONS, CALENDARALIGNED_TIMEWINDOW_OPTIONS, @@ -43,6 +44,7 @@ export function SloEditFormObjectiveSection() { const budgetingSelect = useGeneratedHtmlId({ prefix: 'budgetingSelect' }); const timeWindowTypeSelect = useGeneratedHtmlId({ prefix: 'timeWindowTypeSelect' }); const timeWindowSelect = useGeneratedHtmlId({ prefix: 'timeWindowSelect' }); + const preventBackfillCheckbox = useGeneratedHtmlId({ prefix: 'preventBackfill' }); const timeWindowType = watch('timeWindow.type'); const indicator = watch('indicator.type'); @@ -283,6 +285,43 @@ export function SloEditFormObjectiveSection() { + + + + + + + ( + + {i18n.translate('xpack.slo.sloEdit.settings.preventInitialBackfill.label', { + defaultMessage: 'Prevent initial backfill of data', + })} + + + } + checked={Boolean(field.value)} + onChange={(event: any) => onChange(event.target.checked)} + /> + )} + /> + + + ); } diff --git a/x-pack/plugins/observability_solution/slo/public/pages/slo_edit/constants.ts b/x-pack/plugins/observability_solution/slo/public/pages/slo_edit/constants.ts index 2c79f9736fce7f..123ebdc6609478 100644 --- a/x-pack/plugins/observability_solution/slo/public/pages/slo_edit/constants.ts +++ b/x-pack/plugins/observability_solution/slo/public/pages/slo_edit/constants.ts @@ -219,6 +219,9 @@ export const SLO_EDIT_FORM_DEFAULT_VALUES: CreateSLOForm = { target: 99, }, groupBy: ALL_VALUE, + settings: { + preventInitialBackfill: false, + }, }; export const SLO_EDIT_FORM_DEFAULT_VALUES_CUSTOM_METRIC: CreateSLOForm = { @@ -235,6 +238,9 @@ export const SLO_EDIT_FORM_DEFAULT_VALUES_CUSTOM_METRIC: CreateSLOForm = { target: 99, }, groupBy: ALL_VALUE, + settings: { + preventInitialBackfill: false, + }, }; export const SLO_EDIT_FORM_DEFAULT_VALUES_SYNTHETICS_AVAILABILITY: CreateSLOForm = { @@ -251,6 +257,9 @@ export const SLO_EDIT_FORM_DEFAULT_VALUES_SYNTHETICS_AVAILABILITY: CreateSLOForm target: 99, }, groupBy: SYNTHETICS_DEFAULT_GROUPINGS, + settings: { + preventInitialBackfill: false, + }, }; export const COMPARATOR_GT = i18n.translate('xpack.slo.sloEdit.sliType.timesliceMetric.gtLabel', { diff --git a/x-pack/plugins/observability_solution/slo/public/pages/slo_edit/helpers/__snapshots__/process_slo_form_values.test.ts.snap b/x-pack/plugins/observability_solution/slo/public/pages/slo_edit/helpers/__snapshots__/process_slo_form_values.test.ts.snap index 4ff4e452a3475c..3f7ac0ce83bebb 100644 --- a/x-pack/plugins/observability_solution/slo/public/pages/slo_edit/helpers/__snapshots__/process_slo_form_values.test.ts.snap +++ b/x-pack/plugins/observability_solution/slo/public/pages/slo_edit/helpers/__snapshots__/process_slo_form_values.test.ts.snap @@ -25,6 +25,9 @@ Object { "objective": Object { "target": 99, }, + "settings": Object { + "preventInitialBackfill": false, + }, "tags": Array [], "timeWindow": Object { "duration": "30d", @@ -70,6 +73,9 @@ Object { "objective": Object { "target": 99, }, + "settings": Object { + "preventInitialBackfill": false, + }, "tags": Array [], "timeWindow": Object { "duration": "30d", @@ -97,6 +103,9 @@ Object { "objective": Object { "target": 99, }, + "settings": Object { + "preventInitialBackfill": false, + }, "tags": Array [], "timeWindow": Object { "duration": "30d", @@ -136,6 +145,9 @@ Object { "objective": Object { "target": 99, }, + "settings": Object { + "preventInitialBackfill": false, + }, "tags": Array [], "timeWindow": Object { "duration": "30d", @@ -165,6 +177,9 @@ Object { "timesliceTarget": 95, "timesliceWindow": "2", }, + "settings": Object { + "preventInitialBackfill": false, + }, "tags": Array [], "timeWindow": Object { "duration": "30d", @@ -192,6 +207,9 @@ Object { "objective": Object { "target": 99, }, + "settings": Object { + "preventInitialBackfill": false, + }, "tags": Array [], "timeWindow": Object { "duration": "1M", @@ -220,6 +238,9 @@ Object { "objective": Object { "target": 99, }, + "settings": Object { + "preventInitialBackfill": false, + }, "tags": Array [], "timeWindow": Object { "duration": "30d", @@ -249,6 +270,9 @@ Object { "objective": Object { "target": 99, }, + "settings": Object { + "preventInitialBackfill": false, + }, "tags": Array [], "timeWindow": Object { "duration": "30d", @@ -276,6 +300,9 @@ Object { "objective": Object { "target": 99, }, + "settings": Object { + "preventInitialBackfill": false, + }, "tags": Array [], "timeWindow": Object { "duration": "30d", @@ -303,6 +330,9 @@ Object { "objective": Object { "target": 99, }, + "settings": Object { + "preventInitialBackfill": false, + }, "tags": Array [], "timeWindow": Object { "duration": "30d", diff --git a/x-pack/plugins/observability_solution/slo/public/pages/slo_edit/helpers/process_slo_form_values.ts b/x-pack/plugins/observability_solution/slo/public/pages/slo_edit/helpers/process_slo_form_values.ts index fd8bb96e2d715b..8bbbcf9d2fee96 100644 --- a/x-pack/plugins/observability_solution/slo/public/pages/slo_edit/helpers/process_slo_form_values.ts +++ b/x-pack/plugins/observability_solution/slo/public/pages/slo_edit/helpers/process_slo_form_values.ts @@ -50,6 +50,9 @@ export function transformSloResponseToCreateSloForm( }, groupBy: [values.groupBy].flat(), tags: values.tags, + settings: { + preventInitialBackfill: values.settings?.preventInitialBackfill ?? false, + }, }; } @@ -76,6 +79,9 @@ export function transformCreateSLOFormToCreateSLOInput(values: CreateSLOForm): C }, tags: values.tags, groupBy: [values.groupBy].flat(), + settings: { + preventInitialBackfill: values.settings?.preventInitialBackfill ?? false, + }, }; } @@ -102,6 +108,9 @@ export function transformValuesToUpdateSLOInput(values: CreateSLOForm): UpdateSL }, tags: values.tags, groupBy: [values.groupBy].flat(), + settings: { + preventInitialBackfill: values.settings?.preventInitialBackfill ?? false, + }, }; } @@ -211,5 +220,9 @@ export function transformPartialUrlStateToFormState( state.timeWindow = { duration: values.timeWindow.duration, type: values.timeWindow.type }; } + if (!!values.settings?.preventInitialBackfill) { + state.settings = { preventInitialBackfill: values.settings.preventInitialBackfill }; + } + return state; } diff --git a/x-pack/plugins/observability_solution/slo/public/pages/slo_edit/types.ts b/x-pack/plugins/observability_solution/slo/public/pages/slo_edit/types.ts index d876e3a276208d..5eef9a2d0e5bad 100644 --- a/x-pack/plugins/observability_solution/slo/public/pages/slo_edit/types.ts +++ b/x-pack/plugins/observability_solution/slo/public/pages/slo_edit/types.ts @@ -23,4 +23,7 @@ export interface CreateSLOForm { timesliceWindow?: string; }; groupBy: string[] | string; + settings: { + preventInitialBackfill: boolean; + }; } diff --git a/x-pack/plugins/observability_solution/slo/public/plugin.ts b/x-pack/plugins/observability_solution/slo/public/plugin.ts index 99f586790cd99e..e387a7f85a7e3b 100644 --- a/x-pack/plugins/observability_solution/slo/public/plugin.ts +++ b/x-pack/plugins/observability_solution/slo/public/plugin.ts @@ -28,6 +28,8 @@ import { ExperimentalFeatures, SloConfig } from '../common/config'; import { SLO_OVERVIEW_EMBEDDABLE_ID } from './embeddable/slo/overview/constants'; import { SloOverviewEmbeddableState } from './embeddable/slo/overview/types'; import { SLO_ERROR_BUDGET_ID } from './embeddable/slo/error_budget/constants'; +import { SLO_ALERTS_EMBEDDABLE_ID } from './embeddable/slo/alerts/constants'; + export class SloPlugin implements Plugin { @@ -113,17 +115,19 @@ export class SloPlugin return getOverviewEmbeddableFactory(deps); } ); - const registerSloAlertsEmbeddableFactory = async () => { - const { SloAlertsEmbeddableFactoryDefinition } = await import( - './embeddable/slo/alerts/slo_alerts_embeddable_factory' - ); - const factory = new SloAlertsEmbeddableFactoryDefinition( - coreSetup.getStartServices, - kibanaVersion - ); - pluginsSetup.embeddable.registerEmbeddableFactory(factory.type, factory); - }; - registerSloAlertsEmbeddableFactory(); + + pluginsSetup.embeddable.registerReactEmbeddableFactory( + SLO_ALERTS_EMBEDDABLE_ID, + async () => { + const deps = { ...coreStart, ...pluginsStart }; + + const { getAlertsEmbeddableFactory } = await import( + './embeddable/slo/alerts/slo_alerts_embeddable_factory' + ); + + return getAlertsEmbeddableFactory(deps, kibanaVersion); + } + ); pluginsSetup.embeddable.registerReactEmbeddableFactory(SLO_ERROR_BUDGET_ID, async () => { const deps = { ...coreStart, ...pluginsStart }; diff --git a/x-pack/plugins/observability_solution/slo/public/ui_actions/create_alerts_panel_action.tsx b/x-pack/plugins/observability_solution/slo/public/ui_actions/create_alerts_panel_action.tsx new file mode 100644 index 00000000000000..b365881bf915a0 --- /dev/null +++ b/x-pack/plugins/observability_solution/slo/public/ui_actions/create_alerts_panel_action.tsx @@ -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 type { CoreSetup } from '@kbn/core/public'; +import { apiIsPresentationContainer } from '@kbn/presentation-containers'; +import { + IncompatibleActionError, + type UiActionsActionDefinition, +} from '@kbn/ui-actions-plugin/public'; +import { EmbeddableApiContext } from '@kbn/presentation-publishing'; +import { + ADD_SLO_ALERTS_ACTION_ID, + SLO_ALERTS_EMBEDDABLE_ID, +} from '../embeddable/slo/alerts/constants'; +import { SloPublicPluginsStart, SloPublicStart } from '..'; +import { COMMON_SLO_GROUPING } from '../embeddable/slo/common/constants'; + +export function createAddAlertsPanelAction( + getStartServices: CoreSetup['getStartServices'] +): UiActionsActionDefinition { + return { + id: ADD_SLO_ALERTS_ACTION_ID, + grouping: COMMON_SLO_GROUPING, + getIconType: () => 'alert', + isCompatible: async ({ embeddable }) => { + return apiIsPresentationContainer(embeddable); + }, + execute: async ({ embeddable }) => { + if (!apiIsPresentationContainer(embeddable)) throw new IncompatibleActionError(); + const [coreStart, deps] = await getStartServices(); + try { + const { openSloConfiguration } = await import( + '../embeddable/slo/alerts/slo_alerts_open_configuration' + ); + const initialState = await openSloConfiguration(coreStart, deps); + embeddable.addNewPanel( + { + panelType: SLO_ALERTS_EMBEDDABLE_ID, + initialState, + }, + true + ); + } catch (e) { + return Promise.reject(); + } + }, + getDisplayName: () => + i18n.translate('xpack.slo.sloAlertsEmbeddable.displayName', { + defaultMessage: 'SLO Alerts', + }), + }; +} diff --git a/x-pack/plugins/observability_solution/slo/public/ui_actions/edit_slo_alerts_panel.tsx b/x-pack/plugins/observability_solution/slo/public/ui_actions/edit_slo_alerts_panel.tsx index 454833a26af942..ce9b4d196ffb5b 100644 --- a/x-pack/plugins/observability_solution/slo/public/ui_actions/edit_slo_alerts_panel.tsx +++ b/x-pack/plugins/observability_solution/slo/public/ui_actions/edit_slo_alerts_panel.tsx @@ -17,26 +17,27 @@ import { CanAccessViewMode, HasType, } from '@kbn/presentation-publishing'; -import { createAction } from '@kbn/ui-actions-plugin/public'; -import type { SLOAlertsEmbeddable } from '../embeddable/slo/alerts/slo_alerts_embeddable'; -import { SLO_ALERTS_EMBEDDABLE } from '../embeddable/slo/constants'; +import { type UiActionsActionDefinition } from '@kbn/ui-actions-plugin/public'; +import { SLO_ALERTS_EMBEDDABLE_ID } from '../embeddable/slo/alerts/constants'; import { SloPublicPluginsStart, SloPublicStart } from '..'; -import { HasSloAlertsConfig } from '../embeddable/slo/alerts/types'; - +import { + HasSloAlertsConfig, + SloAlertsEmbeddableActionContext, +} from '../embeddable/slo/alerts/types'; export const EDIT_SLO_ALERTS_ACTION = 'editSloAlertsPanelAction'; type EditSloAlertsPanelApi = CanAccessViewMode & HasType & HasSloAlertsConfig; const isEditSloAlertsPanelApi = (api: unknown): api is EditSloAlertsPanelApi => Boolean( apiHasType(api) && - apiIsOfType(api, SLO_ALERTS_EMBEDDABLE) && + apiIsOfType(api, SLO_ALERTS_EMBEDDABLE_ID) && apiCanAccessViewMode(api) && getInheritedViewMode(api) === ViewMode.EDIT ); export function createEditSloAlertsPanelAction( getStartServices: CoreSetup['getStartServices'] -) { - return createAction({ +): UiActionsActionDefinition { + return { id: EDIT_SLO_ALERTS_ACTION, type: EDIT_SLO_ALERTS_ACTION, getIconType(): string { @@ -46,7 +47,7 @@ export function createEditSloAlertsPanelAction( i18n.translate('xpack.slo.actions.editSloAlertsEmbeddableTitle', { defaultMessage: 'Edit configuration', }), - async execute({ embeddable }: EmbeddableApiContext) { + async execute({ embeddable }) { if (!embeddable) { throw new Error('Not possible to execute an action without the embeddable context'); } @@ -54,21 +55,21 @@ export function createEditSloAlertsPanelAction( const [coreStart, pluginStart] = await getStartServices(); try { - const { resolveEmbeddableSloUserInput } = await import( - '../embeddable/slo/alerts/handle_explicit_input' + const { openSloConfiguration } = await import( + '../embeddable/slo/alerts/slo_alerts_open_configuration' ); - const result = await resolveEmbeddableSloUserInput( + const result = await openSloConfiguration( coreStart, pluginStart, - (embeddable as SLOAlertsEmbeddable).getSloAlertsConfig() + embeddable.getSloAlertsConfig() ); - (embeddable as SLOAlertsEmbeddable).updateInput(result); + embeddable.updateSloAlertsConfig(result); } catch (e) { return Promise.reject(); } }, isCompatible: async ({ embeddable }: EmbeddableApiContext) => isEditSloAlertsPanelApi(embeddable), - }); + }; } diff --git a/x-pack/plugins/observability_solution/slo/public/ui_actions/index.ts b/x-pack/plugins/observability_solution/slo/public/ui_actions/index.ts index e544a36fd84f7c..76862a3afe90d8 100644 --- a/x-pack/plugins/observability_solution/slo/public/ui_actions/index.ts +++ b/x-pack/plugins/observability_solution/slo/public/ui_actions/index.ts @@ -12,6 +12,7 @@ import { createEditSloAlertsPanelAction } from './edit_slo_alerts_panel'; import { createEditSloOverviewPanelAction } from './edit_slo_overview_panel'; import { createOverviewPanelAction } from './create_overview_panel_action'; import { createAddErrorBudgetPanelAction } from './create_error_budget_action'; +import { createAddAlertsPanelAction } from './create_alerts_panel_action'; import { SloPublicPluginsStart, SloPublicStart } from '..'; export function registerSloUiActions( @@ -23,10 +24,12 @@ export function registerSloUiActions( const editSloOverviewPanelAction = createEditSloOverviewPanelAction(core.getStartServices); const addOverviewPanelAction = createOverviewPanelAction(core.getStartServices); const addErrorBudgetPanelAction = createAddErrorBudgetPanelAction(core.getStartServices); + const addAlertsPanelAction = createAddAlertsPanelAction(core.getStartServices); // Assign triggers uiActions.addTriggerAction(CONTEXT_MENU_TRIGGER, editSloAlertsPanelAction); uiActions.addTriggerAction(CONTEXT_MENU_TRIGGER, editSloOverviewPanelAction); uiActions.addTriggerAction('ADD_PANEL_TRIGGER', addOverviewPanelAction); uiActions.addTriggerAction('ADD_PANEL_TRIGGER', addErrorBudgetPanelAction); + uiActions.addTriggerAction('ADD_PANEL_TRIGGER', addAlertsPanelAction); } diff --git a/x-pack/plugins/observability_solution/slo/public/utils/slo/remote_slo_urls.test.ts b/x-pack/plugins/observability_solution/slo/public/utils/slo/remote_slo_urls.test.ts index 28ca2bd977572a..2c8476b9c2e099 100644 --- a/x-pack/plugins/observability_solution/slo/public/utils/slo/remote_slo_urls.test.ts +++ b/x-pack/plugins/observability_solution/slo/public/utils/slo/remote_slo_urls.test.ts @@ -51,7 +51,7 @@ describe('remote SLO URLs Utils', () => { `"https://cloud.elast.co/app/slos/edit/fixed-id"` ); expect(createRemoteSloCloneUrl(remoteSlo)).toMatchInlineSnapshot( - `"https://cloud.elast.co/app/slos/create?_a=(budgetingMethod:occurrences,createdAt:%272022-12-29T10:11:12.000Z%27,description:%27some%20description%20useful%27,enabled:!t,groupBy:%27*%27,groupings:(),indicator:(params:(filter:%27baz:%20foo%20and%20bar%20%3E%202%27,good:%27http_status:%202xx%27,index:some-index,timestampField:custom_timestamp,total:%27a%20query%27),type:sli.kql.custom),instanceId:%27*%27,meta:(),name:%27[Copy]%20super%20important%20level%20service%27,objective:(target:0.98),remote:(kibanaUrl:%27https:/cloud.elast.co/kibana%27,remoteName:remote_cluster),revision:1,settings:(frequency:%271m%27,syncDelay:%271m%27),summary:(errorBudget:(consumed:0.064,initial:0.02,isEstimated:!f,remaining:0.936),sliValue:0.99872,status:HEALTHY),tags:!(k8s,production,critical),timeWindow:(duration:%2730d%27,type:rolling),updatedAt:%272022-12-29T10:11:12.000Z%27,version:2)"` + `"https://cloud.elast.co/app/slos/create?_a=(budgetingMethod:occurrences,createdAt:%272022-12-29T10:11:12.000Z%27,description:%27some%20description%20useful%27,enabled:!t,groupBy:%27*%27,groupings:(),indicator:(params:(filter:%27baz:%20foo%20and%20bar%20%3E%202%27,good:%27http_status:%202xx%27,index:some-index,timestampField:custom_timestamp,total:%27a%20query%27),type:sli.kql.custom),instanceId:%27*%27,meta:(),name:%27[Copy]%20super%20important%20level%20service%27,objective:(target:0.98),remote:(kibanaUrl:%27https:/cloud.elast.co/kibana%27,remoteName:remote_cluster),revision:1,settings:(frequency:%271m%27,preventInitialBackfill:!f,syncDelay:%271m%27),summary:(errorBudget:(consumed:0.064,initial:0.02,isEstimated:!f,remaining:0.936),sliValue:0.99872,status:HEALTHY),tags:!(k8s,production,critical),timeWindow:(duration:%2730d%27,type:rolling),updatedAt:%272022-12-29T10:11:12.000Z%27,version:2)"` ); }); @@ -71,7 +71,7 @@ describe('remote SLO URLs Utils', () => { `"https://cloud.elast.co/s/my-custom-space/app/slos/edit/fixed-id"` ); expect(createRemoteSloCloneUrl(remoteSlo, 'my-custom-space')).toMatchInlineSnapshot( - `"https://cloud.elast.co/s/my-custom-space/app/slos/create?_a=(budgetingMethod:occurrences,createdAt:%272022-12-29T10:11:12.000Z%27,description:%27some%20description%20useful%27,enabled:!t,groupBy:%27*%27,groupings:(),indicator:(params:(filter:%27baz:%20foo%20and%20bar%20%3E%202%27,good:%27http_status:%202xx%27,index:some-index,timestampField:custom_timestamp,total:%27a%20query%27),type:sli.kql.custom),instanceId:%27*%27,meta:(),name:%27[Copy]%20super%20important%20level%20service%27,objective:(target:0.98),remote:(kibanaUrl:%27https:/cloud.elast.co/kibana%27,remoteName:remote_cluster),revision:1,settings:(frequency:%271m%27,syncDelay:%271m%27),summary:(errorBudget:(consumed:0.064,initial:0.02,isEstimated:!f,remaining:0.936),sliValue:0.99872,status:HEALTHY),tags:!(k8s,production,critical),timeWindow:(duration:%2730d%27,type:rolling),updatedAt:%272022-12-29T10:11:12.000Z%27,version:2)"` + `"https://cloud.elast.co/s/my-custom-space/app/slos/create?_a=(budgetingMethod:occurrences,createdAt:%272022-12-29T10:11:12.000Z%27,description:%27some%20description%20useful%27,enabled:!t,groupBy:%27*%27,groupings:(),indicator:(params:(filter:%27baz:%20foo%20and%20bar%20%3E%202%27,good:%27http_status:%202xx%27,index:some-index,timestampField:custom_timestamp,total:%27a%20query%27),type:sli.kql.custom),instanceId:%27*%27,meta:(),name:%27[Copy]%20super%20important%20level%20service%27,objective:(target:0.98),remote:(kibanaUrl:%27https:/cloud.elast.co/kibana%27,remoteName:remote_cluster),revision:1,settings:(frequency:%271m%27,preventInitialBackfill:!f,syncDelay:%271m%27),summary:(errorBudget:(consumed:0.064,initial:0.02,isEstimated:!f,remaining:0.936),sliValue:0.99872,status:HEALTHY),tags:!(k8s,production,critical),timeWindow:(duration:%2730d%27,type:rolling),updatedAt:%272022-12-29T10:11:12.000Z%27,version:2)"` ); }); }); diff --git a/x-pack/plugins/observability_solution/slo/server/domain/services/validate_slo.test.ts b/x-pack/plugins/observability_solution/slo/server/domain/services/validate_slo.test.ts index 7a4a6fc40005b2..5c06c89ee42aa8 100644 --- a/x-pack/plugins/observability_solution/slo/server/domain/services/validate_slo.test.ts +++ b/x-pack/plugins/observability_solution/slo/server/domain/services/validate_slo.test.ts @@ -103,6 +103,7 @@ describe('validateSLO', () => { settings: { frequency: sixHours(), syncDelay: oneMinute(), + preventInitialBackfill: false, }, }); expect(() => validateSLO(slo)).toThrowError('Invalid settings.frequency'); @@ -113,6 +114,7 @@ describe('validateSLO', () => { settings: { frequency: oneMinute(), syncDelay: sixHours(), + preventInitialBackfill: false, }, }); expect(() => validateSLO(slo)).toThrowError('Invalid settings.sync_delay'); diff --git a/x-pack/plugins/observability_solution/slo/server/services/__snapshots__/reset_slo.test.ts.snap b/x-pack/plugins/observability_solution/slo/server/services/__snapshots__/reset_slo.test.ts.snap index 6c472f3a3450ae..9d231462c03afc 100644 --- a/x-pack/plugins/observability_solution/slo/server/services/__snapshots__/reset_slo.test.ts.snap +++ b/x-pack/plugins/observability_solution/slo/server/services/__snapshots__/reset_slo.test.ts.snap @@ -147,6 +147,7 @@ exports[`ResetSLO resets all associated resources 6`] = ` "unit": "m", "value": 1, }, + "preventInitialBackfill": false, "syncDelay": Duration { "unit": "m", "value": 1, @@ -410,6 +411,7 @@ exports[`ResetSLO resets all associated resources 9`] = ` "unit": "m", "value": 1, }, + "preventInitialBackfill": false, "syncDelay": Duration { "unit": "m", "value": 1, diff --git a/x-pack/plugins/observability_solution/slo/server/services/__snapshots__/slo_definition_client.test.ts.snap b/x-pack/plugins/observability_solution/slo/server/services/__snapshots__/slo_definition_client.test.ts.snap index 95b3ca924632eb..671c5c13a39eaa 100644 --- a/x-pack/plugins/observability_solution/slo/server/services/__snapshots__/slo_definition_client.test.ts.snap +++ b/x-pack/plugins/observability_solution/slo/server/services/__snapshots__/slo_definition_client.test.ts.snap @@ -36,6 +36,7 @@ Object { "unit": "m", "value": 1, }, + "preventInitialBackfill": false, "syncDelay": Duration { "unit": "m", "value": 1, diff --git a/x-pack/plugins/observability_solution/slo/server/services/create_slo.test.ts b/x-pack/plugins/observability_solution/slo/server/services/create_slo.test.ts index 8f6bee0fe34c58..cefff32016e2b9 100644 --- a/x-pack/plugins/observability_solution/slo/server/services/create_slo.test.ts +++ b/x-pack/plugins/observability_solution/slo/server/services/create_slo.test.ts @@ -68,6 +68,7 @@ describe('CreateSLO', () => { settings: { syncDelay: oneMinute(), frequency: oneMinute(), + preventInitialBackfill: false, }, revision: 1, tags: [], @@ -108,6 +109,40 @@ describe('CreateSLO', () => { settings: { syncDelay: fiveMinute(), frequency: oneMinute(), + preventInitialBackfill: false, + }, + revision: 1, + tags: ['one', 'two'], + enabled: true, + createdAt: expect.any(Date), + updatedAt: expect.any(Date), + }), + { throwOnConflict: true } + ); + }); + + it('overrides the settings when provided', async () => { + const sloParams = createSLOParams({ + indicator: createAPMTransactionErrorRateIndicator(), + tags: ['one', 'two'], + settings: { + syncDelay: fiveMinute(), + frequency: fiveMinute(), + preventInitialBackfill: true, + }, + }); + mockTransformManager.install.mockResolvedValue('slo-transform-id'); + + await createSLO.execute(sloParams); + + expect(mockRepository.save).toHaveBeenCalledWith( + expect.objectContaining({ + ...sloParams, + id: expect.any(String), + settings: { + syncDelay: fiveMinute(), + frequency: fiveMinute(), + preventInitialBackfill: true, }, revision: 1, tags: ['one', 'two'], diff --git a/x-pack/plugins/observability_solution/slo/server/services/create_slo.ts b/x-pack/plugins/observability_solution/slo/server/services/create_slo.ts index ce8258a222df3d..60a5da93d9bf7d 100644 --- a/x-pack/plugins/observability_solution/slo/server/services/create_slo.ts +++ b/x-pack/plugins/observability_solution/slo/server/services/create_slo.ts @@ -127,6 +127,7 @@ export class CreateSLO { settings: { syncDelay: params.settings?.syncDelay ?? new Duration(1, DurationUnit.Minute), frequency: params.settings?.frequency ?? new Duration(1, DurationUnit.Minute), + preventInitialBackfill: params.settings?.preventInitialBackfill ?? false, }, revision: params.revision ?? 1, enabled: true, diff --git a/x-pack/plugins/observability_solution/slo/server/services/find_slo.test.ts b/x-pack/plugins/observability_solution/slo/server/services/find_slo.test.ts index 239b3aaaec5188..183d4beee19dd5 100644 --- a/x-pack/plugins/observability_solution/slo/server/services/find_slo.test.ts +++ b/x-pack/plugins/observability_solution/slo/server/services/find_slo.test.ts @@ -80,6 +80,7 @@ describe('FindSLO', () => { settings: { syncDelay: '1m', frequency: '1m', + preventInitialBackfill: false, }, summary: { status: 'HEALTHY', diff --git a/x-pack/plugins/observability_solution/slo/server/services/fixtures/slo.ts b/x-pack/plugins/observability_solution/slo/server/services/fixtures/slo.ts index 74a548a2ad4c4f..c290523512b9cf 100644 --- a/x-pack/plugins/observability_solution/slo/server/services/fixtures/slo.ts +++ b/x-pack/plugins/observability_solution/slo/server/services/fixtures/slo.ts @@ -166,6 +166,7 @@ const defaultSLO: Omit { settings: { syncDelay: '1m', frequency: '1m', + preventInitialBackfill: false, }, summary: { status: 'HEALTHY', diff --git a/x-pack/plugins/observability_solution/slo/server/services/slo_repository.ts b/x-pack/plugins/observability_solution/slo/server/services/slo_repository.ts index 1f8e1d415c484e..9f300a148ac2e3 100644 --- a/x-pack/plugins/observability_solution/slo/server/services/slo_repository.ts +++ b/x-pack/plugins/observability_solution/slo/server/services/slo_repository.ts @@ -138,6 +138,11 @@ export class KibanaSavedObjectsSLORepository implements SLORepository { // if not present, we considered the version to be 1, e.g. not migrated. // We would need to call the _reset api on this SLO. version: storedSLO.version ?? 1, + // settings.preventInitialBackfill was added in 8.15.0 + settings: { + ...storedSLO.settings, + preventInitialBackfill: storedSLO.settings?.preventInitialBackfill ?? false, + }, }); if (isLeft(result)) { diff --git a/x-pack/plugins/observability_solution/slo/server/services/transform_generators/apm_transaction_duration.test.ts b/x-pack/plugins/observability_solution/slo/server/services/transform_generators/apm_transaction_duration.test.ts index 4a0ade1c9aa68e..2ab6b264429673 100644 --- a/x-pack/plugins/observability_solution/slo/server/services/transform_generators/apm_transaction_duration.test.ts +++ b/x-pack/plugins/observability_solution/slo/server/services/transform_generators/apm_transaction_duration.test.ts @@ -150,4 +150,28 @@ describe('APM Transaction Duration Transform Generator', () => { expect(transform.source.query).toMatchSnapshot(); expect(transform.pivot?.group_by).toMatchSnapshot(); }); + + it("overrides the range filter when 'preventInitialBackfill' is true", () => { + const slo = createSLO({ + indicator: createAPMTransactionDurationIndicator(), + settings: { + frequency: twoMinute(), + syncDelay: twoMinute(), + preventInitialBackfill: true, + }, + }); + + const transform = generator.getTransformParams(slo); + + // @ts-ignore + const rangeFilter = transform.source.query.bool.filter.find((f) => 'range' in f); + + expect(rangeFilter).toEqual({ + range: { + '@timestamp': { + gte: 'now-300s/m', // 2m + 2m + 60s + }, + }, + }); + }); }); diff --git a/x-pack/plugins/observability_solution/slo/server/services/transform_generators/apm_transaction_duration.ts b/x-pack/plugins/observability_solution/slo/server/services/transform_generators/apm_transaction_duration.ts index 7dde0e0a664e3e..6bbec476754cb7 100644 --- a/x-pack/plugins/observability_solution/slo/server/services/transform_generators/apm_transaction_duration.ts +++ b/x-pack/plugins/observability_solution/slo/server/services/transform_generators/apm_transaction_duration.ts @@ -23,7 +23,7 @@ import { getSLOTransformTemplate } from '../../assets/transform_templates/slo_tr import { APMTransactionDurationIndicator, SLODefinition } from '../../domain/models'; import { InvalidTransformError } from '../../errors'; import { parseIndex } from './common'; -import { getTimesliceTargetComparator } from './common'; +import { getTimesliceTargetComparator, getFilterRange } from './common'; export class ApmTransactionDurationTransformGenerator extends TransformGenerator { public getTransformParams(slo: SLODefinition): TransformPutTransformRequest { @@ -71,15 +71,7 @@ export class ApmTransactionDurationTransformGenerator extends TransformGenerator } private buildSource(slo: SLODefinition, indicator: APMTransactionDurationIndicator) { - const queryFilter: estypes.QueryDslQueryContainer[] = [ - { - range: { - '@timestamp': { - gte: `now-${slo.timeWindow.duration.format()}/d`, - }, - }, - }, - ]; + const queryFilter: estypes.QueryDslQueryContainer[] = [getFilterRange(slo, '@timestamp')]; if (indicator.params.service !== ALL_VALUE) { queryFilter.push({ diff --git a/x-pack/plugins/observability_solution/slo/server/services/transform_generators/apm_transaction_error_rate.test.ts b/x-pack/plugins/observability_solution/slo/server/services/transform_generators/apm_transaction_error_rate.test.ts index c9a85ae5df34e9..5706521cdf2f68 100644 --- a/x-pack/plugins/observability_solution/slo/server/services/transform_generators/apm_transaction_error_rate.test.ts +++ b/x-pack/plugins/observability_solution/slo/server/services/transform_generators/apm_transaction_error_rate.test.ts @@ -6,7 +6,7 @@ */ import { ALL_VALUE } from '@kbn/slo-schema'; -import { twoMinute } from '../fixtures/duration'; +import { oneMinute, twoMinute } from '../fixtures/duration'; import { createAPMTransactionErrorRateIndicator, createSLO, @@ -153,4 +153,28 @@ describe('APM Transaction Error Rate Transform Generator', () => { expect(transform.source.query).toMatchSnapshot(); expect(transform.pivot?.group_by).toMatchSnapshot(); }); + + it("overrides the range filter when 'preventInitialBackfill' is true", () => { + const slo = createSLO({ + indicator: createAPMTransactionErrorRateIndicator(), + settings: { + frequency: oneMinute(), + syncDelay: twoMinute(), + preventInitialBackfill: true, + }, + }); + + const transform = generator.getTransformParams(slo); + + // @ts-ignore + const rangeFilter = transform.source.query.bool.filter.find((f) => 'range' in f); + + expect(rangeFilter).toEqual({ + range: { + '@timestamp': { + gte: 'now-240s/m', // 1m + 2m + 60s + }, + }, + }); + }); }); diff --git a/x-pack/plugins/observability_solution/slo/server/services/transform_generators/apm_transaction_error_rate.ts b/x-pack/plugins/observability_solution/slo/server/services/transform_generators/apm_transaction_error_rate.ts index 99730152ea322c..59b753711a0d89 100644 --- a/x-pack/plugins/observability_solution/slo/server/services/transform_generators/apm_transaction_error_rate.ts +++ b/x-pack/plugins/observability_solution/slo/server/services/transform_generators/apm_transaction_error_rate.ts @@ -21,8 +21,7 @@ import { import { getSLOTransformTemplate } from '../../assets/transform_templates/slo_transform_template'; import { APMTransactionErrorRateIndicator, SLODefinition } from '../../domain/models'; import { InvalidTransformError } from '../../errors'; -import { parseIndex } from './common'; -import { getTimesliceTargetComparator } from './common'; +import { parseIndex, getTimesliceTargetComparator, getFilterRange } from './common'; export class ApmTransactionErrorRateTransformGenerator extends TransformGenerator { public getTransformParams(slo: SLODefinition): TransformPutTransformRequest { @@ -70,15 +69,7 @@ export class ApmTransactionErrorRateTransformGenerator extends TransformGenerato } private buildSource(slo: SLODefinition, indicator: APMTransactionErrorRateIndicator) { - const queryFilter: estypes.QueryDslQueryContainer[] = [ - { - range: { - '@timestamp': { - gte: `now-${slo.timeWindow.duration.format()}/d`, - }, - }, - }, - ]; + const queryFilter: estypes.QueryDslQueryContainer[] = [getFilterRange(slo, '@timestamp')]; if (indicator.params.service !== ALL_VALUE) { queryFilter.push({ diff --git a/x-pack/plugins/observability_solution/slo/server/services/transform_generators/common.test.ts b/x-pack/plugins/observability_solution/slo/server/services/transform_generators/common.test.ts index baf02a9102f029..2e52a5442c12e6 100644 --- a/x-pack/plugins/observability_solution/slo/server/services/transform_generators/common.test.ts +++ b/x-pack/plugins/observability_solution/slo/server/services/transform_generators/common.test.ts @@ -5,7 +5,10 @@ * 2.0. */ -import { getTimesliceTargetComparator, parseIndex } from './common'; +import { fiveMinute, twoMinute } from '../fixtures/duration'; +import { createSLO } from '../fixtures/slo'; +import { thirtyDaysRolling } from '../fixtures/time_window'; +import { getTimesliceTargetComparator, parseIndex, getFilterRange } from './common'; describe('common', () => { describe('parseIndex', () => { @@ -30,4 +33,49 @@ describe('common', () => { expect(getTimesliceTargetComparator(0.000000001)).toBe('>='); }); }); + + describe('getFilterRange', () => { + it('starts at now (accounting for delay) when preventInitialBackfill is true', () => { + expect( + getFilterRange( + createSLO({ + settings: { + frequency: twoMinute(), + syncDelay: fiveMinute(), + preventInitialBackfill: true, + }, + }), + '@timestamp' + ) + ).toEqual({ + range: { + '@timestamp': { + gte: 'now-480s/m', + }, + }, + }); + }); + + it('starts at now minus the time window when preventInitialBackfill is false', () => { + expect( + getFilterRange( + createSLO({ + timeWindow: thirtyDaysRolling(), + settings: { + frequency: twoMinute(), + syncDelay: fiveMinute(), + preventInitialBackfill: false, + }, + }), + '@timestamp' + ) + ).toEqual({ + range: { + '@timestamp': { + gte: 'now-30d/d', + }, + }, + }); + }); + }); }); diff --git a/x-pack/plugins/observability_solution/slo/server/services/transform_generators/common.ts b/x-pack/plugins/observability_solution/slo/server/services/transform_generators/common.ts index 12822d6b41e68f..fd0a65d9c38874 100644 --- a/x-pack/plugins/observability_solution/slo/server/services/transform_generators/common.ts +++ b/x-pack/plugins/observability_solution/slo/server/services/transform_generators/common.ts @@ -6,7 +6,9 @@ */ import { buildEsQuery, fromKueryExpression, toElasticsearchQuery } from '@kbn/es-query'; -import { QuerySchema, kqlQuerySchema } from '@kbn/slo-schema'; +import { kqlQuerySchema, QuerySchema } from '@kbn/slo-schema'; +import { SLODefinition } from '../../domain/models'; +import { getDelayInSecondsFromSLO } from '../../domain/services/get_delay_in_seconds_from_slo'; import { InvalidTransformError } from '../../errors'; export function getElasticsearchQueryOrThrow(kuery: QuerySchema = '') { @@ -39,3 +41,26 @@ export function parseIndex(index: string): string | string[] { export function getTimesliceTargetComparator(timesliceTarget: number) { return timesliceTarget === 0 ? '>' : '>='; } + +/** + * Use the settings.preventInitialBackfill flag to determine the range filter for the rollup transform + * preventInitialBackfill == true: we use the current time minus some buffer to account for the ingestion delay + * preventInitialBackfill === false: we use the time window duration to get the data for the last N days + */ +export function getFilterRange(slo: SLODefinition, timestampField: string) { + return slo.settings.preventInitialBackfill === true + ? { + range: { + [timestampField]: { + gte: `now-${getDelayInSecondsFromSLO(slo)}s/m`, + }, + }, + } + : { + range: { + [timestampField]: { + gte: `now-${slo.timeWindow.duration.format()}/d`, + }, + }, + }; +} diff --git a/x-pack/plugins/observability_solution/slo/server/services/transform_generators/histogram.test.ts b/x-pack/plugins/observability_solution/slo/server/services/transform_generators/histogram.test.ts index 172d7c50cd59e4..2307956564ba3c 100644 --- a/x-pack/plugins/observability_solution/slo/server/services/transform_generators/histogram.test.ts +++ b/x-pack/plugins/observability_solution/slo/server/services/transform_generators/histogram.test.ts @@ -165,4 +165,28 @@ describe('Histogram Transform Generator', () => { expect(transform.pivot!.aggregations!['slo.denominator']).toMatchSnapshot(); }); + + it("overrides the range filter when 'preventInitialBackfill' is true", () => { + const slo = createSLO({ + indicator: createHistogramIndicator(), + settings: { + frequency: twoMinute(), + syncDelay: twoMinute(), + preventInitialBackfill: true, + }, + }); + + const transform = generator.getTransformParams(slo); + + // @ts-ignore + const rangeFilter = transform.source.query.bool.filter.find((f) => 'range' in f); + + expect(rangeFilter).toEqual({ + range: { + log_timestamp: { + gte: 'now-300s/m', // 2m + 2m + 60s + }, + }, + }); + }); }); diff --git a/x-pack/plugins/observability_solution/slo/server/services/transform_generators/histogram.ts b/x-pack/plugins/observability_solution/slo/server/services/transform_generators/histogram.ts index 57fbaa630d367d..c8cfb52239f896 100644 --- a/x-pack/plugins/observability_solution/slo/server/services/transform_generators/histogram.ts +++ b/x-pack/plugins/observability_solution/slo/server/services/transform_generators/histogram.ts @@ -21,7 +21,7 @@ import { getSLOTransformTemplate } from '../../assets/transform_templates/slo_tr import { SLODefinition } from '../../domain/models'; import { InvalidTransformError } from '../../errors'; import { GetHistogramIndicatorAggregation } from '../aggregations'; -import { getTimesliceTargetComparator } from './common'; +import { getTimesliceTargetComparator, getFilterRange } from './common'; export class HistogramTransformGenerator extends TransformGenerator { public getTransformParams(slo: SLODefinition): TransformPutTransformRequest { @@ -52,13 +52,7 @@ export class HistogramTransformGenerator extends TransformGenerator { query: { bool: { filter: [ - { - range: { - [indicator.params.timestampField]: { - gte: `now-${slo.timeWindow.duration.format()}/d`, - }, - }, - }, + getFilterRange(slo, indicator.params.timestampField), getElasticsearchQueryOrThrow(indicator.params.filter), ], }, diff --git a/x-pack/plugins/observability_solution/slo/server/services/transform_generators/kql_custom.test.ts b/x-pack/plugins/observability_solution/slo/server/services/transform_generators/kql_custom.test.ts index 7489ebd06c5f4f..4b74695c8942e3 100644 --- a/x-pack/plugins/observability_solution/slo/server/services/transform_generators/kql_custom.test.ts +++ b/x-pack/plugins/observability_solution/slo/server/services/transform_generators/kql_custom.test.ts @@ -121,4 +121,28 @@ describe('KQL Custom Transform Generator', () => { expect(transform.pivot!.aggregations!['slo.denominator']).toMatchSnapshot(); }); + + it("overrides the range filter when 'preventInitialBackfill' is true", () => { + const slo = createSLO({ + indicator: createKQLCustomIndicator(), + settings: { + frequency: twoMinute(), + syncDelay: twoMinute(), + preventInitialBackfill: true, + }, + }); + + const transform = generator.getTransformParams(slo); + + // @ts-ignore + const rangeFilter = transform.source.query.bool.filter.find((f) => 'range' in f); + + expect(rangeFilter).toEqual({ + range: { + log_timestamp: { + gte: 'now-300s/m', // 2m + 2m + 60s + }, + }, + }); + }); }); diff --git a/x-pack/plugins/observability_solution/slo/server/services/transform_generators/kql_custom.ts b/x-pack/plugins/observability_solution/slo/server/services/transform_generators/kql_custom.ts index fb52772b762cd2..573a83a5400de9 100644 --- a/x-pack/plugins/observability_solution/slo/server/services/transform_generators/kql_custom.ts +++ b/x-pack/plugins/observability_solution/slo/server/services/transform_generators/kql_custom.ts @@ -16,7 +16,7 @@ import { import { getSLOTransformTemplate } from '../../assets/transform_templates/slo_transform_template'; import { KQLCustomIndicator, SLODefinition } from '../../domain/models'; import { InvalidTransformError } from '../../errors'; -import { getTimesliceTargetComparator } from './common'; +import { getTimesliceTargetComparator, getFilterRange } from './common'; export class KQLCustomTransformGenerator extends TransformGenerator { public getTransformParams(slo: SLODefinition): TransformPutTransformRequest { @@ -47,13 +47,7 @@ export class KQLCustomTransformGenerator extends TransformGenerator { query: { bool: { filter: [ - { - range: { - [indicator.params.timestampField]: { - gte: `now-${slo.timeWindow.duration.format()}/d`, - }, - }, - }, + getFilterRange(slo, indicator.params.timestampField), getElasticsearchQueryOrThrow(indicator.params.filter), ], }, diff --git a/x-pack/plugins/observability_solution/slo/server/services/transform_generators/metric_custom.test.ts b/x-pack/plugins/observability_solution/slo/server/services/transform_generators/metric_custom.test.ts index 9ebacde28f0ed1..1f6a1638baf1e3 100644 --- a/x-pack/plugins/observability_solution/slo/server/services/transform_generators/metric_custom.test.ts +++ b/x-pack/plugins/observability_solution/slo/server/services/transform_generators/metric_custom.test.ts @@ -5,6 +5,7 @@ * 2.0. */ +import { twoMinute } from '../fixtures/duration'; import { createMetricCustomIndicator, createSLO, @@ -215,4 +216,28 @@ describe('Metric Custom Transform Generator', () => { expect(transform.pivot!.aggregations!['slo.denominator']).toMatchSnapshot(); }); + + it("overrides the range filter when 'preventInitialBackfill' is true", () => { + const slo = createSLO({ + indicator: createMetricCustomIndicator(), + settings: { + frequency: twoMinute(), + syncDelay: twoMinute(), + preventInitialBackfill: true, + }, + }); + + const transform = generator.getTransformParams(slo); + + // @ts-ignore + const rangeFilter = transform.source.query.bool.filter.find((f) => 'range' in f); + + expect(rangeFilter).toEqual({ + range: { + log_timestamp: { + gte: 'now-300s/m', // 2m + 2m + 60s + }, + }, + }); + }); }); diff --git a/x-pack/plugins/observability_solution/slo/server/services/transform_generators/metric_custom.ts b/x-pack/plugins/observability_solution/slo/server/services/transform_generators/metric_custom.ts index 9242e80b092701..d004b3e01d890a 100644 --- a/x-pack/plugins/observability_solution/slo/server/services/transform_generators/metric_custom.ts +++ b/x-pack/plugins/observability_solution/slo/server/services/transform_generators/metric_custom.ts @@ -17,7 +17,7 @@ import { getSLOTransformTemplate } from '../../assets/transform_templates/slo_tr import { MetricCustomIndicator, SLODefinition } from '../../domain/models'; import { InvalidTransformError } from '../../errors'; import { GetCustomMetricIndicatorAggregation } from '../aggregations'; -import { getTimesliceTargetComparator } from './common'; +import { getTimesliceTargetComparator, getFilterRange } from './common'; export const INVALID_EQUATION_REGEX = /[^A-Z|+|\-|\s|\d+|\.|\(|\)|\/|\*|>|<|=|\?|\:|&|\!|\|]+/g; @@ -50,13 +50,7 @@ export class MetricCustomTransformGenerator extends TransformGenerator { query: { bool: { filter: [ - { - range: { - [indicator.params.timestampField]: { - gte: `now-${slo.timeWindow.duration.format()}/d`, - }, - }, - }, + getFilterRange(slo, indicator.params.timestampField), getElasticsearchQueryOrThrow(indicator.params.filter), ], }, diff --git a/x-pack/plugins/observability_solution/slo/server/services/transform_generators/synthetics_availability.test.ts b/x-pack/plugins/observability_solution/slo/server/services/transform_generators/synthetics_availability.test.ts index 78d6da1eb5bcaf..2a3a6f188eaa9a 100644 --- a/x-pack/plugins/observability_solution/slo/server/services/transform_generators/synthetics_availability.test.ts +++ b/x-pack/plugins/observability_solution/slo/server/services/transform_generators/synthetics_availability.test.ts @@ -10,6 +10,7 @@ import { SLODefinition } from '../../domain/models'; import { createSLO, createSyntheticsAvailabilityIndicator } from '../fixtures/slo'; import { SyntheticsAvailabilityTransformGenerator } from './synthetics_availability'; import { SYNTHETICS_INDEX_PATTERN } from '../../../common/constants'; +import { twoMinute } from '../fixtures/duration'; const generator = new SyntheticsAvailabilityTransformGenerator(); @@ -404,4 +405,28 @@ describe('Synthetics Availability Transform Generator', () => { }, }); }); + + it("overrides the range filter when 'preventInitialBackfill' is true", () => { + const slo = createSLO({ + indicator: createSyntheticsAvailabilityIndicator(), + settings: { + frequency: twoMinute(), + syncDelay: twoMinute(), + preventInitialBackfill: true, + }, + }); + + const transform = generator.getTransformParams(slo, 'default'); + + // @ts-ignore + const rangeFilter = transform.source.query.bool.filter.find((f) => 'range' in f); + + expect(rangeFilter).toEqual({ + range: { + '@timestamp': { + gte: 'now-300s/m', // 2m + 2m + 60s + }, + }, + }); + }); }); diff --git a/x-pack/plugins/observability_solution/slo/server/services/transform_generators/synthetics_availability.ts b/x-pack/plugins/observability_solution/slo/server/services/transform_generators/synthetics_availability.ts index ca820f524d7ef3..a98f2a2b3e2320 100644 --- a/x-pack/plugins/observability_solution/slo/server/services/transform_generators/synthetics_availability.ts +++ b/x-pack/plugins/observability_solution/slo/server/services/transform_generators/synthetics_availability.ts @@ -24,6 +24,8 @@ import { import { getSLOTransformTemplate } from '../../assets/transform_templates/slo_transform_template'; import { InvalidTransformError } from '../../errors'; import { SLODefinition } from '../../domain/models'; +import { getFilterRange } from './common'; + export class SyntheticsAvailabilityTransformGenerator extends TransformGenerator { public getTransformParams(slo: SLODefinition, spaceId: string): TransformPutTransformRequest { if (!syntheticsAvailabilityIndicatorSchema.is(slo.indicator)) { @@ -108,13 +110,7 @@ export class SyntheticsAvailabilityTransformGenerator extends TransformGenerator const queryFilter: estypes.QueryDslQueryContainer[] = [ { term: { 'summary.final_attempt': true } }, { term: { 'meta.space_id': spaceId } }, - { - range: { - '@timestamp': { - gte: `now-${slo.timeWindow.duration.format()}/d`, - }, - }, - }, + getFilterRange(slo, '@timestamp'), ]; const { monitorIds, tags, projects } = buildParamValues({ monitorIds: indicator.params.monitorIds || [], diff --git a/x-pack/plugins/observability_solution/slo/server/services/transform_generators/timeslice_metric.test.ts b/x-pack/plugins/observability_solution/slo/server/services/transform_generators/timeslice_metric.test.ts index 7d221c25c27ede..1fcd8c6abe892b 100644 --- a/x-pack/plugins/observability_solution/slo/server/services/transform_generators/timeslice_metric.test.ts +++ b/x-pack/plugins/observability_solution/slo/server/services/transform_generators/timeslice_metric.test.ts @@ -5,6 +5,7 @@ * 2.0. */ +import { twoMinute } from '../fixtures/duration'; import { createTimesliceMetricIndicator, createSLOWithTimeslicesBudgetingMethod, @@ -164,4 +165,28 @@ describe('Timeslice Metric Transform Generator', () => { }, }); }); + + it("overrides the range filter when 'preventInitialBackfill' is true", () => { + const slo = createSLOWithTimeslicesBudgetingMethod({ + indicator: everythingIndicator, + settings: { + frequency: twoMinute(), + syncDelay: twoMinute(), + preventInitialBackfill: true, + }, + }); + + const transform = generator.getTransformParams(slo); + + // @ts-ignore + const rangeFilter = transform.source.query.bool.filter.find((f) => 'range' in f); + + expect(rangeFilter).toEqual({ + range: { + '@timestamp': { + gte: 'now-360s/m', // 2m + 2m + 2m slice window + }, + }, + }); + }); }); diff --git a/x-pack/plugins/observability_solution/slo/server/services/transform_generators/timeslice_metric.ts b/x-pack/plugins/observability_solution/slo/server/services/transform_generators/timeslice_metric.ts index 678dc2f4d76d81..719766b0c4efe2 100644 --- a/x-pack/plugins/observability_solution/slo/server/services/transform_generators/timeslice_metric.ts +++ b/x-pack/plugins/observability_solution/slo/server/services/transform_generators/timeslice_metric.ts @@ -12,7 +12,6 @@ import { timesliceMetricIndicatorSchema, timeslicesBudgetingMethodSchema, } from '@kbn/slo-schema'; - import { InvalidTransformError } from '../../errors'; import { getSLOTransformTemplate } from '../../assets/transform_templates/slo_transform_template'; import { getElasticsearchQueryOrThrow, parseIndex, TransformGenerator } from '.'; @@ -23,6 +22,7 @@ import { } from '../../../common/constants'; import { SLODefinition } from '../../domain/models'; import { GetTimesliceMetricIndicatorAggregation } from '../aggregations'; +import { getFilterRange } from './common'; const INVALID_EQUATION_REGEX = /[^A-Z|+|\-|\s|\d+|\.|\(|\)|\/|\*|>|<|=|\?|\:|&|\!|\|]+/g; @@ -55,13 +55,7 @@ export class TimesliceMetricTransformGenerator extends TransformGenerator { query: { bool: { filter: [ - { - range: { - [indicator.params.timestampField]: { - gte: `now-${slo.timeWindow.duration.format()}/d`, - }, - }, - }, + getFilterRange(slo, indicator.params.timestampField), getElasticsearchQueryOrThrow(indicator.params.filter), ], }, diff --git a/x-pack/plugins/observability_solution/slo/server/services/unsafe_federated/__snapshots__/remote_summary_doc_to_slo.test.ts.snap b/x-pack/plugins/observability_solution/slo/server/services/unsafe_federated/__snapshots__/remote_summary_doc_to_slo.test.ts.snap index 153c007adcfa11..4825ed65e4fc92 100644 --- a/x-pack/plugins/observability_solution/slo/server/services/unsafe_federated/__snapshots__/remote_summary_doc_to_slo.test.ts.snap +++ b/x-pack/plugins/observability_solution/slo/server/services/unsafe_federated/__snapshots__/remote_summary_doc_to_slo.test.ts.snap @@ -32,6 +32,7 @@ Object { "unit": "m", "value": 1, }, + "preventInitialBackfill": false, "syncDelay": Duration { "unit": "m", "value": 1, @@ -81,6 +82,7 @@ Object { "unit": "m", "value": 1, }, + "preventInitialBackfill": false, "syncDelay": Duration { "unit": "m", "value": 1, diff --git a/x-pack/plugins/observability_solution/slo/server/services/unsafe_federated/remote_summary_doc_to_slo.ts b/x-pack/plugins/observability_solution/slo/server/services/unsafe_federated/remote_summary_doc_to_slo.ts index d63194c8711391..0d98a021f505fc 100644 --- a/x-pack/plugins/observability_solution/slo/server/services/unsafe_federated/remote_summary_doc_to_slo.ts +++ b/x-pack/plugins/observability_solution/slo/server/services/unsafe_federated/remote_summary_doc_to_slo.ts @@ -29,7 +29,12 @@ export function fromRemoteSummaryDocumentToSloDefinition( timesliceTarget: summaryDoc.slo.objective.timesliceTarget ?? undefined, timesliceWindow: summaryDoc.slo.objective.timesliceWindow ?? undefined, }, - settings: { syncDelay: '1m', frequency: '1m' }, + settings: { + syncDelay: '1m', + frequency: '1m', + // added in 8.15.0 + preventInitialBackfill: false, + }, revision: summaryDoc.slo.revision, enabled: true, tags: summaryDoc.slo.tags, diff --git a/x-pack/plugins/observability_solution/slo/server/services/update_slo.test.ts b/x-pack/plugins/observability_solution/slo/server/services/update_slo.test.ts index e520b952128fd6..974f72561aeff1 100644 --- a/x-pack/plugins/observability_solution/slo/server/services/update_slo.test.ts +++ b/x-pack/plugins/observability_solution/slo/server/services/update_slo.test.ts @@ -192,7 +192,11 @@ describe('UpdateSLO', () => { const slo = createSLO(); mockRepository.findById.mockResolvedValueOnce(slo); - const newSettings = { ...slo.settings, timestamp_field: 'newField' }; + const newSettings = { + ...slo.settings, + frequency: fiveMinute(), + preventInitialBackfill: true, + }; await updateSLO.execute(slo.id, { settings: newSettings }); expectDeletionOfOriginalSLOResources(slo); diff --git a/x-pack/plugins/observability_solution/slo/server/services/update_slo.ts b/x-pack/plugins/observability_solution/slo/server/services/update_slo.ts index a55f4b2ce9fca1..5c8812cd4a8863 100644 --- a/x-pack/plugins/observability_solution/slo/server/services/update_slo.ts +++ b/x-pack/plugins/observability_solution/slo/server/services/update_slo.ts @@ -39,6 +39,7 @@ export class UpdateSLO { const originalSlo = await this.repository.findById(sloId); let updatedSlo: SLODefinition = Object.assign({}, originalSlo, params, { groupBy: !!params.groupBy ? params.groupBy : originalSlo.groupBy, + settings: mergePartialSettings(originalSlo.settings, params.settings), }); if (isEqual(originalSlo, updatedSlo)) { @@ -183,3 +184,13 @@ export class UpdateSLO { return updateSLOResponseSchema.encode(slo); } } + +/** + * Settings are merged by overwriting the original settings with the optional new partial settings. + */ +function mergePartialSettings( + originalSettings: SLODefinition['settings'], + newPartialSettings: UpdateSLOParams['settings'] +) { + return Object.assign({}, originalSettings, newPartialSettings); +} diff --git a/x-pack/plugins/observability_solution/uptime/server/legacy_uptime/lib/requests/get_snapshot_counts.ts b/x-pack/plugins/observability_solution/uptime/server/legacy_uptime/lib/requests/get_snapshot_counts.ts index b128c77fa59153..d3128caf2f8a87 100644 --- a/x-pack/plugins/observability_solution/uptime/server/legacy_uptime/lib/requests/get_snapshot_counts.ts +++ b/x-pack/plugins/observability_solution/uptime/server/legacy_uptime/lib/requests/get_snapshot_counts.ts @@ -48,12 +48,8 @@ export const getSnapshotCount: UMElasticsearchQueryFn => { - const { body: res } = await context.search( - { - body: statusCountBody(await context.dateAndCustomFilters(), context), - }, - 'getSnapshotCount' - ); + const body = statusCountBody(await context.dateAndCustomFilters(), context); + const { body: res } = await context.search(body, 'getSnapshotCount'); return ( (res.aggregations?.counts?.value as Snapshot) ?? { diff --git a/x-pack/plugins/observability_solution/uptime/server/legacy_uptime/lib/requests/search/find_potential_matches.ts b/x-pack/plugins/observability_solution/uptime/server/legacy_uptime/lib/requests/search/find_potential_matches.ts index 2649205d3b4733..7d988ed50c4355 100644 --- a/x-pack/plugins/observability_solution/uptime/server/legacy_uptime/lib/requests/search/find_potential_matches.ts +++ b/x-pack/plugins/observability_solution/uptime/server/legacy_uptime/lib/requests/search/find_potential_matches.ts @@ -41,11 +41,7 @@ export const findPotentialMatches = async ( const query = async (queryContext: QueryContext, searchAfter: any, size: number) => { const body = await queryBody(queryContext, searchAfter, size); - const params = { - body, - }; - - const response = await queryContext.search(params, 'getMonitorList-potentialMatches'); + const response = await queryContext.search(body, 'getMonitorList-potentialMatches'); return response; }; diff --git a/x-pack/plugins/observability_solution/uptime/server/legacy_uptime/lib/requests/search/query_context.ts b/x-pack/plugins/observability_solution/uptime/server/legacy_uptime/lib/requests/search/query_context.ts index 1ab9d9ece169ff..9f76d8e37b099c 100644 --- a/x-pack/plugins/observability_solution/uptime/server/legacy_uptime/lib/requests/search/query_context.ts +++ b/x-pack/plugins/observability_solution/uptime/server/legacy_uptime/lib/requests/search/query_context.ts @@ -7,7 +7,7 @@ import moment from 'moment'; import type { ESFilter } from '@kbn/es-types'; -import type { SearchRequest } from '@kbn/data-plugin/common'; +import type { SearchRequest } from '@elastic/elasticsearch/lib/api/types'; import type { CursorPagination } from './types'; import { CursorDirection, SortOrder } from '../../../../../common/runtime_types'; import { UptimeEsClient } from '../../lib'; diff --git a/x-pack/plugins/observability_solution/uptime/server/legacy_uptime/lib/requests/search/refine_potential_matches.ts b/x-pack/plugins/observability_solution/uptime/server/legacy_uptime/lib/requests/search/refine_potential_matches.ts index 7956966ec71c56..0b38979676e4ea 100644 --- a/x-pack/plugins/observability_solution/uptime/server/legacy_uptime/lib/requests/search/refine_potential_matches.ts +++ b/x-pack/plugins/observability_solution/uptime/server/legacy_uptime/lib/requests/search/refine_potential_matches.ts @@ -5,6 +5,7 @@ * 2.0. */ +import type { SearchRequest } from '@elastic/elasticsearch/lib/api/types'; import { QueryContext } from './query_context'; import { MonitorSummary, Ping } from '../../../../../common/runtime_types'; @@ -115,50 +116,48 @@ export const query = async ( queryContext: QueryContext, potentialMatchMonitorIDs: string[] ): Promise => { - const params = { - body: { - size: 0, - query: { - bool: { - filter: [ - await queryContext.dateRangeFilter(), - { terms: { 'monitor.id': potentialMatchMonitorIDs } }, - ], - }, + const params: SearchRequest = { + size: 0, + query: { + bool: { + filter: [ + await queryContext.dateRangeFilter(), + { terms: { 'monitor.id': potentialMatchMonitorIDs } }, + ], }, - aggs: { - monitor: { - terms: { - field: 'monitor.id', - size: potentialMatchMonitorIDs.length, - order: { _key: queryContext.cursorOrder() }, - }, - aggs: { - location: { - terms: { field: 'observer.geo.name', missing: 'N/A', size: 100 }, - aggs: { - summaries: { - // only match summary docs because we only want the latest *complete* check group. - filter: { exists: { field: 'summary' } }, - aggs: { - latest: { - top_hits: { - sort: [{ '@timestamp': 'desc' }], - size: 1, - }, + }, + aggs: { + monitor: { + terms: { + field: 'monitor.id', + size: potentialMatchMonitorIDs.length, + order: { _key: queryContext.cursorOrder() }, + }, + aggs: { + location: { + terms: { field: 'observer.geo.name', missing: 'N/A', size: 100 }, + aggs: { + summaries: { + // only match summary docs because we only want the latest *complete* check group. + filter: { exists: { field: 'summary' } }, + aggs: { + latest: { + top_hits: { + sort: [{ '@timestamp': 'desc' }], + size: 1, }, }, }, - // We want to find the latest check group, even if it's not part of a summary - latest_matching: { - filter: queryContext.filterClause || { match_all: {} }, - aggs: { - top: { - top_hits: { - _source: ['@timestamp'], - sort: [{ '@timestamp': 'desc' }], - size: 1, - }, + }, + // We want to find the latest check group, even if it's not part of a summary + latest_matching: { + filter: queryContext.filterClause || { match_all: {} }, + aggs: { + top: { + top_hits: { + _source: ['@timestamp'], + sort: [{ '@timestamp': 'desc' }], + size: 1, }, }, }, diff --git a/x-pack/plugins/osquery/server/search_strategy/osquery/factory/actions/results/query.action_results.dsl.ts b/x-pack/plugins/osquery/server/search_strategy/osquery/factory/actions/results/query.action_results.dsl.ts index 2ed9d406895a55..18a9c4fab8332f 100644 --- a/x-pack/plugins/osquery/server/search_strategy/osquery/factory/actions/results/query.action_results.dsl.ts +++ b/x-pack/plugins/osquery/server/search_strategy/osquery/factory/actions/results/query.action_results.dsl.ts @@ -47,11 +47,11 @@ export const buildActionResultsQuery = ({ let index: string; if (useNewDataStream) { - index = ACTION_RESPONSES_DATA_STREAM_INDEX; + index = `${ACTION_RESPONSES_DATA_STREAM_INDEX}*`; } else if (componentTemplateExists) { - index = ACTION_RESPONSES_INDEX; + index = `${ACTION_RESPONSES_INDEX}*`; } else { - index = AGENT_ACTIONS_RESULTS_INDEX; + index = `${AGENT_ACTIONS_RESULTS_INDEX}*`; } return { diff --git a/x-pack/plugins/osquery/server/search_strategy/osquery/index.ts b/x-pack/plugins/osquery/server/search_strategy/osquery/index.ts index 3cc3f8ad6b6c14..efc9dd6582ced8 100644 --- a/x-pack/plugins/osquery/server/search_strategy/osquery/index.ts +++ b/x-pack/plugins/osquery/server/search_strategy/osquery/index.ts @@ -40,6 +40,8 @@ export const osquerySearchStrategyProvider = ( }), newDataStreamIndexExists: esClient.asInternalUser.indices.exists({ index: `${ACTION_RESPONSES_DATA_STREAM_INDEX}*`, + allow_no_indices: false, + expand_wildcards: 'all', }), }).pipe( mergeMap(({ actionsIndexExists, newDataStreamIndexExists }) => { diff --git a/x-pack/plugins/rule_registry/tsconfig.json b/x-pack/plugins/rule_registry/tsconfig.json index 385c3fe82e0f8b..879d2eb9536cc6 100644 --- a/x-pack/plugins/rule_registry/tsconfig.json +++ b/x-pack/plugins/rule_registry/tsconfig.json @@ -31,11 +31,11 @@ "@kbn/logging", "@kbn/securitysolution-io-ts-utils", "@kbn/share-plugin", - "@kbn/alerting-state-types", "@kbn/alerts-as-data-utils", "@kbn/core-http-router-server-mocks", "@kbn/core-http-server", "@kbn/search-types", + "@kbn/alerting-state-types" ], "exclude": [ "target/**/*", diff --git a/x-pack/plugins/search_playground/__mocks__/fetch_query_source_fields.mock.ts b/x-pack/plugins/search_playground/__mocks__/fetch_query_source_fields.mock.ts index ecdb85dfaa4457..355494b4a217e2 100644 --- a/x-pack/plugins/search_playground/__mocks__/fetch_query_source_fields.mock.ts +++ b/x-pack/plugins/search_playground/__mocks__/fetch_query_source_fields.mock.ts @@ -5,72 +5,135 @@ * 2.0. */ +import { SearchResponse } from '@elastic/elasticsearch/lib/api/types'; + export const ELSER_PASSAGE_CHUNKED_TWO_INDICES_DOCS = [ { - _index: 'workplace_index', - _id: '248629d8-64d7-4e91-a4eb-dbd8282d9f24', - _score: 1, - _ignored: ['metadata.summary.keyword', 'text.keyword'], - _source: { - metadata: { - summary: 'This policy', - rolePermissions: ['demo', 'manager'], - name: 'Work From Home Policy', - }, - vector: { - tokens: {}, - model_id: '.elser_model_2', - }, - text: 'Effective: March 2020', - }, - }, + took: 0, + timed_out: false, + _shards: { + total: 1, + successful: 1, + skipped: 0, + failed: 0, + }, + hits: { + total: { + value: 1, + relation: 'eq', + }, + max_score: null, + hits: [], + }, + aggregations: { + 'vector.model_id': { + doc_count_error_upper_bound: 0, + sum_other_doc_count: 0, + buckets: [ + { + key: '.elser_model_2', + doc_count: 1, + }, + ], + }, + }, + } as SearchResponse, { - _index: 'workplace_index2', - _id: 'b047762c-24eb-4846-aeb5-808346d54c54', - _score: 1, - _ignored: ['content.keyword', 'metadata.summary.keyword'], - _source: { - metadata: { - summary: - 'This policy outlines the guidelines for full-time remote work, including eligibility, equipment and resources, workspace requirements, communication expectations, performance expectations, time tracking and overtime, confidentiality and data security, health and well-being, and policy reviews and updates. Employees are encouraged to direct any questions or concerns', - rolePermissions: ['demo', 'manager'], - name: 'Work From Home Policy', - }, - content: 'Effective', - content_vector: { - tokens: {}, - model_id: '.elser_model_2', - }, - }, - }, + took: 0, + timed_out: false, + _shards: { + total: 1, + successful: 1, + skipped: 0, + failed: 0, + }, + hits: { + total: { + value: 1, + relation: 'eq', + }, + max_score: null, + hits: [], + }, + aggregations: { + 'content_vector.model_id': { + doc_count_error_upper_bound: 0, + sum_other_doc_count: 0, + buckets: [ + { + key: '.elser_model_2', + doc_count: 1, + }, + ], + }, + }, + } as SearchResponse, ]; export const DENSE_INPUT_OUTPUT_ONE_INDEX = [ { - _index: 'index2', - _id: 'KQ6Wco8BO787m9kIp1Ug', - _score: 1, - _source: { - text_embedding: [0.03889123350381851], - text: 'hello there', - model_id: '.multilingual-e5-small', - }, - }, + took: 0, + timed_out: false, + _shards: { + total: 1, + successful: 1, + skipped: 0, + failed: 0, + }, + hits: { + total: { + value: 1, + relation: 'eq', + }, + max_score: null, + hits: [], + }, + aggregations: { + model_id: { + doc_count_error_upper_bound: 0, + sum_other_doc_count: 0, + buckets: [ + { + key: '.multilingual-e5-small', + doc_count: 1, + }, + ], + }, + }, + } as SearchResponse, ]; export const SPARSE_INPUT_OUTPUT_ONE_INDEX = [ { - _index: 'index', - _id: 'Iw6Bco8BO787m9kIa1Wo', - _score: 1, - _source: { - text_embedding: { - interview: 0.42307013, - }, - text: 'hello there', - model_id: '.elser_model_2', - }, - }, + took: 0, + timed_out: false, + _shards: { + total: 1, + successful: 1, + skipped: 0, + failed: 0, + }, + hits: { + total: { + value: 1, + relation: 'eq', + }, + max_score: null, + hits: [], + }, + aggregations: { + model_id: { + doc_count_error_upper_bound: 0, + sum_other_doc_count: 0, + buckets: [ + { + key: '.elser_model_2', + doc_count: 1, + }, + ], + }, + }, + } as SearchResponse, ]; export const SPARSE_INPUT_OUTPUT_ONE_INDEX_FIELD_CAPS = { @@ -111,6 +174,36 @@ export const SPARSE_INPUT_OUTPUT_ONE_INDEX_FIELD_CAPS = { }, }; +export const SPARSE_INPUT_OUTPUT_ONE_INDEX_FIELD_CAPS_MODEL_ID_KEYWORD = { + indices: ['index'], + fields: { + text_embedding: { + sparse_vector: { + type: 'sparse_vector', + metadata_field: false, + searchable: true, + aggregatable: false, + }, + }, + text: { + text: { + type: 'text', + metadata_field: false, + searchable: true, + aggregatable: false, + }, + }, + model_id: { + keyword: { + type: 'keyword', + metadata_field: false, + searchable: true, + aggregatable: true, + }, + }, + }, +}; + export const DENSE_INPUT_OUTPUT_ONE_INDEX_FIELD_CAPS = { indices: ['index2'], fields: { @@ -151,572 +244,23 @@ export const DENSE_INPUT_OUTPUT_ONE_INDEX_FIELD_CAPS = { export const DENSE_VECTOR_DOCUMENT_FIRST = [ { - _index: 'workplace_index_nested', - _id: 'MXEeQo4BweykRPD22e0N', - _score: 1, - _ignored: ['content.keyword', 'metadata.summary.keyword', 'metadata.content.keyword'], - _source: { - metadata: { - summary: - 'This policy outlines the guidelines for full-time remote work, including eligibility, equipment and resources, workspace requirements, communication expectations, performance expectations, time tracking and overtime, confidentiality and data security, health and well-being, and policy reviews and updates. Employees are encouraged to direct any questions or concerns', - _run_ml_inference: true, - updated_at: '2020-03-01', - created_on: '2020-03-01', - rolePermissions: ['demo', 'manager'], - name: 'Work From Home Policy', - category: 'teams', - content: `Effective: March 2020 -Purpose - -The purpose of this full-time work-from-home policy is to provide guidelines and support for employees to conduct their work remotely, ensuring the continuity and productivity of business operations during the COVID-19 pandemic and beyond. -Scope - -This policy applies to all employees who are eligible for remote work as determined by their role and responsibilities. It is designed to allow employees to work from home full time while maintaining the same level of performance and collaboration as they would in the office. -Eligibility - -Employees who can perform their work duties remotely and have received approval from their direct supervisor and the HR department are eligible for this work-from-home arrangement. -Equipment and Resources - -The necessary equipment and resources will be provided to employees for remote work, including a company-issued laptop, software licenses, and access to secure communication tools. Employees are responsible for maintaining and protecting the company's equipment and data. -Workspace - -Employees working from home are responsible for creating a comfortable and safe workspace that is conducive to productivity. This includes ensuring that their home office is ergonomically designed, well-lit, and free from distractions. -Communication - -Effective communication is vital for successful remote work. Employees are expected to maintain regular communication with their supervisors, colleagues, and team members through email, phone calls, video conferences, and other approved communication tools. -Work Hours and Availability - -Employees are expected to maintain their regular work hours and be available during normal business hours, unless otherwise agreed upon with their supervisor. Any changes to work hours or availability must be communicated to the employee's supervisor and the HR department. -Performance Expectations - -Employees working from home are expected to maintain the same level of performance and productivity as if they were working in the office. Supervisors and team members will collaborate to establish clear expectations and goals for remote work. -Time Tracking and Overtime - -Employees are required to accurately track their work hours using the company's time tracking system. Non-exempt employees must obtain approval from their supervisor before working overtime. -Confidentiality and Data Security - -Employees must adhere to the company's confidentiality and data security policies while working from home. This includes safeguarding sensitive information, securing personal devices and internet connections, and reporting any security breaches to the IT department. -Health and Well-being - -The company encourages employees to prioritize their health and well-being while working from home. This includes taking regular breaks, maintaining a work-life balance, and seeking support from supervisors and colleagues when needed. -Policy Review and Updates - -This work-from-home policy will be reviewed periodically and updated as necessary, taking into account changes in public health guidance, business needs, and employee feedback. -Questions and Concerns - -Employees are encouraged to direct any questions or concerns about this policy to their supervisor or the HR department. -`, - url: './sharepoint/Work from home policy.txt', - }, - passages: [ - { - vector: { - model_id: '.multilingual-e5-small', - }, - text: 'Effective: March 2020 Purpose', - }, - { - vector: { - model_id: '.multilingual-e5-small', - }, - text: 'The purpose of this full-time work-from-home policy is to provide guidelines and support for employees to conduct their work remotely, ensuring the continuity and productivity of business operations', - }, - { - vector: { - model_id: '.multilingual-e5-small', - }, - text: 'of this full-time work-from-home policy is to provide guidelines and support for employees to conduct their work remotely, ensuring the continuity and productivity of business operations during the', - }, - { - vector: { - model_id: '.multilingual-e5-small', - }, - text: 'full-time work-from-home policy is to provide guidelines and support for employees to conduct their work remotely, ensuring the continuity and productivity of business operations during the COVID-19', - }, - { - vector: { - model_id: '.multilingual-e5-small', - }, - text: 'work-from-home policy is to provide guidelines and support for employees to conduct their work remotely, ensuring the continuity and productivity of business operations during the COVID-19 pandemic', - }, - { - vector: { - model_id: '.multilingual-e5-small', - }, - text: 'policy is to provide guidelines and support for employees to conduct their work remotely, ensuring the continuity and productivity of business operations during the COVID-19 pandemic and beyond.', - }, - { - vector: { - model_id: '.multilingual-e5-small', - }, - text: 'Scope', - }, - { - vector: { - model_id: '.multilingual-e5-small', - }, - text: 'This policy applies to all employees who are eligible for remote work as determined by their role and responsibilities. It is designed to allow employees to work from home full time while maintaining', - }, - { - vector: { - model_id: '.multilingual-e5-small', - }, - text: 'policy applies to all employees who are eligible for remote work as determined by their role and responsibilities. It is designed to allow employees to work from home full time while maintaining the', - }, - { - vector: { - model_id: '.multilingual-e5-small', - }, - text: 'applies to all employees who are eligible for remote work as determined by their role and responsibilities. It is designed to allow employees to work from home full time while maintaining the same', - }, - { - vector: { - model_id: '.multilingual-e5-small', - }, - text: 'to all employees who are eligible for remote work as determined by their role and responsibilities. It is designed to allow employees to work from home full time while maintaining the same level of', - }, - { - vector: { - model_id: '.multilingual-e5-small', - }, - text: 'who are eligible for remote work as determined by their role and responsibilities. It is designed to allow employees to work from home full time while maintaining the same level of performance and', - }, - { - vector: { - model_id: '.multilingual-e5-small', - }, - text: 'for remote work as determined by their role and responsibilities. It is designed to allow employees to work from home full time while maintaining the same level of performance and collaboration as', - }, - { - vector: { - model_id: '.multilingual-e5-small', - }, - text: 'remote work as determined by their role and responsibilities. It is designed to allow employees to work from home full time while maintaining the same level of performance and collaboration as they', - }, - { - vector: { - model_id: '.multilingual-e5-small', - }, - text: 'work as determined by their role and responsibilities. It is designed to allow employees to work from home full time while maintaining the same level of performance and collaboration as they would in', - }, - { - vector: { - model_id: '.multilingual-e5-small', - }, - text: 'as determined by their role and responsibilities. It is designed to allow employees to work from home full time while maintaining the same level of performance and collaboration as they would in the', - }, - { - vector: { - model_id: '.multilingual-e5-small', - }, - text: 'by their role and responsibilities. It is designed to allow employees to work from home full time while maintaining the same level of performance and collaboration as they would in the office.', - }, - { - vector: { - model_id: '.multilingual-e5-small', - }, - text: 'Eligibility', - }, - { - vector: { - model_id: '.multilingual-e5-small', - }, - text: 'Employees who can perform their work duties remotely and have received approval from their direct supervisor and the HR department are eligible for this work-from-home arrangement.', - }, - { - vector: { - model_id: '.multilingual-e5-small', - }, - text: 'Equipment and Resources', - }, - { - vector: { - model_id: '.multilingual-e5-small', - }, - text: 'The necessary equipment and resources will be provided to employees for remote work, including a company-issued laptop, software licenses, and access to secure communication tools. Employees are', - }, - { - vector: { - model_id: '.multilingual-e5-small', - }, - text: 'equipment and resources will be provided to employees for remote work, including a company-issued laptop, software licenses, and access to secure communication tools. Employees are responsible for', - }, - { - vector: { - model_id: '.multilingual-e5-small', - }, - text: 'and resources will be provided to employees for remote work, including a company-issued laptop, software licenses, and access to secure communication tools. Employees are responsible for maintaining', - }, - { - vector: { - model_id: '.multilingual-e5-small', - }, - text: 'resources will be provided to employees for remote work, including a company-issued laptop, software licenses, and access to secure communication tools. Employees are responsible for maintaining and', - }, - { - vector: { - model_id: '.multilingual-e5-small', - }, - text: 'will be provided to employees for remote work, including a company-issued laptop, software licenses, and access to secure communication tools. Employees are responsible for maintaining and protecting', - }, - { - vector: { - model_id: '.multilingual-e5-small', - }, - text: 'be provided to employees for remote work, including a company-issued laptop, software licenses, and access to secure communication tools. Employees are responsible for maintaining and protecting the', - }, - { - vector: { - model_id: '.multilingual-e5-small', - }, - text: "to employees for remote work, including a company-issued laptop, software licenses, and access to secure communication tools. Employees are responsible for maintaining and protecting the company's", - }, - { - vector: { - model_id: '.multilingual-e5-small', - }, - text: "for remote work, including a company-issued laptop, software licenses, and access to secure communication tools. Employees are responsible for maintaining and protecting the company's equipment and", - }, - { - vector: { - model_id: '.multilingual-e5-small', - }, - text: "remote work, including a company-issued laptop, software licenses, and access to secure communication tools. Employees are responsible for maintaining and protecting the company's equipment and data.", - }, - { - vector: { - model_id: '.multilingual-e5-small', - }, - text: 'Workspace', - }, - { - vector: { - model_id: '.multilingual-e5-small', - }, - text: 'Employees working from home are responsible for creating a comfortable and safe workspace that is conducive to productivity. This includes ensuring that their home office is ergonomically designed,', - }, - { - vector: { - model_id: '.multilingual-e5-small', - }, - text: 'working from home are responsible for creating a comfortable and safe workspace that is conducive to productivity. This includes ensuring that their home office is ergonomically designed, well-lit,', - }, - { - vector: { - model_id: '.multilingual-e5-small', - }, - text: 'from home are responsible for creating a comfortable and safe workspace that is conducive to productivity. This includes ensuring that their home office is ergonomically designed, well-lit, and free', - }, - { - vector: { - model_id: '.multilingual-e5-small', - }, - text: 'home are responsible for creating a comfortable and safe workspace that is conducive to productivity. This includes ensuring that their home office is ergonomically designed, well-lit, and free from', - }, - { - vector: { - model_id: '.multilingual-e5-small', - }, - text: 'for creating a comfortable and safe workspace that is conducive to productivity. This includes ensuring that their home office is ergonomically designed, well-lit, and free from distractions.', - }, - { - vector: { - model_id: '.multilingual-e5-small', - }, - text: 'Communication', - }, - { - vector: { - model_id: '.multilingual-e5-small', - }, - text: 'Effective communication is vital for successful remote work. Employees are expected to maintain regular communication with their supervisors, colleagues, and team members through email, phone calls,', - }, - { - vector: { - model_id: '.multilingual-e5-small', - }, - text: 'communication is vital for successful remote work. Employees are expected to maintain regular communication with their supervisors, colleagues, and team members through email, phone calls, video', - }, - { - vector: { - model_id: '.multilingual-e5-small', - }, - text: 'is vital for successful remote work. Employees are expected to maintain regular communication with their supervisors, colleagues, and team members through email, phone calls, video conferences, and', - }, - { - vector: { - model_id: '.multilingual-e5-small', - }, - text: 'for successful remote work. Employees are expected to maintain regular communication with their supervisors, colleagues, and team members through email, phone calls, video conferences, and other', - }, - { - vector: { - model_id: '.multilingual-e5-small', - }, - text: 'successful remote work. Employees are expected to maintain regular communication with their supervisors, colleagues, and team members through email, phone calls, video conferences, and other approved', - }, - { - vector: { - model_id: '.multilingual-e5-small', - }, - text: 'work. Employees are expected to maintain regular communication with their supervisors, colleagues, and team members through email, phone calls, video conferences, and other approved communication', - }, - { - vector: { - model_id: '.multilingual-e5-small', - }, - text: 'Employees are expected to maintain regular communication with their supervisors, colleagues, and team members through email, phone calls, video conferences, and other approved communication tools.', - }, - { - vector: { - model_id: '.multilingual-e5-small', - }, - text: 'Work Hours and Availability', - }, - { - vector: { - model_id: '.multilingual-e5-small', - }, - text: 'Employees are expected to maintain their regular work hours and be available during normal business hours, unless otherwise agreed upon with their supervisor. Any changes to work hours or', - }, - { - vector: { - model_id: '.multilingual-e5-small', - }, - text: 'are expected to maintain their regular work hours and be available during normal business hours, unless otherwise agreed upon with their supervisor. Any changes to work hours or availability must be', - }, - { - vector: { - model_id: '.multilingual-e5-small', - }, - text: 'to maintain their regular work hours and be available during normal business hours, unless otherwise agreed upon with their supervisor. Any changes to work hours or availability must be communicated', - }, - { - vector: { - model_id: '.multilingual-e5-small', - }, - text: 'maintain their regular work hours and be available during normal business hours, unless otherwise agreed upon with their supervisor. Any changes to work hours or availability must be communicated to', - }, - { - vector: { - model_id: '.multilingual-e5-small', - }, - text: 'their regular work hours and be available during normal business hours, unless otherwise agreed upon with their supervisor. Any changes to work hours or availability must be communicated to the', - }, - { - vector: { - model_id: '.multilingual-e5-small', - }, - text: "regular work hours and be available during normal business hours, unless otherwise agreed upon with their supervisor. Any changes to work hours or availability must be communicated to the employee's", - }, - { - vector: { - model_id: '.multilingual-e5-small', - }, - text: "hours and be available during normal business hours, unless otherwise agreed upon with their supervisor. Any changes to work hours or availability must be communicated to the employee's supervisor", - }, - { - vector: { - model_id: '.multilingual-e5-small', - }, - text: "and be available during normal business hours, unless otherwise agreed upon with their supervisor. Any changes to work hours or availability must be communicated to the employee's supervisor and the", - }, - { - vector: { - model_id: '.multilingual-e5-small', - }, - text: "be available during normal business hours, unless otherwise agreed upon with their supervisor. Any changes to work hours or availability must be communicated to the employee's supervisor and the HR", - }, - { - vector: { - model_id: '.multilingual-e5-small', - }, - text: "during normal business hours, unless otherwise agreed upon with their supervisor. Any changes to work hours or availability must be communicated to the employee's supervisor and the HR department.", - }, - { - vector: { - model_id: '.multilingual-e5-small', - }, - text: 'Performance Expectations', - }, - { - vector: { - model_id: '.multilingual-e5-small', - }, - text: 'Employees working from home are expected to maintain the same level of performance and productivity as if they were working in the office. Supervisors and team members will collaborate to establish', - }, - { - vector: { - model_id: '.multilingual-e5-small', - }, - text: 'working from home are expected to maintain the same level of performance and productivity as if they were working in the office. Supervisors and team members will collaborate to establish clear', - }, - { - vector: { - model_id: '.multilingual-e5-small', - }, - text: 'from home are expected to maintain the same level of performance and productivity as if they were working in the office. Supervisors and team members will collaborate to establish clear expectations', - }, - { - vector: { - model_id: '.multilingual-e5-small', - }, - text: 'home are expected to maintain the same level of performance and productivity as if they were working in the office. Supervisors and team members will collaborate to establish clear expectations and', - }, - { - vector: { - model_id: '.multilingual-e5-small', - }, - text: 'are expected to maintain the same level of performance and productivity as if they were working in the office. Supervisors and team members will collaborate to establish clear expectations and goals', - }, - { - vector: { - model_id: '.multilingual-e5-small', - }, - text: 'expected to maintain the same level of performance and productivity as if they were working in the office. Supervisors and team members will collaborate to establish clear expectations and goals for', - }, - { - vector: { - model_id: '.multilingual-e5-small', - }, - text: 'to maintain the same level of performance and productivity as if they were working in the office. Supervisors and team members will collaborate to establish clear expectations and goals for remote', - }, - { - vector: { - model_id: '.multilingual-e5-small', - }, - text: 'maintain the same level of performance and productivity as if they were working in the office. Supervisors and team members will collaborate to establish clear expectations and goals for remote work.', - }, - { - vector: { - model_id: '.multilingual-e5-small', - }, - text: 'Time Tracking and Overtime', - }, - { - vector: { - model_id: '.multilingual-e5-small', - }, - text: "Employees are required to accurately track their work hours using the company's time tracking system. Non-exempt employees must obtain approval from their supervisor before working overtime.", - }, - { - vector: { - model_id: '.multilingual-e5-small', - }, - text: 'Confidentiality and Data Security', - }, - { - vector: { - model_id: '.multilingual-e5-small', - }, - text: "Employees must adhere to the company's confidentiality and data security policies while working from home. This includes safeguarding sensitive information, securing personal devices and internet", - }, - { - vector: { - model_id: '.multilingual-e5-small', - }, - text: "must adhere to the company's confidentiality and data security policies while working from home. This includes safeguarding sensitive information, securing personal devices and internet connections,", - }, - { - vector: { - model_id: '.multilingual-e5-small', - }, - text: "adhere to the company's confidentiality and data security policies while working from home. This includes safeguarding sensitive information, securing personal devices and internet connections, and", - }, - { - vector: { - model_id: '.multilingual-e5-small', - }, - text: "the company's confidentiality and data security policies while working from home. This includes safeguarding sensitive information, securing personal devices and internet connections, and reporting", - }, - { - vector: { - model_id: '.multilingual-e5-small', - }, - text: "company's confidentiality and data security policies while working from home. This includes safeguarding sensitive information, securing personal devices and internet connections, and reporting any", - }, - { - vector: { - model_id: '.multilingual-e5-small', - }, - text: 'confidentiality and data security policies while working from home. This includes safeguarding sensitive information, securing personal devices and internet connections, and reporting any security', - }, - { - vector: { - model_id: '.multilingual-e5-small', - }, - text: 'and data security policies while working from home. This includes safeguarding sensitive information, securing personal devices and internet connections, and reporting any security breaches to the IT', - }, - { - vector: { - model_id: '.multilingual-e5-small', - }, - text: 'policies while working from home. This includes safeguarding sensitive information, securing personal devices and internet connections, and reporting any security breaches to the IT department.', - }, - { - vector: { - model_id: '.multilingual-e5-small', - }, - text: 'Health and Well-being', - }, - { - vector: { - model_id: '.multilingual-e5-small', - }, - text: 'The company encourages employees to prioritize their health and well-being while working from home. This includes taking regular breaks, maintaining a work-life balance, and seeking support from', - }, - { - vector: { - model_id: '.multilingual-e5-small', - }, - text: 'encourages employees to prioritize their health and well-being while working from home. This includes taking regular breaks, maintaining a work-life balance, and seeking support from supervisors and', - }, - { - vector: { - model_id: '.multilingual-e5-small', - }, - text: 'employees to prioritize their health and well-being while working from home. This includes taking regular breaks, maintaining a work-life balance, and seeking support from supervisors and colleagues', - }, - { - vector: { - model_id: '.multilingual-e5-small', - }, - text: 'to prioritize their health and well-being while working from home. This includes taking regular breaks, maintaining a work-life balance, and seeking support from supervisors and colleagues when', - }, - { - vector: { - model_id: '.multilingual-e5-small', - }, - text: 'prioritize their health and well-being while working from home. This includes taking regular breaks, maintaining a work-life balance, and seeking support from supervisors and colleagues when needed.', - }, - { - vector: { - model_id: '.multilingual-e5-small', - }, - text: 'Policy Review and Updates', - }, - { - vector: { - model_id: '.multilingual-e5-small', - }, - text: 'This work-from-home policy will be reviewed periodically and updated as necessary, taking into account changes in public health guidance, business needs, and employee feedback.', - }, - { - vector: { - model_id: '.multilingual-e5-small', - }, - text: `This work-from-home policy will be reviewed periodically and updated as necessary, taking into account changes in public health guidance, business needs, and employee feedback. -Questions and Concerns`, - }, - { - vector: { - model_id: '.multilingual-e5-small', - }, - text: 'Employees are encouraged to direct any questions or concerns about this policy to their supervisor or the HR department.', - }, - ], - }, - }, + took: 0, + timed_out: false, + _shards: { + total: 1, + successful: 1, + skipped: 0, + failed: 0, + }, + hits: { + total: { + value: 1, + relation: 'eq', + }, + max_score: null, + hits: [], + }, + } as SearchResponse, ]; export const DENSE_VECTOR_DOCUMENT_FIRST_FIELD_CAPS = { @@ -1258,51 +802,35 @@ export const ELSER_PASSAGE_CHUNKED = { }; export const SPARSE_DOC_SINGLE_INDEX = { - _index: 'search-nethys', - _id: '662812de44a5e12074edcc6f', - _score: 1, - _ignored: ['body_content.enum'], - _source: { - last_crawled_at: '2024-04-23T20:05:14Z', - additional_urls: ['https://2e.aonprd.com/Ancestries.aspx?ID=31'], - body_content: - "Ancestry Guide pg. 89 2.0 Fleshwarps are people whose forms were created or radically transformed by magic, alchemy, or unnatural energies. Their unorthodox appearance can make it difficult for them to find a place for themselves in the world. Magic and science that can warp bone and twist sinew are all too common on Golarion. Fleshwarps are those who have been permanently altered by such methods—sometimes a sapient being created whole cloth from inanimate flesh, but often a victim unwillingly transformed by strange energies or sadistic creators. The ancestry name “fleshwarp” is an umbrella term, since on Golarion the actual fleshwarping process is more infamously well-known than are alterations caused by uncontrolled magic, technology, or fringe science. Whether practiced by Thassilonian wizards, Nexian fleshcrafters, or the drow of the Darklands, fleshwarping is the craft of reshaping flesh and mind in vats of foul magical reagents. This has led some scholars of monsters to argue that only those beings created by traditional fleshwarping should be considered fleshwarps. Regardless of the source of their altered forms, fleshwarps bear their new shape forever, transformed beings living a wild and strange existence beyond what was possible for their original ancestry. Although fleshwarps are humanoid, no two look the same. One might possess limbs in unusual places and skin as smooth as glass, while another might have a thick matting of spiny fur. Some might have animalistic features, like a boar snout, scales, or cloven hooves. Others have entirely alien appearances, such as bulging eyes on the backs of their hands. Some may have only subtly uncanny features that differentiate them, such as glowing teeth, smoking eyes, or fingernails made of bone. The only commonality among fleshwarps is their mismatched nature. Let your imagination run wild when creating a fleshwarp character! If you want a character who is tough and hardy, can change their form as they grow, and can use their wholly unique appearance to inspire awe or fear in others, you should play a fleshwarp. You Might... Embrace your unusual appearance to inspire respect or fear. Be used to relying on yourself. Distrust large groups of people, particularly mobs, based on past experiences. Others Probably... Find your physiology fascinating or terrifying. Assume you are an expert on strange creatures or occult phenomena. Consider you an enigmatic and unpredictable—and perhaps even dangerous—outsider. Physical Description Fleshwarps are humanoids, ranging from 5 to 7 feet tall and from just under 100 pounds to more than 300 pounds. The proportion and appearance of their limbs and features differ widely, but fleshwarps functionally have two legs, two arms, and a single head; a fleshwarp with more limbs than this should consider an appropriate ancestry feat to reflect this variance, or one of their limbs might be vestigial and mostly nonfunctional. Fleshwarps differ widely in their appearance due to the unique circumstances of their creation. Even fleshwarp siblings or two people transformed through the same procedure might look wildly different. Society Fleshwarps are so few in number that congregations of them are rare. They most often live on their own, with a small family group, or at the outskirts of a community. Some thrive in cities, however, where they can remain anonymous among the crowds while pursuing careers that allow them to avoid contact with people who might fear or persecute them. Fleshwarps value endurance and are quick to learn from others, so those who come into contact with others of their kind usually share stories that help each other survive, hide, or thrive more effectively. How a fleshwarp formed can be a painful or horrifying subject, one they consider rude to discuss with anyone besides close friends or loved ones. Alignment and Religion Fleshwarps have little to gain from the broader society, and therefore rarely work to support society in turn, beyond perhaps helping other fleshwarps. They need to be able to adapt quickly to survive on their own. As a consequence, few fleshwarps are lawful. Although bigoted or short-sighted people view fleshwarps as monsters, fleshwarps are no more or less prone to evil than any other people, and most seek only to live their lives without trouble. Most are neutral in alignment, for while alienation doesn't force a fleshwarp to feel contempt for others, neither does it encourage a fleshwarp to avoid it. This is especially true for fleshwarps living in the societies that gave birth to their traumatic transformation. Fleshwarps aren't often casually religious; most either have little to do with faith at all (viewing themselves as scorned by the gods or simply seeing faith as impractical for survival) or are exceptionally devout. Religious fleshwarps often revere Arazni , Calistria , Desna , or Gozreh ; evil fleshwarps typically turn to Lamashtu , finding consolation in the Mother of Monsters. Adventurers Fleshwarps often live on the margins of society. The hermit , hunter , nomad , or street urchin backgrounds work well for many fleshwarps; others might be criminals , entertainers , or prisoners . The need to defend themselves leads many fleshwarps to become barbarians , fighters , rogues , or rangers . champions and druids are common callings among fleshwarps who seek to defend and better the lot of others of their kind. Names Fleshwarps can come from—and thus have names from—any culture or ancestry, but some give themselves new names after being transformed, whether to celebrate the change, recognize a new phase of their lives, or conceal their past identity. Many fleshwarps also carry a descriptive nickname granted to them by others, such as “Triple Handed,” “Barkfoot,” or “Many-Mouth.” Fleshwarps don't keep nicknames they find personally offensive, but they tend to keep ones that describe their distinctive appearances or that are given by people they care about. Sample Names Borble, Dag, Feff, Hurn, Kemp, Omber, Ostro, Shurni, Surm, Wumpin Other Information Fleshforges In the city of Ecanus, the archmage Nex created the Fleshforges—massive edifices that churn and tremble with the birth sequences' roar of smelting flesh and printing bone—to produce fleshwarp soldiers to fuel his war against Geb. The Fleshforgers still engineer a bedazzling array of fleshwarps here, from chimeric messengers, who meld house pet and golem, to disciplined scale-sheathed cataphracts and stupendous dreadnoughts, whose fists and footsteps bend steel and pulverize stone. Recently, the Fleshforges have experienced uninitiated activations with alarming frequency, delivering atypical fleshwarps unconfined by design purviews or production schedules. Ecanus's authorities fervently hunt these unlicensed creations, but some escape, lurching into the night to hide in the city's recesses or venturing afield into the Mana Wastes. Fleshwarp Legends Legends of famous fleshwarps travel quickly amid fleshwarp communities. Lady Kedley : A wealthy noble in a life she doesn't remember, Kedley emerged transformed from deep below Westcrown. She uses her family's vast fortune to aid other fleshwarps. Spinhead Vanluk : This Mana Wastes warlord brings mutants and fleshwarps under his banner. He (literally) has eyes in the back of his head. Fleshwarp Motives Fleshwarps have a variety of reasons for taking up the life of an adventurer. Some are turned out of their homes by an uncaring parent or a suspicious mob. Other fleshwarps travel to learn more about their own transformation or to seek out others of their kind. A few even actively seek the means—either technological or magical—to undo their transformations or adopt a new form that won't incite repulsion or fear in common people. Still others understand that a good way to earn respect is to solve a community's problems—then quickly leave the area—and therefore fall into the role of itinerant adventurers-for-hire. Fleshwarp Settlements No settlements consisting entirely of fleshwarps exist openly outside of the Mana Wastes; elsewhere, citizens keep the secret so well that their community's existence isn't known to the world at large. Fleshwarps are more likely to live on the fringes of other settlements, working in industries where their hardy constitution is an advantage and their uncanny appearance isn't a liability. Some are herbalists or trappers, working in the wilderness and interacting with others only rarely. Locals might come to consider a fleshwarp their equal or friend, and take umbrage at outsiders who make a big deal out of the “monster” in their midst. Sentiment might turn quickly upon supernatural events or strange attacks, however, and, tragically, more than a few fleshwarps have been turned out of homes they've occupied for decades when faced with a misguided mob. The Mana Wastes The Mana Wastes are the vast swaths of desolation adjoining once-warring Geb and Nex. Over more than a thousand years, both nations' unrestricted use of magical fusillades, creeping plagues, and other atrocities of spellcraft transformed a once fertile and beautiful land into a desert where reality screams and bleeds. Wellspring surges of magic form into riptides and whirlwinds of chaotic force throughout the Mana Wastes, rending and twisting everything in their path. Many fleshwarps make their homes in this inhospitable land. Some are native to the region, mutated by the Mana Wastes' volatile outbursts, while others are exiles and refugees from across the Impossible Lands who find the otherworldly hazards of the Mana Wastes preferable to the all-too-worldly persecution of their former homelands and compatriots. Fleshwarp Mechanics Hit Points 10 Size Medium or Small Speed 25 feet Ability Boosts Constitution Free Languages Common Additional languages equal to your Intelligence modifier (if positive). Choose from Aklo , Draconic , Dwarven , Elven , Goblin , Undercommon , and any other languages to which you have access (such as the languages prevalent in your region). Low-Light Vision You can see in dim light as though it were bright light, and you ignore the concealed condition due to dim light. Unusual Anatomy Your unorthodox body resists physical afflictions meant for other creatures. You gain a +1 circumstance bonus to saves against diseases and poisons .", - domains: ['https://2e.aonprd.com'], - title: 'Fleshwarp - Ancestries - Archives of Nethys: Pathfinder 2nd Edition Database', - meta_keywords: - 'Archives, Nethys, Wiki, Archives of Nethys, Pathfinder, Official, AoN, AoNPRD, PRD, PFSRD, 2E, 2nd Edition, Ancestries, Ancestry, Fleshwarp', - url: 'https://2e.aonprd.com/Ancestries.aspx?ID=31', - url_scheme: 'https', - meta_description: - 'Magic and science that can warp bone and twist sinew are all too common on Golarion. Fleshwarps are those who have been permanently altered by such methods—sometimes a sapient being created whole cloth from inanimate flesh, but often a victim unwillingly transformed by strange energies or sadistic creators.

The ancestry name “fleshwarp” is an umbrella term, since on Golarion the actual fleshwarping process is more infamously well-known than are alterations caused by uncontrolled magic, technology, or fringe science. Whether practiced by Thassilonian wizards, Nexian fleshcrafters, or the drow of the Darklands, fleshwarping is the craft of reshaping flesh and mind in vats of foul magical reagents. This has led some scholars of monsters to argue that only those beings created by traditional fleshwarping should be considered fleshwarps. Regardless of the source of their altered forms, fleshwarps bear their new shape forever, transformed beings living a wild and strange existence beyond what w…', - _ingest: { - processors: [ + took: 0, + timed_out: false, + _shards: { + total: 1, + successful: 1, + skipped: 0, + failed: 0, + }, + hits: { + total: { + value: 1, + relation: 'eq', + }, + max_score: null, + hits: [], + }, + aggregations: { + 'ml.inference.body_content_expanded.model_id': { + doc_count_error_upper_bound: 0, + sum_other_doc_count: 0, + buckets: [ { - pipeline: 'ml.inference.nethys', - model_version: '12.0.0', - types: ['pytorch', 'text_expansion'], - processed_timestamp: '2024-04-23T20:05:15.723072191Z', + key: '.elser_model_2_linux-x86_64', + doc_count: 1, }, ], }, - headings: ['Alchemist'], - links: ['https://2e.aonprd.com/Causes.aspx'], - id: '662812de44a5e12074edcc6f', - url_port: 443, - url_host: '2e.aonprd.com', - url_path: '/Ancestries.aspx', - url_path_dir1: 'Ancestries.aspx', - ml: { - inference: { - body_content_expanded: { - predicted_value: {}, - is_truncated: true, - model_id: '.elser_model_2_linux-x86_64', - }, - }, - }, }, -}; +} as SearchResponse; export const DENSE_PASSAGE_FIRST_SINGLE_INDEX_FIELD_CAPS = { indices: ['search-example-main'], @@ -1663,37 +1191,32 @@ export const DENSE_PASSAGE_FIRST_SINGLE_INDEX_FIELD_CAPS = { }; export const DENSE_PASSAGE_FIRST_SINGLE_INDEX_DOC = { - _index: 'search-example-main', - _id: 'id', - _version: 1, - _seq_no: 2, - _primary_term: 1, - found: true, - _source: { - page_notification: '-', - 'main_button.button_new_tab': '-', - page_content_key: '', - label: '', - bread_crumbs: 'breadcrumbs', - title: 'title', - type: '11', - url: '/', - page_content_text: 'page_content_text', - page_id: '2,061', - 'buttons.button_title': '-', - page_content_e5_embbeding: { - predicted_value: [0.09232209622859955], - model_id: '.multilingual-e5-small_linux-x86_64', - }, - category_id: 'category_id', - filter_list: 'filter', - 'buttons.button_link': '-', - 'buttons.button_new_tab': '-', - 'main_button.button_title': '-', - title_text: 'title_text', - 'main_button.button_link': '-', - page_content: 'bla', - updated_date: '2024-03-21T11:23:12.503000', - title_keyword: 'title_keyword', + took: 0, + timed_out: false, + _shards: { + total: 1, + successful: 1, + skipped: 0, + failed: 0, }, -}; + hits: { + total: { + value: 1, + relation: 'eq', + }, + max_score: null, + hits: [], + }, + aggregations: { + 'page_content_e5_embbeding.model_id': { + doc_count_error_upper_bound: 0, + sum_other_doc_count: 0, + buckets: [ + { + key: '.multilingual-e5-small_linux-x86_64', + doc_count: 1, + }, + ], + }, + }, +} as SearchResponse; diff --git a/x-pack/plugins/search_playground/server/lib/fetch_query_source_fields.test.ts b/x-pack/plugins/search_playground/server/lib/fetch_query_source_fields.test.ts index 70b640f1ea70da..108ddabb0a73fd 100644 --- a/x-pack/plugins/search_playground/server/lib/fetch_query_source_fields.test.ts +++ b/x-pack/plugins/search_playground/server/lib/fetch_query_source_fields.test.ts @@ -18,8 +18,13 @@ import { DENSE_INPUT_OUTPUT_ONE_INDEX_FIELD_CAPS, SPARSE_INPUT_OUTPUT_ONE_INDEX, SPARSE_INPUT_OUTPUT_ONE_INDEX_FIELD_CAPS, + SPARSE_INPUT_OUTPUT_ONE_INDEX_FIELD_CAPS_MODEL_ID_KEYWORD, } from '../../__mocks__/fetch_query_source_fields.mock'; -import { parseFieldsCapabilities } from './fetch_query_source_fields'; +import { + fetchFields, + getModelIdFields, + parseFieldsCapabilities, +} from './fetch_query_source_fields'; describe('fetch_query_source_fields', () => { describe('parseFieldsCapabilities', () => { @@ -252,4 +257,92 @@ describe('fetch_query_source_fields', () => { }); }); }); + + describe('getModelIdFields', () => { + it('should return the model_id field for field specific - dense', () => { + expect(getModelIdFields(DENSE_PASSAGE_FIRST_SINGLE_INDEX_FIELD_CAPS)).toEqual([ + { + aggField: 'page_content_e5_embbeding.model_id.keyword', + path: 'page_content_e5_embbeding.model_id', + }, + { aggField: 'page_content_ner.model_id', path: 'page_content_ner.model_id' }, + ]); + }); + + it('should return the model_id field for field specific - elser', () => { + expect(getModelIdFields(DENSE_VECTOR_DOCUMENT_FIRST_FIELD_CAPS)).toEqual([ + { aggField: 'passages.vector.model_id.keyword', path: 'passages.vector.model_id' }, + ]); + }); + + it('should return top level model_id', () => { + expect(getModelIdFields(SPARSE_INPUT_OUTPUT_ONE_INDEX_FIELD_CAPS)).toEqual([ + { aggField: 'model_id.keyword', path: 'model_id' }, + ]); + }); + + it('should return the model_id as aggField if its a keyword field', () => { + expect(getModelIdFields(SPARSE_INPUT_OUTPUT_ONE_INDEX_FIELD_CAPS_MODEL_ID_KEYWORD)).toEqual([ + { aggField: 'model_id', path: 'model_id' }, + ]); + }); + }); + + describe('fetchFields', () => { + it('should perform a search request with the correct parameters', async () => { + const client = { + asCurrentUser: { + fieldCaps: jest.fn().mockResolvedValue(DENSE_PASSAGE_FIRST_SINGLE_INDEX_FIELD_CAPS), + search: jest.fn().mockResolvedValue(DENSE_PASSAGE_FIRST_SINGLE_INDEX_DOC), + }, + } as any; + const indices = ['search-example-main']; + await fetchFields(client, indices); + expect(client.asCurrentUser.search).toHaveBeenCalledWith({ + index: 'search-example-main', + body: { + size: 0, + aggs: { + 'page_content_e5_embbeding.model_id': { + terms: { + field: 'page_content_e5_embbeding.model_id.keyword', + size: 1, + }, + }, + 'page_content_ner.model_id': { + terms: { + field: 'page_content_ner.model_id', + size: 1, + }, + }, + }, + }, + }); + }); + + it('should perform a search request with the correct parameters with top level model id', async () => { + const client = { + asCurrentUser: { + fieldCaps: jest.fn().mockResolvedValue(SPARSE_INPUT_OUTPUT_ONE_INDEX_FIELD_CAPS), + search: jest.fn().mockResolvedValue(SPARSE_INPUT_OUTPUT_ONE_INDEX), + }, + } as any; + const indices = ['index']; + await fetchFields(client, indices); + expect(client.asCurrentUser.search).toHaveBeenCalledWith({ + index: 'index', + body: { + size: 0, + aggs: { + model_id: { + terms: { + field: 'model_id.keyword', + size: 1, + }, + }, + }, + }, + }); + }); + }); }); diff --git a/x-pack/plugins/search_playground/server/lib/fetch_query_source_fields.ts b/x-pack/plugins/search_playground/server/lib/fetch_query_source_fields.ts index 6bea3f8f03978b..b333a0e95962bb 100644 --- a/x-pack/plugins/search_playground/server/lib/fetch_query_source_fields.ts +++ b/x-pack/plugins/search_playground/server/lib/fetch_query_source_fields.ts @@ -5,11 +5,51 @@ * 2.0. */ -import { FieldCapsResponse, SearchHit } from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; +import { SearchResponse, FieldCapsResponse } from '@elastic/elasticsearch/lib/api/types'; import { IScopedClusterClient } from '@kbn/core-elasticsearch-server'; -import { get, has } from 'lodash'; import { IndicesQuerySourceFields } from '../types'; +interface FieldModelId { + field: string; + modelId: string | undefined; +} + +interface IndexFieldModel { + index: string; + fields: FieldModelId[]; +} + +export const getModelIdFields = (fieldCapsResponse: FieldCapsResponse) => { + const { fields } = fieldCapsResponse; + return Object.keys(fields).reduce>((acc, fieldKey) => { + const field = fields[fieldKey]; + if (fieldKey.endsWith('model_id')) { + if ('keyword' in field && field.keyword.aggregatable) { + acc.push({ + path: fieldKey, + aggField: fieldKey, + }); + return acc; + } + const keywordModelIdField = fields[fieldKey + '.keyword']; + + if ( + keywordModelIdField && + `keyword` in keywordModelIdField && + keywordModelIdField.keyword.aggregatable + ) { + acc.push({ + path: fieldKey, + aggField: fieldKey + '.keyword', + }); + return acc; + } + } + + return acc; + }, []); +}; + export const fetchFields = async ( client: IScopedClusterClient, indices: string[] @@ -21,56 +61,51 @@ export const fetchFields = async ( index: indices, }); - const indexDocs = []; + const modelIdFields = getModelIdFields(fieldCapabilities); - for (const index of indices) { - const x = await client.asCurrentUser.search({ + const indicesAggs = await Promise.all( + indices.map(async (index) => ({ index, - body: { - query: { - match_all: {}, - }, - size: 1, - }, - }); - - if (x.hits.total !== 0) { - indexDocs.push({ + doc: await client.asCurrentUser.search({ index, - doc: x.hits.hits[0], - }); - } - } + body: { + size: 0, + aggs: modelIdFields.reduce( + (sum, modelIdField) => ({ + ...sum, + [modelIdField.path]: { + terms: { + field: modelIdField.aggField, + size: 1, + }, + }, + }), + {} + ), + }, + }), + })) + ); - return parseFieldsCapabilities(fieldCapabilities, indexDocs); + return parseFieldsCapabilities(fieldCapabilities, indicesAggs); }; const INFERENCE_MODEL_FIELD_REGEXP = /\.predicted_value|\.tokens/; -const hasModelField = (field: string, indexDoc: any, nestedField: string | false) => { - if (field.match(INFERENCE_MODEL_FIELD_REGEXP)) { - const path = nestedField ? field.replace(`${nestedField}.`, `${nestedField}[0].`) : field; - return has( - indexDoc.doc, - `_source.${[path.replace(INFERENCE_MODEL_FIELD_REGEXP, '.model_id')]}` - ); - } - return false; -}; - -// For input_output inferred fields, the model_id is at the top level -const hasTopLevelModelField = (indexDoc: any) => { - return has(indexDoc.doc, `_source.model_id`); -}; +const getModelField = (field: string, modelIdFields: FieldModelId[]) => { + // For input_output inferred fields, the model_id is at the top level + const topLevelModelField = modelIdFields.find( + (modelIdField) => modelIdField.field === 'model_id' + )?.modelId; -const getModelField = (field: string, indexDoc: any, nestedField: string | false) => { - if (hasTopLevelModelField(indexDoc)) { - return get(indexDoc.doc, `_source.model_id`); + if (topLevelModelField) { + return topLevelModelField; } - // If the field is nested, we need to get the first occurrence as its an array - const path = nestedField ? field.replace(`${nestedField}.`, `${nestedField}[0].`) : field; - return get(indexDoc.doc, `_source.${[path.replace(INFERENCE_MODEL_FIELD_REGEXP, '.model_id')]}`); + return modelIdFields.find( + (modelIdField) => + modelIdField.field === field.replace(INFERENCE_MODEL_FIELD_REGEXP, '.model_id') + )?.modelId; }; const isFieldNested = (field: string, fieldCapsResponse: FieldCapsResponse) => { @@ -94,11 +129,25 @@ const isFieldNested = (field: string, fieldCapsResponse: FieldCapsResponse) => { export const parseFieldsCapabilities = ( fieldCapsResponse: FieldCapsResponse, - indexDocs: Array<{ index: string; doc: SearchHit }> + aggDocs: Array<{ index: string; doc: SearchResponse }> ): IndicesQuerySourceFields => { const { fields, indices: indexOrIndices } = fieldCapsResponse; const indices = Array.isArray(indexOrIndices) ? indexOrIndices : [indexOrIndices]; + const indexModelIdFields = aggDocs.map((aggDoc) => { + const modelIdFields = Object.keys(aggDoc.doc.aggregations || {}).map((field) => { + return { + field, + modelId: (aggDoc.doc.aggregations![field] as any)?.buckets?.[0]?.key, + }; + }); + + return { + index: aggDoc.index, + fields: modelIdFields, + }; + }); + const indicesFieldsMap = indices.reduce((acc, index) => { acc[index] = { elser_query_fields: [], @@ -125,20 +174,21 @@ export const parseFieldsCapabilities = ( : (indices as unknown as string[]); for (const index of indicesPresentIn) { - const indexDoc = indexDocs.find((x) => x.index === index); + const modelIdFields = indexModelIdFields.find( + (indexModelIdField) => indexModelIdField.index === index + )!.fields; + if ('rank_features' in field || 'sparse_vector' in field) { const nestedField = isFieldNested(fieldKey, fieldCapsResponse); + const modelId = getModelField(fieldKey, modelIdFields); // Check if the sparse vector field has a model_id associated with it // skip this field if has no model associated with it // and the vectors were embedded outside of stack - if ( - (hasModelField(fieldKey, indexDoc, nestedField) || hasTopLevelModelField(indexDoc)) && - !nestedField - ) { + if (modelId && !nestedField) { const elserModelField = { field: fieldKey, - model_id: getModelField(fieldKey, indexDoc, nestedField), + model_id: modelId, nested: !!isFieldNested(fieldKey, fieldCapsResponse), indices: indicesPresentIn, }; @@ -148,17 +198,15 @@ export const parseFieldsCapabilities = ( } } else if ('dense_vector' in field) { const nestedField = isFieldNested(fieldKey, fieldCapsResponse); + const modelId = getModelField(fieldKey, modelIdFields); // Check if the dense vector field has a model_id associated with it // skip this field if has no model associated with it // and the vectors were embedded outside of stack - if ( - (hasModelField(fieldKey, indexDoc, nestedField) || hasTopLevelModelField(indexDoc)) && - !nestedField - ) { + if (modelId && !nestedField) { const denseVectorField = { field: fieldKey, - model_id: getModelField(fieldKey, indexDoc, nestedField), + model_id: modelId, nested: !!nestedField, indices: indicesPresentIn, }; diff --git a/x-pack/plugins/security_solution/common/api/detection_engine/rule_monitoring/index.ts b/x-pack/plugins/security_solution/common/api/detection_engine/rule_monitoring/index.ts index ddb132ebf64bbb..216b32b0136277 100644 --- a/x-pack/plugins/security_solution/common/api/detection_engine/rule_monitoring/index.ts +++ b/x-pack/plugins/security_solution/common/api/detection_engine/rule_monitoring/index.ts @@ -20,5 +20,6 @@ export * from './model/execution_result.gen'; export * from './model/execution_settings'; export * from './model/execution_status.gen'; export * from './model/execution_status'; +export * from './model/execution_run_type.gen'; export * from './model/execution_summary.gen'; export * from './model/log_level'; diff --git a/x-pack/plugins/security_solution/common/api/detection_engine/rule_monitoring/model/execution_result.gen.ts b/x-pack/plugins/security_solution/common/api/detection_engine/rule_monitoring/model/execution_result.gen.ts index a1706fe0f41416..8a4c49d049d56c 100644 --- a/x-pack/plugins/security_solution/common/api/detection_engine/rule_monitoring/model/execution_result.gen.ts +++ b/x-pack/plugins/security_solution/common/api/detection_engine/rule_monitoring/model/execution_result.gen.ts @@ -41,6 +41,15 @@ export const RuleExecutionResult = z.object({ gap_duration_s: z.number().int(), security_status: z.string(), security_message: z.string(), + /** + * Backfill information for the rule execution result with source event date range + */ + backfill: z + .object({ + from: z.string().datetime(), + to: z.string().datetime(), + }) + .optional(), }); /** diff --git a/x-pack/plugins/security_solution/common/api/detection_engine/rule_monitoring/model/execution_result.schema.yaml b/x-pack/plugins/security_solution/common/api/detection_engine/rule_monitoring/model/execution_result.schema.yaml index 3c8d91d3b9d7f9..f8cf67bd423eaf 100644 --- a/x-pack/plugins/security_solution/common/api/detection_engine/rule_monitoring/model/execution_result.schema.yaml +++ b/x-pack/plugins/security_solution/common/api/detection_engine/rule_monitoring/model/execution_result.schema.yaml @@ -52,6 +52,19 @@ components: type: string security_message: type: string + backfill: + type: object + description: Backfill information for the rule execution result with source event date range + properties: + from: + type: string + format: date-time + to: + type: string + format: date-time + required: + - from + - to required: - execution_uuid - timestamp diff --git a/x-pack/plugins/security_solution/common/api/detection_engine/rule_monitoring/model/execution_run_type.gen.ts b/x-pack/plugins/security_solution/common/api/detection_engine/rule_monitoring/model/execution_run_type.gen.ts new file mode 100644 index 00000000000000..425d5c9e922afb --- /dev/null +++ b/x-pack/plugins/security_solution/common/api/detection_engine/rule_monitoring/model/execution_run_type.gen.ts @@ -0,0 +1,25 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { z } from 'zod'; + +/* + * NOTICE: Do not edit this file manually. + * This file is automatically generated by the OpenAPI Generator, @kbn/openapi-generator. + * + * info: + * title: Execution Run Type Schema + * version: not applicable + */ + +/** + * Type of rule execution run. + */ +export type RuleRunType = z.infer; +export const RuleRunType = z.enum(['backfill', 'standard']); +export type RuleRunTypeEnum = typeof RuleRunType.enum; +export const RuleRunTypeEnum = RuleRunType.enum; diff --git a/x-pack/plugins/security_solution/common/api/detection_engine/rule_monitoring/model/execution_run_type.schema.yaml b/x-pack/plugins/security_solution/common/api/detection_engine/rule_monitoring/model/execution_run_type.schema.yaml new file mode 100644 index 00000000000000..689e8393bcbe4a --- /dev/null +++ b/x-pack/plugins/security_solution/common/api/detection_engine/rule_monitoring/model/execution_run_type.schema.yaml @@ -0,0 +1,14 @@ +openapi: 3.0.0 +info: + title: Execution Run Type Schema + version: not applicable +paths: {} +components: + x-codegen-enabled: true + schemas: + RuleRunType: + type: string + description: Type of rule execution run. + enum: + - backfill + - standard \ No newline at end of file diff --git a/x-pack/plugins/security_solution/common/api/detection_engine/rule_monitoring/rule_execution_logs/get_rule_execution_results/get_rule_execution_results_route.gen.ts b/x-pack/plugins/security_solution/common/api/detection_engine/rule_monitoring/rule_execution_logs/get_rule_execution_results/get_rule_execution_results_route.gen.ts index 50cb863588a066..0c65eb39b88f4a 100644 --- a/x-pack/plugins/security_solution/common/api/detection_engine/rule_monitoring/rule_execution_logs/get_rule_execution_results/get_rule_execution_results_route.gen.ts +++ b/x-pack/plugins/security_solution/common/api/detection_engine/rule_monitoring/rule_execution_logs/get_rule_execution_results/get_rule_execution_results_route.gen.ts @@ -18,6 +18,7 @@ import { ArrayFromString } from '@kbn/zod-helpers'; */ import { RuleExecutionStatus } from '../../model/execution_status.gen'; +import { RuleRunType } from '../../model/execution_run_type.gen'; import { SortFieldOfRuleExecutionResult, RuleExecutionResult, @@ -44,6 +45,10 @@ export const GetRuleExecutionResultsRequestQuery = z.object({ * Comma-separated list of rule execution statuses to filter results by */ status_filters: ArrayFromString(RuleExecutionStatus).optional().default([]), + /** + * Comma-separated list of rule run types to filter results by + */ + run_type_filters: ArrayFromString(RuleRunType).optional().default([]), /** * Field to sort results by */ diff --git a/x-pack/plugins/security_solution/common/api/detection_engine/rule_monitoring/rule_execution_logs/get_rule_execution_results/get_rule_execution_results_route.schema.yaml b/x-pack/plugins/security_solution/common/api/detection_engine/rule_monitoring/rule_execution_logs/get_rule_execution_results/get_rule_execution_results_route.schema.yaml index 8bba4b2811e31c..42f8d54a2e6166 100644 --- a/x-pack/plugins/security_solution/common/api/detection_engine/rule_monitoring/rule_execution_logs/get_rule_execution_results/get_rule_execution_results_route.schema.yaml +++ b/x-pack/plugins/security_solution/common/api/detection_engine/rule_monitoring/rule_execution_logs/get_rule_execution_results/get_rule_execution_results_route.schema.yaml @@ -48,6 +48,15 @@ paths: items: $ref: '../../model/execution_status.schema.yaml#/components/schemas/RuleExecutionStatus' default: [] + - name: run_type_filters + in: query + required: false + description: Comma-separated list of rule run types to filter results by + schema: + type: array + items: + $ref: '../../model/execution_run_type.schema.yaml#/components/schemas/RuleRunType' + default: [] - name: sort_field in: query required: false diff --git a/x-pack/plugins/security_solution/common/api/detection_engine/rule_monitoring/rule_execution_logs/get_rule_execution_results/get_rule_execution_results_route.test.ts b/x-pack/plugins/security_solution/common/api/detection_engine/rule_monitoring/rule_execution_logs/get_rule_execution_results/get_rule_execution_results_route.test.ts index 742290de8bf43e..4a3a7e74e9d356 100644 --- a/x-pack/plugins/security_solution/common/api/detection_engine/rule_monitoring/rule_execution_logs/get_rule_execution_results/get_rule_execution_results_route.test.ts +++ b/x-pack/plugins/security_solution/common/api/detection_engine/rule_monitoring/rule_execution_logs/get_rule_execution_results/get_rule_execution_results_route.test.ts @@ -255,6 +255,7 @@ describe('Request schema of Get rule execution results', () => { sort_order: 'desc', start: '2021-08-01T00:00:00.000Z', status_filters: [], + run_type_filters: [], }); }); }); diff --git a/x-pack/plugins/security_solution/common/constants.ts b/x-pack/plugins/security_solution/common/constants.ts index 5be5462a805f91..c477a069c9c2ee 100644 --- a/x-pack/plugins/security_solution/common/constants.ts +++ b/x-pack/plugins/security_solution/common/constants.ts @@ -434,6 +434,9 @@ export const NEW_FEATURES_TOUR_STORAGE_KEYS = { export const RULE_DETAILS_EXECUTION_LOG_TABLE_SHOW_METRIC_COLUMNS_STORAGE_KEY = 'securitySolution.ruleDetails.ruleExecutionLog.showMetrics.v8.2'; +export const RULE_DETAILS_EXECUTION_LOG_TABLE_SHOW_SOURCE_EVENT_TIME_RANGE_STORAGE_KEY = + 'securitySolution.ruleDetails.ruleExecutionLog.showSourceEventTimeRange.v8.15'; + // TODO: https://github.com/elastic/kibana/pull/142950 /** * Error codes that can be thrown during _bulk_action API dry_run call and be processed and displayed to end user diff --git a/x-pack/plugins/security_solution/common/detection_engine/rule_management/execution_log.ts b/x-pack/plugins/security_solution/common/detection_engine/rule_management/execution_log.ts new file mode 100644 index 00000000000000..9a2205b0b42c2f --- /dev/null +++ b/x-pack/plugins/security_solution/common/detection_engine/rule_management/execution_log.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. + */ + +import type { RuleExecutionStatus, RuleRunType } from '../../api/detection_engine/rule_monitoring'; +import { + RuleExecutionStatusEnum, + RuleRunTypeEnum, +} from '../../api/detection_engine/rule_monitoring'; + +export const RUN_TYPE_FILTERS: RuleRunType[] = [RuleRunTypeEnum.standard, RuleRunTypeEnum.backfill]; + +export const STATUS_FILTERS: RuleExecutionStatus[] = [ + RuleExecutionStatusEnum.succeeded, + RuleExecutionStatusEnum.failed, + RuleExecutionStatusEnum['partial failure'], +]; diff --git a/x-pack/plugins/security_solution/common/experimental_features.ts b/x-pack/plugins/security_solution/common/experimental_features.ts index 01f03d4ac09ea4..c3b6a09470a8a6 100644 --- a/x-pack/plugins/security_solution/common/experimental_features.ts +++ b/x-pack/plugins/security_solution/common/experimental_features.ts @@ -286,6 +286,11 @@ export const allowedExperimentalValues = Object.freeze({ * Enables the new rule's bulk action to manage custom highlighted fields */ bulkCustomHighlightedFieldsEnabled: false, + + /** + * Enables the manual rule run + */ + manualRuleRunEnabled: false, }); type ExperimentalConfigKeys = Array; diff --git a/x-pack/plugins/security_solution/public/common/components/control_columns/row_action/index.test.tsx b/x-pack/plugins/security_solution/public/common/components/control_columns/row_action/index.test.tsx index 0dd60a0f9c2f8f..a6500a2ba5d10c 100644 --- a/x-pack/plugins/security_solution/public/common/components/control_columns/row_action/index.test.tsx +++ b/x-pack/plugins/security_solution/public/common/components/control_columns/row_action/index.test.tsx @@ -32,6 +32,7 @@ jest.mock('../../../utils/route/use_route_spy'); jest.mock('@kbn/expandable-flyout', () => { return { useExpandableFlyoutApi: () => ({ openFlyout: mockOpenFlyout }), + useExpandableFlyoutState: () => ({ left: false }), }; }); @@ -55,6 +56,7 @@ jest.mock('@kbn/kibana-react-plugin/public', () => { ...original, }; }); +jest.mock('../../guided_onboarding_tour/tour_step'); const mockRouteSpy: RouteSpyState = { pageName: SecurityPageName.overview, diff --git a/x-pack/plugins/security_solution/public/common/components/control_columns/row_action/index.tsx b/x-pack/plugins/security_solution/public/common/components/control_columns/row_action/index.tsx index bc9969d2a7e42f..181b707e88d2d1 100644 --- a/x-pack/plugins/security_solution/public/common/components/control_columns/row_action/index.tsx +++ b/x-pack/plugins/security_solution/public/common/components/control_columns/row_action/index.tsx @@ -30,6 +30,8 @@ import type { TimelineItem, TimelineNonEcsData } from '../../../../../common/sea import type { ColumnHeaderOptions, OnRowSelected } from '../../../../../common/types/timeline'; import { TimelineId } from '../../../../../common/types'; import { useIsExperimentalFeatureEnabled } from '../../../hooks/use_experimental_features'; +import { useTourContext } from '../../guided_onboarding_tour'; +import { AlertsCasesTourSteps, SecurityStepId } from '../../guided_onboarding_tour/tour_config'; type Props = EuiDataGridCellValueElementProps & { columnHeaders: ColumnHeaderOptions[]; @@ -83,6 +85,11 @@ const RowActionComponent = ({ const isExpandableFlyoutInCreateRuleEnabled = useIsExperimentalFeatureEnabled( 'expandableFlyoutInCreateRuleEnabled' ); + const { activeStep, isTourShown } = useTourContext(); + const shouldFocusOnOverviewTab = + (activeStep === AlertsCasesTourSteps.expandEvent || + activeStep === AlertsCasesTourSteps.reviewAlertDetailsFlyout) && + isTourShown(SecurityStepId.alertsCases); const columnValues = useMemo( () => @@ -123,6 +130,7 @@ const RowActionComponent = ({ openFlyout({ right: { id: DocumentDetailsRightPanelKey, + path: shouldFocusOnOverviewTab ? { tab: 'overview' } : undefined, params: { id: eventId, indexName, @@ -156,7 +164,17 @@ const RowActionComponent = ({ }) ); } - }, [dispatch, eventId, indexName, openFlyout, tabType, tableId, showExpandableFlyout, telemetry]); + }, [ + eventId, + indexName, + showExpandableFlyout, + tableId, + openFlyout, + shouldFocusOnOverviewTab, + telemetry, + dispatch, + tabType, + ]); const Action = controlColumn.rowCellRender; diff --git a/x-pack/plugins/security_solution/public/common/components/event_details/event_details.test.tsx b/x-pack/plugins/security_solution/public/common/components/event_details/event_details.test.tsx index f03e204301d87f..d7e4a92fe5a2a0 100644 --- a/x-pack/plugins/security_solution/public/common/components/event_details/event_details.test.tsx +++ b/x-pack/plugins/security_solution/public/common/components/event_details/event_details.test.tsx @@ -57,6 +57,12 @@ jest.mock('../../../detection_engine/rule_management/logic/use_rule_with_fallbac }; }); +jest.mock('../guided_onboarding_tour/tour_step', () => ({ + GuidedOnboardingTourStep: jest.fn(({ children }) => ( +
{children}
+ )), +})); + jest.mock('../link_to'); describe('EventDetails', () => { const defaultProps = { @@ -168,6 +174,10 @@ describe('EventDetails', () => { EVENT_DETAILS_CONTEXT_ID ); }); + + test('renders GuidedOnboardingTourStep', () => { + expect(alertsWrapper.find('[data-test-subj="guided-onboarding"]').exists()).toEqual(true); + }); }); describe('threat intel tab', () => { diff --git a/x-pack/plugins/security_solution/public/common/components/event_details/event_details.tsx b/x-pack/plugins/security_solution/public/common/components/event_details/event_details.tsx index 6c1dd9bce910a0..70d0c29eeda4b0 100644 --- a/x-pack/plugins/security_solution/public/common/components/event_details/event_details.tsx +++ b/x-pack/plugins/security_solution/public/common/components/event_details/event_details.tsx @@ -231,104 +231,111 @@ const EventDetailsComponent: React.FC = ({ name: i18n.OVERVIEW, 'data-test-subj': 'overviewTab', content: ( - <> - - - - {threatDetails && threatDetails[0] && ( - - <> - -
{threatDetails[0].title}
-
- - {threatDetails[0].description} - - -
- )} - - {renderer != null && detailsEcsData != null && ( -
- -
{i18n.ALERT_REASON}
-
- - - {renderer.renderRow({ - contextId: EVENT_DETAILS_CONTEXT_ID, - data: detailsEcsData, - isDraggable: isDraggable ?? false, - scopeId, - })} - -
- )} - - - - - - {showThreatSummary && ( - + <> + + + + {threatDetails && threatDetails[0] && ( + + <> + +
{threatDetails[0].title}
+
+ + {threatDetails[0].description} + + +
+ )} + + {renderer != null && detailsEcsData != null && ( +
+ +
{i18n.ALERT_REASON}
+
+ + + {renderer.renderRow({ + contextId: EVENT_DETAILS_CONTEXT_ID, + data: detailsEcsData, + isDraggable: isDraggable ?? false, + scopeId, + })} + +
+ )} + + + + - )} - {isEnrichmentsLoading && ( - <> - - - )} + {showThreatSummary && ( + + )} + + {isEnrichmentsLoading && ( + <> + + + )} - {basicAlertData.ruleId && maybeRule?.note && ( - - )} - + {basicAlertData.ruleId && maybeRule?.note && ( + + )} + + ), } : undefined, [ isAlert, + isTourAnchor, browserFields, scopeId, data, @@ -340,7 +347,7 @@ const EventDetailsComponent: React.FC = ({ detailsEcsData, isDraggable, goToTableTab, - maybeRule?.investigation_fields, + maybeRule?.investigation_fields?.field_names, maybeRule?.note, showThreatSummary, hostRisk, @@ -469,23 +476,17 @@ const EventDetailsComponent: React.FC = ({ ); return ( - - <> - - - - + <> + + + ); }; EventDetailsComponent.displayName = 'EventDetailsComponent'; diff --git a/x-pack/plugins/security_solution/public/common/components/event_details/insights/related_cases.test.tsx b/x-pack/plugins/security_solution/public/common/components/event_details/insights/related_cases.test.tsx index 52a6d5eb1eb421..54c30fa38a5887 100644 --- a/x-pack/plugins/security_solution/public/common/components/event_details/insights/related_cases.test.tsx +++ b/x-pack/plugins/security_solution/public/common/components/event_details/insights/related_cases.test.tsx @@ -7,7 +7,7 @@ import { act, render, screen } from '@testing-library/react'; import React from 'react'; - +import { casesPluginMock } from '@kbn/cases-plugin/public/mocks'; import { TestProviders } from '../../../mock'; import { useKibana as mockUseKibana } from '../../../lib/kibana/__mocks__'; import { RelatedCases } from './related_cases'; @@ -17,13 +17,17 @@ import { useTourContext } from '../../guided_onboarding_tour'; import { AlertsCasesTourSteps } from '../../guided_onboarding_tour/tour_config'; const mockedUseKibana = mockUseKibana(); -const mockGetRelatedCases = jest.fn(); -const mockCanUseCases = jest.fn(); -jest.mock('../../guided_onboarding_tour'); +const mockCasesContract = casesPluginMock.createStartContract(); +const mockGetRelatedCases = mockCasesContract.api.getRelatedCases as jest.Mock; +mockGetRelatedCases.mockReturnValue([{ id: '789', title: 'Test Case' }]); +const mockCanUseCases = mockCasesContract.helpers.canUseCases as jest.Mock; +mockCanUseCases.mockReturnValue(readCasesPermissions()); + +const mockUseTourContext = useTourContext as jest.Mock; + jest.mock('../../../lib/kibana', () => { const original = jest.requireActual('../../../lib/kibana'); - return { ...original, useToasts: jest.fn().mockReturnValue({ addWarning: jest.fn() }), @@ -31,68 +35,63 @@ jest.mock('../../../lib/kibana', () => { ...mockedUseKibana, services: { ...mockedUseKibana.services, - cases: { - api: { - getRelatedCases: mockGetRelatedCases, - }, - helpers: { canUseCases: mockCanUseCases }, - }, + cases: mockCasesContract, }, }), }; }); +jest.mock('../../guided_onboarding_tour'); +const defaultUseTourContextValue = { + activeStep: AlertsCasesTourSteps.viewCase, + incrementStep: () => null, + endTourStep: () => null, + isTourShown: () => false, +}; + +jest.mock('../../guided_onboarding_tour/tour_step'); + const eventId = '1c84d9bff4884dabe6aa1bb15f08433463b848d9269e587078dc56669550d27a'; const scrollToMock = jest.fn(); window.HTMLElement.prototype.scrollIntoView = scrollToMock; describe('Related Cases', () => { beforeEach(() => { - mockCanUseCases.mockReturnValue(readCasesPermissions()); - (useTourContext as jest.Mock).mockReturnValue({ - activeStep: AlertsCasesTourSteps.viewCase, - incrementStep: () => null, - endTourStep: () => null, - isTourShown: () => false, - }); + mockUseTourContext.mockReturnValue(defaultUseTourContextValue); jest.clearAllMocks(); }); + describe('When user does not have cases read permissions', () => { beforeEach(() => { mockCanUseCases.mockReturnValue(noCasesPermissions()); }); + test('should not show related cases when user does not have permissions', async () => { await act(async () => { - render( - - - - ); + render(, { wrapper: TestProviders }); }); expect(screen.queryByText('cases')).toBeNull(); }); }); + describe('When user does have case read permissions', () => { + beforeEach(() => { + mockCanUseCases.mockReturnValue(readCasesPermissions()); + }); + test('Should show the loading message', async () => { + mockGetRelatedCases.mockReturnValueOnce([]); await act(async () => { - mockGetRelatedCases.mockReturnValue([]); - render( - - - - ); - expect(screen.getByText(CASES_LOADING)).toBeInTheDocument(); + render(, { wrapper: TestProviders }); + expect(screen.queryByText(CASES_LOADING)).toBeInTheDocument(); }); + expect(screen.queryByText(CASES_LOADING)).not.toBeInTheDocument(); }); test('Should show 0 related cases when there are none', async () => { + mockGetRelatedCases.mockReturnValueOnce([]); await act(async () => { - mockGetRelatedCases.mockReturnValue([]); - render( - - - - ); + render(, { wrapper: TestProviders }); }); expect(screen.getByText(CASES_COUNT(0))).toBeInTheDocument(); @@ -100,28 +99,19 @@ describe('Related Cases', () => { test('Should show 1 related case', async () => { await act(async () => { - mockGetRelatedCases.mockReturnValue([{ id: '789', title: 'Test Case' }]); - render( - - - - ); + render(, { wrapper: TestProviders }); }); expect(screen.getByText(CASES_COUNT(1))).toBeInTheDocument(); expect(screen.getByTestId('case-details-link')).toHaveTextContent('Test Case'); }); test('Should show 2 related cases', async () => { + mockGetRelatedCases.mockReturnValueOnce([ + { id: '789', title: 'Test Case 1' }, + { id: '456', title: 'Test Case 2' }, + ]); await act(async () => { - mockGetRelatedCases.mockReturnValue([ - { id: '789', title: 'Test Case 1' }, - { id: '456', title: 'Test Case 2' }, - ]); - render( - - - - ); + render(, { wrapper: TestProviders }); }); expect(screen.getByText(CASES_COUNT(2))).toBeInTheDocument(); const cases = screen.getAllByTestId('case-details-link'); @@ -131,13 +121,8 @@ describe('Related Cases', () => { }); test('Should not open the related cases accordion when isTourActive=false', async () => { - mockGetRelatedCases.mockReturnValue([{ id: '789', title: 'Test Case' }]); await act(async () => { - render( - - - - ); + render(, { wrapper: TestProviders }); }); expect(scrollToMock).not.toHaveBeenCalled(); expect( @@ -146,19 +131,13 @@ describe('Related Cases', () => { }); test('Should automatically open the related cases accordion when isTourActive=true', async () => { - (useTourContext as jest.Mock).mockReturnValue({ - activeStep: AlertsCasesTourSteps.viewCase, - incrementStep: () => null, - endTourStep: () => null, + // this hook is called twice, so we can not use mockReturnValueOnce + mockUseTourContext.mockReturnValue({ + ...defaultUseTourContextValue, isTourShown: () => true, }); - mockGetRelatedCases.mockReturnValue([{ id: '789', title: 'Test Case' }]); await act(async () => { - render( - - - - ); + render(, { wrapper: TestProviders }); }); expect(scrollToMock).toHaveBeenCalled(); expect( diff --git a/x-pack/plugins/ml/public/embeddables/anomaly_charts/embeddable_anomaly_charts_container_lazy.tsx b/x-pack/plugins/security_solution/public/common/components/guided_onboarding_tour/__mocks__/tour_step.tsx similarity index 67% rename from x-pack/plugins/ml/public/embeddables/anomaly_charts/embeddable_anomaly_charts_container_lazy.tsx rename to x-pack/plugins/security_solution/public/common/components/guided_onboarding_tour/__mocks__/tour_step.tsx index 38f48ea4a018bf..fe1ce6c6b1c1da 100644 --- a/x-pack/plugins/ml/public/embeddables/anomaly_charts/embeddable_anomaly_charts_container_lazy.tsx +++ b/x-pack/plugins/security_solution/public/common/components/guided_onboarding_tour/__mocks__/tour_step.tsx @@ -7,6 +7,6 @@ import React from 'react'; -export const EmbeddableAnomalyChartsContainer = React.lazy( - () => import('./embeddable_anomaly_charts_container') -); +export const GuidedOnboardingTourStep = jest.fn(({ children }) => ( +
{children}
+)); diff --git a/x-pack/plugins/security_solution/public/common/components/guided_onboarding_tour/tour.tsx b/x-pack/plugins/security_solution/public/common/components/guided_onboarding_tour/tour.tsx index 2cf0153a1a3fed..a4fca35acf56b4 100644 --- a/x-pack/plugins/security_solution/public/common/components/guided_onboarding_tour/tour.tsx +++ b/x-pack/plugins/security_solution/public/common/components/guided_onboarding_tour/tour.tsx @@ -23,6 +23,8 @@ export interface TourContextValue { incrementStep: (tourId: SecurityStepId) => void; isTourShown: (tourId: SecurityStepId) => boolean; setStep: (tourId: SecurityStepId, step: AlertsCasesTourSteps) => void; + hidden: boolean; + setAllTourStepsHidden: (h: boolean) => void; } const initialState: TourContextValue = { @@ -31,12 +33,19 @@ const initialState: TourContextValue = { incrementStep: () => {}, isTourShown: () => false, setStep: () => {}, + hidden: false, + setAllTourStepsHidden: () => {}, }; const TourContext = createContext(initialState); export const RealTourContextProvider = ({ children }: { children: ReactChild }) => { const { guidedOnboarding } = useKibana().services; + const [hidden, setHidden] = useState(false); + + const setAllTourStepsHidden = useCallback((h: boolean) => { + setHidden(h); + }, []); const isRulesTourActive = useObservable( guidedOnboarding?.guidedOnboardingApi @@ -61,13 +70,16 @@ export const RealTourContextProvider = ({ children }: { children: ReactChild }) const tourStatus = useMemo( () => ({ - [SecurityStepId.rules]: isRulesTourActive, - [SecurityStepId.alertsCases]: isAlertsCasesTourActive, + [SecurityStepId.rules]: { active: isRulesTourActive, hidden: false }, + [SecurityStepId.alertsCases]: { active: isAlertsCasesTourActive, hidden: false }, }), [isRulesTourActive, isAlertsCasesTourActive] ); - const isTourShown = useCallback((tourId: SecurityStepId) => tourStatus[tourId], [tourStatus]); + const isTourShown = useCallback( + (tourId: SecurityStepId) => tourStatus[tourId].active, + [tourStatus] + ); const [activeStep, _setActiveStep] = useState(1); const incrementStep = useCallback((tourId: SecurityStepId) => { @@ -106,13 +118,15 @@ export const RealTourContextProvider = ({ children }: { children: ReactChild }) const context = useMemo(() => { return { + hidden, + setAllTourStepsHidden, activeStep, endTourStep, incrementStep, isTourShown, setStep, }; - }, [activeStep, endTourStep, incrementStep, isTourShown, setStep]); + }, [activeStep, endTourStep, hidden, incrementStep, isTourShown, setAllTourStepsHidden, setStep]); return {children}; }; diff --git a/x-pack/plugins/security_solution/public/common/components/guided_onboarding_tour/tour_config.ts b/x-pack/plugins/security_solution/public/common/components/guided_onboarding_tour/tour_config.ts index ca52149bfc3292..dd04b76d061a85 100644 --- a/x-pack/plugins/security_solution/public/common/components/guided_onboarding_tour/tour_config.ts +++ b/x-pack/plugins/security_solution/public/common/components/guided_onboarding_tour/tour_config.ts @@ -59,6 +59,23 @@ const defaultConfig = { export const getTourAnchor = (step: number, tourId: SecurityStepId) => `tourStepAnchor-${tourId}-${step}`; +export const hiddenWhenLeftExpandableFlyoutExpanded: Record = { + [SecurityStepId.alertsCases]: [ + AlertsCasesTourSteps.pointToAlertName, + AlertsCasesTourSteps.expandEvent, + ], +}; + +export const hiddenWhenCaseFlyoutExpanded: Record = { + [SecurityStepId.alertsCases]: [ + AlertsCasesTourSteps.pointToAlertName, + AlertsCasesTourSteps.expandEvent, + AlertsCasesTourSteps.reviewAlertDetailsFlyout, + AlertsCasesTourSteps.addAlertToCase, + AlertsCasesTourSteps.viewCase, + ], +}; + const alertsCasesConfig: StepConfig[] = [ { ...defaultConfig, diff --git a/x-pack/plugins/security_solution/public/common/components/guided_onboarding_tour/tour_step.test.tsx b/x-pack/plugins/security_solution/public/common/components/guided_onboarding_tour/tour_step.test.tsx index cf693392f83c45..0490bf882edd0b 100644 --- a/x-pack/plugins/security_solution/public/common/components/guided_onboarding_tour/tour_step.test.tsx +++ b/x-pack/plugins/security_solution/public/common/components/guided_onboarding_tour/tour_step.test.tsx @@ -12,15 +12,38 @@ import { AlertsCasesTourSteps, SecurityStepId } from './tour_config'; import { useTourContext } from './tour'; import { mockGlobalState, TestProviders, createMockStore } from '../../mock'; import { TimelineId } from '../../../../common/types'; +import { casesPluginMock } from '@kbn/cases-plugin/public/mocks'; +import { useKibana as mockUseKibana } from '../../lib/kibana/__mocks__'; +import { useHiddenByFlyout } from './use_hidden_by_flyout'; + +const mockedUseKibana = mockUseKibana(); +const mockCasesContract = casesPluginMock.createStartContract(); +const mockUseIsAddToCaseOpen = mockCasesContract.hooks.useIsAddToCaseOpen as jest.Mock; +mockUseIsAddToCaseOpen.mockReturnValue(false); +const mockUseTourContext = useTourContext as jest.Mock; + +jest.mock('../../lib/kibana', () => { + const original = jest.requireActual('../../lib/kibana'); + return { + ...original, + useToasts: jest.fn().mockReturnValue({ addWarning: jest.fn() }), + useKibana: () => ({ + ...mockedUseKibana, + services: { + ...mockedUseKibana.services, + cases: mockCasesContract, + }, + }), + }; +}); jest.mock('./tour'); -const mockTourStep = jest - .fn() - .mockImplementation(({ children, footerAction }: EuiTourStepProps) => ( - - {children} {footerAction} - - )); + +const useHiddenByFlyoutMock = useHiddenByFlyout as jest.Mock; +jest.mock('./use_hidden_by_flyout', () => ({ + useHiddenByFlyout: jest.fn(), +})); + jest.mock('@elastic/eui', () => { const original = jest.requireActual('@elastic/eui'); return { @@ -29,23 +52,37 @@ jest.mock('@elastic/eui', () => { EuiTourStep: (props: any) => mockTourStep(props), }; }); + +const mockTourStep = jest + .fn() + .mockImplementation(({ children, footerAction }: EuiTourStepProps) => ( + + {children} {footerAction} + + )); + const defaultProps = { isTourAnchor: true, - step: 1, + step: AlertsCasesTourSteps.pointToAlertName, tourId: SecurityStepId.alertsCases, }; const mockChildren =

{'random child element'}

; +const incrementStep = jest.fn(); + +const defaultUseTourContextValue = { + activeStep: AlertsCasesTourSteps.pointToAlertName, + incrementStep, + endTourStep: jest.fn(), + isTourShown: jest.fn(() => true), + hidden: false, +}; describe('GuidedOnboardingTourStep', () => { - const incrementStep = jest.fn(); beforeEach(() => { - (useTourContext as jest.Mock).mockReturnValue({ - activeStep: 1, - incrementStep, - isTourShown: () => true, - }); jest.clearAllMocks(); + mockUseTourContext.mockReturnValue(defaultUseTourContextValue); + useHiddenByFlyoutMock.mockReturnValue(false); }); it('renders as a tour step', () => { const { getByTestId } = render( @@ -57,6 +94,17 @@ describe('GuidedOnboardingTourStep', () => { expect(tourStep).toBeInTheDocument(); expect(header).toBeInTheDocument(); }); + it('useHiddenByFlyout equals to true, just render children', () => { + useHiddenByFlyoutMock.mockReturnValue(true); + const { getByTestId, queryByTestId } = render( + {mockChildren}, + { wrapper: TestProviders } + ); + const tourStep = queryByTestId('tourStepMock'); + const header = getByTestId('h1'); + expect(tourStep).not.toBeInTheDocument(); + expect(header).toBeInTheDocument(); + }); it('isTourAnchor={false}, just render children', () => { const { getByTestId, queryByTestId } = render( @@ -100,19 +148,14 @@ describe('GuidedOnboardingTourStep', () => { describe('SecurityTourStep', () => { const { isTourAnchor: _, ...stepDefaultProps } = defaultProps; beforeEach(() => { - (useTourContext as jest.Mock).mockReturnValue({ - activeStep: 1, - incrementStep: jest.fn(), - isTourShown: () => true, - }); + (useTourContext as jest.Mock).mockReturnValue(defaultUseTourContextValue); jest.clearAllMocks(); }); it('does not render if tour step does not exist', () => { - (useTourContext as jest.Mock).mockReturnValue({ + mockUseTourContext.mockReturnValue({ + ...defaultUseTourContextValue, activeStep: 99, - incrementStep: jest.fn(), - isTourShown: () => true, }); render( @@ -134,10 +177,10 @@ describe('SecurityTourStep', () => { }); it('does not render if security tour step is not shown', () => { - (useTourContext as jest.Mock).mockReturnValue({ + mockUseTourContext.mockReturnValue({ + ...defaultUseTourContextValue, activeStep: 1, - incrementStep: jest.fn(), - isTourShown: () => false, + isTourShown: jest.fn(() => false), }); render({mockChildren}, { wrapper: TestProviders, @@ -166,10 +209,9 @@ describe('SecurityTourStep', () => { }); it('renders next button', () => { - (useTourContext as jest.Mock).mockReturnValue({ + mockUseTourContext.mockReturnValue({ + ...defaultUseTourContextValue, activeStep: 3, - incrementStep: jest.fn(), - isTourShown: () => true, }); const { getByTestId } = render( @@ -182,9 +224,8 @@ describe('SecurityTourStep', () => { it('if a step has an anchor declared, the tour step should be a sibling of the mockChildren', () => { (useTourContext as jest.Mock).mockReturnValue({ + ...defaultUseTourContextValue, activeStep: 3, - incrementStep: jest.fn(), - isTourShown: () => true, }); const { container } = render( @@ -203,10 +244,9 @@ describe('SecurityTourStep', () => { }); it('if a step does not an anchor declared, the tour step should be the parent of the mockChildren', () => { - (useTourContext as jest.Mock).mockReturnValue({ + mockUseTourContext.mockReturnValue({ + ...defaultUseTourContextValue, activeStep: 2, - incrementStep: jest.fn(), - isTourShown: () => true, }); const { container } = render( @@ -265,9 +305,8 @@ describe('SecurityTourStep', () => { it('does not render next button if step hideNextButton=true ', () => { (useTourContext as jest.Mock).mockReturnValue({ + ...defaultUseTourContextValue, activeStep: 6, - incrementStep: jest.fn(), - isTourShown: () => true, }); const { queryByTestId } = render( diff --git a/x-pack/plugins/security_solution/public/common/components/guided_onboarding_tour/tour_step.tsx b/x-pack/plugins/security_solution/public/common/components/guided_onboarding_tour/tour_step.tsx index 156604160be747..4f3d06eac53d5c 100644 --- a/x-pack/plugins/security_solution/public/common/components/guided_onboarding_tour/tour_step.tsx +++ b/x-pack/plugins/security_solution/public/common/components/guided_onboarding_tour/tour_step.tsx @@ -18,6 +18,7 @@ import { timelineDefaults } from '../../../timelines/store/defaults'; import { timelineSelectors } from '../../../timelines/store'; import { useTourContext } from './tour'; import { AlertsCasesTourSteps, SecurityStepId, securityTourConfig } from './tour_config'; +import { useHiddenByFlyout } from './use_hidden_by_flyout'; interface SecurityTourStep { children?: React.ReactElement; @@ -37,104 +38,113 @@ const StyledTourStep = styled(EuiTourStep) { - const { activeStep, incrementStep, isTourShown } = useTourContext(); - const tourStep = useMemo( - () => securityTourConfig[tourId].find((config) => config.step === step), - [step, tourId] - ); - - const getTimeline = useMemo(() => timelineSelectors.getTimelineByIdSelector(), []); - const showTimeline = useShallowEqualSelector( - (state) => (getTimeline(state, TimelineId.active) ?? timelineDefaults).show - ); - - const onClickNext = useCallback( - // onClick should call incrementStep itself - () => (onClick ? onClick() : incrementStep(tourId)), - [incrementStep, onClick, tourId] - ); - - // EUI bug, will remove once bug resolve. will link issue here as soon as i have it - const onKeyDown = useCallback((e) => { - if (e.key === 'Enter') { - e.stopPropagation(); +export const SecurityTourStep = React.memo( + ({ children, onClick, step, tourId }: SecurityTourStep) => { + const { activeStep, incrementStep, isTourShown } = useTourContext(); + const tourStep = useMemo( + () => securityTourConfig[tourId].find((config) => config.step === step), + [step, tourId] + ); + + const getTimeline = useMemo(() => timelineSelectors.getTimelineByIdSelector(), []); + const showTimeline = useShallowEqualSelector( + (state) => (getTimeline(state, TimelineId.active) ?? timelineDefaults).show + ); + const onClickNext = useCallback( + // onClick should call incrementStep itself + () => (onClick ? onClick() : incrementStep(tourId)), + [incrementStep, onClick, tourId] + ); + + // EUI bug, will remove once bug resolve. will link issue here as soon as i have it + const onKeyDown = useCallback((e) => { + if (e.key === 'Enter') { + e.stopPropagation(); + } + }, []); + + // steps in Cases app are out of context. + // If we mount this step, we know we need to render it + // we are also managing the context on the siem end in the background + const overrideContext = isStepExternallyMounted(tourId, step); + + if ( + tourStep == null || + ((step !== activeStep || !isTourShown(tourId)) && !overrideContext) || + showTimeline + ) { + return children ? children : null; } - }, []); - - // steps in Cases app are out of context. - // If we mount this step, we know we need to render it - // we are also managing the context on the siem end in the background - const overrideContext = isStepExternallyMounted(tourId, step); - - if ( - tourStep == null || - ((step !== activeStep || !isTourShown(tourId)) && !overrideContext) || - showTimeline - ) { - return children ? children : null; - } - const { anchor, content, imageConfig, dataTestSubj, hideNextButton = false, ...rest } = tourStep; - const footerAction: EuiTourStepProps['footerAction'] = !hideNextButton ? ( - - - - ) : ( - <> - {/* Passing empty element instead of undefined. If undefined "Skip tour" button is shown, we do not want that*/} - - ); - - const commonProps = { - ...rest, - content: ( + const { + anchor, + content, + imageConfig, + dataTestSubj, + hideNextButton = false, + ...rest + } = tourStep; + const footerAction: EuiTourStepProps['footerAction'] = !hideNextButton ? ( + + + + ) : ( <> - -

{content}

-
- {imageConfig && ( - <> - - - - )} + {/* Passing empty element instead of undefined. If undefined "Skip tour" button is shown, we do not want that*/} - ), - footerAction, - // we would not have mounted this component if it was not open - isStepOpen: true, - // guided onboarding does not allow skipping tour through the steps - onFinish: () => null, - stepsTotal: securityTourConfig[tourId].length, - panelProps: { - 'data-test-subj': dataTestSubj, - }, - }; - - // tour step either needs children or an anchor element - // see type EuiTourStepAnchorProps - return anchor != null ? ( - <> - - <>{children} - - ) : children != null ? ( - - {children} - - ) : null; -}; + ); + + const commonProps = { + ...rest, + content: ( + <> + +

{content}

+
+ {imageConfig && ( + <> + + + + )} + + ), + footerAction, + // we would not have mounted this component if it was not open + isStepOpen: true, + // guided onboarding does not allow skipping tour through the steps + onFinish: () => null, + stepsTotal: securityTourConfig[tourId].length, + panelProps: { + 'data-test-subj': dataTestSubj, + }, + }; + + // tour step either needs children or an anchor element + // see type EuiTourStepAnchorProps + return anchor != null ? ( + <> + + <>{children} + + ) : children != null ? ( + + {children} + + ) : null; + } +); +SecurityTourStep.displayName = 'SecurityTourStep'; interface GuidedOnboardingTourStep extends SecurityTourStep { // can be false if the anchor is an iterative element @@ -145,11 +155,30 @@ interface GuidedOnboardingTourStep extends SecurityTourStep { // wraps tour anchor component // and gives the tour step itself a place to mount once it is active // mounts the tour step with a delay to ensure the anchor will render first -export const GuidedOnboardingTourStep = ({ - children, - // can be false if the anchor is an iterative element - // do not use this as an "is tour active" check, the SecurityTourStep checks that anyway - isTourAnchor = true, - ...props -}: GuidedOnboardingTourStep) => - isTourAnchor ? {children} : <>{children}; +export const GuidedOnboardingTourStep = React.memo( + ({ + children, + // can be false if the anchor is an iterative element + // do not use this as an "is tour active" check, the SecurityTourStep checks that anyway + isTourAnchor = true, + ...props + }: GuidedOnboardingTourStep) => { + return isTourAnchor ? ( + {children} + ) : ( + <>{children} + ); + } +); +GuidedOnboardingTourStep.displayName = 'GuidedOnboardingTourStep'; + +const SecurityTourStepAnchor = React.memo(({ children, ...props }: SecurityTourStep) => { + const { hidden: allStepsHidden } = useTourContext(); + const hiddenByFlyout = useHiddenByFlyout({ tourId: props.tourId, step: props.step }); + return !allStepsHidden && !hiddenByFlyout ? ( + {children} + ) : ( + <>{children} + ); +}); +SecurityTourStepAnchor.displayName = 'SecurityTourStepAnchor'; diff --git a/x-pack/plugins/security_solution/public/common/components/guided_onboarding_tour/use_hidden_by_flyout.ts b/x-pack/plugins/security_solution/public/common/components/guided_onboarding_tour/use_hidden_by_flyout.ts new file mode 100644 index 00000000000000..c0c96be21610f0 --- /dev/null +++ b/x-pack/plugins/security_solution/public/common/components/guided_onboarding_tour/use_hidden_by_flyout.ts @@ -0,0 +1,60 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { useMemo } from 'react'; +import { useLocation } from 'react-router-dom'; +import type { RisonValue } from '@kbn/rison'; +import type { AlertsCasesTourSteps } from './tour_config'; +import { + hiddenWhenCaseFlyoutExpanded, + hiddenWhenLeftExpandableFlyoutExpanded, + SecurityStepId, +} from './tour_config'; +import { useKibana } from '../../lib/kibana'; +import { URL_PARAM_KEY } from '../../hooks/use_url_state'; +import { getObjectFromQueryString } from '../../utils/global_query_string/helpers'; + +interface UseHiddenByFlyoutProps { + tourId: SecurityStepId; + step: AlertsCasesTourSteps; +} + +/* + ** To check if given Guided tour step should be hidden when the LEFT expandable flyout + ** or any case modal is opened + */ +export const useHiddenByFlyout = ({ tourId, step }: UseHiddenByFlyoutProps) => { + const { useIsAddToCaseOpen } = useKibana().services.cases.hooks; + const isAddToCaseOpen = useIsAddToCaseOpen(); + + const { search } = useLocation(); + + const expandableFlyoutKey = useMemo( + () => + getObjectFromQueryString<{ left: RisonValue; right: RisonValue }>( + URL_PARAM_KEY.flyout, + search + ), + [search] + ); + + const isExpandableFlyoutExpanded = expandableFlyoutKey?.left; + + const hiddenWhenExpandableFlyoutOpened = useMemo( + () => + isExpandableFlyoutExpanded && + hiddenWhenLeftExpandableFlyoutExpanded[SecurityStepId.alertsCases]?.includes(step), + [isExpandableFlyoutExpanded, step] + ); + + const hiddenWhenCasesModalFlyoutExpanded = useMemo( + () => isAddToCaseOpen && hiddenWhenCaseFlyoutExpanded[tourId]?.includes(step), + [isAddToCaseOpen, tourId, step] + ); + + return hiddenWhenExpandableFlyoutOpened || hiddenWhenCasesModalFlyoutExpanded; +}; diff --git a/x-pack/plugins/security_solution/public/common/components/header_actions/actions.test.tsx b/x-pack/plugins/security_solution/public/common/components/header_actions/actions.test.tsx index c2f6f8af965f86..9c79d28ce20698 100644 --- a/x-pack/plugins/security_solution/public/common/components/header_actions/actions.test.tsx +++ b/x-pack/plugins/security_solution/public/common/components/header_actions/actions.test.tsx @@ -19,7 +19,12 @@ import { SecurityStepId } from '../guided_onboarding_tour/tour_config'; import { Actions } from './actions'; import { initialUserPrivilegesState as mockInitialUserPrivilegesState } from '../user_privileges/user_privileges_context'; import { useUserPrivileges } from '../user_privileges'; +import { useHiddenByFlyout } from '../guided_onboarding_tour/use_hidden_by_flyout'; +const useHiddenByFlyoutMock = useHiddenByFlyout as jest.Mock; +jest.mock('../guided_onboarding_tour/use_hidden_by_flyout', () => ({ + useHiddenByFlyout: jest.fn(), +})); jest.mock('../guided_onboarding_tour'); jest.mock('../user_privileges'); jest.mock('../../../detections/components/user_info', () => ({ @@ -203,6 +208,19 @@ describe('Actions', () => { expect(wrapper.find(SecurityTourStep).exists()).toEqual(true); }); + test('if left expandable flyout is expanded, SecurityTourStep not active', () => { + useHiddenByFlyoutMock.mockReturnValue(true); + + const wrapper = mount( + + + + ); + + expect(wrapper.find(GuidedOnboardingTourStep).exists()).toEqual(true); + expect(wrapper.find(SecurityTourStep).exists()).toEqual(false); + }); + test('on expand event click and SecurityTourStep is active, incrementStep', () => { const wrapper = mount( diff --git a/x-pack/plugins/security_solution/public/common/translations.ts b/x-pack/plugins/security_solution/public/common/translations.ts index 126aa4eabe3b0b..9720baa0d5aa0e 100644 --- a/x-pack/plugins/security_solution/public/common/translations.ts +++ b/x-pack/plugins/security_solution/public/common/translations.ts @@ -113,3 +113,17 @@ export const getAgentTypeName = (agentType: ResponseActionAgentType) => { return agentType; } }; + +export const RULE_EXECUTION_TYPE_BACKFILL = i18n.translate( + 'xpack.securitySolution.detectionEngine.executionRunType.backfill', + { + defaultMessage: 'Manual', + } +); + +export const RULE_EXECUTION_TYPE_STANDARD = i18n.translate( + 'xpack.securitySolution.detectionEngine.executionRunType.standard', + { + defaultMessage: 'Scheduled', + } +); diff --git a/x-pack/plugins/security_solution/public/common/utils/alerts.ts b/x-pack/plugins/security_solution/public/common/utils/alerts.ts index 031fa556b7ab09..780ab831f8dbb2 100644 --- a/x-pack/plugins/security_solution/public/common/utils/alerts.ts +++ b/x-pack/plugins/security_solution/public/common/utils/alerts.ts @@ -9,7 +9,7 @@ import { merge } from '@kbn/std'; import { isPlainObject } from 'lodash'; import type { Ecs } from '@kbn/cases-plugin/common'; import { TableId } from '@kbn/securitysolution-data-table'; -import type { GroupOption } from '@kbn/securitysolution-grouping'; +import type { GroupOption } from '@kbn/grouping'; import * as i18n from './translations'; export const buildAlertsQuery = (alertIds: string[]) => { diff --git a/x-pack/plugins/security_solution/public/common/utils/global_query_string/helpers.test.tsx b/x-pack/plugins/security_solution/public/common/utils/global_query_string/helpers.test.tsx new file mode 100644 index 00000000000000..69f1b5fcbf4e05 --- /dev/null +++ b/x-pack/plugins/security_solution/public/common/utils/global_query_string/helpers.test.tsx @@ -0,0 +1,127 @@ +/* + * Copyright 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 { + isDetectionsPages, + getQueryStringFromLocation, + getParamFromQueryString, + getObjectFromQueryString, + useGetInitialUrlParamValue, + encodeQueryString, + useReplaceUrlParams, + createHistoryEntry, +} from './helpers'; +import { renderHook } from '@testing-library/react-hooks'; +import { createMemoryHistory } from 'history'; +import { Router } from 'react-router-dom'; +import React from 'react'; + +const flyoutString = + "(left:(id:document-details-left,params:(id:'04cf6ea970ab702721f17755b718c1296b5f8b5fcf7b74b053eab9bc885ceb9c',indexName:.internal.alerts-security.alerts-default-000001,scopeId:alerts-page)),right:(id:document-details-right,params:(id:'04cf6ea970ab702721f17755b718c1296b5f8b5fcf7b74b053eab9bc885ceb9c',indexName:.internal.alerts-security.alerts-default-000001,scopeId:alerts-page)))"; +const testString = `sourcerer=(default:(id:security-solution-default,selectedPatterns:!(.alerts-security.alerts-default)))&timerange=(global:(linkTo:!(),timerange:(from:%272024-05-14T23:00:00.000Z%27,fromStr:now%2Fd,kind:relative,to:%272024-05-15T22:59:59.999Z%27,toStr:now%2Fd)),timeline:(linkTo:!(),timerange:(from:%272024-05-14T09:32:36.347Z%27,kind:absolute,to:%272024-05-15T09:32:36.347Z%27)))&timeline=(activeTab:query,graphEventId:%27%27,isOpen:!f)&pageFilters=!((exclude:!f,existsSelected:!f,fieldName:kibana.alert.workflow_status,hideActionBar:!t,selectedOptions:!(open),title:Status),(exclude:!f,existsSelected:!f,fieldName:kibana.alert.severity,hideActionBar:!t,selectedOptions:!(),title:Severity),(exclude:!f,existsSelected:!f,fieldName:user.name,hideActionBar:!f,selectedOptions:!(),title:User),(exclude:!f,existsSelected:!f,fieldName:host.name,hideActionBar:!f,selectedOptions:!(),title:Host))&flyout=${flyoutString}&timelineFlyout=()`; + +const flyoutObject = { + left: { + id: 'document-details-left', + params: { + id: '04cf6ea970ab702721f17755b718c1296b5f8b5fcf7b74b053eab9bc885ceb9c', + indexName: '.internal.alerts-security.alerts-default-000001', + scopeId: 'alerts-page', + }, + }, + right: { + id: 'document-details-right', + params: { + id: '04cf6ea970ab702721f17755b718c1296b5f8b5fcf7b74b053eab9bc885ceb9c', + indexName: '.internal.alerts-security.alerts-default-000001', + scopeId: 'alerts-page', + }, + }, +}; + +describe('helpers', () => { + describe('isDetectionsPages', () => { + it('returns true for detections pages', () => { + expect(isDetectionsPages('alerts')).toBe(true); + expect(isDetectionsPages('rules')).toBe(true); + expect(isDetectionsPages('rules-add')).toBe(true); + expect(isDetectionsPages('rules-create')).toBe(true); + expect(isDetectionsPages('exceptions')).toBe(true); + }); + + it('returns false for non-detections pages', () => { + expect(isDetectionsPages('otherPage')).toBe(false); + }); + }); + + describe('getQueryStringFromLocation', () => { + it('returns the query string without the leading "?"', () => { + expect(getQueryStringFromLocation('?param=value')).toBe('param=value'); + }); + }); + + describe('getParamFromQueryString', () => { + it('returns the value of the specified query parameter', () => { + expect(getParamFromQueryString(testString, 'flyout')).toBe(flyoutString); + }); + + it('returns undefined if the query parameter is not found', () => { + expect(getParamFromQueryString(testString, 'param')).toBeUndefined(); + }); + + it('returns the first value if the query parameter is an array', () => { + const queryString = 'param1=value1¶m1=value2'; + expect(getParamFromQueryString(queryString, 'param1')).toBe('value1'); + }); + }); + + describe('getObjectFromQueryString', () => { + it('returns the decoded value of the specified query parameter', () => { + expect(getObjectFromQueryString('flyout', testString)).toEqual(flyoutObject); + }); + + it('returns null if the query parameter is not found', () => { + expect(getObjectFromQueryString('param', testString)).toBeNull(); + }); + }); + + describe('useGetInitialUrlParamValue', () => { + it('returns a function that gets the initial URL parameter value', () => { + window.history.pushState({}, '', `?${testString}`); + const { result } = renderHook(() => useGetInitialUrlParamValue('flyout')); + expect(result.current()).toEqual(flyoutObject); + }); + }); + + describe('encodeQueryString', () => { + it('returns an encoded query string from the given parameters', () => { + const params = { param1: 'value1', param2: 'value2' }; + expect(encodeQueryString(params)).toBe('param1=value1¶m2=value2'); + }); + }); + + describe('useReplaceUrlParams', () => { + it('replaces URL parameters correctly', () => { + const history = createMemoryHistory(); + const Wrapper: React.FC<{ children: React.ReactNode }> = ({ children }) => ( + {children} + ); + const { result } = renderHook(() => useReplaceUrlParams(), { wrapper: Wrapper }); + + window.history.pushState({}, '', '?param1=value1'); + result.current({ param1: 'value2' }); + expect(history.location.search).toBe('?param1=value2'); + }); + }); + + describe('createHistoryEntry', () => { + it('creates a new history entry', () => { + const initialHistoryLength = window.history.length; + createHistoryEntry(); + expect(window.history.length).toBe(initialHistoryLength + 1); + }); + }); +}); diff --git a/x-pack/plugins/security_solution/public/common/utils/global_query_string/helpers.ts b/x-pack/plugins/security_solution/public/common/utils/global_query_string/helpers.ts index 1be01959e2d0a7..af3cd8161a778e 100644 --- a/x-pack/plugins/security_solution/public/common/utils/global_query_string/helpers.ts +++ b/x-pack/plugins/security_solution/public/common/utils/global_query_string/helpers.ts @@ -33,6 +33,17 @@ export const getParamFromQueryString = ( return Array.isArray(queryParam) ? queryParam[0] : queryParam; }; +export const getObjectFromQueryString = ( + urlParamKey: string, + search?: string +) => { + const rawParamValue = getParamFromQueryString( + getQueryStringFromLocation(search ?? window.location.search), + urlParamKey + ); + return safeDecode(rawParamValue ?? '') as State | null; +}; + /** * * Gets the value of the URL param from the query string. @@ -44,15 +55,10 @@ export const useGetInitialUrlParamValue = ( ): (() => State | null) => { // window.location.search provides the most updated representation of the url search. // It also guarantees that we don't overwrite URL param managed outside react-router. - const getInitialUrlParamValue = useCallback((): State | null => { - const rawParamValue = getParamFromQueryString( - getQueryStringFromLocation(window.location.search), - urlParamKey - ); - const paramValue = safeDecode(rawParamValue ?? '') as State | null; - - return paramValue; - }, [urlParamKey]); + const getInitialUrlParamValue = useCallback( + (): State | null => getObjectFromQueryString(urlParamKey), + [urlParamKey] + ); return getInitialUrlParamValue; }; diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_details_ui/pages/rule_details/__mocks__/rule_details_context.tsx b/x-pack/plugins/security_solution/public/detection_engine/rule_details_ui/pages/rule_details/__mocks__/rule_details_context.tsx index a853e8f1422c79..45e84fcd24db93 100644 --- a/x-pack/plugins/security_solution/public/detection_engine/rule_details_ui/pages/rule_details/__mocks__/rule_details_context.tsx +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_details_ui/pages/rule_details/__mocks__/rule_details_context.tsx @@ -31,6 +31,8 @@ export const useRuleDetailsContextMock = { sortField: 'timestamp', sortDirection: 'desc', }, + runTypeFilters: [], + showSourceEventTimeRange: true, }, actions: { setEnd: jest.fn(), @@ -45,6 +47,8 @@ export const useRuleDetailsContextMock = { setSortField: jest.fn(), setStart: jest.fn(), setStatusFilters: jest.fn(), + setRunTypeFilters: jest.fn(), + setShowSourceEventTimeRange: jest.fn(), }, }, }), diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_details_ui/pages/rule_details/execution_log_table/__snapshots__/execution_log_search_bar.test.tsx.snap b/x-pack/plugins/security_solution/public/detection_engine/rule_details_ui/pages/rule_details/execution_log_table/__snapshots__/execution_log_search_bar.test.tsx.snap index 241f363d4885f4..009e6dcc58acee 100644 --- a/x-pack/plugins/security_solution/public/detection_engine/rule_details_ui/pages/rule_details/execution_log_table/__snapshots__/execution_log_search_bar.test.tsx.snap +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_details_ui/pages/rule_details/execution_log_table/__snapshots__/execution_log_search_bar.test.tsx.snap @@ -10,17 +10,25 @@ exports[`ExecutionLogSearchBar snapshots renders correctly against snapshot 1`] - + + + + +
`; diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_details_ui/pages/rule_details/execution_log_table/execution_log_columns.tsx b/x-pack/plugins/security_solution/public/detection_engine/rule_details_ui/pages/rule_details/execution_log_table/execution_log_columns.tsx index c8bacabbbbc4f5..4428d6cca6de6b 100644 --- a/x-pack/plugins/security_solution/public/detection_engine/rule_details_ui/pages/rule_details/execution_log_table/execution_log_columns.tsx +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_details_ui/pages/rule_details/execution_log_table/execution_log_columns.tsx @@ -25,6 +25,10 @@ import type { import { getEmptyValue } from '../../../../../common/components/empty_value'; import { FormattedDate } from '../../../../../common/components/formatted_date'; +import { + RULE_EXECUTION_TYPE_BACKFILL, + RULE_EXECUTION_TYPE_STANDARD, +} from '../../../../../common/translations'; import { ExecutionStatusIndicator } from '../../../../rule_monitoring'; import { PopoverTooltip } from '../../../../rule_management_ui/components/rules_table/popover_tooltip'; import { TableHeaderTooltipCell } from '../../../../rule_management_ui/components/rules_table/table_header_tooltip_cell'; @@ -79,6 +83,19 @@ export const EXECUTION_LOG_COLUMNS: Array { + return ( + + {record.backfill ? RULE_EXECUTION_TYPE_BACKFILL : RULE_EXECUTION_TYPE_STANDARD} + + ); + }, + }, { field: 'timestamp', name: ( @@ -139,6 +156,34 @@ export const getMessageColumn = (width: string) => ({ width, }); +export const getSourceEventTimeRangeColumns = () => [ + { + name: ( + + ), + field: 'backfill', + render: (backfill: { to: string; from: string }) => { + return backfill ? ( +
+
+ +
+ {'-'} +
+ +
+
+ ) : ( + getEmptyValue() + ); + }, + width: '20%', + }, +]; + export const getExecutionLogMetricsColumns = ( docLinks: DocLinksStart ): Array> => [ diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_details_ui/pages/rule_details/execution_log_table/execution_log_search_bar.test.tsx b/x-pack/plugins/security_solution/public/detection_engine/rule_details_ui/pages/rule_details/execution_log_table/execution_log_search_bar.test.tsx index abda12afde7637..584d3f93b474ef 100644 --- a/x-pack/plugins/security_solution/public/detection_engine/rule_details_ui/pages/rule_details/execution_log_table/execution_log_search_bar.test.tsx +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_details_ui/pages/rule_details/execution_log_table/execution_log_search_bar.test.tsx @@ -10,6 +10,10 @@ import React from 'react'; import { ExecutionLogSearchBar } from './execution_log_search_bar'; import { noop } from 'lodash/fp'; +jest.mock('../../../../../common/hooks/use_experimental_features', () => ({ + useIsExperimentalFeatureEnabled: jest.fn(), +})); + /** * NOTE: This component is currently not shown in the UI as custom search queries * are not yet fully supported by the Rule Execution Log aggregation API since @@ -27,6 +31,8 @@ describe('ExecutionLogSearchBar', () => { onlyShowFilters={true} selectedStatuses={[]} onStatusFilterChange={noop} + selectedRunTypes={[]} + onRunTypeFilterChange={noop} onSearch={noop} /> ); diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_details_ui/pages/rule_details/execution_log_table/execution_log_search_bar.tsx b/x-pack/plugins/security_solution/public/detection_engine/rule_details_ui/pages/rule_details/execution_log_table/execution_log_search_bar.tsx index 3622182aa7fc33..db43b104ec7138 100644 --- a/x-pack/plugins/security_solution/public/detection_engine/rule_details_ui/pages/rule_details/execution_log_table/execution_log_search_bar.tsx +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_details_ui/pages/rule_details/execution_log_table/execution_log_search_bar.tsx @@ -9,10 +9,18 @@ import React, { useCallback } from 'react'; import { replace } from 'lodash'; import { EuiFieldSearch, EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; -import type { RuleExecutionStatus } from '../../../../../../common/api/detection_engine/rule_monitoring'; -import { RuleExecutionStatusEnum } from '../../../../../../common/api/detection_engine/rule_monitoring'; -import { ExecutionStatusFilter } from '../../../../rule_monitoring'; +import type { + RuleExecutionStatus, + RuleRunType, +} from '../../../../../../common/api/detection_engine/rule_monitoring'; +import { + RUN_TYPE_FILTERS, + STATUS_FILTERS, +} from '../../../../../../common/detection_engine/rule_management/execution_log'; + +import { ExecutionStatusFilter, ExecutionRunTypeFilter } from '../../../../rule_monitoring'; +import { useIsExperimentalFeatureEnabled } from '../../../../../common/hooks/use_experimental_features'; import * as i18n from './translations'; export const EXECUTION_LOG_SCHEMA_MAPPING = { @@ -36,18 +44,13 @@ export const replaceQueryTextAliases = (queryText: string): string => { ); }; -// This only includes statuses which are or can be final -const STATUS_FILTERS: RuleExecutionStatus[] = [ - RuleExecutionStatusEnum.succeeded, - RuleExecutionStatusEnum.failed, - RuleExecutionStatusEnum['partial failure'], -]; - interface ExecutionLogTableSearchProps { onlyShowFilters: true; selectedStatuses: RuleExecutionStatus[]; onStatusFilterChange: (selectedStatuses: RuleExecutionStatus[]) => void; onSearch: (queryText: string) => void; + selectedRunTypes: RuleRunType[]; + onRunTypeFilterChange: (selectedRunTypes: RuleRunType[]) => void; } /** @@ -58,13 +61,21 @@ interface ExecutionLogTableSearchProps { * Please see this comment for history/details: https://github.com/elastic/kibana/pull/127339/files#r825240516 */ export const ExecutionLogSearchBar = React.memo( - ({ onlyShowFilters, selectedStatuses, onStatusFilterChange, onSearch }) => { + ({ + onlyShowFilters, + selectedStatuses, + onStatusFilterChange, + onSearch, + selectedRunTypes, + onRunTypeFilterChange, + }) => { const handleSearch = useCallback( (queryText: string) => { onSearch(replaceQueryTextAliases(queryText)); }, [onSearch] ); + const isManualRuleRunEnabled = useIsExperimentalFeatureEnabled('manualRuleRunEnabled'); return ( @@ -81,11 +92,24 @@ export const ExecutionLogSearchBar = React.memo( )} - + + {isManualRuleRunEnabled && ( + + + + )} + + + + ); diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_details_ui/pages/rule_details/execution_log_table/execution_log_table.tsx b/x-pack/plugins/security_solution/public/detection_engine/rule_details_ui/pages/rule_details/execution_log_table/execution_log_table.tsx index 77705480755111..26b3523c054721 100644 --- a/x-pack/plugins/security_solution/public/detection_engine/rule_details_ui/pages/rule_details/execution_log_table/execution_log_table.tsx +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_details_ui/pages/rule_details/execution_log_table/execution_log_table.tsx @@ -33,7 +33,10 @@ import type { ThemeServiceStart } from '@kbn/core-theme-browser'; import { InputsModelId } from '../../../../../common/store/inputs/constants'; -import { RULE_DETAILS_EXECUTION_LOG_TABLE_SHOW_METRIC_COLUMNS_STORAGE_KEY } from '../../../../../../common/constants'; +import { + RULE_DETAILS_EXECUTION_LOG_TABLE_SHOW_METRIC_COLUMNS_STORAGE_KEY, + RULE_DETAILS_EXECUTION_LOG_TABLE_SHOW_SOURCE_EVENT_TIME_RANGE_STORAGE_KEY, +} from '../../../../../../common/constants'; import type { RuleExecutionResult, RuleExecutionStatus, @@ -72,8 +75,10 @@ import { getMessageColumn, getExecutionLogMetricsColumns, expanderColumn, + getSourceEventTimeRangeColumns, } from './execution_log_columns'; import { ExecutionLogSearchBar } from './execution_log_search_bar'; +import { useIsExperimentalFeatureEnabled } from '../../../../../common/hooks/use_experimental_features'; const EXECUTION_UUID_FIELD_NAME = 'kibana.alert.rule.execution.uuid'; @@ -115,6 +120,7 @@ const ExecutionLogTableComponent: React.FC = ({ storage, timelines, } = useKibana().services; + const isManualRuleRunEnabled = useIsExperimentalFeatureEnabled('manualRuleRunEnabled'); const { [RuleDetailTabs.executionResults]: { @@ -122,7 +128,9 @@ const ExecutionLogTableComponent: React.FC = ({ superDatePicker: { recentlyUsedRanges, refreshInterval, isPaused, start, end }, queryText, statusFilters, + runTypeFilters, showMetricColumns, + showSourceEventTimeRange, pagination: { pageIndex, pageSize }, sort: { sortField, sortDirection }, }, @@ -139,6 +147,8 @@ const ExecutionLogTableComponent: React.FC = ({ setSortField, setStart, setStatusFilters, + setRunTypeFilters, + setShowSourceEventTimeRange, }, }, } = useRuleDetailsContext(); @@ -207,6 +217,7 @@ const ExecutionLogTableComponent: React.FC = ({ end, queryText, statusFilters, + runTypeFilters, page: pageIndex, perPage: pageSize, sortField, @@ -348,6 +359,17 @@ const ExecutionLogTableComponent: React.FC = ({ ] ); + const onShowSourceEventTimeRange = useCallback( + (showEventTimeRange: boolean) => { + storage.set( + RULE_DETAILS_EXECUTION_LOG_TABLE_SHOW_SOURCE_EVENT_TIME_RANGE_STORAGE_KEY, + showEventTimeRange + ); + setShowSourceEventTimeRange(showEventTimeRange); + }, + [setShowSourceEventTimeRange, storage] + ); + const onShowMetricColumnsCallback = useCallback( (showMetrics: boolean) => { storage.set(RULE_DETAILS_EXECUTION_LOG_TABLE_SHOW_METRIC_COLUMNS_STORAGE_KEY, showMetrics); @@ -431,12 +453,27 @@ const ExecutionLogTableComponent: React.FC = ({ }); const executionLogColumns = useMemo(() => { - const columns = [...EXECUTION_LOG_COLUMNS]; + const columns = [...EXECUTION_LOG_COLUMNS].filter((item) => { + if ('field' in item) { + return item.field === 'type' ? isManualRuleRunEnabled : true; + } + return true; + }); + let messageColumnWidth = 50; + + if (showSourceEventTimeRange && isManualRuleRunEnabled) { + columns.push(...getSourceEventTimeRangeColumns()); + messageColumnWidth = 30; + } if (showMetricColumns) { - columns.push(getMessageColumn('20%'), ...getExecutionLogMetricsColumns(docLinks)); + messageColumnWidth = 20; + columns.push( + getMessageColumn(`${messageColumnWidth}%`), + ...getExecutionLogMetricsColumns(docLinks) + ); } else { - columns.push(getMessageColumn('50%')); + columns.push(getMessageColumn(`${messageColumnWidth}%`)); } columns.push( @@ -448,10 +485,18 @@ const ExecutionLogTableComponent: React.FC = ({ ); return columns; - }, [actions, docLinks, showMetricColumns, rows.toggleRowExpanded, rows.isRowExpanded]); + }, [ + isManualRuleRunEnabled, + actions, + docLinks, + showMetricColumns, + showSourceEventTimeRange, + rows.toggleRowExpanded, + rows.isRowExpanded, + ]); return ( - + {/* Filter bar */} @@ -463,6 +508,8 @@ const ExecutionLogTableComponent: React.FC = ({ selectedStatuses={statusFilters} onStatusFilterChange={onStatusFilterChangeCallback} onSearch={onSearchCallback} + selectedRunTypes={runTypeFilters} + onRunTypeFilterChange={setRunTypeFilters} /> @@ -516,6 +563,14 @@ const ExecutionLogTableComponent: React.FC = ({ updatedAt: dataUpdatedAt, })} + {isManualRuleRunEnabled && ( + onShowSourceEventTimeRange(e.target.checked)} + /> + )} = ({ onChange={onTableChangeCallback} itemId={getItemId} itemIdToExpandedRowMap={rows.itemIdToExpandedRowMap} + data-test-subj="executionsTable" /> ); diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_details_ui/pages/rule_details/execution_log_table/translations.ts b/x-pack/plugins/security_solution/public/detection_engine/rule_details_ui/pages/rule_details/execution_log_table/translations.ts index 0b4e91c5934af8..f84e0b6b859c25 100644 --- a/x-pack/plugins/security_solution/public/detection_engine/rule_details_ui/pages/rule_details/execution_log_table/translations.ts +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_details_ui/pages/rule_details/execution_log_table/translations.ts @@ -55,6 +55,13 @@ export const RULE_EXECUTION_LOG_SHOW_METRIC_COLUMNS_SWITCH = i18n.translate( } ); +export const RULE_EXECUTION_LOG_SHOW_SOURCE_EVENT_TIME_RANGE = i18n.translate( + 'xpack.securitySolution.detectionEngine.ruleDetails.ruleExecutionLog.showSourceEventTimeRangeSwitchTitle', + { + defaultMessage: 'Show source event time range', + } +); + export const COLUMN_STATUS = i18n.translate( 'xpack.securitySolution.detectionEngine.ruleDetails.ruleExecutionLog.statusColumn', { @@ -69,6 +76,27 @@ export const COLUMN_STATUS_TOOLTIP = i18n.translate( } ); +export const COLUMN_TYPE = i18n.translate( + 'xpack.securitySolution.detectionEngine.ruleDetails.ruleExecutionLog.type', + { + defaultMessage: 'Type', + } +); + +export const COLUMN_SOURCE_EVENT_TIME_RANGE = i18n.translate( + 'xpack.securitySolution.detectionEngine.ruleDetails.ruleExecutionLog.sourceEventTimeRange', + { + defaultMessage: 'Source event time range', + } +); + +export const COLUMN_SOURCE_EVENT_TIME_RANGE_TOOLTIP = i18n.translate( + 'xpack.securitySolution.detectionEngine.ruleDetails.ruleExecutionLog.sourceEventTimeRangeTooltip', + { + defaultMessage: "Only for manual rule runs. Don't include additional lookback time.", + } +); + export const COLUMN_TIMESTAMP = i18n.translate( 'xpack.securitySolution.detectionEngine.ruleDetails.ruleExecutionLog.timestampColumn', { diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_details_ui/pages/rule_details/rule_details_context.tsx b/x-pack/plugins/security_solution/public/detection_engine/rule_details_ui/pages/rule_details/rule_details_context.tsx index e08e32662cca66..7134fb981c7900 100644 --- a/x-pack/plugins/security_solution/public/detection_engine/rule_details_ui/pages/rule_details/rule_details_context.tsx +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_details_ui/pages/rule_details/rule_details_context.tsx @@ -8,10 +8,14 @@ import type { SortOrder } from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; import type { DurationRange } from '@elastic/eui/src/components/date_picker/types'; import React, { createContext, useContext, useMemo, useState } from 'react'; -import { RULE_DETAILS_EXECUTION_LOG_TABLE_SHOW_METRIC_COLUMNS_STORAGE_KEY } from '../../../../../common/constants'; +import { + RULE_DETAILS_EXECUTION_LOG_TABLE_SHOW_METRIC_COLUMNS_STORAGE_KEY, + RULE_DETAILS_EXECUTION_LOG_TABLE_SHOW_SOURCE_EVENT_TIME_RANGE_STORAGE_KEY, +} from '../../../../../common/constants'; import type { RuleExecutionResult, RuleExecutionStatus, + RuleRunType, } from '../../../../../common/api/detection_engine/rule_monitoring'; import { invariant } from '../../../../../common/utils/invariant'; import { useKibana } from '../../../../common/lib/kibana'; @@ -55,6 +59,11 @@ export interface ExecutionLogTableState { * Whether or not to show additional metric columnbs */ showMetricColumns: boolean; + /** + * Whether or not to show source event time range + */ + + showSourceEventTimeRange: boolean; /** * Currently selected page and number of rows per page */ @@ -66,6 +75,7 @@ export interface ExecutionLogTableState { sortField: keyof RuleExecutionResult; sortDirection: SortOrder; }; + runTypeFilters: RuleRunType[]; } // @ts-expect-error unused constant @@ -79,7 +89,9 @@ const DEFAULT_STATE: ExecutionLogTableState = { }, queryText: '', statusFilters: [], + runTypeFilters: [], showMetricColumns: false, + showSourceEventTimeRange: false, pagination: { pageIndex: 1, pageSize: 5, @@ -103,6 +115,8 @@ export interface ExecutionLogTableActions { setPageSize: React.Dispatch>; setSortField: React.Dispatch>; setSortDirection: React.Dispatch>; + setShowSourceEventTimeRange: React.Dispatch>; + setRunTypeFilters: React.Dispatch>; } export interface RuleDetailsContextType { @@ -133,9 +147,13 @@ export const RuleDetailsContextProvider = ({ children }: RuleDetailsContextProvi // Searchbar/Filter/Settings state const [queryText, setQueryText] = useState(''); const [statusFilters, setStatusFilters] = useState([]); + const [runTypeFilters, setRunTypeFilters] = useState([]); const [showMetricColumns, setShowMetricColumns] = useState( storage.get(RULE_DETAILS_EXECUTION_LOG_TABLE_SHOW_METRIC_COLUMNS_STORAGE_KEY) ?? false ); + const [showSourceEventTimeRange, setShowSourceEventTimeRange] = useState( + storage.get(RULE_DETAILS_EXECUTION_LOG_TABLE_SHOW_SOURCE_EVENT_TIME_RANGE_STORAGE_KEY) ?? false + ); // Pagination state const [pageIndex, setPageIndex] = useState(1); const [pageSize, setPageSize] = useState(5); @@ -156,7 +174,9 @@ export const RuleDetailsContextProvider = ({ children }: RuleDetailsContextProvi }, queryText, statusFilters, + runTypeFilters, showMetricColumns, + showSourceEventTimeRange, pagination: { pageIndex, pageSize, @@ -179,6 +199,8 @@ export const RuleDetailsContextProvider = ({ children }: RuleDetailsContextProvi setSortField, setStart, setStatusFilters, + setRunTypeFilters, + setShowSourceEventTimeRange, }, }, }), @@ -195,6 +217,8 @@ export const RuleDetailsContextProvider = ({ children }: RuleDetailsContextProvi sortField, start, statusFilters, + runTypeFilters, + showSourceEventTimeRange, ] ); diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_monitoring/api/api_client.ts b/x-pack/plugins/security_solution/public/detection_engine/rule_monitoring/api/api_client.ts index bd1bf80a0f4c32..9290e97dcfcd78 100644 --- a/x-pack/plugins/security_solution/public/detection_engine/rule_monitoring/api/api_client.ts +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_monitoring/api/api_client.ts @@ -87,6 +87,7 @@ export const api: IRuleMonitoringApiClient = { sortField, sortOrder, signal, + runTypeFilters, } = args; const url = getRuleExecutionResultsUrl(ruleId); @@ -105,6 +106,7 @@ export const api: IRuleMonitoringApiClient = { sort_order: sortOrder, page, per_page: perPage, + run_type_filters: runTypeFilters?.sort()?.join(','), }, signal, }); diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_monitoring/api/api_client_interface.ts b/x-pack/plugins/security_solution/public/detection_engine/rule_monitoring/api/api_client_interface.ts index a51059a16ef429..06170430a455e4 100644 --- a/x-pack/plugins/security_solution/public/detection_engine/rule_monitoring/api/api_client_interface.ts +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_monitoring/api/api_client_interface.ts @@ -13,6 +13,7 @@ import type { RuleExecutionEventType, RuleExecutionResult, RuleExecutionStatus, + RuleRunType, } from '../../../../common/api/detection_engine/rule_monitoring'; export interface IRuleMonitoringApiClient { @@ -139,4 +140,9 @@ export interface FetchRuleExecutionResultsArgs extends RuleMonitoringApiCallArgs * Number of results to fetch per page. */ perPage?: number; + + /** + * Array of `runTypeFilters` (e.g. `manual,scheduled`) + */ + runTypeFilters?: RuleRunType[]; } diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_monitoring/components/basic/filters/execution_run_type_filter/index.tsx b/x-pack/plugins/security_solution/public/detection_engine/rule_monitoring/components/basic/filters/execution_run_type_filter/index.tsx new file mode 100644 index 00000000000000..773e64e71ffc92 --- /dev/null +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_monitoring/components/basic/filters/execution_run_type_filter/index.tsx @@ -0,0 +1,52 @@ +/* + * Copyright 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 { MultiselectFilter } from '../multiselect_filter'; +import type { RuleRunType } from '../../../../../../../common/api/detection_engine/rule_monitoring'; +import { RuleRunTypeEnum } from '../../../../../../../common/api/detection_engine/rule_monitoring'; +import * as i18n from './translations'; +import { + RULE_EXECUTION_TYPE_BACKFILL, + RULE_EXECUTION_TYPE_STANDARD, +} from '../../../../../../common/translations'; + +interface ExecutionRunTypeFilterProps { + items: RuleRunType[]; + selectedItems: RuleRunType[]; + onChange: (selectedItems: RuleRunType[]) => void; +} + +const ExecutionRunTypeFilterComponent: React.FC = ({ + items, + selectedItems, + onChange, +}) => { + const renderItem = useCallback((item: RuleRunType) => { + if (item === RuleRunTypeEnum.backfill) { + return RULE_EXECUTION_TYPE_BACKFILL; + } else if (item === RuleRunTypeEnum.standard) { + return RULE_EXECUTION_TYPE_STANDARD; + } else { + return '-'; + } + }, []); + + return ( + + dataTestSubj="ExecutionRunTypeFilter" + title={i18n.FILTER_TITLE} + items={items} + selectedItems={selectedItems} + onSelectionChange={onChange} + renderItem={renderItem} + /> + ); +}; + +export const ExecutionRunTypeFilter = React.memo(ExecutionRunTypeFilterComponent); +ExecutionRunTypeFilter.displayName = 'ExecutionRunTypeFilter'; diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_monitoring/components/basic/filters/execution_run_type_filter/translations.ts b/x-pack/plugins/security_solution/public/detection_engine/rule_monitoring/components/basic/filters/execution_run_type_filter/translations.ts new file mode 100644 index 00000000000000..18ace6b7b1ca06 --- /dev/null +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_monitoring/components/basic/filters/execution_run_type_filter/translations.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 { i18n } from '@kbn/i18n'; + +export const FILTER_TITLE = i18n.translate( + 'xpack.securitySolution.detectionEngine.ruleMonitoring.executionRunTypeFilter.filterTitle', + { + defaultMessage: 'Run type', + } +); diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_monitoring/components/execution_results_table/use_execution_results.test.tsx b/x-pack/plugins/security_solution/public/detection_engine/rule_monitoring/components/execution_results_table/use_execution_results.test.tsx index 65d7ea7c3cda7c..d5d2e2409e2a30 100644 --- a/x-pack/plugins/security_solution/public/detection_engine/rule_monitoring/components/execution_results_table/use_execution_results.test.tsx +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_monitoring/components/execution_results_table/use_execution_results.test.tsx @@ -12,6 +12,10 @@ import { useToasts } from '../../../../common/lib/kibana'; import { api } from '../../api'; import { createReactQueryWrapper } from '../../../../common/mock'; +jest.mock('../../../../common/hooks/use_experimental_features', () => ({ + useIsExperimentalFeatureEnabled: jest.fn(), +})); + jest.mock('../../../../common/lib/kibana'); jest.mock('../../api'); diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_monitoring/components/execution_results_table/use_execution_results.tsx b/x-pack/plugins/security_solution/public/detection_engine/rule_monitoring/components/execution_results_table/use_execution_results.tsx index ac18f11c75907e..8660139676351f 100644 --- a/x-pack/plugins/security_solution/public/detection_engine/rule_monitoring/components/execution_results_table/use_execution_results.tsx +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_monitoring/components/execution_results_table/use_execution_results.tsx @@ -8,21 +8,30 @@ import { useQuery } from '@tanstack/react-query'; import { useAppToasts } from '../../../../common/hooks/use_app_toasts'; +import { RuleRunTypeEnum } from '../../../../../common/api/detection_engine/rule_monitoring'; import type { GetRuleExecutionResultsResponse } from '../../../../../common/api/detection_engine/rule_monitoring'; import type { FetchRuleExecutionResultsArgs } from '../../api'; import { api } from '../../api'; - +import { useIsExperimentalFeatureEnabled } from '../../../../common/hooks/use_experimental_features'; import * as i18n from './translations'; export type UseExecutionResultsArgs = Omit; export const useExecutionResults = (args: UseExecutionResultsArgs) => { const { addError } = useAppToasts(); + const isManualRuleRunEnabled = useIsExperimentalFeatureEnabled('manualRuleRunEnabled'); return useQuery( ['detectionEngine', 'ruleMonitoring', 'executionResults', args], ({ signal }) => { - return api.fetchRuleExecutionResults({ ...args, signal }); + let runTypeFilters = args.runTypeFilters; + + // if manual rule run is disabled, only show standard runs + if (!isManualRuleRunEnabled) { + runTypeFilters = [RuleRunTypeEnum.standard]; + } + + return api.fetchRuleExecutionResults({ ...args, runTypeFilters, signal }); }, { keepPreviousData: true, diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_monitoring/index.ts b/x-pack/plugins/security_solution/public/detection_engine/rule_monitoring/index.ts index b186a8d7fd8376..9eb42fb3d6b6cb 100644 --- a/x-pack/plugins/security_solution/public/detection_engine/rule_monitoring/index.ts +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_monitoring/index.ts @@ -8,6 +8,7 @@ export * from './api'; export * from './components/basic/filters/execution_status_filter'; +export * from './components/basic/filters/execution_run_type_filter'; export * from './components/basic/indicators/execution_status_indicator'; export * from './components/execution_events_table/execution_events_table'; export * from './components/execution_results_table/use_execution_results'; diff --git a/x-pack/plugins/security_solution/public/detections/components/alerts_table/alerts_grouping.tsx b/x-pack/plugins/security_solution/public/detections/components/alerts_table/alerts_grouping.tsx index db2562f4a3b8a3..4bcbc61d14833c 100644 --- a/x-pack/plugins/security_solution/public/detections/components/alerts_table/alerts_grouping.tsx +++ b/x-pack/plugins/security_solution/public/detections/components/alerts_table/alerts_grouping.tsx @@ -8,7 +8,7 @@ import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react'; import { useDispatch } from 'react-redux'; import type { Filter, Query } from '@kbn/es-query'; -import { isNoneGroup, useGrouping } from '@kbn/securitysolution-grouping'; +import { isNoneGroup, useGrouping } from '@kbn/grouping'; import { isEmpty, isEqual } from 'lodash/fp'; import type { Storage } from '@kbn/kibana-utils-plugin/public'; import type { TableIdLiteral } from '@kbn/securitysolution-data-table'; diff --git a/x-pack/plugins/security_solution/public/detections/components/alerts_table/alerts_sub_grouping.tsx b/x-pack/plugins/security_solution/public/detections/components/alerts_table/alerts_sub_grouping.tsx index eaf9277d4bb488..6976338dfbe82d 100644 --- a/x-pack/plugins/security_solution/public/detections/components/alerts_table/alerts_sub_grouping.tsx +++ b/x-pack/plugins/security_solution/public/detections/components/alerts_table/alerts_sub_grouping.tsx @@ -9,12 +9,12 @@ import React, { useCallback, useEffect, useMemo, useRef } from 'react'; import { v4 as uuidv4 } from 'uuid'; import type { Filter, Query } from '@kbn/es-query'; import { buildEsQuery } from '@kbn/es-query'; -import type { GroupingAggregation } from '@kbn/securitysolution-grouping'; -import { isNoneGroup } from '@kbn/securitysolution-grouping'; +import type { GroupingAggregation } from '@kbn/grouping'; +import { isNoneGroup } from '@kbn/grouping'; import { getEsQueryConfig } from '@kbn/data-plugin/common'; -import type { DynamicGroupingProps } from '@kbn/securitysolution-grouping/src'; +import type { DynamicGroupingProps } from '@kbn/grouping/src'; import type { TableIdLiteral } from '@kbn/securitysolution-data-table'; -import { parseGroupingQuery } from '@kbn/securitysolution-grouping/src'; +import { parseGroupingQuery } from '@kbn/grouping/src'; import type { RunTimeMappings } from '../../../common/store/sourcerer/model'; import { combineQueries } from '../../../common/lib/kuery'; import { SourcererScopeName } from '../../../common/store/sourcerer/model'; diff --git a/x-pack/plugins/security_solution/public/detections/components/alerts_table/grouping_settings/group_panel_renderers.tsx b/x-pack/plugins/security_solution/public/detections/components/alerts_table/grouping_settings/group_panel_renderers.tsx index ac703e496d6178..cd858b74399a2b 100644 --- a/x-pack/plugins/security_solution/public/detections/components/alerts_table/grouping_settings/group_panel_renderers.tsx +++ b/x-pack/plugins/security_solution/public/detections/components/alerts_table/grouping_settings/group_panel_renderers.tsx @@ -19,7 +19,7 @@ import { import { euiThemeVars } from '@kbn/ui-theme'; import { isArray } from 'lodash/fp'; import React from 'react'; -import type { GroupPanelRenderer } from '@kbn/securitysolution-grouping/src'; +import type { GroupPanelRenderer } from '@kbn/grouping/src'; import type { AlertsGroupingAggregation } from './types'; import { firstNonNullValue } from '../../../../../common/endpoint/models/ecs_safety_helpers'; import type { GenericBuckets } from '../../../../../common/search_strategy'; diff --git a/x-pack/plugins/security_solution/public/detections/components/alerts_table/grouping_settings/group_stats.tsx b/x-pack/plugins/security_solution/public/detections/components/alerts_table/grouping_settings/group_stats.tsx index 8c52242be8c41e..96cc365d93fabd 100644 --- a/x-pack/plugins/security_solution/public/detections/components/alerts_table/grouping_settings/group_stats.tsx +++ b/x-pack/plugins/security_solution/public/detections/components/alerts_table/grouping_settings/group_stats.tsx @@ -7,7 +7,7 @@ import { EuiIcon } from '@elastic/eui'; import React from 'react'; -import type { RawBucket, StatRenderer } from '@kbn/securitysolution-grouping'; +import type { RawBucket, StatRenderer } from '@kbn/grouping'; import type { AlertsGroupingAggregation } from './types'; import * as i18n from '../translations'; diff --git a/x-pack/plugins/security_solution/public/detections/components/alerts_table/grouping_settings/query_builder.ts b/x-pack/plugins/security_solution/public/detections/components/alerts_table/grouping_settings/query_builder.ts index eda89f3d537efb..839bea01780be7 100644 --- a/x-pack/plugins/security_solution/public/detections/components/alerts_table/grouping_settings/query_builder.ts +++ b/x-pack/plugins/security_solution/public/detections/components/alerts_table/grouping_settings/query_builder.ts @@ -6,8 +6,8 @@ */ import type { BoolQuery } from '@kbn/es-query'; -import type { NamedAggregation } from '@kbn/securitysolution-grouping'; -import { isNoneGroup, getGroupingQuery } from '@kbn/securitysolution-grouping'; +import type { NamedAggregation } from '@kbn/grouping'; +import { isNoneGroup, getGroupingQuery } from '@kbn/grouping'; import type { RunTimeMappings } from '../../../../common/store/sourcerer/model'; interface AlertsGroupingQueryParams { diff --git a/x-pack/plugins/security_solution/public/detections/components/alerts_table/grouping_settings/types.ts b/x-pack/plugins/security_solution/public/detections/components/alerts_table/grouping_settings/types.ts index d271551f83460f..fcdacf94a0688f 100644 --- a/x-pack/plugins/security_solution/public/detections/components/alerts_table/grouping_settings/types.ts +++ b/x-pack/plugins/security_solution/public/detections/components/alerts_table/grouping_settings/types.ts @@ -5,7 +5,7 @@ * 2.0. */ -import type { GenericBuckets } from '@kbn/securitysolution-grouping/src'; +import type { GenericBuckets } from '@kbn/grouping/src'; // Elasticsearch returns `null` when a sub-aggregation cannot be computed type NumberOrNull = number | null; export interface AlertsGroupingAggregation { diff --git a/x-pack/plugins/security_solution/public/detections/components/alerts_table/timeline_actions/use_add_exception_flyout.tsx b/x-pack/plugins/security_solution/public/detections/components/alerts_table/timeline_actions/use_add_exception_flyout.tsx index 85892f4ba5b536..40c855341a1e9a 100644 --- a/x-pack/plugins/security_solution/public/detections/components/alerts_table/timeline_actions/use_add_exception_flyout.tsx +++ b/x-pack/plugins/security_solution/public/detections/components/alerts_table/timeline_actions/use_add_exception_flyout.tsx @@ -9,6 +9,7 @@ import { useCallback, useState } from 'react'; import type { ExceptionListTypeEnum } from '@kbn/securitysolution-io-ts-list-types'; import type { inputsModel } from '../../../../common/store'; +import { useTourContext } from '../../../../common/components/guided_onboarding_tour'; interface UseExceptionFlyoutProps { refetch?: inputsModel.Refetch; @@ -32,20 +33,26 @@ export const useExceptionFlyout = ({ onRuleChange, isActiveTimelines, }: UseExceptionFlyoutProps): UseExceptionFlyout => { + const { setAllTourStepsHidden } = useTourContext(); const [openAddExceptionFlyout, setOpenAddExceptionFlyout] = useState(false); const [exceptionFlyoutType, setExceptionFlyoutType] = useState( null ); - const onAddExceptionTypeClick = useCallback((exceptionListType?: ExceptionListTypeEnum): void => { - setExceptionFlyoutType(exceptionListType ?? null); - setOpenAddExceptionFlyout(true); - }, []); + const onAddExceptionTypeClick = useCallback( + (exceptionListType?: ExceptionListTypeEnum): void => { + setExceptionFlyoutType(exceptionListType ?? null); + setAllTourStepsHidden(true); + setOpenAddExceptionFlyout(true); + }, + [setAllTourStepsHidden] + ); const onAddExceptionCancel = useCallback(() => { setExceptionFlyoutType(null); + setAllTourStepsHidden(false); setOpenAddExceptionFlyout(false); - }, []); + }, [setAllTourStepsHidden]); const onAddExceptionConfirm = useCallback( (didRuleChange: boolean, didCloseAlert: boolean, didBulkCloseAlert) => { @@ -55,9 +62,10 @@ export const useExceptionFlyout = ({ if (onRuleChange != null && didRuleChange) { onRuleChange(); } + setAllTourStepsHidden(false); setOpenAddExceptionFlyout(false); }, - [onRuleChange, refetch, isActiveTimelines] + [refetch, isActiveTimelines, onRuleChange, setAllTourStepsHidden] ); return { diff --git a/x-pack/plugins/security_solution/public/detections/components/take_action_dropdown/index.test.tsx b/x-pack/plugins/security_solution/public/detections/components/take_action_dropdown/index.test.tsx index ff1ef27195e690..ce5126664fb14e 100644 --- a/x-pack/plugins/security_solution/public/detections/components/take_action_dropdown/index.test.tsx +++ b/x-pack/plugins/security_solution/public/detections/components/take_action_dropdown/index.test.tsx @@ -49,6 +49,7 @@ jest.mock('../user_info', () => ({ })); jest.mock('../../../common/lib/kibana'); +jest.mock('../../../common/components/guided_onboarding_tour/tour_step'); jest.mock('../../containers/detection_engine/alerts/use_alerts_privileges', () => ({ useAlertsPrivileges: jest.fn().mockReturnValue({ hasIndexWrite: true, hasKibanaCRUD: true }), diff --git a/x-pack/plugins/security_solution/public/detections/configurations/security_solution_detections/render_cell_value.test.tsx b/x-pack/plugins/security_solution/public/detections/configurations/security_solution_detections/render_cell_value.test.tsx index 20afb552e045aa..5abe8a3dfbd937 100644 --- a/x-pack/plugins/security_solution/public/detections/configurations/security_solution_detections/render_cell_value.test.tsx +++ b/x-pack/plugins/security_solution/public/detections/configurations/security_solution_detections/render_cell_value.test.tsx @@ -29,6 +29,7 @@ jest.mock('../../../common/containers/sourcerer', () => ({ indicesExist: true, }), })); +jest.mock('../../../common/components/guided_onboarding_tour/tour_step'); describe('RenderCellValue', () => { const columnId = '@timestamp'; @@ -82,4 +83,21 @@ describe('RenderCellValue', () => { expect(wrapper.find(DefaultCellRenderer).props()).toEqual(props); }); + + test('it renders a GuidedOnboardingTourStep', () => { + const RenderCellValue = getRenderCellValueHook({ + scopeId: SourcererScopeName.default, + tableId: TableId.test, + }); + + const wrapper = mount( + + + + + + ); + + expect(wrapper.find('[data-test-subj="GuidedOnboardingTourStep"]').exists()).toEqual(true); + }); }); diff --git a/x-pack/plugins/security_solution/public/detections/configurations/security_solution_detections/render_cell_value.tsx b/x-pack/plugins/security_solution/public/detections/configurations/security_solution_detections/render_cell_value.tsx index 413e9beba6015e..3262e0bee184ef 100644 --- a/x-pack/plugins/security_solution/public/detections/configurations/security_solution_detections/render_cell_value.tsx +++ b/x-pack/plugins/security_solution/public/detections/configurations/security_solution_detections/render_cell_value.tsx @@ -151,27 +151,27 @@ export const RenderCellValue: React.FC ); }, [ - isTourAnchor, - finalData, - browserFieldsByName, header, columnId, + browserFieldsByName, + columnHeaders, ecsData, - linkValues, - rowRenderers, + isTourAnchor, + browserFields, + finalData, + eventId, isDetails, - isExpandable, isDraggable, + isExpandable, isExpanded, + linkValues, + rowIndex, colIndex, - eventId, + rowRenderers, setCellProps, + tableId, truncate, context, - tableId, - browserFields, - rowIndex, - columnHeaders, ]); return columnId === SIGNAL_RULE_NAME_FIELD_NAME && actualSuppressionCount ? ( diff --git a/x-pack/plugins/security_solution/public/detections/hooks/trigger_actions_alert_table/use_persistent_controls.tsx b/x-pack/plugins/security_solution/public/detections/hooks/trigger_actions_alert_table/use_persistent_controls.tsx index f662914c870e96..8acb8f8d672dbb 100644 --- a/x-pack/plugins/security_solution/public/detections/hooks/trigger_actions_alert_table/use_persistent_controls.tsx +++ b/x-pack/plugins/security_solution/public/detections/hooks/trigger_actions_alert_table/use_persistent_controls.tsx @@ -13,8 +13,8 @@ import { dataTableActions, } from '@kbn/securitysolution-data-table'; import type { ViewSelection, TableId } from '@kbn/securitysolution-data-table'; -import { useGetGroupSelectorStateless } from '@kbn/securitysolution-grouping/src/hooks/use_get_group_selector'; -import { getTelemetryEvent } from '@kbn/securitysolution-grouping/src/telemetry/const'; +import { useGetGroupSelectorStateless } from '@kbn/grouping/src/hooks/use_get_group_selector'; +import { getTelemetryEvent } from '@kbn/grouping/src/telemetry/const'; import { groupIdSelector } from '../../../common/store/grouping/selectors'; import { useSourcererDataView } from '../../../common/containers/sourcerer'; import { SourcererScopeName } from '../../../common/store/sourcerer/model'; diff --git a/x-pack/plugins/security_solution/public/flyout/document_details/left/components/related_cases.tsx b/x-pack/plugins/security_solution/public/flyout/document_details/left/components/related_cases.tsx index bef7b15a7a9710..13df33a2deb1bc 100644 --- a/x-pack/plugins/security_solution/public/flyout/document_details/left/components/related_cases.tsx +++ b/x-pack/plugins/security_solution/public/flyout/document_details/left/components/related_cases.tsx @@ -5,7 +5,7 @@ * 2.0. */ -import React from 'react'; +import React, { useMemo } from 'react'; import type { EuiBasicTableColumn } from '@elastic/eui'; import { EuiInMemoryTable } from '@elastic/eui'; import type { RelatedCase } from '@kbn/cases-plugin/common'; @@ -21,7 +21,7 @@ import { ExpandablePanel } from '../../../shared/components/expandable_panel'; const ICON = 'warning'; -const columns: Array> = [ +const getColumns: (data: RelatedCase[]) => Array> = (data) => [ { field: 'title', name: ( @@ -30,13 +30,16 @@ const columns: Array> = [ defaultMessage="Name" /> ), - render: (value: string, caseData: RelatedCase) => ( - - - {caseData.title} - - - ), + render: (value: string, caseData: RelatedCase) => { + const index = data.findIndex((d) => d.id === caseData.id); + return ( + + + {caseData.title} + + + ); + }, }, { field: 'status', @@ -63,6 +66,7 @@ export interface RelatedCasesProps { */ export const RelatedCases: React.FC = ({ eventId }) => { const { loading, error, data, dataCount } = useFetchRelatedCases({ eventId }); + const columns = useMemo(() => getColumns(data), [data]); if (error) { return null; diff --git a/x-pack/plugins/security_solution/public/flyout/document_details/right/components/correlations_overview.test.tsx b/x-pack/plugins/security_solution/public/flyout/document_details/right/components/correlations_overview.test.tsx index bb4fcdc79bb83a..2ae680fc54ba95 100644 --- a/x-pack/plugins/security_solution/public/flyout/document_details/right/components/correlations_overview.test.tsx +++ b/x-pack/plugins/security_solution/public/flyout/document_details/right/components/correlations_overview.test.tsx @@ -40,6 +40,8 @@ import { EXPANDABLE_PANEL_HEADER_TITLE_TEXT_TEST_ID, EXPANDABLE_PANEL_TOGGLE_ICON_TEST_ID, } from '../../../shared/components/test_ids'; +import { useTourContext } from '../../../../common/components/guided_onboarding_tour'; +import { AlertsCasesTourSteps } from '../../../../common/components/guided_onboarding_tour/tour_config'; jest.mock('../../shared/hooks/use_show_related_alerts_by_ancestry'); jest.mock('../../shared/hooks/use_show_related_alerts_by_same_source_event'); @@ -100,11 +102,24 @@ jest.mock('../../../../timelines/containers/use_timeline_data_filters', () => ({ })); const mockUseTimelineDataFilters = useTimelineDataFilters as jest.Mock; +jest.mock('../../../../common/components/guided_onboarding_tour', () => ({ + useTourContext: jest.fn(), +})); + const originalEventId = 'originalEventId'; describe('', () => { beforeAll(() => { jest.mocked(useExpandableFlyoutApi).mockReturnValue(flyoutContextValue); + jest.mocked(useTourContext).mockReturnValue({ + hidden: false, + setAllTourStepsHidden: jest.fn(), + activeStep: AlertsCasesTourSteps.viewCase, + endTourStep: jest.fn(), + incrementStep: jest.fn(), + isTourShown: jest.fn(), + setStep: jest.fn(), + }); mockUseTimelineDataFilters.mockReturnValue({ selectedPatterns: ['index'] }); }); @@ -211,4 +226,24 @@ describe('', () => { }, }); }); + + it('should navigate to the left section Insights tab automatically when active step is "view case"', () => { + render( + + + + + + ); + + expect(flyoutContextValue.openLeftPanel).toHaveBeenCalledWith({ + id: DocumentDetailsLeftPanelKey, + path: { tab: LeftPanelInsightsTab, subTab: CORRELATIONS_TAB_ID }, + params: { + id: panelContextValue.eventId, + indexName: panelContextValue.indexName, + scopeId: panelContextValue.scopeId, + }, + }); + }); }); diff --git a/x-pack/plugins/security_solution/public/flyout/document_details/right/components/correlations_overview.tsx b/x-pack/plugins/security_solution/public/flyout/document_details/right/components/correlations_overview.tsx index e6bf84f039c521..2125eb47316aee 100644 --- a/x-pack/plugins/security_solution/public/flyout/document_details/right/components/correlations_overview.tsx +++ b/x-pack/plugins/security_solution/public/flyout/document_details/right/components/correlations_overview.tsx @@ -6,7 +6,7 @@ */ import { get } from 'lodash'; -import React, { useCallback } from 'react'; +import React, { useCallback, useEffect } from 'react'; import { EuiFlexGroup } from '@elastic/eui'; import { useExpandableFlyoutApi } from '@kbn/expandable-flyout'; import { FormattedMessage } from '@kbn/i18n-react'; @@ -30,6 +30,11 @@ import { LeftPanelInsightsTab } from '../../left'; import { CORRELATIONS_TAB_ID } from '../../left/components/correlations_details'; import { useTimelineDataFilters } from '../../../../timelines/containers/use_timeline_data_filters'; import { isActiveTimeline } from '../../../../helpers'; +import { useTourContext } from '../../../../common/components/guided_onboarding_tour'; +import { + AlertsCasesTourSteps, + SecurityStepId, +} from '../../../../common/components/guided_onboarding_tour/tour_config'; /** * Correlations section under Insights section, overview tab. @@ -40,6 +45,7 @@ export const CorrelationsOverview: React.FC = () => { const { dataAsNestedObject, eventId, indexName, getFieldsData, scopeId, isPreview } = useRightPanelContext(); const { openLeftPanel } = useExpandableFlyoutApi(); + const { isTourShown, activeStep } = useTourContext(); const { selectedPatterns } = useTimelineDataFilters(isActiveTimeline(scopeId)); @@ -58,6 +64,12 @@ export const CorrelationsOverview: React.FC = () => { }); }, [eventId, openLeftPanel, indexName, scopeId]); + useEffect(() => { + if (isTourShown(SecurityStepId.alertsCases) && activeStep === AlertsCasesTourSteps.viewCase) { + goToCorrelationsTab(); + } + }, [activeStep, goToCorrelationsTab, isTourShown]); + const { show: showAlertsByAncestry, documentId } = useShowRelatedAlertsByAncestry({ getFieldsData, dataAsNestedObject, diff --git a/x-pack/plugins/security_solution/public/flyout/document_details/right/components/insights_section.test.tsx b/x-pack/plugins/security_solution/public/flyout/document_details/right/components/insights_section.test.tsx index 81f9c6d54457e0..ebba19bd2d6484 100644 --- a/x-pack/plugins/security_solution/public/flyout/document_details/right/components/insights_section.test.tsx +++ b/x-pack/plugins/security_solution/public/flyout/document_details/right/components/insights_section.test.tsx @@ -29,6 +29,7 @@ import { useAlertPrevalence } from '../../../../common/containers/alerts/use_ale import { useRiskScore } from '../../../../entity_analytics/api/hooks/use_risk_score'; import { useExpandSection } from '../hooks/use_expand_section'; import { useTimelineDataFilters } from '../../../../timelines/containers/use_timeline_data_filters'; +import { useTourContext } from '../../../../common/components/guided_onboarding_tour'; jest.mock('../../../../common/containers/alerts/use_alert_prevalence'); @@ -96,6 +97,11 @@ jest.mock('../hooks/use_fetch_threat_intelligence'); jest.mock('../../shared/hooks/use_prevalence'); +const mockUseTourContext = useTourContext as jest.Mock; +jest.mock('../../../../common/components/guided_onboarding_tour', () => ({ + useTourContext: jest.fn().mockReturnValue({ activeStep: 1, isTourShown: jest.fn(() => true) }), +})); + const renderInsightsSection = (contextValue: RightPanelContext) => render( @@ -163,6 +169,20 @@ describe('', () => { expect(wrapper.getByTestId(INSIGHTS_CONTENT_TEST_ID)).toBeVisible(); }); + it('should render the component expanded if guided onboarding tour is shown', () => { + (useExpandSection as jest.Mock).mockReturnValue(false); + mockUseTourContext.mockReturnValue({ activeStep: 7, isTourShown: jest.fn(() => true) }); + + const contextValue = { + eventId: 'some_Id', + dataFormattedForFieldBrowser: mockDataFormattedForFieldBrowser, + getFieldsData: mockGetFieldsData, + } as unknown as RightPanelContext; + + const wrapper = renderInsightsSection(contextValue); + expect(wrapper.getByTestId(INSIGHTS_CONTENT_TEST_ID)).toBeVisible(); + }); + it('should render all children when event kind is signal', () => { (useExpandSection as jest.Mock).mockReturnValue(true); diff --git a/x-pack/plugins/security_solution/public/flyout/document_details/right/components/insights_section.tsx b/x-pack/plugins/security_solution/public/flyout/document_details/right/components/insights_section.tsx index 831124c5539e25..7ea4b67734ae4a 100644 --- a/x-pack/plugins/security_solution/public/flyout/document_details/right/components/insights_section.tsx +++ b/x-pack/plugins/security_solution/public/flyout/document_details/right/components/insights_section.tsx @@ -18,6 +18,11 @@ import { ExpandableSection } from './expandable_section'; import { useRightPanelContext } from '../context'; import { getField } from '../../shared/utils'; import { EventKind } from '../../shared/constants/event_kinds'; +import { useTourContext } from '../../../../common/components/guided_onboarding_tour'; +import { + AlertsCasesTourSteps, + SecurityStepId, +} from '../../../../common/components/guided_onboarding_tour/tour_config'; const KEY = 'insights'; @@ -28,7 +33,12 @@ export const InsightsSection = memo(() => { const { getFieldsData } = useRightPanelContext(); const eventKind = getField(getFieldsData('event.kind')); - const expanded = useExpandSection({ title: KEY, defaultValue: false }); + const { activeStep, isTourShown } = useTourContext(); + const isGuidedOnboardingTourShown = + isTourShown(SecurityStepId.alertsCases) && activeStep === AlertsCasesTourSteps.viewCase; + + const expanded = + useExpandSection({ title: KEY, defaultValue: false }) || isGuidedOnboardingTourShown; return ( render( @@ -50,10 +56,11 @@ describe('', () => { services: { ...mockedUseKibana.services, storage: storageMock, + cases: mockCasesContract, }, }); (useIsTimelineFlyoutOpen as jest.Mock).mockReturnValue(false); - + (useTourContext as jest.Mock).mockReturnValue({ isTourShown: jest.fn(() => false) }); storageMock.clear(); }); @@ -82,7 +89,19 @@ describe('', () => { expect(queryByText('Next')).not.toBeInTheDocument(); }); - it('should not render tour for non-alerts', () => { + it('should not render tour when guided onboarding tour is active', () => { + (useTourContext as jest.Mock).mockReturnValue({ isTourShown: jest.fn(() => true) }); + const { queryByText, queryByTestId } = renderRightPanelTour({ + ...mockContextValue, + getFieldsData: () => '', + }); + + expect(queryByTestId(`${FLYOUT_TOUR_TEST_ID}-1`)).not.toBeInTheDocument(); + expect(queryByText('Next')).not.toBeInTheDocument(); + }); + + it('should not render tour when case modal is open', () => { + mockUseIsAddToCaseOpen.mockReturnValue(true); const { queryByText, queryByTestId } = renderRightPanelTour({ ...mockContextValue, getFieldsData: () => '', diff --git a/x-pack/plugins/security_solution/public/flyout/document_details/right/components/tour.tsx b/x-pack/plugins/security_solution/public/flyout/document_details/right/components/tour.tsx index 5003139a92577f..9fd7219007dd89 100644 --- a/x-pack/plugins/security_solution/public/flyout/document_details/right/components/tour.tsx +++ b/x-pack/plugins/security_solution/public/flyout/document_details/right/components/tour.tsx @@ -20,18 +20,32 @@ import { } from '../../shared/constants/panel_keys'; import { EventKind } from '../../shared/constants/event_kinds'; import { useIsTimelineFlyoutOpen } from '../../shared/hooks/use_is_timeline_flyout_open'; +import { useTourContext } from '../../../../common/components/guided_onboarding_tour/tour'; +import { SecurityStepId } from '../../../../common/components/guided_onboarding_tour/tour_config'; +import { useKibana } from '../../../../common/lib/kibana'; /** * Guided tour for the right panel in details flyout */ export const RightPanelTour = memo(() => { + const { useIsAddToCaseOpen } = useKibana().services.cases.hooks; + + const casesFlyoutExpanded = useIsAddToCaseOpen(); + + const { isTourShown: isGuidedOnboardingTourShown } = useTourContext(); + const { openLeftPanel, openRightPanel } = useExpandableFlyoutApi(); const { eventId, indexName, scopeId, isPreview, getFieldsData } = useRightPanelContext(); const eventKind = getField(getFieldsData('event.kind')); const isAlert = eventKind === EventKind.signal; const isTimelineFlyoutOpen = useIsTimelineFlyoutOpen(); - const showTour = isAlert && !isPreview && !isTimelineFlyoutOpen; + const showTour = + isAlert && + !isPreview && + !isTimelineFlyoutOpen && + !isGuidedOnboardingTourShown(SecurityStepId.alertsCases) && + !casesFlyoutExpanded; const goToLeftPanel = useCallback(() => { openLeftPanel({ diff --git a/x-pack/plugins/security_solution/public/flyout/document_details/right/header.test.tsx b/x-pack/plugins/security_solution/public/flyout/document_details/right/header.test.tsx new file mode 100644 index 00000000000000..81f8e14b8b981c --- /dev/null +++ b/x-pack/plugins/security_solution/public/flyout/document_details/right/header.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 React from 'react'; + +import { render } from '@testing-library/react'; +import { PanelHeader } from './header'; +import { allThreeTabs } from './hooks/use_tabs'; +import { GuidedOnboardingTourStep } from '../../../common/components/guided_onboarding_tour/tour_step'; +import { useBasicDataFromDetailsData } from '../../../timelines/components/side_panel/event_details/helpers'; + +jest.mock('./context', () => ({ + useRightPanelContext: jest.fn().mockReturnValue({ dataFormattedForFieldBrowser: [] }), +})); +jest.mock('../../../timelines/components/side_panel/event_details/helpers', () => ({ + useBasicDataFromDetailsData: jest.fn(), +})); +jest.mock('../../../common/components/guided_onboarding_tour/tour_step', () => ({ + GuidedOnboardingTourStep: jest.fn().mockReturnValue(
), +})); + +jest.mock('./components/alert_header_title', () => ({ + AlertHeaderTitle: jest.fn().mockReturnValue(
), +})); + +jest.mock('./components/event_header_title', () => ({ + EventHeaderTitle: jest.fn().mockReturnValue(
), +})); + +const mockUseBasicDataFromDetailsData = useBasicDataFromDetailsData as jest.Mock; +const mockGuidedOnboardingTourStep = GuidedOnboardingTourStep as unknown as jest.Mock; + +describe('PanelHeader', () => { + beforeEach(() => { + jest.clearAllMocks(); + }); + + it('should render tab name', () => { + mockUseBasicDataFromDetailsData.mockReturnValue({ isAlert: false }); + const { getByText } = render( + + ); + expect(GuidedOnboardingTourStep).not.toBeCalled(); + expect(getByText('Overview')).toBeInTheDocument(); + }); + + it('should render event header title when isAlert equals false', () => { + mockUseBasicDataFromDetailsData.mockReturnValue({ isAlert: false }); + const { queryByTestId } = render( + + ); + expect(queryByTestId('alert-header')).not.toBeInTheDocument(); + expect(queryByTestId('event-header')).toBeInTheDocument(); + }); + + it('should render alert header title when isAlert equals true', () => { + mockUseBasicDataFromDetailsData.mockReturnValue({ isAlert: true }); + const { queryByTestId } = render( + + ); + expect(queryByTestId('alert-header')).toBeInTheDocument(); + expect(queryByTestId('event-header')).not.toBeInTheDocument(); + }); + + it('should render tab name with guided onboarding tour info', () => { + mockUseBasicDataFromDetailsData.mockReturnValue({ isAlert: true }); + render( + + ); + expect(mockGuidedOnboardingTourStep.mock.calls[0][0].isTourAnchor).toBe(true); + expect(mockGuidedOnboardingTourStep.mock.calls[0][0].step).toBe(3); + expect(mockGuidedOnboardingTourStep.mock.calls[0][0].tourId).toBe('alertsCases'); + }); +}); diff --git a/x-pack/plugins/security_solution/public/flyout/document_details/right/header.tsx b/x-pack/plugins/security_solution/public/flyout/document_details/right/header.tsx index 70c27e18faa23e..b85476d2679fc8 100644 --- a/x-pack/plugins/security_solution/public/flyout/document_details/right/header.tsx +++ b/x-pack/plugins/security_solution/public/flyout/document_details/right/header.tsx @@ -7,7 +7,7 @@ import { EuiSpacer, EuiTab } from '@elastic/eui'; import type { FC } from 'react'; -import React, { memo } from 'react'; +import React, { memo, useMemo } from 'react'; import type { RightPanelPaths } from '.'; import type { RightPanelTabType } from './tabs'; import { FlyoutHeader } from '../../shared/components/flyout_header'; @@ -16,6 +16,12 @@ import { AlertHeaderTitle } from './components/alert_header_title'; import { EventHeaderTitle } from './components/event_header_title'; import { useRightPanelContext } from './context'; import { useBasicDataFromDetailsData } from '../../../timelines/components/side_panel/event_details/helpers'; +import { + AlertsCasesTourSteps, + getTourAnchor, + SecurityStepId, +} from '../../../common/components/guided_onboarding_tour/tour_config'; +import { GuidedOnboardingTourStep } from '../../../common/components/guided_onboarding_tour/tour_step'; export interface PanelHeaderProps { /** @@ -38,16 +44,48 @@ export const PanelHeader: FC = memo( const { dataFormattedForFieldBrowser } = useRightPanelContext(); const { isAlert } = useBasicDataFromDetailsData(dataFormattedForFieldBrowser); const onSelectedTabChanged = (id: RightPanelPaths) => setSelectedTabId(id); - const renderTabs = tabs.map((tab, index) => ( - onSelectedTabChanged(tab.id)} - isSelected={tab.id === selectedTabId} - key={index} - data-test-subj={tab['data-test-subj']} - > - {tab.name} - - )); + + const tourAnchor = useMemo( + () => + isAlert + ? { + 'tour-step': getTourAnchor( + AlertsCasesTourSteps.reviewAlertDetailsFlyout, + SecurityStepId.alertsCases + ), + } + : {}, + [isAlert] + ); + + const renderTabs = tabs.map((tab, index) => + isAlert && tab.id === 'overview' ? ( + + onSelectedTabChanged(tab.id)} + isSelected={tab.id === selectedTabId} + key={index} + data-test-subj={tab['data-test-subj']} + {...tourAnchor} + > + {tab.name} + + + ) : ( + onSelectedTabChanged(tab.id)} + isSelected={tab.id === selectedTabId} + key={index} + data-test-subj={tab['data-test-subj']} + > + {tab.name} + + ) + ); return ( diff --git a/x-pack/plugins/security_solution/public/overview/components/detection_response/cases_table/cases_table.test.tsx b/x-pack/plugins/security_solution/public/overview/components/detection_response/cases_table/cases_table.test.tsx index 9cc2e246d94dad..0f597270077034 100644 --- a/x-pack/plugins/security_solution/public/overview/components/detection_response/cases_table/cases_table.test.tsx +++ b/x-pack/plugins/security_solution/public/overview/components/detection_response/cases_table/cases_table.test.tsx @@ -25,6 +25,8 @@ jest.mock('../../../../common/lib/kibana/hooks', () => { }; }); +jest.mock('../../../../common/components/guided_onboarding_tour/tour_step'); + type UseCaseItemsReturn = ReturnType; const defaultCaseItemsReturn: UseCaseItemsReturn = { items: [], diff --git a/x-pack/plugins/security_solution/public/timelines/components/side_panel/event_details/flyout/footer.test.tsx b/x-pack/plugins/security_solution/public/timelines/components/side_panel/event_details/flyout/footer.test.tsx index b12fdb34e7f394..e48dbe5215f39f 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/side_panel/event_details/flyout/footer.test.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/side_panel/event_details/flyout/footer.test.tsx @@ -96,6 +96,7 @@ jest.mock( } ); jest.mock('../../../../../detections/components/alerts_table/actions'); +jest.mock('../../../../../common/components/guided_onboarding_tour/tour_step'); const defaultProps = { scopeId: TimelineId.test, diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/events/event_column_view.test.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/events/event_column_view.test.tsx index f64f3cf5fd73d5..9d4264dd5b0794 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/events/event_column_view.test.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/events/event_column_view.test.tsx @@ -42,7 +42,7 @@ jest.mock('../../../../../common/components/user_privileges', () => { }), }; }); - +jest.mock('../../../../../common/components/guided_onboarding_tour/tour_step'); jest.mock('../../../../../common/lib/kibana', () => { const originalModule = jest.requireActual('../../../../../common/lib/kibana'); diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/index.test.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/index.test.tsx index 57ba0234f0c78a..eb8aa54168461c 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/index.test.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/index.test.tsx @@ -40,6 +40,7 @@ import type { } from '@hello-pangea/dnd'; jest.mock('../../../../common/hooks/use_app_toasts'); +jest.mock('../../../../common/components/guided_onboarding_tour/tour_step'); jest.mock( '../../../../detections/components/alerts_table/timeline_actions/use_add_to_case_actions' ); diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/api/install_prebuilt_rules_and_timelines/install_prebuilt_rules_and_timelines_route.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/api/install_prebuilt_rules_and_timelines/install_prebuilt_rules_and_timelines_route.ts index 20f1bc962232e9..8140f78e285abf 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/api/install_prebuilt_rules_and_timelines/install_prebuilt_rules_and_timelines_route.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/api/install_prebuilt_rules_and_timelines/install_prebuilt_rules_and_timelines_route.ts @@ -85,6 +85,7 @@ export const createPrepackagedRules = async ( const savedObjectsClient = context.core.savedObjects.client; const siemClient = context.getAppClient(); const exceptionsListClient = context.getExceptionListClient() ?? exceptionsClient; + const rulesManagementClient = context.getRulesManagementClient(); const ruleAssetsClient = createPrebuiltRuleAssetsClient(savedObjectsClient); if (!siemClient || !rulesClient) { @@ -106,14 +107,14 @@ export const createPrepackagedRules = async ( const rulesToInstall = getRulesToInstall(latestPrebuiltRules, installedPrebuiltRules); const rulesToUpdate = getRulesToUpdate(latestPrebuiltRules, installedPrebuiltRules); - const result = await createPrebuiltRules(rulesClient, rulesToInstall); + const result = await createPrebuiltRules(rulesManagementClient, rulesToInstall); if (result.errors.length > 0) { throw new AggregateError(result.errors, 'Error installing new prebuilt rules'); } const { result: timelinesResult } = await performTimelinesInstallation(context); - await upgradePrebuiltRules(rulesClient, rulesToUpdate); + await upgradePrebuiltRules(rulesManagementClient, rulesToUpdate); const prebuiltRulesOutput: InstallPrebuiltRulesAndTimelinesResponse = { rules_installed: rulesToInstall.length, diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/api/perform_rule_installation/perform_rule_installation_route.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/api/perform_rule_installation/perform_rule_installation_route.ts index d76c6d93fc8520..61e928a92a39dd 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/api/perform_rule_installation/perform_rule_installation_route.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/api/perform_rule_installation/perform_rule_installation_route.ts @@ -59,6 +59,7 @@ export const performRuleInstallationRoute = (router: SecuritySolutionPluginRoute const config = ctx.securitySolution.getConfig(); const soClient = ctx.core.savedObjects.client; const rulesClient = ctx.alerting.getRulesClient(); + const rulesManagementClient = ctx.securitySolution.getRulesManagementClient(); const ruleAssetsClient = createPrebuiltRuleAssetsClient(soClient); const ruleObjectsClient = createPrebuiltRuleObjectsClient(rulesClient); const exceptionsListClient = ctx.securitySolution.getExceptionListClient(); @@ -109,7 +110,7 @@ export const performRuleInstallationRoute = (router: SecuritySolutionPluginRoute } const { results: installedRules, errors: installationErrors } = await createPrebuiltRules( - rulesClient, + rulesManagementClient, installableRules ); const ruleErrors = [...fetchErrors, ...installationErrors]; diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/api/perform_rule_upgrade/perform_rule_upgrade_route.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/api/perform_rule_upgrade/perform_rule_upgrade_route.ts index 09342a9150a50b..22951de09689ca 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/api/perform_rule_upgrade/perform_rule_upgrade_route.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/api/perform_rule_upgrade/perform_rule_upgrade_route.ts @@ -60,6 +60,7 @@ export const performRuleUpgradeRoute = (router: SecuritySolutionPluginRouter) => const ctx = await context.resolve(['core', 'alerting', 'securitySolution']); const soClient = ctx.core.savedObjects.client; const rulesClient = ctx.alerting.getRulesClient(); + const rulesManagementClient = ctx.securitySolution.getRulesManagementClient(); const ruleAssetsClient = createPrebuiltRuleAssetsClient(soClient); const ruleObjectsClient = createPrebuiltRuleObjectsClient(rulesClient); @@ -156,7 +157,7 @@ export const performRuleUpgradeRoute = (router: SecuritySolutionPluginRouter) => // Perform the upgrade const { results: updatedRules, errors: installationErrors } = await upgradePrebuiltRules( - rulesClient, + rulesManagementClient, targetRules ); const ruleErrors = [...fetchErrors, ...installationErrors]; diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/logic/rule_objects/create_prebuilt_rules.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/logic/rule_objects/create_prebuilt_rules.ts index 54b4361bf868bd..9f65149e7c46e5 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/logic/rule_objects/create_prebuilt_rules.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/logic/rule_objects/create_prebuilt_rules.ts @@ -5,27 +5,27 @@ * 2.0. */ -import type { RulesClient } from '@kbn/alerting-plugin/server'; import { MAX_RULES_TO_UPDATE_IN_PARALLEL } from '../../../../../../common/constants'; import { initPromisePool } from '../../../../../utils/promise_pool'; import { withSecuritySpan } from '../../../../../utils/with_security_span'; -import { createRules } from '../../../rule_management/logic/crud/create_rules'; import type { PrebuiltRuleAsset } from '../../model/rule_assets/prebuilt_rule_asset'; +import type { IRulesManagementClient } from '../../../rule_management/logic/rule_management/rules_management_client'; -export const createPrebuiltRules = (rulesClient: RulesClient, rules: PrebuiltRuleAsset[]) => - withSecuritySpan('createPrebuiltRules', async () => { +export const createPrebuiltRules = ( + rulesManagementClient: IRulesManagementClient, + rules: PrebuiltRuleAsset[] +) => { + return withSecuritySpan('createPrebuiltRules', async () => { const result = await initPromisePool({ concurrency: MAX_RULES_TO_UPDATE_IN_PARALLEL, items: rules, executor: async (rule) => { - return createRules({ - rulesClient, - params: rule, - immutable: true, - defaultEnabled: false, + return rulesManagementClient.createPrebuiltRule({ + ruleAsset: rule, }); }, }); return result; }); +}; diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/logic/rule_objects/upgrade_prebuilt_rules.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/logic/rule_objects/upgrade_prebuilt_rules.test.ts deleted file mode 100644 index f4845be99c68ec..00000000000000 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/logic/rule_objects/upgrade_prebuilt_rules.test.ts +++ /dev/null @@ -1,240 +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 { omit } from 'lodash'; -import { rulesClientMock } from '@kbn/alerting-plugin/server/mocks'; -import { - getRuleMock, - getFindResultWithSingleHit, - getFindResultWithMultiHits, -} from '../../../routes/__mocks__/request_responses'; -import { upgradePrebuiltRules } from './upgrade_prebuilt_rules'; -import { patchRules } from '../../../rule_management/logic/crud/patch_rules'; -import { createRules } from '../../../rule_management/logic/crud/create_rules'; -import { deleteRules } from '../../../rule_management/logic/crud/delete_rules'; -import { getPrebuiltRuleMock, getPrebuiltThreatMatchRuleMock } from '../../mocks'; -import { getQueryRuleParams, getThreatRuleParams } from '../../../rule_schema/mocks'; - -jest.mock('../../../rule_management/logic/crud/patch_rules'); -jest.mock('../../../rule_management/logic/crud/create_rules'); -jest.mock('../../../rule_management/logic/crud/delete_rules'); - -describe('updatePrebuiltRules', () => { - let rulesClient: ReturnType; - - beforeEach(() => { - rulesClient = rulesClientMock.create(); - }); - - describe('when upgrading a prebuilt rule to a newer version with the same rule type', () => { - const prepackagedRule = getPrebuiltRuleMock({ - rule_id: 'rule-to-upgrade', - }); - - beforeEach(() => { - const installedRule = getRuleMock( - getQueryRuleParams({ - ruleId: 'rule-to-upgrade', - }) - ); - - rulesClient.find.mockResolvedValue( - getFindResultWithMultiHits({ - data: [installedRule], - }) - ); - }); - - it('patches existing rule with incoming version data', async () => { - await upgradePrebuiltRules(rulesClient, [prepackagedRule]); - - expect(patchRules).toHaveBeenCalledWith( - expect.objectContaining({ - nextParams: expect.objectContaining(prepackagedRule), - }) - ); - }); - - it('makes sure enabled state is preserved', async () => { - await upgradePrebuiltRules(rulesClient, [prepackagedRule]); - - expect(patchRules).toHaveBeenCalledWith( - expect.objectContaining({ - nextParams: expect.objectContaining({ - enabled: undefined, - }), - }) - ); - }); - }); - - describe('when upgrading a prebuilt rule to a newer version with a different rule type', () => { - const prepackagedRule = getPrebuiltRuleMock({ - rule_id: 'rule-to-upgrade', - type: 'eql', - language: 'eql', - query: 'host where host.name == "something"', - }); - const actions = [ - { - group: 'group', - id: 'id', - actionTypeId: 'action_type_id', - params: {}, - }, - ]; - const installedRule = getRuleMock( - getQueryRuleParams({ - ruleId: 'rule-to-upgrade', - type: 'query', - exceptionsList: [ - { - id: 'exception_list_1', - list_id: 'exception_list_1', - namespace_type: 'agnostic', - type: 'rule_default', - }, - ], - timelineId: 'some-timeline-id', - timelineTitle: 'Some timeline title', - }), - { - id: 'installed-rule-so-id', - actions, - } - ); - - beforeEach(() => { - rulesClient.find.mockResolvedValue( - getFindResultWithMultiHits({ - data: [installedRule], - }) - ); - }); - - it('deletes rule before creation', async () => { - let lastCalled!: string; - - (deleteRules as jest.Mock).mockImplementation(() => (lastCalled = 'deleteRules')); - (createRules as jest.Mock).mockImplementation(() => (lastCalled = 'createRules')); - - await upgradePrebuiltRules(rulesClient, [prepackagedRule]); - - expect(deleteRules).toHaveBeenCalledTimes(1); - expect(createRules).toHaveBeenCalledTimes(1); - expect(lastCalled).toBe('createRules'); - }); - - it('recreates a rule with incoming version data', async () => { - await upgradePrebuiltRules(rulesClient, [prepackagedRule]); - - expect(createRules).toHaveBeenCalledWith( - expect.objectContaining({ - immutable: true, - params: expect.objectContaining(prepackagedRule), - }) - ); - }); - - it('restores saved object id', async () => { - await upgradePrebuiltRules(rulesClient, [prepackagedRule]); - - expect(createRules).toHaveBeenCalledWith( - expect.objectContaining({ - id: 'installed-rule-so-id', - }) - ); - }); - - it('restores enabled state', async () => { - await upgradePrebuiltRules(rulesClient, [prepackagedRule]); - - expect(createRules).toHaveBeenCalledWith( - expect.objectContaining({ - params: expect.objectContaining({ enabled: installedRule.enabled }), - }) - ); - }); - - it('restores actions', async () => { - await upgradePrebuiltRules(rulesClient, [prepackagedRule]); - - expect(createRules).toHaveBeenCalledWith( - expect.objectContaining({ - params: expect.objectContaining({ - actions: actions.map((a) => ({ - ...omit(a, 'actionTypeId'), - action_type_id: a.actionTypeId, - })), - }), - }) - ); - }); - - it('restores exceptions list', async () => { - await upgradePrebuiltRules(rulesClient, [prepackagedRule]); - - expect(createRules).toHaveBeenCalledWith( - expect.objectContaining({ - params: expect.objectContaining({ exceptions_list: installedRule.params.exceptionsList }), - }) - ); - }); - - it('restores timeline reference', async () => { - await upgradePrebuiltRules(rulesClient, [prepackagedRule]); - - expect(createRules).toHaveBeenCalledWith( - expect.objectContaining({ - params: expect.objectContaining({ - timeline_id: installedRule.params.timelineId, - timeline_title: installedRule.params.timelineTitle, - }), - }) - ); - }); - }); - - it('should update threat match rules', async () => { - const updatedThreatParams = { - threat_index: ['test-index'], - threat_indicator_path: 'test.path', - threat_query: 'threat:*', - }; - const prepackagedRule = getPrebuiltThreatMatchRuleMock(); - rulesClient.find.mockResolvedValue({ - ...getFindResultWithSingleHit(), - data: [getRuleMock(getThreatRuleParams())], - }); - - await upgradePrebuiltRules(rulesClient, [{ ...prepackagedRule, ...updatedThreatParams }]); - - expect(patchRules).toHaveBeenCalledWith( - expect.objectContaining({ - nextParams: expect.objectContaining({ - threat_indicator_path: 'test.path', - }), - }) - ); - - expect(patchRules).toHaveBeenCalledWith( - expect.objectContaining({ - nextParams: expect.objectContaining({ - threat_index: ['test-index'], - }), - }) - ); - - expect(patchRules).toHaveBeenCalledWith( - expect.objectContaining({ - nextParams: expect.objectContaining({ - threat_query: 'threat:*', - }), - }) - ); - }); -}); diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/logic/rule_objects/upgrade_prebuilt_rules.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/logic/rule_objects/upgrade_prebuilt_rules.ts index 98afa86114e4fe..a6ff9b0dea9c3b 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/logic/rule_objects/upgrade_prebuilt_rules.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/logic/rule_objects/upgrade_prebuilt_rules.ts @@ -5,106 +5,31 @@ * 2.0. */ -import type { SanitizedRule } from '@kbn/alerting-plugin/common'; -import type { RulesClient } from '@kbn/alerting-plugin/server'; import { MAX_RULES_TO_UPDATE_IN_PARALLEL } from '../../../../../../common/constants'; -import { transformAlertToRuleAction } from '../../../../../../common/detection_engine/transform_actions'; import { initPromisePool } from '../../../../../utils/promise_pool'; import { withSecuritySpan } from '../../../../../utils/with_security_span'; -import { createRules } from '../../../rule_management/logic/crud/create_rules'; -import { deleteRules } from '../../../rule_management/logic/crud/delete_rules'; -import { patchRules } from '../../../rule_management/logic/crud/patch_rules'; -import { readRules } from '../../../rule_management/logic/crud/read_rules'; -import type { RuleParams } from '../../../rule_schema'; -import { PrepackagedRulesError } from '../../api/install_prebuilt_rules_and_timelines/install_prebuilt_rules_and_timelines_route'; import type { PrebuiltRuleAsset } from '../../model/rule_assets/prebuilt_rule_asset'; +import type { IRulesManagementClient } from '../../../rule_management/logic/rule_management/rules_management_client'; /** * Upgrades existing prebuilt rules given a set of rules and output index. * This implements a chunked approach to not saturate network connections and * avoid being a "noisy neighbor". - * @param rulesClient Alerting client + * @param rulesManagementClient RulesManagementClient * @param rules The rules to apply the update for */ -export const upgradePrebuiltRules = async (rulesClient: RulesClient, rules: PrebuiltRuleAsset[]) => +export const upgradePrebuiltRules = async ( + rulesManagementClient: IRulesManagementClient, + rules: PrebuiltRuleAsset[] +) => withSecuritySpan('upgradePrebuiltRules', async () => { const result = await initPromisePool({ concurrency: MAX_RULES_TO_UPDATE_IN_PARALLEL, items: rules, executor: async (rule) => { - return upgradeRule(rulesClient, rule); + return rulesManagementClient.upgradePrebuiltRule({ ruleAsset: rule }); }, }); return result; }); - -/** - * Upgrades a rule - * - * @param rulesClient Alerting client - * @param rule The rule to apply the update for - * @returns Promise of what was updated. - */ -const upgradeRule = async ( - rulesClient: RulesClient, - rule: PrebuiltRuleAsset -): Promise> => { - const existingRule = await readRules({ - rulesClient, - ruleId: rule.rule_id, - id: undefined, - }); - - if (!existingRule) { - throw new PrepackagedRulesError(`Failed to find rule ${rule.rule_id}`, 500); - } - - // If we're trying to change the type of a prepackaged rule, we need to delete the old one - // and replace it with the new rule, keeping the enabled setting, actions, throttle, id, - // and exception lists from the old rule - if (rule.type !== existingRule.params.type) { - await deleteRules({ - ruleId: existingRule.id, - rulesClient, - }); - - return createRules({ - rulesClient, - immutable: true, - id: existingRule.id, - params: { - ...rule, - // Force the prepackaged rule to use the enabled state from the existing rule, - // regardless of what the prepackaged rule says - enabled: existingRule.enabled, - exceptions_list: existingRule.params.exceptionsList, - actions: existingRule.actions.map(transformAlertToRuleAction), - timeline_id: existingRule.params.timelineId, - timeline_title: existingRule.params.timelineTitle, - }, - }); - } - - await patchRules({ - rulesClient, - existingRule, - nextParams: { - ...rule, - // Force enabled to use the enabled state from the existing rule by passing in undefined to patchRules - enabled: undefined, - }, - }); - - const updatedRule = await readRules({ - rulesClient, - ruleId: rule.rule_id, - id: undefined, - }); - - if (!updatedRule) { - throw new PrepackagedRulesError(`Rule ${rule.rule_id} not found after upgrade`, 500); - } - - return updatedRule; -}; diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/__mocks__/request_context.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/__mocks__/request_context.ts index 0893c7297763cc..ebc18daba1c9f9 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/__mocks__/request_context.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/__mocks__/request_context.ts @@ -38,6 +38,7 @@ import { riskEngineDataClientMock } from '../../../entity_analytics/risk_engine/ import { riskScoreDataClientMock } from '../../../entity_analytics/risk_score/risk_score_data_client.mock'; import { assetCriticalityDataClientMock } from '../../../entity_analytics/asset_criticality/asset_criticality_data_client.mock'; import { auditLoggerMock } from '@kbn/security-plugin/server/audit/mocks'; +import { rulesManagementClientMock } from '../../rule_management/logic/rule_management/__mocks__/rules_management_client'; export const createMockClients = () => { const core = coreMock.createRequestHandlerContext(); @@ -57,6 +58,7 @@ export const createMockClients = () => { exceptionListClient: listMock.getExceptionListClient(core.savedObjects.client), }, rulesClient: rulesClientMock.create(), + rulesManagementClient: rulesManagementClientMock.create(), actionsClient: actionsClientMock.create(), ruleDataService: ruleRegistryMocks.createRuleDataService(), @@ -140,6 +142,7 @@ const createSecuritySolutionRequestContextMock = ( }), getSpaceId: jest.fn(() => 'default'), getRuleDataService: jest.fn(() => clients.ruleDataService), + getRulesManagementClient: jest.fn(() => clients.rulesManagementClient), getDetectionEngineHealthClient: jest.fn(() => clients.detectionEngineHealthClient), getRuleExecutionLog: jest.fn(() => clients.ruleExecutionLog), getExceptionListClient: jest.fn(() => clients.lists.exceptionListClient), diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_exceptions/api/create_rule_exceptions/route.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_exceptions/api/create_rule_exceptions/route.ts index 5f5b29abdf38e4..0334ded224f437 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_exceptions/api/create_rule_exceptions/route.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_exceptions/api/create_rule_exceptions/route.ts @@ -26,7 +26,6 @@ import { import { formatErrors, validate } from '@kbn/securitysolution-io-ts-utils'; import type { SanitizedRule } from '@kbn/alerting-plugin/common'; import type { ExceptionListClient } from '@kbn/lists-plugin/server'; -import type { RulesClient } from '@kbn/alerting-plugin/server'; import type { CreateRuleExceptionsRequestBodyDecoded, @@ -38,13 +37,13 @@ import { CreateRuleExceptionsRequestParams, } from '../../../../../../common/api/detection_engine/rule_exceptions'; -import { readRules } from '../../../rule_management/logic/crud/read_rules'; -import { patchRules } from '../../../rule_management/logic/crud/patch_rules'; +import { readRules } from '../../../rule_management/logic/rule_management/read_rules'; import { checkDefaultRuleExceptionListReferences } from '../../../rule_management/logic/exceptions/check_for_default_rule_exception_list'; import type { RuleParams } from '../../../rule_schema'; import type { SecuritySolutionPluginRouter } from '../../../../../types'; import { buildSiemResponse } from '../../../routes/utils'; import { buildRouteValidation } from '../../../../../utils/build_validation/route_validation'; +import type { IRulesManagementClient } from '../../../rule_management/logic/rule_management/rules_management_client'; export const createRuleExceptionsRoute = (router: SecuritySolutionPluginRouter) => { router.versioned @@ -84,6 +83,7 @@ export const createRuleExceptionsRoute = (router: SecuritySolutionPluginRouter) ]); const rulesClient = ctx.alerting.getRulesClient(); const listsClient = ctx.securitySolution.getExceptionListClient(); + const rulesManagementClient = ctx.securitySolution.getRulesManagementClient(); const { items } = request.body; const { id: ruleId } = request.params; @@ -106,7 +106,7 @@ export const createRuleExceptionsRoute = (router: SecuritySolutionPluginRouter) items, rule, listsClient, - rulesClient, + rulesManagementClient, }); const [validated, errors] = validate(createdItems, t.array(exceptionListItemSchema)); @@ -130,11 +130,11 @@ export const createRuleExceptions = async ({ items, rule, listsClient, - rulesClient, + rulesManagementClient, }: { items: CreateRuleExceptionListItemSchemaDecoded[]; listsClient: ExceptionListClient | null; - rulesClient: RulesClient; + rulesManagementClient: IRulesManagementClient; rule: SanitizedRule; }) => { const ruleDefaultLists = rule.params.exceptionsList.filter( @@ -168,8 +168,8 @@ export const createRuleExceptions = async ({ // and update the rule's exceptions lists to include newly created default list. const defaultList = await createAndAssociateDefaultExceptionList({ rule, - rulesClient, listsClient, + rulesManagementClient, removeOldAssociation: true, }); @@ -178,8 +178,8 @@ export const createRuleExceptions = async ({ } else { const defaultList = await createAndAssociateDefaultExceptionList({ rule, - rulesClient, listsClient, + rulesManagementClient, removeOldAssociation: false, }); @@ -272,12 +272,12 @@ export const createExceptionList = async ({ export const createAndAssociateDefaultExceptionList = async ({ rule, listsClient, - rulesClient, + rulesManagementClient, removeOldAssociation, }: { rule: SanitizedRule; listsClient: ExceptionListClient | null; - rulesClient: RulesClient; + rulesManagementClient: IRulesManagementClient; removeOldAssociation: boolean; }): Promise => { const exceptionListToAssociate = await createExceptionList({ rule, listsClient }); @@ -294,10 +294,9 @@ export const createAndAssociateDefaultExceptionList = async ({ ? existingRuleExceptionLists.filter((list) => list.type !== ExceptionListTypeEnum.RULE_DEFAULT) : existingRuleExceptionLists; - await patchRules({ - rulesClient, - existingRule: rule, + await rulesManagementClient.patchRule({ nextParams: { + rule_id: rule.params.ruleId, ...rule.params, exceptions_list: [ ...ruleExceptionLists, diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/api/register_routes.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/api/register_routes.ts index 75414a8cd08ef5..e6999cc9e429ed 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/api/register_routes.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/api/register_routes.ts @@ -34,16 +34,16 @@ export const registerRuleManagementRoutes = ( logger: Logger ) => { // Rules CRUD - createRuleRoute(router, ml); + createRuleRoute(router); readRuleRoute(router, logger); - updateRuleRoute(router, ml); - patchRuleRoute(router, ml); + updateRuleRoute(router); + patchRuleRoute(router); deleteRuleRoute(router); // Rules bulk CRUD - bulkCreateRulesRoute(router, ml, logger); - bulkUpdateRulesRoute(router, ml, logger); - bulkPatchRulesRoute(router, ml, logger); + bulkCreateRulesRoute(router, logger); + bulkUpdateRulesRoute(router, logger); + bulkPatchRulesRoute(router, logger); bulkDeleteRulesRoute(router, logger); // Rules bulk actions @@ -51,7 +51,7 @@ export const registerRuleManagementRoutes = ( // Rules export/import exportRulesRoute(router, config, logger); - importRulesRoute(router, config, ml); + importRulesRoute(router, config); // Rules search findRulesRoute(router, logger); diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/api/rules/bulk_actions/fetch_rules_by_query_or_ids.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/api/rules/bulk_actions/fetch_rules_by_query_or_ids.ts index 2a07ecc95509bc..1ef231bb0b5ad9 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/api/rules/bulk_actions/fetch_rules_by_query_or_ids.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/api/rules/bulk_actions/fetch_rules_by_query_or_ids.ts @@ -11,7 +11,7 @@ import { MAX_RULES_TO_UPDATE_IN_PARALLEL } from '../../../../../../../common/con import type { PromisePoolOutcome } from '../../../../../../utils/promise_pool'; import { initPromisePool } from '../../../../../../utils/promise_pool'; import type { RuleAlertType } from '../../../../rule_schema'; -import { readRules } from '../../../logic/crud/read_rules'; +import { readRules } from '../../../logic/rule_management/read_rules'; import { findRules } from '../../../logic/search/find_rules'; import { MAX_RULES_TO_PROCESS_TOTAL } from './route'; diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/api/rules/bulk_actions/route.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/api/rules/bulk_actions/route.test.ts index 0b7710db264252..d74afe0bea65e3 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/api/rules/bulk_actions/route.test.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/api/rules/bulk_actions/route.test.ts @@ -30,10 +30,10 @@ import { getBulkDisableRuleActionSchemaMock, } from '../../../../../../../common/api/detection_engine/rule_management/mocks'; import { loggingSystemMock } from '@kbn/core/server/mocks'; -import { readRules } from '../../../logic/crud/read_rules'; +import { readRules } from '../../../logic/rule_management/read_rules'; jest.mock('../../../../../machine_learning/authz'); -jest.mock('../../../logic/crud/read_rules', () => ({ readRules: jest.fn() })); +jest.mock('../../../logic/rule_management/read_rules', () => ({ readRules: jest.fn() })); describe('Perform bulk action route', () => { const readRulesMock = readRules as jest.Mock; 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 ae0298ada97c16..f5932285a8e330 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 @@ -35,7 +35,6 @@ import { dryRunValidateBulkEditRule, validateBulkDuplicateRule, } from '../../../logic/bulk_actions/validations'; -import { deleteRules } from '../../../logic/crud/delete_rules'; import { getExportByObjectIds } from '../../../logic/export/get_export_by_object_ids'; import { RULE_MANAGEMENT_BULK_ACTION_SOCKET_TIMEOUT_MS } from '../../timeouts'; import type { BulkActionError } from './bulk_actions_response'; @@ -121,6 +120,7 @@ export const performBulkActionRoute = ( const exceptionsClient = ctx.lists?.getExceptionListClient(); const savedObjectsClient = ctx.core.savedObjects.client; const actionsClient = ctx.actions.getActionsClient(); + const rulesManagementClient = ctx.securitySolution.getRulesManagementClient(); const { getExporter, getClient } = ctx.core.savedObjects; const client = getClient({ includedHiddenTypes: ['action'] }); @@ -203,9 +203,8 @@ export const performBulkActionRoute = ( return null; } - await deleteRules({ + await rulesManagementClient.deleteRule({ ruleId: rule.id, - rulesClient, }); return null; diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/api/rules/bulk_create_rules/route.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/api/rules/bulk_create_rules/route.test.ts index 59e875a2616777..8c73c4334dde6c 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/api/rules/bulk_create_rules/route.test.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/api/rules/bulk_create_rules/route.test.ts @@ -6,8 +6,6 @@ */ import { DETECTION_ENGINE_RULES_BULK_CREATE } from '../../../../../../../common/constants'; -import { mlServicesMock } from '../../../../../machine_learning/mocks'; -import { buildMlAuthz } from '../../../../../machine_learning/authz'; import { getReadBulkRequest, getFindResultWithSingleHit, @@ -23,27 +21,26 @@ import { getCreateRulesSchemaMock } from '../../../../../../../common/api/detect import { elasticsearchClientMock } from '@kbn/core-elasticsearch-client-server-mocks'; import { getQueryRuleParams } from '../../../../rule_schema/mocks'; import { loggingSystemMock } from '@kbn/core/server/mocks'; - -jest.mock('../../../../../machine_learning/authz'); +import { HttpAuthzError } from '../../../../../machine_learning/validation'; describe('Bulk create rules route', () => { let server: ReturnType; let { clients, context } = requestContextMock.createTools(); - let ml: ReturnType; beforeEach(() => { server = serverMock.create(); ({ clients, context } = requestContextMock.createTools()); - ml = mlServicesMock.createSetupContract(); const logger = loggingSystemMock.createLogger(); clients.rulesClient.find.mockResolvedValue(getEmptyFindResult()); // no existing rules clients.rulesClient.create.mockResolvedValue(getRuleMock(getQueryRuleParams())); // successful creation - + clients.rulesManagementClient.createCustomRule.mockResolvedValue( + getRuleMock(getQueryRuleParams()) + ); context.core.elasticsearch.client.asCurrentUser.search.mockResolvedValue( elasticsearchClientMock.createSuccessTransportRequestPromise(getBasicEmptySearchResponse()) ); - bulkCreateRulesRoute(server.router, ml, logger); + bulkCreateRulesRoute(server.router, logger); }); describe('status codes', () => { @@ -58,10 +55,8 @@ describe('Bulk create rules route', () => { describe('unhappy paths', () => { test('returns a 403 error object if ML Authz fails', async () => { - (buildMlAuthz as jest.Mock).mockReturnValueOnce({ - validateRuleType: jest - .fn() - .mockResolvedValue({ valid: false, message: 'mocked validation message' }), + clients.rulesManagementClient.createCustomRule.mockImplementationOnce(async () => { + throw new HttpAuthzError('mocked validation message'); }); const response = await server.inject( @@ -113,7 +108,7 @@ describe('Bulk create rules route', () => { }); test('catches error if creation throws', async () => { - clients.rulesClient.create.mockImplementation(async () => { + clients.rulesManagementClient.createCustomRule.mockImplementation(async () => { throw new Error('Test error'); }); const response = await server.inject( diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/api/rules/bulk_create_rules/route.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/api/rules/bulk_create_rules/route.ts index abd14b7fbb4a94..ee9be34e14e987 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/api/rules/bulk_create_rules/route.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/api/rules/bulk_create_rules/route.ts @@ -16,11 +16,7 @@ import { } from '../../../../../../../common/api/detection_engine/rule_management'; import type { SecuritySolutionPluginRouter } from '../../../../../../types'; -import type { SetupPlugins } from '../../../../../../plugin'; -import { buildMlAuthz } from '../../../../../machine_learning/authz'; -import { throwAuthzError } from '../../../../../machine_learning/validation'; -import { createRules } from '../../../logic/crud/create_rules'; -import { readRules } from '../../../logic/crud/read_rules'; +import { readRules } from '../../../logic/rule_management/read_rules'; import { getDuplicates } from './get_duplicates'; import { transformValidateBulkError } from '../../../utils/validate'; import { buildRouteValidationWithZod } from '../../../../../../utils/build_validation/route_validation'; @@ -37,11 +33,7 @@ import { getDeprecatedBulkEndpointHeader, logDeprecatedBulkEndpoint } from '../. /** * @deprecated since version 8.2.0. Use the detection_engine/rules/_bulk_action API instead */ -export const bulkCreateRulesRoute = ( - router: SecuritySolutionPluginRouter, - ml: SetupPlugins['ml'], - logger: Logger -) => { +export const bulkCreateRulesRoute = (router: SecuritySolutionPluginRouter, logger: Logger) => { router.versioned .post({ access: 'public', @@ -69,16 +61,8 @@ export const bulkCreateRulesRoute = ( try { const ctx = await context.resolve(['core', 'securitySolution', 'licensing', 'alerting']); - const rulesClient = ctx.alerting.getRulesClient(); - const savedObjectsClient = ctx.core.savedObjects.client; - - const mlAuthz = buildMlAuthz({ - license: ctx.licensing.license, - ml, - request, - savedObjectsClient, - }); + const rulesManagementClient = ctx.securitySolution.getRulesManagementClient(); const ruleDefinitions = request.body; const dupes = getDuplicates(ruleDefinitions, 'rule_id'); @@ -125,10 +109,7 @@ export const bulkCreateRulesRoute = ( }); } - throwAuthzError(await mlAuthz.validateRuleType(payloadRule.type)); - - const createdRule = await createRules({ - rulesClient, + const createdRule = await rulesManagementClient.createCustomRule({ params: payloadRule, }); diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/api/rules/bulk_delete_rules/route.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/api/rules/bulk_delete_rules/route.ts index 98c938f43a7bb0..a8c3b3d96060e4 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/api/rules/bulk_delete_rules/route.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/api/rules/bulk_delete_rules/route.ts @@ -24,8 +24,7 @@ import { createBulkErrorObject, transformBulkError, } from '../../../../routes/utils'; -import { deleteRules } from '../../../logic/crud/delete_rules'; -import { readRules } from '../../../logic/crud/read_rules'; +import { readRules } from '../../../logic/rule_management/read_rules'; import { getIdBulkError } from '../../../utils/utils'; import { transformValidateBulkError } from '../../../utils/validate'; import { getDeprecatedBulkEndpointHeader, logDeprecatedBulkEndpoint } from '../../deprecation'; @@ -56,6 +55,7 @@ export const bulkDeleteRulesRoute = (router: SecuritySolutionPluginRouter, logge const ctx = await context.resolve(['core', 'securitySolution', 'alerting']); const rulesClient = ctx.alerting.getRulesClient(); + const rulesManagementClient = ctx.securitySolution.getRulesManagementClient(); const rules = await Promise.all( request.body.map(async (payloadRule) => { @@ -76,9 +76,8 @@ export const bulkDeleteRulesRoute = (router: SecuritySolutionPluginRouter, logge return getIdBulkError({ id, ruleId }); } - await deleteRules({ + await rulesManagementClient.deleteRule({ ruleId: rule.id, - rulesClient, }); return transformValidateBulkError(idOrRuleIdOrUnknown, rule); diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/api/rules/bulk_patch_rules/route.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/api/rules/bulk_patch_rules/route.test.ts index ca3cde890b738d..b5644237d8f261 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/api/rules/bulk_patch_rules/route.test.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/api/rules/bulk_patch_rules/route.test.ts @@ -9,8 +9,6 @@ import { DETECTION_ENGINE_RULES_BULK_UPDATE, DETECTION_ENGINE_RULES_URL, } from '../../../../../../../common/constants'; -import { mlServicesMock } from '../../../../../machine_learning/mocks'; -import { buildMlAuthz } from '../../../../../machine_learning/authz'; import { getEmptyFindResult, getFindResultWithSingleHit, @@ -23,24 +21,22 @@ import { bulkPatchRulesRoute } from './route'; import { getCreateRulesSchemaMock } from '../../../../../../../common/api/detection_engine/model/rule_schema/mocks'; import { getMlRuleParams, getQueryRuleParams } from '../../../../rule_schema/mocks'; import { loggingSystemMock } from '@kbn/core/server/mocks'; - -jest.mock('../../../../../machine_learning/authz'); +import { HttpAuthzError } from '../../../../../machine_learning/validation'; describe('Bulk patch rules route', () => { let server: ReturnType; let { clients, context } = requestContextMock.createTools(); - let ml: ReturnType; beforeEach(() => { server = serverMock.create(); ({ clients, context } = requestContextMock.createTools()); - ml = mlServicesMock.createSetupContract(); const logger = loggingSystemMock.createLogger(); clients.rulesClient.find.mockResolvedValue(getFindResultWithSingleHit()); // rule exists clients.rulesClient.update.mockResolvedValue(getRuleMock(getQueryRuleParams())); // update succeeds + clients.rulesManagementClient.patchRule.mockResolvedValue(getRuleMock(getQueryRuleParams())); - bulkPatchRulesRoute(server.router, ml, logger); + bulkPatchRulesRoute(server.router, logger); }); describe('status codes', () => { @@ -68,11 +64,23 @@ describe('Bulk patch rules route', () => { }); test('allows ML Params to be patched', async () => { + const anomalyThreshold = 4; + const machineLearningJobId = 'some_job_id'; + clients.rulesClient.get.mockResolvedValueOnce(getRuleMock(getMlRuleParams())); clients.rulesClient.find.mockResolvedValueOnce({ ...getFindResultWithSingleHit(), data: [getRuleMock(getMlRuleParams())], }); + clients.rulesManagementClient.patchRule.mockResolvedValueOnce( + getRuleMock( + getMlRuleParams({ + anomalyThreshold, + machineLearningJobId: [machineLearningJobId], + }) + ) + ); + const request = requestMock.create({ method: 'patch', path: `${DETECTION_ENGINE_RULES_URL}/bulk_update`, @@ -80,31 +88,23 @@ describe('Bulk patch rules route', () => { { type: 'machine_learning', rule_id: 'my-rule-id', - anomaly_threshold: 4, - machine_learning_job_id: 'some_job_id', + anomaly_threshold: anomalyThreshold, + machine_learning_job_id: machineLearningJobId, }, ], }); - await server.inject(request, requestContextMock.convertContext(context)); - - expect(clients.rulesClient.update).toHaveBeenCalledWith( - expect.objectContaining({ - data: expect.objectContaining({ - params: expect.objectContaining({ - anomalyThreshold: 4, - machineLearningJobId: ['some_job_id'], - }), - }), - }) - ); + const response = await server.inject(request, requestContextMock.convertContext(context)); + + expect(response.status).toEqual(200); + expect(response.body[0].machine_learning_job_id).toEqual([machineLearningJobId]); + expect(response.body[0].anomaly_threshold).toEqual(anomalyThreshold); }); it('rejects patching a rule to ML if mlAuthz fails', async () => { - (buildMlAuthz as jest.Mock).mockReturnValueOnce({ - validateRuleType: jest - .fn() - .mockResolvedValue({ valid: false, message: 'mocked validation message' }), + clients.rulesManagementClient.patchRule.mockImplementationOnce(async () => { + throw new HttpAuthzError('mocked validation message'); }); + const request = requestMock.create({ method: 'patch', path: DETECTION_ENGINE_RULES_BULK_UPDATE, @@ -125,10 +125,8 @@ describe('Bulk patch rules route', () => { }); it('rejects patching an existing ML rule if mlAuthz fails', async () => { - (buildMlAuthz as jest.Mock).mockReturnValueOnce({ - validateRuleType: jest - .fn() - .mockResolvedValue({ valid: false, message: 'mocked validation message' }), + clients.rulesManagementClient.patchRule.mockImplementationOnce(async () => { + throw new HttpAuthzError('mocked validation message'); }); const { type, ...payloadWithoutType } = typicalMlRulePayload(); const request = requestMock.create({ diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/api/rules/bulk_patch_rules/route.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/api/rules/bulk_patch_rules/route.ts index 5d6d74f230ce5c..06840ed328ee50 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/api/rules/bulk_patch_rules/route.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/api/rules/bulk_patch_rules/route.ts @@ -16,14 +16,10 @@ import { import { buildRouteValidationWithZod } from '../../../../../../utils/build_validation/route_validation'; import type { SecuritySolutionPluginRouter } from '../../../../../../types'; -import type { SetupPlugins } from '../../../../../../plugin'; -import { buildMlAuthz } from '../../../../../machine_learning/authz'; -import { throwAuthzError } from '../../../../../machine_learning/validation'; import { transformBulkError, buildSiemResponse } from '../../../../routes/utils'; import { getIdBulkError } from '../../../utils/utils'; import { transformValidateBulkError } from '../../../utils/validate'; -import { patchRules } from '../../../logic/crud/patch_rules'; -import { readRules } from '../../../logic/crud/read_rules'; +import { readRules } from '../../../logic/rule_management/read_rules'; import { getDeprecatedBulkEndpointHeader, logDeprecatedBulkEndpoint } from '../../deprecation'; import { validateRuleDefaultExceptionList } from '../../../logic/exceptions/validate_rule_default_exception_list'; import { validateRulesWithDuplicatedDefaultExceptionsList } from '../../../logic/exceptions/validate_rules_with_duplicated_default_exceptions_list'; @@ -32,11 +28,7 @@ import { RULE_MANAGEMENT_BULK_ACTION_SOCKET_TIMEOUT_MS } from '../../timeouts'; /** * @deprecated since version 8.2.0. Use the detection_engine/rules/_bulk_action API instead */ -export const bulkPatchRulesRoute = ( - router: SecuritySolutionPluginRouter, - ml: SetupPlugins['ml'], - logger: Logger -) => { +export const bulkPatchRulesRoute = (router: SecuritySolutionPluginRouter, logger: Logger) => { router.versioned .patch({ access: 'public', @@ -64,35 +56,22 @@ export const bulkPatchRulesRoute = ( try { const ctx = await context.resolve(['core', 'securitySolution', 'alerting', 'licensing']); - const rulesClient = ctx.alerting.getRulesClient(); - const savedObjectsClient = ctx.core.savedObjects.client; - - const mlAuthz = buildMlAuthz({ - license: ctx.licensing.license, - ml, - request, - savedObjectsClient, - }); + const rulesManagementClient = ctx.securitySolution.getRulesManagementClient(); const rules = await Promise.all( request.body.map(async (payloadRule) => { const idOrRuleIdOrUnknown = payloadRule.id ?? payloadRule.rule_id ?? '(unknown id)'; try { - if (payloadRule.type) { - // reject an unauthorized "promotion" to ML - throwAuthzError(await mlAuthz.validateRuleType(payloadRule.type)); - } - const existingRule = await readRules({ rulesClient, ruleId: payloadRule.rule_id, id: payloadRule.id, }); - if (existingRule?.params.type) { - // reject an unauthorized modification of an ML rule - throwAuthzError(await mlAuthz.validateRuleType(existingRule?.params.type)); + + if (!existingRule) { + return getIdBulkError({ id: payloadRule.id, ruleId: payloadRule.rule_id }); } validateRulesWithDuplicatedDefaultExceptionsList({ @@ -108,16 +87,11 @@ export const bulkPatchRulesRoute = ( ruleId: payloadRule.id, }); - const rule = await patchRules({ - existingRule, - rulesClient, + const rule = await rulesManagementClient.patchRule({ nextParams: payloadRule, }); - if (rule != null && rule.enabled != null && rule.name != null) { - return transformValidateBulkError(rule.id, rule); - } else { - return getIdBulkError({ id: payloadRule.id, ruleId: payloadRule.rule_id }); - } + + return transformValidateBulkError(rule.id, rule); } catch (err) { return transformBulkError(idOrRuleIdOrUnknown, err); } diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/api/rules/bulk_update_rules/route.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/api/rules/bulk_update_rules/route.test.ts index 98752160fab96d..8f1b066c8b4ba5 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/api/rules/bulk_update_rules/route.test.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/api/rules/bulk_update_rules/route.test.ts @@ -6,8 +6,6 @@ */ import { DETECTION_ENGINE_RULES_BULK_UPDATE } from '../../../../../../../common/constants'; -import { mlServicesMock } from '../../../../../machine_learning/mocks'; -import { buildMlAuthz } from '../../../../../machine_learning/authz'; import { getEmptyFindResult, getRuleMock, @@ -21,26 +19,23 @@ import type { BulkError } from '../../../../routes/utils'; import { getCreateRulesSchemaMock } from '../../../../../../../common/api/detection_engine/model/rule_schema/mocks'; import { getQueryRuleParams } from '../../../../rule_schema/mocks'; import { loggingSystemMock } from '@kbn/core/server/mocks'; - -jest.mock('../../../../../machine_learning/authz'); +import { HttpAuthzError } from '../../../../../machine_learning/validation'; describe('Bulk update rules route', () => { let server: ReturnType; let { clients, context } = requestContextMock.createTools(); - let ml: ReturnType; beforeEach(() => { server = serverMock.create(); ({ clients, context } = requestContextMock.createTools()); - ml = mlServicesMock.createSetupContract(); const logger = loggingSystemMock.createLogger(); clients.rulesClient.find.mockResolvedValue(getFindResultWithSingleHit()); clients.rulesClient.update.mockResolvedValue(getRuleMock(getQueryRuleParams())); - + clients.rulesManagementClient.updateRule.mockResolvedValue(getRuleMock(getQueryRuleParams())); clients.appClient.getSignalsIndex.mockReturnValue('.siem-signals-test-index'); - bulkUpdateRulesRoute(server.router, ml, logger); + bulkUpdateRulesRoute(server.router, logger); }); describe('status codes', () => { @@ -71,7 +66,7 @@ describe('Bulk update rules route', () => { }); test('returns an error if update throws', async () => { - clients.rulesClient.update.mockImplementation(() => { + clients.rulesManagementClient.updateRule.mockImplementation(() => { throw new Error('Test error'); }); @@ -90,11 +85,10 @@ describe('Bulk update rules route', () => { }); it('returns a 403 error object if mlAuthz fails', async () => { - (buildMlAuthz as jest.Mock).mockReturnValueOnce({ - validateRuleType: jest - .fn() - .mockResolvedValue({ valid: false, message: 'mocked validation message' }), + clients.rulesManagementClient.updateRule.mockImplementationOnce(async () => { + throw new HttpAuthzError('mocked validation message'); }); + const request = requestMock.create({ method: 'put', path: DETECTION_ENGINE_RULES_BULK_UPDATE, diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/api/rules/bulk_update_rules/route.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/api/rules/bulk_update_rules/route.ts index 7bec95bf90da9a..b7bef15b5f6b1b 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/api/rules/bulk_update_rules/route.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/api/rules/bulk_update_rules/route.ts @@ -17,9 +17,6 @@ import { import { buildRouteValidationWithZod } from '../../../../../../utils/build_validation/route_validation'; import type { SecuritySolutionPluginRouter } from '../../../../../../types'; import { DETECTION_ENGINE_RULES_BULK_UPDATE } from '../../../../../../../common/constants'; -import type { SetupPlugins } from '../../../../../../plugin'; -import { buildMlAuthz } from '../../../../../machine_learning/authz'; -import { throwAuthzError } from '../../../../../machine_learning/validation'; import { getIdBulkError } from '../../../utils/utils'; import { transformValidateBulkError } from '../../../utils/validate'; import { @@ -27,8 +24,7 @@ import { buildSiemResponse, createBulkErrorObject, } from '../../../../routes/utils'; -import { updateRules } from '../../../logic/crud/update_rules'; -import { readRules } from '../../../logic/crud/read_rules'; +import { readRules } from '../../../logic/rule_management/read_rules'; import { getDeprecatedBulkEndpointHeader, logDeprecatedBulkEndpoint } from '../../deprecation'; import { validateRuleDefaultExceptionList } from '../../../logic/exceptions/validate_rule_default_exception_list'; import { validateRulesWithDuplicatedDefaultExceptionsList } from '../../../logic/exceptions/validate_rules_with_duplicated_default_exceptions_list'; @@ -37,11 +33,7 @@ import { RULE_MANAGEMENT_BULK_ACTION_SOCKET_TIMEOUT_MS } from '../../timeouts'; /** * @deprecated since version 8.2.0. Use the detection_engine/rules/_bulk_action API instead */ -export const bulkUpdateRulesRoute = ( - router: SecuritySolutionPluginRouter, - ml: SetupPlugins['ml'], - logger: Logger -) => { +export const bulkUpdateRulesRoute = (router: SecuritySolutionPluginRouter, logger: Logger) => { router.versioned .put({ access: 'public', @@ -69,16 +61,8 @@ export const bulkUpdateRulesRoute = ( try { const ctx = await context.resolve(['core', 'securitySolution', 'alerting', 'licensing']); - const rulesClient = ctx.alerting.getRulesClient(); - const savedObjectsClient = ctx.core.savedObjects.client; - - const mlAuthz = buildMlAuthz({ - license: ctx.licensing.license, - ml, - request, - savedObjectsClient, - }); + const rulesManagementClient = ctx.securitySolution.getRulesManagementClient(); const rules = await Promise.all( request.body.map(async (payloadRule) => { @@ -93,14 +77,16 @@ export const bulkUpdateRulesRoute = ( }); } - throwAuthzError(await mlAuthz.validateRuleType(payloadRule.type)); - const existingRule = await readRules({ rulesClient, ruleId: payloadRule.rule_id, id: payloadRule.id, }); + if (!existingRule) { + return getIdBulkError({ id: payloadRule.id, ruleId: payloadRule.rule_id }); + } + validateRulesWithDuplicatedDefaultExceptionsList({ allRules: request.body, exceptionsList: payloadRule.exceptions_list, @@ -113,16 +99,11 @@ export const bulkUpdateRulesRoute = ( ruleId: payloadRule.id, }); - const rule = await updateRules({ - rulesClient, - existingRule, + const rule = await rulesManagementClient.updateRule({ ruleUpdate: payloadRule, }); - if (rule != null) { - return transformValidateBulkError(rule.id, rule); - } else { - return getIdBulkError({ id: payloadRule.id, ruleId: payloadRule.rule_id }); - } + + return transformValidateBulkError(rule.id, rule); } catch (err) { return transformBulkError(idOrRuleIdOrUnknown, err); } diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/api/rules/create_rule/route.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/api/rules/create_rule/route.test.ts index 5abdc9b4904e45..4b23757112bdd9 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/api/rules/create_rule/route.test.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/api/rules/create_rule/route.test.ts @@ -14,33 +14,31 @@ import { createMlRuleRequest, getBasicEmptySearchResponse, } from '../../../../routes/__mocks__/request_responses'; -import { mlServicesMock } from '../../../../../machine_learning/mocks'; -import { buildMlAuthz } from '../../../../../machine_learning/authz'; import { requestContextMock, serverMock, requestMock } from '../../../../routes/__mocks__'; import { createRuleRoute } from './route'; import { getCreateRulesSchemaMock } from '../../../../../../../common/api/detection_engine/model/rule_schema/mocks'; import { elasticsearchClientMock } from '@kbn/core-elasticsearch-client-server-mocks'; import { getQueryRuleParams } from '../../../../rule_schema/mocks'; - -jest.mock('../../../../../machine_learning/authz'); +import { HttpAuthzError } from '../../../../../machine_learning/validation'; describe('Create rule route', () => { let server: ReturnType; let { clients, context } = requestContextMock.createTools(); - let ml: ReturnType; beforeEach(() => { server = serverMock.create(); ({ clients, context } = requestContextMock.createTools()); - ml = mlServicesMock.createSetupContract(); clients.rulesClient.find.mockResolvedValue(getEmptyFindResult()); // no current rules clients.rulesClient.create.mockResolvedValue(getRuleMock(getQueryRuleParams())); // creation succeeds + clients.rulesManagementClient.createCustomRule.mockResolvedValue( + getRuleMock(getQueryRuleParams()) + ); context.core.elasticsearch.client.asCurrentUser.search.mockResolvedValue( elasticsearchClientMock.createSuccessTransportRequestPromise(getBasicEmptySearchResponse()) ); - createRuleRoute(server.router, ml); + createRuleRoute(server.router); }); describe('status codes', () => { @@ -73,10 +71,8 @@ describe('Create rule route', () => { }); test('returns a 403 if ML Authz fails', async () => { - (buildMlAuthz as jest.Mock).mockReturnValueOnce({ - validateRuleType: jest - .fn() - .mockResolvedValue({ valid: false, message: 'mocked validation message' }), + clients.rulesManagementClient.createCustomRule.mockImplementation(async () => { + throw new HttpAuthzError('mocked validation message'); }); const response = await server.inject( @@ -107,7 +103,7 @@ describe('Create rule route', () => { }); test('catches error if creation throws', async () => { - clients.rulesClient.create.mockImplementation(async () => { + clients.rulesManagementClient.createCustomRule.mockImplementation(async () => { throw new Error('Test error'); }); const response = await server.inject( diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/api/rules/create_rule/route.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/api/rules/create_rule/route.ts index e9c84c1c50f5cb..8ab0af5cda7428 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/api/rules/create_rule/route.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/api/rules/create_rule/route.ts @@ -13,22 +13,15 @@ import { validateCreateRuleProps, } from '../../../../../../../common/api/detection_engine/rule_management'; import { DETECTION_ENGINE_RULES_URL } from '../../../../../../../common/constants'; -import type { SetupPlugins } from '../../../../../../plugin'; import type { SecuritySolutionPluginRouter } from '../../../../../../types'; import { buildRouteValidationWithZod } from '../../../../../../utils/build_validation/route_validation'; -import { buildMlAuthz } from '../../../../../machine_learning/authz'; -import { throwAuthzError } from '../../../../../machine_learning/validation'; import { buildSiemResponse } from '../../../../routes/utils'; -import { createRules } from '../../../logic/crud/create_rules'; -import { readRules } from '../../../logic/crud/read_rules'; +import { readRules } from '../../../logic/rule_management/read_rules'; import { checkDefaultRuleExceptionListReferences } from '../../../logic/exceptions/check_for_default_rule_exception_list'; import { validateRuleDefaultExceptionList } from '../../../logic/exceptions/validate_rule_default_exception_list'; import { transformValidate, validateResponseActionsPermissions } from '../../../utils/validate'; -export const createRuleRoute = ( - router: SecuritySolutionPluginRouter, - ml: SetupPlugins['ml'] -): void => { +export const createRuleRoute = (router: SecuritySolutionPluginRouter): void => { router.versioned .post({ access: 'public', @@ -64,7 +57,7 @@ export const createRuleRoute = ( ]); const rulesClient = ctx.alerting.getRulesClient(); - const savedObjectsClient = ctx.core.savedObjects.client; + const rulesManagementClient = ctx.securitySolution.getRulesManagementClient(); const exceptionsClient = ctx.lists?.getExceptionListClient(); if (request.body.rule_id != null) { @@ -81,14 +74,6 @@ export const createRuleRoute = ( } } - const mlAuthz = buildMlAuthz({ - license: ctx.licensing.license, - ml, - request, - savedObjectsClient, - }); - throwAuthzError(await mlAuthz.validateRuleType(request.body.type)); - // This will create the endpoint list if it does not exist yet await exceptionsClient?.createEndpointList(); checkDefaultRuleExceptionListReferences({ @@ -104,8 +89,7 @@ export const createRuleRoute = ( await validateResponseActionsPermissions(ctx.securitySolution, request.body); - const createdRule = await createRules({ - rulesClient, + const createdRule = await rulesManagementClient.createCustomRule({ params: request.body, }); diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/api/rules/delete_rule/route.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/api/rules/delete_rule/route.test.ts index 11dd8946419f9a..74d6ed952cf157 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/api/rules/delete_rule/route.test.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/api/rules/delete_rule/route.test.ts @@ -27,6 +27,7 @@ describe('Delete rule route', () => { ({ clients, context } = requestContextMock.createTools()); clients.rulesClient.find.mockResolvedValue(getFindResultWithSingleHit()); + clients.rulesManagementClient.deleteRule.mockResolvedValue(); clients.savedObjectsClient.find.mockResolvedValue(getEmptySavedObjectsResponse()); deleteRuleRoute(server.router); @@ -68,7 +69,7 @@ describe('Delete rule route', () => { }); test('catches error if deletion throws error', async () => { - clients.rulesClient.delete.mockImplementation(async () => { + clients.rulesManagementClient.deleteRule.mockImplementation(async () => { throw new Error('Test error'); }); const response = await server.inject( diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/api/rules/delete_rule/route.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/api/rules/delete_rule/route.ts index f45901082d393a..7db52619ff0b58 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/api/rules/delete_rule/route.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/api/rules/delete_rule/route.ts @@ -16,8 +16,7 @@ import { DETECTION_ENGINE_RULES_URL } from '../../../../../../../common/constant import type { SecuritySolutionPluginRouter } from '../../../../../../types'; import { buildRouteValidationWithZod } from '../../../../../../utils/build_validation/route_validation'; import { buildSiemResponse } from '../../../../routes/utils'; -import { deleteRules } from '../../../logic/crud/delete_rules'; -import { readRules } from '../../../logic/crud/read_rules'; +import { readRules } from '../../../logic/rule_management/read_rules'; import { getIdError, transform } from '../../../utils/utils'; export const deleteRuleRoute = (router: SecuritySolutionPluginRouter) => { @@ -50,6 +49,7 @@ export const deleteRuleRoute = (router: SecuritySolutionPluginRouter) => { const ctx = await context.resolve(['core', 'securitySolution', 'alerting']); const rulesClient = ctx.alerting.getRulesClient(); + const rulesManagementClient = ctx.securitySolution.getRulesManagementClient(); const rule = await readRules({ rulesClient, id, ruleId }); @@ -61,9 +61,8 @@ export const deleteRuleRoute = (router: SecuritySolutionPluginRouter) => { }); } - await deleteRules({ + await rulesManagementClient.deleteRule({ ruleId: rule.id, - rulesClient, }); const transformed = transform(rule); diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/api/rules/import_rules/route.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/api/rules/import_rules/route.test.ts index da4f9e67d7b8f1..1320c8d5a32969 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/api/rules/import_rules/route.test.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/api/rules/import_rules/route.test.ts @@ -13,9 +13,6 @@ import { rulesToNdJsonString, } from '../../../../../../../common/api/detection_engine/rule_management/mocks'; -import { buildMlAuthz } from '../../../../../machine_learning/authz'; -import { mlServicesMock } from '../../../../../machine_learning/mocks'; - import type { requestMock } from '../../../../routes/__mocks__'; import { createMockConfig, requestContextMock, serverMock } from '../../../../routes/__mocks__'; import { buildHapiStream } from '../../../../routes/__mocks__/utils'; @@ -31,6 +28,7 @@ import { import * as createRulesAndExceptionsStreamFromNdJson from '../../../logic/import/create_rules_stream_from_ndjson'; import { getQueryRuleParams } from '../../../../rule_schema/mocks'; import { importRulesRoute } from './route'; +import { HttpAuthzError } from '../../../../../machine_learning/validation'; jest.mock('../../../../../machine_learning/authz'); @@ -39,7 +37,6 @@ describe('Import rules route', () => { let server: ReturnType; let request: ReturnType; let { clients, context } = requestContextMock.createTools(); - let ml: ReturnType; beforeEach(() => { server = serverMock.create(); @@ -47,15 +44,15 @@ describe('Import rules route', () => { config = createMockConfig(); const hapiStream = buildHapiStream(ruleIdsToNdJsonString(['rule-1'])); request = getImportRulesRequest(hapiStream); - ml = mlServicesMock.createSetupContract(); clients.rulesClient.find.mockResolvedValue(getEmptyFindResult()); // no extant rules clients.rulesClient.update.mockResolvedValue(getRuleMock(getQueryRuleParams())); + clients.rulesManagementClient.importRule.mockResolvedValue(getRuleMock(getQueryRuleParams())); clients.actionsClient.getAll.mockResolvedValue([]); context.core.elasticsearch.client.asCurrentUser.search.mockResolvedValue( elasticsearchClientMock.createSuccessTransportRequestPromise(getBasicEmptySearchResponse()) ); - importRulesRoute(server.router, config, ml); + importRulesRoute(server.router, config); }); describe('status codes', () => { @@ -83,13 +80,12 @@ describe('Import rules route', () => { describe('unhappy paths', () => { test('returns a 403 error object if ML Authz fails', async () => { - (buildMlAuthz as jest.Mock).mockReturnValueOnce({ - validateRuleType: jest - .fn() - .mockResolvedValue({ valid: false, message: 'mocked validation message' }), + clients.rulesManagementClient.importRule.mockImplementationOnce(async () => { + throw new HttpAuthzError('mocked validation message'); }); const response = await server.inject(request, requestContextMock.convertContext(context)); + expect(response.status).toEqual(200); expect(response.body).toEqual({ errors: [ @@ -189,6 +185,10 @@ describe('Import rules route', () => { describe('rule with existing rule_id', () => { test('returns with reported conflict if `overwrite` is set to `false`', async () => { clients.rulesClient.find.mockResolvedValue(getFindResultWithSingleHit()); // extant rule + clients.rulesManagementClient.importRule.mockRejectedValue({ + message: 'rule_id: "rule-1" already exists', + statusCode: 409, + }); const response = await server.inject(request, requestContextMock.convertContext(context)); expect(response.status).toEqual(200); @@ -403,6 +403,10 @@ describe('Import rules route', () => { }); test('returns with reported conflict if `overwrite` is set to `false`', async () => { + clients.rulesManagementClient.importRule.mockRejectedValueOnce({ + message: 'rule_id: "rule-1" already exists', + statusCode: 409, + }); const multiRequest = getImportRulesRequest( buildHapiStream(ruleIdsToNdJsonString(['rule-1', 'rule-2', 'rule-3'])) ); diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/api/rules/import_rules/route.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/api/rules/import_rules/route.ts index 0e1013f452d666..a085aced46ab4d 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/api/rules/import_rules/route.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/api/rules/import_rules/route.ts @@ -17,10 +17,8 @@ import { } from '../../../../../../../common/api/detection_engine/rule_management'; import { DETECTION_ENGINE_RULES_URL } from '../../../../../../../common/constants'; import type { ConfigType } from '../../../../../../config'; -import type { SetupPlugins } from '../../../../../../plugin'; import type { HapiReadableStream, SecuritySolutionPluginRouter } from '../../../../../../types'; import { buildRouteValidationWithZod } from '../../../../../../utils/build_validation/route_validation'; -import { buildMlAuthz } from '../../../../../machine_learning/authz'; import type { BulkError, ImportRuleResponse } from '../../../../routes/utils'; import { buildSiemResponse, isBulkError, isImportRegular } from '../../../../routes/utils'; import { importRuleActionConnectors } from '../../../logic/import/action_connectors/import_rule_action_connectors'; @@ -37,11 +35,7 @@ import { RULE_MANAGEMENT_IMPORT_EXPORT_SOCKET_TIMEOUT_MS } from '../../timeouts' const CHUNK_PARSED_OBJECT_SIZE = 50; -export const importRulesRoute = ( - router: SecuritySolutionPluginRouter, - config: ConfigType, - ml: SetupPlugins['ml'] -) => { +export const importRulesRoute = (router: SecuritySolutionPluginRouter, config: ConfigType) => { router.versioned .post({ access: 'public', @@ -80,7 +74,7 @@ export const importRulesRoute = ( 'licensing', ]); - const rulesClient = ctx.alerting.getRulesClient(); + const rulesManagementClient = ctx.securitySolution.getRulesManagementClient(); const actionsClient = ctx.actions.getActionsClient(); const actionSOClient = ctx.core.savedObjects.getClient({ includedHiddenTypes: ['action'], @@ -90,13 +84,6 @@ export const importRulesRoute = ( const savedObjectsClient = ctx.core.savedObjects.client; const exceptionsClient = ctx.lists?.getExceptionListClient(); - const mlAuthz = buildMlAuthz({ - license: ctx.licensing.license, - ml, - request, - savedObjectsClient, - }); - const { filename } = (request.body.file as HapiReadableStream).hapi; const fileExtension = extname(filename).toLowerCase(); if (fileExtension !== '.ndjson') { @@ -166,9 +153,8 @@ export const importRulesRoute = ( const importRuleResponse: ImportRuleResponse[] = await importRulesHelper({ ruleChunks: chunkParseObjects, rulesResponseAcc: [...actionConnectorErrors, ...duplicateIdErrors], - mlAuthz, overwriteRules: request.query.overwrite, - rulesClient, + rulesManagementClient, existingLists: foundReferencedExceptionLists, allowMissingConnectorSecrets: !!actionConnectors.length, }); diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/api/rules/patch_rule/route.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/api/rules/patch_rule/route.test.ts index 1255287cf52f5d..22a2c9c058895a 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/api/rules/patch_rule/route.test.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/api/rules/patch_rule/route.test.ts @@ -8,9 +8,6 @@ import { DETECTION_ENGINE_RULES_URL } from '../../../../../../../common/constants'; import { getPatchRulesSchemaMock } from '../../../../../../../common/api/detection_engine/rule_management/mocks'; -import { buildMlAuthz } from '../../../../../machine_learning/authz'; -import { mlServicesMock } from '../../../../../machine_learning/mocks'; - import { requestContextMock, serverMock, requestMock } from '../../../../routes/__mocks__'; import { getEmptyFindResult, @@ -24,24 +21,22 @@ import { import { getMlRuleParams, getQueryRuleParams } from '../../../../rule_schema/mocks'; import { patchRuleRoute } from './route'; - -jest.mock('../../../../../machine_learning/authz'); +import { HttpAuthzError } from '../../../../../machine_learning/validation'; describe('Patch rule route', () => { let server: ReturnType; let { clients, context } = requestContextMock.createTools(); - let ml: ReturnType; beforeEach(() => { server = serverMock.create(); ({ clients, context } = requestContextMock.createTools()); - ml = mlServicesMock.createSetupContract(); clients.rulesClient.get.mockResolvedValue(getRuleMock(getQueryRuleParams())); // existing rule clients.rulesClient.find.mockResolvedValue(getFindResultWithSingleHit()); // existing rule clients.rulesClient.update.mockResolvedValue(getRuleMock(getQueryRuleParams())); // successful update + clients.rulesManagementClient.patchRule.mockResolvedValue(getRuleMock(getQueryRuleParams())); - patchRuleRoute(server.router, ml); + patchRuleRoute(server.router); }); describe('status codes', () => { @@ -50,6 +45,7 @@ describe('Patch rule route', () => { getPatchRequest(), requestContextMock.convertContext(context) ); + expect(response.status).toEqual(200); }); @@ -80,7 +76,7 @@ describe('Patch rule route', () => { }); test('catches error if update throws error', async () => { - clients.rulesClient.update.mockImplementation(async () => { + clients.rulesManagementClient.patchRule.mockImplementation(async () => { throw new Error('Test error'); }); const response = await server.inject( @@ -100,36 +96,39 @@ describe('Patch rule route', () => { ...getFindResultWithSingleHit(), data: [getRuleMock(getMlRuleParams())], }); + + const anomalyThreshold = 4; + const machineLearningJobId = 'some_job_id'; + clients.rulesManagementClient.patchRule.mockResolvedValueOnce( + getRuleMock( + getMlRuleParams({ + anomalyThreshold, + machineLearningJobId: [machineLearningJobId], + }) + ) + ); + const request = requestMock.create({ method: 'patch', path: DETECTION_ENGINE_RULES_URL, body: { type: 'machine_learning', rule_id: 'my-rule-id', - anomaly_threshold: 4, - machine_learning_job_id: 'some_job_id', + anomaly_threshold: anomalyThreshold, + machine_learning_job_id: machineLearningJobId, }, }); - await server.inject(request, requestContextMock.convertContext(context)); - - expect(clients.rulesClient.update).toHaveBeenCalledWith( - expect.objectContaining({ - data: expect.objectContaining({ - params: expect.objectContaining({ - anomalyThreshold: 4, - machineLearningJobId: ['some_job_id'], - }), - }), - }) - ); + const response = await server.inject(request, requestContextMock.convertContext(context)); + expect(response.status).toEqual(200); + expect(response.body.machine_learning_job_id).toEqual([machineLearningJobId]); + expect(response.body.anomaly_threshold).toEqual(anomalyThreshold); }); it('rejects patching a rule to ML if mlAuthz fails', async () => { - (buildMlAuthz as jest.Mock).mockReturnValueOnce({ - validateRuleType: jest - .fn() - .mockResolvedValue({ valid: false, message: 'mocked validation message' }), + clients.rulesManagementClient.patchRule.mockImplementationOnce(async () => { + throw new HttpAuthzError('mocked validation message'); }); + const request = requestMock.create({ method: 'patch', path: DETECTION_ENGINE_RULES_URL, @@ -145,11 +144,10 @@ describe('Patch rule route', () => { }); it('rejects patching an ML rule if mlAuthz fails', async () => { - (buildMlAuthz as jest.Mock).mockReturnValueOnce({ - validateRuleType: jest - .fn() - .mockResolvedValue({ valid: false, message: 'mocked validation message' }), + clients.rulesManagementClient.patchRule.mockImplementationOnce(async () => { + throw new HttpAuthzError('mocked validation message'); }); + const { type, ...payloadWithoutType } = typicalMlRulePayload(); const request = requestMock.create({ method: 'patch', diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/api/rules/patch_rule/route.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/api/rules/patch_rule/route.ts index 5401e2361ca585..6cbba8ce65759e 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/api/rules/patch_rule/route.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/api/rules/patch_rule/route.ts @@ -13,20 +13,16 @@ import { validatePatchRuleRequestBody, } from '../../../../../../../common/api/detection_engine/rule_management'; import { DETECTION_ENGINE_RULES_URL } from '../../../../../../../common/constants'; -import type { SetupPlugins } from '../../../../../../plugin'; import type { SecuritySolutionPluginRouter } from '../../../../../../types'; import { buildRouteValidationWithZod } from '../../../../../../utils/build_validation/route_validation'; -import { buildMlAuthz } from '../../../../../machine_learning/authz'; -import { throwAuthzError } from '../../../../../machine_learning/validation'; import { buildSiemResponse } from '../../../../routes/utils'; -import { patchRules } from '../../../logic/crud/patch_rules'; -import { readRules } from '../../../logic/crud/read_rules'; +import { readRules } from '../../../logic/rule_management/read_rules'; import { checkDefaultRuleExceptionListReferences } from '../../../logic/exceptions/check_for_default_rule_exception_list'; import { validateRuleDefaultExceptionList } from '../../../logic/exceptions/validate_rule_default_exception_list'; import { getIdError } from '../../../utils/utils'; import { transformValidate } from '../../../utils/validate'; -export const patchRuleRoute = (router: SecuritySolutionPluginRouter, ml: SetupPlugins['ml']) => { +export const patchRuleRoute = (router: SecuritySolutionPluginRouter) => { router.versioned .patch({ access: 'public', @@ -56,27 +52,20 @@ export const patchRuleRoute = (router: SecuritySolutionPluginRouter, ml: SetupPl try { const params = request.body; const rulesClient = (await context.alerting).getRulesClient(); - const savedObjectsClient = (await context.core).savedObjects.client; - - const mlAuthz = buildMlAuthz({ - license: (await context.licensing).license, - ml, - request, - savedObjectsClient, - }); - if (params.type) { - // reject an unauthorized "promotion" to ML - throwAuthzError(await mlAuthz.validateRuleType(params.type)); - } + const rulesManagementClient = (await context.securitySolution).getRulesManagementClient(); const existingRule = await readRules({ rulesClient, ruleId: params.rule_id, id: params.id, }); - if (existingRule?.params.type) { - // reject an unauthorized modification of an ML rule - throwAuthzError(await mlAuthz.validateRuleType(existingRule?.params.type)); + + if (!existingRule) { + const error = getIdError({ id: params.id, ruleId: params.rule_id }); + return siemResponse.error({ + body: error.message, + statusCode: error.statusCode, + }); } checkDefaultRuleExceptionListReferences({ exceptionLists: params.exceptions_list }); @@ -87,22 +76,13 @@ export const patchRuleRoute = (router: SecuritySolutionPluginRouter, ml: SetupPl ruleId: params.id, }); - const rule = await patchRules({ - rulesClient, - existingRule, + const rule = await rulesManagementClient.patchRule({ nextParams: params, }); - if (rule != null && rule.enabled != null && rule.name != null) { - return response.ok({ - body: transformValidate(rule), - }); - } else { - const error = getIdError({ id: params.id, ruleId: params.rule_id }); - return siemResponse.error({ - body: error.message, - statusCode: error.statusCode, - }); - } + + return response.ok({ + body: transformValidate(rule), + }); } catch (err) { const error = transformError(err); return siemResponse.error({ diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/api/rules/read_rule/route.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/api/rules/read_rule/route.ts index ade1f280c046d0..aabf5383679308 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/api/rules/read_rule/route.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/api/rules/read_rule/route.ts @@ -16,7 +16,7 @@ import { DETECTION_ENGINE_RULES_URL } from '../../../../../../../common/constant import type { SecuritySolutionPluginRouter } from '../../../../../../types'; import { buildRouteValidationWithZod } from '../../../../../../utils/build_validation/route_validation'; import { buildSiemResponse } from '../../../../routes/utils'; -import { readRules } from '../../../logic/crud/read_rules'; +import { readRules } from '../../../logic/rule_management/read_rules'; import { getIdError, transform } from '../../../utils/utils'; export const readRuleRoute = (router: SecuritySolutionPluginRouter, logger: Logger) => { diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/api/rules/update_rule/route.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/api/rules/update_rule/route.test.ts index 4eea0dc1797d5b..624eaf882b0fa8 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/api/rules/update_rule/route.test.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/api/rules/update_rule/route.test.ts @@ -4,9 +4,6 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ - -import { mlServicesMock } from '../../../../../machine_learning/mocks'; -import { buildMlAuthz } from '../../../../../machine_learning/authz'; import { getEmptyFindResult, getRuleMock, @@ -24,25 +21,23 @@ import { } from '../../../../../../../common/api/detection_engine/model/rule_schema/mocks'; import { getQueryRuleParams } from '../../../../rule_schema/mocks'; import { ResponseActionTypesEnum } from '../../../../../../../common/api/detection_engine'; - -jest.mock('../../../../../machine_learning/authz'); +import { HttpAuthzError } from '../../../../../machine_learning/validation'; describe('Update rule route', () => { let server: ReturnType; let { clients, context } = requestContextMock.createTools(); - let ml: ReturnType; beforeEach(() => { server = serverMock.create(); ({ clients, context } = requestContextMock.createTools()); - ml = mlServicesMock.createSetupContract(); clients.rulesClient.get.mockResolvedValue(getRuleMock(getQueryRuleParams())); // existing rule clients.rulesClient.find.mockResolvedValue(getFindResultWithSingleHit()); // rule exists clients.rulesClient.update.mockResolvedValue(getRuleMock(getQueryRuleParams())); // successful update + clients.rulesManagementClient.updateRule.mockResolvedValue(getRuleMock(getQueryRuleParams())); clients.appClient.getSignalsIndex.mockReturnValue('.siem-signals-test-index'); - updateRuleRoute(server.router, ml); + updateRuleRoute(server.router); }); describe('status codes', () => { @@ -99,10 +94,8 @@ describe('Update rule route', () => { }); it('returns a 403 if mlAuthz fails', async () => { - (buildMlAuthz as jest.Mock).mockReturnValueOnce({ - validateRuleType: jest - .fn() - .mockResolvedValue({ valid: false, message: 'mocked validation message' }), + clients.rulesManagementClient.updateRule.mockImplementationOnce(async () => { + throw new HttpAuthzError('mocked validation message'); }); const request = requestMock.create({ method: 'put', diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/api/rules/update_rule/route.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/api/rules/update_rule/route.ts index 215a0827b015fd..55bd63f1e09656 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/api/rules/update_rule/route.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/api/rules/update_rule/route.ts @@ -13,20 +13,16 @@ import { validateUpdateRuleProps, } from '../../../../../../../common/api/detection_engine/rule_management'; import { DETECTION_ENGINE_RULES_URL } from '../../../../../../../common/constants'; -import type { SetupPlugins } from '../../../../../../plugin'; import type { SecuritySolutionPluginRouter } from '../../../../../../types'; import { buildRouteValidationWithZod } from '../../../../../../utils/build_validation/route_validation'; -import { buildMlAuthz } from '../../../../../machine_learning/authz'; -import { throwAuthzError } from '../../../../../machine_learning/validation'; import { buildSiemResponse } from '../../../../routes/utils'; -import { readRules } from '../../../logic/crud/read_rules'; -import { updateRules } from '../../../logic/crud/update_rules'; +import { readRules } from '../../../logic/rule_management/read_rules'; import { checkDefaultRuleExceptionListReferences } from '../../../logic/exceptions/check_for_default_rule_exception_list'; import { validateRuleDefaultExceptionList } from '../../../logic/exceptions/validate_rule_default_exception_list'; import { getIdError } from '../../../utils/utils'; import { transformValidate, validateResponseActionsPermissions } from '../../../utils/validate'; -export const updateRuleRoute = (router: SecuritySolutionPluginRouter, ml: SetupPlugins['ml']) => { +export const updateRuleRoute = (router: SecuritySolutionPluginRouter) => { router.versioned .put({ access: 'public', @@ -52,17 +48,8 @@ export const updateRuleRoute = (router: SecuritySolutionPluginRouter, ml: SetupP } try { const ctx = await context.resolve(['core', 'securitySolution', 'alerting', 'licensing']); - const rulesClient = ctx.alerting.getRulesClient(); - const savedObjectsClient = ctx.core.savedObjects.client; - - const mlAuthz = buildMlAuthz({ - license: ctx.licensing.license, - ml, - request, - savedObjectsClient, - }); - throwAuthzError(await mlAuthz.validateRuleType(request.body.type)); + const rulesManagementClient = ctx.securitySolution.getRulesManagementClient(); checkDefaultRuleExceptionListReferences({ exceptionLists: request.body.exceptions_list }); @@ -79,29 +66,27 @@ export const updateRuleRoute = (router: SecuritySolutionPluginRouter, ml: SetupP id: request.body.id, }); + if (existingRule == null) { + const error = getIdError({ id: request.body.id, ruleId: request.body.rule_id }); + return siemResponse.error({ + body: error.message, + statusCode: error.statusCode, + }); + } + await validateResponseActionsPermissions( ctx.securitySolution, request.body, existingRule ); - const rule = await updateRules({ - rulesClient, - existingRule, + const rule = await rulesManagementClient.updateRule({ ruleUpdate: request.body, }); - if (rule != null) { - return response.ok({ - body: transformValidate(rule), - }); - } else { - const error = getIdError({ id: request.body.id, ruleId: request.body.rule_id }); - return siemResponse.error({ - body: error.message, - statusCode: error.statusCode, - }); - } + return response.ok({ + body: transformValidate(rule), + }); } catch (err) { const error = transformError(err); return siemResponse.error({ diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/crud/create_rules.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/crud/create_rules.test.ts deleted file mode 100644 index 59b7d9996b5b45..00000000000000 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/crud/create_rules.test.ts +++ /dev/null @@ -1,82 +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 { rulesClientMock } from '@kbn/alerting-plugin/server/mocks'; -import { createRules } from './create_rules'; -import { DEFAULT_INDICATOR_SOURCE_PATH } from '../../../../../../common/constants'; -import { - getCreateMachineLearningRulesSchemaMock, - getCreateThreatMatchRulesSchemaMock, -} from '../../../../../../common/api/detection_engine/model/rule_schema/mocks'; - -describe('createRules', () => { - it('calls the rulesClient with legacy ML params', async () => { - const rulesClient = rulesClientMock.create(); - await createRules({ rulesClient, params: getCreateMachineLearningRulesSchemaMock() }); - expect(rulesClient.create).toHaveBeenCalledWith( - expect.objectContaining({ - data: expect.objectContaining({ - params: expect.objectContaining({ - anomalyThreshold: 58, - machineLearningJobId: ['typical-ml-job-id'], - }), - }), - }) - ); - }); - - it('calls the rulesClient with ML params', async () => { - const rulesClient = rulesClientMock.create(); - await createRules({ - rulesClient, - params: { - ...getCreateMachineLearningRulesSchemaMock(), - machine_learning_job_id: ['new_job_1', 'new_job_2'], - }, - }); - expect(rulesClient.create).toHaveBeenCalledWith( - expect.objectContaining({ - data: expect.objectContaining({ - params: expect.objectContaining({ - anomalyThreshold: 58, - machineLearningJobId: ['new_job_1', 'new_job_2'], - }), - }), - }) - ); - }); - - it('populates a threatIndicatorPath value for threat_match rule if empty', async () => { - const rulesClient = rulesClientMock.create(); - const params = getCreateThreatMatchRulesSchemaMock(); - delete params.threat_indicator_path; - await createRules({ rulesClient, params }); - expect(rulesClient.create).toHaveBeenCalledWith( - expect.objectContaining({ - data: expect.objectContaining({ - params: expect.objectContaining({ - threatIndicatorPath: DEFAULT_INDICATOR_SOURCE_PATH, - }), - }), - }) - ); - }); - - it('does not populate a threatIndicatorPath value for other rules if empty', async () => { - const rulesClient = rulesClientMock.create(); - await createRules({ rulesClient, params: getCreateMachineLearningRulesSchemaMock() }); - expect(rulesClient.create).not.toHaveBeenCalledWith( - expect.objectContaining({ - data: expect.objectContaining({ - params: expect.objectContaining({ - threatIndicatorPath: DEFAULT_INDICATOR_SOURCE_PATH, - }), - }), - }) - ); - }); -}); diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/crud/create_rules.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/crud/create_rules.ts deleted file mode 100644 index 0c39d0f39411fd..00000000000000 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/crud/create_rules.ts +++ /dev/null @@ -1,42 +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 { SanitizedRule } from '@kbn/alerting-plugin/common'; -import type { RulesClient } from '@kbn/alerting-plugin/server'; - -import type { RuleCreateProps } from '../../../../../../common/api/detection_engine/model/rule_schema'; -import { convertCreateAPIToInternalSchema } from '../../normalization/rule_converters'; -import type { RuleParams } from '../../../rule_schema'; - -export interface CreateRulesOptions { - rulesClient: RulesClient; - params: T; - id?: string; - immutable?: boolean; - defaultEnabled?: boolean; - allowMissingConnectorSecrets?: boolean; -} - -export const createRules = async ({ - rulesClient, - params, - id, - immutable = false, - defaultEnabled = true, - allowMissingConnectorSecrets, -}: CreateRulesOptions): Promise> => { - const internalRule = convertCreateAPIToInternalSchema(params, immutable, defaultEnabled); - const rule = await rulesClient.create({ - options: { - id, - }, - data: internalRule, - allowMissingConnectorSecrets, - }); - - return rule; -}; diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/crud/delete_rules.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/crud/delete_rules.ts deleted file mode 100644 index 23e09d8e35c62f..00000000000000 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/crud/delete_rules.ts +++ /dev/null @@ -1,18 +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 { RulesClient } from '@kbn/alerting-plugin/server'; -import type { RuleObjectId } from '../../../../../../common/api/detection_engine/model/rule_schema'; - -export interface DeleteRuleOptions { - ruleId: RuleObjectId; - rulesClient: RulesClient; -} - -export const deleteRules = async ({ ruleId, rulesClient }: DeleteRuleOptions) => { - await rulesClient.delete({ id: ruleId }); -}; diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/crud/patch_rules.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/crud/patch_rules.ts deleted file mode 100644 index ede7b86bd737e1..00000000000000 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/crud/patch_rules.ts +++ /dev/null @@ -1,56 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import type { PartialRule, RulesClient } from '@kbn/alerting-plugin/server'; - -import type { PatchRuleRequestBody } from '../../../../../../common/api/detection_engine/rule_management'; -import type { RuleAlertType, RuleParams } from '../../../rule_schema'; -import { convertPatchAPIToInternalSchema } from '../../normalization/rule_converters'; - -export interface PatchRulesOptions { - rulesClient: RulesClient; - nextParams: PatchRuleRequestBody; - existingRule: RuleAlertType | null | undefined; - allowMissingConnectorSecrets?: boolean; - - shouldIncrementRevision?: boolean; -} - -export const patchRules = async ({ - rulesClient, - existingRule, - nextParams, - allowMissingConnectorSecrets, - shouldIncrementRevision = true, -}: PatchRulesOptions): Promise | null> => { - if (existingRule == null) { - return null; - } - - const patchedRule = convertPatchAPIToInternalSchema(nextParams, existingRule); - - const update = await rulesClient.update({ - id: existingRule.id, - data: patchedRule, - allowMissingConnectorSecrets, - shouldIncrementRevision: () => shouldIncrementRevision, - }); - - if (existingRule.enabled && nextParams.enabled === false) { - await rulesClient.disable({ id: existingRule.id }); - } else if (!existingRule.enabled && nextParams.enabled === true) { - await rulesClient.enable({ id: existingRule.id }); - } else { - // enabled is null or undefined and we do not touch the rule - } - - if (nextParams.enabled != null) { - return { ...update, enabled: nextParams.enabled }; - } else { - return update; - } -}; diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/crud/update_rules.mock.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/crud/update_rules.mock.ts deleted file mode 100644 index c30fdb107b83ef..00000000000000 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/crud/update_rules.mock.ts +++ /dev/null @@ -1,26 +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 { rulesClientMock } from '@kbn/alerting-plugin/server/mocks'; -import { - getUpdateMachineLearningSchemaMock, - getUpdateRulesSchemaMock, -} from '../../../../../../common/api/detection_engine/model/rule_schema/mocks'; -import { getRuleMock } from '../../../routes/__mocks__/request_responses'; -import { getQueryRuleParams } from '../../../rule_schema/mocks'; - -export const getUpdateRulesOptionsMock = () => ({ - rulesClient: rulesClientMock.create(), - existingRule: getRuleMock(getQueryRuleParams()), - ruleUpdate: getUpdateRulesSchemaMock(), -}); - -export const getUpdateMlRulesOptionsMock = () => ({ - rulesClient: rulesClientMock.create(), - existingRule: getRuleMock(getQueryRuleParams()), - ruleUpdate: getUpdateMachineLearningSchemaMock(), -}); diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/crud/update_rules.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/crud/update_rules.test.ts deleted file mode 100644 index 6abba4dba0ab9e..00000000000000 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/crud/update_rules.test.ts +++ /dev/null @@ -1,81 +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 { RulesClientMock } from '@kbn/alerting-plugin/server/rules_client.mock'; -import { getRuleMock, resolveRuleMock } from '../../../routes/__mocks__/request_responses'; -import { getMlRuleParams, getQueryRuleParams } from '../../../rule_schema/mocks'; -import { updateRules } from './update_rules'; -import { getUpdateRulesOptionsMock, getUpdateMlRulesOptionsMock } from './update_rules.mock'; - -// Failing with rule registry enabled -describe('updateRules', () => { - it('should call rulesClient.disable if the rule was enabled and enabled is false', async () => { - const rulesOptionsMock = getUpdateRulesOptionsMock(); - rulesOptionsMock.ruleUpdate.enabled = false; - (rulesOptionsMock.rulesClient as unknown as RulesClientMock).update.mockResolvedValue( - getRuleMock(getQueryRuleParams()) - ); - - await updateRules(rulesOptionsMock); - - expect(rulesOptionsMock.rulesClient.disable).toHaveBeenCalledWith( - expect.objectContaining({ - id: rulesOptionsMock.ruleUpdate.id, - }) - ); - }); - - it('should call rulesClient.enable if the rule was disabled and enabled is true', async () => { - const baseRulesOptionsMock = getUpdateRulesOptionsMock(); - const rulesOptionsMock = { - ...baseRulesOptionsMock, - existingRule: { - ...baseRulesOptionsMock.existingRule, - enabled: false, - }, - }; - rulesOptionsMock.ruleUpdate.enabled = true; - - (rulesOptionsMock.rulesClient as unknown as RulesClientMock).update.mockResolvedValue( - getRuleMock(getQueryRuleParams()) - ); - - await updateRules(rulesOptionsMock); - - expect(rulesOptionsMock.rulesClient.enable).toHaveBeenCalledWith( - expect.objectContaining({ - id: rulesOptionsMock.ruleUpdate.id, - }) - ); - }); - - it('calls the rulesClient with params', async () => { - const rulesOptionsMock = getUpdateMlRulesOptionsMock(); - rulesOptionsMock.ruleUpdate.enabled = true; - - (rulesOptionsMock.rulesClient as unknown as RulesClientMock).update.mockResolvedValue( - getRuleMock(getMlRuleParams()) - ); - - (rulesOptionsMock.rulesClient as unknown as RulesClientMock).resolve.mockResolvedValue( - resolveRuleMock(getMlRuleParams()) - ); - - await updateRules(rulesOptionsMock); - - expect(rulesOptionsMock.rulesClient.update).toHaveBeenCalledWith( - expect.objectContaining({ - data: expect.objectContaining({ - params: expect.objectContaining({ - type: 'machine_learning', - severity: 'high', - }), - }), - }) - ); - }); -}); 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 deleted file mode 100644 index ec790f9f6f71b4..00000000000000 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/crud/update_rules.ts +++ /dev/null @@ -1,97 +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. - */ - -/* eslint-disable complexity */ -import type { PartialRule, RulesClient } from '@kbn/alerting-plugin/server'; -import { DEFAULT_MAX_SIGNALS } from '../../../../../../common/constants'; -import type { RuleUpdateProps } from '../../../../../../common/api/detection_engine/model/rule_schema'; -import { transformRuleToAlertAction } from '../../../../../../common/detection_engine/transform_actions'; - -import type { InternalRuleUpdate, RuleParams, RuleAlertType } from '../../../rule_schema'; -import { transformToActionFrequency } from '../../normalization/rule_actions'; -import { typeSpecificSnakeToCamel } from '../../normalization/rule_converters'; -import { addEcsToRequiredFields } from '../../utils/utils'; - -export interface UpdateRulesOptions { - rulesClient: RulesClient; - existingRule: RuleAlertType | null | undefined; - ruleUpdate: RuleUpdateProps; - allowMissingConnectorSecrets?: boolean; -} - -export const updateRules = async ({ - rulesClient, - existingRule, - ruleUpdate, - allowMissingConnectorSecrets, -}: UpdateRulesOptions): Promise | null> => { - if (existingRule == null) { - return null; - } - - const alertActions = - ruleUpdate.actions?.map((action) => transformRuleToAlertAction(action)) ?? []; - const actions = transformToActionFrequency(alertActions, ruleUpdate.throttle); - - const typeSpecificParams = typeSpecificSnakeToCamel(ruleUpdate); - const enabled = ruleUpdate.enabled ?? true; - - const newInternalRule: InternalRuleUpdate = { - name: ruleUpdate.name, - tags: ruleUpdate.tags ?? [], - params: { - author: ruleUpdate.author ?? [], - buildingBlockType: ruleUpdate.building_block_type, - description: ruleUpdate.description, - ruleId: existingRule.params.ruleId, - falsePositives: ruleUpdate.false_positives ?? [], - from: ruleUpdate.from ?? 'now-6m', - investigationFields: ruleUpdate.investigation_fields, - // Unlike the create route, immutable comes from the existing rule here - immutable: existingRule.params.immutable, - license: ruleUpdate.license, - outputIndex: ruleUpdate.output_index ?? '', - timelineId: ruleUpdate.timeline_id, - timelineTitle: ruleUpdate.timeline_title, - meta: ruleUpdate.meta, - maxSignals: ruleUpdate.max_signals ?? DEFAULT_MAX_SIGNALS, - relatedIntegrations: ruleUpdate.related_integrations ?? [], - requiredFields: addEcsToRequiredFields(ruleUpdate.required_fields), - riskScore: ruleUpdate.risk_score, - riskScoreMapping: ruleUpdate.risk_score_mapping ?? [], - ruleNameOverride: ruleUpdate.rule_name_override, - setup: ruleUpdate.setup, - severity: ruleUpdate.severity, - severityMapping: ruleUpdate.severity_mapping ?? [], - threat: ruleUpdate.threat ?? [], - timestampOverride: ruleUpdate.timestamp_override, - timestampOverrideFallbackDisabled: ruleUpdate.timestamp_override_fallback_disabled, - to: ruleUpdate.to ?? 'now', - references: ruleUpdate.references ?? [], - namespace: ruleUpdate.namespace, - note: ruleUpdate.note, - version: ruleUpdate.version ?? existingRule.params.version, - exceptionsList: ruleUpdate.exceptions_list ?? [], - ...typeSpecificParams, - }, - schedule: { interval: ruleUpdate.interval ?? '5m' }, - actions, - }; - - const update = await rulesClient.update({ - id: existingRule.id, - data: newInternalRule, - allowMissingConnectorSecrets, - }); - - if (existingRule.enabled && enabled === false) { - await rulesClient.disable({ id: existingRule.id }); - } else if (!existingRule.enabled && enabled === true) { - await rulesClient.enable({ id: existingRule.id }); - } - return { ...update, enabled }; -}; diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/import/import_rules_utils.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/import/import_rules_utils.test.ts index 7842febda68212..4ac14268a762be 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/import/import_rules_utils.test.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/import/import_rules_utils.test.ts @@ -9,31 +9,19 @@ import { getImportRulesSchemaMock } from '../../../../../../common/api/detection import { getQueryRuleParams } from '../../../rule_schema/mocks'; import { requestContextMock } from '../../../routes/__mocks__'; -import { - getRuleMock, - getEmptyFindResult, - getFindResultWithSingleHit, - getFindResultWithMultiHits, -} from '../../../routes/__mocks__/request_responses'; +import { getRuleMock, getEmptyFindResult } from '../../../routes/__mocks__/request_responses'; -import { createRules } from '../crud/create_rules'; -import { updateRules } from '../crud/update_rules'; import { importRules } from './import_rules_utils'; - -jest.mock('../crud/create_rules'); -jest.mock('../crud/update_rules'); +import { createBulkErrorObject } from '../../../routes/utils'; describe('importRules', () => { - const mlAuthz = { - validateRuleType: jest - .fn() - .mockResolvedValue({ valid: true, message: 'mocked validation message' }), - }; const { clients, context } = requestContextMock.createTools(); + const importedRule = getRuleMock(getQueryRuleParams()); beforeEach(() => { clients.rulesClient.find.mockResolvedValue(getEmptyFindResult()); - clients.rulesClient.update.mockResolvedValue(getRuleMock(getQueryRuleParams())); + clients.rulesClient.update.mockResolvedValue(importedRule); + clients.rulesManagementClient.importRule.mockResolvedValue(importedRule); clients.actionsClient.getAll.mockResolvedValue([]); jest.clearAllMocks(); @@ -43,9 +31,8 @@ describe('importRules', () => { const result = await importRules({ ruleChunks: [], rulesResponseAcc: [], - mlAuthz, overwriteRules: false, - rulesClient: context.alerting.getRulesClient(), + rulesManagementClient: context.securitySolution.getRulesManagementClient(), existingLists: {}, }); @@ -56,9 +43,8 @@ describe('importRules', () => { const result = await importRules({ ruleChunks: [[new Error('error importing')]], rulesResponseAcc: [], - mlAuthz, overwriteRules: false, - rulesClient: context.alerting.getRulesClient(), + rulesManagementClient: context.securitySolution.getRulesManagementClient(), existingLists: {}, }); @@ -73,181 +59,43 @@ describe('importRules', () => { ]); }); - it('creates rule if no matching existing rule found', async () => { - const result = await importRules({ - ruleChunks: [[getImportRulesSchemaMock({ rule_id: 'rule-1' })]], - rulesResponseAcc: [], - mlAuthz, - overwriteRules: false, - rulesClient: context.alerting.getRulesClient(), - existingLists: {}, + it('returns 409 error if ruleManagementClient throws with 409 - existing rule', async () => { + clients.rulesManagementClient.importRule.mockImplementationOnce(async () => { + throw createBulkErrorObject({ + ruleId: importedRule.params.ruleId, + statusCode: 409, + message: `rule_id: "${importedRule.params.ruleId}" already exists`, + }); }); - - expect(result).toEqual([{ rule_id: 'rule-1', status_code: 200 }]); - expect(createRules).toHaveBeenCalled(); - expect(updateRules).not.toHaveBeenCalled(); - }); - - it('reports error if "overwriteRules" is "false" and matching rule found', async () => { - clients.rulesClient.find.mockResolvedValue(getFindResultWithSingleHit()); - + const ruleChunk = [getImportRulesSchemaMock({ rule_id: importedRule.params.ruleId })]; const result = await importRules({ - ruleChunks: [[getImportRulesSchemaMock({ rule_id: 'rule-1' })]], + ruleChunks: [ruleChunk], rulesResponseAcc: [], - mlAuthz, overwriteRules: false, - rulesClient: context.alerting.getRulesClient(), - existingLists: {}, - }); - - expect(result).toEqual([ - { - error: { message: 'rule_id: "rule-1" already exists', status_code: 409 }, - rule_id: 'rule-1', - }, - ]); - expect(createRules).not.toHaveBeenCalled(); - expect(updateRules).not.toHaveBeenCalled(); - }); - - it('updates rule if "overwriteRules" is "true" and matching rule found', async () => { - clients.rulesClient.find.mockResolvedValue(getFindResultWithSingleHit()); - - const result = await importRules({ - ruleChunks: [ - [ - getImportRulesSchemaMock({ - rule_id: 'rule-1', - }), - ], - ], - rulesResponseAcc: [], - mlAuthz, - overwriteRules: true, - rulesClient: context.alerting.getRulesClient(), - existingLists: {}, - }); - - expect(result).toEqual([{ rule_id: 'rule-1', status_code: 200 }]); - expect(createRules).not.toHaveBeenCalled(); - expect(updateRules).toHaveBeenCalled(); - }); - - /** - * Existing rule may have nullable fields set to a value (e.g. `timestamp_override` is set to `some.value`) but - * a rule to import doesn't have these fields set (e.g. `timestamp_override` is NOT present at all in the ndjson file). - * We expect the updated rule won't have such fields preserved (e.g. `timestamp_override` will be removed). - * - * Unit test is only able to check `updateRules()` receives a proper update object. - */ - it('ensures overwritten rule DOES NOT preserve fields missed in the imported rule when "overwriteRules" is "true" and matching rule found', async () => { - const existingRule = getRuleMock( - getQueryRuleParams({ - timestampOverride: 'some.value', - }) - ); - - clients.rulesClient.find.mockResolvedValue( - getFindResultWithMultiHits({ data: [existingRule] }) - ); - - const result = await importRules({ - ruleChunks: [ - [ - { - ...getImportRulesSchemaMock(), - rule_id: 'rule-1', - }, - ], - ], - rulesResponseAcc: [], - mlAuthz, - overwriteRules: true, - rulesClient: context.alerting.getRulesClient(), - existingLists: {}, - }); - - expect(result).toEqual([{ rule_id: 'rule-1', status_code: 200 }]); - expect(createRules).not.toHaveBeenCalled(); - expect(updateRules).toHaveBeenCalledWith( - expect.objectContaining({ - ruleUpdate: expect.not.objectContaining({ - timestamp_override: expect.anything(), - timestampOverride: expect.anything(), - }), - }) - ); - }); - - it('reports error if rulesClient throws', async () => { - clients.rulesClient.find.mockRejectedValue(new Error('error reading rule')); - - const result = await importRules({ - ruleChunks: [[getImportRulesSchemaMock({ rule_id: 'rule-1' })]], - rulesResponseAcc: [], - mlAuthz, - overwriteRules: true, - rulesClient: context.alerting.getRulesClient(), + rulesManagementClient: context.securitySolution.getRulesManagementClient(), existingLists: {}, }); expect(result).toEqual([ { error: { - message: 'error reading rule', - status_code: 400, + message: `rule_id: "${importedRule.params.ruleId}" already exists`, + status_code: 409, }, - rule_id: 'rule-1', + rule_id: importedRule.params.ruleId, }, ]); - expect(createRules).not.toHaveBeenCalled(); - expect(updateRules).not.toHaveBeenCalled(); }); - - it('reports error if "createRules" throws', async () => { - (createRules as jest.Mock).mockRejectedValue(new Error('error creating rule')); - + it('creates rule if no matching existing rule found', async () => { + const ruleChunk = [getImportRulesSchemaMock({ rule_id: importedRule.params.ruleId })]; const result = await importRules({ - ruleChunks: [[getImportRulesSchemaMock({ rule_id: 'rule-1' })]], + ruleChunks: [ruleChunk], rulesResponseAcc: [], - mlAuthz, overwriteRules: false, - rulesClient: context.alerting.getRulesClient(), - existingLists: {}, - }); - - expect(result).toEqual([ - { - error: { - message: 'error creating rule', - status_code: 400, - }, - rule_id: 'rule-1', - }, - ]); - }); - - it('reports error if "updateRules" throws', async () => { - (updateRules as jest.Mock).mockRejectedValue(new Error('import rule error')); - clients.rulesClient.find.mockResolvedValue(getFindResultWithSingleHit()); - - const result = await importRules({ - ruleChunks: [[getImportRulesSchemaMock({ rule_id: 'rule-1' })]], - rulesResponseAcc: [], - mlAuthz, - overwriteRules: true, - rulesClient: context.alerting.getRulesClient(), + rulesManagementClient: context.securitySolution.getRulesManagementClient(), existingLists: {}, }); - expect(result).toEqual([ - { - error: { - message: 'import rule error', - status_code: 400, - }, - rule_id: 'rule-1', - }, - ]); + expect(result).toEqual([{ rule_id: importedRule.params.ruleId, status_code: 200 }]); }); }); diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/import/import_rules_utils.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/import/import_rules_utils.ts index 179c4069596e77..5c1ac63ec8a70d 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/import/import_rules_utils.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/import/import_rules_utils.ts @@ -12,17 +12,11 @@ import type { ExceptionListSchema, } from '@kbn/securitysolution-io-ts-list-types'; -import type { RulesClient } from '@kbn/alerting-plugin/server'; - import type { RuleToImport } from '../../../../../../common/api/detection_engine/rule_management'; import type { ImportRuleResponse } from '../../../routes/utils'; import { createBulkErrorObject } from '../../../routes/utils'; -import { createRules } from '../crud/create_rules'; -import { readRules } from '../crud/read_rules'; -import { updateRules } from '../crud/update_rules'; -import type { MlAuthz } from '../../../../machine_learning/authz'; -import { throwAuthzError } from '../../../../machine_learning/validation'; import { checkRuleExceptionReferences } from './check_rule_exception_references'; +import type { IRulesManagementClient } from '../rule_management/rules_management_client'; export type PromiseFromStreams = RuleToImport | Error; export interface RuleExceptionsPromiseFromStreams { @@ -40,7 +34,7 @@ export interface RuleExceptionsPromiseFromStreams { * @param mlAuthz {object} * @param overwriteRules {boolean} - whether to overwrite existing rules * with imported rules if their rule_id matches - * @param rulesClient {object} + * @param rulesManagementClient {object} * @param existingLists {object} - all exception lists referenced by * rules that were found to exist * @returns {Promise} an array of error and success messages from import @@ -48,17 +42,15 @@ export interface RuleExceptionsPromiseFromStreams { export const importRules = async ({ ruleChunks, rulesResponseAcc, - mlAuthz, overwriteRules, - rulesClient, + rulesManagementClient, existingLists, allowMissingConnectorSecrets, }: { ruleChunks: PromiseFromStreams[][]; rulesResponseAcc: ImportRuleResponse[]; - mlAuthz: MlAuthz; overwriteRules: boolean; - rulesClient: RulesClient; + rulesManagementClient: IRulesManagementClient; existingLists: Record; allowMissingConnectorSecrets?: boolean; }) => { @@ -96,54 +88,28 @@ export const importRules = async ({ importRuleResponse = [...importRuleResponse, ...exceptionErrors]; - throwAuthzError(await mlAuthz.validateRuleType(parsedRule.type)); - const rule = await readRules({ - rulesClient, - ruleId: parsedRule.rule_id, - id: undefined, + const importedRule = await rulesManagementClient.importRule({ + ruleToImport: { + ...parsedRule, + exceptions_list: [...exceptions], + }, + overwriteRules, + options: { + allowMissingConnectorSecrets, + }, }); - if (rule == null) { - await createRules({ - rulesClient, - params: { - ...parsedRule, - exceptions_list: [...exceptions], - }, - allowMissingConnectorSecrets, - }); - resolve({ - rule_id: parsedRule.rule_id, - status_code: 200, - }); - } else if (rule != null && overwriteRules) { - await updateRules({ - rulesClient, - existingRule: rule, - ruleUpdate: { - ...parsedRule, - exceptions_list: [...exceptions], - }, - }); - resolve({ - rule_id: parsedRule.rule_id, - status_code: 200, - }); - } else if (rule != null) { - resolve( - createBulkErrorObject({ - ruleId: parsedRule.rule_id, - statusCode: 409, - message: `rule_id: "${parsedRule.rule_id}" already exists`, - }) - ); - } + resolve({ + rule_id: importedRule.params.ruleId, + status_code: 200, + }); } catch (err) { + const { error, statusCode, message } = err; resolve( createBulkErrorObject({ ruleId: parsedRule.rule_id, - statusCode: err.statusCode ?? 400, - message: err.message, + statusCode: statusCode ?? error?.status_code ?? 400, + message: message ?? error?.message ?? 'unknown error', }) ); } diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/rule_management/__mocks__/read_rules.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/rule_management/__mocks__/read_rules.ts new file mode 100644 index 00000000000000..7a35c8db1a001a --- /dev/null +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/rule_management/__mocks__/read_rules.ts @@ -0,0 +1,21 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import type { SanitizedRule } from '@kbn/alerting-plugin/common'; +import { getQueryRuleParams } from '../../../../rule_schema/mocks'; +import { getRuleMock } from '../../../../routes/__mocks__/request_responses'; +import type { RuleParams } from '../../../../rule_schema'; + +export const readRules = jest + .fn() + .mockImplementation(async (): Promise | null> => { + const mockRule: SanitizedRule = getRuleMock({ + ...getQueryRuleParams(), + ruleId: 'rule-id', + }); + return mockRule; + }); diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/rule_management/__mocks__/rules_management_client.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/rule_management/__mocks__/rules_management_client.ts new file mode 100644 index 00000000000000..629bd43f84c2e9 --- /dev/null +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/rule_management/__mocks__/rules_management_client.ts @@ -0,0 +1,74 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import type { + IRulesManagementClient, + CreateRuleOptions, + _UpdateRuleProps, + _PatchRuleProps, +} from '../rules_management_client'; +import type { RulesClient } from '@kbn/alerting-plugin/server'; +import type { + RuleCreateProps, + RuleObjectId, +} from '../../../../../../../common/api/detection_engine'; +import type { PrebuiltRuleAsset } from '../../../../prebuilt_rules'; +import type { RuleAlertType } from '../../../../rule_schema'; + +export type RulesManagementClientMock = jest.Mocked; + +const createRulesManagementClientMock = () => { + const mocked: RulesManagementClientMock = { + createCustomRule: jest.fn(), + createPrebuiltRule: jest.fn(), + updateRule: jest.fn(), + patchRule: jest.fn(), + deleteRule: jest.fn(), + upgradePrebuiltRule: jest.fn(), + importRule: jest.fn(), + }; + return mocked; +}; + +export const rulesManagementClientMock: { + create: () => RulesManagementClientMock; +} = { + create: createRulesManagementClientMock, +}; + +/* Mocks for internal methods */ +export const _createRule: jest.Mock< + ( + rulesClient: RulesClient, + params: RuleCreateProps, + options: CreateRuleOptions + ) => Promise +> = jest.fn(); + +export const _updateRule: jest.Mock< + (rulesClient: RulesClient, updateRulePayload: _UpdateRuleProps) => Promise +> = jest.fn(); + +export const patchRuleMock: jest.Mock< + (rulesClient: RulesClient, patchRulePayload: _PatchRuleProps) => Promise +> = jest.fn(); + +export const _upgradePrebuiltRuleWithTypeChange: jest.Mock< + ( + rulesClient: RulesClient, + ruleAsset: PrebuiltRuleAsset, + existingRule: RuleAlertType + ) => Promise +> = jest.fn(); + +export const _toggleRuleEnabledOnUpdate: jest.Mock< + (rulesClient: RulesClient, existingRule: RuleAlertType, enabled: boolean) => Promise +> = jest.fn(); + +export const _deleteRule: jest.Mock< + (rulesClient: RulesClient, deleteRulePayload: { ruleId: RuleObjectId }) => Promise +> = jest.fn(); diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/rule_management/create_custom_rule.rule_management_client.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/rule_management/create_custom_rule.rule_management_client.test.ts new file mode 100644 index 00000000000000..450c767f7b3ce6 --- /dev/null +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/rule_management/create_custom_rule.rule_management_client.test.ts @@ -0,0 +1,155 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { rulesClientMock } from '@kbn/alerting-plugin/server/mocks'; + +import { createCustomRule } from './rules_management_client'; + +import { + getCreateRulesSchemaMock, + getCreateMachineLearningRulesSchemaMock, + getCreateThreatMatchRulesSchemaMock, +} from '../../../../../../common/api/detection_engine/model/rule_schema/mocks'; +import { DEFAULT_INDICATOR_SOURCE_PATH } from '../../../../../../common/constants'; +import { buildMlAuthz } from '../../../../machine_learning/authz'; +import { throwAuthzError } from '../../../../machine_learning/validation'; + +jest.mock('../../../../machine_learning/authz'); +jest.mock('../../../../machine_learning/validation'); + +describe('RuleManagementClient.createCustomRule', () => { + let rulesClient: ReturnType; + const mlAuthz = (buildMlAuthz as jest.Mock)(); + + beforeEach(() => { + jest.resetAllMocks(); + rulesClient = rulesClientMock.create(); + }); + + it('should create a rule with the correct parameters and options', async () => { + const params = getCreateRulesSchemaMock(); + + await createCustomRule(rulesClient, { params }, mlAuthz); + + expect(rulesClient.create).toHaveBeenCalledWith( + expect.objectContaining({ + data: expect.objectContaining({ + enabled: true, + params: expect.objectContaining({ + description: params.description, + immutable: false, + }), + }), + options: {}, + allowMissingConnectorSecrets: undefined, + }) + ); + }); + + it('throws if mlAuth fails', async () => { + (throwAuthzError as jest.Mock).mockImplementationOnce(() => { + throw new Error('mocked MLAuth error'); + }); + + await expect( + createCustomRule(rulesClient, { params: getCreateMachineLearningRulesSchemaMock() }, mlAuthz) + ).rejects.toThrow('mocked MLAuth error'); + + expect(rulesClient.create).not.toHaveBeenCalled(); + }); + + it('calls the rulesClient with legacy ML params', async () => { + await createCustomRule( + rulesClient, + { params: getCreateMachineLearningRulesSchemaMock() }, + mlAuthz + ); + + expect(rulesClient.create).toHaveBeenCalledWith( + expect.objectContaining({ + data: expect.objectContaining({ + params: expect.objectContaining({ + anomalyThreshold: 58, + machineLearningJobId: ['typical-ml-job-id'], + immutable: false, + }), + }), + options: {}, + allowMissingConnectorSecrets: undefined, + }) + ); + }); + + it('calls the rulesClient with ML params', async () => { + await createCustomRule( + rulesClient, + { + params: { + ...getCreateMachineLearningRulesSchemaMock(), + machine_learning_job_id: ['new_job_1', 'new_job_2'], + }, + }, + mlAuthz + ); + + expect(rulesClient.create).toHaveBeenCalledWith( + expect.objectContaining({ + data: expect.objectContaining({ + params: expect.objectContaining({ + anomalyThreshold: 58, + machineLearningJobId: ['new_job_1', 'new_job_2'], + immutable: false, + }), + }), + options: {}, + allowMissingConnectorSecrets: undefined, + }) + ); + }); + + it('populates a threatIndicatorPath value for threat_match rule if empty', async () => { + const params = getCreateThreatMatchRulesSchemaMock(); + delete params.threat_indicator_path; + + await createCustomRule( + rulesClient, + { + params, + }, + mlAuthz + ); + + expect(rulesClient.create).toHaveBeenCalledWith( + expect.objectContaining({ + data: expect.objectContaining({ + params: expect.objectContaining({ + threatIndicatorPath: DEFAULT_INDICATOR_SOURCE_PATH, + immutable: false, + }), + }), + options: {}, + allowMissingConnectorSecrets: undefined, + }) + ); + }); + + it('does not populate a threatIndicatorPath value for other rules if empty', async () => { + await createCustomRule(rulesClient, { params: getCreateRulesSchemaMock() }, mlAuthz); + expect(rulesClient.create).not.toHaveBeenCalledWith( + expect.objectContaining({ + data: expect.objectContaining({ + params: expect.objectContaining({ + threatIndicatorPath: DEFAULT_INDICATOR_SOURCE_PATH, + immutable: false, + }), + }), + options: {}, + allowMissingConnectorSecrets: undefined, + }) + ); + }); +}); diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/rule_management/create_prebuilt_rule.rule_management_client.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/rule_management/create_prebuilt_rule.rule_management_client.test.ts new file mode 100644 index 00000000000000..ca9ecc6a0814d2 --- /dev/null +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/rule_management/create_prebuilt_rule.rule_management_client.test.ts @@ -0,0 +1,172 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { rulesClientMock } from '@kbn/alerting-plugin/server/mocks'; + +import { createPrebuiltRule } from './rules_management_client'; + +import { + getCreateRulesSchemaMock, + getCreateMachineLearningRulesSchemaMock, + getCreateThreatMatchRulesSchemaMock, +} from '../../../../../../common/api/detection_engine/model/rule_schema/mocks'; +import { DEFAULT_INDICATOR_SOURCE_PATH } from '../../../../../../common/constants'; +import { buildMlAuthz } from '../../../../machine_learning/authz'; +import { throwAuthzError } from '../../../../machine_learning/validation'; + +jest.mock('../../../../machine_learning/authz'); +jest.mock('../../../../machine_learning/validation'); + +describe('RuleManagementClient.createPrebuiltRule', () => { + let rulesClient: ReturnType; + const mlAuthz = (buildMlAuthz as jest.Mock)(); + + beforeEach(() => { + jest.resetAllMocks(); + rulesClient = rulesClientMock.create(); + }); + + it('creates a rule with the correct parameters and options', async () => { + const ruleAsset = { ...getCreateRulesSchemaMock(), version: 1, rule_id: 'rule-id' }; + + await createPrebuiltRule(rulesClient, { ruleAsset }, mlAuthz); + + expect(rulesClient.create).toHaveBeenCalledWith( + expect.objectContaining({ + data: expect.objectContaining({ + enabled: false, + name: ruleAsset.name, + params: expect.objectContaining({ + ruleId: ruleAsset.rule_id, + immutable: true, + }), + }), + options: {}, + allowMissingConnectorSecrets: undefined, + }) + ); + }); + + it('throws if mlAuth fails', async () => { + const ruleAsset = { ...getCreateRulesSchemaMock(), version: 1, rule_id: 'rule-id' }; + (throwAuthzError as jest.Mock).mockImplementationOnce(() => { + throw new Error('mocked MLAuth error'); + }); + + await expect(createPrebuiltRule(rulesClient, { ruleAsset }, mlAuthz)).rejects.toThrow( + 'mocked MLAuth error' + ); + + expect(rulesClient.create).not.toHaveBeenCalled(); + }); + + it('calls the rulesClient with legacy ML params', async () => { + const ruleAsset = { + ...getCreateMachineLearningRulesSchemaMock(), + version: 1, + rule_id: 'rule-id', + }; + await createPrebuiltRule( + rulesClient, + { + ruleAsset, + }, + mlAuthz + ); + + expect(rulesClient.create).toHaveBeenCalledWith( + expect.objectContaining({ + data: expect.objectContaining({ + enabled: false, + params: expect.objectContaining({ + anomalyThreshold: ruleAsset.anomaly_threshold, + machineLearningJobId: [ruleAsset.machine_learning_job_id], + immutable: true, + }), + }), + options: {}, + allowMissingConnectorSecrets: undefined, + }) + ); + }); + + it('calls the rulesClient with ML params', async () => { + const ruleAsset = { + ...getCreateMachineLearningRulesSchemaMock(), + machine_learning_job_id: ['new_job_1', 'new_job_2'], + version: 1, + rule_id: 'rule-id', + }; + await createPrebuiltRule( + rulesClient, + { + ruleAsset, + }, + mlAuthz + ); + + expect(rulesClient.create).toHaveBeenCalledWith( + expect.objectContaining({ + data: expect.objectContaining({ + enabled: false, + params: expect.objectContaining({ + anomalyThreshold: 58, + machineLearningJobId: ['new_job_1', 'new_job_2'], + immutable: true, + }), + }), + options: {}, + allowMissingConnectorSecrets: undefined, + }) + ); + }); + + it('populates a threatIndicatorPath value for threat_match rule if empty', async () => { + const ruleAsset = { ...getCreateThreatMatchRulesSchemaMock(), version: 1, rule_id: 'rule-id' }; + delete ruleAsset.threat_indicator_path; + + await createPrebuiltRule( + rulesClient, + { + ruleAsset, + }, + mlAuthz + ); + + expect(rulesClient.create).toHaveBeenCalledWith( + expect.objectContaining({ + data: expect.objectContaining({ + enabled: false, + params: expect.objectContaining({ + threatIndicatorPath: DEFAULT_INDICATOR_SOURCE_PATH, + immutable: true, + }), + }), + options: {}, + allowMissingConnectorSecrets: undefined, + }) + ); + }); + + it('does not populate a threatIndicatorPath value for other rules if empty', async () => { + const ruleAsset = { ...getCreateRulesSchemaMock(), version: 1, rule_id: 'rule-id' }; + await createPrebuiltRule(rulesClient, { ruleAsset }, mlAuthz); + expect(rulesClient.create).not.toHaveBeenCalledWith( + expect.objectContaining({ + data: expect.objectContaining({ + enabled: false, + params: expect.objectContaining({ + threatIndicatorPath: DEFAULT_INDICATOR_SOURCE_PATH, + immutable: true, + }), + }), + options: {}, + allowMissingConnectorSecrets: undefined, + }) + ); + }); +}); diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/crud/delete_rules.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/rule_management/delete_rule.rule_management_client.test.ts similarity index 52% rename from x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/crud/delete_rules.test.ts rename to x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/rule_management/delete_rule.rule_management_client.test.ts index c34adec573f158..e675d5e01590d2 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/crud/delete_rules.test.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/rule_management/delete_rule.rule_management_client.test.ts @@ -6,24 +6,21 @@ */ import { rulesClientMock } from '@kbn/alerting-plugin/server/mocks'; -import type { DeleteRuleOptions } from './delete_rules'; -import { deleteRules } from './delete_rules'; +import { deleteRule } from './rules_management_client'; -describe('deleteRules', () => { +describe('RuleManagementClient.deleteRule', () => { let rulesClient: ReturnType; beforeEach(() => { rulesClient = rulesClientMock.create(); }); - it('should delete the rule along with its actions, and statuses', async () => { - const options: DeleteRuleOptions = { - ruleId: 'ruleId', - rulesClient, - }; + it('should call rulesClient.delete passing the expected ruleId', async () => { + const ruleId = 'ruleId'; + await deleteRule(rulesClient, { + ruleId, + }); - await deleteRules(options); - - expect(rulesClient.delete).toHaveBeenCalledWith({ id: options.ruleId }); + expect(rulesClient.delete).toHaveBeenCalledWith({ id: ruleId }); }); }); diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/rule_management/import_rule.rule_management_client.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/rule_management/import_rule.rule_management_client.test.ts new file mode 100644 index 00000000000000..891c4f613b3c80 --- /dev/null +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/rule_management/import_rule.rule_management_client.test.ts @@ -0,0 +1,185 @@ +/* + * Copyright 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 { rulesClientMock } from '@kbn/alerting-plugin/server/mocks'; +import { importRule } from './rules_management_client'; +import { readRules } from './read_rules'; +import { getCreateRulesSchemaMock } from '../../../../../../common/api/detection_engine/model/rule_schema/mocks'; +import { getRuleMock } from '../../../routes/__mocks__/request_responses'; +import { getQueryRuleParams } from '../../../rule_schema/mocks'; +import { buildMlAuthz } from '../../../../machine_learning/authz'; +import { throwAuthzError } from '../../../../machine_learning/validation'; + +jest.mock('../../../../machine_learning/authz'); +jest.mock('../../../../machine_learning/validation'); + +jest.mock('./read_rules'); + +describe('RuleManagementClient.importRule', () => { + let rulesClient: ReturnType; + const mlAuthz = (buildMlAuthz as jest.Mock)(); + const immutable = false as const; // Can only take value of false + const allowMissingConnectorSecrets = true; + const ruleToImport = { + ...getCreateRulesSchemaMock(), + tags: ['import-tag'], + rule_id: 'rule-id', + version: 1, + immutable, + }; + const existingRule = getRuleMock({ + ...getQueryRuleParams({ + ruleId: ruleToImport.rule_id, + }), + }); + + beforeEach(() => { + rulesClient = rulesClientMock.create(); + }); + + it('calls rulesClient.create with the correct parameters when rule_id does not match an installed rule', async () => { + (readRules as jest.Mock).mockResolvedValue(null); + await importRule( + rulesClient, + { + ruleToImport, + overwriteRules: true, + options: { allowMissingConnectorSecrets }, + }, + mlAuthz + ); + + expect(rulesClient.create).toHaveBeenCalledWith( + expect.objectContaining({ + data: expect.objectContaining({ + name: ruleToImport.name, + tags: ruleToImport.tags, + params: expect.objectContaining({ + immutable, + ruleId: ruleToImport.rule_id, + version: ruleToImport.version, + }), + }), + options: {}, + allowMissingConnectorSecrets, + }) + ); + }); + + it('throws if mlAuth fails', async () => { + (throwAuthzError as jest.Mock).mockImplementationOnce(() => { + throw new Error('mocked MLAuth error'); + }); + + await expect( + importRule( + rulesClient, + { + ruleToImport, + overwriteRules: true, + options: { allowMissingConnectorSecrets }, + }, + mlAuthz + ) + ).rejects.toThrow('mocked MLAuth error'); + + expect(rulesClient.create).not.toHaveBeenCalled(); + expect(rulesClient.update).not.toHaveBeenCalled(); + }); + + describe('when rule_id matches an installed rule', () => { + it('calls rulesClient.update with the correct parameters when overwriteRules is true', async () => { + (readRules as jest.Mock).mockResolvedValue(existingRule); + await importRule( + rulesClient, + { + ruleToImport, + overwriteRules: true, + options: { allowMissingConnectorSecrets }, + }, + mlAuthz + ); + + expect(rulesClient.update).toHaveBeenCalledWith( + expect.objectContaining({ + data: expect.objectContaining({ + name: ruleToImport.name, + tags: ruleToImport.tags, + params: expect.objectContaining({ + index: ruleToImport.index, + description: ruleToImport.description, + }), + }), + id: existingRule.id, + }) + ); + }); + + /** + * Existing rule may have nullable fields set to a value (e.g. `timestamp_override` is set to `some.value`) but + * a rule to import doesn't have these fields set (e.g. `timestamp_override` is NOT present at all in the ndjson file). + * We expect the updated rule won't have such fields preserved (e.g. `timestamp_override` will be removed). + * + * Unit test is only able to check `updateRules()` receives a proper update object. + */ + it('ensures overwritten rule DOES NOT preserve fields missed in the imported rule when "overwriteRules" is "true" and matching rule found', async () => { + const existingRuleWithTimestampOverride = { + ...existingRule, + params: { + ...existingRule.params, + timestamp_override: '2020-01-01T00:00:00Z', + }, + }; + (readRules as jest.Mock).mockResolvedValue(existingRuleWithTimestampOverride); + + await importRule( + rulesClient, + { + ruleToImport: { + ...ruleToImport, + timestamp_override: undefined, + }, + overwriteRules: true, + options: { allowMissingConnectorSecrets }, + }, + mlAuthz + ); + + expect(rulesClient.create).not.toHaveBeenCalled(); + expect(rulesClient.update).toHaveBeenCalledWith( + expect.objectContaining({ + id: existingRule.id, + data: expect.not.objectContaining({ + timestamp_override: expect.anything(), + timestampOverride: expect.anything(), + }), + }) + ); + }); + + it('rejects when overwriteRules is false', async () => { + (readRules as jest.Mock).mockResolvedValue(existingRule); + await expect( + importRule( + rulesClient, + { + ruleToImport, + overwriteRules: false, + options: { allowMissingConnectorSecrets }, + }, + mlAuthz + ) + ).rejects.toMatchObject({ + error: { + status_code: 409, + message: `rule_id: "${ruleToImport.rule_id}" already exists`, + }, + rule_id: ruleToImport.rule_id, + }); + }); + }); +}); 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/rule_management/patch_rule.rule_management_client.test.ts similarity index 64% rename from x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/crud/patch_rules.test.ts rename to x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/rule_management/patch_rule.rule_management_client.test.ts index 3191d25a40e021..a27d83bed85ca4 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/rule_management/patch_rule.rule_management_client.test.ts @@ -7,59 +7,69 @@ import { rulesClientMock } from '@kbn/alerting-plugin/server/rules_client.mock'; -import { patchRules } from './patch_rules'; import { getRuleMock } from '../../../routes/__mocks__/request_responses'; import { getMlRuleParams, getQueryRuleParams } from '../../../rule_schema/mocks'; import { getCreateMachineLearningRulesSchemaMock, getCreateRulesSchemaMock, } from '../../../../../../common/api/detection_engine/model/rule_schema/mocks'; +import { patchRule } from './rules_management_client'; +import { readRules } from './read_rules'; +import { buildMlAuthz } from '../../../../machine_learning/authz'; +import { throwAuthzError } from '../../../../machine_learning/validation'; -describe('patchRules', () => { - it('should call rulesClient.disable if the rule was enabled and enabled is false', async () => { - const rulesClient = rulesClientMock.create(); - const nextParams = { - ...getCreateRulesSchemaMock(), - enabled: false, - }; - const existingRule = { - ...getRuleMock(getQueryRuleParams()), - enabled: true, - }; +jest.mock('../../../../machine_learning/authz'); +jest.mock('../../../../machine_learning/validation'); + +jest.mock('./read_rules'); + +describe('RuleManagementClient.patchRule', () => { + let rulesClient: ReturnType; + const mlAuthz = (buildMlAuthz as jest.Mock)(); + + beforeEach(() => { + rulesClient = rulesClientMock.create(); + }); + + it('calls the rulesClient with expected params', async () => { + const nextParams = getCreateRulesSchemaMock(); + const existingRule = getRuleMock(getQueryRuleParams()); + (readRules as jest.Mock).mockResolvedValueOnce(existingRule); rulesClient.update.mockResolvedValue(getRuleMock(getQueryRuleParams())); - await patchRules({ rulesClient, nextParams, existingRule }); - expect(rulesClient.disable).toHaveBeenCalledWith( + + await patchRule(rulesClient, { nextParams }, mlAuthz); + + expect(rulesClient.update).toHaveBeenCalledWith( expect.objectContaining({ - id: existingRule.id, + data: expect.objectContaining({ + name: nextParams.name, + params: expect.objectContaining({ + ruleId: nextParams.rule_id, + description: nextParams.description, + }), + }), }) ); }); - it('should call rulesClient.enable if the rule was disabled and enabled is true', async () => { - const rulesClient = rulesClientMock.create(); - const nextParams = { - ...getCreateRulesSchemaMock(), - enabled: true, - }; - const existingRule = { - ...getRuleMock(getQueryRuleParams()), - enabled: false, - }; + it('returns rule enabled: true if the nexParams have enabled: true', async () => { + const nextParams = { ...getCreateRulesSchemaMock(), enabled: true }; + const existingRule = getRuleMock(getQueryRuleParams()); + (readRules as jest.Mock).mockResolvedValueOnce(existingRule); rulesClient.update.mockResolvedValue(getRuleMock(getQueryRuleParams())); - await patchRules({ rulesClient, nextParams, existingRule }); - expect(rulesClient.enable).toHaveBeenCalledWith( - expect.objectContaining({ - id: existingRule.id, - }) - ); + + const rule = await patchRule(rulesClient, { nextParams }, mlAuthz); + + expect(rule.enabled).toBe(true); }); it('calls the rulesClient with legacy ML params', async () => { - const rulesClient = rulesClientMock.create(); const nextParams = getCreateMachineLearningRulesSchemaMock(); const existingRule = getRuleMock(getMlRuleParams()); + (readRules as jest.Mock).mockResolvedValueOnce(existingRule); rulesClient.update.mockResolvedValue(getRuleMock(getMlRuleParams())); - await patchRules({ rulesClient, nextParams, existingRule }); + + await patchRule(rulesClient, { nextParams }, mlAuthz); expect(rulesClient.update).toHaveBeenCalledWith( expect.objectContaining({ data: expect.objectContaining({ @@ -73,14 +83,16 @@ describe('patchRules', () => { }); it('calls the rulesClient with new ML params', async () => { - const rulesClient = rulesClientMock.create(); const nextParams = { ...getCreateMachineLearningRulesSchemaMock(), machine_learning_job_id: ['new_job_1', 'new_job_2'], }; const existingRule = getRuleMock(getMlRuleParams()); + (readRules as jest.Mock).mockResolvedValueOnce(existingRule); rulesClient.update.mockResolvedValue(getRuleMock(getMlRuleParams())); - await patchRules({ rulesClient, nextParams, existingRule }); + + await patchRule(rulesClient, { nextParams }, mlAuthz); + expect(rulesClient.update).toHaveBeenCalledWith( expect.objectContaining({ data: expect.objectContaining({ @@ -93,9 +105,67 @@ describe('patchRules', () => { ); }); + it('should call rulesClient.disable if the rule was enabled and enabled is false', async () => { + const nextParams = { + ...getCreateRulesSchemaMock(), + enabled: false, + }; + const existingRule = { + ...getRuleMock(getQueryRuleParams()), + enabled: true, + }; + (readRules as jest.Mock).mockResolvedValueOnce(existingRule); + rulesClient.update.mockResolvedValue(getRuleMock(getQueryRuleParams())); + + await patchRule(rulesClient, { nextParams }, mlAuthz); + + expect(rulesClient.disable).toHaveBeenCalledWith( + expect.objectContaining({ + id: existingRule.id, + }) + ); + }); + + it('should call rulesClient.enable if the rule was disabled and enabled is true', async () => { + const nextParams = { + ...getCreateRulesSchemaMock(), + enabled: true, + }; + const existingRule = { + ...getRuleMock(getQueryRuleParams()), + enabled: false, + }; + (readRules as jest.Mock).mockResolvedValueOnce(existingRule); + rulesClient.update.mockResolvedValue(getRuleMock(getQueryRuleParams())); + + await patchRule(rulesClient, { nextParams }, mlAuthz); + + expect(rulesClient.enable).toHaveBeenCalledWith( + expect.objectContaining({ + id: existingRule.id, + }) + ); + }); + + it('throws if mlAuth fails', async () => { + (throwAuthzError as jest.Mock).mockImplementationOnce(() => { + throw new Error('mocked MLAuth error'); + }); + + const nextParams = { + ...getCreateRulesSchemaMock(), + enabled: true, + }; + + await expect(patchRule(rulesClient, { nextParams }, mlAuthz)).rejects.toThrow( + 'mocked MLAuth error' + ); + + expect(rulesClient.create).not.toHaveBeenCalled(); + }); + describe('regression tests', () => { it("updates the rule's actions if provided", async () => { - const rulesClient = rulesClientMock.create(); const nextParams = { ...getCreateRulesSchemaMock(), actions: [ @@ -110,8 +180,11 @@ describe('patchRules', () => { ], }; const existingRule = getRuleMock(getQueryRuleParams()); + (readRules as jest.Mock).mockResolvedValueOnce(existingRule); rulesClient.update.mockResolvedValue(getRuleMock(getQueryRuleParams())); - await patchRules({ rulesClient, nextParams, existingRule }); + + await patchRule(rulesClient, { nextParams }, mlAuthz); + expect(rulesClient.update).toHaveBeenCalledWith( expect.objectContaining({ data: expect.objectContaining({ @@ -132,7 +205,6 @@ describe('patchRules', () => { }); it('does not update actions if none are specified', async () => { - const rulesClient = rulesClientMock.create(); const nextParams = getCreateRulesSchemaMock(); delete nextParams.actions; const existingRule = getRuleMock(getQueryRuleParams()); @@ -146,8 +218,11 @@ describe('patchRules', () => { group: 'default', }, ]; + (readRules as jest.Mock).mockResolvedValueOnce(existingRule); rulesClient.update.mockResolvedValue(getRuleMock(getQueryRuleParams())); - await patchRules({ rulesClient, nextParams, existingRule }); + + await patchRule(rulesClient, { nextParams }, mlAuthz); + expect(rulesClient.update).toHaveBeenCalledWith( expect.objectContaining({ data: expect.objectContaining({ diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/crud/read_rules.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/rule_management/read_rules.test.ts similarity index 100% rename from x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/crud/read_rules.test.ts rename to x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/rule_management/read_rules.test.ts diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/crud/read_rules.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/rule_management/read_rules.ts similarity index 100% rename from x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/crud/read_rules.ts rename to x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/rule_management/read_rules.ts diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/rule_management/rules_management_client.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/rule_management/rules_management_client.ts new file mode 100644 index 00000000000000..c88e6883f14300 --- /dev/null +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/rule_management/rules_management_client.ts @@ -0,0 +1,430 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import type { RulesClient } from '@kbn/alerting-plugin/server'; + +import type { KibanaRequest } from '@kbn/core-http-server'; +import type { SavedObjectsClientContract } from '@kbn/core-saved-objects-api-server'; +import type { LicensingApiRequestHandlerContext } from '@kbn/licensing-plugin/server'; +import type { SharedServices } from '@kbn/ml-plugin/server/shared_services'; +import type { Type } from '@kbn/securitysolution-io-ts-alerting-types'; +import type { MlAuthz } from '../../../../machine_learning/authz'; +import { buildMlAuthz } from '../../../../machine_learning/authz'; +import type { + RuleCreateProps, + RuleObjectId, + RuleToImport, + PatchRuleRequestBody, + RuleUpdateProps, +} from '../../../../../../common/api/detection_engine'; + +import type { PrebuiltRuleAsset } from '../../../prebuilt_rules'; + +import { readRules } from './read_rules'; + +import { + convertPatchAPIToInternalSchema, + convertUpdateAPIToInternalSchema, + convertCreateAPIToInternalSchema, +} from '../../normalization/rule_converters'; +import { transformAlertToRuleAction } from '../../../../../../common/detection_engine/transform_actions'; +import type { RuleAlertType, RuleParams } from '../../../rule_schema'; +import { createBulkErrorObject } from '../../../routes/utils'; +import { getIdError } from '../../utils/utils'; +import { throwAuthzError } from '../../../../machine_learning/validation'; + +class ClientError extends Error { + public readonly statusCode: number; + constructor(message: string, statusCode: number) { + super(message); + this.statusCode = statusCode; + } +} + +export interface CreateRuleOptions { + /* Optionally pass an ID to use for the rule document. If not provided, an ID will be generated. */ + /* This is the ES document ID, NOT the rule_id */ + id?: string; + immutable?: boolean; + defaultEnabled?: boolean; + allowMissingConnectorSecrets?: boolean; +} + +export interface _UpdateRuleProps { + existingRule: RuleAlertType; + ruleUpdate: RuleUpdateProps; +} + +export interface _PatchRuleProps { + existingRule: RuleAlertType; + nextParams: PatchRuleRequestBody; +} + +interface CreateCustomRuleProps { + params: RuleCreateProps; +} + +interface CreatePrebuiltRuleProps { + ruleAsset: PrebuiltRuleAsset; +} + +interface UpdateRuleProps { + ruleUpdate: RuleUpdateProps; +} + +interface PatchRuleProps { + nextParams: PatchRuleRequestBody; +} + +interface DeleteRuleProps { + ruleId: RuleObjectId; +} + +interface UpgradePrebuiltRuleProps { + ruleAsset: PrebuiltRuleAsset; +} + +interface ImportRuleOptions { + allowMissingConnectorSecrets?: boolean; +} + +interface ImportRuleProps { + ruleToImport: RuleToImport; + overwriteRules?: boolean; + options: ImportRuleOptions; +} + +export interface IRulesManagementClient { + createCustomRule: (createCustomRulePayload: CreateCustomRuleProps) => Promise; + createPrebuiltRule: ( + createPrebuiltRulePayload: CreatePrebuiltRuleProps + ) => Promise; + updateRule: (updateRulePayload: UpdateRuleProps) => Promise; + patchRule: (patchRulePayload: PatchRuleProps) => Promise; + deleteRule: (deleteRulePayload: DeleteRuleProps) => Promise; + upgradePrebuiltRule: ( + upgradePrebuiltRulePayload: UpgradePrebuiltRuleProps + ) => Promise; + importRule: (importRulePayload: ImportRuleProps) => Promise; +} + +export const createRulesManagementClient = ( + rulesClient: RulesClient, + request: KibanaRequest, + savedObjectsClient: SavedObjectsClientContract, + licensing: LicensingApiRequestHandlerContext, + ml?: SharedServices +): IRulesManagementClient => { + const mlAuthz = buildMlAuthz({ + license: licensing.license, + ml, + request, + savedObjectsClient, + }); + + const client = { + createCustomRule: async ( + createCustomRulePayload: CreateCustomRuleProps + ): Promise => { + return createCustomRule(rulesClient, createCustomRulePayload, mlAuthz); + }, + + createPrebuiltRule: async ( + createPrebuiltRulePayload: CreatePrebuiltRuleProps + ): Promise => { + return createPrebuiltRule(rulesClient, createPrebuiltRulePayload, mlAuthz); + }, + + updateRule: async (updateRulePayload: UpdateRuleProps): Promise => { + return updateRule(rulesClient, updateRulePayload, mlAuthz); + }, + + patchRule: async (patchRulePayload: PatchRuleProps): Promise => { + return patchRule(rulesClient, patchRulePayload, mlAuthz); + }, + + deleteRule: async (deleteRulePayload: DeleteRuleProps): Promise => { + return deleteRule(rulesClient, deleteRulePayload); + }, + + upgradePrebuiltRule: async ( + upgradePrebuiltRulePayload: UpgradePrebuiltRuleProps + ): Promise => { + return upgradePrebuiltRule(rulesClient, upgradePrebuiltRulePayload, mlAuthz); + }, + + importRule: async (importRulePayload: ImportRuleProps): Promise => { + return importRule(rulesClient, importRulePayload, mlAuthz); + }, + }; + + return client; +}; + +export const createCustomRule = async ( + rulesClient: RulesClient, + createCustomRulePayload: CreateCustomRuleProps, + mlAuthz: MlAuthz +): Promise => { + const { params } = createCustomRulePayload; + await _validateMlAuth(mlAuthz, params.type); + + const rule = await _createRule(rulesClient, params, { immutable: false }); + return rule; +}; + +export const createPrebuiltRule = async ( + rulesClient: RulesClient, + createPrebuiltRulePayload: CreatePrebuiltRuleProps, + mlAuthz: MlAuthz +): Promise => { + const { ruleAsset } = createPrebuiltRulePayload; + + await _validateMlAuth(mlAuthz, ruleAsset.type); + + const rule = await _createRule(rulesClient, ruleAsset, { + immutable: true, + defaultEnabled: false, + }); + + return rule; +}; + +export const updateRule = async ( + rulesClient: RulesClient, + updateRulePayload: UpdateRuleProps, + mlAuthz: MlAuthz +): Promise => { + const { ruleUpdate } = updateRulePayload; + const { rule_id: ruleId, id } = ruleUpdate; + + await _validateMlAuth(mlAuthz, ruleUpdate.type); + + const existingRule = await readRules({ + rulesClient, + ruleId, + id, + }); + + if (existingRule == null) { + const error = getIdError({ id, ruleId }); + throw new ClientError(error.message, error.statusCode); + } + + const update = await _updateRule(rulesClient, { ruleUpdate, existingRule }); + + await _toggleRuleEnabledOnUpdate(rulesClient, existingRule, ruleUpdate.enabled); + + return { ...update, enabled: ruleUpdate.enabled ?? existingRule.enabled }; +}; + +export const patchRule = async ( + rulesClient: RulesClient, + patchRulePayload: PatchRuleProps, + mlAuthz: MlAuthz +): Promise => { + const { nextParams } = patchRulePayload; + const { rule_id: ruleId, id } = nextParams; + + const existingRule = await readRules({ + rulesClient, + ruleId, + id, + }); + + if (existingRule == null) { + const error = getIdError({ id, ruleId }); + throw new ClientError(error.message, error.statusCode); + } + + await _validateMlAuth(mlAuthz, nextParams.type ?? existingRule.params.type); + + const update = await _patchRule(rulesClient, { existingRule, nextParams }); + + await _toggleRuleEnabledOnUpdate(rulesClient, existingRule, nextParams.enabled); + + if (nextParams.enabled != null) { + return { ...update, enabled: nextParams.enabled }; + } else { + return update; + } +}; + +export const deleteRule = async ( + rulesClient: RulesClient, + deleteRulePayload: DeleteRuleProps +): Promise => { + const { ruleId } = deleteRulePayload; + await rulesClient.delete({ id: ruleId }); +}; + +export const upgradePrebuiltRule = async ( + rulesClient: RulesClient, + upgradePrebuiltRulePayload: UpgradePrebuiltRuleProps, + mlAuthz: MlAuthz +): Promise => { + const { ruleAsset } = upgradePrebuiltRulePayload; + + await _validateMlAuth(mlAuthz, ruleAsset.type); + + const existingRule = await readRules({ + rulesClient, + ruleId: ruleAsset.rule_id, + id: undefined, + }); + + if (!existingRule) { + throw new ClientError(`Failed to find rule ${ruleAsset.rule_id}`, 500); + } + + // If rule has change its type during upgrade, delete and recreate it + if (ruleAsset.type !== existingRule.params.type) { + return _upgradePrebuiltRuleWithTypeChange(rulesClient, ruleAsset, existingRule); + } + + // Else, simply patch it. + await _patchRule(rulesClient, { existingRule, nextParams: ruleAsset }); + + const updatedRule = await readRules({ + rulesClient, + ruleId: ruleAsset.rule_id, + id: undefined, + }); + + if (!updatedRule) { + throw new ClientError(`Rule ${ruleAsset.rule_id} not found after upgrade`, 500); + } + + return updatedRule; +}; + +export const importRule = async ( + rulesClient: RulesClient, + importRulePayload: ImportRuleProps, + mlAuthz: MlAuthz +): Promise => { + const { ruleToImport, overwriteRules, options } = importRulePayload; + + await _validateMlAuth(mlAuthz, ruleToImport.type); + + const existingRule = await readRules({ + rulesClient, + ruleId: ruleToImport.rule_id, + id: undefined, + }); + + if (!existingRule) { + return _createRule(rulesClient, ruleToImport, { + immutable: false, + allowMissingConnectorSecrets: options?.allowMissingConnectorSecrets, + }); + } else if (existingRule && overwriteRules) { + return _updateRule(rulesClient, { + existingRule, + ruleUpdate: ruleToImport, + }); + } else { + throw createBulkErrorObject({ + ruleId: existingRule.params.ruleId, + statusCode: 409, + message: `rule_id: "${existingRule.params.ruleId}" already exists`, + }); + } +}; + +/* -------- Internal Methods -------- */ +const _createRule = async ( + rulesClient: RulesClient, + params: RuleCreateProps, + options: CreateRuleOptions +) => { + const rulesClientCreateRuleOptions = options.id ? { id: options.id } : {}; + + const internalRule = convertCreateAPIToInternalSchema(params, options); + const rule = await rulesClient.create({ + data: internalRule, + options: rulesClientCreateRuleOptions, + allowMissingConnectorSecrets: options.allowMissingConnectorSecrets, + }); + + return rule; +}; + +const _updateRule = async ( + rulesClient: RulesClient, + updateRulePayload: _UpdateRuleProps +): Promise => { + const { ruleUpdate, existingRule } = updateRulePayload; + + const newInternalRule = convertUpdateAPIToInternalSchema({ + existingRule, + ruleUpdate, + }); + + const update = await rulesClient.update({ + id: existingRule.id, + data: newInternalRule, + }); + + return update; +}; + +const _patchRule = async ( + rulesClient: RulesClient, + patchRulePayload: _PatchRuleProps +): Promise => { + const { nextParams, existingRule } = patchRulePayload; + + const patchedRule = convertPatchAPIToInternalSchema(nextParams, existingRule); + + const update = await rulesClient.update({ + id: existingRule.id, + data: patchedRule, + }); + + return update; +}; + +const _upgradePrebuiltRuleWithTypeChange = async ( + rulesClient: RulesClient, + ruleAsset: PrebuiltRuleAsset, + existingRule: RuleAlertType +) => { + // If we're trying to change the type of a prepackaged rule, we need to delete the old one + // and replace it with the new rule, keeping the enabled setting, actions, throttle, id, + // and exception lists from the old rule + await rulesClient.delete({ id: existingRule.id }); + + return _createRule( + rulesClient, + { + ...ruleAsset, + enabled: existingRule.enabled, + exceptions_list: existingRule.params.exceptionsList, + actions: existingRule.actions.map(transformAlertToRuleAction), + timeline_id: existingRule.params.timelineId, + timeline_title: existingRule.params.timelineTitle, + }, + { immutable: true, defaultEnabled: existingRule.enabled, id: existingRule.id } + ); +}; + +const _toggleRuleEnabledOnUpdate = async ( + rulesClient: RulesClient, + existingRule: RuleAlertType, + updatedRuleEnabled?: boolean +) => { + if (existingRule.enabled && updatedRuleEnabled === false) { + await rulesClient.disable({ id: existingRule.id }); + } else if (!existingRule.enabled && updatedRuleEnabled === true) { + await rulesClient.enable({ id: existingRule.id }); + } +}; + +const _validateMlAuth = async (mlAuthz: MlAuthz, ruleType: Type) => { + throwAuthzError(await mlAuthz.validateRuleType(ruleType)); +}; diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/rule_management/update_rule.rule_management_client.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/rule_management/update_rule.rule_management_client.test.ts new file mode 100644 index 00000000000000..2d7bf7bc310e7a --- /dev/null +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/rule_management/update_rule.rule_management_client.test.ts @@ -0,0 +1,236 @@ +/* + * Copyright 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 { rulesClientMock } from '@kbn/alerting-plugin/server/rules_client.mock'; + +import { getRuleMock } from '../../../routes/__mocks__/request_responses'; +import { getMlRuleParams, getQueryRuleParams } from '../../../rule_schema/mocks'; +import { + getCreateMachineLearningRulesSchemaMock, + getCreateRulesSchemaMock, +} from '../../../../../../common/api/detection_engine/model/rule_schema/mocks'; +import { updateRule } from './rules_management_client'; +import { readRules } from './read_rules'; +import { buildMlAuthz } from '../../../../machine_learning/authz'; +import { throwAuthzError } from '../../../../machine_learning/validation'; + +jest.mock('../../../../machine_learning/authz'); +jest.mock('../../../../machine_learning/validation'); + +jest.mock('./read_rules'); + +describe('RuleManagementClient.updateRule', () => { + let rulesClient: ReturnType; + const mlAuthz = (buildMlAuthz as jest.Mock)(); + + beforeEach(() => { + rulesClient = rulesClientMock.create(); + }); + + it('calls the rulesClient with expected params', async () => { + const ruleUpdate = getCreateRulesSchemaMock(); + const existingRule = getRuleMock(getQueryRuleParams()); + (readRules as jest.Mock).mockResolvedValueOnce(existingRule); + rulesClient.update.mockResolvedValue(getRuleMock(getQueryRuleParams())); + + await updateRule(rulesClient, { ruleUpdate }, mlAuthz); + + expect(rulesClient.update).toHaveBeenCalledWith( + expect.objectContaining({ + data: expect.objectContaining({ + name: ruleUpdate.name, + params: expect.objectContaining({ + ruleId: ruleUpdate.rule_id, + description: ruleUpdate.description, + }), + }), + }) + ); + }); + + it('returns rule enabled: true if the nexParams have enabled: true', async () => { + const ruleUpdate = { ...getCreateRulesSchemaMock(), enabled: true }; + const existingRule = getRuleMock(getQueryRuleParams()); + (readRules as jest.Mock).mockResolvedValueOnce(existingRule); + rulesClient.update.mockResolvedValue(getRuleMock(getQueryRuleParams())); + + const rule = await updateRule(rulesClient, { ruleUpdate }, mlAuthz); + + expect(rule.enabled).toBe(true); + }); + + it('calls the rulesClient with legacy ML params', async () => { + const ruleUpdate = getCreateMachineLearningRulesSchemaMock(); + const existingRule = getRuleMock(getMlRuleParams()); + (readRules as jest.Mock).mockResolvedValueOnce(existingRule); + rulesClient.update.mockResolvedValue(getRuleMock(getMlRuleParams())); + + await updateRule(rulesClient, { ruleUpdate }, mlAuthz); + + expect(rulesClient.update).toHaveBeenCalledWith( + expect.objectContaining({ + data: expect.objectContaining({ + params: expect.objectContaining({ + anomalyThreshold: 58, + machineLearningJobId: ['typical-ml-job-id'], + }), + }), + }) + ); + }); + + it('calls the rulesClient with new ML params', async () => { + const ruleUpdate = { + ...getCreateMachineLearningRulesSchemaMock(), + machine_learning_job_id: ['new_job_1', 'new_job_2'], + }; + const existingRule = getRuleMock(getMlRuleParams()); + (readRules as jest.Mock).mockResolvedValueOnce(existingRule); + rulesClient.update.mockResolvedValue(getRuleMock(getMlRuleParams())); + + await updateRule(rulesClient, { ruleUpdate }, mlAuthz); + + expect(rulesClient.update).toHaveBeenCalledWith( + expect.objectContaining({ + data: expect.objectContaining({ + params: expect.objectContaining({ + anomalyThreshold: 58, + machineLearningJobId: ['new_job_1', 'new_job_2'], + }), + }), + }) + ); + }); + + it('should call rulesClient.disable if the rule was enabled and enabled is false', async () => { + const ruleUpdate = { + ...getCreateRulesSchemaMock(), + enabled: false, + }; + const existingRule = { + ...getRuleMock(getQueryRuleParams()), + enabled: true, + }; + rulesClient.update.mockResolvedValue(getRuleMock(getQueryRuleParams())); + (readRules as jest.Mock).mockResolvedValueOnce(existingRule); + + await updateRule(rulesClient, { ruleUpdate }, mlAuthz); + + expect(rulesClient.disable).toHaveBeenCalledWith( + expect.objectContaining({ + id: existingRule.id, + }) + ); + }); + + it('should call rulesClient.enable if the rule was disabled and enabled is true', async () => { + const ruleUpdate = { + ...getCreateRulesSchemaMock(), + enabled: true, + }; + const existingRule = { + ...getRuleMock(getQueryRuleParams()), + enabled: false, + }; + rulesClient.update.mockResolvedValue(getRuleMock(getQueryRuleParams())); + (readRules as jest.Mock).mockResolvedValueOnce(existingRule); + + await updateRule(rulesClient, { ruleUpdate }, mlAuthz); + + expect(rulesClient.enable).toHaveBeenCalledWith( + expect.objectContaining({ + id: existingRule.id, + }) + ); + }); + + it('throws if mlAuth fails', async () => { + (throwAuthzError as jest.Mock).mockImplementationOnce(() => { + throw new Error('mocked MLAuth error'); + }); + + const ruleUpdate = { + ...getCreateRulesSchemaMock(), + enabled: true, + }; + + await expect(updateRule(rulesClient, { ruleUpdate }, mlAuthz)).rejects.toThrow( + 'mocked MLAuth error' + ); + + expect(rulesClient.create).not.toHaveBeenCalled(); + }); + + describe('regression tests', () => { + it("updates the rule's actions if provided", async () => { + const ruleUpdate = { + ...getCreateRulesSchemaMock(), + actions: [ + { + action_type_id: '.slack', + id: '2933e581-d81c-4fe3-88fe-c57c6b8a5bfd', + params: { + message: 'Rule {{context.rule.name}} generated {{state.signals_count}} signals', + }, + group: 'default', + }, + ], + }; + const existingRule = getRuleMock(getQueryRuleParams()); + (readRules as jest.Mock).mockResolvedValueOnce(existingRule); + rulesClient.update.mockResolvedValue(getRuleMock(getQueryRuleParams())); + + await updateRule(rulesClient, { ruleUpdate }, mlAuthz); + + expect(rulesClient.update).toHaveBeenCalledWith( + expect.objectContaining({ + data: expect.objectContaining({ + actions: [ + { + actionTypeId: '.slack', + id: '2933e581-d81c-4fe3-88fe-c57c6b8a5bfd', + params: { + message: 'Rule {{context.rule.name}} generated {{state.signals_count}} signals', + }, + group: 'default', + frequency: { summary: true, throttle: null, notifyWhen: 'onActiveAlert' }, + }, + ], + }), + }) + ); + }); + + it('updates actions to empty if none are specified', async () => { + const ruleUpdate = getCreateRulesSchemaMock(); + delete ruleUpdate.actions; + const existingRule = getRuleMock(getQueryRuleParams()); + existingRule.actions = [ + { + actionTypeId: '.slack', + id: '2933e581-d81c-4fe3-88fe-c57c6b8a5bfd', + params: { + message: 'Rule {{context.rule.name}} generated {{state.signals_count}} signals', + }, + group: 'default', + }, + ]; + rulesClient.update.mockResolvedValue(getRuleMock(getQueryRuleParams())); + (readRules as jest.Mock).mockResolvedValueOnce(existingRule); + + await updateRule(rulesClient, { ruleUpdate }, mlAuthz); + + expect(rulesClient.update).toHaveBeenCalledWith( + expect.objectContaining({ + data: expect.objectContaining({ + actions: [], + }), + }) + ); + }); + }); +}); diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/rule_management/upgrade_prebuilt_rule.rule_management_client.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/rule_management/upgrade_prebuilt_rule.rule_management_client.test.ts new file mode 100644 index 00000000000000..eac57c1a6d368b --- /dev/null +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/rule_management/upgrade_prebuilt_rule.rule_management_client.test.ts @@ -0,0 +1,170 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { rulesClientMock } from '@kbn/alerting-plugin/server/mocks'; + +import { upgradePrebuiltRule } from './rules_management_client'; + +import { + getCreateEqlRuleSchemaMock, + getCreateRulesSchemaMock, +} from '../../../../../../common/api/detection_engine/model/rule_schema/mocks'; +import type { PrebuiltRuleAsset } from '../../../prebuilt_rules'; + +import { readRules } from './read_rules'; +import { getRuleMock } from '../../../routes/__mocks__/request_responses'; +import { getEqlRuleParams, getQueryRuleParams } from '../../../rule_schema/mocks'; + +import { buildMlAuthz } from '../../../../machine_learning/authz'; +import { throwAuthzError } from '../../../../machine_learning/validation'; + +jest.mock('../../../../machine_learning/authz'); +jest.mock('../../../../machine_learning/validation'); +jest.mock('./read_rules'); + +describe('RuleManagementClient.upgradePrebuiltRule', () => { + let rulesClient: ReturnType; + const mlAuthz = (buildMlAuthz as jest.Mock)(); + + beforeEach(() => { + rulesClient = rulesClientMock.create(); + }); + + it('throws if no matching rule_id is found', async () => { + const ruleAsset: PrebuiltRuleAsset = { + ...getCreateRulesSchemaMock(), + version: 1, + rule_id: 'rule-id', + }; + + (readRules as jest.Mock).mockResolvedValue(null); + await expect(upgradePrebuiltRule(rulesClient, { ruleAsset }, mlAuthz)).rejects.toThrow( + `Failed to find rule ${ruleAsset.rule_id}` + ); + }); + + it('throws if mlAuth fails', async () => { + (throwAuthzError as jest.Mock).mockImplementationOnce(() => { + throw new Error('mocked MLAuth error'); + }); + + const ruleAsset: PrebuiltRuleAsset = { + ...getCreateRulesSchemaMock(), + version: 1, + rule_id: 'rule-id', + }; + + await expect(upgradePrebuiltRule(rulesClient, { ruleAsset }, mlAuthz)).rejects.toThrow( + 'mocked MLAuth error' + ); + + expect(rulesClient.create).not.toHaveBeenCalled(); + expect(rulesClient.delete).not.toHaveBeenCalled(); + expect(rulesClient.update).not.toHaveBeenCalled(); + }); + + describe('if the new version has a different type than the existing version', () => { + // New version is "eql" + const ruleAsset: PrebuiltRuleAsset = { + ...getCreateEqlRuleSchemaMock(), + tags: ['test'], + type: 'eql', + version: 1, + rule_id: 'rule-id', + }; + // Installed version is "query" + const installedRule = getRuleMock({ + ...getQueryRuleParams({ + exceptionsList: [ + { id: 'test_id', list_id: 'hi', type: 'detection', namespace_type: 'agnostic' }, + ], + }), + actions: [ + { + group: 'default', + id: 'test_id', + action_type_id: '.index', + config: { + index: ['index-1', 'index-2'], + }, + }, + ], + ruleId: 'rule-id', + }); + beforeEach(() => { + (readRules as jest.Mock).mockResolvedValue(installedRule); + }); + + it('deletes the old rule ', async () => { + await upgradePrebuiltRule(rulesClient, { ruleAsset }, mlAuthz); + expect(rulesClient.delete).toHaveBeenCalled(); + }); + + it('creates a new rule with the new type and expected params of the original rules', async () => { + await upgradePrebuiltRule(rulesClient, { ruleAsset }, mlAuthz); + expect(rulesClient.create).toHaveBeenCalledWith( + expect.objectContaining({ + data: expect.objectContaining({ + name: ruleAsset.name, + tags: ruleAsset.tags, + // enabled and actions are kept from original rule + actions: installedRule.actions, + enabled: installedRule.enabled, + params: expect.objectContaining({ + index: ruleAsset.index, + description: ruleAsset.description, + immutable: true, + // exceptions_lists, actions, timeline_id and timeline_title are maintained + timelineTitle: installedRule.params.timelineTitle, + timelineId: installedRule.params.timelineId, + exceptionsList: installedRule.params.exceptionsList, + }), + }), + options: { + id: installedRule.id, // id is maintained + }, + allowMissingConnectorSecrets: undefined, + }) + ); + }); + }); + + describe('if the new version has the same type than the existing version', () => { + // New version is "eql" + const ruleAsset: PrebuiltRuleAsset = { + ...getCreateEqlRuleSchemaMock(), + tags: ['test'], + type: 'eql', + version: 1, + rule_id: 'rule-id', + }; + // Installed version is "eql" + const installedRule = getRuleMock({ + ...getEqlRuleParams(), + }); + beforeEach(() => { + (readRules as jest.Mock).mockResolvedValue(installedRule); + }); + + it('patches the existing rule with the new params from the rule asset', async () => { + await upgradePrebuiltRule(rulesClient, { ruleAsset }, mlAuthz); + expect(rulesClient.update).toHaveBeenCalledWith( + expect.objectContaining({ + data: expect.objectContaining({ + name: ruleAsset.name, + tags: ruleAsset.tags, + params: expect.objectContaining({ + index: ruleAsset.index, + description: ruleAsset.description, + }), + }), + id: installedRule.id, + }) + ); + }); + }); +}); 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 fd77213b178b5c..e894a46642f9b2 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 @@ -21,8 +21,8 @@ import { import type { PatchRuleRequestBody } from '../../../../../common/api/detection_engine/rule_management'; import type { - RelatedIntegrationArray, RuleCreateProps, + RuleUpdateProps, TypeSpecificCreateProps, TypeSpecificResponse, } from '../../../../../common/api/detection_engine/model/rule_schema'; @@ -430,11 +430,68 @@ export const patchTypeSpecificSnakeToCamel = ( } }; +interface ConvertUpdateAPIToInternalSchemaProps { + existingRule: SanitizedRule; + ruleUpdate: RuleUpdateProps; +} + +export const convertUpdateAPIToInternalSchema = ({ + existingRule, + ruleUpdate, +}: ConvertUpdateAPIToInternalSchemaProps) => { + const alertActions = + ruleUpdate.actions?.map((action) => transformRuleToAlertAction(action)) ?? []; + const actions = transformToActionFrequency(alertActions, ruleUpdate.throttle); + + const typeSpecificParams = typeSpecificSnakeToCamel(ruleUpdate); + + const newInternalRule: InternalRuleUpdate = { + name: ruleUpdate.name, + tags: ruleUpdate.tags ?? [], + params: { + author: ruleUpdate.author ?? [], + buildingBlockType: ruleUpdate.building_block_type, + description: ruleUpdate.description, + ruleId: existingRule.params.ruleId, + falsePositives: ruleUpdate.false_positives ?? [], + from: ruleUpdate.from ?? 'now-6m', + investigationFields: ruleUpdate.investigation_fields, + immutable: existingRule.params.immutable, + license: ruleUpdate.license, + outputIndex: ruleUpdate.output_index ?? '', + timelineId: ruleUpdate.timeline_id, + timelineTitle: ruleUpdate.timeline_title, + meta: ruleUpdate.meta, + maxSignals: ruleUpdate.max_signals ?? DEFAULT_MAX_SIGNALS, + relatedIntegrations: ruleUpdate.related_integrations ?? [], + requiredFields: addEcsToRequiredFields(ruleUpdate.required_fields), + riskScore: ruleUpdate.risk_score, + riskScoreMapping: ruleUpdate.risk_score_mapping ?? [], + ruleNameOverride: ruleUpdate.rule_name_override, + setup: ruleUpdate.setup, + severity: ruleUpdate.severity, + severityMapping: ruleUpdate.severity_mapping ?? [], + threat: ruleUpdate.threat ?? [], + timestampOverride: ruleUpdate.timestamp_override, + timestampOverrideFallbackDisabled: ruleUpdate.timestamp_override_fallback_disabled, + to: ruleUpdate.to ?? 'now', + references: ruleUpdate.references ?? [], + namespace: ruleUpdate.namespace, + note: ruleUpdate.note, + version: ruleUpdate.version ?? existingRule.params.version, + exceptionsList: ruleUpdate.exceptions_list ?? [], + ...typeSpecificParams, + }, + schedule: { interval: ruleUpdate.interval ?? '5m' }, + actions, + }; + + return newInternalRule; +}; + // eslint-disable-next-line complexity export const convertPatchAPIToInternalSchema = ( - nextParams: PatchRuleRequestBody & { - related_integrations?: RelatedIntegrationArray; - }, + nextParams: PatchRuleRequestBody, existingRule: SanitizedRule ): InternalRuleUpdate => { const typeSpecificParams = patchTypeSpecificSnakeToCamel(nextParams, existingRule.params); @@ -489,13 +546,18 @@ export const convertPatchAPIToInternalSchema = ( }; }; +interface RuleCreateOptions { + immutable?: boolean; + defaultEnabled?: boolean; +} + +// eslint-disable-next-line complexity export const convertCreateAPIToInternalSchema = ( - input: RuleCreateProps & { - related_integrations?: RelatedIntegrationArray; - }, - immutable = false, - defaultEnabled = true + input: RuleCreateProps, + options?: RuleCreateOptions ): InternalRuleCreate => { + const { immutable = false, defaultEnabled = true } = options ?? {}; + const typeSpecificParams = typeSpecificSnakeToCamel(input); const newRuleId = input.rule_id ?? uuidv4(); diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_monitoring/api/rule_execution_logs/get_rule_execution_results/get_rule_execution_results_route.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_monitoring/api/rule_execution_logs/get_rule_execution_results/get_rule_execution_results_route.ts index e1981cf9a39372..6c71652d27e343 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_monitoring/api/rule_execution_logs/get_rule_execution_results/get_rule_execution_results_route.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_monitoring/api/rule_execution_logs/get_rule_execution_results/get_rule_execution_results_route.ts @@ -56,6 +56,7 @@ export const getRuleExecutionResultsRoute = (router: SecuritySolutionPluginRoute per_page: perPage, sort_field: sortField, sort_order: sortOrder, + run_type_filters: runTypeFilters, } = request.query; const siemResponse = buildSiemResponse(response); @@ -73,6 +74,7 @@ export const getRuleExecutionResultsRoute = (router: SecuritySolutionPluginRoute perPage, sortField, sortOrder, + runTypeFilters, }); return response.ok({ body: executionResultsResponse }); diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_monitoring/logic/detection_engine_health/rule_objects/fetch_rule_by_id.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_monitoring/logic/detection_engine_health/rule_objects/fetch_rule_by_id.ts index a2082b548660e0..9975669d7e4a4e 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_monitoring/logic/detection_engine_health/rule_objects/fetch_rule_by_id.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_monitoring/logic/detection_engine_health/rule_objects/fetch_rule_by_id.ts @@ -11,7 +11,7 @@ import type { RuleObjectId, RuleResponse, } from '../../../../../../../common/api/detection_engine/model/rule_schema'; -import { readRules } from '../../../../rule_management/logic/crud/read_rules'; +import { readRules } from '../../../../rule_management/logic/rule_management/read_rules'; import { transform } from '../../../../rule_management/utils/utils'; // TODO: https://github.com/elastic/kibana/issues/125642 Move to rule_management into a RuleManagementClient diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_monitoring/logic/rule_execution_log/client_for_routes/client_interface.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_monitoring/logic/rule_execution_log/client_for_routes/client_interface.ts index 6f3c867ee3ed47..2110294d6eb0ef 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_monitoring/logic/rule_execution_log/client_for_routes/client_interface.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_monitoring/logic/rule_execution_log/client_for_routes/client_interface.ts @@ -12,6 +12,7 @@ import type { RuleExecutionEventType, RuleExecutionStatus, SortFieldOfRuleExecutionResult, + RuleRunType, } from '../../../../../../../common/api/detection_engine/rule_monitoring'; import type { RuleObjectId } from '../../../../../../../common/api/detection_engine/model/rule_schema'; import type { SortOrder } from '../../../../../../../common/api/detection_engine'; @@ -91,4 +92,6 @@ export interface GetExecutionResultsArgs { /** Number of results to fetch per page. */ perPage: number; + + runTypeFilters: RuleRunType[]; } diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_monitoring/logic/rule_execution_log/event_log/aggregations/execution_results/index.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_monitoring/logic/rule_execution_log/event_log/aggregations/execution_results/index.test.ts index 79ce2503cbe9bf..cfa8a4b36a375e 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_monitoring/logic/rule_execution_log/event_log/aggregations/execution_results/index.test.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_monitoring/logic/rule_execution_log/event_log/aggregations/execution_results/index.test.ts @@ -33,6 +33,7 @@ describe('getExecutionEventAggregation', () => { page: 1, perPage: 10, sort: [{ timestamp: { order: 'asc' } }], + runTypeFilters: [], }); }).toThrowErrorMatchingInlineSnapshot( `"Invalid maxExecutions requested \\"1001\\" - must be less than ${MAX_EXECUTION_EVENTS_DISPLAYED}"` @@ -46,6 +47,7 @@ describe('getExecutionEventAggregation', () => { page: 0, perPage: 10, sort: [{ timestamp: { order: 'asc' } }], + runTypeFilters: [], }); }).toThrowErrorMatchingInlineSnapshot(`"Invalid page field \\"0\\" - must be greater than 0"`); }); @@ -57,6 +59,7 @@ describe('getExecutionEventAggregation', () => { page: 1, perPage: 0, sort: [{ timestamp: { order: 'asc' } }], + runTypeFilters: [], }); }).toThrowErrorMatchingInlineSnapshot( `"Invalid perPage field \\"0\\" - must be greater than 0"` @@ -70,6 +73,7 @@ describe('getExecutionEventAggregation', () => { page: 1, perPage: 10, sort: [{ notsortable: { order: 'asc' } }], + runTypeFilters: [], }); }).toThrowErrorMatchingInlineSnapshot( `"Invalid sort field \\"notsortable\\" - must be one of [timestamp,duration_ms,indexing_duration_ms,search_duration_ms,gap_duration_s,schedule_delay_ms,num_triggered_actions]"` @@ -83,6 +87,7 @@ describe('getExecutionEventAggregation', () => { page: 1, perPage: 10, sort: [{ notsortable: { order: 'asc' } }, { timestamp: { order: 'asc' } }], + runTypeFilters: [], }); }).toThrowErrorMatchingInlineSnapshot( `"Invalid sort field \\"notsortable\\" - must be one of [timestamp,duration_ms,indexing_duration_ms,search_duration_ms,gap_duration_s,schedule_delay_ms,num_triggered_actions]"` @@ -96,6 +101,7 @@ describe('getExecutionEventAggregation', () => { page: 2, perPage: 10, sort: [{ timestamp: { order: 'asc' } }, { duration_ms: { order: 'desc' } }], + runTypeFilters: [], }) ).toEqual({ totalExecutions: { @@ -139,15 +145,18 @@ describe('getExecutionEventAggregation', () => { actionExecution: { filter: { bool: { + minimum_should_match: 1, must: [ { match: { - 'event.action': 'execute', + 'event.provider': 'actions', }, }, + ], + should: [ { match: { - 'event.provider': 'actions', + 'event.action': 'execute', }, }, ], @@ -165,7 +174,15 @@ describe('getExecutionEventAggregation', () => { ruleExecution: { filter: { bool: { + minimum_should_match: 1, must: [ + { + match: { + 'event.provider': 'alerting', + }, + }, + ], + should: [ { match: { 'event.action': 'execute', @@ -173,7 +190,7 @@ describe('getExecutionEventAggregation', () => { }, { match: { - 'event.provider': 'alerting', + 'event.action': 'execute-backfill', }, }, ], @@ -213,6 +230,14 @@ describe('getExecutionEventAggregation', () => { }, }, }, + backfill: { + top_hits: { + size: 1, + _source: { + includes: ['kibana.alert.rule.execution.backfill'], + }, + }, + }, }, }, securityMetrics: { @@ -221,15 +246,18 @@ describe('getExecutionEventAggregation', () => { must: [ { match: { - 'event.action': 'execution-metrics', + 'event.provider': 'securitySolution.ruleExecution', }, }, + ], + should: [ { match: { - 'event.provider': 'securitySolution.ruleExecution', + 'event.action': 'execution-metrics', }, }, ], + minimum_should_match: 1, }, }, aggs: { @@ -257,15 +285,18 @@ describe('getExecutionEventAggregation', () => { must: [ { match: { - 'event.action': 'status-change', + 'event.provider': 'securitySolution.ruleExecution', }, }, + ], + should: [ { match: { - 'event.provider': 'securitySolution.ruleExecution', + 'event.action': 'status-change', }, }, ], + minimum_should_match: 1, }, }, aggs: { @@ -300,15 +331,18 @@ describe('getExecutionEventAggregation', () => { timeoutMessage: { filter: { bool: { + minimum_should_match: 1, must: [ { match: { - 'event.action': 'execute-timeout', + 'event.provider': 'alerting', }, }, + ], + should: [ { match: { - 'event.provider': 'alerting', + 'event.action': 'execute-timeout', }, }, ], @@ -325,10 +359,9 @@ describe('getProviderAndActionFilter', () => { test('should correctly format array of sort combinations for bucket sorting', () => { expect(getProviderAndActionFilter('securitySolution.ruleExecution', 'status-change')).toEqual({ bool: { - must: [ - { match: { 'event.action': 'status-change' } }, - { match: { 'event.provider': 'securitySolution.ruleExecution' } }, - ], + minimum_should_match: 1, + must: [{ match: { 'event.provider': 'securitySolution.ruleExecution' } }], + should: [{ match: { 'event.action': 'status-change' } }], }, }); }); @@ -363,6 +396,7 @@ describe('formatExecutionEventResponse', () => { events: [], }); }); + test('should format results correctly', () => { const results = { aggregations: { @@ -1321,6 +1355,212 @@ describe('formatExecutionEventResponse', () => { ], }); }); + + test('should format results correctly when backfull occur', () => { + const results = { + aggregations: { + executionUuid: { + meta: {}, + doc_count_error_upper_bound: -1, + sum_other_doc_count: 1184, + buckets: [ + { + key: '02b7c7a4-ae1a-4da5-b134-c2fb96eb0e04', + doc_count: 5, + timeoutMessage: { + meta: {}, + doc_count: 0, + }, + securityMetrics: { + meta: {}, + doc_count: 1, + searchDuration: { + value: 9.0, + }, + indexDuration: { + value: 0.0, + }, + gapDuration: { + value: 0.0, + }, + }, + ruleExecution: { + meta: {}, + doc_count: 1, + numTriggeredActions: { + value: 0.0, + }, + scheduleDelay: { + value: 9.96e8, + }, + outcomeAndMessage: { + hits: { + total: { + value: 1, + relation: 'eq', + }, + max_score: 1.0, + hits: [ + { + _index: '.kibana-event-log-8.2.0-000001', + _id: 'pK84iX8Brb7RSEAg3a-L', + _score: 1.0, + _source: { + event: { + outcome: 'success', + }, + message: + "rule executed: siem.queryRule:7457b121-a3a8-11ec-a0f0-cbd1c2ae6ee8: 'Endpoint Security'", + }, + }, + ], + }, + }, + backfill: { + hits: { + total: { + value: 1, + relation: 'eq', + }, + max_score: 1.0, + hits: [ + { + _index: '.kibana-event-log-8.2.0-000001', + _id: 'J4T5fI8BE4fKEPKOLKON', + _score: 0.003465116, + _source: { + kibana: { + alert: { + rule: { + execution: { + backfill: { + id: '341536e6-5ac9-4d0e-a25c-dc1c70cda7d5', + start: '2024-04-01T17:50:00.000Z', + interval: '1m', + }, + }, + }, + }, + }, + }, + }, + ], + }, + }, + esSearchDuration: { + value: 5.0, + }, + executionDuration: { + value: 1.922e9, + }, + executeStartTime: { + value: 1.647274677664e12, + value_as_string: '2022-03-14T16:17:57.664Z', + }, + }, + actionExecution: { + meta: {}, + doc_count: 0, + actionOutcomes: { + doc_count_error_upper_bound: 0, + sum_other_doc_count: 0, + buckets: [], + }, + }, + securityStatus: { + meta: {}, + doc_count: 2, + message: { + hits: { + total: { + value: 2, + relation: 'eq', + }, + max_score: null, + hits: [ + { + _index: '.kibana-event-log-8.2.0-000001', + _id: 'o684iX8Brb7RSEAg2a-j', + _score: null, + _source: { + message: 'succeeded', + }, + sort: [1647274678629], + }, + ], + }, + }, + status: { + hits: { + total: { + value: 2, + relation: 'eq', + }, + max_score: null, + hits: [ + { + _index: '.kibana-event-log-8.2.0-000001', + _id: 'o684iX8Brb7RSEAg2a-j', + _score: null, + _source: { + kibana: { + alert: { + rule: { + execution: { + status: 'succeeded', + }, + }, + }, + }, + }, + sort: [1647274678629], + }, + ], + }, + }, + }, + }, + ], + }, + totalExecutions: { + value: 768, + }, + }, + }; + + expect(formatExecutionEventResponse(results)).toEqual({ + total: 768, + events: [ + { + execution_uuid: '02b7c7a4-ae1a-4da5-b134-c2fb96eb0e04', + timestamp: '2022-03-14T16:17:57.664Z', + duration_ms: 1922, + status: 'success', + message: + "rule executed: siem.queryRule:7457b121-a3a8-11ec-a0f0-cbd1c2ae6ee8: 'Endpoint Security'", + num_active_alerts: 0, + num_new_alerts: 0, + num_recovered_alerts: 0, + num_triggered_actions: 0, + num_succeeded_actions: 0, + num_errored_actions: 0, + total_search_duration_ms: 0, + es_search_duration_ms: 5, + schedule_delay_ms: 996, + timed_out: false, + indexing_duration_ms: 0, + search_duration_ms: 9, + gap_duration_s: 0, + security_status: 'succeeded', + security_message: 'succeeded', + backfill: { + to: '2024-04-01T17:50:00.000Z', + from: '2024-04-01T17:49:00.000Z', + }, + }, + ], + }); + }); }); describe('mapRuleStatusToPlatformStatus', () => { diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_monitoring/logic/rule_execution_log/event_log/aggregations/execution_results/index.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_monitoring/logic/rule_execution_log/event_log/aggregations/execution_results/index.ts index e4e394fd16149c..a744732ddc32dc 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_monitoring/logic/rule_execution_log/event_log/aggregations/execution_results/index.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_monitoring/logic/rule_execution_log/event_log/aggregations/execution_results/index.ts @@ -10,7 +10,8 @@ import type { AggregateEventsBySavedObjectResult } from '@kbn/event-log-plugin/s import { BadRequestError } from '@kbn/securitysolution-es-utils'; import { MAX_EXECUTION_EVENTS_DISPLAYED } from '@kbn/securitysolution-rules'; import { flatMap, get } from 'lodash'; - +import { parseDuration } from '@kbn/alerting-plugin/server'; +import moment from 'moment'; import type { GetRuleExecutionResultsResponse, RuleExecutionResult, @@ -48,6 +49,7 @@ const GAP_DURATION_FIELD = 'kibana.alert.rule.execution.metrics.execution_gap_du const INDEXING_DURATION_FIELD = 'kibana.alert.rule.execution.metrics.total_indexing_duration_ms'; const SEARCH_DURATION_FIELD = 'kibana.alert.rule.execution.metrics.total_search_duration_ms'; const STATUS_FIELD = 'kibana.alert.rule.execution.status'; +const BACKFILL_FIELD = 'kibana.alert.rule.execution.backfill'; const ONE_MILLISECOND_AS_NANOSECONDS = 1_000_000; @@ -143,7 +145,7 @@ export const getExecutionEventAggregation = ({ }, // Filter by alerting execute doc to retrieve platform metrics ruleExecution: { - filter: getProviderAndActionFilter('alerting', 'execute'), + filter: getProviderAndActionFilter('alerting', ['execute', 'execute-backfill']), aggs: { executeStartTime: { min: { @@ -170,6 +172,14 @@ export const getExecutionEventAggregation = ({ field: DURATION_FIELD, }, }, + backfill: { + top_hits: { + size: 1, + _source: { + includes: [BACKFILL_FIELD], + }, + }, + }, outcomeAndMessage: { top_hits: { size: 1, @@ -248,25 +258,46 @@ export const getExecutionEventAggregation = ({ * @param provider provider to match * @param action action to match */ -export const getProviderAndActionFilter = (provider: string, action: string) => { +export const getProviderAndActionFilter = (provider: string, action: string | string[]) => { + const actions = Array.isArray(action) ? action : [action]; return { bool: { must: [ - { - match: { - [ACTION_FIELD]: action, - }, - }, { match: { [PROVIDER_FIELD]: provider, }, }, ], + should: actions.map((a) => ({ + match: { + [ACTION_FIELD]: a, + }, + })), + minimum_should_match: 1, }, }; }; +const getBackfill = (bucket: ExecutionUuidAggBucket) => { + const backfill = + bucket?.ruleExecution?.backfill?.hits?.hits[0]?._source?.kibana?.alert?.rule?.execution + ?.backfill; + + if (backfill) { + backfill.from = moment(backfill.start) + .subtract(parseDuration(backfill.interval), 'ms') + .toISOString(); + + return { + from: moment(backfill.start).subtract(parseDuration(backfill.interval), 'ms').toISOString(), + to: backfill.start, + }; + } + + return backfill; +}; + /** * Formats aggregate execution event from bucket response * @param bucket @@ -281,6 +312,7 @@ export const formatAggExecutionEventFromBucket = ( const actionOutcomes = bucket?.actionExecution?.actionOutcomes?.buckets ?? []; const actionExecutionSuccess = actionOutcomes.find((b) => b?.key === 'success')?.doc_count ?? 0; const actionExecutionError = actionOutcomes.find((b) => b?.key === 'failure')?.doc_count ?? 0; + const backfill = getBackfill(bucket); return { execution_uuid: bucket?.key ?? '', @@ -313,6 +345,7 @@ export const formatAggExecutionEventFromBucket = ( security_message: bucket?.securityStatus?.message?.hits?.hits[0]?._source?.message ?? bucket?.ruleExecution?.outcomeAndMessage?.hits?.hits[0]?._source?.error?.message, + backfill, }; }; diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_monitoring/logic/rule_execution_log/event_log/aggregations/execution_results/types.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_monitoring/logic/rule_execution_log/event_log/aggregations/execution_results/types.ts index 429da6eeac8bc5..c09a8c4edc57bb 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_monitoring/logic/rule_execution_log/event_log/aggregations/execution_results/types.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_monitoring/logic/rule_execution_log/event_log/aggregations/execution_results/types.ts @@ -6,6 +6,7 @@ */ import type * as estypes from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; +import type { RuleRunType } from '../../../../../../../../../common/api/detection_engine/rule_monitoring'; type AlertCounts = estypes.AggregationsMultiBucketAggregateBase & { buckets: { @@ -32,6 +33,7 @@ export type ExecutionUuidAggBucket = estypes.AggregationsStringTermsBucketKeys & totalSearchDuration: estypes.AggregationsMaxAggregate; numTriggeredActions: estypes.AggregationsMaxAggregate; outcomeAndMessage: estypes.AggregationsTopHitsAggregate; + backfill: estypes.AggregationsTopHitsAggregate; }; alertCounts: AlertCounts; actionExecution: { @@ -58,4 +60,5 @@ export interface ExecutionEventAggregationOptions { page: number; perPage: number; sort: estypes.Sort; + runTypeFilters: RuleRunType[]; } diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_monitoring/logic/rule_execution_log/event_log/event_log_reader.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_monitoring/logic/rule_execution_log/event_log/event_log_reader.ts index 669f3d7e5ee043..768e32d277b949 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_monitoring/logic/rule_execution_log/event_log/event_log_reader.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_monitoring/logic/rule_execution_log/event_log/event_log_reader.ts @@ -8,18 +8,23 @@ import type * as estypes from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; import type { IEventLogClient, IValidatedEvent } from '@kbn/event-log-plugin/server'; import { MAX_EXECUTION_EVENTS_DISPLAYED } from '@kbn/securitysolution-rules'; - import type { GetRuleExecutionEventsResponse, GetRuleExecutionResultsResponse, RuleExecutionEvent, } from '../../../../../../../common/api/detection_engine/rule_monitoring'; import { + RuleRunTypeEnum, LogLevel, LogLevelEnum, RuleExecutionEventType, RuleExecutionEventTypeEnum, } from '../../../../../../../common/api/detection_engine/rule_monitoring'; +import { + RUN_TYPE_FILTERS, + STATUS_FILTERS, +} from '../../../../../../../common/detection_engine/rule_management/execution_log'; + import { prepareKQLStringParam } from '../../../../../../../common/utils/kql'; import { assertUnreachable } from '../../../../../../../common/utility_types'; @@ -100,75 +105,104 @@ export const createEventLogReader = (eventLog: IEventLogClient): IEventLogReader async getExecutionResults( args: GetExecutionResultsArgs ): Promise { - const { ruleId, start, end, statusFilters, page, perPage, sortField, sortOrder } = args; + const { + ruleId, + start, + end, + statusFilters, + page, + perPage, + sortField, + sortOrder, + runTypeFilters, + } = args; const soType = RULE_SAVED_OBJECT_TYPE; const soIds = [ruleId]; + let totalExecutions: number | undefined; + let executionIdsFilter: string = ''; + + // Similar workaround to the above for status filters + // First fetch execution uuid's by run type filter if provided + // Then use those ID's to filter by status if provided + const MAX_RUN_TYPE_FILTERS = RUN_TYPE_FILTERS.length; + const someRunTypeFiltersSelected = + runTypeFilters.length > 0 && runTypeFilters.length < MAX_RUN_TYPE_FILTERS; + if (someRunTypeFiltersSelected) { + const ruleRunEventActions = runTypeFilters.map((runType) => { + return { + [RuleRunTypeEnum.standard]: 'execute', + [RuleRunTypeEnum.backfill]: 'execute-backfill', + }[runType]; + }); + + const { executionIds, total } = await findRuleExecutionIds({ + eventLog, + soType, + soIds, + start, + end, + filter: `event.provider:alerting AND (${ruleRunEventActions + .map((runType) => `event.action:${runType}`) + .join(' OR ')}) `, + }); + + if (executionIds.length === 0) { + return { + total: 0, + events: [], + }; + } else { + totalExecutions = total; + executionIdsFilter = `${f.RULE_EXECUTION_UUID}:(${executionIds.join(' OR ')})`; + } + } + // Current workaround to support root level filters without missing fields in the aggregate event // or including events from statuses that aren't selected // TODO: See: https://github.com/elastic/kibana/pull/127339/files#r825240516 // First fetch execution uuid's by status filter if provided - let statusIds: string[] = []; - let totalExecutions: number | undefined; // If 0 or 3 statuses are selected we can search for all statuses and don't need this pre-filter by ID - if (statusFilters.length > 0 && statusFilters.length < 3) { + const MAX_STATUSES_FILTERS = STATUS_FILTERS.length; + const someStatusFiltersSelected = + statusFilters.length > 0 && statusFilters.length < MAX_STATUSES_FILTERS; + if (someStatusFiltersSelected) { const outcomes = mapRuleExecutionStatusToPlatformStatus(statusFilters); const outcomeFilter = outcomes.length ? `OR event.outcome:(${outcomes.join(' OR ')})` : ''; - const statusResults = await eventLog.aggregateEventsBySavedObjectIds(soType, soIds, { + const { executionIds, total } = await findRuleExecutionIds({ + eventLog, + soType, + soIds, start, end, - // Also query for `event.outcome` to catch executions that only contain platform events - filter: `${f.RULE_EXECUTION_STATUS}:(${statusFilters.join(' OR ')}) ${outcomeFilter}`, - aggs: { - totalExecutions: { - cardinality: { - field: f.RULE_EXECUTION_UUID, - }, - }, - filteredExecutionUUIDs: { - terms: { - field: f.RULE_EXECUTION_UUID, - order: { executeStartTime: 'desc' }, - size: MAX_EXECUTION_EVENTS_DISPLAYED, - }, - aggs: { - executeStartTime: { - min: { - field: f.TIMESTAMP, - }, - }, - }, - }, - }, + filter: `(${f.RULE_EXECUTION_STATUS}:(${statusFilters.join(' OR ')}) ${outcomeFilter}) ${ + executionIdsFilter ? `AND ${executionIdsFilter}` : '' + }`, }); - const filteredExecutionUUIDs = statusResults.aggregations - ?.filteredExecutionUUIDs as ExecutionUuidAggResult; - statusIds = filteredExecutionUUIDs?.buckets?.map((b) => b.key) ?? []; - totalExecutions = ( - statusResults.aggregations?.totalExecutions as estypes.AggregationsCardinalityAggregate - ).value; + // Early return if no results based on status filter - if (statusIds.length === 0) { + if (executionIds.length === 0) { return { total: 0, events: [], }; + } else { + executionIdsFilter = `${f.RULE_EXECUTION_UUID}:(${executionIds.join(' OR ')})`; + totalExecutions = total; } } // Now query for aggregate events, and pass any ID's as filters as determined from the above status/queryText results - const idsFilter = statusIds.length - ? `${f.RULE_EXECUTION_UUID}:(${statusIds.join(' OR ')})` - : ''; const results = await eventLog.aggregateEventsBySavedObjectIds(soType, soIds, { start, end, - filter: idsFilter, + filter: executionIdsFilter, aggs: getExecutionEventAggregation({ maxExecutions: MAX_EXECUTION_EVENTS_DISPLAYED, page, perPage, sort: [{ [sortField]: { order: sortOrder } }], + runTypeFilters, }), }); @@ -305,3 +339,58 @@ const buildEventLogKqlFilter = ({ return kqlAnd(filters); }; + +const findRuleExecutionIds = async ({ + eventLog, + soType, + soIds, + start, + end, + filter, +}: { + eventLog: IEventLogClient; + soType: string; + soIds: string[]; + start: string; + end: string; + filter: string; +}) => { + const runTypesResponse = await eventLog.aggregateEventsBySavedObjectIds(soType, soIds, { + start, + end, + filter, + aggs: { + totalExecutions: { + cardinality: { + field: f.RULE_EXECUTION_UUID, + }, + }, + filteredExecutionUUIDs: { + terms: { + field: f.RULE_EXECUTION_UUID, + order: { executeStartTime: 'desc' }, + size: MAX_EXECUTION_EVENTS_DISPLAYED, + }, + aggs: { + executeStartTime: { + min: { + field: f.TIMESTAMP, + }, + }, + }, + }, + }, + }); + + const total = + (runTypesResponse.aggregations?.totalExecutions as estypes.AggregationsCardinalityAggregate) + ?.value ?? 0; + const filteredExecutionUUIDs = runTypesResponse.aggregations + ?.filteredExecutionUUIDs as ExecutionUuidAggResult; + const executionIds = filteredExecutionUUIDs?.buckets?.map((b) => b.key) ?? []; + + return { + executionIds, + total, + }; +}; diff --git a/x-pack/plugins/security_solution/server/lib/machine_learning/__mocks__/validation.ts b/x-pack/plugins/security_solution/server/lib/machine_learning/__mocks__/validation.ts new file mode 100644 index 00000000000000..3112d258ef28c4 --- /dev/null +++ b/x-pack/plugins/security_solution/server/lib/machine_learning/__mocks__/validation.ts @@ -0,0 +1,9 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +export const throwAuthzError = jest.fn(); +export const toAuthzError = jest.fn(); diff --git a/x-pack/plugins/security_solution/server/request_context_factory.ts b/x-pack/plugins/security_solution/server/request_context_factory.ts index ddb825cb642e66..28096984e0584e 100644 --- a/x-pack/plugins/security_solution/server/request_context_factory.ts +++ b/x-pack/plugins/security_solution/server/request_context_factory.ts @@ -28,6 +28,7 @@ import type { EndpointAppContextService } from './endpoint/endpoint_app_context_ import { RiskEngineDataClient } from './lib/entity_analytics/risk_engine/risk_engine_data_client'; import { RiskScoreDataClient } from './lib/entity_analytics/risk_score/risk_score_data_client'; import { AssetCriticalityDataClient } from './lib/entity_analytics/asset_criticality'; +import { createRulesManagementClient } from './lib/detection_engine/rule_management/logic/rule_management/rules_management_client'; export interface IRequestContextFactory { create( @@ -66,6 +67,7 @@ export class RequestContextFactory implements IRequestContextFactory { const [, startPlugins] = await core.getStartServices(); const frameworkRequest = await buildFrameworkRequest(context, security, request); const coreContext = await context.core; + const licensing = await context.licensing; const getSpaceId = (): string => startPlugins.spaces?.spacesService?.getSpaceId(request) || DEFAULT_SPACE_ID; @@ -111,6 +113,15 @@ export class RequestContextFactory implements IRequestContextFactory { getAuditLogger, + getRulesManagementClient: () => + createRulesManagementClient( + startPlugins.alerting.getRulesClientWithRequest(request), + request, + coreContext.savedObjects.client, + licensing, + plugins.ml + ), + getDetectionEngineHealthClient: memoize(() => ruleMonitoringService.createDetectionEngineHealthClient({ rulesClient: startPlugins.alerting.getRulesClientWithRequest(request), diff --git a/x-pack/plugins/security_solution/server/types.ts b/x-pack/plugins/security_solution/server/types.ts index 513eb6079e0414..d8536c00ca4d02 100644 --- a/x-pack/plugins/security_solution/server/types.ts +++ b/x-pack/plugins/security_solution/server/types.ts @@ -33,6 +33,7 @@ import type { EndpointInternalFleetServicesInterface } from './endpoint/services import type { RiskEngineDataClient } from './lib/entity_analytics/risk_engine/risk_engine_data_client'; import type { RiskScoreDataClient } from './lib/entity_analytics/risk_score/risk_score_data_client'; import type { AssetCriticalityDataClient } from './lib/entity_analytics/asset_criticality'; +import type { IRulesManagementClient } from './lib/detection_engine/rule_management/logic/rule_management/rules_management_client'; export { AppClient }; export interface SecuritySolutionApiRequestHandlerContext { @@ -44,6 +45,7 @@ export interface SecuritySolutionApiRequestHandlerContext { getAppClient: () => AppClient; getSpaceId: () => string; getRuleDataService: () => IRuleDataService; + getRulesManagementClient: () => IRulesManagementClient; getDetectionEngineHealthClient: () => IDetectionEngineHealthClient; getRuleExecutionLog: () => IRuleExecutionLogForRoutes; getRacClient: (req: KibanaRequest) => Promise; diff --git a/x-pack/plugins/security_solution/tsconfig.json b/x-pack/plugins/security_solution/tsconfig.json index 0a74be06da7d02..99457d439339d9 100644 --- a/x-pack/plugins/security_solution/tsconfig.json +++ b/x-pack/plugins/security_solution/tsconfig.json @@ -142,7 +142,7 @@ "@kbn/alerts-as-data-utils", "@kbn/cloud-defend-plugin", "@kbn/expandable-flyout", - "@kbn/securitysolution-grouping", + "@kbn/grouping", "@kbn/securitysolution-data-table", "@kbn/core-analytics-server", "@kbn/analytics-client", diff --git a/x-pack/plugins/spaces/common/types/space/v1.ts b/x-pack/plugins/spaces/common/types/space/v1.ts index 0d0a32d758da86..e18ad5e656efc6 100644 --- a/x-pack/plugins/spaces/common/types/space/v1.ts +++ b/x-pack/plugins/spaces/common/types/space/v1.ts @@ -58,6 +58,11 @@ export interface Space { * @private */ _reserved?: boolean; + + /** + * Solution selected for this space. + */ + solution?: 'security' | 'observability' | 'search' | 'classic'; } /** diff --git a/x-pack/plugins/spaces/server/lib/space_schema.test.ts b/x-pack/plugins/spaces/server/lib/space_schema.test.ts index d6911a987786e1..c31d7f5ca3c220 100644 --- a/x-pack/plugins/spaces/server/lib/space_schema.test.ts +++ b/x-pack/plugins/spaces/server/lib/space_schema.test.ts @@ -5,7 +5,11 @@ * 2.0. */ -import { spaceSchema } from './space_schema'; +import { getSpaceSchema } from './space_schema'; + +// non-serverless space schema +const spaceBaseSchema = getSpaceSchema(false); +const spaceServerlessSchema = getSpaceSchema(true); const defaultProperties = { id: 'foo', @@ -15,7 +19,7 @@ const defaultProperties = { describe('#id', () => { test('is required', () => { expect(() => - spaceSchema.validate({ + spaceBaseSchema.validate({ ...defaultProperties, id: undefined, }) @@ -26,7 +30,7 @@ describe('#id', () => { test('allows lowercase a-z, 0-9, "_" and "-"', () => { expect(() => - spaceSchema.validate({ + spaceBaseSchema.validate({ ...defaultProperties, id: 'abcdefghijklmnopqrstuvwxyz0123456789_-', }) @@ -35,7 +39,7 @@ describe('#id', () => { test(`doesn't allow uppercase`, () => { expect(() => - spaceSchema.validate({ + spaceBaseSchema.validate({ ...defaultProperties, id: 'Foo', }) @@ -46,7 +50,7 @@ describe('#id', () => { test(`doesn't allow an empty string`, () => { expect(() => - spaceSchema.validate({ + spaceBaseSchema.validate({ ...defaultProperties, id: '', }) @@ -59,7 +63,7 @@ describe('#id', () => { (invalidCharacter) => { test(`doesn't allow ${invalidCharacter}`, () => { expect(() => - spaceSchema.validate({ + spaceBaseSchema.validate({ ...defaultProperties, id: `foo-${invalidCharacter}`, }) @@ -72,7 +76,7 @@ describe('#id', () => { describe('#disabledFeatures', () => { test('is optional', () => { expect(() => - spaceSchema.validate({ + spaceBaseSchema.validate({ ...defaultProperties, disabledFeatures: undefined, }) @@ -80,7 +84,7 @@ describe('#disabledFeatures', () => { }); test('defaults to an empty array', () => { - const result = spaceSchema.validate({ + const result = spaceBaseSchema.validate({ ...defaultProperties, disabledFeatures: undefined, }); @@ -89,7 +93,7 @@ describe('#disabledFeatures', () => { test('must be an array if provided', () => { expect(() => - spaceSchema.validate({ + spaceBaseSchema.validate({ ...defaultProperties, disabledFeatures: 'foo', }) @@ -100,7 +104,7 @@ describe('#disabledFeatures', () => { test('allows an array of strings', () => { expect(() => - spaceSchema.validate({ + spaceBaseSchema.validate({ ...defaultProperties, disabledFeatures: ['foo', 'bar'], }) @@ -109,7 +113,7 @@ describe('#disabledFeatures', () => { test('does not allow an array containing non-string elements', () => { expect(() => - spaceSchema.validate({ + spaceBaseSchema.validate({ ...defaultProperties, disabledFeatures: ['foo', true], }) @@ -122,7 +126,7 @@ describe('#disabledFeatures', () => { describe('#color', () => { test('is optional', () => { expect(() => - spaceSchema.validate({ + spaceBaseSchema.validate({ ...defaultProperties, color: undefined, }) @@ -131,7 +135,7 @@ describe('#color', () => { test(`doesn't allow an empty string`, () => { expect(() => - spaceSchema.validate({ + spaceBaseSchema.validate({ ...defaultProperties, color: '', }) @@ -142,7 +146,7 @@ describe('#color', () => { test(`allows lower case hex color code`, () => { expect(() => - spaceSchema.validate({ + spaceBaseSchema.validate({ ...defaultProperties, color: '#aabbcc', }) @@ -151,7 +155,7 @@ describe('#color', () => { test(`allows upper case hex color code`, () => { expect(() => - spaceSchema.validate({ + spaceBaseSchema.validate({ ...defaultProperties, color: '#AABBCC', }) @@ -160,7 +164,7 @@ describe('#color', () => { test(`allows numeric hex color code`, () => { expect(() => - spaceSchema.validate({ + spaceBaseSchema.validate({ ...defaultProperties, color: '#123456', }) @@ -169,7 +173,7 @@ describe('#color', () => { test(`must start with a hash`, () => { expect(() => - spaceSchema.validate({ + spaceBaseSchema.validate({ ...defaultProperties, color: '123456', }) @@ -180,7 +184,7 @@ describe('#color', () => { test(`cannot exceed 6 digits following the hash`, () => { expect(() => - spaceSchema.validate({ + spaceBaseSchema.validate({ ...defaultProperties, color: '1234567', }) @@ -191,7 +195,7 @@ describe('#color', () => { test(`cannot be fewer than 6 digits following the hash`, () => { expect(() => - spaceSchema.validate({ + spaceBaseSchema.validate({ ...defaultProperties, color: '12345', }) @@ -204,7 +208,7 @@ describe('#color', () => { describe('#imageUrl', () => { test('is optional', () => { expect(() => - spaceSchema.validate({ + spaceBaseSchema.validate({ ...defaultProperties, imageUrl: undefined, }) @@ -213,7 +217,7 @@ describe('#imageUrl', () => { test(`must start with data:image`, () => { expect(() => - spaceSchema.validate({ + spaceBaseSchema.validate({ ...defaultProperties, imageUrl: 'notValid', }) @@ -222,7 +226,7 @@ describe('#imageUrl', () => { test(`checking that a valid image is accepted as imageUrl`, () => { expect(() => - spaceSchema.validate({ + spaceBaseSchema.validate({ ...defaultProperties, imageUrl: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADAAAAAwCAIAAADYYG7QAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAAZdEVYdFNvZnR3YXJlAHBhaW50Lm5ldCA0LjAuMTnU1rJkAAAB3klEQVRYR+2WzUrDQBCARzwqehE8ir1WPfgqRRA1bePBXgpe/MGCB9/Aiw+j+ASCB6kotklaEwW1F0WwNSaps9lV69awGzBpDzt8pJP9mXxsmk3ABH2oUEIilJAIJSRCCYlQQiKUkIh4QgY5agZodVjBowFrBktWQzDBU2ykiYaDuQpCYgnl3QunGzM6Z6YF+b5SkcgK1UH/aLbYReQiYL9d9/o+XFop5IU0Vl4uapAzoXC3eEBPw9vH1/wT6Vs2otPSkoH/IZzlzO/TU2vgQm8nl69Hp0H7nZ4OXogLJSSKBIUC3w88n+Ueyfv56fVZnqCQNVnCHbLrkV0Gd2d+GNkglsk438dhaTxloZDutV4wb06Vf40JcWZ2sMttPpE8NaHGeBnzIAhwPXqHseVB11EyLD0hxLUeaYud2a3B0g3k7GyFtrhX7F2RqhC+yV3jgTb2Rqdqf7/kUxYiWBOlTtXxfPJEtc8b5thGb+8AhL4ohnCNqQjZ2T2+K5rnw2M6KwEhKNDSGM3pTdxjhDgLbHkw/v/zw4AiPuSsfMzAiTidKxiF/ArpFqyzK8SMOlkwvloUMYRCtNvZLWeuIomd2Za/WZS4QomjhEQoIRFKSIQSEqGERAyfEH4YDBFQ/ARU6BiBxCAIQQAAAABJRU5ErkJggg==', @@ -230,3 +234,32 @@ describe('#imageUrl', () => { ).not.toThrowError(); }); }); + +describe('#solution', () => { + it('should throw error if solution is defined in serverless offering', () => { + expect(() => + spaceServerlessSchema.validate({ ...defaultProperties, solution: 'search' }) + ).toThrow(); + }); + + it('should not throw error if solution is undefined in classic offering', () => { + expect(() => + spaceBaseSchema.validate({ ...defaultProperties, solution: undefined }, {}) + ).not.toThrow(); + }); + + it('should throw error if solution is invalid in classic offering', () => { + expect(() => spaceBaseSchema.validate({ ...defaultProperties, solution: 'some_value' }, {})) + .toThrowErrorMatchingInlineSnapshot(` + "[solution]: types that failed validation: + - [solution.0]: expected value to equal [security] + - [solution.1]: expected value to equal [observability] + - [solution.2]: expected value to equal [search] + - [solution.3]: expected value to equal [classic]" + `); + + expect(() => + spaceBaseSchema.validate({ ...defaultProperties, solution: ' search ' }, {}) + ).toThrow(); + }); +}); diff --git a/x-pack/plugins/spaces/server/lib/space_schema.ts b/x-pack/plugins/spaces/server/lib/space_schema.ts index b736994c4d13ba..cb184e322b5a94 100644 --- a/x-pack/plugins/spaces/server/lib/space_schema.ts +++ b/x-pack/plugins/spaces/server/lib/space_schema.ts @@ -11,7 +11,7 @@ import { MAX_SPACE_INITIALS } from '../../common'; export const SPACE_ID_REGEX = /^[a-z0-9_\-]+$/; -export const spaceSchema = schema.object({ +const spaceSchema = schema.object({ id: schema.string({ validate: (value) => { if (!SPACE_ID_REGEX.test(value)) { @@ -43,3 +43,18 @@ export const spaceSchema = schema.object({ }) ), }); + +const solutionSchema = schema.oneOf([ + schema.literal('security'), + schema.literal('observability'), + schema.literal('search'), + schema.literal('classic'), +]); + +export const getSpaceSchema = (isServerless: boolean) => { + if (isServerless) { + return spaceSchema; + } + + return spaceSchema.extends({ solution: schema.maybe(solutionSchema) }); +}; diff --git a/x-pack/plugins/spaces/server/plugin.ts b/x-pack/plugins/spaces/server/plugin.ts index 9084a74e24265c..a20396c3e46957 100644 --- a/x-pack/plugins/spaces/server/plugin.ts +++ b/x-pack/plugins/spaces/server/plugin.ts @@ -125,7 +125,10 @@ export class SpacesPlugin this.hasOnlyDefaultSpace$ = this.config$.pipe(map(({ maxSpaces }) => maxSpaces === 1)); this.log = initializerContext.logger.get(); this.spacesService = new SpacesService(); - this.spacesClientService = new SpacesClientService((message) => this.log.debug(message)); + this.spacesClientService = new SpacesClientService( + (message) => this.log.debug(message), + initializerContext.env.packageInfo.buildFlavor + ); } public setup(core: CoreSetup, plugins: PluginsSetup): SpacesPluginSetup { @@ -168,16 +171,14 @@ export class SpacesPlugin const router = core.http.createRouter(); - initExternalSpacesApi( - { - router, - log: this.log, - getStartServices: core.getStartServices, - getSpacesService, - usageStatsServicePromise, - }, - this.initializerContext.env.packageInfo.buildFlavor - ); + initExternalSpacesApi({ + router, + log: this.log, + getStartServices: core.getStartServices, + getSpacesService, + usageStatsServicePromise, + isServerless: this.initializerContext.env.packageInfo.buildFlavor === 'serverless', + }); initInternalSpacesApi({ router, diff --git a/x-pack/plugins/spaces/server/routes/api/external/copy_to_space.test.ts b/x-pack/plugins/spaces/server/routes/api/external/copy_to_space.test.ts index 77223167990764..8c5782aacd519b 100644 --- a/x-pack/plugins/spaces/server/routes/api/external/copy_to_space.test.ts +++ b/x-pack/plugins/spaces/server/routes/api/external/copy_to_space.test.ts @@ -57,7 +57,7 @@ describe('copy to space', () => { createResolveSavedObjectsImportErrorsMock() ); - const clientService = new SpacesClientService(jest.fn()); + const clientService = new SpacesClientService(jest.fn(), 'traditional'); clientService .setup({ config$: Rx.of(spacesConfig) }) .setClientRepositoryFactory(() => savedObjectsRepositoryMock); @@ -85,6 +85,7 @@ describe('copy to space', () => { log, getSpacesService: () => spacesServiceStart, usageStatsServicePromise, + isServerless: false, }); const [[ctsRouteDefinition, ctsRouteHandler], [resolveRouteDefinition, resolveRouteHandler]] = diff --git a/x-pack/plugins/spaces/server/routes/api/external/delete.test.ts b/x-pack/plugins/spaces/server/routes/api/external/delete.test.ts index 09ed757a5df74d..99ec34917eb5a8 100644 --- a/x-pack/plugins/spaces/server/routes/api/external/delete.test.ts +++ b/x-pack/plugins/spaces/server/routes/api/external/delete.test.ts @@ -42,7 +42,7 @@ describe('Spaces Public API', () => { const coreStart = coreMock.createStart(); - const clientService = new SpacesClientService(jest.fn()); + const clientService = new SpacesClientService(jest.fn(), 'traditional'); clientService .setup({ config$: Rx.of(spacesConfig) }) .setClientRepositoryFactory(() => savedObjectsRepositoryMock); @@ -67,6 +67,7 @@ describe('Spaces Public API', () => { log, getSpacesService: () => spacesServiceStart, usageStatsServicePromise, + isServerless: false, }); const [routeDefinition, routeHandler] = router.delete.mock.calls[0]; diff --git a/x-pack/plugins/spaces/server/routes/api/external/disable_legacy_url_aliases.test.ts b/x-pack/plugins/spaces/server/routes/api/external/disable_legacy_url_aliases.test.ts index ed7c403182ab53..e6f665f817c556 100644 --- a/x-pack/plugins/spaces/server/routes/api/external/disable_legacy_url_aliases.test.ts +++ b/x-pack/plugins/spaces/server/routes/api/external/disable_legacy_url_aliases.test.ts @@ -42,7 +42,7 @@ describe('_disable_legacy_url_aliases', () => { const log = loggingSystemMock.create().get('spaces'); - const clientService = new SpacesClientService(jest.fn()); + const clientService = new SpacesClientService(jest.fn(), 'traditional'); clientService .setup({ config$: Rx.of(spacesConfig) }) .setClientRepositoryFactory(() => savedObjectsRepositoryMock); @@ -70,6 +70,7 @@ describe('_disable_legacy_url_aliases', () => { log, getSpacesService: () => spacesServiceStart, usageStatsServicePromise, + isServerless: false, }); const [routeDefinition, routeHandler] = router.post.mock.calls[0]; diff --git a/x-pack/plugins/spaces/server/routes/api/external/get.test.ts b/x-pack/plugins/spaces/server/routes/api/external/get.test.ts index 34c1ed01e0ce4a..938af432b0cfab 100644 --- a/x-pack/plugins/spaces/server/routes/api/external/get.test.ts +++ b/x-pack/plugins/spaces/server/routes/api/external/get.test.ts @@ -41,7 +41,7 @@ describe('GET space', () => { const log = loggingSystemMock.create().get('spaces'); - const clientService = new SpacesClientService(jest.fn()); + const clientService = new SpacesClientService(jest.fn(), 'traditional'); clientService .setup({ config$: Rx.of(spacesConfig) }) .setClientRepositoryFactory(() => savedObjectsRepositoryMock); @@ -66,6 +66,7 @@ describe('GET space', () => { log, getSpacesService: () => spacesServiceStart, usageStatsServicePromise, + isServerless: false, }); return { diff --git a/x-pack/plugins/spaces/server/routes/api/external/get.ts b/x-pack/plugins/spaces/server/routes/api/external/get.ts index ce89aac5fe1869..496c3408b7d2e3 100644 --- a/x-pack/plugins/spaces/server/routes/api/external/get.ts +++ b/x-pack/plugins/spaces/server/routes/api/external/get.ts @@ -30,7 +30,9 @@ export function initGetSpaceApi(deps: ExternalRouteDeps) { try { const space = await spacesClient.get(spaceId); - return response.ok({ body: space }); + return response.ok({ + body: space, + }); } catch (error) { if (SavedObjectsErrorHelpers.isNotFoundError(error)) { return response.notFound(); diff --git a/x-pack/plugins/spaces/server/routes/api/external/get_all.test.ts b/x-pack/plugins/spaces/server/routes/api/external/get_all.test.ts index aefef389c7d26a..a3d4f151c6615c 100644 --- a/x-pack/plugins/spaces/server/routes/api/external/get_all.test.ts +++ b/x-pack/plugins/spaces/server/routes/api/external/get_all.test.ts @@ -43,7 +43,7 @@ describe('GET /spaces/space', () => { const log = loggingSystemMock.create().get('spaces'); - const clientService = new SpacesClientService(jest.fn()); + const clientService = new SpacesClientService(jest.fn(), 'traditional'); clientService .setup({ config$: Rx.of(spacesConfig) }) .setClientRepositoryFactory(() => savedObjectsRepositoryMock); @@ -68,6 +68,7 @@ describe('GET /spaces/space', () => { log, getSpacesService: () => spacesServiceStart, usageStatsServicePromise, + isServerless: false, }); return { diff --git a/x-pack/plugins/spaces/server/routes/api/external/get_shareable_references.test.ts b/x-pack/plugins/spaces/server/routes/api/external/get_shareable_references.test.ts index c9ea0d71c11b70..8c9c60f78f4615 100644 --- a/x-pack/plugins/spaces/server/routes/api/external/get_shareable_references.test.ts +++ b/x-pack/plugins/spaces/server/routes/api/external/get_shareable_references.test.ts @@ -42,7 +42,7 @@ describe('get shareable references', () => { const { savedObjects, savedObjectsClient } = createMockSavedObjectsService(spaces); coreStart.savedObjects = savedObjects; - const clientService = new SpacesClientService(jest.fn()); + const clientService = new SpacesClientService(jest.fn(), 'traditional'); clientService .setup({ config$: Rx.of(spacesConfig) }) .setClientRepositoryFactory(() => savedObjectsRepositoryMock); @@ -66,6 +66,7 @@ describe('get shareable references', () => { log, getSpacesService: () => spacesServiceStart, usageStatsServicePromise, + isServerless: false, }); const [[getShareableReferences, getShareableReferencesRouteHandler]] = router.post.mock.calls; diff --git a/x-pack/plugins/spaces/server/routes/api/external/index.ts b/x-pack/plugins/spaces/server/routes/api/external/index.ts index 290807f9b5f4db..6b919b68987095 100644 --- a/x-pack/plugins/spaces/server/routes/api/external/index.ts +++ b/x-pack/plugins/spaces/server/routes/api/external/index.ts @@ -4,8 +4,6 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ - -import type { BuildFlavor } from '@kbn/config/src/types'; import type { CoreSetup, Logger } from '@kbn/core/server'; import { initCopyToSpacesApi } from './copy_to_space'; @@ -27,9 +25,10 @@ export interface ExternalRouteDeps { getSpacesService: () => SpacesServiceStart; usageStatsServicePromise: Promise; log: Logger; + isServerless: boolean; } -export function initExternalSpacesApi(deps: ExternalRouteDeps, buildFlavor: BuildFlavor) { +export function initExternalSpacesApi(deps: ExternalRouteDeps) { // These two routes are always registered, internal in serverless by default initGetSpaceApi(deps); initGetAllSpacesApi(deps); @@ -37,7 +36,7 @@ export function initExternalSpacesApi(deps: ExternalRouteDeps, buildFlavor: Buil // In the serverless environment, Spaces are enabled but are effectively hidden from the user. We // do not support more than 1 space: the default space. These HTTP APIs for creating, deleting, // updating, and manipulating saved objects across multiple spaces are not needed. - if (buildFlavor !== 'serverless') { + if (!deps.isServerless) { initPutSpacesApi(deps); initDeleteSpacesApi(deps); initPostSpacesApi(deps); diff --git a/x-pack/plugins/spaces/server/routes/api/external/post.test.ts b/x-pack/plugins/spaces/server/routes/api/external/post.test.ts index 88c763f31c0bea..b36a3619a48e0a 100644 --- a/x-pack/plugins/spaces/server/routes/api/external/post.test.ts +++ b/x-pack/plugins/spaces/server/routes/api/external/post.test.ts @@ -42,7 +42,7 @@ describe('Spaces Public API', () => { const log = loggingSystemMock.create().get('spaces'); - const clientService = new SpacesClientService(jest.fn()); + const clientService = new SpacesClientService(jest.fn(), 'traditional'); clientService .setup({ config$: Rx.of(spacesConfig) }) .setClientRepositoryFactory(() => savedObjectsRepositoryMock); @@ -67,6 +67,7 @@ describe('Spaces Public API', () => { log, getSpacesService: () => spacesServiceStart, usageStatsServicePromise, + isServerless: false, }); const [routeDefinition, routeHandler] = router.post.mock.calls[0]; diff --git a/x-pack/plugins/spaces/server/routes/api/external/post.ts b/x-pack/plugins/spaces/server/routes/api/external/post.ts index d8091a0140e001..34a9c7f854dad5 100644 --- a/x-pack/plugins/spaces/server/routes/api/external/post.ts +++ b/x-pack/plugins/spaces/server/routes/api/external/post.ts @@ -11,17 +11,17 @@ import { SavedObjectsErrorHelpers } from '@kbn/core/server'; import type { ExternalRouteDeps } from '.'; import { wrapError } from '../../../lib/errors'; -import { spaceSchema } from '../../../lib/space_schema'; +import { getSpaceSchema } from '../../../lib/space_schema'; import { createLicensedRouteHandler } from '../../lib'; export function initPostSpacesApi(deps: ExternalRouteDeps) { - const { router, log, getSpacesService } = deps; + const { router, log, getSpacesService, isServerless } = deps; router.post( { path: '/api/spaces/space', validate: { - body: spaceSchema, + body: getSpaceSchema(isServerless), }, }, createLicensedRouteHandler(async (context, request, response) => { diff --git a/x-pack/plugins/spaces/server/routes/api/external/put.test.ts b/x-pack/plugins/spaces/server/routes/api/external/put.test.ts index 5e1e6077a758e9..2141e369507b91 100644 --- a/x-pack/plugins/spaces/server/routes/api/external/put.test.ts +++ b/x-pack/plugins/spaces/server/routes/api/external/put.test.ts @@ -42,7 +42,7 @@ describe('PUT /api/spaces/space', () => { const log = loggingSystemMock.create().get('spaces'); - const clientService = new SpacesClientService(jest.fn()); + const clientService = new SpacesClientService(jest.fn(), 'traditional'); clientService .setup({ config$: Rx.of(spacesConfig) }) .setClientRepositoryFactory(() => savedObjectsRepositoryMock); @@ -67,6 +67,7 @@ describe('PUT /api/spaces/space', () => { log, getSpacesService: () => spacesServiceStart, usageStatsServicePromise, + isServerless: false, }); const [routeDefinition, routeHandler] = router.put.mock.calls[0]; diff --git a/x-pack/plugins/spaces/server/routes/api/external/put.ts b/x-pack/plugins/spaces/server/routes/api/external/put.ts index 753ec8e0289259..7d0c255392ee44 100644 --- a/x-pack/plugins/spaces/server/routes/api/external/put.ts +++ b/x-pack/plugins/spaces/server/routes/api/external/put.ts @@ -11,11 +11,11 @@ import { SavedObjectsErrorHelpers } from '@kbn/core/server'; import type { ExternalRouteDeps } from '.'; import type { Space } from '../../../../common'; import { wrapError } from '../../../lib/errors'; -import { spaceSchema } from '../../../lib/space_schema'; +import { getSpaceSchema } from '../../../lib/space_schema'; import { createLicensedRouteHandler } from '../../lib'; export function initPutSpacesApi(deps: ExternalRouteDeps) { - const { router, getSpacesService } = deps; + const { router, getSpacesService, isServerless } = deps; router.put( { @@ -24,7 +24,7 @@ export function initPutSpacesApi(deps: ExternalRouteDeps) { params: schema.object({ id: schema.string(), }), - body: spaceSchema, + body: getSpaceSchema(isServerless), }, }, createLicensedRouteHandler(async (context, request, response) => { diff --git a/x-pack/plugins/spaces/server/routes/api/external/update_objects_spaces.test.ts b/x-pack/plugins/spaces/server/routes/api/external/update_objects_spaces.test.ts index 195db8148a3783..874b5d42849589 100644 --- a/x-pack/plugins/spaces/server/routes/api/external/update_objects_spaces.test.ts +++ b/x-pack/plugins/spaces/server/routes/api/external/update_objects_spaces.test.ts @@ -43,7 +43,7 @@ describe('update_objects_spaces', () => { const { savedObjects, savedObjectsClient } = createMockSavedObjectsService(spaces); coreStart.savedObjects = savedObjects; - const clientService = new SpacesClientService(jest.fn()); + const clientService = new SpacesClientService(jest.fn(), 'traditional'); clientService .setup({ config$: Rx.of(spacesConfig) }) .setClientRepositoryFactory(() => savedObjectsRepositoryMock); @@ -67,6 +67,7 @@ describe('update_objects_spaces', () => { log, getSpacesService: () => spacesServiceStart, usageStatsServicePromise, + isServerless: false, }); const [[updateObjectsSpaces, updateObjectsSpacesRouteHandler]] = router.post.mock.calls; diff --git a/x-pack/plugins/spaces/server/routes/api/internal/get_content_summary.test.ts b/x-pack/plugins/spaces/server/routes/api/internal/get_content_summary.test.ts index 8f316d12209bab..09fdbe78967614 100644 --- a/x-pack/plugins/spaces/server/routes/api/internal/get_content_summary.test.ts +++ b/x-pack/plugins/spaces/server/routes/api/internal/get_content_summary.test.ts @@ -45,7 +45,7 @@ describe('GET /internal/spaces/{spaceId}/content_summary', () => { const savedObjectsRepositoryMock = createMockSavedObjectsRepository(spacesSavedObjects); - const clientService = new SpacesClientService(jest.fn()); + const clientService = new SpacesClientService(jest.fn(), 'traditional'); clientService .setup({ config$: Rx.of(spacesConfig) }) .setClientRepositoryFactory(() => savedObjectsRepositoryMock); diff --git a/x-pack/plugins/spaces/server/saved_objects/mappings.ts b/x-pack/plugins/spaces/server/saved_objects/mappings.ts index 90d514e9fd10cd..6f4313c3b1ed51 100644 --- a/x-pack/plugins/spaces/server/saved_objects/mappings.ts +++ b/x-pack/plugins/spaces/server/saved_objects/mappings.ts @@ -19,6 +19,9 @@ export const SpacesSavedObjectMappings = deepFreeze({ }, }, }, + solution: { + type: 'keyword', + }, }, } as const); diff --git a/x-pack/plugins/spaces/server/saved_objects/saved_objects_service.ts b/x-pack/plugins/spaces/server/saved_objects/saved_objects_service.ts index b86bbf58f065ce..1cdfa00d63238f 100644 --- a/x-pack/plugins/spaces/server/saved_objects/saved_objects_service.ts +++ b/x-pack/plugins/spaces/server/saved_objects/saved_objects_service.ts @@ -5,6 +5,7 @@ * 2.0. */ +import { schema } from '@kbn/config-schema'; import type { CoreSetup } from '@kbn/core/server'; import { SpacesSavedObjectMappings, UsageStatsMappings } from './mappings'; @@ -30,6 +31,30 @@ export class SpacesSavedObjectsService { migrations: { '6.6.0': spaceMigrations.migrateTo660, }, + modelVersions: { + 1: { + changes: [ + { + type: 'mappings_addition', + addedMappings: { + solution: { type: 'keyword' }, + }, + }, + ], + schemas: { + create: SpacesSavedObjectSchemas['8.8.0'].extends({ + solution: schema.maybe( + schema.oneOf([ + schema.literal('security'), + schema.literal('observability'), + schema.literal('search'), + schema.literal('classic'), + ]) + ), + }), + }, + }, + }, }); core.savedObjects.registerType({ diff --git a/x-pack/plugins/spaces/server/spaces_client/spaces_client.test.ts b/x-pack/plugins/spaces/server/spaces_client/spaces_client.test.ts index 0f689cd492e113..600f6d1aa316cd 100644 --- a/x-pack/plugins/spaces/server/spaces_client/spaces_client.test.ts +++ b/x-pack/plugins/spaces/server/spaces_client/spaces_client.test.ts @@ -42,6 +42,7 @@ describe('#getAll', () => { imageUrl: 'go-bots/predates/transformers', disabledFeatures: [], _reserved: true, + solution: 'search', bar: 'foo-bar', // an extra attribute that will be ignored during conversion }, }, @@ -80,6 +81,7 @@ describe('#getAll', () => { initials: 'FB', imageUrl: 'go-bots/predates/transformers', disabledFeatures: [], + solution: 'search', _reserved: true, }, { @@ -105,7 +107,13 @@ describe('#getAll', () => { } as any); const mockConfig = createMockConfig(); - const client = new SpacesClient(mockDebugLogger, mockConfig, mockCallWithRequestRepository, []); + const client = new SpacesClient( + mockDebugLogger, + mockConfig, + mockCallWithRequestRepository, + [], + 'traditional' + ); const actualSpaces = await client.getAll(); expect(actualSpaces).toEqual(expectedSpaces); @@ -117,11 +125,46 @@ describe('#getAll', () => { }); }); + test('strips solution property in serverless build', async () => { + const mockDebugLogger = createMockDebugLogger(); + const [SOWithSolution] = savedObjects; + const mockCallWithRequestRepository = savedObjectsRepositoryMock.create(); + mockCallWithRequestRepository.find.mockResolvedValue({ + saved_objects: [SOWithSolution], + } as any); + const mockConfig = createMockConfig(); + + const client = new SpacesClient( + mockDebugLogger, + mockConfig, + mockCallWithRequestRepository, + [], + 'serverless' + ); + const [actualSpace] = await client.getAll(); + const [{ solution, ...expectedSpace }] = expectedSpaces; + + expect(actualSpace.solution).toBeUndefined(); + expect(actualSpace).toEqual(expectedSpace); + expect(mockCallWithRequestRepository.find).toHaveBeenCalledWith({ + type: 'space', + page: 1, + perPage: mockConfig.maxSpaces, + sortField: 'name.keyword', + }); + }); + test(`throws Boom.badRequest when an invalid purpose is provided'`, async () => { const mockDebugLogger = createMockDebugLogger(); const mockCallWithRequestRepository = savedObjectsRepositoryMock.create(); const mockConfig = createMockConfig(); - const client = new SpacesClient(mockDebugLogger, mockConfig, mockCallWithRequestRepository, []); + const client = new SpacesClient( + mockDebugLogger, + mockConfig, + mockCallWithRequestRepository, + [], + 'traditional' + ); await expect( client.getAll({ purpose: 'invalid_purpose' as GetAllSpacesPurpose }) ).rejects.toThrowErrorMatchingInlineSnapshot(`"unsupported space purpose: invalid_purpose"`); @@ -162,13 +205,64 @@ describe('#get', () => { const mockCallWithRequestRepository = savedObjectsRepositoryMock.create(); mockCallWithRequestRepository.get.mockResolvedValue(savedObject); - const client = new SpacesClient(mockDebugLogger, mockConfig, mockCallWithRequestRepository, []); + const client = new SpacesClient( + mockDebugLogger, + mockConfig, + mockCallWithRequestRepository, + [], + 'traditional' + ); const id = savedObject.id; const actualSpace = await client.get(id); expect(actualSpace).toEqual(expectedSpace); expect(mockCallWithRequestRepository.get).toHaveBeenCalledWith('space', id); }); + + test('strips solution property in serverless build', async () => { + const mockDebugLogger = createMockDebugLogger(); + const mockCallWithRequestRepository = savedObjectsRepositoryMock.create(); + mockCallWithRequestRepository.get.mockResolvedValue({ + ...savedObject, + attributes: { ...(savedObject.attributes as Record), solution: 'search' }, + }); + const mockConfig = createMockConfig(); + + const client = new SpacesClient( + mockDebugLogger, + mockConfig, + mockCallWithRequestRepository, + [], + 'serverless' + ); + const id = savedObject.id; + const actualSpace = await client.get(id); + + expect(actualSpace.solution).toBeUndefined(); + expect(actualSpace).toEqual(expectedSpace); + }); + + test(`doesn't strip solution property in traditional build`, async () => { + const mockDebugLogger = createMockDebugLogger(); + const mockCallWithRequestRepository = savedObjectsRepositoryMock.create(); + mockCallWithRequestRepository.get.mockResolvedValue({ + ...savedObject, + attributes: { ...(savedObject.attributes as Record), solution: 'search' }, + }); + const mockConfig = createMockConfig(); + + const client = new SpacesClient( + mockDebugLogger, + mockConfig, + mockCallWithRequestRepository, + [], + 'traditional' + ); + const id = savedObject.id; + const actualSpace = await client.get(id); + + expect(actualSpace).toEqual({ ...expectedSpace, solution: 'search' }); + }); }); describe('#create', () => { @@ -219,7 +313,13 @@ describe('#create', () => { allowFeatureVisibility: true, }); - const client = new SpacesClient(mockDebugLogger, mockConfig, mockCallWithRequestRepository, []); + const client = new SpacesClient( + mockDebugLogger, + mockConfig, + mockCallWithRequestRepository, + [], + 'traditional' + ); const actualSpace = await client.create(spaceToCreate); @@ -249,7 +349,13 @@ describe('#create', () => { allowFeatureVisibility: true, }); - const client = new SpacesClient(mockDebugLogger, mockConfig, mockCallWithRequestRepository, []); + const client = new SpacesClient( + mockDebugLogger, + mockConfig, + mockCallWithRequestRepository, + [], + 'traditional' + ); await expect(client.create(spaceToCreate)).rejects.toThrowErrorMatchingInlineSnapshot( `"Unable to create Space, this exceeds the maximum number of spaces set by the xpack.spaces.maxSpaces setting"` @@ -263,6 +369,93 @@ describe('#create', () => { expect(mockCallWithRequestRepository.create).not.toHaveBeenCalled(); }); + test('throws bad request when solution property is provided in serverless build', async () => { + const maxSpaces = 5; + const mockDebugLogger = createMockDebugLogger(); + const mockCallWithRequestRepository = savedObjectsRepositoryMock.create(); + mockCallWithRequestRepository.create.mockResolvedValue(savedObject); + mockCallWithRequestRepository.find.mockResolvedValue({ + total: maxSpaces - 1, + } as any); + + const mockConfig = createMockConfig({ + enabled: true, + maxSpaces, + allowFeatureVisibility: true, + }); + + const client = new SpacesClient( + mockDebugLogger, + mockConfig, + mockCallWithRequestRepository, + [], + 'serverless' + ); + + await expect( + client.create({ ...spaceToCreate, solution: undefined }) + ).rejects.toThrowErrorMatchingInlineSnapshot( + `"Unable to create Space, solution property is forbidden in serverless"` + ); + + await expect( + client.create({ ...spaceToCreate, solution: 'search' }) + ).rejects.toThrowErrorMatchingInlineSnapshot( + `"Unable to create Space, solution property is forbidden in serverless"` + ); + + expect(mockCallWithRequestRepository.find).toHaveBeenCalledWith({ + type: 'space', + page: 1, + perPage: 0, + }); + expect(mockCallWithRequestRepository.create).not.toHaveBeenCalled(); + }); + + test('creates space when solution property is provided in traditional build', async () => { + const maxSpaces = 5; + const mockDebugLogger = createMockDebugLogger(); + const mockCallWithRequestRepository = savedObjectsRepositoryMock.create(); + mockCallWithRequestRepository.create.mockResolvedValue({ + ...savedObject, + attributes: { ...(savedObject.attributes as Record), solution: 'search' }, + }); + mockCallWithRequestRepository.find.mockResolvedValue({ + total: maxSpaces - 1, + } as any); + + const mockConfig = createMockConfig({ + enabled: true, + maxSpaces, + allowFeatureVisibility: true, + }); + + const client = new SpacesClient( + mockDebugLogger, + mockConfig, + mockCallWithRequestRepository, + [], + 'traditional' + ); + + const actualSpace = await client.create({ ...spaceToCreate, solution: 'search' }); + + expect(actualSpace).toEqual({ ...expectedReturnedSpace, solution: 'search' }); + + expect(mockCallWithRequestRepository.find).toHaveBeenCalledWith({ + type: 'space', + page: 1, + perPage: 0, + }); + expect(mockCallWithRequestRepository.create).toHaveBeenCalledWith( + 'space', + { ...attributes, solution: 'search' }, + { + id, + } + ); + }); + describe('when config.allowFeatureVisibility is disabled', () => { test(`creates space without disabledFeatures`, async () => { const maxSpaces = 5; @@ -283,7 +476,8 @@ describe('#create', () => { mockDebugLogger, mockConfig, mockCallWithRequestRepository, - [] + [], + 'traditional' ); const actualSpace = await client.create(spaceToCreate); @@ -318,7 +512,8 @@ describe('#create', () => { mockDebugLogger, mockConfig, mockCallWithRequestRepository, - [] + [], + 'traditional' ); await expect( @@ -377,7 +572,13 @@ describe('#update', () => { const mockCallWithRequestRepository = savedObjectsRepositoryMock.create(); mockCallWithRequestRepository.get.mockResolvedValue(savedObject); - const client = new SpacesClient(mockDebugLogger, mockConfig, mockCallWithRequestRepository, []); + const client = new SpacesClient( + mockDebugLogger, + mockConfig, + mockCallWithRequestRepository, + [], + 'traditional' + ); const id = savedObject.id; const actualSpace = await client.update(id, spaceToUpdate); @@ -386,6 +587,87 @@ describe('#update', () => { expect(mockCallWithRequestRepository.get).toHaveBeenCalledWith('space', id); }); + test('throws bad request when solution property is provided in serverless build', async () => { + const mockDebugLogger = createMockDebugLogger(); + const mockConfig = createMockConfig(); + const mockCallWithRequestRepository = savedObjectsRepositoryMock.create(); + mockCallWithRequestRepository.get.mockResolvedValue(savedObject); + + const client = new SpacesClient( + mockDebugLogger, + mockConfig, + mockCallWithRequestRepository, + [], + 'serverless' + ); + const id = savedObject.id; + + await expect( + client.update(id, { ...spaceToUpdate, solution: undefined }) + ).rejects.toThrowErrorMatchingInlineSnapshot( + `"Unable to update Space, solution property is forbidden in serverless"` + ); + + await expect( + client.update(id, { ...spaceToUpdate, solution: 'search' }) + ).rejects.toThrowErrorMatchingInlineSnapshot( + `"Unable to update Space, solution property is forbidden in serverless"` + ); + + expect(mockCallWithRequestRepository.update).not.toHaveBeenCalled(); + + expect(mockCallWithRequestRepository.get).not.toHaveBeenCalled(); + }); + + test('throws bad request when solution property is undefined in traditional build', async () => { + const mockDebugLogger = createMockDebugLogger(); + const mockConfig = createMockConfig(); + const mockCallWithRequestRepository = savedObjectsRepositoryMock.create(); + mockCallWithRequestRepository.get.mockResolvedValue(savedObject); + + const client = new SpacesClient( + mockDebugLogger, + mockConfig, + mockCallWithRequestRepository, + [], + 'traditional' + ); + const id = savedObject.id; + + await expect( + client.update(id, { ...spaceToUpdate, solution: undefined }) + ).rejects.toThrowErrorMatchingInlineSnapshot( + `"Unable to update Space, solution property cannot be empty"` + ); + + expect(mockCallWithRequestRepository.update).not.toHaveBeenCalled(); + + expect(mockCallWithRequestRepository.get).not.toHaveBeenCalled(); + }); + + test('updates space with solution property using callWithRequestRepository in traditional build', async () => { + const mockDebugLogger = createMockDebugLogger(); + const mockConfig = createMockConfig(); + const mockCallWithRequestRepository = savedObjectsRepositoryMock.create(); + mockCallWithRequestRepository.get.mockResolvedValue(savedObject); + + const client = new SpacesClient( + mockDebugLogger, + mockConfig, + mockCallWithRequestRepository, + [], + 'traditional' + ); + const id = savedObject.id; + await client.update(id, { ...spaceToUpdate, solution: 'search' }); + + expect(mockCallWithRequestRepository.update).toHaveBeenCalledWith('space', id, { + ...attributes, + solution: 'search', + }); + expect(mockCallWithRequestRepository.get).toHaveBeenCalledWith('space', id); + }); + describe('when config.allowFeatureVisibility is disabled', () => { test(`updates space without disabledFeatures`, async () => { const mockDebugLogger = createMockDebugLogger(); @@ -401,7 +683,8 @@ describe('#update', () => { mockDebugLogger, mockConfig, mockCallWithRequestRepository, - [] + [], + 'traditional' ); const id = savedObject.id; const actualSpace = await client.update(id, spaceToUpdate); @@ -425,7 +708,8 @@ describe('#update', () => { mockDebugLogger, mockConfig, mockCallWithRequestRepository, - [] + [], + 'traditional' ); const id = savedObject.id; @@ -473,7 +757,13 @@ describe('#delete', () => { const mockCallWithRequestRepository = savedObjectsRepositoryMock.create(); mockCallWithRequestRepository.get.mockResolvedValue(reservedSavedObject); - const client = new SpacesClient(mockDebugLogger, mockConfig, mockCallWithRequestRepository, []); + const client = new SpacesClient( + mockDebugLogger, + mockConfig, + mockCallWithRequestRepository, + [], + 'traditional' + ); await expect(client.delete(id)).rejects.toThrowErrorMatchingInlineSnapshot( `"The foo space cannot be deleted because it is reserved."` @@ -488,7 +778,13 @@ describe('#delete', () => { const mockCallWithRequestRepository = savedObjectsRepositoryMock.create(); mockCallWithRequestRepository.get.mockResolvedValue(notReservedSavedObject); - const client = new SpacesClient(mockDebugLogger, mockConfig, mockCallWithRequestRepository, []); + const client = new SpacesClient( + mockDebugLogger, + mockConfig, + mockCallWithRequestRepository, + [], + 'traditional' + ); await client.delete(id); @@ -504,7 +800,13 @@ describe('#disableLegacyUrlAliases', () => { const mockConfig = createMockConfig(); const mockCallWithRequestRepository = savedObjectsRepositoryMock.create(); - const client = new SpacesClient(mockDebugLogger, mockConfig, mockCallWithRequestRepository, []); + const client = new SpacesClient( + mockDebugLogger, + mockConfig, + mockCallWithRequestRepository, + [], + 'traditional' + ); const aliases = [ { targetSpace: 'space1', targetType: 'foo', sourceId: '123' }, { targetSpace: 'space2', targetType: 'bar', sourceId: '456' }, diff --git a/x-pack/plugins/spaces/server/spaces_client/spaces_client.ts b/x-pack/plugins/spaces/server/spaces_client/spaces_client.ts index cc4058ad22485a..536765e8dac473 100644 --- a/x-pack/plugins/spaces/server/spaces_client/spaces_client.ts +++ b/x-pack/plugins/spaces/server/spaces_client/spaces_client.ts @@ -7,6 +7,7 @@ import Boom from '@hapi/boom'; +import type { BuildFlavor } from '@kbn/config/src/types'; import type { ISavedObjectsPointInTimeFinder, ISavedObjectsRepository, @@ -80,12 +81,17 @@ export interface ISpacesClient { * Client for interacting with spaces. */ export class SpacesClient implements ISpacesClient { + private isServerless = false; + constructor( private readonly debugLogger: (message: string) => void, private readonly config: ConfigType, private readonly repository: ISavedObjectsRepository, - private readonly nonGlobalTypeNames: string[] - ) {} + private readonly nonGlobalTypeNames: string[], + private readonly buildFlavour: BuildFlavor + ) { + this.isServerless = this.buildFlavour === 'serverless'; + } public async getAll(options: v1.GetAllSpacesOptions = {}): Promise { const { purpose = DEFAULT_PURPOSE } = options; @@ -130,10 +136,19 @@ export class SpacesClient implements ISpacesClient { ); } + if (this.isServerless && space.hasOwnProperty('solution')) { + throw Boom.badRequest('Unable to create Space, solution property is forbidden in serverless'); + } + + if (space.hasOwnProperty('solution') && !space.solution) { + throw Boom.badRequest('Unable to create Space, solution property cannot be empty'); + } + this.debugLogger(`SpacesClient.create(), using RBAC. Attempting to create space`); const id = space.id; const attributes = this.generateSpaceAttributes(space); + const createdSavedObject = await this.repository.create('space', attributes, { id }); this.debugLogger(`SpacesClient.create(), created space object`); @@ -148,6 +163,14 @@ export class SpacesClient implements ISpacesClient { ); } + if (this.isServerless && space.hasOwnProperty('solution')) { + throw Boom.badRequest('Unable to update Space, solution property is forbidden in serverless'); + } + + if (space.hasOwnProperty('solution') && !space.solution) { + throw Boom.badRequest('Unable to update Space, solution property cannot be empty'); + } + const attributes = this.generateSpaceAttributes(space); await this.repository.update('space', id, attributes); const updatedSavedObject = await this.repository.get('space', id); @@ -181,7 +204,7 @@ export class SpacesClient implements ISpacesClient { await this.repository.bulkUpdate(objectsToUpdate); } - private transformSavedObjectToSpace(savedObject: SavedObject): v1.Space { + private transformSavedObjectToSpace = (savedObject: SavedObject): v1.Space => { return { id: savedObject.id, name: savedObject.attributes.name ?? '', @@ -191,10 +214,11 @@ export class SpacesClient implements ISpacesClient { imageUrl: savedObject.attributes.imageUrl, disabledFeatures: savedObject.attributes.disabledFeatures ?? [], _reserved: savedObject.attributes._reserved, + ...(!this.isServerless ? { solution: savedObject.attributes.solution } : {}), } as v1.Space; - } + }; - private generateSpaceAttributes(space: v1.Space) { + private generateSpaceAttributes = (space: v1.Space) => { return { name: space.name, description: space.description, @@ -202,6 +226,7 @@ export class SpacesClient implements ISpacesClient { initials: space.initials, imageUrl: space.imageUrl, disabledFeatures: space.disabledFeatures, + ...(!this.isServerless && space.solution ? { solution: space.solution } : {}), }; - } + }; } diff --git a/x-pack/plugins/spaces/server/spaces_client/spaces_client_service.test.ts b/x-pack/plugins/spaces/server/spaces_client/spaces_client_service.test.ts index 455387a816bd50..cedcdec858e557 100644 --- a/x-pack/plugins/spaces/server/spaces_client/spaces_client_service.test.ts +++ b/x-pack/plugins/spaces/server/spaces_client/spaces_client_service.test.ts @@ -20,7 +20,7 @@ const debugLogger = jest.fn(); describe('SpacesClientService', () => { describe('#setup', () => { it('allows a single repository factory to be set', () => { - const service = new SpacesClientService(debugLogger); + const service = new SpacesClientService(debugLogger, 'traditional'); const setup = service.setup({ config$: Rx.of(spacesConfig) }); const repositoryFactory = jest.fn(); @@ -32,7 +32,7 @@ describe('SpacesClientService', () => { }); it('allows a single client wrapper to be set', () => { - const service = new SpacesClientService(debugLogger); + const service = new SpacesClientService(debugLogger, 'traditional'); const setup = service.setup({ config$: Rx.of(spacesConfig) }); const clientWrapper = jest.fn(); @@ -46,7 +46,7 @@ describe('SpacesClientService', () => { describe('#start', () => { it('throws if config is not available', () => { - const service = new SpacesClientService(debugLogger); + const service = new SpacesClientService(debugLogger, 'traditional'); service.setup({ config$: new Rx.Observable() }); const coreStart = coreMock.createStart(); const start = service.start(coreStart); @@ -60,7 +60,7 @@ describe('SpacesClientService', () => { describe('without a custom repository factory or wrapper', () => { it('returns an instance of the spaces client using the scoped repository', () => { - const service = new SpacesClientService(debugLogger); + const service = new SpacesClientService(debugLogger, 'traditional'); service.setup({ config$: Rx.of(spacesConfig) }); const coreStart = coreMock.createStart(); @@ -78,7 +78,7 @@ describe('SpacesClientService', () => { }); it('uses the custom repository factory when set', () => { - const service = new SpacesClientService(debugLogger); + const service = new SpacesClientService(debugLogger, 'traditional'); const setup = service.setup({ config$: Rx.of(spacesConfig) }); const customRepositoryFactory = jest.fn(); @@ -98,7 +98,7 @@ describe('SpacesClientService', () => { }); it('wraps the client in the wrapper when registered', () => { - const service = new SpacesClientService(debugLogger); + const service = new SpacesClientService(debugLogger, 'traditional'); const setup = service.setup({ config$: Rx.of(spacesConfig) }); const wrapper = Symbol() as unknown as ISpacesClient; @@ -123,7 +123,7 @@ describe('SpacesClientService', () => { }); it('wraps the client in the wrapper when registered, using the custom repository factory when configured', () => { - const service = new SpacesClientService(debugLogger); + const service = new SpacesClientService(debugLogger, 'traditional'); const setup = service.setup({ config$: Rx.of(spacesConfig) }); const customRepositoryFactory = jest.fn(); diff --git a/x-pack/plugins/spaces/server/spaces_client/spaces_client_service.ts b/x-pack/plugins/spaces/server/spaces_client/spaces_client_service.ts index 8a6c0eaab0d3b8..26d4fef85ea6dc 100644 --- a/x-pack/plugins/spaces/server/spaces_client/spaces_client_service.ts +++ b/x-pack/plugins/spaces/server/spaces_client/spaces_client_service.ts @@ -7,6 +7,7 @@ import type { Observable } from 'rxjs'; +import type { BuildFlavor } from '@kbn/config/src/types'; import type { CoreStart, ISavedObjectsRepository, @@ -72,7 +73,10 @@ export class SpacesClientService { private clientWrapper?: SpacesClientWrapper; - constructor(private readonly debugLogger: (message: string) => void) {} + constructor( + private readonly debugLogger: (message: string) => void, + private readonly buildFlavour: BuildFlavor + ) {} public setup({ config$ }: SetupDeps): SpacesClientServiceSetup { config$.subscribe((nextConfig) => { @@ -117,7 +121,8 @@ export class SpacesClientService { this.debugLogger, this.config, this.repositoryFactory!(request, coreStart.savedObjects), - nonGlobalTypeNames + nonGlobalTypeNames, + this.buildFlavour ); if (this.clientWrapper) { return this.clientWrapper(request, baseClient); diff --git a/x-pack/plugins/spaces/server/spaces_service/spaces_service.test.ts b/x-pack/plugins/spaces/server/spaces_service/spaces_service.test.ts index 694fb5b69e46ae..8222b43018b2e9 100644 --- a/x-pack/plugins/spaces/server/spaces_service/spaces_service.test.ts +++ b/x-pack/plugins/spaces/server/spaces_service/spaces_service.test.ts @@ -69,7 +69,7 @@ const createService = (serverBasePath: string = '') => { basePath: httpSetup.basePath, }); - const spacesClientService = new SpacesClientService(jest.fn()); + const spacesClientService = new SpacesClientService(jest.fn(), 'traditional'); spacesClientService.setup({ config$: Rx.of(spacesConfig), }); diff --git a/x-pack/plugins/stack_alerts/public/rule_types/es_query/constants.ts b/x-pack/plugins/stack_alerts/public/rule_types/es_query/constants.ts index 5c3c811143042d..7d01043bfbc214 100644 --- a/x-pack/plugins/stack_alerts/public/rule_types/es_query/constants.ts +++ b/x-pack/plugins/stack_alerts/public/rule_types/es_query/constants.ts @@ -5,8 +5,7 @@ * 2.0. */ -import { COMPARATORS } from '@kbn/triggers-actions-ui-plugin/public'; - +import { COMPARATORS } from '@kbn/alerting-comparators'; export const DEFAULT_VALUES = { THRESHOLD_COMPARATOR: COMPARATORS.GREATER_THAN, QUERY: `{ diff --git a/x-pack/plugins/stack_alerts/public/rule_types/es_query/validation.ts b/x-pack/plugins/stack_alerts/public/rule_types/es_query/validation.ts index d987c5c6377a8a..c8119110d76a2a 100644 --- a/x-pack/plugins/stack_alerts/public/rule_types/es_query/validation.ts +++ b/x-pack/plugins/stack_alerts/public/rule_types/es_query/validation.ts @@ -12,8 +12,8 @@ import { builtInComparators, builtInAggregationTypes, builtInGroupByTypes, - COMPARATORS, } from '@kbn/triggers-actions-ui-plugin/public'; +import { COMPARATORS } from '@kbn/alerting-comparators'; import { MAX_SELECTABLE_SOURCE_FIELDS, MAX_SELECTABLE_GROUP_BY_TERMS, diff --git a/x-pack/plugins/stack_alerts/public/rule_types/threshold/expression.tsx b/x-pack/plugins/stack_alerts/public/rule_types/threshold/expression.tsx index 515517577251f7..de0091a8202c7d 100644 --- a/x-pack/plugins/stack_alerts/public/rule_types/threshold/expression.tsx +++ b/x-pack/plugins/stack_alerts/public/rule_types/threshold/expression.tsx @@ -20,7 +20,6 @@ import { HttpSetup } from '@kbn/core/public'; import { useKibana } from '@kbn/kibana-react-plugin/public'; import { getFields, - COMPARATORS, builtInComparators, OfExpression, ThresholdExpression, @@ -30,6 +29,7 @@ import { builtInAggregationTypes, RuleTypeParamsExpressionProps, } from '@kbn/triggers-actions-ui-plugin/public'; +import { COMPARATORS } from '@kbn/alerting-comparators'; import { ThresholdVisualization } from './visualization'; import { IndexThresholdRuleParams } from './types'; import './expression.scss'; diff --git a/x-pack/plugins/stack_alerts/public/rule_types/threshold/visualization.tsx b/x-pack/plugins/stack_alerts/public/rule_types/threshold/visualization.tsx index 918c87c5d13a87..aee314a86cd783 100644 --- a/x-pack/plugins/stack_alerts/public/rule_types/threshold/visualization.tsx +++ b/x-pack/plugins/stack_alerts/public/rule_types/threshold/visualization.tsx @@ -33,7 +33,8 @@ import { FormattedMessage } from '@kbn/i18n-react'; import { ChartsPluginSetup } from '@kbn/charts-plugin/public'; import { FieldFormatsStart } from '@kbn/field-formats-plugin/public'; import { useKibana } from '@kbn/kibana-react-plugin/public'; -import { AggregationType, Comparator } from '@kbn/triggers-actions-ui-plugin/public'; +import { AggregationType } from '@kbn/triggers-actions-ui-plugin/public'; +import type { Comparator } from '@kbn/alerting-comparators'; import { parseDuration } from '@kbn/alerting-plugin/common/parse_duration'; import { i18n } from '@kbn/i18n'; import { diff --git a/x-pack/plugins/stack_alerts/tsconfig.json b/x-pack/plugins/stack_alerts/tsconfig.json index a765291e89d98f..b005183198f32a 100644 --- a/x-pack/plugins/stack_alerts/tsconfig.json +++ b/x-pack/plugins/stack_alerts/tsconfig.json @@ -51,6 +51,7 @@ "@kbn/esql-utils", "@kbn/data-view-utils", "@kbn/search-types", + "@kbn/alerting-comparators" ], "exclude": [ "target/**/*", diff --git a/x-pack/plugins/task_manager/tsconfig.json b/x-pack/plugins/task_manager/tsconfig.json index d1accb045d5266..2c265a93e55a1d 100644 --- a/x-pack/plugins/task_manager/tsconfig.json +++ b/x-pack/plugins/task_manager/tsconfig.json @@ -10,7 +10,6 @@ "common/**/*" ], "kbn_references": [ - "@kbn/alerting-state-types", "@kbn/core", "@kbn/usage-collection-plugin", "@kbn/config-schema", @@ -22,7 +21,8 @@ "@kbn/core-saved-objects-common", "@kbn/core-saved-objects-utils-server", "@kbn/core-test-helpers-kbn-server", - "@kbn/core-saved-objects-server" + "@kbn/core-saved-objects-server", + "@kbn/alerting-state-types" ], "exclude": [ "target/**/*", diff --git a/x-pack/plugins/translations/translations/fr-FR.json b/x-pack/plugins/translations/translations/fr-FR.json index a762a716fa1171..f68a59f484638b 100644 --- a/x-pack/plugins/translations/translations/fr-FR.json +++ b/x-pack/plugins/translations/translations/fr-FR.json @@ -21654,7 +21654,6 @@ "xpack.infra.metrics.alertFlyout.groupDisappearHelpText": "Activez cette option pour déclencher l’action si un groupe précédemment détecté cesse de signaler des résultats. Ce n’est pas recommandé pour les infrastructures à montée en charge dynamique qui peuvent rapidement lancer ou stopper des nœuds automatiquement.", "xpack.infra.metrics.alertFlyout.noDataHelpText": "Activez cette option pour déclencher l'action si l'indicateur ou les indicateurs ne signale(nt) aucune donnée sur la période attendue, ou si l'alerte ne parvient pas à interroger Elasticsearch", "xpack.infra.metrics.alertFlyout.ofExpression.popoverLinkLabel": "Apprenez comment ajouter davantage de données", - "xpack.infra.metrics.alertFlyout.outsideRangeLabel": "N'est pas entre", "xpack.infra.metrics.alertFlyout.removeCondition": "Retirer la condition", "xpack.infra.metrics.alertFlyout.removeWarningThreshold": "Retirer le seuil d'avertissement", "xpack.infra.metrics.alertFlyout.warningThreshold": "Avertissement", @@ -25464,7 +25463,6 @@ "xpack.ml.actions.clearSelectionTitle": "Effacer la sélection", "xpack.ml.actions.createADJobFromLens": "Créer une tâche de détection des anomalies", "xpack.ml.actions.createADJobFromPatternAnalysis": "Créer une tâche de catégorisation et de détection des anomalies", - "xpack.ml.actions.editAnomalyChartsTitle": "Modifier les graphiques d'anomalies", "xpack.ml.actions.openInAnomalyExplorerTitle": "Ouvrir dans Anomaly Explorer", "xpack.ml.actions.runPatternAnalysis.description": "Déclenché lorsque l'utilisateur souhaite effectuer une analyse du modèle sur un champ.", "xpack.ml.actions.runPatternAnalysis.title": "Exécuter l'analyse du modèle", @@ -25673,7 +25671,6 @@ "xpack.ml.anomalyChartsEmbeddable.maxSeriesToPlotLabel": "Nombre maximal de séries à tracer", "xpack.ml.anomalyChartsEmbeddable.panelTitleLabel": "Titre du panneau", "xpack.ml.anomalyChartsEmbeddable.setupModal.cancelButtonLabel": "Annuler", - "xpack.ml.anomalyChartsEmbeddable.setupModal.confirmButtonLabel": "Confirmer les configurations", "xpack.ml.anomalyChartsEmbeddable.setupModal.title": "Configuration des graphiques Anomaly Explorer", "xpack.ml.anomalyDetection.anomalyExplorer.docTitle": "Anomaly Explorer (Explorateur d'anomalies)", "xpack.ml.anomalyDetection.anomalyExplorerLabel": "Anomaly Explorer (Explorateur d'anomalies)", @@ -29974,7 +29971,6 @@ "xpack.observability.customThreshold.rule.alertFlyout.error.thresholdTypeRequired": "Les seuils doivent contenir un nombre valide.", "xpack.observability.customThreshold.rule.alertFlyout.error.timeRequred": "Durée obligatoire.", "xpack.observability.customThreshold.rule.alertFlyout.groupDisappearHelpText": "Activez cette option pour déclencher l’action si un groupe précédemment détecté cesse de signaler des résultats. Ce n’est pas recommandé pour les infrastructures à montée en charge dynamique qui peuvent rapidement lancer ou stopper des nœuds automatiquement.", - "xpack.observability.customThreshold.rule.alertFlyout.outsideRangeLabel": "N'est pas entre", "xpack.observability.customThreshold.rule.alertFlyout.removeCondition": "Retirer la condition", "xpack.observability.customThreshold.rule.alertFlyout.searchBar.placeholder": "Rechercher des données d'observabilité… (par exemple host.name:host-1)", "xpack.observability.customThreshold.rule.alertFlyout.selectDataViewPrompt": "Sélectionner une vue de données", diff --git a/x-pack/plugins/translations/translations/ja-JP.json b/x-pack/plugins/translations/translations/ja-JP.json index 9d39d1d2cea59b..e7d393cd5595c5 100644 --- a/x-pack/plugins/translations/translations/ja-JP.json +++ b/x-pack/plugins/translations/translations/ja-JP.json @@ -21628,7 +21628,6 @@ "xpack.infra.metrics.alertFlyout.groupDisappearHelpText": "以前に検出されたグループが結果を報告しなくなった場合は、これを有効にすると、アクションがトリガーされます。自動的に急速にノードを開始および停止することがある動的に拡張するインフラストラクチャーでは、これは推奨されません。", "xpack.infra.metrics.alertFlyout.noDataHelpText": "有効にすると、メトリックが想定された期間内にデータを報告しない場合、またはアラートがElasticsearchをクエリできない場合に、アクションをトリガーします", "xpack.infra.metrics.alertFlyout.ofExpression.popoverLinkLabel": "データの追加方法", - "xpack.infra.metrics.alertFlyout.outsideRangeLabel": "is not between", "xpack.infra.metrics.alertFlyout.removeCondition": "条件を削除", "xpack.infra.metrics.alertFlyout.removeWarningThreshold": "warningThresholdを削除", "xpack.infra.metrics.alertFlyout.warningThreshold": "警告", @@ -25437,7 +25436,6 @@ "xpack.ml.actions.clearSelectionTitle": "選択した項目をクリア", "xpack.ml.actions.createADJobFromLens": "異常検知ジョブの作成", "xpack.ml.actions.createADJobFromPatternAnalysis": "分類異常検知ジョブを作成", - "xpack.ml.actions.editAnomalyChartsTitle": "異常グラフを編集", "xpack.ml.actions.openInAnomalyExplorerTitle": "異常エクスプローラーで開く", "xpack.ml.actions.runPatternAnalysis.description": "ユーザーがフィールドに対してパターン分析を実行する場合にトリガーされます。", "xpack.ml.actions.runPatternAnalysis.title": "パターン分析を実行", @@ -25646,7 +25644,6 @@ "xpack.ml.anomalyChartsEmbeddable.maxSeriesToPlotLabel": "プロットする最大系列数", "xpack.ml.anomalyChartsEmbeddable.panelTitleLabel": "パネルタイトル", "xpack.ml.anomalyChartsEmbeddable.setupModal.cancelButtonLabel": "キャンセル", - "xpack.ml.anomalyChartsEmbeddable.setupModal.confirmButtonLabel": "構成を確認", "xpack.ml.anomalyChartsEmbeddable.setupModal.title": "異常エクスプローラーグラフ構成", "xpack.ml.anomalyDetection.anomalyExplorer.docTitle": "異常エクスプローラー", "xpack.ml.anomalyDetection.anomalyExplorerLabel": "異常エクスプローラー", @@ -29946,7 +29943,6 @@ "xpack.observability.customThreshold.rule.alertFlyout.error.thresholdTypeRequired": "しきい値には有効な数値を含める必要があります。", "xpack.observability.customThreshold.rule.alertFlyout.error.timeRequred": "ページサイズが必要です。", "xpack.observability.customThreshold.rule.alertFlyout.groupDisappearHelpText": "以前に検出されたグループが結果を報告しなくなった場合は、これを有効にすると、アクションがトリガーされます。自動的に急速にノードを開始および停止することがある動的に拡張するインフラストラクチャーでは、これは推奨されません。", - "xpack.observability.customThreshold.rule.alertFlyout.outsideRangeLabel": "is not between", "xpack.observability.customThreshold.rule.alertFlyout.removeCondition": "条件を削除", "xpack.observability.customThreshold.rule.alertFlyout.searchBar.placeholder": "オブザーバビリティデータを検索…(例:host.name:host-1)", "xpack.observability.customThreshold.rule.alertFlyout.selectDataViewPrompt": "データビューを選択", diff --git a/x-pack/plugins/translations/translations/zh-CN.json b/x-pack/plugins/translations/translations/zh-CN.json index 213e903c5dee2c..10435bf1bdfc83 100644 --- a/x-pack/plugins/translations/translations/zh-CN.json +++ b/x-pack/plugins/translations/translations/zh-CN.json @@ -21661,7 +21661,6 @@ "xpack.infra.metrics.alertFlyout.groupDisappearHelpText": "启用此选项可在之前检测的组开始不报告任何数据时触发操作。不建议将此选项用于可能会快速自动启动和停止节点的动态扩展基础架构。", "xpack.infra.metrics.alertFlyout.noDataHelpText": "启用此选项可在指标在预期的时间段中未报告任何数据时或告警无法查询 Elasticsearch 时触发操作", "xpack.infra.metrics.alertFlyout.ofExpression.popoverLinkLabel": "了解如何添加更多数据", - "xpack.infra.metrics.alertFlyout.outsideRangeLabel": "不介于", "xpack.infra.metrics.alertFlyout.removeCondition": "删除条件", "xpack.infra.metrics.alertFlyout.removeWarningThreshold": "移除警告阈值", "xpack.infra.metrics.alertFlyout.warningThreshold": "警告", @@ -25476,7 +25475,6 @@ "xpack.ml.actions.clearSelectionTitle": "清除所选内容", "xpack.ml.actions.createADJobFromLens": "创建异常检测作业", "xpack.ml.actions.createADJobFromPatternAnalysis": "创建归类异常检测作业", - "xpack.ml.actions.editAnomalyChartsTitle": "编辑异常图表", "xpack.ml.actions.openInAnomalyExplorerTitle": "在 Anomaly Explorer 中打开", "xpack.ml.actions.runPatternAnalysis.description": "在用户希望对字段运行模式分析时触发。", "xpack.ml.actions.runPatternAnalysis.title": "运行模式分析", @@ -25685,7 +25683,6 @@ "xpack.ml.anomalyChartsEmbeddable.maxSeriesToPlotLabel": "要绘制的最大序列数目", "xpack.ml.anomalyChartsEmbeddable.panelTitleLabel": "面板标题", "xpack.ml.anomalyChartsEmbeddable.setupModal.cancelButtonLabel": "取消", - "xpack.ml.anomalyChartsEmbeddable.setupModal.confirmButtonLabel": "确认配置", "xpack.ml.anomalyChartsEmbeddable.setupModal.title": "异常浏览器图表配置", "xpack.ml.anomalyDetection.anomalyExplorer.docTitle": "Anomaly Explorer", "xpack.ml.anomalyDetection.anomalyExplorerLabel": "Anomaly Explorer", @@ -29987,7 +29984,6 @@ "xpack.observability.customThreshold.rule.alertFlyout.error.thresholdTypeRequired": "阈值必须包含有效数字。", "xpack.observability.customThreshold.rule.alertFlyout.error.timeRequred": "“时间大小”必填。", "xpack.observability.customThreshold.rule.alertFlyout.groupDisappearHelpText": "启用此选项可在之前检测的组开始不报告任何数据时触发操作。不建议将此选项用于可能会快速自动启动和停止节点的动态扩展基础架构。", - "xpack.observability.customThreshold.rule.alertFlyout.outsideRangeLabel": "不介于", "xpack.observability.customThreshold.rule.alertFlyout.removeCondition": "删除条件", "xpack.observability.customThreshold.rule.alertFlyout.searchBar.placeholder": "搜索 Observability 数据……(例如 host.name:host-1)", "xpack.observability.customThreshold.rule.alertFlyout.selectDataViewPrompt": "选择数据视图", diff --git a/x-pack/plugins/triggers_actions_ui/kibana.jsonc b/x-pack/plugins/triggers_actions_ui/kibana.jsonc index 928ae6f648f010..66fcd64dabb937 100644 --- a/x-pack/plugins/triggers_actions_ui/kibana.jsonc +++ b/x-pack/plugins/triggers_actions_ui/kibana.jsonc @@ -10,7 +10,6 @@ "xpack", "trigger_actions_ui" ], - "requiredPlugins": [ "management", "charts", @@ -46,7 +45,7 @@ ], "extraPublicDirs": [ "public/common", - "public/common/constants" + "public/common/constants", ] } -} +} \ No newline at end of file diff --git a/x-pack/plugins/triggers_actions_ui/public/common/constants/comparators.ts b/x-pack/plugins/triggers_actions_ui/public/common/constants/comparators.ts deleted file mode 100644 index 0bec60efab15d8..00000000000000 --- a/x-pack/plugins/triggers_actions_ui/public/common/constants/comparators.ts +++ /dev/null @@ -1,69 +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 { i18n } from '@kbn/i18n'; -import { Comparator } from '../types'; - -export enum COMPARATORS { - GREATER_THAN = '>', - GREATER_THAN_OR_EQUALS = '>=', - BETWEEN = 'between', - LESS_THAN = '<', - LESS_THAN_OR_EQUALS = '<=', - NOT_BETWEEN = 'notBetween', -} - -export const builtInComparators: { [key: string]: Comparator } = { - [COMPARATORS.GREATER_THAN]: { - text: i18n.translate('xpack.triggersActionsUI.common.constants.comparators.isAboveLabel', { - defaultMessage: 'Is above', - }), - value: COMPARATORS.GREATER_THAN, - requiredValues: 1, - }, - [COMPARATORS.GREATER_THAN_OR_EQUALS]: { - text: i18n.translate( - 'xpack.triggersActionsUI.common.constants.comparators.isAboveOrEqualsLabel', - { - defaultMessage: 'Is above or equals', - } - ), - value: COMPARATORS.GREATER_THAN_OR_EQUALS, - requiredValues: 1, - }, - [COMPARATORS.LESS_THAN]: { - text: i18n.translate('xpack.triggersActionsUI.common.constants.comparators.isBelowLabel', { - defaultMessage: 'Is below', - }), - value: COMPARATORS.LESS_THAN, - requiredValues: 1, - }, - [COMPARATORS.LESS_THAN_OR_EQUALS]: { - text: i18n.translate( - 'xpack.triggersActionsUI.common.constants.comparators.isBelowOrEqualsLabel', - { - defaultMessage: 'Is below or equals', - } - ), - value: COMPARATORS.LESS_THAN_OR_EQUALS, - requiredValues: 1, - }, - [COMPARATORS.BETWEEN]: { - text: i18n.translate('xpack.triggersActionsUI.common.constants.comparators.isBetweenLabel', { - defaultMessage: 'Is between', - }), - value: COMPARATORS.BETWEEN, - requiredValues: 2, - }, - [COMPARATORS.NOT_BETWEEN]: { - text: i18n.translate('xpack.triggersActionsUI.common.constants.comparators.isNotBetweenLabel', { - defaultMessage: 'Not between', - }), - value: COMPARATORS.NOT_BETWEEN, - requiredValues: 2, - }, -}; diff --git a/x-pack/plugins/triggers_actions_ui/public/common/constants/index.ts b/x-pack/plugins/triggers_actions_ui/public/common/constants/index.ts index c9437c21c12ad9..1398b33e787b83 100644 --- a/x-pack/plugins/triggers_actions_ui/public/common/constants/index.ts +++ b/x-pack/plugins/triggers_actions_ui/public/common/constants/index.ts @@ -5,7 +5,9 @@ * 2.0. */ -export { COMPARATORS, builtInComparators } from './comparators'; +import { Comparator, COMPARATORS } from '@kbn/alerting-comparators'; +import { i18n } from '@kbn/i18n'; + export { AGGREGATION_TYPES, builtInAggregationTypes } from './aggregation_types'; export { loadAllActions, loadActionTypes } from '../../application/lib/action_connector_api'; export { ConnectorAddModal } from '../../application/sections/action_connector_form'; @@ -20,3 +22,54 @@ export const PLUGIN_ID = 'triggersActions'; export const ALERTS_PAGE_ID = 'triggersActionsAlerts'; export const CONNECTORS_PLUGIN_ID = 'triggersActionsConnectors'; export * from './i18n_weekdays'; + +export const builtInComparators: { [key: string]: Comparator } = { + [COMPARATORS.GREATER_THAN]: { + text: i18n.translate('xpack.triggersActionsUI.common.constants.comparators.isAboveLabel', { + defaultMessage: 'Is above', + }), + value: COMPARATORS.GREATER_THAN, + requiredValues: 1, + }, + [COMPARATORS.GREATER_THAN_OR_EQUALS]: { + text: i18n.translate( + 'xpack.triggersActionsUI.common.constants.comparators.isAboveOrEqualsLabel', + { + defaultMessage: 'Is above or equals', + } + ), + value: COMPARATORS.GREATER_THAN_OR_EQUALS, + requiredValues: 1, + }, + [COMPARATORS.LESS_THAN]: { + text: i18n.translate('xpack.triggersActionsUI.common.constants.comparators.isBelowLabel', { + defaultMessage: 'Is below', + }), + value: COMPARATORS.LESS_THAN, + requiredValues: 1, + }, + [COMPARATORS.LESS_THAN_OR_EQUALS]: { + text: i18n.translate( + 'xpack.triggersActionsUI.common.constants.comparators.isBelowOrEqualsLabel', + { + defaultMessage: 'Is below or equals', + } + ), + value: COMPARATORS.LESS_THAN_OR_EQUALS, + requiredValues: 1, + }, + [COMPARATORS.BETWEEN]: { + text: i18n.translate('xpack.triggersActionsUI.common.constants.comparators.isBetweenLabel', { + defaultMessage: 'Is between', + }), + value: COMPARATORS.BETWEEN, + requiredValues: 2, + }, + [COMPARATORS.NOT_BETWEEN]: { + text: i18n.translate('xpack.triggersActionsUI.common.constants.comparators.isNotBetweenLabel', { + defaultMessage: 'Is not between', + }), + value: COMPARATORS.NOT_BETWEEN, + requiredValues: 2, + }, +}; diff --git a/x-pack/plugins/triggers_actions_ui/public/common/expression_items/threshold.test.tsx b/x-pack/plugins/triggers_actions_ui/public/common/expression_items/threshold.test.tsx index 7e8eb5d6feee02..02ee05c125db6a 100644 --- a/x-pack/plugins/triggers_actions_ui/public/common/expression_items/threshold.test.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/common/expression_items/threshold.test.tsx @@ -49,7 +49,7 @@ describe('threshold expression', () => { "value": "between", }, Object { - "text": "Not between", + "text": "Is not between", "value": "notBetween", }, ] diff --git a/x-pack/plugins/triggers_actions_ui/public/common/expression_items/threshold.tsx b/x-pack/plugins/triggers_actions_ui/public/common/expression_items/threshold.tsx index fdbd0f6bc70220..5e24ced438a681 100644 --- a/x-pack/plugins/triggers_actions_ui/public/common/expression_items/threshold.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/common/expression_items/threshold.tsx @@ -18,8 +18,8 @@ import { EuiText, } from '@elastic/eui'; import { isNil } from 'lodash'; +import { Comparator } from '@kbn/alerting-comparators'; import { builtInComparators } from '../constants'; -import { Comparator } from '../types'; import { IErrorObject } from '../../types'; import { ClosablePopoverTitle } from './components'; diff --git a/x-pack/plugins/triggers_actions_ui/public/common/index.ts b/x-pack/plugins/triggers_actions_ui/public/common/index.ts index 52b39919227ead..de048e4b2e57c1 100644 --- a/x-pack/plugins/triggers_actions_ui/public/common/index.ts +++ b/x-pack/plugins/triggers_actions_ui/public/common/index.ts @@ -15,18 +15,12 @@ export { OfExpression, ThresholdExpression, } from './expression_items'; -export { - COMPARATORS, - builtInComparators, - builtInAggregationTypes, - builtInGroupByTypes, -} from './constants'; +export { builtInComparators, builtInAggregationTypes, builtInGroupByTypes } from './constants'; export { connectorDeprecatedMessage, deprecatedMessage } from './connectors_selection'; export type { IOption } from './index_controls'; export { getFields, getIndexOptions, firstFieldOption } from './index_controls'; export { getTimeFieldOptions, getTimeOptions, useKibana } from './lib'; export type { - Comparator, AggregationType, GroupByType, RuleStatus, diff --git a/x-pack/plugins/triggers_actions_ui/public/common/types.ts b/x-pack/plugins/triggers_actions_ui/public/common/types.ts index 2bf6d601ff4764..c22332a9ad028a 100644 --- a/x-pack/plugins/triggers_actions_ui/public/common/types.ts +++ b/x-pack/plugins/triggers_actions_ui/public/common/types.ts @@ -7,12 +7,6 @@ import { KBN_FIELD_TYPES } from '@kbn/field-types'; -export interface Comparator { - text: string; - value: string; - requiredValues: number; -} - export type ValidNormalizedTypes = `${Exclude< KBN_FIELD_TYPES, | KBN_FIELD_TYPES.UNKNOWN diff --git a/x-pack/plugins/triggers_actions_ui/public/index.ts b/x-pack/plugins/triggers_actions_ui/public/index.ts index f4276eb8a891b8..3f00b4ffb8ae5c 100644 --- a/x-pack/plugins/triggers_actions_ui/public/index.ts +++ b/x-pack/plugins/triggers_actions_ui/public/index.ts @@ -102,7 +102,7 @@ export function plugin(context: PluginInitializerContext) { } export { useKibana } from './common'; -export type { AggregationType, Comparator, ValidNormalizedTypes } from './common'; +export type { AggregationType, ValidNormalizedTypes } from './common'; export { WhenExpression, @@ -119,7 +119,6 @@ export { getTimeFieldOptions, getTimeOptions, GroupByExpression, - COMPARATORS, connectorDeprecatedMessage, deprecatedMessage, } from './common'; diff --git a/x-pack/plugins/triggers_actions_ui/tsconfig.json b/x-pack/plugins/triggers_actions_ui/tsconfig.json index 12cdcbdfe14427..bb6ad80dd27c90 100644 --- a/x-pack/plugins/triggers_actions_ui/tsconfig.json +++ b/x-pack/plugins/triggers_actions_ui/tsconfig.json @@ -66,7 +66,8 @@ "@kbn/react-kibana-mount", "@kbn/react-kibana-context-theme", "@kbn/controls-plugin", - "@kbn/search-types" + "@kbn/search-types", + "@kbn/alerting-comparators" ], "exclude": ["target/**/*"] } diff --git a/x-pack/plugins/upgrade_assistant/server/routes/reindex_indices/batch_reindex_indices.ts b/x-pack/plugins/upgrade_assistant/server/routes/reindex_indices/batch_reindex_indices.ts index c9a8eedceffe37..391306aa187235 100644 --- a/x-pack/plugins/upgrade_assistant/server/routes/reindex_indices/batch_reindex_indices.ts +++ b/x-pack/plugins/upgrade_assistant/server/routes/reindex_indices/batch_reindex_indices.ts @@ -36,6 +36,10 @@ export function registerBatchReindexIndicesRoutes( router.get( { path: `${BASE_PATH}/batch/queue`, + options: { + access: 'public', + description: `Get the batch reindex queue`, + }, validate: {}, }, versionCheckHandlerWrapper(async ({ core }, request, response) => { @@ -71,6 +75,10 @@ export function registerBatchReindexIndicesRoutes( router.post( { path: `${BASE_PATH}/batch`, + options: { + access: 'public', + description: `Batch start or resume reindex`, + }, validate: { body: schema.object({ indexNames: schema.arrayOf(schema.string()), diff --git a/x-pack/plugins/upgrade_assistant/server/routes/reindex_indices/reindex_indices.ts b/x-pack/plugins/upgrade_assistant/server/routes/reindex_indices/reindex_indices.ts index 0ffa9335c4de70..9b2b046169b775 100644 --- a/x-pack/plugins/upgrade_assistant/server/routes/reindex_indices/reindex_indices.ts +++ b/x-pack/plugins/upgrade_assistant/server/routes/reindex_indices/reindex_indices.ts @@ -34,6 +34,10 @@ export function registerReindexIndicesRoutes( router.post( { path: `${BASE_PATH}/{indexName}`, + options: { + access: 'public', + description: `Start or resume reindex`, + }, validate: { params: schema.object({ indexName: schema.string(), @@ -77,6 +81,10 @@ export function registerReindexIndicesRoutes( router.get( { path: `${BASE_PATH}/{indexName}`, + options: { + access: 'public', + description: `Get reindex status`, + }, validate: { params: schema.object({ indexName: schema.string(), @@ -134,6 +142,10 @@ export function registerReindexIndicesRoutes( router.post( { path: `${BASE_PATH}/{indexName}/cancel`, + options: { + access: 'public', + description: `Cancel reindex`, + }, validate: { params: schema.object({ indexName: schema.string(), diff --git a/x-pack/plugins/upgrade_assistant/server/routes/status.ts b/x-pack/plugins/upgrade_assistant/server/routes/status.ts index 3f6f90d8823f0c..7a44981776319f 100644 --- a/x-pack/plugins/upgrade_assistant/server/routes/status.ts +++ b/x-pack/plugins/upgrade_assistant/server/routes/status.ts @@ -24,6 +24,10 @@ export function registerUpgradeStatusRoute({ router.get( { path: `${API_BASE_PATH}/status`, + options: { + access: 'public', + description: `Get upgrade readiness status`, + }, validate: false, }, versionCheckHandlerWrapper(async ({ core }, request, response) => { diff --git a/x-pack/test/accessibility/apps/group3/ml_embeddables_in_dashboard.ts b/x-pack/test/accessibility/apps/group3/ml_embeddables_in_dashboard.ts index 39a10547c57b97..69a95822be3af5 100644 --- a/x-pack/test/accessibility/apps/group3/ml_embeddables_in_dashboard.ts +++ b/x-pack/test/accessibility/apps/group3/ml_embeddables_in_dashboard.ts @@ -103,12 +103,15 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { }); it('can select jobs', async () => { - await ml.dashboardJobSelectionTable.setRowCheckboxState(testData.jobConfig.job_id, true); - await ml.dashboardJobSelectionTable.applyJobSelection(); + await ml.alerting.selectJobs([testData.jobConfig.job_id]); + await ml.alerting.assertJobSelection([testData.jobConfig.job_id]); + }); + + it('populates with default default info', async () => { await ml.dashboardEmbeddables.assertAnomalyChartsEmbeddableInitializerExists(); + await ml.dashboardEmbeddables.assertSelectMaxSeriesToPlotValue(6); await a11y.testAppSnapshot(); }); - it('create new anomaly charts panel', async () => { await ml.dashboardEmbeddables.clickInitializerConfirmButtonEnabled(); await ml.dashboardEmbeddables.assertDashboardPanelExists(testData.panelTitle); diff --git a/x-pack/test/alerting_api_integration/observability/custom_threshold_rule/avg_pct_fired.ts b/x-pack/test/alerting_api_integration/observability/custom_threshold_rule/avg_pct_fired.ts index 65a14577f06ea8..dc6197320ecf03 100644 --- a/x-pack/test/alerting_api_integration/observability/custom_threshold_rule/avg_pct_fired.ts +++ b/x-pack/test/alerting_api_integration/observability/custom_threshold_rule/avg_pct_fired.ts @@ -7,14 +7,12 @@ import { omit } from 'lodash'; import { cleanup, generate, Dataset, PartialConfig } from '@kbn/data-forge'; -import { - Aggregators, - Comparator, -} from '@kbn/observability-plugin/common/custom_threshold_rule/types'; +import { Aggregators } from '@kbn/observability-plugin/common/custom_threshold_rule/types'; import { FIRED_ACTIONS_ID } from '@kbn/observability-plugin/server/lib/rules/custom_threshold/constants'; import expect from '@kbn/expect'; import { OBSERVABILITY_THRESHOLD_RULE_TYPE_ID } from '@kbn/rule-data-utils'; import { parseSearchParams } from '@kbn/share-plugin/common/url_service'; +import { COMPARATORS } from '@kbn/alerting-comparators'; import { createIndexConnector, createRule } from '../helpers/alerting_api_helper'; import { waitForAlertInIndex, @@ -122,7 +120,7 @@ export default function ({ getService }: FtrProviderContext) { params: { criteria: [ { - comparator: Comparator.GT, + comparator: COMPARATORS.GREATER_THAN, threshold: [0.5], timeSize: 5, timeUnit: 'm', diff --git a/x-pack/test/alerting_api_integration/observability/custom_threshold_rule/avg_pct_no_data.ts b/x-pack/test/alerting_api_integration/observability/custom_threshold_rule/avg_pct_no_data.ts index 8892fee26a28e3..f552eca84b8a4d 100644 --- a/x-pack/test/alerting_api_integration/observability/custom_threshold_rule/avg_pct_no_data.ts +++ b/x-pack/test/alerting_api_integration/observability/custom_threshold_rule/avg_pct_no_data.ts @@ -6,14 +6,12 @@ */ import { omit } from 'lodash'; -import { - Aggregators, - Comparator, -} from '@kbn/observability-plugin/common/custom_threshold_rule/types'; +import { Aggregators } from '@kbn/observability-plugin/common/custom_threshold_rule/types'; import { NO_DATA_ACTIONS_ID } from '@kbn/observability-plugin/server/lib/rules/custom_threshold/constants'; import { parseSearchParams } from '@kbn/share-plugin/common/url_service'; import expect from '@kbn/expect'; import { OBSERVABILITY_THRESHOLD_RULE_TYPE_ID } from '@kbn/rule-data-utils'; +import { COMPARATORS } from '@kbn/alerting-comparators'; import { createIndexConnector, createRule } from '../helpers/alerting_api_helper'; import { createDataView, deleteDataView } from '../helpers/data_view'; @@ -92,7 +90,7 @@ export default function ({ getService }: FtrProviderContext) { params: { criteria: [ { - comparator: Comparator.GT, + comparator: COMPARATORS.GREATER_THAN, threshold: [0.5], timeSize: 5, timeUnit: 'm', diff --git a/x-pack/test/alerting_api_integration/observability/custom_threshold_rule/avg_us_fired.ts b/x-pack/test/alerting_api_integration/observability/custom_threshold_rule/avg_us_fired.ts index fac69cd18a9bb5..391acf747eb252 100644 --- a/x-pack/test/alerting_api_integration/observability/custom_threshold_rule/avg_us_fired.ts +++ b/x-pack/test/alerting_api_integration/observability/custom_threshold_rule/avg_us_fired.ts @@ -8,13 +8,11 @@ import moment from 'moment'; import { ApmSynthtraceEsClient } from '@kbn/apm-synthtrace'; import { format } from 'url'; -import { - Aggregators, - Comparator, -} from '@kbn/observability-plugin/common/custom_threshold_rule/types'; +import { Aggregators } from '@kbn/observability-plugin/common/custom_threshold_rule/types'; import { FIRED_ACTIONS_ID } from '@kbn/observability-plugin/server/lib/rules/custom_threshold/constants'; import expect from '@kbn/expect'; import { OBSERVABILITY_THRESHOLD_RULE_TYPE_ID } from '@kbn/rule-data-utils'; +import { COMPARATORS } from '@kbn/alerting-comparators'; import { FtrProviderContext } from '../../common/ftr_provider_context'; import { createIndexConnector, createRule } from '../helpers/alerting_api_helper'; import { createDataView, deleteDataView } from '../helpers/data_view'; @@ -104,7 +102,7 @@ export default function ({ getService }: FtrProviderContext) { criteria: [ { aggType: 'custom', - comparator: Comparator.GT, + comparator: COMPARATORS.GREATER_THAN, threshold: [7500000], timeSize: 5, timeUnit: 'm', diff --git a/x-pack/test/alerting_api_integration/observability/custom_threshold_rule/custom_eq_avg_bytes_fired.ts b/x-pack/test/alerting_api_integration/observability/custom_threshold_rule/custom_eq_avg_bytes_fired.ts index b4c39683fade99..56370d31a38f76 100644 --- a/x-pack/test/alerting_api_integration/observability/custom_threshold_rule/custom_eq_avg_bytes_fired.ts +++ b/x-pack/test/alerting_api_integration/observability/custom_threshold_rule/custom_eq_avg_bytes_fired.ts @@ -6,10 +6,8 @@ */ import { cleanup, generate, Dataset, PartialConfig } from '@kbn/data-forge'; -import { - Aggregators, - Comparator, -} from '@kbn/observability-plugin/common/custom_threshold_rule/types'; +import { Aggregators } from '@kbn/observability-plugin/common/custom_threshold_rule/types'; +import { COMPARATORS } from '@kbn/alerting-comparators'; import { FIRED_ACTIONS_ID } from '@kbn/observability-plugin/server/lib/rules/custom_threshold/constants'; import expect from '@kbn/expect'; import { OBSERVABILITY_THRESHOLD_RULE_TYPE_ID } from '@kbn/rule-data-utils'; @@ -120,7 +118,7 @@ export default function ({ getService }: FtrProviderContext) { params: { criteria: [ { - comparator: Comparator.GT, + comparator: COMPARATORS.GREATER_THAN, threshold: [0.9], timeSize: 1, timeUnit: 'm', @@ -220,7 +218,7 @@ export default function ({ getService }: FtrProviderContext) { .eql({ criteria: [ { - comparator: Comparator.GT, + comparator: COMPARATORS.GREATER_THAN, threshold: [0.9], timeSize: 1, timeUnit: 'm', diff --git a/x-pack/test/alerting_api_integration/observability/custom_threshold_rule/documents_count_fired.ts b/x-pack/test/alerting_api_integration/observability/custom_threshold_rule/documents_count_fired.ts index 1c20df4f014f55..5c56dbdf9e8082 100644 --- a/x-pack/test/alerting_api_integration/observability/custom_threshold_rule/documents_count_fired.ts +++ b/x-pack/test/alerting_api_integration/observability/custom_threshold_rule/documents_count_fired.ts @@ -8,13 +8,11 @@ import { omit } from 'lodash'; import expect from '@kbn/expect'; import { cleanup, generate, Dataset, PartialConfig } from '@kbn/data-forge'; -import { - Aggregators, - Comparator, -} from '@kbn/observability-plugin/common/custom_threshold_rule/types'; +import { Aggregators } from '@kbn/observability-plugin/common/custom_threshold_rule/types'; import { FIRED_ACTIONS_ID } from '@kbn/observability-plugin/server/lib/rules/custom_threshold/constants'; import { OBSERVABILITY_THRESHOLD_RULE_TYPE_ID } from '@kbn/rule-data-utils'; import { parseSearchParams } from '@kbn/share-plugin/common/url_service'; +import { COMPARATORS } from '@kbn/alerting-comparators'; import { createIndexConnector, createRule } from '../helpers/alerting_api_helper'; import { createDataView, deleteDataView } from '../helpers/data_view'; import { @@ -124,7 +122,7 @@ export default function ({ getService }: FtrProviderContext) { params: { criteria: [ { - comparator: Comparator.OUTSIDE_RANGE, + comparator: COMPARATORS.NOT_BETWEEN, threshold: [1, 2], timeSize: 1, timeUnit: 'm', @@ -223,7 +221,7 @@ export default function ({ getService }: FtrProviderContext) { .eql({ criteria: [ { - comparator: Comparator.OUTSIDE_RANGE, + comparator: COMPARATORS.NOT_BETWEEN, threshold: [1, 2], timeSize: 1, timeUnit: 'm', diff --git a/x-pack/test/alerting_api_integration/observability/custom_threshold_rule/group_by_fired.ts b/x-pack/test/alerting_api_integration/observability/custom_threshold_rule/group_by_fired.ts index b5aa7c4d03904b..fcb5fac564df45 100644 --- a/x-pack/test/alerting_api_integration/observability/custom_threshold_rule/group_by_fired.ts +++ b/x-pack/test/alerting_api_integration/observability/custom_threshold_rule/group_by_fired.ts @@ -6,13 +6,11 @@ */ import { cleanup, generate, Dataset, PartialConfig } from '@kbn/data-forge'; -import { - Aggregators, - Comparator, -} from '@kbn/observability-plugin/common/custom_threshold_rule/types'; +import { Aggregators } from '@kbn/observability-plugin/common/custom_threshold_rule/types'; import { FIRED_ACTIONS_ID } from '@kbn/observability-plugin/server/lib/rules/custom_threshold/constants'; import expect from '@kbn/expect'; import { OBSERVABILITY_THRESHOLD_RULE_TYPE_ID } from '@kbn/rule-data-utils'; +import { COMPARATORS } from '@kbn/alerting-comparators'; import { createIndexConnector, createRule } from '../helpers/alerting_api_helper'; import { createDataView, deleteDataView } from '../helpers/data_view'; import { @@ -120,7 +118,7 @@ export default function ({ getService }: FtrProviderContext) { params: { criteria: [ { - comparator: Comparator.GT_OR_EQ, + comparator: COMPARATORS.GREATER_THAN_OR_EQUALS, threshold: [0.2], timeSize: 1, timeUnit: 'm', diff --git a/x-pack/test/alerting_api_integration/observability/custom_threshold_rule/p99_pct_fired.ts b/x-pack/test/alerting_api_integration/observability/custom_threshold_rule/p99_pct_fired.ts index 46a6874d34588a..b76c09c12642de 100644 --- a/x-pack/test/alerting_api_integration/observability/custom_threshold_rule/p99_pct_fired.ts +++ b/x-pack/test/alerting_api_integration/observability/custom_threshold_rule/p99_pct_fired.ts @@ -7,10 +7,8 @@ import { omit } from 'lodash'; import { cleanup, generate, Dataset, PartialConfig } from '@kbn/data-forge'; -import { - Aggregators, - Comparator, -} from '@kbn/observability-plugin/common/custom_threshold_rule/types'; +import { Aggregators } from '@kbn/observability-plugin/common/custom_threshold_rule/types'; +import { COMPARATORS } from '@kbn/alerting-comparators'; import { FIRED_ACTIONS_ID } from '@kbn/observability-plugin/server/lib/rules/custom_threshold/constants'; import expect from '@kbn/expect'; import { OBSERVABILITY_THRESHOLD_RULE_TYPE_ID } from '@kbn/rule-data-utils'; @@ -119,7 +117,7 @@ export default function ({ getService }: FtrProviderContext) { params: { criteria: [ { - comparator: Comparator.GT, + comparator: COMPARATORS.GREATER_THAN, threshold: [0.5], timeSize: 5, timeUnit: 'm', diff --git a/x-pack/test/alerting_api_integration/observability/custom_threshold_rule/rate_bytes_fired.ts b/x-pack/test/alerting_api_integration/observability/custom_threshold_rule/rate_bytes_fired.ts index da613a8a4fb7cd..d56310dd9b0d8b 100644 --- a/x-pack/test/alerting_api_integration/observability/custom_threshold_rule/rate_bytes_fired.ts +++ b/x-pack/test/alerting_api_integration/observability/custom_threshold_rule/rate_bytes_fired.ts @@ -6,10 +6,8 @@ */ import { cleanup, generate, Dataset, PartialConfig } from '@kbn/data-forge'; -import { - Aggregators, - Comparator, -} from '@kbn/observability-plugin/common/custom_threshold_rule/types'; +import { Aggregators } from '@kbn/observability-plugin/common/custom_threshold_rule/types'; +import { COMPARATORS } from '@kbn/alerting-comparators'; import { FIRED_ACTIONS_ID } from '@kbn/observability-plugin/server/lib/rules/custom_threshold/constants'; import expect from '@kbn/expect'; import { OBSERVABILITY_THRESHOLD_RULE_TYPE_ID } from '@kbn/rule-data-utils'; @@ -118,7 +116,7 @@ export default function ({ getService }: FtrProviderContext) { params: { criteria: [ { - comparator: Comparator.GT_OR_EQ, + comparator: COMPARATORS.GREATER_THAN_OR_EQUALS, threshold: [50_000], timeSize: 1, timeUnit: 'm', diff --git a/x-pack/test/alerting_api_integration/observability/custom_threshold_rule_data_view.ts b/x-pack/test/alerting_api_integration/observability/custom_threshold_rule_data_view.ts index 9ecccffbd596e2..0565d759df20d7 100644 --- a/x-pack/test/alerting_api_integration/observability/custom_threshold_rule_data_view.ts +++ b/x-pack/test/alerting_api_integration/observability/custom_threshold_rule_data_view.ts @@ -6,12 +6,10 @@ */ import expect from '@kbn/expect'; -import { - Aggregators, - Comparator, -} from '@kbn/observability-plugin/common/custom_threshold_rule/types'; +import { Aggregators } from '@kbn/observability-plugin/common/custom_threshold_rule/types'; import { OBSERVABILITY_THRESHOLD_RULE_TYPE_ID } from '@kbn/rule-data-utils'; +import { COMPARATORS } from '@kbn/alerting-comparators'; import { FtrProviderContext } from '../common/ftr_provider_context'; import { getUrlPrefix, ObjectRemover } from '../common/lib'; import { createRule } from './helpers/alerting_api_helper'; @@ -78,7 +76,7 @@ export default function ({ getService }: FtrProviderContext) { params: { criteria: [ { - comparator: Comparator.GT, + comparator: COMPARATORS.GREATER_THAN, threshold: [7500000], timeSize: 5, timeUnit: 'm', diff --git a/x-pack/test/alerting_api_integration/observability/metric_threshold_rule.ts b/x-pack/test/alerting_api_integration/observability/metric_threshold_rule.ts index 808df288b094e8..bc36566f91f52d 100644 --- a/x-pack/test/alerting_api_integration/observability/metric_threshold_rule.ts +++ b/x-pack/test/alerting_api_integration/observability/metric_threshold_rule.ts @@ -10,10 +10,10 @@ import expect from '@kbn/expect'; import { cleanup, generate, Dataset, PartialConfig } from '@kbn/data-forge'; import { Aggregators, - Comparator, InfraRuleType, MetricThresholdParams, } from '@kbn/infra-plugin/common/alerting/metrics'; +import { COMPARATORS } from '@kbn/alerting-comparators'; import { waitForDocumentInIndex, waitForAlertInIndex, @@ -86,7 +86,7 @@ export default function ({ getService }: FtrProviderContext) { criteria: [ { aggType: Aggregators.AVERAGE, - comparator: Comparator.GT, + comparator: COMPARATORS.GREATER_THAN, threshold: [0.5], timeSize: 5, timeUnit: 'm', @@ -227,7 +227,7 @@ export default function ({ getService }: FtrProviderContext) { `https://localhost:5601/app/observability/alerts?_a=(kuery:%27kibana.alert.uuid:%20%22${alertId}%22%27%2CrangeFrom:%27${rangeFrom}%27%2CrangeTo:now%2Cstatus:all)` ); expect(resp.hits.hits[0]._source?.reason).eql( - `system.cpu.user.pct is 90% in the last 5 mins. Alert when > 50%.` + `system.cpu.user.pct is 90% in the last 5 mins. Alert when above 50%.` ); }); }); diff --git a/x-pack/test/alerting_api_integration/security_and_spaces/group1/tests/alerting/backfill/task_runner.ts b/x-pack/test/alerting_api_integration/security_and_spaces/group1/tests/alerting/backfill/task_runner.ts index aa81a4713a8af6..f513866cd5ee5a 100644 --- a/x-pack/test/alerting_api_integration/security_and_spaces/group1/tests/alerting/backfill/task_runner.ts +++ b/x-pack/test/alerting_api_integration/security_and_spaces/group1/tests/alerting/backfill/task_runner.ts @@ -235,6 +235,7 @@ export default function createBackfillTaskRunnerTests({ getService }: FtrProvide namespace: 'space1', }, { + rel: 'primary', type: RULE_SAVED_OBJECT_TYPE, id: ruleId, type_id: 'siem.queryRule', @@ -467,6 +468,7 @@ export default function createBackfillTaskRunnerTests({ getService }: FtrProvide namespace: 'space1', }, { + rel: 'primary', type: RULE_SAVED_OBJECT_TYPE, id: ruleId, type_id: 'test.patternFiringAutoRecoverFalse', @@ -499,6 +501,7 @@ export default function createBackfillTaskRunnerTests({ getService }: FtrProvide namespace: 'space1', }, { + rel: 'primary', type: RULE_SAVED_OBJECT_TYPE, id: ruleId, type_id: 'test.patternFiringAutoRecoverFalse', @@ -604,6 +607,7 @@ export default function createBackfillTaskRunnerTests({ getService }: FtrProvide namespace: 'space1', }, { + rel: 'primary', type: RULE_SAVED_OBJECT_TYPE, id: ruleId, type_id: 'test.patternFiringAutoRecoverFalse', diff --git a/x-pack/test/api_integration/apis/metrics_ui/inventory_threshold_alert.ts b/x-pack/test/api_integration/apis/metrics_ui/inventory_threshold_alert.ts index f76dd0e63fd87b..4a60158c77e800 100644 --- a/x-pack/test/api_integration/apis/metrics_ui/inventory_threshold_alert.ts +++ b/x-pack/test/api_integration/apis/metrics_ui/inventory_threshold_alert.ts @@ -6,10 +6,11 @@ */ import expect from '@kbn/expect'; -import { Comparator, InventoryMetricConditions } from '@kbn/infra-plugin/common/alerting/metrics'; +import { InventoryMetricConditions } from '@kbn/infra-plugin/common/alerting/metrics'; import { InventoryItemType, SnapshotMetricType } from '@kbn/metrics-data-access-plugin/common'; import { evaluateCondition } from '@kbn/infra-plugin/server/lib/alerting/inventory_metric_threshold/evaluate_condition'; import { InfraSource } from '@kbn/infra-plugin/server/lib/sources'; +import { COMPARATORS } from '@kbn/alerting-comparators'; import { FtrProviderContext } from '../../ftr_provider_context'; import { DATES } from './constants'; import { createFakeLogger } from './create_fake_logger'; @@ -26,7 +27,7 @@ export default function ({ getService }: FtrProviderContext) { timeUnit: 'm', sourceId: 'default', threshold: [100], - comparator: Comparator.GT, + comparator: COMPARATORS.GREATER_THAN, }; const source: InfraSource = { @@ -207,7 +208,7 @@ export default function ({ getService }: FtrProviderContext) { ...baseCondition, metric: 'rx', threshold: [107374182400], - comparator: Comparator.LT, + comparator: COMPARATORS.LESS_THAN, }, esClient, }); diff --git a/x-pack/test/api_integration/apis/metrics_ui/metric_threshold_alert.ts b/x-pack/test/api_integration/apis/metrics_ui/metric_threshold_alert.ts index 18d46a45ded9ea..96aa8f91a2c9c2 100644 --- a/x-pack/test/api_integration/apis/metrics_ui/metric_threshold_alert.ts +++ b/x-pack/test/api_integration/apis/metrics_ui/metric_threshold_alert.ts @@ -9,7 +9,6 @@ import expect from '@kbn/expect'; import moment from 'moment'; import { Aggregators, - Comparator, CountMetricExpressionParams, CustomMetricExpressionParams, NonCountMetricExpressionParams, @@ -19,6 +18,7 @@ import { EvaluatedRuleParams, evaluateRule, } from '@kbn/infra-plugin/server/lib/alerting/metric_threshold/lib/evaluate_rule'; +import { COMPARATORS } from '@kbn/alerting-comparators'; import { FtrProviderContext } from '../../ftr_provider_context'; import { DATES } from './constants'; import { createFakeLogger } from './create_fake_logger'; @@ -38,7 +38,7 @@ export default function ({ getService }: FtrProviderContext) { timeSize: 5, timeUnit: 'm', threshold: [1], - comparator: Comparator.GT_OR_EQ, + comparator: COMPARATORS.GREATER_THAN_OR_EQUALS, aggType: Aggregators.SUM, metric: 'value', } as NonCountMetricExpressionParams, @@ -89,7 +89,7 @@ export default function ({ getService }: FtrProviderContext) { timeSize: 5, timeUnit: 'm', threshold: [10000], - comparator: Comparator.GT_OR_EQ, + comparator: COMPARATORS.GREATER_THAN_OR_EQUALS, aggType: Aggregators.COUNT, } as CountMetricExpressionParams, ], @@ -144,7 +144,7 @@ export default function ({ getService }: FtrProviderContext) { timeSize: 5, timeUnit: 'm', threshold: [1], - comparator: Comparator.GT_OR_EQ, + comparator: COMPARATORS.GREATER_THAN_OR_EQUALS, aggType: Aggregators.CUSTOM, customMetrics: [ { name: 'A', aggType: 'count', filter: 'event.dataset: "apache2.error"' }, @@ -214,7 +214,7 @@ export default function ({ getService }: FtrProviderContext) { timeSize: 5, timeUnit: 'm', threshold: [20000], - comparator: Comparator.GT_OR_EQ, + comparator: COMPARATORS.GREATER_THAN_OR_EQUALS, aggType: Aggregators.COUNT, } as CountMetricExpressionParams, ], @@ -270,7 +270,7 @@ export default function ({ getService }: FtrProviderContext) { timeSize: 5, timeUnit: 'm', threshold: [20000], - comparator: Comparator.GT_OR_EQ, + comparator: COMPARATORS.GREATER_THAN_OR_EQUALS, aggType: Aggregators.COUNT, } as CountMetricExpressionParams, ], @@ -349,7 +349,7 @@ export default function ({ getService }: FtrProviderContext) { timeSize: 5, timeUnit: 'm', threshold: [1], - comparator: Comparator.GT_OR_EQ, + comparator: COMPARATORS.GREATER_THAN_OR_EQUALS, aggType: Aggregators.COUNT, } as CountMetricExpressionParams, ], @@ -432,7 +432,7 @@ export default function ({ getService }: FtrProviderContext) { timeSize: 5, timeUnit: 'm', threshold: [2], - comparator: Comparator.GT_OR_EQ, + comparator: COMPARATORS.GREATER_THAN_OR_EQUALS, aggType: Aggregators.COUNT, } as CountMetricExpressionParams, ], @@ -496,7 +496,7 @@ export default function ({ getService }: FtrProviderContext) { timeSize: 5, timeUnit: 'm', threshold: [0], - comparator: Comparator.LT_OR_EQ, + comparator: COMPARATORS.LESS_THAN_OR_EQUALS, aggType: Aggregators.COUNT, } as CountMetricExpressionParams, ], @@ -591,7 +591,7 @@ export default function ({ getService }: FtrProviderContext) { timeSize: 5, timeUnit: 'm', threshold: [0], - comparator: Comparator.LT_OR_EQ, + comparator: COMPARATORS.LESS_THAN_OR_EQUALS, aggType: Aggregators.COUNT, } as CountMetricExpressionParams, ], @@ -645,7 +645,7 @@ export default function ({ getService }: FtrProviderContext) { timeSize: 5, timeUnit: 'm', threshold: [0], - comparator: Comparator.LT_OR_EQ, + comparator: COMPARATORS.LESS_THAN_OR_EQUALS, aggType: Aggregators.COUNT, } as CountMetricExpressionParams, ], @@ -752,7 +752,7 @@ export default function ({ getService }: FtrProviderContext) { timeSize: 5, timeUnit: 'm', threshold: [1], - comparator: Comparator.GT_OR_EQ, + comparator: COMPARATORS.GREATER_THAN_OR_EQUALS, aggType: Aggregators.COUNT, } as CountMetricExpressionParams, ], @@ -803,7 +803,7 @@ export default function ({ getService }: FtrProviderContext) { timeSize: 5, timeUnit: 'm', threshold: [0], - comparator: Comparator.LT_OR_EQ, + comparator: COMPARATORS.LESS_THAN_OR_EQUALS, aggType: Aggregators.COUNT, } as CountMetricExpressionParams, ], @@ -897,7 +897,7 @@ export default function ({ getService }: FtrProviderContext) { timeSize: 5, timeUnit: 'm', threshold: [1], - comparator: Comparator.GT_OR_EQ, + comparator: COMPARATORS.GREATER_THAN_OR_EQUALS, aggType: Aggregators.COUNT, } as CountMetricExpressionParams, ], @@ -1083,7 +1083,7 @@ export default function ({ getService }: FtrProviderContext) { timeSize: 5, timeUnit: 'm', threshold: [100], - comparator: Comparator.GT, + comparator: COMPARATORS.GREATER_THAN, aggType: Aggregators.SUM, metric: 'value', } as NonCountMetricExpressionParams, @@ -1183,7 +1183,7 @@ export default function ({ getService }: FtrProviderContext) { timeSize: 1, timeUnit: 'm', threshold: [107374182400], - comparator: Comparator.LT_OR_EQ, + comparator: COMPARATORS.LESS_THAN_OR_EQUALS, aggType: Aggregators.RATE, metric: 'value', } as NonCountMetricExpressionParams, @@ -1236,7 +1236,7 @@ export default function ({ getService }: FtrProviderContext) { timeSize: 1, timeUnit: 'm', threshold: [0.5], - comparator: Comparator.GT_OR_EQ, + comparator: COMPARATORS.GREATER_THAN_OR_EQUALS, aggType: Aggregators.RATE, metric: 'value', } as NonCountMetricExpressionParams, @@ -1291,9 +1291,9 @@ export default function ({ getService }: FtrProviderContext) { timeSize: 1, timeUnit: 'm', threshold: [1], - comparator: Comparator.GT_OR_EQ, + comparator: COMPARATORS.GREATER_THAN_OR_EQUALS, warningThreshold: [0.5], - warningComparator: Comparator.GT_OR_EQ, + warningComparator: COMPARATORS.GREATER_THAN_OR_EQUALS, aggType: Aggregators.RATE, metric: 'value', } as NonCountMetricExpressionParams, diff --git a/x-pack/test/api_integration/apis/metrics_ui/metrics_alerting.ts b/x-pack/test/api_integration/apis/metrics_ui/metrics_alerting.ts index 2e7a900e0fe877..a091a912f8cb5d 100644 --- a/x-pack/test/api_integration/apis/metrics_ui/metrics_alerting.ts +++ b/x-pack/test/api_integration/apis/metrics_ui/metrics_alerting.ts @@ -7,8 +7,9 @@ import expect from '@kbn/expect'; import moment from 'moment'; -import { Comparator, MetricExpressionParams } from '@kbn/infra-plugin/common/alerting/metrics'; +import { MetricExpressionParams } from '@kbn/infra-plugin/common/alerting/metrics'; import { getElasticsearchMetricQuery } from '@kbn/infra-plugin/server/lib/alerting/metric_threshold/lib/metric_query'; +import { COMPARATORS } from '@kbn/alerting-comparators'; import { FtrProviderContext } from '../../ftr_provider_context'; export default function ({ getService }: FtrProviderContext) { @@ -19,7 +20,7 @@ export default function ({ getService }: FtrProviderContext) { aggType, timeUnit: 'm', threshold: [0], - comparator: Comparator.GT_OR_EQ, + comparator: COMPARATORS.GREATER_THAN_OR_EQUALS, timeSize: 5, ...(aggType !== 'count' ? { metric: 'test.metric' } : {}), } as MetricExpressionParams); diff --git a/x-pack/test/api_integration/apis/slos/create_slo.ts b/x-pack/test/api_integration/apis/slos/create_slo.ts index c61de233fb4236..72735e7f668bb7 100644 --- a/x-pack/test/api_integration/apis/slos/create_slo.ts +++ b/x-pack/test/api_integration/apis/slos/create_slo.ts @@ -90,6 +90,7 @@ export default function ({ getService }: FtrProviderContext) { settings: { frequency: '1m', syncDelay: '1m', + preventInitialBackfill: false, }, tags: ['test'], timeWindow: { diff --git a/x-pack/test/api_integration/apis/slos/update_slo.ts b/x-pack/test/api_integration/apis/slos/update_slo.ts index 4d920895ffcc67..ebddd16c6508fb 100644 --- a/x-pack/test/api_integration/apis/slos/update_slo.ts +++ b/x-pack/test/api_integration/apis/slos/update_slo.ts @@ -94,6 +94,7 @@ export default function ({ getService }: FtrProviderContext) { settings: { frequency: '1m', syncDelay: '1m', + preventInitialBackfill: false, }, tags: ['test'], timeWindow: { diff --git a/x-pack/test/common/utils/security_solution/detections_response/rules/index.ts b/x-pack/test/common/utils/security_solution/detections_response/rules/index.ts index 41559d7c01c05c..0f757b0a8d6a7c 100644 --- a/x-pack/test/common/utils/security_solution/detections_response/rules/index.ts +++ b/x-pack/test/common/utils/security_solution/detections_response/rules/index.ts @@ -10,3 +10,4 @@ export * from './delete_all_rules'; export * from './delete_rule'; export * from './get_rule_for_alert_testing'; export * from './wait_for_rule_status'; +export * from './manual_run'; diff --git a/x-pack/test/common/utils/security_solution/detections_response/rules/manual_run.ts b/x-pack/test/common/utils/security_solution/detections_response/rules/manual_run.ts new file mode 100644 index 00000000000000..8e1f47440009bb --- /dev/null +++ b/x-pack/test/common/utils/security_solution/detections_response/rules/manual_run.ts @@ -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 type SuperTest from 'supertest'; +import { INTERNAL_BASE_ALERTING_API_PATH } from '@kbn/alerting-plugin/common'; +import { ScheduleBackfillResponse } from '@kbn/alerting-plugin/common/routes/backfill/apis/schedule'; +import { routeWithNamespace } from '../route_with_namespace'; + +const BACKFILL_RULE_URL = `${INTERNAL_BASE_ALERTING_API_PATH}/rules/backfill`; +const BACKFILL_RULE_URL_SCHEDULE = `${BACKFILL_RULE_URL}/_schedule`; + +export const manualRuleRun = async ({ + supertest, + ruleId, + start, + end, + namespace, +}: { + ruleId: string; + start: string; + end: string; + namespace?: string; + supertest: SuperTest.Agent; +}): Promise => { + const route = routeWithNamespace(BACKFILL_RULE_URL_SCHEDULE, namespace); + const response = await supertest + .post(route) + .set('kbn-xsrf', 'true') + .set('x-elastic-internal-origin', 'Kibana') + .send([ + { + rule_id: ruleId, + start, + end, + }, + ]); + + return response; +}; diff --git a/x-pack/test/functional/apps/infra/metrics_source_configuration.ts b/x-pack/test/functional/apps/infra/metrics_source_configuration.ts index 4e041c18f4ea79..571594a8b94b63 100644 --- a/x-pack/test/functional/apps/infra/metrics_source_configuration.ts +++ b/x-pack/test/functional/apps/infra/metrics_source_configuration.ts @@ -9,11 +9,11 @@ import { cleanup, Dataset, generate, PartialConfig } from '@kbn/data-forge'; import expect from '@kbn/expect'; import { Aggregators, - Comparator, InfraRuleType, MetricThresholdParams, } from '@kbn/infra-plugin/common/alerting/metrics'; +import { COMPARATORS } from '@kbn/alerting-comparators'; import { createRule } from '../../../alerting_api_integration/observability/helpers/alerting_api_helper'; import { waitForDocumentInIndex, @@ -192,7 +192,7 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { criteria: [ { aggType: Aggregators.AVERAGE, - comparator: Comparator.GT, + comparator: COMPARATORS.GREATER_THAN, threshold: [0.5], timeSize: 5, timeUnit: 'm', diff --git a/x-pack/test/functional/apps/ml/anomaly_detection_integrations/anomaly_charts_dashboard_embeddables.ts b/x-pack/test/functional/apps/ml/anomaly_detection_integrations/anomaly_charts_dashboard_embeddables.ts index 19b48858b115b2..7f13c22c9eb917 100644 --- a/x-pack/test/functional/apps/ml/anomaly_detection_integrations/anomaly_charts_dashboard_embeddables.ts +++ b/x-pack/test/functional/apps/ml/anomaly_detection_integrations/anomaly_charts_dashboard_embeddables.ts @@ -73,10 +73,12 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { ML_EMBEDDABLE_TYPES.ANOMALY_CHARTS ); }); - it('can select jobs', async () => { - await ml.dashboardJobSelectionTable.setRowCheckboxState(testData.jobConfig.job_id, true); - await ml.dashboardJobSelectionTable.applyJobSelection(); + await ml.alerting.selectJobs([testData.jobConfig.job_id]); + await ml.alerting.assertJobSelection([testData.jobConfig.job_id]); + }); + + it('populates with default default info', async () => { await ml.dashboardEmbeddables.assertAnomalyChartsEmbeddableInitializerExists(); await ml.dashboardEmbeddables.assertSelectMaxSeriesToPlotValue(6); }); diff --git a/x-pack/test/functional/apps/ml/anomaly_detection_jobs/single_metric_job.ts b/x-pack/test/functional/apps/ml/anomaly_detection_jobs/single_metric_job.ts index 8b29adf3d53848..957ac090e1ade6 100644 --- a/x-pack/test/functional/apps/ml/anomaly_detection_jobs/single_metric_job.ts +++ b/x-pack/test/functional/apps/ml/anomaly_detection_jobs/single_metric_job.ts @@ -388,5 +388,90 @@ export default function ({ getService }: FtrProviderContext) { ); await ml.api.assertNoJobResultsExist(jobIdClone); }); + + it('job cloning with too short of a job creation time range results in validation callouts', async () => { + await ml.testExecution.logTestStep('job cloning loads the job management page'); + await ml.navigation.navigateToMl(); + await ml.navigation.navigateToJobManagement(); + + await ml.testExecution.logTestStep(`cloning job: [${jobId}]`); + await ml.jobTable.clickCloneJobAction(jobId); + + await ml.testExecution.logTestStep('job cloning displays the time range step'); + await ml.jobWizardCommon.assertTimeRangeSectionExists(); + + await ml.testExecution.logTestStep('job cloning sets the time range'); + await ml.jobWizardCommon.clickUseFullDataButton( + 'Feb 7, 2016 @ 00:00:00.000', + 'Feb 11, 2016 @ 23:59:54.000' + ); + + await ml.jobWizardCommon.goToTimeRangeStep(); + + const { startDate: origStartDate } = await ml.jobWizardCommon.getSelectedDateRange(); + + await ml.testExecution.logTestStep('calculate the new end date'); + const shortDurationEndDate: string = `${origStartDate?.split(':', 1)[0]}:01:00.000`; + + await ml.testExecution.logTestStep('set the new end date'); + await ml.jobWizardCommon.setTimeRange({ endTime: shortDurationEndDate }); + + // assert time is set as expected + await ml.jobWizardCommon.assertDateRangeSelection( + origStartDate as string, + shortDurationEndDate + ); + + await ml.jobWizardCommon.advanceToPickFieldsSection(); + await ml.jobWizardCommon.advanceToJobDetailsSection(); + await ml.jobWizardCommon.assertJobIdInputExists(); + await ml.jobWizardCommon.setJobId(`${jobIdClone}-again`); + await ml.jobWizardCommon.advanceToValidationSection(); + await ml.jobWizardCommon.assertValidationCallouts([ + 'mlValidationCallout warning', + 'mlValidationCallout error', + ]); + await ml.jobWizardCommon.assertCalloutText( + 'mlValidationCallout warning', + /Time range\s*The selected or available time range might be too short/ + ); + + await ml.jobWizardCommon.goToTimeRangeStep(); + await ml.jobWizardCommon.clickUseFullDataButton( + 'Feb 7, 2016 @ 00:00:00.000', + 'Feb 11, 2016 @ 23:59:54.000' + ); + await ml.jobWizardCommon.goToValidationStep(); + await ml.jobWizardCommon.assertValidationCallouts(['mlValidationCallout success']); + await ml.jobWizardCommon.assertCalloutText( + 'mlValidationCallout success', + /Time range\s*Valid and long enough to model patterns in the data/ + ); + }); + + it('job creation and toggling model change annotation triggers enable annotation recommendation callout', async () => { + await ml.jobWizardCommon.goToJobDetailsStep(); + await ml.jobWizardCommon.ensureAdvancedSectionOpen(); + + await ml.commonUI.toggleSwitchIfNeeded('mlJobWizardSwitchAnnotations', false); + await ml.jobWizardCommon.assertAnnotationRecommendationCalloutVisible(); + + await ml.commonUI.toggleSwitchIfNeeded('mlJobWizardSwitchAnnotations', true); + await ml.jobWizardCommon.assertAnnotationRecommendationCalloutVisible(false); + }); + + it('job creation memory limit too large results in validation callout', async () => { + await ml.jobWizardCommon.goToJobDetailsStep(); + + const tooLarge = '100000000MB'; + await ml.jobWizardCommon.setModelMemoryLimit(tooLarge); + + await ml.jobWizardCommon.advanceToValidationSection(); + await ml.jobWizardCommon.assertValidationCallouts(['mlValidationCallout warning']); + await ml.jobWizardCommon.assertCalloutText( + 'mlValidationCallout warning', + /Job will not be able to run in the current cluster because model memory limit is higher than/ + ); + }); }); } diff --git a/x-pack/test/functional/apps/ml/anomaly_detection_result_views/anomaly_explorer.ts b/x-pack/test/functional/apps/ml/anomaly_detection_result_views/anomaly_explorer.ts index 549757883101f7..c323356c73e6eb 100644 --- a/x-pack/test/functional/apps/ml/anomaly_detection_result_views/anomaly_explorer.ts +++ b/x-pack/test/functional/apps/ml/anomaly_detection_result_views/anomaly_explorer.ts @@ -7,7 +7,6 @@ import type { Job, Datafeed } from '@kbn/ml-plugin/common/types/anomaly_detection_jobs'; import type { AnomalySwimLaneEmbeddableState } from '@kbn/ml-plugin/public'; -import type { AnomalyChartsEmbeddableInput } from '@kbn/ml-plugin/public/embeddables'; import { stringHash } from '@kbn/ml-string-hash'; import type { FtrProviderContext } from '../../../ftr_provider_context'; import { USER } from '../../../services/ml/security_common'; @@ -521,7 +520,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { const expectedAttachment = { jobIds: [testData.jobConfig.job_id], maxSeriesToPlot: 6, - } as AnomalyChartsEmbeddableInput; + }; // @ts-expect-error Setting id to be undefined here // since time range expected is of the chart plotEarliest/plotLatest, not of the global time range diff --git a/x-pack/test/functional/services/ml/cases.ts b/x-pack/test/functional/services/ml/cases.ts index 016ffcb8486def..6c481df8b99a8b 100644 --- a/x-pack/test/functional/services/ml/cases.ts +++ b/x-pack/test/functional/services/ml/cases.ts @@ -7,7 +7,6 @@ import type { SwimlaneType } from '@kbn/ml-plugin/public/application/explorer/explorer_constants'; import type { AnomalySwimLaneEmbeddableState } from '@kbn/ml-plugin/public'; -import type { AnomalyChartsEmbeddableInput } from '@kbn/ml-plugin/public/embeddables'; import type { FtrProviderContext } from '../../ftr_provider_context'; import type { MlAnomalySwimLane } from './swim_lane'; import type { MlAnomalyCharts } from './anomaly_charts'; @@ -71,7 +70,7 @@ export function MachineLearningCasesProvider( async assertCaseWithAnomalyChartsAttachment( params: CaseParams, - attachment: AnomalyChartsEmbeddableInput, + attachment: { id?: string; jobIds: string[]; maxSeriesToPlot: number }, expectedChartsCount: number ) { await this.assertBasicCaseProps(params); diff --git a/x-pack/test/functional/services/ml/common_ui.ts b/x-pack/test/functional/services/ml/common_ui.ts index ef2605a716dbfd..282ae1aba5033c 100644 --- a/x-pack/test/functional/services/ml/common_ui.ts +++ b/x-pack/test/functional/services/ml/common_ui.ts @@ -447,5 +447,9 @@ export function MachineLearningCommonUIProvider({ await testSubjects.missingOrFail(selector); } }, + + async toggleSwitchIfNeeded(testSubj: string, targetState: boolean) { + await testSubjects.setEuiSwitch(testSubj, targetState ? 'check' : 'uncheck'); + }, }; } diff --git a/x-pack/test/functional/services/ml/dashboard_embeddables.ts b/x-pack/test/functional/services/ml/dashboard_embeddables.ts index cf403bab147d84..9a5428276479e8 100644 --- a/x-pack/test/functional/services/ml/dashboard_embeddables.ts +++ b/x-pack/test/functional/services/ml/dashboard_embeddables.ts @@ -117,19 +117,19 @@ export function MachineLearningDashboardEmbeddablesProvider( async openAnomalyJobSelectionFlyout( mlEmbeddableType: 'ml_anomaly_swimlane' | 'ml_anomaly_charts' | 'ml_single_metric_viewer' ) { + const name = { + ml_anomaly_swimlane: 'Anomaly swim lane', + ml_single_metric_viewer: 'Single metric viewer', + ml_anomaly_charts: 'Anomaly chart', + }; await retry.tryForTime(60 * 1000, async () => { await dashboardAddPanel.clickEditorMenuButton(); await testSubjects.existOrFail('dashboardEditorContextMenu', { timeout: 2000 }); await dashboardAddPanel.clickEmbeddableFactoryGroupButton('ml'); - if (mlEmbeddableType === 'ml_single_metric_viewer') { - await dashboardAddPanel.clickAddNewPanelFromUIActionLink('Single metric viewer'); - await testSubjects.existOrFail('mlAnomalyJobSelectionControls', { timeout: 2000 }); - } else { - await dashboardAddPanel.clickAddNewEmbeddableLink(mlEmbeddableType); - await mlDashboardJobSelectionTable.assertJobSelectionTableExists(); - } + await dashboardAddPanel.clickAddNewPanelFromUIActionLink(name[mlEmbeddableType]); + await testSubjects.existOrFail('mlAnomalyJobSelectionControls', { timeout: 2000 }); }); }, }; diff --git a/x-pack/test/functional/services/ml/data_frame_analytics_creation.ts b/x-pack/test/functional/services/ml/data_frame_analytics_creation.ts index 50d7738abf732c..8e0c1e84b4f5d7 100644 --- a/x-pack/test/functional/services/ml/data_frame_analytics_creation.ts +++ b/x-pack/test/functional/services/ml/data_frame_analytics_creation.ts @@ -553,12 +553,12 @@ export function MachineLearningDataFrameAnalyticsCreationProvider( async assertValidationCalloutsExists() { await retry.tryForTime(4000, async () => { - await testSubjects.existOrFail('mlValidationCallout'); + await testSubjects.existOrFail('~mlValidationCallout'); }); }, async assertAllValidationCalloutsPresent(expectedNumCallouts: number) { - const validationCallouts = await testSubjects.findAll('mlValidationCallout'); + const validationCallouts = await testSubjects.findAll('~mlValidationCallout'); expect(validationCallouts.length).to.eql(expectedNumCallouts); }, diff --git a/x-pack/test/functional/services/ml/job_wizard_common.ts b/x-pack/test/functional/services/ml/job_wizard_common.ts index e1e5b4ea0d45b3..b1671626f191f0 100644 --- a/x-pack/test/functional/services/ml/job_wizard_common.ts +++ b/x-pack/test/functional/services/ml/job_wizard_common.ts @@ -26,6 +26,7 @@ export function MachineLearningJobWizardCommonProvider( const retry = getService('retry'); const testSubjects = getService('testSubjects'); const headerPage = getPageObject('header'); + const browser = getService('browser'); function advancedSectionSelector(subSelector?: string) { const subj = 'mlJobWizardAdvancedSection'; @@ -628,5 +629,78 @@ export function MachineLearningJobWizardCommonProvider( await testSubjects.existOrFail(expectedSelector); }); }, + + async assertAnnotationRecommendationCalloutVisible(expectVisible: boolean = true) { + const callOutTestSubj = 'mlJobWizardAlsoEnableAnnotationsRecommendationCallout'; + if (expectVisible) + await testSubjects.existOrFail(callOutTestSubj, { + timeout: 3_000, + }); + else + await testSubjects.missingOrFail(callOutTestSubj, { + timeout: 3_000, + }); + }, + + async goToTimeRangeStep() { + await retry.tryForTime(60_000, async () => { + await testSubjects.existOrFail('mlJobWizardTimeRangeStep'); + await testSubjects.click('mlJobWizardTimeRangeStep'); + await this.assertTimeRangeSectionExists(); + }); + }, + + async goToValidationStep() { + await retry.tryForTime(60_000, async () => { + await testSubjects.existOrFail('mlJobWizardValidationStep'); + await testSubjects.click('mlJobWizardValidationStep'); + await this.assertValidationSectionExists(); + }); + }, + + async setTimeRange({ startTime, endTime }: { startTime?: string; endTime?: string }) { + const opts = { + clearWithKeyboard: true, + typeCharByChar: true, + }; + + if (startTime) + await testSubjects.setValue('mlJobWizardDatePickerRangeStartDate', startTime, opts); + if (endTime) await testSubjects.setValue('mlJobWizardDatePickerRangeEndDate', endTime, opts); + + // escape popover + await browser.pressKeys(browser.keys.ESCAPE); + }, + + async goToJobDetailsStep() { + await testSubjects.existOrFail('mlJobWizardJobDetailsStep', { + timeout: 3_000, + }); + await testSubjects.click('mlJobWizardJobDetailsStep'); + await this.assertJobDetailsSectionExists(); + }, + + async assertValidationCallouts(expectedCallOutSelectors: string[]) { + for await (const sel of expectedCallOutSelectors) + await testSubjects.existOrFail(sel, { + timeout: 3_000, + }); + }, + + async assertCalloutText(calloutStatusTestSubj: string, expectedText: RegExp) { + const allCalloutStatusTexts = await testSubjects.getVisibleTextAll(calloutStatusTestSubj); + + const oneCalloutMatches = allCalloutStatusTexts.some( + (visibleText) => !!visibleText.match(expectedText) + ); + expect(oneCalloutMatches).to.eql( + true, + `Expect one of the callouts [${calloutStatusTestSubj}] to match [${expectedText}], instead found ${JSON.stringify( + allCalloutStatusTexts, + null, + 2 + )}` + ); + }, }; } diff --git a/x-pack/test/security_solution_api_integration/config/ess/config.base.ts b/x-pack/test/security_solution_api_integration/config/ess/config.base.ts index d134546c6f6335..0fbe6b12a7adea 100644 --- a/x-pack/test/security_solution_api_integration/config/ess/config.base.ts +++ b/x-pack/test/security_solution_api_integration/config/ess/config.base.ts @@ -84,6 +84,7 @@ export function createTestConfig(options: CreateTestConfigOptions, testFiles?: s 'riskScoringPersistence', 'riskScoringRoutesEnabled', 'bulkCustomHighlightedFieldsEnabled', + 'manualRuleRunEnabled', ])}`, '--xpack.task_manager.poll_interval=1000', `--xpack.actions.preconfigured=${JSON.stringify(PRECONFIGURED_ACTION_CONNECTORS)}`, diff --git a/x-pack/test/security_solution_api_integration/test_suites/detections_response/rules_management/rule_management/trial_license_complete_tier/configs/serverless.config.ts b/x-pack/test/security_solution_api_integration/test_suites/detections_response/rules_management/rule_management/trial_license_complete_tier/configs/serverless.config.ts index ca9396db04661e..52a1074c87904a 100644 --- a/x-pack/test/security_solution_api_integration/test_suites/detections_response/rules_management/rule_management/trial_license_complete_tier/configs/serverless.config.ts +++ b/x-pack/test/security_solution_api_integration/test_suites/detections_response/rules_management/rule_management/trial_license_complete_tier/configs/serverless.config.ts @@ -12,4 +12,7 @@ export default createTestConfig({ reportName: 'Rules Management - Rule Management Integration Tests - Serverless Env - Complete Tier', }, + kbnTestServerArgs: [ + `--xpack.securitySolution.enableExperimental=${JSON.stringify(['manualRuleRunEnabled'])}`, + ], }); diff --git a/x-pack/test/security_solution_api_integration/test_suites/detections_response/rules_management/rule_management/trial_license_complete_tier/get_rule_execution_results.ts b/x-pack/test/security_solution_api_integration/test_suites/detections_response/rules_management/rule_management/trial_license_complete_tier/get_rule_execution_results.ts index cd8c2a11f50f49..45280cd0ec51cf 100644 --- a/x-pack/test/security_solution_api_integration/test_suites/detections_response/rules_management/rule_management/trial_license_complete_tier/get_rule_execution_results.ts +++ b/x-pack/test/security_solution_api_integration/test_suites/detections_response/rules_management/rule_management/trial_license_complete_tier/get_rule_execution_results.ts @@ -29,6 +29,7 @@ import { getRuleForAlertTesting, waitForRulePartialFailure, waitForRuleSuccess, + manualRuleRun, } from '../../../../../../common/utils/security_solution'; import { failedGapExecution, @@ -51,7 +52,7 @@ export default ({ getService }: FtrProviderContext) => { // FLAKY: https://github.com/elastic/kibana/issues/177223 // Failing: See https://github.com/elastic/kibana/issues/177223 - describe.skip('@ess @serverless Get Rule Execution Results', () => { + describe('@ess @serverless Get Rule Execution Results', () => { before(async () => { await esArchiver.load(auditbeatPath); await esArchiver.load('x-pack/test/functional/es_archives/security_solution/alias'); @@ -261,5 +262,88 @@ export default ({ getService }: FtrProviderContext) => { expect(response.body.total).to.eql(1002); expect(response.body.events[0].duration_ms).to.eql(3); }); + + it('should return execution events with backfill information', async () => { + const rule = { + ...getRuleForAlertTesting(['auditbeat-*']), + query: 'process.executable: "/usr/bin/sudo"', + }; + const { id } = await createRule(supertest, log, rule); + const fromManualRuleRun = dateMath.parse('now-1m')?.utc().toISOString() ?? ''; + const toManualRuleRun = dateMath.parse('now', { roundUp: true })?.utc().toISOString() ?? ''; + await manualRuleRun({ + ruleId: id, + supertest, + start: fromManualRuleRun, + end: toManualRuleRun, + }); + + await waitForRuleSuccess({ supertest, log, id }); + await waitForEventLogExecuteComplete(es, log, id, 1, 'execute-backfill'); + + const start = dateMath.parse('now-24h')?.utc().toISOString(); + const end = dateMath.parse('now', { roundUp: true })?.utc().toISOString(); + const response = await supertest + .get(getRuleExecutionResultsUrl(id)) + .set(X_ELASTIC_INTERNAL_ORIGIN_REQUEST, 'kibana') + .set('kbn-xsrf', 'true') + .set(ELASTIC_HTTP_VERSION_HEADER, '1') + .query({ start, end, run_type_filters: ['backfill'] }); + + expect(response.status).to.eql(200); + expect(response.body.total).to.eql(1); + expect(response.body.events[0].duration_ms).to.greaterThan(0); + expect(response.body.events[0].search_duration_ms).to.greaterThan(0); + expect(response.body.events[0].schedule_delay_ms).to.greaterThan(0); + expect(response.body.events[0].indexing_duration_ms).to.greaterThan(0); + expect(response.body.events[0].gap_duration_s).to.eql(0); + expect(response.body.events[0].security_status).to.eql('succeeded'); + expect(response.body.events[0].security_message).to.eql( + 'Rule execution completed successfully' + ); + const backfillStart = moment(fromManualRuleRun).add(5, 'm').toISOString(); + expect(response.body.events[0].backfill.to).to.eql(backfillStart); + expect(response.body.events[0].backfill.from).to.eql(fromManualRuleRun); + }); + + it('should reflect run_type_filters in params', async () => { + const rule = { + ...getRuleForAlertTesting(['auditbeat-*']), + query: 'process.executable: "/usr/bin/sudo"', + }; + const { id } = await createRule(supertest, log, rule); + const startManualRuleRun = dateMath.parse('now-1m')?.utc().toISOString() ?? ''; + const endManualRuleRun = dateMath.parse('now', { roundUp: true })?.utc().toISOString() ?? ''; + await manualRuleRun({ + ruleId: id, + supertest, + start: startManualRuleRun, + end: endManualRuleRun, + }); + await waitForRuleSuccess({ supertest, log, id }); + await waitForEventLogExecuteComplete(es, log, id, 1, 'execute-backfill'); + + const start = dateMath.parse('now-24h')?.utc().toISOString(); + const end = dateMath.parse('now', { roundUp: true })?.utc().toISOString(); + const responseWithAllRunTypes = await supertest + .get(getRuleExecutionResultsUrl(id)) + .set(X_ELASTIC_INTERNAL_ORIGIN_REQUEST, 'kibana') + .set('kbn-xsrf', 'true') + .set(ELASTIC_HTTP_VERSION_HEADER, '1') + .query({ start, end, run_type_filters: [] }); + + expect(responseWithAllRunTypes.status).to.eql(200); + expect(responseWithAllRunTypes.body.total).to.eql(2); + + const responseWithOnlyStandard = await supertest + .get(getRuleExecutionResultsUrl(id)) + .set(X_ELASTIC_INTERNAL_ORIGIN_REQUEST, 'kibana') + .set('kbn-xsrf', 'true') + .set(ELASTIC_HTTP_VERSION_HEADER, '1') + .query({ start, end, run_type_filters: ['standard'] }); + + expect(responseWithOnlyStandard.status).to.eql(200); + expect(responseWithOnlyStandard.body.total).to.eql(1); + }); }); }; diff --git a/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/event_log/get_event_log_execute_complete_by_id.ts b/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/event_log/get_event_log_execute_complete_by_id.ts index b6750c9aa168ac..0d702badf75365 100644 --- a/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/event_log/get_event_log_execute_complete_by_id.ts +++ b/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/event_log/get_event_log_execute_complete_by_id.ts @@ -19,7 +19,8 @@ import type { SearchTotalHits } from '@elastic/elasticsearch/lib/api/types'; export const getEventLogExecuteCompleteById = async ( es: Client, log: ToolingLog, - ruleId: string + ruleId: string, + executionType: string = 'execute' ): Promise => { const response = await es.search({ index: '.kibana-event-log*', @@ -36,7 +37,7 @@ export const getEventLogExecuteCompleteById = async ( }, { match_phrase: { - 'event.action': 'execute', + 'event.action': executionType, }, }, { diff --git a/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/event_log/wait_for_event_log_execute_complete.ts b/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/event_log/wait_for_event_log_execute_complete.ts index 584fddced9db63..ca8941f1bdabd8 100644 --- a/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/event_log/wait_for_event_log_execute_complete.ts +++ b/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/event_log/wait_for_event_log_execute_complete.ts @@ -23,11 +23,12 @@ export const waitForEventLogExecuteComplete = async ( es: Client, log: ToolingLog, ruleId: string, - totalExecutions = 1 + totalExecutions = 1, + executionType = 'execute' ): Promise => { await waitFor( async () => { - const executionCount = await getEventLogExecuteCompleteById(es, log, ruleId); + const executionCount = await getEventLogExecuteCompleteById(es, log, ruleId, executionType); return executionCount >= totalExecutions; }, 'waitForEventLogExecuteComplete', diff --git a/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/rule_management/rule_details/execution_log.cy.ts b/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/rule_management/rule_details/execution_log.cy.ts new file mode 100644 index 00000000000000..77c88c0b8573bf --- /dev/null +++ b/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/rule_management/rule_details/execution_log.cy.ts @@ -0,0 +1,77 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import moment from 'moment'; +import { deleteAlertsAndRules } from '../../../../tasks/api_calls/common'; +import { login } from '../../../../tasks/login'; +import { visit } from '../../../../tasks/navigation'; +import { ruleDetailsUrl } from '../../../../urls/rule_details'; +import { createRule } from '../../../../tasks/api_calls/rules'; +import { waitForAlertsToPopulate } from '../../../../tasks/create_new_rule'; +import { + goToExecutionLogTab, + getExecutionLogTableRow, + refreshRuleExecutionTable, + filterByRunType, +} from '../../../../tasks/rule_details'; +import { getNewRule } from '../../../../objects/rule'; +import { EXECUTION_SHOWING } from '../../../../screens/rule_details'; +import { manualRuleRun } from '../../../../tasks/api_calls/backfill'; + +describe( + 'Event log', + { + tags: ['@ess', '@serverless'], + env: { + ftrConfig: { + kbnServerArgs: [ + `--xpack.securitySolution.enableExperimental=${JSON.stringify(['manualRuleRunEnabled'])}`, + ], + }, + }, + }, + function () { + before(() => { + login(); + deleteAlertsAndRules(); + createRule({ + ...getNewRule(), + }).then((rule) => { + cy.wrap(rule.body.id).as('ruleId'); + }); + }); + + it('should display the execution log', function () { + visit(ruleDetailsUrl(this.ruleId)); + waitForAlertsToPopulate(); + goToExecutionLogTab(); + + cy.get(EXECUTION_SHOWING).contains('Showing 1 rule execution'); + getExecutionLogTableRow().should('have.length', 1); + manualRuleRun({ + ruleId: this.ruleId, + start: moment().subtract(5, 'm').toISOString(), + end: moment().toISOString(), + }); + + cy.waitUntil( + () => { + cy.log('Waiting for assignees to appear in popover'); + refreshRuleExecutionTable(); + return getExecutionLogTableRow().then((rows) => { + return rows.length === 2; + }); + }, + { interval: 5000, timeout: 20000 } + ); + cy.get(EXECUTION_SHOWING).contains('Showing 2 rule executions'); + filterByRunType('Manual'); + + getExecutionLogTableRow().should('have.length', 1); + }); + } +); diff --git a/x-pack/test/security_solution_cypress/cypress/screens/rule_details.ts b/x-pack/test/security_solution_cypress/cypress/screens/rule_details.ts index 15a4d1864e00fc..f9c78f5167256b 100644 --- a/x-pack/test/security_solution_cypress/cypress/screens/rule_details.ts +++ b/x-pack/test/security_solution_cypress/cypress/screens/rule_details.ts @@ -163,3 +163,15 @@ export const MAX_SIGNALS_DETAILS = '[data-test-subj="maxSignalsPropertyValue"]'; export const DESCRIPTION_SETUP_GUIDE_BUTTON = '[data-test-subj="stepAboutDetailsToggle-setup"]'; export const DESCRIPTION_SETUP_GUIDE_CONTENT = '[data-test-subj="stepAboutDetailsSetupContent"]'; + +export const EXECUTIONS_TAB = 'a[data-test-subj="navigation-execution_results"]'; + +export const EXECUTION_SHOWING = `[data-test-subj="executionsShowing"]`; + +export const EXECUTION_TABLE = `[data-test-subj="executionsTable"]`; + +export const EXECUTION_LOG_CONTAINER = `[data-test-subj="executionLogContainer"]`; + +export const EXECUTION_RUN_TYPE_FILTER = `[data-test-subj="ExecutionRunTypeFilter"]`; + +export const EXECUTION_RUN_TYPE_FILTER_ITEM = `[data-test-subj="ExecutionRunTypeFilter-item"]`; diff --git a/x-pack/test/security_solution_cypress/cypress/tasks/api_calls/backfill.ts b/x-pack/test/security_solution_cypress/cypress/tasks/api_calls/backfill.ts new file mode 100644 index 00000000000000..9d14c5b8905fec --- /dev/null +++ b/x-pack/test/security_solution_cypress/cypress/tasks/api_calls/backfill.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 { INTERNAL_BASE_ALERTING_API_PATH } from '@kbn/alerting-plugin/common'; + +const BACKFILL_RULE_URL = `${INTERNAL_BASE_ALERTING_API_PATH}/rules/backfill`; +const BACKFILL_RULE_URL_SCHEDULE = `${BACKFILL_RULE_URL}/_schedule`; + +export const manualRuleRun = ({ + ruleId, + start, + end, +}: { + ruleId: string; + start: string; + end: string; +}): Cypress.Chainable> => { + return cy.request({ + method: 'POST', + url: BACKFILL_RULE_URL_SCHEDULE, + headers: { 'kbn-xsrf': 'cypress-creds', 'x-elastic-internal-origin': 'Kibana' }, + body: [ + { + rule_id: ruleId, + start, + end, + }, + ], + }); +}; diff --git a/x-pack/test/security_solution_cypress/cypress/tasks/rule_details.ts b/x-pack/test/security_solution_cypress/cypress/tasks/rule_details.ts index b5b82d78783c40..aae7be96c33fb9 100644 --- a/x-pack/test/security_solution_cypress/cypress/tasks/rule_details.ts +++ b/x-pack/test/security_solution_cypress/cypress/tasks/rule_details.ts @@ -36,6 +36,11 @@ import { RULE_NAME_HEADER, INVESTIGATION_FIELDS_DETAILS, ABOUT_DETAILS, + EXECUTIONS_TAB, + EXECUTION_TABLE, + EXECUTION_LOG_CONTAINER, + EXECUTION_RUN_TYPE_FILTER, + EXECUTION_RUN_TYPE_FILTER_ITEM, } from '../screens/rule_details'; import { RuleDetailsTabs, ruleDetailsUrl } from '../urls/rule_details'; import { @@ -46,6 +51,7 @@ import { } from './exceptions'; import { addsFields, closeFieldsBrowser, filterFieldsBrowser } from './fields_browser'; import { visit } from './navigation'; +import { LOCAL_DATE_PICKER_APPLY_BUTTON_TIMELINE } from '../screens/date_picker'; interface VisitRuleDetailsPageOptions { tab?: RuleDetailsTabs; @@ -122,6 +128,10 @@ export const goToExceptionsTab = () => { cy.get(EXCEPTIONS_TAB).click(); }; +export const goToExecutionLogTab = () => { + cy.get(EXECUTIONS_TAB).click(); +}; + export const viewExpiredExceptionItems = () => { cy.get(EXCEPTIONS_TAB_EXPIRED_FILTER).click(); cy.get(EXCEPTIONS_TAB_ACTIVE_FILTER).click(); @@ -190,3 +200,13 @@ export const hasInvestigationFields = (fields: string) => { export const goToRuleEditSettings = () => { cy.get(EDIT_RULE_SETTINGS_LINK).click(); }; + +export const getExecutionLogTableRow = () => cy.get(EXECUTION_TABLE).find('tbody tr'); + +export const refreshRuleExecutionTable = () => + cy.get(`${EXECUTION_LOG_CONTAINER} ${LOCAL_DATE_PICKER_APPLY_BUTTON_TIMELINE}`).click(); + +export const filterByRunType = (ruleType: string) => { + cy.get(EXECUTION_RUN_TYPE_FILTER).click(); + cy.get(EXECUTION_RUN_TYPE_FILTER_ITEM).contains(ruleType).click(); +}; diff --git a/x-pack/test/spaces_api_integration/common/suites/create.ts b/x-pack/test/spaces_api_integration/common/suites/create.ts index b882e328cc3a24..c7aab659ce960f 100644 --- a/x-pack/test/spaces_api_integration/common/suites/create.ts +++ b/x-pack/test/spaces_api_integration/common/suites/create.ts @@ -19,6 +19,7 @@ interface CreateTests { newSpace: CreateTest; alreadyExists: CreateTest; reservedSpecified: CreateTest; + solutionSpecified: CreateTest; } interface CreateTestDefinition { @@ -63,6 +64,17 @@ export function createTestSuiteFactory(esArchiver: any, supertest: SuperTest) => { + expect(resp.body).to.eql({ + id: 'solution', + name: 'space with solution', + description: 'a description', + color: '#5c5959', + disabledFeatures: [], + solution: 'search', + }); + }; + const makeCreateTest = (describeFn: DescribeFn) => (description: string, { user = {}, spaceId, tests }: CreateTestDefinition) => { @@ -128,6 +140,24 @@ export function createTestSuiteFactory(esArchiver: any, supertest: SuperTest { + it(`should return ${tests.solutionSpecified.statusCode}`, async () => { + return supertest + .post(`${urlPrefix}/api/spaces/space`) + .auth(user.username, user.password) + .send({ + name: 'space with solution', + id: 'solution', + description: 'a description', + color: '#5c5959', + solution: 'search', + disabledFeatures: [], + }) + .expect(tests.solutionSpecified.statusCode) + .then(tests.solutionSpecified.response); + }); + }); }); }); }; @@ -142,5 +172,6 @@ export function createTestSuiteFactory(esArchiver: any, supertest: SuperTest { @@ -68,6 +69,10 @@ export default function createSpacesOnlySuite({ getService }: FtrProviderContext statusCode: 403, response: expectRbacForbiddenResponse, }, + solutionSpecified: { + statusCode: 403, + response: expectRbacForbiddenResponse, + }, }, }); @@ -87,6 +92,10 @@ export default function createSpacesOnlySuite({ getService }: FtrProviderContext statusCode: 200, response: expectReservedSpecifiedResult, }, + solutionSpecified: { + statusCode: 200, + response: expectSolutionSpecifiedResult, + }, }, }); @@ -106,6 +115,10 @@ export default function createSpacesOnlySuite({ getService }: FtrProviderContext statusCode: 200, response: expectReservedSpecifiedResult, }, + solutionSpecified: { + statusCode: 200, + response: expectSolutionSpecifiedResult, + }, }, }); @@ -125,6 +138,10 @@ export default function createSpacesOnlySuite({ getService }: FtrProviderContext statusCode: 200, response: expectReservedSpecifiedResult, }, + solutionSpecified: { + statusCode: 200, + response: expectSolutionSpecifiedResult, + }, }, }); @@ -144,6 +161,10 @@ export default function createSpacesOnlySuite({ getService }: FtrProviderContext statusCode: 403, response: expectRbacForbiddenResponse, }, + solutionSpecified: { + statusCode: 403, + response: expectRbacForbiddenResponse, + }, }, }); @@ -163,6 +184,10 @@ export default function createSpacesOnlySuite({ getService }: FtrProviderContext statusCode: 403, response: expectRbacForbiddenResponse, }, + solutionSpecified: { + statusCode: 403, + response: expectRbacForbiddenResponse, + }, }, }); @@ -182,6 +207,10 @@ export default function createSpacesOnlySuite({ getService }: FtrProviderContext statusCode: 403, response: expectRbacForbiddenResponse, }, + solutionSpecified: { + statusCode: 403, + response: expectRbacForbiddenResponse, + }, }, }); @@ -201,6 +230,10 @@ export default function createSpacesOnlySuite({ getService }: FtrProviderContext statusCode: 403, response: expectRbacForbiddenResponse, }, + solutionSpecified: { + statusCode: 403, + response: expectRbacForbiddenResponse, + }, }, }); }); diff --git a/x-pack/test/spaces_api_integration/spaces_only/apis/create.ts b/x-pack/test/spaces_api_integration/spaces_only/apis/create.ts index 4d18c9b51dd550..000d6b8f2ebe70 100644 --- a/x-pack/test/spaces_api_integration/spaces_only/apis/create.ts +++ b/x-pack/test/spaces_api_integration/spaces_only/apis/create.ts @@ -19,6 +19,7 @@ export default function createSpacesOnlySuite({ getService }: FtrProviderContext expectNewSpaceResult, expectConflictResponse, expectReservedSpecifiedResult, + expectSolutionSpecifiedResult, } = createTestSuiteFactory(esArchiver, supertestWithoutAuth); describe('create', () => { @@ -45,6 +46,10 @@ export default function createSpacesOnlySuite({ getService }: FtrProviderContext statusCode: 200, response: expectReservedSpecifiedResult, }, + solutionSpecified: { + statusCode: 200, + response: expectSolutionSpecifiedResult, + }, }, }); }); diff --git a/x-pack/test/tsconfig.json b/x-pack/test/tsconfig.json index 2df784cd0c8ba2..e86c9b1039fafe 100644 --- a/x-pack/test/tsconfig.json +++ b/x-pack/test/tsconfig.json @@ -123,7 +123,6 @@ "@kbn/discover-plugin", "@kbn/files-plugin", "@kbn/shared-ux-file-types", - "@kbn/alerting-state-types", "@kbn/assetManager-plugin", "@kbn/guided-onboarding-plugin", "@kbn/field-formats-plugin", @@ -175,6 +174,8 @@ "@kbn/esql-utils", "@kbn/search-types", "@kbn/analytics-ftr-helpers-plugin", - "@kbn/reporting-server", + "@kbn/alerting-comparators", + "@kbn/alerting-state-types", + "@kbn/reporting-server" ] } diff --git a/x-pack/test_serverless/api_integration/test_suites/common/management/ingest_pipelines.ts b/x-pack/test_serverless/api_integration/test_suites/common/management/ingest_pipelines.ts index 0f4866f3c3a22f..cf6750f91247d9 100644 --- a/x-pack/test_serverless/api_integration/test_suites/common/management/ingest_pipelines.ts +++ b/x-pack/test_serverless/api_integration/test_suites/common/management/ingest_pipelines.ts @@ -8,24 +8,34 @@ import expect from '@kbn/expect'; import { IngestPutPipelineRequest } from '@elastic/elasticsearch/lib/api/types'; import { FtrProviderContext } from '../../../ftr_provider_context'; +import { InternalRequestHeader, RoleCredentials } from '../../../../shared/services'; export default function ({ getService }: FtrProviderContext) { - const supertest = getService('supertest'); const ingestPipelines = getService('ingestPipelines'); const log = getService('log'); + const svlCommonApi = getService('svlCommonApi'); + const svlUserManager = getService('svlUserManager'); + const supertestWithoutAuth = getService('supertestWithoutAuth'); + let roleAuthc: RoleCredentials; + let internalReqHeader: InternalRequestHeader; describe('Ingest Pipelines', function () { + before(async () => { + roleAuthc = await svlUserManager.createApiKeyForRole('admin'); + internalReqHeader = svlCommonApi.getInternalRequestHeader(); + }); after(async () => { await ingestPipelines.api.deletePipelines(); + await svlUserManager.invalidateApiKeyForRole(roleAuthc); }); describe('Create', () => { it('should create a pipeline', async () => { const pipelineRequestBody = ingestPipelines.fixtures.createPipelineBody(); - const { body } = await supertest + const { body } = await supertestWithoutAuth .post(ingestPipelines.fixtures.apiBasePath) - .set('kbn-xsrf', 'xxx') - .set('x-elastic-internal-origin', 'xxx') + .set(internalReqHeader) + .set(roleAuthc.apiKeyHeader) .send(pipelineRequestBody); expect(body).to.eql({ @@ -36,10 +46,10 @@ export default function ({ getService }: FtrProviderContext) { it('should create a pipeline with only required fields', async () => { const pipelineRequestBody = ingestPipelines.fixtures.createPipelineBodyWithRequiredFields(); // Includes name and processors[] only - const { body } = await supertest + const { body } = await supertestWithoutAuth .post(ingestPipelines.fixtures.apiBasePath) - .set('kbn-xsrf', 'xxx') - .set('x-elastic-internal-origin', 'xxx') + .set(internalReqHeader) + .set(roleAuthc.apiKeyHeader) .send(pipelineRequestBody) .expect(200); @@ -56,10 +66,10 @@ export default function ({ getService }: FtrProviderContext) { await ingestPipelines.api.createPipeline({ id: name, ...esPipelineRequestBody }); // Then, create a pipeline with our internal API - const { body } = await supertest + const { body } = await supertestWithoutAuth .post(ingestPipelines.fixtures.apiBasePath) - .set('kbn-xsrf', 'xxx') - .set('x-elastic-internal-origin', 'xxx') + .set(internalReqHeader) + .set(roleAuthc.apiKeyHeader) .send(pipelineRequestBody) .expect(409); @@ -94,10 +104,10 @@ export default function ({ getService }: FtrProviderContext) { it('should allow an existing pipeline to be updated', async () => { const uri = `${ingestPipelines.fixtures.apiBasePath}/${pipelineName}`; - const { body } = await supertest + const { body } = await supertestWithoutAuth .put(uri) - .set('kbn-xsrf', 'xxx') - .set('x-elastic-internal-origin', 'xxx') + .set(internalReqHeader) + .set(roleAuthc.apiKeyHeader) .send({ ...pipeline, description: 'updated test pipeline description', @@ -116,10 +126,10 @@ export default function ({ getService }: FtrProviderContext) { it('should allow optional fields to be removed', async () => { const uri = `${ingestPipelines.fixtures.apiBasePath}/${pipelineName}`; - const { body } = await supertest + const { body } = await supertestWithoutAuth .put(uri) - .set('kbn-xsrf', 'xxx') - .set('x-elastic-internal-origin', 'xxx') + .set(internalReqHeader) + .set(roleAuthc.apiKeyHeader) .send({ // removes description, version, on_failure, and _meta processors: pipeline.processors, @@ -134,10 +144,10 @@ export default function ({ getService }: FtrProviderContext) { it('should not allow a non-existing pipeline to be updated', async () => { const uri = `${ingestPipelines.fixtures.apiBasePath}/pipeline_does_not_exist`; - const { body } = await supertest + const { body } = await supertestWithoutAuth .put(uri) - .set('kbn-xsrf', 'xxx') - .set('x-elastic-internal-origin', 'xxx') + .set(internalReqHeader) + .set(roleAuthc.apiKeyHeader) .send({ ...pipeline, description: 'updated test pipeline description', @@ -179,10 +189,10 @@ export default function ({ getService }: FtrProviderContext) { describe('all pipelines', () => { it('should return an array of pipelines', async () => { - const { body } = await supertest + const { body } = await supertestWithoutAuth .get(ingestPipelines.fixtures.apiBasePath) - .set('kbn-xsrf', 'xxx') - .set('x-elastic-internal-origin', 'xxx') + .set(internalReqHeader) + .set(roleAuthc.apiKeyHeader) .expect(200); expect(Array.isArray(body)).to.be(true); @@ -202,10 +212,10 @@ export default function ({ getService }: FtrProviderContext) { it('should return a single pipeline', async () => { const uri = `${ingestPipelines.fixtures.apiBasePath}/${pipelineName}`; - const { body } = await supertest + const { body } = await supertestWithoutAuth .get(uri) - .set('kbn-xsrf', 'xxx') - .set('x-elastic-internal-origin', 'xxx') + .set(internalReqHeader) + .set(roleAuthc.apiKeyHeader) .expect(200); expect(body).to.eql({ @@ -244,10 +254,10 @@ export default function ({ getService }: FtrProviderContext) { const uri = `${ingestPipelines.fixtures.apiBasePath}/${pipelineA}`; - const { body } = await supertest + const { body } = await supertestWithoutAuth .delete(uri) - .set('kbn-xsrf', 'xxx') - .set('x-elastic-internal-origin', 'xxx') + .set(internalReqHeader) + .set(roleAuthc.apiKeyHeader) .expect(200); expect(body).to.eql({ @@ -263,10 +273,10 @@ export default function ({ getService }: FtrProviderContext) { const { body: { itemsDeleted, errors }, - } = await supertest + } = await supertestWithoutAuth .delete(uri) - .set('kbn-xsrf', 'xxx') - .set('x-elastic-internal-origin', 'xxx') + .set(internalReqHeader) + .set(roleAuthc.apiKeyHeader) .expect(200); expect(errors).to.eql([]); @@ -283,10 +293,10 @@ export default function ({ getService }: FtrProviderContext) { const uri = `${ingestPipelines.fixtures.apiBasePath}/${pipelineD},${PIPELINE_DOES_NOT_EXIST}`; - const { body } = await supertest + const { body } = await supertestWithoutAuth .delete(uri) - .set('kbn-xsrf', 'xxx') - .set('x-elastic-internal-origin', 'xxx') + .set(internalReqHeader) + .set(roleAuthc.apiKeyHeader) .expect(200); expect(body).to.eql({ @@ -315,10 +325,10 @@ export default function ({ getService }: FtrProviderContext) { it('should successfully simulate a pipeline', async () => { const { name, ...pipeline } = ingestPipelines.fixtures.createPipelineBody(); const documents = ingestPipelines.fixtures.createDocuments(); - const { body } = await supertest + const { body } = await supertestWithoutAuth .post(`${ingestPipelines.fixtures.apiBasePath}/simulate`) - .set('kbn-xsrf', 'xxx') - .set('x-elastic-internal-origin', 'xxx') + .set(internalReqHeader) + .set(roleAuthc.apiKeyHeader) .send({ pipeline, documents, @@ -334,10 +344,10 @@ export default function ({ getService }: FtrProviderContext) { const { name, ...pipeline } = ingestPipelines.fixtures.createPipelineBodyWithRequiredFields(); const documents = ingestPipelines.fixtures.createDocuments(); - const { body } = await supertest + const { body } = await supertestWithoutAuth .post(`${ingestPipelines.fixtures.apiBasePath}/simulate`) - .set('kbn-xsrf', 'xxx') - .set('x-elastic-internal-origin', 'xxx') + .set(internalReqHeader) + .set(roleAuthc.apiKeyHeader) .send({ pipeline, documents, @@ -380,10 +390,10 @@ export default function ({ getService }: FtrProviderContext) { it('should return a document', async () => { const uri = `${ingestPipelines.fixtures.apiBasePath}/documents/${INDEX}/${DOCUMENT_ID}`; - const { body } = await supertest + const { body } = await supertestWithoutAuth .get(uri) - .set('kbn-xsrf', 'xxx') - .set('x-elastic-internal-origin', 'xxx') + .set(internalReqHeader) + .set(roleAuthc.apiKeyHeader) .expect(200); expect(body).to.eql({ @@ -396,10 +406,10 @@ export default function ({ getService }: FtrProviderContext) { it('should return an error if the document does not exist', async () => { const uri = `${ingestPipelines.fixtures.apiBasePath}/documents/${INDEX}/2`; // Document 2 does not exist - const { body } = await supertest + const { body } = await supertestWithoutAuth .get(uri) - .set('kbn-xsrf', 'xxx') - .set('x-elastic-internal-origin', 'xxx') + .set(internalReqHeader) + .set(roleAuthc.apiKeyHeader) .expect(404); expect(body).to.eql({ @@ -415,10 +425,10 @@ export default function ({ getService }: FtrProviderContext) { it('should map to a pipeline', async () => { const validCsv = 'source_field,copy_action,format_action,timestamp_format,destination_field,Notes\nsrcip,,,,source.address,Copying srcip to source.address'; - const { body } = await supertest + const { body } = await supertestWithoutAuth .post(`${ingestPipelines.fixtures.apiBasePath}/parse_csv`) - .set('kbn-xsrf', 'xxx') - .set('x-elastic-internal-origin', 'xxx') + .set(internalReqHeader) + .set(roleAuthc.apiKeyHeader) .send({ copyAction: 'copy', file: validCsv, diff --git a/x-pack/test_serverless/api_integration/test_suites/common/management/rollups.ts b/x-pack/test_serverless/api_integration/test_suites/common/management/rollups.ts index ce413ce5a7f3a4..727b8b9abc9270 100644 --- a/x-pack/test_serverless/api_integration/test_suites/common/management/rollups.ts +++ b/x-pack/test_serverless/api_integration/test_suites/common/management/rollups.ts @@ -12,13 +12,20 @@ import { X_ELASTIC_INTERNAL_ORIGIN_REQUEST } from '@kbn/core-http-common/src/con import { FIELDS_FOR_WILDCARD_PATH as BASE_URI } from '@kbn/data-views-plugin/common/constants'; import { DataViewType } from '@kbn/data-views-plugin/common'; import { FtrProviderContext } from '../../../ftr_provider_context'; +import { InternalRequestHeader, RoleCredentials } from '../../../../shared/services'; export default function ({ getService }: FtrProviderContext) { - const supertest = getService('supertest'); const esArchiver = getService('esArchiver'); + const svlCommonApi = getService('svlCommonApi'); + const svlUserManager = getService('svlUserManager'); + const supertestWithoutAuth = getService('supertestWithoutAuth'); + let roleAuthc: RoleCredentials; + let internalReqHeader: InternalRequestHeader; describe('rollup data views - fields for wildcard', function () { before(async () => { + roleAuthc = await svlUserManager.createApiKeyForRole('admin'); + internalReqHeader = svlCommonApi.getInternalRequestHeader(); await esArchiver.load('test/api_integration/fixtures/es_archiver/index_patterns/basic_index'); }); @@ -26,9 +33,10 @@ export default function ({ getService }: FtrProviderContext) { await esArchiver.unload( 'test/api_integration/fixtures/es_archiver/index_patterns/basic_index' ); + await svlUserManager.invalidateApiKeyForRole(roleAuthc); }); it('returns 200 and best effort response despite lack of rollup support', async () => { - const response = await supertest + const response = await supertestWithoutAuth .get(BASE_URI) .query({ pattern: 'basic_index', @@ -36,7 +44,9 @@ export default function ({ getService }: FtrProviderContext) { rollup_index: 'bar', }) .set(ELASTIC_HTTP_VERSION_HEADER, INITIAL_REST_VERSION_INTERNAL) - .set(X_ELASTIC_INTERNAL_ORIGIN_REQUEST, 'true'); + .set(X_ELASTIC_INTERNAL_ORIGIN_REQUEST, 'true') + .set(internalReqHeader) + .set(roleAuthc.apiKeyHeader); expect(response.status).toBe(200); expect(response.body.fields.length).toEqual(5); diff --git a/x-pack/test_serverless/api_integration/test_suites/common/management/scripted_fields.ts b/x-pack/test_serverless/api_integration/test_suites/common/management/scripted_fields.ts index 0f17561ea7edc2..af41fd61cc015f 100644 --- a/x-pack/test_serverless/api_integration/test_suites/common/management/scripted_fields.ts +++ b/x-pack/test_serverless/api_integration/test_suites/common/management/scripted_fields.ts @@ -8,14 +8,21 @@ import expect from 'expect'; import { DATA_VIEW_PATH } from '@kbn/data-views-plugin/server'; import { FtrProviderContext } from '../../../ftr_provider_context'; +import { InternalRequestHeader, RoleCredentials } from '../../../../shared/services'; export default function ({ getService }: FtrProviderContext) { - const supertest = getService('supertest'); const esArchiver = getService('esArchiver'); const kibanaServer = getService('kibanaServer'); + const svlCommonApi = getService('svlCommonApi'); + const svlUserManager = getService('svlUserManager'); + const supertestWithoutAuth = getService('supertestWithoutAuth'); + let roleAuthc: RoleCredentials; + let internalReqHeader: InternalRequestHeader; describe('scripted fields disabled', function () { before(async () => { + roleAuthc = await svlUserManager.createApiKeyForRole('admin'); + internalReqHeader = svlCommonApi.getInternalRequestHeader(); // TODO: We're running into a 'Duplicate data view: basic_index' // error in Serverless, so make sure to clean up first await kibanaServer.savedObjects.cleanStandardList(); @@ -26,11 +33,13 @@ export default function ({ getService }: FtrProviderContext) { await esArchiver.unload( 'test/api_integration/fixtures/es_archiver/index_patterns/basic_index' ); + await svlUserManager.invalidateApiKeyForRole(roleAuthc); }); it('scripted fields are ignored when disabled', async () => { - const response = await supertest + const response = await supertestWithoutAuth .post(DATA_VIEW_PATH) - .set('kbn-xsrf', 'some-xsrf-token') + .set(internalReqHeader) + .set(roleAuthc.apiKeyHeader) .send({ data_view: { title: 'basic_index', diff --git a/x-pack/test_serverless/api_integration/test_suites/common/management/spaces.ts b/x-pack/test_serverless/api_integration/test_suites/common/management/spaces.ts index a93cdd048d21f0..19cacb01641dc8 100644 --- a/x-pack/test_serverless/api_integration/test_suites/common/management/spaces.ts +++ b/x-pack/test_serverless/api_integration/test_suites/common/management/spaces.ts @@ -34,7 +34,7 @@ export default function ({ getService }: FtrProviderContext) { it('#delete', async () => { const { body, status } = await supertestWithoutAuth .delete('/api/spaces/space/default') - .set(commonRequestHeader) + .set(internalRequestHeader) .set(roleAuthc.apiKeyHeader); // normally we'd get a 400 bad request if we tried to delete the default space @@ -44,7 +44,7 @@ export default function ({ getService }: FtrProviderContext) { it('#create', async () => { const { body, status } = await supertestWithoutAuth .post('/api/spaces/space') - .set(commonRequestHeader) + .set(internalRequestHeader) .set(roleAuthc.apiKeyHeader) .send({ id: 'custom', @@ -58,7 +58,7 @@ export default function ({ getService }: FtrProviderContext) { it('#update requires internal header', async () => { const { body, status } = await supertestWithoutAuth .put('/api/spaces/space/default') - .set(commonRequestHeader) + .set(internalRequestHeader) .set(roleAuthc.apiKeyHeader) .send({ id: 'default', @@ -72,7 +72,7 @@ export default function ({ getService }: FtrProviderContext) { it('#copyToSpace', async () => { const { body, status } = await supertestWithoutAuth .post('/api/spaces/_copy_saved_objects') - .set(commonRequestHeader) + .set(internalRequestHeader) .set(roleAuthc.apiKeyHeader); // without a request body we would normally a 400 bad request if the endpoint was registered @@ -82,7 +82,7 @@ export default function ({ getService }: FtrProviderContext) { it('#resolveCopyToSpaceErrors', async () => { const { body, status } = await supertestWithoutAuth .post('/api/spaces/_resolve_copy_saved_objects_errors') - .set(commonRequestHeader) + .set(internalRequestHeader) .set(roleAuthc.apiKeyHeader); // without a request body we would normally a 400 bad request if the endpoint was registered @@ -92,7 +92,7 @@ export default function ({ getService }: FtrProviderContext) { it('#updateObjectsSpaces', async () => { const { body, status } = await supertestWithoutAuth .post('/api/spaces/_update_objects_spaces') - .set(commonRequestHeader) + .set(internalRequestHeader) .set(roleAuthc.apiKeyHeader); // without a request body we would normally a 400 bad request if the endpoint was registered @@ -102,7 +102,7 @@ export default function ({ getService }: FtrProviderContext) { it('#getShareableReferences', async () => { const { body, status } = await supertestWithoutAuth .post('/api/spaces/_get_shareable_references') - .set(commonRequestHeader) + .set(internalRequestHeader) .set(roleAuthc.apiKeyHeader); // without a request body we would normally a 400 bad request if the endpoint was registered @@ -112,7 +112,7 @@ export default function ({ getService }: FtrProviderContext) { it('#disableLegacyUrlAliases', async () => { const { body, status } = await supertestWithoutAuth .post('/api/spaces/_disable_legacy_url_aliases') - .set(commonRequestHeader) + .set(internalRequestHeader) .set(roleAuthc.apiKeyHeader); // without a request body we would normally a 400 bad request if the endpoint was registered diff --git a/x-pack/test_serverless/api_integration/test_suites/common/painless_lab/painless_lab.ts b/x-pack/test_serverless/api_integration/test_suites/common/painless_lab/painless_lab.ts index 1d0d5cefedb86f..122b1d9c879167 100644 --- a/x-pack/test_serverless/api_integration/test_suites/common/painless_lab/painless_lab.ts +++ b/x-pack/test_serverless/api_integration/test_suites/common/painless_lab/painless_lab.ts @@ -7,22 +7,32 @@ import expect from '@kbn/expect'; import { FtrProviderContext } from '../../../ftr_provider_context'; +import { RoleCredentials } from '../../../../shared/services'; const API_BASE_PATH = '/api/painless_lab'; export default function ({ getService }: FtrProviderContext) { - const supertest = getService('supertest'); const svlCommonApi = getService('svlCommonApi'); + const svlUserManager = getService('svlUserManager'); + const supertestWithoutAuth = getService('supertestWithoutAuth'); + let roleAuthc: RoleCredentials; describe('Painless Lab Routes', function () { + before(async () => { + roleAuthc = await svlUserManager.createApiKeyForRole('admin'); + }); + after(async () => { + await svlUserManager.invalidateApiKeyForRole(roleAuthc); + }); describe('Execute', () => { it('should execute a valid painless script', async () => { const script = '"{\\n \\"script\\": {\\n \\"source\\": \\"return true;\\",\\n \\"params\\": {\\n \\"string_parameter\\": \\"string value\\",\\n \\"number_parameter\\": 1.5,\\n \\"boolean_parameter\\": true\\n}\\n }\\n}"'; - const { body } = await supertest + const { body } = await supertestWithoutAuth .post(`${API_BASE_PATH}/execute`) .set(svlCommonApi.getInternalRequestHeader()) + .set(roleAuthc.apiKeyHeader) .set('Content-Type', 'application/json;charset=UTF-8') .send(script) .expect(200); @@ -36,10 +46,11 @@ export default function ({ getService }: FtrProviderContext) { const invalidScript = '"{\\n \\"script\\": {\\n \\"source\\": \\"foobar\\",\\n \\"params\\": {\\n \\"string_parameter\\": \\"string value\\",\\n \\"number_parameter\\": 1.5,\\n \\"boolean_parameter\\": true\\n}\\n }\\n}"'; - const { body } = await supertest + const { body } = await supertestWithoutAuth .post(`${API_BASE_PATH}/execute`) .set(svlCommonApi.getInternalRequestHeader()) .set('Content-Type', 'application/json;charset=UTF-8') + .set(roleAuthc.apiKeyHeader) .send(invalidScript) .expect(200); diff --git a/x-pack/test_serverless/api_integration/test_suites/observability/custom_threshold_rule/avg_pct_fired.ts b/x-pack/test_serverless/api_integration/test_suites/observability/custom_threshold_rule/avg_pct_fired.ts index 3f3e9b8223a8d8..312792e33d9d31 100644 --- a/x-pack/test_serverless/api_integration/test_suites/observability/custom_threshold_rule/avg_pct_fired.ts +++ b/x-pack/test_serverless/api_integration/test_suites/observability/custom_threshold_rule/avg_pct_fired.ts @@ -6,15 +6,13 @@ */ import { cleanup, generate, Dataset, PartialConfig } from '@kbn/data-forge'; -import { - Aggregators, - Comparator, -} from '@kbn/observability-plugin/common/custom_threshold_rule/types'; +import { Aggregators } from '@kbn/observability-plugin/common/custom_threshold_rule/types'; import { FIRED_ACTIONS_ID } from '@kbn/observability-plugin/server/lib/rules/custom_threshold/constants'; import expect from '@kbn/expect'; import { OBSERVABILITY_THRESHOLD_RULE_TYPE_ID } from '@kbn/rule-data-utils'; import { parseSearchParams } from '@kbn/share-plugin/common/url_service'; import { omit } from 'lodash'; +import { COMPARATORS } from '@kbn/alerting-comparators'; import { FtrProviderContext } from '../../../ftr_provider_context'; import { ISO_DATE_REGEX } from './constants'; import { ActionDocument, LogsExplorerLocatorParsedParams } from './typings'; @@ -109,7 +107,7 @@ export default function ({ getService }: FtrProviderContext) { params: { criteria: [ { - comparator: Comparator.GT, + comparator: COMPARATORS.GREATER_THAN, threshold: [0.5], timeSize: 5, timeUnit: 'm', diff --git a/x-pack/test_serverless/api_integration/test_suites/observability/custom_threshold_rule/avg_pct_no_data.ts b/x-pack/test_serverless/api_integration/test_suites/observability/custom_threshold_rule/avg_pct_no_data.ts index 6f7bfec08e919b..50772ee1057041 100644 --- a/x-pack/test_serverless/api_integration/test_suites/observability/custom_threshold_rule/avg_pct_no_data.ts +++ b/x-pack/test_serverless/api_integration/test_suites/observability/custom_threshold_rule/avg_pct_no_data.ts @@ -5,15 +5,13 @@ * 2.0. */ -import { - Aggregators, - Comparator, -} from '@kbn/observability-plugin/common/custom_threshold_rule/types'; +import { Aggregators } from '@kbn/observability-plugin/common/custom_threshold_rule/types'; import { NO_DATA_ACTIONS_ID } from '@kbn/observability-plugin/server/lib/rules/custom_threshold/constants'; import expect from '@kbn/expect'; import { OBSERVABILITY_THRESHOLD_RULE_TYPE_ID } from '@kbn/rule-data-utils'; import { parseSearchParams } from '@kbn/share-plugin/common/url_service'; import { omit } from 'lodash'; +import { COMPARATORS } from '@kbn/alerting-comparators'; import { FtrProviderContext } from '../../../ftr_provider_context'; import { ISO_DATE_REGEX } from './constants'; import { ActionDocument, LogsExplorerLocatorParsedParams } from './typings'; @@ -83,7 +81,7 @@ export default function ({ getService }: FtrProviderContext) { params: { criteria: [ { - comparator: Comparator.GT, + comparator: COMPARATORS.GREATER_THAN, threshold: [0.5], timeSize: 5, timeUnit: 'm', diff --git a/x-pack/test_serverless/api_integration/test_suites/observability/custom_threshold_rule/custom_eq_avg_bytes_fired.ts b/x-pack/test_serverless/api_integration/test_suites/observability/custom_threshold_rule/custom_eq_avg_bytes_fired.ts index 5ba84e3f51401f..9e4b0be5139458 100644 --- a/x-pack/test_serverless/api_integration/test_suites/observability/custom_threshold_rule/custom_eq_avg_bytes_fired.ts +++ b/x-pack/test_serverless/api_integration/test_suites/observability/custom_threshold_rule/custom_eq_avg_bytes_fired.ts @@ -12,10 +12,8 @@ */ import { cleanup, Dataset, generate, PartialConfig } from '@kbn/data-forge'; -import { - Aggregators, - Comparator, -} from '@kbn/observability-plugin/common/custom_threshold_rule/types'; +import { Aggregators } from '@kbn/observability-plugin/common/custom_threshold_rule/types'; +import { COMPARATORS } from '@kbn/alerting-comparators'; import { FIRED_ACTIONS_ID } from '@kbn/observability-plugin/server/lib/rules/custom_threshold/constants'; import expect from '@kbn/expect'; import { OBSERVABILITY_THRESHOLD_RULE_TYPE_ID } from '@kbn/rule-data-utils'; @@ -112,7 +110,7 @@ export default function ({ getService }: FtrProviderContext) { params: { criteria: [ { - comparator: Comparator.GT, + comparator: COMPARATORS.GREATER_THAN, threshold: [0.9], timeSize: 1, timeUnit: 'm', @@ -212,7 +210,7 @@ export default function ({ getService }: FtrProviderContext) { .eql({ criteria: [ { - comparator: Comparator.GT, + comparator: COMPARATORS.GREATER_THAN, threshold: [0.9], timeSize: 1, timeUnit: 'm', diff --git a/x-pack/test_serverless/api_integration/test_suites/observability/custom_threshold_rule/documents_count_fired.ts b/x-pack/test_serverless/api_integration/test_suites/observability/custom_threshold_rule/documents_count_fired.ts index 4f26d2caf5c527..589ebe7a29fa27 100644 --- a/x-pack/test_serverless/api_integration/test_suites/observability/custom_threshold_rule/documents_count_fired.ts +++ b/x-pack/test_serverless/api_integration/test_suites/observability/custom_threshold_rule/documents_count_fired.ts @@ -6,15 +6,13 @@ */ import { cleanup, generate, Dataset, PartialConfig } from '@kbn/data-forge'; -import { - Aggregators, - Comparator, -} from '@kbn/observability-plugin/common/custom_threshold_rule/types'; +import { Aggregators } from '@kbn/observability-plugin/common/custom_threshold_rule/types'; import { FIRED_ACTIONS_ID } from '@kbn/observability-plugin/server/lib/rules/custom_threshold/constants'; import expect from '@kbn/expect'; import { OBSERVABILITY_THRESHOLD_RULE_TYPE_ID } from '@kbn/rule-data-utils'; import { parseSearchParams } from '@kbn/share-plugin/common/url_service'; import { omit } from 'lodash'; +import { COMPARATORS } from '@kbn/alerting-comparators'; import { FtrProviderContext } from '../../../ftr_provider_context'; import { ISO_DATE_REGEX } from './constants'; import { ActionDocument, LogsExplorerLocatorParsedParams } from './typings'; @@ -113,7 +111,7 @@ export default function ({ getService }: FtrProviderContext) { params: { criteria: [ { - comparator: Comparator.OUTSIDE_RANGE, + comparator: COMPARATORS.NOT_BETWEEN, threshold: [1, 2], timeSize: 1, timeUnit: 'm', @@ -211,7 +209,7 @@ export default function ({ getService }: FtrProviderContext) { .eql({ criteria: [ { - comparator: Comparator.OUTSIDE_RANGE, + comparator: COMPARATORS.NOT_BETWEEN, threshold: [1, 2], timeSize: 1, timeUnit: 'm', diff --git a/x-pack/test_serverless/api_integration/test_suites/observability/custom_threshold_rule/group_by_fired.ts b/x-pack/test_serverless/api_integration/test_suites/observability/custom_threshold_rule/group_by_fired.ts index 3dbb64f471b5d5..08baeb3a96eb33 100644 --- a/x-pack/test_serverless/api_integration/test_suites/observability/custom_threshold_rule/group_by_fired.ts +++ b/x-pack/test_serverless/api_integration/test_suites/observability/custom_threshold_rule/group_by_fired.ts @@ -13,13 +13,11 @@ import { kbnTestConfig } from '@kbn/test'; import { cleanup, generate, Dataset, PartialConfig } from '@kbn/data-forge'; -import { - Aggregators, - Comparator, -} from '@kbn/observability-plugin/common/custom_threshold_rule/types'; +import { Aggregators } from '@kbn/observability-plugin/common/custom_threshold_rule/types'; import { FIRED_ACTIONS_ID } from '@kbn/observability-plugin/server/lib/rules/custom_threshold/constants'; import expect from '@kbn/expect'; import { OBSERVABILITY_THRESHOLD_RULE_TYPE_ID } from '@kbn/rule-data-utils'; +import { COMPARATORS } from '@kbn/alerting-comparators'; import { FtrProviderContext } from '../../../ftr_provider_context'; import { ActionDocument } from './typings'; @@ -113,7 +111,7 @@ export default function ({ getService }: FtrProviderContext) { params: { criteria: [ { - comparator: Comparator.GT_OR_EQ, + comparator: COMPARATORS.GREATER_THAN_OR_EQUALS, threshold: [0.2], timeSize: 1, timeUnit: 'm', diff --git a/x-pack/test_serverless/api_integration/test_suites/observability/custom_threshold_rule/p99_pct_fired.ts b/x-pack/test_serverless/api_integration/test_suites/observability/custom_threshold_rule/p99_pct_fired.ts index 884259f107109e..f21dc4a3c08bad 100644 --- a/x-pack/test_serverless/api_integration/test_suites/observability/custom_threshold_rule/p99_pct_fired.ts +++ b/x-pack/test_serverless/api_integration/test_suites/observability/custom_threshold_rule/p99_pct_fired.ts @@ -6,15 +6,13 @@ */ import { cleanup, generate, Dataset, PartialConfig } from '@kbn/data-forge'; -import { - Aggregators, - Comparator, -} from '@kbn/observability-plugin/common/custom_threshold_rule/types'; +import { Aggregators } from '@kbn/observability-plugin/common/custom_threshold_rule/types'; import { FIRED_ACTIONS_ID } from '@kbn/observability-plugin/server/lib/rules/custom_threshold/constants'; import expect from '@kbn/expect'; import { OBSERVABILITY_THRESHOLD_RULE_TYPE_ID } from '@kbn/rule-data-utils'; import { parseSearchParams } from '@kbn/share-plugin/common/url_service'; import { omit } from 'lodash'; +import { COMPARATORS } from '@kbn/alerting-comparators'; import { FtrProviderContext } from '../../../ftr_provider_context'; import { ISO_DATE_REGEX } from './constants'; import { ActionDocument, LogsExplorerLocatorParsedParams } from './typings'; @@ -112,7 +110,7 @@ export default function ({ getService }: FtrProviderContext) { params: { criteria: [ { - comparator: Comparator.GT, + comparator: COMPARATORS.GREATER_THAN, threshold: [0.5], timeSize: 5, timeUnit: 'm', diff --git a/x-pack/test_serverless/api_integration/test_suites/observability/synthetics/synthetics_enablement.ts b/x-pack/test_serverless/api_integration/test_suites/observability/synthetics/synthetics_enablement.ts index 04debdb1b640d4..e19bc8e2279699 100644 --- a/x-pack/test_serverless/api_integration/test_suites/observability/synthetics/synthetics_enablement.ts +++ b/x-pack/test_serverless/api_integration/test_suites/observability/synthetics/synthetics_enablement.ts @@ -43,6 +43,8 @@ export default function ({ getService }: FtrProviderContext) { }; describe('SyntheticsEnablement', function () { + // fails on MKI, see https://github.com/elastic/kibana/issues/184273 + this.tags(['failsOnMKI']); const svlUserManager = getService('svlUserManager'); const svlCommonApi = getService('svlCommonApi'); const supertestWithoutAuth = getService('supertestWithoutAuth'); diff --git a/x-pack/test_serverless/tsconfig.json b/x-pack/test_serverless/tsconfig.json index 020b861654dc93..639a092b6f45db 100644 --- a/x-pack/test_serverless/tsconfig.json +++ b/x-pack/test_serverless/tsconfig.json @@ -99,6 +99,7 @@ "@kbn/utility-types", "@kbn/synthetics-plugin", "@kbn/dataset-quality-plugin", + "@kbn/alerting-comparators", "@kbn/search-types" ] } diff --git a/yarn.lock b/yarn.lock index 5f1865d8d5f8ac..caa1bc8afa0b98 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3107,6 +3107,10 @@ version "0.0.0" uid "" +"@kbn/alerting-comparators@link:x-pack/packages/kbn-alerting-comparators": + version "0.0.0" + uid "" + "@kbn/alerting-example-plugin@link:x-pack/examples/alerting_example": version "0.0.0" uid "" @@ -4903,6 +4907,10 @@ version "0.0.0" uid "" +"@kbn/grouping@link:packages/kbn-grouping": + version "0.0.0" + uid "" + "@kbn/guided-onboarding-example-plugin@link:examples/guided_onboarding_example": version "0.0.0" uid "" @@ -5963,10 +5971,6 @@ version "0.0.0" uid "" -"@kbn/securitysolution-grouping@link:packages/kbn-securitysolution-grouping": - version "0.0.0" - uid "" - "@kbn/securitysolution-hook-utils@link:packages/kbn-securitysolution-hook-utils": version "0.0.0" uid ""