diff --git a/.buildkite/ftr_configs.yml b/.buildkite/ftr_configs.yml index 43bee5ba27a7d..93e089866afcc 100644 --- a/.buildkite/ftr_configs.yml +++ b/.buildkite/ftr_configs.yml @@ -305,6 +305,7 @@ enabled: - x-pack/test/functional/apps/ml/short_tests/config.ts - x-pack/test/functional/apps/ml/stack_management_jobs/config.ts - x-pack/test/functional/apps/monitoring/config.ts + - x-pack/test/functional/apps/navigation/config.ts - x-pack/test/functional/apps/observability_logs_explorer/config.ts - x-pack/test/functional/apps/dataset_quality/config.ts - x-pack/test/functional/apps/painless_lab/config.ts diff --git a/.buildkite/scripts/steps/esql_grammar_sync.sh b/.buildkite/scripts/steps/esql_grammar_sync.sh index 33091b7524387..88c6871794b3b 100755 --- a/.buildkite/scripts/steps/esql_grammar_sync.sh +++ b/.buildkite/scripts/steps/esql_grammar_sync.sh @@ -113,7 +113,7 @@ main () { git checkout -b "$BRANCH_NAME" - git add esql/antlr/* + git add antlr/* git commit -m "Update ES|QL grammars" report_main_step "Changes committed. Creating pull request." diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 1088b4e8a1176..badb25a143bb4 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -1252,6 +1252,7 @@ x-pack/plugins/cloud_integrations/cloud_full_story/server/config.ts @elastic/kib /x-pack/test/security_functional/ @elastic/kibana-security /x-pack/test/spaces_api_integration/ @elastic/kibana-security /x-pack/test/saved_object_api_integration/ @elastic/kibana-security +/packages/core/http/core-http-server-internal/src/cdn_config/ @elastic/kibana-security @elastic/kibana-core #CC# /x-pack/plugins/security/ @elastic/kibana-security # Response Ops team diff --git a/api_docs/actions.mdx b/api_docs/actions.mdx index 4def64fb8d471..631dc92c56f16 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-03-29 +date: 2024-04-02 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 637b8ced9f776..394ba0cb8e8b8 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-03-29 +date: 2024-04-02 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 6003dfbf8e7b5..ab28cf9ccd3bd 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-03-29 +date: 2024-04-02 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 6242a2651d5bc..8efffe1294172 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-03-29 +date: 2024-04-02 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 18b1534574546..c19509f12d214 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-03-29 +date: 2024-04-02 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 a7c024f712d5b..6cc5e9ad084c2 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-03-29 +date: 2024-04-02 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 8af17a05779a4..c5f3e3b8c4b0e 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-03-29 +date: 2024-04-02 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 e54cb35f49d9d..9e50ccf6149f8 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-03-29 +date: 2024-04-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'assetManager'] --- import assetManagerObj from './asset_manager.devdocs.json'; diff --git a/api_docs/banners.mdx b/api_docs/banners.mdx index 6c6ee547f2e5e..befc77fdda141 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-03-29 +date: 2024-04-02 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 98ed4426cc95f..90915494620b2 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-03-29 +date: 2024-04-02 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 a112849cc9fb5..5873c2a9a5ad1 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-03-29 +date: 2024-04-02 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 db60d683f41a4..97068d9c57ceb 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-03-29 +date: 2024-04-02 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 0f77c7e436dff..65235a10fbb71 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-03-29 +date: 2024-04-02 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 221b01a287731..822b0057acff3 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-03-29 +date: 2024-04-02 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 937334bd3b8c6..11a35a2e00f35 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-03-29 +date: 2024-04-02 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 69917cba0eea1..897437871d0be 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-03-29 +date: 2024-04-02 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 eb40504828c62..03015b8ce3057 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-03-29 +date: 2024-04-02 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 e5dffafe96828..0d3f5ba24308a 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-03-29 +date: 2024-04-02 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 2ec87d170fb70..92e8036110422 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-03-29 +date: 2024-04-02 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 b56166f5d5c9c..72134b1fe5e39 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-03-29 +date: 2024-04-02 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 133feab57dcd2..ae79b543c1ae1 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-03-29 +date: 2024-04-02 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 f8ef6b6a8ee52..e79f3cd69823c 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-03-29 +date: 2024-04-02 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 105118b443b69..8e499c2fd20b0 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-03-29 +date: 2024-04-02 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 31d9a02160fdf..342aca738dc0c 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-03-29 +date: 2024-04-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'dashboardEnhanced'] --- import dashboardEnhancedObj from './dashboard_enhanced.devdocs.json'; diff --git a/api_docs/data.mdx b/api_docs/data.mdx index c8eb9b4b4fca3..d3db433dafa14 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-03-29 +date: 2024-04-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'data'] --- import dataObj from './data.devdocs.json'; diff --git a/api_docs/data_query.mdx b/api_docs/data_query.mdx index ba29ba4df2a20..a388cd0c346ee 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-03-29 +date: 2024-04-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'data.query'] --- import dataQueryObj from './data_query.devdocs.json'; diff --git a/api_docs/data_search.mdx b/api_docs/data_search.mdx index 2141efaeb5438..d8a8fbb2657b1 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-03-29 +date: 2024-04-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'data.search'] --- import dataSearchObj from './data_search.devdocs.json'; diff --git a/api_docs/data_view_editor.mdx b/api_docs/data_view_editor.mdx index 798f99b4ea186..ca9165187428a 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-03-29 +date: 2024-04-02 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 cfd3db4563747..97bb00a38e7c2 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-03-29 +date: 2024-04-02 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 12edea45db481..3dd4ecef75921 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-03-29 +date: 2024-04-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'dataViewManagement'] --- import dataViewManagementObj from './data_view_management.devdocs.json'; diff --git a/api_docs/data_views.mdx b/api_docs/data_views.mdx index 910e78f32f0db..4da9a290b7728 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-03-29 +date: 2024-04-02 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 1724ec71ef4e4..8cd24d4faf063 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-03-29 +date: 2024-04-02 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 bd864e33b8fb0..2ec18e198635c 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-03-29 +date: 2024-04-02 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 1a95873501df9..0755ce0b98be7 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-03-29 +date: 2024-04-02 tags: ['contributor', 'dev', 'apidocs', 'kibana'] --- diff --git a/api_docs/deprecations_by_plugin.mdx b/api_docs/deprecations_by_plugin.mdx index 22258bd91f0f2..10783c63f2b7c 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-03-29 +date: 2024-04-02 tags: ['contributor', 'dev', 'apidocs', 'kibana'] --- diff --git a/api_docs/deprecations_by_team.mdx b/api_docs/deprecations_by_team.mdx index 49d8223d864a6..88889623e317e 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-03-29 +date: 2024-04-02 tags: ['contributor', 'dev', 'apidocs', 'kibana'] --- diff --git a/api_docs/dev_tools.mdx b/api_docs/dev_tools.mdx index ebe320d869c01..101691abc47c7 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-03-29 +date: 2024-04-02 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 7bf8046ce657f..edb34363efa51 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-03-29 +date: 2024-04-02 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 52b6ac4bac770..589b8cc46a603 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-03-29 +date: 2024-04-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'discoverEnhanced'] --- import discoverEnhancedObj from './discover_enhanced.devdocs.json'; diff --git a/api_docs/ecs_data_quality_dashboard.mdx b/api_docs/ecs_data_quality_dashboard.mdx index 582d323bd2ebb..fc99774496b13 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-03-29 +date: 2024-04-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'ecsDataQualityDashboard'] --- import ecsDataQualityDashboardObj from './ecs_data_quality_dashboard.devdocs.json'; diff --git a/api_docs/elastic_assistant.devdocs.json b/api_docs/elastic_assistant.devdocs.json index b9377a42a87aa..7a99968afbdac 100644 --- a/api_docs/elastic_assistant.devdocs.json +++ b/api_docs/elastic_assistant.devdocs.json @@ -1499,7 +1499,7 @@ "label": "onNewReplacements", "description": [], "signature": [ - "((newReplacements: { value: string; uuid: string; }[]) => void) | undefined" + "((newReplacements: Zod.objectOutputType<{}, Zod.ZodString, \"strip\">) => void) | undefined" ], "path": "x-pack/plugins/elastic_assistant/server/types.ts", "deprecated": false, @@ -1508,12 +1508,12 @@ { "parentPluginId": "elasticAssistant", "id": "def-server.AssistantToolParams.onNewReplacements.$1", - "type": "Array", + "type": "CompoundType", "tags": [], "label": "newReplacements", "description": [], "signature": [ - "{ value: string; uuid: string; }[]" + "Zod.objectOutputType<{}, Zod.ZodString, \"strip\">" ], "path": "x-pack/plugins/elastic_assistant/server/types.ts", "deprecated": false, @@ -1526,12 +1526,12 @@ { "parentPluginId": "elasticAssistant", "id": "def-server.AssistantToolParams.replacements", - "type": "Array", + "type": "CompoundType", "tags": [], "label": "replacements", "description": [], "signature": [ - "{ value: string; uuid: string; }[] | undefined" + "Zod.objectOutputType<{}, Zod.ZodString, \"strip\"> | undefined" ], "path": "x-pack/plugins/elastic_assistant/server/types.ts", "deprecated": false, @@ -1552,7 +1552,7 @@ "section": "def-common.KibanaRequest", "text": "KibanaRequest" }, - "" + "" ], "path": "x-pack/plugins/elastic_assistant/server/types.ts", "deprecated": false, diff --git a/api_docs/elastic_assistant.mdx b/api_docs/elastic_assistant.mdx index 868b670cc1c3c..184d43e71f9ad 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-03-29 +date: 2024-04-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'elasticAssistant'] --- import elasticAssistantObj from './elastic_assistant.devdocs.json'; diff --git a/api_docs/embeddable.mdx b/api_docs/embeddable.mdx index 180ee91f8cee4..0abec445fc2c7 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-03-29 +date: 2024-04-02 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 df478d4f00875..236632a4c5540 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-03-29 +date: 2024-04-02 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 fbf834a28a783..295ee0f8b7d70 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-03-29 +date: 2024-04-02 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 ee4116f0171d8..2eb6d8c012e3a 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-03-29 +date: 2024-04-02 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 2c47ca38a4824..8e572bbb72ca0 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-03-29 +date: 2024-04-02 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 a5807395ab952..e71094ac98fae 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-03-29 +date: 2024-04-02 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 3682e1e8123cb..dd632b8f50d32 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-03-29 +date: 2024-04-02 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 05796bbee6fef..ed077c15aedf9 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-03-29 +date: 2024-04-02 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 f5dd9ee96af25..adeea6d27dbd9 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-03-29 +date: 2024-04-02 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 3b84f5480d023..9e6f89e5e165b 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-03-29 +date: 2024-04-02 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 4b0db4cf0c9a7..2bdde44513ab6 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-03-29 +date: 2024-04-02 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 2447a1728dcf8..8c0e4531a0f17 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-03-29 +date: 2024-04-02 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 1b79a4007ab34..4cef6bdb944c0 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-03-29 +date: 2024-04-02 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 129819b03d364..4ff48dba9f45d 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-03-29 +date: 2024-04-02 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 b9d619de792d3..35ed642a920be 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-03-29 +date: 2024-04-02 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 48224798f7187..0bc10d2c86aa1 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-03-29 +date: 2024-04-02 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 ed67301ebe0f7..535b50dc7a2a5 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-03-29 +date: 2024-04-02 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 bb8cc39a919eb..f81c24d7e3788 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-03-29 +date: 2024-04-02 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 6a34d7a48d9db..369c04f6c5de3 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-03-29 +date: 2024-04-02 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 a97d95ca97d39..b433f343e1d72 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-03-29 +date: 2024-04-02 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 0e3ed4d472f40..bf9950b1c836c 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-03-29 +date: 2024-04-02 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 1279195e5be89..553df1de43671 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-03-29 +date: 2024-04-02 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 3c76469cfc943..4b0f465918676 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-03-29 +date: 2024-04-02 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 0b8b76351f1b0..6d0d197c4ec9c 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-03-29 +date: 2024-04-02 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 3c9e00a9bcf1e..0c6da51bc62d7 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-03-29 +date: 2024-04-02 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 96d3a9538bf53..c00b8a36aaf9e 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-03-29 +date: 2024-04-02 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 d7e0668ce8f94..866d792cbe922 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-03-29 +date: 2024-04-02 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 a8d915f337670..5536a5a99222c 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-03-29 +date: 2024-04-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'filesManagement'] --- import filesManagementObj from './files_management.devdocs.json'; diff --git a/api_docs/fleet.mdx b/api_docs/fleet.mdx index d9310adfbec15..3faee1d60ffdc 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-03-29 +date: 2024-04-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'fleet'] --- import fleetObj from './fleet.devdocs.json'; diff --git a/api_docs/global_search.mdx b/api_docs/global_search.mdx index 62ff31bf927fb..da83c5d45488a 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-03-29 +date: 2024-04-02 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 21b9a5b474829..3639bb26e62f3 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-03-29 +date: 2024-04-02 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 27040a67e149e..a64c619d3c0e1 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-03-29 +date: 2024-04-02 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 18fa8b9bb6084..8581ba261643c 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-03-29 +date: 2024-04-02 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 a1454519ba5e7..869c4fa18eafd 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-03-29 +date: 2024-04-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'indexLifecycleManagement'] --- import indexLifecycleManagementObj from './index_lifecycle_management.devdocs.json'; diff --git a/api_docs/index_management.mdx b/api_docs/index_management.mdx index 181b94fe0861a..b5c40f9efcc7e 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-03-29 +date: 2024-04-02 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 86432edb111d5..e06bb1f03db1c 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-03-29 +date: 2024-04-02 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 4af76d34229ab..d49741d65175d 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-03-29 +date: 2024-04-02 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 36d14c5827c24..707f720ea447d 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-03-29 +date: 2024-04-02 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 aaa967104da34..74df6e526f776 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-03-29 +date: 2024-04-02 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 e7ef1deb9599a..f322ce994b8ed 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-03-29 +date: 2024-04-02 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 ae7d29aa40268..8c8158a018898 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-03-29 +date: 2024-04-02 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 6bfd65bf28fc2..ab0cfd23cd05c 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-03-29 +date: 2024-04-02 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 7d778452ba84c..b019cb755cc25 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-03-29 +date: 2024-04-02 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 909ac063dfdfb..29f7dee71be48 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-03-29 +date: 2024-04-02 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 12b494c862163..2bec052e57921 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-03-29 +date: 2024-04-02 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 3f6fa0fec0cf5..7bffe0acea2b5 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-03-29 +date: 2024-04-02 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 3ca0d0c4062c9..4c62d87911cc5 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-03-29 +date: 2024-04-02 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 7eab84dce75e3..b3988a5ec3f81 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-03-29 +date: 2024-04-02 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 9ff24899ff3eb..f76abb640f048 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-03-29 +date: 2024-04-02 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 17c4677b77080..444460e8e63c3 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-03-29 +date: 2024-04-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/analytics'] --- import kbnAnalyticsObj from './kbn_analytics.devdocs.json'; diff --git a/api_docs/kbn_analytics_client.devdocs.json b/api_docs/kbn_analytics_client.devdocs.json index b7b05824d24a3..d545e4a404f80 100644 --- a/api_docs/kbn_analytics_client.devdocs.json +++ b/api_docs/kbn_analytics_client.devdocs.json @@ -718,6 +718,10 @@ "plugin": "dashboard", "path": "src/plugins/dashboard/public/services/analytics/analytics_service.ts" }, + { + "plugin": "observabilityAIAssistant", + "path": "x-pack/plugins/observability_solution/observability_ai_assistant/server/functions/context.ts" + }, { "plugin": "observabilityAIAssistant", "path": "x-pack/plugins/observability_solution/observability_ai_assistant/public/analytics/index.ts" @@ -2006,7 +2010,7 @@ "tags": [], "label": "OptInConfig", "description": [ - "\n" + "\nOptions for the optIn API" ], "path": "packages/analytics/client/src/analytics_client/types.ts", "deprecated": false, diff --git a/api_docs/kbn_analytics_client.mdx b/api_docs/kbn_analytics_client.mdx index 8b5d52388c0ea..b1c29ff35e00f 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-03-29 +date: 2024-04-02 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 58fcea59ab012..45fab9dfd2769 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-03-29 +date: 2024-04-02 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 15775d74ead62..35bf6721c1cda 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-03-29 +date: 2024-04-02 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 c8967678af117..f1581d6419a56 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-03-29 +date: 2024-04-02 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 409676ff880d6..fdd51d7e8c417 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-03-29 +date: 2024-04-02 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 9a50b3b1e3799..7c6d42ba9352a 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-03-29 +date: 2024-04-02 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 0701e49f0176b..4a66486e6422a 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-03-29 +date: 2024-04-02 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 2d700e9e8414e..387fa104c73f0 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-03-29 +date: 2024-04-02 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 31f55d320470c..d9adc623e6dae 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-03-29 +date: 2024-04-02 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 d95a5c4a4688c..1512fdbfab978 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-03-29 +date: 2024-04-02 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 9f29cee587d84..e4d9c238e457f 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-03-29 +date: 2024-04-02 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 9d25c5da0b62d..f3e279c14c22f 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-03-29 +date: 2024-04-02 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 7e99d5e83231a..1630c3ff0e4ee 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-03-29 +date: 2024-04-02 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 da3ce945aa375..09c887bd64230 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-03-29 +date: 2024-04-02 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 4412d8bbb9c80..81c482139b9ec 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-03-29 +date: 2024-04-02 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 fafcc4c1971be..e235456e669a4 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-03-29 +date: 2024-04-02 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 d9fdbea730d56..a9739c024a894 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-03-29 +date: 2024-04-02 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 02e7356e82159..e219ca987ea43 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-03-29 +date: 2024-04-02 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 ca4d52ae32d65..fc5726f150c02 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-03-29 +date: 2024-04-02 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 09fede6de645d..1ecf6a0eebbd4 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-03-29 +date: 2024-04-02 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 3118672ae9cd2..ad384a8f79c08 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-03-29 +date: 2024-04-02 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 91ddb6fb17ed2..567e04140d39f 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-03-29 +date: 2024-04-02 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 f0982e7f84398..4ca58ed18582d 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-03-29 +date: 2024-04-02 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 0076f7f871884..b437f3100e149 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-03-29 +date: 2024-04-02 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 187572f5084a9..afbfc0e375c6c 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-03-29 +date: 2024-04-02 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 f986856928316..0e337dcaf7c58 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-03-29 +date: 2024-04-02 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 e4dedbc7c7975..3a6a7c9fbbccc 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-03-29 +date: 2024-04-02 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 fcc7d1a9a8db6..930890659d801 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-03-29 +date: 2024-04-02 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 9859524516e1e..ca0ffcb2439d0 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-03-29 +date: 2024-04-02 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 51d834612caf5..206c6d907cfed 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-03-29 +date: 2024-04-02 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 22abe6a08d24b..51b35efbb1d02 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-03-29 +date: 2024-04-02 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 48f25af93fc96..0a119aa16cfdf 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-03-29 +date: 2024-04-02 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 fbe973692dca3..21f894eff5fdc 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-03-29 +date: 2024-04-02 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 c224cdf097cb0..17a96c67469a1 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-03-29 +date: 2024-04-02 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 d456edf3a05a7..55609bfe70386 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-03-29 +date: 2024-04-02 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 fe7ebd0c9f4f4..4e2341abf7f9f 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-03-29 +date: 2024-04-02 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 a232402cf50d5..078940b489ecf 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-03-29 +date: 2024-04-02 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 729d0327f9792..f02879855f95d 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-03-29 +date: 2024-04-02 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 f5b6daa50d064..51e2a825de031 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-03-29 +date: 2024-04-02 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 36c319a4ef4ba..6e9e4a2108d24 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-03-29 +date: 2024-04-02 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 0800ce757c48c..1876f3e73bd0d 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-03-29 +date: 2024-04-02 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 e823c645024f7..3a940aff25fa5 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-03-29 +date: 2024-04-02 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 fa37d0bf0ca1b..a008c0d5318bd 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-03-29 +date: 2024-04-02 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 963897fa4c370..74a8a03389c00 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-03-29 +date: 2024-04-02 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 bde54788ee422..35df5ec08dcfc 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-03-29 +date: 2024-04-02 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 ec8672f756944..03da7b572026b 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-03-29 +date: 2024-04-02 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 cde9f8b8f5797..aabf5a2226ed5 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-03-29 +date: 2024-04-02 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 e8ea3d14b2cff..9e687fbee4caf 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-03-29 +date: 2024-04-02 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 9b0d6c3fdf3c4..a6f1501f46771 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-03-29 +date: 2024-04-02 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 ed8ae5062f8f4..16643e7f2f7f8 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-03-29 +date: 2024-04-02 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 a5412efd48680..b199f26bc695e 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-03-29 +date: 2024-04-02 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 2952827636001..1f84bd1db4017 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-03-29 +date: 2024-04-02 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 4c599dab77ccf..fc7aa3bb31953 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-03-29 +date: 2024-04-02 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 b0d041f53bdb4..dce22c1dbee51 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-03-29 +date: 2024-04-02 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 f027558f7ff36..19838ef72a1bc 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-03-29 +date: 2024-04-02 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 00ac8e3027a38..830bcf9a8edb7 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-03-29 +date: 2024-04-02 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 dc564b31aed5f..6dfe7d1a5dde7 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-03-29 +date: 2024-04-02 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 c405060b1ae19..064ebc7daed9d 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-03-29 +date: 2024-04-02 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 941794f5d751f..4eeb357f29744 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-03-29 +date: 2024-04-02 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 42311a79d670c..2a264f6d429db 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-03-29 +date: 2024-04-02 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 bee923095aaa0..54fcfe0fde3db 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-03-29 +date: 2024-04-02 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 19fe89265ff5b..647b10232b8c7 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-03-29 +date: 2024-04-02 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 54b21d17821d2..d5069c70bd30f 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-03-29 +date: 2024-04-02 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 4cdc02127691a..36d3f425f5805 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-03-29 +date: 2024-04-02 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 229a8db63ecf3..d564729253285 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-03-29 +date: 2024-04-02 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 cc5df6b0a1a03..39bdd563e0431 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-03-29 +date: 2024-04-02 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 8c380c84dce1d..61aa4c1535c9a 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-03-29 +date: 2024-04-02 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 7a779df785973..7dd7a0c16d25c 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-03-29 +date: 2024-04-02 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 ef10512b9e675..2f96104352776 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-03-29 +date: 2024-04-02 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 523a175d8d703..e025563eb2730 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-03-29 +date: 2024-04-02 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 7cdd06231d23d..78abc52c3833f 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-03-29 +date: 2024-04-02 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 025aa9cf39140..37db444a34ca1 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-03-29 +date: 2024-04-02 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 e0c2951d1d450..e275702dd2979 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-03-29 +date: 2024-04-02 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 c92c0332c235e..84b05559dbed3 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-03-29 +date: 2024-04-02 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 a222c4b3b33d8..35786cba4ee0a 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-03-29 +date: 2024-04-02 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 919835ba4c729..356381d30555f 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-03-29 +date: 2024-04-02 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 08ffa4a4f2aed..483a15fa5d7b9 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-03-29 +date: 2024-04-02 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 af226fc6b4379..dd23eb4e60c19 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-03-29 +date: 2024-04-02 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 b1948970404ef..641e297454943 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-03-29 +date: 2024-04-02 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 63b52fbb68cff..b702ab9166ede 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-03-29 +date: 2024-04-02 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 5b8883937a8f9..99529933fe363 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-03-29 +date: 2024-04-02 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 bb509f3d301f3..c7000227c5d1f 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-03-29 +date: 2024-04-02 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 6e8c171de019a..eb2889aeda878 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-03-29 +date: 2024-04-02 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 79e9566cadef3..78f1180195988 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-03-29 +date: 2024-04-02 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 2333bbf40af0f..6d128c60cd68b 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-03-29 +date: 2024-04-02 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 e5104fac683c3..6cefe8aa4f698 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-03-29 +date: 2024-04-02 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 d2bf8a04e1987..60bcff8262758 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-03-29 +date: 2024-04-02 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 5edef422346b9..ecdde9335a62c 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-03-29 +date: 2024-04-02 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 1b63c1bc3291a..62e65d9a35043 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-03-29 +date: 2024-04-02 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 1a13f92ab72ee..0bb690f541ca4 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-03-29 +date: 2024-04-02 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 6127c3a835e06..63ab3847a822e 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-03-29 +date: 2024-04-02 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 aa28b6c2e4e7a..80aa4dcbf756e 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-03-29 +date: 2024-04-02 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 8fa0e2e4d9614..ca03665c218f3 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-03-29 +date: 2024-04-02 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 71408e745abf1..774606f68ebf8 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-03-29 +date: 2024-04-02 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 29c9405f6b52c..351e29f26f9be 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-03-29 +date: 2024-04-02 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 b280f010fd458..d79d94ba9e29a 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-03-29 +date: 2024-04-02 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 2870f17cb8db7..695f6f909e5e7 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-03-29 +date: 2024-04-02 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 4d46a24b5a160..8c04cf5bc2cca 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-03-29 +date: 2024-04-02 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 0fffd878ce343..abf3fb29a8ea1 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-03-29 +date: 2024-04-02 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 457ce8f467a8f..2a7cd7c1872fb 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-03-29 +date: 2024-04-02 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 6207009c2480d..07bf4a436d783 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-03-29 +date: 2024-04-02 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 ba6f8c99d79da..04114991d7474 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-03-29 +date: 2024-04-02 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 155ca3b745b9b..b414446548c60 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-03-29 +date: 2024-04-02 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 89e66eb2d9f29..c4d466848ef97 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-03-29 +date: 2024-04-02 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 f39c67ed53a92..eda908a41e1cf 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-03-29 +date: 2024-04-02 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 a99538d959126..91ac2c7f54f63 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-03-29 +date: 2024-04-02 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 d80b7001c562b..6a9502726327f 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-03-29 +date: 2024-04-02 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 10e7ec83905d3..d18ee2108bbf6 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-03-29 +date: 2024-04-02 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 3727fd2090c5e..3c6cc33928401 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-03-29 +date: 2024-04-02 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 1435e809ea9e6..d453358329cd9 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-03-29 +date: 2024-04-02 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 05aa7897f7f89..36abb3afe0cbb 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-03-29 +date: 2024-04-02 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 f5d38ee10f792..a2fbb4f692a6f 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-03-29 +date: 2024-04-02 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 522d6c461bdfe..807ddf84c63c1 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-03-29 +date: 2024-04-02 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 2cec53d9fb586..04ed682e70975 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-03-29 +date: 2024-04-02 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 514a022ad1743..2706b1ffa74b0 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-03-29 +date: 2024-04-02 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 69b211c43e496..4399d08b11515 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-03-29 +date: 2024-04-02 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 2cb7c2f8f4d8a..b757af0f00a44 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-03-29 +date: 2024-04-02 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 bc78567e838c1..1494bfef8a88e 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-03-29 +date: 2024-04-02 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 cb0792f23eac3..b92ad5374ae6d 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-03-29 +date: 2024-04-02 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 c4e4bb4a8229d..42efc8d85db30 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-03-29 +date: 2024-04-02 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 6d4c452b772a5..db9114659fbdf 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-03-29 +date: 2024-04-02 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 9d1410fe8598c..04be04d4d34a5 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-03-29 +date: 2024-04-02 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 5e6ecd575dce9..406626cd074f5 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-03-29 +date: 2024-04-02 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 64b74bca6b91f..a1515c895c659 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-03-29 +date: 2024-04-02 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 facf1de878745..4311e3348ff76 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-03-29 +date: 2024-04-02 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 d8d7031baf3c3..0e0e10181e7e1 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-03-29 +date: 2024-04-02 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 135771aa7fc02..51e88965692ff 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-03-29 +date: 2024-04-02 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 ed4d38cc230f8..6e2b695e1b59e 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-03-29 +date: 2024-04-02 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 4a5a689ad1cd2..8632cc59a6d95 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-03-29 +date: 2024-04-02 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 c5373a4e5fa7b..8d554e81d0ef9 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-03-29 +date: 2024-04-02 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 9b73f90a94821..95c603302f085 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-03-29 +date: 2024-04-02 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 6c314a04ada90..6bcf0062fadc1 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-03-29 +date: 2024-04-02 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 6a6582b41b794..43f26352e00a6 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-03-29 +date: 2024-04-02 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 2ae65b86ad5b6..d7d3cdfa96eaf 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-03-29 +date: 2024-04-02 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 35af99e70b115..e9a4ad7cbd889 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-03-29 +date: 2024-04-02 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 fefc0d276d7ff..2b16e0e96c6a1 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-03-29 +date: 2024-04-02 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 bd790a5caa384..dc54672e7f941 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-03-29 +date: 2024-04-02 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 34c9d1d3ef8b6..3f209dfd79e97 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-03-29 +date: 2024-04-02 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 29cf5aa731a01..e11ce8f1708b8 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-03-29 +date: 2024-04-02 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 589516189295f..0dde477f28ac7 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-03-29 +date: 2024-04-02 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 f1488a14409ca..e70aa947e7916 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-03-29 +date: 2024-04-02 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 83b73a10481d5..b9ae103463375 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-03-29 +date: 2024-04-02 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 e393d974d9942..609cd97c1e92c 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-03-29 +date: 2024-04-02 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 47636c9aa38ac..7f6b502d7106c 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-03-29 +date: 2024-04-02 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 d850008297d2c..e566627a33b1b 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-03-29 +date: 2024-04-02 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 ec34a88489564..261b4b85263de 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-03-29 +date: 2024-04-02 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 1e13bdd7d04e6..e966ab7fc14c1 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-03-29 +date: 2024-04-02 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 7640505b06a9d..645ed71158a6f 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-03-29 +date: 2024-04-02 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 86260ed944575..e2bf482e01e9e 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-03-29 +date: 2024-04-02 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 67b5c80a5a1df..9e1e518b5aaf5 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-03-29 +date: 2024-04-02 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 f65200e56e764..af737dd0586bf 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-03-29 +date: 2024-04-02 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 c0dfabdc7782d..9a921e384b034 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-03-29 +date: 2024-04-02 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 fbc23c8a7fba4..674e6f0228c34 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-03-29 +date: 2024-04-02 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 24e681a697c66..cb66819eb4141 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-03-29 +date: 2024-04-02 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 a45365611b74f..7c8e83c9985c6 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-03-29 +date: 2024-04-02 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 bbdeab598a3e4..671f4a5fa710d 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-03-29 +date: 2024-04-02 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 14940fa1a99e2..e14796d96be3f 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-03-29 +date: 2024-04-02 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 1915d752e9b4f..be4f0d7f2fa50 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-03-29 +date: 2024-04-02 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 ee882491b042a..7d830b86d7efd 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-03-29 +date: 2024-04-02 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 5cbbb88d1481d..607935714c7e4 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-03-29 +date: 2024-04-02 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 3f25c127f64d8..8c3e514ebab6e 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-03-29 +date: 2024-04-02 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 7316734ea7273..78af3f0996da6 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-03-29 +date: 2024-04-02 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 ec5d25e737943..840ef176c02bb 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-03-29 +date: 2024-04-02 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 25ae8dcf6d4d9..0a22c0e02ce36 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-03-29 +date: 2024-04-02 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 0de4ca12201b2..c847840ef62cf 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-03-29 +date: 2024-04-02 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 734b8def9e4d5..6aa3ccb1ece1b 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-03-29 +date: 2024-04-02 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 9ffd08109386e..262a18d72b47a 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-03-29 +date: 2024-04-02 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 9a80ba4d120dd..dd300a769f493 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-03-29 +date: 2024-04-02 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 af0fa984939e2..2fc92ef4199b8 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-03-29 +date: 2024-04-02 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 a08dd03e6584a..fbeb64b47b6f1 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-03-29 +date: 2024-04-02 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 e1d31a2e3a11f..12d19b338c42d 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-03-29 +date: 2024-04-02 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 41a0054961a7a..dc03ad29fbb23 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-03-29 +date: 2024-04-02 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 2b1c580720769..7c553bc634d37 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-03-29 +date: 2024-04-02 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 fc07f93611cdb..a8dc1025b788a 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-03-29 +date: 2024-04-02 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 aa89e3587e74e..f3f95c8cc035d 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-03-29 +date: 2024-04-02 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 b6d31ca515b94..4c81e6cd11856 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-03-29 +date: 2024-04-02 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 1c45bd106c74b..a3c91921006fb 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-03-29 +date: 2024-04-02 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 e9717f26af7ae..75ee5b4fe6c11 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-03-29 +date: 2024-04-02 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 a214c246199dc..94a4e3fc40cdb 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-03-29 +date: 2024-04-02 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 4986ca405f362..9979bb996fc12 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-03-29 +date: 2024-04-02 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 d5d63bc58599e..91600ade0108d 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-03-29 +date: 2024-04-02 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 48c43f101f248..fbdea73d4de0a 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-03-29 +date: 2024-04-02 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 1b80494474cce..70fc29754f1a6 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-03-29 +date: 2024-04-02 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 55e0025fdea5e..018b1d7e06bd4 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-03-29 +date: 2024-04-02 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 8cc11ec0299f7..134b8f6aa7b21 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-03-29 +date: 2024-04-02 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 498ce321d65a5..994a21c026e9a 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-03-29 +date: 2024-04-02 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 5c786f25248fe..6c9b0bdb4713c 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-03-29 +date: 2024-04-02 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 14865007c019b..417a16b8f5ba6 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-03-29 +date: 2024-04-02 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 39bba00eee5ae..4c7a53c550ad7 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-03-29 +date: 2024-04-02 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 ff3b77a2b74e6..a8f707262cbf7 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-03-29 +date: 2024-04-02 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 fd23e3462464d..cf6ebd9361fe7 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-03-29 +date: 2024-04-02 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 90b169ebf2767..4265fbc95dbea 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-03-29 +date: 2024-04-02 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 d88695a69161a..458517148b5b3 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-03-29 +date: 2024-04-02 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 35640d97e9488..479e35e56295d 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-03-29 +date: 2024-04-02 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 93bc4145eabd2..cdb3331b2a1b3 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-03-29 +date: 2024-04-02 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 4f1b2a3c99ea1..8e5e3e2c7ead3 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-03-29 +date: 2024-04-02 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 13d3aab3a2534..3ad8fddfb0498 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-03-29 +date: 2024-04-02 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 8619de76ff3f3..4ac1a91e89187 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-03-29 +date: 2024-04-02 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 8261cb6476eec..62f3ee2cd78b2 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-03-29 +date: 2024-04-02 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_settings_server.mdx b/api_docs/kbn_core_user_settings_server.mdx index 6cad6f9a48546..fd38f2c762426 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-03-29 +date: 2024-04-02 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_internal.mdx b/api_docs/kbn_core_user_settings_server_internal.mdx index 7ddd6800248ae..b11900a402b7e 100644 --- a/api_docs/kbn_core_user_settings_server_internal.mdx +++ b/api_docs/kbn_core_user_settings_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-user-settings-server-internal title: "@kbn/core-user-settings-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-user-settings-server-internal plugin -date: 2024-03-29 +date: 2024-04-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-user-settings-server-internal'] --- import kbnCoreUserSettingsServerInternalObj from './kbn_core_user_settings_server_internal.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 04f0ee7668689..15ef1e4715a28 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-03-29 +date: 2024-04-02 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 c0b0506b23a86..56088dd86c092 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-03-29 +date: 2024-04-02 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 5979cb864a5e9..3b34fbeea4952 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-03-29 +date: 2024-04-02 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 9bbcfb230802b..8d727a51047f6 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-03-29 +date: 2024-04-02 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 b513e0aa9d406..09da776b6b1a7 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-03-29 +date: 2024-04-02 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 98eda18e68c35..1c72b505bf151 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-03-29 +date: 2024-04-02 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 8f3873348e792..87ed4480b1ede 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-03-29 +date: 2024-04-02 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 196fd0dd5698b..de631656f8daa 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-03-29 +date: 2024-04-02 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 cc86e2e592694..a9b1cce4d8825 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-03-29 +date: 2024-04-02 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 60991b0a3d6b0..7dbefe2347c68 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-03-29 +date: 2024-04-02 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 60668e976c30a..690edfb682c0b 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-03-29 +date: 2024-04-02 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 680d5042448ac..8892b453d5a4a 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-03-29 +date: 2024-04-02 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 1af012814ac98..c6f1d233cce24 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-03-29 +date: 2024-04-02 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 6b09542d8df80..714df593c3ddb 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-03-29 +date: 2024-04-02 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 748e87302bce8..e044e7f27a2f4 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-03-29 +date: 2024-04-02 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 3948bf37b3677..926a0ff6d1aad 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-03-29 +date: 2024-04-02 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 332f39b98826b..00af00d6c1576 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-03-29 +date: 2024-04-02 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 83671cf24950a..a3c00cc2ecd1d 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-03-29 +date: 2024-04-02 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 99e50a5f9c267..e99d45fa7ea6c 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-03-29 +date: 2024-04-02 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 eca9d1322adc0..cba16e6a38cda 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-03-29 +date: 2024-04-02 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 f4a0e188ca522..59f9635aac38a 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-03-29 +date: 2024-04-02 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 c4f856cee49de..c3849979490a8 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-03-29 +date: 2024-04-02 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 2180295b18b33..ac7667ddf0b93 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-03-29 +date: 2024-04-02 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 a7ff373337e3b..4cf7c01f06ddf 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-03-29 +date: 2024-04-02 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 ecf5bb827888d..6588ecada0883 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-03-29 +date: 2024-04-02 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 d6e4288490e1b..dad9fe02cd564 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-03-29 +date: 2024-04-02 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 9ef1ca387b1e9..e46658a3a14cb 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-03-29 +date: 2024-04-02 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 dcc15dc8fe82b..7ce231db11210 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-03-29 +date: 2024-04-02 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 9fe77dda909e7..c1f8dc1cfea26 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-03-29 +date: 2024-04-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/discover-utils'] --- import kbnDiscoverUtilsObj from './kbn_discover_utils.devdocs.json'; diff --git a/api_docs/kbn_doc_links.devdocs.json b/api_docs/kbn_doc_links.devdocs.json index 963afbd73f71a..fe5c044505ad8 100644 --- a/api_docs/kbn_doc_links.devdocs.json +++ b/api_docs/kbn_doc_links.devdocs.json @@ -300,7 +300,7 @@ "label": "enterpriseSearch", "description": [], "signature": [ - "{ readonly aiSearchDoc: string; readonly aiSearchHelp: string; readonly apiKeys: string; readonly behavioralAnalytics: string; readonly behavioralAnalyticsCORS: string; readonly behavioralAnalyticsEvents: string; readonly buildConnector: string; readonly bulkApi: string; readonly configuration: string; readonly connectors: string; readonly connectorsAzureBlobStorage: string; readonly connectorsBox: string; readonly connectorsClients: string; readonly connectorsConfluence: string; readonly connectorsContentExtraction: string; readonly connectorsDropbox: string; readonly connectorsGithub: string; readonly connectorsGoogleCloudStorage: string; readonly connectorsGoogleDrive: string; readonly connectorsGmail: string; readonly connectorsJira: string; readonly connectorsMicrosoftSQL: string; readonly connectorsMongoDB: string; readonly connectorsMySQL: string; readonly connectorsNative: string; readonly connectorsNetworkDrive: string; readonly connectorsNotion: string; readonly connectorsOneDrive: string; readonly connectorsOracle: string; readonly connectorsOutlook: string; readonly connectorsPostgreSQL: string; readonly connectorsRedis: string; readonly connectorsS3: string; readonly connectorsSalesforce: string; readonly connectorsServiceNow: string; readonly connectorsSharepoint: string; readonly connectorsSharepointOnline: string; readonly connectorsTeams: string; readonly connectorsSlack: string; readonly connectorsZoom: string; readonly crawlerExtractionRules: string; readonly crawlerManaging: string; readonly crawlerOverview: string; readonly deployTrainedModels: string; readonly documentLevelSecurity: string; readonly elser: string; readonly engines: string; readonly indexApi: string; readonly ingestionApis: string; readonly ingestPipelines: string; readonly knnSearch: string; readonly knnSearchCombine: string; readonly languageAnalyzers: string; readonly languageClients: string; readonly licenseManagement: string; readonly machineLearningStart: string; readonly mailService: string; readonly mlDocumentEnrichment: string; readonly searchApplicationsTemplates: string; readonly searchApplicationsSearchApi: string; readonly searchApplications: string; readonly searchApplicationsSearch: string; readonly searchLabs: string; readonly searchLabsRepo: string; readonly searchTemplates: string; readonly start: string; readonly supportedNlpModels: string; readonly syncRules: string; readonly trainedModels: string; readonly textEmbedding: string; readonly troubleshootSetup: string; readonly usersAccess: string; }" + "{ readonly aiSearchDoc: string; readonly aiSearchHelp: string; readonly apiKeys: string; readonly behavioralAnalytics: string; readonly behavioralAnalyticsCORS: string; readonly behavioralAnalyticsEvents: string; readonly buildConnector: string; readonly bulkApi: string; readonly configuration: string; readonly connectors: string; readonly connectorsMappings: string; readonly connectorsAzureBlobStorage: string; readonly connectorsBox: string; readonly connectorsClients: string; readonly connectorsConfluence: string; readonly connectorsContentExtraction: string; readonly connectorsDropbox: string; readonly connectorsGithub: string; readonly connectorsGoogleCloudStorage: string; readonly connectorsGoogleDrive: string; readonly connectorsGmail: string; readonly connectorsJira: string; readonly connectorsMicrosoftSQL: string; readonly connectorsMongoDB: string; readonly connectorsMySQL: string; readonly connectorsNative: string; readonly connectorsNetworkDrive: string; readonly connectorsNotion: string; readonly connectorsOneDrive: string; readonly connectorsOracle: string; readonly connectorsOutlook: string; readonly connectorsPostgreSQL: string; readonly connectorsRedis: string; readonly connectorsS3: string; readonly connectorsSalesforce: string; readonly connectorsServiceNow: string; readonly connectorsSharepoint: string; readonly connectorsSharepointOnline: string; readonly connectorsTeams: string; readonly connectorsSlack: string; readonly connectorsZoom: string; readonly crawlerExtractionRules: string; readonly crawlerManaging: string; readonly crawlerOverview: string; readonly deployTrainedModels: string; readonly documentLevelSecurity: string; readonly elser: string; readonly engines: string; readonly indexApi: string; readonly ingestionApis: string; readonly ingestPipelines: string; readonly knnSearch: string; readonly knnSearchCombine: string; readonly languageAnalyzers: string; readonly languageClients: string; readonly licenseManagement: string; readonly machineLearningStart: string; readonly mailService: string; readonly mlDocumentEnrichment: string; readonly searchApplicationsTemplates: string; readonly searchApplicationsSearchApi: string; readonly searchApplications: string; readonly searchApplicationsSearch: string; readonly searchLabs: string; readonly searchLabsRepo: string; readonly searchTemplates: string; readonly start: string; readonly supportedNlpModels: string; readonly syncRules: string; readonly trainedModels: string; readonly textEmbedding: string; readonly troubleshootSetup: string; readonly usersAccess: string; }" ], "path": "packages/kbn-doc-links/src/types.ts", "deprecated": false, diff --git a/api_docs/kbn_doc_links.mdx b/api_docs/kbn_doc_links.mdx index 8cd36acfcdca0..cff17854e89a1 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-03-29 +date: 2024-04-02 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 86d33a569cc14..1482e7eaabfe8 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-03-29 +date: 2024-04-02 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 7963073720cc8..38e83d2a27fff 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-03-29 +date: 2024-04-02 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 facc13b0a2a30..b6d45a087c189 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-03-29 +date: 2024-04-02 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 28ca2120149e0..5198b9e195bee 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-03-29 +date: 2024-04-02 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 5f8138d951014..354fd0f4fd897 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-03-29 +date: 2024-04-02 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.devdocs.json b/api_docs/kbn_elastic_assistant.devdocs.json index ee1e394a5a2ef..85eecc8a70c85 100644 --- a/api_docs/kbn_elastic_assistant.devdocs.json +++ b/api_docs/kbn_elastic_assistant.devdocs.json @@ -1611,12 +1611,12 @@ { "parentPluginId": "@kbn/elastic-assistant", "id": "def-public.Conversation.replacements", - "type": "Array", + "type": "CompoundType", "tags": [], "label": "replacements", "description": [], "signature": [ - "{ value: string; uuid: string; }[]" + "{} & { [k: string]: string; }" ], "path": "x-pack/packages/kbn-elastic-assistant/impl/assistant_context/types.tsx", "deprecated": false, @@ -1930,12 +1930,12 @@ { "parentPluginId": "@kbn/elastic-assistant", "id": "def-public.Message.replacements", - "type": "Array", + "type": "CompoundType", "tags": [], "label": "replacements", "description": [], "signature": [ - "{ value: string; uuid: string; }[] | undefined" + "Zod.objectOutputType<{}, Zod.ZodString, \"strip\"> | undefined" ], "path": "x-pack/packages/kbn-elastic-assistant/impl/assistant_context/types.tsx", "deprecated": false, diff --git a/api_docs/kbn_elastic_assistant.mdx b/api_docs/kbn_elastic_assistant.mdx index fedd5477eb305..d618b77256805 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-03-29 +date: 2024-04-02 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.devdocs.json b/api_docs/kbn_elastic_assistant_common.devdocs.json index beede79e1ad88..3f88d87da04e5 100644 --- a/api_docs/kbn_elastic_assistant_common.devdocs.json +++ b/api_docs/kbn_elastic_assistant_common.devdocs.json @@ -408,7 +408,7 @@ "label": "replaceAnonymizedValuesWithOriginalValues", "description": [], "signature": [ - "({ messageContent, replacements, }: { messageContent: string; replacements: { value: string; uuid: string; }[]; }) => string" + "({ messageContent, replacements, }: { messageContent: string; replacements: Zod.objectOutputType<{}, Zod.ZodString, \"strip\">; }) => string" ], "path": "x-pack/packages/kbn-elastic-assistant-common/impl/data_anonymization/helpers/index.ts", "deprecated": false, @@ -439,12 +439,12 @@ { "parentPluginId": "@kbn/elastic-assistant-common", "id": "def-common.replaceAnonymizedValuesWithOriginalValues.$1.replacements", - "type": "Array", + "type": "CompoundType", "tags": [], "label": "replacements", "description": [], "signature": [ - "{ value: string; uuid: string; }[]" + "{} & { [k: string]: string; }" ], "path": "x-pack/packages/kbn-elastic-assistant-common/impl/data_anonymization/helpers/index.ts", "deprecated": false, @@ -464,7 +464,7 @@ "label": "replaceOriginalValuesWithUuidValues", "description": [], "signature": [ - "({ messageContent, replacements, }: { messageContent: string; replacements: { value: string; uuid: string; }[]; }) => string" + "({ messageContent, replacements, }: { messageContent: string; replacements: Zod.objectOutputType<{}, Zod.ZodString, \"strip\">; }) => string" ], "path": "x-pack/packages/kbn-elastic-assistant-common/impl/data_anonymization/helpers/index.ts", "deprecated": false, @@ -495,12 +495,12 @@ { "parentPluginId": "@kbn/elastic-assistant-common", "id": "def-common.replaceOriginalValuesWithUuidValues.$1.replacements", - "type": "Array", + "type": "CompoundType", "tags": [], "label": "replacements", "description": [], "signature": [ - "{ value: string; uuid: string; }[]" + "{} & { [k: string]: string; }" ], "path": "x-pack/packages/kbn-elastic-assistant-common/impl/data_anonymization/helpers/index.ts", "deprecated": false, @@ -520,7 +520,7 @@ "label": "transformRawData", "description": [], "signature": [ - "({ allow, allowReplacement, currentReplacements, getAnonymizedValue, onNewReplacements, rawData, }: { allow: string[]; allowReplacement: string[]; currentReplacements: { value: string; uuid: string; }[] | undefined; getAnonymizedValue: ({ currentReplacements, rawValue, }: { currentReplacements: Record | undefined; rawValue: string; }) => string; onNewReplacements?: ((replacements: { value: string; uuid: string; }[]) => void) | undefined; rawData: string | Record; }) => string" + "({ allow, allowReplacement, currentReplacements, getAnonymizedValue, onNewReplacements, rawData, }: { allow: string[]; allowReplacement: string[]; currentReplacements: Zod.objectOutputType<{}, Zod.ZodString, \"strip\"> | undefined; getAnonymizedValue: ({ currentReplacements, rawValue, }: { currentReplacements: Record | undefined; rawValue: string; }) => string; onNewReplacements?: ((replacements: Zod.objectOutputType<{}, Zod.ZodString, \"strip\">) => void) | undefined; rawData: string | Record; }) => string" ], "path": "x-pack/packages/kbn-elastic-assistant-common/impl/data_anonymization/transform_raw_data/index.tsx", "deprecated": false, @@ -568,12 +568,12 @@ { "parentPluginId": "@kbn/elastic-assistant-common", "id": "def-common.transformRawData.$1.currentReplacements", - "type": "Array", + "type": "CompoundType", "tags": [], "label": "currentReplacements", "description": [], "signature": [ - "{ value: string; uuid: string; }[] | undefined" + "Zod.objectOutputType<{}, Zod.ZodString, \"strip\"> | undefined" ], "path": "x-pack/packages/kbn-elastic-assistant-common/impl/data_anonymization/transform_raw_data/index.tsx", "deprecated": false, @@ -642,7 +642,7 @@ "label": "onNewReplacements", "description": [], "signature": [ - "((replacements: { value: string; uuid: string; }[]) => void) | undefined" + "((replacements: Zod.objectOutputType<{}, Zod.ZodString, \"strip\">) => void) | undefined" ], "path": "x-pack/packages/kbn-elastic-assistant-common/impl/data_anonymization/transform_raw_data/index.tsx", "deprecated": false, @@ -651,12 +651,12 @@ { "parentPluginId": "@kbn/elastic-assistant-common", "id": "def-common.transformRawData.$1.onNewReplacements.$1", - "type": "Array", + "type": "CompoundType", "tags": [], "label": "replacements", "description": [], "signature": [ - "{ value: string; uuid: string; }[]" + "Zod.objectOutputType<{}, Zod.ZodString, \"strip\">" ], "path": "x-pack/packages/kbn-elastic-assistant-common/impl/data_anonymization/transform_raw_data/index.tsx", "deprecated": false, @@ -773,7 +773,7 @@ "label": "AppendConversationMessageResponse", "description": [], "signature": [ - "{ id: string; namespace: string; title: string; createdAt: string; category: \"assistant\" | \"insights\"; users: { id?: string | undefined; name?: string | undefined; }[]; summary?: { content?: string | undefined; timestamp?: string | undefined; public?: boolean | undefined; confidence?: \"medium\" | \"low\" | \"high\" | undefined; } | undefined; timestamp?: string | undefined; updatedAt?: string | undefined; replacements?: { value: string; uuid: string; }[] | undefined; messages?: { timestamp: string; role: \"user\" | \"system\" | \"assistant\"; content: string; reader?: Zod.objectOutputType<{}, Zod.ZodUnknown, \"strip\"> | undefined; isError?: boolean | undefined; traceData?: { transactionId?: string | undefined; traceId?: string | undefined; } | undefined; }[] | undefined; apiConfig?: { connectorId: string; defaultSystemPromptId?: string | undefined; provider?: \"OpenAI\" | \"Azure OpenAI\" | undefined; model?: string | undefined; } | undefined; isDefault?: boolean | undefined; excludeFromLastConversationStorage?: boolean | undefined; }" + "{ id: string; namespace: string; title: string; createdAt: string; category: \"assistant\" | \"insights\"; users: { id?: string | undefined; name?: string | undefined; }[]; summary?: { content?: string | undefined; timestamp?: string | undefined; public?: boolean | undefined; confidence?: \"medium\" | \"low\" | \"high\" | undefined; } | undefined; timestamp?: string | undefined; updatedAt?: string | undefined; replacements?: Zod.objectOutputType<{}, Zod.ZodString, \"strip\"> | undefined; messages?: { timestamp: string; role: \"user\" | \"system\" | \"assistant\"; content: string; reader?: Zod.objectOutputType<{}, Zod.ZodUnknown, \"strip\"> | undefined; isError?: boolean | undefined; traceData?: { transactionId?: string | undefined; traceId?: string | undefined; } | undefined; }[] | undefined; apiConfig?: { connectorId: string; defaultSystemPromptId?: string | undefined; provider?: \"OpenAI\" | \"Azure OpenAI\" | undefined; model?: string | undefined; } | undefined; isDefault?: boolean | undefined; excludeFromLastConversationStorage?: boolean | undefined; }" ], "path": "x-pack/packages/kbn-elastic-assistant-common/impl/schemas/conversations/crud_conversation_route.gen.ts", "deprecated": false, @@ -850,7 +850,7 @@ "label": "BulkCrudActionResponse", "description": [], "signature": [ - "{ attributes: { results: { created: { id: string; namespace: string; title: string; createdAt: string; category: \"assistant\" | \"insights\"; users: { id?: string | undefined; name?: string | undefined; }[]; summary?: { content?: string | undefined; timestamp?: string | undefined; public?: boolean | undefined; confidence?: \"medium\" | \"low\" | \"high\" | undefined; } | undefined; timestamp?: string | undefined; updatedAt?: string | undefined; replacements?: { value: string; uuid: string; }[] | undefined; messages?: { timestamp: string; role: \"user\" | \"system\" | \"assistant\"; content: string; reader?: Zod.objectOutputType<{}, Zod.ZodUnknown, \"strip\"> | undefined; isError?: boolean | undefined; traceData?: { transactionId?: string | undefined; traceId?: string | undefined; } | undefined; }[] | undefined; apiConfig?: { connectorId: string; defaultSystemPromptId?: string | undefined; provider?: \"OpenAI\" | \"Azure OpenAI\" | undefined; model?: string | undefined; } | undefined; isDefault?: boolean | undefined; excludeFromLastConversationStorage?: boolean | undefined; }[]; updated: { id: string; namespace: string; title: string; createdAt: string; category: \"assistant\" | \"insights\"; users: { id?: string | undefined; name?: string | undefined; }[]; summary?: { content?: string | undefined; timestamp?: string | undefined; public?: boolean | undefined; confidence?: \"medium\" | \"low\" | \"high\" | undefined; } | undefined; timestamp?: string | undefined; updatedAt?: string | undefined; replacements?: { value: string; uuid: string; }[] | undefined; messages?: { timestamp: string; role: \"user\" | \"system\" | \"assistant\"; content: string; reader?: Zod.objectOutputType<{}, Zod.ZodUnknown, \"strip\"> | undefined; isError?: boolean | undefined; traceData?: { transactionId?: string | undefined; traceId?: string | undefined; } | undefined; }[] | undefined; apiConfig?: { connectorId: string; defaultSystemPromptId?: string | undefined; provider?: \"OpenAI\" | \"Azure OpenAI\" | undefined; model?: string | undefined; } | undefined; isDefault?: boolean | undefined; excludeFromLastConversationStorage?: boolean | undefined; }[]; skipped: { id: string; skip_reason: \"CONVERSATION_NOT_MODIFIED\"; name?: string | undefined; }[]; deleted: string[]; }; summary: { total: number; succeeded: number; failed: number; skipped: number; }; errors?: { message: string; status_code: number; conversations: { id: string; name?: string | undefined; }[]; err_code?: string | undefined; }[] | undefined; }; success?: boolean | undefined; status_code?: number | undefined; message?: string | undefined; conversations_count?: number | undefined; }" + "{ attributes: { results: { created: { id: string; namespace: string; title: string; createdAt: string; category: \"assistant\" | \"insights\"; users: { id?: string | undefined; name?: string | undefined; }[]; summary?: { content?: string | undefined; timestamp?: string | undefined; public?: boolean | undefined; confidence?: \"medium\" | \"low\" | \"high\" | undefined; } | undefined; timestamp?: string | undefined; updatedAt?: string | undefined; replacements?: Zod.objectOutputType<{}, Zod.ZodString, \"strip\"> | undefined; messages?: { timestamp: string; role: \"user\" | \"system\" | \"assistant\"; content: string; reader?: Zod.objectOutputType<{}, Zod.ZodUnknown, \"strip\"> | undefined; isError?: boolean | undefined; traceData?: { transactionId?: string | undefined; traceId?: string | undefined; } | undefined; }[] | undefined; apiConfig?: { connectorId: string; defaultSystemPromptId?: string | undefined; provider?: \"OpenAI\" | \"Azure OpenAI\" | undefined; model?: string | undefined; } | undefined; isDefault?: boolean | undefined; excludeFromLastConversationStorage?: boolean | undefined; }[]; updated: { id: string; namespace: string; title: string; createdAt: string; category: \"assistant\" | \"insights\"; users: { id?: string | undefined; name?: string | undefined; }[]; summary?: { content?: string | undefined; timestamp?: string | undefined; public?: boolean | undefined; confidence?: \"medium\" | \"low\" | \"high\" | undefined; } | undefined; timestamp?: string | undefined; updatedAt?: string | undefined; replacements?: Zod.objectOutputType<{}, Zod.ZodString, \"strip\"> | undefined; messages?: { timestamp: string; role: \"user\" | \"system\" | \"assistant\"; content: string; reader?: Zod.objectOutputType<{}, Zod.ZodUnknown, \"strip\"> | undefined; isError?: boolean | undefined; traceData?: { transactionId?: string | undefined; traceId?: string | undefined; } | undefined; }[] | undefined; apiConfig?: { connectorId: string; defaultSystemPromptId?: string | undefined; provider?: \"OpenAI\" | \"Azure OpenAI\" | undefined; model?: string | undefined; } | undefined; isDefault?: boolean | undefined; excludeFromLastConversationStorage?: boolean | undefined; }[]; skipped: { id: string; skip_reason: \"CONVERSATION_NOT_MODIFIED\"; name?: string | undefined; }[]; deleted: string[]; }; summary: { total: number; succeeded: number; failed: number; skipped: number; }; errors?: { message: string; status_code: number; conversations: { id: string; name?: string | undefined; }[]; err_code?: string | undefined; }[] | undefined; }; success?: boolean | undefined; status_code?: number | undefined; message?: string | undefined; conversations_count?: number | undefined; }" ], "path": "x-pack/packages/kbn-elastic-assistant-common/impl/schemas/conversations/bulk_crud_conversations_route.gen.ts", "deprecated": false, @@ -865,7 +865,7 @@ "label": "BulkCrudActionResults", "description": [], "signature": [ - "{ created: { id: string; namespace: string; title: string; createdAt: string; category: \"assistant\" | \"insights\"; users: { id?: string | undefined; name?: string | undefined; }[]; summary?: { content?: string | undefined; timestamp?: string | undefined; public?: boolean | undefined; confidence?: \"medium\" | \"low\" | \"high\" | undefined; } | undefined; timestamp?: string | undefined; updatedAt?: string | undefined; replacements?: { value: string; uuid: string; }[] | undefined; messages?: { timestamp: string; role: \"user\" | \"system\" | \"assistant\"; content: string; reader?: Zod.objectOutputType<{}, Zod.ZodUnknown, \"strip\"> | undefined; isError?: boolean | undefined; traceData?: { transactionId?: string | undefined; traceId?: string | undefined; } | undefined; }[] | undefined; apiConfig?: { connectorId: string; defaultSystemPromptId?: string | undefined; provider?: \"OpenAI\" | \"Azure OpenAI\" | undefined; model?: string | undefined; } | undefined; isDefault?: boolean | undefined; excludeFromLastConversationStorage?: boolean | undefined; }[]; updated: { id: string; namespace: string; title: string; createdAt: string; category: \"assistant\" | \"insights\"; users: { id?: string | undefined; name?: string | undefined; }[]; summary?: { content?: string | undefined; timestamp?: string | undefined; public?: boolean | undefined; confidence?: \"medium\" | \"low\" | \"high\" | undefined; } | undefined; timestamp?: string | undefined; updatedAt?: string | undefined; replacements?: { value: string; uuid: string; }[] | undefined; messages?: { timestamp: string; role: \"user\" | \"system\" | \"assistant\"; content: string; reader?: Zod.objectOutputType<{}, Zod.ZodUnknown, \"strip\"> | undefined; isError?: boolean | undefined; traceData?: { transactionId?: string | undefined; traceId?: string | undefined; } | undefined; }[] | undefined; apiConfig?: { connectorId: string; defaultSystemPromptId?: string | undefined; provider?: \"OpenAI\" | \"Azure OpenAI\" | undefined; model?: string | undefined; } | undefined; isDefault?: boolean | undefined; excludeFromLastConversationStorage?: boolean | undefined; }[]; skipped: { id: string; skip_reason: \"CONVERSATION_NOT_MODIFIED\"; name?: string | undefined; }[]; deleted: string[]; }" + "{ created: { id: string; namespace: string; title: string; createdAt: string; category: \"assistant\" | \"insights\"; users: { id?: string | undefined; name?: string | undefined; }[]; summary?: { content?: string | undefined; timestamp?: string | undefined; public?: boolean | undefined; confidence?: \"medium\" | \"low\" | \"high\" | undefined; } | undefined; timestamp?: string | undefined; updatedAt?: string | undefined; replacements?: Zod.objectOutputType<{}, Zod.ZodString, \"strip\"> | undefined; messages?: { timestamp: string; role: \"user\" | \"system\" | \"assistant\"; content: string; reader?: Zod.objectOutputType<{}, Zod.ZodUnknown, \"strip\"> | undefined; isError?: boolean | undefined; traceData?: { transactionId?: string | undefined; traceId?: string | undefined; } | undefined; }[] | undefined; apiConfig?: { connectorId: string; defaultSystemPromptId?: string | undefined; provider?: \"OpenAI\" | \"Azure OpenAI\" | undefined; model?: string | undefined; } | undefined; isDefault?: boolean | undefined; excludeFromLastConversationStorage?: boolean | undefined; }[]; updated: { id: string; namespace: string; title: string; createdAt: string; category: \"assistant\" | \"insights\"; users: { id?: string | undefined; name?: string | undefined; }[]; summary?: { content?: string | undefined; timestamp?: string | undefined; public?: boolean | undefined; confidence?: \"medium\" | \"low\" | \"high\" | undefined; } | undefined; timestamp?: string | undefined; updatedAt?: string | undefined; replacements?: Zod.objectOutputType<{}, Zod.ZodString, \"strip\"> | undefined; messages?: { timestamp: string; role: \"user\" | \"system\" | \"assistant\"; content: string; reader?: Zod.objectOutputType<{}, Zod.ZodUnknown, \"strip\"> | undefined; isError?: boolean | undefined; traceData?: { transactionId?: string | undefined; traceId?: string | undefined; } | undefined; }[] | undefined; apiConfig?: { connectorId: string; defaultSystemPromptId?: string | undefined; provider?: \"OpenAI\" | \"Azure OpenAI\" | undefined; model?: string | undefined; } | undefined; isDefault?: boolean | undefined; excludeFromLastConversationStorage?: boolean | undefined; }[]; skipped: { id: string; skip_reason: \"CONVERSATION_NOT_MODIFIED\"; name?: string | undefined; }[]; deleted: string[]; }" ], "path": "x-pack/packages/kbn-elastic-assistant-common/impl/schemas/conversations/bulk_crud_conversations_route.gen.ts", "deprecated": false, @@ -959,7 +959,7 @@ "label": "ConversationCreateProps", "description": [], "signature": [ - "{ title: string; category?: \"assistant\" | \"insights\" | undefined; messages?: { timestamp: string; role: \"user\" | \"system\" | \"assistant\"; content: string; reader?: Zod.objectOutputType<{}, Zod.ZodUnknown, \"strip\"> | undefined; isError?: boolean | undefined; traceData?: { transactionId?: string | undefined; traceId?: string | undefined; } | undefined; }[] | undefined; apiConfig?: { connectorId: string; defaultSystemPromptId?: string | undefined; provider?: \"OpenAI\" | \"Azure OpenAI\" | undefined; model?: string | undefined; } | undefined; isDefault?: boolean | undefined; excludeFromLastConversationStorage?: boolean | undefined; replacements?: { value: string; uuid: string; }[] | undefined; }" + "{ title: string; category?: \"assistant\" | \"insights\" | undefined; messages?: { timestamp: string; role: \"user\" | \"system\" | \"assistant\"; content: string; reader?: Zod.objectOutputType<{}, Zod.ZodUnknown, \"strip\"> | undefined; isError?: boolean | undefined; traceData?: { transactionId?: string | undefined; traceId?: string | undefined; } | undefined; }[] | undefined; apiConfig?: { connectorId: string; defaultSystemPromptId?: string | undefined; provider?: \"OpenAI\" | \"Azure OpenAI\" | undefined; model?: string | undefined; } | undefined; isDefault?: boolean | undefined; excludeFromLastConversationStorage?: boolean | undefined; replacements?: Zod.objectOutputType<{}, Zod.ZodString, \"strip\"> | undefined; }" ], "path": "x-pack/packages/kbn-elastic-assistant-common/impl/schemas/conversations/common_attributes.gen.ts", "deprecated": false, @@ -1004,7 +1004,7 @@ "label": "ConversationResponse", "description": [], "signature": [ - "{ id: string; namespace: string; title: string; createdAt: string; category: \"assistant\" | \"insights\"; users: { id?: string | undefined; name?: string | undefined; }[]; summary?: { content?: string | undefined; timestamp?: string | undefined; public?: boolean | undefined; confidence?: \"medium\" | \"low\" | \"high\" | undefined; } | undefined; timestamp?: string | undefined; updatedAt?: string | undefined; replacements?: { value: string; uuid: string; }[] | undefined; messages?: { timestamp: string; role: \"user\" | \"system\" | \"assistant\"; content: string; reader?: Zod.objectOutputType<{}, Zod.ZodUnknown, \"strip\"> | undefined; isError?: boolean | undefined; traceData?: { transactionId?: string | undefined; traceId?: string | undefined; } | undefined; }[] | undefined; apiConfig?: { connectorId: string; defaultSystemPromptId?: string | undefined; provider?: \"OpenAI\" | \"Azure OpenAI\" | undefined; model?: string | undefined; } | undefined; isDefault?: boolean | undefined; excludeFromLastConversationStorage?: boolean | undefined; }" + "{ id: string; namespace: string; title: string; createdAt: string; category: \"assistant\" | \"insights\"; users: { id?: string | undefined; name?: string | undefined; }[]; summary?: { content?: string | undefined; timestamp?: string | undefined; public?: boolean | undefined; confidence?: \"medium\" | \"low\" | \"high\" | undefined; } | undefined; timestamp?: string | undefined; updatedAt?: string | undefined; replacements?: Zod.objectOutputType<{}, Zod.ZodString, \"strip\"> | undefined; messages?: { timestamp: string; role: \"user\" | \"system\" | \"assistant\"; content: string; reader?: Zod.objectOutputType<{}, Zod.ZodUnknown, \"strip\"> | undefined; isError?: boolean | undefined; traceData?: { transactionId?: string | undefined; traceId?: string | undefined; } | undefined; }[] | undefined; apiConfig?: { connectorId: string; defaultSystemPromptId?: string | undefined; provider?: \"OpenAI\" | \"Azure OpenAI\" | undefined; model?: string | undefined; } | undefined; isDefault?: boolean | undefined; excludeFromLastConversationStorage?: boolean | undefined; }" ], "path": "x-pack/packages/kbn-elastic-assistant-common/impl/schemas/conversations/common_attributes.gen.ts", "deprecated": false, @@ -1034,7 +1034,7 @@ "label": "ConversationUpdateProps", "description": [], "signature": [ - "{ id: string; title?: string | undefined; category?: \"assistant\" | \"insights\" | undefined; messages?: { timestamp: string; role: \"user\" | \"system\" | \"assistant\"; content: string; reader?: Zod.objectOutputType<{}, Zod.ZodUnknown, \"strip\"> | undefined; isError?: boolean | undefined; traceData?: { transactionId?: string | undefined; traceId?: string | undefined; } | undefined; }[] | undefined; apiConfig?: { connectorId: string; defaultSystemPromptId?: string | undefined; provider?: \"OpenAI\" | \"Azure OpenAI\" | undefined; model?: string | undefined; } | undefined; summary?: { content?: string | undefined; timestamp?: string | undefined; public?: boolean | undefined; confidence?: \"medium\" | \"low\" | \"high\" | undefined; } | undefined; excludeFromLastConversationStorage?: boolean | undefined; replacements?: { value: string; uuid: string; }[] | undefined; }" + "{ id: string; title?: string | undefined; category?: \"assistant\" | \"insights\" | undefined; messages?: { timestamp: string; role: \"user\" | \"system\" | \"assistant\"; content: string; reader?: Zod.objectOutputType<{}, Zod.ZodUnknown, \"strip\"> | undefined; isError?: boolean | undefined; traceData?: { transactionId?: string | undefined; traceId?: string | undefined; } | undefined; }[] | undefined; apiConfig?: { connectorId: string; defaultSystemPromptId?: string | undefined; provider?: \"OpenAI\" | \"Azure OpenAI\" | undefined; model?: string | undefined; } | undefined; summary?: { content?: string | undefined; timestamp?: string | undefined; public?: boolean | undefined; confidence?: \"medium\" | \"low\" | \"high\" | undefined; } | undefined; excludeFromLastConversationStorage?: boolean | undefined; replacements?: Zod.objectOutputType<{}, Zod.ZodString, \"strip\"> | undefined; }" ], "path": "x-pack/packages/kbn-elastic-assistant-common/impl/schemas/conversations/common_attributes.gen.ts", "deprecated": false, @@ -1049,7 +1049,7 @@ "label": "CreateConversationRequestBody", "description": [], "signature": [ - "{ title: string; category?: \"assistant\" | \"insights\" | undefined; messages?: { timestamp: string; role: \"user\" | \"system\" | \"assistant\"; content: string; reader?: Zod.objectOutputType<{}, Zod.ZodUnknown, \"strip\"> | undefined; isError?: boolean | undefined; traceData?: { transactionId?: string | undefined; traceId?: string | undefined; } | undefined; }[] | undefined; apiConfig?: { connectorId: string; defaultSystemPromptId?: string | undefined; provider?: \"OpenAI\" | \"Azure OpenAI\" | undefined; model?: string | undefined; } | undefined; isDefault?: boolean | undefined; excludeFromLastConversationStorage?: boolean | undefined; replacements?: { value: string; uuid: string; }[] | undefined; }" + "{ title: string; category?: \"assistant\" | \"insights\" | undefined; messages?: { timestamp: string; role: \"user\" | \"system\" | \"assistant\"; content: string; reader?: Zod.objectOutputType<{}, Zod.ZodUnknown, \"strip\"> | undefined; isError?: boolean | undefined; traceData?: { transactionId?: string | undefined; traceId?: string | undefined; } | undefined; }[] | undefined; apiConfig?: { connectorId: string; defaultSystemPromptId?: string | undefined; provider?: \"OpenAI\" | \"Azure OpenAI\" | undefined; model?: string | undefined; } | undefined; isDefault?: boolean | undefined; excludeFromLastConversationStorage?: boolean | undefined; replacements?: Zod.objectOutputType<{}, Zod.ZodString, \"strip\"> | undefined; }" ], "path": "x-pack/packages/kbn-elastic-assistant-common/impl/schemas/conversations/crud_conversation_route.gen.ts", "deprecated": false, @@ -1064,7 +1064,7 @@ "label": "CreateConversationRequestBodyInput", "description": [], "signature": [ - "{ title: string; category?: \"assistant\" | \"insights\" | undefined; messages?: { timestamp: string; role: \"user\" | \"system\" | \"assistant\"; content: string; reader?: Zod.objectInputType<{}, Zod.ZodUnknown, \"strip\"> | undefined; isError?: boolean | undefined; traceData?: { transactionId?: string | undefined; traceId?: string | undefined; } | undefined; }[] | undefined; apiConfig?: { connectorId: string; defaultSystemPromptId?: string | undefined; provider?: \"OpenAI\" | \"Azure OpenAI\" | undefined; model?: string | undefined; } | undefined; isDefault?: boolean | undefined; excludeFromLastConversationStorage?: boolean | undefined; replacements?: { value: string; uuid: string; }[] | undefined; }" + "{ title: string; category?: \"assistant\" | \"insights\" | undefined; messages?: { timestamp: string; role: \"user\" | \"system\" | \"assistant\"; content: string; reader?: Zod.objectInputType<{}, Zod.ZodUnknown, \"strip\"> | undefined; isError?: boolean | undefined; traceData?: { transactionId?: string | undefined; traceId?: string | undefined; } | undefined; }[] | undefined; apiConfig?: { connectorId: string; defaultSystemPromptId?: string | undefined; provider?: \"OpenAI\" | \"Azure OpenAI\" | undefined; model?: string | undefined; } | undefined; isDefault?: boolean | undefined; excludeFromLastConversationStorage?: boolean | undefined; replacements?: Zod.objectInputType<{}, Zod.ZodString, \"strip\"> | undefined; }" ], "path": "x-pack/packages/kbn-elastic-assistant-common/impl/schemas/conversations/crud_conversation_route.gen.ts", "deprecated": false, @@ -1079,7 +1079,7 @@ "label": "CreateConversationResponse", "description": [], "signature": [ - "{ id: string; namespace: string; title: string; createdAt: string; category: \"assistant\" | \"insights\"; users: { id?: string | undefined; name?: string | undefined; }[]; summary?: { content?: string | undefined; timestamp?: string | undefined; public?: boolean | undefined; confidence?: \"medium\" | \"low\" | \"high\" | undefined; } | undefined; timestamp?: string | undefined; updatedAt?: string | undefined; replacements?: { value: string; uuid: string; }[] | undefined; messages?: { timestamp: string; role: \"user\" | \"system\" | \"assistant\"; content: string; reader?: Zod.objectOutputType<{}, Zod.ZodUnknown, \"strip\"> | undefined; isError?: boolean | undefined; traceData?: { transactionId?: string | undefined; traceId?: string | undefined; } | undefined; }[] | undefined; apiConfig?: { connectorId: string; defaultSystemPromptId?: string | undefined; provider?: \"OpenAI\" | \"Azure OpenAI\" | undefined; model?: string | undefined; } | undefined; isDefault?: boolean | undefined; excludeFromLastConversationStorage?: boolean | undefined; }" + "{ id: string; namespace: string; title: string; createdAt: string; category: \"assistant\" | \"insights\"; users: { id?: string | undefined; name?: string | undefined; }[]; summary?: { content?: string | undefined; timestamp?: string | undefined; public?: boolean | undefined; confidence?: \"medium\" | \"low\" | \"high\" | undefined; } | undefined; timestamp?: string | undefined; updatedAt?: string | undefined; replacements?: Zod.objectOutputType<{}, Zod.ZodString, \"strip\"> | undefined; messages?: { timestamp: string; role: \"user\" | \"system\" | \"assistant\"; content: string; reader?: Zod.objectOutputType<{}, Zod.ZodUnknown, \"strip\"> | undefined; isError?: boolean | undefined; traceData?: { transactionId?: string | undefined; traceId?: string | undefined; } | undefined; }[] | undefined; apiConfig?: { connectorId: string; defaultSystemPromptId?: string | undefined; provider?: \"OpenAI\" | \"Azure OpenAI\" | undefined; model?: string | undefined; } | undefined; isDefault?: boolean | undefined; excludeFromLastConversationStorage?: boolean | undefined; }" ], "path": "x-pack/packages/kbn-elastic-assistant-common/impl/schemas/conversations/crud_conversation_route.gen.ts", "deprecated": false, @@ -1199,7 +1199,7 @@ "label": "DeleteConversationResponse", "description": [], "signature": [ - "{ id: string; namespace: string; title: string; createdAt: string; category: \"assistant\" | \"insights\"; users: { id?: string | undefined; name?: string | undefined; }[]; summary?: { content?: string | undefined; timestamp?: string | undefined; public?: boolean | undefined; confidence?: \"medium\" | \"low\" | \"high\" | undefined; } | undefined; timestamp?: string | undefined; updatedAt?: string | undefined; replacements?: { value: string; uuid: string; }[] | undefined; messages?: { timestamp: string; role: \"user\" | \"system\" | \"assistant\"; content: string; reader?: Zod.objectOutputType<{}, Zod.ZodUnknown, \"strip\"> | undefined; isError?: boolean | undefined; traceData?: { transactionId?: string | undefined; traceId?: string | undefined; } | undefined; }[] | undefined; apiConfig?: { connectorId: string; defaultSystemPromptId?: string | undefined; provider?: \"OpenAI\" | \"Azure OpenAI\" | undefined; model?: string | undefined; } | undefined; isDefault?: boolean | undefined; excludeFromLastConversationStorage?: boolean | undefined; }" + "{ id: string; namespace: string; title: string; createdAt: string; category: \"assistant\" | \"insights\"; users: { id?: string | undefined; name?: string | undefined; }[]; summary?: { content?: string | undefined; timestamp?: string | undefined; public?: boolean | undefined; confidence?: \"medium\" | \"low\" | \"high\" | undefined; } | undefined; timestamp?: string | undefined; updatedAt?: string | undefined; replacements?: Zod.objectOutputType<{}, Zod.ZodString, \"strip\"> | undefined; messages?: { timestamp: string; role: \"user\" | \"system\" | \"assistant\"; content: string; reader?: Zod.objectOutputType<{}, Zod.ZodUnknown, \"strip\"> | undefined; isError?: boolean | undefined; traceData?: { transactionId?: string | undefined; traceId?: string | undefined; } | undefined; }[] | undefined; apiConfig?: { connectorId: string; defaultSystemPromptId?: string | undefined; provider?: \"OpenAI\" | \"Azure OpenAI\" | undefined; model?: string | undefined; } | undefined; isDefault?: boolean | undefined; excludeFromLastConversationStorage?: boolean | undefined; }" ], "path": "x-pack/packages/kbn-elastic-assistant-common/impl/schemas/conversations/crud_conversation_route.gen.ts", "deprecated": false, @@ -1451,7 +1451,7 @@ "label": "ExecuteConnectorRequestBody", "description": [], "signature": [ - "{ subAction: \"invokeAI\" | \"invokeStream\"; replacements: { value: string; uuid: string; }[]; conversationId?: string | undefined; message?: string | undefined; model?: string | undefined; alertsIndexPattern?: string | undefined; allow?: string[] | undefined; allowReplacement?: string[] | undefined; isEnabledKnowledgeBase?: boolean | undefined; isEnabledRAGAlerts?: boolean | undefined; size?: number | undefined; }" + "{ subAction: \"invokeAI\" | \"invokeStream\"; replacements: {} & { [k: string]: string; }; conversationId?: string | undefined; message?: string | undefined; model?: string | undefined; alertsIndexPattern?: string | undefined; allow?: string[] | undefined; allowReplacement?: string[] | undefined; isEnabledKnowledgeBase?: boolean | undefined; isEnabledRAGAlerts?: boolean | undefined; size?: number | undefined; }" ], "path": "x-pack/packages/kbn-elastic-assistant-common/impl/schemas/actions_connector/post_actions_connector_execute_route.gen.ts", "deprecated": false, @@ -1466,7 +1466,7 @@ "label": "ExecuteConnectorRequestBodyInput", "description": [], "signature": [ - "{ subAction: \"invokeAI\" | \"invokeStream\"; replacements: { value: string; uuid: string; }[]; conversationId?: string | undefined; message?: string | undefined; model?: string | undefined; alertsIndexPattern?: string | undefined; allow?: string[] | undefined; allowReplacement?: string[] | undefined; isEnabledKnowledgeBase?: boolean | undefined; isEnabledRAGAlerts?: boolean | undefined; size?: number | undefined; }" + "{ subAction: \"invokeAI\" | \"invokeStream\"; replacements: {} & { [k: string]: string; }; conversationId?: string | undefined; message?: string | undefined; model?: string | undefined; alertsIndexPattern?: string | undefined; allow?: string[] | undefined; allowReplacement?: string[] | undefined; isEnabledKnowledgeBase?: boolean | undefined; isEnabledRAGAlerts?: boolean | undefined; size?: number | undefined; }" ], "path": "x-pack/packages/kbn-elastic-assistant-common/impl/schemas/actions_connector/post_actions_connector_execute_route.gen.ts", "deprecated": false, @@ -1511,7 +1511,7 @@ "label": "ExecuteConnectorResponse", "description": [], "signature": [ - "{ data?: string | undefined; connector_id?: string | undefined; replacements?: { value: string; uuid: string; }[] | undefined; status?: string | undefined; trace_data?: { transactionId?: string | undefined; traceId?: string | undefined; } | undefined; }" + "{ data?: string | undefined; connector_id?: string | undefined; replacements?: Zod.objectOutputType<{}, Zod.ZodString, \"strip\"> | undefined; status?: string | undefined; trace_data?: { transactionId?: string | undefined; traceId?: string | undefined; } | undefined; }" ], "path": "x-pack/packages/kbn-elastic-assistant-common/impl/schemas/actions_connector/post_actions_connector_execute_route.gen.ts", "deprecated": false, @@ -1556,7 +1556,7 @@ "label": "FindConversationsResponse", "description": [], "signature": [ - "{ page: number; perPage: number; total: number; data: { id: string; namespace: string; title: string; createdAt: string; category: \"assistant\" | \"insights\"; users: { id?: string | undefined; name?: string | undefined; }[]; summary?: { content?: string | undefined; timestamp?: string | undefined; public?: boolean | undefined; confidence?: \"medium\" | \"low\" | \"high\" | undefined; } | undefined; timestamp?: string | undefined; updatedAt?: string | undefined; replacements?: { value: string; uuid: string; }[] | undefined; messages?: { timestamp: string; role: \"user\" | \"system\" | \"assistant\"; content: string; reader?: Zod.objectOutputType<{}, Zod.ZodUnknown, \"strip\"> | undefined; isError?: boolean | undefined; traceData?: { transactionId?: string | undefined; traceId?: string | undefined; } | undefined; }[] | undefined; apiConfig?: { connectorId: string; defaultSystemPromptId?: string | undefined; provider?: \"OpenAI\" | \"Azure OpenAI\" | undefined; model?: string | undefined; } | undefined; isDefault?: boolean | undefined; excludeFromLastConversationStorage?: boolean | undefined; }[]; }" + "{ page: number; perPage: number; total: number; data: { id: string; namespace: string; title: string; createdAt: string; category: \"assistant\" | \"insights\"; users: { id?: string | undefined; name?: string | undefined; }[]; summary?: { content?: string | undefined; timestamp?: string | undefined; public?: boolean | undefined; confidence?: \"medium\" | \"low\" | \"high\" | undefined; } | undefined; timestamp?: string | undefined; updatedAt?: string | undefined; replacements?: Zod.objectOutputType<{}, Zod.ZodString, \"strip\"> | undefined; messages?: { timestamp: string; role: \"user\" | \"system\" | \"assistant\"; content: string; reader?: Zod.objectOutputType<{}, Zod.ZodUnknown, \"strip\"> | undefined; isError?: boolean | undefined; traceData?: { transactionId?: string | undefined; traceId?: string | undefined; } | undefined; }[] | undefined; apiConfig?: { connectorId: string; defaultSystemPromptId?: string | undefined; provider?: \"OpenAI\" | \"Azure OpenAI\" | undefined; model?: string | undefined; } | undefined; isDefault?: boolean | undefined; excludeFromLastConversationStorage?: boolean | undefined; }[]; }" ], "path": "x-pack/packages/kbn-elastic-assistant-common/impl/schemas/conversations/find_conversations_route.gen.ts", "deprecated": false, @@ -1631,7 +1631,7 @@ "label": "FindCurrentUserConversationsResponse", "description": [], "signature": [ - "{ page: number; perPage: number; total: number; data: { id: string; namespace: string; title: string; createdAt: string; category: \"assistant\" | \"insights\"; users: { id?: string | undefined; name?: string | undefined; }[]; summary?: { content?: string | undefined; timestamp?: string | undefined; public?: boolean | undefined; confidence?: \"medium\" | \"low\" | \"high\" | undefined; } | undefined; timestamp?: string | undefined; updatedAt?: string | undefined; replacements?: { value: string; uuid: string; }[] | undefined; messages?: { timestamp: string; role: \"user\" | \"system\" | \"assistant\"; content: string; reader?: Zod.objectOutputType<{}, Zod.ZodUnknown, \"strip\"> | undefined; isError?: boolean | undefined; traceData?: { transactionId?: string | undefined; traceId?: string | undefined; } | undefined; }[] | undefined; apiConfig?: { connectorId: string; defaultSystemPromptId?: string | undefined; provider?: \"OpenAI\" | \"Azure OpenAI\" | undefined; model?: string | undefined; } | undefined; isDefault?: boolean | undefined; excludeFromLastConversationStorage?: boolean | undefined; }[]; }" + "{ page: number; perPage: number; total: number; data: { id: string; namespace: string; title: string; createdAt: string; category: \"assistant\" | \"insights\"; users: { id?: string | undefined; name?: string | undefined; }[]; summary?: { content?: string | undefined; timestamp?: string | undefined; public?: boolean | undefined; confidence?: \"medium\" | \"low\" | \"high\" | undefined; } | undefined; timestamp?: string | undefined; updatedAt?: string | undefined; replacements?: Zod.objectOutputType<{}, Zod.ZodString, \"strip\"> | undefined; messages?: { timestamp: string; role: \"user\" | \"system\" | \"assistant\"; content: string; reader?: Zod.objectOutputType<{}, Zod.ZodUnknown, \"strip\"> | undefined; isError?: boolean | undefined; traceData?: { transactionId?: string | undefined; traceId?: string | undefined; } | undefined; }[] | undefined; apiConfig?: { connectorId: string; defaultSystemPromptId?: string | undefined; provider?: \"OpenAI\" | \"Azure OpenAI\" | undefined; model?: string | undefined; } | undefined; isDefault?: boolean | undefined; excludeFromLastConversationStorage?: boolean | undefined; }[]; }" ], "path": "x-pack/packages/kbn-elastic-assistant-common/impl/schemas/conversations/find_conversations_route.gen.ts", "deprecated": false, @@ -1804,7 +1804,7 @@ "label": "PerformBulkActionRequestBody", "description": [], "signature": [ - "{ delete?: { query?: string | undefined; ids?: string[] | undefined; } | undefined; create?: { title: string; category?: \"assistant\" | \"insights\" | undefined; messages?: { timestamp: string; role: \"user\" | \"system\" | \"assistant\"; content: string; reader?: Zod.objectOutputType<{}, Zod.ZodUnknown, \"strip\"> | undefined; isError?: boolean | undefined; traceData?: { transactionId?: string | undefined; traceId?: string | undefined; } | undefined; }[] | undefined; apiConfig?: { connectorId: string; defaultSystemPromptId?: string | undefined; provider?: \"OpenAI\" | \"Azure OpenAI\" | undefined; model?: string | undefined; } | undefined; isDefault?: boolean | undefined; excludeFromLastConversationStorage?: boolean | undefined; replacements?: { value: string; uuid: string; }[] | undefined; }[] | undefined; update?: { id: string; title?: string | undefined; category?: \"assistant\" | \"insights\" | undefined; messages?: { timestamp: string; role: \"user\" | \"system\" | \"assistant\"; content: string; reader?: Zod.objectOutputType<{}, Zod.ZodUnknown, \"strip\"> | undefined; isError?: boolean | undefined; traceData?: { transactionId?: string | undefined; traceId?: string | undefined; } | undefined; }[] | undefined; apiConfig?: { connectorId: string; defaultSystemPromptId?: string | undefined; provider?: \"OpenAI\" | \"Azure OpenAI\" | undefined; model?: string | undefined; } | undefined; summary?: { content?: string | undefined; timestamp?: string | undefined; public?: boolean | undefined; confidence?: \"medium\" | \"low\" | \"high\" | undefined; } | undefined; excludeFromLastConversationStorage?: boolean | undefined; replacements?: { value: string; uuid: string; }[] | undefined; }[] | undefined; }" + "{ delete?: { query?: string | undefined; ids?: string[] | undefined; } | undefined; create?: { title: string; category?: \"assistant\" | \"insights\" | undefined; messages?: { timestamp: string; role: \"user\" | \"system\" | \"assistant\"; content: string; reader?: Zod.objectOutputType<{}, Zod.ZodUnknown, \"strip\"> | undefined; isError?: boolean | undefined; traceData?: { transactionId?: string | undefined; traceId?: string | undefined; } | undefined; }[] | undefined; apiConfig?: { connectorId: string; defaultSystemPromptId?: string | undefined; provider?: \"OpenAI\" | \"Azure OpenAI\" | undefined; model?: string | undefined; } | undefined; isDefault?: boolean | undefined; excludeFromLastConversationStorage?: boolean | undefined; replacements?: Zod.objectOutputType<{}, Zod.ZodString, \"strip\"> | undefined; }[] | undefined; update?: { id: string; title?: string | undefined; category?: \"assistant\" | \"insights\" | undefined; messages?: { timestamp: string; role: \"user\" | \"system\" | \"assistant\"; content: string; reader?: Zod.objectOutputType<{}, Zod.ZodUnknown, \"strip\"> | undefined; isError?: boolean | undefined; traceData?: { transactionId?: string | undefined; traceId?: string | undefined; } | undefined; }[] | undefined; apiConfig?: { connectorId: string; defaultSystemPromptId?: string | undefined; provider?: \"OpenAI\" | \"Azure OpenAI\" | undefined; model?: string | undefined; } | undefined; summary?: { content?: string | undefined; timestamp?: string | undefined; public?: boolean | undefined; confidence?: \"medium\" | \"low\" | \"high\" | undefined; } | undefined; excludeFromLastConversationStorage?: boolean | undefined; replacements?: Zod.objectOutputType<{}, Zod.ZodString, \"strip\"> | undefined; }[] | undefined; }" ], "path": "x-pack/packages/kbn-elastic-assistant-common/impl/schemas/conversations/bulk_crud_conversations_route.gen.ts", "deprecated": false, @@ -1819,7 +1819,7 @@ "label": "PerformBulkActionRequestBodyInput", "description": [], "signature": [ - "{ delete?: { query?: string | undefined; ids?: string[] | undefined; } | undefined; create?: { title: string; category?: \"assistant\" | \"insights\" | undefined; messages?: { timestamp: string; role: \"user\" | \"system\" | \"assistant\"; content: string; reader?: Zod.objectInputType<{}, Zod.ZodUnknown, \"strip\"> | undefined; isError?: boolean | undefined; traceData?: { transactionId?: string | undefined; traceId?: string | undefined; } | undefined; }[] | undefined; apiConfig?: { connectorId: string; defaultSystemPromptId?: string | undefined; provider?: \"OpenAI\" | \"Azure OpenAI\" | undefined; model?: string | undefined; } | undefined; isDefault?: boolean | undefined; excludeFromLastConversationStorage?: boolean | undefined; replacements?: { value: string; uuid: string; }[] | undefined; }[] | undefined; update?: { id: string; title?: string | undefined; category?: \"assistant\" | \"insights\" | undefined; messages?: { timestamp: string; role: \"user\" | \"system\" | \"assistant\"; content: string; reader?: Zod.objectInputType<{}, Zod.ZodUnknown, \"strip\"> | undefined; isError?: boolean | undefined; traceData?: { transactionId?: string | undefined; traceId?: string | undefined; } | undefined; }[] | undefined; apiConfig?: { connectorId: string; defaultSystemPromptId?: string | undefined; provider?: \"OpenAI\" | \"Azure OpenAI\" | undefined; model?: string | undefined; } | undefined; summary?: { content?: string | undefined; timestamp?: string | undefined; public?: boolean | undefined; confidence?: \"medium\" | \"low\" | \"high\" | undefined; } | undefined; excludeFromLastConversationStorage?: boolean | undefined; replacements?: { value: string; uuid: string; }[] | undefined; }[] | undefined; }" + "{ delete?: { query?: string | undefined; ids?: string[] | undefined; } | undefined; create?: { title: string; category?: \"assistant\" | \"insights\" | undefined; messages?: { timestamp: string; role: \"user\" | \"system\" | \"assistant\"; content: string; reader?: Zod.objectInputType<{}, Zod.ZodUnknown, \"strip\"> | undefined; isError?: boolean | undefined; traceData?: { transactionId?: string | undefined; traceId?: string | undefined; } | undefined; }[] | undefined; apiConfig?: { connectorId: string; defaultSystemPromptId?: string | undefined; provider?: \"OpenAI\" | \"Azure OpenAI\" | undefined; model?: string | undefined; } | undefined; isDefault?: boolean | undefined; excludeFromLastConversationStorage?: boolean | undefined; replacements?: Zod.objectInputType<{}, Zod.ZodString, \"strip\"> | undefined; }[] | undefined; update?: { id: string; title?: string | undefined; category?: \"assistant\" | \"insights\" | undefined; messages?: { timestamp: string; role: \"user\" | \"system\" | \"assistant\"; content: string; reader?: Zod.objectInputType<{}, Zod.ZodUnknown, \"strip\"> | undefined; isError?: boolean | undefined; traceData?: { transactionId?: string | undefined; traceId?: string | undefined; } | undefined; }[] | undefined; apiConfig?: { connectorId: string; defaultSystemPromptId?: string | undefined; provider?: \"OpenAI\" | \"Azure OpenAI\" | undefined; model?: string | undefined; } | undefined; summary?: { content?: string | undefined; timestamp?: string | undefined; public?: boolean | undefined; confidence?: \"medium\" | \"low\" | \"high\" | undefined; } | undefined; excludeFromLastConversationStorage?: boolean | undefined; replacements?: Zod.objectInputType<{}, Zod.ZodString, \"strip\"> | undefined; }[] | undefined; }" ], "path": "x-pack/packages/kbn-elastic-assistant-common/impl/schemas/conversations/bulk_crud_conversations_route.gen.ts", "deprecated": false, @@ -1834,7 +1834,7 @@ "label": "PerformBulkActionResponse", "description": [], "signature": [ - "{ attributes: { results: { created: { id: string; namespace: string; title: string; createdAt: string; category: \"assistant\" | \"insights\"; users: { id?: string | undefined; name?: string | undefined; }[]; summary?: { content?: string | undefined; timestamp?: string | undefined; public?: boolean | undefined; confidence?: \"medium\" | \"low\" | \"high\" | undefined; } | undefined; timestamp?: string | undefined; updatedAt?: string | undefined; replacements?: { value: string; uuid: string; }[] | undefined; messages?: { timestamp: string; role: \"user\" | \"system\" | \"assistant\"; content: string; reader?: Zod.objectOutputType<{}, Zod.ZodUnknown, \"strip\"> | undefined; isError?: boolean | undefined; traceData?: { transactionId?: string | undefined; traceId?: string | undefined; } | undefined; }[] | undefined; apiConfig?: { connectorId: string; defaultSystemPromptId?: string | undefined; provider?: \"OpenAI\" | \"Azure OpenAI\" | undefined; model?: string | undefined; } | undefined; isDefault?: boolean | undefined; excludeFromLastConversationStorage?: boolean | undefined; }[]; updated: { id: string; namespace: string; title: string; createdAt: string; category: \"assistant\" | \"insights\"; users: { id?: string | undefined; name?: string | undefined; }[]; summary?: { content?: string | undefined; timestamp?: string | undefined; public?: boolean | undefined; confidence?: \"medium\" | \"low\" | \"high\" | undefined; } | undefined; timestamp?: string | undefined; updatedAt?: string | undefined; replacements?: { value: string; uuid: string; }[] | undefined; messages?: { timestamp: string; role: \"user\" | \"system\" | \"assistant\"; content: string; reader?: Zod.objectOutputType<{}, Zod.ZodUnknown, \"strip\"> | undefined; isError?: boolean | undefined; traceData?: { transactionId?: string | undefined; traceId?: string | undefined; } | undefined; }[] | undefined; apiConfig?: { connectorId: string; defaultSystemPromptId?: string | undefined; provider?: \"OpenAI\" | \"Azure OpenAI\" | undefined; model?: string | undefined; } | undefined; isDefault?: boolean | undefined; excludeFromLastConversationStorage?: boolean | undefined; }[]; skipped: { id: string; skip_reason: \"CONVERSATION_NOT_MODIFIED\"; name?: string | undefined; }[]; deleted: string[]; }; summary: { total: number; succeeded: number; failed: number; skipped: number; }; errors?: { message: string; status_code: number; conversations: { id: string; name?: string | undefined; }[]; err_code?: string | undefined; }[] | undefined; }; success?: boolean | undefined; status_code?: number | undefined; message?: string | undefined; conversations_count?: number | undefined; }" + "{ attributes: { results: { created: { id: string; namespace: string; title: string; createdAt: string; category: \"assistant\" | \"insights\"; users: { id?: string | undefined; name?: string | undefined; }[]; summary?: { content?: string | undefined; timestamp?: string | undefined; public?: boolean | undefined; confidence?: \"medium\" | \"low\" | \"high\" | undefined; } | undefined; timestamp?: string | undefined; updatedAt?: string | undefined; replacements?: Zod.objectOutputType<{}, Zod.ZodString, \"strip\"> | undefined; messages?: { timestamp: string; role: \"user\" | \"system\" | \"assistant\"; content: string; reader?: Zod.objectOutputType<{}, Zod.ZodUnknown, \"strip\"> | undefined; isError?: boolean | undefined; traceData?: { transactionId?: string | undefined; traceId?: string | undefined; } | undefined; }[] | undefined; apiConfig?: { connectorId: string; defaultSystemPromptId?: string | undefined; provider?: \"OpenAI\" | \"Azure OpenAI\" | undefined; model?: string | undefined; } | undefined; isDefault?: boolean | undefined; excludeFromLastConversationStorage?: boolean | undefined; }[]; updated: { id: string; namespace: string; title: string; createdAt: string; category: \"assistant\" | \"insights\"; users: { id?: string | undefined; name?: string | undefined; }[]; summary?: { content?: string | undefined; timestamp?: string | undefined; public?: boolean | undefined; confidence?: \"medium\" | \"low\" | \"high\" | undefined; } | undefined; timestamp?: string | undefined; updatedAt?: string | undefined; replacements?: Zod.objectOutputType<{}, Zod.ZodString, \"strip\"> | undefined; messages?: { timestamp: string; role: \"user\" | \"system\" | \"assistant\"; content: string; reader?: Zod.objectOutputType<{}, Zod.ZodUnknown, \"strip\"> | undefined; isError?: boolean | undefined; traceData?: { transactionId?: string | undefined; traceId?: string | undefined; } | undefined; }[] | undefined; apiConfig?: { connectorId: string; defaultSystemPromptId?: string | undefined; provider?: \"OpenAI\" | \"Azure OpenAI\" | undefined; model?: string | undefined; } | undefined; isDefault?: boolean | undefined; excludeFromLastConversationStorage?: boolean | undefined; }[]; skipped: { id: string; skip_reason: \"CONVERSATION_NOT_MODIFIED\"; name?: string | undefined; }[]; deleted: string[]; }; summary: { total: number; succeeded: number; failed: number; skipped: number; }; errors?: { message: string; status_code: number; conversations: { id: string; name?: string | undefined; }[]; err_code?: string | undefined; }[] | undefined; }; success?: boolean | undefined; status_code?: number | undefined; message?: string | undefined; conversations_count?: number | undefined; }" ], "path": "x-pack/packages/kbn-elastic-assistant-common/impl/schemas/conversations/bulk_crud_conversations_route.gen.ts", "deprecated": false, @@ -2016,7 +2016,7 @@ "label": "ReadConversationResponse", "description": [], "signature": [ - "{ id: string; namespace: string; title: string; createdAt: string; category: \"assistant\" | \"insights\"; users: { id?: string | undefined; name?: string | undefined; }[]; summary?: { content?: string | undefined; timestamp?: string | undefined; public?: boolean | undefined; confidence?: \"medium\" | \"low\" | \"high\" | undefined; } | undefined; timestamp?: string | undefined; updatedAt?: string | undefined; replacements?: { value: string; uuid: string; }[] | undefined; messages?: { timestamp: string; role: \"user\" | \"system\" | \"assistant\"; content: string; reader?: Zod.objectOutputType<{}, Zod.ZodUnknown, \"strip\"> | undefined; isError?: boolean | undefined; traceData?: { transactionId?: string | undefined; traceId?: string | undefined; } | undefined; }[] | undefined; apiConfig?: { connectorId: string; defaultSystemPromptId?: string | undefined; provider?: \"OpenAI\" | \"Azure OpenAI\" | undefined; model?: string | undefined; } | undefined; isDefault?: boolean | undefined; excludeFromLastConversationStorage?: boolean | undefined; }" + "{ id: string; namespace: string; title: string; createdAt: string; category: \"assistant\" | \"insights\"; users: { id?: string | undefined; name?: string | undefined; }[]; summary?: { content?: string | undefined; timestamp?: string | undefined; public?: boolean | undefined; confidence?: \"medium\" | \"low\" | \"high\" | undefined; } | undefined; timestamp?: string | undefined; updatedAt?: string | undefined; replacements?: Zod.objectOutputType<{}, Zod.ZodString, \"strip\"> | undefined; messages?: { timestamp: string; role: \"user\" | \"system\" | \"assistant\"; content: string; reader?: Zod.objectOutputType<{}, Zod.ZodUnknown, \"strip\"> | undefined; isError?: boolean | undefined; traceData?: { transactionId?: string | undefined; traceId?: string | undefined; } | undefined; }[] | undefined; apiConfig?: { connectorId: string; defaultSystemPromptId?: string | undefined; provider?: \"OpenAI\" | \"Azure OpenAI\" | undefined; model?: string | undefined; } | undefined; isDefault?: boolean | undefined; excludeFromLastConversationStorage?: boolean | undefined; }" ], "path": "x-pack/packages/kbn-elastic-assistant-common/impl/schemas/conversations/crud_conversation_route.gen.ts", "deprecated": false, @@ -2085,15 +2085,15 @@ }, { "parentPluginId": "@kbn/elastic-assistant-common", - "id": "def-common.Replacement", + "id": "def-common.Replacements", "type": "Type", "tags": [], - "label": "Replacement", + "label": "Replacements", "description": [ "\nReplacements object used to anonymize/deanomymize messsages" ], "signature": [ - "{ value: string; uuid: string; }" + "{} & { [k: string]: string; }" ], "path": "x-pack/packages/kbn-elastic-assistant-common/impl/schemas/conversations/common_attributes.gen.ts", "deprecated": false, @@ -2155,7 +2155,7 @@ "label": "UpdateConversationRequestBody", "description": [], "signature": [ - "{ id: string; title?: string | undefined; category?: \"assistant\" | \"insights\" | undefined; messages?: { timestamp: string; role: \"user\" | \"system\" | \"assistant\"; content: string; reader?: Zod.objectOutputType<{}, Zod.ZodUnknown, \"strip\"> | undefined; isError?: boolean | undefined; traceData?: { transactionId?: string | undefined; traceId?: string | undefined; } | undefined; }[] | undefined; apiConfig?: { connectorId: string; defaultSystemPromptId?: string | undefined; provider?: \"OpenAI\" | \"Azure OpenAI\" | undefined; model?: string | undefined; } | undefined; summary?: { content?: string | undefined; timestamp?: string | undefined; public?: boolean | undefined; confidence?: \"medium\" | \"low\" | \"high\" | undefined; } | undefined; excludeFromLastConversationStorage?: boolean | undefined; replacements?: { value: string; uuid: string; }[] | undefined; }" + "{ id: string; title?: string | undefined; category?: \"assistant\" | \"insights\" | undefined; messages?: { timestamp: string; role: \"user\" | \"system\" | \"assistant\"; content: string; reader?: Zod.objectOutputType<{}, Zod.ZodUnknown, \"strip\"> | undefined; isError?: boolean | undefined; traceData?: { transactionId?: string | undefined; traceId?: string | undefined; } | undefined; }[] | undefined; apiConfig?: { connectorId: string; defaultSystemPromptId?: string | undefined; provider?: \"OpenAI\" | \"Azure OpenAI\" | undefined; model?: string | undefined; } | undefined; summary?: { content?: string | undefined; timestamp?: string | undefined; public?: boolean | undefined; confidence?: \"medium\" | \"low\" | \"high\" | undefined; } | undefined; excludeFromLastConversationStorage?: boolean | undefined; replacements?: Zod.objectOutputType<{}, Zod.ZodString, \"strip\"> | undefined; }" ], "path": "x-pack/packages/kbn-elastic-assistant-common/impl/schemas/conversations/crud_conversation_route.gen.ts", "deprecated": false, @@ -2170,7 +2170,7 @@ "label": "UpdateConversationRequestBodyInput", "description": [], "signature": [ - "{ id: string; title?: string | undefined; category?: \"assistant\" | \"insights\" | undefined; messages?: { timestamp: string; role: \"user\" | \"system\" | \"assistant\"; content: string; reader?: Zod.objectInputType<{}, Zod.ZodUnknown, \"strip\"> | undefined; isError?: boolean | undefined; traceData?: { transactionId?: string | undefined; traceId?: string | undefined; } | undefined; }[] | undefined; apiConfig?: { connectorId: string; defaultSystemPromptId?: string | undefined; provider?: \"OpenAI\" | \"Azure OpenAI\" | undefined; model?: string | undefined; } | undefined; summary?: { content?: string | undefined; timestamp?: string | undefined; public?: boolean | undefined; confidence?: \"medium\" | \"low\" | \"high\" | undefined; } | undefined; excludeFromLastConversationStorage?: boolean | undefined; replacements?: { value: string; uuid: string; }[] | undefined; }" + "{ id: string; title?: string | undefined; category?: \"assistant\" | \"insights\" | undefined; messages?: { timestamp: string; role: \"user\" | \"system\" | \"assistant\"; content: string; reader?: Zod.objectInputType<{}, Zod.ZodUnknown, \"strip\"> | undefined; isError?: boolean | undefined; traceData?: { transactionId?: string | undefined; traceId?: string | undefined; } | undefined; }[] | undefined; apiConfig?: { connectorId: string; defaultSystemPromptId?: string | undefined; provider?: \"OpenAI\" | \"Azure OpenAI\" | undefined; model?: string | undefined; } | undefined; summary?: { content?: string | undefined; timestamp?: string | undefined; public?: boolean | undefined; confidence?: \"medium\" | \"low\" | \"high\" | undefined; } | undefined; excludeFromLastConversationStorage?: boolean | undefined; replacements?: Zod.objectInputType<{}, Zod.ZodString, \"strip\"> | undefined; }" ], "path": "x-pack/packages/kbn-elastic-assistant-common/impl/schemas/conversations/crud_conversation_route.gen.ts", "deprecated": false, @@ -2215,7 +2215,7 @@ "label": "UpdateConversationResponse", "description": [], "signature": [ - "{ id: string; namespace: string; title: string; createdAt: string; category: \"assistant\" | \"insights\"; users: { id?: string | undefined; name?: string | undefined; }[]; summary?: { content?: string | undefined; timestamp?: string | undefined; public?: boolean | undefined; confidence?: \"medium\" | \"low\" | \"high\" | undefined; } | undefined; timestamp?: string | undefined; updatedAt?: string | undefined; replacements?: { value: string; uuid: string; }[] | undefined; messages?: { timestamp: string; role: \"user\" | \"system\" | \"assistant\"; content: string; reader?: Zod.objectOutputType<{}, Zod.ZodUnknown, \"strip\"> | undefined; isError?: boolean | undefined; traceData?: { transactionId?: string | undefined; traceId?: string | undefined; } | undefined; }[] | undefined; apiConfig?: { connectorId: string; defaultSystemPromptId?: string | undefined; provider?: \"OpenAI\" | \"Azure OpenAI\" | undefined; model?: string | undefined; } | undefined; isDefault?: boolean | undefined; excludeFromLastConversationStorage?: boolean | undefined; }" + "{ id: string; namespace: string; title: string; createdAt: string; category: \"assistant\" | \"insights\"; users: { id?: string | undefined; name?: string | undefined; }[]; summary?: { content?: string | undefined; timestamp?: string | undefined; public?: boolean | undefined; confidence?: \"medium\" | \"low\" | \"high\" | undefined; } | undefined; timestamp?: string | undefined; updatedAt?: string | undefined; replacements?: Zod.objectOutputType<{}, Zod.ZodString, \"strip\"> | undefined; messages?: { timestamp: string; role: \"user\" | \"system\" | \"assistant\"; content: string; reader?: Zod.objectOutputType<{}, Zod.ZodUnknown, \"strip\"> | undefined; isError?: boolean | undefined; traceData?: { transactionId?: string | undefined; traceId?: string | undefined; } | undefined; }[] | undefined; apiConfig?: { connectorId: string; defaultSystemPromptId?: string | undefined; provider?: \"OpenAI\" | \"Azure OpenAI\" | undefined; model?: string | undefined; } | undefined; isDefault?: boolean | undefined; excludeFromLastConversationStorage?: boolean | undefined; }" ], "path": "x-pack/packages/kbn-elastic-assistant-common/impl/schemas/conversations/crud_conversation_route.gen.ts", "deprecated": false, @@ -2373,7 +2373,7 @@ "label": "AppendConversationMessageResponse", "description": [], "signature": [ - "Zod.ZodObject<{ id: Zod.ZodUnion<[Zod.ZodString, Zod.ZodString]>; title: Zod.ZodString; category: Zod.ZodEnum<[\"assistant\", \"insights\"]>; summary: Zod.ZodOptional; timestamp: Zod.ZodOptional; public: Zod.ZodOptional; confidence: Zod.ZodOptional>; }, \"strip\", Zod.ZodTypeAny, { content?: string | undefined; timestamp?: string | undefined; public?: boolean | undefined; confidence?: \"medium\" | \"low\" | \"high\" | undefined; }, { content?: string | undefined; timestamp?: string | undefined; public?: boolean | undefined; confidence?: \"medium\" | \"low\" | \"high\" | undefined; }>>; timestamp: Zod.ZodOptional; updatedAt: Zod.ZodOptional; createdAt: Zod.ZodString; replacements: Zod.ZodOptional, \"many\">>; users: Zod.ZodArray; name: Zod.ZodOptional; }, \"strip\", Zod.ZodTypeAny, { id?: string | undefined; name?: string | undefined; }, { id?: string | undefined; name?: string | undefined; }>, \"many\">; messages: Zod.ZodOptional, Zod.objectInputType<{}, Zod.ZodUnknown, \"strip\">>>; role: Zod.ZodEnum<[\"system\", \"user\", \"assistant\"]>; timestamp: Zod.ZodString; isError: Zod.ZodOptional; traceData: Zod.ZodOptional; traceId: Zod.ZodOptional; }, \"strip\", Zod.ZodTypeAny, { transactionId?: string | undefined; traceId?: string | undefined; }, { transactionId?: string | undefined; traceId?: string | undefined; }>>; }, \"strip\", Zod.ZodTypeAny, { timestamp: string; role: \"user\" | \"system\" | \"assistant\"; content: string; reader?: Zod.objectOutputType<{}, Zod.ZodUnknown, \"strip\"> | undefined; isError?: boolean | undefined; traceData?: { transactionId?: string | undefined; traceId?: string | undefined; } | undefined; }, { timestamp: string; role: \"user\" | \"system\" | \"assistant\"; content: string; reader?: Zod.objectInputType<{}, Zod.ZodUnknown, \"strip\"> | undefined; isError?: boolean | undefined; traceData?: { transactionId?: string | undefined; traceId?: string | undefined; } | undefined; }>, \"many\">>; apiConfig: Zod.ZodOptional; provider: Zod.ZodOptional>; model: Zod.ZodOptional; }, \"strip\", Zod.ZodTypeAny, { connectorId: string; defaultSystemPromptId?: string | undefined; provider?: \"OpenAI\" | \"Azure OpenAI\" | undefined; model?: string | undefined; }, { connectorId: string; defaultSystemPromptId?: string | undefined; provider?: \"OpenAI\" | \"Azure OpenAI\" | undefined; model?: string | undefined; }>>; isDefault: Zod.ZodOptional; excludeFromLastConversationStorage: Zod.ZodOptional; namespace: Zod.ZodString; }, \"strip\", Zod.ZodTypeAny, { id: string; namespace: string; title: string; createdAt: string; category: \"assistant\" | \"insights\"; users: { id?: string | undefined; name?: string | undefined; }[]; summary?: { content?: string | undefined; timestamp?: string | undefined; public?: boolean | undefined; confidence?: \"medium\" | \"low\" | \"high\" | undefined; } | undefined; timestamp?: string | undefined; updatedAt?: string | undefined; replacements?: { value: string; uuid: string; }[] | undefined; messages?: { timestamp: string; role: \"user\" | \"system\" | \"assistant\"; content: string; reader?: Zod.objectOutputType<{}, Zod.ZodUnknown, \"strip\"> | undefined; isError?: boolean | undefined; traceData?: { transactionId?: string | undefined; traceId?: string | undefined; } | undefined; }[] | undefined; apiConfig?: { connectorId: string; defaultSystemPromptId?: string | undefined; provider?: \"OpenAI\" | \"Azure OpenAI\" | undefined; model?: string | undefined; } | undefined; isDefault?: boolean | undefined; excludeFromLastConversationStorage?: boolean | undefined; }, { id: string; namespace: string; title: string; createdAt: string; category: \"assistant\" | \"insights\"; users: { id?: string | undefined; name?: string | undefined; }[]; summary?: { content?: string | undefined; timestamp?: string | undefined; public?: boolean | undefined; confidence?: \"medium\" | \"low\" | \"high\" | undefined; } | undefined; timestamp?: string | undefined; updatedAt?: string | undefined; replacements?: { value: string; uuid: string; }[] | undefined; messages?: { timestamp: string; role: \"user\" | \"system\" | \"assistant\"; content: string; reader?: Zod.objectInputType<{}, Zod.ZodUnknown, \"strip\"> | undefined; isError?: boolean | undefined; traceData?: { transactionId?: string | undefined; traceId?: string | undefined; } | undefined; }[] | undefined; apiConfig?: { connectorId: string; defaultSystemPromptId?: string | undefined; provider?: \"OpenAI\" | \"Azure OpenAI\" | undefined; model?: string | undefined; } | undefined; isDefault?: boolean | undefined; excludeFromLastConversationStorage?: boolean | undefined; }>" + "Zod.ZodObject<{ id: Zod.ZodUnion<[Zod.ZodString, Zod.ZodString]>; title: Zod.ZodString; category: Zod.ZodEnum<[\"assistant\", \"insights\"]>; summary: Zod.ZodOptional; timestamp: Zod.ZodOptional; public: Zod.ZodOptional; confidence: Zod.ZodOptional>; }, \"strip\", Zod.ZodTypeAny, { content?: string | undefined; timestamp?: string | undefined; public?: boolean | undefined; confidence?: \"medium\" | \"low\" | \"high\" | undefined; }, { content?: string | undefined; timestamp?: string | undefined; public?: boolean | undefined; confidence?: \"medium\" | \"low\" | \"high\" | undefined; }>>; timestamp: Zod.ZodOptional; updatedAt: Zod.ZodOptional; createdAt: Zod.ZodString; replacements: Zod.ZodOptional, Zod.objectInputType<{}, Zod.ZodString, \"strip\">>>; users: Zod.ZodArray; name: Zod.ZodOptional; }, \"strip\", Zod.ZodTypeAny, { id?: string | undefined; name?: string | undefined; }, { id?: string | undefined; name?: string | undefined; }>, \"many\">; messages: Zod.ZodOptional, Zod.objectInputType<{}, Zod.ZodUnknown, \"strip\">>>; role: Zod.ZodEnum<[\"system\", \"user\", \"assistant\"]>; timestamp: Zod.ZodString; isError: Zod.ZodOptional; traceData: Zod.ZodOptional; traceId: Zod.ZodOptional; }, \"strip\", Zod.ZodTypeAny, { transactionId?: string | undefined; traceId?: string | undefined; }, { transactionId?: string | undefined; traceId?: string | undefined; }>>; }, \"strip\", Zod.ZodTypeAny, { timestamp: string; role: \"user\" | \"system\" | \"assistant\"; content: string; reader?: Zod.objectOutputType<{}, Zod.ZodUnknown, \"strip\"> | undefined; isError?: boolean | undefined; traceData?: { transactionId?: string | undefined; traceId?: string | undefined; } | undefined; }, { timestamp: string; role: \"user\" | \"system\" | \"assistant\"; content: string; reader?: Zod.objectInputType<{}, Zod.ZodUnknown, \"strip\"> | undefined; isError?: boolean | undefined; traceData?: { transactionId?: string | undefined; traceId?: string | undefined; } | undefined; }>, \"many\">>; apiConfig: Zod.ZodOptional; provider: Zod.ZodOptional>; model: Zod.ZodOptional; }, \"strip\", Zod.ZodTypeAny, { connectorId: string; defaultSystemPromptId?: string | undefined; provider?: \"OpenAI\" | \"Azure OpenAI\" | undefined; model?: string | undefined; }, { connectorId: string; defaultSystemPromptId?: string | undefined; provider?: \"OpenAI\" | \"Azure OpenAI\" | undefined; model?: string | undefined; }>>; isDefault: Zod.ZodOptional; excludeFromLastConversationStorage: Zod.ZodOptional; namespace: Zod.ZodString; }, \"strip\", Zod.ZodTypeAny, { id: string; namespace: string; title: string; createdAt: string; category: \"assistant\" | \"insights\"; users: { id?: string | undefined; name?: string | undefined; }[]; summary?: { content?: string | undefined; timestamp?: string | undefined; public?: boolean | undefined; confidence?: \"medium\" | \"low\" | \"high\" | undefined; } | undefined; timestamp?: string | undefined; updatedAt?: string | undefined; replacements?: Zod.objectOutputType<{}, Zod.ZodString, \"strip\"> | undefined; messages?: { timestamp: string; role: \"user\" | \"system\" | \"assistant\"; content: string; reader?: Zod.objectOutputType<{}, Zod.ZodUnknown, \"strip\"> | undefined; isError?: boolean | undefined; traceData?: { transactionId?: string | undefined; traceId?: string | undefined; } | undefined; }[] | undefined; apiConfig?: { connectorId: string; defaultSystemPromptId?: string | undefined; provider?: \"OpenAI\" | \"Azure OpenAI\" | undefined; model?: string | undefined; } | undefined; isDefault?: boolean | undefined; excludeFromLastConversationStorage?: boolean | undefined; }, { id: string; namespace: string; title: string; createdAt: string; category: \"assistant\" | \"insights\"; users: { id?: string | undefined; name?: string | undefined; }[]; summary?: { content?: string | undefined; timestamp?: string | undefined; public?: boolean | undefined; confidence?: \"medium\" | \"low\" | \"high\" | undefined; } | undefined; timestamp?: string | undefined; updatedAt?: string | undefined; replacements?: Zod.objectInputType<{}, Zod.ZodString, \"strip\"> | undefined; messages?: { timestamp: string; role: \"user\" | \"system\" | \"assistant\"; content: string; reader?: Zod.objectInputType<{}, Zod.ZodUnknown, \"strip\"> | undefined; isError?: boolean | undefined; traceData?: { transactionId?: string | undefined; traceId?: string | undefined; } | undefined; }[] | undefined; apiConfig?: { connectorId: string; defaultSystemPromptId?: string | undefined; provider?: \"OpenAI\" | \"Azure OpenAI\" | undefined; model?: string | undefined; } | undefined; isDefault?: boolean | undefined; excludeFromLastConversationStorage?: boolean | undefined; }>" ], "path": "x-pack/packages/kbn-elastic-assistant-common/impl/schemas/conversations/crud_conversation_route.gen.ts", "deprecated": false, @@ -2433,7 +2433,7 @@ "label": "BulkCrudActionResponse", "description": [], "signature": [ - "Zod.ZodObject<{ success: Zod.ZodOptional; status_code: Zod.ZodOptional; message: Zod.ZodOptional; conversations_count: Zod.ZodOptional; attributes: Zod.ZodObject<{ results: Zod.ZodObject<{ updated: Zod.ZodArray; title: Zod.ZodString; category: Zod.ZodEnum<[\"assistant\", \"insights\"]>; summary: Zod.ZodOptional; timestamp: Zod.ZodOptional; public: Zod.ZodOptional; confidence: Zod.ZodOptional>; }, \"strip\", Zod.ZodTypeAny, { content?: string | undefined; timestamp?: string | undefined; public?: boolean | undefined; confidence?: \"medium\" | \"low\" | \"high\" | undefined; }, { content?: string | undefined; timestamp?: string | undefined; public?: boolean | undefined; confidence?: \"medium\" | \"low\" | \"high\" | undefined; }>>; timestamp: Zod.ZodOptional; updatedAt: Zod.ZodOptional; createdAt: Zod.ZodString; replacements: Zod.ZodOptional, \"many\">>; users: Zod.ZodArray; name: Zod.ZodOptional; }, \"strip\", Zod.ZodTypeAny, { id?: string | undefined; name?: string | undefined; }, { id?: string | undefined; name?: string | undefined; }>, \"many\">; messages: Zod.ZodOptional, Zod.objectInputType<{}, Zod.ZodUnknown, \"strip\">>>; role: Zod.ZodEnum<[\"system\", \"user\", \"assistant\"]>; timestamp: Zod.ZodString; isError: Zod.ZodOptional; traceData: Zod.ZodOptional; traceId: Zod.ZodOptional; }, \"strip\", Zod.ZodTypeAny, { transactionId?: string | undefined; traceId?: string | undefined; }, { transactionId?: string | undefined; traceId?: string | undefined; }>>; }, \"strip\", Zod.ZodTypeAny, { timestamp: string; role: \"user\" | \"system\" | \"assistant\"; content: string; reader?: Zod.objectOutputType<{}, Zod.ZodUnknown, \"strip\"> | undefined; isError?: boolean | undefined; traceData?: { transactionId?: string | undefined; traceId?: string | undefined; } | undefined; }, { timestamp: string; role: \"user\" | \"system\" | \"assistant\"; content: string; reader?: Zod.objectInputType<{}, Zod.ZodUnknown, \"strip\"> | undefined; isError?: boolean | undefined; traceData?: { transactionId?: string | undefined; traceId?: string | undefined; } | undefined; }>, \"many\">>; apiConfig: Zod.ZodOptional; provider: Zod.ZodOptional>; model: Zod.ZodOptional; }, \"strip\", Zod.ZodTypeAny, { connectorId: string; defaultSystemPromptId?: string | undefined; provider?: \"OpenAI\" | \"Azure OpenAI\" | undefined; model?: string | undefined; }, { connectorId: string; defaultSystemPromptId?: string | undefined; provider?: \"OpenAI\" | \"Azure OpenAI\" | undefined; model?: string | undefined; }>>; isDefault: Zod.ZodOptional; excludeFromLastConversationStorage: Zod.ZodOptional; namespace: Zod.ZodString; }, \"strip\", Zod.ZodTypeAny, { id: string; namespace: string; title: string; createdAt: string; category: \"assistant\" | \"insights\"; users: { id?: string | undefined; name?: string | undefined; }[]; summary?: { content?: string | undefined; timestamp?: string | undefined; public?: boolean | undefined; confidence?: \"medium\" | \"low\" | \"high\" | undefined; } | undefined; timestamp?: string | undefined; updatedAt?: string | undefined; replacements?: { value: string; uuid: string; }[] | undefined; messages?: { timestamp: string; role: \"user\" | \"system\" | \"assistant\"; content: string; reader?: Zod.objectOutputType<{}, Zod.ZodUnknown, \"strip\"> | undefined; isError?: boolean | undefined; traceData?: { transactionId?: string | undefined; traceId?: string | undefined; } | undefined; }[] | undefined; apiConfig?: { connectorId: string; defaultSystemPromptId?: string | undefined; provider?: \"OpenAI\" | \"Azure OpenAI\" | undefined; model?: string | undefined; } | undefined; isDefault?: boolean | undefined; excludeFromLastConversationStorage?: boolean | undefined; }, { id: string; namespace: string; title: string; createdAt: string; category: \"assistant\" | \"insights\"; users: { id?: string | undefined; name?: string | undefined; }[]; summary?: { content?: string | undefined; timestamp?: string | undefined; public?: boolean | undefined; confidence?: \"medium\" | \"low\" | \"high\" | undefined; } | undefined; timestamp?: string | undefined; updatedAt?: string | undefined; replacements?: { value: string; uuid: string; }[] | undefined; messages?: { timestamp: string; role: \"user\" | \"system\" | \"assistant\"; content: string; reader?: Zod.objectInputType<{}, Zod.ZodUnknown, \"strip\"> | undefined; isError?: boolean | undefined; traceData?: { transactionId?: string | undefined; traceId?: string | undefined; } | undefined; }[] | undefined; apiConfig?: { connectorId: string; defaultSystemPromptId?: string | undefined; provider?: \"OpenAI\" | \"Azure OpenAI\" | undefined; model?: string | undefined; } | undefined; isDefault?: boolean | undefined; excludeFromLastConversationStorage?: boolean | undefined; }>, \"many\">; created: Zod.ZodArray; title: Zod.ZodString; category: Zod.ZodEnum<[\"assistant\", \"insights\"]>; summary: Zod.ZodOptional; timestamp: Zod.ZodOptional; public: Zod.ZodOptional; confidence: Zod.ZodOptional>; }, \"strip\", Zod.ZodTypeAny, { content?: string | undefined; timestamp?: string | undefined; public?: boolean | undefined; confidence?: \"medium\" | \"low\" | \"high\" | undefined; }, { content?: string | undefined; timestamp?: string | undefined; public?: boolean | undefined; confidence?: \"medium\" | \"low\" | \"high\" | undefined; }>>; timestamp: Zod.ZodOptional; updatedAt: Zod.ZodOptional; createdAt: Zod.ZodString; replacements: Zod.ZodOptional, \"many\">>; users: Zod.ZodArray; name: Zod.ZodOptional; }, \"strip\", Zod.ZodTypeAny, { id?: string | undefined; name?: string | undefined; }, { id?: string | undefined; name?: string | undefined; }>, \"many\">; messages: Zod.ZodOptional, Zod.objectInputType<{}, Zod.ZodUnknown, \"strip\">>>; role: Zod.ZodEnum<[\"system\", \"user\", \"assistant\"]>; timestamp: Zod.ZodString; isError: Zod.ZodOptional; traceData: Zod.ZodOptional; traceId: Zod.ZodOptional; }, \"strip\", Zod.ZodTypeAny, { transactionId?: string | undefined; traceId?: string | undefined; }, { transactionId?: string | undefined; traceId?: string | undefined; }>>; }, \"strip\", Zod.ZodTypeAny, { timestamp: string; role: \"user\" | \"system\" | \"assistant\"; content: string; reader?: Zod.objectOutputType<{}, Zod.ZodUnknown, \"strip\"> | undefined; isError?: boolean | undefined; traceData?: { transactionId?: string | undefined; traceId?: string | undefined; } | undefined; }, { timestamp: string; role: \"user\" | \"system\" | \"assistant\"; content: string; reader?: Zod.objectInputType<{}, Zod.ZodUnknown, \"strip\"> | undefined; isError?: boolean | undefined; traceData?: { transactionId?: string | undefined; traceId?: string | undefined; } | undefined; }>, \"many\">>; apiConfig: Zod.ZodOptional; provider: Zod.ZodOptional>; model: Zod.ZodOptional; }, \"strip\", Zod.ZodTypeAny, { connectorId: string; defaultSystemPromptId?: string | undefined; provider?: \"OpenAI\" | \"Azure OpenAI\" | undefined; model?: string | undefined; }, { connectorId: string; defaultSystemPromptId?: string | undefined; provider?: \"OpenAI\" | \"Azure OpenAI\" | undefined; model?: string | undefined; }>>; isDefault: Zod.ZodOptional; excludeFromLastConversationStorage: Zod.ZodOptional; namespace: Zod.ZodString; }, \"strip\", Zod.ZodTypeAny, { id: string; namespace: string; title: string; createdAt: string; category: \"assistant\" | \"insights\"; users: { id?: string | undefined; name?: string | undefined; }[]; summary?: { content?: string | undefined; timestamp?: string | undefined; public?: boolean | undefined; confidence?: \"medium\" | \"low\" | \"high\" | undefined; } | undefined; timestamp?: string | undefined; updatedAt?: string | undefined; replacements?: { value: string; uuid: string; }[] | undefined; messages?: { timestamp: string; role: \"user\" | \"system\" | \"assistant\"; content: string; reader?: Zod.objectOutputType<{}, Zod.ZodUnknown, \"strip\"> | undefined; isError?: boolean | undefined; traceData?: { transactionId?: string | undefined; traceId?: string | undefined; } | undefined; }[] | undefined; apiConfig?: { connectorId: string; defaultSystemPromptId?: string | undefined; provider?: \"OpenAI\" | \"Azure OpenAI\" | undefined; model?: string | undefined; } | undefined; isDefault?: boolean | undefined; excludeFromLastConversationStorage?: boolean | undefined; }, { id: string; namespace: string; title: string; createdAt: string; category: \"assistant\" | \"insights\"; users: { id?: string | undefined; name?: string | undefined; }[]; summary?: { content?: string | undefined; timestamp?: string | undefined; public?: boolean | undefined; confidence?: \"medium\" | \"low\" | \"high\" | undefined; } | undefined; timestamp?: string | undefined; updatedAt?: string | undefined; replacements?: { value: string; uuid: string; }[] | undefined; messages?: { timestamp: string; role: \"user\" | \"system\" | \"assistant\"; content: string; reader?: Zod.objectInputType<{}, Zod.ZodUnknown, \"strip\"> | undefined; isError?: boolean | undefined; traceData?: { transactionId?: string | undefined; traceId?: string | undefined; } | undefined; }[] | undefined; apiConfig?: { connectorId: string; defaultSystemPromptId?: string | undefined; provider?: \"OpenAI\" | \"Azure OpenAI\" | undefined; model?: string | undefined; } | undefined; isDefault?: boolean | undefined; excludeFromLastConversationStorage?: boolean | undefined; }>, \"many\">; deleted: Zod.ZodArray; skipped: Zod.ZodArray; skip_reason: Zod.ZodLiteral<\"CONVERSATION_NOT_MODIFIED\">; }, \"strip\", Zod.ZodTypeAny, { id: string; skip_reason: \"CONVERSATION_NOT_MODIFIED\"; name?: string | undefined; }, { id: string; skip_reason: \"CONVERSATION_NOT_MODIFIED\"; name?: string | undefined; }>, \"many\">; }, \"strip\", Zod.ZodTypeAny, { created: { id: string; namespace: string; title: string; createdAt: string; category: \"assistant\" | \"insights\"; users: { id?: string | undefined; name?: string | undefined; }[]; summary?: { content?: string | undefined; timestamp?: string | undefined; public?: boolean | undefined; confidence?: \"medium\" | \"low\" | \"high\" | undefined; } | undefined; timestamp?: string | undefined; updatedAt?: string | undefined; replacements?: { value: string; uuid: string; }[] | undefined; messages?: { timestamp: string; role: \"user\" | \"system\" | \"assistant\"; content: string; reader?: Zod.objectOutputType<{}, Zod.ZodUnknown, \"strip\"> | undefined; isError?: boolean | undefined; traceData?: { transactionId?: string | undefined; traceId?: string | undefined; } | undefined; }[] | undefined; apiConfig?: { connectorId: string; defaultSystemPromptId?: string | undefined; provider?: \"OpenAI\" | \"Azure OpenAI\" | undefined; model?: string | undefined; } | undefined; isDefault?: boolean | undefined; excludeFromLastConversationStorage?: boolean | undefined; }[]; updated: { id: string; namespace: string; title: string; createdAt: string; category: \"assistant\" | \"insights\"; users: { id?: string | undefined; name?: string | undefined; }[]; summary?: { content?: string | undefined; timestamp?: string | undefined; public?: boolean | undefined; confidence?: \"medium\" | \"low\" | \"high\" | undefined; } | undefined; timestamp?: string | undefined; updatedAt?: string | undefined; replacements?: { value: string; uuid: string; }[] | undefined; messages?: { timestamp: string; role: \"user\" | \"system\" | \"assistant\"; content: string; reader?: Zod.objectOutputType<{}, Zod.ZodUnknown, \"strip\"> | undefined; isError?: boolean | undefined; traceData?: { transactionId?: string | undefined; traceId?: string | undefined; } | undefined; }[] | undefined; apiConfig?: { connectorId: string; defaultSystemPromptId?: string | undefined; provider?: \"OpenAI\" | \"Azure OpenAI\" | undefined; model?: string | undefined; } | undefined; isDefault?: boolean | undefined; excludeFromLastConversationStorage?: boolean | undefined; }[]; skipped: { id: string; skip_reason: \"CONVERSATION_NOT_MODIFIED\"; name?: string | undefined; }[]; deleted: string[]; }, { created: { id: string; namespace: string; title: string; createdAt: string; category: \"assistant\" | \"insights\"; users: { id?: string | undefined; name?: string | undefined; }[]; summary?: { content?: string | undefined; timestamp?: string | undefined; public?: boolean | undefined; confidence?: \"medium\" | \"low\" | \"high\" | undefined; } | undefined; timestamp?: string | undefined; updatedAt?: string | undefined; replacements?: { value: string; uuid: string; }[] | undefined; messages?: { timestamp: string; role: \"user\" | \"system\" | \"assistant\"; content: string; reader?: Zod.objectInputType<{}, Zod.ZodUnknown, \"strip\"> | undefined; isError?: boolean | undefined; traceData?: { transactionId?: string | undefined; traceId?: string | undefined; } | undefined; }[] | undefined; apiConfig?: { connectorId: string; defaultSystemPromptId?: string | undefined; provider?: \"OpenAI\" | \"Azure OpenAI\" | undefined; model?: string | undefined; } | undefined; isDefault?: boolean | undefined; excludeFromLastConversationStorage?: boolean | undefined; }[]; updated: { id: string; namespace: string; title: string; createdAt: string; category: \"assistant\" | \"insights\"; users: { id?: string | undefined; name?: string | undefined; }[]; summary?: { content?: string | undefined; timestamp?: string | undefined; public?: boolean | undefined; confidence?: \"medium\" | \"low\" | \"high\" | undefined; } | undefined; timestamp?: string | undefined; updatedAt?: string | undefined; replacements?: { value: string; uuid: string; }[] | undefined; messages?: { timestamp: string; role: \"user\" | \"system\" | \"assistant\"; content: string; reader?: Zod.objectInputType<{}, Zod.ZodUnknown, \"strip\"> | undefined; isError?: boolean | undefined; traceData?: { transactionId?: string | undefined; traceId?: string | undefined; } | undefined; }[] | undefined; apiConfig?: { connectorId: string; defaultSystemPromptId?: string | undefined; provider?: \"OpenAI\" | \"Azure OpenAI\" | undefined; model?: string | undefined; } | undefined; isDefault?: boolean | undefined; excludeFromLastConversationStorage?: boolean | undefined; }[]; skipped: { id: string; skip_reason: \"CONVERSATION_NOT_MODIFIED\"; name?: string | undefined; }[]; deleted: string[]; }>; summary: Zod.ZodObject<{ failed: Zod.ZodNumber; skipped: Zod.ZodNumber; succeeded: Zod.ZodNumber; total: Zod.ZodNumber; }, \"strip\", Zod.ZodTypeAny, { total: number; succeeded: number; failed: number; skipped: number; }, { total: number; succeeded: number; failed: number; skipped: number; }>; errors: Zod.ZodOptional; conversations: Zod.ZodArray; }, \"strip\", Zod.ZodTypeAny, { id: string; name?: string | undefined; }, { id: string; name?: string | undefined; }>, \"many\">; }, \"strip\", Zod.ZodTypeAny, { message: string; status_code: number; conversations: { id: string; name?: string | undefined; }[]; err_code?: string | undefined; }, { message: string; status_code: number; conversations: { id: string; name?: string | undefined; }[]; err_code?: string | undefined; }>, \"many\">>; }, \"strip\", Zod.ZodTypeAny, { results: { created: { id: string; namespace: string; title: string; createdAt: string; category: \"assistant\" | \"insights\"; users: { id?: string | undefined; name?: string | undefined; }[]; summary?: { content?: string | undefined; timestamp?: string | undefined; public?: boolean | undefined; confidence?: \"medium\" | \"low\" | \"high\" | undefined; } | undefined; timestamp?: string | undefined; updatedAt?: string | undefined; replacements?: { value: string; uuid: string; }[] | undefined; messages?: { timestamp: string; role: \"user\" | \"system\" | \"assistant\"; content: string; reader?: Zod.objectOutputType<{}, Zod.ZodUnknown, \"strip\"> | undefined; isError?: boolean | undefined; traceData?: { transactionId?: string | undefined; traceId?: string | undefined; } | undefined; }[] | undefined; apiConfig?: { connectorId: string; defaultSystemPromptId?: string | undefined; provider?: \"OpenAI\" | \"Azure OpenAI\" | undefined; model?: string | undefined; } | undefined; isDefault?: boolean | undefined; excludeFromLastConversationStorage?: boolean | undefined; }[]; updated: { id: string; namespace: string; title: string; createdAt: string; category: \"assistant\" | \"insights\"; users: { id?: string | undefined; name?: string | undefined; }[]; summary?: { content?: string | undefined; timestamp?: string | undefined; public?: boolean | undefined; confidence?: \"medium\" | \"low\" | \"high\" | undefined; } | undefined; timestamp?: string | undefined; updatedAt?: string | undefined; replacements?: { value: string; uuid: string; }[] | undefined; messages?: { timestamp: string; role: \"user\" | \"system\" | \"assistant\"; content: string; reader?: Zod.objectOutputType<{}, Zod.ZodUnknown, \"strip\"> | undefined; isError?: boolean | undefined; traceData?: { transactionId?: string | undefined; traceId?: string | undefined; } | undefined; }[] | undefined; apiConfig?: { connectorId: string; defaultSystemPromptId?: string | undefined; provider?: \"OpenAI\" | \"Azure OpenAI\" | undefined; model?: string | undefined; } | undefined; isDefault?: boolean | undefined; excludeFromLastConversationStorage?: boolean | undefined; }[]; skipped: { id: string; skip_reason: \"CONVERSATION_NOT_MODIFIED\"; name?: string | undefined; }[]; deleted: string[]; }; summary: { total: number; succeeded: number; failed: number; skipped: number; }; errors?: { message: string; status_code: number; conversations: { id: string; name?: string | undefined; }[]; err_code?: string | undefined; }[] | undefined; }, { results: { created: { id: string; namespace: string; title: string; createdAt: string; category: \"assistant\" | \"insights\"; users: { id?: string | undefined; name?: string | undefined; }[]; summary?: { content?: string | undefined; timestamp?: string | undefined; public?: boolean | undefined; confidence?: \"medium\" | \"low\" | \"high\" | undefined; } | undefined; timestamp?: string | undefined; updatedAt?: string | undefined; replacements?: { value: string; uuid: string; }[] | undefined; messages?: { timestamp: string; role: \"user\" | \"system\" | \"assistant\"; content: string; reader?: Zod.objectInputType<{}, Zod.ZodUnknown, \"strip\"> | undefined; isError?: boolean | undefined; traceData?: { transactionId?: string | undefined; traceId?: string | undefined; } | undefined; }[] | undefined; apiConfig?: { connectorId: string; defaultSystemPromptId?: string | undefined; provider?: \"OpenAI\" | \"Azure OpenAI\" | undefined; model?: string | undefined; } | undefined; isDefault?: boolean | undefined; excludeFromLastConversationStorage?: boolean | undefined; }[]; updated: { id: string; namespace: string; title: string; createdAt: string; category: \"assistant\" | \"insights\"; users: { id?: string | undefined; name?: string | undefined; }[]; summary?: { content?: string | undefined; timestamp?: string | undefined; public?: boolean | undefined; confidence?: \"medium\" | \"low\" | \"high\" | undefined; } | undefined; timestamp?: string | undefined; updatedAt?: string | undefined; replacements?: { value: string; uuid: string; }[] | undefined; messages?: { timestamp: string; role: \"user\" | \"system\" | \"assistant\"; content: string; reader?: Zod.objectInputType<{}, Zod.ZodUnknown, \"strip\"> | undefined; isError?: boolean | undefined; traceData?: { transactionId?: string | undefined; traceId?: string | undefined; } | undefined; }[] | undefined; apiConfig?: { connectorId: string; defaultSystemPromptId?: string | undefined; provider?: \"OpenAI\" | \"Azure OpenAI\" | undefined; model?: string | undefined; } | undefined; isDefault?: boolean | undefined; excludeFromLastConversationStorage?: boolean | undefined; }[]; skipped: { id: string; skip_reason: \"CONVERSATION_NOT_MODIFIED\"; name?: string | undefined; }[]; deleted: string[]; }; summary: { total: number; succeeded: number; failed: number; skipped: number; }; errors?: { message: string; status_code: number; conversations: { id: string; name?: string | undefined; }[]; err_code?: string | undefined; }[] | undefined; }>; }, \"strip\", Zod.ZodTypeAny, { attributes: { results: { created: { id: string; namespace: string; title: string; createdAt: string; category: \"assistant\" | \"insights\"; users: { id?: string | undefined; name?: string | undefined; }[]; summary?: { content?: string | undefined; timestamp?: string | undefined; public?: boolean | undefined; confidence?: \"medium\" | \"low\" | \"high\" | undefined; } | undefined; timestamp?: string | undefined; updatedAt?: string | undefined; replacements?: { value: string; uuid: string; }[] | undefined; messages?: { timestamp: string; role: \"user\" | \"system\" | \"assistant\"; content: string; reader?: Zod.objectOutputType<{}, Zod.ZodUnknown, \"strip\"> | undefined; isError?: boolean | undefined; traceData?: { transactionId?: string | undefined; traceId?: string | undefined; } | undefined; }[] | undefined; apiConfig?: { connectorId: string; defaultSystemPromptId?: string | undefined; provider?: \"OpenAI\" | \"Azure OpenAI\" | undefined; model?: string | undefined; } | undefined; isDefault?: boolean | undefined; excludeFromLastConversationStorage?: boolean | undefined; }[]; updated: { id: string; namespace: string; title: string; createdAt: string; category: \"assistant\" | \"insights\"; users: { id?: string | undefined; name?: string | undefined; }[]; summary?: { content?: string | undefined; timestamp?: string | undefined; public?: boolean | undefined; confidence?: \"medium\" | \"low\" | \"high\" | undefined; } | undefined; timestamp?: string | undefined; updatedAt?: string | undefined; replacements?: { value: string; uuid: string; }[] | undefined; messages?: { timestamp: string; role: \"user\" | \"system\" | \"assistant\"; content: string; reader?: Zod.objectOutputType<{}, Zod.ZodUnknown, \"strip\"> | undefined; isError?: boolean | undefined; traceData?: { transactionId?: string | undefined; traceId?: string | undefined; } | undefined; }[] | undefined; apiConfig?: { connectorId: string; defaultSystemPromptId?: string | undefined; provider?: \"OpenAI\" | \"Azure OpenAI\" | undefined; model?: string | undefined; } | undefined; isDefault?: boolean | undefined; excludeFromLastConversationStorage?: boolean | undefined; }[]; skipped: { id: string; skip_reason: \"CONVERSATION_NOT_MODIFIED\"; name?: string | undefined; }[]; deleted: string[]; }; summary: { total: number; succeeded: number; failed: number; skipped: number; }; errors?: { message: string; status_code: number; conversations: { id: string; name?: string | undefined; }[]; err_code?: string | undefined; }[] | undefined; }; success?: boolean | undefined; status_code?: number | undefined; message?: string | undefined; conversations_count?: number | undefined; }, { attributes: { results: { created: { id: string; namespace: string; title: string; createdAt: string; category: \"assistant\" | \"insights\"; users: { id?: string | undefined; name?: string | undefined; }[]; summary?: { content?: string | undefined; timestamp?: string | undefined; public?: boolean | undefined; confidence?: \"medium\" | \"low\" | \"high\" | undefined; } | undefined; timestamp?: string | undefined; updatedAt?: string | undefined; replacements?: { value: string; uuid: string; }[] | undefined; messages?: { timestamp: string; role: \"user\" | \"system\" | \"assistant\"; content: string; reader?: Zod.objectInputType<{}, Zod.ZodUnknown, \"strip\"> | undefined; isError?: boolean | undefined; traceData?: { transactionId?: string | undefined; traceId?: string | undefined; } | undefined; }[] | undefined; apiConfig?: { connectorId: string; defaultSystemPromptId?: string | undefined; provider?: \"OpenAI\" | \"Azure OpenAI\" | undefined; model?: string | undefined; } | undefined; isDefault?: boolean | undefined; excludeFromLastConversationStorage?: boolean | undefined; }[]; updated: { id: string; namespace: string; title: string; createdAt: string; category: \"assistant\" | \"insights\"; users: { id?: string | undefined; name?: string | undefined; }[]; summary?: { content?: string | undefined; timestamp?: string | undefined; public?: boolean | undefined; confidence?: \"medium\" | \"low\" | \"high\" | undefined; } | undefined; timestamp?: string | undefined; updatedAt?: string | undefined; replacements?: { value: string; uuid: string; }[] | undefined; messages?: { timestamp: string; role: \"user\" | \"system\" | \"assistant\"; content: string; reader?: Zod.objectInputType<{}, Zod.ZodUnknown, \"strip\"> | undefined; isError?: boolean | undefined; traceData?: { transactionId?: string | undefined; traceId?: string | undefined; } | undefined; }[] | undefined; apiConfig?: { connectorId: string; defaultSystemPromptId?: string | undefined; provider?: \"OpenAI\" | \"Azure OpenAI\" | undefined; model?: string | undefined; } | undefined; isDefault?: boolean | undefined; excludeFromLastConversationStorage?: boolean | undefined; }[]; skipped: { id: string; skip_reason: \"CONVERSATION_NOT_MODIFIED\"; name?: string | undefined; }[]; deleted: string[]; }; summary: { total: number; succeeded: number; failed: number; skipped: number; }; errors?: { message: string; status_code: number; conversations: { id: string; name?: string | undefined; }[]; err_code?: string | undefined; }[] | undefined; }; success?: boolean | undefined; status_code?: number | undefined; message?: string | undefined; conversations_count?: number | undefined; }>" + "Zod.ZodObject<{ success: Zod.ZodOptional; status_code: Zod.ZodOptional; message: Zod.ZodOptional; conversations_count: Zod.ZodOptional; attributes: Zod.ZodObject<{ results: Zod.ZodObject<{ updated: Zod.ZodArray; title: Zod.ZodString; category: Zod.ZodEnum<[\"assistant\", \"insights\"]>; summary: Zod.ZodOptional; timestamp: Zod.ZodOptional; public: Zod.ZodOptional; confidence: Zod.ZodOptional>; }, \"strip\", Zod.ZodTypeAny, { content?: string | undefined; timestamp?: string | undefined; public?: boolean | undefined; confidence?: \"medium\" | \"low\" | \"high\" | undefined; }, { content?: string | undefined; timestamp?: string | undefined; public?: boolean | undefined; confidence?: \"medium\" | \"low\" | \"high\" | undefined; }>>; timestamp: Zod.ZodOptional; updatedAt: Zod.ZodOptional; createdAt: Zod.ZodString; replacements: Zod.ZodOptional, Zod.objectInputType<{}, Zod.ZodString, \"strip\">>>; users: Zod.ZodArray; name: Zod.ZodOptional; }, \"strip\", Zod.ZodTypeAny, { id?: string | undefined; name?: string | undefined; }, { id?: string | undefined; name?: string | undefined; }>, \"many\">; messages: Zod.ZodOptional, Zod.objectInputType<{}, Zod.ZodUnknown, \"strip\">>>; role: Zod.ZodEnum<[\"system\", \"user\", \"assistant\"]>; timestamp: Zod.ZodString; isError: Zod.ZodOptional; traceData: Zod.ZodOptional; traceId: Zod.ZodOptional; }, \"strip\", Zod.ZodTypeAny, { transactionId?: string | undefined; traceId?: string | undefined; }, { transactionId?: string | undefined; traceId?: string | undefined; }>>; }, \"strip\", Zod.ZodTypeAny, { timestamp: string; role: \"user\" | \"system\" | \"assistant\"; content: string; reader?: Zod.objectOutputType<{}, Zod.ZodUnknown, \"strip\"> | undefined; isError?: boolean | undefined; traceData?: { transactionId?: string | undefined; traceId?: string | undefined; } | undefined; }, { timestamp: string; role: \"user\" | \"system\" | \"assistant\"; content: string; reader?: Zod.objectInputType<{}, Zod.ZodUnknown, \"strip\"> | undefined; isError?: boolean | undefined; traceData?: { transactionId?: string | undefined; traceId?: string | undefined; } | undefined; }>, \"many\">>; apiConfig: Zod.ZodOptional; provider: Zod.ZodOptional>; model: Zod.ZodOptional; }, \"strip\", Zod.ZodTypeAny, { connectorId: string; defaultSystemPromptId?: string | undefined; provider?: \"OpenAI\" | \"Azure OpenAI\" | undefined; model?: string | undefined; }, { connectorId: string; defaultSystemPromptId?: string | undefined; provider?: \"OpenAI\" | \"Azure OpenAI\" | undefined; model?: string | undefined; }>>; isDefault: Zod.ZodOptional; excludeFromLastConversationStorage: Zod.ZodOptional; namespace: Zod.ZodString; }, \"strip\", Zod.ZodTypeAny, { id: string; namespace: string; title: string; createdAt: string; category: \"assistant\" | \"insights\"; users: { id?: string | undefined; name?: string | undefined; }[]; summary?: { content?: string | undefined; timestamp?: string | undefined; public?: boolean | undefined; confidence?: \"medium\" | \"low\" | \"high\" | undefined; } | undefined; timestamp?: string | undefined; updatedAt?: string | undefined; replacements?: Zod.objectOutputType<{}, Zod.ZodString, \"strip\"> | undefined; messages?: { timestamp: string; role: \"user\" | \"system\" | \"assistant\"; content: string; reader?: Zod.objectOutputType<{}, Zod.ZodUnknown, \"strip\"> | undefined; isError?: boolean | undefined; traceData?: { transactionId?: string | undefined; traceId?: string | undefined; } | undefined; }[] | undefined; apiConfig?: { connectorId: string; defaultSystemPromptId?: string | undefined; provider?: \"OpenAI\" | \"Azure OpenAI\" | undefined; model?: string | undefined; } | undefined; isDefault?: boolean | undefined; excludeFromLastConversationStorage?: boolean | undefined; }, { id: string; namespace: string; title: string; createdAt: string; category: \"assistant\" | \"insights\"; users: { id?: string | undefined; name?: string | undefined; }[]; summary?: { content?: string | undefined; timestamp?: string | undefined; public?: boolean | undefined; confidence?: \"medium\" | \"low\" | \"high\" | undefined; } | undefined; timestamp?: string | undefined; updatedAt?: string | undefined; replacements?: Zod.objectInputType<{}, Zod.ZodString, \"strip\"> | undefined; messages?: { timestamp: string; role: \"user\" | \"system\" | \"assistant\"; content: string; reader?: Zod.objectInputType<{}, Zod.ZodUnknown, \"strip\"> | undefined; isError?: boolean | undefined; traceData?: { transactionId?: string | undefined; traceId?: string | undefined; } | undefined; }[] | undefined; apiConfig?: { connectorId: string; defaultSystemPromptId?: string | undefined; provider?: \"OpenAI\" | \"Azure OpenAI\" | undefined; model?: string | undefined; } | undefined; isDefault?: boolean | undefined; excludeFromLastConversationStorage?: boolean | undefined; }>, \"many\">; created: Zod.ZodArray; title: Zod.ZodString; category: Zod.ZodEnum<[\"assistant\", \"insights\"]>; summary: Zod.ZodOptional; timestamp: Zod.ZodOptional; public: Zod.ZodOptional; confidence: Zod.ZodOptional>; }, \"strip\", Zod.ZodTypeAny, { content?: string | undefined; timestamp?: string | undefined; public?: boolean | undefined; confidence?: \"medium\" | \"low\" | \"high\" | undefined; }, { content?: string | undefined; timestamp?: string | undefined; public?: boolean | undefined; confidence?: \"medium\" | \"low\" | \"high\" | undefined; }>>; timestamp: Zod.ZodOptional; updatedAt: Zod.ZodOptional; createdAt: Zod.ZodString; replacements: Zod.ZodOptional, Zod.objectInputType<{}, Zod.ZodString, \"strip\">>>; users: Zod.ZodArray; name: Zod.ZodOptional; }, \"strip\", Zod.ZodTypeAny, { id?: string | undefined; name?: string | undefined; }, { id?: string | undefined; name?: string | undefined; }>, \"many\">; messages: Zod.ZodOptional, Zod.objectInputType<{}, Zod.ZodUnknown, \"strip\">>>; role: Zod.ZodEnum<[\"system\", \"user\", \"assistant\"]>; timestamp: Zod.ZodString; isError: Zod.ZodOptional; traceData: Zod.ZodOptional; traceId: Zod.ZodOptional; }, \"strip\", Zod.ZodTypeAny, { transactionId?: string | undefined; traceId?: string | undefined; }, { transactionId?: string | undefined; traceId?: string | undefined; }>>; }, \"strip\", Zod.ZodTypeAny, { timestamp: string; role: \"user\" | \"system\" | \"assistant\"; content: string; reader?: Zod.objectOutputType<{}, Zod.ZodUnknown, \"strip\"> | undefined; isError?: boolean | undefined; traceData?: { transactionId?: string | undefined; traceId?: string | undefined; } | undefined; }, { timestamp: string; role: \"user\" | \"system\" | \"assistant\"; content: string; reader?: Zod.objectInputType<{}, Zod.ZodUnknown, \"strip\"> | undefined; isError?: boolean | undefined; traceData?: { transactionId?: string | undefined; traceId?: string | undefined; } | undefined; }>, \"many\">>; apiConfig: Zod.ZodOptional; provider: Zod.ZodOptional>; model: Zod.ZodOptional; }, \"strip\", Zod.ZodTypeAny, { connectorId: string; defaultSystemPromptId?: string | undefined; provider?: \"OpenAI\" | \"Azure OpenAI\" | undefined; model?: string | undefined; }, { connectorId: string; defaultSystemPromptId?: string | undefined; provider?: \"OpenAI\" | \"Azure OpenAI\" | undefined; model?: string | undefined; }>>; isDefault: Zod.ZodOptional; excludeFromLastConversationStorage: Zod.ZodOptional; namespace: Zod.ZodString; }, \"strip\", Zod.ZodTypeAny, { id: string; namespace: string; title: string; createdAt: string; category: \"assistant\" | \"insights\"; users: { id?: string | undefined; name?: string | undefined; }[]; summary?: { content?: string | undefined; timestamp?: string | undefined; public?: boolean | undefined; confidence?: \"medium\" | \"low\" | \"high\" | undefined; } | undefined; timestamp?: string | undefined; updatedAt?: string | undefined; replacements?: Zod.objectOutputType<{}, Zod.ZodString, \"strip\"> | undefined; messages?: { timestamp: string; role: \"user\" | \"system\" | \"assistant\"; content: string; reader?: Zod.objectOutputType<{}, Zod.ZodUnknown, \"strip\"> | undefined; isError?: boolean | undefined; traceData?: { transactionId?: string | undefined; traceId?: string | undefined; } | undefined; }[] | undefined; apiConfig?: { connectorId: string; defaultSystemPromptId?: string | undefined; provider?: \"OpenAI\" | \"Azure OpenAI\" | undefined; model?: string | undefined; } | undefined; isDefault?: boolean | undefined; excludeFromLastConversationStorage?: boolean | undefined; }, { id: string; namespace: string; title: string; createdAt: string; category: \"assistant\" | \"insights\"; users: { id?: string | undefined; name?: string | undefined; }[]; summary?: { content?: string | undefined; timestamp?: string | undefined; public?: boolean | undefined; confidence?: \"medium\" | \"low\" | \"high\" | undefined; } | undefined; timestamp?: string | undefined; updatedAt?: string | undefined; replacements?: Zod.objectInputType<{}, Zod.ZodString, \"strip\"> | undefined; messages?: { timestamp: string; role: \"user\" | \"system\" | \"assistant\"; content: string; reader?: Zod.objectInputType<{}, Zod.ZodUnknown, \"strip\"> | undefined; isError?: boolean | undefined; traceData?: { transactionId?: string | undefined; traceId?: string | undefined; } | undefined; }[] | undefined; apiConfig?: { connectorId: string; defaultSystemPromptId?: string | undefined; provider?: \"OpenAI\" | \"Azure OpenAI\" | undefined; model?: string | undefined; } | undefined; isDefault?: boolean | undefined; excludeFromLastConversationStorage?: boolean | undefined; }>, \"many\">; deleted: Zod.ZodArray; skipped: Zod.ZodArray; skip_reason: Zod.ZodLiteral<\"CONVERSATION_NOT_MODIFIED\">; }, \"strip\", Zod.ZodTypeAny, { id: string; skip_reason: \"CONVERSATION_NOT_MODIFIED\"; name?: string | undefined; }, { id: string; skip_reason: \"CONVERSATION_NOT_MODIFIED\"; name?: string | undefined; }>, \"many\">; }, \"strip\", Zod.ZodTypeAny, { created: { id: string; namespace: string; title: string; createdAt: string; category: \"assistant\" | \"insights\"; users: { id?: string | undefined; name?: string | undefined; }[]; summary?: { content?: string | undefined; timestamp?: string | undefined; public?: boolean | undefined; confidence?: \"medium\" | \"low\" | \"high\" | undefined; } | undefined; timestamp?: string | undefined; updatedAt?: string | undefined; replacements?: Zod.objectOutputType<{}, Zod.ZodString, \"strip\"> | undefined; messages?: { timestamp: string; role: \"user\" | \"system\" | \"assistant\"; content: string; reader?: Zod.objectOutputType<{}, Zod.ZodUnknown, \"strip\"> | undefined; isError?: boolean | undefined; traceData?: { transactionId?: string | undefined; traceId?: string | undefined; } | undefined; }[] | undefined; apiConfig?: { connectorId: string; defaultSystemPromptId?: string | undefined; provider?: \"OpenAI\" | \"Azure OpenAI\" | undefined; model?: string | undefined; } | undefined; isDefault?: boolean | undefined; excludeFromLastConversationStorage?: boolean | undefined; }[]; updated: { id: string; namespace: string; title: string; createdAt: string; category: \"assistant\" | \"insights\"; users: { id?: string | undefined; name?: string | undefined; }[]; summary?: { content?: string | undefined; timestamp?: string | undefined; public?: boolean | undefined; confidence?: \"medium\" | \"low\" | \"high\" | undefined; } | undefined; timestamp?: string | undefined; updatedAt?: string | undefined; replacements?: Zod.objectOutputType<{}, Zod.ZodString, \"strip\"> | undefined; messages?: { timestamp: string; role: \"user\" | \"system\" | \"assistant\"; content: string; reader?: Zod.objectOutputType<{}, Zod.ZodUnknown, \"strip\"> | undefined; isError?: boolean | undefined; traceData?: { transactionId?: string | undefined; traceId?: string | undefined; } | undefined; }[] | undefined; apiConfig?: { connectorId: string; defaultSystemPromptId?: string | undefined; provider?: \"OpenAI\" | \"Azure OpenAI\" | undefined; model?: string | undefined; } | undefined; isDefault?: boolean | undefined; excludeFromLastConversationStorage?: boolean | undefined; }[]; skipped: { id: string; skip_reason: \"CONVERSATION_NOT_MODIFIED\"; name?: string | undefined; }[]; deleted: string[]; }, { created: { id: string; namespace: string; title: string; createdAt: string; category: \"assistant\" | \"insights\"; users: { id?: string | undefined; name?: string | undefined; }[]; summary?: { content?: string | undefined; timestamp?: string | undefined; public?: boolean | undefined; confidence?: \"medium\" | \"low\" | \"high\" | undefined; } | undefined; timestamp?: string | undefined; updatedAt?: string | undefined; replacements?: Zod.objectInputType<{}, Zod.ZodString, \"strip\"> | undefined; messages?: { timestamp: string; role: \"user\" | \"system\" | \"assistant\"; content: string; reader?: Zod.objectInputType<{}, Zod.ZodUnknown, \"strip\"> | undefined; isError?: boolean | undefined; traceData?: { transactionId?: string | undefined; traceId?: string | undefined; } | undefined; }[] | undefined; apiConfig?: { connectorId: string; defaultSystemPromptId?: string | undefined; provider?: \"OpenAI\" | \"Azure OpenAI\" | undefined; model?: string | undefined; } | undefined; isDefault?: boolean | undefined; excludeFromLastConversationStorage?: boolean | undefined; }[]; updated: { id: string; namespace: string; title: string; createdAt: string; category: \"assistant\" | \"insights\"; users: { id?: string | undefined; name?: string | undefined; }[]; summary?: { content?: string | undefined; timestamp?: string | undefined; public?: boolean | undefined; confidence?: \"medium\" | \"low\" | \"high\" | undefined; } | undefined; timestamp?: string | undefined; updatedAt?: string | undefined; replacements?: Zod.objectInputType<{}, Zod.ZodString, \"strip\"> | undefined; messages?: { timestamp: string; role: \"user\" | \"system\" | \"assistant\"; content: string; reader?: Zod.objectInputType<{}, Zod.ZodUnknown, \"strip\"> | undefined; isError?: boolean | undefined; traceData?: { transactionId?: string | undefined; traceId?: string | undefined; } | undefined; }[] | undefined; apiConfig?: { connectorId: string; defaultSystemPromptId?: string | undefined; provider?: \"OpenAI\" | \"Azure OpenAI\" | undefined; model?: string | undefined; } | undefined; isDefault?: boolean | undefined; excludeFromLastConversationStorage?: boolean | undefined; }[]; skipped: { id: string; skip_reason: \"CONVERSATION_NOT_MODIFIED\"; name?: string | undefined; }[]; deleted: string[]; }>; summary: Zod.ZodObject<{ failed: Zod.ZodNumber; skipped: Zod.ZodNumber; succeeded: Zod.ZodNumber; total: Zod.ZodNumber; }, \"strip\", Zod.ZodTypeAny, { total: number; succeeded: number; failed: number; skipped: number; }, { total: number; succeeded: number; failed: number; skipped: number; }>; errors: Zod.ZodOptional; conversations: Zod.ZodArray; }, \"strip\", Zod.ZodTypeAny, { id: string; name?: string | undefined; }, { id: string; name?: string | undefined; }>, \"many\">; }, \"strip\", Zod.ZodTypeAny, { message: string; status_code: number; conversations: { id: string; name?: string | undefined; }[]; err_code?: string | undefined; }, { message: string; status_code: number; conversations: { id: string; name?: string | undefined; }[]; err_code?: string | undefined; }>, \"many\">>; }, \"strip\", Zod.ZodTypeAny, { results: { created: { id: string; namespace: string; title: string; createdAt: string; category: \"assistant\" | \"insights\"; users: { id?: string | undefined; name?: string | undefined; }[]; summary?: { content?: string | undefined; timestamp?: string | undefined; public?: boolean | undefined; confidence?: \"medium\" | \"low\" | \"high\" | undefined; } | undefined; timestamp?: string | undefined; updatedAt?: string | undefined; replacements?: Zod.objectOutputType<{}, Zod.ZodString, \"strip\"> | undefined; messages?: { timestamp: string; role: \"user\" | \"system\" | \"assistant\"; content: string; reader?: Zod.objectOutputType<{}, Zod.ZodUnknown, \"strip\"> | undefined; isError?: boolean | undefined; traceData?: { transactionId?: string | undefined; traceId?: string | undefined; } | undefined; }[] | undefined; apiConfig?: { connectorId: string; defaultSystemPromptId?: string | undefined; provider?: \"OpenAI\" | \"Azure OpenAI\" | undefined; model?: string | undefined; } | undefined; isDefault?: boolean | undefined; excludeFromLastConversationStorage?: boolean | undefined; }[]; updated: { id: string; namespace: string; title: string; createdAt: string; category: \"assistant\" | \"insights\"; users: { id?: string | undefined; name?: string | undefined; }[]; summary?: { content?: string | undefined; timestamp?: string | undefined; public?: boolean | undefined; confidence?: \"medium\" | \"low\" | \"high\" | undefined; } | undefined; timestamp?: string | undefined; updatedAt?: string | undefined; replacements?: Zod.objectOutputType<{}, Zod.ZodString, \"strip\"> | undefined; messages?: { timestamp: string; role: \"user\" | \"system\" | \"assistant\"; content: string; reader?: Zod.objectOutputType<{}, Zod.ZodUnknown, \"strip\"> | undefined; isError?: boolean | undefined; traceData?: { transactionId?: string | undefined; traceId?: string | undefined; } | undefined; }[] | undefined; apiConfig?: { connectorId: string; defaultSystemPromptId?: string | undefined; provider?: \"OpenAI\" | \"Azure OpenAI\" | undefined; model?: string | undefined; } | undefined; isDefault?: boolean | undefined; excludeFromLastConversationStorage?: boolean | undefined; }[]; skipped: { id: string; skip_reason: \"CONVERSATION_NOT_MODIFIED\"; name?: string | undefined; }[]; deleted: string[]; }; summary: { total: number; succeeded: number; failed: number; skipped: number; }; errors?: { message: string; status_code: number; conversations: { id: string; name?: string | undefined; }[]; err_code?: string | undefined; }[] | undefined; }, { results: { created: { id: string; namespace: string; title: string; createdAt: string; category: \"assistant\" | \"insights\"; users: { id?: string | undefined; name?: string | undefined; }[]; summary?: { content?: string | undefined; timestamp?: string | undefined; public?: boolean | undefined; confidence?: \"medium\" | \"low\" | \"high\" | undefined; } | undefined; timestamp?: string | undefined; updatedAt?: string | undefined; replacements?: Zod.objectInputType<{}, Zod.ZodString, \"strip\"> | undefined; messages?: { timestamp: string; role: \"user\" | \"system\" | \"assistant\"; content: string; reader?: Zod.objectInputType<{}, Zod.ZodUnknown, \"strip\"> | undefined; isError?: boolean | undefined; traceData?: { transactionId?: string | undefined; traceId?: string | undefined; } | undefined; }[] | undefined; apiConfig?: { connectorId: string; defaultSystemPromptId?: string | undefined; provider?: \"OpenAI\" | \"Azure OpenAI\" | undefined; model?: string | undefined; } | undefined; isDefault?: boolean | undefined; excludeFromLastConversationStorage?: boolean | undefined; }[]; updated: { id: string; namespace: string; title: string; createdAt: string; category: \"assistant\" | \"insights\"; users: { id?: string | undefined; name?: string | undefined; }[]; summary?: { content?: string | undefined; timestamp?: string | undefined; public?: boolean | undefined; confidence?: \"medium\" | \"low\" | \"high\" | undefined; } | undefined; timestamp?: string | undefined; updatedAt?: string | undefined; replacements?: Zod.objectInputType<{}, Zod.ZodString, \"strip\"> | undefined; messages?: { timestamp: string; role: \"user\" | \"system\" | \"assistant\"; content: string; reader?: Zod.objectInputType<{}, Zod.ZodUnknown, \"strip\"> | undefined; isError?: boolean | undefined; traceData?: { transactionId?: string | undefined; traceId?: string | undefined; } | undefined; }[] | undefined; apiConfig?: { connectorId: string; defaultSystemPromptId?: string | undefined; provider?: \"OpenAI\" | \"Azure OpenAI\" | undefined; model?: string | undefined; } | undefined; isDefault?: boolean | undefined; excludeFromLastConversationStorage?: boolean | undefined; }[]; skipped: { id: string; skip_reason: \"CONVERSATION_NOT_MODIFIED\"; name?: string | undefined; }[]; deleted: string[]; }; summary: { total: number; succeeded: number; failed: number; skipped: number; }; errors?: { message: string; status_code: number; conversations: { id: string; name?: string | undefined; }[]; err_code?: string | undefined; }[] | undefined; }>; }, \"strip\", Zod.ZodTypeAny, { attributes: { results: { created: { id: string; namespace: string; title: string; createdAt: string; category: \"assistant\" | \"insights\"; users: { id?: string | undefined; name?: string | undefined; }[]; summary?: { content?: string | undefined; timestamp?: string | undefined; public?: boolean | undefined; confidence?: \"medium\" | \"low\" | \"high\" | undefined; } | undefined; timestamp?: string | undefined; updatedAt?: string | undefined; replacements?: Zod.objectOutputType<{}, Zod.ZodString, \"strip\"> | undefined; messages?: { timestamp: string; role: \"user\" | \"system\" | \"assistant\"; content: string; reader?: Zod.objectOutputType<{}, Zod.ZodUnknown, \"strip\"> | undefined; isError?: boolean | undefined; traceData?: { transactionId?: string | undefined; traceId?: string | undefined; } | undefined; }[] | undefined; apiConfig?: { connectorId: string; defaultSystemPromptId?: string | undefined; provider?: \"OpenAI\" | \"Azure OpenAI\" | undefined; model?: string | undefined; } | undefined; isDefault?: boolean | undefined; excludeFromLastConversationStorage?: boolean | undefined; }[]; updated: { id: string; namespace: string; title: string; createdAt: string; category: \"assistant\" | \"insights\"; users: { id?: string | undefined; name?: string | undefined; }[]; summary?: { content?: string | undefined; timestamp?: string | undefined; public?: boolean | undefined; confidence?: \"medium\" | \"low\" | \"high\" | undefined; } | undefined; timestamp?: string | undefined; updatedAt?: string | undefined; replacements?: Zod.objectOutputType<{}, Zod.ZodString, \"strip\"> | undefined; messages?: { timestamp: string; role: \"user\" | \"system\" | \"assistant\"; content: string; reader?: Zod.objectOutputType<{}, Zod.ZodUnknown, \"strip\"> | undefined; isError?: boolean | undefined; traceData?: { transactionId?: string | undefined; traceId?: string | undefined; } | undefined; }[] | undefined; apiConfig?: { connectorId: string; defaultSystemPromptId?: string | undefined; provider?: \"OpenAI\" | \"Azure OpenAI\" | undefined; model?: string | undefined; } | undefined; isDefault?: boolean | undefined; excludeFromLastConversationStorage?: boolean | undefined; }[]; skipped: { id: string; skip_reason: \"CONVERSATION_NOT_MODIFIED\"; name?: string | undefined; }[]; deleted: string[]; }; summary: { total: number; succeeded: number; failed: number; skipped: number; }; errors?: { message: string; status_code: number; conversations: { id: string; name?: string | undefined; }[]; err_code?: string | undefined; }[] | undefined; }; success?: boolean | undefined; status_code?: number | undefined; message?: string | undefined; conversations_count?: number | undefined; }, { attributes: { results: { created: { id: string; namespace: string; title: string; createdAt: string; category: \"assistant\" | \"insights\"; users: { id?: string | undefined; name?: string | undefined; }[]; summary?: { content?: string | undefined; timestamp?: string | undefined; public?: boolean | undefined; confidence?: \"medium\" | \"low\" | \"high\" | undefined; } | undefined; timestamp?: string | undefined; updatedAt?: string | undefined; replacements?: Zod.objectInputType<{}, Zod.ZodString, \"strip\"> | undefined; messages?: { timestamp: string; role: \"user\" | \"system\" | \"assistant\"; content: string; reader?: Zod.objectInputType<{}, Zod.ZodUnknown, \"strip\"> | undefined; isError?: boolean | undefined; traceData?: { transactionId?: string | undefined; traceId?: string | undefined; } | undefined; }[] | undefined; apiConfig?: { connectorId: string; defaultSystemPromptId?: string | undefined; provider?: \"OpenAI\" | \"Azure OpenAI\" | undefined; model?: string | undefined; } | undefined; isDefault?: boolean | undefined; excludeFromLastConversationStorage?: boolean | undefined; }[]; updated: { id: string; namespace: string; title: string; createdAt: string; category: \"assistant\" | \"insights\"; users: { id?: string | undefined; name?: string | undefined; }[]; summary?: { content?: string | undefined; timestamp?: string | undefined; public?: boolean | undefined; confidence?: \"medium\" | \"low\" | \"high\" | undefined; } | undefined; timestamp?: string | undefined; updatedAt?: string | undefined; replacements?: Zod.objectInputType<{}, Zod.ZodString, \"strip\"> | undefined; messages?: { timestamp: string; role: \"user\" | \"system\" | \"assistant\"; content: string; reader?: Zod.objectInputType<{}, Zod.ZodUnknown, \"strip\"> | undefined; isError?: boolean | undefined; traceData?: { transactionId?: string | undefined; traceId?: string | undefined; } | undefined; }[] | undefined; apiConfig?: { connectorId: string; defaultSystemPromptId?: string | undefined; provider?: \"OpenAI\" | \"Azure OpenAI\" | undefined; model?: string | undefined; } | undefined; isDefault?: boolean | undefined; excludeFromLastConversationStorage?: boolean | undefined; }[]; skipped: { id: string; skip_reason: \"CONVERSATION_NOT_MODIFIED\"; name?: string | undefined; }[]; deleted: string[]; }; summary: { total: number; succeeded: number; failed: number; skipped: number; }; errors?: { message: string; status_code: number; conversations: { id: string; name?: string | undefined; }[]; err_code?: string | undefined; }[] | undefined; }; success?: boolean | undefined; status_code?: number | undefined; message?: string | undefined; conversations_count?: number | undefined; }>" ], "path": "x-pack/packages/kbn-elastic-assistant-common/impl/schemas/conversations/bulk_crud_conversations_route.gen.ts", "deprecated": false, @@ -2448,7 +2448,7 @@ "label": "BulkCrudActionResults", "description": [], "signature": [ - "Zod.ZodObject<{ updated: Zod.ZodArray; title: Zod.ZodString; category: Zod.ZodEnum<[\"assistant\", \"insights\"]>; summary: Zod.ZodOptional; timestamp: Zod.ZodOptional; public: Zod.ZodOptional; confidence: Zod.ZodOptional>; }, \"strip\", Zod.ZodTypeAny, { content?: string | undefined; timestamp?: string | undefined; public?: boolean | undefined; confidence?: \"medium\" | \"low\" | \"high\" | undefined; }, { content?: string | undefined; timestamp?: string | undefined; public?: boolean | undefined; confidence?: \"medium\" | \"low\" | \"high\" | undefined; }>>; timestamp: Zod.ZodOptional; updatedAt: Zod.ZodOptional; createdAt: Zod.ZodString; replacements: Zod.ZodOptional, \"many\">>; users: Zod.ZodArray; name: Zod.ZodOptional; }, \"strip\", Zod.ZodTypeAny, { id?: string | undefined; name?: string | undefined; }, { id?: string | undefined; name?: string | undefined; }>, \"many\">; messages: Zod.ZodOptional, Zod.objectInputType<{}, Zod.ZodUnknown, \"strip\">>>; role: Zod.ZodEnum<[\"system\", \"user\", \"assistant\"]>; timestamp: Zod.ZodString; isError: Zod.ZodOptional; traceData: Zod.ZodOptional; traceId: Zod.ZodOptional; }, \"strip\", Zod.ZodTypeAny, { transactionId?: string | undefined; traceId?: string | undefined; }, { transactionId?: string | undefined; traceId?: string | undefined; }>>; }, \"strip\", Zod.ZodTypeAny, { timestamp: string; role: \"user\" | \"system\" | \"assistant\"; content: string; reader?: Zod.objectOutputType<{}, Zod.ZodUnknown, \"strip\"> | undefined; isError?: boolean | undefined; traceData?: { transactionId?: string | undefined; traceId?: string | undefined; } | undefined; }, { timestamp: string; role: \"user\" | \"system\" | \"assistant\"; content: string; reader?: Zod.objectInputType<{}, Zod.ZodUnknown, \"strip\"> | undefined; isError?: boolean | undefined; traceData?: { transactionId?: string | undefined; traceId?: string | undefined; } | undefined; }>, \"many\">>; apiConfig: Zod.ZodOptional; provider: Zod.ZodOptional>; model: Zod.ZodOptional; }, \"strip\", Zod.ZodTypeAny, { connectorId: string; defaultSystemPromptId?: string | undefined; provider?: \"OpenAI\" | \"Azure OpenAI\" | undefined; model?: string | undefined; }, { connectorId: string; defaultSystemPromptId?: string | undefined; provider?: \"OpenAI\" | \"Azure OpenAI\" | undefined; model?: string | undefined; }>>; isDefault: Zod.ZodOptional; excludeFromLastConversationStorage: Zod.ZodOptional; namespace: Zod.ZodString; }, \"strip\", Zod.ZodTypeAny, { id: string; namespace: string; title: string; createdAt: string; category: \"assistant\" | \"insights\"; users: { id?: string | undefined; name?: string | undefined; }[]; summary?: { content?: string | undefined; timestamp?: string | undefined; public?: boolean | undefined; confidence?: \"medium\" | \"low\" | \"high\" | undefined; } | undefined; timestamp?: string | undefined; updatedAt?: string | undefined; replacements?: { value: string; uuid: string; }[] | undefined; messages?: { timestamp: string; role: \"user\" | \"system\" | \"assistant\"; content: string; reader?: Zod.objectOutputType<{}, Zod.ZodUnknown, \"strip\"> | undefined; isError?: boolean | undefined; traceData?: { transactionId?: string | undefined; traceId?: string | undefined; } | undefined; }[] | undefined; apiConfig?: { connectorId: string; defaultSystemPromptId?: string | undefined; provider?: \"OpenAI\" | \"Azure OpenAI\" | undefined; model?: string | undefined; } | undefined; isDefault?: boolean | undefined; excludeFromLastConversationStorage?: boolean | undefined; }, { id: string; namespace: string; title: string; createdAt: string; category: \"assistant\" | \"insights\"; users: { id?: string | undefined; name?: string | undefined; }[]; summary?: { content?: string | undefined; timestamp?: string | undefined; public?: boolean | undefined; confidence?: \"medium\" | \"low\" | \"high\" | undefined; } | undefined; timestamp?: string | undefined; updatedAt?: string | undefined; replacements?: { value: string; uuid: string; }[] | undefined; messages?: { timestamp: string; role: \"user\" | \"system\" | \"assistant\"; content: string; reader?: Zod.objectInputType<{}, Zod.ZodUnknown, \"strip\"> | undefined; isError?: boolean | undefined; traceData?: { transactionId?: string | undefined; traceId?: string | undefined; } | undefined; }[] | undefined; apiConfig?: { connectorId: string; defaultSystemPromptId?: string | undefined; provider?: \"OpenAI\" | \"Azure OpenAI\" | undefined; model?: string | undefined; } | undefined; isDefault?: boolean | undefined; excludeFromLastConversationStorage?: boolean | undefined; }>, \"many\">; created: Zod.ZodArray; title: Zod.ZodString; category: Zod.ZodEnum<[\"assistant\", \"insights\"]>; summary: Zod.ZodOptional; timestamp: Zod.ZodOptional; public: Zod.ZodOptional; confidence: Zod.ZodOptional>; }, \"strip\", Zod.ZodTypeAny, { content?: string | undefined; timestamp?: string | undefined; public?: boolean | undefined; confidence?: \"medium\" | \"low\" | \"high\" | undefined; }, { content?: string | undefined; timestamp?: string | undefined; public?: boolean | undefined; confidence?: \"medium\" | \"low\" | \"high\" | undefined; }>>; timestamp: Zod.ZodOptional; updatedAt: Zod.ZodOptional; createdAt: Zod.ZodString; replacements: Zod.ZodOptional, \"many\">>; users: Zod.ZodArray; name: Zod.ZodOptional; }, \"strip\", Zod.ZodTypeAny, { id?: string | undefined; name?: string | undefined; }, { id?: string | undefined; name?: string | undefined; }>, \"many\">; messages: Zod.ZodOptional, Zod.objectInputType<{}, Zod.ZodUnknown, \"strip\">>>; role: Zod.ZodEnum<[\"system\", \"user\", \"assistant\"]>; timestamp: Zod.ZodString; isError: Zod.ZodOptional; traceData: Zod.ZodOptional; traceId: Zod.ZodOptional; }, \"strip\", Zod.ZodTypeAny, { transactionId?: string | undefined; traceId?: string | undefined; }, { transactionId?: string | undefined; traceId?: string | undefined; }>>; }, \"strip\", Zod.ZodTypeAny, { timestamp: string; role: \"user\" | \"system\" | \"assistant\"; content: string; reader?: Zod.objectOutputType<{}, Zod.ZodUnknown, \"strip\"> | undefined; isError?: boolean | undefined; traceData?: { transactionId?: string | undefined; traceId?: string | undefined; } | undefined; }, { timestamp: string; role: \"user\" | \"system\" | \"assistant\"; content: string; reader?: Zod.objectInputType<{}, Zod.ZodUnknown, \"strip\"> | undefined; isError?: boolean | undefined; traceData?: { transactionId?: string | undefined; traceId?: string | undefined; } | undefined; }>, \"many\">>; apiConfig: Zod.ZodOptional; provider: Zod.ZodOptional>; model: Zod.ZodOptional; }, \"strip\", Zod.ZodTypeAny, { connectorId: string; defaultSystemPromptId?: string | undefined; provider?: \"OpenAI\" | \"Azure OpenAI\" | undefined; model?: string | undefined; }, { connectorId: string; defaultSystemPromptId?: string | undefined; provider?: \"OpenAI\" | \"Azure OpenAI\" | undefined; model?: string | undefined; }>>; isDefault: Zod.ZodOptional; excludeFromLastConversationStorage: Zod.ZodOptional; namespace: Zod.ZodString; }, \"strip\", Zod.ZodTypeAny, { id: string; namespace: string; title: string; createdAt: string; category: \"assistant\" | \"insights\"; users: { id?: string | undefined; name?: string | undefined; }[]; summary?: { content?: string | undefined; timestamp?: string | undefined; public?: boolean | undefined; confidence?: \"medium\" | \"low\" | \"high\" | undefined; } | undefined; timestamp?: string | undefined; updatedAt?: string | undefined; replacements?: { value: string; uuid: string; }[] | undefined; messages?: { timestamp: string; role: \"user\" | \"system\" | \"assistant\"; content: string; reader?: Zod.objectOutputType<{}, Zod.ZodUnknown, \"strip\"> | undefined; isError?: boolean | undefined; traceData?: { transactionId?: string | undefined; traceId?: string | undefined; } | undefined; }[] | undefined; apiConfig?: { connectorId: string; defaultSystemPromptId?: string | undefined; provider?: \"OpenAI\" | \"Azure OpenAI\" | undefined; model?: string | undefined; } | undefined; isDefault?: boolean | undefined; excludeFromLastConversationStorage?: boolean | undefined; }, { id: string; namespace: string; title: string; createdAt: string; category: \"assistant\" | \"insights\"; users: { id?: string | undefined; name?: string | undefined; }[]; summary?: { content?: string | undefined; timestamp?: string | undefined; public?: boolean | undefined; confidence?: \"medium\" | \"low\" | \"high\" | undefined; } | undefined; timestamp?: string | undefined; updatedAt?: string | undefined; replacements?: { value: string; uuid: string; }[] | undefined; messages?: { timestamp: string; role: \"user\" | \"system\" | \"assistant\"; content: string; reader?: Zod.objectInputType<{}, Zod.ZodUnknown, \"strip\"> | undefined; isError?: boolean | undefined; traceData?: { transactionId?: string | undefined; traceId?: string | undefined; } | undefined; }[] | undefined; apiConfig?: { connectorId: string; defaultSystemPromptId?: string | undefined; provider?: \"OpenAI\" | \"Azure OpenAI\" | undefined; model?: string | undefined; } | undefined; isDefault?: boolean | undefined; excludeFromLastConversationStorage?: boolean | undefined; }>, \"many\">; deleted: Zod.ZodArray; skipped: Zod.ZodArray; skip_reason: Zod.ZodLiteral<\"CONVERSATION_NOT_MODIFIED\">; }, \"strip\", Zod.ZodTypeAny, { id: string; skip_reason: \"CONVERSATION_NOT_MODIFIED\"; name?: string | undefined; }, { id: string; skip_reason: \"CONVERSATION_NOT_MODIFIED\"; name?: string | undefined; }>, \"many\">; }, \"strip\", Zod.ZodTypeAny, { created: { id: string; namespace: string; title: string; createdAt: string; category: \"assistant\" | \"insights\"; users: { id?: string | undefined; name?: string | undefined; }[]; summary?: { content?: string | undefined; timestamp?: string | undefined; public?: boolean | undefined; confidence?: \"medium\" | \"low\" | \"high\" | undefined; } | undefined; timestamp?: string | undefined; updatedAt?: string | undefined; replacements?: { value: string; uuid: string; }[] | undefined; messages?: { timestamp: string; role: \"user\" | \"system\" | \"assistant\"; content: string; reader?: Zod.objectOutputType<{}, Zod.ZodUnknown, \"strip\"> | undefined; isError?: boolean | undefined; traceData?: { transactionId?: string | undefined; traceId?: string | undefined; } | undefined; }[] | undefined; apiConfig?: { connectorId: string; defaultSystemPromptId?: string | undefined; provider?: \"OpenAI\" | \"Azure OpenAI\" | undefined; model?: string | undefined; } | undefined; isDefault?: boolean | undefined; excludeFromLastConversationStorage?: boolean | undefined; }[]; updated: { id: string; namespace: string; title: string; createdAt: string; category: \"assistant\" | \"insights\"; users: { id?: string | undefined; name?: string | undefined; }[]; summary?: { content?: string | undefined; timestamp?: string | undefined; public?: boolean | undefined; confidence?: \"medium\" | \"low\" | \"high\" | undefined; } | undefined; timestamp?: string | undefined; updatedAt?: string | undefined; replacements?: { value: string; uuid: string; }[] | undefined; messages?: { timestamp: string; role: \"user\" | \"system\" | \"assistant\"; content: string; reader?: Zod.objectOutputType<{}, Zod.ZodUnknown, \"strip\"> | undefined; isError?: boolean | undefined; traceData?: { transactionId?: string | undefined; traceId?: string | undefined; } | undefined; }[] | undefined; apiConfig?: { connectorId: string; defaultSystemPromptId?: string | undefined; provider?: \"OpenAI\" | \"Azure OpenAI\" | undefined; model?: string | undefined; } | undefined; isDefault?: boolean | undefined; excludeFromLastConversationStorage?: boolean | undefined; }[]; skipped: { id: string; skip_reason: \"CONVERSATION_NOT_MODIFIED\"; name?: string | undefined; }[]; deleted: string[]; }, { created: { id: string; namespace: string; title: string; createdAt: string; category: \"assistant\" | \"insights\"; users: { id?: string | undefined; name?: string | undefined; }[]; summary?: { content?: string | undefined; timestamp?: string | undefined; public?: boolean | undefined; confidence?: \"medium\" | \"low\" | \"high\" | undefined; } | undefined; timestamp?: string | undefined; updatedAt?: string | undefined; replacements?: { value: string; uuid: string; }[] | undefined; messages?: { timestamp: string; role: \"user\" | \"system\" | \"assistant\"; content: string; reader?: Zod.objectInputType<{}, Zod.ZodUnknown, \"strip\"> | undefined; isError?: boolean | undefined; traceData?: { transactionId?: string | undefined; traceId?: string | undefined; } | undefined; }[] | undefined; apiConfig?: { connectorId: string; defaultSystemPromptId?: string | undefined; provider?: \"OpenAI\" | \"Azure OpenAI\" | undefined; model?: string | undefined; } | undefined; isDefault?: boolean | undefined; excludeFromLastConversationStorage?: boolean | undefined; }[]; updated: { id: string; namespace: string; title: string; createdAt: string; category: \"assistant\" | \"insights\"; users: { id?: string | undefined; name?: string | undefined; }[]; summary?: { content?: string | undefined; timestamp?: string | undefined; public?: boolean | undefined; confidence?: \"medium\" | \"low\" | \"high\" | undefined; } | undefined; timestamp?: string | undefined; updatedAt?: string | undefined; replacements?: { value: string; uuid: string; }[] | undefined; messages?: { timestamp: string; role: \"user\" | \"system\" | \"assistant\"; content: string; reader?: Zod.objectInputType<{}, Zod.ZodUnknown, \"strip\"> | undefined; isError?: boolean | undefined; traceData?: { transactionId?: string | undefined; traceId?: string | undefined; } | undefined; }[] | undefined; apiConfig?: { connectorId: string; defaultSystemPromptId?: string | undefined; provider?: \"OpenAI\" | \"Azure OpenAI\" | undefined; model?: string | undefined; } | undefined; isDefault?: boolean | undefined; excludeFromLastConversationStorage?: boolean | undefined; }[]; skipped: { id: string; skip_reason: \"CONVERSATION_NOT_MODIFIED\"; name?: string | undefined; }[]; deleted: string[]; }>" + "Zod.ZodObject<{ updated: Zod.ZodArray; title: Zod.ZodString; category: Zod.ZodEnum<[\"assistant\", \"insights\"]>; summary: Zod.ZodOptional; timestamp: Zod.ZodOptional; public: Zod.ZodOptional; confidence: Zod.ZodOptional>; }, \"strip\", Zod.ZodTypeAny, { content?: string | undefined; timestamp?: string | undefined; public?: boolean | undefined; confidence?: \"medium\" | \"low\" | \"high\" | undefined; }, { content?: string | undefined; timestamp?: string | undefined; public?: boolean | undefined; confidence?: \"medium\" | \"low\" | \"high\" | undefined; }>>; timestamp: Zod.ZodOptional; updatedAt: Zod.ZodOptional; createdAt: Zod.ZodString; replacements: Zod.ZodOptional, Zod.objectInputType<{}, Zod.ZodString, \"strip\">>>; users: Zod.ZodArray; name: Zod.ZodOptional; }, \"strip\", Zod.ZodTypeAny, { id?: string | undefined; name?: string | undefined; }, { id?: string | undefined; name?: string | undefined; }>, \"many\">; messages: Zod.ZodOptional, Zod.objectInputType<{}, Zod.ZodUnknown, \"strip\">>>; role: Zod.ZodEnum<[\"system\", \"user\", \"assistant\"]>; timestamp: Zod.ZodString; isError: Zod.ZodOptional; traceData: Zod.ZodOptional; traceId: Zod.ZodOptional; }, \"strip\", Zod.ZodTypeAny, { transactionId?: string | undefined; traceId?: string | undefined; }, { transactionId?: string | undefined; traceId?: string | undefined; }>>; }, \"strip\", Zod.ZodTypeAny, { timestamp: string; role: \"user\" | \"system\" | \"assistant\"; content: string; reader?: Zod.objectOutputType<{}, Zod.ZodUnknown, \"strip\"> | undefined; isError?: boolean | undefined; traceData?: { transactionId?: string | undefined; traceId?: string | undefined; } | undefined; }, { timestamp: string; role: \"user\" | \"system\" | \"assistant\"; content: string; reader?: Zod.objectInputType<{}, Zod.ZodUnknown, \"strip\"> | undefined; isError?: boolean | undefined; traceData?: { transactionId?: string | undefined; traceId?: string | undefined; } | undefined; }>, \"many\">>; apiConfig: Zod.ZodOptional; provider: Zod.ZodOptional>; model: Zod.ZodOptional; }, \"strip\", Zod.ZodTypeAny, { connectorId: string; defaultSystemPromptId?: string | undefined; provider?: \"OpenAI\" | \"Azure OpenAI\" | undefined; model?: string | undefined; }, { connectorId: string; defaultSystemPromptId?: string | undefined; provider?: \"OpenAI\" | \"Azure OpenAI\" | undefined; model?: string | undefined; }>>; isDefault: Zod.ZodOptional; excludeFromLastConversationStorage: Zod.ZodOptional; namespace: Zod.ZodString; }, \"strip\", Zod.ZodTypeAny, { id: string; namespace: string; title: string; createdAt: string; category: \"assistant\" | \"insights\"; users: { id?: string | undefined; name?: string | undefined; }[]; summary?: { content?: string | undefined; timestamp?: string | undefined; public?: boolean | undefined; confidence?: \"medium\" | \"low\" | \"high\" | undefined; } | undefined; timestamp?: string | undefined; updatedAt?: string | undefined; replacements?: Zod.objectOutputType<{}, Zod.ZodString, \"strip\"> | undefined; messages?: { timestamp: string; role: \"user\" | \"system\" | \"assistant\"; content: string; reader?: Zod.objectOutputType<{}, Zod.ZodUnknown, \"strip\"> | undefined; isError?: boolean | undefined; traceData?: { transactionId?: string | undefined; traceId?: string | undefined; } | undefined; }[] | undefined; apiConfig?: { connectorId: string; defaultSystemPromptId?: string | undefined; provider?: \"OpenAI\" | \"Azure OpenAI\" | undefined; model?: string | undefined; } | undefined; isDefault?: boolean | undefined; excludeFromLastConversationStorage?: boolean | undefined; }, { id: string; namespace: string; title: string; createdAt: string; category: \"assistant\" | \"insights\"; users: { id?: string | undefined; name?: string | undefined; }[]; summary?: { content?: string | undefined; timestamp?: string | undefined; public?: boolean | undefined; confidence?: \"medium\" | \"low\" | \"high\" | undefined; } | undefined; timestamp?: string | undefined; updatedAt?: string | undefined; replacements?: Zod.objectInputType<{}, Zod.ZodString, \"strip\"> | undefined; messages?: { timestamp: string; role: \"user\" | \"system\" | \"assistant\"; content: string; reader?: Zod.objectInputType<{}, Zod.ZodUnknown, \"strip\"> | undefined; isError?: boolean | undefined; traceData?: { transactionId?: string | undefined; traceId?: string | undefined; } | undefined; }[] | undefined; apiConfig?: { connectorId: string; defaultSystemPromptId?: string | undefined; provider?: \"OpenAI\" | \"Azure OpenAI\" | undefined; model?: string | undefined; } | undefined; isDefault?: boolean | undefined; excludeFromLastConversationStorage?: boolean | undefined; }>, \"many\">; created: Zod.ZodArray; title: Zod.ZodString; category: Zod.ZodEnum<[\"assistant\", \"insights\"]>; summary: Zod.ZodOptional; timestamp: Zod.ZodOptional; public: Zod.ZodOptional; confidence: Zod.ZodOptional>; }, \"strip\", Zod.ZodTypeAny, { content?: string | undefined; timestamp?: string | undefined; public?: boolean | undefined; confidence?: \"medium\" | \"low\" | \"high\" | undefined; }, { content?: string | undefined; timestamp?: string | undefined; public?: boolean | undefined; confidence?: \"medium\" | \"low\" | \"high\" | undefined; }>>; timestamp: Zod.ZodOptional; updatedAt: Zod.ZodOptional; createdAt: Zod.ZodString; replacements: Zod.ZodOptional, Zod.objectInputType<{}, Zod.ZodString, \"strip\">>>; users: Zod.ZodArray; name: Zod.ZodOptional; }, \"strip\", Zod.ZodTypeAny, { id?: string | undefined; name?: string | undefined; }, { id?: string | undefined; name?: string | undefined; }>, \"many\">; messages: Zod.ZodOptional, Zod.objectInputType<{}, Zod.ZodUnknown, \"strip\">>>; role: Zod.ZodEnum<[\"system\", \"user\", \"assistant\"]>; timestamp: Zod.ZodString; isError: Zod.ZodOptional; traceData: Zod.ZodOptional; traceId: Zod.ZodOptional; }, \"strip\", Zod.ZodTypeAny, { transactionId?: string | undefined; traceId?: string | undefined; }, { transactionId?: string | undefined; traceId?: string | undefined; }>>; }, \"strip\", Zod.ZodTypeAny, { timestamp: string; role: \"user\" | \"system\" | \"assistant\"; content: string; reader?: Zod.objectOutputType<{}, Zod.ZodUnknown, \"strip\"> | undefined; isError?: boolean | undefined; traceData?: { transactionId?: string | undefined; traceId?: string | undefined; } | undefined; }, { timestamp: string; role: \"user\" | \"system\" | \"assistant\"; content: string; reader?: Zod.objectInputType<{}, Zod.ZodUnknown, \"strip\"> | undefined; isError?: boolean | undefined; traceData?: { transactionId?: string | undefined; traceId?: string | undefined; } | undefined; }>, \"many\">>; apiConfig: Zod.ZodOptional; provider: Zod.ZodOptional>; model: Zod.ZodOptional; }, \"strip\", Zod.ZodTypeAny, { connectorId: string; defaultSystemPromptId?: string | undefined; provider?: \"OpenAI\" | \"Azure OpenAI\" | undefined; model?: string | undefined; }, { connectorId: string; defaultSystemPromptId?: string | undefined; provider?: \"OpenAI\" | \"Azure OpenAI\" | undefined; model?: string | undefined; }>>; isDefault: Zod.ZodOptional; excludeFromLastConversationStorage: Zod.ZodOptional; namespace: Zod.ZodString; }, \"strip\", Zod.ZodTypeAny, { id: string; namespace: string; title: string; createdAt: string; category: \"assistant\" | \"insights\"; users: { id?: string | undefined; name?: string | undefined; }[]; summary?: { content?: string | undefined; timestamp?: string | undefined; public?: boolean | undefined; confidence?: \"medium\" | \"low\" | \"high\" | undefined; } | undefined; timestamp?: string | undefined; updatedAt?: string | undefined; replacements?: Zod.objectOutputType<{}, Zod.ZodString, \"strip\"> | undefined; messages?: { timestamp: string; role: \"user\" | \"system\" | \"assistant\"; content: string; reader?: Zod.objectOutputType<{}, Zod.ZodUnknown, \"strip\"> | undefined; isError?: boolean | undefined; traceData?: { transactionId?: string | undefined; traceId?: string | undefined; } | undefined; }[] | undefined; apiConfig?: { connectorId: string; defaultSystemPromptId?: string | undefined; provider?: \"OpenAI\" | \"Azure OpenAI\" | undefined; model?: string | undefined; } | undefined; isDefault?: boolean | undefined; excludeFromLastConversationStorage?: boolean | undefined; }, { id: string; namespace: string; title: string; createdAt: string; category: \"assistant\" | \"insights\"; users: { id?: string | undefined; name?: string | undefined; }[]; summary?: { content?: string | undefined; timestamp?: string | undefined; public?: boolean | undefined; confidence?: \"medium\" | \"low\" | \"high\" | undefined; } | undefined; timestamp?: string | undefined; updatedAt?: string | undefined; replacements?: Zod.objectInputType<{}, Zod.ZodString, \"strip\"> | undefined; messages?: { timestamp: string; role: \"user\" | \"system\" | \"assistant\"; content: string; reader?: Zod.objectInputType<{}, Zod.ZodUnknown, \"strip\"> | undefined; isError?: boolean | undefined; traceData?: { transactionId?: string | undefined; traceId?: string | undefined; } | undefined; }[] | undefined; apiConfig?: { connectorId: string; defaultSystemPromptId?: string | undefined; provider?: \"OpenAI\" | \"Azure OpenAI\" | undefined; model?: string | undefined; } | undefined; isDefault?: boolean | undefined; excludeFromLastConversationStorage?: boolean | undefined; }>, \"many\">; deleted: Zod.ZodArray; skipped: Zod.ZodArray; skip_reason: Zod.ZodLiteral<\"CONVERSATION_NOT_MODIFIED\">; }, \"strip\", Zod.ZodTypeAny, { id: string; skip_reason: \"CONVERSATION_NOT_MODIFIED\"; name?: string | undefined; }, { id: string; skip_reason: \"CONVERSATION_NOT_MODIFIED\"; name?: string | undefined; }>, \"many\">; }, \"strip\", Zod.ZodTypeAny, { created: { id: string; namespace: string; title: string; createdAt: string; category: \"assistant\" | \"insights\"; users: { id?: string | undefined; name?: string | undefined; }[]; summary?: { content?: string | undefined; timestamp?: string | undefined; public?: boolean | undefined; confidence?: \"medium\" | \"low\" | \"high\" | undefined; } | undefined; timestamp?: string | undefined; updatedAt?: string | undefined; replacements?: Zod.objectOutputType<{}, Zod.ZodString, \"strip\"> | undefined; messages?: { timestamp: string; role: \"user\" | \"system\" | \"assistant\"; content: string; reader?: Zod.objectOutputType<{}, Zod.ZodUnknown, \"strip\"> | undefined; isError?: boolean | undefined; traceData?: { transactionId?: string | undefined; traceId?: string | undefined; } | undefined; }[] | undefined; apiConfig?: { connectorId: string; defaultSystemPromptId?: string | undefined; provider?: \"OpenAI\" | \"Azure OpenAI\" | undefined; model?: string | undefined; } | undefined; isDefault?: boolean | undefined; excludeFromLastConversationStorage?: boolean | undefined; }[]; updated: { id: string; namespace: string; title: string; createdAt: string; category: \"assistant\" | \"insights\"; users: { id?: string | undefined; name?: string | undefined; }[]; summary?: { content?: string | undefined; timestamp?: string | undefined; public?: boolean | undefined; confidence?: \"medium\" | \"low\" | \"high\" | undefined; } | undefined; timestamp?: string | undefined; updatedAt?: string | undefined; replacements?: Zod.objectOutputType<{}, Zod.ZodString, \"strip\"> | undefined; messages?: { timestamp: string; role: \"user\" | \"system\" | \"assistant\"; content: string; reader?: Zod.objectOutputType<{}, Zod.ZodUnknown, \"strip\"> | undefined; isError?: boolean | undefined; traceData?: { transactionId?: string | undefined; traceId?: string | undefined; } | undefined; }[] | undefined; apiConfig?: { connectorId: string; defaultSystemPromptId?: string | undefined; provider?: \"OpenAI\" | \"Azure OpenAI\" | undefined; model?: string | undefined; } | undefined; isDefault?: boolean | undefined; excludeFromLastConversationStorage?: boolean | undefined; }[]; skipped: { id: string; skip_reason: \"CONVERSATION_NOT_MODIFIED\"; name?: string | undefined; }[]; deleted: string[]; }, { created: { id: string; namespace: string; title: string; createdAt: string; category: \"assistant\" | \"insights\"; users: { id?: string | undefined; name?: string | undefined; }[]; summary?: { content?: string | undefined; timestamp?: string | undefined; public?: boolean | undefined; confidence?: \"medium\" | \"low\" | \"high\" | undefined; } | undefined; timestamp?: string | undefined; updatedAt?: string | undefined; replacements?: Zod.objectInputType<{}, Zod.ZodString, \"strip\"> | undefined; messages?: { timestamp: string; role: \"user\" | \"system\" | \"assistant\"; content: string; reader?: Zod.objectInputType<{}, Zod.ZodUnknown, \"strip\"> | undefined; isError?: boolean | undefined; traceData?: { transactionId?: string | undefined; traceId?: string | undefined; } | undefined; }[] | undefined; apiConfig?: { connectorId: string; defaultSystemPromptId?: string | undefined; provider?: \"OpenAI\" | \"Azure OpenAI\" | undefined; model?: string | undefined; } | undefined; isDefault?: boolean | undefined; excludeFromLastConversationStorage?: boolean | undefined; }[]; updated: { id: string; namespace: string; title: string; createdAt: string; category: \"assistant\" | \"insights\"; users: { id?: string | undefined; name?: string | undefined; }[]; summary?: { content?: string | undefined; timestamp?: string | undefined; public?: boolean | undefined; confidence?: \"medium\" | \"low\" | \"high\" | undefined; } | undefined; timestamp?: string | undefined; updatedAt?: string | undefined; replacements?: Zod.objectInputType<{}, Zod.ZodString, \"strip\"> | undefined; messages?: { timestamp: string; role: \"user\" | \"system\" | \"assistant\"; content: string; reader?: Zod.objectInputType<{}, Zod.ZodUnknown, \"strip\"> | undefined; isError?: boolean | undefined; traceData?: { transactionId?: string | undefined; traceId?: string | undefined; } | undefined; }[] | undefined; apiConfig?: { connectorId: string; defaultSystemPromptId?: string | undefined; provider?: \"OpenAI\" | \"Azure OpenAI\" | undefined; model?: string | undefined; } | undefined; isDefault?: boolean | undefined; excludeFromLastConversationStorage?: boolean | undefined; }[]; skipped: { id: string; skip_reason: \"CONVERSATION_NOT_MODIFIED\"; name?: string | undefined; }[]; deleted: string[]; }>" ], "path": "x-pack/packages/kbn-elastic-assistant-common/impl/schemas/conversations/bulk_crud_conversations_route.gen.ts", "deprecated": false, @@ -2538,7 +2538,7 @@ "label": "ConversationCreateProps", "description": [], "signature": [ - "Zod.ZodObject<{ title: Zod.ZodString; category: Zod.ZodOptional>; messages: Zod.ZodOptional, Zod.objectInputType<{}, Zod.ZodUnknown, \"strip\">>>; role: Zod.ZodEnum<[\"system\", \"user\", \"assistant\"]>; timestamp: Zod.ZodString; isError: Zod.ZodOptional; traceData: Zod.ZodOptional; traceId: Zod.ZodOptional; }, \"strip\", Zod.ZodTypeAny, { transactionId?: string | undefined; traceId?: string | undefined; }, { transactionId?: string | undefined; traceId?: string | undefined; }>>; }, \"strip\", Zod.ZodTypeAny, { timestamp: string; role: \"user\" | \"system\" | \"assistant\"; content: string; reader?: Zod.objectOutputType<{}, Zod.ZodUnknown, \"strip\"> | undefined; isError?: boolean | undefined; traceData?: { transactionId?: string | undefined; traceId?: string | undefined; } | undefined; }, { timestamp: string; role: \"user\" | \"system\" | \"assistant\"; content: string; reader?: Zod.objectInputType<{}, Zod.ZodUnknown, \"strip\"> | undefined; isError?: boolean | undefined; traceData?: { transactionId?: string | undefined; traceId?: string | undefined; } | undefined; }>, \"many\">>; apiConfig: Zod.ZodOptional; provider: Zod.ZodOptional>; model: Zod.ZodOptional; }, \"strip\", Zod.ZodTypeAny, { connectorId: string; defaultSystemPromptId?: string | undefined; provider?: \"OpenAI\" | \"Azure OpenAI\" | undefined; model?: string | undefined; }, { connectorId: string; defaultSystemPromptId?: string | undefined; provider?: \"OpenAI\" | \"Azure OpenAI\" | undefined; model?: string | undefined; }>>; isDefault: Zod.ZodOptional; excludeFromLastConversationStorage: Zod.ZodOptional; replacements: Zod.ZodOptional, \"many\">>; }, \"strip\", Zod.ZodTypeAny, { title: string; category?: \"assistant\" | \"insights\" | undefined; messages?: { timestamp: string; role: \"user\" | \"system\" | \"assistant\"; content: string; reader?: Zod.objectOutputType<{}, Zod.ZodUnknown, \"strip\"> | undefined; isError?: boolean | undefined; traceData?: { transactionId?: string | undefined; traceId?: string | undefined; } | undefined; }[] | undefined; apiConfig?: { connectorId: string; defaultSystemPromptId?: string | undefined; provider?: \"OpenAI\" | \"Azure OpenAI\" | undefined; model?: string | undefined; } | undefined; isDefault?: boolean | undefined; excludeFromLastConversationStorage?: boolean | undefined; replacements?: { value: string; uuid: string; }[] | undefined; }, { title: string; category?: \"assistant\" | \"insights\" | undefined; messages?: { timestamp: string; role: \"user\" | \"system\" | \"assistant\"; content: string; reader?: Zod.objectInputType<{}, Zod.ZodUnknown, \"strip\"> | undefined; isError?: boolean | undefined; traceData?: { transactionId?: string | undefined; traceId?: string | undefined; } | undefined; }[] | undefined; apiConfig?: { connectorId: string; defaultSystemPromptId?: string | undefined; provider?: \"OpenAI\" | \"Azure OpenAI\" | undefined; model?: string | undefined; } | undefined; isDefault?: boolean | undefined; excludeFromLastConversationStorage?: boolean | undefined; replacements?: { value: string; uuid: string; }[] | undefined; }>" + "Zod.ZodObject<{ title: Zod.ZodString; category: Zod.ZodOptional>; messages: Zod.ZodOptional, Zod.objectInputType<{}, Zod.ZodUnknown, \"strip\">>>; role: Zod.ZodEnum<[\"system\", \"user\", \"assistant\"]>; timestamp: Zod.ZodString; isError: Zod.ZodOptional; traceData: Zod.ZodOptional; traceId: Zod.ZodOptional; }, \"strip\", Zod.ZodTypeAny, { transactionId?: string | undefined; traceId?: string | undefined; }, { transactionId?: string | undefined; traceId?: string | undefined; }>>; }, \"strip\", Zod.ZodTypeAny, { timestamp: string; role: \"user\" | \"system\" | \"assistant\"; content: string; reader?: Zod.objectOutputType<{}, Zod.ZodUnknown, \"strip\"> | undefined; isError?: boolean | undefined; traceData?: { transactionId?: string | undefined; traceId?: string | undefined; } | undefined; }, { timestamp: string; role: \"user\" | \"system\" | \"assistant\"; content: string; reader?: Zod.objectInputType<{}, Zod.ZodUnknown, \"strip\"> | undefined; isError?: boolean | undefined; traceData?: { transactionId?: string | undefined; traceId?: string | undefined; } | undefined; }>, \"many\">>; apiConfig: Zod.ZodOptional; provider: Zod.ZodOptional>; model: Zod.ZodOptional; }, \"strip\", Zod.ZodTypeAny, { connectorId: string; defaultSystemPromptId?: string | undefined; provider?: \"OpenAI\" | \"Azure OpenAI\" | undefined; model?: string | undefined; }, { connectorId: string; defaultSystemPromptId?: string | undefined; provider?: \"OpenAI\" | \"Azure OpenAI\" | undefined; model?: string | undefined; }>>; isDefault: Zod.ZodOptional; excludeFromLastConversationStorage: Zod.ZodOptional; replacements: Zod.ZodOptional, Zod.objectInputType<{}, Zod.ZodString, \"strip\">>>; }, \"strip\", Zod.ZodTypeAny, { title: string; category?: \"assistant\" | \"insights\" | undefined; messages?: { timestamp: string; role: \"user\" | \"system\" | \"assistant\"; content: string; reader?: Zod.objectOutputType<{}, Zod.ZodUnknown, \"strip\"> | undefined; isError?: boolean | undefined; traceData?: { transactionId?: string | undefined; traceId?: string | undefined; } | undefined; }[] | undefined; apiConfig?: { connectorId: string; defaultSystemPromptId?: string | undefined; provider?: \"OpenAI\" | \"Azure OpenAI\" | undefined; model?: string | undefined; } | undefined; isDefault?: boolean | undefined; excludeFromLastConversationStorage?: boolean | undefined; replacements?: Zod.objectOutputType<{}, Zod.ZodString, \"strip\"> | undefined; }, { title: string; category?: \"assistant\" | \"insights\" | undefined; messages?: { timestamp: string; role: \"user\" | \"system\" | \"assistant\"; content: string; reader?: Zod.objectInputType<{}, Zod.ZodUnknown, \"strip\"> | undefined; isError?: boolean | undefined; traceData?: { transactionId?: string | undefined; traceId?: string | undefined; } | undefined; }[] | undefined; apiConfig?: { connectorId: string; defaultSystemPromptId?: string | undefined; provider?: \"OpenAI\" | \"Azure OpenAI\" | undefined; model?: string | undefined; } | undefined; isDefault?: boolean | undefined; excludeFromLastConversationStorage?: boolean | undefined; replacements?: Zod.objectInputType<{}, Zod.ZodString, \"strip\"> | undefined; }>" ], "path": "x-pack/packages/kbn-elastic-assistant-common/impl/schemas/conversations/common_attributes.gen.ts", "deprecated": false, @@ -2583,7 +2583,7 @@ "label": "ConversationResponse", "description": [], "signature": [ - "Zod.ZodObject<{ id: Zod.ZodUnion<[Zod.ZodString, Zod.ZodString]>; title: Zod.ZodString; category: Zod.ZodEnum<[\"assistant\", \"insights\"]>; summary: Zod.ZodOptional; timestamp: Zod.ZodOptional; public: Zod.ZodOptional; confidence: Zod.ZodOptional>; }, \"strip\", Zod.ZodTypeAny, { content?: string | undefined; timestamp?: string | undefined; public?: boolean | undefined; confidence?: \"medium\" | \"low\" | \"high\" | undefined; }, { content?: string | undefined; timestamp?: string | undefined; public?: boolean | undefined; confidence?: \"medium\" | \"low\" | \"high\" | undefined; }>>; timestamp: Zod.ZodOptional; updatedAt: Zod.ZodOptional; createdAt: Zod.ZodString; replacements: Zod.ZodOptional, \"many\">>; users: Zod.ZodArray; name: Zod.ZodOptional; }, \"strip\", Zod.ZodTypeAny, { id?: string | undefined; name?: string | undefined; }, { id?: string | undefined; name?: string | undefined; }>, \"many\">; messages: Zod.ZodOptional, Zod.objectInputType<{}, Zod.ZodUnknown, \"strip\">>>; role: Zod.ZodEnum<[\"system\", \"user\", \"assistant\"]>; timestamp: Zod.ZodString; isError: Zod.ZodOptional; traceData: Zod.ZodOptional; traceId: Zod.ZodOptional; }, \"strip\", Zod.ZodTypeAny, { transactionId?: string | undefined; traceId?: string | undefined; }, { transactionId?: string | undefined; traceId?: string | undefined; }>>; }, \"strip\", Zod.ZodTypeAny, { timestamp: string; role: \"user\" | \"system\" | \"assistant\"; content: string; reader?: Zod.objectOutputType<{}, Zod.ZodUnknown, \"strip\"> | undefined; isError?: boolean | undefined; traceData?: { transactionId?: string | undefined; traceId?: string | undefined; } | undefined; }, { timestamp: string; role: \"user\" | \"system\" | \"assistant\"; content: string; reader?: Zod.objectInputType<{}, Zod.ZodUnknown, \"strip\"> | undefined; isError?: boolean | undefined; traceData?: { transactionId?: string | undefined; traceId?: string | undefined; } | undefined; }>, \"many\">>; apiConfig: Zod.ZodOptional; provider: Zod.ZodOptional>; model: Zod.ZodOptional; }, \"strip\", Zod.ZodTypeAny, { connectorId: string; defaultSystemPromptId?: string | undefined; provider?: \"OpenAI\" | \"Azure OpenAI\" | undefined; model?: string | undefined; }, { connectorId: string; defaultSystemPromptId?: string | undefined; provider?: \"OpenAI\" | \"Azure OpenAI\" | undefined; model?: string | undefined; }>>; isDefault: Zod.ZodOptional; excludeFromLastConversationStorage: Zod.ZodOptional; namespace: Zod.ZodString; }, \"strip\", Zod.ZodTypeAny, { id: string; namespace: string; title: string; createdAt: string; category: \"assistant\" | \"insights\"; users: { id?: string | undefined; name?: string | undefined; }[]; summary?: { content?: string | undefined; timestamp?: string | undefined; public?: boolean | undefined; confidence?: \"medium\" | \"low\" | \"high\" | undefined; } | undefined; timestamp?: string | undefined; updatedAt?: string | undefined; replacements?: { value: string; uuid: string; }[] | undefined; messages?: { timestamp: string; role: \"user\" | \"system\" | \"assistant\"; content: string; reader?: Zod.objectOutputType<{}, Zod.ZodUnknown, \"strip\"> | undefined; isError?: boolean | undefined; traceData?: { transactionId?: string | undefined; traceId?: string | undefined; } | undefined; }[] | undefined; apiConfig?: { connectorId: string; defaultSystemPromptId?: string | undefined; provider?: \"OpenAI\" | \"Azure OpenAI\" | undefined; model?: string | undefined; } | undefined; isDefault?: boolean | undefined; excludeFromLastConversationStorage?: boolean | undefined; }, { id: string; namespace: string; title: string; createdAt: string; category: \"assistant\" | \"insights\"; users: { id?: string | undefined; name?: string | undefined; }[]; summary?: { content?: string | undefined; timestamp?: string | undefined; public?: boolean | undefined; confidence?: \"medium\" | \"low\" | \"high\" | undefined; } | undefined; timestamp?: string | undefined; updatedAt?: string | undefined; replacements?: { value: string; uuid: string; }[] | undefined; messages?: { timestamp: string; role: \"user\" | \"system\" | \"assistant\"; content: string; reader?: Zod.objectInputType<{}, Zod.ZodUnknown, \"strip\"> | undefined; isError?: boolean | undefined; traceData?: { transactionId?: string | undefined; traceId?: string | undefined; } | undefined; }[] | undefined; apiConfig?: { connectorId: string; defaultSystemPromptId?: string | undefined; provider?: \"OpenAI\" | \"Azure OpenAI\" | undefined; model?: string | undefined; } | undefined; isDefault?: boolean | undefined; excludeFromLastConversationStorage?: boolean | undefined; }>" + "Zod.ZodObject<{ id: Zod.ZodUnion<[Zod.ZodString, Zod.ZodString]>; title: Zod.ZodString; category: Zod.ZodEnum<[\"assistant\", \"insights\"]>; summary: Zod.ZodOptional; timestamp: Zod.ZodOptional; public: Zod.ZodOptional; confidence: Zod.ZodOptional>; }, \"strip\", Zod.ZodTypeAny, { content?: string | undefined; timestamp?: string | undefined; public?: boolean | undefined; confidence?: \"medium\" | \"low\" | \"high\" | undefined; }, { content?: string | undefined; timestamp?: string | undefined; public?: boolean | undefined; confidence?: \"medium\" | \"low\" | \"high\" | undefined; }>>; timestamp: Zod.ZodOptional; updatedAt: Zod.ZodOptional; createdAt: Zod.ZodString; replacements: Zod.ZodOptional, Zod.objectInputType<{}, Zod.ZodString, \"strip\">>>; users: Zod.ZodArray; name: Zod.ZodOptional; }, \"strip\", Zod.ZodTypeAny, { id?: string | undefined; name?: string | undefined; }, { id?: string | undefined; name?: string | undefined; }>, \"many\">; messages: Zod.ZodOptional, Zod.objectInputType<{}, Zod.ZodUnknown, \"strip\">>>; role: Zod.ZodEnum<[\"system\", \"user\", \"assistant\"]>; timestamp: Zod.ZodString; isError: Zod.ZodOptional; traceData: Zod.ZodOptional; traceId: Zod.ZodOptional; }, \"strip\", Zod.ZodTypeAny, { transactionId?: string | undefined; traceId?: string | undefined; }, { transactionId?: string | undefined; traceId?: string | undefined; }>>; }, \"strip\", Zod.ZodTypeAny, { timestamp: string; role: \"user\" | \"system\" | \"assistant\"; content: string; reader?: Zod.objectOutputType<{}, Zod.ZodUnknown, \"strip\"> | undefined; isError?: boolean | undefined; traceData?: { transactionId?: string | undefined; traceId?: string | undefined; } | undefined; }, { timestamp: string; role: \"user\" | \"system\" | \"assistant\"; content: string; reader?: Zod.objectInputType<{}, Zod.ZodUnknown, \"strip\"> | undefined; isError?: boolean | undefined; traceData?: { transactionId?: string | undefined; traceId?: string | undefined; } | undefined; }>, \"many\">>; apiConfig: Zod.ZodOptional; provider: Zod.ZodOptional>; model: Zod.ZodOptional; }, \"strip\", Zod.ZodTypeAny, { connectorId: string; defaultSystemPromptId?: string | undefined; provider?: \"OpenAI\" | \"Azure OpenAI\" | undefined; model?: string | undefined; }, { connectorId: string; defaultSystemPromptId?: string | undefined; provider?: \"OpenAI\" | \"Azure OpenAI\" | undefined; model?: string | undefined; }>>; isDefault: Zod.ZodOptional; excludeFromLastConversationStorage: Zod.ZodOptional; namespace: Zod.ZodString; }, \"strip\", Zod.ZodTypeAny, { id: string; namespace: string; title: string; createdAt: string; category: \"assistant\" | \"insights\"; users: { id?: string | undefined; name?: string | undefined; }[]; summary?: { content?: string | undefined; timestamp?: string | undefined; public?: boolean | undefined; confidence?: \"medium\" | \"low\" | \"high\" | undefined; } | undefined; timestamp?: string | undefined; updatedAt?: string | undefined; replacements?: Zod.objectOutputType<{}, Zod.ZodString, \"strip\"> | undefined; messages?: { timestamp: string; role: \"user\" | \"system\" | \"assistant\"; content: string; reader?: Zod.objectOutputType<{}, Zod.ZodUnknown, \"strip\"> | undefined; isError?: boolean | undefined; traceData?: { transactionId?: string | undefined; traceId?: string | undefined; } | undefined; }[] | undefined; apiConfig?: { connectorId: string; defaultSystemPromptId?: string | undefined; provider?: \"OpenAI\" | \"Azure OpenAI\" | undefined; model?: string | undefined; } | undefined; isDefault?: boolean | undefined; excludeFromLastConversationStorage?: boolean | undefined; }, { id: string; namespace: string; title: string; createdAt: string; category: \"assistant\" | \"insights\"; users: { id?: string | undefined; name?: string | undefined; }[]; summary?: { content?: string | undefined; timestamp?: string | undefined; public?: boolean | undefined; confidence?: \"medium\" | \"low\" | \"high\" | undefined; } | undefined; timestamp?: string | undefined; updatedAt?: string | undefined; replacements?: Zod.objectInputType<{}, Zod.ZodString, \"strip\"> | undefined; messages?: { timestamp: string; role: \"user\" | \"system\" | \"assistant\"; content: string; reader?: Zod.objectInputType<{}, Zod.ZodUnknown, \"strip\"> | undefined; isError?: boolean | undefined; traceData?: { transactionId?: string | undefined; traceId?: string | undefined; } | undefined; }[] | undefined; apiConfig?: { connectorId: string; defaultSystemPromptId?: string | undefined; provider?: \"OpenAI\" | \"Azure OpenAI\" | undefined; model?: string | undefined; } | undefined; isDefault?: boolean | undefined; excludeFromLastConversationStorage?: boolean | undefined; }>" ], "path": "x-pack/packages/kbn-elastic-assistant-common/impl/schemas/conversations/common_attributes.gen.ts", "deprecated": false, @@ -2613,7 +2613,7 @@ "label": "ConversationUpdateProps", "description": [], "signature": [ - "Zod.ZodObject<{ id: Zod.ZodUnion<[Zod.ZodString, Zod.ZodString]>; title: Zod.ZodOptional; category: Zod.ZodOptional>; messages: Zod.ZodOptional, Zod.objectInputType<{}, Zod.ZodUnknown, \"strip\">>>; role: Zod.ZodEnum<[\"system\", \"user\", \"assistant\"]>; timestamp: Zod.ZodString; isError: Zod.ZodOptional; traceData: Zod.ZodOptional; traceId: Zod.ZodOptional; }, \"strip\", Zod.ZodTypeAny, { transactionId?: string | undefined; traceId?: string | undefined; }, { transactionId?: string | undefined; traceId?: string | undefined; }>>; }, \"strip\", Zod.ZodTypeAny, { timestamp: string; role: \"user\" | \"system\" | \"assistant\"; content: string; reader?: Zod.objectOutputType<{}, Zod.ZodUnknown, \"strip\"> | undefined; isError?: boolean | undefined; traceData?: { transactionId?: string | undefined; traceId?: string | undefined; } | undefined; }, { timestamp: string; role: \"user\" | \"system\" | \"assistant\"; content: string; reader?: Zod.objectInputType<{}, Zod.ZodUnknown, \"strip\"> | undefined; isError?: boolean | undefined; traceData?: { transactionId?: string | undefined; traceId?: string | undefined; } | undefined; }>, \"many\">>; apiConfig: Zod.ZodOptional; provider: Zod.ZodOptional>; model: Zod.ZodOptional; }, \"strip\", Zod.ZodTypeAny, { connectorId: string; defaultSystemPromptId?: string | undefined; provider?: \"OpenAI\" | \"Azure OpenAI\" | undefined; model?: string | undefined; }, { connectorId: string; defaultSystemPromptId?: string | undefined; provider?: \"OpenAI\" | \"Azure OpenAI\" | undefined; model?: string | undefined; }>>; summary: Zod.ZodOptional; timestamp: Zod.ZodOptional; public: Zod.ZodOptional; confidence: Zod.ZodOptional>; }, \"strip\", Zod.ZodTypeAny, { content?: string | undefined; timestamp?: string | undefined; public?: boolean | undefined; confidence?: \"medium\" | \"low\" | \"high\" | undefined; }, { content?: string | undefined; timestamp?: string | undefined; public?: boolean | undefined; confidence?: \"medium\" | \"low\" | \"high\" | undefined; }>>; excludeFromLastConversationStorage: Zod.ZodOptional; replacements: Zod.ZodOptional, \"many\">>; }, \"strip\", Zod.ZodTypeAny, { id: string; title?: string | undefined; category?: \"assistant\" | \"insights\" | undefined; messages?: { timestamp: string; role: \"user\" | \"system\" | \"assistant\"; content: string; reader?: Zod.objectOutputType<{}, Zod.ZodUnknown, \"strip\"> | undefined; isError?: boolean | undefined; traceData?: { transactionId?: string | undefined; traceId?: string | undefined; } | undefined; }[] | undefined; apiConfig?: { connectorId: string; defaultSystemPromptId?: string | undefined; provider?: \"OpenAI\" | \"Azure OpenAI\" | undefined; model?: string | undefined; } | undefined; summary?: { content?: string | undefined; timestamp?: string | undefined; public?: boolean | undefined; confidence?: \"medium\" | \"low\" | \"high\" | undefined; } | undefined; excludeFromLastConversationStorage?: boolean | undefined; replacements?: { value: string; uuid: string; }[] | undefined; }, { id: string; title?: string | undefined; category?: \"assistant\" | \"insights\" | undefined; messages?: { timestamp: string; role: \"user\" | \"system\" | \"assistant\"; content: string; reader?: Zod.objectInputType<{}, Zod.ZodUnknown, \"strip\"> | undefined; isError?: boolean | undefined; traceData?: { transactionId?: string | undefined; traceId?: string | undefined; } | undefined; }[] | undefined; apiConfig?: { connectorId: string; defaultSystemPromptId?: string | undefined; provider?: \"OpenAI\" | \"Azure OpenAI\" | undefined; model?: string | undefined; } | undefined; summary?: { content?: string | undefined; timestamp?: string | undefined; public?: boolean | undefined; confidence?: \"medium\" | \"low\" | \"high\" | undefined; } | undefined; excludeFromLastConversationStorage?: boolean | undefined; replacements?: { value: string; uuid: string; }[] | undefined; }>" + "Zod.ZodObject<{ id: Zod.ZodUnion<[Zod.ZodString, Zod.ZodString]>; title: Zod.ZodOptional; category: Zod.ZodOptional>; messages: Zod.ZodOptional, Zod.objectInputType<{}, Zod.ZodUnknown, \"strip\">>>; role: Zod.ZodEnum<[\"system\", \"user\", \"assistant\"]>; timestamp: Zod.ZodString; isError: Zod.ZodOptional; traceData: Zod.ZodOptional; traceId: Zod.ZodOptional; }, \"strip\", Zod.ZodTypeAny, { transactionId?: string | undefined; traceId?: string | undefined; }, { transactionId?: string | undefined; traceId?: string | undefined; }>>; }, \"strip\", Zod.ZodTypeAny, { timestamp: string; role: \"user\" | \"system\" | \"assistant\"; content: string; reader?: Zod.objectOutputType<{}, Zod.ZodUnknown, \"strip\"> | undefined; isError?: boolean | undefined; traceData?: { transactionId?: string | undefined; traceId?: string | undefined; } | undefined; }, { timestamp: string; role: \"user\" | \"system\" | \"assistant\"; content: string; reader?: Zod.objectInputType<{}, Zod.ZodUnknown, \"strip\"> | undefined; isError?: boolean | undefined; traceData?: { transactionId?: string | undefined; traceId?: string | undefined; } | undefined; }>, \"many\">>; apiConfig: Zod.ZodOptional; provider: Zod.ZodOptional>; model: Zod.ZodOptional; }, \"strip\", Zod.ZodTypeAny, { connectorId: string; defaultSystemPromptId?: string | undefined; provider?: \"OpenAI\" | \"Azure OpenAI\" | undefined; model?: string | undefined; }, { connectorId: string; defaultSystemPromptId?: string | undefined; provider?: \"OpenAI\" | \"Azure OpenAI\" | undefined; model?: string | undefined; }>>; summary: Zod.ZodOptional; timestamp: Zod.ZodOptional; public: Zod.ZodOptional; confidence: Zod.ZodOptional>; }, \"strip\", Zod.ZodTypeAny, { content?: string | undefined; timestamp?: string | undefined; public?: boolean | undefined; confidence?: \"medium\" | \"low\" | \"high\" | undefined; }, { content?: string | undefined; timestamp?: string | undefined; public?: boolean | undefined; confidence?: \"medium\" | \"low\" | \"high\" | undefined; }>>; excludeFromLastConversationStorage: Zod.ZodOptional; replacements: Zod.ZodOptional, Zod.objectInputType<{}, Zod.ZodString, \"strip\">>>; }, \"strip\", Zod.ZodTypeAny, { id: string; title?: string | undefined; category?: \"assistant\" | \"insights\" | undefined; messages?: { timestamp: string; role: \"user\" | \"system\" | \"assistant\"; content: string; reader?: Zod.objectOutputType<{}, Zod.ZodUnknown, \"strip\"> | undefined; isError?: boolean | undefined; traceData?: { transactionId?: string | undefined; traceId?: string | undefined; } | undefined; }[] | undefined; apiConfig?: { connectorId: string; defaultSystemPromptId?: string | undefined; provider?: \"OpenAI\" | \"Azure OpenAI\" | undefined; model?: string | undefined; } | undefined; summary?: { content?: string | undefined; timestamp?: string | undefined; public?: boolean | undefined; confidence?: \"medium\" | \"low\" | \"high\" | undefined; } | undefined; excludeFromLastConversationStorage?: boolean | undefined; replacements?: Zod.objectOutputType<{}, Zod.ZodString, \"strip\"> | undefined; }, { id: string; title?: string | undefined; category?: \"assistant\" | \"insights\" | undefined; messages?: { timestamp: string; role: \"user\" | \"system\" | \"assistant\"; content: string; reader?: Zod.objectInputType<{}, Zod.ZodUnknown, \"strip\"> | undefined; isError?: boolean | undefined; traceData?: { transactionId?: string | undefined; traceId?: string | undefined; } | undefined; }[] | undefined; apiConfig?: { connectorId: string; defaultSystemPromptId?: string | undefined; provider?: \"OpenAI\" | \"Azure OpenAI\" | undefined; model?: string | undefined; } | undefined; summary?: { content?: string | undefined; timestamp?: string | undefined; public?: boolean | undefined; confidence?: \"medium\" | \"low\" | \"high\" | undefined; } | undefined; excludeFromLastConversationStorage?: boolean | undefined; replacements?: Zod.objectInputType<{}, Zod.ZodString, \"strip\"> | undefined; }>" ], "path": "x-pack/packages/kbn-elastic-assistant-common/impl/schemas/conversations/common_attributes.gen.ts", "deprecated": false, @@ -2628,7 +2628,7 @@ "label": "CreateConversationRequestBody", "description": [], "signature": [ - "Zod.ZodObject<{ title: Zod.ZodString; category: Zod.ZodOptional>; messages: Zod.ZodOptional, Zod.objectInputType<{}, Zod.ZodUnknown, \"strip\">>>; role: Zod.ZodEnum<[\"system\", \"user\", \"assistant\"]>; timestamp: Zod.ZodString; isError: Zod.ZodOptional; traceData: Zod.ZodOptional; traceId: Zod.ZodOptional; }, \"strip\", Zod.ZodTypeAny, { transactionId?: string | undefined; traceId?: string | undefined; }, { transactionId?: string | undefined; traceId?: string | undefined; }>>; }, \"strip\", Zod.ZodTypeAny, { timestamp: string; role: \"user\" | \"system\" | \"assistant\"; content: string; reader?: Zod.objectOutputType<{}, Zod.ZodUnknown, \"strip\"> | undefined; isError?: boolean | undefined; traceData?: { transactionId?: string | undefined; traceId?: string | undefined; } | undefined; }, { timestamp: string; role: \"user\" | \"system\" | \"assistant\"; content: string; reader?: Zod.objectInputType<{}, Zod.ZodUnknown, \"strip\"> | undefined; isError?: boolean | undefined; traceData?: { transactionId?: string | undefined; traceId?: string | undefined; } | undefined; }>, \"many\">>; apiConfig: Zod.ZodOptional; provider: Zod.ZodOptional>; model: Zod.ZodOptional; }, \"strip\", Zod.ZodTypeAny, { connectorId: string; defaultSystemPromptId?: string | undefined; provider?: \"OpenAI\" | \"Azure OpenAI\" | undefined; model?: string | undefined; }, { connectorId: string; defaultSystemPromptId?: string | undefined; provider?: \"OpenAI\" | \"Azure OpenAI\" | undefined; model?: string | undefined; }>>; isDefault: Zod.ZodOptional; excludeFromLastConversationStorage: Zod.ZodOptional; replacements: Zod.ZodOptional, \"many\">>; }, \"strip\", Zod.ZodTypeAny, { title: string; category?: \"assistant\" | \"insights\" | undefined; messages?: { timestamp: string; role: \"user\" | \"system\" | \"assistant\"; content: string; reader?: Zod.objectOutputType<{}, Zod.ZodUnknown, \"strip\"> | undefined; isError?: boolean | undefined; traceData?: { transactionId?: string | undefined; traceId?: string | undefined; } | undefined; }[] | undefined; apiConfig?: { connectorId: string; defaultSystemPromptId?: string | undefined; provider?: \"OpenAI\" | \"Azure OpenAI\" | undefined; model?: string | undefined; } | undefined; isDefault?: boolean | undefined; excludeFromLastConversationStorage?: boolean | undefined; replacements?: { value: string; uuid: string; }[] | undefined; }, { title: string; category?: \"assistant\" | \"insights\" | undefined; messages?: { timestamp: string; role: \"user\" | \"system\" | \"assistant\"; content: string; reader?: Zod.objectInputType<{}, Zod.ZodUnknown, \"strip\"> | undefined; isError?: boolean | undefined; traceData?: { transactionId?: string | undefined; traceId?: string | undefined; } | undefined; }[] | undefined; apiConfig?: { connectorId: string; defaultSystemPromptId?: string | undefined; provider?: \"OpenAI\" | \"Azure OpenAI\" | undefined; model?: string | undefined; } | undefined; isDefault?: boolean | undefined; excludeFromLastConversationStorage?: boolean | undefined; replacements?: { value: string; uuid: string; }[] | undefined; }>" + "Zod.ZodObject<{ title: Zod.ZodString; category: Zod.ZodOptional>; messages: Zod.ZodOptional, Zod.objectInputType<{}, Zod.ZodUnknown, \"strip\">>>; role: Zod.ZodEnum<[\"system\", \"user\", \"assistant\"]>; timestamp: Zod.ZodString; isError: Zod.ZodOptional; traceData: Zod.ZodOptional; traceId: Zod.ZodOptional; }, \"strip\", Zod.ZodTypeAny, { transactionId?: string | undefined; traceId?: string | undefined; }, { transactionId?: string | undefined; traceId?: string | undefined; }>>; }, \"strip\", Zod.ZodTypeAny, { timestamp: string; role: \"user\" | \"system\" | \"assistant\"; content: string; reader?: Zod.objectOutputType<{}, Zod.ZodUnknown, \"strip\"> | undefined; isError?: boolean | undefined; traceData?: { transactionId?: string | undefined; traceId?: string | undefined; } | undefined; }, { timestamp: string; role: \"user\" | \"system\" | \"assistant\"; content: string; reader?: Zod.objectInputType<{}, Zod.ZodUnknown, \"strip\"> | undefined; isError?: boolean | undefined; traceData?: { transactionId?: string | undefined; traceId?: string | undefined; } | undefined; }>, \"many\">>; apiConfig: Zod.ZodOptional; provider: Zod.ZodOptional>; model: Zod.ZodOptional; }, \"strip\", Zod.ZodTypeAny, { connectorId: string; defaultSystemPromptId?: string | undefined; provider?: \"OpenAI\" | \"Azure OpenAI\" | undefined; model?: string | undefined; }, { connectorId: string; defaultSystemPromptId?: string | undefined; provider?: \"OpenAI\" | \"Azure OpenAI\" | undefined; model?: string | undefined; }>>; isDefault: Zod.ZodOptional; excludeFromLastConversationStorage: Zod.ZodOptional; replacements: Zod.ZodOptional, Zod.objectInputType<{}, Zod.ZodString, \"strip\">>>; }, \"strip\", Zod.ZodTypeAny, { title: string; category?: \"assistant\" | \"insights\" | undefined; messages?: { timestamp: string; role: \"user\" | \"system\" | \"assistant\"; content: string; reader?: Zod.objectOutputType<{}, Zod.ZodUnknown, \"strip\"> | undefined; isError?: boolean | undefined; traceData?: { transactionId?: string | undefined; traceId?: string | undefined; } | undefined; }[] | undefined; apiConfig?: { connectorId: string; defaultSystemPromptId?: string | undefined; provider?: \"OpenAI\" | \"Azure OpenAI\" | undefined; model?: string | undefined; } | undefined; isDefault?: boolean | undefined; excludeFromLastConversationStorage?: boolean | undefined; replacements?: Zod.objectOutputType<{}, Zod.ZodString, \"strip\"> | undefined; }, { title: string; category?: \"assistant\" | \"insights\" | undefined; messages?: { timestamp: string; role: \"user\" | \"system\" | \"assistant\"; content: string; reader?: Zod.objectInputType<{}, Zod.ZodUnknown, \"strip\"> | undefined; isError?: boolean | undefined; traceData?: { transactionId?: string | undefined; traceId?: string | undefined; } | undefined; }[] | undefined; apiConfig?: { connectorId: string; defaultSystemPromptId?: string | undefined; provider?: \"OpenAI\" | \"Azure OpenAI\" | undefined; model?: string | undefined; } | undefined; isDefault?: boolean | undefined; excludeFromLastConversationStorage?: boolean | undefined; replacements?: Zod.objectInputType<{}, Zod.ZodString, \"strip\"> | undefined; }>" ], "path": "x-pack/packages/kbn-elastic-assistant-common/impl/schemas/conversations/crud_conversation_route.gen.ts", "deprecated": false, @@ -2643,7 +2643,7 @@ "label": "CreateConversationResponse", "description": [], "signature": [ - "Zod.ZodObject<{ id: Zod.ZodUnion<[Zod.ZodString, Zod.ZodString]>; title: Zod.ZodString; category: Zod.ZodEnum<[\"assistant\", \"insights\"]>; summary: Zod.ZodOptional; timestamp: Zod.ZodOptional; public: Zod.ZodOptional; confidence: Zod.ZodOptional>; }, \"strip\", Zod.ZodTypeAny, { content?: string | undefined; timestamp?: string | undefined; public?: boolean | undefined; confidence?: \"medium\" | \"low\" | \"high\" | undefined; }, { content?: string | undefined; timestamp?: string | undefined; public?: boolean | undefined; confidence?: \"medium\" | \"low\" | \"high\" | undefined; }>>; timestamp: Zod.ZodOptional; updatedAt: Zod.ZodOptional; createdAt: Zod.ZodString; replacements: Zod.ZodOptional, \"many\">>; users: Zod.ZodArray; name: Zod.ZodOptional; }, \"strip\", Zod.ZodTypeAny, { id?: string | undefined; name?: string | undefined; }, { id?: string | undefined; name?: string | undefined; }>, \"many\">; messages: Zod.ZodOptional, Zod.objectInputType<{}, Zod.ZodUnknown, \"strip\">>>; role: Zod.ZodEnum<[\"system\", \"user\", \"assistant\"]>; timestamp: Zod.ZodString; isError: Zod.ZodOptional; traceData: Zod.ZodOptional; traceId: Zod.ZodOptional; }, \"strip\", Zod.ZodTypeAny, { transactionId?: string | undefined; traceId?: string | undefined; }, { transactionId?: string | undefined; traceId?: string | undefined; }>>; }, \"strip\", Zod.ZodTypeAny, { timestamp: string; role: \"user\" | \"system\" | \"assistant\"; content: string; reader?: Zod.objectOutputType<{}, Zod.ZodUnknown, \"strip\"> | undefined; isError?: boolean | undefined; traceData?: { transactionId?: string | undefined; traceId?: string | undefined; } | undefined; }, { timestamp: string; role: \"user\" | \"system\" | \"assistant\"; content: string; reader?: Zod.objectInputType<{}, Zod.ZodUnknown, \"strip\"> | undefined; isError?: boolean | undefined; traceData?: { transactionId?: string | undefined; traceId?: string | undefined; } | undefined; }>, \"many\">>; apiConfig: Zod.ZodOptional; provider: Zod.ZodOptional>; model: Zod.ZodOptional; }, \"strip\", Zod.ZodTypeAny, { connectorId: string; defaultSystemPromptId?: string | undefined; provider?: \"OpenAI\" | \"Azure OpenAI\" | undefined; model?: string | undefined; }, { connectorId: string; defaultSystemPromptId?: string | undefined; provider?: \"OpenAI\" | \"Azure OpenAI\" | undefined; model?: string | undefined; }>>; isDefault: Zod.ZodOptional; excludeFromLastConversationStorage: Zod.ZodOptional; namespace: Zod.ZodString; }, \"strip\", Zod.ZodTypeAny, { id: string; namespace: string; title: string; createdAt: string; category: \"assistant\" | \"insights\"; users: { id?: string | undefined; name?: string | undefined; }[]; summary?: { content?: string | undefined; timestamp?: string | undefined; public?: boolean | undefined; confidence?: \"medium\" | \"low\" | \"high\" | undefined; } | undefined; timestamp?: string | undefined; updatedAt?: string | undefined; replacements?: { value: string; uuid: string; }[] | undefined; messages?: { timestamp: string; role: \"user\" | \"system\" | \"assistant\"; content: string; reader?: Zod.objectOutputType<{}, Zod.ZodUnknown, \"strip\"> | undefined; isError?: boolean | undefined; traceData?: { transactionId?: string | undefined; traceId?: string | undefined; } | undefined; }[] | undefined; apiConfig?: { connectorId: string; defaultSystemPromptId?: string | undefined; provider?: \"OpenAI\" | \"Azure OpenAI\" | undefined; model?: string | undefined; } | undefined; isDefault?: boolean | undefined; excludeFromLastConversationStorage?: boolean | undefined; }, { id: string; namespace: string; title: string; createdAt: string; category: \"assistant\" | \"insights\"; users: { id?: string | undefined; name?: string | undefined; }[]; summary?: { content?: string | undefined; timestamp?: string | undefined; public?: boolean | undefined; confidence?: \"medium\" | \"low\" | \"high\" | undefined; } | undefined; timestamp?: string | undefined; updatedAt?: string | undefined; replacements?: { value: string; uuid: string; }[] | undefined; messages?: { timestamp: string; role: \"user\" | \"system\" | \"assistant\"; content: string; reader?: Zod.objectInputType<{}, Zod.ZodUnknown, \"strip\"> | undefined; isError?: boolean | undefined; traceData?: { transactionId?: string | undefined; traceId?: string | undefined; } | undefined; }[] | undefined; apiConfig?: { connectorId: string; defaultSystemPromptId?: string | undefined; provider?: \"OpenAI\" | \"Azure OpenAI\" | undefined; model?: string | undefined; } | undefined; isDefault?: boolean | undefined; excludeFromLastConversationStorage?: boolean | undefined; }>" + "Zod.ZodObject<{ id: Zod.ZodUnion<[Zod.ZodString, Zod.ZodString]>; title: Zod.ZodString; category: Zod.ZodEnum<[\"assistant\", \"insights\"]>; summary: Zod.ZodOptional; timestamp: Zod.ZodOptional; public: Zod.ZodOptional; confidence: Zod.ZodOptional>; }, \"strip\", Zod.ZodTypeAny, { content?: string | undefined; timestamp?: string | undefined; public?: boolean | undefined; confidence?: \"medium\" | \"low\" | \"high\" | undefined; }, { content?: string | undefined; timestamp?: string | undefined; public?: boolean | undefined; confidence?: \"medium\" | \"low\" | \"high\" | undefined; }>>; timestamp: Zod.ZodOptional; updatedAt: Zod.ZodOptional; createdAt: Zod.ZodString; replacements: Zod.ZodOptional, Zod.objectInputType<{}, Zod.ZodString, \"strip\">>>; users: Zod.ZodArray; name: Zod.ZodOptional; }, \"strip\", Zod.ZodTypeAny, { id?: string | undefined; name?: string | undefined; }, { id?: string | undefined; name?: string | undefined; }>, \"many\">; messages: Zod.ZodOptional, Zod.objectInputType<{}, Zod.ZodUnknown, \"strip\">>>; role: Zod.ZodEnum<[\"system\", \"user\", \"assistant\"]>; timestamp: Zod.ZodString; isError: Zod.ZodOptional; traceData: Zod.ZodOptional; traceId: Zod.ZodOptional; }, \"strip\", Zod.ZodTypeAny, { transactionId?: string | undefined; traceId?: string | undefined; }, { transactionId?: string | undefined; traceId?: string | undefined; }>>; }, \"strip\", Zod.ZodTypeAny, { timestamp: string; role: \"user\" | \"system\" | \"assistant\"; content: string; reader?: Zod.objectOutputType<{}, Zod.ZodUnknown, \"strip\"> | undefined; isError?: boolean | undefined; traceData?: { transactionId?: string | undefined; traceId?: string | undefined; } | undefined; }, { timestamp: string; role: \"user\" | \"system\" | \"assistant\"; content: string; reader?: Zod.objectInputType<{}, Zod.ZodUnknown, \"strip\"> | undefined; isError?: boolean | undefined; traceData?: { transactionId?: string | undefined; traceId?: string | undefined; } | undefined; }>, \"many\">>; apiConfig: Zod.ZodOptional; provider: Zod.ZodOptional>; model: Zod.ZodOptional; }, \"strip\", Zod.ZodTypeAny, { connectorId: string; defaultSystemPromptId?: string | undefined; provider?: \"OpenAI\" | \"Azure OpenAI\" | undefined; model?: string | undefined; }, { connectorId: string; defaultSystemPromptId?: string | undefined; provider?: \"OpenAI\" | \"Azure OpenAI\" | undefined; model?: string | undefined; }>>; isDefault: Zod.ZodOptional; excludeFromLastConversationStorage: Zod.ZodOptional; namespace: Zod.ZodString; }, \"strip\", Zod.ZodTypeAny, { id: string; namespace: string; title: string; createdAt: string; category: \"assistant\" | \"insights\"; users: { id?: string | undefined; name?: string | undefined; }[]; summary?: { content?: string | undefined; timestamp?: string | undefined; public?: boolean | undefined; confidence?: \"medium\" | \"low\" | \"high\" | undefined; } | undefined; timestamp?: string | undefined; updatedAt?: string | undefined; replacements?: Zod.objectOutputType<{}, Zod.ZodString, \"strip\"> | undefined; messages?: { timestamp: string; role: \"user\" | \"system\" | \"assistant\"; content: string; reader?: Zod.objectOutputType<{}, Zod.ZodUnknown, \"strip\"> | undefined; isError?: boolean | undefined; traceData?: { transactionId?: string | undefined; traceId?: string | undefined; } | undefined; }[] | undefined; apiConfig?: { connectorId: string; defaultSystemPromptId?: string | undefined; provider?: \"OpenAI\" | \"Azure OpenAI\" | undefined; model?: string | undefined; } | undefined; isDefault?: boolean | undefined; excludeFromLastConversationStorage?: boolean | undefined; }, { id: string; namespace: string; title: string; createdAt: string; category: \"assistant\" | \"insights\"; users: { id?: string | undefined; name?: string | undefined; }[]; summary?: { content?: string | undefined; timestamp?: string | undefined; public?: boolean | undefined; confidence?: \"medium\" | \"low\" | \"high\" | undefined; } | undefined; timestamp?: string | undefined; updatedAt?: string | undefined; replacements?: Zod.objectInputType<{}, Zod.ZodString, \"strip\"> | undefined; messages?: { timestamp: string; role: \"user\" | \"system\" | \"assistant\"; content: string; reader?: Zod.objectInputType<{}, Zod.ZodUnknown, \"strip\"> | undefined; isError?: boolean | undefined; traceData?: { transactionId?: string | undefined; traceId?: string | undefined; } | undefined; }[] | undefined; apiConfig?: { connectorId: string; defaultSystemPromptId?: string | undefined; provider?: \"OpenAI\" | \"Azure OpenAI\" | undefined; model?: string | undefined; } | undefined; isDefault?: boolean | undefined; excludeFromLastConversationStorage?: boolean | undefined; }>" ], "path": "x-pack/packages/kbn-elastic-assistant-common/impl/schemas/conversations/crud_conversation_route.gen.ts", "deprecated": false, @@ -2750,7 +2750,7 @@ "label": "DeleteConversationResponse", "description": [], "signature": [ - "Zod.ZodObject<{ id: Zod.ZodUnion<[Zod.ZodString, Zod.ZodString]>; title: Zod.ZodString; category: Zod.ZodEnum<[\"assistant\", \"insights\"]>; summary: Zod.ZodOptional; timestamp: Zod.ZodOptional; public: Zod.ZodOptional; confidence: Zod.ZodOptional>; }, \"strip\", Zod.ZodTypeAny, { content?: string | undefined; timestamp?: string | undefined; public?: boolean | undefined; confidence?: \"medium\" | \"low\" | \"high\" | undefined; }, { content?: string | undefined; timestamp?: string | undefined; public?: boolean | undefined; confidence?: \"medium\" | \"low\" | \"high\" | undefined; }>>; timestamp: Zod.ZodOptional; updatedAt: Zod.ZodOptional; createdAt: Zod.ZodString; replacements: Zod.ZodOptional, \"many\">>; users: Zod.ZodArray; name: Zod.ZodOptional; }, \"strip\", Zod.ZodTypeAny, { id?: string | undefined; name?: string | undefined; }, { id?: string | undefined; name?: string | undefined; }>, \"many\">; messages: Zod.ZodOptional, Zod.objectInputType<{}, Zod.ZodUnknown, \"strip\">>>; role: Zod.ZodEnum<[\"system\", \"user\", \"assistant\"]>; timestamp: Zod.ZodString; isError: Zod.ZodOptional; traceData: Zod.ZodOptional; traceId: Zod.ZodOptional; }, \"strip\", Zod.ZodTypeAny, { transactionId?: string | undefined; traceId?: string | undefined; }, { transactionId?: string | undefined; traceId?: string | undefined; }>>; }, \"strip\", Zod.ZodTypeAny, { timestamp: string; role: \"user\" | \"system\" | \"assistant\"; content: string; reader?: Zod.objectOutputType<{}, Zod.ZodUnknown, \"strip\"> | undefined; isError?: boolean | undefined; traceData?: { transactionId?: string | undefined; traceId?: string | undefined; } | undefined; }, { timestamp: string; role: \"user\" | \"system\" | \"assistant\"; content: string; reader?: Zod.objectInputType<{}, Zod.ZodUnknown, \"strip\"> | undefined; isError?: boolean | undefined; traceData?: { transactionId?: string | undefined; traceId?: string | undefined; } | undefined; }>, \"many\">>; apiConfig: Zod.ZodOptional; provider: Zod.ZodOptional>; model: Zod.ZodOptional; }, \"strip\", Zod.ZodTypeAny, { connectorId: string; defaultSystemPromptId?: string | undefined; provider?: \"OpenAI\" | \"Azure OpenAI\" | undefined; model?: string | undefined; }, { connectorId: string; defaultSystemPromptId?: string | undefined; provider?: \"OpenAI\" | \"Azure OpenAI\" | undefined; model?: string | undefined; }>>; isDefault: Zod.ZodOptional; excludeFromLastConversationStorage: Zod.ZodOptional; namespace: Zod.ZodString; }, \"strip\", Zod.ZodTypeAny, { id: string; namespace: string; title: string; createdAt: string; category: \"assistant\" | \"insights\"; users: { id?: string | undefined; name?: string | undefined; }[]; summary?: { content?: string | undefined; timestamp?: string | undefined; public?: boolean | undefined; confidence?: \"medium\" | \"low\" | \"high\" | undefined; } | undefined; timestamp?: string | undefined; updatedAt?: string | undefined; replacements?: { value: string; uuid: string; }[] | undefined; messages?: { timestamp: string; role: \"user\" | \"system\" | \"assistant\"; content: string; reader?: Zod.objectOutputType<{}, Zod.ZodUnknown, \"strip\"> | undefined; isError?: boolean | undefined; traceData?: { transactionId?: string | undefined; traceId?: string | undefined; } | undefined; }[] | undefined; apiConfig?: { connectorId: string; defaultSystemPromptId?: string | undefined; provider?: \"OpenAI\" | \"Azure OpenAI\" | undefined; model?: string | undefined; } | undefined; isDefault?: boolean | undefined; excludeFromLastConversationStorage?: boolean | undefined; }, { id: string; namespace: string; title: string; createdAt: string; category: \"assistant\" | \"insights\"; users: { id?: string | undefined; name?: string | undefined; }[]; summary?: { content?: string | undefined; timestamp?: string | undefined; public?: boolean | undefined; confidence?: \"medium\" | \"low\" | \"high\" | undefined; } | undefined; timestamp?: string | undefined; updatedAt?: string | undefined; replacements?: { value: string; uuid: string; }[] | undefined; messages?: { timestamp: string; role: \"user\" | \"system\" | \"assistant\"; content: string; reader?: Zod.objectInputType<{}, Zod.ZodUnknown, \"strip\"> | undefined; isError?: boolean | undefined; traceData?: { transactionId?: string | undefined; traceId?: string | undefined; } | undefined; }[] | undefined; apiConfig?: { connectorId: string; defaultSystemPromptId?: string | undefined; provider?: \"OpenAI\" | \"Azure OpenAI\" | undefined; model?: string | undefined; } | undefined; isDefault?: boolean | undefined; excludeFromLastConversationStorage?: boolean | undefined; }>" + "Zod.ZodObject<{ id: Zod.ZodUnion<[Zod.ZodString, Zod.ZodString]>; title: Zod.ZodString; category: Zod.ZodEnum<[\"assistant\", \"insights\"]>; summary: Zod.ZodOptional; timestamp: Zod.ZodOptional; public: Zod.ZodOptional; confidence: Zod.ZodOptional>; }, \"strip\", Zod.ZodTypeAny, { content?: string | undefined; timestamp?: string | undefined; public?: boolean | undefined; confidence?: \"medium\" | \"low\" | \"high\" | undefined; }, { content?: string | undefined; timestamp?: string | undefined; public?: boolean | undefined; confidence?: \"medium\" | \"low\" | \"high\" | undefined; }>>; timestamp: Zod.ZodOptional; updatedAt: Zod.ZodOptional; createdAt: Zod.ZodString; replacements: Zod.ZodOptional, Zod.objectInputType<{}, Zod.ZodString, \"strip\">>>; users: Zod.ZodArray; name: Zod.ZodOptional; }, \"strip\", Zod.ZodTypeAny, { id?: string | undefined; name?: string | undefined; }, { id?: string | undefined; name?: string | undefined; }>, \"many\">; messages: Zod.ZodOptional, Zod.objectInputType<{}, Zod.ZodUnknown, \"strip\">>>; role: Zod.ZodEnum<[\"system\", \"user\", \"assistant\"]>; timestamp: Zod.ZodString; isError: Zod.ZodOptional; traceData: Zod.ZodOptional; traceId: Zod.ZodOptional; }, \"strip\", Zod.ZodTypeAny, { transactionId?: string | undefined; traceId?: string | undefined; }, { transactionId?: string | undefined; traceId?: string | undefined; }>>; }, \"strip\", Zod.ZodTypeAny, { timestamp: string; role: \"user\" | \"system\" | \"assistant\"; content: string; reader?: Zod.objectOutputType<{}, Zod.ZodUnknown, \"strip\"> | undefined; isError?: boolean | undefined; traceData?: { transactionId?: string | undefined; traceId?: string | undefined; } | undefined; }, { timestamp: string; role: \"user\" | \"system\" | \"assistant\"; content: string; reader?: Zod.objectInputType<{}, Zod.ZodUnknown, \"strip\"> | undefined; isError?: boolean | undefined; traceData?: { transactionId?: string | undefined; traceId?: string | undefined; } | undefined; }>, \"many\">>; apiConfig: Zod.ZodOptional; provider: Zod.ZodOptional>; model: Zod.ZodOptional; }, \"strip\", Zod.ZodTypeAny, { connectorId: string; defaultSystemPromptId?: string | undefined; provider?: \"OpenAI\" | \"Azure OpenAI\" | undefined; model?: string | undefined; }, { connectorId: string; defaultSystemPromptId?: string | undefined; provider?: \"OpenAI\" | \"Azure OpenAI\" | undefined; model?: string | undefined; }>>; isDefault: Zod.ZodOptional; excludeFromLastConversationStorage: Zod.ZodOptional; namespace: Zod.ZodString; }, \"strip\", Zod.ZodTypeAny, { id: string; namespace: string; title: string; createdAt: string; category: \"assistant\" | \"insights\"; users: { id?: string | undefined; name?: string | undefined; }[]; summary?: { content?: string | undefined; timestamp?: string | undefined; public?: boolean | undefined; confidence?: \"medium\" | \"low\" | \"high\" | undefined; } | undefined; timestamp?: string | undefined; updatedAt?: string | undefined; replacements?: Zod.objectOutputType<{}, Zod.ZodString, \"strip\"> | undefined; messages?: { timestamp: string; role: \"user\" | \"system\" | \"assistant\"; content: string; reader?: Zod.objectOutputType<{}, Zod.ZodUnknown, \"strip\"> | undefined; isError?: boolean | undefined; traceData?: { transactionId?: string | undefined; traceId?: string | undefined; } | undefined; }[] | undefined; apiConfig?: { connectorId: string; defaultSystemPromptId?: string | undefined; provider?: \"OpenAI\" | \"Azure OpenAI\" | undefined; model?: string | undefined; } | undefined; isDefault?: boolean | undefined; excludeFromLastConversationStorage?: boolean | undefined; }, { id: string; namespace: string; title: string; createdAt: string; category: \"assistant\" | \"insights\"; users: { id?: string | undefined; name?: string | undefined; }[]; summary?: { content?: string | undefined; timestamp?: string | undefined; public?: boolean | undefined; confidence?: \"medium\" | \"low\" | \"high\" | undefined; } | undefined; timestamp?: string | undefined; updatedAt?: string | undefined; replacements?: Zod.objectInputType<{}, Zod.ZodString, \"strip\"> | undefined; messages?: { timestamp: string; role: \"user\" | \"system\" | \"assistant\"; content: string; reader?: Zod.objectInputType<{}, Zod.ZodUnknown, \"strip\"> | undefined; isError?: boolean | undefined; traceData?: { transactionId?: string | undefined; traceId?: string | undefined; } | undefined; }[] | undefined; apiConfig?: { connectorId: string; defaultSystemPromptId?: string | undefined; provider?: \"OpenAI\" | \"Azure OpenAI\" | undefined; model?: string | undefined; } | undefined; isDefault?: boolean | undefined; excludeFromLastConversationStorage?: boolean | undefined; }>" ], "path": "x-pack/packages/kbn-elastic-assistant-common/impl/schemas/conversations/crud_conversation_route.gen.ts", "deprecated": false, @@ -2810,7 +2810,7 @@ "label": "ExecuteConnectorRequestBody", "description": [], "signature": [ - "Zod.ZodObject<{ conversationId: Zod.ZodOptional; message: Zod.ZodOptional; model: Zod.ZodOptional; subAction: Zod.ZodEnum<[\"invokeAI\", \"invokeStream\"]>; alertsIndexPattern: Zod.ZodOptional; allow: Zod.ZodOptional>; allowReplacement: Zod.ZodOptional>; isEnabledKnowledgeBase: Zod.ZodOptional; isEnabledRAGAlerts: Zod.ZodOptional; replacements: Zod.ZodArray, \"many\">; size: Zod.ZodOptional; }, \"strip\", Zod.ZodTypeAny, { subAction: \"invokeAI\" | \"invokeStream\"; replacements: { value: string; uuid: string; }[]; conversationId?: string | undefined; message?: string | undefined; model?: string | undefined; alertsIndexPattern?: string | undefined; allow?: string[] | undefined; allowReplacement?: string[] | undefined; isEnabledKnowledgeBase?: boolean | undefined; isEnabledRAGAlerts?: boolean | undefined; size?: number | undefined; }, { subAction: \"invokeAI\" | \"invokeStream\"; replacements: { value: string; uuid: string; }[]; conversationId?: string | undefined; message?: string | undefined; model?: string | undefined; alertsIndexPattern?: string | undefined; allow?: string[] | undefined; allowReplacement?: string[] | undefined; isEnabledKnowledgeBase?: boolean | undefined; isEnabledRAGAlerts?: boolean | undefined; size?: number | undefined; }>" + "Zod.ZodObject<{ conversationId: Zod.ZodOptional; message: Zod.ZodOptional; model: Zod.ZodOptional; subAction: Zod.ZodEnum<[\"invokeAI\", \"invokeStream\"]>; alertsIndexPattern: Zod.ZodOptional; allow: Zod.ZodOptional>; allowReplacement: Zod.ZodOptional>; isEnabledKnowledgeBase: Zod.ZodOptional; isEnabledRAGAlerts: Zod.ZodOptional; replacements: Zod.ZodObject<{}, \"strip\", Zod.ZodString, Zod.objectOutputType<{}, Zod.ZodString, \"strip\">, Zod.objectInputType<{}, Zod.ZodString, \"strip\">>; size: Zod.ZodOptional; }, \"strip\", Zod.ZodTypeAny, { subAction: \"invokeAI\" | \"invokeStream\"; replacements: {} & { [k: string]: string; }; conversationId?: string | undefined; message?: string | undefined; model?: string | undefined; alertsIndexPattern?: string | undefined; allow?: string[] | undefined; allowReplacement?: string[] | undefined; isEnabledKnowledgeBase?: boolean | undefined; isEnabledRAGAlerts?: boolean | undefined; size?: number | undefined; }, { subAction: \"invokeAI\" | \"invokeStream\"; replacements: {} & { [k: string]: string; }; conversationId?: string | undefined; message?: string | undefined; model?: string | undefined; alertsIndexPattern?: string | undefined; allow?: string[] | undefined; allowReplacement?: string[] | undefined; isEnabledKnowledgeBase?: boolean | undefined; isEnabledRAGAlerts?: boolean | undefined; size?: number | undefined; }>" ], "path": "x-pack/packages/kbn-elastic-assistant-common/impl/schemas/actions_connector/post_actions_connector_execute_route.gen.ts", "deprecated": false, @@ -2840,7 +2840,7 @@ "label": "ExecuteConnectorResponse", "description": [], "signature": [ - "Zod.ZodObject<{ data: Zod.ZodOptional; connector_id: Zod.ZodOptional; replacements: Zod.ZodOptional, \"many\">>; status: Zod.ZodOptional; trace_data: Zod.ZodOptional; traceId: Zod.ZodOptional; }, \"strip\", Zod.ZodTypeAny, { transactionId?: string | undefined; traceId?: string | undefined; }, { transactionId?: string | undefined; traceId?: string | undefined; }>>; }, \"strip\", Zod.ZodTypeAny, { data?: string | undefined; connector_id?: string | undefined; replacements?: { value: string; uuid: string; }[] | undefined; status?: string | undefined; trace_data?: { transactionId?: string | undefined; traceId?: string | undefined; } | undefined; }, { data?: string | undefined; connector_id?: string | undefined; replacements?: { value: string; uuid: string; }[] | undefined; status?: string | undefined; trace_data?: { transactionId?: string | undefined; traceId?: string | undefined; } | undefined; }>" + "Zod.ZodObject<{ data: Zod.ZodOptional; connector_id: Zod.ZodOptional; replacements: Zod.ZodOptional, Zod.objectInputType<{}, Zod.ZodString, \"strip\">>>; status: Zod.ZodOptional; trace_data: Zod.ZodOptional; traceId: Zod.ZodOptional; }, \"strip\", Zod.ZodTypeAny, { transactionId?: string | undefined; traceId?: string | undefined; }, { transactionId?: string | undefined; traceId?: string | undefined; }>>; }, \"strip\", Zod.ZodTypeAny, { data?: string | undefined; connector_id?: string | undefined; replacements?: Zod.objectOutputType<{}, Zod.ZodString, \"strip\"> | undefined; status?: string | undefined; trace_data?: { transactionId?: string | undefined; traceId?: string | undefined; } | undefined; }, { data?: string | undefined; connector_id?: string | undefined; replacements?: Zod.objectInputType<{}, Zod.ZodString, \"strip\"> | undefined; status?: string | undefined; trace_data?: { transactionId?: string | undefined; traceId?: string | undefined; } | undefined; }>" ], "path": "x-pack/packages/kbn-elastic-assistant-common/impl/schemas/actions_connector/post_actions_connector_execute_route.gen.ts", "deprecated": false, @@ -2870,7 +2870,7 @@ "label": "FindConversationsResponse", "description": [], "signature": [ - "Zod.ZodObject<{ page: Zod.ZodNumber; perPage: Zod.ZodNumber; total: Zod.ZodNumber; data: Zod.ZodArray; title: Zod.ZodString; category: Zod.ZodEnum<[\"assistant\", \"insights\"]>; summary: Zod.ZodOptional; timestamp: Zod.ZodOptional; public: Zod.ZodOptional; confidence: Zod.ZodOptional>; }, \"strip\", Zod.ZodTypeAny, { content?: string | undefined; timestamp?: string | undefined; public?: boolean | undefined; confidence?: \"medium\" | \"low\" | \"high\" | undefined; }, { content?: string | undefined; timestamp?: string | undefined; public?: boolean | undefined; confidence?: \"medium\" | \"low\" | \"high\" | undefined; }>>; timestamp: Zod.ZodOptional; updatedAt: Zod.ZodOptional; createdAt: Zod.ZodString; replacements: Zod.ZodOptional, \"many\">>; users: Zod.ZodArray; name: Zod.ZodOptional; }, \"strip\", Zod.ZodTypeAny, { id?: string | undefined; name?: string | undefined; }, { id?: string | undefined; name?: string | undefined; }>, \"many\">; messages: Zod.ZodOptional, Zod.objectInputType<{}, Zod.ZodUnknown, \"strip\">>>; role: Zod.ZodEnum<[\"system\", \"user\", \"assistant\"]>; timestamp: Zod.ZodString; isError: Zod.ZodOptional; traceData: Zod.ZodOptional; traceId: Zod.ZodOptional; }, \"strip\", Zod.ZodTypeAny, { transactionId?: string | undefined; traceId?: string | undefined; }, { transactionId?: string | undefined; traceId?: string | undefined; }>>; }, \"strip\", Zod.ZodTypeAny, { timestamp: string; role: \"user\" | \"system\" | \"assistant\"; content: string; reader?: Zod.objectOutputType<{}, Zod.ZodUnknown, \"strip\"> | undefined; isError?: boolean | undefined; traceData?: { transactionId?: string | undefined; traceId?: string | undefined; } | undefined; }, { timestamp: string; role: \"user\" | \"system\" | \"assistant\"; content: string; reader?: Zod.objectInputType<{}, Zod.ZodUnknown, \"strip\"> | undefined; isError?: boolean | undefined; traceData?: { transactionId?: string | undefined; traceId?: string | undefined; } | undefined; }>, \"many\">>; apiConfig: Zod.ZodOptional; provider: Zod.ZodOptional>; model: Zod.ZodOptional; }, \"strip\", Zod.ZodTypeAny, { connectorId: string; defaultSystemPromptId?: string | undefined; provider?: \"OpenAI\" | \"Azure OpenAI\" | undefined; model?: string | undefined; }, { connectorId: string; defaultSystemPromptId?: string | undefined; provider?: \"OpenAI\" | \"Azure OpenAI\" | undefined; model?: string | undefined; }>>; isDefault: Zod.ZodOptional; excludeFromLastConversationStorage: Zod.ZodOptional; namespace: Zod.ZodString; }, \"strip\", Zod.ZodTypeAny, { id: string; namespace: string; title: string; createdAt: string; category: \"assistant\" | \"insights\"; users: { id?: string | undefined; name?: string | undefined; }[]; summary?: { content?: string | undefined; timestamp?: string | undefined; public?: boolean | undefined; confidence?: \"medium\" | \"low\" | \"high\" | undefined; } | undefined; timestamp?: string | undefined; updatedAt?: string | undefined; replacements?: { value: string; uuid: string; }[] | undefined; messages?: { timestamp: string; role: \"user\" | \"system\" | \"assistant\"; content: string; reader?: Zod.objectOutputType<{}, Zod.ZodUnknown, \"strip\"> | undefined; isError?: boolean | undefined; traceData?: { transactionId?: string | undefined; traceId?: string | undefined; } | undefined; }[] | undefined; apiConfig?: { connectorId: string; defaultSystemPromptId?: string | undefined; provider?: \"OpenAI\" | \"Azure OpenAI\" | undefined; model?: string | undefined; } | undefined; isDefault?: boolean | undefined; excludeFromLastConversationStorage?: boolean | undefined; }, { id: string; namespace: string; title: string; createdAt: string; category: \"assistant\" | \"insights\"; users: { id?: string | undefined; name?: string | undefined; }[]; summary?: { content?: string | undefined; timestamp?: string | undefined; public?: boolean | undefined; confidence?: \"medium\" | \"low\" | \"high\" | undefined; } | undefined; timestamp?: string | undefined; updatedAt?: string | undefined; replacements?: { value: string; uuid: string; }[] | undefined; messages?: { timestamp: string; role: \"user\" | \"system\" | \"assistant\"; content: string; reader?: Zod.objectInputType<{}, Zod.ZodUnknown, \"strip\"> | undefined; isError?: boolean | undefined; traceData?: { transactionId?: string | undefined; traceId?: string | undefined; } | undefined; }[] | undefined; apiConfig?: { connectorId: string; defaultSystemPromptId?: string | undefined; provider?: \"OpenAI\" | \"Azure OpenAI\" | undefined; model?: string | undefined; } | undefined; isDefault?: boolean | undefined; excludeFromLastConversationStorage?: boolean | undefined; }>, \"many\">; }, \"strip\", Zod.ZodTypeAny, { page: number; perPage: number; total: number; data: { id: string; namespace: string; title: string; createdAt: string; category: \"assistant\" | \"insights\"; users: { id?: string | undefined; name?: string | undefined; }[]; summary?: { content?: string | undefined; timestamp?: string | undefined; public?: boolean | undefined; confidence?: \"medium\" | \"low\" | \"high\" | undefined; } | undefined; timestamp?: string | undefined; updatedAt?: string | undefined; replacements?: { value: string; uuid: string; }[] | undefined; messages?: { timestamp: string; role: \"user\" | \"system\" | \"assistant\"; content: string; reader?: Zod.objectOutputType<{}, Zod.ZodUnknown, \"strip\"> | undefined; isError?: boolean | undefined; traceData?: { transactionId?: string | undefined; traceId?: string | undefined; } | undefined; }[] | undefined; apiConfig?: { connectorId: string; defaultSystemPromptId?: string | undefined; provider?: \"OpenAI\" | \"Azure OpenAI\" | undefined; model?: string | undefined; } | undefined; isDefault?: boolean | undefined; excludeFromLastConversationStorage?: boolean | undefined; }[]; }, { page: number; perPage: number; total: number; data: { id: string; namespace: string; title: string; createdAt: string; category: \"assistant\" | \"insights\"; users: { id?: string | undefined; name?: string | undefined; }[]; summary?: { content?: string | undefined; timestamp?: string | undefined; public?: boolean | undefined; confidence?: \"medium\" | \"low\" | \"high\" | undefined; } | undefined; timestamp?: string | undefined; updatedAt?: string | undefined; replacements?: { value: string; uuid: string; }[] | undefined; messages?: { timestamp: string; role: \"user\" | \"system\" | \"assistant\"; content: string; reader?: Zod.objectInputType<{}, Zod.ZodUnknown, \"strip\"> | undefined; isError?: boolean | undefined; traceData?: { transactionId?: string | undefined; traceId?: string | undefined; } | undefined; }[] | undefined; apiConfig?: { connectorId: string; defaultSystemPromptId?: string | undefined; provider?: \"OpenAI\" | \"Azure OpenAI\" | undefined; model?: string | undefined; } | undefined; isDefault?: boolean | undefined; excludeFromLastConversationStorage?: boolean | undefined; }[]; }>" + "Zod.ZodObject<{ page: Zod.ZodNumber; perPage: Zod.ZodNumber; total: Zod.ZodNumber; data: Zod.ZodArray; title: Zod.ZodString; category: Zod.ZodEnum<[\"assistant\", \"insights\"]>; summary: Zod.ZodOptional; timestamp: Zod.ZodOptional; public: Zod.ZodOptional; confidence: Zod.ZodOptional>; }, \"strip\", Zod.ZodTypeAny, { content?: string | undefined; timestamp?: string | undefined; public?: boolean | undefined; confidence?: \"medium\" | \"low\" | \"high\" | undefined; }, { content?: string | undefined; timestamp?: string | undefined; public?: boolean | undefined; confidence?: \"medium\" | \"low\" | \"high\" | undefined; }>>; timestamp: Zod.ZodOptional; updatedAt: Zod.ZodOptional; createdAt: Zod.ZodString; replacements: Zod.ZodOptional, Zod.objectInputType<{}, Zod.ZodString, \"strip\">>>; users: Zod.ZodArray; name: Zod.ZodOptional; }, \"strip\", Zod.ZodTypeAny, { id?: string | undefined; name?: string | undefined; }, { id?: string | undefined; name?: string | undefined; }>, \"many\">; messages: Zod.ZodOptional, Zod.objectInputType<{}, Zod.ZodUnknown, \"strip\">>>; role: Zod.ZodEnum<[\"system\", \"user\", \"assistant\"]>; timestamp: Zod.ZodString; isError: Zod.ZodOptional; traceData: Zod.ZodOptional; traceId: Zod.ZodOptional; }, \"strip\", Zod.ZodTypeAny, { transactionId?: string | undefined; traceId?: string | undefined; }, { transactionId?: string | undefined; traceId?: string | undefined; }>>; }, \"strip\", Zod.ZodTypeAny, { timestamp: string; role: \"user\" | \"system\" | \"assistant\"; content: string; reader?: Zod.objectOutputType<{}, Zod.ZodUnknown, \"strip\"> | undefined; isError?: boolean | undefined; traceData?: { transactionId?: string | undefined; traceId?: string | undefined; } | undefined; }, { timestamp: string; role: \"user\" | \"system\" | \"assistant\"; content: string; reader?: Zod.objectInputType<{}, Zod.ZodUnknown, \"strip\"> | undefined; isError?: boolean | undefined; traceData?: { transactionId?: string | undefined; traceId?: string | undefined; } | undefined; }>, \"many\">>; apiConfig: Zod.ZodOptional; provider: Zod.ZodOptional>; model: Zod.ZodOptional; }, \"strip\", Zod.ZodTypeAny, { connectorId: string; defaultSystemPromptId?: string | undefined; provider?: \"OpenAI\" | \"Azure OpenAI\" | undefined; model?: string | undefined; }, { connectorId: string; defaultSystemPromptId?: string | undefined; provider?: \"OpenAI\" | \"Azure OpenAI\" | undefined; model?: string | undefined; }>>; isDefault: Zod.ZodOptional; excludeFromLastConversationStorage: Zod.ZodOptional; namespace: Zod.ZodString; }, \"strip\", Zod.ZodTypeAny, { id: string; namespace: string; title: string; createdAt: string; category: \"assistant\" | \"insights\"; users: { id?: string | undefined; name?: string | undefined; }[]; summary?: { content?: string | undefined; timestamp?: string | undefined; public?: boolean | undefined; confidence?: \"medium\" | \"low\" | \"high\" | undefined; } | undefined; timestamp?: string | undefined; updatedAt?: string | undefined; replacements?: Zod.objectOutputType<{}, Zod.ZodString, \"strip\"> | undefined; messages?: { timestamp: string; role: \"user\" | \"system\" | \"assistant\"; content: string; reader?: Zod.objectOutputType<{}, Zod.ZodUnknown, \"strip\"> | undefined; isError?: boolean | undefined; traceData?: { transactionId?: string | undefined; traceId?: string | undefined; } | undefined; }[] | undefined; apiConfig?: { connectorId: string; defaultSystemPromptId?: string | undefined; provider?: \"OpenAI\" | \"Azure OpenAI\" | undefined; model?: string | undefined; } | undefined; isDefault?: boolean | undefined; excludeFromLastConversationStorage?: boolean | undefined; }, { id: string; namespace: string; title: string; createdAt: string; category: \"assistant\" | \"insights\"; users: { id?: string | undefined; name?: string | undefined; }[]; summary?: { content?: string | undefined; timestamp?: string | undefined; public?: boolean | undefined; confidence?: \"medium\" | \"low\" | \"high\" | undefined; } | undefined; timestamp?: string | undefined; updatedAt?: string | undefined; replacements?: Zod.objectInputType<{}, Zod.ZodString, \"strip\"> | undefined; messages?: { timestamp: string; role: \"user\" | \"system\" | \"assistant\"; content: string; reader?: Zod.objectInputType<{}, Zod.ZodUnknown, \"strip\"> | undefined; isError?: boolean | undefined; traceData?: { transactionId?: string | undefined; traceId?: string | undefined; } | undefined; }[] | undefined; apiConfig?: { connectorId: string; defaultSystemPromptId?: string | undefined; provider?: \"OpenAI\" | \"Azure OpenAI\" | undefined; model?: string | undefined; } | undefined; isDefault?: boolean | undefined; excludeFromLastConversationStorage?: boolean | undefined; }>, \"many\">; }, \"strip\", Zod.ZodTypeAny, { page: number; perPage: number; total: number; data: { id: string; namespace: string; title: string; createdAt: string; category: \"assistant\" | \"insights\"; users: { id?: string | undefined; name?: string | undefined; }[]; summary?: { content?: string | undefined; timestamp?: string | undefined; public?: boolean | undefined; confidence?: \"medium\" | \"low\" | \"high\" | undefined; } | undefined; timestamp?: string | undefined; updatedAt?: string | undefined; replacements?: Zod.objectOutputType<{}, Zod.ZodString, \"strip\"> | undefined; messages?: { timestamp: string; role: \"user\" | \"system\" | \"assistant\"; content: string; reader?: Zod.objectOutputType<{}, Zod.ZodUnknown, \"strip\"> | undefined; isError?: boolean | undefined; traceData?: { transactionId?: string | undefined; traceId?: string | undefined; } | undefined; }[] | undefined; apiConfig?: { connectorId: string; defaultSystemPromptId?: string | undefined; provider?: \"OpenAI\" | \"Azure OpenAI\" | undefined; model?: string | undefined; } | undefined; isDefault?: boolean | undefined; excludeFromLastConversationStorage?: boolean | undefined; }[]; }, { page: number; perPage: number; total: number; data: { id: string; namespace: string; title: string; createdAt: string; category: \"assistant\" | \"insights\"; users: { id?: string | undefined; name?: string | undefined; }[]; summary?: { content?: string | undefined; timestamp?: string | undefined; public?: boolean | undefined; confidence?: \"medium\" | \"low\" | \"high\" | undefined; } | undefined; timestamp?: string | undefined; updatedAt?: string | undefined; replacements?: Zod.objectInputType<{}, Zod.ZodString, \"strip\"> | undefined; messages?: { timestamp: string; role: \"user\" | \"system\" | \"assistant\"; content: string; reader?: Zod.objectInputType<{}, Zod.ZodUnknown, \"strip\"> | undefined; isError?: boolean | undefined; traceData?: { transactionId?: string | undefined; traceId?: string | undefined; } | undefined; }[] | undefined; apiConfig?: { connectorId: string; defaultSystemPromptId?: string | undefined; provider?: \"OpenAI\" | \"Azure OpenAI\" | undefined; model?: string | undefined; } | undefined; isDefault?: boolean | undefined; excludeFromLastConversationStorage?: boolean | undefined; }[]; }>" ], "path": "x-pack/packages/kbn-elastic-assistant-common/impl/schemas/conversations/find_conversations_route.gen.ts", "deprecated": false, @@ -2930,7 +2930,7 @@ "label": "FindCurrentUserConversationsResponse", "description": [], "signature": [ - "Zod.ZodObject<{ page: Zod.ZodNumber; perPage: Zod.ZodNumber; total: Zod.ZodNumber; data: Zod.ZodArray; title: Zod.ZodString; category: Zod.ZodEnum<[\"assistant\", \"insights\"]>; summary: Zod.ZodOptional; timestamp: Zod.ZodOptional; public: Zod.ZodOptional; confidence: Zod.ZodOptional>; }, \"strip\", Zod.ZodTypeAny, { content?: string | undefined; timestamp?: string | undefined; public?: boolean | undefined; confidence?: \"medium\" | \"low\" | \"high\" | undefined; }, { content?: string | undefined; timestamp?: string | undefined; public?: boolean | undefined; confidence?: \"medium\" | \"low\" | \"high\" | undefined; }>>; timestamp: Zod.ZodOptional; updatedAt: Zod.ZodOptional; createdAt: Zod.ZodString; replacements: Zod.ZodOptional, \"many\">>; users: Zod.ZodArray; name: Zod.ZodOptional; }, \"strip\", Zod.ZodTypeAny, { id?: string | undefined; name?: string | undefined; }, { id?: string | undefined; name?: string | undefined; }>, \"many\">; messages: Zod.ZodOptional, Zod.objectInputType<{}, Zod.ZodUnknown, \"strip\">>>; role: Zod.ZodEnum<[\"system\", \"user\", \"assistant\"]>; timestamp: Zod.ZodString; isError: Zod.ZodOptional; traceData: Zod.ZodOptional; traceId: Zod.ZodOptional; }, \"strip\", Zod.ZodTypeAny, { transactionId?: string | undefined; traceId?: string | undefined; }, { transactionId?: string | undefined; traceId?: string | undefined; }>>; }, \"strip\", Zod.ZodTypeAny, { timestamp: string; role: \"user\" | \"system\" | \"assistant\"; content: string; reader?: Zod.objectOutputType<{}, Zod.ZodUnknown, \"strip\"> | undefined; isError?: boolean | undefined; traceData?: { transactionId?: string | undefined; traceId?: string | undefined; } | undefined; }, { timestamp: string; role: \"user\" | \"system\" | \"assistant\"; content: string; reader?: Zod.objectInputType<{}, Zod.ZodUnknown, \"strip\"> | undefined; isError?: boolean | undefined; traceData?: { transactionId?: string | undefined; traceId?: string | undefined; } | undefined; }>, \"many\">>; apiConfig: Zod.ZodOptional; provider: Zod.ZodOptional>; model: Zod.ZodOptional; }, \"strip\", Zod.ZodTypeAny, { connectorId: string; defaultSystemPromptId?: string | undefined; provider?: \"OpenAI\" | \"Azure OpenAI\" | undefined; model?: string | undefined; }, { connectorId: string; defaultSystemPromptId?: string | undefined; provider?: \"OpenAI\" | \"Azure OpenAI\" | undefined; model?: string | undefined; }>>; isDefault: Zod.ZodOptional; excludeFromLastConversationStorage: Zod.ZodOptional; namespace: Zod.ZodString; }, \"strip\", Zod.ZodTypeAny, { id: string; namespace: string; title: string; createdAt: string; category: \"assistant\" | \"insights\"; users: { id?: string | undefined; name?: string | undefined; }[]; summary?: { content?: string | undefined; timestamp?: string | undefined; public?: boolean | undefined; confidence?: \"medium\" | \"low\" | \"high\" | undefined; } | undefined; timestamp?: string | undefined; updatedAt?: string | undefined; replacements?: { value: string; uuid: string; }[] | undefined; messages?: { timestamp: string; role: \"user\" | \"system\" | \"assistant\"; content: string; reader?: Zod.objectOutputType<{}, Zod.ZodUnknown, \"strip\"> | undefined; isError?: boolean | undefined; traceData?: { transactionId?: string | undefined; traceId?: string | undefined; } | undefined; }[] | undefined; apiConfig?: { connectorId: string; defaultSystemPromptId?: string | undefined; provider?: \"OpenAI\" | \"Azure OpenAI\" | undefined; model?: string | undefined; } | undefined; isDefault?: boolean | undefined; excludeFromLastConversationStorage?: boolean | undefined; }, { id: string; namespace: string; title: string; createdAt: string; category: \"assistant\" | \"insights\"; users: { id?: string | undefined; name?: string | undefined; }[]; summary?: { content?: string | undefined; timestamp?: string | undefined; public?: boolean | undefined; confidence?: \"medium\" | \"low\" | \"high\" | undefined; } | undefined; timestamp?: string | undefined; updatedAt?: string | undefined; replacements?: { value: string; uuid: string; }[] | undefined; messages?: { timestamp: string; role: \"user\" | \"system\" | \"assistant\"; content: string; reader?: Zod.objectInputType<{}, Zod.ZodUnknown, \"strip\"> | undefined; isError?: boolean | undefined; traceData?: { transactionId?: string | undefined; traceId?: string | undefined; } | undefined; }[] | undefined; apiConfig?: { connectorId: string; defaultSystemPromptId?: string | undefined; provider?: \"OpenAI\" | \"Azure OpenAI\" | undefined; model?: string | undefined; } | undefined; isDefault?: boolean | undefined; excludeFromLastConversationStorage?: boolean | undefined; }>, \"many\">; }, \"strip\", Zod.ZodTypeAny, { page: number; perPage: number; total: number; data: { id: string; namespace: string; title: string; createdAt: string; category: \"assistant\" | \"insights\"; users: { id?: string | undefined; name?: string | undefined; }[]; summary?: { content?: string | undefined; timestamp?: string | undefined; public?: boolean | undefined; confidence?: \"medium\" | \"low\" | \"high\" | undefined; } | undefined; timestamp?: string | undefined; updatedAt?: string | undefined; replacements?: { value: string; uuid: string; }[] | undefined; messages?: { timestamp: string; role: \"user\" | \"system\" | \"assistant\"; content: string; reader?: Zod.objectOutputType<{}, Zod.ZodUnknown, \"strip\"> | undefined; isError?: boolean | undefined; traceData?: { transactionId?: string | undefined; traceId?: string | undefined; } | undefined; }[] | undefined; apiConfig?: { connectorId: string; defaultSystemPromptId?: string | undefined; provider?: \"OpenAI\" | \"Azure OpenAI\" | undefined; model?: string | undefined; } | undefined; isDefault?: boolean | undefined; excludeFromLastConversationStorage?: boolean | undefined; }[]; }, { page: number; perPage: number; total: number; data: { id: string; namespace: string; title: string; createdAt: string; category: \"assistant\" | \"insights\"; users: { id?: string | undefined; name?: string | undefined; }[]; summary?: { content?: string | undefined; timestamp?: string | undefined; public?: boolean | undefined; confidence?: \"medium\" | \"low\" | \"high\" | undefined; } | undefined; timestamp?: string | undefined; updatedAt?: string | undefined; replacements?: { value: string; uuid: string; }[] | undefined; messages?: { timestamp: string; role: \"user\" | \"system\" | \"assistant\"; content: string; reader?: Zod.objectInputType<{}, Zod.ZodUnknown, \"strip\"> | undefined; isError?: boolean | undefined; traceData?: { transactionId?: string | undefined; traceId?: string | undefined; } | undefined; }[] | undefined; apiConfig?: { connectorId: string; defaultSystemPromptId?: string | undefined; provider?: \"OpenAI\" | \"Azure OpenAI\" | undefined; model?: string | undefined; } | undefined; isDefault?: boolean | undefined; excludeFromLastConversationStorage?: boolean | undefined; }[]; }>" + "Zod.ZodObject<{ page: Zod.ZodNumber; perPage: Zod.ZodNumber; total: Zod.ZodNumber; data: Zod.ZodArray; title: Zod.ZodString; category: Zod.ZodEnum<[\"assistant\", \"insights\"]>; summary: Zod.ZodOptional; timestamp: Zod.ZodOptional; public: Zod.ZodOptional; confidence: Zod.ZodOptional>; }, \"strip\", Zod.ZodTypeAny, { content?: string | undefined; timestamp?: string | undefined; public?: boolean | undefined; confidence?: \"medium\" | \"low\" | \"high\" | undefined; }, { content?: string | undefined; timestamp?: string | undefined; public?: boolean | undefined; confidence?: \"medium\" | \"low\" | \"high\" | undefined; }>>; timestamp: Zod.ZodOptional; updatedAt: Zod.ZodOptional; createdAt: Zod.ZodString; replacements: Zod.ZodOptional, Zod.objectInputType<{}, Zod.ZodString, \"strip\">>>; users: Zod.ZodArray; name: Zod.ZodOptional; }, \"strip\", Zod.ZodTypeAny, { id?: string | undefined; name?: string | undefined; }, { id?: string | undefined; name?: string | undefined; }>, \"many\">; messages: Zod.ZodOptional, Zod.objectInputType<{}, Zod.ZodUnknown, \"strip\">>>; role: Zod.ZodEnum<[\"system\", \"user\", \"assistant\"]>; timestamp: Zod.ZodString; isError: Zod.ZodOptional; traceData: Zod.ZodOptional; traceId: Zod.ZodOptional; }, \"strip\", Zod.ZodTypeAny, { transactionId?: string | undefined; traceId?: string | undefined; }, { transactionId?: string | undefined; traceId?: string | undefined; }>>; }, \"strip\", Zod.ZodTypeAny, { timestamp: string; role: \"user\" | \"system\" | \"assistant\"; content: string; reader?: Zod.objectOutputType<{}, Zod.ZodUnknown, \"strip\"> | undefined; isError?: boolean | undefined; traceData?: { transactionId?: string | undefined; traceId?: string | undefined; } | undefined; }, { timestamp: string; role: \"user\" | \"system\" | \"assistant\"; content: string; reader?: Zod.objectInputType<{}, Zod.ZodUnknown, \"strip\"> | undefined; isError?: boolean | undefined; traceData?: { transactionId?: string | undefined; traceId?: string | undefined; } | undefined; }>, \"many\">>; apiConfig: Zod.ZodOptional; provider: Zod.ZodOptional>; model: Zod.ZodOptional; }, \"strip\", Zod.ZodTypeAny, { connectorId: string; defaultSystemPromptId?: string | undefined; provider?: \"OpenAI\" | \"Azure OpenAI\" | undefined; model?: string | undefined; }, { connectorId: string; defaultSystemPromptId?: string | undefined; provider?: \"OpenAI\" | \"Azure OpenAI\" | undefined; model?: string | undefined; }>>; isDefault: Zod.ZodOptional; excludeFromLastConversationStorage: Zod.ZodOptional; namespace: Zod.ZodString; }, \"strip\", Zod.ZodTypeAny, { id: string; namespace: string; title: string; createdAt: string; category: \"assistant\" | \"insights\"; users: { id?: string | undefined; name?: string | undefined; }[]; summary?: { content?: string | undefined; timestamp?: string | undefined; public?: boolean | undefined; confidence?: \"medium\" | \"low\" | \"high\" | undefined; } | undefined; timestamp?: string | undefined; updatedAt?: string | undefined; replacements?: Zod.objectOutputType<{}, Zod.ZodString, \"strip\"> | undefined; messages?: { timestamp: string; role: \"user\" | \"system\" | \"assistant\"; content: string; reader?: Zod.objectOutputType<{}, Zod.ZodUnknown, \"strip\"> | undefined; isError?: boolean | undefined; traceData?: { transactionId?: string | undefined; traceId?: string | undefined; } | undefined; }[] | undefined; apiConfig?: { connectorId: string; defaultSystemPromptId?: string | undefined; provider?: \"OpenAI\" | \"Azure OpenAI\" | undefined; model?: string | undefined; } | undefined; isDefault?: boolean | undefined; excludeFromLastConversationStorage?: boolean | undefined; }, { id: string; namespace: string; title: string; createdAt: string; category: \"assistant\" | \"insights\"; users: { id?: string | undefined; name?: string | undefined; }[]; summary?: { content?: string | undefined; timestamp?: string | undefined; public?: boolean | undefined; confidence?: \"medium\" | \"low\" | \"high\" | undefined; } | undefined; timestamp?: string | undefined; updatedAt?: string | undefined; replacements?: Zod.objectInputType<{}, Zod.ZodString, \"strip\"> | undefined; messages?: { timestamp: string; role: \"user\" | \"system\" | \"assistant\"; content: string; reader?: Zod.objectInputType<{}, Zod.ZodUnknown, \"strip\"> | undefined; isError?: boolean | undefined; traceData?: { transactionId?: string | undefined; traceId?: string | undefined; } | undefined; }[] | undefined; apiConfig?: { connectorId: string; defaultSystemPromptId?: string | undefined; provider?: \"OpenAI\" | \"Azure OpenAI\" | undefined; model?: string | undefined; } | undefined; isDefault?: boolean | undefined; excludeFromLastConversationStorage?: boolean | undefined; }>, \"many\">; }, \"strip\", Zod.ZodTypeAny, { page: number; perPage: number; total: number; data: { id: string; namespace: string; title: string; createdAt: string; category: \"assistant\" | \"insights\"; users: { id?: string | undefined; name?: string | undefined; }[]; summary?: { content?: string | undefined; timestamp?: string | undefined; public?: boolean | undefined; confidence?: \"medium\" | \"low\" | \"high\" | undefined; } | undefined; timestamp?: string | undefined; updatedAt?: string | undefined; replacements?: Zod.objectOutputType<{}, Zod.ZodString, \"strip\"> | undefined; messages?: { timestamp: string; role: \"user\" | \"system\" | \"assistant\"; content: string; reader?: Zod.objectOutputType<{}, Zod.ZodUnknown, \"strip\"> | undefined; isError?: boolean | undefined; traceData?: { transactionId?: string | undefined; traceId?: string | undefined; } | undefined; }[] | undefined; apiConfig?: { connectorId: string; defaultSystemPromptId?: string | undefined; provider?: \"OpenAI\" | \"Azure OpenAI\" | undefined; model?: string | undefined; } | undefined; isDefault?: boolean | undefined; excludeFromLastConversationStorage?: boolean | undefined; }[]; }, { page: number; perPage: number; total: number; data: { id: string; namespace: string; title: string; createdAt: string; category: \"assistant\" | \"insights\"; users: { id?: string | undefined; name?: string | undefined; }[]; summary?: { content?: string | undefined; timestamp?: string | undefined; public?: boolean | undefined; confidence?: \"medium\" | \"low\" | \"high\" | undefined; } | undefined; timestamp?: string | undefined; updatedAt?: string | undefined; replacements?: Zod.objectInputType<{}, Zod.ZodString, \"strip\"> | undefined; messages?: { timestamp: string; role: \"user\" | \"system\" | \"assistant\"; content: string; reader?: Zod.objectInputType<{}, Zod.ZodUnknown, \"strip\"> | undefined; isError?: boolean | undefined; traceData?: { transactionId?: string | undefined; traceId?: string | undefined; } | undefined; }[] | undefined; apiConfig?: { connectorId: string; defaultSystemPromptId?: string | undefined; provider?: \"OpenAI\" | \"Azure OpenAI\" | undefined; model?: string | undefined; } | undefined; isDefault?: boolean | undefined; excludeFromLastConversationStorage?: boolean | undefined; }[]; }>" ], "path": "x-pack/packages/kbn-elastic-assistant-common/impl/schemas/conversations/find_conversations_route.gen.ts", "deprecated": false, @@ -3080,7 +3080,7 @@ "label": "PerformBulkActionRequestBody", "description": [], "signature": [ - "Zod.ZodObject<{ delete: Zod.ZodOptional; ids: Zod.ZodOptional>; }, \"strip\", Zod.ZodTypeAny, { query?: string | undefined; ids?: string[] | undefined; }, { query?: string | undefined; ids?: string[] | undefined; }>>; create: Zod.ZodOptional>; messages: Zod.ZodOptional, Zod.objectInputType<{}, Zod.ZodUnknown, \"strip\">>>; role: Zod.ZodEnum<[\"system\", \"user\", \"assistant\"]>; timestamp: Zod.ZodString; isError: Zod.ZodOptional; traceData: Zod.ZodOptional; traceId: Zod.ZodOptional; }, \"strip\", Zod.ZodTypeAny, { transactionId?: string | undefined; traceId?: string | undefined; }, { transactionId?: string | undefined; traceId?: string | undefined; }>>; }, \"strip\", Zod.ZodTypeAny, { timestamp: string; role: \"user\" | \"system\" | \"assistant\"; content: string; reader?: Zod.objectOutputType<{}, Zod.ZodUnknown, \"strip\"> | undefined; isError?: boolean | undefined; traceData?: { transactionId?: string | undefined; traceId?: string | undefined; } | undefined; }, { timestamp: string; role: \"user\" | \"system\" | \"assistant\"; content: string; reader?: Zod.objectInputType<{}, Zod.ZodUnknown, \"strip\"> | undefined; isError?: boolean | undefined; traceData?: { transactionId?: string | undefined; traceId?: string | undefined; } | undefined; }>, \"many\">>; apiConfig: Zod.ZodOptional; provider: Zod.ZodOptional>; model: Zod.ZodOptional; }, \"strip\", Zod.ZodTypeAny, { connectorId: string; defaultSystemPromptId?: string | undefined; provider?: \"OpenAI\" | \"Azure OpenAI\" | undefined; model?: string | undefined; }, { connectorId: string; defaultSystemPromptId?: string | undefined; provider?: \"OpenAI\" | \"Azure OpenAI\" | undefined; model?: string | undefined; }>>; isDefault: Zod.ZodOptional; excludeFromLastConversationStorage: Zod.ZodOptional; replacements: Zod.ZodOptional, \"many\">>; }, \"strip\", Zod.ZodTypeAny, { title: string; category?: \"assistant\" | \"insights\" | undefined; messages?: { timestamp: string; role: \"user\" | \"system\" | \"assistant\"; content: string; reader?: Zod.objectOutputType<{}, Zod.ZodUnknown, \"strip\"> | undefined; isError?: boolean | undefined; traceData?: { transactionId?: string | undefined; traceId?: string | undefined; } | undefined; }[] | undefined; apiConfig?: { connectorId: string; defaultSystemPromptId?: string | undefined; provider?: \"OpenAI\" | \"Azure OpenAI\" | undefined; model?: string | undefined; } | undefined; isDefault?: boolean | undefined; excludeFromLastConversationStorage?: boolean | undefined; replacements?: { value: string; uuid: string; }[] | undefined; }, { title: string; category?: \"assistant\" | \"insights\" | undefined; messages?: { timestamp: string; role: \"user\" | \"system\" | \"assistant\"; content: string; reader?: Zod.objectInputType<{}, Zod.ZodUnknown, \"strip\"> | undefined; isError?: boolean | undefined; traceData?: { transactionId?: string | undefined; traceId?: string | undefined; } | undefined; }[] | undefined; apiConfig?: { connectorId: string; defaultSystemPromptId?: string | undefined; provider?: \"OpenAI\" | \"Azure OpenAI\" | undefined; model?: string | undefined; } | undefined; isDefault?: boolean | undefined; excludeFromLastConversationStorage?: boolean | undefined; replacements?: { value: string; uuid: string; }[] | undefined; }>, \"many\">>; update: Zod.ZodOptional; title: Zod.ZodOptional; category: Zod.ZodOptional>; messages: Zod.ZodOptional, Zod.objectInputType<{}, Zod.ZodUnknown, \"strip\">>>; role: Zod.ZodEnum<[\"system\", \"user\", \"assistant\"]>; timestamp: Zod.ZodString; isError: Zod.ZodOptional; traceData: Zod.ZodOptional; traceId: Zod.ZodOptional; }, \"strip\", Zod.ZodTypeAny, { transactionId?: string | undefined; traceId?: string | undefined; }, { transactionId?: string | undefined; traceId?: string | undefined; }>>; }, \"strip\", Zod.ZodTypeAny, { timestamp: string; role: \"user\" | \"system\" | \"assistant\"; content: string; reader?: Zod.objectOutputType<{}, Zod.ZodUnknown, \"strip\"> | undefined; isError?: boolean | undefined; traceData?: { transactionId?: string | undefined; traceId?: string | undefined; } | undefined; }, { timestamp: string; role: \"user\" | \"system\" | \"assistant\"; content: string; reader?: Zod.objectInputType<{}, Zod.ZodUnknown, \"strip\"> | undefined; isError?: boolean | undefined; traceData?: { transactionId?: string | undefined; traceId?: string | undefined; } | undefined; }>, \"many\">>; apiConfig: Zod.ZodOptional; provider: Zod.ZodOptional>; model: Zod.ZodOptional; }, \"strip\", Zod.ZodTypeAny, { connectorId: string; defaultSystemPromptId?: string | undefined; provider?: \"OpenAI\" | \"Azure OpenAI\" | undefined; model?: string | undefined; }, { connectorId: string; defaultSystemPromptId?: string | undefined; provider?: \"OpenAI\" | \"Azure OpenAI\" | undefined; model?: string | undefined; }>>; summary: Zod.ZodOptional; timestamp: Zod.ZodOptional; public: Zod.ZodOptional; confidence: Zod.ZodOptional>; }, \"strip\", Zod.ZodTypeAny, { content?: string | undefined; timestamp?: string | undefined; public?: boolean | undefined; confidence?: \"medium\" | \"low\" | \"high\" | undefined; }, { content?: string | undefined; timestamp?: string | undefined; public?: boolean | undefined; confidence?: \"medium\" | \"low\" | \"high\" | undefined; }>>; excludeFromLastConversationStorage: Zod.ZodOptional; replacements: Zod.ZodOptional, \"many\">>; }, \"strip\", Zod.ZodTypeAny, { id: string; title?: string | undefined; category?: \"assistant\" | \"insights\" | undefined; messages?: { timestamp: string; role: \"user\" | \"system\" | \"assistant\"; content: string; reader?: Zod.objectOutputType<{}, Zod.ZodUnknown, \"strip\"> | undefined; isError?: boolean | undefined; traceData?: { transactionId?: string | undefined; traceId?: string | undefined; } | undefined; }[] | undefined; apiConfig?: { connectorId: string; defaultSystemPromptId?: string | undefined; provider?: \"OpenAI\" | \"Azure OpenAI\" | undefined; model?: string | undefined; } | undefined; summary?: { content?: string | undefined; timestamp?: string | undefined; public?: boolean | undefined; confidence?: \"medium\" | \"low\" | \"high\" | undefined; } | undefined; excludeFromLastConversationStorage?: boolean | undefined; replacements?: { value: string; uuid: string; }[] | undefined; }, { id: string; title?: string | undefined; category?: \"assistant\" | \"insights\" | undefined; messages?: { timestamp: string; role: \"user\" | \"system\" | \"assistant\"; content: string; reader?: Zod.objectInputType<{}, Zod.ZodUnknown, \"strip\"> | undefined; isError?: boolean | undefined; traceData?: { transactionId?: string | undefined; traceId?: string | undefined; } | undefined; }[] | undefined; apiConfig?: { connectorId: string; defaultSystemPromptId?: string | undefined; provider?: \"OpenAI\" | \"Azure OpenAI\" | undefined; model?: string | undefined; } | undefined; summary?: { content?: string | undefined; timestamp?: string | undefined; public?: boolean | undefined; confidence?: \"medium\" | \"low\" | \"high\" | undefined; } | undefined; excludeFromLastConversationStorage?: boolean | undefined; replacements?: { value: string; uuid: string; }[] | undefined; }>, \"many\">>; }, \"strip\", Zod.ZodTypeAny, { delete?: { query?: string | undefined; ids?: string[] | undefined; } | undefined; create?: { title: string; category?: \"assistant\" | \"insights\" | undefined; messages?: { timestamp: string; role: \"user\" | \"system\" | \"assistant\"; content: string; reader?: Zod.objectOutputType<{}, Zod.ZodUnknown, \"strip\"> | undefined; isError?: boolean | undefined; traceData?: { transactionId?: string | undefined; traceId?: string | undefined; } | undefined; }[] | undefined; apiConfig?: { connectorId: string; defaultSystemPromptId?: string | undefined; provider?: \"OpenAI\" | \"Azure OpenAI\" | undefined; model?: string | undefined; } | undefined; isDefault?: boolean | undefined; excludeFromLastConversationStorage?: boolean | undefined; replacements?: { value: string; uuid: string; }[] | undefined; }[] | undefined; update?: { id: string; title?: string | undefined; category?: \"assistant\" | \"insights\" | undefined; messages?: { timestamp: string; role: \"user\" | \"system\" | \"assistant\"; content: string; reader?: Zod.objectOutputType<{}, Zod.ZodUnknown, \"strip\"> | undefined; isError?: boolean | undefined; traceData?: { transactionId?: string | undefined; traceId?: string | undefined; } | undefined; }[] | undefined; apiConfig?: { connectorId: string; defaultSystemPromptId?: string | undefined; provider?: \"OpenAI\" | \"Azure OpenAI\" | undefined; model?: string | undefined; } | undefined; summary?: { content?: string | undefined; timestamp?: string | undefined; public?: boolean | undefined; confidence?: \"medium\" | \"low\" | \"high\" | undefined; } | undefined; excludeFromLastConversationStorage?: boolean | undefined; replacements?: { value: string; uuid: string; }[] | undefined; }[] | undefined; }, { delete?: { query?: string | undefined; ids?: string[] | undefined; } | undefined; create?: { title: string; category?: \"assistant\" | \"insights\" | undefined; messages?: { timestamp: string; role: \"user\" | \"system\" | \"assistant\"; content: string; reader?: Zod.objectInputType<{}, Zod.ZodUnknown, \"strip\"> | undefined; isError?: boolean | undefined; traceData?: { transactionId?: string | undefined; traceId?: string | undefined; } | undefined; }[] | undefined; apiConfig?: { connectorId: string; defaultSystemPromptId?: string | undefined; provider?: \"OpenAI\" | \"Azure OpenAI\" | undefined; model?: string | undefined; } | undefined; isDefault?: boolean | undefined; excludeFromLastConversationStorage?: boolean | undefined; replacements?: { value: string; uuid: string; }[] | undefined; }[] | undefined; update?: { id: string; title?: string | undefined; category?: \"assistant\" | \"insights\" | undefined; messages?: { timestamp: string; role: \"user\" | \"system\" | \"assistant\"; content: string; reader?: Zod.objectInputType<{}, Zod.ZodUnknown, \"strip\"> | undefined; isError?: boolean | undefined; traceData?: { transactionId?: string | undefined; traceId?: string | undefined; } | undefined; }[] | undefined; apiConfig?: { connectorId: string; defaultSystemPromptId?: string | undefined; provider?: \"OpenAI\" | \"Azure OpenAI\" | undefined; model?: string | undefined; } | undefined; summary?: { content?: string | undefined; timestamp?: string | undefined; public?: boolean | undefined; confidence?: \"medium\" | \"low\" | \"high\" | undefined; } | undefined; excludeFromLastConversationStorage?: boolean | undefined; replacements?: { value: string; uuid: string; }[] | undefined; }[] | undefined; }>" + "Zod.ZodObject<{ delete: Zod.ZodOptional; ids: Zod.ZodOptional>; }, \"strip\", Zod.ZodTypeAny, { query?: string | undefined; ids?: string[] | undefined; }, { query?: string | undefined; ids?: string[] | undefined; }>>; create: Zod.ZodOptional>; messages: Zod.ZodOptional, Zod.objectInputType<{}, Zod.ZodUnknown, \"strip\">>>; role: Zod.ZodEnum<[\"system\", \"user\", \"assistant\"]>; timestamp: Zod.ZodString; isError: Zod.ZodOptional; traceData: Zod.ZodOptional; traceId: Zod.ZodOptional; }, \"strip\", Zod.ZodTypeAny, { transactionId?: string | undefined; traceId?: string | undefined; }, { transactionId?: string | undefined; traceId?: string | undefined; }>>; }, \"strip\", Zod.ZodTypeAny, { timestamp: string; role: \"user\" | \"system\" | \"assistant\"; content: string; reader?: Zod.objectOutputType<{}, Zod.ZodUnknown, \"strip\"> | undefined; isError?: boolean | undefined; traceData?: { transactionId?: string | undefined; traceId?: string | undefined; } | undefined; }, { timestamp: string; role: \"user\" | \"system\" | \"assistant\"; content: string; reader?: Zod.objectInputType<{}, Zod.ZodUnknown, \"strip\"> | undefined; isError?: boolean | undefined; traceData?: { transactionId?: string | undefined; traceId?: string | undefined; } | undefined; }>, \"many\">>; apiConfig: Zod.ZodOptional; provider: Zod.ZodOptional>; model: Zod.ZodOptional; }, \"strip\", Zod.ZodTypeAny, { connectorId: string; defaultSystemPromptId?: string | undefined; provider?: \"OpenAI\" | \"Azure OpenAI\" | undefined; model?: string | undefined; }, { connectorId: string; defaultSystemPromptId?: string | undefined; provider?: \"OpenAI\" | \"Azure OpenAI\" | undefined; model?: string | undefined; }>>; isDefault: Zod.ZodOptional; excludeFromLastConversationStorage: Zod.ZodOptional; replacements: Zod.ZodOptional, Zod.objectInputType<{}, Zod.ZodString, \"strip\">>>; }, \"strip\", Zod.ZodTypeAny, { title: string; category?: \"assistant\" | \"insights\" | undefined; messages?: { timestamp: string; role: \"user\" | \"system\" | \"assistant\"; content: string; reader?: Zod.objectOutputType<{}, Zod.ZodUnknown, \"strip\"> | undefined; isError?: boolean | undefined; traceData?: { transactionId?: string | undefined; traceId?: string | undefined; } | undefined; }[] | undefined; apiConfig?: { connectorId: string; defaultSystemPromptId?: string | undefined; provider?: \"OpenAI\" | \"Azure OpenAI\" | undefined; model?: string | undefined; } | undefined; isDefault?: boolean | undefined; excludeFromLastConversationStorage?: boolean | undefined; replacements?: Zod.objectOutputType<{}, Zod.ZodString, \"strip\"> | undefined; }, { title: string; category?: \"assistant\" | \"insights\" | undefined; messages?: { timestamp: string; role: \"user\" | \"system\" | \"assistant\"; content: string; reader?: Zod.objectInputType<{}, Zod.ZodUnknown, \"strip\"> | undefined; isError?: boolean | undefined; traceData?: { transactionId?: string | undefined; traceId?: string | undefined; } | undefined; }[] | undefined; apiConfig?: { connectorId: string; defaultSystemPromptId?: string | undefined; provider?: \"OpenAI\" | \"Azure OpenAI\" | undefined; model?: string | undefined; } | undefined; isDefault?: boolean | undefined; excludeFromLastConversationStorage?: boolean | undefined; replacements?: Zod.objectInputType<{}, Zod.ZodString, \"strip\"> | undefined; }>, \"many\">>; update: Zod.ZodOptional; title: Zod.ZodOptional; category: Zod.ZodOptional>; messages: Zod.ZodOptional, Zod.objectInputType<{}, Zod.ZodUnknown, \"strip\">>>; role: Zod.ZodEnum<[\"system\", \"user\", \"assistant\"]>; timestamp: Zod.ZodString; isError: Zod.ZodOptional; traceData: Zod.ZodOptional; traceId: Zod.ZodOptional; }, \"strip\", Zod.ZodTypeAny, { transactionId?: string | undefined; traceId?: string | undefined; }, { transactionId?: string | undefined; traceId?: string | undefined; }>>; }, \"strip\", Zod.ZodTypeAny, { timestamp: string; role: \"user\" | \"system\" | \"assistant\"; content: string; reader?: Zod.objectOutputType<{}, Zod.ZodUnknown, \"strip\"> | undefined; isError?: boolean | undefined; traceData?: { transactionId?: string | undefined; traceId?: string | undefined; } | undefined; }, { timestamp: string; role: \"user\" | \"system\" | \"assistant\"; content: string; reader?: Zod.objectInputType<{}, Zod.ZodUnknown, \"strip\"> | undefined; isError?: boolean | undefined; traceData?: { transactionId?: string | undefined; traceId?: string | undefined; } | undefined; }>, \"many\">>; apiConfig: Zod.ZodOptional; provider: Zod.ZodOptional>; model: Zod.ZodOptional; }, \"strip\", Zod.ZodTypeAny, { connectorId: string; defaultSystemPromptId?: string | undefined; provider?: \"OpenAI\" | \"Azure OpenAI\" | undefined; model?: string | undefined; }, { connectorId: string; defaultSystemPromptId?: string | undefined; provider?: \"OpenAI\" | \"Azure OpenAI\" | undefined; model?: string | undefined; }>>; summary: Zod.ZodOptional; timestamp: Zod.ZodOptional; public: Zod.ZodOptional; confidence: Zod.ZodOptional>; }, \"strip\", Zod.ZodTypeAny, { content?: string | undefined; timestamp?: string | undefined; public?: boolean | undefined; confidence?: \"medium\" | \"low\" | \"high\" | undefined; }, { content?: string | undefined; timestamp?: string | undefined; public?: boolean | undefined; confidence?: \"medium\" | \"low\" | \"high\" | undefined; }>>; excludeFromLastConversationStorage: Zod.ZodOptional; replacements: Zod.ZodOptional, Zod.objectInputType<{}, Zod.ZodString, \"strip\">>>; }, \"strip\", Zod.ZodTypeAny, { id: string; title?: string | undefined; category?: \"assistant\" | \"insights\" | undefined; messages?: { timestamp: string; role: \"user\" | \"system\" | \"assistant\"; content: string; reader?: Zod.objectOutputType<{}, Zod.ZodUnknown, \"strip\"> | undefined; isError?: boolean | undefined; traceData?: { transactionId?: string | undefined; traceId?: string | undefined; } | undefined; }[] | undefined; apiConfig?: { connectorId: string; defaultSystemPromptId?: string | undefined; provider?: \"OpenAI\" | \"Azure OpenAI\" | undefined; model?: string | undefined; } | undefined; summary?: { content?: string | undefined; timestamp?: string | undefined; public?: boolean | undefined; confidence?: \"medium\" | \"low\" | \"high\" | undefined; } | undefined; excludeFromLastConversationStorage?: boolean | undefined; replacements?: Zod.objectOutputType<{}, Zod.ZodString, \"strip\"> | undefined; }, { id: string; title?: string | undefined; category?: \"assistant\" | \"insights\" | undefined; messages?: { timestamp: string; role: \"user\" | \"system\" | \"assistant\"; content: string; reader?: Zod.objectInputType<{}, Zod.ZodUnknown, \"strip\"> | undefined; isError?: boolean | undefined; traceData?: { transactionId?: string | undefined; traceId?: string | undefined; } | undefined; }[] | undefined; apiConfig?: { connectorId: string; defaultSystemPromptId?: string | undefined; provider?: \"OpenAI\" | \"Azure OpenAI\" | undefined; model?: string | undefined; } | undefined; summary?: { content?: string | undefined; timestamp?: string | undefined; public?: boolean | undefined; confidence?: \"medium\" | \"low\" | \"high\" | undefined; } | undefined; excludeFromLastConversationStorage?: boolean | undefined; replacements?: Zod.objectInputType<{}, Zod.ZodString, \"strip\"> | undefined; }>, \"many\">>; }, \"strip\", Zod.ZodTypeAny, { delete?: { query?: string | undefined; ids?: string[] | undefined; } | undefined; create?: { title: string; category?: \"assistant\" | \"insights\" | undefined; messages?: { timestamp: string; role: \"user\" | \"system\" | \"assistant\"; content: string; reader?: Zod.objectOutputType<{}, Zod.ZodUnknown, \"strip\"> | undefined; isError?: boolean | undefined; traceData?: { transactionId?: string | undefined; traceId?: string | undefined; } | undefined; }[] | undefined; apiConfig?: { connectorId: string; defaultSystemPromptId?: string | undefined; provider?: \"OpenAI\" | \"Azure OpenAI\" | undefined; model?: string | undefined; } | undefined; isDefault?: boolean | undefined; excludeFromLastConversationStorage?: boolean | undefined; replacements?: Zod.objectOutputType<{}, Zod.ZodString, \"strip\"> | undefined; }[] | undefined; update?: { id: string; title?: string | undefined; category?: \"assistant\" | \"insights\" | undefined; messages?: { timestamp: string; role: \"user\" | \"system\" | \"assistant\"; content: string; reader?: Zod.objectOutputType<{}, Zod.ZodUnknown, \"strip\"> | undefined; isError?: boolean | undefined; traceData?: { transactionId?: string | undefined; traceId?: string | undefined; } | undefined; }[] | undefined; apiConfig?: { connectorId: string; defaultSystemPromptId?: string | undefined; provider?: \"OpenAI\" | \"Azure OpenAI\" | undefined; model?: string | undefined; } | undefined; summary?: { content?: string | undefined; timestamp?: string | undefined; public?: boolean | undefined; confidence?: \"medium\" | \"low\" | \"high\" | undefined; } | undefined; excludeFromLastConversationStorage?: boolean | undefined; replacements?: Zod.objectOutputType<{}, Zod.ZodString, \"strip\"> | undefined; }[] | undefined; }, { delete?: { query?: string | undefined; ids?: string[] | undefined; } | undefined; create?: { title: string; category?: \"assistant\" | \"insights\" | undefined; messages?: { timestamp: string; role: \"user\" | \"system\" | \"assistant\"; content: string; reader?: Zod.objectInputType<{}, Zod.ZodUnknown, \"strip\"> | undefined; isError?: boolean | undefined; traceData?: { transactionId?: string | undefined; traceId?: string | undefined; } | undefined; }[] | undefined; apiConfig?: { connectorId: string; defaultSystemPromptId?: string | undefined; provider?: \"OpenAI\" | \"Azure OpenAI\" | undefined; model?: string | undefined; } | undefined; isDefault?: boolean | undefined; excludeFromLastConversationStorage?: boolean | undefined; replacements?: Zod.objectInputType<{}, Zod.ZodString, \"strip\"> | undefined; }[] | undefined; update?: { id: string; title?: string | undefined; category?: \"assistant\" | \"insights\" | undefined; messages?: { timestamp: string; role: \"user\" | \"system\" | \"assistant\"; content: string; reader?: Zod.objectInputType<{}, Zod.ZodUnknown, \"strip\"> | undefined; isError?: boolean | undefined; traceData?: { transactionId?: string | undefined; traceId?: string | undefined; } | undefined; }[] | undefined; apiConfig?: { connectorId: string; defaultSystemPromptId?: string | undefined; provider?: \"OpenAI\" | \"Azure OpenAI\" | undefined; model?: string | undefined; } | undefined; summary?: { content?: string | undefined; timestamp?: string | undefined; public?: boolean | undefined; confidence?: \"medium\" | \"low\" | \"high\" | undefined; } | undefined; excludeFromLastConversationStorage?: boolean | undefined; replacements?: Zod.objectInputType<{}, Zod.ZodString, \"strip\"> | undefined; }[] | undefined; }>" ], "path": "x-pack/packages/kbn-elastic-assistant-common/impl/schemas/conversations/bulk_crud_conversations_route.gen.ts", "deprecated": false, @@ -3095,7 +3095,7 @@ "label": "PerformBulkActionResponse", "description": [], "signature": [ - "Zod.ZodObject<{ success: Zod.ZodOptional; status_code: Zod.ZodOptional; message: Zod.ZodOptional; conversations_count: Zod.ZodOptional; attributes: Zod.ZodObject<{ results: Zod.ZodObject<{ updated: Zod.ZodArray; title: Zod.ZodString; category: Zod.ZodEnum<[\"assistant\", \"insights\"]>; summary: Zod.ZodOptional; timestamp: Zod.ZodOptional; public: Zod.ZodOptional; confidence: Zod.ZodOptional>; }, \"strip\", Zod.ZodTypeAny, { content?: string | undefined; timestamp?: string | undefined; public?: boolean | undefined; confidence?: \"medium\" | \"low\" | \"high\" | undefined; }, { content?: string | undefined; timestamp?: string | undefined; public?: boolean | undefined; confidence?: \"medium\" | \"low\" | \"high\" | undefined; }>>; timestamp: Zod.ZodOptional; updatedAt: Zod.ZodOptional; createdAt: Zod.ZodString; replacements: Zod.ZodOptional, \"many\">>; users: Zod.ZodArray; name: Zod.ZodOptional; }, \"strip\", Zod.ZodTypeAny, { id?: string | undefined; name?: string | undefined; }, { id?: string | undefined; name?: string | undefined; }>, \"many\">; messages: Zod.ZodOptional, Zod.objectInputType<{}, Zod.ZodUnknown, \"strip\">>>; role: Zod.ZodEnum<[\"system\", \"user\", \"assistant\"]>; timestamp: Zod.ZodString; isError: Zod.ZodOptional; traceData: Zod.ZodOptional; traceId: Zod.ZodOptional; }, \"strip\", Zod.ZodTypeAny, { transactionId?: string | undefined; traceId?: string | undefined; }, { transactionId?: string | undefined; traceId?: string | undefined; }>>; }, \"strip\", Zod.ZodTypeAny, { timestamp: string; role: \"user\" | \"system\" | \"assistant\"; content: string; reader?: Zod.objectOutputType<{}, Zod.ZodUnknown, \"strip\"> | undefined; isError?: boolean | undefined; traceData?: { transactionId?: string | undefined; traceId?: string | undefined; } | undefined; }, { timestamp: string; role: \"user\" | \"system\" | \"assistant\"; content: string; reader?: Zod.objectInputType<{}, Zod.ZodUnknown, \"strip\"> | undefined; isError?: boolean | undefined; traceData?: { transactionId?: string | undefined; traceId?: string | undefined; } | undefined; }>, \"many\">>; apiConfig: Zod.ZodOptional; provider: Zod.ZodOptional>; model: Zod.ZodOptional; }, \"strip\", Zod.ZodTypeAny, { connectorId: string; defaultSystemPromptId?: string | undefined; provider?: \"OpenAI\" | \"Azure OpenAI\" | undefined; model?: string | undefined; }, { connectorId: string; defaultSystemPromptId?: string | undefined; provider?: \"OpenAI\" | \"Azure OpenAI\" | undefined; model?: string | undefined; }>>; isDefault: Zod.ZodOptional; excludeFromLastConversationStorage: Zod.ZodOptional; namespace: Zod.ZodString; }, \"strip\", Zod.ZodTypeAny, { id: string; namespace: string; title: string; createdAt: string; category: \"assistant\" | \"insights\"; users: { id?: string | undefined; name?: string | undefined; }[]; summary?: { content?: string | undefined; timestamp?: string | undefined; public?: boolean | undefined; confidence?: \"medium\" | \"low\" | \"high\" | undefined; } | undefined; timestamp?: string | undefined; updatedAt?: string | undefined; replacements?: { value: string; uuid: string; }[] | undefined; messages?: { timestamp: string; role: \"user\" | \"system\" | \"assistant\"; content: string; reader?: Zod.objectOutputType<{}, Zod.ZodUnknown, \"strip\"> | undefined; isError?: boolean | undefined; traceData?: { transactionId?: string | undefined; traceId?: string | undefined; } | undefined; }[] | undefined; apiConfig?: { connectorId: string; defaultSystemPromptId?: string | undefined; provider?: \"OpenAI\" | \"Azure OpenAI\" | undefined; model?: string | undefined; } | undefined; isDefault?: boolean | undefined; excludeFromLastConversationStorage?: boolean | undefined; }, { id: string; namespace: string; title: string; createdAt: string; category: \"assistant\" | \"insights\"; users: { id?: string | undefined; name?: string | undefined; }[]; summary?: { content?: string | undefined; timestamp?: string | undefined; public?: boolean | undefined; confidence?: \"medium\" | \"low\" | \"high\" | undefined; } | undefined; timestamp?: string | undefined; updatedAt?: string | undefined; replacements?: { value: string; uuid: string; }[] | undefined; messages?: { timestamp: string; role: \"user\" | \"system\" | \"assistant\"; content: string; reader?: Zod.objectInputType<{}, Zod.ZodUnknown, \"strip\"> | undefined; isError?: boolean | undefined; traceData?: { transactionId?: string | undefined; traceId?: string | undefined; } | undefined; }[] | undefined; apiConfig?: { connectorId: string; defaultSystemPromptId?: string | undefined; provider?: \"OpenAI\" | \"Azure OpenAI\" | undefined; model?: string | undefined; } | undefined; isDefault?: boolean | undefined; excludeFromLastConversationStorage?: boolean | undefined; }>, \"many\">; created: Zod.ZodArray; title: Zod.ZodString; category: Zod.ZodEnum<[\"assistant\", \"insights\"]>; summary: Zod.ZodOptional; timestamp: Zod.ZodOptional; public: Zod.ZodOptional; confidence: Zod.ZodOptional>; }, \"strip\", Zod.ZodTypeAny, { content?: string | undefined; timestamp?: string | undefined; public?: boolean | undefined; confidence?: \"medium\" | \"low\" | \"high\" | undefined; }, { content?: string | undefined; timestamp?: string | undefined; public?: boolean | undefined; confidence?: \"medium\" | \"low\" | \"high\" | undefined; }>>; timestamp: Zod.ZodOptional; updatedAt: Zod.ZodOptional; createdAt: Zod.ZodString; replacements: Zod.ZodOptional, \"many\">>; users: Zod.ZodArray; name: Zod.ZodOptional; }, \"strip\", Zod.ZodTypeAny, { id?: string | undefined; name?: string | undefined; }, { id?: string | undefined; name?: string | undefined; }>, \"many\">; messages: Zod.ZodOptional, Zod.objectInputType<{}, Zod.ZodUnknown, \"strip\">>>; role: Zod.ZodEnum<[\"system\", \"user\", \"assistant\"]>; timestamp: Zod.ZodString; isError: Zod.ZodOptional; traceData: Zod.ZodOptional; traceId: Zod.ZodOptional; }, \"strip\", Zod.ZodTypeAny, { transactionId?: string | undefined; traceId?: string | undefined; }, { transactionId?: string | undefined; traceId?: string | undefined; }>>; }, \"strip\", Zod.ZodTypeAny, { timestamp: string; role: \"user\" | \"system\" | \"assistant\"; content: string; reader?: Zod.objectOutputType<{}, Zod.ZodUnknown, \"strip\"> | undefined; isError?: boolean | undefined; traceData?: { transactionId?: string | undefined; traceId?: string | undefined; } | undefined; }, { timestamp: string; role: \"user\" | \"system\" | \"assistant\"; content: string; reader?: Zod.objectInputType<{}, Zod.ZodUnknown, \"strip\"> | undefined; isError?: boolean | undefined; traceData?: { transactionId?: string | undefined; traceId?: string | undefined; } | undefined; }>, \"many\">>; apiConfig: Zod.ZodOptional; provider: Zod.ZodOptional>; model: Zod.ZodOptional; }, \"strip\", Zod.ZodTypeAny, { connectorId: string; defaultSystemPromptId?: string | undefined; provider?: \"OpenAI\" | \"Azure OpenAI\" | undefined; model?: string | undefined; }, { connectorId: string; defaultSystemPromptId?: string | undefined; provider?: \"OpenAI\" | \"Azure OpenAI\" | undefined; model?: string | undefined; }>>; isDefault: Zod.ZodOptional; excludeFromLastConversationStorage: Zod.ZodOptional; namespace: Zod.ZodString; }, \"strip\", Zod.ZodTypeAny, { id: string; namespace: string; title: string; createdAt: string; category: \"assistant\" | \"insights\"; users: { id?: string | undefined; name?: string | undefined; }[]; summary?: { content?: string | undefined; timestamp?: string | undefined; public?: boolean | undefined; confidence?: \"medium\" | \"low\" | \"high\" | undefined; } | undefined; timestamp?: string | undefined; updatedAt?: string | undefined; replacements?: { value: string; uuid: string; }[] | undefined; messages?: { timestamp: string; role: \"user\" | \"system\" | \"assistant\"; content: string; reader?: Zod.objectOutputType<{}, Zod.ZodUnknown, \"strip\"> | undefined; isError?: boolean | undefined; traceData?: { transactionId?: string | undefined; traceId?: string | undefined; } | undefined; }[] | undefined; apiConfig?: { connectorId: string; defaultSystemPromptId?: string | undefined; provider?: \"OpenAI\" | \"Azure OpenAI\" | undefined; model?: string | undefined; } | undefined; isDefault?: boolean | undefined; excludeFromLastConversationStorage?: boolean | undefined; }, { id: string; namespace: string; title: string; createdAt: string; category: \"assistant\" | \"insights\"; users: { id?: string | undefined; name?: string | undefined; }[]; summary?: { content?: string | undefined; timestamp?: string | undefined; public?: boolean | undefined; confidence?: \"medium\" | \"low\" | \"high\" | undefined; } | undefined; timestamp?: string | undefined; updatedAt?: string | undefined; replacements?: { value: string; uuid: string; }[] | undefined; messages?: { timestamp: string; role: \"user\" | \"system\" | \"assistant\"; content: string; reader?: Zod.objectInputType<{}, Zod.ZodUnknown, \"strip\"> | undefined; isError?: boolean | undefined; traceData?: { transactionId?: string | undefined; traceId?: string | undefined; } | undefined; }[] | undefined; apiConfig?: { connectorId: string; defaultSystemPromptId?: string | undefined; provider?: \"OpenAI\" | \"Azure OpenAI\" | undefined; model?: string | undefined; } | undefined; isDefault?: boolean | undefined; excludeFromLastConversationStorage?: boolean | undefined; }>, \"many\">; deleted: Zod.ZodArray; skipped: Zod.ZodArray; skip_reason: Zod.ZodLiteral<\"CONVERSATION_NOT_MODIFIED\">; }, \"strip\", Zod.ZodTypeAny, { id: string; skip_reason: \"CONVERSATION_NOT_MODIFIED\"; name?: string | undefined; }, { id: string; skip_reason: \"CONVERSATION_NOT_MODIFIED\"; name?: string | undefined; }>, \"many\">; }, \"strip\", Zod.ZodTypeAny, { created: { id: string; namespace: string; title: string; createdAt: string; category: \"assistant\" | \"insights\"; users: { id?: string | undefined; name?: string | undefined; }[]; summary?: { content?: string | undefined; timestamp?: string | undefined; public?: boolean | undefined; confidence?: \"medium\" | \"low\" | \"high\" | undefined; } | undefined; timestamp?: string | undefined; updatedAt?: string | undefined; replacements?: { value: string; uuid: string; }[] | undefined; messages?: { timestamp: string; role: \"user\" | \"system\" | \"assistant\"; content: string; reader?: Zod.objectOutputType<{}, Zod.ZodUnknown, \"strip\"> | undefined; isError?: boolean | undefined; traceData?: { transactionId?: string | undefined; traceId?: string | undefined; } | undefined; }[] | undefined; apiConfig?: { connectorId: string; defaultSystemPromptId?: string | undefined; provider?: \"OpenAI\" | \"Azure OpenAI\" | undefined; model?: string | undefined; } | undefined; isDefault?: boolean | undefined; excludeFromLastConversationStorage?: boolean | undefined; }[]; updated: { id: string; namespace: string; title: string; createdAt: string; category: \"assistant\" | \"insights\"; users: { id?: string | undefined; name?: string | undefined; }[]; summary?: { content?: string | undefined; timestamp?: string | undefined; public?: boolean | undefined; confidence?: \"medium\" | \"low\" | \"high\" | undefined; } | undefined; timestamp?: string | undefined; updatedAt?: string | undefined; replacements?: { value: string; uuid: string; }[] | undefined; messages?: { timestamp: string; role: \"user\" | \"system\" | \"assistant\"; content: string; reader?: Zod.objectOutputType<{}, Zod.ZodUnknown, \"strip\"> | undefined; isError?: boolean | undefined; traceData?: { transactionId?: string | undefined; traceId?: string | undefined; } | undefined; }[] | undefined; apiConfig?: { connectorId: string; defaultSystemPromptId?: string | undefined; provider?: \"OpenAI\" | \"Azure OpenAI\" | undefined; model?: string | undefined; } | undefined; isDefault?: boolean | undefined; excludeFromLastConversationStorage?: boolean | undefined; }[]; skipped: { id: string; skip_reason: \"CONVERSATION_NOT_MODIFIED\"; name?: string | undefined; }[]; deleted: string[]; }, { created: { id: string; namespace: string; title: string; createdAt: string; category: \"assistant\" | \"insights\"; users: { id?: string | undefined; name?: string | undefined; }[]; summary?: { content?: string | undefined; timestamp?: string | undefined; public?: boolean | undefined; confidence?: \"medium\" | \"low\" | \"high\" | undefined; } | undefined; timestamp?: string | undefined; updatedAt?: string | undefined; replacements?: { value: string; uuid: string; }[] | undefined; messages?: { timestamp: string; role: \"user\" | \"system\" | \"assistant\"; content: string; reader?: Zod.objectInputType<{}, Zod.ZodUnknown, \"strip\"> | undefined; isError?: boolean | undefined; traceData?: { transactionId?: string | undefined; traceId?: string | undefined; } | undefined; }[] | undefined; apiConfig?: { connectorId: string; defaultSystemPromptId?: string | undefined; provider?: \"OpenAI\" | \"Azure OpenAI\" | undefined; model?: string | undefined; } | undefined; isDefault?: boolean | undefined; excludeFromLastConversationStorage?: boolean | undefined; }[]; updated: { id: string; namespace: string; title: string; createdAt: string; category: \"assistant\" | \"insights\"; users: { id?: string | undefined; name?: string | undefined; }[]; summary?: { content?: string | undefined; timestamp?: string | undefined; public?: boolean | undefined; confidence?: \"medium\" | \"low\" | \"high\" | undefined; } | undefined; timestamp?: string | undefined; updatedAt?: string | undefined; replacements?: { value: string; uuid: string; }[] | undefined; messages?: { timestamp: string; role: \"user\" | \"system\" | \"assistant\"; content: string; reader?: Zod.objectInputType<{}, Zod.ZodUnknown, \"strip\"> | undefined; isError?: boolean | undefined; traceData?: { transactionId?: string | undefined; traceId?: string | undefined; } | undefined; }[] | undefined; apiConfig?: { connectorId: string; defaultSystemPromptId?: string | undefined; provider?: \"OpenAI\" | \"Azure OpenAI\" | undefined; model?: string | undefined; } | undefined; isDefault?: boolean | undefined; excludeFromLastConversationStorage?: boolean | undefined; }[]; skipped: { id: string; skip_reason: \"CONVERSATION_NOT_MODIFIED\"; name?: string | undefined; }[]; deleted: string[]; }>; summary: Zod.ZodObject<{ failed: Zod.ZodNumber; skipped: Zod.ZodNumber; succeeded: Zod.ZodNumber; total: Zod.ZodNumber; }, \"strip\", Zod.ZodTypeAny, { total: number; succeeded: number; failed: number; skipped: number; }, { total: number; succeeded: number; failed: number; skipped: number; }>; errors: Zod.ZodOptional; conversations: Zod.ZodArray; }, \"strip\", Zod.ZodTypeAny, { id: string; name?: string | undefined; }, { id: string; name?: string | undefined; }>, \"many\">; }, \"strip\", Zod.ZodTypeAny, { message: string; status_code: number; conversations: { id: string; name?: string | undefined; }[]; err_code?: string | undefined; }, { message: string; status_code: number; conversations: { id: string; name?: string | undefined; }[]; err_code?: string | undefined; }>, \"many\">>; }, \"strip\", Zod.ZodTypeAny, { results: { created: { id: string; namespace: string; title: string; createdAt: string; category: \"assistant\" | \"insights\"; users: { id?: string | undefined; name?: string | undefined; }[]; summary?: { content?: string | undefined; timestamp?: string | undefined; public?: boolean | undefined; confidence?: \"medium\" | \"low\" | \"high\" | undefined; } | undefined; timestamp?: string | undefined; updatedAt?: string | undefined; replacements?: { value: string; uuid: string; }[] | undefined; messages?: { timestamp: string; role: \"user\" | \"system\" | \"assistant\"; content: string; reader?: Zod.objectOutputType<{}, Zod.ZodUnknown, \"strip\"> | undefined; isError?: boolean | undefined; traceData?: { transactionId?: string | undefined; traceId?: string | undefined; } | undefined; }[] | undefined; apiConfig?: { connectorId: string; defaultSystemPromptId?: string | undefined; provider?: \"OpenAI\" | \"Azure OpenAI\" | undefined; model?: string | undefined; } | undefined; isDefault?: boolean | undefined; excludeFromLastConversationStorage?: boolean | undefined; }[]; updated: { id: string; namespace: string; title: string; createdAt: string; category: \"assistant\" | \"insights\"; users: { id?: string | undefined; name?: string | undefined; }[]; summary?: { content?: string | undefined; timestamp?: string | undefined; public?: boolean | undefined; confidence?: \"medium\" | \"low\" | \"high\" | undefined; } | undefined; timestamp?: string | undefined; updatedAt?: string | undefined; replacements?: { value: string; uuid: string; }[] | undefined; messages?: { timestamp: string; role: \"user\" | \"system\" | \"assistant\"; content: string; reader?: Zod.objectOutputType<{}, Zod.ZodUnknown, \"strip\"> | undefined; isError?: boolean | undefined; traceData?: { transactionId?: string | undefined; traceId?: string | undefined; } | undefined; }[] | undefined; apiConfig?: { connectorId: string; defaultSystemPromptId?: string | undefined; provider?: \"OpenAI\" | \"Azure OpenAI\" | undefined; model?: string | undefined; } | undefined; isDefault?: boolean | undefined; excludeFromLastConversationStorage?: boolean | undefined; }[]; skipped: { id: string; skip_reason: \"CONVERSATION_NOT_MODIFIED\"; name?: string | undefined; }[]; deleted: string[]; }; summary: { total: number; succeeded: number; failed: number; skipped: number; }; errors?: { message: string; status_code: number; conversations: { id: string; name?: string | undefined; }[]; err_code?: string | undefined; }[] | undefined; }, { results: { created: { id: string; namespace: string; title: string; createdAt: string; category: \"assistant\" | \"insights\"; users: { id?: string | undefined; name?: string | undefined; }[]; summary?: { content?: string | undefined; timestamp?: string | undefined; public?: boolean | undefined; confidence?: \"medium\" | \"low\" | \"high\" | undefined; } | undefined; timestamp?: string | undefined; updatedAt?: string | undefined; replacements?: { value: string; uuid: string; }[] | undefined; messages?: { timestamp: string; role: \"user\" | \"system\" | \"assistant\"; content: string; reader?: Zod.objectInputType<{}, Zod.ZodUnknown, \"strip\"> | undefined; isError?: boolean | undefined; traceData?: { transactionId?: string | undefined; traceId?: string | undefined; } | undefined; }[] | undefined; apiConfig?: { connectorId: string; defaultSystemPromptId?: string | undefined; provider?: \"OpenAI\" | \"Azure OpenAI\" | undefined; model?: string | undefined; } | undefined; isDefault?: boolean | undefined; excludeFromLastConversationStorage?: boolean | undefined; }[]; updated: { id: string; namespace: string; title: string; createdAt: string; category: \"assistant\" | \"insights\"; users: { id?: string | undefined; name?: string | undefined; }[]; summary?: { content?: string | undefined; timestamp?: string | undefined; public?: boolean | undefined; confidence?: \"medium\" | \"low\" | \"high\" | undefined; } | undefined; timestamp?: string | undefined; updatedAt?: string | undefined; replacements?: { value: string; uuid: string; }[] | undefined; messages?: { timestamp: string; role: \"user\" | \"system\" | \"assistant\"; content: string; reader?: Zod.objectInputType<{}, Zod.ZodUnknown, \"strip\"> | undefined; isError?: boolean | undefined; traceData?: { transactionId?: string | undefined; traceId?: string | undefined; } | undefined; }[] | undefined; apiConfig?: { connectorId: string; defaultSystemPromptId?: string | undefined; provider?: \"OpenAI\" | \"Azure OpenAI\" | undefined; model?: string | undefined; } | undefined; isDefault?: boolean | undefined; excludeFromLastConversationStorage?: boolean | undefined; }[]; skipped: { id: string; skip_reason: \"CONVERSATION_NOT_MODIFIED\"; name?: string | undefined; }[]; deleted: string[]; }; summary: { total: number; succeeded: number; failed: number; skipped: number; }; errors?: { message: string; status_code: number; conversations: { id: string; name?: string | undefined; }[]; err_code?: string | undefined; }[] | undefined; }>; }, \"strip\", Zod.ZodTypeAny, { attributes: { results: { created: { id: string; namespace: string; title: string; createdAt: string; category: \"assistant\" | \"insights\"; users: { id?: string | undefined; name?: string | undefined; }[]; summary?: { content?: string | undefined; timestamp?: string | undefined; public?: boolean | undefined; confidence?: \"medium\" | \"low\" | \"high\" | undefined; } | undefined; timestamp?: string | undefined; updatedAt?: string | undefined; replacements?: { value: string; uuid: string; }[] | undefined; messages?: { timestamp: string; role: \"user\" | \"system\" | \"assistant\"; content: string; reader?: Zod.objectOutputType<{}, Zod.ZodUnknown, \"strip\"> | undefined; isError?: boolean | undefined; traceData?: { transactionId?: string | undefined; traceId?: string | undefined; } | undefined; }[] | undefined; apiConfig?: { connectorId: string; defaultSystemPromptId?: string | undefined; provider?: \"OpenAI\" | \"Azure OpenAI\" | undefined; model?: string | undefined; } | undefined; isDefault?: boolean | undefined; excludeFromLastConversationStorage?: boolean | undefined; }[]; updated: { id: string; namespace: string; title: string; createdAt: string; category: \"assistant\" | \"insights\"; users: { id?: string | undefined; name?: string | undefined; }[]; summary?: { content?: string | undefined; timestamp?: string | undefined; public?: boolean | undefined; confidence?: \"medium\" | \"low\" | \"high\" | undefined; } | undefined; timestamp?: string | undefined; updatedAt?: string | undefined; replacements?: { value: string; uuid: string; }[] | undefined; messages?: { timestamp: string; role: \"user\" | \"system\" | \"assistant\"; content: string; reader?: Zod.objectOutputType<{}, Zod.ZodUnknown, \"strip\"> | undefined; isError?: boolean | undefined; traceData?: { transactionId?: string | undefined; traceId?: string | undefined; } | undefined; }[] | undefined; apiConfig?: { connectorId: string; defaultSystemPromptId?: string | undefined; provider?: \"OpenAI\" | \"Azure OpenAI\" | undefined; model?: string | undefined; } | undefined; isDefault?: boolean | undefined; excludeFromLastConversationStorage?: boolean | undefined; }[]; skipped: { id: string; skip_reason: \"CONVERSATION_NOT_MODIFIED\"; name?: string | undefined; }[]; deleted: string[]; }; summary: { total: number; succeeded: number; failed: number; skipped: number; }; errors?: { message: string; status_code: number; conversations: { id: string; name?: string | undefined; }[]; err_code?: string | undefined; }[] | undefined; }; success?: boolean | undefined; status_code?: number | undefined; message?: string | undefined; conversations_count?: number | undefined; }, { attributes: { results: { created: { id: string; namespace: string; title: string; createdAt: string; category: \"assistant\" | \"insights\"; users: { id?: string | undefined; name?: string | undefined; }[]; summary?: { content?: string | undefined; timestamp?: string | undefined; public?: boolean | undefined; confidence?: \"medium\" | \"low\" | \"high\" | undefined; } | undefined; timestamp?: string | undefined; updatedAt?: string | undefined; replacements?: { value: string; uuid: string; }[] | undefined; messages?: { timestamp: string; role: \"user\" | \"system\" | \"assistant\"; content: string; reader?: Zod.objectInputType<{}, Zod.ZodUnknown, \"strip\"> | undefined; isError?: boolean | undefined; traceData?: { transactionId?: string | undefined; traceId?: string | undefined; } | undefined; }[] | undefined; apiConfig?: { connectorId: string; defaultSystemPromptId?: string | undefined; provider?: \"OpenAI\" | \"Azure OpenAI\" | undefined; model?: string | undefined; } | undefined; isDefault?: boolean | undefined; excludeFromLastConversationStorage?: boolean | undefined; }[]; updated: { id: string; namespace: string; title: string; createdAt: string; category: \"assistant\" | \"insights\"; users: { id?: string | undefined; name?: string | undefined; }[]; summary?: { content?: string | undefined; timestamp?: string | undefined; public?: boolean | undefined; confidence?: \"medium\" | \"low\" | \"high\" | undefined; } | undefined; timestamp?: string | undefined; updatedAt?: string | undefined; replacements?: { value: string; uuid: string; }[] | undefined; messages?: { timestamp: string; role: \"user\" | \"system\" | \"assistant\"; content: string; reader?: Zod.objectInputType<{}, Zod.ZodUnknown, \"strip\"> | undefined; isError?: boolean | undefined; traceData?: { transactionId?: string | undefined; traceId?: string | undefined; } | undefined; }[] | undefined; apiConfig?: { connectorId: string; defaultSystemPromptId?: string | undefined; provider?: \"OpenAI\" | \"Azure OpenAI\" | undefined; model?: string | undefined; } | undefined; isDefault?: boolean | undefined; excludeFromLastConversationStorage?: boolean | undefined; }[]; skipped: { id: string; skip_reason: \"CONVERSATION_NOT_MODIFIED\"; name?: string | undefined; }[]; deleted: string[]; }; summary: { total: number; succeeded: number; failed: number; skipped: number; }; errors?: { message: string; status_code: number; conversations: { id: string; name?: string | undefined; }[]; err_code?: string | undefined; }[] | undefined; }; success?: boolean | undefined; status_code?: number | undefined; message?: string | undefined; conversations_count?: number | undefined; }>" + "Zod.ZodObject<{ success: Zod.ZodOptional; status_code: Zod.ZodOptional; message: Zod.ZodOptional; conversations_count: Zod.ZodOptional; attributes: Zod.ZodObject<{ results: Zod.ZodObject<{ updated: Zod.ZodArray; title: Zod.ZodString; category: Zod.ZodEnum<[\"assistant\", \"insights\"]>; summary: Zod.ZodOptional; timestamp: Zod.ZodOptional; public: Zod.ZodOptional; confidence: Zod.ZodOptional>; }, \"strip\", Zod.ZodTypeAny, { content?: string | undefined; timestamp?: string | undefined; public?: boolean | undefined; confidence?: \"medium\" | \"low\" | \"high\" | undefined; }, { content?: string | undefined; timestamp?: string | undefined; public?: boolean | undefined; confidence?: \"medium\" | \"low\" | \"high\" | undefined; }>>; timestamp: Zod.ZodOptional; updatedAt: Zod.ZodOptional; createdAt: Zod.ZodString; replacements: Zod.ZodOptional, Zod.objectInputType<{}, Zod.ZodString, \"strip\">>>; users: Zod.ZodArray; name: Zod.ZodOptional; }, \"strip\", Zod.ZodTypeAny, { id?: string | undefined; name?: string | undefined; }, { id?: string | undefined; name?: string | undefined; }>, \"many\">; messages: Zod.ZodOptional, Zod.objectInputType<{}, Zod.ZodUnknown, \"strip\">>>; role: Zod.ZodEnum<[\"system\", \"user\", \"assistant\"]>; timestamp: Zod.ZodString; isError: Zod.ZodOptional; traceData: Zod.ZodOptional; traceId: Zod.ZodOptional; }, \"strip\", Zod.ZodTypeAny, { transactionId?: string | undefined; traceId?: string | undefined; }, { transactionId?: string | undefined; traceId?: string | undefined; }>>; }, \"strip\", Zod.ZodTypeAny, { timestamp: string; role: \"user\" | \"system\" | \"assistant\"; content: string; reader?: Zod.objectOutputType<{}, Zod.ZodUnknown, \"strip\"> | undefined; isError?: boolean | undefined; traceData?: { transactionId?: string | undefined; traceId?: string | undefined; } | undefined; }, { timestamp: string; role: \"user\" | \"system\" | \"assistant\"; content: string; reader?: Zod.objectInputType<{}, Zod.ZodUnknown, \"strip\"> | undefined; isError?: boolean | undefined; traceData?: { transactionId?: string | undefined; traceId?: string | undefined; } | undefined; }>, \"many\">>; apiConfig: Zod.ZodOptional; provider: Zod.ZodOptional>; model: Zod.ZodOptional; }, \"strip\", Zod.ZodTypeAny, { connectorId: string; defaultSystemPromptId?: string | undefined; provider?: \"OpenAI\" | \"Azure OpenAI\" | undefined; model?: string | undefined; }, { connectorId: string; defaultSystemPromptId?: string | undefined; provider?: \"OpenAI\" | \"Azure OpenAI\" | undefined; model?: string | undefined; }>>; isDefault: Zod.ZodOptional; excludeFromLastConversationStorage: Zod.ZodOptional; namespace: Zod.ZodString; }, \"strip\", Zod.ZodTypeAny, { id: string; namespace: string; title: string; createdAt: string; category: \"assistant\" | \"insights\"; users: { id?: string | undefined; name?: string | undefined; }[]; summary?: { content?: string | undefined; timestamp?: string | undefined; public?: boolean | undefined; confidence?: \"medium\" | \"low\" | \"high\" | undefined; } | undefined; timestamp?: string | undefined; updatedAt?: string | undefined; replacements?: Zod.objectOutputType<{}, Zod.ZodString, \"strip\"> | undefined; messages?: { timestamp: string; role: \"user\" | \"system\" | \"assistant\"; content: string; reader?: Zod.objectOutputType<{}, Zod.ZodUnknown, \"strip\"> | undefined; isError?: boolean | undefined; traceData?: { transactionId?: string | undefined; traceId?: string | undefined; } | undefined; }[] | undefined; apiConfig?: { connectorId: string; defaultSystemPromptId?: string | undefined; provider?: \"OpenAI\" | \"Azure OpenAI\" | undefined; model?: string | undefined; } | undefined; isDefault?: boolean | undefined; excludeFromLastConversationStorage?: boolean | undefined; }, { id: string; namespace: string; title: string; createdAt: string; category: \"assistant\" | \"insights\"; users: { id?: string | undefined; name?: string | undefined; }[]; summary?: { content?: string | undefined; timestamp?: string | undefined; public?: boolean | undefined; confidence?: \"medium\" | \"low\" | \"high\" | undefined; } | undefined; timestamp?: string | undefined; updatedAt?: string | undefined; replacements?: Zod.objectInputType<{}, Zod.ZodString, \"strip\"> | undefined; messages?: { timestamp: string; role: \"user\" | \"system\" | \"assistant\"; content: string; reader?: Zod.objectInputType<{}, Zod.ZodUnknown, \"strip\"> | undefined; isError?: boolean | undefined; traceData?: { transactionId?: string | undefined; traceId?: string | undefined; } | undefined; }[] | undefined; apiConfig?: { connectorId: string; defaultSystemPromptId?: string | undefined; provider?: \"OpenAI\" | \"Azure OpenAI\" | undefined; model?: string | undefined; } | undefined; isDefault?: boolean | undefined; excludeFromLastConversationStorage?: boolean | undefined; }>, \"many\">; created: Zod.ZodArray; title: Zod.ZodString; category: Zod.ZodEnum<[\"assistant\", \"insights\"]>; summary: Zod.ZodOptional; timestamp: Zod.ZodOptional; public: Zod.ZodOptional; confidence: Zod.ZodOptional>; }, \"strip\", Zod.ZodTypeAny, { content?: string | undefined; timestamp?: string | undefined; public?: boolean | undefined; confidence?: \"medium\" | \"low\" | \"high\" | undefined; }, { content?: string | undefined; timestamp?: string | undefined; public?: boolean | undefined; confidence?: \"medium\" | \"low\" | \"high\" | undefined; }>>; timestamp: Zod.ZodOptional; updatedAt: Zod.ZodOptional; createdAt: Zod.ZodString; replacements: Zod.ZodOptional, Zod.objectInputType<{}, Zod.ZodString, \"strip\">>>; users: Zod.ZodArray; name: Zod.ZodOptional; }, \"strip\", Zod.ZodTypeAny, { id?: string | undefined; name?: string | undefined; }, { id?: string | undefined; name?: string | undefined; }>, \"many\">; messages: Zod.ZodOptional, Zod.objectInputType<{}, Zod.ZodUnknown, \"strip\">>>; role: Zod.ZodEnum<[\"system\", \"user\", \"assistant\"]>; timestamp: Zod.ZodString; isError: Zod.ZodOptional; traceData: Zod.ZodOptional; traceId: Zod.ZodOptional; }, \"strip\", Zod.ZodTypeAny, { transactionId?: string | undefined; traceId?: string | undefined; }, { transactionId?: string | undefined; traceId?: string | undefined; }>>; }, \"strip\", Zod.ZodTypeAny, { timestamp: string; role: \"user\" | \"system\" | \"assistant\"; content: string; reader?: Zod.objectOutputType<{}, Zod.ZodUnknown, \"strip\"> | undefined; isError?: boolean | undefined; traceData?: { transactionId?: string | undefined; traceId?: string | undefined; } | undefined; }, { timestamp: string; role: \"user\" | \"system\" | \"assistant\"; content: string; reader?: Zod.objectInputType<{}, Zod.ZodUnknown, \"strip\"> | undefined; isError?: boolean | undefined; traceData?: { transactionId?: string | undefined; traceId?: string | undefined; } | undefined; }>, \"many\">>; apiConfig: Zod.ZodOptional; provider: Zod.ZodOptional>; model: Zod.ZodOptional; }, \"strip\", Zod.ZodTypeAny, { connectorId: string; defaultSystemPromptId?: string | undefined; provider?: \"OpenAI\" | \"Azure OpenAI\" | undefined; model?: string | undefined; }, { connectorId: string; defaultSystemPromptId?: string | undefined; provider?: \"OpenAI\" | \"Azure OpenAI\" | undefined; model?: string | undefined; }>>; isDefault: Zod.ZodOptional; excludeFromLastConversationStorage: Zod.ZodOptional; namespace: Zod.ZodString; }, \"strip\", Zod.ZodTypeAny, { id: string; namespace: string; title: string; createdAt: string; category: \"assistant\" | \"insights\"; users: { id?: string | undefined; name?: string | undefined; }[]; summary?: { content?: string | undefined; timestamp?: string | undefined; public?: boolean | undefined; confidence?: \"medium\" | \"low\" | \"high\" | undefined; } | undefined; timestamp?: string | undefined; updatedAt?: string | undefined; replacements?: Zod.objectOutputType<{}, Zod.ZodString, \"strip\"> | undefined; messages?: { timestamp: string; role: \"user\" | \"system\" | \"assistant\"; content: string; reader?: Zod.objectOutputType<{}, Zod.ZodUnknown, \"strip\"> | undefined; isError?: boolean | undefined; traceData?: { transactionId?: string | undefined; traceId?: string | undefined; } | undefined; }[] | undefined; apiConfig?: { connectorId: string; defaultSystemPromptId?: string | undefined; provider?: \"OpenAI\" | \"Azure OpenAI\" | undefined; model?: string | undefined; } | undefined; isDefault?: boolean | undefined; excludeFromLastConversationStorage?: boolean | undefined; }, { id: string; namespace: string; title: string; createdAt: string; category: \"assistant\" | \"insights\"; users: { id?: string | undefined; name?: string | undefined; }[]; summary?: { content?: string | undefined; timestamp?: string | undefined; public?: boolean | undefined; confidence?: \"medium\" | \"low\" | \"high\" | undefined; } | undefined; timestamp?: string | undefined; updatedAt?: string | undefined; replacements?: Zod.objectInputType<{}, Zod.ZodString, \"strip\"> | undefined; messages?: { timestamp: string; role: \"user\" | \"system\" | \"assistant\"; content: string; reader?: Zod.objectInputType<{}, Zod.ZodUnknown, \"strip\"> | undefined; isError?: boolean | undefined; traceData?: { transactionId?: string | undefined; traceId?: string | undefined; } | undefined; }[] | undefined; apiConfig?: { connectorId: string; defaultSystemPromptId?: string | undefined; provider?: \"OpenAI\" | \"Azure OpenAI\" | undefined; model?: string | undefined; } | undefined; isDefault?: boolean | undefined; excludeFromLastConversationStorage?: boolean | undefined; }>, \"many\">; deleted: Zod.ZodArray; skipped: Zod.ZodArray; skip_reason: Zod.ZodLiteral<\"CONVERSATION_NOT_MODIFIED\">; }, \"strip\", Zod.ZodTypeAny, { id: string; skip_reason: \"CONVERSATION_NOT_MODIFIED\"; name?: string | undefined; }, { id: string; skip_reason: \"CONVERSATION_NOT_MODIFIED\"; name?: string | undefined; }>, \"many\">; }, \"strip\", Zod.ZodTypeAny, { created: { id: string; namespace: string; title: string; createdAt: string; category: \"assistant\" | \"insights\"; users: { id?: string | undefined; name?: string | undefined; }[]; summary?: { content?: string | undefined; timestamp?: string | undefined; public?: boolean | undefined; confidence?: \"medium\" | \"low\" | \"high\" | undefined; } | undefined; timestamp?: string | undefined; updatedAt?: string | undefined; replacements?: Zod.objectOutputType<{}, Zod.ZodString, \"strip\"> | undefined; messages?: { timestamp: string; role: \"user\" | \"system\" | \"assistant\"; content: string; reader?: Zod.objectOutputType<{}, Zod.ZodUnknown, \"strip\"> | undefined; isError?: boolean | undefined; traceData?: { transactionId?: string | undefined; traceId?: string | undefined; } | undefined; }[] | undefined; apiConfig?: { connectorId: string; defaultSystemPromptId?: string | undefined; provider?: \"OpenAI\" | \"Azure OpenAI\" | undefined; model?: string | undefined; } | undefined; isDefault?: boolean | undefined; excludeFromLastConversationStorage?: boolean | undefined; }[]; updated: { id: string; namespace: string; title: string; createdAt: string; category: \"assistant\" | \"insights\"; users: { id?: string | undefined; name?: string | undefined; }[]; summary?: { content?: string | undefined; timestamp?: string | undefined; public?: boolean | undefined; confidence?: \"medium\" | \"low\" | \"high\" | undefined; } | undefined; timestamp?: string | undefined; updatedAt?: string | undefined; replacements?: Zod.objectOutputType<{}, Zod.ZodString, \"strip\"> | undefined; messages?: { timestamp: string; role: \"user\" | \"system\" | \"assistant\"; content: string; reader?: Zod.objectOutputType<{}, Zod.ZodUnknown, \"strip\"> | undefined; isError?: boolean | undefined; traceData?: { transactionId?: string | undefined; traceId?: string | undefined; } | undefined; }[] | undefined; apiConfig?: { connectorId: string; defaultSystemPromptId?: string | undefined; provider?: \"OpenAI\" | \"Azure OpenAI\" | undefined; model?: string | undefined; } | undefined; isDefault?: boolean | undefined; excludeFromLastConversationStorage?: boolean | undefined; }[]; skipped: { id: string; skip_reason: \"CONVERSATION_NOT_MODIFIED\"; name?: string | undefined; }[]; deleted: string[]; }, { created: { id: string; namespace: string; title: string; createdAt: string; category: \"assistant\" | \"insights\"; users: { id?: string | undefined; name?: string | undefined; }[]; summary?: { content?: string | undefined; timestamp?: string | undefined; public?: boolean | undefined; confidence?: \"medium\" | \"low\" | \"high\" | undefined; } | undefined; timestamp?: string | undefined; updatedAt?: string | undefined; replacements?: Zod.objectInputType<{}, Zod.ZodString, \"strip\"> | undefined; messages?: { timestamp: string; role: \"user\" | \"system\" | \"assistant\"; content: string; reader?: Zod.objectInputType<{}, Zod.ZodUnknown, \"strip\"> | undefined; isError?: boolean | undefined; traceData?: { transactionId?: string | undefined; traceId?: string | undefined; } | undefined; }[] | undefined; apiConfig?: { connectorId: string; defaultSystemPromptId?: string | undefined; provider?: \"OpenAI\" | \"Azure OpenAI\" | undefined; model?: string | undefined; } | undefined; isDefault?: boolean | undefined; excludeFromLastConversationStorage?: boolean | undefined; }[]; updated: { id: string; namespace: string; title: string; createdAt: string; category: \"assistant\" | \"insights\"; users: { id?: string | undefined; name?: string | undefined; }[]; summary?: { content?: string | undefined; timestamp?: string | undefined; public?: boolean | undefined; confidence?: \"medium\" | \"low\" | \"high\" | undefined; } | undefined; timestamp?: string | undefined; updatedAt?: string | undefined; replacements?: Zod.objectInputType<{}, Zod.ZodString, \"strip\"> | undefined; messages?: { timestamp: string; role: \"user\" | \"system\" | \"assistant\"; content: string; reader?: Zod.objectInputType<{}, Zod.ZodUnknown, \"strip\"> | undefined; isError?: boolean | undefined; traceData?: { transactionId?: string | undefined; traceId?: string | undefined; } | undefined; }[] | undefined; apiConfig?: { connectorId: string; defaultSystemPromptId?: string | undefined; provider?: \"OpenAI\" | \"Azure OpenAI\" | undefined; model?: string | undefined; } | undefined; isDefault?: boolean | undefined; excludeFromLastConversationStorage?: boolean | undefined; }[]; skipped: { id: string; skip_reason: \"CONVERSATION_NOT_MODIFIED\"; name?: string | undefined; }[]; deleted: string[]; }>; summary: Zod.ZodObject<{ failed: Zod.ZodNumber; skipped: Zod.ZodNumber; succeeded: Zod.ZodNumber; total: Zod.ZodNumber; }, \"strip\", Zod.ZodTypeAny, { total: number; succeeded: number; failed: number; skipped: number; }, { total: number; succeeded: number; failed: number; skipped: number; }>; errors: Zod.ZodOptional; conversations: Zod.ZodArray; }, \"strip\", Zod.ZodTypeAny, { id: string; name?: string | undefined; }, { id: string; name?: string | undefined; }>, \"many\">; }, \"strip\", Zod.ZodTypeAny, { message: string; status_code: number; conversations: { id: string; name?: string | undefined; }[]; err_code?: string | undefined; }, { message: string; status_code: number; conversations: { id: string; name?: string | undefined; }[]; err_code?: string | undefined; }>, \"many\">>; }, \"strip\", Zod.ZodTypeAny, { results: { created: { id: string; namespace: string; title: string; createdAt: string; category: \"assistant\" | \"insights\"; users: { id?: string | undefined; name?: string | undefined; }[]; summary?: { content?: string | undefined; timestamp?: string | undefined; public?: boolean | undefined; confidence?: \"medium\" | \"low\" | \"high\" | undefined; } | undefined; timestamp?: string | undefined; updatedAt?: string | undefined; replacements?: Zod.objectOutputType<{}, Zod.ZodString, \"strip\"> | undefined; messages?: { timestamp: string; role: \"user\" | \"system\" | \"assistant\"; content: string; reader?: Zod.objectOutputType<{}, Zod.ZodUnknown, \"strip\"> | undefined; isError?: boolean | undefined; traceData?: { transactionId?: string | undefined; traceId?: string | undefined; } | undefined; }[] | undefined; apiConfig?: { connectorId: string; defaultSystemPromptId?: string | undefined; provider?: \"OpenAI\" | \"Azure OpenAI\" | undefined; model?: string | undefined; } | undefined; isDefault?: boolean | undefined; excludeFromLastConversationStorage?: boolean | undefined; }[]; updated: { id: string; namespace: string; title: string; createdAt: string; category: \"assistant\" | \"insights\"; users: { id?: string | undefined; name?: string | undefined; }[]; summary?: { content?: string | undefined; timestamp?: string | undefined; public?: boolean | undefined; confidence?: \"medium\" | \"low\" | \"high\" | undefined; } | undefined; timestamp?: string | undefined; updatedAt?: string | undefined; replacements?: Zod.objectOutputType<{}, Zod.ZodString, \"strip\"> | undefined; messages?: { timestamp: string; role: \"user\" | \"system\" | \"assistant\"; content: string; reader?: Zod.objectOutputType<{}, Zod.ZodUnknown, \"strip\"> | undefined; isError?: boolean | undefined; traceData?: { transactionId?: string | undefined; traceId?: string | undefined; } | undefined; }[] | undefined; apiConfig?: { connectorId: string; defaultSystemPromptId?: string | undefined; provider?: \"OpenAI\" | \"Azure OpenAI\" | undefined; model?: string | undefined; } | undefined; isDefault?: boolean | undefined; excludeFromLastConversationStorage?: boolean | undefined; }[]; skipped: { id: string; skip_reason: \"CONVERSATION_NOT_MODIFIED\"; name?: string | undefined; }[]; deleted: string[]; }; summary: { total: number; succeeded: number; failed: number; skipped: number; }; errors?: { message: string; status_code: number; conversations: { id: string; name?: string | undefined; }[]; err_code?: string | undefined; }[] | undefined; }, { results: { created: { id: string; namespace: string; title: string; createdAt: string; category: \"assistant\" | \"insights\"; users: { id?: string | undefined; name?: string | undefined; }[]; summary?: { content?: string | undefined; timestamp?: string | undefined; public?: boolean | undefined; confidence?: \"medium\" | \"low\" | \"high\" | undefined; } | undefined; timestamp?: string | undefined; updatedAt?: string | undefined; replacements?: Zod.objectInputType<{}, Zod.ZodString, \"strip\"> | undefined; messages?: { timestamp: string; role: \"user\" | \"system\" | \"assistant\"; content: string; reader?: Zod.objectInputType<{}, Zod.ZodUnknown, \"strip\"> | undefined; isError?: boolean | undefined; traceData?: { transactionId?: string | undefined; traceId?: string | undefined; } | undefined; }[] | undefined; apiConfig?: { connectorId: string; defaultSystemPromptId?: string | undefined; provider?: \"OpenAI\" | \"Azure OpenAI\" | undefined; model?: string | undefined; } | undefined; isDefault?: boolean | undefined; excludeFromLastConversationStorage?: boolean | undefined; }[]; updated: { id: string; namespace: string; title: string; createdAt: string; category: \"assistant\" | \"insights\"; users: { id?: string | undefined; name?: string | undefined; }[]; summary?: { content?: string | undefined; timestamp?: string | undefined; public?: boolean | undefined; confidence?: \"medium\" | \"low\" | \"high\" | undefined; } | undefined; timestamp?: string | undefined; updatedAt?: string | undefined; replacements?: Zod.objectInputType<{}, Zod.ZodString, \"strip\"> | undefined; messages?: { timestamp: string; role: \"user\" | \"system\" | \"assistant\"; content: string; reader?: Zod.objectInputType<{}, Zod.ZodUnknown, \"strip\"> | undefined; isError?: boolean | undefined; traceData?: { transactionId?: string | undefined; traceId?: string | undefined; } | undefined; }[] | undefined; apiConfig?: { connectorId: string; defaultSystemPromptId?: string | undefined; provider?: \"OpenAI\" | \"Azure OpenAI\" | undefined; model?: string | undefined; } | undefined; isDefault?: boolean | undefined; excludeFromLastConversationStorage?: boolean | undefined; }[]; skipped: { id: string; skip_reason: \"CONVERSATION_NOT_MODIFIED\"; name?: string | undefined; }[]; deleted: string[]; }; summary: { total: number; succeeded: number; failed: number; skipped: number; }; errors?: { message: string; status_code: number; conversations: { id: string; name?: string | undefined; }[]; err_code?: string | undefined; }[] | undefined; }>; }, \"strip\", Zod.ZodTypeAny, { attributes: { results: { created: { id: string; namespace: string; title: string; createdAt: string; category: \"assistant\" | \"insights\"; users: { id?: string | undefined; name?: string | undefined; }[]; summary?: { content?: string | undefined; timestamp?: string | undefined; public?: boolean | undefined; confidence?: \"medium\" | \"low\" | \"high\" | undefined; } | undefined; timestamp?: string | undefined; updatedAt?: string | undefined; replacements?: Zod.objectOutputType<{}, Zod.ZodString, \"strip\"> | undefined; messages?: { timestamp: string; role: \"user\" | \"system\" | \"assistant\"; content: string; reader?: Zod.objectOutputType<{}, Zod.ZodUnknown, \"strip\"> | undefined; isError?: boolean | undefined; traceData?: { transactionId?: string | undefined; traceId?: string | undefined; } | undefined; }[] | undefined; apiConfig?: { connectorId: string; defaultSystemPromptId?: string | undefined; provider?: \"OpenAI\" | \"Azure OpenAI\" | undefined; model?: string | undefined; } | undefined; isDefault?: boolean | undefined; excludeFromLastConversationStorage?: boolean | undefined; }[]; updated: { id: string; namespace: string; title: string; createdAt: string; category: \"assistant\" | \"insights\"; users: { id?: string | undefined; name?: string | undefined; }[]; summary?: { content?: string | undefined; timestamp?: string | undefined; public?: boolean | undefined; confidence?: \"medium\" | \"low\" | \"high\" | undefined; } | undefined; timestamp?: string | undefined; updatedAt?: string | undefined; replacements?: Zod.objectOutputType<{}, Zod.ZodString, \"strip\"> | undefined; messages?: { timestamp: string; role: \"user\" | \"system\" | \"assistant\"; content: string; reader?: Zod.objectOutputType<{}, Zod.ZodUnknown, \"strip\"> | undefined; isError?: boolean | undefined; traceData?: { transactionId?: string | undefined; traceId?: string | undefined; } | undefined; }[] | undefined; apiConfig?: { connectorId: string; defaultSystemPromptId?: string | undefined; provider?: \"OpenAI\" | \"Azure OpenAI\" | undefined; model?: string | undefined; } | undefined; isDefault?: boolean | undefined; excludeFromLastConversationStorage?: boolean | undefined; }[]; skipped: { id: string; skip_reason: \"CONVERSATION_NOT_MODIFIED\"; name?: string | undefined; }[]; deleted: string[]; }; summary: { total: number; succeeded: number; failed: number; skipped: number; }; errors?: { message: string; status_code: number; conversations: { id: string; name?: string | undefined; }[]; err_code?: string | undefined; }[] | undefined; }; success?: boolean | undefined; status_code?: number | undefined; message?: string | undefined; conversations_count?: number | undefined; }, { attributes: { results: { created: { id: string; namespace: string; title: string; createdAt: string; category: \"assistant\" | \"insights\"; users: { id?: string | undefined; name?: string | undefined; }[]; summary?: { content?: string | undefined; timestamp?: string | undefined; public?: boolean | undefined; confidence?: \"medium\" | \"low\" | \"high\" | undefined; } | undefined; timestamp?: string | undefined; updatedAt?: string | undefined; replacements?: Zod.objectInputType<{}, Zod.ZodString, \"strip\"> | undefined; messages?: { timestamp: string; role: \"user\" | \"system\" | \"assistant\"; content: string; reader?: Zod.objectInputType<{}, Zod.ZodUnknown, \"strip\"> | undefined; isError?: boolean | undefined; traceData?: { transactionId?: string | undefined; traceId?: string | undefined; } | undefined; }[] | undefined; apiConfig?: { connectorId: string; defaultSystemPromptId?: string | undefined; provider?: \"OpenAI\" | \"Azure OpenAI\" | undefined; model?: string | undefined; } | undefined; isDefault?: boolean | undefined; excludeFromLastConversationStorage?: boolean | undefined; }[]; updated: { id: string; namespace: string; title: string; createdAt: string; category: \"assistant\" | \"insights\"; users: { id?: string | undefined; name?: string | undefined; }[]; summary?: { content?: string | undefined; timestamp?: string | undefined; public?: boolean | undefined; confidence?: \"medium\" | \"low\" | \"high\" | undefined; } | undefined; timestamp?: string | undefined; updatedAt?: string | undefined; replacements?: Zod.objectInputType<{}, Zod.ZodString, \"strip\"> | undefined; messages?: { timestamp: string; role: \"user\" | \"system\" | \"assistant\"; content: string; reader?: Zod.objectInputType<{}, Zod.ZodUnknown, \"strip\"> | undefined; isError?: boolean | undefined; traceData?: { transactionId?: string | undefined; traceId?: string | undefined; } | undefined; }[] | undefined; apiConfig?: { connectorId: string; defaultSystemPromptId?: string | undefined; provider?: \"OpenAI\" | \"Azure OpenAI\" | undefined; model?: string | undefined; } | undefined; isDefault?: boolean | undefined; excludeFromLastConversationStorage?: boolean | undefined; }[]; skipped: { id: string; skip_reason: \"CONVERSATION_NOT_MODIFIED\"; name?: string | undefined; }[]; deleted: string[]; }; summary: { total: number; succeeded: number; failed: number; skipped: number; }; errors?: { message: string; status_code: number; conversations: { id: string; name?: string | undefined; }[]; err_code?: string | undefined; }[] | undefined; }; success?: boolean | undefined; status_code?: number | undefined; message?: string | undefined; conversations_count?: number | undefined; }>" ], "path": "x-pack/packages/kbn-elastic-assistant-common/impl/schemas/conversations/bulk_crud_conversations_route.gen.ts", "deprecated": false, @@ -3215,7 +3215,7 @@ "label": "ReadConversationResponse", "description": [], "signature": [ - "Zod.ZodObject<{ id: Zod.ZodUnion<[Zod.ZodString, Zod.ZodString]>; title: Zod.ZodString; category: Zod.ZodEnum<[\"assistant\", \"insights\"]>; summary: Zod.ZodOptional; timestamp: Zod.ZodOptional; public: Zod.ZodOptional; confidence: Zod.ZodOptional>; }, \"strip\", Zod.ZodTypeAny, { content?: string | undefined; timestamp?: string | undefined; public?: boolean | undefined; confidence?: \"medium\" | \"low\" | \"high\" | undefined; }, { content?: string | undefined; timestamp?: string | undefined; public?: boolean | undefined; confidence?: \"medium\" | \"low\" | \"high\" | undefined; }>>; timestamp: Zod.ZodOptional; updatedAt: Zod.ZodOptional; createdAt: Zod.ZodString; replacements: Zod.ZodOptional, \"many\">>; users: Zod.ZodArray; name: Zod.ZodOptional; }, \"strip\", Zod.ZodTypeAny, { id?: string | undefined; name?: string | undefined; }, { id?: string | undefined; name?: string | undefined; }>, \"many\">; messages: Zod.ZodOptional, Zod.objectInputType<{}, Zod.ZodUnknown, \"strip\">>>; role: Zod.ZodEnum<[\"system\", \"user\", \"assistant\"]>; timestamp: Zod.ZodString; isError: Zod.ZodOptional; traceData: Zod.ZodOptional; traceId: Zod.ZodOptional; }, \"strip\", Zod.ZodTypeAny, { transactionId?: string | undefined; traceId?: string | undefined; }, { transactionId?: string | undefined; traceId?: string | undefined; }>>; }, \"strip\", Zod.ZodTypeAny, { timestamp: string; role: \"user\" | \"system\" | \"assistant\"; content: string; reader?: Zod.objectOutputType<{}, Zod.ZodUnknown, \"strip\"> | undefined; isError?: boolean | undefined; traceData?: { transactionId?: string | undefined; traceId?: string | undefined; } | undefined; }, { timestamp: string; role: \"user\" | \"system\" | \"assistant\"; content: string; reader?: Zod.objectInputType<{}, Zod.ZodUnknown, \"strip\"> | undefined; isError?: boolean | undefined; traceData?: { transactionId?: string | undefined; traceId?: string | undefined; } | undefined; }>, \"many\">>; apiConfig: Zod.ZodOptional; provider: Zod.ZodOptional>; model: Zod.ZodOptional; }, \"strip\", Zod.ZodTypeAny, { connectorId: string; defaultSystemPromptId?: string | undefined; provider?: \"OpenAI\" | \"Azure OpenAI\" | undefined; model?: string | undefined; }, { connectorId: string; defaultSystemPromptId?: string | undefined; provider?: \"OpenAI\" | \"Azure OpenAI\" | undefined; model?: string | undefined; }>>; isDefault: Zod.ZodOptional; excludeFromLastConversationStorage: Zod.ZodOptional; namespace: Zod.ZodString; }, \"strip\", Zod.ZodTypeAny, { id: string; namespace: string; title: string; createdAt: string; category: \"assistant\" | \"insights\"; users: { id?: string | undefined; name?: string | undefined; }[]; summary?: { content?: string | undefined; timestamp?: string | undefined; public?: boolean | undefined; confidence?: \"medium\" | \"low\" | \"high\" | undefined; } | undefined; timestamp?: string | undefined; updatedAt?: string | undefined; replacements?: { value: string; uuid: string; }[] | undefined; messages?: { timestamp: string; role: \"user\" | \"system\" | \"assistant\"; content: string; reader?: Zod.objectOutputType<{}, Zod.ZodUnknown, \"strip\"> | undefined; isError?: boolean | undefined; traceData?: { transactionId?: string | undefined; traceId?: string | undefined; } | undefined; }[] | undefined; apiConfig?: { connectorId: string; defaultSystemPromptId?: string | undefined; provider?: \"OpenAI\" | \"Azure OpenAI\" | undefined; model?: string | undefined; } | undefined; isDefault?: boolean | undefined; excludeFromLastConversationStorage?: boolean | undefined; }, { id: string; namespace: string; title: string; createdAt: string; category: \"assistant\" | \"insights\"; users: { id?: string | undefined; name?: string | undefined; }[]; summary?: { content?: string | undefined; timestamp?: string | undefined; public?: boolean | undefined; confidence?: \"medium\" | \"low\" | \"high\" | undefined; } | undefined; timestamp?: string | undefined; updatedAt?: string | undefined; replacements?: { value: string; uuid: string; }[] | undefined; messages?: { timestamp: string; role: \"user\" | \"system\" | \"assistant\"; content: string; reader?: Zod.objectInputType<{}, Zod.ZodUnknown, \"strip\"> | undefined; isError?: boolean | undefined; traceData?: { transactionId?: string | undefined; traceId?: string | undefined; } | undefined; }[] | undefined; apiConfig?: { connectorId: string; defaultSystemPromptId?: string | undefined; provider?: \"OpenAI\" | \"Azure OpenAI\" | undefined; model?: string | undefined; } | undefined; isDefault?: boolean | undefined; excludeFromLastConversationStorage?: boolean | undefined; }>" + "Zod.ZodObject<{ id: Zod.ZodUnion<[Zod.ZodString, Zod.ZodString]>; title: Zod.ZodString; category: Zod.ZodEnum<[\"assistant\", \"insights\"]>; summary: Zod.ZodOptional; timestamp: Zod.ZodOptional; public: Zod.ZodOptional; confidence: Zod.ZodOptional>; }, \"strip\", Zod.ZodTypeAny, { content?: string | undefined; timestamp?: string | undefined; public?: boolean | undefined; confidence?: \"medium\" | \"low\" | \"high\" | undefined; }, { content?: string | undefined; timestamp?: string | undefined; public?: boolean | undefined; confidence?: \"medium\" | \"low\" | \"high\" | undefined; }>>; timestamp: Zod.ZodOptional; updatedAt: Zod.ZodOptional; createdAt: Zod.ZodString; replacements: Zod.ZodOptional, Zod.objectInputType<{}, Zod.ZodString, \"strip\">>>; users: Zod.ZodArray; name: Zod.ZodOptional; }, \"strip\", Zod.ZodTypeAny, { id?: string | undefined; name?: string | undefined; }, { id?: string | undefined; name?: string | undefined; }>, \"many\">; messages: Zod.ZodOptional, Zod.objectInputType<{}, Zod.ZodUnknown, \"strip\">>>; role: Zod.ZodEnum<[\"system\", \"user\", \"assistant\"]>; timestamp: Zod.ZodString; isError: Zod.ZodOptional; traceData: Zod.ZodOptional; traceId: Zod.ZodOptional; }, \"strip\", Zod.ZodTypeAny, { transactionId?: string | undefined; traceId?: string | undefined; }, { transactionId?: string | undefined; traceId?: string | undefined; }>>; }, \"strip\", Zod.ZodTypeAny, { timestamp: string; role: \"user\" | \"system\" | \"assistant\"; content: string; reader?: Zod.objectOutputType<{}, Zod.ZodUnknown, \"strip\"> | undefined; isError?: boolean | undefined; traceData?: { transactionId?: string | undefined; traceId?: string | undefined; } | undefined; }, { timestamp: string; role: \"user\" | \"system\" | \"assistant\"; content: string; reader?: Zod.objectInputType<{}, Zod.ZodUnknown, \"strip\"> | undefined; isError?: boolean | undefined; traceData?: { transactionId?: string | undefined; traceId?: string | undefined; } | undefined; }>, \"many\">>; apiConfig: Zod.ZodOptional; provider: Zod.ZodOptional>; model: Zod.ZodOptional; }, \"strip\", Zod.ZodTypeAny, { connectorId: string; defaultSystemPromptId?: string | undefined; provider?: \"OpenAI\" | \"Azure OpenAI\" | undefined; model?: string | undefined; }, { connectorId: string; defaultSystemPromptId?: string | undefined; provider?: \"OpenAI\" | \"Azure OpenAI\" | undefined; model?: string | undefined; }>>; isDefault: Zod.ZodOptional; excludeFromLastConversationStorage: Zod.ZodOptional; namespace: Zod.ZodString; }, \"strip\", Zod.ZodTypeAny, { id: string; namespace: string; title: string; createdAt: string; category: \"assistant\" | \"insights\"; users: { id?: string | undefined; name?: string | undefined; }[]; summary?: { content?: string | undefined; timestamp?: string | undefined; public?: boolean | undefined; confidence?: \"medium\" | \"low\" | \"high\" | undefined; } | undefined; timestamp?: string | undefined; updatedAt?: string | undefined; replacements?: Zod.objectOutputType<{}, Zod.ZodString, \"strip\"> | undefined; messages?: { timestamp: string; role: \"user\" | \"system\" | \"assistant\"; content: string; reader?: Zod.objectOutputType<{}, Zod.ZodUnknown, \"strip\"> | undefined; isError?: boolean | undefined; traceData?: { transactionId?: string | undefined; traceId?: string | undefined; } | undefined; }[] | undefined; apiConfig?: { connectorId: string; defaultSystemPromptId?: string | undefined; provider?: \"OpenAI\" | \"Azure OpenAI\" | undefined; model?: string | undefined; } | undefined; isDefault?: boolean | undefined; excludeFromLastConversationStorage?: boolean | undefined; }, { id: string; namespace: string; title: string; createdAt: string; category: \"assistant\" | \"insights\"; users: { id?: string | undefined; name?: string | undefined; }[]; summary?: { content?: string | undefined; timestamp?: string | undefined; public?: boolean | undefined; confidence?: \"medium\" | \"low\" | \"high\" | undefined; } | undefined; timestamp?: string | undefined; updatedAt?: string | undefined; replacements?: Zod.objectInputType<{}, Zod.ZodString, \"strip\"> | undefined; messages?: { timestamp: string; role: \"user\" | \"system\" | \"assistant\"; content: string; reader?: Zod.objectInputType<{}, Zod.ZodUnknown, \"strip\"> | undefined; isError?: boolean | undefined; traceData?: { transactionId?: string | undefined; traceId?: string | undefined; } | undefined; }[] | undefined; apiConfig?: { connectorId: string; defaultSystemPromptId?: string | undefined; provider?: \"OpenAI\" | \"Azure OpenAI\" | undefined; model?: string | undefined; } | undefined; isDefault?: boolean | undefined; excludeFromLastConversationStorage?: boolean | undefined; }>" ], "path": "x-pack/packages/kbn-elastic-assistant-common/impl/schemas/conversations/crud_conversation_route.gen.ts", "deprecated": false, @@ -3269,13 +3269,13 @@ }, { "parentPluginId": "@kbn/elastic-assistant-common", - "id": "def-common.Replacement", + "id": "def-common.Replacements", "type": "Object", "tags": [], - "label": "Replacement", + "label": "Replacements", "description": [], "signature": [ - "Zod.ZodObject<{ value: Zod.ZodString; uuid: Zod.ZodString; }, \"strip\", Zod.ZodTypeAny, { value: string; uuid: string; }, { value: string; uuid: string; }>" + "Zod.ZodObject<{}, \"strip\", Zod.ZodString, Zod.objectOutputType<{}, Zod.ZodString, \"strip\">, Zod.objectInputType<{}, Zod.ZodString, \"strip\">>" ], "path": "x-pack/packages/kbn-elastic-assistant-common/impl/schemas/conversations/common_attributes.gen.ts", "deprecated": false, @@ -3335,7 +3335,7 @@ "label": "UpdateConversationRequestBody", "description": [], "signature": [ - "Zod.ZodObject<{ id: Zod.ZodUnion<[Zod.ZodString, Zod.ZodString]>; title: Zod.ZodOptional; category: Zod.ZodOptional>; messages: Zod.ZodOptional, Zod.objectInputType<{}, Zod.ZodUnknown, \"strip\">>>; role: Zod.ZodEnum<[\"system\", \"user\", \"assistant\"]>; timestamp: Zod.ZodString; isError: Zod.ZodOptional; traceData: Zod.ZodOptional; traceId: Zod.ZodOptional; }, \"strip\", Zod.ZodTypeAny, { transactionId?: string | undefined; traceId?: string | undefined; }, { transactionId?: string | undefined; traceId?: string | undefined; }>>; }, \"strip\", Zod.ZodTypeAny, { timestamp: string; role: \"user\" | \"system\" | \"assistant\"; content: string; reader?: Zod.objectOutputType<{}, Zod.ZodUnknown, \"strip\"> | undefined; isError?: boolean | undefined; traceData?: { transactionId?: string | undefined; traceId?: string | undefined; } | undefined; }, { timestamp: string; role: \"user\" | \"system\" | \"assistant\"; content: string; reader?: Zod.objectInputType<{}, Zod.ZodUnknown, \"strip\"> | undefined; isError?: boolean | undefined; traceData?: { transactionId?: string | undefined; traceId?: string | undefined; } | undefined; }>, \"many\">>; apiConfig: Zod.ZodOptional; provider: Zod.ZodOptional>; model: Zod.ZodOptional; }, \"strip\", Zod.ZodTypeAny, { connectorId: string; defaultSystemPromptId?: string | undefined; provider?: \"OpenAI\" | \"Azure OpenAI\" | undefined; model?: string | undefined; }, { connectorId: string; defaultSystemPromptId?: string | undefined; provider?: \"OpenAI\" | \"Azure OpenAI\" | undefined; model?: string | undefined; }>>; summary: Zod.ZodOptional; timestamp: Zod.ZodOptional; public: Zod.ZodOptional; confidence: Zod.ZodOptional>; }, \"strip\", Zod.ZodTypeAny, { content?: string | undefined; timestamp?: string | undefined; public?: boolean | undefined; confidence?: \"medium\" | \"low\" | \"high\" | undefined; }, { content?: string | undefined; timestamp?: string | undefined; public?: boolean | undefined; confidence?: \"medium\" | \"low\" | \"high\" | undefined; }>>; excludeFromLastConversationStorage: Zod.ZodOptional; replacements: Zod.ZodOptional, \"many\">>; }, \"strip\", Zod.ZodTypeAny, { id: string; title?: string | undefined; category?: \"assistant\" | \"insights\" | undefined; messages?: { timestamp: string; role: \"user\" | \"system\" | \"assistant\"; content: string; reader?: Zod.objectOutputType<{}, Zod.ZodUnknown, \"strip\"> | undefined; isError?: boolean | undefined; traceData?: { transactionId?: string | undefined; traceId?: string | undefined; } | undefined; }[] | undefined; apiConfig?: { connectorId: string; defaultSystemPromptId?: string | undefined; provider?: \"OpenAI\" | \"Azure OpenAI\" | undefined; model?: string | undefined; } | undefined; summary?: { content?: string | undefined; timestamp?: string | undefined; public?: boolean | undefined; confidence?: \"medium\" | \"low\" | \"high\" | undefined; } | undefined; excludeFromLastConversationStorage?: boolean | undefined; replacements?: { value: string; uuid: string; }[] | undefined; }, { id: string; title?: string | undefined; category?: \"assistant\" | \"insights\" | undefined; messages?: { timestamp: string; role: \"user\" | \"system\" | \"assistant\"; content: string; reader?: Zod.objectInputType<{}, Zod.ZodUnknown, \"strip\"> | undefined; isError?: boolean | undefined; traceData?: { transactionId?: string | undefined; traceId?: string | undefined; } | undefined; }[] | undefined; apiConfig?: { connectorId: string; defaultSystemPromptId?: string | undefined; provider?: \"OpenAI\" | \"Azure OpenAI\" | undefined; model?: string | undefined; } | undefined; summary?: { content?: string | undefined; timestamp?: string | undefined; public?: boolean | undefined; confidence?: \"medium\" | \"low\" | \"high\" | undefined; } | undefined; excludeFromLastConversationStorage?: boolean | undefined; replacements?: { value: string; uuid: string; }[] | undefined; }>" + "Zod.ZodObject<{ id: Zod.ZodUnion<[Zod.ZodString, Zod.ZodString]>; title: Zod.ZodOptional; category: Zod.ZodOptional>; messages: Zod.ZodOptional, Zod.objectInputType<{}, Zod.ZodUnknown, \"strip\">>>; role: Zod.ZodEnum<[\"system\", \"user\", \"assistant\"]>; timestamp: Zod.ZodString; isError: Zod.ZodOptional; traceData: Zod.ZodOptional; traceId: Zod.ZodOptional; }, \"strip\", Zod.ZodTypeAny, { transactionId?: string | undefined; traceId?: string | undefined; }, { transactionId?: string | undefined; traceId?: string | undefined; }>>; }, \"strip\", Zod.ZodTypeAny, { timestamp: string; role: \"user\" | \"system\" | \"assistant\"; content: string; reader?: Zod.objectOutputType<{}, Zod.ZodUnknown, \"strip\"> | undefined; isError?: boolean | undefined; traceData?: { transactionId?: string | undefined; traceId?: string | undefined; } | undefined; }, { timestamp: string; role: \"user\" | \"system\" | \"assistant\"; content: string; reader?: Zod.objectInputType<{}, Zod.ZodUnknown, \"strip\"> | undefined; isError?: boolean | undefined; traceData?: { transactionId?: string | undefined; traceId?: string | undefined; } | undefined; }>, \"many\">>; apiConfig: Zod.ZodOptional; provider: Zod.ZodOptional>; model: Zod.ZodOptional; }, \"strip\", Zod.ZodTypeAny, { connectorId: string; defaultSystemPromptId?: string | undefined; provider?: \"OpenAI\" | \"Azure OpenAI\" | undefined; model?: string | undefined; }, { connectorId: string; defaultSystemPromptId?: string | undefined; provider?: \"OpenAI\" | \"Azure OpenAI\" | undefined; model?: string | undefined; }>>; summary: Zod.ZodOptional; timestamp: Zod.ZodOptional; public: Zod.ZodOptional; confidence: Zod.ZodOptional>; }, \"strip\", Zod.ZodTypeAny, { content?: string | undefined; timestamp?: string | undefined; public?: boolean | undefined; confidence?: \"medium\" | \"low\" | \"high\" | undefined; }, { content?: string | undefined; timestamp?: string | undefined; public?: boolean | undefined; confidence?: \"medium\" | \"low\" | \"high\" | undefined; }>>; excludeFromLastConversationStorage: Zod.ZodOptional; replacements: Zod.ZodOptional, Zod.objectInputType<{}, Zod.ZodString, \"strip\">>>; }, \"strip\", Zod.ZodTypeAny, { id: string; title?: string | undefined; category?: \"assistant\" | \"insights\" | undefined; messages?: { timestamp: string; role: \"user\" | \"system\" | \"assistant\"; content: string; reader?: Zod.objectOutputType<{}, Zod.ZodUnknown, \"strip\"> | undefined; isError?: boolean | undefined; traceData?: { transactionId?: string | undefined; traceId?: string | undefined; } | undefined; }[] | undefined; apiConfig?: { connectorId: string; defaultSystemPromptId?: string | undefined; provider?: \"OpenAI\" | \"Azure OpenAI\" | undefined; model?: string | undefined; } | undefined; summary?: { content?: string | undefined; timestamp?: string | undefined; public?: boolean | undefined; confidence?: \"medium\" | \"low\" | \"high\" | undefined; } | undefined; excludeFromLastConversationStorage?: boolean | undefined; replacements?: Zod.objectOutputType<{}, Zod.ZodString, \"strip\"> | undefined; }, { id: string; title?: string | undefined; category?: \"assistant\" | \"insights\" | undefined; messages?: { timestamp: string; role: \"user\" | \"system\" | \"assistant\"; content: string; reader?: Zod.objectInputType<{}, Zod.ZodUnknown, \"strip\"> | undefined; isError?: boolean | undefined; traceData?: { transactionId?: string | undefined; traceId?: string | undefined; } | undefined; }[] | undefined; apiConfig?: { connectorId: string; defaultSystemPromptId?: string | undefined; provider?: \"OpenAI\" | \"Azure OpenAI\" | undefined; model?: string | undefined; } | undefined; summary?: { content?: string | undefined; timestamp?: string | undefined; public?: boolean | undefined; confidence?: \"medium\" | \"low\" | \"high\" | undefined; } | undefined; excludeFromLastConversationStorage?: boolean | undefined; replacements?: Zod.objectInputType<{}, Zod.ZodString, \"strip\"> | undefined; }>" ], "path": "x-pack/packages/kbn-elastic-assistant-common/impl/schemas/conversations/crud_conversation_route.gen.ts", "deprecated": false, @@ -3365,7 +3365,7 @@ "label": "UpdateConversationResponse", "description": [], "signature": [ - "Zod.ZodObject<{ id: Zod.ZodUnion<[Zod.ZodString, Zod.ZodString]>; title: Zod.ZodString; category: Zod.ZodEnum<[\"assistant\", \"insights\"]>; summary: Zod.ZodOptional; timestamp: Zod.ZodOptional; public: Zod.ZodOptional; confidence: Zod.ZodOptional>; }, \"strip\", Zod.ZodTypeAny, { content?: string | undefined; timestamp?: string | undefined; public?: boolean | undefined; confidence?: \"medium\" | \"low\" | \"high\" | undefined; }, { content?: string | undefined; timestamp?: string | undefined; public?: boolean | undefined; confidence?: \"medium\" | \"low\" | \"high\" | undefined; }>>; timestamp: Zod.ZodOptional; updatedAt: Zod.ZodOptional; createdAt: Zod.ZodString; replacements: Zod.ZodOptional, \"many\">>; users: Zod.ZodArray; name: Zod.ZodOptional; }, \"strip\", Zod.ZodTypeAny, { id?: string | undefined; name?: string | undefined; }, { id?: string | undefined; name?: string | undefined; }>, \"many\">; messages: Zod.ZodOptional, Zod.objectInputType<{}, Zod.ZodUnknown, \"strip\">>>; role: Zod.ZodEnum<[\"system\", \"user\", \"assistant\"]>; timestamp: Zod.ZodString; isError: Zod.ZodOptional; traceData: Zod.ZodOptional; traceId: Zod.ZodOptional; }, \"strip\", Zod.ZodTypeAny, { transactionId?: string | undefined; traceId?: string | undefined; }, { transactionId?: string | undefined; traceId?: string | undefined; }>>; }, \"strip\", Zod.ZodTypeAny, { timestamp: string; role: \"user\" | \"system\" | \"assistant\"; content: string; reader?: Zod.objectOutputType<{}, Zod.ZodUnknown, \"strip\"> | undefined; isError?: boolean | undefined; traceData?: { transactionId?: string | undefined; traceId?: string | undefined; } | undefined; }, { timestamp: string; role: \"user\" | \"system\" | \"assistant\"; content: string; reader?: Zod.objectInputType<{}, Zod.ZodUnknown, \"strip\"> | undefined; isError?: boolean | undefined; traceData?: { transactionId?: string | undefined; traceId?: string | undefined; } | undefined; }>, \"many\">>; apiConfig: Zod.ZodOptional; provider: Zod.ZodOptional>; model: Zod.ZodOptional; }, \"strip\", Zod.ZodTypeAny, { connectorId: string; defaultSystemPromptId?: string | undefined; provider?: \"OpenAI\" | \"Azure OpenAI\" | undefined; model?: string | undefined; }, { connectorId: string; defaultSystemPromptId?: string | undefined; provider?: \"OpenAI\" | \"Azure OpenAI\" | undefined; model?: string | undefined; }>>; isDefault: Zod.ZodOptional; excludeFromLastConversationStorage: Zod.ZodOptional; namespace: Zod.ZodString; }, \"strip\", Zod.ZodTypeAny, { id: string; namespace: string; title: string; createdAt: string; category: \"assistant\" | \"insights\"; users: { id?: string | undefined; name?: string | undefined; }[]; summary?: { content?: string | undefined; timestamp?: string | undefined; public?: boolean | undefined; confidence?: \"medium\" | \"low\" | \"high\" | undefined; } | undefined; timestamp?: string | undefined; updatedAt?: string | undefined; replacements?: { value: string; uuid: string; }[] | undefined; messages?: { timestamp: string; role: \"user\" | \"system\" | \"assistant\"; content: string; reader?: Zod.objectOutputType<{}, Zod.ZodUnknown, \"strip\"> | undefined; isError?: boolean | undefined; traceData?: { transactionId?: string | undefined; traceId?: string | undefined; } | undefined; }[] | undefined; apiConfig?: { connectorId: string; defaultSystemPromptId?: string | undefined; provider?: \"OpenAI\" | \"Azure OpenAI\" | undefined; model?: string | undefined; } | undefined; isDefault?: boolean | undefined; excludeFromLastConversationStorage?: boolean | undefined; }, { id: string; namespace: string; title: string; createdAt: string; category: \"assistant\" | \"insights\"; users: { id?: string | undefined; name?: string | undefined; }[]; summary?: { content?: string | undefined; timestamp?: string | undefined; public?: boolean | undefined; confidence?: \"medium\" | \"low\" | \"high\" | undefined; } | undefined; timestamp?: string | undefined; updatedAt?: string | undefined; replacements?: { value: string; uuid: string; }[] | undefined; messages?: { timestamp: string; role: \"user\" | \"system\" | \"assistant\"; content: string; reader?: Zod.objectInputType<{}, Zod.ZodUnknown, \"strip\"> | undefined; isError?: boolean | undefined; traceData?: { transactionId?: string | undefined; traceId?: string | undefined; } | undefined; }[] | undefined; apiConfig?: { connectorId: string; defaultSystemPromptId?: string | undefined; provider?: \"OpenAI\" | \"Azure OpenAI\" | undefined; model?: string | undefined; } | undefined; isDefault?: boolean | undefined; excludeFromLastConversationStorage?: boolean | undefined; }>" + "Zod.ZodObject<{ id: Zod.ZodUnion<[Zod.ZodString, Zod.ZodString]>; title: Zod.ZodString; category: Zod.ZodEnum<[\"assistant\", \"insights\"]>; summary: Zod.ZodOptional; timestamp: Zod.ZodOptional; public: Zod.ZodOptional; confidence: Zod.ZodOptional>; }, \"strip\", Zod.ZodTypeAny, { content?: string | undefined; timestamp?: string | undefined; public?: boolean | undefined; confidence?: \"medium\" | \"low\" | \"high\" | undefined; }, { content?: string | undefined; timestamp?: string | undefined; public?: boolean | undefined; confidence?: \"medium\" | \"low\" | \"high\" | undefined; }>>; timestamp: Zod.ZodOptional; updatedAt: Zod.ZodOptional; createdAt: Zod.ZodString; replacements: Zod.ZodOptional, Zod.objectInputType<{}, Zod.ZodString, \"strip\">>>; users: Zod.ZodArray; name: Zod.ZodOptional; }, \"strip\", Zod.ZodTypeAny, { id?: string | undefined; name?: string | undefined; }, { id?: string | undefined; name?: string | undefined; }>, \"many\">; messages: Zod.ZodOptional, Zod.objectInputType<{}, Zod.ZodUnknown, \"strip\">>>; role: Zod.ZodEnum<[\"system\", \"user\", \"assistant\"]>; timestamp: Zod.ZodString; isError: Zod.ZodOptional; traceData: Zod.ZodOptional; traceId: Zod.ZodOptional; }, \"strip\", Zod.ZodTypeAny, { transactionId?: string | undefined; traceId?: string | undefined; }, { transactionId?: string | undefined; traceId?: string | undefined; }>>; }, \"strip\", Zod.ZodTypeAny, { timestamp: string; role: \"user\" | \"system\" | \"assistant\"; content: string; reader?: Zod.objectOutputType<{}, Zod.ZodUnknown, \"strip\"> | undefined; isError?: boolean | undefined; traceData?: { transactionId?: string | undefined; traceId?: string | undefined; } | undefined; }, { timestamp: string; role: \"user\" | \"system\" | \"assistant\"; content: string; reader?: Zod.objectInputType<{}, Zod.ZodUnknown, \"strip\"> | undefined; isError?: boolean | undefined; traceData?: { transactionId?: string | undefined; traceId?: string | undefined; } | undefined; }>, \"many\">>; apiConfig: Zod.ZodOptional; provider: Zod.ZodOptional>; model: Zod.ZodOptional; }, \"strip\", Zod.ZodTypeAny, { connectorId: string; defaultSystemPromptId?: string | undefined; provider?: \"OpenAI\" | \"Azure OpenAI\" | undefined; model?: string | undefined; }, { connectorId: string; defaultSystemPromptId?: string | undefined; provider?: \"OpenAI\" | \"Azure OpenAI\" | undefined; model?: string | undefined; }>>; isDefault: Zod.ZodOptional; excludeFromLastConversationStorage: Zod.ZodOptional; namespace: Zod.ZodString; }, \"strip\", Zod.ZodTypeAny, { id: string; namespace: string; title: string; createdAt: string; category: \"assistant\" | \"insights\"; users: { id?: string | undefined; name?: string | undefined; }[]; summary?: { content?: string | undefined; timestamp?: string | undefined; public?: boolean | undefined; confidence?: \"medium\" | \"low\" | \"high\" | undefined; } | undefined; timestamp?: string | undefined; updatedAt?: string | undefined; replacements?: Zod.objectOutputType<{}, Zod.ZodString, \"strip\"> | undefined; messages?: { timestamp: string; role: \"user\" | \"system\" | \"assistant\"; content: string; reader?: Zod.objectOutputType<{}, Zod.ZodUnknown, \"strip\"> | undefined; isError?: boolean | undefined; traceData?: { transactionId?: string | undefined; traceId?: string | undefined; } | undefined; }[] | undefined; apiConfig?: { connectorId: string; defaultSystemPromptId?: string | undefined; provider?: \"OpenAI\" | \"Azure OpenAI\" | undefined; model?: string | undefined; } | undefined; isDefault?: boolean | undefined; excludeFromLastConversationStorage?: boolean | undefined; }, { id: string; namespace: string; title: string; createdAt: string; category: \"assistant\" | \"insights\"; users: { id?: string | undefined; name?: string | undefined; }[]; summary?: { content?: string | undefined; timestamp?: string | undefined; public?: boolean | undefined; confidence?: \"medium\" | \"low\" | \"high\" | undefined; } | undefined; timestamp?: string | undefined; updatedAt?: string | undefined; replacements?: Zod.objectInputType<{}, Zod.ZodString, \"strip\"> | undefined; messages?: { timestamp: string; role: \"user\" | \"system\" | \"assistant\"; content: string; reader?: Zod.objectInputType<{}, Zod.ZodUnknown, \"strip\"> | undefined; isError?: boolean | undefined; traceData?: { transactionId?: string | undefined; traceId?: string | undefined; } | undefined; }[] | undefined; apiConfig?: { connectorId: string; defaultSystemPromptId?: string | undefined; provider?: \"OpenAI\" | \"Azure OpenAI\" | undefined; model?: string | undefined; } | undefined; isDefault?: boolean | undefined; excludeFromLastConversationStorage?: boolean | undefined; }>" ], "path": "x-pack/packages/kbn-elastic-assistant-common/impl/schemas/conversations/crud_conversation_route.gen.ts", "deprecated": false, diff --git a/api_docs/kbn_elastic_assistant_common.mdx b/api_docs/kbn_elastic_assistant_common.mdx index 258444a1cf180..49ecc85a6523d 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-03-29 +date: 2024-04-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/elastic-assistant-common'] --- import kbnElasticAssistantCommonObj from './kbn_elastic_assistant_common.devdocs.json'; diff --git a/api_docs/kbn_es.mdx b/api_docs/kbn_es.mdx index 026ac67daa34a..6eebc7cf480a2 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-03-29 +date: 2024-04-02 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 83a3d6fbca2cb..b0719f8e6c918 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-03-29 +date: 2024-04-02 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 65c3d6e0edaba..5f205dfce335a 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-03-29 +date: 2024-04-02 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 19f7dbaa23f32..e02b0c5adee4d 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-03-29 +date: 2024-04-02 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 e7ffc8baf0f06..4be01e672aea5 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-03-29 +date: 2024-04-02 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 59d82b2603278..4ca234e0fd4cb 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-03-29 +date: 2024-04-02 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 76e4659f0b2f5..a0cc20bb35144 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-03-29 +date: 2024-04-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/esql-ast'] --- import kbnEsqlAstObj from './kbn_esql_ast.devdocs.json'; diff --git a/api_docs/kbn_esql_utils.mdx b/api_docs/kbn_esql_utils.mdx index 4bb287b77c240..0f47e8cdc9d24 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-03-29 +date: 2024-04-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/esql-utils'] --- import kbnEsqlUtilsObj from './kbn_esql_utils.devdocs.json'; diff --git a/api_docs/kbn_esql_validation_autocomplete.mdx b/api_docs/kbn_esql_validation_autocomplete.mdx index deb9394406e00..1a6912d109db1 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-03-29 +date: 2024-04-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/esql-validation-autocomplete'] --- import kbnEsqlValidationAutocompleteObj from './kbn_esql_validation_autocomplete.devdocs.json'; diff --git a/api_docs/kbn_event_annotation_common.mdx b/api_docs/kbn_event_annotation_common.mdx index d5ce0677b2808..14d9cfdc218f4 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-03-29 +date: 2024-04-02 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 adc7e93cadac7..0e8c59a9be708 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-03-29 +date: 2024-04-02 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 9da11c5b3f3a5..eecc038cda84a 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-03-29 +date: 2024-04-02 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 143350a0b3630..b457fb764c6dc 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-03-29 +date: 2024-04-02 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 63b35dbda0ce5..c9b04ef646ba1 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-03-29 +date: 2024-04-02 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 44e3a5c3b3570..b2ef36feb93bd 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-03-29 +date: 2024-04-02 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 2e4e6037a43b6..be6f54f010a4b 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-03-29 +date: 2024-04-02 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 d3c33ffeb72db..7a366195a90f3 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-03-29 +date: 2024-04-02 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 ffe61da264b73..74b9dad033d73 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-03-29 +date: 2024-04-02 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 6fdf03066fda9..249e4acd63253 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-03-29 +date: 2024-04-02 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 d49adc6ad477b..8b58637938255 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-03-29 +date: 2024-04-02 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 eaf0127ffeacb..18246e0386154 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-03-29 +date: 2024-04-02 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 8572a1be2bc4c..db69157609023 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-03-29 +date: 2024-04-02 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 6b059712df984..ec4748d5c1cbe 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-03-29 +date: 2024-04-02 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 45a7136b68f28..4a6b1ec03dad9 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-03-29 +date: 2024-04-02 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 78016c62abb4f..66fcc3f06de8c 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-03-29 +date: 2024-04-02 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 c708a853cfe41..01485c1a4211c 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-03-29 +date: 2024-04-02 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 d78894f634c05..ebb3c05b2db3e 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-03-29 +date: 2024-04-02 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 f08e15d189015..6428a6f5dc59f 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-03-29 +date: 2024-04-02 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 81c25766b7125..212b850a43ce6 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-03-29 +date: 2024-04-02 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 b1ba4eb3c848b..01f1f213d6e1c 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-03-29 +date: 2024-04-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/import-resolver'] --- import kbnImportResolverObj from './kbn_import_resolver.devdocs.json'; diff --git a/api_docs/kbn_infra_forge.mdx b/api_docs/kbn_infra_forge.mdx index e902392ed5d37..d4cf9bcc609e6 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-03-29 +date: 2024-04-02 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 1e7193988d229..d52e417f76f8d 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-03-29 +date: 2024-04-02 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 83c0689942fa4..1c368ce4491fb 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-03-29 +date: 2024-04-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/io-ts-utils'] --- import kbnIoTsUtilsObj from './kbn_io_ts_utils.devdocs.json'; diff --git a/api_docs/kbn_jest_serializers.mdx b/api_docs/kbn_jest_serializers.mdx index 7c17603c91d65..708585bffaa45 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-03-29 +date: 2024-04-02 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 2f6aa167be088..4d697f1c0d8bb 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-03-29 +date: 2024-04-02 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 1fffaeb0a3a60..a9c36cd833c76 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-03-29 +date: 2024-04-02 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 277b0e643ee5e..155d9ee0f1014 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-03-29 +date: 2024-04-02 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 bb3f109e05b71..1c2e8950db86e 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-03-29 +date: 2024-04-02 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 7a395c04126f9..4e03ec2c4062e 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-03-29 +date: 2024-04-02 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 7db23a911ab34..5572e3bcc4f0c 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-03-29 +date: 2024-04-02 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 f4986a6633231..2e77a9e4c1f31 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-03-29 +date: 2024-04-02 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 228345af9eef5..5cc97e832e7a4 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-03-29 +date: 2024-04-02 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 4de334317c012..f73eefca3de9c 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-03-29 +date: 2024-04-02 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 9c522d2d13d90..673ff84a2e25a 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-03-29 +date: 2024-04-02 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 02ac76d19acf8..7817f8022a422 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-03-29 +date: 2024-04-02 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 e17b91a1a0737..6d78e93568da3 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-03-29 +date: 2024-04-02 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 fa403f6e2b9ec..8a67a6dc2a9f8 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-03-29 +date: 2024-04-02 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 f626db1410ef1..cc429f2ca6b8d 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-03-29 +date: 2024-04-02 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 c7ca72277ea1d..fe808b3846645 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-03-29 +date: 2024-04-02 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 a9c44a633ba7a..30c0187bd4804 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-03-29 +date: 2024-04-02 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 c85a2636e8b37..9be409fc03feb 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-03-29 +date: 2024-04-02 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.mdx b/api_docs/kbn_management_settings_ids.mdx index 63f0b99a97d7a..47cd33618b43e 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-03-29 +date: 2024-04-02 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 29bbaeb8267f1..dc18e4057cab8 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-03-29 +date: 2024-04-02 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 00dcd80c2d370..5f2983bf9d87e 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-03-29 +date: 2024-04-02 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 0be92e5cba76e..fc24d3c36b031 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-03-29 +date: 2024-04-02 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 33ec85abb8354..66c8d3dea5c1b 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-03-29 +date: 2024-04-02 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 13c0574a260ac..3ae7e76d0556d 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-03-29 +date: 2024-04-02 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 6addd6d7bc628..59e944d86598a 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-03-29 +date: 2024-04-02 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 7f2d99f45f50d..79b7324e402af 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-03-29 +date: 2024-04-02 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 4d9f3a9b5996e..46bf1631c26cd 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-03-29 +date: 2024-04-02 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 50b6d939dc54f..f9c5864ff7f20 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-03-29 +date: 2024-04-02 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 f2ca259c97461..ac656394c1685 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-03-29 +date: 2024-04-02 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 de473ace41979..e46bedde403d3 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-03-29 +date: 2024-04-02 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 59d79a6087e75..7ee5e36494f5a 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-03-29 +date: 2024-04-02 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 0ca19166d42fb..e215e71e5b602 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-03-29 +date: 2024-04-02 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 d3c284e53cd77..e294cb2bb69d3 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-03-29 +date: 2024-04-02 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 498751633b267..4ddd098037f37 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-03-29 +date: 2024-04-02 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 1036cfa62f0a9..2f723510e76d0 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-03-29 +date: 2024-04-02 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 aeae5e381d810..aef0f7d34606c 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-03-29 +date: 2024-04-02 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 417f96ff83a7a..ce75f63499c75 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-03-29 +date: 2024-04-02 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 feb7e098aa1b0..3b15d10db4188 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-03-29 +date: 2024-04-02 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 0145841609c95..9fe2bca54d0ea 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-03-29 +date: 2024-04-02 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 76225c83c5398..531bb536f583d 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-03-29 +date: 2024-04-02 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 a0ecbb6415408..1a2a1ffd526e6 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-03-29 +date: 2024-04-02 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 3c5c70271e8fe..54614866cbf41 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-03-29 +date: 2024-04-02 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 972d112002cd4..327cf26ebcb0d 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-03-29 +date: 2024-04-02 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 eb7e3acab465e..6c559a31bb49c 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-03-29 +date: 2024-04-02 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 73fddd5824aa5..23a66c54fcc4b 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-03-29 +date: 2024-04-02 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 085d723f29dff..e66de132c3657 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-03-29 +date: 2024-04-02 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 fac62f8603c08..719d8daba32ec 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-03-29 +date: 2024-04-02 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 26396cd9bee93..19aaca5deff3a 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-03-29 +date: 2024-04-02 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 9140822730cef..2310b017d671d 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-03-29 +date: 2024-04-02 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 f6dc391d522d2..ef320acf6ea14 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-03-29 +date: 2024-04-02 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 6d09b96aae345..688f9871942b5 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-03-29 +date: 2024-04-02 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 61f3f83be0158..b03729a7edf75 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-03-29 +date: 2024-04-02 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 4d148ec403405..82bb8f512da6d 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-03-29 +date: 2024-04-02 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 417990d5b1b25..5e167afbbb4fb 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-03-29 +date: 2024-04-02 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 650ed91727255..f354086da59fd 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-03-29 +date: 2024-04-02 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 f8b645f1ae2de..54f45a496b4d6 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-03-29 +date: 2024-04-02 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 22f6c7b04f6f1..ecde82e5f8823 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-03-29 +date: 2024-04-02 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 bd1d7cac6e199..03233e6c74a42 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-03-29 +date: 2024-04-02 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 d95f1d842c0e9..448ce7adc05d0 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-03-29 +date: 2024-04-02 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 a701cf1e9fe38..4596ff03f344f 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-03-29 +date: 2024-04-02 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 098957ad57364..f02fae6bdb47b 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-03-29 +date: 2024-04-02 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 de2473167abfa..a77eeb5e7a4c7 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-03-29 +date: 2024-04-02 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 211b7e2594431..913216f083529 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-03-29 +date: 2024-04-02 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 a4a8e34aa92e9..87fadce2b3b01 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-03-29 +date: 2024-04-02 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 1f33e7d7e0d6a..69ad8c806c4eb 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-03-29 +date: 2024-04-02 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 5d6a0f29e0a4d..04e16baf5a7d4 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-03-29 +date: 2024-04-02 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 e1d92ee551395..126717e6015ae 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-03-29 +date: 2024-04-02 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 a8e6132a708f7..fff6ffeb4c7c3 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-03-29 +date: 2024-04-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/presentation-containers'] --- import kbnPresentationContainersObj from './kbn_presentation_containers.devdocs.json'; diff --git a/api_docs/kbn_presentation_publishing.mdx b/api_docs/kbn_presentation_publishing.mdx index 856b4304b9886..6532382c78090 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-03-29 +date: 2024-04-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/presentation-publishing'] --- import kbnPresentationPublishingObj from './kbn_presentation_publishing.devdocs.json'; diff --git a/api_docs/kbn_profiling_utils.mdx b/api_docs/kbn_profiling_utils.mdx index b3aac98fffebf..4c400274d9028 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-03-29 +date: 2024-04-02 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 28a7a855f892b..5602b96c43c80 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-03-29 +date: 2024-04-02 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 ba61d523ec6c0..a0ce16844256b 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-03-29 +date: 2024-04-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/react-field'] --- import kbnReactFieldObj from './kbn_react_field.devdocs.json'; diff --git a/api_docs/kbn_react_kibana_context_common.mdx b/api_docs/kbn_react_kibana_context_common.mdx index ddcedcc28ebd0..a6343f11c862b 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-03-29 +date: 2024-04-02 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 c049aad3dd124..4e0ae5db70eae 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-03-29 +date: 2024-04-02 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 b92e649a10b77..af49a4b63dc12 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-03-29 +date: 2024-04-02 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 0a0acc30df3ac..d36f9b0a84ba4 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-03-29 +date: 2024-04-02 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 5691f74a34c0a..b21de9cb3c7bb 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-03-29 +date: 2024-04-02 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 321dbcd415da0..95e7df92e57ac 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-03-29 +date: 2024-04-02 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 5ebc56bc66de1..2ea696afb9fcf 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-03-29 +date: 2024-04-02 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 e08bf5e0fce6f..6cdc8e048fd2f 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-03-29 +date: 2024-04-02 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 b88aa2e716fba..086333d35a9d3 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-03-29 +date: 2024-04-02 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 d0e4815842de7..c9637922522c7 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-03-29 +date: 2024-04-02 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 f43294e1eedca..f23a3e9830db5 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-03-29 +date: 2024-04-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/reporting-common'] --- import kbnReportingCommonObj from './kbn_reporting_common.devdocs.json'; diff --git a/api_docs/kbn_reporting_export_types_csv.mdx b/api_docs/kbn_reporting_export_types_csv.mdx index 7bd88f1e47956..d45301324491c 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-03-29 +date: 2024-04-02 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 454f06d3a862f..c104a3bbf50fe 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-03-29 +date: 2024-04-02 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 92de1212470c9..f80dd0a9233f4 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-03-29 +date: 2024-04-02 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 7334877692278..df0ea10d9faf8 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-03-29 +date: 2024-04-02 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 e3c9d748f430f..70a98797808dc 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-03-29 +date: 2024-04-02 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 443fbd84e32a4..997b525b416cc 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-03-29 +date: 2024-04-02 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 f61e40120baeb..be36f128c4db7 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-03-29 +date: 2024-04-02 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 7773f7cb13a44..110925c133e7a 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-03-29 +date: 2024-04-02 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 2b49e8dae5409..ce284d7515560 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-03-29 +date: 2024-04-02 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 eeb6604a7ce60..8a6a2fcc21766 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-03-29 +date: 2024-04-02 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 df0da95d0fd35..f8ab843a68f21 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-03-29 +date: 2024-04-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/rison'] --- import kbnRisonObj from './kbn_rison.devdocs.json'; diff --git a/api_docs/kbn_router_utils.mdx b/api_docs/kbn_router_utils.mdx index 698c2d476f204..1de0be79028f9 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-03-29 +date: 2024-04-02 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 f1263df0b162a..74cf987b2e0c3 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-03-29 +date: 2024-04-02 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 3b1f012b47e08..7927cd09a380f 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-03-29 +date: 2024-04-02 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 ad99162609e5b..37c8a4eaabea1 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-03-29 +date: 2024-04-02 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 26fb29cfff663..3afc13a3036f1 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-03-29 +date: 2024-04-02 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.mdx b/api_docs/kbn_search_connectors.mdx index 9396e1507cc60..0843af0b3c71a 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-03-29 +date: 2024-04-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/search-connectors'] --- import kbnSearchConnectorsObj from './kbn_search_connectors.devdocs.json'; diff --git a/api_docs/kbn_search_errors.mdx b/api_docs/kbn_search_errors.mdx index 835b41573c964..e5dbdf656a05a 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-03-29 +date: 2024-04-02 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 4640096f2c69a..4d9a6602950bb 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-03-29 +date: 2024-04-02 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 773fb080d4b83..e596aa3053f45 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-03-29 +date: 2024-04-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/search-response-warnings'] --- import kbnSearchResponseWarningsObj from './kbn_search_response_warnings.devdocs.json'; diff --git a/api_docs/kbn_security_hardening.mdx b/api_docs/kbn_security_hardening.mdx index ad27c3c0e9fcd..f50608af21b8e 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-03-29 +date: 2024-04-02 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 c01ec2d480336..b2e3d56688a48 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-03-29 +date: 2024-04-02 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 5507ec6e1db8d..ca0358a1a0c60 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-03-29 +date: 2024-04-02 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 6968013e40da5..01a6cf2bdcf60 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-03-29 +date: 2024-04-02 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 acfacfa32adc2..13edf258d5274 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-03-29 +date: 2024-04-02 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 04f81f15b7d9a..54994728f3fad 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-03-29 +date: 2024-04-02 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 9224eaff03983..c8448b166e65c 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-03-29 +date: 2024-04-02 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 8b09f78abbd35..543e9d4735c09 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-03-29 +date: 2024-04-02 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 a7d7526b93ef2..4ee8ec10ff6a6 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-03-29 +date: 2024-04-02 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 af5e86f2c2551..d61774060998e 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-03-29 +date: 2024-04-02 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 17da2b9956405..125b22ccc9e88 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-03-29 +date: 2024-04-02 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 c0843405b1ba3..9af72bada8f52 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-03-29 +date: 2024-04-02 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 bee9f754a28f7..f848a2accd53b 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-03-29 +date: 2024-04-02 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 aceb7111aa61a..0553103f321e3 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-03-29 +date: 2024-04-02 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 64c47edb97883..db3f552244617 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-03-29 +date: 2024-04-02 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 e866ab3a29d5c..114108e2323ce 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-03-29 +date: 2024-04-02 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 c52ae9f83c9b0..516b9721493a5 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-03-29 +date: 2024-04-02 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 2e984346cbc3a..8b3cea7f34fcb 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-03-29 +date: 2024-04-02 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 3a72659e7e3f6..84230567ddfaa 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-03-29 +date: 2024-04-02 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 1aab80eef434c..a663d8dbc96f0 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-03-29 +date: 2024-04-02 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 8c775e960f484..b95a38657119b 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-03-29 +date: 2024-04-02 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 09bdaaadc7913..6ea474048ece1 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-03-29 +date: 2024-04-02 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 e860a2f010ad9..8e4e78ee49449 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-03-29 +date: 2024-04-02 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 eb4045a5e9809..d83903c99d5fd 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-03-29 +date: 2024-04-02 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 000daa3061c56..13ae7af46dc46 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-03-29 +date: 2024-04-02 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 f4a755aff5e7c..079b40038a949 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-03-29 +date: 2024-04-02 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 6f54b608e9442..5c65739857307 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-03-29 +date: 2024-04-02 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 46dbb5e64880d..ad3805505e259 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-03-29 +date: 2024-04-02 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 81ff8a2114b55..288d99c7dbe97 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-03-29 +date: 2024-04-02 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 d7738df3f83a9..e641e18b44cc7 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-03-29 +date: 2024-04-02 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 6f2afb52a93ed..968c3b188defc 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-03-29 +date: 2024-04-02 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 cd669f204dd2e..03f9b29dd06fe 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-03-29 +date: 2024-04-02 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 18eab47d5fa2e..1a10d69a58b40 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-03-29 +date: 2024-04-02 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 605b2d430f4e2..f13b13f950f56 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-03-29 +date: 2024-04-02 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 87868038bd1bb..e996bd19a02c3 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-03-29 +date: 2024-04-02 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 c7d03006b1db1..0898e469705e5 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-03-29 +date: 2024-04-02 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 be8d8d498ecd4..63433ad9fb135 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-03-29 +date: 2024-04-02 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 80bf5493b2ef9..7b9ef77a7d420 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-03-29 +date: 2024-04-02 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 685189ccdc070..c49bbfdffeecc 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-03-29 +date: 2024-04-02 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 898836761ab1a..1fb664693ca42 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-03-29 +date: 2024-04-02 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 9ac714115c7bc..a76bb2809299d 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-03-29 +date: 2024-04-02 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 0677ff219873b..7af62e4966a69 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-03-29 +date: 2024-04-02 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 6b7dc026e46c9..c4cc138480627 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-03-29 +date: 2024-04-02 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 a78de940dfae5..bf6db16aed3f6 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-03-29 +date: 2024-04-02 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 3cf56db1d1d5d..96642c9c5cd70 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-03-29 +date: 2024-04-02 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 4bce6ee7a43e5..93aa8353ab9db 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-03-29 +date: 2024-04-02 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 5e4e25e10e4c0..62fb9e17ed72e 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-03-29 +date: 2024-04-02 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 a41f99b502e2c..6ead15a8dd872 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-03-29 +date: 2024-04-02 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 c4f6cc1f48cb0..e606abb757165 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-03-29 +date: 2024-04-02 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 a7033a2aa7db6..4d7ebc7109d9c 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-03-29 +date: 2024-04-02 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 4b533a646b52d..c871d3df3e0c3 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-03-29 +date: 2024-04-02 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 d2c186cc6a266..1d3bbac6939c1 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-03-29 +date: 2024-04-02 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 167e2d6b2fb5f..d6a65907773ed 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-03-29 +date: 2024-04-02 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 97acd66d1fcaf..5588cc51f7e50 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-03-29 +date: 2024-04-02 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 9322ce61cb092..57ce5a4e2323a 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-03-29 +date: 2024-04-02 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 ab8503fe2344c..2bd10f862ef2f 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-03-29 +date: 2024-04-02 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 48eee1fee8040..0c9844366fe6f 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-03-29 +date: 2024-04-02 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 7d1a649ba68ff..59b2583c69566 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-03-29 +date: 2024-04-02 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 b05ebb4091ffb..5eb9e64b760e1 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-03-29 +date: 2024-04-02 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 abc7f852ca4d5..d440f62ddf3d7 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-03-29 +date: 2024-04-02 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 ac6eb73b5faac..3c20b5e057feb 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-03-29 +date: 2024-04-02 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 98abe398caa8c..7beb459daa2b2 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-03-29 +date: 2024-04-02 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 339c354e3fd78..75714bcab715e 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-03-29 +date: 2024-04-02 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 0d0e47546414a..a6b7973622eb8 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-03-29 +date: 2024-04-02 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 4dde53b3aed8e..de3ecc53bc073 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-03-29 +date: 2024-04-02 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 09770f0a66fbb..f10462cb10dec 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-03-29 +date: 2024-04-02 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 692664014c25a..f3fcd52a19927 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-03-29 +date: 2024-04-02 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 60c7107289806..f3e0ba1ecbc84 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-03-29 +date: 2024-04-02 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 9f794eae23f95..4699317cad4f4 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-03-29 +date: 2024-04-02 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 c5b0c26e228bb..02701f3b79b18 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-03-29 +date: 2024-04-02 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 3eca9e332349d..b05546c3b9d22 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-03-29 +date: 2024-04-02 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 250b7d5d692da..0c7f2323639b5 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-03-29 +date: 2024-04-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-storybook-mock'] --- import kbnSharedUxStorybookMockObj from './kbn_shared_ux_storybook_mock.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_utility.mdx b/api_docs/kbn_shared_ux_utility.mdx index ff954dd87d100..372a5823dff3c 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-03-29 +date: 2024-04-02 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 44a74e780c84e..6237a7baea8ea 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-03-29 +date: 2024-04-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/slo-schema'] --- import kbnSloSchemaObj from './kbn_slo_schema.devdocs.json'; diff --git a/api_docs/kbn_solution_nav_es.mdx b/api_docs/kbn_solution_nav_es.mdx index 305a8bb7af84e..8a9e503c7da76 100644 --- a/api_docs/kbn_solution_nav_es.mdx +++ b/api_docs/kbn_solution_nav_es.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-solution-nav-es title: "@kbn/solution-nav-es" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/solution-nav-es plugin -date: 2024-03-29 +date: 2024-04-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/solution-nav-es'] --- import kbnSolutionNavEsObj from './kbn_solution_nav_es.devdocs.json'; diff --git a/api_docs/kbn_solution_nav_oblt.mdx b/api_docs/kbn_solution_nav_oblt.mdx index bb20df9de486d..76bec16d0178e 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-03-29 +date: 2024-04-02 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 f2a27980e8fea..381516ba9b141 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-03-29 +date: 2024-04-02 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 af4b35e0d3c4c..3cc21d68f6727 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-03-29 +date: 2024-04-02 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 d7c646713b30a..3c0513fb02a97 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-03-29 +date: 2024-04-02 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 6cf2e7a04a576..7d73f384f8290 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-03-29 +date: 2024-04-02 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 378f44b7b4cbb..78a182852b0d1 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-03-29 +date: 2024-04-02 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 eb392ff8e157c..1e1ca8754fe6a 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-03-29 +date: 2024-04-02 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 74d323410cd39..847f5198b4ebb 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-03-29 +date: 2024-04-02 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 ea58b6848ca2f..d1e0edda9f334 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-03-29 +date: 2024-04-02 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 84dede14392d5..92a2ef35baaf7 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-03-29 +date: 2024-04-02 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 72b957109ab9c..801a226ac8eb8 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-03-29 +date: 2024-04-02 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 4584a049c28af..48126d4cb3a46 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-03-29 +date: 2024-04-02 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 867a5d596d72f..892d6ef93d294 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-03-29 +date: 2024-04-02 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 65c5ff18c425f..994f1d60383da 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-03-29 +date: 2024-04-02 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 f489eef1fe3bd..c0305e6fa6503 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-03-29 +date: 2024-04-02 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_ts_projects.mdx b/api_docs/kbn_ts_projects.mdx index e21530a9aaaac..fa6080a60d37c 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-03-29 +date: 2024-04-02 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 62c8f6fcfad2c..50ff0ffeb735d 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-03-29 +date: 2024-04-02 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 75c386c5d91c5..d25376203d276 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-03-29 +date: 2024-04-02 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 8937013504322..a6bd3bcdbd6e4 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-03-29 +date: 2024-04-02 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 fd7ed3208ce4d..4b8abcb145176 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-03-29 +date: 2024-04-02 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 22a65530aa35f..c8ce82116df68 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-03-29 +date: 2024-04-02 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 f61afe5c137b7..9ba36a49b92c2 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-03-29 +date: 2024-04-02 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 7c3ee91d23955..b54be9ca0cf8b 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-03-29 +date: 2024-04-02 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 aba5dae93704c..879dc066d2104 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-03-29 +date: 2024-04-02 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 5c629e002c8c1..b0de235121704 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-03-29 +date: 2024-04-02 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 49c93c1193b29..655bf70bed690 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-03-29 +date: 2024-04-02 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 01f74c839fe40..f38b3c32d6be4 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-03-29 +date: 2024-04-02 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 57ae909df16a5..f7e6680d815a4 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-03-29 +date: 2024-04-02 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 bf796edfd9e48..38f95dea57162 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-03-29 +date: 2024-04-02 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 d1a1ecc55d35b..d3ed13f168995 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-03-29 +date: 2024-04-02 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 57cef96f42496..b78061f7e126c 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-03-29 +date: 2024-04-02 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 0dceb49794f55..b193a7b175f2d 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-03-29 +date: 2024-04-02 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 a96d1a53d8d6c..c863952c82899 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-03-29 +date: 2024-04-02 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 e003ce1f0c7af..a40c4d5a41b5d 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-03-29 +date: 2024-04-02 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 15c909dc0dca3..e307bf96a34c1 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-03-29 +date: 2024-04-02 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 ca8e1b516f5d5..c1fbc1ab54492 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-03-29 +date: 2024-04-02 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 1fc73205d18b7..3657531db5d20 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-03-29 +date: 2024-04-02 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 511e03a2641e5..c238db055bc87 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-03-29 +date: 2024-04-02 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 02352d791c3c3..477ec9f3fc2ba 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-03-29 +date: 2024-04-02 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 3c17dab01bbfc..94da93edd5fb3 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-03-29 +date: 2024-04-02 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 022f251802022..c55c86345ddc7 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-03-29 +date: 2024-04-02 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 5d865e0e1a851..f9e7e9fb31155 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-03-29 +date: 2024-04-02 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 40f3be4a9de1e..8d7ca8da2e691 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-03-29 +date: 2024-04-02 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 7944fe45759f2..5d9a2aa51a415 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-03-29 +date: 2024-04-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'lists'] --- import listsObj from './lists.devdocs.json'; diff --git a/api_docs/logs_explorer.mdx b/api_docs/logs_explorer.mdx index 5f10fe1d8ebb1..cff63b095dfce 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-03-29 +date: 2024-04-02 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 d97d5881f1bd9..125df55983aac 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-03-29 +date: 2024-04-02 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 f5a6026ee2e9e..5435d6c1ccb47 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-03-29 +date: 2024-04-02 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 606c70cd8e101..119cc08cbc22a 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-03-29 +date: 2024-04-02 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 ec1678c8f9dd9..4f153382eef80 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-03-29 +date: 2024-04-02 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 4aa051e760e51..568f735838113 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-03-29 +date: 2024-04-02 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 d48e110c9e08f..56eac3cd2ef35 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-03-29 +date: 2024-04-02 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 3e80758a9247f..2662e0506bedf 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-03-29 +date: 2024-04-02 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 9d804eed7eebc..8e806fbfd51c0 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-03-29 +date: 2024-04-02 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 3b98fe3f9896e..9513c33f3e127 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-03-29 +date: 2024-04-02 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 0e365ece4d398..91c779771b3b7 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-03-29 +date: 2024-04-02 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 833d6b33410a5..8ccded382f576 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-03-29 +date: 2024-04-02 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 483d69cbd5a5b..db17be1fadf09 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-03-29 +date: 2024-04-02 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 85e8c5501cbea..f9b30df50b64e 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-03-29 +date: 2024-04-02 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 cd091620f94ec..3c953ba36d46a 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-03-29 +date: 2024-04-02 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 d57958afb8dcf..6bd7979b14afe 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-03-29 +date: 2024-04-02 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 137287e8342db..20ba59101f826 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-03-29 +date: 2024-04-02 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 6cf655311df36..654a8b3790cf3 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-03-29 +date: 2024-04-02 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 8b527c2370323..4e8a48bef96ad 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-03-29 +date: 2024-04-02 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 fcf1d521a9735..feff371f0b596 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-03-29 +date: 2024-04-02 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 9133e8863e98d..92b7f457d1a2d 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-03-29 +date: 2024-04-02 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 d17b7cf232527..8e775c5a6b755 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-03-29 +date: 2024-04-02 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 fafff903d554b..87b45093501cf 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-03-29 +date: 2024-04-02 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 1cfb45d393208..16cee9ed0a5d9 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-03-29 +date: 2024-04-02 tags: ['contributor', 'dev', 'apidocs', 'kibana'] --- @@ -21,7 +21,7 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana'] | API Count | Any Count | Missing comments | Missing exports | |--------------|----------|-----------------|--------| -| 46667 | 234 | 35406 | 1805 | +| 46666 | 234 | 35405 | 1805 | ## Plugin Directory @@ -169,7 +169,7 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana'] | | [@elastic/enterprise-search-frontend](https://github.com/orgs/elastic/teams/enterprise-search-frontend) | - | 15 | 0 | 9 | 0 | | searchprofiler | [@elastic/kibana-management](https://github.com/orgs/elastic/teams/kibana-management) | - | 0 | 0 | 0 | 0 | | | [@elastic/kibana-security](https://github.com/orgs/elastic/teams/kibana-security) | This plugin provides authentication and authorization features, and exposes functionality to understand the capabilities of the currently authenticated user. | 409 | 0 | 199 | 2 | -| | [@elastic/security-solution](https://github.com/orgs/elastic/teams/security-solution) | - | 194 | 0 | 123 | 37 | +| | [@elastic/security-solution](https://github.com/orgs/elastic/teams/security-solution) | - | 193 | 0 | 122 | 37 | | | [@elastic/security-solution](https://github.com/orgs/elastic/teams/security-solution) | ESS customizations for Security Solution. | 6 | 0 | 6 | 0 | | | [@elastic/security-solution](https://github.com/orgs/elastic/teams/security-solution) | Serverless customizations for security. | 7 | 0 | 7 | 0 | | | [@elastic/appex-sharedux](https://github.com/orgs/elastic/teams/appex-sharedux) | The core Serverless plugin, providing APIs to Serverless Project plugins. | 24 | 0 | 23 | 0 | diff --git a/api_docs/presentation_panel.mdx b/api_docs/presentation_panel.mdx index 129ed69a35d49..f8c688cd3ff6a 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-03-29 +date: 2024-04-02 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 73ceb7379bef2..e08af41e82e1d 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-03-29 +date: 2024-04-02 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 cf83d79db032e..5df64c5ac2ec0 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-03-29 +date: 2024-04-02 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 cd67767164053..3f223b2f66d88 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-03-29 +date: 2024-04-02 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 7987174da6329..1ba52c74a7290 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-03-29 +date: 2024-04-02 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 d6e356bf14265..5defdd714bbbe 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-03-29 +date: 2024-04-02 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 7e86ac6f08469..6dbd6f57dbc28 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-03-29 +date: 2024-04-02 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 277395a649900..9f64fd578ec2b 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-03-29 +date: 2024-04-02 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 0a62efb910e8c..c470f2dfc6c92 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-03-29 +date: 2024-04-02 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 492d26beb221e..69fdaf5ec462c 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-03-29 +date: 2024-04-02 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 2c6aa1002338b..b4b4cea15f613 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-03-29 +date: 2024-04-02 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 da7919dd3ef0e..fd9f46123c6b7 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-03-29 +date: 2024-04-02 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 557f49cf3d823..cd2463deefe35 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-03-29 +date: 2024-04-02 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 9e26b44e020ac..bff365d7354b6 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-03-29 +date: 2024-04-02 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 ee206edd3af39..0b08774235652 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-03-29 +date: 2024-04-02 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 03dd047868869..81faf6f03d068 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-03-29 +date: 2024-04-02 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 b044889d3c0af..32caa21f0c0d9 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-03-29 +date: 2024-04-02 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 c2af7d4f6cdfd..e00f2149f433d 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-03-29 +date: 2024-04-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'searchConnectors'] --- import searchConnectorsObj from './search_connectors.devdocs.json'; diff --git a/api_docs/search_playground.mdx b/api_docs/search_playground.mdx index 229fb76e46e17..4b3538c984a6d 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-03-29 +date: 2024-04-02 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 a00a6b3406485..e746f1014e34b 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-03-29 +date: 2024-04-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'security'] --- import securityObj from './security.devdocs.json'; diff --git a/api_docs/security_solution.devdocs.json b/api_docs/security_solution.devdocs.json index c9983973a0249..d04dac1135e95 100644 --- a/api_docs/security_solution.devdocs.json +++ b/api_docs/security_solution.devdocs.json @@ -1830,20 +1830,6 @@ "deprecated": false, "trackAdoption": false }, - { - "parentPluginId": "securitySolution", - "id": "def-public.TimelineModel.isDiscoverSavedSearchLoaded", - "type": "CompoundType", - "tags": [], - "label": "isDiscoverSavedSearchLoaded", - "description": [], - "signature": [ - "boolean | undefined" - ], - "path": "x-pack/plugins/security_solution/public/timelines/store/model.ts", - "deprecated": false, - "trackAdoption": false - }, { "parentPluginId": "securitySolution", "id": "def-public.TimelineModel.isDataProviderVisible", diff --git a/api_docs/security_solution.mdx b/api_docs/security_solution.mdx index 918613efe2ac5..e2dea3c81f6a9 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-03-29 +date: 2024-04-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'securitySolution'] --- import securitySolutionObj from './security_solution.devdocs.json'; @@ -21,7 +21,7 @@ Contact [@elastic/security-solution](https://github.com/orgs/elastic/teams/secur | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 194 | 0 | 123 | 37 | +| 193 | 0 | 122 | 37 | ## Client diff --git a/api_docs/security_solution_ess.mdx b/api_docs/security_solution_ess.mdx index 00978b41dee5a..a6e7a006837fd 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-03-29 +date: 2024-04-02 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 d17543719f0f2..2339aa40092d5 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-03-29 +date: 2024-04-02 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 e593b2fcb5131..6713a5639bc72 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-03-29 +date: 2024-04-02 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 1bbe3fac9fc9e..e25377570affe 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-03-29 +date: 2024-04-02 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 71ce04e5200a9..b8847d38df1c8 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-03-29 +date: 2024-04-02 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 4dbc067939160..f707c1b505d82 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-03-29 +date: 2024-04-02 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 0865926d5cac3..4dee33b09de2e 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-03-29 +date: 2024-04-02 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 9e1425872f4ac..7c6758ae066ec 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-03-29 +date: 2024-04-02 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 e3b9889608461..d0ffe81f62e6b 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-03-29 +date: 2024-04-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'snapshotRestore'] --- import snapshotRestoreObj from './snapshot_restore.devdocs.json'; diff --git a/api_docs/spaces.mdx b/api_docs/spaces.mdx index 80bae240ddb35..61c1d316c4e92 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-03-29 +date: 2024-04-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'spaces'] --- import spacesObj from './spaces.devdocs.json'; diff --git a/api_docs/stack_alerts.mdx b/api_docs/stack_alerts.mdx index 2386bec886d23..10150e168835e 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-03-29 +date: 2024-04-02 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 9f964267f8d99..30c28e676e146 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-03-29 +date: 2024-04-02 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 554e19b9662c7..03dd808fed98f 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-03-29 +date: 2024-04-02 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 197cd06819d98..885cdcd1e9482 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-03-29 +date: 2024-04-02 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 b630a0ec59805..84838b45657de 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-03-29 +date: 2024-04-02 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 b0b44cbb63b94..e5f6d7943fa63 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-03-29 +date: 2024-04-02 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 9903b407c5b64..d13eafb1b365f 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-03-29 +date: 2024-04-02 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 2f069198c0e94..d04d44112a37b 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-03-29 +date: 2024-04-02 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 e6e2510d5da65..990974687d8a0 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-03-29 +date: 2024-04-02 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 20442e3f2a2f2..d4e11801e8a48 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-03-29 +date: 2024-04-02 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 e3bac1df2e580..29059348a0f63 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-03-29 +date: 2024-04-02 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 45e967dd229d7..20ba448185ebf 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-03-29 +date: 2024-04-02 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 85d434e845f28..e5027ca4df8f2 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-03-29 +date: 2024-04-02 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 9415ffa3456d5..bfa2929a294af 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-03-29 +date: 2024-04-02 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 b9a8c4f9fd0cd..54d9c7e994303 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-03-29 +date: 2024-04-02 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 fed935c610bd6..c9c0d08a9e88a 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-03-29 +date: 2024-04-02 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 070465a3c649a..3f37f5681cd00 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-03-29 +date: 2024-04-02 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 65fbaa5212e0f..dea847eb9bd62 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-03-29 +date: 2024-04-02 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 8195d510ae1fd..2f7a17cbcf4f2 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-03-29 +date: 2024-04-02 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 c470a75a47fd6..2ceeb00dcbf04 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-03-29 +date: 2024-04-02 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 e412dcc259c02..f649a7886a556 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-03-29 +date: 2024-04-02 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 b459d56222f15..8a66d48b2dc43 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-03-29 +date: 2024-04-02 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 bd1e3ea97787c..317ec35c86657 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-03-29 +date: 2024-04-02 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 8f9891764e3ad..4e552264fc8fe 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-03-29 +date: 2024-04-02 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 70d807bb4a7e5..6761588d5b7e8 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-03-29 +date: 2024-04-02 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 b36c788a5eb06..b5302b9064617 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-03-29 +date: 2024-04-02 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 45c9cc4591a6c..c977545cc5cf2 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-03-29 +date: 2024-04-02 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 3fee3e8509577..15b6f49530bef 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-03-29 +date: 2024-04-02 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 76953d91d5ed4..5e1a8b4101ebc 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-03-29 +date: 2024-04-02 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 7fb41e0dacc33..fc26696b0c411 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-03-29 +date: 2024-04-02 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 25a0a15cc758a..2c71119096a7d 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-03-29 +date: 2024-04-02 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 2e93e8c70ff7f..e47c3fb59cfeb 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-03-29 +date: 2024-04-02 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 c826c27eb4e4b..93bb6ef76ef0e 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-03-29 +date: 2024-04-02 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'visualizations'] --- import visualizationsObj from './visualizations.devdocs.json'; diff --git a/docs/CHANGELOG.asciidoc b/docs/CHANGELOG.asciidoc index db9993184b26a..0ff00622c7cc7 100644 --- a/docs/CHANGELOG.asciidoc +++ b/docs/CHANGELOG.asciidoc @@ -10,6 +10,7 @@ Review important information about the {kib} 8.x releases. +* <> * <> * <> * <> @@ -61,6 +62,37 @@ Review important information about the {kib} 8.x releases. * <> -- + +[[release-notes-8.13.1]] +== {kib} 8.13.1 + +The 8.13.1 release includes the following bug fixes. + +[float] +[[enhancement-v8.13.1]] +=== Enhancements +Fleet:: +* Remove `index.query.default_field` setting from managed component template settings ({kibana-pull}178020[#178020]). + +[float] +[[fixes-v8.13.1]] +=== Bug Fixes +Alerting:: +* Limit useEffect to calculate view in app URL ({kibana-pull}179197[#179197]). +Data Discovery:: +* Hide "Save"/"Save as" actions from "Unsaved changes" badge for read-only users ({kibana-pull}179132[#179132]). +Elastic Security:: +For the Elastic Security 8.13.1 release information, refer to {security-guide}/release-notes.html[_Elastic Security Solution Release Notes_]. +Fleet:: +* Use index exists check in fleet-metrics-task ({kibana-pull}179404[#179404]). +Lens & Visualizations:: +* Fixes wilcard complex scenarios ({kibana-pull}178938[#178938]). +Machine Learning:: +* AIOps: Fix text field candidate selection for log rate analysis ({kibana-pull}179699[#179699]). +Observability:: +* Fall back to top 5 docs on scoring error ({kibana-pull}179615[#179615]). +* Fixing APM data view id ({kibana-pull}179257[#179257]). + [[release-notes-8.13.0]] == {kib} 8.13.0 diff --git a/docs/developer/architecture/development/csv-integration.asciidoc b/docs/developer/architecture/development/csv-integration.asciidoc index 45b4e50e06240..1d659f611f04b 100644 --- a/docs/developer/architecture/development/csv-integration.asciidoc +++ b/docs/developer/architecture/development/csv-integration.asciidoc @@ -2,50 +2,43 @@ === CSV [float] -==== Job parameters -If integrating with Reporting via a custom UI, the following job parameters must be Rison encoded and posted to -the aforementioned generate job url: +==== Job parameters of CsvSearchSource +The export type to generate CSV reports and is available in Discover uses "search source" objects. This export type is called +`csv_searchsource` in the code. A configuration for a CSV report job is represented with an interface that includes the +`BaseParams` and the following fields. To create a request for a CSV report, these required job parameters are Rison encoded into +a query string variable of the report generation URL: ---- -interface jobParameters { - type: string; - title: string; - searchRequest: { - index: string; - body: object; - }; - fields: string[]; - metaFields: string[]; - conflictedTypesFields: string[]; - indexPatternId: string; -} +interface JobParamsCSV { + searchSource: SerializedSearchSourceFields; <1> + columns?: string[]; <2> +}; ---- - -The `searchRequest.body` should abide by the {ref}/search-search.html[Elasticsearch Search Request Body] syntax +<1> An object of serialized data that internally represents a search object in Kibana. It will contain a reference to a DataView +saved object. +<2> An array of field names to include as columns in the CSV report. [float] -==== `export-config` Directive -If integrating with Reporting via the `export-config` directive, the AngularJS controller that contains the directive should expose -the following methods and the `export-config` directive will POST them to the reporting API: +==== Job parameters of CsvFromSavedObject +A newer export type to generate CSV reports is available, currently only by API. This export type is called `csv_v2` in the code. ---- -interface sharingData { - searchRequest: { - index: string; - body: object; - }; - fields: string[]; - metaFields: string[]; - conflictedTypesFields: string[]; - indexPatternId: string; -} - -function getSharingData(): sharingData; - -function getSharingType(): string; +interface JobParamsCsvFromSavedObject { + locatorParams: LocatorParams[]; <1> +}; +---- +<1> The `locatorParams` value is controlled by the Discover application and identifies a search loaded in Discover, including the +selection of DataView, columns and filters. Only a single value in the array is permitted in the `createJob` method. -function getSharingTitle() string; +[float] +==== Job payload +After the job parameters are received by the route handler for the report generation URL, an additional field is automatically +added to the fields from job parameters: ---- - -The `sharingData.searchRequest.body` should abide by the {ref}/search-search.html[{es} Search Request Body] syntax \ No newline at end of file +interface TaskPayloadCSV { + pagingStrategy: 'scan' | 'pit' <1> +} +---- +<1> The `pagingStrategy` value is taken from the value of the `xpack.reporting.csv.scroll.strategy` setting in kibana.yml and used +to control how the `runTask` method pages through all of the data. diff --git a/docs/developer/architecture/development/index.asciidoc b/docs/developer/architecture/development/index.asciidoc index 4e86c803bd82d..87f091ce3265d 100644 --- a/docs/developer/architecture/development/index.asciidoc +++ b/docs/developer/architecture/development/index.asciidoc @@ -1,11 +1,8 @@ [role="xpack"] [[reporting-integration]] == Reporting integration -Integrating a {kib} application with the {report-features} requires a minimum -amount of code, and the goal is to not have to modify the reporting code as we -add additional applications. Instead, applications abide by a contract that -{report-features} use to determine the information that is required to export -CSVs and PDFs. +Applications abide by a contract that {report-features} use to determine the information that is required to request exports of +data from {kib}, and how to generate and store the reports. [IMPORTANT] ============================================== @@ -14,16 +11,56 @@ However, these docs will be kept up-to-date to reflect the current implementatio ============================================== [float] -[[reporting-nav-bar-extensions]] +=== Reporting Export Types +"Export Types" are pieces of code that plug into the {kib} Reporting framework, and are responsible for exporting data on behalf +of a {kib} application. These pieces of code are implemented as TypeScript classes that extend an abstract base class, and +implement methods for controlling the creation of report jobs, and asynchronously generating report contents. Their `createJob` +methods handle requests to create report jobs, by accepting jobParams objects and returning "task payload" objects. Their +`runTask` methods generate the report contents by accepting the task payload object created from the `createJob` function, which +is then stored in a system index in Elasticsearch. + +[float] +[[reporting-share-service-registrations]] === Share menu extensions -X-Pack uses the `share` plugin of the Kibana platform to register actions in the share menu. +X-Pack services, such as the {report-features}, register with the `share` plugin of the Kibana platform to register additional +actions available to make content shareable. + +[float] +=== Generate a report job URL +To generate a new reporting job, different export types require different `jobParams` objects, which are Rison-encoded and used as +a `jobParams` query string variable in the Reporting generation endpoint URL. If you use the aforementioned +<> then this detail will be abstracted away. If your +application does not use the Share menu extensions, you will have to generate the URL and create a POST request to the URL. [float] -=== Generate job URL -To generate a new reporting job, different export types require different `jobParams` that are Rison encoded into a URL -that abide by the following convention: `/api/reporting/generate?jobParams=${rison.encode(jobParams)}`. If you use the -aforementioned <> then this detail will be abstracted away, but if you -provide a custom UI for generating the report, you will have to generate the URL and create a POST request to the URL. +=== Basic job parameters +Certain fields of Reporting job parameters are required for every type of export. + +---- +interface BaseParams { + title: string; <1> + objectType: string; <2> + browserTimezone: string; <3> + version: string; <4> +}; +---- +<1> The `title` for the report. This is shown in the listing of reports in **Stack Management > Alerts and +Insights > Reporting** and used as the filename when the report is downloaded. +<2> The `objectType` field is automatically added when using internal Reporting APIs. This value used for choosing an icon for the +report job in the listing of reports in {kib}. +<3> The `browserTimezone` field is automatically added when using internal Reporting APIs to craft the job parameters. This is +used to correctly format time-based data in the user's desired timezone. +<4> The `version` field is automatically added when using internal Reporting APIs. This is used in cases where job parameters are +reused after an upgrade of Kibana, and a migration may be needed. include::csv-integration.asciidoc[] include::pdf-integration.asciidoc[] + +=== Using POST URLs for debugging +Developers can capture a POST URL from a reporting-capable application to access the `jobParams` query string variable in the +public API report generation endpoint. The query string variable can be passed through a URL de-encoder and then passed through a +Rison-to-JSON converter to make the job parameters human-readable. + +If attempting to send requests to the POST URL to test generating a report, use a shell script containing the curl command that +POSTs the request. This will avoid any unintentional character escaping that can happen if running the curl command in an +interactive shell. diff --git a/docs/developer/architecture/development/pdf-integration.asciidoc b/docs/developer/architecture/development/pdf-integration.asciidoc index e9f32de41baab..2d0bfbb8e9e2f 100644 --- a/docs/developer/architecture/development/pdf-integration.asciidoc +++ b/docs/developer/architecture/development/pdf-integration.asciidoc @@ -1,53 +1,47 @@ [float] -=== PDF +=== PDF and PNG [float] ==== Job parameters -If integrating with Reporting via a custom UI, the following job parameters must be Rison encoded and posted to -the aforementioned generate job url: +A configuration for a PDF or PNG report job is represented with an interface that includes the `BaseParams` and the following +fields. To create a request for one of these report types, these required job parameters are encoded into a query string variable +of the report generation URL: ---- -interface jobParameters { - objectType: string; - title: string; - browserTimezone: string; - relativeUrls: string[]; +interface BaseParamsPDFV2 { layout: { - id: string; + id: string; <1> dimensions: { height: number; width: number; }; }; + locatorParams: LocatorParams[]; <2> } ----- - -`jobParameters.browserTimezone` is a string that appears in the tz database -`jobParameters.layout.id` presently only accepts "print" and "preserve_layout" -`jobParameters.layout.dimensions` is only currently used by "preserve_layout" - -[float] -==== `export-config` directive -If integrating with Reporting via the `export-config` directive, the AngularJS controller that contains -the directive should expose the following methods and the `export-config` directive will POST them to the -reporting API: - ----- - -function getSharingType(): string; - -function getSharingTitle(): string; +interface BaseParamsPNGV2 { + layout: { + id: string; <3> + dimensions: { + height: number; + width: number; + }; + }; + locatorParams: LocatorParams; <4> +} ---- - -The `export-config` directive will use the browser's current URL and timezone when generating the job -parameters automatically. The `export-config` directive will also grab the height/width of the element -with the `data-shared-items-container` attribute and use this as the dimensions. +<1> The available `layout.id` options for PDF exports are `preserve_layout`, `print`, and `canvas`. These control how dashboard +panels are captured and positioned into pages in the PDF file. +<2> The `locatorParams` value is controlled by the application loaded in the browser for which a screenshot will be captured. The +parameters to generate a PDF report allow an array of `locatorParams` to support multi-page PDF reports. +<3> The only available `layout.id` option for PNG exports is `preserve_layout`. +<4> The parameters to generate a PNG report allow a single value for `locatorParams`. [float] -==== Screenshot capturing attributes -When generating the PDF, reporting looks for a number of attributes in the DOM to determine which elements -should have their screenshot taken and when the Visualizations are done rendering. +==== How applications make themselves screenshot-capable +When generating the PDF, the headless browser launched by the Reporting export type runs a script that looks for a number of +attributes in the DOM to determine which elements should have their screenshot taken and when the Visualizations are done +rendering. The print layout takes a screenshot of every element with the `data-shared-item` attribute and includes the individual screenshots in the PDF. The print layout also uses the `data-title` and `data-description` @@ -58,10 +52,12 @@ reporting will resize the element with the `data-shared-items-container` to be t The preserve layout also uses the `data-title` and `data-description` attributes on the HTMLElement with the `data-shared-items-container` attribute to specify the title/description for the entire PDF. -Reporting needs to determine when all of the visualizations have completed rendering, so that it can begin taking screenshots. -If there are multiple visualizations, the `data-shared-items-count` attribute should be specified to let Reporting know how -many Visualizations to look for. Reporting will look at every element with the `data-shared-item` attribute and use the corresponding -`data-render-complete` attribute and `renderComplete` events to listen for rendering to complete. When rendering is complete for a visualization -the `data-render-complete` attribute should be set to "true" and it should dispatch a custom DOM `renderComplete` event. +Reporting needs to determine when all of the visualizations have completed rendering, so that it can begin taking screenshots. If +there are multiple visualizations, the `data-shared-items-count` attribute should be specified to let Reporting know how many +Visualizations to look for. Reporting will look at every element with the `data-shared-item` attribute and use the corresponding +`data-render-complete` attribute and `renderComplete` events to listen for rendering to complete. When rendering is complete for a +visualization the `data-render-complete` attribute should be set to "true" and it should dispatch a custom DOM `renderComplete` +event. -If the reporting job uses multiple URLs, before looking for any of the `data-shared-item` or `data-shared-items-count` attributes, it waits for a `data-shared-page` attribute that specifies which page is being loaded. +If the reporting job uses multiple URLs, before looking for any of the `data-shared-item` or `data-shared-items-count` attributes, +it waits for a `data-shared-page` attribute that specifies which page is being loaded. diff --git a/package.json b/package.json index 1628778cbe832..acd2f530a9de4 100644 --- a/package.json +++ b/package.json @@ -106,7 +106,7 @@ "@elastic/ecs": "^8.11.1", "@elastic/elasticsearch": "^8.12.2", "@elastic/ems-client": "8.5.1", - "@elastic/eui": "93.5.1", + "@elastic/eui": "93.5.2", "@elastic/filesaver": "1.1.2", "@elastic/node-crypto": "1.2.1", "@elastic/numeral": "^2.5.1", @@ -1103,7 +1103,7 @@ "react-syntax-highlighter": "^15.3.1", "react-use": "^15.3.8", "react-virtualized": "^9.22.5", - "react-window": "^1.8.9", + "react-window": "^1.8.10", "reduce-reducers": "^1.0.4", "redux": "^4.2.1", "redux-actions": "^2.6.5", @@ -1490,7 +1490,7 @@ "@types/react-syntax-highlighter": "^15.4.0", "@types/react-test-renderer": "^17.0.2", "@types/react-virtualized": "^9.21.22", - "@types/react-window": "^1.8.5", + "@types/react-window": "^1.8.8", "@types/redux-actions": "^2.6.1", "@types/resolve": "^1.20.1", "@types/seedrandom": ">=2.0.0 <4.0.0", diff --git a/packages/analytics/client/src/analytics_client/analytics_client.ts b/packages/analytics/client/src/analytics_client/analytics_client.ts index 6844882459c84..1029a87f2b935 100644 --- a/packages/analytics/client/src/analytics_client/analytics_client.ts +++ b/packages/analytics/client/src/analytics_client/analytics_client.ts @@ -168,7 +168,6 @@ export class AnalyticsClient implements IAnalyticsClient { this.optInConfig$.next(optInConfigInstance); }; - // @ts-expect-error upgrade typescript v4.9.5 public registerContextProvider = (contextProviderOpts: ContextProviderOpts) => { this.contextService.registerContextProvider(contextProviderOpts); }; diff --git a/packages/analytics/client/src/analytics_client/context_service.ts b/packages/analytics/client/src/analytics_client/context_service.ts index 75074c94a3fc0..490aea84aeb39 100644 --- a/packages/analytics/client/src/analytics_client/context_service.ts +++ b/packages/analytics/client/src/analytics_client/context_service.ts @@ -14,7 +14,7 @@ import type { ContextProviderName, ContextProviderOpts } from './types'; import { schemaToIoTs, validateSchema } from '../schema/validation'; export class ContextService { - private readonly contextProvidersRegistry = new Map>(); + private readonly contextProvidersRegistry = new Map(); private readonly contextProvidersSubscriptions = new Map(); constructor( @@ -30,7 +30,7 @@ export class ContextService { public registerContextProvider({ name, context$, - schema, // @ts-expect-error upgrade typescript v4.9.5 + schema, }: ContextProviderOpts) { if (this.contextProvidersSubscriptions.has(name)) { throw new Error(`Context provider with name '${name}' already registered`); @@ -58,9 +58,8 @@ export class ContextService { }) ) .subscribe((context) => { - // We store each context linked to the context provider so they can increase and reduce + // We store each context linked to the context provider, so they can increase and reduce // the number of fields they report without having left-overs in the global context. - // @ts-expect-error upgrade typescript v4.9.5 this.contextProvidersRegistry.set(name, context); // For every context change, we rebuild the global context. @@ -87,7 +86,7 @@ export class ContextService { */ private updateGlobalContext() { this.context$.next( - [...this.contextProvidersRegistry.values()].reduce((acc, context) => { + [...this.contextProvidersRegistry.values()].reduce((acc: Partial, context) => { return { ...acc, ...this.removeEmptyValues(context), @@ -96,8 +95,8 @@ export class ContextService { ); } - private removeEmptyValues(context?: Partial) { - if (!context) { + private removeEmptyValues(context: unknown) { + if (!isObject(context)) { return {}; } return Object.keys(context).reduce((acc, key) => { @@ -108,3 +107,7 @@ export class ContextService { }, {} as Partial); } } + +function isObject(value: unknown): value is Record { + return typeof value === 'object' && value !== null; +} diff --git a/packages/analytics/client/src/analytics_client/index.ts b/packages/analytics/client/src/analytics_client/index.ts index 7a682de3c9772..77c4c8ce6fa16 100644 --- a/packages/analytics/client/src/analytics_client/index.ts +++ b/packages/analytics/client/src/analytics_client/index.ts @@ -19,6 +19,7 @@ export type { ShipperName, // Types for the registerContextProvider API ContextProviderOpts, + ContextProviderName, // Types for the registerEventType API EventTypeOpts, } from './types'; diff --git a/packages/analytics/client/src/analytics_client/types.ts b/packages/analytics/client/src/analytics_client/types.ts index 8188ecde86161..18b55e88f5a91 100644 --- a/packages/analytics/client/src/analytics_client/types.ts +++ b/packages/analytics/client/src/analytics_client/types.ts @@ -13,7 +13,7 @@ import type { Observable } from 'rxjs'; import type { Logger } from '@kbn/logging'; import type { IShipper } from '../shippers'; -import type { EventContext, EventType, TelemetryCounter } from '../events'; +import type { EventType, TelemetryCounter } from '../events'; import type { RootSchema } from '../schema'; /** @@ -93,7 +93,7 @@ export interface OptInConfigPerType { } /** - * + * Options for the optIn API */ export interface OptInConfig { /** @@ -128,7 +128,7 @@ export type ContextProviderName = string; /** * Definition of a context provider */ -export interface ContextProviderOpts> { +export interface ContextProviderOpts { /** * The name of the provider. */ @@ -205,7 +205,6 @@ export interface IAnalyticsClient { * * @track-adoption */ - // @ts-expect-error upgrade typescript v4.9.5 registerContextProvider: (contextProviderOpts: ContextProviderOpts) => void; /** * Removes the context provider and stop enriching the events from its context. diff --git a/packages/core/chrome/core-chrome-browser-internal/src/chrome_service.tsx b/packages/core/chrome/core-chrome-browser-internal/src/chrome_service.tsx index 0e248a069e326..7f14b54a0863a 100644 --- a/packages/core/chrome/core-chrome-browser-internal/src/chrome_service.tsx +++ b/packages/core/chrome/core-chrome-browser-internal/src/chrome_service.tsx @@ -9,7 +9,7 @@ import React, { useMemo } from 'react'; import { FormattedMessage } from '@kbn/i18n-react'; import { BehaviorSubject, combineLatest, merge, type Observable, of, ReplaySubject } from 'rxjs'; -import { mergeMap, map, takeUntil } from 'rxjs/operators'; +import { mergeMap, map, takeUntil, filter } from 'rxjs/operators'; import { parse } from 'url'; import { EuiLink } from '@elastic/eui'; import useObservable from 'react-use/lib/useObservable'; @@ -199,7 +199,10 @@ export class ChromeService { const customNavLink$ = new BehaviorSubject(undefined); const helpSupportUrl$ = new BehaviorSubject(docLinks.links.kibana.askElastic); const isNavDrawerLocked$ = new BehaviorSubject(localStorage.getItem(IS_LOCKED_KEY) === 'true'); - const chromeStyle$ = new BehaviorSubject('classic'); + // ChromeStyle is set to undefined by default, which means that no header will be rendered until + // setChromeStyle(). This is to avoid a flickering between the "classic" and "project" header meanwhile + // we load the user profile to check if the user opted out of the new solution navigation. + const chromeStyle$ = new BehaviorSubject(undefined); const getKbnVersionClass = () => { // we assume that the version is valid and has the form 'X.X.X' @@ -283,9 +286,9 @@ export class ChromeService { LinkId extends AppDeepLinkId = AppDeepLinkId, Id extends string = string, ChildrenId extends string = Id - >(navigationTree$: Observable>) { + >(id: string, navigationTree$: Observable>) { validateChromeStyle(); - projectNavigation.initNavigation(navigationTree$); + projectNavigation.initNavigation(id, navigationTree$); } const setProjectBreadcrumbs = ( @@ -361,6 +364,8 @@ export class ChromeService { ); } + if (chromeStyle === undefined) return null; + // render header if (chromeStyle === 'project') { const projectNavigationComponent$ = projectNavigation.getProjectSideNavComponent$(); @@ -526,7 +531,11 @@ export class ChromeService { getBodyClasses$: () => bodyClasses$.pipe(takeUntil(this.stop$)), setChromeStyle, - getChromeStyle$: () => chromeStyle$.pipe(takeUntil(this.stop$)), + getChromeStyle$: () => + chromeStyle$.pipe( + filter((style): style is ChromeStyle => style !== undefined), + takeUntil(this.stop$) + ), getIsSideNavCollapsed$: () => this.isSideNavCollapsed$.asObservable(), project: { setHome: setProjectHome, diff --git a/packages/core/chrome/core-chrome-browser-internal/src/project_navigation/project_navigation_service.test.ts b/packages/core/chrome/core-chrome-browser-internal/src/project_navigation/project_navigation_service.test.ts index 1089d1ba46209..52e22f7aef03a 100644 --- a/packages/core/chrome/core-chrome-browser-internal/src/project_navigation/project_navigation_service.test.ts +++ b/packages/core/chrome/core-chrome-browser-internal/src/project_navigation/project_navigation_service.test.ts @@ -110,6 +110,7 @@ describe('initNavigation()', () => { beforeAll(() => { projectNavigation.initNavigation( + 'foo', of({ body: [ { @@ -184,6 +185,7 @@ describe('initNavigation()', () => { const { projectNavigation: projNavigation, getNavigationTree: getNavTree } = setupInitNavigation(); projNavigation.initNavigation( + 'foo', of({ body: [ { @@ -208,6 +210,7 @@ describe('initNavigation()', () => { const { projectNavigation: projNavigation } = setupInitNavigation(); projNavigation.initNavigation( + 'foo', of({ body: [ { @@ -392,6 +395,7 @@ describe('initNavigation()', () => { // 2. initNavigation() is called projectNavigation.initNavigation( + 'foo', of({ body: [ { @@ -419,6 +423,7 @@ describe('initNavigation()', () => { }); projectNavigation.initNavigation( + 'foo', // @ts-expect-error - We pass a non valid cloudLink that is not TS valid of({ body: [ @@ -524,7 +529,7 @@ describe('breadcrumbs', () => { const obs = subj.asObservable(); if (initiateNavigation) { - projectNavigation.initNavigation(obs); + projectNavigation.initNavigation('foo', obs); } return { @@ -737,7 +742,7 @@ describe('breadcrumbs', () => { { text: 'custom1', href: '/custom1' }, { text: 'custom2', href: '/custom1/custom2' }, ]); - projectNavigation.initNavigation(of(mockNavigation)); // init navigation + projectNavigation.initNavigation('foo', of(mockNavigation)); // init navigation const breadcrumbs = await firstValueFrom(projectNavigation.getProjectBreadcrumbs$()); expect(breadcrumbs).toHaveLength(4); @@ -776,6 +781,7 @@ describe('getActiveNodes$()', () => { expect(activeNodes).toEqual([]); projectNavigation.initNavigation( + 'foo', of({ body: [ { @@ -831,6 +837,7 @@ describe('getActiveNodes$()', () => { expect(activeNodes).toEqual([]); projectNavigation.initNavigation( + 'foo', of({ body: [ { diff --git a/packages/core/chrome/core-chrome-browser-internal/src/project_navigation/project_navigation_service.ts b/packages/core/chrome/core-chrome-browser-internal/src/project_navigation/project_navigation_service.ts index 73b789b5bf349..63675ea742aaf 100644 --- a/packages/core/chrome/core-chrome-browser-internal/src/project_navigation/project_navigation_service.ts +++ b/packages/core/chrome/core-chrome-browser-internal/src/project_navigation/project_navigation_service.ts @@ -141,9 +141,10 @@ export class ProjectNavigationService { return this.projectName$.asObservable(); }, initNavigation: ( - navTreeDefinition: Observable> + id: string, + navTreeDefinition$: Observable> ) => { - this.initNavigation(navTreeDefinition); + this.initNavigation(id, navTreeDefinition$); }, getNavigationTreeUi$: this.getNavigationTreeUi$.bind(this), getActiveNodes$: () => { @@ -219,18 +220,19 @@ export class ProjectNavigationService { * Initialize a "serverless style" navigation. For stateful deployments (not serverless), this * handler initialize one of the solution navigations registered. * + * @param id Id for the navigation tree definition * @param navTreeDefinition$ The navigation tree definition - * @param location Optional location to use to detect the active node in the new navigation tree */ - private initNavigation( - navTreeDefinition$: Observable, - location?: Location - ) { + private initNavigation(id: string, navTreeDefinition$: Observable) { + if (this.activeSolutionNavDefinitionId$.getValue() === id) return; + + this.activeSolutionNavDefinitionId$.next(id); + if (this.navigationChangeSubscription) { this.navigationChangeSubscription.unsubscribe(); } - let redirectLocation = location; + let initialised = false; this.projectNavigationNavTreeFlattened = {}; this.navigationChangeSubscription = combineLatest([ navTreeDefinition$, @@ -252,8 +254,11 @@ export class ProjectNavigationService { this.navigationTreeUi$.next(navigationTreeUI); this.projectNavigationNavTreeFlattened = flattenNav(navigationTree); - this.updateActiveProjectNavigationNodes(redirectLocation); - redirectLocation = undefined; // we don't want to redirect on subsequent changes, only when initiating + + // At initialization, we want to force the update of the active nodes, so 2 empty arrays [] + // are not considered equal and we update the Observable value. + this.updateActiveProjectNavigationNodes({ forceUpdate: !initialised }); + initialised = true; }, error: (err) => { this.logger?.error(err); @@ -292,14 +297,18 @@ export class ProjectNavigationService { * and update the activeNodes$ Observable. * * @param location Optional location to use to detect the active node in the new navigation tree, if not set the current location is used + * @param forceUpdate Optional flag to force the update of the active nodes even if the active nodes are the same */ - private updateActiveProjectNavigationNodes(location?: Location) { + private updateActiveProjectNavigationNodes({ + location, + forceUpdate = false, + }: { location?: Location; forceUpdate?: boolean } = {}) { const activeNodes = this.findActiveNodes({ location }); // Each time we call findActiveNodes() we create a new array of activeNodes. As this array is used // in React in useCallback() and useMemo() dependencies arrays it triggers an infinite navigation // tree registration loop. To avoid that we only notify the listeners when the activeNodes array // has actually changed. - const requiresUpdate = !deepEqual(activeNodes, this.activeNodes$.value); + const requiresUpdate = forceUpdate ? true : !deepEqual(activeNodes, this.activeNodes$.value); if (!requiresUpdate) return; @@ -308,7 +317,7 @@ export class ProjectNavigationService { private onHistoryLocationChange(location: Location) { this.location$.next(location); - this.updateActiveProjectNavigationNodes(location); + this.updateActiveProjectNavigationNodes({ location }); } private handleActiveNodesChange() { @@ -411,6 +420,7 @@ export class ProjectNavigationService { if (id === null) { this.setChromeStyle('classic'); this.navigationTree$.next(undefined); + this.activeSolutionNavDefinitionId$.next(null); } else { const definition = definitions[id]; if (!definition) { @@ -436,14 +446,11 @@ export class ProjectNavigationService { } } - // We want to pass the upcoming location where we are going to navigate to - // so we can immediately set the active nodes based on the new location and we - // don't have to wait for the location change event to be triggered. - this.initNavigation(definition.navigationTree$, location); + this.initNavigation(id, definition.navigationTree$); } + } else if (id !== null) { + this.activeSolutionNavDefinitionId$.next(id); } - - this.activeSolutionNavDefinitionId$.next(id); } private getSolutionsNavDefinitions$() { diff --git a/packages/core/chrome/core-chrome-browser-internal/src/types.ts b/packages/core/chrome/core-chrome-browser-internal/src/types.ts index 5497234cce31b..6413c0a683112 100644 --- a/packages/core/chrome/core-chrome-browser-internal/src/types.ts +++ b/packages/core/chrome/core-chrome-browser-internal/src/types.ts @@ -65,6 +65,7 @@ export interface InternalChromeStart extends ChromeStart { Id extends string = string, ChildrenId extends string = Id >( + id: string, navigationTree$: Observable> ): void; diff --git a/packages/core/http/core-http-router-server-internal/src/validator.ts b/packages/core/http/core-http-router-server-internal/src/validator.ts index 69cb6b253b826..80d6e96b2ccb0 100644 --- a/packages/core/http/core-http-router-server-internal/src/validator.ts +++ b/packages/core/http/core-http-router-server-internal/src/validator.ts @@ -7,7 +7,7 @@ */ import { Stream } from 'stream'; -import { ValidationError, Type, schema, isConfigSchema } from '@kbn/config-schema'; +import { ValidationError, schema, isConfigSchema } from '@kbn/config-schema'; import type { RouteValidationSpec, RouteValidationFunction, @@ -18,15 +18,6 @@ import type { } from '@kbn/core-http-server'; import { RouteValidationError } from '@kbn/core-http-server'; -// Ugly as hell but we need this conditional typing to have proper type inference -type RouteValidationResultType | undefined> = NonNullable< - T extends RouteValidationFunction - ? ReturnType['value'] - : T extends Type - ? T['type'] - : undefined ->; - /** * Route validator class to define the validation logic for each new route. * @@ -92,9 +83,9 @@ export class RouteValidator

{ unsafe?: boolean, data?: unknown, namespace?: string - ): RouteValidationResultType { + ): T { if (typeof validationRule === 'undefined') { - return {}; + return {} as T; } let precheckedData = this.preValidateSchema(data).validate(data, {}, namespace); @@ -125,12 +116,10 @@ export class RouteValidator

{ validationRule: RouteValidationSpec, data?: unknown, namespace?: string - ): RouteValidationResultType { + ): T { if (isConfigSchema(validationRule)) { - // @ts-expect-error upgrade typescript v4.9.5 return validationRule.validate(data, {}, namespace); } else if (typeof validationRule === 'function') { - // @ts-expect-error upgrade typescript v4.9.5 return this.validateFunction(validationRule, data, namespace); } else { throw new ValidationError( diff --git a/packages/core/http/core-http-server-internal/src/cdn_config.test.ts b/packages/core/http/core-http-server-internal/src/cdn_config/cdn_config.test.ts similarity index 98% rename from packages/core/http/core-http-server-internal/src/cdn_config.test.ts rename to packages/core/http/core-http-server-internal/src/cdn_config/cdn_config.test.ts index b6a954782f523..a728966b5e916 100644 --- a/packages/core/http/core-http-server-internal/src/cdn_config.test.ts +++ b/packages/core/http/core-http-server-internal/src/cdn_config/cdn_config.test.ts @@ -41,7 +41,7 @@ describe('CdnConfig', () => { it('generates the expected CSP additions', () => { const cdnConfig = CdnConfig.from({ url: 'https://foo.bar:9999' }); expect(cdnConfig.getCspConfig()).toEqual({ - connect_src: ['foo.bar:9999'], + connect_src: ['https:'], font_src: ['foo.bar:9999'], img_src: ['foo.bar:9999'], script_src: ['foo.bar:9999'], diff --git a/packages/core/http/core-http-server-internal/src/cdn_config.ts b/packages/core/http/core-http-server-internal/src/cdn_config/cdn_config.ts similarity index 81% rename from packages/core/http/core-http-server-internal/src/cdn_config.ts rename to packages/core/http/core-http-server-internal/src/cdn_config/cdn_config.ts index e6fa29200ac74..68a255a62f5c2 100644 --- a/packages/core/http/core-http-server-internal/src/cdn_config.ts +++ b/packages/core/http/core-http-server-internal/src/cdn_config/cdn_config.ts @@ -7,7 +7,7 @@ */ import { URL, format } from 'node:url'; -import type { CspAdditionalConfig } from './csp'; +import type { CspAdditionalConfig } from '../csp'; export interface Input { url?: string; @@ -34,13 +34,16 @@ export class CdnConfig { public getCspConfig(): CspAdditionalConfig { const host = this.host; if (!host) return {}; + // Since CDN is only used in specific envs we set `connect_src` to allow any + // but require https. This hardens security a bit, but allows apps like + // maps to still work as expected. return { + connect_src: ['https:'], font_src: [host], img_src: [host], script_src: [host], style_src: [host], worker_src: [host], - connect_src: [host], }; } diff --git a/packages/core/http/core-http-server-internal/src/cdn_config/index.ts b/packages/core/http/core-http-server-internal/src/cdn_config/index.ts new file mode 100644 index 0000000000000..e2e30ffbea243 --- /dev/null +++ b/packages/core/http/core-http-server-internal/src/cdn_config/index.ts @@ -0,0 +1,9 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +export { CdnConfig, type Input } from './cdn_config'; diff --git a/packages/core/http/core-http-server-internal/src/cookie_session_storage.ts b/packages/core/http/core-http-server-internal/src/cookie_session_storage.ts index 94dbd10970cc5..ab3dd92aa6f7b 100644 --- a/packages/core/http/core-http-server-internal/src/cookie_session_storage.ts +++ b/packages/core/http/core-http-server-internal/src/cookie_session_storage.ts @@ -18,7 +18,7 @@ import type { } from '@kbn/core-http-server'; import { ensureRawRequest } from '@kbn/core-http-router-server-internal'; -class ScopedCookieSessionStorage> implements SessionStorage { +class ScopedCookieSessionStorage implements SessionStorage { constructor( private readonly log: Logger, private readonly server: Server, @@ -72,7 +72,7 @@ function validateOptions(options: SessionStorageCookieOptions) { * @param server - hapi server to create SessionStorage for * @param cookieOptions - cookies configuration */ -export async function createCookieSessionStorageFactory( +export async function createCookieSessionStorageFactory( log: Logger, server: Server, cookieOptions: SessionStorageCookieOptions, @@ -113,7 +113,6 @@ export async function createCookieSessionStorageFactory( return { asScoped(request: KibanaRequest) { - // @ts-expect-error upgrade typescript v4.9.5 return new ScopedCookieSessionStorage(log, server, ensureRawRequest(request)); }, }; diff --git a/packages/core/http/core-http-server-internal/src/http_server.ts b/packages/core/http/core-http-server-internal/src/http_server.ts index ae9025d5cd9a7..c023f39206868 100644 --- a/packages/core/http/core-http-server-internal/src/http_server.ts +++ b/packages/core/http/core-http-server-internal/src/http_server.ts @@ -292,8 +292,9 @@ export class HttpServer { registerAuth: this.registerAuth.bind(this), registerOnPostAuth: this.registerOnPostAuth.bind(this), registerOnPreResponse: this.registerOnPreResponse.bind(this), - createCookieSessionStorageFactory: (cookieOptions: SessionStorageCookieOptions) => - this.createCookieSessionStorageFactory(cookieOptions, config.basePath), + createCookieSessionStorageFactory: ( + cookieOptions: SessionStorageCookieOptions + ) => this.createCookieSessionStorageFactory(cookieOptions, config.basePath), basePath: basePathService, csp: config.csp, auth: { @@ -554,7 +555,7 @@ export class HttpServer { this.server.ext('onPreResponse', adoptToHapiOnPreResponseFormat(fn, this.log)); } - private async createCookieSessionStorageFactory( + private async createCookieSessionStorageFactory( cookieOptions: SessionStorageCookieOptions, basePath?: string ) { diff --git a/packages/core/http/core-http-server-mocks/src/cookie_session_storage.mocks.ts b/packages/core/http/core-http-server-mocks/src/cookie_session_storage.mocks.ts index 279858a257eb1..5f70893c9114b 100644 --- a/packages/core/http/core-http-server-mocks/src/cookie_session_storage.mocks.ts +++ b/packages/core/http/core-http-server-mocks/src/cookie_session_storage.mocks.ts @@ -22,7 +22,7 @@ type ReturnMocked = { type DeepMocked = jest.Mocked>; -const creatSessionStorageFactoryMock = () => { +const creatSessionStorageFactoryMock = () => { const mocked: DeepMocked> = { asScoped: jest.fn(), }; diff --git a/packages/core/http/core-http-server/src/http_contract.ts b/packages/core/http/core-http-server/src/http_contract.ts index 8ac34b26a386c..09250abf8adae 100644 --- a/packages/core/http/core-http-server/src/http_contract.ts +++ b/packages/core/http/core-http-server/src/http_contract.ts @@ -218,7 +218,7 @@ export interface HttpServiceSetup< * Creates cookie based session storage factory {@link SessionStorageFactory} * @param cookieOptions {@link SessionStorageCookieOptions} - options to configure created cookie session storage. */ - createCookieSessionStorageFactory: ( + createCookieSessionStorageFactory: ( cookieOptions: SessionStorageCookieOptions ) => Promise>; diff --git a/packages/core/saved-objects/core-saved-objects-api-server-internal/src/lib/apis/bulk_update.ts b/packages/core/saved-objects/core-saved-objects-api-server-internal/src/lib/apis/bulk_update.ts index b195141924659..7b8163107ac6b 100644 --- a/packages/core/saved-objects/core-saved-objects-api-server-internal/src/lib/apis/bulk_update.ts +++ b/packages/core/saved-objects/core-saved-objects-api-server-internal/src/lib/apis/bulk_update.ts @@ -262,9 +262,8 @@ export const performBulkUpdate = async ( const typeDefinition = registry.getType(type)!; const updatedAttributes = mergeForUpdate({ - // @ts-expect-error upgrade typescript v4.9.5 targetAttributes: { - ...migrated!.attributes, + ...(migrated!.attributes as Record), }, updatedAttributes: await encryptionHelper.optionallyEncryptAttributes( type, diff --git a/packages/core/saved-objects/core-saved-objects-api-server-internal/src/lib/apis/update.ts b/packages/core/saved-objects/core-saved-objects-api-server-internal/src/lib/apis/update.ts index e25320f8a842e..ee76a0243b3eb 100644 --- a/packages/core/saved-objects/core-saved-objects-api-server-internal/src/lib/apis/update.ts +++ b/packages/core/saved-objects/core-saved-objects-api-server-internal/src/lib/apis/update.ts @@ -247,9 +247,8 @@ export const executeUpdate = async ( // therefor we can safely process with the "standard" update sequence. const updatedAttributes = mergeForUpdate({ - // @ts-expect-error upgrade typescript v4.9.5 targetAttributes: { - ...migrated!.attributes, + ...(migrated!.attributes as Record), }, updatedAttributes: await encryptionHelper.optionallyEncryptAttributes( type, diff --git a/packages/core/saved-objects/core-saved-objects-common/src/server_types.ts b/packages/core/saved-objects/core-saved-objects-common/src/server_types.ts index 9ea8f1c9f0668..8b3ffe0b733d6 100644 --- a/packages/core/saved-objects/core-saved-objects-common/src/server_types.ts +++ b/packages/core/saved-objects/core-saved-objects-common/src/server_types.ts @@ -66,7 +66,7 @@ export interface SavedObjectReference { export interface SavedObject { /** The ID of this Saved Object, guaranteed to be unique for all objects of the same `type` */ id: string; - /** The type of Saved Object. Each plugin can define it's own custom Saved Object types. */ + /** The type of Saved Object. Each plugin can define its own custom Saved Object types. */ type: string; /** An opaque version number which changes on each successful write operation. Can be used for implementing optimistic concurrency control. */ version?: string; diff --git a/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/core/build_active_mappings.ts b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/core/build_active_mappings.ts index e3de61646d749..898a1114f8e57 100644 --- a/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/core/build_active_mappings.ts +++ b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/core/build_active_mappings.ts @@ -40,6 +40,10 @@ export function buildActiveMappings( * @returns {IndexMapping} */ export function getBaseMappings(): IndexMapping { + // Important: the ZDT algorithm won't trigger a reindex on documents + // when changes on root field mappings are detected, meaning that adding + // a non-indexed root field and then later switching it to indexed is + // not support atm and would require changes to the ZDT algo. return { dynamic: 'strict', properties: { diff --git a/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/core/compare_mappings.test.ts b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/core/compare_mappings.test.ts index eec6e52090cae..29a1d6cfe4849 100644 --- a/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/core/compare_mappings.test.ts +++ b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/core/compare_mappings.test.ts @@ -118,4 +118,32 @@ describe('getUpdatedRootFields', () => { expect(updatedFields).toEqual(['namespace', 'references']); }); + + it('ignores fields not being present on the base mapping for the diff', () => { + const updatedFields = getUpdatedRootFields({ + properties: { + ...getBaseMappings().properties, + someUnknownField: { + type: 'text', + }, + }, + }); + + expect(updatedFields).toEqual([]); + }); + + it('ignores fields not being present on the base mapping even with nested props', () => { + const updatedFields = getUpdatedRootFields({ + properties: { + ...getBaseMappings().properties, + someTypeProps: { + properties: { + foo: { type: 'text' }, + }, + }, + }, + }); + + expect(updatedFields).toEqual([]); + }); }); diff --git a/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/zdt/model/stages/init.test.ts b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/zdt/model/stages/init.test.ts index a68d6c266a652..bdaabfcc863cd 100644 --- a/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/zdt/model/stages/init.test.ts +++ b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/zdt/model/stages/init.test.ts @@ -345,7 +345,7 @@ describe('Stage: init', () => { expect(generateAdditiveMappingDiffMock).toHaveBeenCalledTimes(1); expect(generateAdditiveMappingDiffMock).toHaveBeenCalledWith({ types: ['foo', 'bar'].map((type) => context.typeRegistry.getType(type)), - meta: fetchIndexResponse[currentIndex].mappings._meta, + mapping: fetchIndexResponse[currentIndex].mappings, deletedTypes: context.deletedTypes, }); }); diff --git a/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/zdt/model/stages/init.ts b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/zdt/model/stages/init.ts index ceb97cb045987..7524e7b2d9f09 100644 --- a/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/zdt/model/stages/init.ts +++ b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/zdt/model/stages/init.ts @@ -164,7 +164,7 @@ export const init: ModelStage< case 'greater': const additiveMappingChanges = generateAdditiveMappingDiff({ types, - meta: currentMappings._meta ?? {}, + mapping: currentMappings, deletedTypes: context.deletedTypes, }); return { diff --git a/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/zdt/utils/check_version_compatibility.test.mocks.ts b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/zdt/utils/check_version_compatibility.test.mocks.ts index 8d7cbe657758c..e2ff1de594076 100644 --- a/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/zdt/utils/check_version_compatibility.test.mocks.ts +++ b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/zdt/utils/check_version_compatibility.test.mocks.ts @@ -19,3 +19,13 @@ jest.doMock('@kbn/core-saved-objects-base-server-internal', () => { getVirtualVersionMap: getVirtualVersionMapMock, }; }); + +export const getUpdatedRootFieldsMock = jest.fn(); + +jest.doMock('../../core/compare_mappings', () => { + const actual = jest.requireActual('../../core/compare_mappings'); + return { + ...actual, + getUpdatedRootFields: getUpdatedRootFieldsMock, + }; +}); diff --git a/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/zdt/utils/check_version_compatibility.test.ts b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/zdt/utils/check_version_compatibility.test.ts index 528855417e90f..640b60d5aeb3f 100644 --- a/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/zdt/utils/check_version_compatibility.test.ts +++ b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/zdt/utils/check_version_compatibility.test.ts @@ -10,6 +10,7 @@ import { compareVirtualVersionsMock, getVirtualVersionMapMock, getVirtualVersionsFromMappingsMock, + getUpdatedRootFieldsMock, } from './check_version_compatibility.test.mocks'; import type { SavedObjectsType } from '@kbn/core-saved-objects-server'; import type { @@ -30,6 +31,7 @@ describe('checkVersionCompatibility', () => { compareVirtualVersionsMock.mockReset().mockReturnValue({}); getVirtualVersionMapMock.mockReset().mockReturnValue({}); getVirtualVersionsFromMappingsMock.mockReset().mockReturnValue({ status: 'equal' }); + getUpdatedRootFieldsMock.mockReset().mockReturnValue([]); types = [createType({ name: 'foo' }), createType({ name: 'bar' })]; @@ -88,24 +90,112 @@ describe('checkVersionCompatibility', () => { }); }); - it('returns the result of the compareModelVersions call', () => { - const expected: CompareModelVersionResult = { - status: 'lesser', - details: { - greater: [], - lesser: [], - equal: [], - }, - }; - compareVirtualVersionsMock.mockReturnValue(expected); - - const result = checkVersionCompatibility({ + it('calls getUpdatedRootFields with the correct parameters', () => { + checkVersionCompatibility({ types, mappings, source: 'mappingVersions', deletedTypes, }); - expect(result).toEqual(expected); + expect(getUpdatedRootFieldsMock).toHaveBeenCalledTimes(1); + expect(getUpdatedRootFieldsMock).toHaveBeenCalledWith(mappings); + }); + + describe('without updated root fields', () => { + it('returns the result of the compareModelVersions call', () => { + const expected: CompareModelVersionResult = { + status: 'lesser', + details: { greater: [], lesser: [], equal: [] }, + }; + compareVirtualVersionsMock.mockReturnValue(expected); + + const result = checkVersionCompatibility({ + types, + mappings, + source: 'mappingVersions', + deletedTypes, + }); + + expect(result).toEqual({ + status: expected.status, + versionDetails: expected.details, + updatedRootFields: [], + }); + }); + }); + + describe('with updated root fields', () => { + beforeEach(() => { + getUpdatedRootFieldsMock.mockReturnValue(['rootA']); + }); + + it('returns the correct status for `greater` version status check', () => { + const expected: CompareModelVersionResult = { + status: 'greater', + details: { greater: [], lesser: [], equal: [] }, + }; + compareVirtualVersionsMock.mockReturnValue(expected); + + const result = checkVersionCompatibility({ + types, + mappings, + source: 'mappingVersions', + deletedTypes, + }); + + expect(result.status).toEqual('greater'); + }); + + it('returns the correct status for `lesser` version status check', () => { + const expected: CompareModelVersionResult = { + status: 'lesser', + details: { greater: [], lesser: [], equal: [] }, + }; + compareVirtualVersionsMock.mockReturnValue(expected); + + const result = checkVersionCompatibility({ + types, + mappings, + source: 'mappingVersions', + deletedTypes, + }); + + expect(result.status).toEqual('conflict'); + }); + + it('returns the correct status for `equal` version status check', () => { + const expected: CompareModelVersionResult = { + status: 'equal', + details: { greater: [], lesser: [], equal: [] }, + }; + compareVirtualVersionsMock.mockReturnValue(expected); + + const result = checkVersionCompatibility({ + types, + mappings, + source: 'mappingVersions', + deletedTypes, + }); + + expect(result.status).toEqual('greater'); + }); + + it('returns the correct status for `conflict` version status check', () => { + const expected: CompareModelVersionResult = { + status: 'conflict', + details: { greater: [], lesser: [], equal: [] }, + }; + compareVirtualVersionsMock.mockReturnValue(expected); + + const result = checkVersionCompatibility({ + types, + mappings, + source: 'mappingVersions', + deletedTypes, + }); + + expect(result.status).toEqual('conflict'); + }); }); }); diff --git a/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/zdt/utils/check_version_compatibility.ts b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/zdt/utils/check_version_compatibility.ts index 651875e92c438..8dc60c639a56a 100644 --- a/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/zdt/utils/check_version_compatibility.ts +++ b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/zdt/utils/check_version_compatibility.ts @@ -12,8 +12,10 @@ import { compareVirtualVersions, getVirtualVersionMap, type IndexMapping, - type CompareModelVersionResult, + type CompareModelVersionStatus, + type CompareModelVersionDetails, } from '@kbn/core-saved-objects-base-server-internal'; +import { getUpdatedRootFields } from '../../core/compare_mappings'; interface CheckVersionCompatibilityOpts { mappings: IndexMapping; @@ -22,12 +24,20 @@ interface CheckVersionCompatibilityOpts { deletedTypes: string[]; } +type CheckVersionCompatibilityStatus = 'greater' | 'lesser' | 'equal' | 'conflict'; + +interface CheckVersionCompatibilityResult { + status: CheckVersionCompatibilityStatus; + versionDetails: CompareModelVersionDetails; + updatedRootFields: string[]; +} + export const checkVersionCompatibility = ({ mappings, types, source, deletedTypes, -}: CheckVersionCompatibilityOpts): CompareModelVersionResult => { +}: CheckVersionCompatibilityOpts): CheckVersionCompatibilityResult => { const appVersions = getVirtualVersionMap(types); const indexVersions = getVirtualVersionsFromMappings({ mappings, @@ -37,5 +47,34 @@ export const checkVersionCompatibility = ({ if (!indexVersions) { throw new Error(`Cannot check version: ${source} not present in the mapping meta`); } - return compareVirtualVersions({ appVersions, indexVersions, deletedTypes }); + + const updatedRootFields = getUpdatedRootFields(mappings); + const modelVersionStatus = compareVirtualVersions({ appVersions, indexVersions, deletedTypes }); + const status = getCompatibilityStatus(modelVersionStatus.status, updatedRootFields.length > 0); + + return { + status, + updatedRootFields, + versionDetails: modelVersionStatus.details, + }; +}; + +const getCompatibilityStatus = ( + versionStatus: CompareModelVersionStatus, + hasUpdatedRootFields: boolean +): CheckVersionCompatibilityStatus => { + if (!hasUpdatedRootFields) { + return versionStatus; + } + switch (versionStatus) { + case 'lesser': + // lower model versions but additional root mappings => conflict + return 'conflict'; + case 'equal': + // no change on model versions but additional root mappings => greater + return 'greater'; + default: + // greater and conflict are not impacted + return versionStatus; + } }; diff --git a/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/zdt/utils/generate_additive_mapping_diff.test.mocks.ts b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/zdt/utils/generate_additive_mapping_diff.test.mocks.ts new file mode 100644 index 0000000000000..b6cd5f063873f --- /dev/null +++ b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/zdt/utils/generate_additive_mapping_diff.test.mocks.ts @@ -0,0 +1,27 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +export const getBaseMappingsMock = jest.fn(); + +jest.doMock('../../core/build_active_mappings', () => { + const actual = jest.requireActual('../../core/build_active_mappings'); + return { + ...actual, + getBaseMappings: getBaseMappingsMock, + }; +}); + +export const getUpdatedRootFieldsMock = jest.fn(); + +jest.doMock('../../core/compare_mappings', () => { + const actual = jest.requireActual('../../core/compare_mappings'); + return { + ...actual, + getUpdatedRootFields: getUpdatedRootFieldsMock, + }; +}); diff --git a/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/zdt/utils/generate_additive_mapping_diff.test.ts b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/zdt/utils/generate_additive_mapping_diff.test.ts index 9083e4657b4ba..04f8754e9fe48 100644 --- a/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/zdt/utils/generate_additive_mapping_diff.test.ts +++ b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/zdt/utils/generate_additive_mapping_diff.test.ts @@ -6,9 +6,15 @@ * Side Public License, v 1. */ +import { + getBaseMappingsMock, + getUpdatedRootFieldsMock, +} from './generate_additive_mapping_diff.test.mocks'; + import type { SavedObjectsModelVersion } from '@kbn/core-saved-objects-server'; -import type { IndexMappingMeta } from '@kbn/core-saved-objects-base-server-internal'; +import type { IndexMappingMeta, IndexMapping } from '@kbn/core-saved-objects-base-server-internal'; import { generateAdditiveMappingDiff } from './generate_additive_mapping_diff'; +import { getBaseMappings } from '../../core/build_active_mappings'; import { createType } from '../test_helpers'; describe('generateAdditiveMappingDiff', () => { @@ -19,6 +25,11 @@ describe('generateAdditiveMappingDiff', () => { changes: [{ type: 'mappings_addition', addedMappings: {} }], }; + beforeEach(() => { + getBaseMappingsMock.mockReset().mockReturnValue({ properties: {} }); + getUpdatedRootFieldsMock.mockReset().mockReturnValue([]); + }); + const getTypes = () => { const foo = createType({ name: 'foo', @@ -41,6 +52,13 @@ describe('generateAdditiveMappingDiff', () => { return { foo, bar }; }; + const mappingFromMeta = (meta: IndexMappingMeta): IndexMapping => { + return { + properties: getBaseMappings().properties, + _meta: meta, + }; + }; + it('aggregates the mappings of the types with versions higher than in the index', () => { const { foo, bar } = getTypes(); const types = [foo, bar]; @@ -53,7 +71,7 @@ describe('generateAdditiveMappingDiff', () => { const addedMappings = generateAdditiveMappingDiff({ types, - meta, + mapping: mappingFromMeta(meta), deletedTypes, }); @@ -75,7 +93,7 @@ describe('generateAdditiveMappingDiff', () => { const addedMappings = generateAdditiveMappingDiff({ types, - meta, + mapping: mappingFromMeta(meta), deletedTypes, }); @@ -97,7 +115,7 @@ describe('generateAdditiveMappingDiff', () => { const addedMappings = generateAdditiveMappingDiff({ types, - meta, + mapping: mappingFromMeta(meta), deletedTypes, }); @@ -120,7 +138,7 @@ describe('generateAdditiveMappingDiff', () => { expect(() => generateAdditiveMappingDiff({ types, - meta, + mapping: mappingFromMeta(meta), deletedTypes, }) ).toThrowErrorMatchingInlineSnapshot( @@ -136,11 +154,142 @@ describe('generateAdditiveMappingDiff', () => { expect(() => generateAdditiveMappingDiff({ types, - meta, + mapping: mappingFromMeta(meta), deletedTypes, }) ).toThrowErrorMatchingInlineSnapshot( `"Cannot generate additive mapping diff: mappingVersions not present on index meta"` ); }); + + it('throws an error if _meta is not present on the index', () => { + expect(() => + generateAdditiveMappingDiff({ + types: [], + mapping: { + properties: {}, + }, + deletedTypes: [], + }) + ).toThrowErrorMatchingInlineSnapshot( + `"Cannot generate additive mapping diff: meta not present on index"` + ); + }); + + it('includes the root fields that were added', () => { + const { foo, bar } = getTypes(); + const types = [foo, bar]; + const meta: IndexMappingMeta = { + mappingVersions: { + foo: '10.2.0', + bar: '8.5.0', + }, + }; + + getBaseMappingsMock.mockReturnValue({ + properties: { + rootA: { type: 'keyword' }, + rootB: { type: 'keyword' }, + }, + }); + getUpdatedRootFieldsMock.mockReturnValue(['rootA']); + + const addedMappings = generateAdditiveMappingDiff({ + types, + mapping: mappingFromMeta(meta), + deletedTypes, + }); + + expect(addedMappings).toEqual({ + rootA: { type: 'keyword' }, + }); + }); + + it('includes the root fields that were modified', () => { + const { foo, bar } = getTypes(); + const types = [foo, bar]; + const meta: IndexMappingMeta = { + mappingVersions: { + foo: '10.2.0', + bar: '8.5.0', + }, + }; + + getBaseMappingsMock.mockReturnValue({ + properties: { + rootA: { type: 'keyword' }, + rootB: { type: 'keyword' }, + references: { + type: 'nested', + properties: { + name: { + type: 'keyword', + }, + type: { + type: 'keyword', + }, + id: { + type: 'keyword', + }, + }, + }, + }, + }); + getUpdatedRootFieldsMock.mockReturnValue(['rootA', 'references']); + + const addedMappings = generateAdditiveMappingDiff({ + types, + mapping: mappingFromMeta(meta), + deletedTypes, + }); + + expect(addedMappings).toEqual({ + rootA: { type: 'keyword' }, + references: { + type: 'nested', + properties: { + name: { + type: 'keyword', + }, + type: { + type: 'keyword', + }, + id: { + type: 'keyword', + }, + }, + }, + }); + }); + + it('combines the changes from the types and from the root fields', () => { + const { foo, bar } = getTypes(); + const types = [foo, bar]; + const meta: IndexMappingMeta = { + mappingVersions: { + foo: '10.1.0', + bar: '7.9.0', + }, + }; + + getBaseMappingsMock.mockReturnValue({ + properties: { + rootA: { type: 'keyword' }, + rootB: { type: 'keyword' }, + }, + }); + getUpdatedRootFieldsMock.mockReturnValue(['rootA']); + + const addedMappings = generateAdditiveMappingDiff({ + types, + mapping: mappingFromMeta(meta), + deletedTypes, + }); + + expect(addedMappings).toEqual({ + foo: foo.mappings, + bar: bar.mappings, + rootA: { type: 'keyword' }, + }); + }); }); diff --git a/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/zdt/utils/generate_additive_mapping_diff.ts b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/zdt/utils/generate_additive_mapping_diff.ts index 0ef7437b2d6c3..2c6d51881b844 100644 --- a/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/zdt/utils/generate_additive_mapping_diff.ts +++ b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/zdt/utils/generate_additive_mapping_diff.ts @@ -11,15 +11,17 @@ import type { SavedObjectsMappingProperties, } from '@kbn/core-saved-objects-server'; import { - IndexMappingMeta, + type IndexMapping, getVirtualVersionsFromMappingMeta, getVirtualVersionMap, getModelVersionDelta, } from '@kbn/core-saved-objects-base-server-internal'; +import { getUpdatedRootFields } from '../../core/compare_mappings'; +import { getBaseMappings } from '../../core/build_active_mappings'; interface GenerateAdditiveMappingsDiffOpts { types: SavedObjectsType[]; - meta: IndexMappingMeta; + mapping: IndexMapping; deletedTypes: string[]; } @@ -32,9 +34,15 @@ interface GenerateAdditiveMappingsDiffOpts { */ export const generateAdditiveMappingDiff = ({ types, - meta, + mapping, deletedTypes, }: GenerateAdditiveMappingsDiffOpts): SavedObjectsMappingProperties => { + const meta = mapping._meta; + if (!meta) { + // should never occur given we only generate additive mapping diff when we've recognized a zdt index + throw new Error('Cannot generate additive mapping diff: meta not present on index'); + } + const typeVersions = getVirtualVersionMap(types); const mappingVersion = getVirtualVersionsFromMappingMeta({ meta, @@ -69,5 +77,13 @@ export const generateAdditiveMappingDiff = ({ addedMappings[type] = typeMap[type].mappings; }); + const changedRootFields = getUpdatedRootFields(mapping); + if (changedRootFields.length) { + const baseMappings = getBaseMappings(); + changedRootFields.forEach((changedRootField) => { + addedMappings[changedRootField] = baseMappings.properties[changedRootField]; + }); + } + return addedMappings; }; diff --git a/packages/deeplinks/analytics/constants.ts b/packages/deeplinks/analytics/constants.ts index 9793f7bb1864f..3eed43677281c 100644 --- a/packages/deeplinks/analytics/constants.ts +++ b/packages/deeplinks/analytics/constants.ts @@ -13,3 +13,7 @@ export const DASHBOARD_APP_ID = 'dashboards'; export const VISUALIZE_APP_ID = 'visualize'; export const DISCOVER_ESQL_LOCATOR = 'DISCOVER_ESQL_LOCATOR'; + +export const DASHBOARD_APP_LOCATOR = 'DASHBOARD_APP_LOCATOR'; + +export const DASHBOARD_SAVED_OBJECT_TYPE = 'dashboard'; diff --git a/packages/deeplinks/analytics/index.ts b/packages/deeplinks/analytics/index.ts index aa01b0036a52d..ced2aeba50417 100644 --- a/packages/deeplinks/analytics/index.ts +++ b/packages/deeplinks/analytics/index.ts @@ -11,6 +11,7 @@ export { DISCOVER_APP_ID, VISUALIZE_APP_ID, DISCOVER_ESQL_LOCATOR, + DASHBOARD_APP_LOCATOR, } from './constants'; export type { AppId, DeepLinkId } from './deep_links'; diff --git a/packages/deeplinks/management/constants.ts b/packages/deeplinks/management/constants.ts index 1910641ac47c8..d094d97268574 100644 --- a/packages/deeplinks/management/constants.ts +++ b/packages/deeplinks/management/constants.ts @@ -11,3 +11,4 @@ export const INTEGRATIONS_APP_ID = 'integrations'; export const FLEET_APP_ID = 'fleet'; export const OSQUERY_APP_ID = 'osquery'; export const MANAGEMENT_APP_ID = 'management'; +export const MANAGEMENT_APP_LOCATOR = 'MANAGEMENT_APP_LOCATOR'; diff --git a/packages/kbn-doc-links/src/get_doc_links.ts b/packages/kbn-doc-links/src/get_doc_links.ts index 413062e56a878..7e3a622eff928 100644 --- a/packages/kbn-doc-links/src/get_doc_links.ts +++ b/packages/kbn-doc-links/src/get_doc_links.ts @@ -157,6 +157,7 @@ export const getDocLinks = ({ kibanaBranch, buildFlavor }: GetDocLinkOptions): D bulkApi: `${ELASTICSEARCH_DOCS}docs-bulk.html`, configuration: `${ENTERPRISE_SEARCH_DOCS}configuration.html`, connectors: `${ENTERPRISE_SEARCH_DOCS}connectors.html`, + connectorsClientDeploy: `${ENTERPRISE_SEARCH_DOCS}deploy-connector-client.html`, connectorsMappings: `${ENTERPRISE_SEARCH_DOCS}connectors-usage.html#connectors-usage-index-create-configure-existing-index`, connectorsAzureBlobStorage: `${ENTERPRISE_SEARCH_DOCS}connectors-azure-blob.html`, connectorsBox: `${ENTERPRISE_SEARCH_DOCS}connectors-box.html`, diff --git a/packages/kbn-doc-links/src/types.ts b/packages/kbn-doc-links/src/types.ts index 564aa8730e5cd..a1d4bd8863d11 100644 --- a/packages/kbn-doc-links/src/types.ts +++ b/packages/kbn-doc-links/src/types.ts @@ -122,6 +122,7 @@ export interface DocLinks { readonly bulkApi: string; readonly configuration: string; readonly connectors: string; + readonly connectorsClientDeploy: string; readonly connectorsMappings: string; readonly connectorsAzureBlobStorage: string; readonly connectorsBox: string; diff --git a/packages/kbn-router-utils/src/get_router_link_props/index.ts b/packages/kbn-router-utils/src/get_router_link_props/index.ts index f3def2e88650b..a0b7f53afc440 100644 --- a/packages/kbn-router-utils/src/get_router_link_props/index.ts +++ b/packages/kbn-router-utils/src/get_router_link_props/index.ts @@ -6,6 +6,11 @@ * Side Public License, v 1. */ +export interface RouterLinkProps { + href: string | undefined; + onClick: (event: React.MouseEvent) => void; +} + interface GetRouterLinkPropsDeps { href?: string; onClick(): void; @@ -28,7 +33,7 @@ const isLeftClickEvent = (event: React.MouseEvent) => event.b * @returns An object that contains an href and a guardedClick handler that will * manage behaviours such as leftClickEvent and event with modifiers (Ctrl, Shift, etc) */ -export const getRouterLinkProps = ({ href, onClick }: GetRouterLinkPropsDeps) => { +export const getRouterLinkProps = ({ href, onClick }: GetRouterLinkPropsDeps): RouterLinkProps => { const guardedClickHandler = (event: React.MouseEvent) => { if (event.defaultPrevented) { return; diff --git a/packages/kbn-test/src/kbn_client/kbn_client_saved_objects.ts b/packages/kbn-test/src/kbn_client/kbn_client_saved_objects.ts index caaf072c934ca..9828415a0a95c 100644 --- a/packages/kbn-test/src/kbn_client/kbn_client_saved_objects.ts +++ b/packages/kbn-test/src/kbn_client/kbn_client_saved_objects.ts @@ -125,6 +125,7 @@ const STANDARD_LIST_TYPES = [ 'epm-packages-assets', 'fleet-preconfiguration-deletion-record', 'fleet-fleet-server-host', + 'fleet-proxy', 'fleet-uninstall-tokens', ]; diff --git a/packages/kbn-user-profile-components/src/hooks/use_update_user_profile.test.tsx b/packages/kbn-user-profile-components/src/hooks/use_update_user_profile.test.tsx index 3f3621b8aae18..e84dccc3ba5c0 100644 --- a/packages/kbn-user-profile-components/src/hooks/use_update_user_profile.test.tsx +++ b/packages/kbn-user-profile-components/src/hooks/use_update_user_profile.test.tsx @@ -24,7 +24,10 @@ const security = { bulkGet: jest.fn(), suggest: jest.fn(), update: jest.fn(), + partialUpdate: jest.fn(), userProfile$: of({}), + userProfileLoaded$: of(true), + enabled$: of(true), }, uiApi: {}, }; @@ -42,16 +45,16 @@ const wrapper: WrapperComponent = ({ children }) => ( ); describe('useUpdateUserProfile() hook', () => { - const updateUserProfiles = jest.fn(); + const partialUpdateUserProfiles = jest.fn(); beforeEach(() => { security.userProfiles = { ...security.userProfiles, - update: updateUserProfiles, + partialUpdate: partialUpdateUserProfiles, userProfile$: of({}), }; - updateUserProfiles.mockReset().mockResolvedValue({}); + partialUpdateUserProfiles.mockReset().mockResolvedValue({}); http.get.mockReset(); http.post.mockReset().mockResolvedValue(undefined); notifications.toasts.addSuccess.mockReset(); @@ -65,12 +68,12 @@ describe('useUpdateUserProfile() hook', () => { update({ userSettings: { darkMode: 'dark' } }); }); - expect(updateUserProfiles).toHaveBeenCalledWith({ userSettings: { darkMode: 'dark' } }); + expect(partialUpdateUserProfiles).toHaveBeenCalledWith({ userSettings: { darkMode: 'dark' } }); }); test('should update the isLoading state while updating', async () => { const updateDone = new BehaviorSubject(false); - updateUserProfiles.mockImplementationOnce(async () => { + partialUpdateUserProfiles.mockImplementationOnce(async () => { await lastValueFrom(updateDone.pipe(first((v) => v === true))); }); diff --git a/packages/kbn-user-profile-components/src/hooks/use_update_user_profile.tsx b/packages/kbn-user-profile-components/src/hooks/use_update_user_profile.tsx index 3e015fe92eac5..5d4f59269f5c5 100644 --- a/packages/kbn-user-profile-components/src/hooks/use_update_user_profile.tsx +++ b/packages/kbn-user-profile-components/src/hooks/use_update_user_profile.tsx @@ -7,9 +7,10 @@ */ import { EuiButton, EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; -import React, { useCallback, useRef, useState } from 'react'; +import React, { useCallback, useRef, useState, useEffect } from 'react'; import useObservable from 'react-use/lib/useObservable'; import { i18n } from '@kbn/i18n'; +import { merge } from 'lodash'; import type { UserProfileData } from '../types'; import { useUserProfiles } from '../services'; @@ -52,7 +53,7 @@ export const useUpdateUserProfile = ({ pageReloadChecker, }: Props = {}) => { const { userProfileApiClient, notifySuccess } = useUserProfiles(); - const { userProfile$ } = userProfileApiClient; + const { userProfile$, enabled$ } = userProfileApiClient; const { enabled: notificationSuccessEnabled = true, title: notificationTitle = i18nTexts.notificationSuccess.title, @@ -60,8 +61,10 @@ export const useUpdateUserProfile = ({ } = notificationSuccess; const [isLoading, setIsLoading] = useState(false); const userProfileData = useObservable(userProfile$); + const userProfileEnabled = useObservable(enabled$); // Keep a snapshot before updating the user profile so we can compare previous and updated values const userProfileSnapshot = useRef(); + const isMounted = useRef(false); const showSuccessNotification = useCallback( ({ isRefreshRequired = false }: { isRefreshRequired?: boolean } = {}) => { @@ -102,7 +105,9 @@ export const useUpdateUserProfile = ({ const onUserProfileUpdate = useCallback( (updatedData: UserProfileData) => { - setIsLoading(false); + if (isMounted.current) { + setIsLoading(false); + } if (notificationSuccessEnabled) { const isRefreshRequired = pageReloadChecker?.(userProfileSnapshot.current, updatedData); @@ -113,14 +118,23 @@ export const useUpdateUserProfile = ({ ); const update = useCallback( - (updatedData: D) => { - userProfileSnapshot.current = userProfileData; + >(updatedData: D) => { + userProfileSnapshot.current = merge({}, userProfileData); setIsLoading(true); - return userProfileApiClient.update(updatedData).then(() => onUserProfileUpdate(updatedData)); + return userProfileApiClient + .partialUpdate(updatedData) + .then(() => onUserProfileUpdate(updatedData)); }, [userProfileApiClient, onUserProfileUpdate, userProfileData] ); + useEffect(() => { + isMounted.current = true; + return () => { + isMounted.current = false; + }; + }, []); + return { /** Update the user profile */ update, @@ -130,6 +144,8 @@ export const useUpdateUserProfile = ({ userProfileData, /** Flag to indicate if currently updating */ isLoading, + /** Flag to indicate if user profile is enabled */ + userProfileEnabled, }; }; diff --git a/packages/kbn-user-profile-components/src/types.ts b/packages/kbn-user-profile-components/src/types.ts index 77b6895756bdf..fd514a800437a 100644 --- a/packages/kbn-user-profile-components/src/types.ts +++ b/packages/kbn-user-profile-components/src/types.ts @@ -33,6 +33,7 @@ export type DarkModeValue = '' | 'dark' | 'light'; */ export interface UserSettingsData { darkMode?: DarkModeValue; + solutionNavOptOut?: boolean; } export interface UserProfileData { @@ -43,5 +44,6 @@ export interface UserProfileData { export interface UserProfileAPIClient { userProfile$: Observable; - update: (data: D) => Promise; + enabled$: Observable; + partialUpdate: >(data: D) => Promise; } diff --git a/packages/shared-ux/page/kibana_template/impl/src/__snapshots__/page_template_inner.test.tsx.snap b/packages/shared-ux/page/kibana_template/impl/src/__snapshots__/page_template_inner.test.tsx.snap index 9b89225ff7ce3..87d4c10015c20 100644 --- a/packages/shared-ux/page/kibana_template/impl/src/__snapshots__/page_template_inner.test.tsx.snap +++ b/packages/shared-ux/page/kibana_template/impl/src/__snapshots__/page_template_inner.test.tsx.snap @@ -8,6 +8,18 @@ exports[`KibanaPageTemplateInner isEmpty no pageHeader 1`] = ` /> `; +exports[`KibanaPageTemplateInner isEmpty no pageHeader, isEmptyState, emptyPageBody 1`] = ` +<_EuiPageTemplate + className="kbnPageTemplate" + minHeight={0} + offset={0} +> +

+ custom empty page body +
+ +`; + exports[`KibanaPageTemplateInner isEmpty pageHeader & children 1`] = ` <_EuiPageTemplate className="kbnPageTemplate" diff --git a/packages/shared-ux/page/kibana_template/impl/src/page_template_inner.test.tsx b/packages/shared-ux/page/kibana_template/impl/src/page_template_inner.test.tsx index d7278ac889669..145a5177f3069 100644 --- a/packages/shared-ux/page/kibana_template/impl/src/page_template_inner.test.tsx +++ b/packages/shared-ux/page/kibana_template/impl/src/page_template_inner.test.tsx @@ -48,6 +48,17 @@ describe('KibanaPageTemplateInner', () => { ); expect(component).toMatchSnapshot(); }); + + test('no pageHeader, isEmptyState, emptyPageBody', () => { + const component = shallow( + {'custom empty page body'}} + /> + ); + expect(component).toMatchSnapshot(); + }); }); test('page sidebar', () => { diff --git a/packages/shared-ux/page/kibana_template/impl/src/page_template_inner.tsx b/packages/shared-ux/page/kibana_template/impl/src/page_template_inner.tsx index 5da29ba797041..df72e52282ebe 100644 --- a/packages/shared-ux/page/kibana_template/impl/src/page_template_inner.tsx +++ b/packages/shared-ux/page/kibana_template/impl/src/page_template_inner.tsx @@ -31,6 +31,7 @@ export const KibanaPageTemplateInner: FC = ({ isEmptyState, pageSideBar, pageSideBarProps, + emptyPageBody, ...rest }) => { let header; @@ -52,6 +53,11 @@ export const KibanaPageTemplateInner: FC = ({ header = ; } + // NOTE: with emptyPageBody, page contents are replaced entirely with the provided element + if (isEmptyState && emptyPageBody) { + children = emptyPageBody; + } + let sideBar; if (pageSideBar) { const sideBarProps = { ...pageSideBarProps }; diff --git a/packages/shared-ux/page/kibana_template/types/index.d.ts b/packages/shared-ux/page/kibana_template/types/index.d.ts index a8181876204e2..4162502897bf7 100644 --- a/packages/shared-ux/page/kibana_template/types/index.d.ts +++ b/packages/shared-ux/page/kibana_template/types/index.d.ts @@ -25,6 +25,12 @@ export type KibanaPageTemplateProps = EuiPageTemplateProps & { * Converts the `pageHeader` contents into an EuiEmptyPrompt when no `children` are present */ isEmptyState?: boolean; + + /** + * Combined with isEmptyState, this prop allows complete override of the empty page + */ + emptyPageBody?: ReactNode; + /** * Quick creation of EuiSideNav. Hooks up mobile instance too */ diff --git a/packages/shared-ux/router/impl/routes.tsx b/packages/shared-ux/router/impl/routes.tsx index 80d4da6178ae2..fb0981dc8c61e 100644 --- a/packages/shared-ux/router/impl/routes.tsx +++ b/packages/shared-ux/router/impl/routes.tsx @@ -13,6 +13,18 @@ import { Switch, useRouteMatch } from 'react-router-dom'; import { Routes as ReactRouterRoutes, Route } from 'react-router-dom-v5-compat'; import { Route as LegacyRoute, MatchPropagator } from './route'; +type RouterElementChildren = Array< + React.ReactElement< + { + path: string; + render: Function; + children: RouterElementChildren; + component: Function; + }, + string | React.JSXElementConstructor + > +>; + export const Routes = ({ legacySwitch = true, children, @@ -26,16 +38,13 @@ export const Routes = ({ {children} ) : ( - {Children.map(children, (child) => { + {Children.map(children as RouterElementChildren, (child) => { if (React.isValidElement(child) && child.type === LegacyRoute) { const path = replace(child?.props.path, match.url + '/', ''); const renderFunction = - // @ts-expect-error upgrade typescript v4.9.5 typeof child?.props.children === 'function' - ? // @ts-expect-error upgrade typescript v4.9.5 - child?.props.children - : // @ts-expect-error upgrade typescript v4.9.5 - child?.props.render; + ? child?.props.children + : child?.props.render; return ( - {/* @ts-expect-error upgrade typescript v4.9.5*/} {(child?.props?.component && ) || (renderFunction && renderFunction()) || children} diff --git a/src/core/server/integration_tests/saved_objects/migrations/zdt_2/root_field_addition.test.ts b/src/core/server/integration_tests/saved_objects/migrations/zdt_2/root_field_addition.test.ts new file mode 100644 index 0000000000000..47f853187b18f --- /dev/null +++ b/src/core/server/integration_tests/saved_objects/migrations/zdt_2/root_field_addition.test.ts @@ -0,0 +1,124 @@ +/* + * Copyright 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. + */ + +const mockGetBaseMappings = jest.fn(); + +// No other way to simulate a change in root mappings unfortunately +jest.mock( + '@kbn/core-saved-objects-migration-server-internal/src/core/build_active_mappings', + () => { + const actual = jest.requireActual( + '@kbn/core-saved-objects-migration-server-internal/src/core/build_active_mappings' + ); + return { + ...actual, + getBaseMappings: () => mockGetBaseMappings(), + }; + } +); + +import Path from 'path'; +import fs from 'fs/promises'; +import { type TestElasticsearchUtils } from '@kbn/core-test-helpers-kbn-server'; +import '../jest_matchers'; +import { getKibanaMigratorTestKit, startElasticsearch } from '../kibana_migrator_test_kit'; +import { delay, parseLogFile } from '../test_utils'; +import { getBaseMigratorParams, getFooType } from '../fixtures/zdt_base.fixtures'; + +export const logFilePath = Path.join(__dirname, 'root_field_addition.test.log'); + +describe('ZDT upgrades - introducing new root fields', () => { + let esServer: TestElasticsearchUtils['es']; + + beforeAll(async () => { + await fs.unlink(logFilePath).catch(() => {}); + esServer = await startElasticsearch(); + }); + + afterAll(async () => { + await esServer?.stop(); + await delay(10); + }); + + const baseMappings = { + dynamic: 'strict', + properties: { + type: { + type: 'keyword', + }, + namespace: { + type: 'keyword', + }, + coreMigrationVersion: { + type: 'keyword', + }, + typeMigrationVersion: { + type: 'version', + }, + }, + }; + + const createBaseline = async () => { + mockGetBaseMappings.mockReturnValue(baseMappings); + + const fooType = getFooType(); + const { runMigrations } = await getKibanaMigratorTestKit({ + ...getBaseMigratorParams(), + types: [fooType], + }); + await runMigrations(); + }; + + it('should support adding the new root fields', async () => { + await createBaseline(); + + const updatedMappings = { + ...baseMappings, + properties: { + ...baseMappings.properties, + someNewRootField: { + type: 'keyword', + }, + anotherNewRootField: { + type: 'text', + }, + }, + }; + mockGetBaseMappings.mockReturnValue(updatedMappings); + + const fooType = getFooType(); + + const { runMigrations, client } = await getKibanaMigratorTestKit({ + ...getBaseMigratorParams(), + logFilePath, + types: [fooType], + }); + + await runMigrations(); + + const records = await parseLogFile(logFilePath); + + expect(records).toContainLogEntries( + [ + 'mapping version check result: greater', + 'INIT -> UPDATE_INDEX_MAPPINGS', + 'INDEX_STATE_UPDATE_DONE -> DOCUMENTS_UPDATE_INIT', + '-> DONE', + 'Migration completed', + ], + { ordered: true } + ); + + const mappings = await client.indices.getMapping({ index: '.kibana_1' }); + + expect(mappings['.kibana_1'].mappings.properties).toEqual({ + ...updatedMappings.properties, + foo: fooType.mappings, + }); + }); +}); diff --git a/src/core/server/mocks.ts b/src/core/server/mocks.ts index 4618204e7c70a..89fbd1a560758 100644 --- a/src/core/server/mocks.ts +++ b/src/core/server/mocks.ts @@ -145,8 +145,9 @@ export type CustomRequestHandlerMock = { [Key in keyof T]: T[Key] extends Promise ? T[Key] : Promise; }; -const createCustomRequestHandlerContextMock = (contextParts: T): CustomRequestHandlerMock => { - // @ts-expect-error upgrade typescript v4.9.5 +const createCustomRequestHandlerContextMock = >( + contextParts: T +): CustomRequestHandlerMock => { const mock = Object.entries(contextParts).reduce( (context, [key, value]) => { // @ts-expect-error type matching from inferred types is hard diff --git a/src/dev/license_checker/config.ts b/src/dev/license_checker/config.ts index 97358bb006ed4..27fd83d736b31 100644 --- a/src/dev/license_checker/config.ts +++ b/src/dev/license_checker/config.ts @@ -86,7 +86,7 @@ export const LICENSE_OVERRIDES = { 'jsts@1.6.2': ['Eclipse Distribution License - v 1.0'], // cf. https://github.com/bjornharrtell/jsts '@mapbox/jsonlint-lines-primitives@2.0.2': ['MIT'], // license in readme https://github.com/tmcw/jsonlint '@elastic/ems-client@8.5.1': ['Elastic License 2.0'], - '@elastic/eui@93.5.1': ['SSPL-1.0 OR Elastic License 2.0'], + '@elastic/eui@93.5.2': ['SSPL-1.0 OR Elastic License 2.0'], 'language-subtag-registry@0.3.21': ['CC-BY-4.0'], // retired ODC‑By license https://github.com/mattcg/language-subtag-registry 'buffers@0.1.1': ['MIT'], // license in importing module https://www.npmjs.com/package/binary '@bufbuild/protobuf@1.2.1': ['Apache-2.0'], // license (Apache-2.0 AND BSD-3-Clause) diff --git a/src/plugins/dashboard/public/dashboard_app/dashboard_app.tsx b/src/plugins/dashboard/public/dashboard_app/dashboard_app.tsx index 1ff789ab61201..320887bbf551c 100644 --- a/src/plugins/dashboard/public/dashboard_app/dashboard_app.tsx +++ b/src/plugins/dashboard/public/dashboard_app/dashboard_app.tsx @@ -15,6 +15,7 @@ import { ViewMode } from '@kbn/embeddable-plugin/public'; import { useExecutionContext } from '@kbn/kibana-react-plugin/public'; import { createKbnUrlStateStorage, withNotifyOnErrors } from '@kbn/kibana-utils-plugin/public'; +import { DASHBOARD_APP_LOCATOR } from '@kbn/deeplinks-analytics'; import { DashboardAppNoDataPage, isDashboardAppInNoDataState, @@ -31,7 +32,6 @@ import { } from './url/search_sessions_integration'; import { DashboardAPI, DashboardRenderer } from '..'; import { type DashboardEmbedSettings } from './types'; -import { DASHBOARD_APP_LOCATOR } from './locator/locator'; import { pluginServices } from '../services/plugin_services'; import { AwaitingDashboardAPI } from '../dashboard_container'; import { DashboardRedirect } from '../dashboard_container/types'; diff --git a/src/plugins/dashboard/public/dashboard_app/locator/locator.ts b/src/plugins/dashboard/public/dashboard_app/locator/locator.ts index c902bc369e044..15eb56cd44714 100644 --- a/src/plugins/dashboard/public/dashboard_app/locator/locator.ts +++ b/src/plugins/dashboard/public/dashboard_app/locator/locator.ts @@ -14,6 +14,7 @@ import { setStateToKbnUrl } from '@kbn/kibana-utils-plugin/public'; import type { LocatorDefinition, LocatorPublic } from '@kbn/share-plugin/public'; import type { GlobalQueryStateFromUrl } from '@kbn/data-plugin/public'; +import { DASHBOARD_APP_LOCATOR } from '@kbn/deeplinks-analytics'; import { DASHBOARD_APP_ID, SEARCH_SESSION_ID } from '../../dashboard_constants'; import { DashboardLocatorParams } from '../..'; @@ -31,8 +32,6 @@ export const cleanEmptyKeys = (stateObj: Record) => { return stateObj; }; -export const DASHBOARD_APP_LOCATOR = 'DASHBOARD_APP_LOCATOR'; - export type DashboardAppLocator = LocatorPublic; export interface DashboardAppLocatorDependencies { diff --git a/src/plugins/dashboard/public/dashboard_app/top_nav/share/show_share_modal.tsx b/src/plugins/dashboard/public/dashboard_app/top_nav/share/show_share_modal.tsx index b2d5cd9f4fe98..24a383244dc8c 100644 --- a/src/plugins/dashboard/public/dashboard_app/top_nav/share/show_share_modal.tsx +++ b/src/plugins/dashboard/public/dashboard_app/top_nav/share/show_share_modal.tsx @@ -19,11 +19,11 @@ import { getStateFromKbnUrl } from '@kbn/kibana-utils-plugin/public'; import { setStateToKbnUrl, unhashUrl } from '@kbn/kibana-utils-plugin/public'; import type { SerializableControlGroupInput } from '@kbn/controls-plugin/common'; +import { DASHBOARD_APP_LOCATOR } from '@kbn/deeplinks-analytics'; import { dashboardUrlParams } from '../../dashboard_router'; import { shareModalStrings } from '../../_dashboard_app_strings'; import { pluginServices } from '../../../services/plugin_services'; import { convertPanelMapToSavedPanels } from '../../../../common'; -import { DASHBOARD_APP_LOCATOR } from '../../locator/locator'; import { DashboardLocatorParams } from '../../../dashboard_container'; const showFilterBarId = 'showFilterBar'; diff --git a/src/plugins/dashboard/public/dashboard_app/url/search_sessions_integration.ts b/src/plugins/dashboard/public/dashboard_app/url/search_sessions_integration.ts index 91451b225658c..7a93c530767a2 100644 --- a/src/plugins/dashboard/public/dashboard_app/url/search_sessions_integration.ts +++ b/src/plugins/dashboard/public/dashboard_app/url/search_sessions_integration.ts @@ -18,11 +18,11 @@ import { replaceUrlHashQuery } from '@kbn/kibana-utils-plugin/common'; import type { Query } from '@kbn/es-query'; import { SearchSessionInfoProvider } from '@kbn/data-plugin/public'; +import { DASHBOARD_APP_LOCATOR } from '@kbn/deeplinks-analytics'; import { SEARCH_SESSION_ID } from '../../dashboard_constants'; import { DashboardContainer, DashboardLocatorParams } from '../../dashboard_container'; import { convertPanelMapToSavedPanels } from '../../../common'; import { pluginServices } from '../../services/plugin_services'; -import { DASHBOARD_APP_LOCATOR } from '../locator/locator'; export const removeSearchSessionIdFromURL = (kbnUrlStateStorage: IKbnUrlStateStorage) => { kbnUrlStateStorage.kbnUrlControls.updateAsync((nextUrl) => { diff --git a/src/plugins/dashboard/tsconfig.json b/src/plugins/dashboard/tsconfig.json index 7244e9ee9472f..b484a9f6c51c3 100644 --- a/src/plugins/dashboard/tsconfig.json +++ b/src/plugins/dashboard/tsconfig.json @@ -76,6 +76,7 @@ "@kbn/shared-ux-utility", "@kbn/managed-content-badge", "@kbn/core-test-helpers-model-versions", + "@kbn/deeplinks-analytics", ], "exclude": ["target/**/*"] } diff --git a/src/plugins/files/server/routes/file_kind/enhance_router.ts b/src/plugins/files/server/routes/file_kind/enhance_router.ts index d1757ab51e2cd..fabd880098f04 100644 --- a/src/plugins/files/server/routes/file_kind/enhance_router.ts +++ b/src/plugins/files/server/routes/file_kind/enhance_router.ts @@ -26,8 +26,7 @@ export function enhanceRouter({ router, fileKind }: Args): FileKindRouter { const handlerWrapper: (handler: FileKindHandler) => FileKindHandler = (handler) => async (ctx, req, res) => { return handler( - // @ts-expect-error upgrade typescript v4.9.5 - Object.create(ctx, { fileKind: { value: fileKind, enumerable: true, writeable: false } }), + Object.create(ctx, { fileKind: { value: fileKind, enumerable: true, writable: false } }), req, res ); diff --git a/src/plugins/files/server/routes/types.ts b/src/plugins/files/server/routes/types.ts index 58ddd8ca803c4..9a1332137c811 100644 --- a/src/plugins/files/server/routes/types.ts +++ b/src/plugins/files/server/routes/types.ts @@ -7,13 +7,15 @@ */ import type { - RequestHandlerContext, + HttpResponsePayload, + IKibanaResponse, IRouter, - RequestHandler, - RouteMethod, KibanaResponseFactory, - IKibanaResponse, Logger, + RequestHandler, + RequestHandlerContext, + ResponseError, + RouteMethod, } from '@kbn/core/server'; import type { SecurityPluginStart } from '@kbn/security-plugin/server'; import type { FileServiceStart } from '../file_service'; @@ -41,8 +43,9 @@ export type FilesRequestHandler< Method extends RouteMethod = any > = RequestHandler; -// @ts-expect-error upgrade typescript v4.9.5 -export type AsyncResponse = Promise>; +export type AsyncResponse = Promise< + IKibanaResponse +>; export type CreateHandler = FilesRequestHandler< E['inputs']['params'], diff --git a/src/plugins/management/common/locator.test.ts b/src/plugins/management/common/locator.test.ts index 20773b9732782..c45632a9ad776 100644 --- a/src/plugins/management/common/locator.test.ts +++ b/src/plugins/management/common/locator.test.ts @@ -6,8 +6,9 @@ * Side Public License, v 1. */ +import { MANAGEMENT_APP_LOCATOR } from '@kbn/deeplinks-management/constants'; import { MANAGEMENT_APP_ID } from './contants'; -import { ManagementAppLocatorDefinition, MANAGEMENT_APP_LOCATOR } from './locator'; +import { ManagementAppLocatorDefinition } from './locator'; test('locator has the right ID', () => { const locator = new ManagementAppLocatorDefinition(); diff --git a/src/plugins/management/common/locator.ts b/src/plugins/management/common/locator.ts index 5169919846d9d..73b1d63f6610e 100644 --- a/src/plugins/management/common/locator.ts +++ b/src/plugins/management/common/locator.ts @@ -8,10 +8,9 @@ import type { SerializableRecord } from '@kbn/utility-types'; import { LocatorDefinition, LocatorPublic } from '@kbn/share-plugin/common'; +import { MANAGEMENT_APP_LOCATOR } from '@kbn/deeplinks-management/constants'; import { MANAGEMENT_APP_ID } from './contants'; -export const MANAGEMENT_APP_LOCATOR = 'MANAGEMENT_APP_LOCATOR'; - export interface ManagementAppLocatorParams extends SerializableRecord { sectionId: string; appId?: string; diff --git a/src/plugins/management/tsconfig.json b/src/plugins/management/tsconfig.json index df0a876b4b3c4..116debbf72496 100644 --- a/src/plugins/management/tsconfig.json +++ b/src/plugins/management/tsconfig.json @@ -26,6 +26,7 @@ "@kbn/config-schema", "@kbn/serverless", "@kbn/shared-ux-error-boundary", + "@kbn/deeplinks-management", ], "exclude": [ "target/**/*" diff --git a/src/plugins/navigation/kibana.jsonc b/src/plugins/navigation/kibana.jsonc index 6a32cda8d0df0..3c0e6246e66a9 100644 --- a/src/plugins/navigation/kibana.jsonc +++ b/src/plugins/navigation/kibana.jsonc @@ -6,8 +6,8 @@ "id": "navigation", "server": true, "browser": true, - "optionalPlugins": ["cloud"], + "optionalPlugins": ["cloud","security"], "requiredPlugins": ["unifiedSearch"], - "requiredBundles": [] + "requiredBundles": ["kibanaReact"] } } diff --git a/src/plugins/navigation/public/mocks.ts b/src/plugins/navigation/public/mocks.ts index 3625be538f249..df1d34e15a807 100644 --- a/src/plugins/navigation/public/mocks.ts +++ b/src/plugins/navigation/public/mocks.ts @@ -6,6 +6,7 @@ * Side Public License, v 1. */ +import { of } from 'rxjs'; import { Plugin } from '.'; export type Setup = jest.Mocked>; @@ -27,7 +28,7 @@ const createStartContract = (): jest.Mocked => { AggregateQueryTopNavMenu: jest.fn(), }, addSolutionNavigation: jest.fn(), - isSolutionNavigationEnabled: jest.fn(), + isSolutionNavEnabled$: of(false), }; return startContract; }; diff --git a/src/plugins/navigation/public/plugin.test.ts b/src/plugins/navigation/public/plugin.test.ts index 5a30125f9e5ca..4e5579500d55f 100644 --- a/src/plugins/navigation/public/plugin.test.ts +++ b/src/plugins/navigation/public/plugin.test.ts @@ -8,8 +8,11 @@ import { coreMock } from '@kbn/core/public/mocks'; import { unifiedSearchPluginMock } from '@kbn/unified-search-plugin/public/mocks'; +import { securityMock } from '@kbn/security-plugin/public/mocks'; import { cloudMock } from '@kbn/cloud-plugin/public/mocks'; -import { of } from 'rxjs'; +import { firstValueFrom, of } from 'rxjs'; +import type { BuildFlavor } from '@kbn/config'; +import type { UserSettingsData } from '@kbn/user-profile-components'; import { DEFAULT_SOLUTION_NAV_UI_SETTING_ID, ENABLE_SOLUTION_NAV_UI_SETTING_ID, @@ -17,7 +20,15 @@ import { } from '../common'; import { NavigationPublicPlugin } from './plugin'; import { ConfigSchema } from './types'; -import type { BuildFlavor } from '@kbn/config'; +import { SolutionNavUserProfileToggle } from './solution_nav_userprofile_toggle'; + +jest.mock('rxjs', () => { + const original = jest.requireActual('rxjs'); + return { + ...original, + debounceTime: () => (source: any) => source, + }; +}); const defaultConfig: ConfigSchema['solutionNavigation'] = { featureOn: true, @@ -30,7 +41,10 @@ const setup = ( partialConfig: Partial & { featureOn: boolean; }, - { buildFlavor = 'traditional' }: { buildFlavor?: BuildFlavor } = {} + { + buildFlavor = 'traditional', + userSettings = {}, + }: { buildFlavor?: BuildFlavor; userSettings?: UserSettingsData } = {} ) => { const initializerContext = coreMock.createPluginInitializerContext( { @@ -46,6 +60,9 @@ const setup = ( const coreStart = coreMock.createStart(); const unifiedSearch = unifiedSearchPluginMock.createStartContract(); const cloud = cloudMock.createStart(); + const security = securityMock.createStart(); + security.userProfiles.userProfileLoaded$ = of(true); + security.userProfiles.userProfile$ = of({ userSettings }); const getGlobalSetting$ = jest.fn(); const settingsGlobalClient = { @@ -54,7 +71,7 @@ const setup = ( }; coreStart.settings.globalClient = settingsGlobalClient; - return { plugin, coreStart, unifiedSearch, cloud, getGlobalSetting$ }; + return { plugin, coreStart, unifiedSearch, cloud, security, getGlobalSetting$ }; }; describe('Navigation Plugin', () => { @@ -68,9 +85,12 @@ describe('Navigation Plugin', () => { expect(coreStart.chrome.project.changeActiveSolutionNavigation).not.toHaveBeenCalled(); }); - it('should return flag to indicate that the solution navigation is disabled', () => { + it('should return flag to indicate that the solution navigation is disabled', async () => { const { plugin, coreStart, unifiedSearch } = setup({ featureOn }); - expect(plugin.start(coreStart, { unifiedSearch }).isSolutionNavigationEnabled()).toBe(false); + const isEnabled = await firstValueFrom( + plugin.start(coreStart, { unifiedSearch }).isSolutionNavEnabled$ + ); + expect(isEnabled).toBe(false); }); }); @@ -146,21 +166,211 @@ describe('Navigation Plugin', () => { }); }); - it('should return flag to indicate that the solution navigation is enabled', () => { - const { plugin, coreStart, unifiedSearch, cloud } = setup({ featureOn }); - expect(plugin.start(coreStart, { unifiedSearch, cloud }).isSolutionNavigationEnabled()).toBe( - true - ); + it('should add the opt in/out toggle in the user menu', () => { + const { plugin, coreStart, unifiedSearch, cloud, security, getGlobalSetting$ } = setup({ + featureOn, + }); + + const uiSettingsValues: Record = { + [ENABLE_SOLUTION_NAV_UI_SETTING_ID]: true, + [OPT_IN_STATUS_SOLUTION_NAV_UI_SETTING_ID]: 'visible', + [DEFAULT_SOLUTION_NAV_UI_SETTING_ID]: 'es', + }; + + getGlobalSetting$.mockImplementation((settingId: string) => { + const value = uiSettingsValues[settingId] ?? 'unknown'; + return of(value); + }); + + plugin.start(coreStart, { unifiedSearch, cloud, security }); + + expect(security.navControlService.addUserMenuLinks).toHaveBeenCalled(); + const [menuLink] = security.navControlService.addUserMenuLinks.mock.calls[0][0]; + expect((menuLink.content as any)?.type).toBe(SolutionNavUserProfileToggle); }); - it('on serverless should return flag to indicate that the solution navigation is disabled', () => { - const { plugin, coreStart, unifiedSearch, cloud } = setup( - { featureOn }, - { buildFlavor: 'serverless' } - ); - expect(plugin.start(coreStart, { unifiedSearch, cloud }).isSolutionNavigationEnabled()).toBe( - false - ); + describe('isSolutionNavEnabled$', () => { + describe('user has not opted in or out (undefined)', () => { + const testCases: Array<[Record, string, boolean]> = [ + [ + { + [ENABLE_SOLUTION_NAV_UI_SETTING_ID]: true, + [OPT_IN_STATUS_SOLUTION_NAV_UI_SETTING_ID]: 'visible', + [DEFAULT_SOLUTION_NAV_UI_SETTING_ID]: 'es', + }, + 'should be enabled', + true, + ], + [ + { + [ENABLE_SOLUTION_NAV_UI_SETTING_ID]: false, // feature not enabled + [OPT_IN_STATUS_SOLUTION_NAV_UI_SETTING_ID]: 'visible', + [DEFAULT_SOLUTION_NAV_UI_SETTING_ID]: 'es', + }, + 'should not be enabled', + false, + ], + [ + { + [ENABLE_SOLUTION_NAV_UI_SETTING_ID]: true, + [OPT_IN_STATUS_SOLUTION_NAV_UI_SETTING_ID]: 'hidden', // not visible + [DEFAULT_SOLUTION_NAV_UI_SETTING_ID]: 'es', + }, + 'should not be enabled', + false, + ], + ]; + + testCases.forEach(([uiSettingsValues, description, expected]) => { + it(description, async () => { + const { plugin, coreStart, unifiedSearch, cloud, getGlobalSetting$ } = setup( + { + featureOn, + }, + { userSettings: { solutionNavOptOut: undefined } } // user has not opted in or out + ); + + getGlobalSetting$.mockImplementation((settingId: string) => { + const value = uiSettingsValues[settingId] ?? 'unknown'; + return of(value); + }); + + const { isSolutionNavEnabled$ } = plugin.start(coreStart, { unifiedSearch, cloud }); + expect(await firstValueFrom(isSolutionNavEnabled$)).toBe(expected); + }); + }); + }); + + describe('user has opted in', () => { + const testCases: Array<[Record, string, boolean]> = [ + [ + { + [ENABLE_SOLUTION_NAV_UI_SETTING_ID]: true, + [OPT_IN_STATUS_SOLUTION_NAV_UI_SETTING_ID]: 'visible', + [DEFAULT_SOLUTION_NAV_UI_SETTING_ID]: 'es', + }, + 'should be enabled', + true, + ], + [ + { + [ENABLE_SOLUTION_NAV_UI_SETTING_ID]: false, // feature not enabled + [OPT_IN_STATUS_SOLUTION_NAV_UI_SETTING_ID]: 'visible', + [DEFAULT_SOLUTION_NAV_UI_SETTING_ID]: 'es', + }, + 'should not be enabled', + false, + ], + [ + { + [ENABLE_SOLUTION_NAV_UI_SETTING_ID]: true, + [OPT_IN_STATUS_SOLUTION_NAV_UI_SETTING_ID]: 'hidden', // not visible + [DEFAULT_SOLUTION_NAV_UI_SETTING_ID]: 'es', + }, + 'should be enabled', + true, + ], + ]; + + testCases.forEach(([uiSettingsValues, description, expected]) => { + it(description, async () => { + const { plugin, coreStart, unifiedSearch, cloud, security, getGlobalSetting$ } = setup( + { + featureOn, + }, + { userSettings: { solutionNavOptOut: false } } // user has opted in + ); + + getGlobalSetting$.mockImplementation((settingId: string) => { + const value = uiSettingsValues[settingId] ?? 'unknown'; + return of(value); + }); + + const { isSolutionNavEnabled$ } = plugin.start(coreStart, { + security, + unifiedSearch, + cloud, + }); + expect(await firstValueFrom(isSolutionNavEnabled$)).toBe(expected); + }); + }); + }); + + describe('user has opted out', () => { + const testCases: Array<[Record, string, boolean]> = [ + [ + { + [ENABLE_SOLUTION_NAV_UI_SETTING_ID]: true, + [OPT_IN_STATUS_SOLUTION_NAV_UI_SETTING_ID]: 'visible', + [DEFAULT_SOLUTION_NAV_UI_SETTING_ID]: 'es', + }, + 'should not be enabled', + false, + ], + [ + { + [ENABLE_SOLUTION_NAV_UI_SETTING_ID]: false, // feature not enabled + [OPT_IN_STATUS_SOLUTION_NAV_UI_SETTING_ID]: 'visible', + [DEFAULT_SOLUTION_NAV_UI_SETTING_ID]: 'es', + }, + 'should not be enabled', + false, + ], + [ + { + [ENABLE_SOLUTION_NAV_UI_SETTING_ID]: true, + [OPT_IN_STATUS_SOLUTION_NAV_UI_SETTING_ID]: 'hidden', + [DEFAULT_SOLUTION_NAV_UI_SETTING_ID]: 'es', + }, + 'should not be enabled', + false, + ], + ]; + + testCases.forEach(([uiSettingsValues, description, expected]) => { + it(description, async () => { + const { plugin, coreStart, unifiedSearch, cloud, security, getGlobalSetting$ } = setup( + { + featureOn, + }, + { userSettings: { solutionNavOptOut: true } } // user has opted out + ); + + getGlobalSetting$.mockImplementation((settingId: string) => { + const value = uiSettingsValues[settingId] ?? 'unknown'; + return of(value); + }); + + const { isSolutionNavEnabled$ } = plugin.start(coreStart, { + security, + unifiedSearch, + cloud, + }); + expect(await firstValueFrom(isSolutionNavEnabled$)).toBe(expected); + }); + }); + }); + + it('on serverless should flag must be disabled', async () => { + const { plugin, coreStart, unifiedSearch, cloud, getGlobalSetting$ } = setup( + { featureOn }, + { buildFlavor: 'serverless' } + ); + const uiSettingsValues: Record = { + [ENABLE_SOLUTION_NAV_UI_SETTING_ID]: true, // enabled, but we are on serverless + [OPT_IN_STATUS_SOLUTION_NAV_UI_SETTING_ID]: 'visible', // should not matter + [DEFAULT_SOLUTION_NAV_UI_SETTING_ID]: 'es', + }; + + getGlobalSetting$.mockImplementation((settingId: string) => { + const value = uiSettingsValues[settingId] ?? 'unknown'; + return of(value); + }); + + const { isSolutionNavEnabled$ } = plugin.start(coreStart, { unifiedSearch, cloud }); + const isEnabled = await firstValueFrom(isSolutionNavEnabled$); + expect(isEnabled).toBe(false); + }); }); }); }); diff --git a/src/plugins/navigation/public/plugin.tsx b/src/plugins/navigation/public/plugin.tsx index c4b91615e3970..8dcf871f290b0 100644 --- a/src/plugins/navigation/public/plugin.tsx +++ b/src/plugins/navigation/public/plugin.tsx @@ -6,9 +6,21 @@ * Side Public License, v 1. */ import React from 'react'; -import { combineLatest, debounceTime, ReplaySubject, takeUntil } from 'rxjs'; +import { + combineLatest, + debounceTime, + distinctUntilChanged, + map, + Observable, + of, + ReplaySubject, + skipWhile, + switchMap, + takeUntil, +} from 'rxjs'; import { PluginInitializerContext, CoreSetup, CoreStart, Plugin } from '@kbn/core/public'; import type { UnifiedSearchPublicPluginStart } from '@kbn/unified-search-plugin/public'; +import type { SecurityPluginStart, UserMenuLink } from '@kbn/security-plugin/public'; import type { SolutionNavigationDefinition, SolutionNavigationDefinitions, @@ -17,22 +29,28 @@ import { InternalChromeStart } from '@kbn/core-chrome-browser-internal'; import { definition as esDefinition } from '@kbn/solution-nav-es'; import { definition as obltDefinition } from '@kbn/solution-nav-oblt'; import type { PanelContentProvider } from '@kbn/shared-ux-chrome-navigation'; +import { UserProfileData } from '@kbn/user-profile-components'; import { ENABLE_SOLUTION_NAV_UI_SETTING_ID, OPT_IN_STATUS_SOLUTION_NAV_UI_SETTING_ID, DEFAULT_SOLUTION_NAV_UI_SETTING_ID, } from '../common'; -import { +import type { NavigationPublicSetup, NavigationPublicStart, NavigationPublicSetupDependencies, NavigationPublicStartDependencies, ConfigSchema, SolutionNavigation, + SolutionNavigationOptInStatus, + SolutionType, } from './types'; import { TopNavMenuExtensionsRegistry, createTopNav } from './top_nav_menu'; import { RegisteredTopNavMenuData } from './top_nav_menu/top_nav_menu_data'; import { SideNavComponent } from './side_navigation'; +import { SolutionNavUserProfileToggle } from './solution_nav_userprofile_toggle'; + +const DEFAULT_OPT_OUT_NEW_NAV = false; export class NavigationPublicPlugin implements @@ -48,6 +66,9 @@ export class NavigationPublicPlugin private readonly stop$ = new ReplaySubject(1); private coreStart?: CoreStart; private depsStart?: NavigationPublicStartDependencies; + private isSolutionNavEnabled$ = of(false); + private userProfileOptOut$: Observable = of(undefined); + private userProfileMenuItemAdded = false; constructor(private initializerContext: PluginInitializerContext) {} @@ -66,10 +87,25 @@ export class NavigationPublicPlugin this.coreStart = core; this.depsStart = depsStart; - const { unifiedSearch, cloud } = depsStart; + const { unifiedSearch, cloud, security } = depsStart; const extensions = this.topNavMenuExtensionsRegistry.getAll(); const chrome = core.chrome as InternalChromeStart; + if (security) { + this.userProfileOptOut$ = security.userProfiles.userProfileLoaded$.pipe( + skipWhile((loaded) => { + return !loaded; + }), + switchMap(() => { + return security.userProfiles.userProfile$ as Observable; + }), + map((profile) => { + return profile?.userSettings?.solutionNavOptOut; + }), + distinctUntilChanged() + ); + } + /* * * This helps clients of navigation to create @@ -98,11 +134,31 @@ export class NavigationPublicPlugin const onCloud = cloud !== undefined; // The new side nav will initially only be available to cloud users const isServerless = this.initializerContext.env.packageInfo.buildFlavor === 'serverless'; const isSolutionNavEnabled = isSolutionNavigationFeatureOn && onCloud && !isServerless; + this.isSolutionNavEnabled$ = of(isSolutionNavEnabled); if (isSolutionNavEnabled) { chrome.project.setCloudUrls(cloud); this.addDefaultSolutionNavigation({ chrome }); - this.susbcribeToSolutionNavUiSettings(core); + + this.isSolutionNavEnabled$ = combineLatest([ + core.settings.globalClient.get$(ENABLE_SOLUTION_NAV_UI_SETTING_ID), + core.settings.globalClient.get$( + OPT_IN_STATUS_SOLUTION_NAV_UI_SETTING_ID + ), + this.userProfileOptOut$, + ]).pipe( + takeUntil(this.stop$), + debounceTime(10), + map(([enabled, status, userOptedOut]) => { + if (!enabled || userOptedOut === true) return false; + if (status === 'hidden' && userOptedOut === undefined) return false; + return true; + }) + ); + + this.susbcribeToSolutionNavUiSettings({ core, security }); + } else if (!isServerless) { + chrome.setChromeStyle('classic'); } return { @@ -122,7 +178,7 @@ export class NavigationPublicPlugin if (!isSolutionNavEnabled) return; return this.addSolutionNavigation(solutionNavigation); }, - isSolutionNavigationEnabled: () => isSolutionNavEnabled, + isSolutionNavEnabled$: this.isSolutionNavEnabled$, }; } @@ -130,23 +186,46 @@ export class NavigationPublicPlugin this.stop$.next(); } - private susbcribeToSolutionNavUiSettings(core: CoreStart) { + private susbcribeToSolutionNavUiSettings({ + core, + security, + }: { + core: CoreStart; + security?: SecurityPluginStart; + }) { const chrome = core.chrome as InternalChromeStart; combineLatest([ - core.settings.globalClient.get$(ENABLE_SOLUTION_NAV_UI_SETTING_ID), - core.settings.globalClient.get$(OPT_IN_STATUS_SOLUTION_NAV_UI_SETTING_ID), - core.settings.globalClient.get$(DEFAULT_SOLUTION_NAV_UI_SETTING_ID), + core.settings.globalClient.get$(ENABLE_SOLUTION_NAV_UI_SETTING_ID), + core.settings.globalClient.get$( + OPT_IN_STATUS_SOLUTION_NAV_UI_SETTING_ID + ), + core.settings.globalClient.get$(DEFAULT_SOLUTION_NAV_UI_SETTING_ID), + this.userProfileOptOut$, ]) .pipe(takeUntil(this.stop$), debounceTime(10)) - .subscribe(([enabled, status, defaultSolution]) => { - if (!enabled) { + .subscribe(([enabled, status, defaultSolution, userOptedOut]) => { + if (enabled) { + // Add menu item in the user profile menu to opt in/out of the new navigation + this.addOptInOutUserProfile({ core, security, optInStatusSetting: status, userOptedOut }); + } else { + // TODO. Remove the user profile menu item if the feature is disabled. + // But first let's wait as maybe there will be a page refresh when opting out. + } + + if (!enabled || userOptedOut === true) { chrome.project.changeActiveSolutionNavigation(null); + chrome.setChromeStyle('classic'); } else { - // TODO: Here we will need to check if the user has opt-in or not.... (value set in their user profile) - const changeImmediately = status === 'visible'; + const changeToSolutionNav = + status === 'visible' || (status === 'hidden' && userOptedOut === false); + + if (!changeToSolutionNav) { + chrome.setChromeStyle('classic'); + } + chrome.project.changeActiveSolutionNavigation( - changeImmediately ? defaultSolution : null, + changeToSolutionNav ? defaultSolution : null, { onlyIfNotSet: true } ); } @@ -209,4 +288,42 @@ export class NavigationPublicPlugin chrome.project.updateSolutionNavigations(solutionNavs, true); } + + private addOptInOutUserProfile({ + core, + security, + optInStatusSetting, + userOptedOut, + }: { + core: CoreStart; + userOptedOut?: boolean; + optInStatusSetting?: SolutionNavigationOptInStatus; + security?: SecurityPluginStart; + }) { + if (!security || this.userProfileMenuItemAdded) return; + + let defaultOptOutValue = userOptedOut !== undefined ? userOptedOut : DEFAULT_OPT_OUT_NEW_NAV; + if (optInStatusSetting === 'visible' && userOptedOut === undefined) { + defaultOptOutValue = false; + } else if (optInStatusSetting === 'hidden' && userOptedOut === undefined) { + defaultOptOutValue = true; + } + + const menuLink: UserMenuLink = { + content: ( + + ), + order: 500, + label: '', + iconType: '', + href: '', + }; + + security.navControlService.addUserMenuLinks([menuLink]); + this.userProfileMenuItemAdded = true; + } } diff --git a/src/plugins/navigation/public/solution_nav_userprofile_toggle/index.ts b/src/plugins/navigation/public/solution_nav_userprofile_toggle/index.ts new file mode 100644 index 0000000000000..72d79d0de52a2 --- /dev/null +++ b/src/plugins/navigation/public/solution_nav_userprofile_toggle/index.ts @@ -0,0 +1,9 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +export { SolutionNavUserProfileToggle } from './solution_nav_userprofile_toggle'; diff --git a/src/plugins/navigation/public/solution_nav_userprofile_toggle/solution_nav_userprofile_toggle.test.tsx b/src/plugins/navigation/public/solution_nav_userprofile_toggle/solution_nav_userprofile_toggle.test.tsx new file mode 100644 index 0000000000000..13dec96df0d16 --- /dev/null +++ b/src/plugins/navigation/public/solution_nav_userprofile_toggle/solution_nav_userprofile_toggle.test.tsx @@ -0,0 +1,86 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import React from 'react'; +import { of } from 'rxjs'; +import { render, fireEvent } from '@testing-library/react'; +import '@testing-library/jest-dom'; +import { coreMock } from '@kbn/core/public/mocks'; +import { securityMock } from '@kbn/security-plugin/public/mocks'; + +import { SolutionNavUserProfileToggle } from './solution_nav_userprofile_toggle'; + +const mockUseUpdateUserProfile = jest.fn(); + +jest.mock('@kbn/user-profile-components', () => { + const original = jest.requireActual('@kbn/user-profile-components'); + return { + ...original, + useUpdateUserProfile: () => mockUseUpdateUserProfile(), + }; +}); + +describe('SolutionNavUserProfileToggle', () => { + it('renders correctly and toggles opt out of new nav', () => { + const security = securityMock.createStart(); + const core = coreMock.createStart(); + + const mockUpdate = jest.fn(); + mockUseUpdateUserProfile.mockReturnValue({ + userProfileData: { userSettings: { solutionNavOptOut: undefined } }, + isLoading: false, + update: mockUpdate, + userProfileEnabled: true, + }); + + const { getByTestId, rerender } = render( + + ); + + const toggleSwitch = getByTestId('solutionNavToggleSwitch'); + fireEvent.click(toggleSwitch); + expect(mockUpdate).toHaveBeenCalledWith({ userSettings: { solutionNavOptOut: true } }); + + // Now we want to simulate toggling back to light + mockUseUpdateUserProfile.mockReturnValue({ + userProfileData: { userSettings: { solutionNavOptOut: true } }, + isLoading: false, + update: mockUpdate, + userProfileEnabled: true, + }); + + // Rerender the component to apply the new props + rerender( + + ); + + fireEvent.click(toggleSwitch); + expect(mockUpdate).toHaveBeenLastCalledWith({ userSettings: { solutionNavOptOut: false } }); + }); + + it('does not render if user profile is disabled', async () => { + const security = securityMock.createStart(); + security.userProfiles.enabled$ = of(false); + const core = coreMock.createStart(); + + const mockUpdate = jest.fn(); + mockUseUpdateUserProfile.mockReturnValue({ + userProfileData: { userSettings: { solutionNavOptOut: undefined } }, + isLoading: false, + update: mockUpdate, + userProfileEnabled: false, + }); + + const { queryByTestId } = render( + + ); + const toggleSwitch = await queryByTestId('solutionNavToggleSwitch'); + + expect(toggleSwitch).toBeNull(); + }); +}); diff --git a/src/plugins/navigation/public/solution_nav_userprofile_toggle/solution_nav_userprofile_toggle.tsx b/src/plugins/navigation/public/solution_nav_userprofile_toggle/solution_nav_userprofile_toggle.tsx new file mode 100644 index 0000000000000..ac33ce5d6e860 --- /dev/null +++ b/src/plugins/navigation/public/solution_nav_userprofile_toggle/solution_nav_userprofile_toggle.tsx @@ -0,0 +1,91 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import React from 'react'; +import { + EuiContextMenuItem, + EuiFlexGroup, + EuiFlexItem, + EuiSwitch, + useEuiTheme, + useGeneratedHtmlId, +} from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; +import type { SecurityPluginStart } from '@kbn/security-plugin/public'; +import { UserProfilesKibanaProvider } from '@kbn/user-profile-components'; +import { CoreStart } from '@kbn/core-lifecycle-browser'; +import { toMountPoint } from '@kbn/kibana-react-plugin/public'; + +import { useSolutionNavUserProfileToggle } from './use_solution_nav_userprofile_toggle'; + +interface Props { + security: SecurityPluginStart; + core: CoreStart; + defaultOptOutValue: boolean; +} + +export const SolutionNavUserProfileToggle = ({ security, core, defaultOptOutValue }: Props) => { + return ( + + + + ); +}; + +function SolutionNavUserProfileToggleUi({ defaultOptOutValue }: { defaultOptOutValue: boolean }) { + const toggleTextSwitchId = useGeneratedHtmlId({ prefix: 'toggleSolutionNavSwitch' }); + const { euiTheme } = useEuiTheme(); + + const { userProfileEnabled, toggle, hasOptOut } = useSolutionNavUserProfileToggle({ + defaultOptOutValue, + }); + + if (!userProfileEnabled) { + return null; + } + + return ( + + + { + toggle(!hasOptOut); + }} + data-test-subj="solutionNavToggle" + > + {i18n.translate('navigation.userMenuLinks.useClassicNavigation', { + defaultMessage: 'Use classic navigation', + })} + + + + { + toggle(e.target.checked); + }} + aria-describedby={toggleTextSwitchId} + data-test-subj="solutionNavToggleSwitch" + compressed + /> + + + ); +} diff --git a/src/plugins/navigation/public/solution_nav_userprofile_toggle/use_solution_nav_userprofile_toggle.ts b/src/plugins/navigation/public/solution_nav_userprofile_toggle/use_solution_nav_userprofile_toggle.ts new file mode 100644 index 0000000000000..3f1a28c843ff0 --- /dev/null +++ b/src/plugins/navigation/public/solution_nav_userprofile_toggle/use_solution_nav_userprofile_toggle.ts @@ -0,0 +1,49 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { useCallback, useEffect, useState } from 'react'; +import { useUpdateUserProfile } from '@kbn/user-profile-components'; + +interface Deps { + defaultOptOutValue: boolean; +} + +export const useSolutionNavUserProfileToggle = ({ defaultOptOutValue }: Deps) => { + const [hasOptOut, setHasOptOut] = useState(defaultOptOutValue); + const { userProfileData, isLoading, update, userProfileEnabled } = useUpdateUserProfile(); + + const { userSettings: { solutionNavOptOut = defaultOptOutValue } = {} } = userProfileData ?? {}; + + const toggle = useCallback( + (on: boolean) => { + if (isLoading) { + return; + } + + // optimistic update + setHasOptOut(on); + + update({ + userSettings: { + solutionNavOptOut: on, + }, + }); + }, + [isLoading, update] + ); + + useEffect(() => { + setHasOptOut(solutionNavOptOut); + }, [solutionNavOptOut]); + + return { + toggle, + hasOptOut, + userProfileEnabled, + }; +}; diff --git a/src/plugins/navigation/public/types.ts b/src/plugins/navigation/public/types.ts index b7bfaab6a2957..e9a7c298c1a97 100644 --- a/src/plugins/navigation/public/types.ts +++ b/src/plugins/navigation/public/types.ts @@ -6,13 +6,15 @@ * Side Public License, v 1. */ -import { AggregateQuery, Query } from '@kbn/es-query'; -import { UnifiedSearchPublicPluginStart } from '@kbn/unified-search-plugin/public'; +import type { AggregateQuery, Query } from '@kbn/es-query'; +import type { Observable } from 'rxjs'; +import type { UnifiedSearchPublicPluginStart } from '@kbn/unified-search-plugin/public'; import type { SolutionNavigationDefinition } from '@kbn/core-chrome-browser'; import type { CloudSetup, CloudStart } from '@kbn/cloud-plugin/public'; +import type { SecurityPluginSetup, SecurityPluginStart } from '@kbn/security-plugin/public'; import { TopNavMenuProps, TopNavMenuExtensionsRegistrySetup, createTopNav } from './top_nav_menu'; -import { RegisteredTopNavMenuData } from './top_nav_menu/top_nav_menu_data'; +import type { RegisteredTopNavMenuData } from './top_nav_menu/top_nav_menu_data'; export interface NavigationPublicSetup { registerMenuItem: TopNavMenuExtensionsRegistrySetup['register']; @@ -31,20 +33,19 @@ export interface NavigationPublicStart { }; /** Add a solution navigation to the header nav switcher. */ addSolutionNavigation: (solutionNavigation: SolutionNavigation) => void; - /** - * Use this handler verify if the solution navigation is enabled. - * @returns true if the solution navigation is enabled, false otherwise. - */ - isSolutionNavigationEnabled: () => boolean; + /** Flag to indicate if the solution navigation is enabled.*/ + isSolutionNavEnabled$: Observable; } export interface NavigationPublicSetupDependencies { cloud?: CloudSetup; + security?: SecurityPluginSetup; } export interface NavigationPublicStartDependencies { unifiedSearch: UnifiedSearchPublicPluginStart; cloud?: CloudStart; + security?: SecurityPluginStart; } export type SolutionNavigationOptInStatus = 'visible' | 'hidden' | 'ask'; diff --git a/src/plugins/navigation/tsconfig.json b/src/plugins/navigation/tsconfig.json index 1450b9f0aa1a7..197f50d1d568f 100644 --- a/src/plugins/navigation/tsconfig.json +++ b/src/plugins/navigation/tsconfig.json @@ -27,6 +27,10 @@ "@kbn/solution-nav-es", "@kbn/solution-nav-oblt", "@kbn/config", + "@kbn/kibana-react-plugin", + "@kbn/security-plugin", + "@kbn/user-profile-components", + "@kbn/core-lifecycle-browser", ], "exclude": [ "target/**/*", diff --git a/test/functional/apps/discover/group4/_runtime_fields_editor.ts b/test/functional/apps/discover/group4/_runtime_fields_editor.ts index 7c084f64dc72a..1e79a2b829fa4 100644 --- a/test/functional/apps/discover/group4/_runtime_fields_editor.ts +++ b/test/functional/apps/discover/group4/_runtime_fields_editor.ts @@ -36,12 +36,11 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { await fieldEditor.enableValue(); await fieldEditor.typeScript("emit('abc')"); await fieldEditor.save(); + await fieldEditor.waitUntilClosed(); await PageObjects.header.waitUntilLoadingHasFinished(); }; - // Failing: See https://github.com/elastic/kibana/issues/179297 - // FLAKY: https://github.com/elastic/kibana/issues/179310 - describe.skip('discover integration with runtime fields editor', function describeIndexTests() { + describe('discover integration with runtime fields editor', function describeIndexTests() { before(async function () { await security.testUser.setRoles(['kibana_admin', 'test_logstash_reader']); await esArchiver.loadIfNeeded('test/functional/fixtures/es_archiver/logstash_functional'); @@ -54,7 +53,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { after(async () => { await security.testUser.restoreDefaults(); await kibanaServer.importExport.unload('test/functional/fixtures/kbn_archiver/discover'); - await kibanaServer.savedObjects.clean({ types: ['saved-search'] }); + await kibanaServer.savedObjects.cleanStandardList(); }); it('allows adding custom label to existing fields', async function () { @@ -63,6 +62,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { await fieldEditor.enableCustomLabel(); await fieldEditor.setCustomLabel(customLabel); await fieldEditor.save(); + await fieldEditor.waitUntilClosed(); await PageObjects.header.waitUntilLoadingHasFinished(); expect((await PageObjects.unifiedFieldList.getAllFieldNames()).includes(customLabel)).to.be( true @@ -72,38 +72,42 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { }); it('allows adding custom description to existing fields', async function () { - const customDescription = 'custom bytes description here'; + const customDescription = 'custom agent description here'; const customDescription2 = `${customDescription} updated`; // set a custom description - await PageObjects.discover.editField('bytes'); + await PageObjects.discover.editField('agent'); await fieldEditor.enableCustomDescription(); await fieldEditor.setCustomDescription(customDescription); await fieldEditor.save(); + await fieldEditor.waitUntilClosed(); await PageObjects.header.waitUntilLoadingHasFinished(); - await PageObjects.unifiedFieldList.clickFieldListItem('bytes'); + await PageObjects.unifiedFieldList.clickFieldListItem('agent'); await retry.waitFor('field popover text', async () => { - return (await testSubjects.getVisibleText('fieldDescription-bytes')) === customDescription; + return (await testSubjects.getVisibleText('fieldDescription-agent')) === customDescription; }); - await PageObjects.unifiedFieldList.clickFieldListItemToggle('bytes'); + await PageObjects.unifiedFieldList.clickFieldListItemToggle('agent'); - // edit the custom description - await PageObjects.discover.editField('bytes'); + await PageObjects.header.waitUntilLoadingHasFinished(); + await PageObjects.discover.waitUntilSearchingHasFinished(); + + // edit the custom description again + await PageObjects.discover.editField('agent'); await fieldEditor.enableCustomDescription(); await fieldEditor.setCustomDescription(customDescription2); await fieldEditor.save(); await fieldEditor.waitUntilClosed(); await PageObjects.header.waitUntilLoadingHasFinished(); - await PageObjects.unifiedFieldList.clickFieldListItem('bytes'); + await PageObjects.unifiedFieldList.clickFieldListItem('agent'); await retry.waitFor('field popover text', async () => { - return (await testSubjects.getVisibleText('fieldDescription-bytes')) === customDescription2; + return (await testSubjects.getVisibleText('fieldDescription-agent')) === customDescription2; }); - await PageObjects.unifiedFieldList.clickFieldListItemToggle('bytes'); + await PageObjects.unifiedFieldList.clickFieldListItemToggle('agent'); // check it in the doc viewer too await dataGrid.clickRowToggle({ rowIndex: 0 }); - await testSubjects.click('fieldDescriptionPopoverButton-bytes'); + await testSubjects.click('fieldDescriptionPopoverButton-agent'); await retry.waitFor('doc viewer popover text', async () => { - return (await testSubjects.getVisibleText('fieldDescription-bytes')) === customDescription2; + return (await testSubjects.getVisibleText('fieldDescription-agent')) === customDescription2; }); await dataGrid.closeFlyout(); @@ -125,7 +129,12 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { it('allows creation of a new field', async function () { const field = '_runtimefield'; await createRuntimeField(field); - await retry.waitForWithTimeout('fieldNames to include runtimefield', 5000, async () => { + + await PageObjects.header.waitUntilLoadingHasFinished(); + await PageObjects.discover.waitForDocTableLoadingComplete(); + await PageObjects.unifiedFieldList.waitUntilSidebarHasLoaded(); + + await retry.waitFor('fieldNames to include runtimefield', async () => { const fieldNames = await PageObjects.unifiedFieldList.getAllFieldNames(); return fieldNames.includes(field); }); @@ -139,8 +148,10 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { await fieldEditor.setName(newFieldName, true); await fieldEditor.save(); await fieldEditor.confirmSave(); + await fieldEditor.waitUntilClosed(); await PageObjects.header.waitUntilLoadingHasFinished(); await PageObjects.discover.waitForDocTableLoadingComplete(); + await PageObjects.unifiedFieldList.waitUntilSidebarHasLoaded(); await retry.waitForWithTimeout('fieldNames to include edits', 5000, async () => { const fieldNames = await PageObjects.unifiedFieldList.getAllFieldNames(); diff --git a/x-pack/examples/triggers_actions_ui_example/kibana.jsonc b/x-pack/examples/triggers_actions_ui_example/kibana.jsonc index 9de248572a5bb..e3e6c8adc4a97 100644 --- a/x-pack/examples/triggers_actions_ui_example/kibana.jsonc +++ b/x-pack/examples/triggers_actions_ui_example/kibana.jsonc @@ -4,7 +4,7 @@ "owner": "@elastic/response-ops", "plugin": { "id": "triggersActionsUiExample", - "server": false, + "server": true, "browser": true, "requiredPlugins": [ "triggersActionsUi", @@ -12,10 +12,9 @@ "alerting", "developerExamples", "kibanaReact", - "cases" - ], - "optionalPlugins": [ - "spaces" + "cases", + "actions" ], + "optionalPlugins": ["spaces"] } } diff --git a/x-pack/examples/triggers_actions_ui_example/public/connector_types/system_log_example/system_log_example.tsx b/x-pack/examples/triggers_actions_ui_example/public/connector_types/system_log_example/system_log_example.tsx new file mode 100644 index 0000000000000..6992cda11c09f --- /dev/null +++ b/x-pack/examples/triggers_actions_ui_example/public/connector_types/system_log_example/system_log_example.tsx @@ -0,0 +1,55 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { lazy } from 'react'; +import { i18n } from '@kbn/i18n'; +import type { + ActionTypeModel as ConnectorTypeModel, + GenericValidationResult, +} from '@kbn/triggers-actions-ui-plugin/public/types'; +import { SystemLogActionParams } from '../types'; + +export function getConnectorType(): ConnectorTypeModel { + return { + id: '.system-log-example', + iconClass: 'logsApp', + selectMessage: i18n.translate( + 'xpack.stackConnectors.components.systemLogExample.selectMessageText', + { + defaultMessage: 'Example of a system action that sends logs to the Kibana server', + } + ), + actionTypeTitle: i18n.translate( + 'xpack.stackConnectors.components.serverLog.connectorTypeTitle', + { + defaultMessage: 'Send to System log - Example', + } + ), + validateParams: ( + actionParams: SystemLogActionParams + ): Promise>> => { + const errors = { + message: new Array(), + }; + const validationResult = { errors }; + if (!actionParams.message?.length) { + errors.message.push( + i18n.translate( + 'xpack.stackConnectors.components.serverLog.error.requiredServerLogMessageText', + { + defaultMessage: 'Message is required.', + } + ) + ); + } + return Promise.resolve(validationResult); + }, + actionConnectorFields: null, + actionParamsFields: lazy(() => import('./system_log_example_params')), + isSystemActionType: true, + }; +} diff --git a/x-pack/examples/triggers_actions_ui_example/public/connector_types/system_log_example/system_log_example_params.tsx b/x-pack/examples/triggers_actions_ui_example/public/connector_types/system_log_example/system_log_example_params.tsx new file mode 100644 index 0000000000000..199b86b3ac5ce --- /dev/null +++ b/x-pack/examples/triggers_actions_ui_example/public/connector_types/system_log_example/system_log_example_params.tsx @@ -0,0 +1,65 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React, { useEffect, useState } from 'react'; +import { i18n } from '@kbn/i18n'; +import type { ActionParamsProps } from '@kbn/triggers-actions-ui-plugin/public'; +import { TextAreaWithMessageVariables } from '@kbn/triggers-actions-ui-plugin/public'; +import { SystemLogActionParams } from '../types'; + +export const ServerLogParamsFields: React.FunctionComponent< + ActionParamsProps +> = ({ + actionParams, + editAction, + index, + errors, + messageVariables, + defaultMessage, + useDefaultMessage, +}) => { + const { message } = actionParams; + + const [[isUsingDefault, defaultMessageUsed], setDefaultMessageUsage] = useState< + [boolean, string | undefined] + >([false, defaultMessage]); + // This params component is derived primarily from server_log_params.tsx, see that file and its + // corresponding unit tests for details on functionality + useEffect(() => { + if ( + useDefaultMessage || + !actionParams?.message || + (isUsingDefault && + actionParams?.message === defaultMessageUsed && + defaultMessageUsed !== defaultMessage) + ) { + setDefaultMessageUsage([true, defaultMessage]); + editAction('message', defaultMessage, index); + } + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [defaultMessage]); + + return ( + + ); +}; + +// eslint-disable-next-line import/no-default-export +export { ServerLogParamsFields as default }; diff --git a/x-pack/plugins/alerting/server/application/alerts_filter_query/constants.ts b/x-pack/examples/triggers_actions_ui_example/public/connector_types/types/index.ts similarity index 57% rename from x-pack/plugins/alerting/server/application/alerts_filter_query/constants.ts rename to x-pack/examples/triggers_actions_ui_example/public/connector_types/types/index.ts index bce6890c22f2c..98181b856c3fa 100644 --- a/x-pack/plugins/alerting/server/application/alerts_filter_query/constants.ts +++ b/x-pack/examples/triggers_actions_ui_example/public/connector_types/types/index.ts @@ -5,9 +5,6 @@ * 2.0. */ -export const filterStateStore = { - APP_STATE: 'appState', - GLOBAL_STATE: 'globalState', -} as const; - -export type FilterStateStore = typeof filterStateStore[keyof typeof filterStateStore]; +export interface SystemLogActionParams { + message: string; +} diff --git a/x-pack/examples/triggers_actions_ui_example/public/plugin.tsx b/x-pack/examples/triggers_actions_ui_example/public/plugin.tsx index 0ee31733b6a06..ee5c3229cb713 100644 --- a/x-pack/examples/triggers_actions_ui_example/public/plugin.tsx +++ b/x-pack/examples/triggers_actions_ui_example/public/plugin.tsx @@ -23,6 +23,7 @@ import { } from '@kbn/triggers-actions-ui-plugin/public/types'; import { SortCombinations } from '@elastic/elasticsearch/lib/api/types'; import { EuiDataGridColumn } from '@elastic/eui'; +import { getConnectorType as getSystemLogExampleConnectorType } from './connector_types/system_log_example/system_log_example'; export interface TriggersActionsUiExamplePublicSetupDeps { alerting: AlertingSetup; @@ -145,6 +146,8 @@ export class TriggersActionsUiExamplePlugin }; alertsTableConfigurationRegistry.register(config); + + triggersActionsUi.actionTypeRegistry.register(getSystemLogExampleConnectorType()); } public stop() {} diff --git a/x-pack/examples/triggers_actions_ui_example/server/connector_types/system_log_example.ts b/x-pack/examples/triggers_actions_ui_example/server/connector_types/system_log_example.ts new file mode 100644 index 0000000000000..e952a7ed01195 --- /dev/null +++ b/x-pack/examples/triggers_actions_ui_example/server/connector_types/system_log_example.ts @@ -0,0 +1,99 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { i18n } from '@kbn/i18n'; +import { schema, TypeOf } from '@kbn/config-schema'; + +import { LogMeta } from '@kbn/core/server'; +import type { + ActionType as ConnectorType, + ActionTypeExecutorOptions as ConnectorTypeExecutorOptions, + ActionTypeExecutorResult as ConnectorTypeExecutorResult, +} from '@kbn/actions-plugin/server/types'; +import { + AlertingConnectorFeatureId, + UptimeConnectorFeatureId, +} from '@kbn/actions-plugin/common/connector_feature_config'; +import { ConnectorAdapter } from '@kbn/alerting-plugin/server'; + +// see: https://en.wikipedia.org/wiki/Unicode_control_characters +// but don't include tabs (0x09), they're fine +const CONTROL_CHAR_PATTERN = /[\x00-\x08]|[\x0A-\x1F]|[\x7F-\x9F]|[\u2028-\u2029]/g; + +// replaces control characters in string with ;, but leaves tabs +function withoutControlCharacters(s: string): string { + return s.replace(CONTROL_CHAR_PATTERN, ';'); +} + +export type ServerLogConnectorType = ConnectorType<{}, {}, ActionParamsType>; +export type ServerLogConnectorTypeExecutorOptions = ConnectorTypeExecutorOptions< + {}, + {}, + ActionParamsType +>; + +// params definition + +export type ActionParamsType = TypeOf; + +const ParamsSchema = schema.object({ + message: schema.string(), +}); + +export const ConnectorTypeId = '.system-log-example'; +// connector type definition +export function getConnectorType(): ServerLogConnectorType { + return { + id: ConnectorTypeId, + isSystemActionType: true, + minimumLicenseRequired: 'gold', // Third party action types require at least gold + name: i18n.translate('xpack.stackConnectors.systemLogExample.title', { + defaultMessage: 'System log - example', + }), + supportedFeatureIds: [AlertingConnectorFeatureId, UptimeConnectorFeatureId], + validate: { + config: { schema: schema.object({}, { defaultValue: {} }) }, + secrets: { schema: schema.object({}, { defaultValue: {} }) }, + params: { + schema: ParamsSchema, + }, + }, + executor, + }; +} + +export const connectorAdapter: ConnectorAdapter = { + connectorTypeId: ConnectorTypeId, + ruleActionParamsSchema: ParamsSchema, + buildActionParams: ({ alerts, rule, params, spaceId, ruleUrl }) => { + return { ...params }; + }, +}; + +// action executor + +async function executor( + execOptions: ServerLogConnectorTypeExecutorOptions +): Promise> { + const { actionId, params, logger } = execOptions; + const sanitizedMessage = withoutControlCharacters(params.message); + try { + logger.info(`SYSTEM ACTION EXAMPLE Server log: ${sanitizedMessage}`); + } catch (err) { + const message = i18n.translate('xpack.stackConnectors.serverLog.errorLoggingErrorMessage', { + defaultMessage: 'error logging message', + }); + return { + status: 'error', + message, + serviceMessage: err.message, + actionId, + }; + } + + return { status: 'ok', actionId }; +} diff --git a/x-pack/examples/triggers_actions_ui_example/server/index.ts b/x-pack/examples/triggers_actions_ui_example/server/index.ts new file mode 100644 index 0000000000000..6479960a80604 --- /dev/null +++ b/x-pack/examples/triggers_actions_ui_example/server/index.ts @@ -0,0 +1,13 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { PluginInitializer } from '@kbn/core/server'; + +export const plugin: PluginInitializer = async () => { + const { TriggersActionsUiExamplePlugin } = await import('./plugin'); + return new TriggersActionsUiExamplePlugin(); +}; diff --git a/x-pack/examples/triggers_actions_ui_example/server/plugin.ts b/x-pack/examples/triggers_actions_ui_example/server/plugin.ts new file mode 100644 index 0000000000000..6d55bbb2cb55d --- /dev/null +++ b/x-pack/examples/triggers_actions_ui_example/server/plugin.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 { Plugin, CoreSetup } from '@kbn/core/server'; + +import { PluginSetupContract as ActionsSetup } from '@kbn/actions-plugin/server'; +import { PluginSetupContract as AlertingSetup } from '@kbn/alerting-plugin/server'; + +import { + getConnectorType as getSystemLogExampleConnectorType, + connectorAdapter as systemLogConnectorAdapter, +} from './connector_types/system_log_example'; + +// this plugin's dependencies +export interface TriggersActionsUiExampleDeps { + alerting: AlertingSetup; + actions: ActionsSetup; +} +export class TriggersActionsUiExamplePlugin + implements Plugin +{ + public setup(core: CoreSetup, { actions, alerting }: TriggersActionsUiExampleDeps) { + actions.registerType(getSystemLogExampleConnectorType()); + alerting.registerConnectorAdapter(systemLogConnectorAdapter); + } + + public start() {} + public stop() {} +} diff --git a/x-pack/examples/triggers_actions_ui_example/tsconfig.json b/x-pack/examples/triggers_actions_ui_example/tsconfig.json index f999d2709a662..64e9254eadc95 100644 --- a/x-pack/examples/triggers_actions_ui_example/tsconfig.json +++ b/x-pack/examples/triggers_actions_ui_example/tsconfig.json @@ -23,5 +23,8 @@ "@kbn/data-plugin", "@kbn/i18n-react", "@kbn/shared-ux-router", + "@kbn/i18n", + "@kbn/actions-plugin", + "@kbn/config-schema", ] } diff --git a/x-pack/packages/kbn-elastic-assistant-common/impl/data_anonymization/helpers/index.ts b/x-pack/packages/kbn-elastic-assistant-common/impl/data_anonymization/helpers/index.ts index 3aa74fe80ff0a..8989d766e78c7 100644 --- a/x-pack/packages/kbn-elastic-assistant-common/impl/data_anonymization/helpers/index.ts +++ b/x-pack/packages/kbn-elastic-assistant-common/impl/data_anonymization/helpers/index.ts @@ -5,7 +5,7 @@ * 2.0. */ -import { Replacement } from '../../schemas'; +import { Replacements } from '../../schemas'; export const getIsDataAnonymizable = (rawData: string | Record): boolean => typeof rawData !== 'string'; @@ -29,12 +29,12 @@ export const replaceAnonymizedValuesWithOriginalValues = ({ replacements, }: { messageContent: string; - replacements: Replacement[]; + replacements: Replacements; }): string => replacements != null - ? replacements.reduce((acc, replacement) => { - const value = replacement.value; - return replacement.uuid && value ? acc.replaceAll(replacement.uuid, value) : acc; + ? Object.keys(replacements).reduce((acc, key) => { + const value = replacements[key]; + return acc.replaceAll(key, value); }, messageContent) : messageContent; @@ -43,11 +43,11 @@ export const replaceOriginalValuesWithUuidValues = ({ replacements, }: { messageContent: string; - replacements: Replacement[]; + replacements: Replacements; }): string => replacements != null - ? replacements.reduce((acc, replacement) => { - const value = replacement.value; - return replacement.uuid && value ? acc.replaceAll(value, replacement.uuid) : acc; + ? Object.keys(replacements).reduce((acc, key) => { + const value = replacements[key]; + return value ? acc.replaceAll(value, key) : acc; }, messageContent) : messageContent; diff --git a/x-pack/packages/kbn-elastic-assistant-common/impl/data_anonymization/transform_raw_data/index.test.tsx b/x-pack/packages/kbn-elastic-assistant-common/impl/data_anonymization/transform_raw_data/index.test.tsx index 7c3cf5c02b02a..fb8b1c5b42f6b 100644 --- a/x-pack/packages/kbn-elastic-assistant-common/impl/data_anonymization/transform_raw_data/index.test.tsx +++ b/x-pack/packages/kbn-elastic-assistant-common/impl/data_anonymization/transform_raw_data/index.test.tsx @@ -20,7 +20,7 @@ describe('transformRawData', () => { const result = transformRawData({ allow: inputRawData.allow, allowReplacement: inputRawData.allowReplacement, - currentReplacements: [], + currentReplacements: {}, getAnonymizedValue: mockGetAnonymizedValue, onNewReplacements: () => {}, rawData: inputRawData.rawData, @@ -42,13 +42,13 @@ describe('transformRawData', () => { transformRawData({ allow: inputRawData.allow, allowReplacement: inputRawData.allowReplacement, - currentReplacements: [], + currentReplacements: {}, getAnonymizedValue: mockGetAnonymizedValue, onNewReplacements, rawData: inputRawData.rawData, }); - expect(onNewReplacements).toHaveBeenCalledWith([{ uuid: '1eulav', value: 'value1' }]); + expect(onNewReplacements).toHaveBeenCalledWith({ '1eulav': 'value1' }); }); it('returns the expected mix of anonymized and non-anonymized data as a CSV string', () => { @@ -62,7 +62,7 @@ describe('transformRawData', () => { const result = transformRawData({ allow: inputRawData.allow, allowReplacement: inputRawData.allowReplacement, - currentReplacements: [], + currentReplacements: {}, getAnonymizedValue: mockGetAnonymizedValue, onNewReplacements: () => {}, rawData: inputRawData.rawData, @@ -86,7 +86,7 @@ describe('transformRawData', () => { const result = transformRawData({ allow: inputRawData.allow, allowReplacement: inputRawData.allowReplacement, - currentReplacements: [], + currentReplacements: {}, getAnonymizedValue: mockGetAnonymizedValue, onNewReplacements: () => {}, rawData: inputRawData.rawData, diff --git a/x-pack/packages/kbn-elastic-assistant-common/impl/data_anonymization/transform_raw_data/index.tsx b/x-pack/packages/kbn-elastic-assistant-common/impl/data_anonymization/transform_raw_data/index.tsx index 7665225927b94..8b4040f7b277a 100644 --- a/x-pack/packages/kbn-elastic-assistant-common/impl/data_anonymization/transform_raw_data/index.tsx +++ b/x-pack/packages/kbn-elastic-assistant-common/impl/data_anonymization/transform_raw_data/index.tsx @@ -5,7 +5,7 @@ * 2.0. */ -import { Replacement } from '../../schemas'; +import { Replacements } from '../../schemas'; import { getAnonymizedData } from '../get_anonymized_data'; import { getAnonymizedValues } from '../get_anonymized_values'; import { getCsvFromData } from '../get_csv_from_data'; @@ -20,7 +20,7 @@ export const transformRawData = ({ }: { allow: string[]; allowReplacement: string[]; - currentReplacements: Replacement[] | undefined; + currentReplacements: Replacements | undefined; getAnonymizedValue: ({ currentReplacements, rawValue, @@ -28,7 +28,7 @@ export const transformRawData = ({ currentReplacements: Record | undefined; rawValue: string; }) => string; - onNewReplacements?: (replacements: Replacement[]) => void; + onNewReplacements?: (replacements: Replacements) => void; rawData: string | Record; }): string => { if (typeof rawData === 'string') { @@ -38,22 +38,14 @@ export const transformRawData = ({ const anonymizedData = getAnonymizedData({ allow, allowReplacement, - currentReplacements: currentReplacements?.reduce((acc: Record, r) => { - acc[r.uuid] = r.value; - return acc; - }, {}), + currentReplacements, rawData, getAnonymizedValue, getAnonymizedValues, }); if (onNewReplacements != null) { - onNewReplacements( - Object.keys(anonymizedData.replacements).map((key) => ({ - uuid: key, - value: anonymizedData.replacements[key], - })) - ); + onNewReplacements(anonymizedData.replacements); } return getCsvFromData(anonymizedData.anonymizedData); diff --git a/x-pack/packages/kbn-elastic-assistant-common/impl/schemas/actions_connector/post_actions_connector_execute_route.gen.ts b/x-pack/packages/kbn-elastic-assistant-common/impl/schemas/actions_connector/post_actions_connector_execute_route.gen.ts index 8224d2e32686d..9d16dbe33a13d 100644 --- a/x-pack/packages/kbn-elastic-assistant-common/impl/schemas/actions_connector/post_actions_connector_execute_route.gen.ts +++ b/x-pack/packages/kbn-elastic-assistant-common/impl/schemas/actions_connector/post_actions_connector_execute_route.gen.ts @@ -16,7 +16,7 @@ import { z } from 'zod'; * version: 1 */ -import { UUID, Replacement } from '../conversations/common_attributes.gen'; +import { UUID, Replacements } from '../conversations/common_attributes.gen'; export type ExecuteConnectorRequestParams = z.infer; export const ExecuteConnectorRequestParams = z.object({ @@ -38,7 +38,7 @@ export const ExecuteConnectorRequestBody = z.object({ allowReplacement: z.array(z.string()).optional(), isEnabledKnowledgeBase: z.boolean().optional(), isEnabledRAGAlerts: z.boolean().optional(), - replacements: z.array(Replacement), + replacements: Replacements, size: z.number().optional(), }); export type ExecuteConnectorRequestBodyInput = z.input; @@ -47,7 +47,7 @@ export type ExecuteConnectorResponse = z.infer; export const ExecuteConnectorResponse = z.object({ data: z.string().optional(), connector_id: z.string().optional(), - replacements: z.array(Replacement).optional(), + replacements: Replacements.optional(), status: z.string().optional(), /** * Trace Data diff --git a/x-pack/packages/kbn-elastic-assistant-common/impl/schemas/actions_connector/post_actions_connector_execute_route.schema.yaml b/x-pack/packages/kbn-elastic-assistant-common/impl/schemas/actions_connector/post_actions_connector_execute_route.schema.yaml index 6e36d8e8c2d81..9bd85fa584fe5 100644 --- a/x-pack/packages/kbn-elastic-assistant-common/impl/schemas/actions_connector/post_actions_connector_execute_route.schema.yaml +++ b/x-pack/packages/kbn-elastic-assistant-common/impl/schemas/actions_connector/post_actions_connector_execute_route.schema.yaml @@ -55,9 +55,7 @@ paths: isEnabledRAGAlerts: type: boolean replacements: - type: array - items: - $ref: '../conversations/common_attributes.schema.yaml#/components/schemas/Replacement' + $ref: '../conversations/common_attributes.schema.yaml#/components/schemas/Replacements' size: type: number responses: @@ -73,9 +71,7 @@ paths: connector_id: type: string replacements: - type: array - items: - $ref: '../conversations/common_attributes.schema.yaml#/components/schemas/Replacement' + $ref: '../conversations/common_attributes.schema.yaml#/components/schemas/Replacements' status: type: string trace_data: diff --git a/x-pack/packages/kbn-elastic-assistant-common/impl/schemas/conversations/common_attributes.gen.ts b/x-pack/packages/kbn-elastic-assistant-common/impl/schemas/conversations/common_attributes.gen.ts index 04bf99a88fd0d..566c8f7dcd358 100644 --- a/x-pack/packages/kbn-elastic-assistant-common/impl/schemas/conversations/common_attributes.gen.ts +++ b/x-pack/packages/kbn-elastic-assistant-common/impl/schemas/conversations/common_attributes.gen.ts @@ -64,14 +64,8 @@ export const TraceData = z.object({ /** * Replacements object used to anonymize/deanomymize messsages */ -export type Replacement = z.infer; -export const Replacement = z.object({ - /** - * Actual value was anonymized. - */ - value: z.string(), - uuid: UUID, -}); +export type Replacements = z.infer; +export const Replacements = z.object({}).catchall(z.string()); export type Reader = z.infer; export const Reader = z.object({}).catchall(z.unknown()); @@ -211,7 +205,7 @@ export const ConversationResponse = z.object({ * The last time conversation was updated. */ createdAt: z.string(), - replacements: z.array(Replacement).optional(), + replacements: Replacements.optional(), users: z.array(User), /** * The conversation messages. @@ -259,7 +253,7 @@ export const ConversationUpdateProps = z.object({ * excludeFromLastConversationStorage. */ excludeFromLastConversationStorage: z.boolean().optional(), - replacements: z.array(Replacement).optional(), + replacements: Replacements.optional(), }); export type ConversationCreateProps = z.infer; @@ -288,7 +282,7 @@ export const ConversationCreateProps = z.object({ * excludeFromLastConversationStorage. */ excludeFromLastConversationStorage: z.boolean().optional(), - replacements: z.array(Replacement).optional(), + replacements: Replacements.optional(), }); export type ConversationMessageCreateProps = z.infer; diff --git a/x-pack/packages/kbn-elastic-assistant-common/impl/schemas/conversations/common_attributes.schema.yaml b/x-pack/packages/kbn-elastic-assistant-common/impl/schemas/conversations/common_attributes.schema.yaml index 208f7017d16dc..6914f662a0c3b 100644 --- a/x-pack/packages/kbn-elastic-assistant-common/impl/schemas/conversations/common_attributes.schema.yaml +++ b/x-pack/packages/kbn-elastic-assistant-common/impl/schemas/conversations/common_attributes.schema.yaml @@ -39,18 +39,11 @@ components: type: string description: Could be any string, not necessarily a UUID - Replacement: + Replacements: type: object - required: - - 'value' - - 'uuid' + additionalProperties: + type: string description: Replacements object used to anonymize/deanomymize messsages - properties: - value: - type: string - description: Actual value was anonymized. - uuid: - $ref: '#/components/schemas/UUID' Reader: type: object @@ -198,9 +191,7 @@ components: description: The last time conversation was updated. type: string replacements: - type: array - items: - $ref: '#/components/schemas/Replacement' + $ref: '#/components/schemas/Replacements' users: type: array items: @@ -252,9 +243,7 @@ components: description: excludeFromLastConversationStorage. type: boolean replacements: - type: array - items: - $ref: '#/components/schemas/Replacement' + $ref: '#/components/schemas/Replacements' ConversationCreateProps: type: object @@ -282,9 +271,7 @@ components: description: excludeFromLastConversationStorage. type: boolean replacements: - type: array - items: - $ref: '#/components/schemas/Replacement' + $ref: '#/components/schemas/Replacements' ConversationMessageCreateProps: type: object diff --git a/x-pack/packages/kbn-elastic-assistant/impl/assistant/api/conversations/conversations.ts b/x-pack/packages/kbn-elastic-assistant/impl/assistant/api/conversations/conversations.ts index 54883e6aa8aef..c59c185c4334b 100644 --- a/x-pack/packages/kbn-elastic-assistant/impl/assistant/api/conversations/conversations.ts +++ b/x-pack/packages/kbn-elastic-assistant/impl/assistant/api/conversations/conversations.ts @@ -11,7 +11,7 @@ import { ELASTIC_AI_ASSISTANT_CONVERSATIONS_URL, ELASTIC_AI_ASSISTANT_API_CURRENT_VERSION, ApiConfig, - Replacement, + Replacements, } from '@kbn/elastic-assistant-common'; import { Conversation, Message } from '../../../assistant_context/types'; @@ -151,7 +151,7 @@ export interface PutConversationMessageParams { title?: string; messages?: Message[]; apiConfig?: ApiConfig; - replacements?: Replacement[]; + replacements?: Replacements; excludeFromLastConversationStorage?: boolean; signal?: AbortSignal | undefined; } diff --git a/x-pack/packages/kbn-elastic-assistant/impl/assistant/api/conversations/use_bulk_actions_conversations.test.ts b/x-pack/packages/kbn-elastic-assistant/impl/assistant/api/conversations/use_bulk_actions_conversations.test.ts index 06351ad4b0827..c93ce0de7b294 100644 --- a/x-pack/packages/kbn-elastic-assistant/impl/assistant/api/conversations/use_bulk_actions_conversations.test.ts +++ b/x-pack/packages/kbn-elastic-assistant/impl/assistant/api/conversations/use_bulk_actions_conversations.test.ts @@ -17,7 +17,7 @@ const conversation1 = { id: 'conversation1', title: 'Conversation 1', apiConfig: { connectorId: '123' }, - replacements: [], + replacements: {}, category: 'default', messages: [ { diff --git a/x-pack/packages/kbn-elastic-assistant/impl/assistant/api/index.test.tsx b/x-pack/packages/kbn-elastic-assistant/impl/assistant/api/index.test.tsx index c54cfa56b4898..03a7907baf077 100644 --- a/x-pack/packages/kbn-elastic-assistant/impl/assistant/api/index.test.tsx +++ b/x-pack/packages/kbn-elastic-assistant/impl/assistant/api/index.test.tsx @@ -38,7 +38,7 @@ const fetchConnectorArgs: FetchConnectorExecuteAction = { http: mockHttp, message: 'This is a test', conversationId: 'test', - replacements: [], + replacements: {}, }; describe('API tests', () => { beforeEach(() => { @@ -52,7 +52,7 @@ describe('API tests', () => { expect(mockHttp.fetch).toHaveBeenCalledWith( '/internal/elastic_assistant/actions/connector/foo/_execute', { - body: '{"model":"gpt-4","message":"This is a test","subAction":"invokeAI","conversationId":"test","replacements":[],"isEnabledKnowledgeBase":true,"isEnabledRAGAlerts":false}', + body: '{"model":"gpt-4","message":"This is a test","subAction":"invokeAI","conversationId":"test","replacements":{},"isEnabledKnowledgeBase":true,"isEnabledRAGAlerts":false}', headers: { 'Content-Type': 'application/json' }, method: 'POST', signal: undefined, @@ -72,7 +72,7 @@ describe('API tests', () => { expect(mockHttp.fetch).toHaveBeenCalledWith( '/internal/elastic_assistant/actions/connector/foo/_execute', { - body: '{"model":"gpt-4","message":"This is a test","subAction":"invokeStream","conversationId":"test","replacements":[],"isEnabledKnowledgeBase":false,"isEnabledRAGAlerts":false}', + body: '{"model":"gpt-4","message":"This is a test","subAction":"invokeStream","conversationId":"test","replacements":{},"isEnabledKnowledgeBase":false,"isEnabledRAGAlerts":false}', method: 'POST', asResponse: true, rawResponse: true, @@ -89,7 +89,7 @@ describe('API tests', () => { alertsIndexPattern: '.alerts-security.alerts-default', allow: ['a', 'b', 'c'], allowReplacement: ['b', 'c'], - replacements: [{ uuid: 'auuid', value: 'real.hostname' }], + replacements: { auuid: 'real.hostname' }, size: 30, }; @@ -98,7 +98,7 @@ describe('API tests', () => { expect(mockHttp.fetch).toHaveBeenCalledWith( '/internal/elastic_assistant/actions/connector/foo/_execute', { - body: '{"model":"gpt-4","message":"This is a test","subAction":"invokeAI","conversationId":"test","replacements":[{"uuid":"auuid","value":"real.hostname"}],"isEnabledKnowledgeBase":true,"isEnabledRAGAlerts":true,"alertsIndexPattern":".alerts-security.alerts-default","allow":["a","b","c"],"allowReplacement":["b","c"],"size":30}', + body: '{"model":"gpt-4","message":"This is a test","subAction":"invokeAI","conversationId":"test","replacements":{"auuid":"real.hostname"},"isEnabledKnowledgeBase":true,"isEnabledRAGAlerts":true,"alertsIndexPattern":".alerts-security.alerts-default","allow":["a","b","c"],"allowReplacement":["b","c"],"size":30}', headers: { 'Content-Type': 'application/json', }, @@ -121,7 +121,7 @@ describe('API tests', () => { expect(mockHttp.fetch).toHaveBeenCalledWith( '/internal/elastic_assistant/actions/connector/foo/_execute', { - body: '{"model":"gpt-4","message":"This is a test","subAction":"invokeAI","conversationId":"test","replacements":[],"isEnabledKnowledgeBase":false,"isEnabledRAGAlerts":false}', + body: '{"model":"gpt-4","message":"This is a test","subAction":"invokeAI","conversationId":"test","replacements":{},"isEnabledKnowledgeBase":false,"isEnabledRAGAlerts":false}', method: 'POST', headers: { 'Content-Type': 'application/json', @@ -144,7 +144,7 @@ describe('API tests', () => { expect(mockHttp.fetch).toHaveBeenCalledWith( '/internal/elastic_assistant/actions/connector/foo/_execute', { - body: '{"model":"gpt-4","message":"This is a test","subAction":"invokeAI","conversationId":"test","replacements":[],"isEnabledKnowledgeBase":false,"isEnabledRAGAlerts":true}', + body: '{"model":"gpt-4","message":"This is a test","subAction":"invokeAI","conversationId":"test","replacements":{},"isEnabledKnowledgeBase":false,"isEnabledRAGAlerts":true}', method: 'POST', headers: { 'Content-Type': 'application/json', diff --git a/x-pack/packages/kbn-elastic-assistant/impl/assistant/api/index.tsx b/x-pack/packages/kbn-elastic-assistant/impl/assistant/api/index.tsx index b28024f152678..49ee6de11e0c7 100644 --- a/x-pack/packages/kbn-elastic-assistant/impl/assistant/api/index.tsx +++ b/x-pack/packages/kbn-elastic-assistant/impl/assistant/api/index.tsx @@ -7,7 +7,7 @@ import { HttpSetup } from '@kbn/core/public'; import { IHttpFetchError } from '@kbn/core-http-browser'; -import { ApiConfig, Replacement } from '@kbn/elastic-assistant-common'; +import { ApiConfig, Replacements } from '@kbn/elastic-assistant-common'; import { API_ERROR } from '../translations'; import { getOptionalRequestParams } from '../helpers'; export * from './conversations'; @@ -23,7 +23,7 @@ export interface FetchConnectorExecuteAction { apiConfig: ApiConfig; http: HttpSetup; message?: string; - replacements: Replacement[]; + replacements: Replacements; signal?: AbortSignal | undefined; size?: number; } @@ -114,7 +114,7 @@ export const fetchConnectorExecuteAction = async ({ connector_id: string; status: string; data: string; - replacements?: Replacement[]; + replacements?: Replacements; service_message?: string; trace_data?: { transaction_id: string; diff --git a/x-pack/packages/kbn-elastic-assistant/impl/assistant/assistant_header/index.test.tsx b/x-pack/packages/kbn-elastic-assistant/impl/assistant/assistant_header/index.test.tsx index 036320378f84f..47b4f11629a9c 100644 --- a/x-pack/packages/kbn-elastic-assistant/impl/assistant/assistant_header/index.test.tsx +++ b/x-pack/packages/kbn-elastic-assistant/impl/assistant/assistant_header/index.test.tsx @@ -79,7 +79,7 @@ describe('AssistantHeader', () => { const { getByText, getByTestId } = render( , { wrapper: TestProviders, diff --git a/x-pack/packages/kbn-elastic-assistant/impl/assistant/chat_send/use_chat_send.test.tsx b/x-pack/packages/kbn-elastic-assistant/impl/assistant/chat_send/use_chat_send.test.tsx index 75672e8d7375d..ea9b21853a912 100644 --- a/x-pack/packages/kbn-elastic-assistant/impl/assistant/chat_send/use_chat_send.test.tsx +++ b/x-pack/packages/kbn-elastic-assistant/impl/assistant/chat_send/use_chat_send.test.tsx @@ -27,7 +27,6 @@ const setUserPrompt = jest.fn(); const sendMessage = jest.fn(); const removeLastMessage = jest.fn(); const clearConversation = jest.fn(); -const refresh = jest.fn(); const setCurrentConversation = jest.fn(); export const testProps: UseChatSendProps = { @@ -47,7 +46,6 @@ export const testProps: UseChatSendProps = { setPromptTextPreview, setSelectedPromptContexts, setUserPrompt, - refresh, setCurrentConversation, }; const robotMessage = { response: 'Response message from the robot', isError: false }; @@ -71,6 +69,7 @@ describe('use chat send', () => { }); }); it('handleOnChatCleared clears the conversation', async () => { + (clearConversation as jest.Mock).mockReturnValueOnce(testProps.currentConversation); const { result } = renderHook(() => useChatSend(testProps), { wrapper: TestProviders, }); @@ -80,8 +79,8 @@ describe('use chat send', () => { expect(setUserPrompt).toHaveBeenCalledWith(''); expect(setSelectedPromptContexts).toHaveBeenCalledWith({}); await waitFor(() => { - expect(clearConversation).toHaveBeenCalledWith(testProps.currentConversation.id); - expect(refresh).toHaveBeenCalled(); + expect(clearConversation).toHaveBeenCalledWith(testProps.currentConversation); + expect(setCurrentConversation).toHaveBeenCalled(); }); expect(setEditingSystemPromptId).toHaveBeenCalledWith(defaultSystemPrompt.id); }); diff --git a/x-pack/packages/kbn-elastic-assistant/impl/assistant/chat_send/use_chat_send.tsx b/x-pack/packages/kbn-elastic-assistant/impl/assistant/chat_send/use_chat_send.tsx index e3bea5754ca7b..779e11c042df4 100644 --- a/x-pack/packages/kbn-elastic-assistant/impl/assistant/chat_send/use_chat_send.tsx +++ b/x-pack/packages/kbn-elastic-assistant/impl/assistant/chat_send/use_chat_send.tsx @@ -28,7 +28,6 @@ export interface UseChatSendProps { React.SetStateAction> >; setUserPrompt: React.Dispatch>; - refresh: () => Promise; setCurrentConversation: React.Dispatch>; } @@ -56,7 +55,6 @@ export const useChatSend = ({ setPromptTextPreview, setSelectedPromptContexts, setUserPrompt, - refresh, setCurrentConversation, }: UseChatSendProps): UseChatSend => { const { @@ -182,7 +180,7 @@ export const useChatSend = ({ http, // do not send any new messages, the previous conversation is already stored conversationId: currentConversation.id, - replacements: [], + replacements: {}, }); const responseMessage: Message = getMessageFromRawResponse(rawResponse); @@ -209,15 +207,16 @@ export const useChatSend = ({ setPromptTextPreview(''); setUserPrompt(''); setSelectedPromptContexts({}); - await clearConversation(currentConversation.id); - await refresh(); - + const updatedConversation = await clearConversation(currentConversation); + if (updatedConversation) { + setCurrentConversation(updatedConversation); + } setEditingSystemPromptId(defaultSystemPromptId); }, [ allSystemPrompts, clearConversation, currentConversation, - refresh, + setCurrentConversation, setEditingSystemPromptId, setPromptTextPreview, setSelectedPromptContexts, diff --git a/x-pack/packages/kbn-elastic-assistant/impl/assistant/conversations/conversation_selector/index.tsx b/x-pack/packages/kbn-elastic-assistant/impl/assistant/conversations/conversation_selector/index.tsx index 17599dedd4176..4336d7508fe25 100644 --- a/x-pack/packages/kbn-elastic-assistant/impl/assistant/conversations/conversation_selector/index.tsx +++ b/x-pack/packages/kbn-elastic-assistant/impl/assistant/conversations/conversation_selector/index.tsx @@ -19,6 +19,7 @@ import React, { useCallback, useEffect, useMemo, useState } from 'react'; import useEvent from 'react-use/lib/useEvent'; import { css } from '@emotion/react'; +import { getGenAiConfig } from '../../../connectorland/helpers'; import { AIConnector } from '../../../connectorland/connector_selector'; import { Conversation } from '../../../..'; import { useAssistantContext } from '../../../assistant_context'; @@ -108,18 +109,20 @@ export const ConversationSelector: React.FC = React.memo( let createdConversation; if (!optionExists) { + const config = getGenAiConfig(defaultConnector); const newConversation: Conversation = { id: '', title: searchValue, category: 'assistant', messages: [], - replacements: [], + replacements: {}, ...(defaultConnector ? { apiConfig: { connectorId: defaultConnector.id, provider: defaultConnector.apiProvider, defaultSystemPromptId: defaultSystemPrompt?.id, + model: config?.defaultModel, }, } : {}), diff --git a/x-pack/packages/kbn-elastic-assistant/impl/assistant/conversations/conversation_settings/conversation_settings.test.tsx b/x-pack/packages/kbn-elastic-assistant/impl/assistant/conversations/conversation_settings/conversation_settings.test.tsx index fb75b9d911dda..8cd79a8f94562 100644 --- a/x-pack/packages/kbn-elastic-assistant/impl/assistant/conversations/conversation_settings/conversation_settings.test.tsx +++ b/x-pack/packages/kbn-elastic-assistant/impl/assistant/conversations/conversation_settings/conversation_settings.test.tsx @@ -199,7 +199,7 @@ describe('ConversationSettings', () => { id: '', title: 'Cool new conversation', messages: [], - replacements: [], + replacements: {}, }; expect(setConversationSettings).toHaveBeenLastCalledWith({ ...mockConvos, diff --git a/x-pack/packages/kbn-elastic-assistant/impl/assistant/conversations/conversation_settings/conversation_settings.tsx b/x-pack/packages/kbn-elastic-assistant/impl/assistant/conversations/conversation_settings/conversation_settings.tsx index 544d08b35e888..8c486fd563a8a 100644 --- a/x-pack/packages/kbn-elastic-assistant/impl/assistant/conversations/conversation_settings/conversation_settings.tsx +++ b/x-pack/packages/kbn-elastic-assistant/impl/assistant/conversations/conversation_settings/conversation_settings.tsx @@ -82,7 +82,7 @@ export const ConversationSettings: React.FC = React.m title: c ?? '', category: 'assistant', messages: [], - replacements: [], + replacements: {}, ...(defaultConnector ? { apiConfig: { diff --git a/x-pack/packages/kbn-elastic-assistant/impl/assistant/helpers.test.ts b/x-pack/packages/kbn-elastic-assistant/impl/assistant/helpers.test.ts index 4bb9d099bc7c4..1894f106d08aa 100644 --- a/x-pack/packages/kbn-elastic-assistant/impl/assistant/helpers.test.ts +++ b/x-pack/packages/kbn-elastic-assistant/impl/assistant/helpers.test.ts @@ -24,7 +24,7 @@ describe('helpers', () => { theme: {}, messages: [], apiConfig: { connectorId: '123' }, - replacements: [], + replacements: {}, title: 'conversation_id', }; const result = getBlockBotConversation(conversation, isAssistantEnabled); @@ -47,7 +47,7 @@ describe('helpers', () => { }, ], apiConfig: { connectorId: '123' }, - replacements: [], + replacements: {}, category: 'assistant', title: 'conversation_id', }; @@ -61,7 +61,7 @@ describe('helpers', () => { title: 'conversation_id', messages: enterpriseMessaging, apiConfig: { connectorId: '123' }, - replacements: [], + replacements: {}, category: 'assistant', }; const result = getBlockBotConversation(conversation, isAssistantEnabled); @@ -87,7 +87,7 @@ describe('helpers', () => { }, ], apiConfig: { connectorId: '123' }, - replacements: [], + replacements: {}, }; const result = getBlockBotConversation(conversation, isAssistantEnabled); expect(result.messages.length).toEqual(3); @@ -103,7 +103,7 @@ describe('helpers', () => { category: 'assistant', messages: [], apiConfig: { connectorId: '123' }, - replacements: [], + replacements: {}, }; const result = getBlockBotConversation(conversation, isAssistantEnabled); expect(result.messages.length).toEqual(3); @@ -125,7 +125,7 @@ describe('helpers', () => { }, ], apiConfig: { connectorId: '123' }, - replacements: [], + replacements: {}, }; const result = getBlockBotConversation(conversation, isAssistantEnabled); expect(result.messages.length).toEqual(4); @@ -266,7 +266,7 @@ describe('helpers', () => { category: 'assistant', theme: {}, apiConfig: { connectorId: '123' }, - replacements: [], + replacements: {}, }; const baseConversations = { conversation1: { diff --git a/x-pack/packages/kbn-elastic-assistant/impl/assistant/index.test.tsx b/x-pack/packages/kbn-elastic-assistant/impl/assistant/index.test.tsx index 48733ce80cb9f..1ac6c10b55e3c 100644 --- a/x-pack/packages/kbn-elastic-assistant/impl/assistant/index.test.tsx +++ b/x-pack/packages/kbn-elastic-assistant/impl/assistant/index.test.tsx @@ -51,7 +51,7 @@ const mockData = { category: 'assistant', messages: [], apiConfig: { connectorId: '123' }, - replacements: [], + replacements: {}, }, 'electric sheep': { id: 'electric sheep id', @@ -59,7 +59,7 @@ const mockData = { title: 'electric sheep', messages: [], apiConfig: { connectorId: '123' }, - replacements: [], + replacements: {}, }, }; const mockDeleteConvo = jest.fn(); @@ -140,7 +140,7 @@ describe('Assistant', () => { id: 'Welcome Id', messages: [], title: 'Welcome', - replacements: [], + replacements: {}, }, }) ); @@ -170,7 +170,7 @@ describe('Assistant', () => { expect.objectContaining({ currentConversation: { apiConfig: { connectorId: '123' }, - replacements: [], + replacements: {}, category: 'assistant', id: 'Welcome Id', messages: [], @@ -255,12 +255,12 @@ describe('Assistant', () => { expect(setConversationTitle).toHaveBeenLastCalledWith('electric sheep'); }); it('should fetch current conversation when id has value', async () => { - const chatSendSpy = jest.spyOn(all, 'useChatSend'); + const getConversation = jest + .fn() + .mockResolvedValue({ ...mockData['electric sheep'], title: 'updated title' }); (useConversation as jest.Mock).mockReturnValue({ ...mockUseConversation, - getConversation: jest - .fn() - .mockResolvedValue({ ...mockData['electric sheep'], title: 'updated title' }), + getConversation, }); renderAssistant(); @@ -269,14 +269,7 @@ describe('Assistant', () => { fireEvent.click(previousConversationButton); }); - expect(chatSendSpy).toHaveBeenLastCalledWith( - expect.objectContaining({ - currentConversation: { - ...mockData['electric sheep'], - title: 'updated title', - }, - }) - ); + expect(getConversation).toHaveBeenCalledWith('electric sheep id'); expect(persistToLocalStorage).toHaveBeenLastCalledWith('updated title'); }); diff --git a/x-pack/packages/kbn-elastic-assistant/impl/assistant/index.tsx b/x-pack/packages/kbn-elastic-assistant/impl/assistant/index.tsx index 64fffb8181678..d96f5c8e76231 100644 --- a/x-pack/packages/kbn-elastic-assistant/impl/assistant/index.tsx +++ b/x-pack/packages/kbn-elastic-assistant/impl/assistant/index.tsx @@ -173,13 +173,17 @@ const AssistantComponent: React.FC = ({ if (!isLoading && Object.keys(conversations).length > 0) { const conversation = conversations[selectedConversationTitle ?? getLastConversationTitle(conversationTitle)]; - if (conversation) { - setCurrentConversation(conversation); - } + // Set the last conversation as current conversation or use persisted or non-persisted Welcom conversation + setCurrentConversation( + conversation ?? + conversations[WELCOME_CONVERSATION_TITLE] ?? + getDefaultConversation({ cTitle: WELCOME_CONVERSATION_TITLE }) + ); } }, [ conversationTitle, conversations, + getDefaultConversation, getLastConversationTitle, isLoading, selectedConversationTitle, @@ -307,9 +311,15 @@ const AssistantComponent: React.FC = ({ setEditingSystemPromptId( getDefaultSystemPrompt({ allSystemPrompts, conversation: refetchedConversation })?.id ); + if (refetchedConversation) { + setConversations({ + ...conversations, + [refetchedConversation.title]: refetchedConversation, + }); + } } }, - [allSystemPrompts, refetchCurrentConversation, refetchResults] + [allSystemPrompts, conversations, refetchCurrentConversation, refetchResults] ); const { comments: connectorComments, prompt: connectorPrompt } = useConnectorSetup({ @@ -448,7 +458,6 @@ const AssistantComponent: React.FC = ({ selectedPromptContexts, setSelectedPromptContexts, setCurrentConversation, - refresh: refetchCurrentConversation, }); const chatbotComments = useMemo( diff --git a/x-pack/packages/kbn-elastic-assistant/impl/assistant/prompt/helpers.test.ts b/x-pack/packages/kbn-elastic-assistant/impl/assistant/prompt/helpers.test.ts index d9cc3c30a3b52..f264ee3e86018 100644 --- a/x-pack/packages/kbn-elastic-assistant/impl/assistant/prompt/helpers.test.ts +++ b/x-pack/packages/kbn-elastic-assistant/impl/assistant/prompt/helpers.test.ts @@ -64,7 +64,7 @@ describe('helpers', () => { describe('getCombinedMessage', () => { it('returns correct content for a new chat with a system prompt', async () => { const message: Message = await getCombinedMessage({ - currentReplacements: [], + currentReplacements: {}, isNewChat: true, promptText: 'User prompt text', selectedPromptContexts: { @@ -86,7 +86,7 @@ User prompt text`); it('returns correct content for a new chat WITHOUT a system prompt', async () => { const message: Message = await getCombinedMessage({ - currentReplacements: [], + currentReplacements: {}, isNewChat: true, promptText: 'User prompt text', selectedPromptContexts: { @@ -107,7 +107,7 @@ User prompt text`); it('returns the correct content for an existing chat', async () => { const message: Message = await getCombinedMessage({ - currentReplacements: [], + currentReplacements: {}, isNewChat: false, promptText: 'User prompt text', selectedPromptContexts: { @@ -126,7 +126,7 @@ User prompt text`); it('returns the expected role', async () => { const message: Message = await getCombinedMessage({ - currentReplacements: [], + currentReplacements: {}, isNewChat: true, promptText: 'User prompt text', selectedPromptContexts: { @@ -140,7 +140,7 @@ User prompt text`); it('returns a valid timestamp', async () => { const message: Message = await getCombinedMessage({ - currentReplacements: [], + currentReplacements: {}, isNewChat: true, promptText: 'User prompt text', selectedPromptContexts: {}, @@ -163,7 +163,7 @@ User prompt text`); it('invokes `onNewReplacements` with the expected replacements', async () => { const message = await getCombinedMessage({ - currentReplacements: [], + currentReplacements: {}, getAnonymizedValue: mockGetAnonymizedValue, isNewChat: true, promptText: 'User prompt text', @@ -174,19 +174,19 @@ User prompt text`); selectedSystemPrompt: mockSystemPrompt, }); - expect(message.replacements).toEqual([ - { uuid: 'oof', value: 'foo' }, - { uuid: 'rab', value: 'bar' }, - { uuid: 'zab', value: 'baz' }, - { uuid: 'elzoof', value: 'foozle' }, - ]); + expect(message.replacements).toEqual({ + elzoof: 'foozle', + oof: 'foo', + rab: 'bar', + zab: 'baz', + }); }); it('returns the expected content when `isNewChat` is false', async () => { const isNewChat = false; // <-- not a new chat const message: Message = await getCombinedMessage({ - currentReplacements: [], + currentReplacements: {}, getAnonymizedValue: mockGetAnonymizedValue, isNewChat, promptText: 'User prompt text', diff --git a/x-pack/packages/kbn-elastic-assistant/impl/assistant/prompt/helpers.ts b/x-pack/packages/kbn-elastic-assistant/impl/assistant/prompt/helpers.ts index 9cbcd9455e8e5..0266a4ae002c6 100644 --- a/x-pack/packages/kbn-elastic-assistant/impl/assistant/prompt/helpers.ts +++ b/x-pack/packages/kbn-elastic-assistant/impl/assistant/prompt/helpers.ts @@ -5,7 +5,7 @@ * 2.0. */ -import { Replacement, transformRawData } from '@kbn/elastic-assistant-common'; +import { Replacements, transformRawData } from '@kbn/elastic-assistant-common'; import { getAnonymizedValue as defaultGetAnonymizedValue } from '../get_anonymized_value'; import type { Message } from '../../assistant_context/types'; import type { SelectedPromptContext } from '../prompt_context/types'; @@ -40,7 +40,7 @@ export function getCombinedMessage({ selectedPromptContexts, selectedSystemPrompt, }: { - currentReplacements: Replacement[] | undefined; + currentReplacements: Replacements | undefined; getAnonymizedValue?: ({ currentReplacements, rawValue, @@ -53,9 +53,9 @@ export function getCombinedMessage({ selectedPromptContexts: Record; selectedSystemPrompt: Prompt | undefined; }): Message { - const replacements: Replacement[] = currentReplacements ?? []; - const onNewReplacements = (newReplacements: Replacement[]) => { - replacements.push(...newReplacements); + let replacements: Replacements = currentReplacements ?? {}; + const onNewReplacements = (newReplacements: Replacements) => { + replacements = { ...replacements, ...newReplacements }; }; const promptContextsContent = Object.keys(selectedPromptContexts) diff --git a/x-pack/packages/kbn-elastic-assistant/impl/assistant/prompt_editor/system_prompt/index.test.tsx b/x-pack/packages/kbn-elastic-assistant/impl/assistant/prompt_editor/system_prompt/index.test.tsx index 454c9bec9fa06..faa67b1063058 100644 --- a/x-pack/packages/kbn-elastic-assistant/impl/assistant/prompt_editor/system_prompt/index.test.tsx +++ b/x-pack/packages/kbn-elastic-assistant/impl/assistant/prompt_editor/system_prompt/index.test.tsx @@ -380,7 +380,7 @@ describe('SystemPrompt', () => { }, title: 'second', messages: [], - replacements: [], + replacements: {}, }; const localMockConversations: Record = { [DEFAULT_CONVERSATION_TITLE]: BASE_CONVERSATION, diff --git a/x-pack/packages/kbn-elastic-assistant/impl/assistant/use_conversation/helpers.test.ts b/x-pack/packages/kbn-elastic-assistant/impl/assistant/use_conversation/helpers.test.ts index 9d101b4423f26..6ad11f44a6898 100644 --- a/x-pack/packages/kbn-elastic-assistant/impl/assistant/use_conversation/helpers.test.ts +++ b/x-pack/packages/kbn-elastic-assistant/impl/assistant/use_conversation/helpers.test.ts @@ -98,7 +98,7 @@ describe('useConversation helpers', () => { category: 'assistant', id: '1', messages: [], - replacements: [], + replacements: {}, title: '1', }; @@ -111,7 +111,7 @@ describe('useConversation helpers', () => { test('should return the default (starred) isNewConversationDefault system prompt if conversation system prompt does not exist', () => { const conversationWithoutSystemPrompt: Conversation = { apiConfig: { connectorId: '123' }, - replacements: [], + replacements: {}, category: 'assistant', id: '1', messages: [], @@ -128,7 +128,7 @@ describe('useConversation helpers', () => { test('should return the default (starred) isNewConversationDefault system prompt if conversation system prompt does not exist within all system prompts', () => { const conversationWithoutSystemPrompt: Conversation = { apiConfig: { connectorId: '123' }, - replacements: [], + replacements: {}, category: 'assistant', id: '4', // this id does not exist within allSystemPrompts messages: [], @@ -145,7 +145,7 @@ describe('useConversation helpers', () => { test('should return the first prompt if both conversation system prompt and default new system prompt do not exist', () => { const conversationWithoutSystemPrompt: Conversation = { apiConfig: { connectorId: '123' }, - replacements: [], + replacements: {}, category: 'assistant', id: '1', messages: [], @@ -162,7 +162,7 @@ describe('useConversation helpers', () => { test('should return undefined if conversation system prompt does not exist and there are no system prompts', () => { const conversationWithoutSystemPrompt: Conversation = { apiConfig: { connectorId: '123' }, - replacements: [], + replacements: {}, category: 'assistant', id: '1', messages: [], @@ -179,7 +179,7 @@ describe('useConversation helpers', () => { test('should return undefined if conversation system prompt does not exist within all system prompts', () => { const conversationWithoutSystemPrompt: Conversation = { apiConfig: { connectorId: '123' }, - replacements: [], + replacements: {}, category: 'assistant', id: '4', // this id does not exist within allSystemPrompts messages: [], diff --git a/x-pack/packages/kbn-elastic-assistant/impl/assistant/use_conversation/index.test.tsx b/x-pack/packages/kbn-elastic-assistant/impl/assistant/use_conversation/index.test.tsx index 29567aed6f1da..f1170c58e2966 100644 --- a/x-pack/packages/kbn-elastic-assistant/impl/assistant/use_conversation/index.test.tsx +++ b/x-pack/packages/kbn-elastic-assistant/impl/assistant/use_conversation/index.test.tsx @@ -65,7 +65,7 @@ describe('useConversation', () => { const createResult = await result.current.createConversation({ id: mockConvo.id, messages: mockConvo.messages, - replacements: [], + replacements: {}, apiConfig: { connectorId: '123', defaultSystemPromptId: 'default-system-prompt', diff --git a/x-pack/packages/kbn-elastic-assistant/impl/assistant/use_conversation/index.tsx b/x-pack/packages/kbn-elastic-assistant/impl/assistant/use_conversation/index.tsx index ff523c9d62d61..b39ea430d9994 100644 --- a/x-pack/packages/kbn-elastic-assistant/impl/assistant/use_conversation/index.tsx +++ b/x-pack/packages/kbn-elastic-assistant/impl/assistant/use_conversation/index.tsx @@ -21,9 +21,9 @@ import { import { WELCOME_CONVERSATION } from './sample_conversations'; export const DEFAULT_CONVERSATION_STATE: Conversation = { - id: i18n.DEFAULT_CONVERSATION_TITLE, + id: '', messages: [], - replacements: [], + replacements: {}, category: 'assistant', title: i18n.DEFAULT_CONVERSATION_TITLE, }; @@ -39,7 +39,7 @@ interface SetApiConfigProps { } interface UseConversation { - clearConversation: (conversationId: string) => Promise; + clearConversation: (conversation: Conversation) => Promise; getDefaultConversation: ({ cTitle, messages }: CreateConversationProps) => Conversation; deleteConversation: (conversationId: string) => void; removeLastMessage: (conversationId: string) => Promise; @@ -83,21 +83,20 @@ export const useConversation = (): UseConversation => { ); const clearConversation = useCallback( - async (conversationId: string) => { - const conversation = await getConversationById({ http, id: conversationId, toasts }); - if (conversation && conversation.apiConfig) { + async (conversation: Conversation) => { + if (conversation.apiConfig) { const defaultSystemPromptId = getDefaultSystemPrompt({ allSystemPrompts, conversation, })?.id; - await updateConversation({ + return updateConversation({ http, toasts, - conversationId, + conversationId: conversation.id, apiConfig: { ...conversation.apiConfig, defaultSystemPromptId }, messages: [], - replacements: [], + replacements: {}, }); } }, diff --git a/x-pack/packages/kbn-elastic-assistant/impl/assistant/use_conversation/sample_conversations.tsx b/x-pack/packages/kbn-elastic-assistant/impl/assistant/use_conversation/sample_conversations.tsx index e287a83f2c3ff..397d8b4795592 100644 --- a/x-pack/packages/kbn-elastic-assistant/impl/assistant/use_conversation/sample_conversations.tsx +++ b/x-pack/packages/kbn-elastic-assistant/impl/assistant/use_conversation/sample_conversations.tsx @@ -42,7 +42,7 @@ export const WELCOME_CONVERSATION: Conversation = { }, }, ], - replacements: [], + replacements: {}, }; export const enterpriseMessaging: Message[] = [ diff --git a/x-pack/packages/kbn-elastic-assistant/impl/assistant/use_send_message/index.tsx b/x-pack/packages/kbn-elastic-assistant/impl/assistant/use_send_message/index.tsx index 44e788ae9a853..51a8e9504931d 100644 --- a/x-pack/packages/kbn-elastic-assistant/impl/assistant/use_send_message/index.tsx +++ b/x-pack/packages/kbn-elastic-assistant/impl/assistant/use_send_message/index.tsx @@ -7,7 +7,7 @@ import { HttpSetup } from '@kbn/core-http-browser'; import { useCallback, useState } from 'react'; -import { ApiConfig, Replacement } from '@kbn/elastic-assistant-common'; +import { ApiConfig, Replacements } from '@kbn/elastic-assistant-common'; import { useAssistantContext } from '../../assistant_context'; import { fetchConnectorExecuteAction, FetchConnectorExecuteResponse } from '../api'; @@ -18,7 +18,7 @@ interface SendMessageProps { http: HttpSetup; message?: string; conversationId: string; - replacements: Replacement[]; + replacements: Replacements; } interface UseSendMessage { diff --git a/x-pack/packages/kbn-elastic-assistant/impl/assistant_context/types.tsx b/x-pack/packages/kbn-elastic-assistant/impl/assistant_context/types.tsx index fc7d205d5d66c..ed2a03d26c426 100644 --- a/x-pack/packages/kbn-elastic-assistant/impl/assistant_context/types.tsx +++ b/x-pack/packages/kbn-elastic-assistant/impl/assistant_context/types.tsx @@ -5,7 +5,7 @@ * 2.0. */ -import { ApiConfig, Replacement } from '@kbn/elastic-assistant-common'; +import { ApiConfig, Replacements } from '@kbn/elastic-assistant-common'; export type ConversationRole = 'system' | 'user' | 'assistant'; @@ -17,7 +17,7 @@ export interface MessagePresentation { export interface Message { role: ConversationRole; reader?: ReadableStreamDefaultReader; - replacements?: Replacement[]; + replacements?: Replacements; content?: string; timestamp: string; isError?: boolean; @@ -62,7 +62,7 @@ export interface Conversation { messages: Message[]; updatedAt?: Date; createdAt?: Date; - replacements: Replacement[]; + replacements: Replacements; isDefault?: boolean; excludeFromLastConversationStorage?: boolean; } diff --git a/x-pack/packages/kbn-elastic-assistant/impl/connectorland/connector_selector_inline/connector_selector_inline.test.tsx b/x-pack/packages/kbn-elastic-assistant/impl/connectorland/connector_selector_inline/connector_selector_inline.test.tsx index 44cfd59bdbecc..bb7bbfe27daf0 100644 --- a/x-pack/packages/kbn-elastic-assistant/impl/connectorland/connector_selector_inline/connector_selector_inline.test.tsx +++ b/x-pack/packages/kbn-elastic-assistant/impl/connectorland/connector_selector_inline/connector_selector_inline.test.tsx @@ -78,7 +78,7 @@ describe('ConnectorSelectorInline', () => { category: 'assistant', messages: [], apiConfig: { connectorId: '123' }, - replacements: [], + replacements: {}, title: 'conversation_id', }; const { getByText } = render( @@ -99,7 +99,7 @@ describe('ConnectorSelectorInline', () => { category: 'assistant', messages: [], apiConfig: { connectorId: '123' }, - replacements: [], + replacements: {}, title: 'conversation_id', }; const { getByTestId, queryByTestId } = render( @@ -123,7 +123,7 @@ describe('ConnectorSelectorInline', () => { category: 'assistant', messages: [], apiConfig: { connectorId: '123' }, - replacements: [], + replacements: {}, title: 'conversation_id', }; const { getByTestId, queryByTestId } = render( @@ -148,7 +148,7 @@ describe('ConnectorSelectorInline', () => { }, conversation: { apiConfig: { connectorId: '123' }, - replacements: [], + replacements: {}, category: 'assistant', id: 'conversation_id', messages: [], @@ -162,7 +162,7 @@ describe('ConnectorSelectorInline', () => { category: 'assistant', messages: [], apiConfig: { connectorId: '123' }, - replacements: [], + replacements: {}, title: 'conversation_id', }; const { getByTestId } = render( diff --git a/x-pack/packages/kbn-elastic-assistant/impl/mock/conversation.ts b/x-pack/packages/kbn-elastic-assistant/impl/mock/conversation.ts index 435e74c547256..f97505960574c 100644 --- a/x-pack/packages/kbn-elastic-assistant/impl/mock/conversation.ts +++ b/x-pack/packages/kbn-elastic-assistant/impl/mock/conversation.ts @@ -25,11 +25,11 @@ export const alertConvo: Conversation = { connectorId: 'c29c28a0-20fe-11ee-9306-a1f4d42ec542', provider: OpenAiProviderType.OpenAi, }, - replacements: [ - { uuid: '94277492-11f8-493b-9c52-c1c9ecd330d2', value: '192.168.0.4' }, - { uuid: '67bf8338-261a-4de6-b43e-d30b59e884a7', value: '192.168.0.1' }, - { uuid: '0b2e352b-35fc-47bd-a8d4-43019ed38a25', value: 'Stephs-MacBook-Pro.local' }, - ], + replacements: { + '94277492-11f8-493b-9c52-c1c9ecd330d2': '192.168.0.4', + '67bf8338-261a-4de6-b43e-d30b59e884a7': '192.168.0.1', + '0b2e352b-35fc-47bd-a8d4-43019ed38a25': 'Stephs-MacBook-Pro.local', + }, }; export const emptyWelcomeConvo: Conversation = { @@ -38,7 +38,7 @@ export const emptyWelcomeConvo: Conversation = { category: 'assistant', isDefault: true, messages: [], - replacements: [], + replacements: {}, apiConfig: { connectorId: 'c29c28a0-20fe-11ee-9306-a1f4d42ec542', provider: OpenAiProviderType.OpenAi, @@ -69,7 +69,7 @@ export const customConvo: Conversation = { title: 'Custom option', isDefault: false, messages: [], - replacements: [], + replacements: {}, apiConfig: { connectorId: 'c29c28a0-20fe-11ee-9306-a1f4d42ec542', provider: OpenAiProviderType.OpenAi, diff --git a/x-pack/packages/ml/aiops_log_rate_analysis/queries/__mocks__/field_caps_ecommerce.ts b/x-pack/packages/ml/aiops_log_rate_analysis/queries/__mocks__/field_caps_ecommerce.ts new file mode 100644 index 0000000000000..5de9ac8d2231a --- /dev/null +++ b/x-pack/packages/ml/aiops_log_rate_analysis/queries/__mocks__/field_caps_ecommerce.ts @@ -0,0 +1,253 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor 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 fieldCapsEcommerceMock = { + indices: ['ft_ecommerce'], + fields: { + 'products.manufacturer': { + text: { type: 'text', metadata_field: false, searchable: true, aggregatable: false }, + }, + 'products.discount_amount': { + half_float: { + type: 'half_float', + metadata_field: false, + searchable: true, + aggregatable: true, + }, + }, + 'products.base_unit_price': { + half_float: { + type: 'half_float', + metadata_field: false, + searchable: true, + aggregatable: true, + }, + }, + type: { + keyword: { type: 'keyword', metadata_field: false, searchable: true, aggregatable: true }, + }, + 'products.discount_percentage': { + half_float: { + type: 'half_float', + metadata_field: false, + searchable: true, + aggregatable: true, + }, + }, + 'products._id.keyword': { + keyword: { type: 'keyword', metadata_field: false, searchable: true, aggregatable: true }, + }, + day_of_week_i: { + integer: { type: 'integer', metadata_field: false, searchable: true, aggregatable: true }, + }, + total_quantity: { + integer: { type: 'integer', metadata_field: false, searchable: true, aggregatable: true }, + }, + total_unique_products: { + integer: { type: 'integer', metadata_field: false, searchable: true, aggregatable: true }, + }, + taxless_total_price: { + half_float: { + type: 'half_float', + metadata_field: false, + searchable: true, + aggregatable: true, + }, + }, + 'geoip.continent_name': { + keyword: { type: 'keyword', metadata_field: false, searchable: true, aggregatable: true }, + }, + sku: { + keyword: { type: 'keyword', metadata_field: false, searchable: true, aggregatable: true }, + }, + _version: { + _version: { type: '_version', metadata_field: true, searchable: false, aggregatable: true }, + }, + 'customer_full_name.keyword': { + keyword: { type: 'keyword', metadata_field: false, searchable: true, aggregatable: true }, + }, + 'category.keyword': { + keyword: { type: 'keyword', metadata_field: false, searchable: true, aggregatable: true }, + }, + 'products.taxless_price': { + half_float: { + type: 'half_float', + metadata_field: false, + searchable: true, + aggregatable: true, + }, + }, + 'products.quantity': { + integer: { type: 'integer', metadata_field: false, searchable: true, aggregatable: true }, + }, + 'products.price': { + half_float: { + type: 'half_float', + metadata_field: false, + searchable: true, + aggregatable: true, + }, + }, + customer_first_name: { + text: { type: 'text', metadata_field: false, searchable: true, aggregatable: false }, + }, + customer_phone: { + keyword: { type: 'keyword', metadata_field: false, searchable: true, aggregatable: true }, + }, + 'geoip.region_name': { + keyword: { type: 'keyword', metadata_field: false, searchable: true, aggregatable: true }, + }, + _tier: { + keyword: { type: 'keyword', metadata_field: true, searchable: true, aggregatable: true }, + }, + _seq_no: { + _seq_no: { type: '_seq_no', metadata_field: true, searchable: true, aggregatable: true }, + }, + customer_full_name: { + text: { type: 'text', metadata_field: false, searchable: true, aggregatable: false }, + }, + 'geoip.country_iso_code': { + keyword: { type: 'keyword', metadata_field: false, searchable: true, aggregatable: true }, + }, + _source: { + _source: { type: '_source', metadata_field: true, searchable: false, aggregatable: false }, + }, + _id: { _id: { type: '_id', metadata_field: true, searchable: true, aggregatable: false } }, + order_id: { + keyword: { type: 'keyword', metadata_field: false, searchable: true, aggregatable: true }, + }, + 'products._id': { + text: { type: 'text', metadata_field: false, searchable: true, aggregatable: false }, + }, + 'products.product_name.keyword': { + keyword: { type: 'keyword', metadata_field: false, searchable: true, aggregatable: true }, + }, + _index: { + _index: { type: '_index', metadata_field: true, searchable: true, aggregatable: true }, + }, + 'products.product_id': { + long: { type: 'long', metadata_field: false, searchable: true, aggregatable: true }, + }, + 'products.category': { + text: { type: 'text', metadata_field: false, searchable: true, aggregatable: false }, + }, + 'products.manufacturer.keyword': { + keyword: { type: 'keyword', metadata_field: false, searchable: true, aggregatable: true }, + }, + manufacturer: { + text: { type: 'text', metadata_field: false, searchable: true, aggregatable: false }, + }, + products: { + object: { type: 'object', metadata_field: false, searchable: false, aggregatable: false }, + }, + 'products.unit_discount_amount': { + half_float: { + type: 'half_float', + metadata_field: false, + searchable: true, + aggregatable: true, + }, + }, + customer_last_name: { + text: { type: 'text', metadata_field: false, searchable: true, aggregatable: false }, + }, + 'geoip.location': { + geo_point: { type: 'geo_point', metadata_field: false, searchable: true, aggregatable: true }, + }, + 'products.tax_amount': { + half_float: { + type: 'half_float', + metadata_field: false, + searchable: true, + aggregatable: true, + }, + }, + 'products.product_name': { + text: { type: 'text', metadata_field: false, searchable: true, aggregatable: false }, + }, + 'products.min_price': { + half_float: { + type: 'half_float', + metadata_field: false, + searchable: true, + aggregatable: true, + }, + }, + 'manufacturer.keyword': { + keyword: { type: 'keyword', metadata_field: false, searchable: true, aggregatable: true }, + }, + 'products.taxful_price': { + half_float: { + type: 'half_float', + metadata_field: false, + searchable: true, + aggregatable: true, + }, + }, + currency: { + keyword: { type: 'keyword', metadata_field: false, searchable: true, aggregatable: true }, + }, + 'products.base_price': { + half_float: { + type: 'half_float', + metadata_field: false, + searchable: true, + aggregatable: true, + }, + }, + email: { + keyword: { type: 'keyword', metadata_field: false, searchable: true, aggregatable: true }, + }, + day_of_week: { + keyword: { type: 'keyword', metadata_field: false, searchable: true, aggregatable: true }, + }, + 'products.sku': { + keyword: { type: 'keyword', metadata_field: false, searchable: true, aggregatable: true }, + }, + 'customer_last_name.keyword': { + keyword: { type: 'keyword', metadata_field: false, searchable: true, aggregatable: true }, + }, + geoip: { + object: { type: 'object', metadata_field: false, searchable: false, aggregatable: false }, + }, + 'products.category.keyword': { + keyword: { type: 'keyword', metadata_field: false, searchable: true, aggregatable: true }, + }, + 'geoip.city_name': { + keyword: { type: 'keyword', metadata_field: false, searchable: true, aggregatable: true }, + }, + order_date: { + date: { type: 'date', metadata_field: false, searchable: true, aggregatable: true }, + }, + 'customer_first_name.keyword': { + keyword: { type: 'keyword', metadata_field: false, searchable: true, aggregatable: true }, + }, + 'products.created_on': { + date: { type: 'date', metadata_field: false, searchable: true, aggregatable: true }, + }, + category: { + text: { type: 'text', metadata_field: false, searchable: true, aggregatable: false }, + }, + customer_id: { + keyword: { type: 'keyword', metadata_field: false, searchable: true, aggregatable: true }, + }, + user: { + keyword: { type: 'keyword', metadata_field: false, searchable: true, aggregatable: true }, + }, + customer_gender: { + keyword: { type: 'keyword', metadata_field: false, searchable: true, aggregatable: true }, + }, + taxful_total_price: { + half_float: { + type: 'half_float', + metadata_field: false, + searchable: true, + aggregatable: true, + }, + }, + }, +}; diff --git a/x-pack/packages/ml/aiops_log_rate_analysis/queries/__mocks__/field_caps_large_arrays.ts b/x-pack/packages/ml/aiops_log_rate_analysis/queries/__mocks__/field_caps_large_arrays.ts new file mode 100644 index 0000000000000..93a7c230e23c5 --- /dev/null +++ b/x-pack/packages/ml/aiops_log_rate_analysis/queries/__mocks__/field_caps_large_arrays.ts @@ -0,0 +1,34 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +export const fieldCapsLargeArraysMock = { + indices: ['large_arrays'], + fields: { + _tier: { + keyword: { type: 'keyword', metadata_field: true, searchable: true, aggregatable: true }, + }, + _seq_no: { + _seq_no: { type: '_seq_no', metadata_field: true, searchable: true, aggregatable: true }, + }, + '@timestamp': { + date: { type: 'date', metadata_field: false, searchable: true, aggregatable: true }, + }, + _index: { + _index: { type: '_index', metadata_field: true, searchable: true, aggregatable: true }, + }, + _source: { + _source: { type: '_source', metadata_field: true, searchable: false, aggregatable: false }, + }, + _id: { _id: { type: '_id', metadata_field: true, searchable: true, aggregatable: false } }, + _version: { + _version: { type: '_version', metadata_field: true, searchable: false, aggregatable: true }, + }, + items: { + keyword: { type: 'keyword', metadata_field: false, searchable: true, aggregatable: true }, + }, + }, +}; diff --git a/x-pack/packages/ml/aiops_log_rate_analysis/queries/__mocks__/field_caps_pgbench.ts b/x-pack/packages/ml/aiops_log_rate_analysis/queries/__mocks__/field_caps_pgbench.ts new file mode 100644 index 0000000000000..93fd2ad505ad5 --- /dev/null +++ b/x-pack/packages/ml/aiops_log_rate_analysis/queries/__mocks__/field_caps_pgbench.ts @@ -0,0 +1,286 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor 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 fieldCapsPgBenchMock = { + indices: ['my-index'], + fields: { + stack: { + keyword: { type: 'keyword', metadata_field: false, searchable: true, aggregatable: true }, + }, + metadata: { + flattened: { type: 'flattened', metadata_field: false, searchable: true, aggregatable: true }, + }, + 'kubernetes.namespace_uid': { + keyword: { type: 'keyword', metadata_field: false, searchable: true, aggregatable: true }, + }, + 'host.hostname': { + keyword: { type: 'keyword', metadata_field: false, searchable: true, aggregatable: true }, + }, + 'kubernetes.node.labels.kubernetes_io/os': { + keyword: { type: 'keyword', metadata_field: false, searchable: true, aggregatable: true }, + }, + hostname: { + keyword: { type: 'keyword', metadata_field: false, searchable: true, aggregatable: true }, + }, + _metadata: { + object: { type: 'object', metadata_field: false, searchable: false, aggregatable: false }, + }, + _version: { + _version: { type: '_version', metadata_field: true, searchable: false, aggregatable: true }, + }, + 'req.headers.x-real-ip': { + keyword: { type: 'keyword', metadata_field: false, searchable: true, aggregatable: true }, + }, + amount_f: { + float: { type: 'float', metadata_field: false, searchable: true, aggregatable: true }, + }, + 'kubernetes.node.labels.addon_gke_io/node-local-dns-ds-ready': { + keyword: { type: 'keyword', metadata_field: false, searchable: true, aggregatable: true }, + }, + 'container.labels': { + object: { type: 'object', metadata_field: false, searchable: false, aggregatable: false }, + }, + 'kubernetes.pod.ip': { + ip: { type: 'ip', metadata_field: false, searchable: true, aggregatable: true }, + }, + '_metadata.user_id': { + keyword: { type: 'keyword', metadata_field: false, searchable: true, aggregatable: true }, + }, + 'kubernetes.container.name': { + keyword: { type: 'keyword', metadata_field: false, searchable: true, aggregatable: true }, + }, + 'postgresql.log.database': { + keyword: { type: 'keyword', metadata_field: false, searchable: true, aggregatable: true }, + }, + 'container.labels.annotation_io_kubernetes_container_restartCount': { + keyword: { type: 'keyword', metadata_field: false, searchable: true, aggregatable: true }, + }, + fileset: { + object: { type: 'object', metadata_field: false, searchable: false, aggregatable: false }, + }, + 'kubernetes.node.labels.beta_kubernetes_io/arch': { + keyword: { type: 'keyword', metadata_field: false, searchable: true, aggregatable: true }, + }, + 'host.os.platform': { + keyword: { type: 'keyword', metadata_field: false, searchable: true, aggregatable: true }, + }, + _field_names: { + _field_names: { + type: '_field_names', + metadata_field: true, + searchable: true, + aggregatable: false, + }, + }, + 'cloud.account.id': { + keyword: { type: 'keyword', metadata_field: false, searchable: true, aggregatable: true }, + }, + v: { long: { type: 'long', metadata_field: false, searchable: true, aggregatable: true } }, + 'error.message': { + text: { type: 'text', metadata_field: false, searchable: true, aggregatable: false }, + }, + elasticapm_transaction_id: { + keyword: { type: 'keyword', metadata_field: false, searchable: true, aggregatable: true }, + }, + 'log.file.path': { + keyword: { type: 'keyword', metadata_field: false, searchable: true, aggregatable: true }, + }, + 'kubernetes.node.labels.kubernetes_io/arch': { + keyword: { type: 'keyword', metadata_field: false, searchable: true, aggregatable: true }, + }, + 'container.labels.io_kubernetes_container_name': { + keyword: { type: 'keyword', metadata_field: false, searchable: true, aggregatable: true }, + }, + 'user.name': { + keyword: { type: 'keyword', metadata_field: false, searchable: true, aggregatable: true }, + }, + 'user.name.text': { + text: { type: 'text', metadata_field: false, searchable: true, aggregatable: false }, + }, + 'cloud.instance.name': { + keyword: { type: 'keyword', metadata_field: false, searchable: true, aggregatable: true }, + }, + 'req.headers.accept-encoding': { + keyword: { type: 'keyword', metadata_field: false, searchable: true, aggregatable: true }, + }, + kubernetes: { + object: { type: 'object', metadata_field: false, searchable: false, aggregatable: false }, + }, + agent: { + object: { type: 'object', metadata_field: false, searchable: false, aggregatable: false }, + }, + 'cloud.instance': { + object: { type: 'object', metadata_field: false, searchable: false, aggregatable: false }, + }, + 'container.labels.io_kubernetes_pod_namespace': { + keyword: { type: 'keyword', metadata_field: false, searchable: true, aggregatable: true }, + }, + 'kubernetes.labels.pod-template-hash': { + keyword: { type: 'keyword', metadata_field: false, searchable: true, aggregatable: true }, + }, + 'log.origin': { + object: { type: 'object', metadata_field: false, searchable: false, aggregatable: false }, + }, + 'kubernetes.node.labels.cloud_google_com/machine-family': { + keyword: { type: 'keyword', metadata_field: false, searchable: true, aggregatable: true }, + }, + elasticapm_span_id: { + keyword: { type: 'keyword', metadata_field: false, searchable: true, aggregatable: true }, + }, + 'host.os': { + object: { type: 'object', metadata_field: false, searchable: false, aggregatable: false }, + }, + 'host.os.name': { + keyword: { type: 'keyword', metadata_field: false, searchable: true, aggregatable: true }, + }, + 'host.os.name.text': { + text: { type: 'text', metadata_field: false, searchable: true, aggregatable: false }, + }, + 'log.level': { + keyword: { type: 'keyword', metadata_field: false, searchable: true, aggregatable: true }, + }, + details: { + keyword: { type: 'keyword', metadata_field: false, searchable: true, aggregatable: true }, + }, + 'postgresql.log.query': { + keyword: { type: 'keyword', metadata_field: false, searchable: true, aggregatable: true }, + }, + 'process.thread': { + object: { type: 'object', metadata_field: false, searchable: false, aggregatable: false }, + }, + 'container.labels.annotation_io_kubernetes_pod_terminationGracePeriod': { + keyword: { type: 'keyword', metadata_field: false, searchable: true, aggregatable: true }, + }, + req: { + object: { type: 'object', metadata_field: false, searchable: false, aggregatable: false }, + }, + 'kubernetes.node.labels.cloud_google_com/gke-boot-disk': { + keyword: { type: 'keyword', metadata_field: false, searchable: true, aggregatable: true }, + }, + '_metadata.elastic_apm_trace_id': { + keyword: { type: 'keyword', metadata_field: false, searchable: true, aggregatable: true }, + }, + 'log.file': { + object: { type: 'object', metadata_field: false, searchable: false, aggregatable: false }, + }, + 'log.offset': { + long: { type: 'long', metadata_field: false, searchable: true, aggregatable: true }, + }, + 'client.ip': { + ip: { type: 'ip', metadata_field: false, searchable: true, aggregatable: true }, + }, + 'process.name': { + keyword: { type: 'keyword', metadata_field: false, searchable: true, aggregatable: true }, + }, + 'process.name.text': { + text: { type: 'text', metadata_field: false, searchable: true, aggregatable: false }, + }, + name: { + keyword: { type: 'keyword', metadata_field: false, searchable: true, aggregatable: true }, + }, + 'agent.version': { + keyword: { type: 'keyword', metadata_field: false, searchable: true, aggregatable: true }, + }, + 'host.os.family': { + keyword: { type: 'keyword', metadata_field: false, searchable: true, aggregatable: true }, + }, + 'req.headers.origin': { + keyword: { type: 'keyword', metadata_field: false, searchable: true, aggregatable: true }, + }, + 'kubernetes.node.labels.node_kubernetes_io/instance-type': { + keyword: { type: 'keyword', metadata_field: false, searchable: true, aggregatable: true }, + }, + 'req.headers.tracestate': { + keyword: { type: 'keyword', metadata_field: false, searchable: true, aggregatable: true }, + }, + 'postgresql.log.timestamp': { + keyword: { type: 'keyword', metadata_field: false, searchable: true, aggregatable: true }, + }, + '_metadata.metadata_event_dataset': { + keyword: { type: 'keyword', metadata_field: false, searchable: true, aggregatable: true }, + }, + related: { + object: { type: 'object', metadata_field: false, searchable: false, aggregatable: false }, + }, + 'event.module': { + keyword: { type: 'keyword', metadata_field: false, searchable: true, aggregatable: true }, + }, + 'req.headers': { + object: { type: 'object', metadata_field: false, searchable: false, aggregatable: false }, + }, + 'host.os.kernel': { + keyword: { type: 'keyword', metadata_field: false, searchable: true, aggregatable: true }, + }, + 'kubernetes.node.labels.cloud_google_com/gke-container-runtime': { + keyword: { type: 'keyword', metadata_field: false, searchable: true, aggregatable: true }, + }, + 'kubernetes.pod.name': { + keyword: { type: 'keyword', metadata_field: false, searchable: true, aggregatable: true }, + }, + client: { + object: { type: 'object', metadata_field: false, searchable: false, aggregatable: false }, + }, + 'req.headers.cache-control': { + keyword: { type: 'keyword', metadata_field: false, searchable: true, aggregatable: true }, + }, + 'event.timezone': { + keyword: { type: 'keyword', metadata_field: false, searchable: true, aggregatable: true }, + }, + 'log.origin.file.line': { + long: { type: 'long', metadata_field: false, searchable: true, aggregatable: true }, + }, + 'service.name': { + keyword: { type: 'keyword', metadata_field: false, searchable: true, aggregatable: true }, + }, + 'kubernetes.namespace_labels.kubernetes_io/metadata_name': { + keyword: { type: 'keyword', metadata_field: false, searchable: true, aggregatable: true }, + }, + message: { + text: { type: 'text', metadata_field: false, searchable: true, aggregatable: false }, + }, + _source: { + _source: { type: '_source', metadata_field: true, searchable: false, aggregatable: false }, + }, + log: { + object: { type: 'object', metadata_field: false, searchable: false, aggregatable: false }, + }, + event: { + object: { type: 'object', metadata_field: false, searchable: false, aggregatable: false }, + }, + 'event.duration': { + long: { type: 'long', metadata_field: false, searchable: true, aggregatable: true }, + }, + 'event.ingested': { + date: { type: 'date', metadata_field: false, searchable: true, aggregatable: true }, + }, + '@timestamp': { + date: { type: 'date', metadata_field: false, searchable: true, aggregatable: true }, + }, + transaction: { + object: { type: 'object', metadata_field: false, searchable: false, aggregatable: false }, + }, + span: { + object: { type: 'object', metadata_field: false, searchable: false, aggregatable: false }, + }, + '_metadata.sum': { + long: { type: 'long', metadata_field: false, searchable: true, aggregatable: true }, + }, + _tier: { + keyword: { type: 'keyword', metadata_field: true, searchable: true, aggregatable: true }, + }, + _seq_no: { + _seq_no: { type: '_seq_no', metadata_field: true, searchable: true, aggregatable: true }, + }, + code: { long: { type: 'long', metadata_field: false, searchable: true, aggregatable: true } }, + _index: { + _index: { type: '_index', metadata_field: true, searchable: true, aggregatable: true }, + }, + 'client.geo.location': { + geo_point: { type: 'geo_point', metadata_field: false, searchable: true, aggregatable: true }, + }, + }, +}; diff --git a/x-pack/packages/ml/aiops_log_rate_analysis/queries/fetch_index_info.test.ts b/x-pack/packages/ml/aiops_log_rate_analysis/queries/fetch_index_info.test.ts index 9cda77afd1c42..780c4546259d3 100644 --- a/x-pack/packages/ml/aiops_log_rate_analysis/queries/fetch_index_info.test.ts +++ b/x-pack/packages/ml/aiops_log_rate_analysis/queries/fetch_index_info.test.ts @@ -10,12 +10,15 @@ import type * as estypes from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; import type { ElasticsearchClient } from '@kbn/core/server'; import { paramsSearchQueryMock } from './__mocks__/params_search_query'; +import { fieldCapsPgBenchMock } from './__mocks__/field_caps_pgbench'; +import { fieldCapsEcommerceMock } from './__mocks__/field_caps_ecommerce'; +import { fieldCapsLargeArraysMock } from './__mocks__/field_caps_large_arrays'; import { fetchIndexInfo } from './fetch_index_info'; describe('fetch_index_info', () => { describe('fetchFieldCandidates', () => { - it('returns field candidates and total hits', async () => { + it('returns field candidates and total hits for "my" fields', async () => { const esClientFieldCapsMock = jest.fn(() => ({ fields: { // Should end up as a field candidate @@ -31,15 +34,7 @@ describe('fetch_index_info', () => { const esClientSearchMock = jest.fn((req: estypes.SearchRequest): estypes.SearchResponse => { return { hits: { - hits: [ - { - fields: { - myIpFieldName: '1.1.1.1', - myKeywordFieldName: 'myKeywordFieldValue', - myNumericFieldName: 1234, - }, - }, - ], + hits: [], total: { value: 5000000 }, }, } as unknown as estypes.SearchResponse; @@ -59,5 +54,175 @@ describe('fetch_index_info', () => { expect(esClientFieldCapsMock).toHaveBeenCalledTimes(1); expect(esClientSearchMock).toHaveBeenCalledTimes(2); }); + + it('returns field candidates and total hits for pgBench mappings', async () => { + const esClientFieldCapsMock = jest.fn(() => fieldCapsPgBenchMock); + const esClientSearchMock = jest.fn((req: estypes.SearchRequest): estypes.SearchResponse => { + return { + hits: { + hits: [], + total: { value: 5000000 }, + }, + } as unknown as estypes.SearchResponse; + }); + + const esClientMock = { + fieldCaps: esClientFieldCapsMock, + search: esClientSearchMock, + } as unknown as ElasticsearchClient; + + const { + baselineTotalDocCount, + deviationTotalDocCount, + fieldCandidates, + textFieldCandidates, + } = await fetchIndexInfo(esClientMock, paramsSearchQueryMock); + + expect(fieldCandidates).toEqual([ + '_metadata.elastic_apm_trace_id', + '_metadata.metadata_event_dataset', + '_metadata.user_id', + 'agent.version', + 'client.ip', + 'cloud.account.id', + 'cloud.instance.name', + 'container.labels.annotation_io_kubernetes_container_restartCount', + 'container.labels.annotation_io_kubernetes_pod_terminationGracePeriod', + 'container.labels.io_kubernetes_container_name', + 'container.labels.io_kubernetes_pod_namespace', + 'details', + 'elasticapm_span_id', + 'elasticapm_transaction_id', + 'event.module', + 'event.timezone', + 'host.hostname', + 'host.os.family', + 'host.os.kernel', + 'host.os.name', + 'host.os.platform', + 'hostname', + 'kubernetes.container.name', + 'kubernetes.labels.pod-template-hash', + 'kubernetes.namespace_labels.kubernetes_io/metadata_name', + 'kubernetes.namespace_uid', + 'kubernetes.node.labels.addon_gke_io/node-local-dns-ds-ready', + 'kubernetes.node.labels.beta_kubernetes_io/arch', + 'kubernetes.node.labels.cloud_google_com/gke-boot-disk', + 'kubernetes.node.labels.cloud_google_com/gke-container-runtime', + 'kubernetes.node.labels.cloud_google_com/machine-family', + 'kubernetes.node.labels.kubernetes_io/arch', + 'kubernetes.node.labels.kubernetes_io/os', + 'kubernetes.node.labels.node_kubernetes_io/instance-type', + 'kubernetes.pod.ip', + 'kubernetes.pod.name', + 'log.file.path', + 'log.level', + 'name', + 'postgresql.log.database', + 'postgresql.log.query', + 'postgresql.log.timestamp', + 'process.name', + 'req.headers.accept-encoding', + 'req.headers.cache-control', + 'req.headers.origin', + 'req.headers.tracestate', + 'req.headers.x-real-ip', + 'service.name', + 'stack', + 'user.name', + ]); + expect(textFieldCandidates).toEqual(['error.message', 'message']); + expect(baselineTotalDocCount).toEqual(5000000); + expect(deviationTotalDocCount).toEqual(5000000); + expect(esClientFieldCapsMock).toHaveBeenCalledTimes(1); + expect(esClientSearchMock).toHaveBeenCalledTimes(2); + }); + + it('returns field candidates and total hits for ecommerce mappings', async () => { + const esClientFieldCapsMock = jest.fn(() => fieldCapsEcommerceMock); + const esClientSearchMock = jest.fn((req: estypes.SearchRequest): estypes.SearchResponse => { + return { + hits: { + hits: [], + total: { value: 5000000 }, + }, + } as unknown as estypes.SearchResponse; + }); + + const esClientMock = { + fieldCaps: esClientFieldCapsMock, + search: esClientSearchMock, + } as unknown as ElasticsearchClient; + + const { + baselineTotalDocCount, + deviationTotalDocCount, + fieldCandidates, + textFieldCandidates, + } = await fetchIndexInfo(esClientMock, paramsSearchQueryMock); + + expect(fieldCandidates).toEqual([ + 'category.keyword', + 'currency', + 'customer_first_name.keyword', + 'customer_full_name.keyword', + 'customer_gender', + 'customer_id', + 'customer_last_name.keyword', + 'customer_phone', + 'day_of_week', + 'email', + 'geoip.city_name', + 'geoip.continent_name', + 'geoip.country_iso_code', + 'geoip.region_name', + 'manufacturer.keyword', + 'order_id', + 'products._id.keyword', + 'products.category.keyword', + 'products.manufacturer.keyword', + 'products.product_name.keyword', + 'products.sku', + 'sku', + 'type', + 'user', + ]); + expect(textFieldCandidates).toEqual([]); + expect(baselineTotalDocCount).toEqual(5000000); + expect(deviationTotalDocCount).toEqual(5000000); + expect(esClientFieldCapsMock).toHaveBeenCalledTimes(1); + expect(esClientSearchMock).toHaveBeenCalledTimes(2); + }); + + it('returns field candidates and total hits for large-arrays mappings', async () => { + const esClientFieldCapsMock = jest.fn(() => fieldCapsLargeArraysMock); + const esClientSearchMock = jest.fn((req: estypes.SearchRequest): estypes.SearchResponse => { + return { + hits: { + hits: [], + total: { value: 5000000 }, + }, + } as unknown as estypes.SearchResponse; + }); + + const esClientMock = { + fieldCaps: esClientFieldCapsMock, + search: esClientSearchMock, + } as unknown as ElasticsearchClient; + + const { + baselineTotalDocCount, + deviationTotalDocCount, + fieldCandidates, + textFieldCandidates, + } = await fetchIndexInfo(esClientMock, paramsSearchQueryMock); + + expect(fieldCandidates).toEqual(['items']); + expect(textFieldCandidates).toEqual([]); + expect(baselineTotalDocCount).toEqual(5000000); + expect(deviationTotalDocCount).toEqual(5000000); + expect(esClientFieldCapsMock).toHaveBeenCalledTimes(1); + expect(esClientSearchMock).toHaveBeenCalledTimes(2); + }); }); }); diff --git a/x-pack/packages/ml/aiops_log_rate_analysis/queries/fetch_index_info.ts b/x-pack/packages/ml/aiops_log_rate_analysis/queries/fetch_index_info.ts index f1c2a3cb60ff8..b06a19738f2f9 100644 --- a/x-pack/packages/ml/aiops_log_rate_analysis/queries/fetch_index_info.ts +++ b/x-pack/packages/ml/aiops_log_rate_analysis/queries/fetch_index_info.ts @@ -101,10 +101,13 @@ export const fetchIndexInfo = async ( const fieldCandidates: string[] = [...acceptableFields].filter( (field) => !textFieldCandidatesOverridesWithKeywordPostfix.includes(field) ); - const textFieldCandidates: string[] = [...acceptableTextFields].filter( - (field) => - !allFieldNames.includes(`${field}.keyword`) || textFieldCandidatesOverrides.includes(field) - ); + const textFieldCandidates: string[] = [...acceptableTextFields].filter((field) => { + const fieldName = field.replace(new RegExp(/\.text$/), ''); + return ( + (!fieldCandidates.includes(fieldName) && !fieldCandidates.includes(`${fieldName}.keyword`)) || + textFieldCandidatesOverrides.includes(field) + ); + }); const baselineTotalDocCount = (respBaselineTotalDocCount.hits.total as estypes.SearchTotalHits) .value; @@ -112,8 +115,8 @@ export const fetchIndexInfo = async ( .value; return { - fieldCandidates, - textFieldCandidates, + fieldCandidates: fieldCandidates.sort(), + textFieldCandidates: textFieldCandidates.sort(), baselineTotalDocCount, deviationTotalDocCount, zeroDocsFallback: baselineTotalDocCount === 0 || deviationTotalDocCount === 0, diff --git a/x-pack/packages/security/plugin_types_public/src/user_profile/user_profile_api_client.ts b/x-pack/packages/security/plugin_types_public/src/user_profile/user_profile_api_client.ts index 090d5d5b0fcf4..a397a16e5ad3a 100644 --- a/x-pack/packages/security/plugin_types_public/src/user_profile/user_profile_api_client.ts +++ b/x-pack/packages/security/plugin_types_public/src/user_profile/user_profile_api_client.ts @@ -15,6 +15,14 @@ import type { Observable } from 'rxjs'; export interface UserProfileAPIClient { readonly userProfile$: Observable; + /** + * Indicates if the user profile data has been loaded from the server. + * Useful to distinguish between the case when the user profile data is `null` because the HTTP + * request has not finished or because there is no user profile data for the current user. + */ + readonly userProfileLoaded$: Observable; + /** Flag to indicate if the current user has a user profile. Anonymous users don't have user profiles. */ + readonly enabled$: Observable; /** * Retrieves the user profile of the current user. If the profile isn't available, e.g. for the anonymous users or * users authenticated via authenticating proxies, the `null` value is returned. @@ -63,6 +71,12 @@ export interface UserProfileAPIClient { * @param data Application data to be written (merged with existing data). */ update(data: D): Promise; + + /** + * Partially updates user profile data of the current user, merging the previous data with the provided data. + * @param data Application data to be merged with existing data. + */ + partialUpdate>(data: D): Promise; } /** diff --git a/x-pack/plugins/actions/server/action_type_registry.test.ts b/x-pack/plugins/actions/server/action_type_registry.test.ts index f759712d6b45f..dfc42e42b0b34 100644 --- a/x-pack/plugins/actions/server/action_type_registry.test.ts +++ b/x-pack/plugins/actions/server/action_type_registry.test.ts @@ -495,7 +495,7 @@ describe('actionTypeRegistry', () => { expect(actionTypeRegistry.isActionExecutable('my-slack1', 'foo')).toEqual(true); }); - test('should return false when isActionTypeEnabled is false and isLicenseValidForActionType is true and it has system connectors', async () => { + test('should return true when isActionTypeEnabled is false and isLicenseValidForActionType is true and it has system connectors', async () => { mockedActionsConfig.isActionTypeEnabled.mockReturnValue(false); mockedLicenseState.isLicenseValidForActionType.mockReturnValue({ isValid: true }); @@ -504,7 +504,7 @@ describe('actionTypeRegistry', () => { 'system-connector-test.system-action', 'system-action-type' ) - ).toEqual(false); + ).toEqual(true); }); test('should call isLicenseValidForActionType of the license state with notifyUsage false by default', async () => { diff --git a/x-pack/plugins/actions/server/action_type_registry.ts b/x-pack/plugins/actions/server/action_type_registry.ts index a1ac1c2d3ac21..4e6f9aa86482d 100644 --- a/x-pack/plugins/actions/server/action_type_registry.ts +++ b/x-pack/plugins/actions/server/action_type_registry.ts @@ -92,7 +92,11 @@ export class ActionTypeRegistry { (connector) => connector.id === actionId ); - return actionTypeEnabled || (!actionTypeEnabled && inMemoryConnector?.isPreconfigured === true); + return ( + actionTypeEnabled || + (!actionTypeEnabled && + (inMemoryConnector?.isPreconfigured === true || inMemoryConnector?.isSystemAction === true)) + ); } /** diff --git a/x-pack/plugins/actions/server/actions_client/actions_client.mock.ts b/x-pack/plugins/actions/server/actions_client/actions_client.mock.ts index 5a369272617a2..d58476738b9be 100644 --- a/x-pack/plugins/actions/server/actions_client/actions_client.mock.ts +++ b/x-pack/plugins/actions/server/actions_client/actions_client.mock.ts @@ -18,6 +18,7 @@ const createActionsClientMock = () => { delete: jest.fn(), update: jest.fn(), getAll: jest.fn(), + getAllSystemConnectors: jest.fn(), getBulk: jest.fn(), getOAuthAccessToken: jest.fn(), execute: jest.fn(), diff --git a/x-pack/plugins/actions/server/actions_client/actions_client.test.ts b/x-pack/plugins/actions/server/actions_client/actions_client.test.ts index ff5258ef43cca..8f08bbb4b2d5a 100644 --- a/x-pack/plugins/actions/server/actions_client/actions_client.test.ts +++ b/x-pack/plugins/actions/server/actions_client/actions_client.test.ts @@ -2797,7 +2797,7 @@ describe('execute()', () => { }); expect(authorization.ensureAuthorized).toHaveBeenCalledWith({ - actionTypeId: 'my-action-type', + actionTypeId: '.cases', operation: 'execute', additionalPrivileges: ['test/create'], }); @@ -2930,7 +2930,7 @@ describe('execute()', () => { }); expect(authorization.ensureAuthorized).toHaveBeenCalledWith({ - actionTypeId: 'my-action-type', + actionTypeId: '.cases', operation: 'execute', additionalPrivileges: ['test/create'], }); diff --git a/x-pack/plugins/actions/server/actions_client/actions_client.ts b/x-pack/plugins/actions/server/actions_client/actions_client.ts index a5314ab2d7d3d..1187898006f9e 100644 --- a/x-pack/plugins/actions/server/actions_client/actions_client.ts +++ b/x-pack/plugins/actions/server/actions_client/actions_client.ts @@ -91,6 +91,7 @@ import { } from '../lib/get_execution_log_aggregation'; import { connectorFromSavedObject, isConnectorDeprecated } from '../application/connector/lib'; import { ListTypesParams } from '../application/connector/methods/list_types/types'; +import { getAllSystemConnectors } from '../application/connector/methods/get_all/get_all'; interface ActionUpdate { name: string; @@ -418,6 +419,13 @@ export class ActionsClient { return getAll({ context: this.context, includeSystemActions }); } + /** + * Get all system connectors + */ + public async getAllSystemConnectors(): Promise { + return getAllSystemConnectors({ context: this.context }); + } + /** * Get bulk actions with in-memory list */ @@ -691,7 +699,7 @@ export class ActionsClient { let actionTypeId: string | undefined; try { - if (this.isPreconfigured(actionId)) { + if (this.isPreconfigured(actionId) || this.isSystemAction(actionId)) { const connector = this.context.inMemoryConnectors.find( (inMemoryConnector) => inMemoryConnector.id === actionId ); diff --git a/x-pack/plugins/actions/server/application/connector/methods/get_all/get_all.test.ts b/x-pack/plugins/actions/server/application/connector/methods/get_all/get_all.test.ts index 2032653712a59..6a87cb65daa6b 100644 --- a/x-pack/plugins/actions/server/application/connector/methods/get_all/get_all.test.ts +++ b/x-pack/plugins/actions/server/application/connector/methods/get_all/get_all.test.ts @@ -113,8 +113,152 @@ describe('getAll()', () => { getEventLogClient.mockResolvedValue(eventLogClient); }); - describe('authorization', () => { - function getAllOperation(): ReturnType { + describe('getAll()', () => { + describe('authorization', () => { + function getAllOperation(): ReturnType { + const expectedResult = { + total: 1, + per_page: 10, + page: 1, + saved_objects: [ + { + id: '1', + type: 'type', + attributes: { + name: 'test', + config: { + foo: 'bar', + }, + }, + score: 1, + references: [], + }, + ], + }; + unsecuredSavedObjectsClient.find.mockResolvedValueOnce(expectedResult); + scopedClusterClient.asInternalUser.search.mockResponse( + // @ts-expect-error not full search response + { + aggregations: { + '1': { doc_count: 6 }, + testPreconfigured: { doc_count: 2 }, + }, + } + ); + + actionsClient = new ActionsClient({ + logger, + actionTypeRegistry, + unsecuredSavedObjectsClient, + scopedClusterClient, + kibanaIndices, + actionExecutor, + ephemeralExecutionEnqueuer, + bulkExecutionEnqueuer, + request, + authorization: authorization as unknown as ActionsAuthorization, + inMemoryConnectors: [ + { + id: 'testPreconfigured', + actionTypeId: '.slack', + secrets: {}, + isPreconfigured: true, + isDeprecated: false, + isSystemAction: false, + name: 'test', + config: { + foo: 'bar', + }, + }, + ], + connectorTokenClient: connectorTokenClientMock.create(), + getEventLogClient, + }); + return actionsClient.getAll(); + } + + test('ensures user is authorised to get the type of action', async () => { + await getAllOperation(); + expect(authorization.ensureAuthorized).toHaveBeenCalledWith({ operation: 'get' }); + }); + + test('throws when user is not authorised to create the type of action', async () => { + authorization.ensureAuthorized.mockRejectedValue( + new Error(`Unauthorized to get all actions`) + ); + + await expect(getAllOperation()).rejects.toMatchInlineSnapshot( + `[Error: Unauthorized to get all actions]` + ); + + expect(authorization.ensureAuthorized).toHaveBeenCalledWith({ operation: 'get' }); + }); + }); + + describe('auditLogger', () => { + test('logs audit event when searching connectors', async () => { + unsecuredSavedObjectsClient.find.mockResolvedValueOnce({ + total: 1, + per_page: 10, + page: 1, + saved_objects: [ + { + id: '1', + type: 'type', + attributes: { + name: 'test', + isMissingSecrets: false, + config: { + foo: 'bar', + }, + }, + score: 1, + references: [], + }, + ], + }); + + scopedClusterClient.asInternalUser.search.mockResponse( + // @ts-expect-error not full search response + { + aggregations: { + '1': { doc_count: 6 }, + testPreconfigured: { doc_count: 2 }, + }, + } + ); + + await actionsClient.getAll(); + + expect(auditLogger.log).toHaveBeenCalledWith( + expect.objectContaining({ + event: expect.objectContaining({ + action: 'connector_find', + outcome: 'success', + }), + kibana: { saved_object: { id: '1', type: 'action' } }, + }) + ); + }); + + test('logs audit event when not authorised to search connectors', async () => { + authorization.ensureAuthorized.mockRejectedValue(new Error('Unauthorized')); + + await expect(actionsClient.getAll()).rejects.toThrow(); + + expect(auditLogger.log).toHaveBeenCalledWith( + expect.objectContaining({ + event: expect.objectContaining({ + action: 'connector_find', + outcome: 'failure', + }), + error: { code: 'Error', message: 'Unauthorized' }, + }) + ); + }); + }); + + test('calls unsecuredSavedObjectsClient with parameters and returns inMemoryConnectors correctly', async () => { const expectedResult = { total: 1, per_page: 10, @@ -125,6 +269,7 @@ describe('getAll()', () => { type: 'type', attributes: { name: 'test', + isMissingSecrets: false, config: { foo: 'bar', }, @@ -141,6 +286,7 @@ describe('getAll()', () => { aggregations: { '1': { doc_count: 6 }, testPreconfigured: { doc_count: 2 }, + 'system-connector-.cases': { doc_count: 2 }, }, } ); @@ -169,34 +315,54 @@ describe('getAll()', () => { foo: 'bar', }, }, + /** + * System actions will not + * be returned from getAll + * if no options are provided + */ + { + id: 'system-connector-.cases', + actionTypeId: '.cases', + name: 'System action: .cases', + config: {}, + secrets: {}, + isDeprecated: false, + isMissingSecrets: false, + isPreconfigured: false, + isSystemAction: true, + }, ], connectorTokenClient: connectorTokenClientMock.create(), getEventLogClient, }); - return actionsClient.getAll(); - } - - test('ensures user is authorised to get the type of action', async () => { - await getAllOperation(); - expect(authorization.ensureAuthorized).toHaveBeenCalledWith({ operation: 'get' }); - }); - - test('throws when user is not authorised to create the type of action', async () => { - authorization.ensureAuthorized.mockRejectedValue( - new Error(`Unauthorized to get all actions`) - ); - await expect(getAllOperation()).rejects.toMatchInlineSnapshot( - `[Error: Unauthorized to get all actions]` - ); + const result = await actionsClient.getAll(); - expect(authorization.ensureAuthorized).toHaveBeenCalledWith({ operation: 'get' }); + expect(result).toEqual([ + { + id: '1', + name: 'test', + isMissingSecrets: false, + config: { foo: 'bar' }, + isPreconfigured: false, + isDeprecated: false, + isSystemAction: false, + referencedByCount: 6, + }, + { + id: 'testPreconfigured', + actionTypeId: '.slack', + name: 'test', + isPreconfigured: true, + isSystemAction: false, + isDeprecated: false, + referencedByCount: 2, + }, + ]); }); - }); - describe('auditLogger', () => { - test('logs audit event when searching connectors', async () => { - unsecuredSavedObjectsClient.find.mockResolvedValueOnce({ + test('get system actions correctly', async () => { + const expectedResult = { total: 1, per_page: 10, page: 1, @@ -215,345 +381,329 @@ describe('getAll()', () => { references: [], }, ], - }); + }; + unsecuredSavedObjectsClient.find.mockResolvedValueOnce(expectedResult); scopedClusterClient.asInternalUser.search.mockResponse( // @ts-expect-error not full search response { aggregations: { '1': { doc_count: 6 }, testPreconfigured: { doc_count: 2 }, + 'system-connector-.cases': { doc_count: 2 }, }, } ); - await actionsClient.getAll(); - - expect(auditLogger.log).toHaveBeenCalledWith( - expect.objectContaining({ - event: expect.objectContaining({ - action: 'connector_find', - outcome: 'success', - }), - kibana: { saved_object: { id: '1', type: 'action' } }, - }) - ); - }); - - test('logs audit event when not authorised to search connectors', async () => { - authorization.ensureAuthorized.mockRejectedValue(new Error('Unauthorized')); - - await expect(actionsClient.getAll()).rejects.toThrow(); - - expect(auditLogger.log).toHaveBeenCalledWith( - expect.objectContaining({ - event: expect.objectContaining({ - action: 'connector_find', - outcome: 'failure', - }), - error: { code: 'Error', message: 'Unauthorized' }, - }) - ); - }); - }); - - test('calls unsecuredSavedObjectsClient with parameters and returns inMemoryConnectors correctly', async () => { - const expectedResult = { - total: 1, - per_page: 10, - page: 1, - saved_objects: [ - { - id: '1', - type: 'type', - attributes: { + actionsClient = new ActionsClient({ + logger, + actionTypeRegistry, + unsecuredSavedObjectsClient, + scopedClusterClient, + kibanaIndices, + actionExecutor, + ephemeralExecutionEnqueuer, + bulkExecutionEnqueuer, + request, + authorization: authorization as unknown as ActionsAuthorization, + inMemoryConnectors: [ + { + id: 'testPreconfigured', + actionTypeId: '.slack', + secrets: {}, + isPreconfigured: true, + isDeprecated: false, + isSystemAction: false, name: 'test', - isMissingSecrets: false, config: { foo: 'bar', }, }, - score: 1, - references: [], + { + id: 'system-connector-.cases', + actionTypeId: '.cases', + name: 'System action: .cases', + config: {}, + secrets: {}, + isDeprecated: false, + isMissingSecrets: false, + isPreconfigured: false, + isSystemAction: true, + }, + ], + connectorTokenClient: connectorTokenClientMock.create(), + getEventLogClient, + }); + + const result = await actionsClient.getAll({ includeSystemActions: true }); + + expect(result).toEqual([ + { + actionTypeId: '.cases', + id: 'system-connector-.cases', + isDeprecated: false, + isPreconfigured: false, + isSystemAction: true, + name: 'System action: .cases', + referencedByCount: 2, }, - ], - }; - unsecuredSavedObjectsClient.find.mockResolvedValueOnce(expectedResult); - scopedClusterClient.asInternalUser.search.mockResponse( - // @ts-expect-error not full search response - { - aggregations: { - '1': { doc_count: 6 }, - testPreconfigured: { doc_count: 2 }, - 'system-connector-.cases': { doc_count: 2 }, + { + id: '1', + name: 'test', + isMissingSecrets: false, + config: { foo: 'bar' }, + isPreconfigured: false, + isDeprecated: false, + isSystemAction: false, + referencedByCount: 6, }, - } - ); - - actionsClient = new ActionsClient({ - logger, - actionTypeRegistry, - unsecuredSavedObjectsClient, - scopedClusterClient, - kibanaIndices, - actionExecutor, - ephemeralExecutionEnqueuer, - bulkExecutionEnqueuer, - request, - authorization: authorization as unknown as ActionsAuthorization, - inMemoryConnectors: [ { id: 'testPreconfigured', actionTypeId: '.slack', - secrets: {}, + name: 'test', isPreconfigured: true, - isDeprecated: false, isSystemAction: false, - name: 'test', - config: { - foo: 'bar', - }, - }, - /** - * System actions will not - * be returned from getAll - * if no options are provided - */ - { - id: 'system-connector-.cases', - actionTypeId: '.cases', - name: 'System action: .cases', - config: {}, - secrets: {}, isDeprecated: false, - isMissingSecrets: false, - isPreconfigured: false, - isSystemAction: true, + referencedByCount: 2, }, - ], - connectorTokenClient: connectorTokenClientMock.create(), - getEventLogClient, + ]); }); - const result = await actionsClient.getAll(); - - expect(result).toEqual([ - { - id: '1', - name: 'test', - isMissingSecrets: false, - config: { foo: 'bar' }, - isPreconfigured: false, - isDeprecated: false, - isSystemAction: false, - referencedByCount: 6, - }, - { - id: 'testPreconfigured', - actionTypeId: '.slack', - name: 'test', - isPreconfigured: true, - isSystemAction: false, - isDeprecated: false, - referencedByCount: 2, - }, - ]); - }); - - test('get system actions correctly', async () => { - const expectedResult = { - total: 1, - per_page: 10, - page: 1, - saved_objects: [ + test('validates connectors before return', async () => { + unsecuredSavedObjectsClient.find.mockResolvedValueOnce({ + total: 1, + per_page: 10, + page: 1, + saved_objects: [ + { + id: '1', + type: 'type', + attributes: { + name: 'test', + isMissingSecrets: false, + config: { + foo: 'bar', + }, + }, + score: 1, + references: [], + }, + ], + }); + scopedClusterClient.asInternalUser.search.mockResponse( + // @ts-expect-error not full search response { - id: '1', - type: 'type', - attributes: { + aggregations: { + '1': { doc_count: 6 }, + testPreconfigured: { doc_count: 2 }, + }, + } + ); + + actionsClient = new ActionsClient({ + logger, + actionTypeRegistry, + unsecuredSavedObjectsClient, + scopedClusterClient, + kibanaIndices, + actionExecutor, + ephemeralExecutionEnqueuer, + bulkExecutionEnqueuer, + request, + authorization: authorization as unknown as ActionsAuthorization, + inMemoryConnectors: [ + { + id: 'testPreconfigured', + actionTypeId: '.slack', + secrets: {}, + isPreconfigured: true, + isDeprecated: false, + isSystemAction: false, name: 'test', - isMissingSecrets: false, config: { foo: 'bar', }, }, - score: 1, - references: [], - }, - ], - }; - unsecuredSavedObjectsClient.find.mockResolvedValueOnce(expectedResult); - scopedClusterClient.asInternalUser.search.mockResponse( - // @ts-expect-error not full search response - { - aggregations: { - '1': { doc_count: 6 }, - testPreconfigured: { doc_count: 2 }, - 'system-connector-.cases': { doc_count: 2 }, - }, - } - ); + ], + connectorTokenClient: connectorTokenClientMock.create(), + getEventLogClient, + }); - actionsClient = new ActionsClient({ - logger, - actionTypeRegistry, - unsecuredSavedObjectsClient, - scopedClusterClient, - kibanaIndices, - actionExecutor, - ephemeralExecutionEnqueuer, - bulkExecutionEnqueuer, - request, - authorization: authorization as unknown as ActionsAuthorization, - inMemoryConnectors: [ + const result = await actionsClient.getAll({ includeSystemActions: true }); + expect(result).toEqual([ { - id: 'testPreconfigured', - actionTypeId: '.slack', - secrets: {}, - isPreconfigured: true, - isDeprecated: false, - isSystemAction: false, - name: 'test', config: { foo: 'bar', }, - }, - { - id: 'system-connector-.cases', - actionTypeId: '.cases', - name: 'System action: .cases', - config: {}, - secrets: {}, + id: '1', isDeprecated: false, isMissingSecrets: false, isPreconfigured: false, - isSystemAction: true, + isSystemAction: false, + name: 'test', + referencedByCount: 6, }, - ], - connectorTokenClient: connectorTokenClientMock.create(), - getEventLogClient, + { + actionTypeId: '.slack', + id: 'testPreconfigured', + isDeprecated: false, + isPreconfigured: true, + isSystemAction: false, + name: 'test', + referencedByCount: 2, + }, + ]); + + expect(logger.warn).toHaveBeenCalledWith( + 'Error validating connector: 1, Error: [actionTypeId]: expected value of type [string] but got [undefined]' + ); }); + }); - const result = await actionsClient.getAll({ includeSystemActions: true }); + describe('getAllSystemConnectors()', () => { + describe('authorization', () => { + function getAllOperation(): ReturnType { + scopedClusterClient.asInternalUser.search.mockResponse( + // @ts-expect-error not full search response + { + aggregations: { + 'system-connector-.test': { doc_count: 2 }, + }, + } + ); - expect(result).toEqual([ - { - actionTypeId: '.cases', - id: 'system-connector-.cases', - isDeprecated: false, - isPreconfigured: false, - isSystemAction: true, - name: 'System action: .cases', - referencedByCount: 2, - }, - { - id: '1', - name: 'test', - isMissingSecrets: false, - config: { foo: 'bar' }, - isPreconfigured: false, - isDeprecated: false, - isSystemAction: false, - referencedByCount: 6, - }, - { - id: 'testPreconfigured', - actionTypeId: '.slack', - name: 'test', - isPreconfigured: true, - isSystemAction: false, - isDeprecated: false, - referencedByCount: 2, - }, - ]); - }); + actionsClient = new ActionsClient({ + logger, + actionTypeRegistry, + unsecuredSavedObjectsClient, + scopedClusterClient, + kibanaIndices, + actionExecutor, + ephemeralExecutionEnqueuer, + bulkExecutionEnqueuer, + request, + authorization: authorization as unknown as ActionsAuthorization, + inMemoryConnectors: [ + { + id: 'system-connector-.test', + actionTypeId: '.test', + name: 'Test system action', + config: {}, + secrets: {}, + isDeprecated: false, + isMissingSecrets: false, + isPreconfigured: false, + isSystemAction: true, + }, + ], + connectorTokenClient: connectorTokenClientMock.create(), + getEventLogClient, + }); - test('validates connectors before return', async () => { - unsecuredSavedObjectsClient.find.mockResolvedValueOnce({ - total: 1, - per_page: 10, - page: 1, - saved_objects: [ + return actionsClient.getAllSystemConnectors(); + } + + test('ensures user is authorised to get the type of action', async () => { + await getAllOperation(); + expect(authorization.ensureAuthorized).toHaveBeenCalledWith({ operation: 'get' }); + }); + + test('throws when user is not authorised to get the type of action', async () => { + authorization.ensureAuthorized.mockRejectedValue( + new Error(`Unauthorized to get all actions`) + ); + + await expect(getAllOperation()).rejects.toMatchInlineSnapshot( + `[Error: Unauthorized to get all actions]` + ); + + expect(authorization.ensureAuthorized).toHaveBeenCalledWith({ operation: 'get' }); + }); + }); + + describe('auditLogger', () => { + test('logs audit event when not authorised to search connectors', async () => { + authorization.ensureAuthorized.mockRejectedValue(new Error('Unauthorized')); + + await expect(actionsClient.getAllSystemConnectors()).rejects.toThrow(); + + expect(auditLogger.log).toHaveBeenCalledWith( + expect.objectContaining({ + event: expect.objectContaining({ + action: 'connector_find', + outcome: 'failure', + }), + error: { code: 'Error', message: 'Unauthorized' }, + }) + ); + }); + }); + + test('get all system actions correctly', async () => { + scopedClusterClient.asInternalUser.search.mockResponse( + // @ts-expect-error not full search response { - id: '1', - type: 'type', - attributes: { + aggregations: { + 'system-connector-.test': { doc_count: 2 }, + }, + } + ); + + actionsClient = new ActionsClient({ + logger, + actionTypeRegistry, + unsecuredSavedObjectsClient, + scopedClusterClient, + kibanaIndices, + actionExecutor, + ephemeralExecutionEnqueuer, + bulkExecutionEnqueuer, + request, + authorization: authorization as unknown as ActionsAuthorization, + inMemoryConnectors: [ + { + id: 'testPreconfigured', + actionTypeId: 'my-action-type', + secrets: { + test: 'test1', + }, + isPreconfigured: true, + isDeprecated: false, + isSystemAction: false, name: 'test', - isMissingSecrets: false, config: { foo: 'bar', }, }, - score: 1, - references: [], - }, - ], - }); - scopedClusterClient.asInternalUser.search.mockResponse( - // @ts-expect-error not full search response - { - aggregations: { - '1': { doc_count: 6 }, - testPreconfigured: { doc_count: 2 }, - }, - } - ); + { + id: 'system-connector-.test', + actionTypeId: '.test', + name: 'Test system action', + config: {}, + secrets: {}, + isDeprecated: false, + isMissingSecrets: false, + isPreconfigured: false, + isSystemAction: true, + }, + ], + connectorTokenClient: connectorTokenClientMock.create(), + getEventLogClient, + }); - actionsClient = new ActionsClient({ - logger, - actionTypeRegistry, - unsecuredSavedObjectsClient, - scopedClusterClient, - kibanaIndices, - actionExecutor, - ephemeralExecutionEnqueuer, - bulkExecutionEnqueuer, - request, - authorization: authorization as unknown as ActionsAuthorization, - inMemoryConnectors: [ + const result = await actionsClient.getAllSystemConnectors(); + + expect(result).toEqual([ { - id: 'testPreconfigured', - actionTypeId: '.slack', - secrets: {}, - isPreconfigured: true, + id: 'system-connector-.test', + actionTypeId: '.test', + name: 'Test system action', + isPreconfigured: false, isDeprecated: false, - isSystemAction: false, - name: 'test', - config: { - foo: 'bar', - }, + isSystemAction: true, + referencedByCount: 2, }, - ], - connectorTokenClient: connectorTokenClientMock.create(), - getEventLogClient, + ]); }); - - const result = await actionsClient.getAll({ includeSystemActions: true }); - expect(result).toEqual([ - { - config: { - foo: 'bar', - }, - id: '1', - isDeprecated: false, - isMissingSecrets: false, - isPreconfigured: false, - isSystemAction: false, - name: 'test', - referencedByCount: 6, - }, - { - actionTypeId: '.slack', - id: 'testPreconfigured', - isDeprecated: false, - isPreconfigured: true, - isSystemAction: false, - name: 'test', - referencedByCount: 2, - }, - ]); - - expect(logger.warn).toHaveBeenCalledWith( - 'Error validating connector: 1, Error: [actionTypeId]: expected value of type [string] but got [undefined]' - ); }); }); diff --git a/x-pack/plugins/actions/server/application/connector/methods/get_all/get_all.ts b/x-pack/plugins/actions/server/application/connector/methods/get_all/get_all.ts index 9c3b9c13924fd..1aecd76d40a37 100644 --- a/x-pack/plugins/actions/server/application/connector/methods/get_all/get_all.ts +++ b/x-pack/plugins/actions/server/application/connector/methods/get_all/get_all.ts @@ -127,6 +127,12 @@ async function getAllHelper({ connectors: mergedResult, }); + validateConnectors(connectors, logger); + + return connectors; +} + +const validateConnectors = (connectors: ConnectorWithExtraFindData[], logger: Logger) => { connectors.forEach((connector) => { // Try to validate the connectors, but don't throw. try { @@ -135,6 +141,48 @@ async function getAllHelper({ logger.warn(`Error validating connector: ${connector.id}, ${e}`); } }); +}; + +export async function getAllSystemConnectors({ + context, +}: { + context: GetAllParams['context']; +}): Promise { + try { + await context.authorization.ensureAuthorized({ operation: 'get' }); + } catch (error) { + context.auditLogger?.log( + connectorAuditEvent({ + action: ConnectorAuditAction.FIND, + error, + }) + ); + + throw error; + } + + const systemConnectors = context.inMemoryConnectors.filter( + (connector) => connector.isSystemAction + ); + + const transformedSystemConnectors = systemConnectors + .map((systemConnector) => ({ + id: systemConnector.id, + actionTypeId: systemConnector.actionTypeId, + name: systemConnector.name, + isPreconfigured: systemConnector.isPreconfigured, + isDeprecated: isConnectorDeprecated(systemConnector), + isSystemAction: systemConnector.isSystemAction, + })) + .sort((a, b) => a.name.localeCompare(b.name)); + + const connectors = await injectExtraFindData({ + kibanaIndices: context.kibanaIndices, + esClient: context.scopedClusterClient.asInternalUser, + connectors: transformedSystemConnectors, + }); + + validateConnectors(connectors, context.logger); return connectors; } diff --git a/x-pack/plugins/actions/server/mocks.ts b/x-pack/plugins/actions/server/mocks.ts index ac7d42d658e2b..eb74d320ba8c3 100644 --- a/x-pack/plugins/actions/server/mocks.ts +++ b/x-pack/plugins/actions/server/mocks.ts @@ -13,7 +13,7 @@ import { } from '@kbn/core/server/mocks'; import { encryptedSavedObjectsMock } from '@kbn/encrypted-saved-objects-plugin/server/mocks'; import { Logger } from '@kbn/core/server'; -import { actionsClientMock } from './actions_client/actions_client.mock'; +import { actionsClientMock, ActionsClientMock } from './actions_client/actions_client.mock'; import { PluginSetupContract, PluginStartContract, renderActionParameterTemplates } from './plugin'; import { Services, UnsecuredServices } from './types'; import { actionsAuthorizationMock } from './authorization/actions_authorization.mock'; @@ -21,6 +21,8 @@ import { ConnectorTokenClient } from './lib/connector_token_client'; import { unsecuredActionsClientMock } from './unsecured_actions_client/unsecured_actions_client.mock'; export { actionsAuthorizationMock }; export { actionsClientMock }; +export type { ActionsClientMock }; + const logger = loggingSystemMock.create().get() as jest.Mocked; const createSetupMock = () => { @@ -49,6 +51,7 @@ const createStartMock = () => { .mockReturnValue(actionsAuthorizationMock.create()), inMemoryConnectors: [], renderActionParameterTemplates: jest.fn(), + isSystemActionConnector: jest.fn(), }; return mock; }; diff --git a/x-pack/plugins/actions/server/plugin.test.ts b/x-pack/plugins/actions/server/plugin.test.ts index 004da4cac0339..4d589699a2caa 100644 --- a/x-pack/plugins/actions/server/plugin.test.ts +++ b/x-pack/plugins/actions/server/plugin.test.ts @@ -896,5 +896,53 @@ describe('Actions Plugin', () => { expect(pluginSetup.getActionsHealth()).toEqual({ hasPermanentEncryptionKey: true }); }); }); + + describe('isSystemActionConnector()', () => { + it('should return true if the connector is a system connector', async () => { + // coreMock.createSetup doesn't support Plugin generics + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const pluginSetup = await plugin.setup(coreSetup as any, pluginsSetup); + + pluginSetup.registerType({ + id: '.cases', + name: 'Cases', + minimumLicenseRequired: 'platinum', + supportedFeatureIds: ['alerting'], + validate: { + config: { schema: schema.object({}) }, + secrets: { schema: schema.object({}) }, + params: { schema: schema.object({}) }, + }, + isSystemActionType: true, + executor, + }); + + const pluginStart = await plugin.start(coreStart, pluginsStart); + expect(pluginStart.isSystemActionConnector('system-connector-.cases')).toBe(true); + }); + + it('should return false if the connector is not a system connector', async () => { + // coreMock.createSetup doesn't support Plugin generics + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const pluginSetup = await plugin.setup(coreSetup as any, pluginsSetup); + + pluginSetup.registerType({ + id: '.cases', + name: 'Cases', + minimumLicenseRequired: 'platinum', + supportedFeatureIds: ['alerting'], + validate: { + config: { schema: schema.object({}) }, + secrets: { schema: schema.object({}) }, + params: { schema: schema.object({}) }, + }, + isSystemActionType: true, + executor, + }); + + const pluginStart = await plugin.start(coreStart, pluginsStart); + expect(pluginStart.isSystemActionConnector('preconfiguredServerLog')).toBe(false); + }); + }); }); }); diff --git a/x-pack/plugins/actions/server/plugin.ts b/x-pack/plugins/actions/server/plugin.ts index 1ef28b10e6440..24253e7d01ae4 100644 --- a/x-pack/plugins/actions/server/plugin.ts +++ b/x-pack/plugins/actions/server/plugin.ts @@ -159,6 +159,7 @@ export interface PluginStartContract { params: Params, variables: Record ): Params; + isSystemActionConnector: (connectorId: string) => boolean; } export interface ActionsPluginsSetup { @@ -603,6 +604,12 @@ export class ActionsPlugin implements Plugin renderActionParameterTemplates(this.logger, actionTypeRegistry, ...args), + isSystemActionConnector: (connectorId: string): boolean => { + return this.inMemoryConnectors.some( + (inMemoryConnector) => + inMemoryConnector.isSystemAction && inMemoryConnector.id === connectorId + ); + }, }; } diff --git a/x-pack/plugins/actions/server/routes/connector/get_all_system/get_all_system.test.ts b/x-pack/plugins/actions/server/routes/connector/get_all_system/get_all_system.test.ts new file mode 100644 index 0000000000000..8130c8cd3a809 --- /dev/null +++ b/x-pack/plugins/actions/server/routes/connector/get_all_system/get_all_system.test.ts @@ -0,0 +1,132 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { getAllConnectorsIncludingSystemRoute } from './get_all_system'; +import { httpServiceMock } from '@kbn/core/server/mocks'; +import { licenseStateMock } from '../../../lib/license_state.mock'; +import { mockHandlerArguments } from '../../legacy/_mock_handler_arguments'; +import { verifyAccessAndContext } from '../../verify_access_and_context'; +import { actionsClientMock } from '../../../actions_client/actions_client.mock'; + +jest.mock('../../verify_access_and_context', () => ({ + verifyAccessAndContext: jest.fn(), +})); + +beforeEach(() => { + jest.resetAllMocks(); + (verifyAccessAndContext as jest.Mock).mockImplementation((license, handler) => handler); +}); + +describe('getAllConnectorsIncludingSystemRoute', () => { + it('get all connectors with proper parameters', async () => { + const licenseState = licenseStateMock.create(); + const router = httpServiceMock.createRouter(); + + getAllConnectorsIncludingSystemRoute(router, licenseState); + + const [config, handler] = router.get.mock.calls[0]; + + expect(config.path).toMatchInlineSnapshot(`"/internal/actions/connectors"`); + + const actionsClient = actionsClientMock.create(); + actionsClient.getAll.mockResolvedValueOnce([ + { + id: '.system-action-id', + isPreconfigured: false, + isSystemAction: true, + isDeprecated: false, + name: 'my system action', + actionTypeId: '.system-action-type', + isMissingSecrets: false, + config: {}, + referencedByCount: 0, + }, + ]); + + const [context, req, res] = mockHandlerArguments({ actionsClient }, {}, ['ok']); + + expect(await handler(context, req, res)).toMatchInlineSnapshot(` + Object { + "body": Array [ + Object { + "config": Object {}, + "connector_type_id": ".system-action-type", + "id": ".system-action-id", + "is_deprecated": false, + "is_missing_secrets": false, + "is_preconfigured": false, + "is_system_action": true, + "name": "my system action", + "referenced_by_count": 0, + }, + ], + } + `); + + expect(actionsClient.getAll).toHaveBeenCalledWith({ includeSystemActions: true }); + + expect(res.ok).toHaveBeenCalledWith({ + body: [ + { + config: {}, + connector_type_id: '.system-action-type', + id: '.system-action-id', + is_deprecated: false, + is_missing_secrets: false, + is_preconfigured: false, + is_system_action: true, + name: 'my system action', + referenced_by_count: 0, + }, + ], + }); + }); + + it('ensures the license allows getting all connectors', async () => { + const licenseState = licenseStateMock.create(); + const router = httpServiceMock.createRouter(); + + getAllConnectorsIncludingSystemRoute(router, licenseState); + + const [config, handler] = router.get.mock.calls[0]; + + expect(config.path).toMatchInlineSnapshot(`"/internal/actions/connectors"`); + + const actionsClient = actionsClientMock.create(); + actionsClient.getAll.mockResolvedValueOnce([]); + + const [context, req, res] = mockHandlerArguments({ actionsClient }, {}, ['ok']); + + await handler(context, req, res); + + expect(verifyAccessAndContext).toHaveBeenCalledWith(licenseState, expect.any(Function)); + }); + + it('ensures the license check prevents getting all connectors', async () => { + const licenseState = licenseStateMock.create(); + const router = httpServiceMock.createRouter(); + + (verifyAccessAndContext as jest.Mock).mockImplementation(() => async () => { + throw new Error('OMG'); + }); + + getAllConnectorsIncludingSystemRoute(router, licenseState); + + const [config, handler] = router.get.mock.calls[0]; + + expect(config.path).toMatchInlineSnapshot(`"/internal/actions/connectors"`); + + const actionsClient = actionsClientMock.create(); + actionsClient.getAll.mockResolvedValueOnce([]); + + const [context, req, res] = mockHandlerArguments({ actionsClient }, {}, ['ok']); + + expect(handler(context, req, res)).rejects.toMatchInlineSnapshot(`[Error: OMG]`); + + expect(verifyAccessAndContext).toHaveBeenCalledWith(licenseState, expect.any(Function)); + }); +}); diff --git a/x-pack/plugins/actions/server/routes/connector/get_all_system/get_all_system.ts b/x-pack/plugins/actions/server/routes/connector/get_all_system/get_all_system.ts new file mode 100644 index 0000000000000..9ba51287c56fd --- /dev/null +++ b/x-pack/plugins/actions/server/routes/connector/get_all_system/get_all_system.ts @@ -0,0 +1,37 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { IRouter } from '@kbn/core/server'; +import { AllConnectorsResponseV1 } from '../../../../common/routes/connector/response'; +import { ActionsRequestHandlerContext } from '../../../types'; +import { INTERNAL_BASE_ACTION_API_PATH } from '../../../../common'; +import { ILicenseState } from '../../../lib'; +import { verifyAccessAndContext } from '../../verify_access_and_context'; +import { transformGetAllConnectorsResponseV1 } from '../get_all/transforms'; + +export const getAllConnectorsIncludingSystemRoute = ( + router: IRouter, + licenseState: ILicenseState +) => { + router.get( + { + path: `${INTERNAL_BASE_ACTION_API_PATH}/connectors`, + validate: {}, + }, + router.handleLegacyErrors( + verifyAccessAndContext(licenseState, async function (context, req, res) { + const actionsClient = (await context.actions).getActionsClient(); + const result = await actionsClient.getAll({ + includeSystemActions: true, + }); + + const responseBody: AllConnectorsResponseV1[] = transformGetAllConnectorsResponseV1(result); + return res.ok({ body: responseBody }); + }) + ) + ); +}; diff --git a/x-pack/plugins/alerting/server/data/alerts_filter_query/constants.ts b/x-pack/plugins/actions/server/routes/connector/get_all_system/index.ts similarity index 57% rename from x-pack/plugins/alerting/server/data/alerts_filter_query/constants.ts rename to x-pack/plugins/actions/server/routes/connector/get_all_system/index.ts index bce6890c22f2c..fd5e0e8b89caa 100644 --- a/x-pack/plugins/alerting/server/data/alerts_filter_query/constants.ts +++ b/x-pack/plugins/actions/server/routes/connector/get_all_system/index.ts @@ -5,9 +5,4 @@ * 2.0. */ -export const filterStateStore = { - APP_STATE: 'appState', - GLOBAL_STATE: 'globalState', -} as const; - -export type FilterStateStore = typeof filterStateStore[keyof typeof filterStateStore]; +export { getAllConnectorsIncludingSystemRoute } from './get_all_system'; diff --git a/x-pack/plugins/actions/server/routes/connector/list_types/list_types.ts b/x-pack/plugins/actions/server/routes/connector/list_types/list_types.ts index 078c51743c4d9..b53b213dcc188 100644 --- a/x-pack/plugins/actions/server/routes/connector/list_types/list_types.ts +++ b/x-pack/plugins/actions/server/routes/connector/list_types/list_types.ts @@ -35,7 +35,9 @@ export const listTypesRoute = ( // Assert versioned inputs const query: ConnectorTypesRequestQueryV1 = req.query; - const connectorTypes = await actionsClient.listTypes({ featureId: query?.feature_id }); + const connectorTypes = await actionsClient.listTypes({ + featureId: query?.feature_id, + }); const responseBody: ConnectorTypesResponseV1[] = transformListTypesResponseV1(connectorTypes); diff --git a/x-pack/plugins/actions/server/routes/connector/list_types_system/index.ts b/x-pack/plugins/actions/server/routes/connector/list_types_system/index.ts new file mode 100644 index 0000000000000..0ddc95a6732c3 --- /dev/null +++ b/x-pack/plugins/actions/server/routes/connector/list_types_system/index.ts @@ -0,0 +1,8 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +export { listTypesWithSystemRoute } from './list_types_system'; diff --git a/x-pack/plugins/actions/server/routes/connector/list_types_system/list_types_system.test.ts b/x-pack/plugins/actions/server/routes/connector/list_types_system/list_types_system.test.ts new file mode 100644 index 0000000000000..af6e057cafc9c --- /dev/null +++ b/x-pack/plugins/actions/server/routes/connector/list_types_system/list_types_system.test.ts @@ -0,0 +1,249 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { httpServiceMock } from '@kbn/core/server/mocks'; +import { LicenseType } from '@kbn/licensing-plugin/server'; +import { licenseStateMock } from '../../../lib/license_state.mock'; +import { mockHandlerArguments } from '../../legacy/_mock_handler_arguments'; +import { listTypesWithSystemRoute } from './list_types_system'; +import { verifyAccessAndContext } from '../../verify_access_and_context'; +import { actionsClientMock } from '../../../mocks'; + +jest.mock('../../verify_access_and_context', () => ({ + verifyAccessAndContext: jest.fn(), +})); + +beforeEach(() => { + jest.resetAllMocks(); + (verifyAccessAndContext as jest.Mock).mockImplementation((license, handler) => handler); +}); + +describe('listTypesWithSystemRoute', () => { + it('lists action types with proper parameters', async () => { + const licenseState = licenseStateMock.create(); + const router = httpServiceMock.createRouter(); + + listTypesWithSystemRoute(router, licenseState); + + const [config, handler] = router.get.mock.calls[0]; + + expect(config.path).toMatchInlineSnapshot(`"/internal/actions/connector_types"`); + + const listTypes = [ + { + id: '1', + name: 'name', + enabled: true, + enabledInConfig: true, + enabledInLicense: true, + minimumLicenseRequired: 'gold' as LicenseType, + supportedFeatureIds: ['alerting'], + isSystemActionType: true, + }, + ]; + + const actionsClient = actionsClientMock.create(); + actionsClient.listTypes.mockResolvedValueOnce(listTypes); + const [context, req, res] = mockHandlerArguments({ actionsClient }, {}, ['ok']); + + expect(await handler(context, req, res)).toMatchInlineSnapshot(` + Object { + "body": Array [ + Object { + "enabled": true, + "enabled_in_config": true, + "enabled_in_license": true, + "id": "1", + "is_system_action_type": true, + "minimum_license_required": "gold", + "name": "name", + "supported_feature_ids": Array [ + "alerting", + ], + }, + ], + } + `); + + expect(res.ok).toHaveBeenCalledWith({ + body: [ + { + id: '1', + name: 'name', + enabled: true, + enabled_in_config: true, + enabled_in_license: true, + supported_feature_ids: ['alerting'], + minimum_license_required: 'gold', + is_system_action_type: true, + }, + ], + }); + }); + + it('passes feature_id if provided as query parameter', async () => { + const licenseState = licenseStateMock.create(); + const router = httpServiceMock.createRouter(); + + listTypesWithSystemRoute(router, licenseState); + + const [config, handler] = router.get.mock.calls[0]; + + expect(config.path).toMatchInlineSnapshot(`"/internal/actions/connector_types"`); + + const listTypes = [ + { + id: '1', + name: 'name', + enabled: true, + enabledInConfig: true, + enabledInLicense: true, + supportedFeatureIds: ['alerting'], + minimumLicenseRequired: 'gold' as LicenseType, + isSystemActionType: false, + }, + ]; + + const actionsClient = actionsClientMock.create(); + actionsClient.listTypes.mockResolvedValueOnce(listTypes); + const [context, req, res] = mockHandlerArguments( + { actionsClient }, + { + query: { + feature_id: 'alerting', + }, + }, + ['ok'] + ); + + expect(await handler(context, req, res)).toMatchInlineSnapshot(` + Object { + "body": Array [ + Object { + "enabled": true, + "enabled_in_config": true, + "enabled_in_license": true, + "id": "1", + "is_system_action_type": false, + "minimum_license_required": "gold", + "name": "name", + "supported_feature_ids": Array [ + "alerting", + ], + }, + ], + } + `); + + expect(actionsClient.listTypes).toHaveBeenCalledTimes(1); + expect(actionsClient.listTypes.mock.calls[0]).toMatchInlineSnapshot(` + Array [ + Object { + "featureId": "alerting", + "includeSystemActionTypes": true, + }, + ] + `); + + expect(res.ok).toHaveBeenCalledWith({ + body: [ + { + id: '1', + name: 'name', + enabled: true, + enabled_in_config: true, + enabled_in_license: true, + supported_feature_ids: ['alerting'], + minimum_license_required: 'gold', + is_system_action_type: false, + }, + ], + }); + }); + + it('ensures the license allows listing action types', async () => { + const licenseState = licenseStateMock.create(); + const router = httpServiceMock.createRouter(); + + listTypesWithSystemRoute(router, licenseState); + + const [config, handler] = router.get.mock.calls[0]; + + expect(config.path).toMatchInlineSnapshot(`"/internal/actions/connector_types"`); + + const listTypes = [ + { + id: '1', + name: 'name', + enabled: true, + enabledInConfig: true, + enabledInLicense: true, + supportedFeatureIds: ['alerting'], + minimumLicenseRequired: 'gold' as LicenseType, + isSystemActionType: false, + }, + ]; + + const actionsClient = actionsClientMock.create(); + actionsClient.listTypes.mockResolvedValueOnce(listTypes); + + const [context, req, res] = mockHandlerArguments( + { actionsClient }, + { + params: { id: '1' }, + }, + ['ok'] + ); + + await handler(context, req, res); + + expect(verifyAccessAndContext).toHaveBeenCalledWith(licenseState, expect.any(Function)); + }); + + it('ensures the license check prevents listing action types', async () => { + const licenseState = licenseStateMock.create(); + const router = httpServiceMock.createRouter(); + + (verifyAccessAndContext as jest.Mock).mockImplementation(() => async () => { + throw new Error('OMG'); + }); + + listTypesWithSystemRoute(router, licenseState); + + const [config, handler] = router.get.mock.calls[0]; + + expect(config.path).toMatchInlineSnapshot(`"/internal/actions/connector_types"`); + + const listTypes = [ + { + id: '1', + name: 'name', + enabled: true, + enabledInConfig: true, + enabledInLicense: true, + supportedFeatureIds: ['alerting'], + minimumLicenseRequired: 'gold' as LicenseType, + isSystemActionType: false, + }, + ]; + + const actionsClient = actionsClientMock.create(); + actionsClient.listTypes.mockResolvedValueOnce(listTypes); + + const [context, req, res] = mockHandlerArguments( + { actionsClient }, + { + params: { id: '1' }, + }, + ['ok'] + ); + + expect(handler(context, req, res)).rejects.toMatchInlineSnapshot(`[Error: OMG]`); + + expect(verifyAccessAndContext).toHaveBeenCalledWith(licenseState, expect.any(Function)); + }); +}); diff --git a/x-pack/plugins/actions/server/routes/connector/list_types_system/list_types_system.ts b/x-pack/plugins/actions/server/routes/connector/list_types_system/list_types_system.ts new file mode 100644 index 0000000000000..6611830f6a3c7 --- /dev/null +++ b/x-pack/plugins/actions/server/routes/connector/list_types_system/list_types_system.ts @@ -0,0 +1,50 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { IRouter } from '@kbn/core/server'; +import { ConnectorTypesResponseV1 } from '../../../../common/routes/connector/response'; +import { + connectorTypesQuerySchemaV1, + ConnectorTypesRequestQueryV1, +} from '../../../../common/routes/connector/apis/connector_types'; +import { ActionsRequestHandlerContext } from '../../../types'; +import { INTERNAL_BASE_ACTION_API_PATH } from '../../../../common'; +import { ILicenseState } from '../../../lib'; +import { verifyAccessAndContext } from '../../verify_access_and_context'; +import { transformListTypesResponseV1 } from '../list_types/transforms'; + +export const listTypesWithSystemRoute = ( + router: IRouter, + licenseState: ILicenseState +) => { + router.get( + { + path: `${INTERNAL_BASE_ACTION_API_PATH}/connector_types`, + validate: { + query: connectorTypesQuerySchemaV1, + }, + }, + router.handleLegacyErrors( + verifyAccessAndContext(licenseState, async function (context, req, res) { + const actionsClient = (await context.actions).getActionsClient(); + + // Assert versioned inputs + const query: ConnectorTypesRequestQueryV1 = req.query; + + const connectorTypes = await actionsClient.listTypes({ + featureId: query?.feature_id, + includeSystemActionTypes: true, + }); + + const responseBody: ConnectorTypesResponseV1[] = + transformListTypesResponseV1(connectorTypes); + + return res.ok({ body: responseBody }); + }) + ) + ); +}; diff --git a/x-pack/plugins/actions/server/routes/execute.test.ts b/x-pack/plugins/actions/server/routes/execute.test.ts index 12960aeae47e6..3bcd2cf60811d 100644 --- a/x-pack/plugins/actions/server/routes/execute.test.ts +++ b/x-pack/plugins/actions/server/routes/execute.test.ts @@ -24,6 +24,10 @@ beforeEach(() => { }); describe('executeActionRoute', () => { + beforeEach(() => { + jest.clearAllMocks(); + }); + it('executes an action with proper parameters', async () => { const licenseState = licenseStateMock.create(); const router = httpServiceMock.createRouter(); @@ -167,4 +171,35 @@ describe('executeActionRoute', () => { expect(verifyAccessAndContext).toHaveBeenCalledWith(licenseState, expect.any(Function)); }); + + it('returns a bad request for system connectors', async () => { + const licenseState = licenseStateMock.create(); + const router = httpServiceMock.createRouter(); + + const actionsClient = actionsClientMock.create(); + actionsClient.isSystemAction.mockReturnValue(true); + + const [context, req, res] = mockHandlerArguments( + { actionsClient }, + { + body: { + params: {}, + }, + params: { + id: 'system-connector-.test-connector', + }, + }, + ['ok'] + ); + + executeActionRoute(router, licenseState); + + const [_, handler] = router.post.mock.calls[0]; + + await handler(context, req, res); + + expect(actionsClient.execute).not.toHaveBeenCalled(); + expect(res.ok).not.toHaveBeenCalled(); + expect(res.badRequest).toHaveBeenCalled(); + }); }); diff --git a/x-pack/plugins/actions/server/routes/execute.ts b/x-pack/plugins/actions/server/routes/execute.ts index fff947155bec3..406b9983b7bda 100644 --- a/x-pack/plugins/actions/server/routes/execute.ts +++ b/x-pack/plugins/actions/server/routes/execute.ts @@ -49,12 +49,18 @@ export const executeActionRoute = ( const actionsClient = (await context.actions).getActionsClient(); const { params } = req.body; const { id } = req.params; + + if (actionsClient.isSystemAction(id)) { + return res.badRequest({ body: 'Execution of system action is not allowed' }); + } + const body: ActionTypeExecutorResult = await actionsClient.execute({ params, actionId: id, source: asHttpRequestExecutionSource(req), relatedSavedObjects: [], }); + return body ? res.ok({ body: rewriteBodyRes(body), diff --git a/x-pack/plugins/actions/server/routes/index.ts b/x-pack/plugins/actions/server/routes/index.ts index ad28c55959039..ca25b88bcf798 100644 --- a/x-pack/plugins/actions/server/routes/index.ts +++ b/x-pack/plugins/actions/server/routes/index.ts @@ -8,7 +8,9 @@ import { IRouter } from '@kbn/core/server'; import { UsageCounter } from '@kbn/usage-collection-plugin/server'; import { getAllConnectorsRoute } from './connector/get_all'; +import { getAllConnectorsIncludingSystemRoute } from './connector/get_all_system'; import { listTypesRoute } from './connector/list_types'; +import { listTypesWithSystemRoute } from './connector/list_types_system'; import { ILicenseState } from '../lib'; import { ActionsRequestHandlerContext } from '../types'; import { createActionRoute } from './create'; @@ -45,4 +47,6 @@ export function defineRoutes(opts: RouteOptions) { getGlobalExecutionKPIRoute(router, licenseState); getOAuthAccessToken(router, licenseState, actionsConfigUtils); + getAllConnectorsIncludingSystemRoute(router, licenseState); + listTypesWithSystemRoute(router, licenseState); } diff --git a/x-pack/plugins/alerting/common/maintenance_window.ts b/x-pack/plugins/alerting/common/maintenance_window.ts index 8b7646670cc76..80cdb88858643 100644 --- a/x-pack/plugins/alerting/common/maintenance_window.ts +++ b/x-pack/plugins/alerting/common/maintenance_window.ts @@ -5,6 +5,7 @@ * 2.0. */ import { Logger, SavedObjectsClientContract } from '@kbn/core/server'; +import { FilterStateStore } from '@kbn/es-query'; import { RRuleParams } from './rrule_type'; export enum MaintenanceWindowStatus { @@ -13,14 +14,6 @@ export enum MaintenanceWindowStatus { Finished = 'finished', Archived = 'archived', } - -export const filterStateStore = { - APP_STATE: 'appState', - GLOBAL_STATE: 'globalState', -} as const; - -export type FilterStateStore = typeof filterStateStore[keyof typeof filterStateStore]; - export interface MaintenanceWindowModificationMetadata { createdBy: string | null; updatedBy: string | null; diff --git a/x-pack/plugins/alerting/common/routes/alerts_filter_query/index.ts b/x-pack/plugins/alerting/common/routes/alerts_filter_query/index.ts index 093299dbe66f2..d5a5f1f1f5c57 100644 --- a/x-pack/plugins/alerting/common/routes/alerts_filter_query/index.ts +++ b/x-pack/plugins/alerting/common/routes/alerts_filter_query/index.ts @@ -5,10 +5,5 @@ * 2.0. */ -export { filterStateStore } from './constants/latest'; -export type { FilterStateStore } from './constants/latest'; export { alertsFilterQuerySchema } from './schemas/latest'; - -export { filterStateStore as filterStateStoreV1 } from './constants/v1'; -export type { FilterStateStore as FilterStateStoreV1 } from './constants/v1'; export { alertsFilterQuerySchema as alertsFilterQuerySchemaV1 } from './schemas/v1'; diff --git a/x-pack/plugins/alerting/common/routes/alerts_filter_query/schemas/v1.ts b/x-pack/plugins/alerting/common/routes/alerts_filter_query/schemas/v1.ts index 08614efb96b70..26249eb3a53f2 100644 --- a/x-pack/plugins/alerting/common/routes/alerts_filter_query/schemas/v1.ts +++ b/x-pack/plugins/alerting/common/routes/alerts_filter_query/schemas/v1.ts @@ -6,7 +6,7 @@ */ import { schema } from '@kbn/config-schema'; -import { filterStateStore } from '..'; +import { FilterStateStore } from '@kbn/es-query'; export const alertsFilterQuerySchema = schema.object({ kql: schema.string(), @@ -17,8 +17,8 @@ export const alertsFilterQuerySchema = schema.object({ $state: schema.maybe( schema.object({ store: schema.oneOf([ - schema.literal(filterStateStore.APP_STATE), - schema.literal(filterStateStore.GLOBAL_STATE), + schema.literal(FilterStateStore.APP_STATE), + schema.literal(FilterStateStore.GLOBAL_STATE), ]), }) ), diff --git a/x-pack/plugins/alerting/common/routes/rule/apis/bulk_edit/schemas/v1.ts b/x-pack/plugins/alerting/common/routes/rule/apis/bulk_edit/schemas/v1.ts index 3a17eaee30974..2d05836a3c30e 100644 --- a/x-pack/plugins/alerting/common/routes/rule/apis/bulk_edit/schemas/v1.ts +++ b/x-pack/plugins/alerting/common/routes/rule/apis/bulk_edit/schemas/v1.ts @@ -20,7 +20,7 @@ export const ruleSnoozeScheduleSchema = schema.object({ }); const ruleActionSchema = schema.object({ - group: schema.string(), + group: schema.maybe(schema.string()), id: schema.string(), params: schema.recordOf(schema.string(), schema.any(), { defaultValue: {} }), uuid: schema.maybe(schema.string()), diff --git a/x-pack/plugins/alerting/common/routes/rule/apis/create/schemas/v1.ts b/x-pack/plugins/alerting/common/routes/rule/apis/create/schemas/v1.ts index 7d1a37b7c75f7..f24280eebd44c 100644 --- a/x-pack/plugins/alerting/common/routes/rule/apis/create/schemas/v1.ts +++ b/x-pack/plugins/alerting/common/routes/rule/apis/create/schemas/v1.ts @@ -46,7 +46,7 @@ export const actionAlertsFilterSchema = schema.object({ export const actionSchema = schema.object({ uuid: schema.maybe(schema.string()), - group: schema.string(), + group: schema.maybe(schema.string()), id: schema.string(), actionTypeId: schema.maybe(schema.string()), params: schema.recordOf(schema.string(), schema.maybe(schema.any()), { defaultValue: {} }), diff --git a/x-pack/plugins/alerting/common/routes/rule/common/constants/v1.ts b/x-pack/plugins/alerting/common/routes/rule/common/constants/v1.ts index 3bf7208efe37f..fbeb08ba6bc7f 100644 --- a/x-pack/plugins/alerting/common/routes/rule/common/constants/v1.ts +++ b/x-pack/plugins/alerting/common/routes/rule/common/constants/v1.ts @@ -43,11 +43,6 @@ export const ruleExecutionStatusWarningReason = { MAX_QUEUED_ACTIONS: 'maxQueuedActions', } as const; -export const filterStateStore = { - APP_STATE: 'appState', - GLOBAL_STATE: 'globalState', -} as const; - export type RuleNotifyWhen = typeof ruleNotifyWhen[keyof typeof ruleNotifyWhen]; export type RuleLastRunOutcomeValues = typeof ruleLastRunOutcomeValues[keyof typeof ruleLastRunOutcomeValues]; @@ -57,4 +52,3 @@ export type RuleExecutionStatusErrorReason = typeof ruleExecutionStatusErrorReason[keyof typeof ruleExecutionStatusErrorReason]; export type RuleExecutionStatusWarningReason = typeof ruleExecutionStatusWarningReason[keyof typeof ruleExecutionStatusWarningReason]; -export type FilterStateStore = typeof filterStateStore[keyof typeof filterStateStore]; diff --git a/x-pack/plugins/alerting/common/routes/rule/common/index.ts b/x-pack/plugins/alerting/common/routes/rule/common/index.ts index 669bfc484070a..5989a3a993e7a 100644 --- a/x-pack/plugins/alerting/common/routes/rule/common/index.ts +++ b/x-pack/plugins/alerting/common/routes/rule/common/index.ts @@ -11,7 +11,6 @@ export { ruleExecutionStatusValues, ruleExecutionStatusErrorReason, ruleExecutionStatusWarningReason, - filterStateStore, } from './constants/latest'; export type { @@ -20,7 +19,6 @@ export type { RuleExecutionStatusValues, RuleExecutionStatusErrorReason, RuleExecutionStatusWarningReason, - FilterStateStore, } from './constants/latest'; export { @@ -29,7 +27,6 @@ export { ruleExecutionStatusValues as ruleExecutionStatusValuesV1, ruleExecutionStatusErrorReason as ruleExecutionStatusErrorReasonV1, ruleExecutionStatusWarningReason as ruleExecutionStatusWarningReasonV1, - filterStateStore as filterStateStoreV1, } from './constants/v1'; export type { @@ -38,5 +35,4 @@ export type { RuleExecutionStatusValues as RuleExecutionStatusValuesV1, RuleExecutionStatusErrorReason as RuleExecutionStatusErrorReasonV1, RuleExecutionStatusWarningReason as RuleExecutionStatusWarningReasonV1, - FilterStateStore as FilterStateStoreV1, } from './constants/v1'; diff --git a/x-pack/plugins/alerting/common/routes/rule/response/schemas/v1.ts b/x-pack/plugins/alerting/common/routes/rule/response/schemas/v1.ts index f485aa7374d6c..2b6f09ef0dfd8 100644 --- a/x-pack/plugins/alerting/common/routes/rule/response/schemas/v1.ts +++ b/x-pack/plugins/alerting/common/routes/rule/response/schemas/v1.ts @@ -66,12 +66,13 @@ const actionAlertsFilterSchema = schema.object({ const actionSchema = schema.object({ uuid: schema.maybe(schema.string()), - group: schema.string(), + group: schema.maybe(schema.string()), id: schema.string(), connector_type_id: schema.string(), params: actionParamsSchema, frequency: schema.maybe(actionFrequencySchema), alerts_filter: schema.maybe(actionAlertsFilterSchema), + use_alert_data_for_template: schema.maybe(schema.boolean()), }); export const ruleExecutionStatusSchema = schema.object({ diff --git a/x-pack/plugins/alerting/common/rule.ts b/x-pack/plugins/alerting/common/rule.ts index 6a66b39720402..bc6c60fd75a53 100644 --- a/x-pack/plugins/alerting/common/rule.ts +++ b/x-pack/plugins/alerting/common/rule.ts @@ -122,6 +122,16 @@ export interface RuleAction { useAlertDataForTemplate?: boolean; } +export interface RuleSystemAction { + uuid?: string; + id: string; + actionTypeId: string; + params: RuleActionParams; +} + +export type RuleActionKey = keyof RuleAction; +export type RuleSystemActionKey = keyof RuleSystemAction; + export interface RuleLastRun { outcome: RuleLastRunOutcomes; outcomeOrder?: number; @@ -155,6 +165,7 @@ export interface Rule { consumer: string; schedule: IntervalSchedule; actions: RuleAction[]; + systemActions?: RuleSystemAction[]; params: Params; mapped_params?: MappedParams; scheduledTaskId?: string | null; diff --git a/x-pack/plugins/alerting/server/application/alerts_filter_query/schemas/alerts_filter_query_schemas.ts b/x-pack/plugins/alerting/server/application/alerts_filter_query/schemas/alerts_filter_query_schemas.ts index bf5a24b6e4399..26249eb3a53f2 100644 --- a/x-pack/plugins/alerting/server/application/alerts_filter_query/schemas/alerts_filter_query_schemas.ts +++ b/x-pack/plugins/alerting/server/application/alerts_filter_query/schemas/alerts_filter_query_schemas.ts @@ -6,7 +6,7 @@ */ import { schema } from '@kbn/config-schema'; -import { filterStateStore } from '../constants'; +import { FilterStateStore } from '@kbn/es-query'; export const alertsFilterQuerySchema = schema.object({ kql: schema.string(), @@ -17,8 +17,8 @@ export const alertsFilterQuerySchema = schema.object({ $state: schema.maybe( schema.object({ store: schema.oneOf([ - schema.literal(filterStateStore.APP_STATE), - schema.literal(filterStateStore.GLOBAL_STATE), + schema.literal(FilterStateStore.APP_STATE), + schema.literal(FilterStateStore.GLOBAL_STATE), ]), }) ), diff --git a/x-pack/plugins/alerting/server/application/maintenance_window/constants.ts b/x-pack/plugins/alerting/server/application/maintenance_window/constants.ts index 4b5920d645584..203ce8ec3396a 100644 --- a/x-pack/plugins/alerting/server/application/maintenance_window/constants.ts +++ b/x-pack/plugins/alerting/server/application/maintenance_window/constants.ts @@ -17,8 +17,3 @@ export const maintenanceWindowCategoryIdTypes = { SECURITY_SOLUTION: 'securitySolution', MANAGEMENT: 'management', } as const; - -export const filterStateStore = { - APP_STATE: 'appState', - GLOBAL_STATE: 'globalState', -} as const; diff --git a/x-pack/plugins/alerting/server/application/maintenance_window/methods/create/create_maintenance_window.test.ts b/x-pack/plugins/alerting/server/application/maintenance_window/methods/create/create_maintenance_window.test.ts index ca98ba2ea72d1..efe8a9a9477d5 100644 --- a/x-pack/plugins/alerting/server/application/maintenance_window/methods/create/create_maintenance_window.test.ts +++ b/x-pack/plugins/alerting/server/application/maintenance_window/methods/create/create_maintenance_window.test.ts @@ -16,6 +16,7 @@ import { } from '../../../../../common'; import { getMockMaintenanceWindow } from '../../../../data/maintenance_window/test_helpers'; import type { MaintenanceWindow } from '../../types'; +import { FilterStateStore } from '@kbn/es-query'; const savedObjectsClient = savedObjectsClientMock.create(); @@ -168,7 +169,7 @@ describe('MaintenanceWindowClient - create', () => { type: 'phrase', }, $state: { - store: 'appState', + store: FilterStateStore.APP_STATE, }, query: { match_phrase: { @@ -281,7 +282,7 @@ describe('MaintenanceWindowClient - create', () => { type: 'phrase', }, $state: { - store: 'appState', + store: FilterStateStore.APP_STATE, }, query: { match_phrase: { diff --git a/x-pack/plugins/alerting/server/application/maintenance_window/methods/update/update_maintenance_window.test.ts b/x-pack/plugins/alerting/server/application/maintenance_window/methods/update/update_maintenance_window.test.ts index 08a01556bede8..8d25930aa0784 100644 --- a/x-pack/plugins/alerting/server/application/maintenance_window/methods/update/update_maintenance_window.test.ts +++ b/x-pack/plugins/alerting/server/application/maintenance_window/methods/update/update_maintenance_window.test.ts @@ -17,6 +17,7 @@ import { } from '../../../../../common'; import { getMockMaintenanceWindow } from '../../../../data/maintenance_window/test_helpers'; import type { MaintenanceWindow } from '../../types'; +import { FilterStateStore } from '@kbn/es-query'; const savedObjectsClient = savedObjectsClientMock.create(); @@ -260,7 +261,7 @@ describe('MaintenanceWindowClient - update', () => { type: 'phrase', }, $state: { - store: 'appState', + store: FilterStateStore.APP_STATE, }, query: { match_phrase: { @@ -407,7 +408,7 @@ describe('MaintenanceWindowClient - update', () => { type: 'phrase', }, $state: { - store: 'appState', + store: FilterStateStore.APP_STATE, }, query: { match_phrase: { diff --git a/x-pack/plugins/alerting/server/application/maintenance_window/transforms/transform_maintenance_window_to_maintenance_window_attributes.ts b/x-pack/plugins/alerting/server/application/maintenance_window/transforms/transform_maintenance_window_to_maintenance_window_attributes.ts index 9d26443bdc07d..b6c0e22d04bc4 100644 --- a/x-pack/plugins/alerting/server/application/maintenance_window/transforms/transform_maintenance_window_to_maintenance_window_attributes.ts +++ b/x-pack/plugins/alerting/server/application/maintenance_window/transforms/transform_maintenance_window_to_maintenance_window_attributes.ts @@ -26,7 +26,15 @@ export const transformMaintenanceWindowToMaintenanceWindowAttributes = ( ? { categoryIds: maintenanceWindow.categoryIds } : {}), ...(maintenanceWindow.scopedQuery !== undefined - ? { scopedQuery: maintenanceWindow.scopedQuery } + ? maintenanceWindow?.scopedQuery == null + ? { scopedQuery: maintenanceWindow?.scopedQuery } + : { + scopedQuery: { + filters: maintenanceWindow?.scopedQuery?.filters ?? [], + kql: maintenanceWindow?.scopedQuery?.kql ?? '', + dsl: maintenanceWindow?.scopedQuery?.dsl ?? '', + }, + } : {}), }; }; diff --git a/x-pack/plugins/alerting/server/application/rule/constants.ts b/x-pack/plugins/alerting/server/application/rule/constants.ts index 0881868d9db8f..bc75d91375ecb 100644 --- a/x-pack/plugins/alerting/server/application/rule/constants.ts +++ b/x-pack/plugins/alerting/server/application/rule/constants.ts @@ -42,8 +42,3 @@ export const ruleExecutionStatusWarningReason = { MAX_ALERTS: 'maxAlerts', MAX_QUEUED_ACTIONS: 'maxQueuedActions', } as const; - -export const filterStateStore = { - APP_STATE: 'appState', - GLOBAL_STATE: 'globalState', -} as const; diff --git a/x-pack/plugins/alerting/server/application/rule/methods/aggregate/aggregate_rules.test.ts b/x-pack/plugins/alerting/server/application/rule/methods/aggregate/aggregate_rules.test.ts index 81c4dc8562bf3..858bc6ded1cb3 100644 --- a/x-pack/plugins/alerting/server/application/rule/methods/aggregate/aggregate_rules.test.ts +++ b/x-pack/plugins/alerting/server/application/rule/methods/aggregate/aggregate_rules.test.ts @@ -27,6 +27,7 @@ import { fromKueryExpression, nodeTypes } from '@kbn/es-query'; import { RecoveredActionGroup } from '../../../../../common'; import { DefaultRuleAggregationResult } from '../../../../routes/rule/apis/aggregate/types'; import { defaultRuleAggregationFactory } from '.'; +import { ConnectorAdapterRegistry } from '../../../../connector_adapters/connector_adapter_registry'; import { RULE_SAVED_OBJECT_TYPE } from '../../../../saved_objects'; const taskManager = taskManagerMock.createStart(); @@ -58,11 +59,13 @@ const rulesClientParams: jest.Mocked = { kibanaVersion, isAuthenticationTypeAPIKey: jest.fn(), getAuthenticationAPIKey: jest.fn(), + connectorAdapterRegistry: new ConnectorAdapterRegistry(), getAlertIndicesAlias: jest.fn(), alertsService: null, maxScheduledPerMinute: 1000, internalSavedObjectsRepository, uiSettings: uiSettingsServiceMock.createStartContract(), + isSystemAction: jest.fn(), }; beforeEach(() => { diff --git a/x-pack/plugins/alerting/server/application/rule/methods/bulk_delete/bulk_delete_rules.test.ts b/x-pack/plugins/alerting/server/application/rule/methods/bulk_delete/bulk_delete_rules.test.ts index 6fec4e8a636da..6ccd32879a171 100644 --- a/x-pack/plugins/alerting/server/application/rule/methods/bulk_delete/bulk_delete_rules.test.ts +++ b/x-pack/plugins/alerting/server/application/rule/methods/bulk_delete/bulk_delete_rules.test.ts @@ -18,6 +18,7 @@ import { actionsAuthorizationMock } from '@kbn/actions-plugin/server/mocks'; import { ActionsAuthorization } from '@kbn/actions-plugin/server'; import { auditLoggerMock } from '@kbn/security-plugin/server/audit/mocks'; import { loggerMock } from '@kbn/logging-mocks'; +import { ActionsClient } from '@kbn/actions-plugin/server'; import { ruleTypeRegistryMock } from '../../../../rule_type_registry.mock'; import { alertingAuthorizationMock } from '../../../../authorization/alerting_authorization.mock'; import { RecoveredActionGroup } from '../../../../../common'; @@ -28,12 +29,17 @@ import { enabledRuleForBulkOps1, enabledRuleForBulkOps2, enabledRuleForBulkOps3, - returnedRuleForBulkDelete1, - returnedRuleForBulkDelete2, - returnedRuleForBulkDelete3, + returnedRuleForBulkOps1, + returnedRuleForBulkOps2, + returnedRuleForBulkOps3, siemRuleForBulkOps1, + enabledRuleForBulkOpsWithActions1, + enabledRuleForBulkOpsWithActions2, + returnedRuleForBulkEnableWithActions1, + returnedRuleForBulkEnableWithActions2, } from '../../../../rules_client/tests/test_helpers'; import { migrateLegacyActions } from '../../../../rules_client/lib'; +import { ConnectorAdapterRegistry } from '../../../../connector_adapters/connector_adapter_registry'; import { RULE_SAVED_OBJECT_TYPE } from '../../../../saved_objects'; jest.mock('../../../../rules_client/lib/siem_legacy_actions/migrate_legacy_actions', () => { @@ -84,6 +90,8 @@ const rulesClientParams: jest.Mocked = { minimumScheduleInterval: { value: '1m', enforce: false }, isAuthenticationTypeAPIKey: jest.fn(), getAuthenticationAPIKey: jest.fn(), + connectorAdapterRegistry: new ConnectorAdapterRegistry(), + isSystemAction: jest.fn(), getAlertIndicesAlias: jest.fn(), alertsService: null, uiSettings: uiSettingsServiceMock.createStartContract(), @@ -109,11 +117,12 @@ setGlobalDate(); describe('bulkDelete', () => { let rulesClient: RulesClient; + let actionsClient: jest.Mocked; const mockCreatePointInTimeFinderAsInternalUser = ( response = { saved_objects: [enabledRuleForBulkOps1, enabledRuleForBulkOps2, enabledRuleForBulkOps3], - } + } as unknown ) => { encryptedSavedObjects.createPointInTimeFinderDecryptedAsInternalUser = jest .fn() @@ -165,6 +174,48 @@ describe('bulkDelete', () => { }, validLegacyConsumers: [], }); + + actionsClient = (await rulesClientParams.getActionsClient()) as jest.Mocked; + actionsClient.isSystemAction.mockImplementation((id: string) => id === 'system_action:id'); + rulesClientParams.getActionsClient.mockResolvedValue(actionsClient); + }); + + test('should successfully delete two rule and return right actions', async () => { + mockCreatePointInTimeFinderAsInternalUser({ + saved_objects: [enabledRuleForBulkOpsWithActions1, enabledRuleForBulkOpsWithActions2], + }); + unsecuredSavedObjectsClient.bulkDelete.mockResolvedValue({ + statuses: [ + { id: 'id1', type: 'alert', success: true }, + { id: 'id2', type: 'alert', success: true }, + ], + }); + + const result = await rulesClient.bulkDeleteRules({ filter: 'fake_filter' }); + + expect(unsecuredSavedObjectsClient.bulkDelete).toHaveBeenCalledTimes(1); + expect(unsecuredSavedObjectsClient.bulkDelete).toHaveBeenCalledWith( + [enabledRuleForBulkOps1, enabledRuleForBulkOps2].map(({ id }) => ({ + id, + type: 'alert', + })), + undefined + ); + + expect(taskManager.bulkRemove).toHaveBeenCalledTimes(1); + expect(taskManager.bulkRemove).toHaveBeenCalledWith(['id1', 'id2']); + expect(bulkMarkApiKeysForInvalidation).toHaveBeenCalledTimes(1); + expect(bulkMarkApiKeysForInvalidation).toHaveBeenCalledWith( + { apiKeys: ['MTIzOmFiYw==', 'MzIxOmFiYw=='] }, + expect.anything(), + expect.anything() + ); + expect(result).toStrictEqual({ + rules: [returnedRuleForBulkEnableWithActions1, returnedRuleForBulkEnableWithActions2], + errors: [], + total: 2, + taskIdsFailedToBeDeleted: [], + }); }); test('should try to delete rules, two successful and one with 500 error', async () => { @@ -196,7 +247,7 @@ describe('bulkDelete', () => { expect.anything() ); expect(result).toStrictEqual({ - rules: [returnedRuleForBulkDelete1, returnedRuleForBulkDelete3], + rules: [returnedRuleForBulkOps1, returnedRuleForBulkOps3], errors: [{ message: 'UPS', rule: { id: 'id2', name: 'fakeName' }, status: 500 }], total: 2, taskIdsFailedToBeDeleted: [], @@ -260,7 +311,7 @@ describe('bulkDelete', () => { expect.anything() ); expect(result).toStrictEqual({ - rules: [returnedRuleForBulkDelete1], + rules: [returnedRuleForBulkOps1], errors: [{ message: 'UPS', rule: { id: 'id2', name: 'fakeName' }, status: 409 }], total: 2, taskIdsFailedToBeDeleted: [], @@ -318,7 +369,7 @@ describe('bulkDelete', () => { expect.anything() ); expect(result).toStrictEqual({ - rules: [returnedRuleForBulkDelete1, returnedRuleForBulkDelete2], + rules: [returnedRuleForBulkOps1, returnedRuleForBulkOps2], errors: [], total: 2, taskIdsFailedToBeDeleted: [], diff --git a/x-pack/plugins/alerting/server/application/rule/methods/bulk_delete/bulk_delete_rules.ts b/x-pack/plugins/alerting/server/application/rule/methods/bulk_delete/bulk_delete_rules.ts index 429afed34926c..81f0b3b0f9e58 100644 --- a/x-pack/plugins/alerting/server/application/rule/methods/bulk_delete/bulk_delete_rules.ts +++ b/x-pack/plugins/alerting/server/application/rule/methods/bulk_delete/bulk_delete_rules.ts @@ -50,6 +50,7 @@ export const bulkDeleteRules = async ( } const { ids, filter } = options; + const actionsClient = await context.getActionsClient(); const kueryNodeFilter = ids ? convertRuleIdsToKueryNode(ids) : buildKueryNodeFilter(filter); const authorizationFilter = await getAuthorizationFilter(context, { action: 'DELETE' }); @@ -96,13 +97,17 @@ export const bulkDeleteRules = async ( // fix the type cast from SavedObjectsBulkUpdateObject to SavedObjectsBulkUpdateObject // when we are doing the bulk delete and this should fix itself const ruleType = context.ruleTypeRegistry.get(attributes.alertTypeId!); - const ruleDomain = transformRuleAttributesToRuleDomain(attributes as RuleAttributes, { - id, - logger: context.logger, - ruleType, - references, - omitGeneratedValues: false, - }); + const ruleDomain = transformRuleAttributesToRuleDomain( + attributes as RuleAttributes, + { + id, + logger: context.logger, + ruleType, + references, + omitGeneratedValues: false, + }, + (connectorId: string) => actionsClient.isSystemAction(connectorId) + ); try { ruleDomainSchema.validate(ruleDomain); diff --git a/x-pack/plugins/alerting/server/application/rule/methods/bulk_disable/bulk_disable_rules.test.ts b/x-pack/plugins/alerting/server/application/rule/methods/bulk_disable/bulk_disable_rules.test.ts index 3f43d6077eb35..be64257c86f8c 100644 --- a/x-pack/plugins/alerting/server/application/rule/methods/bulk_disable/bulk_disable_rules.test.ts +++ b/x-pack/plugins/alerting/server/application/rule/methods/bulk_disable/bulk_disable_rules.test.ts @@ -31,14 +31,20 @@ import { savedObjectWith500Error, disabledRuleForBulkDisable1, disabledRuleForBulkDisable2, + returnedRuleForBulkDisableWithActions1, + returnedRuleForBulkDisableWithActions2, enabledRuleForBulkOps1, enabledRuleForBulkOps2, + disabledRuleForBulkOpsWithActions1, + disabledRuleForBulkOpsWithActions2, returnedRuleForBulkDisable1, returnedRuleForBulkDisable2, siemRuleForBulkOps1, siemRuleForBulkOps2, } from '../../../../rules_client/tests/test_helpers'; import { migrateLegacyActions } from '../../../../rules_client/lib'; +import { ConnectorAdapterRegistry } from '../../../../connector_adapters/connector_adapter_registry'; +import { ActionsClient } from '@kbn/actions-plugin/server'; import { RULE_SAVED_OBJECT_TYPE } from '../../../../saved_objects'; jest.mock('../../../../rules_client/lib/siem_legacy_actions/migrate_legacy_actions', () => { @@ -96,6 +102,8 @@ const rulesClientParams: jest.Mocked = { minimumScheduleInterval: { value: '1m', enforce: false }, isAuthenticationTypeAPIKey: jest.fn(), getAuthenticationAPIKey: jest.fn(), + connectorAdapterRegistry: new ConnectorAdapterRegistry(), + isSystemAction: jest.fn(), getAlertIndicesAlias: jest.fn(), alertsService: null, uiSettings: uiSettingsServiceMock.createStartContract(), @@ -110,6 +118,8 @@ setGlobalDate(); describe('bulkDisableRules', () => { let rulesClient: RulesClient; + let actionsClient: jest.Mocked; + const mockCreatePointInTimeFinderAsInternalUser = ( response = { saved_objects: [enabledRule1, enabledRule2] } ) => { @@ -145,6 +155,9 @@ describe('bulkDisableRules', () => { beforeEach(async () => { rulesClient = new RulesClient(rulesClientParams); + actionsClient = (await rulesClientParams.getActionsClient()) as jest.Mocked; + rulesClientParams.getActionsClient.mockResolvedValue(actionsClient); + authorization.getFindAuthorizationFilter.mockResolvedValue({ ensureRuleTypeIsAuthorized() {}, }); @@ -155,6 +168,7 @@ describe('bulkDisableRules', () => { resultedActions: [], resultedReferences: [], }); + actionsClient.isSystemAction.mockImplementation((id: string) => id === 'system_action:id'); }); test('should disable two rule', async () => { @@ -190,6 +204,38 @@ describe('bulkDisableRules', () => { }); }); + test('should disable two rule and return right actions', async () => { + unsecuredSavedObjectsClient.bulkCreate.mockResolvedValue({ + saved_objects: [disabledRuleForBulkOpsWithActions1, disabledRuleForBulkOpsWithActions2], + }); + + const result = await rulesClient.bulkDisableRules({ filter: 'fake_filter' }); + expect(unsecuredSavedObjectsClient.bulkCreate).toHaveBeenCalledTimes(1); + expect(unsecuredSavedObjectsClient.bulkCreate).toHaveBeenCalledWith( + expect.arrayContaining([ + expect.objectContaining({ + id: 'id1', + attributes: expect.objectContaining({ + enabled: false, + }), + }), + expect.objectContaining({ + id: 'id2', + attributes: expect.objectContaining({ + enabled: false, + }), + }), + ]), + { overwrite: true } + ); + + expect(result).toStrictEqual({ + errors: [], + rules: [returnedRuleForBulkDisableWithActions1, returnedRuleForBulkDisableWithActions2], + total: 2, + }); + }); + test('should call untrack alert if untrack is true', async () => { unsecuredSavedObjectsClient.bulkCreate.mockResolvedValue({ saved_objects: [disabledRuleForBulkDisable1, disabledRuleForBulkDisable2], @@ -683,25 +729,25 @@ describe('bulkDisableRules', () => { await rulesClient.bulkDisableRules({ filter: 'fake_filter' }); expect(migrateLegacyActions).toHaveBeenCalledTimes(4); - expect(migrateLegacyActions).toHaveBeenCalledWith(expect.any(Object), { + expect(migrateLegacyActions).toHaveBeenNthCalledWith(1, expect.any(Object), { attributes: enabledRuleForBulkOps1.attributes, ruleId: enabledRuleForBulkOps1.id, actions: [], references: [], }); - expect(migrateLegacyActions).toHaveBeenCalledWith(expect.any(Object), { + expect(migrateLegacyActions).toHaveBeenNthCalledWith(2, expect.any(Object), { attributes: enabledRuleForBulkOps2.attributes, ruleId: enabledRuleForBulkOps2.id, actions: [], references: [], }); - expect(migrateLegacyActions).toHaveBeenCalledWith(expect.any(Object), { + expect(migrateLegacyActions).toHaveBeenNthCalledWith(3, expect.any(Object), { attributes: expect.objectContaining({ consumer: AlertConsumers.SIEM }), ruleId: siemRuleForBulkOps1.id, actions: [], references: [], }); - expect(migrateLegacyActions).toHaveBeenCalledWith(expect.any(Object), { + expect(migrateLegacyActions).toHaveBeenNthCalledWith(4, expect.any(Object), { attributes: expect.objectContaining({ consumer: AlertConsumers.SIEM }), ruleId: siemRuleForBulkOps2.id, actions: [], diff --git a/x-pack/plugins/alerting/server/application/rule/methods/bulk_disable/bulk_disable_rules.ts b/x-pack/plugins/alerting/server/application/rule/methods/bulk_disable/bulk_disable_rules.ts index 84229c4dc665e..72a06868e9a3d 100644 --- a/x-pack/plugins/alerting/server/application/rule/methods/bulk_disable/bulk_disable_rules.ts +++ b/x-pack/plugins/alerting/server/application/rule/methods/bulk_disable/bulk_disable_rules.ts @@ -51,6 +51,7 @@ export const bulkDisableRules = async ( } const { ids, filter, untrack = false } = options; + const actionsClient = await context.getActionsClient(); const kueryNodeFilter = ids ? convertRuleIdsToKueryNode(ids) : buildKueryNodeFilter(filter); const authorizationFilter = await getAuthorizationFilter(context, { action: 'DISABLE' }); @@ -94,13 +95,17 @@ export const bulkDisableRules = async ( // fix the type cast from SavedObjectsBulkUpdateObject to SavedObjectsBulkUpdateObject // when we are doing the bulk disable and this should fix itself const ruleType = context.ruleTypeRegistry.get(attributes.alertTypeId!); - const ruleDomain = transformRuleAttributesToRuleDomain(attributes as RuleAttributes, { - id, - logger: context.logger, - ruleType, - references, - omitGeneratedValues: false, - }); + const ruleDomain = transformRuleAttributesToRuleDomain( + attributes as RuleAttributes, + { + id, + logger: context.logger, + ruleType, + references, + omitGeneratedValues: false, + }, + (connectorId: string) => actionsClient.isSystemAction(connectorId) + ); try { ruleDomainSchema.validate(ruleDomain); diff --git a/x-pack/plugins/alerting/server/application/rule/methods/bulk_edit/bulk_edit_rules.test.ts b/x-pack/plugins/alerting/server/application/rule/methods/bulk_edit/bulk_edit_rules.test.ts index 7faa85b3ef0cd..bc8ca1606e43e 100644 --- a/x-pack/plugins/alerting/server/application/rule/methods/bulk_edit/bulk_edit_rules.test.ts +++ b/x-pack/plugins/alerting/server/application/rule/methods/bulk_edit/bulk_edit_rules.test.ts @@ -27,7 +27,6 @@ import { ActionsAuthorization, ActionsClient } from '@kbn/actions-plugin/server' import { auditLoggerMock } from '@kbn/security-plugin/server/audit/mocks'; import { getBeforeSetup, setGlobalDate } from '../../../../rules_client/tests/lib'; import { bulkMarkApiKeysForInvalidation } from '../../../../invalidate_pending_api_keys/bulk_mark_api_keys_for_invalidation'; -import { NormalizedAlertAction } from '../../../../rules_client/types'; import { enabledRule1, enabledRule2, @@ -36,6 +35,11 @@ import { } from '../../../../rules_client/tests/test_helpers'; import { migrateLegacyActions } from '../../../../rules_client/lib'; import { migrateLegacyActionsMock } from '../../../../rules_client/lib/siem_legacy_actions/retrieve_migrated_legacy_actions.mock'; +import { ConnectorAdapterRegistry } from '../../../../connector_adapters/connector_adapter_registry'; +import { ConnectorAdapter } from '../../../../connector_adapters/types'; +import { RuleAttributes } from '../../../../data/rule/types'; +import { SavedObject } from '@kbn/core/server'; +import { bulkEditOperationsSchema } from './schemas'; import { RULE_SAVED_OBJECT_TYPE } from '../../../../saved_objects'; jest.mock('../../../../rules_client/lib/siem_legacy_actions/migrate_legacy_actions', () => { @@ -104,6 +108,8 @@ const rulesClientParams: jest.Mocked = { minimumScheduleInterval: { value: '1m', enforce: false }, isAuthenticationTypeAPIKey: isAuthenticationTypeApiKeyMock, getAuthenticationAPIKey: getAuthenticationApiKeyMock, + connectorAdapterRegistry: new ConnectorAdapterRegistry(), + isSystemAction: jest.fn(), getAlertIndicesAlias: jest.fn(), alertsService: null, uiSettings: uiSettingsServiceMock.createStartContract(), @@ -245,14 +251,17 @@ describe('bulkEdit()', () => { return { state: {} }; }, category: 'test', + validLegacyConsumers: [], producer: 'alerts', validate: { params: { validate: (params) => params }, }, - validLegacyConsumers: [], }); (migrateLegacyActions as jest.Mock).mockResolvedValue(migrateLegacyActionsMock); + + rulesClientParams.isSystemAction.mockImplementation((id: string) => id === 'system_action-id'); + actionsClient.isSystemAction.mockImplementation((id: string) => id === 'system_action-id'); }); describe('tags operations', () => { @@ -537,6 +546,14 @@ describe('bulkEdit()', () => { }); describe('actions operations', () => { + const connectorAdapter: ConnectorAdapter = { + connectorTypeId: '.test', + ruleActionParamsSchema: schema.object({ foo: schema.string() }), + buildActionParams: jest.fn(), + }; + + rulesClientParams.connectorAdapterRegistry.register(connectorAdapter); + beforeEach(() => { mockCreatePointInTimeFinderAsInternalUser({ saved_objects: [existingDecryptedRule], @@ -546,7 +563,7 @@ describe('bulkEdit()', () => { test('should add uuid to new actions', async () => { const existingAction = { frequency: { - notifyWhen: 'onActiveAlert', + notifyWhen: 'onActiveAlert' as const, summary: false, throttle: null, }, @@ -555,9 +572,10 @@ describe('bulkEdit()', () => { params: {}, uuid: '111', }; + const newAction = { frequency: { - notifyWhen: 'onActiveAlert', + notifyWhen: 'onActiveAlert' as const, summary: false, throttle: null, }, @@ -565,9 +583,10 @@ describe('bulkEdit()', () => { id: '2', params: {}, }; + const newAction2 = { frequency: { - notifyWhen: 'onActiveAlert', + notifyWhen: 'onActiveAlert' as const, summary: false, throttle: null, }, @@ -586,10 +605,12 @@ describe('bulkEdit()', () => { { ...existingAction, actionRef: 'action_0', + actionTypeId: 'test-0', }, { ...newAction, actionRef: 'action_1', + actionTypeId: 'test-1', uuid: '222', }, ], @@ -616,7 +637,7 @@ describe('bulkEdit()', () => { { field: 'actions', operation: 'add', - value: [existingAction, newAction, newAction2] as NormalizedAlertAction[], + value: [existingAction, newAction, newAction2], }, ], }); @@ -678,7 +699,11 @@ describe('bulkEdit()', () => { ...existingRule.attributes.executionStatus, lastExecutionDate: new Date(existingRule.attributes.executionStatus.lastExecutionDate), }, - actions: [existingAction, { ...newAction, uuid: '222' }], + actions: [ + { ...existingAction, actionTypeId: 'test-0' }, + { ...newAction, uuid: '222', actionTypeId: 'test-1' }, + ], + systemActions: [], id: existingRule.id, snoozeSchedule: [], }); @@ -743,7 +768,6 @@ describe('bulkEdit()', () => { async executor() { return { state: {} }; }, - category: 'test', producer: 'alerts', validate: { params: { validate: (params) => params }, @@ -753,11 +777,13 @@ describe('bulkEdit()', () => { mappings: { fieldMap: { field: { type: 'keyword', required: false } } }, shouldWrite: true, }, + category: 'test', validLegacyConsumers: [], }); + const existingAction = { frequency: { - notifyWhen: 'onActiveAlert', + notifyWhen: 'onActiveAlert' as const, summary: false, throttle: null, }, @@ -780,7 +806,7 @@ describe('bulkEdit()', () => { }; const newAction = { frequency: { - notifyWhen: 'onActiveAlert', + notifyWhen: 'onActiveAlert' as const, summary: false, throttle: null, }, @@ -834,7 +860,7 @@ describe('bulkEdit()', () => { { field: 'actions', operation: 'add', - value: [existingAction, newAction] as NormalizedAlertAction[], + value: [existingAction, newAction], }, ], }); @@ -910,8 +936,651 @@ describe('bulkEdit()', () => { ], id: existingRule.id, snoozeSchedule: [], + systemActions: [], + }); + }); + + test('should add system and default actions', async () => { + const defaultAction = { + frequency: { + notifyWhen: 'onActiveAlert' as const, + summary: false, + throttle: null, + }, + group: 'default', + id: '1', + params: {}, + }; + + const systemAction = { + id: 'system_action-id', + params: {}, + }; + + unsecuredSavedObjectsClient.bulkCreate.mockResolvedValue({ + saved_objects: [ + { + ...existingRule, + attributes: { + ...existingRule.attributes, + actions: [ + { + frequency: { + notifyWhen: 'onActiveAlert' as const, + summary: false, + throttle: null, + }, + group: 'default', + params: {}, + actionRef: 'action_0', + actionTypeId: 'test-1', + uuid: '222', + }, + { + params: {}, + actionRef: 'system_action:system_action-id', + actionTypeId: 'test-2', + uuid: '222', + }, + ], + }, + references: [ + { + name: 'action_0', + type: 'action', + id: '1', + }, + ], + }, + ], + }); + + actionsClient.getBulk.mockResolvedValue([ + { + id: '1', + actionTypeId: 'test-1', + config: {}, + isMissingSecrets: false, + name: 'test default connector', + isPreconfigured: false, + isDeprecated: false, + isSystemAction: false, + }, + { + id: 'system_action-id', + actionTypeId: 'test-2', + config: {}, + isMissingSecrets: false, + name: 'system action connector', + isPreconfigured: false, + isDeprecated: false, + isSystemAction: true, + }, + ]); + + const result = await rulesClient.bulkEdit({ + filter: '', + operations: [ + { + field: 'actions', + operation: 'add', + value: [defaultAction, systemAction], + }, + ], + }); + + expect(unsecuredSavedObjectsClient.bulkCreate).toHaveBeenCalledWith( + [ + { + ...existingRule, + attributes: { + ...existingRule.attributes, + actions: [ + { + actionRef: 'action_0', + actionTypeId: 'test-1', + frequency: { notifyWhen: 'onActiveAlert', summary: false, throttle: null }, + group: 'default', + params: {}, + uuid: '103', + }, + { + actionRef: 'system_action:system_action-id', + actionTypeId: 'test-2', + params: {}, + uuid: '104', + }, + ], + apiKey: null, + apiKeyOwner: null, + apiKeyCreatedByUser: null, + meta: { versionApiKeyLastmodified: 'v8.2.0' }, + name: 'my rule name', + enabled: false, + updatedAt: '2019-02-12T21:01:22.479Z', + updatedBy: 'elastic', + tags: ['foo'], + revision: 1, + }, + references: [{ id: '1', name: 'action_0', type: 'action' }], + }, + ], + { overwrite: true } + ); + + expect(result.rules[0]).toEqual({ + ...omit(existingRule.attributes, 'legacyId'), + createdAt: new Date(existingRule.attributes.createdAt), + updatedAt: new Date(existingRule.attributes.updatedAt), + executionStatus: { + ...existingRule.attributes.executionStatus, + lastExecutionDate: new Date(existingRule.attributes.executionStatus.lastExecutionDate), + }, + actions: [{ ...defaultAction, actionTypeId: 'test-1', uuid: '222' }], + systemActions: [{ ...systemAction, actionTypeId: 'test-2', uuid: '222' }], + id: existingRule.id, + snoozeSchedule: [], + }); + }); + + test('should construct the refs correctly and persist the actions correctly', async () => { + const defaultAction = { + frequency: { + notifyWhen: 'onActiveAlert' as const, + summary: false, + throttle: null, + }, + group: 'default', + id: '1', + params: {}, + }; + + const systemAction = { + id: 'system_action-id', + params: {}, + }; + + unsecuredSavedObjectsClient.bulkCreate.mockResolvedValue({ + saved_objects: [ + { + ...existingRule, + attributes: { + ...existingRule.attributes, + actions: [ + { + frequency: { + notifyWhen: 'onActiveAlert' as const, + summary: false, + throttle: null, + }, + group: 'default', + params: {}, + actionRef: 'action_0', + actionTypeId: 'test-1', + uuid: '222', + }, + { + params: {}, + actionRef: 'system_action:system_action-id', + actionTypeId: 'test-2', + uuid: '222', + }, + ], + }, + references: [ + { + name: 'action_0', + type: 'action', + id: '1', + }, + ], + }, + ], + }); + + actionsClient.getBulk.mockResolvedValue([ + { + id: '1', + actionTypeId: 'test-1', + config: {}, + isMissingSecrets: false, + name: 'test default connector', + isPreconfigured: false, + isDeprecated: false, + isSystemAction: false, + }, + { + id: 'system_action-id', + actionTypeId: 'test-2', + config: {}, + isMissingSecrets: false, + name: 'system action connector', + isPreconfigured: false, + isDeprecated: false, + isSystemAction: true, + }, + ]); + + await rulesClient.bulkEdit({ + filter: '', + operations: [ + { + field: 'actions', + operation: 'add', + value: [defaultAction, systemAction], + }, + ], + }); + + const rule = unsecuredSavedObjectsClient.bulkCreate.mock.calls[0][0] as Array< + SavedObject + >; + + expect(rule[0].attributes.actions).toEqual([ + { + actionRef: 'action_0', + actionTypeId: 'test-1', + frequency: { notifyWhen: 'onActiveAlert', summary: false, throttle: null }, + group: 'default', + params: {}, + uuid: '105', + }, + { + actionRef: 'system_action:system_action-id', + actionTypeId: 'test-2', + params: {}, + uuid: '106', + }, + ]); + }); + + test('should transforms the actions correctly', async () => { + const defaultAction = { + frequency: { + notifyWhen: 'onActiveAlert' as const, + summary: false, + throttle: null, + }, + group: 'default', + id: '1', + params: {}, + }; + + const systemAction = { + id: 'system_action-id', + params: {}, + }; + + unsecuredSavedObjectsClient.bulkCreate.mockResolvedValue({ + saved_objects: [ + { + ...existingRule, + attributes: { + ...existingRule.attributes, + actions: [ + { + frequency: { + notifyWhen: 'onActiveAlert' as const, + summary: false, + throttle: null, + }, + group: 'default', + params: {}, + actionRef: 'action_0', + actionTypeId: 'test-1', + uuid: '222', + }, + { + params: {}, + actionRef: 'system_action:system_action-id', + actionTypeId: 'test-2', + uuid: '222', + }, + ], + }, + references: [ + { + name: 'action_0', + type: 'action', + id: '1', + }, + ], + }, + ], + }); + + actionsClient.getBulk.mockResolvedValue([ + { + id: '1', + actionTypeId: 'test-1', + config: {}, + isMissingSecrets: false, + name: 'test default connector', + isPreconfigured: false, + isDeprecated: false, + isSystemAction: false, + }, + { + id: 'system_action-id', + actionTypeId: 'test-2', + config: {}, + isMissingSecrets: false, + name: 'system action connector', + isPreconfigured: false, + isDeprecated: false, + isSystemAction: true, + }, + ]); + + const result = await rulesClient.bulkEdit({ + filter: '', + operations: [ + { + field: 'actions', + operation: 'add', + value: [defaultAction, systemAction], + }, + ], + }); + + expect(result.rules[0].actions).toEqual([ + { ...defaultAction, actionTypeId: 'test-1', uuid: '222' }, + ]); + expect(result.rules[0].systemActions).toEqual([ + { ...systemAction, actionTypeId: 'test-2', uuid: '222' }, + ]); + }); + + it('should return an error if the action does not have the right attributes', async () => { + const action = { + id: 'system_action-id', + uuid: '123', + params: {}, + }; + + actionsClient.isSystemAction.mockReturnValue(false); + actionsClient.getBulk.mockResolvedValue([ + { + id: 'system_action-id', + actionTypeId: 'test-2', + config: {}, + isMissingSecrets: false, + name: 'system action connector', + isPreconfigured: false, + isDeprecated: false, + isSystemAction: true, + }, + ]); + + const result = await rulesClient.bulkEdit({ + filter: '', + operations: [ + { + field: 'actions', + operation: 'add', + value: [action], + }, + ], + }); + + expect(result).toMatchInlineSnapshot(` + Object { + "errors": Array [ + Object { + "message": "Error validating bulk edit rules operations - [0.group]: expected value of type [string] but got [undefined]", + "rule": Object { + "id": "1", + "name": "my rule name", + }, + }, + ], + "rules": Array [], + "skipped": Array [], + "total": 1, + } + `); + }); + + it('should throw an error if the system action contains the group', async () => { + const action = { + id: 'system_action-id', + uuid: '123', + params: {}, + group: 'default', + }; + + actionsClient.isSystemAction.mockReturnValue(true); + actionsClient.getBulk.mockResolvedValue([ + { + id: 'system_action-id', + actionTypeId: 'test-2', + config: {}, + isMissingSecrets: false, + name: 'system action connector', + isPreconfigured: false, + isDeprecated: false, + isSystemAction: true, + }, + ]); + + const res = await rulesClient.bulkEdit({ + filter: '', + operations: [ + { + field: 'actions', + operation: 'add', + value: [action], + }, + ], + }); + + expect(res).toEqual({ + errors: [ + { + message: + 'Error validating bulk edit rules operations - [0.group]: definition for this key is missing', + rule: { + id: '1', + name: 'my rule name', + }, + }, + ], + rules: [], + skipped: [], + total: 1, + }); + }); + + it('should throw an error if the system action contains the frequency', async () => { + const action = { + id: 'system_action-id', + uuid: '123', + params: {}, + frequency: { + notifyWhen: 'onActiveAlert' as const, + summary: false, + throttle: null, + }, + }; + + actionsClient.isSystemAction.mockReturnValue(true); + actionsClient.getBulk.mockResolvedValue([ + { + id: 'system_action-id', + actionTypeId: 'test-2', + config: {}, + isMissingSecrets: false, + name: 'system action connector', + isPreconfigured: false, + isDeprecated: false, + isSystemAction: true, + }, + ]); + + const res = await rulesClient.bulkEdit({ + filter: '', + operations: [ + { + field: 'actions', + operation: 'add', + value: [action], + }, + ], + }); + + expect(res).toEqual({ + errors: [ + { + message: + 'Error validating bulk edit rules operations - [0.frequency]: definition for this key is missing', + rule: { + id: '1', + name: 'my rule name', + }, + }, + ], + rules: [], + skipped: [], + total: 1, + }); + }); + + it('should throw an error if the system action contains the alertsFilter', async () => { + const action = { + id: 'system_action-id', + uuid: '123', + params: {}, + alertsFilter: { + query: { kql: 'test:1', filters: [] }, + }, + }; + + actionsClient.isSystemAction.mockReturnValue(true); + actionsClient.getBulk.mockResolvedValue([ + { + id: 'system_action-id', + actionTypeId: 'test-2', + config: {}, + isMissingSecrets: false, + name: 'system action connector', + isPreconfigured: false, + isDeprecated: false, + isSystemAction: true, + }, + ]); + + const res = await rulesClient.bulkEdit({ + filter: '', + operations: [ + { + field: 'actions', + operation: 'add', + value: [action], + }, + ], + }); + + expect(res).toEqual({ + errors: [ + { + message: + 'Error validating bulk edit rules operations - [0.alertsFilter]: definition for this key is missing', + rule: { + id: '1', + name: 'my rule name', + }, + }, + ], + rules: [], + skipped: [], + total: 1, + }); + }); + + it('should throw an error if the same system action is used twice', async () => { + const action = { + id: 'system_action-id', + uuid: '123', + params: {}, + }; + + actionsClient.isSystemAction.mockReturnValue(true); + actionsClient.getBulk.mockResolvedValue([ + { + id: 'system_action-id', + actionTypeId: 'test-2', + config: {}, + isMissingSecrets: false, + name: 'system action connector', + isPreconfigured: false, + isDeprecated: false, + isSystemAction: true, + }, + ]); + + const res = await rulesClient.bulkEdit({ + filter: '', + operations: [ + { + field: 'actions', + operation: 'add', + value: [action, action], + }, + ], + }); + + expect(res).toEqual({ + errors: [ + { + message: 'Cannot use the same system action twice', + rule: { + id: '1', + name: 'my rule name', + }, + }, + ], + rules: [], + skipped: [], + total: 1, }); }); + + it('should throw an error if the default action does not contain the group', async () => { + const action = { + id: '1', + params: {}, + }; + + actionsClient.isSystemAction.mockReturnValue(false); + + await expect( + rulesClient.bulkEdit({ + filter: '', + operations: [ + { + field: 'actions', + operation: 'add', + value: [action], + }, + ], + }) + ).resolves.toMatchInlineSnapshot(` + Object { + "errors": Array [ + Object { + "message": "Error validating bulk edit rules operations - [0.group]: expected value of type [string] but got [undefined]", + "rule": Object { + "id": "1", + "name": "my rule name", + }, + }, + ], + "rules": Array [], + "skipped": Array [], + "total": 1, + } + `); + }); }); describe('index pattern operations', () => { @@ -969,7 +1638,13 @@ describe('bulkEdit()', () => { const result = await rulesClient.bulkEdit({ filter: '', - operations: [], + operations: [ + { + field: 'tags', + operation: 'add', + value: ['test-tag'], + }, + ], paramsModifier, }); @@ -1038,7 +1713,13 @@ describe('bulkEdit()', () => { const result = await rulesClient.bulkEdit({ filter: '', - operations: [], + operations: [ + { + field: 'tags', + operation: 'add', + value: ['test-tag'], + }, + ], paramsModifier, }); @@ -1063,6 +1744,9 @@ describe('bulkEdit()', () => { }); test('should skip operation when params modifiers does not modify index pattern array', async () => { + const originalValidate = bulkEditOperationsSchema.validate; + bulkEditOperationsSchema.validate = jest.fn(); + paramsModifier.mockResolvedValue({ modifiedParams: { index: ['test-1', 'test-2'], @@ -1081,6 +1765,8 @@ describe('bulkEdit()', () => { expect(unsecuredSavedObjectsClient.bulkCreate).toHaveBeenCalledTimes(0); expect(bulkMarkApiKeysForInvalidation).toHaveBeenCalledTimes(0); + + bulkEditOperationsSchema.validate = originalValidate; }); }); @@ -2325,8 +3011,8 @@ describe('bulkEdit()', () => { async executor() { return { state: {} }; }, - category: 'test', producer: 'alerts', + category: 'test', validLegacyConsumers: [], }); @@ -2371,8 +3057,8 @@ describe('bulkEdit()', () => { async executor() { return { state: {} }; }, - category: 'test', producer: 'alerts', + category: 'test', validLegacyConsumers: [], }); @@ -2411,7 +3097,13 @@ describe('bulkEdit()', () => { const result = await rulesClient.bulkEdit({ filter: '', - operations: [], + operations: [ + { + field: 'tags', + operation: 'add', + value: ['test-1'], + }, + ], paramsModifier: async (params) => { params.index = ['test-index-*']; @@ -2546,6 +3238,49 @@ describe('bulkEdit()', () => { expect(validateScheduleLimit).toHaveBeenCalledTimes(1); }); + + test('should not validate scheduling on system actions', async () => { + mockCreatePointInTimeFinderAsInternalUser({ + saved_objects: [ + { + ...existingDecryptedRule, + attributes: { + ...existingDecryptedRule.attributes, + actions: [ + { + actionRef: 'action_0', + actionTypeId: 'test', + params: {}, + uuid: '111', + }, + ], + // eslint-disable-next-line @typescript-eslint/no-explicit-any + } as any, + references: [ + { + name: 'action_0', + type: 'action', + id: '1', + }, + // eslint-disable-next-line @typescript-eslint/no-explicit-any + ] as any, + }, + ], + }); + + const result = await rulesClient.bulkEdit({ + operations: [ + { + field: 'schedule', + operation: 'set', + value: { interval: '10m' }, + }, + ], + }); + + expect(result.errors).toHaveLength(0); + expect(result.rules).toHaveLength(1); + }); }); describe('paramsModifier', () => { @@ -2579,7 +3314,13 @@ describe('bulkEdit()', () => { const result = await rulesClient.bulkEdit({ filter: '', - operations: [], + operations: [ + { + field: 'tags', + operation: 'add', + value: ['test-1'], + }, + ], paramsModifier: async (params) => { params.index = ['test-index-*']; diff --git a/x-pack/plugins/alerting/server/application/rule/methods/bulk_edit/bulk_edit_rules.ts b/x-pack/plugins/alerting/server/application/rule/methods/bulk_edit/bulk_edit_rules.ts index 1a4898418a8c4..53508a2de0ceb 100644 --- a/x-pack/plugins/alerting/server/application/rule/methods/bulk_edit/bulk_edit_rules.ts +++ b/x-pack/plugins/alerting/server/application/rule/methods/bulk_edit/bulk_edit_rules.ts @@ -15,6 +15,8 @@ import { SavedObjectsFindResult, SavedObjectsUpdateResponse, } from '@kbn/core/server'; +import { validateSystemActions } from '../../../../lib/validate_system_actions'; +import { RuleAction, RuleSystemAction } from '../../../../../common'; import { RULE_SAVED_OBJECT_TYPE } from '../../../../saved_objects'; import { BulkActionSkipResult } from '../../../../../common/bulk_edit'; import { RuleTypeRegistry } from '../../../../types'; @@ -32,10 +34,10 @@ import { retryIfBulkEditConflicts, applyBulkEditOperation, buildKueryNodeFilter, - injectReferencesIntoActions, getBulkSnooze, getBulkUnsnooze, verifySnoozeScheduleLimit, + injectReferencesIntoActions, } from '../../../../rules_client/common'; import { alertingAuthorizationFilterOpts, @@ -56,6 +58,7 @@ import { RuleBulkOperationAggregation, RulesClientContext, NormalizedAlertActionWithGeneratedValues, + NormalizedAlertAction, } from '../../../../rules_client/types'; import { migrateLegacyActions } from '../../../../rules_client/lib'; import { @@ -78,6 +81,10 @@ import { transformRuleDomainToRule, } from '../../transforms'; import { validateScheduleLimit, ValidateScheduleLimitResult } from '../get_schedule_frequency'; +import { + bulkEditDefaultActionsSchema, + bulkEditSystemActionsSchema, +} from './schemas/bulk_edit_rules_option_schemas'; const isValidInterval = (interval: string | undefined): interval is string => { return interval !== undefined; @@ -117,6 +124,7 @@ export async function bulkEditRules( ): Promise> { const queryFilter = (options as BulkEditOptionsFilter).filter; const ids = (options as BulkEditOptionsIds).ids; + const actionsClient = await context.getActionsClient(); if (ids && queryFilter) { throw Boom.badRequest( @@ -231,13 +239,17 @@ export async function bulkEditRules( // fix the type cast from SavedObjectsBulkUpdateObject to SavedObjectsBulkUpdateObject // when we are doing the bulk create and this should fix itself const ruleType = context.ruleTypeRegistry.get(attributes.alertTypeId!); - const ruleDomain = transformRuleAttributesToRuleDomain(attributes as RuleAttributes, { - id, - logger: context.logger, - ruleType, - references, - omitGeneratedValues: false, - }); + const ruleDomain = transformRuleAttributesToRuleDomain( + attributes as RuleAttributes, + { + id, + logger: context.logger, + ruleType, + references, + omitGeneratedValues: false, + }, + (connectorId: string) => actionsClient.isSystemAction(connectorId) + ); try { ruleDomainSchema.validate(ruleDomain); } catch (e) { @@ -358,7 +370,6 @@ async function bulkEditRulesOcc( skipped: [], }; } - const { result, apiKeysToInvalidate } = rules.length > 0 ? await saveBulkUpdatedRules({ @@ -478,7 +489,8 @@ async function updateRuleAttributesAndParamsInMemory( logger: context.logger, ruleType: context.ruleTypeRegistry.get(rule.attributes.alertTypeId), references: rule.references, - } + }, + context.isSystemAction ); const { @@ -490,7 +502,7 @@ async function updateRuleAttributesAndParamsInMemory( context, operations, rule: ruleDomain, - ruleActions: ruleActions as RuleDomain['actions'], // TODO (http-versioning) Remove this cast once we fix injectReferencesIntoActions + ruleActions, ruleType, }); @@ -532,9 +544,9 @@ async function updateRuleAttributesAndParamsInMemory( ); const { - actions: rawAlertActions, references, params: updatedParams, + actions: actionsWithRefs, } = await extractReferences( context, ruleType, @@ -542,10 +554,13 @@ async function updateRuleAttributesAndParamsInMemory( validatedMutatedAlertTypeParams ); - const ruleAttributes = transformRuleDomainToRuleAttributes(updatedRule, { - legacyId: rule.attributes.legacyId, - actionsWithRefs: rawAlertActions, - paramsWithRefs: updatedParams as RuleAttributes['params'], + const ruleAttributes = transformRuleDomainToRuleAttributes({ + actionsWithRefs, + rule: updatedRule, + params: { + legacyId: rule.attributes.legacyId, + paramsWithRefs: updatedParams as RuleAttributes['params'], + }, }); const { apiKeyAttributes } = await prepareApiKeys( @@ -563,7 +578,7 @@ async function updateRuleAttributesAndParamsInMemory( ruleAttributes, apiKeyAttributes, updatedParams, - rawAlertActions, + ruleAttributes.actions, username ); @@ -621,9 +636,11 @@ async function getUpdatedAttributesFromOperations({ context: RulesClientContext; operations: BulkEditOperation[]; rule: RuleDomain; - ruleActions: RuleDomain['actions']; + ruleActions: RuleDomain['actions'] | RuleDomain['systemActions']; ruleType: RuleType; }) { + const actionsClient = await context.getActionsClient(); + let updatedRule = cloneDeep(rule); let updatedRuleActions = ruleActions; let hasUpdateApiKeyOperation = false; @@ -636,21 +653,53 @@ async function getUpdatedAttributesFromOperations({ // the `isAttributesUpdateSkipped` flag to false. switch (operation.field) { case 'actions': { + const systemActions = operation.value.filter((action): action is RuleSystemAction => + actionsClient.isSystemAction(action.id) + ); + if (systemActions.length > 0) { + try { + bulkEditSystemActionsSchema.validate(systemActions); + } catch (error) { + throw Boom.badRequest(`Error validating bulk edit rules operations - ${error.message}`); + } + } + + const defaultActions = operation.value.filter( + (action): action is RuleAction => !actionsClient.isSystemAction(action.id) + ); + if (defaultActions.length > 0) { + try { + bulkEditDefaultActionsSchema.validate(defaultActions); + } catch (error) { + throw Boom.badRequest(`Error validating bulk edit rules operations - ${error.message}`); + } + } + + const { actions: genActions, systemActions: genSystemActions } = + await addGeneratedActionValues(defaultActions, systemActions, context); const updatedOperation = { ...operation, - value: await addGeneratedActionValues(operation.value, context), + value: [...genActions, ...genSystemActions], }; + await validateSystemActions({ + actionsClient, + connectorAdapterRegistry: context.connectorAdapterRegistry, + systemActions: genSystemActions, + }); + try { await validateActions(context, ruleType, { ...updatedRule, - actions: updatedOperation.value, + actions: genActions, + systemActions: genSystemActions, }); } catch (e) { // If validateActions fails on the first attempt, it may be because of legacy rule-level frequency params updatedRule = await attemptToMigrateLegacyFrequency( context, - updatedOperation, + operation.field, + genActions, updatedRule, ruleType ); @@ -669,23 +718,27 @@ async function getUpdatedAttributesFromOperations({ break; } + case 'snoozeSchedule': { if (operation.operation === 'set') { const snoozeAttributes = getBulkSnooze( updatedRule, operation.value as RuleSnoozeSchedule ); + try { verifySnoozeScheduleLimit(snoozeAttributes.snoozeSchedule); } catch (error) { throw Error(`Error updating rule: could not add snooze - ${error.message}`); } + updatedRule = { ...updatedRule, muteAll: snoozeAttributes.muteAll, snoozeSchedule: snoozeAttributes.snoozeSchedule as RuleDomain['snoozeSchedule'], }; } + if (operation.operation === 'delete') { const idsToDelete = operation.value && [...operation.value]; if (idsToDelete?.length === 0) { @@ -702,18 +755,25 @@ async function getUpdatedAttributesFromOperations({ snoozeSchedule: snoozeAttributes.snoozeSchedule as RuleDomain['snoozeSchedule'], }; } + isAttributesUpdateSkipped = false; break; } + case 'apiKey': { hasUpdateApiKeyOperation = true; isAttributesUpdateSkipped = false; break; } + default: { if (operation.field === 'schedule') { - validateScheduleOperation(operation.value, updatedRule.actions, rule.id); + const defaultActions = updatedRule.actions.filter( + (action) => !actionsClient.isSystemAction(action.id) + ); + validateScheduleOperation(operation.value, defaultActions, rule.id); } + const { modifiedAttributes, isAttributeModified } = applyBulkEditOperation( operation, updatedRule @@ -923,18 +983,19 @@ async function saveBulkUpdatedRules({ async function attemptToMigrateLegacyFrequency( context: RulesClientContext, - operation: BulkEditOperation, + operationField: BulkEditOperation['field'], + actions: NormalizedAlertAction[], rule: RuleDomain, ruleType: RuleType ) { - if (operation.field !== 'actions') + if (operationField !== 'actions') throw new Error('Can only perform frequency migration on an action operation'); // Try to remove the rule-level frequency params, and then validate actions if (typeof rule.notifyWhen !== 'undefined') rule.notifyWhen = undefined; if (rule.throttle) rule.throttle = undefined; await validateActions(context, ruleType, { ...rule, - actions: operation.value, + actions, }); return rule; } diff --git a/x-pack/plugins/alerting/server/application/rule/methods/bulk_edit/schemas/bulk_edit_rules_option_schemas.ts b/x-pack/plugins/alerting/server/application/rule/methods/bulk_edit/schemas/bulk_edit_rules_option_schemas.ts index f80d63210cf4a..e133908cc31cd 100644 --- a/x-pack/plugins/alerting/server/application/rule/methods/bulk_edit/schemas/bulk_edit_rules_option_schemas.ts +++ b/x-pack/plugins/alerting/server/application/rule/methods/bulk_edit/schemas/bulk_edit_rules_option_schemas.ts @@ -6,7 +6,7 @@ */ import { schema } from '@kbn/config-schema'; import { rRuleRequestSchema } from '../../../../r_rule/schemas'; -import { notifyWhenSchema } from '../../../schemas'; +import { notifyWhenSchema, actionAlertsFilterSchema } from '../../../schemas'; import { validateDuration } from '../../../validation'; import { validateSnoozeSchedule } from '../validation'; @@ -26,7 +26,7 @@ const bulkEditRuleSnoozeScheduleSchemaWithValidation = schema.object( { validate: validateSnoozeSchedule } ); -const bulkEditActionSchema = schema.object({ +const bulkEditDefaultActionSchema = schema.object({ group: schema.string(), id: schema.string(), params: schema.recordOf(schema.string(), schema.any(), { defaultValue: {} }), @@ -38,8 +38,19 @@ const bulkEditActionSchema = schema.object({ notifyWhen: notifyWhenSchema, }) ), + alertsFilter: schema.maybe(actionAlertsFilterSchema), }); +export const bulkEditDefaultActionsSchema = schema.arrayOf(bulkEditDefaultActionSchema); + +export const bulkEditSystemActionSchema = schema.object({ + id: schema.string(), + params: schema.recordOf(schema.string(), schema.maybe(schema.any()), { defaultValue: {} }), + uuid: schema.maybe(schema.string()), +}); + +export const bulkEditSystemActionsSchema = schema.arrayOf(bulkEditSystemActionSchema); + const bulkEditTagSchema = schema.object({ operation: schema.oneOf([schema.literal('add'), schema.literal('delete'), schema.literal('set')]), field: schema.literal('tags'), @@ -49,7 +60,7 @@ const bulkEditTagSchema = schema.object({ const bulkEditActionsSchema = schema.object({ operation: schema.oneOf([schema.literal('add'), schema.literal('set')]), field: schema.literal('actions'), - value: schema.arrayOf(bulkEditActionSchema), + value: schema.arrayOf(schema.oneOf([bulkEditDefaultActionSchema, bulkEditSystemActionSchema])), }); const bulkEditScheduleSchema = schema.object({ diff --git a/x-pack/plugins/alerting/server/application/rule/methods/bulk_untrack/bulk_untrack_alerts.test.ts b/x-pack/plugins/alerting/server/application/rule/methods/bulk_untrack/bulk_untrack_alerts.test.ts index 78485744e8103..f2de0ed7840dd 100644 --- a/x-pack/plugins/alerting/server/application/rule/methods/bulk_untrack/bulk_untrack_alerts.test.ts +++ b/x-pack/plugins/alerting/server/application/rule/methods/bulk_untrack/bulk_untrack_alerts.test.ts @@ -22,6 +22,7 @@ import { AlertingAuthorization } from '../../../../authorization/alerting_author import { alertsServiceMock } from '../../../../alerts_service/alerts_service.mock'; import { ALERT_RULE_UUID, ALERT_UUID } from '@kbn/rule-data-utils'; import { ConcreteTaskInstance, TaskStatus } from '@kbn/task-manager-plugin/server'; +import { ConnectorAdapterRegistry } from '../../../../connector_adapters/connector_adapter_registry'; const taskManager = taskManagerMock.createStart(); const ruleTypeRegistry = ruleTypeRegistryMock.create(); @@ -61,6 +62,8 @@ const rulesClientParams: jest.Mocked = { getAlertIndicesAlias: jest.fn(), alertsService, uiSettings: uiSettingsServiceMock.createStartContract(), + isSystemAction: jest.fn(), + connectorAdapterRegistry: new ConnectorAdapterRegistry(), }; describe('bulkUntrackAlerts()', () => { diff --git a/x-pack/plugins/alerting/server/application/rule/methods/create/create_rule.test.ts b/x-pack/plugins/alerting/server/application/rule/methods/create/create_rule.test.ts index 07063b4fee4af..7d1fe07d8a7a4 100644 --- a/x-pack/plugins/alerting/server/application/rule/methods/create/create_rule.test.ts +++ b/x-pack/plugins/alerting/server/application/rule/methods/create/create_rule.test.ts @@ -28,6 +28,10 @@ import { getBeforeSetup, setGlobalDate } from '../../../../rules_client/tests/li import { RecoveredActionGroup } from '../../../../../common'; import { bulkMarkApiKeysForInvalidation } from '../../../../invalidate_pending_api_keys/bulk_mark_api_keys_for_invalidation'; import { getRuleExecutionStatusPending, getDefaultMonitoring } from '../../../../lib'; +import { ConnectorAdapterRegistry } from '../../../../connector_adapters/connector_adapter_registry'; +import { ConnectorAdapter } from '../../../../connector_adapters/types'; +import { RuleDomain } from '../../types'; +import { RuleSystemAction } from '../../../../types'; import { RULE_SAVED_OBJECT_TYPE } from '../../../../saved_objects'; jest.mock('../../../../invalidate_pending_api_keys/bulk_mark_api_keys_for_invalidation', () => ({ @@ -61,6 +65,7 @@ const authorization = alertingAuthorizationMock.create(); const actionsAuthorization = actionsAuthorizationMock.create(); const auditLogger = auditLoggerMock.create(); const internalSavedObjectsRepository = savedObjectsRepositoryMock.create(); +const connectorAdapterRegistry = new ConnectorAdapterRegistry(); const kibanaVersion = 'v8.0.0'; const rulesClientParams: jest.Mocked = { @@ -86,6 +91,8 @@ const rulesClientParams: jest.Mocked = { getAuthenticationAPIKey: jest.fn(), getAlertIndicesAlias: jest.fn(), alertsService: null, + connectorAdapterRegistry, + isSystemAction: jest.fn(), uiSettings: uiSettingsServiceMock.createStartContract(), }; @@ -153,6 +160,9 @@ describe('create()', () => { isSystemAction: false, }, ]); + + actionsClient.isSystemAction.mockImplementation((id: string) => id === 'system_action-id'); + taskManager.schedule.mockResolvedValue({ id: 'task-123', taskType: 'alerting:123', @@ -166,6 +176,7 @@ describe('create()', () => { params: {}, ownerId: null, }); + rulesClientParams.getActionsClient.mockResolvedValue(actionsClient); }); @@ -192,6 +203,7 @@ describe('create()', () => { group: 'default', actionRef: 'action_0', actionTypeId: 'test', + uuid: 'test-uuid', params: { foo: true, }, @@ -346,12 +358,14 @@ describe('create()', () => { group: 'default', actionRef: 'action_0', actionTypeId: 'test', + uuid: 'test-uuid', params: { foo: true, }, }, ], }; + unsecuredSavedObjectsClient.create.mockResolvedValueOnce({ id: '1', type: RULE_SAVED_OBJECT_TYPE, @@ -368,6 +382,7 @@ describe('create()', () => { }, ], }); + unsecuredSavedObjectsClient.create.mockResolvedValueOnce({ id: '1', type: RULE_SAVED_OBJECT_TYPE, @@ -385,13 +400,16 @@ describe('create()', () => { }, ], }); + const result = await rulesClient.create({ data }); + expect(authorization.ensureAuthorized).toHaveBeenCalledWith({ entity: 'rule', consumer: 'bar', operation: 'create', ruleTypeId: '123', }); + expect(result).toMatchInlineSnapshot(` Object { "actions": Array [ @@ -402,6 +420,7 @@ describe('create()', () => { "params": Object { "foo": true, }, + "uuid": "test-uuid", }, ], "alertTypeId": "123", @@ -426,6 +445,7 @@ describe('create()', () => { "interval": "1m", }, "scheduledTaskId": "task-123", + "systemActions": Array [], "tags": Array [ "foo", ], @@ -577,6 +597,7 @@ describe('create()', () => { group: 'default', actionRef: 'action_0', actionTypeId: 'test', + uuid: 'test-uuid', params: { foo: true, }, @@ -641,6 +662,7 @@ describe('create()', () => { group: 'default', actionRef: 'action_0', actionTypeId: 'test', + uuid: 'test-uuid', params: { foo: true, }, @@ -818,6 +840,7 @@ describe('create()', () => { group: 'default', actionRef: 'action_0', actionTypeId: 'test', + uuid: 'test-uuid', params: { foo: true, }, @@ -826,6 +849,7 @@ describe('create()', () => { group: 'default', actionRef: 'action_1', actionTypeId: 'test', + uuid: 'test-uuid-1', params: { foo: true, }, @@ -834,6 +858,7 @@ describe('create()', () => { group: 'default', actionRef: 'action_2', actionTypeId: 'test2', + uuid: 'test-uuid-2', params: { foo: true, }, @@ -878,6 +903,7 @@ describe('create()', () => { "params": Object { "foo": true, }, + "uuid": "test-uuid", }, Object { "actionTypeId": "test", @@ -886,6 +912,7 @@ describe('create()', () => { "params": Object { "foo": true, }, + "uuid": "test-uuid-1", }, Object { "actionTypeId": "test2", @@ -894,6 +921,7 @@ describe('create()', () => { "params": Object { "foo": true, }, + "uuid": "test-uuid-2", }, ], "alertTypeId": "123", @@ -912,6 +940,7 @@ describe('create()', () => { "interval": "1m", }, "scheduledTaskId": "task-123", + "systemActions": Array [], "updatedAt": 2019-02-12T21:01:22.479Z, } `); @@ -997,10 +1026,10 @@ describe('create()', () => { isSystemAction: false, }, ]); + actionsClient.isPreconfigured.mockReset(); - actionsClient.isPreconfigured.mockReturnValueOnce(false); - actionsClient.isPreconfigured.mockReturnValueOnce(true); - actionsClient.isPreconfigured.mockReturnValueOnce(false); + actionsClient.isPreconfigured.mockImplementation((id) => id === 'preconfigured'); + unsecuredSavedObjectsClient.create.mockResolvedValueOnce({ id: '1', type: RULE_SAVED_OBJECT_TYPE, @@ -1019,6 +1048,7 @@ describe('create()', () => { group: 'default', actionRef: 'action_0', actionTypeId: 'test', + uuid: 'test-uuid', params: { foo: true, }, @@ -1027,6 +1057,7 @@ describe('create()', () => { group: 'default', actionRef: 'preconfigured:preconfigured', actionTypeId: 'test', + uuid: 'test-uuid-1', params: { foo: true, }, @@ -1035,6 +1066,7 @@ describe('create()', () => { group: 'default', actionRef: 'action_2', actionTypeId: 'test2', + uuid: 'test-uuid-2', params: { foo: true, }, @@ -1075,6 +1107,7 @@ describe('create()', () => { "params": Object { "foo": true, }, + "uuid": "test-uuid", }, Object { "actionTypeId": "test", @@ -1083,6 +1116,7 @@ describe('create()', () => { "params": Object { "foo": true, }, + "uuid": "test-uuid-1", }, Object { "actionTypeId": "test2", @@ -1091,6 +1125,7 @@ describe('create()', () => { "params": Object { "foo": true, }, + "uuid": "test-uuid-2", }, ], "alertTypeId": "123", @@ -1109,6 +1144,7 @@ describe('create()', () => { "interval": "1m", }, "scheduledTaskId": "task-123", + "systemActions": Array [], "updatedAt": 2019-02-12T21:01:22.479Z, } `); @@ -1194,11 +1230,6 @@ describe('create()', () => { foo: true, }, }, - { - group: 'default', - id: 'system_action-id', - params: {}, - }, { group: 'default', id: '2', @@ -1207,6 +1238,12 @@ describe('create()', () => { }, }, ], + systemActions: [ + { + id: 'system_action-id', + params: {}, + }, + ], }); actionsClient.getBulk.mockReset(); @@ -1257,11 +1294,6 @@ describe('create()', () => { }, ]); - actionsClient.isSystemAction.mockReset(); - actionsClient.isSystemAction.mockReturnValueOnce(false); - actionsClient.isSystemAction.mockReturnValueOnce(true); - actionsClient.isSystemAction.mockReturnValueOnce(false); - unsecuredSavedObjectsClient.create.mockResolvedValueOnce({ id: '1', type: RULE_SAVED_OBJECT_TYPE, @@ -1285,14 +1317,13 @@ describe('create()', () => { }, }, { - group: 'default', actionRef: 'system_action:system_action-id', actionTypeId: 'test', params: {}, }, { group: 'default', - actionRef: 'action_2', + actionRef: 'action_1', actionTypeId: 'test2', params: { foo: true, @@ -1308,7 +1339,7 @@ describe('create()', () => { id: '1', }, { - name: 'action_2', + name: 'action_1', type: 'action', id: '2', }, @@ -1337,12 +1368,7 @@ describe('create()', () => { "params": Object { "foo": true, }, - }, - Object { - "actionTypeId": "test", - "group": "default", - "id": "system_action-id", - "params": Object {}, + "uuid": undefined, }, Object { "actionTypeId": "test2", @@ -1351,6 +1377,7 @@ describe('create()', () => { "params": Object { "foo": true, }, + "uuid": undefined, }, ], "alertTypeId": "123", @@ -1369,6 +1396,14 @@ describe('create()', () => { "interval": "1m", }, "scheduledTaskId": "task-123", + "systemActions": Array [ + Object { + "actionTypeId": "test", + "id": "system_action-id", + "params": Object {}, + "uuid": undefined, + }, + ], "updatedAt": 2019-02-12T21:01:22.479Z, } `); @@ -1378,55 +1413,78 @@ describe('create()', () => { { actions: [ { - group: 'default', actionRef: 'action_0', actionTypeId: 'test', + group: 'default', params: { foo: true, }, uuid: '111', }, { - group: 'default', - actionRef: 'system_action:system_action-id', - actionTypeId: 'test', - params: {}, - uuid: '112', - }, - { - group: 'default', - actionRef: 'action_2', + actionRef: 'action_1', actionTypeId: 'test2', + group: 'default', params: { foo: true, }, + uuid: '112', + }, + { + actionRef: 'system_action:system_action-id', + actionTypeId: 'test', + params: {}, uuid: '113', }, ], alertTypeId: '123', apiKey: null, - apiKeyOwner: null, apiKeyCreatedByUser: null, + apiKeyOwner: null, consumer: 'bar', createdAt: '2019-02-12T21:01:22.479Z', createdBy: 'elastic', enabled: true, - legacyId: null, executionStatus: { lastExecutionDate: '2019-02-12T21:01:22.479Z', status: 'pending', }, - monitoring: getDefaultMonitoring('2019-02-12T21:01:22.479Z'), - meta: { versionApiKeyLastmodified: kibanaVersion }, + legacyId: null, + meta: { + versionApiKeyLastmodified: 'v8.0.0', + }, + monitoring: { + run: { + calculated_metrics: { + success_ratio: 0, + }, + history: [], + last_run: { + metrics: { + duration: 0, + gap_duration_s: null, + total_alerts_created: null, + total_alerts_detected: null, + total_indexing_duration_ms: null, + total_search_duration_ms: null, + }, + timestamp: '2019-02-12T21:01:22.479Z', + }, + }, + }, muteAll: false, - snoozeSchedule: [], mutedInstanceIds: [], name: 'abc', notifyWhen: null, - params: { bar: true }, + params: { + bar: true, + }, revision: 0, running: false, - schedule: { interval: '1m' }, + schedule: { + interval: '1m', + }, + snoozeSchedule: [], tags: ['foo'], throttle: null, updatedAt: '2019-02-12T21:01:22.479Z', @@ -1436,11 +1494,10 @@ describe('create()', () => { id: 'mock-saved-object-id', references: [ { id: '1', name: 'action_0', type: 'action' }, - { id: '2', name: 'action_2', type: 'action' }, + { id: '2', name: 'action_1', type: 'action' }, ], } ); - expect(actionsClient.isSystemAction).toHaveBeenCalledTimes(3); }); test('creates a disabled rule', async () => { @@ -1465,6 +1522,7 @@ describe('create()', () => { group: 'default', actionRef: 'action_0', actionTypeId: 'test', + uuid: 'test-uuid', params: { foo: true, }, @@ -1490,6 +1548,7 @@ describe('create()', () => { "params": Object { "foo": true, }, + "uuid": "test-uuid", }, ], "alertTypeId": "123", @@ -1508,6 +1567,7 @@ describe('create()', () => { "schedule": Object { "interval": 10000, }, + "systemActions": Array [], "updatedAt": 2019-02-12T21:01:22.479Z, } `); @@ -1582,6 +1642,7 @@ describe('create()', () => { group: 'default', actionRef: 'action_0', actionTypeId: 'test', + uuid: 'test-uuid', params: { foo: true, }, @@ -1680,6 +1741,7 @@ describe('create()', () => { "params": Object { "foo": true, }, + "uuid": "test-uuid", }, ], "alertTypeId": "123", @@ -1699,6 +1761,7 @@ describe('create()', () => { "interval": "1m", }, "scheduledTaskId": "task-123", + "systemActions": Array [], "updatedAt": 2019-02-12T21:01:22.479Z, } `); @@ -1738,6 +1801,7 @@ describe('create()', () => { return { state: {} }; }, category: 'test', + validLegacyConsumers: [], producer: 'alerts', useSavedObjectReferences: { extractReferences: extractReferencesFn, @@ -1746,7 +1810,6 @@ describe('create()', () => { validate: { params: { validate: (params) => params }, }, - validLegacyConsumers: [], })); const data = getMockData({ params: ruleParams, @@ -1770,6 +1833,7 @@ describe('create()', () => { group: 'default', actionRef: 'action_0', actionTypeId: 'test', + uuid: 'test-uuid', params: { foo: true, }, @@ -1869,6 +1933,7 @@ describe('create()', () => { "params": Object { "foo": true, }, + "uuid": "test-uuid", }, ], "alertTypeId": "123", @@ -1888,6 +1953,7 @@ describe('create()', () => { "interval": "1m", }, "scheduledTaskId": "task-123", + "systemActions": Array [], "updatedAt": 2019-02-12T21:01:22.479Z, } `); @@ -1915,6 +1981,7 @@ describe('create()', () => { group: 'default', actionRef: 'action_0', actionTypeId: 'test', + uuid: 'test-uuid', params: { foo: true, }, @@ -1957,6 +2024,7 @@ describe('create()', () => { group: 'default', actionRef: 'action_0', actionTypeId: 'test', + uuid: 'test-uuid', params: { foo: true, }, @@ -2041,6 +2109,7 @@ describe('create()', () => { "params": Object { "foo": true, }, + "uuid": "test-uuid", }, ], "alertTypeId": "123", @@ -2065,6 +2134,7 @@ describe('create()', () => { "interval": "1m", }, "scheduledTaskId": "task-123", + "systemActions": Array [], "tags": Array [ "foo", ], @@ -2098,6 +2168,7 @@ describe('create()', () => { group: 'default', actionRef: 'action_0', actionTypeId: 'test', + uuid: 'test-uuid', params: { foo: true, }, @@ -2182,6 +2253,7 @@ describe('create()', () => { "params": Object { "foo": true, }, + "uuid": "test-uuid", }, ], "alertTypeId": "123", @@ -2206,6 +2278,7 @@ describe('create()', () => { "interval": "1m", }, "scheduledTaskId": "task-123", + "systemActions": Array [], "tags": Array [ "foo", ], @@ -2239,6 +2312,7 @@ describe('create()', () => { group: 'default', actionRef: 'action_0', actionTypeId: 'test', + uuid: 'test-uuid', params: { foo: true, }, @@ -2323,6 +2397,7 @@ describe('create()', () => { "params": Object { "foo": true, }, + "uuid": "test-uuid", }, ], "alertTypeId": "123", @@ -2347,6 +2422,7 @@ describe('create()', () => { "interval": "1m", }, "scheduledTaskId": "task-123", + "systemActions": Array [], "tags": Array [ "foo", ], @@ -2388,6 +2464,7 @@ describe('create()', () => { group: 'default', actionRef: 'action_0', actionTypeId: 'test', + uuid: 'test-uuid', params: { foo: true, }, @@ -2505,6 +2582,7 @@ describe('create()', () => { "params": Object { "foo": true, }, + "uuid": "test-uuid", }, ], "alertTypeId": "123", @@ -2531,6 +2609,7 @@ describe('create()', () => { "interval": "10s", }, "scheduledTaskId": "task-123", + "systemActions": Array [], "tags": Array [ "foo", ], @@ -2552,6 +2631,8 @@ describe('create()', () => { name: 'Default', }, ], + category: 'test', + validLegacyConsumers: [], defaultActionGroupId: 'default', recoveryActionGroup: RecoveredActionGroup, validate: { @@ -2565,9 +2646,7 @@ describe('create()', () => { async executor() { return { state: {} }; }, - category: 'test', producer: 'alerts', - validLegacyConsumers: [], }); await expect(rulesClient.create({ data })).rejects.toThrowErrorMatchingInlineSnapshot( `"params invalid: [param1]: expected value of type [string] but got [undefined]"` @@ -2624,6 +2703,7 @@ describe('create()', () => { group: 'default', actionRef: 'action_0', actionTypeId: 'test', + uuid: 'test-uuid', params: { foo: true, }, @@ -2673,6 +2753,7 @@ describe('create()', () => { group: 'default', actionRef: 'action_0', actionTypeId: 'test', + uuid: 'test-uuid', params: { foo: true, }, @@ -2720,6 +2801,7 @@ describe('create()', () => { group: 'default', actionRef: 'action_0', actionTypeId: 'test', + uuid: 'test-uuid', params: { foo: true, }, @@ -2778,6 +2860,7 @@ describe('create()', () => { group: 'default', actionRef: 'action_0', actionTypeId: 'test', + uuid: 'test-uuid', params: { foo: true, }, @@ -2881,6 +2964,7 @@ describe('create()', () => { group: 'default', actionRef: 'action_0', actionTypeId: 'test', + uuid: 'test-uuid', params: { foo: true, }, @@ -3035,6 +3119,7 @@ describe('create()', () => { return { state: {} }; }, category: 'test', + validLegacyConsumers: [], producer: 'alerts', useSavedObjectReferences: { extractReferences: jest.fn(), @@ -3043,7 +3128,6 @@ describe('create()', () => { validate: { params: { validate: (params) => params }, }, - validLegacyConsumers: [], })); const createdAttributes = { ...data, @@ -3064,6 +3148,7 @@ describe('create()', () => { group: 'default', actionRef: 'action_0', actionTypeId: 'test', + uuid: 'test-uuid', params: { foo: true, }, @@ -3109,6 +3194,7 @@ describe('create()', () => { return { state: {} }; }, category: 'test', + validLegacyConsumers: [], producer: 'alerts', useSavedObjectReferences: { extractReferences: jest.fn(), @@ -3117,7 +3203,6 @@ describe('create()', () => { validate: { params: { validate: (params) => params }, }, - validLegacyConsumers: [], })); const data = getMockData({ schedule: { interval: '1s' } }); @@ -3133,6 +3218,7 @@ describe('create()', () => { ...rulesClientParams, minimumScheduleInterval: { value: '1m', enforce: true }, }); + ruleTypeRegistry.get.mockImplementation(() => ({ id: '123', name: 'Test', @@ -3148,6 +3234,7 @@ describe('create()', () => { return { state: {} }; }, category: 'test', + validLegacyConsumers: [], producer: 'alerts', useSavedObjectReferences: { extractReferences: jest.fn(), @@ -3156,7 +3243,6 @@ describe('create()', () => { validate: { params: { validate: (params) => params }, }, - validLegacyConsumers: [], })); const data = getMockData({ @@ -3242,6 +3328,7 @@ describe('create()', () => { return { state: {} }; }, category: 'test', + validLegacyConsumers: [], producer: 'alerts', useSavedObjectReferences: { extractReferences: jest.fn(), @@ -3250,7 +3337,6 @@ describe('create()', () => { validate: { params: { validate: (params) => params }, }, - validLegacyConsumers: [], })); const data = getMockData({ @@ -3293,6 +3379,7 @@ describe('create()', () => { return { state: {} }; }, category: 'test', + validLegacyConsumers: [], producer: 'alerts', useSavedObjectReferences: { extractReferences: jest.fn(), @@ -3301,7 +3388,6 @@ describe('create()', () => { validate: { params: { validate: (params) => params }, }, - validLegacyConsumers: [], })); const data = getMockData({ @@ -3357,6 +3443,7 @@ describe('create()', () => { return { state: {} }; }, category: 'test', + validLegacyConsumers: [], producer: 'alerts', useSavedObjectReferences: { extractReferences: jest.fn(), @@ -3365,7 +3452,6 @@ describe('create()', () => { validate: { params: { validate: (params) => params }, }, - validLegacyConsumers: [], })); const data = getMockData({ @@ -3439,6 +3525,7 @@ describe('create()', () => { return { state: {} }; }, category: 'test', + validLegacyConsumers: [], producer: 'alerts', useSavedObjectReferences: { extractReferences: jest.fn(), @@ -3447,7 +3534,6 @@ describe('create()', () => { validate: { params: { validate: (params) => params }, }, - validLegacyConsumers: [], })); const data = getMockData({ @@ -3553,6 +3639,7 @@ describe('create()', () => { group: 'default', actionRef: 'action_0', actionTypeId: '.slack', + uuid: 'test-uuid', params: { foo: true, }, @@ -3597,6 +3684,7 @@ describe('create()', () => { "params": Object { "foo": true, }, + "uuid": "test-uuid", }, ], "alertTypeId": "123", @@ -3614,6 +3702,7 @@ describe('create()', () => { "interval": "1m", }, "scheduledTaskId": "task-123", + "systemActions": Array [], "updatedAt": 2019-02-12T21:01:22.479Z, } `); @@ -3640,6 +3729,7 @@ describe('create()', () => { return { state: {} }; }, category: 'test', + validLegacyConsumers: [], producer: 'alerts', useSavedObjectReferences: { extractReferences: jest.fn(), @@ -3653,7 +3743,6 @@ describe('create()', () => { mappings: { fieldMap: { field: { type: 'keyword', required: false } } }, shouldWrite: true, }, - validLegacyConsumers: [], })); const data = getMockData({ @@ -3699,6 +3788,7 @@ describe('create()', () => { return { state: {} }; }, category: 'test', + validLegacyConsumers: [], producer: 'alerts', useSavedObjectReferences: { extractReferences: jest.fn(), @@ -3707,7 +3797,6 @@ describe('create()', () => { validate: { params: { validate: (params) => params }, }, - validLegacyConsumers: [], })); const data = getMockData({ @@ -3761,6 +3850,7 @@ describe('create()', () => { group: 'default', actionRef: 'action_0', actionTypeId: 'test', + uuid: 'test-uuid', params: { foo: true, }, @@ -3870,4 +3960,398 @@ describe('create()', () => { expect.any(Object) ); }); + + describe('actions', () => { + const connectorAdapter: ConnectorAdapter = { + connectorTypeId: '.test', + ruleActionParamsSchema: schema.object({ foo: schema.string() }), + buildActionParams: jest.fn(), + }; + + connectorAdapterRegistry.register(connectorAdapter); + + beforeEach(() => { + actionsClient.getBulk.mockReset(); + actionsClient.getBulk.mockResolvedValue([ + { + id: '1', + actionTypeId: 'test', + config: { + from: 'me@me.com', + hasAuth: false, + host: 'hello', + port: 22, + secure: null, + service: null, + }, + isMissingSecrets: false, + name: 'email connector', + isPreconfigured: false, + isDeprecated: false, + isSystemAction: false, + }, + { + id: 'system_action-id', + actionTypeId: '.test', + config: {}, + isMissingSecrets: false, + name: 'system action connector', + isPreconfigured: false, + isDeprecated: false, + isSystemAction: true, + }, + ]); + + unsecuredSavedObjectsClient.create.mockResolvedValueOnce({ + id: '1', + type: 'alert', + attributes: { + executionStatus: getRuleExecutionStatusPending('2019-02-12T21:01:22.479Z'), + alertTypeId: '123', + schedule: { interval: '1m' }, + params: { + bar: true, + }, + createdAt: new Date().toISOString(), + updatedAt: new Date().toISOString(), + notifyWhen: null, + actions: [ + { + group: 'default', + actionRef: 'action_0', + actionTypeId: 'test', + uuid: 'test-uuid', + params: { + foo: true, + }, + }, + { + group: 'default', + actionRef: 'system_action:system_action-id', + actionTypeId: 'test', + uuid: 'test-uuid-1', + params: { foo: 'test' }, + }, + ], + running: false, + }, + references: [ + { + name: 'action_0', + type: 'action', + id: '1', + }, + ], + }); + }); + + test('create a rule with system actions and default actions', async () => { + const data = getMockData({ + actions: [ + { + group: 'default', + id: '1', + params: { + foo: true, + }, + }, + ], + systemActions: [ + { + id: 'system_action-id', + params: { + foo: 'test', + }, + }, + ], + }); + + const result = await rulesClient.create({ data }); + + expect(result).toMatchInlineSnapshot(` + Object { + "actions": Array [ + Object { + "actionTypeId": "test", + "group": "default", + "id": "1", + "params": Object { + "foo": true, + }, + "uuid": "test-uuid", + }, + ], + "alertTypeId": "123", + "createdAt": 2019-02-12T21:01:22.479Z, + "executionStatus": Object { + "lastExecutionDate": 2019-02-12T21:01:22.000Z, + "status": "pending", + }, + "id": "1", + "notifyWhen": null, + "params": Object { + "bar": true, + }, + "running": false, + "schedule": Object { + "interval": "1m", + }, + "scheduledTaskId": "task-123", + "systemActions": Array [ + Object { + "actionTypeId": "test", + "id": "system_action-id", + "params": Object { + "foo": "test", + }, + "uuid": "test-uuid-1", + }, + ], + "updatedAt": 2019-02-12T21:01:22.479Z, + } + `); + + expect(unsecuredSavedObjectsClient.create).toHaveBeenCalledWith( + 'alert', + { + actions: [ + { + group: 'default', + actionRef: 'action_0', + actionTypeId: 'test', + params: { + foo: true, + }, + uuid: '156', + }, + { + actionRef: 'system_action:system_action-id', + actionTypeId: '.test', + params: { foo: 'test' }, + uuid: '157', + }, + ], + alertTypeId: '123', + apiKey: null, + apiKeyOwner: null, + apiKeyCreatedByUser: null, + consumer: 'bar', + createdAt: '2019-02-12T21:01:22.479Z', + createdBy: 'elastic', + enabled: true, + legacyId: null, + executionStatus: { + lastExecutionDate: '2019-02-12T21:01:22.479Z', + status: 'pending', + }, + monitoring: getDefaultMonitoring('2019-02-12T21:01:22.479Z'), + meta: { versionApiKeyLastmodified: kibanaVersion }, + muteAll: false, + snoozeSchedule: [], + mutedInstanceIds: [], + name: 'abc', + notifyWhen: null, + params: { bar: true }, + revision: 0, + running: false, + schedule: { interval: '1m' }, + tags: ['foo'], + throttle: null, + updatedAt: '2019-02-12T21:01:22.479Z', + updatedBy: 'elastic', + }, + { + id: 'mock-saved-object-id', + references: [{ id: '1', name: 'action_0', type: 'action' }], + } + ); + }); + + test('should construct the refs correctly and persist the actions to ES correctly', async () => { + const data = getMockData({ + actions: [ + { + group: 'default', + id: '1', + params: { + foo: true, + }, + }, + ], + systemActions: [ + { + id: 'system_action-id', + params: { + foo: 'test', + }, + }, + ], + }); + + await rulesClient.create({ data }); + + const rule = unsecuredSavedObjectsClient.create.mock.calls[0][1] as RuleDomain; + + expect(rule.actions).toEqual([ + { + group: 'default', + actionRef: 'action_0', + actionTypeId: 'test', + params: { + foo: true, + }, + uuid: '158', + }, + { + actionRef: 'system_action:system_action-id', + actionTypeId: '.test', + params: { foo: 'test' }, + uuid: '159', + }, + ]); + }); + + test('should transforms the actions from ES correctly', async () => { + const data = getMockData({ + actions: [ + { + group: 'default', + id: '1', + params: { + foo: true, + }, + }, + ], + systemActions: [ + { + id: 'system_action-id', + params: { + foo: 'test', + }, + }, + ], + }); + + const result = await rulesClient.create({ data }); + + expect(result.actions).toMatchInlineSnapshot(` + Array [ + Object { + "actionTypeId": "test", + "group": "default", + "id": "1", + "params": Object { + "foo": true, + }, + "uuid": "test-uuid", + }, + ] + `); + + expect(result.systemActions).toMatchInlineSnapshot(` + Array [ + Object { + "actionTypeId": "test", + "id": "system_action-id", + "params": Object { + "foo": "test", + }, + "uuid": "test-uuid-1", + }, + ] + `); + }); + + test('should throw an error if the system action does not exist', async () => { + const systemAction: RuleSystemAction = { + id: 'fake-system-action', + uuid: '123', + params: {}, + actionTypeId: '.test', + }; + + const data = getMockData({ actions: [], systemActions: [systemAction] }); + await expect(() => rulesClient.create({ data })).rejects.toMatchInlineSnapshot( + `[Error: Action fake-system-action is not a system action]` + ); + }); + + test('should throw an error if the system action contains the group', async () => { + const systemAction = { + id: 'system_action-id', + uuid: '123', + params: {}, + actionTypeId: '.test', + group: 'default', + }; + + const data = getMockData({ actions: [], systemActions: [systemAction] }); + await expect(() => rulesClient.create({ data })).rejects.toMatchInlineSnapshot( + `[Error: Error validating create data - [systemActions.0.group]: definition for this key is missing]` + ); + }); + + test('should throw an error if the system action contains the frequency', async () => { + const systemAction = { + id: 'system_action-id', + uuid: '123', + params: {}, + actionTypeId: '.test', + frequency: { + summary: false, + notifyWhen: 'onActionGroupChange', + throttle: null, + }, + }; + + const data = getMockData({ actions: [], systemActions: [systemAction] }); + await expect(() => rulesClient.create({ data })).rejects.toMatchInlineSnapshot( + `[Error: Error validating create data - [systemActions.0.frequency]: definition for this key is missing]` + ); + }); + + test('should throw an error if the system action contains the alertsFilter', async () => { + const systemAction = { + id: 'system_action-id', + uuid: '123', + params: {}, + actionTypeId: '.test', + alertsFilter: { + query: { kql: 'test:1', filters: [] }, + }, + }; + + const data = getMockData({ systemActions: [systemAction] }); + await expect(() => rulesClient.create({ data })).rejects.toMatchInlineSnapshot( + `[Error: Error validating create data - [systemActions.0.alertsFilter]: definition for this key is missing]` + ); + }); + + test('should throw an error if the default action does not contain the group', async () => { + const action = { + id: 'action-id-1', + params: {}, + actionTypeId: '.test', + }; + + const data = getMockData({ actions: [action] }); + await expect(() => rulesClient.create({ data })).rejects.toMatchInlineSnapshot( + `[Error: Error validating create data - [actions.0.group]: expected value of type [string] but got [undefined]]` + ); + }); + + test('should throw an error if the same system action is used twice', async () => { + const systemAction: RuleSystemAction = { + id: 'system_action-id', + uuid: '123', + params: { foo: 'test' }, + actionTypeId: '.test', + }; + + const data = getMockData({ actions: [], systemActions: [systemAction, systemAction] }); + await expect(() => rulesClient.create({ data })).rejects.toMatchInlineSnapshot( + `[Error: Cannot use the same system action twice]` + ); + }); + }); }); diff --git a/x-pack/plugins/alerting/server/application/rule/methods/create/create_rule.ts b/x-pack/plugins/alerting/server/application/rule/methods/create/create_rule.ts index ef55b67ef08db..7de8c7effa8f3 100644 --- a/x-pack/plugins/alerting/server/application/rule/methods/create/create_rule.ts +++ b/x-pack/plugins/alerting/server/application/rule/methods/create/create_rule.ts @@ -8,6 +8,7 @@ import Semver from 'semver'; import Boom from '@hapi/boom'; import { SavedObject, SavedObjectsUtils } from '@kbn/core/server'; import { withSpan } from '@kbn/apm-utils'; +import { validateSystemActions } from '../../../../lib/validate_system_actions'; import { RULE_SAVED_OBJECT_TYPE } from '../../../../saved_objects'; import { parseDuration, getRuleCircuitBreakerErrorMessage } from '../../../../../common'; import { WriteOperations, AlertingAuthorizationEntity } from '../../../../authorization'; @@ -24,7 +25,7 @@ import { } from '../../../../rules_client/lib'; import { generateAPIKeyName, apiKeyAsRuleDomainProperties } from '../../../../rules_client/common'; import { ruleAuditEvent, RuleAuditAction } from '../../../../rules_client/common/audit_events'; -import { RulesClientContext, NormalizedAlertAction } from '../../../../rules_client/types'; +import { RulesClientContext } from '../../../../rules_client/types'; import { RuleDomain, RuleParams } from '../../types'; import { SanitizedRule } from '../../../../types'; import { @@ -55,14 +56,18 @@ export async function createRule( // TODO (http-versioning): This should be of type Rule, change this when all rule types are fixed ): Promise> { const { data: initialData, options, allowMissingConnectorSecrets } = createParams; + const actionsClient = await context.getActionsClient(); + + const { actions: genAction, systemActions: genSystemActions } = await addGeneratedActionValues( + initialData.actions, + initialData.systemActions, + context + ); - // TODO (http-versioning): Remove this cast when we fix addGeneratedActionValues const data = { ...initialData, - actions: await addGeneratedActionValues( - initialData.actions as NormalizedAlertAction[], - context - ), + actions: genAction, + systemActions: genSystemActions, }; const id = options?.id || SavedObjectsUtils.generateId(); @@ -144,6 +149,14 @@ export async function createRule( validateActions(context, ruleType, data, allowMissingConnectorSecrets) ); + await withSpan({ name: 'validateSystemActions', type: 'rules' }, () => + validateSystemActions({ + actionsClient, + connectorAdapterRegistry: context.connectorAdapterRegistry, + systemActions: data.systemActions, + }) + ); + // Throw error if schedule interval is less than the minimum and we are enforcing it const intervalInMs = parseDuration(data.schedule.interval); if ( @@ -155,13 +168,14 @@ export async function createRule( ); } + const allActions = [...data.actions, ...(data.systemActions ?? [])]; // Extract saved object references for this rule const { references, params: updatedParams, - actions, + actions: actionsWithRefs, } = await withSpan({ name: 'extractReferences', type: 'rules' }, () => - extractReferences(context, ruleType, data.actions, validatedRuleTypeParams) + extractReferences(context, ruleType, allActions, validatedRuleTypeParams) ); const createTime = Date.now(); @@ -170,10 +184,12 @@ export async function createRule( const notifyWhen = getRuleNotifyWhenType(data.notifyWhen ?? null, data.throttle ?? null); const throttle = data.throttle ?? null; + const { systemActions, actions: actionToNotUse, ...restData } = data; // Convert domain rule object to ES rule attributes - const ruleAttributes = transformRuleDomainToRuleAttributes( - { - ...data, + const ruleAttributes = transformRuleDomainToRuleAttributes({ + actionsWithRefs, + rule: { + ...restData, // TODO (http-versioning) create a rule domain version of this function // Right now this works because the 2 types can interop but it's not ideal ...apiKeyAsRuleDomainProperties(createdAPIKey, username, isAuthTypeApiKey), @@ -192,13 +208,12 @@ export async function createRule( revision: 0, running: false, }, - { + params: { legacyId, - actionsWithRefs: actions, // @ts-expect-error upgrade typescript v4.9.5 paramsWithRefs: updatedParams, - } - ); + }, + }); const createdRuleSavedObject: SavedObject = await withSpan( { name: 'createRuleSavedObject', type: 'rules' }, @@ -221,7 +236,8 @@ export async function createRule( logger: context.logger, ruleType: context.ruleTypeRegistry.get(createdRuleSavedObject.attributes.alertTypeId), references, - } + }, + (connectorId: string) => actionsClient.isSystemAction(connectorId) ); // Try to validate created rule, but don't throw. diff --git a/x-pack/plugins/alerting/server/application/rule/methods/create/schemas/create_rule_data_schema.ts b/x-pack/plugins/alerting/server/application/rule/methods/create/schemas/create_rule_data_schema.ts index 9d1823381d17d..6538042dd4297 100644 --- a/x-pack/plugins/alerting/server/application/rule/methods/create/schemas/create_rule_data_schema.ts +++ b/x-pack/plugins/alerting/server/application/rule/methods/create/schemas/create_rule_data_schema.ts @@ -9,6 +9,30 @@ import { schema } from '@kbn/config-schema'; import { validateDuration } from '../../../validation'; import { notifyWhenSchema, actionAlertsFilterSchema, alertDelaySchema } from '../../../schemas'; +export const defaultActionSchema = schema.object({ + group: schema.string(), + id: schema.string(), + actionTypeId: schema.maybe(schema.string()), + params: schema.recordOf(schema.string(), schema.maybe(schema.any()), { defaultValue: {} }), + frequency: schema.maybe( + schema.object({ + summary: schema.boolean(), + notifyWhen: notifyWhenSchema, + throttle: schema.nullable(schema.string({ validate: validateDuration })), + }) + ), + uuid: schema.maybe(schema.string()), + alertsFilter: schema.maybe(actionAlertsFilterSchema), + useAlertDataForTemplate: schema.maybe(schema.boolean()), +}); + +export const systemActionSchema = schema.object({ + id: schema.string(), + actionTypeId: schema.maybe(schema.string()), + params: schema.recordOf(schema.string(), schema.maybe(schema.any()), { defaultValue: {} }), + uuid: schema.maybe(schema.string()), +}); + export const createRuleDataSchema = schema.object({ name: schema.string(), alertTypeId: schema.string(), @@ -20,24 +44,13 @@ export const createRuleDataSchema = schema.object({ schedule: schema.object({ interval: schema.string({ validate: validateDuration }), }), - actions: schema.arrayOf( - schema.object({ - group: schema.string(), - id: schema.string(), - actionTypeId: schema.maybe(schema.string()), - params: schema.recordOf(schema.string(), schema.maybe(schema.any()), { defaultValue: {} }), - frequency: schema.maybe( - schema.object({ - summary: schema.boolean(), - notifyWhen: notifyWhenSchema, - throttle: schema.nullable(schema.string({ validate: validateDuration })), - }) - ), - uuid: schema.maybe(schema.string()), - alertsFilter: schema.maybe(actionAlertsFilterSchema), - useAlertDataForTemplate: schema.maybe(schema.boolean()), - }), - { defaultValue: [] } + actions: schema.arrayOf(defaultActionSchema, { + defaultValue: [], + }), + systemActions: schema.maybe( + schema.arrayOf(systemActionSchema, { + defaultValue: [], + }) ), notifyWhen: schema.maybe(schema.nullable(notifyWhenSchema)), alertDelay: schema.maybe(alertDelaySchema), diff --git a/x-pack/plugins/alerting/server/application/rule/methods/create/schemas/index.ts b/x-pack/plugins/alerting/server/application/rule/methods/create/schemas/index.ts index 3c9154dcac7f2..9c0e20860bdc6 100644 --- a/x-pack/plugins/alerting/server/application/rule/methods/create/schemas/index.ts +++ b/x-pack/plugins/alerting/server/application/rule/methods/create/schemas/index.ts @@ -5,4 +5,8 @@ * 2.0. */ -export { createRuleDataSchema } from './create_rule_data_schema'; +export { + createRuleDataSchema, + defaultActionSchema, + systemActionSchema, +} from './create_rule_data_schema'; diff --git a/x-pack/plugins/alerting/server/application/rule/methods/create/types/create_rule_data.ts b/x-pack/plugins/alerting/server/application/rule/methods/create/types/create_rule_data.ts index abee30ec9a524..be9f40de388f2 100644 --- a/x-pack/plugins/alerting/server/application/rule/methods/create/types/create_rule_data.ts +++ b/x-pack/plugins/alerting/server/application/rule/methods/create/types/create_rule_data.ts @@ -6,10 +6,12 @@ */ import { TypeOf } from '@kbn/config-schema'; -import { createRuleDataSchema } from '../schemas'; +import { createRuleDataSchema, defaultActionSchema, systemActionSchema } from '../schemas'; import { RuleParams } from '../../../types'; type CreateRuleDataType = TypeOf; +type CreateRuleActionDataType = TypeOf; +type CreateRuleSystemActionDataType = TypeOf; export interface CreateRuleData { name: CreateRuleDataType['name']; @@ -20,7 +22,8 @@ export interface CreateRuleData { throttle?: CreateRuleDataType['throttle']; params: Params; schedule: CreateRuleDataType['schedule']; - actions: CreateRuleDataType['actions']; + actions: CreateRuleActionDataType[]; + systemActions?: CreateRuleSystemActionDataType[]; notifyWhen?: CreateRuleDataType['notifyWhen']; alertDelay?: CreateRuleDataType['alertDelay']; } diff --git a/x-pack/plugins/alerting/server/application/rule/methods/get_schedule_frequency/get_schedule_frequency.test.ts b/x-pack/plugins/alerting/server/application/rule/methods/get_schedule_frequency/get_schedule_frequency.test.ts index 03f23256041b6..a0b44abde3c54 100644 --- a/x-pack/plugins/alerting/server/application/rule/methods/get_schedule_frequency/get_schedule_frequency.test.ts +++ b/x-pack/plugins/alerting/server/application/rule/methods/get_schedule_frequency/get_schedule_frequency.test.ts @@ -21,6 +21,7 @@ import { actionsAuthorizationMock } from '@kbn/actions-plugin/server/mocks'; import { AlertingAuthorization } from '../../../../authorization/alerting_authorization'; import { ActionsAuthorization } from '@kbn/actions-plugin/server'; import { auditLoggerMock } from '@kbn/security-plugin/server/audit/mocks'; +import { ConnectorAdapterRegistry } from '../../../../connector_adapters/connector_adapter_registry'; const taskManager = taskManagerMock.createStart(); const ruleTypeRegistry = ruleTypeRegistryMock.create(); @@ -54,9 +55,11 @@ const rulesClientParams: jest.Mocked = { minimumScheduleInterval: { value: '1m', enforce: false }, isAuthenticationTypeAPIKey: jest.fn(), getAuthenticationAPIKey: jest.fn(), + connectorAdapterRegistry: new ConnectorAdapterRegistry(), getAlertIndicesAlias: jest.fn(), alertsService: null, uiSettings: uiSettingsServiceMock.createStartContract(), + isSystemAction: jest.fn(), }; const getMockAggregationResult = ( diff --git a/x-pack/plugins/alerting/server/application/rule/methods/resolve/resolve.test.ts b/x-pack/plugins/alerting/server/application/rule/methods/resolve/resolve.test.ts new file mode 100644 index 0000000000000..b611f5f2d3ef5 --- /dev/null +++ b/x-pack/plugins/alerting/server/application/rule/methods/resolve/resolve.test.ts @@ -0,0 +1,144 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor 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 { ConstructorOptions, RulesClient } from '../../../../rules_client/rules_client'; +import { + savedObjectsClientMock, + loggingSystemMock, + savedObjectsRepositoryMock, + uiSettingsServiceMock, +} from '@kbn/core/server/mocks'; +import { taskManagerMock } from '@kbn/task-manager-plugin/server/mocks'; +import { ruleTypeRegistryMock } from '../../../../rule_type_registry.mock'; +import { alertingAuthorizationMock } from '../../../../authorization/alerting_authorization.mock'; +import { encryptedSavedObjectsMock } from '@kbn/encrypted-saved-objects-plugin/server/mocks'; +import { actionsAuthorizationMock } from '@kbn/actions-plugin/server/mocks'; +import { AlertingAuthorization } from '../../../../authorization/alerting_authorization'; +import { ActionsAuthorization } from '@kbn/actions-plugin/server'; +import { auditLoggerMock } from '@kbn/security-plugin/server/audit/mocks'; +import { ConnectorAdapterRegistry } from '../../../../connector_adapters/connector_adapter_registry'; +import { RULE_SAVED_OBJECT_TYPE } from '../../../../saved_objects'; +import { getBeforeSetup } from '../../../../rules_client/tests/lib'; + +describe('resolve', () => { + const taskManager = taskManagerMock.createStart(); + const ruleTypeRegistry = ruleTypeRegistryMock.create(); + const unsecuredSavedObjectsClient = savedObjectsClientMock.create(); + const encryptedSavedObjects = encryptedSavedObjectsMock.createClient(); + const authorization = alertingAuthorizationMock.create(); + const actionsAuthorization = actionsAuthorizationMock.create(); + const auditLogger = auditLoggerMock.create(); + const internalSavedObjectsRepository = savedObjectsRepositoryMock.create(); + + const kibanaVersion = 'v8.2.0'; + const createAPIKeyMock = jest.fn(); + const isAuthenticationTypeApiKeyMock = jest.fn(); + const getAuthenticationApiKeyMock = jest.fn(); + + const rulesClientParams: jest.Mocked = { + taskManager, + ruleTypeRegistry, + unsecuredSavedObjectsClient, + authorization: authorization as unknown as AlertingAuthorization, + actionsAuthorization: actionsAuthorization as unknown as ActionsAuthorization, + spaceId: 'default', + namespace: 'default', + getUserName: jest.fn(), + createAPIKey: createAPIKeyMock, + logger: loggingSystemMock.create().get(), + internalSavedObjectsRepository, + encryptedSavedObjectsClient: encryptedSavedObjects, + getActionsClient: jest.fn(), + getEventLogClient: jest.fn(), + kibanaVersion, + auditLogger, + maxScheduledPerMinute: 10000, + minimumScheduleInterval: { value: '1m', enforce: false }, + isAuthenticationTypeAPIKey: isAuthenticationTypeApiKeyMock, + getAuthenticationAPIKey: getAuthenticationApiKeyMock, + connectorAdapterRegistry: new ConnectorAdapterRegistry(), + isSystemAction: jest.fn(), + getAlertIndicesAlias: jest.fn(), + alertsService: null, + uiSettings: uiSettingsServiceMock.createStartContract(), + }; + + let rulesClient: RulesClient; + + beforeEach(() => { + jest.clearAllMocks(); + getBeforeSetup(rulesClientParams, taskManager, ruleTypeRegistry); + rulesClient = new RulesClient(rulesClientParams); + }); + + describe('actions', () => { + it('transform actions correctly', async () => { + unsecuredSavedObjectsClient.resolve.mockResolvedValue({ + outcome: 'exactMatch', + saved_object: { + id: 'test-rule', + type: RULE_SAVED_OBJECT_TYPE, + attributes: { + alertTypeId: '123', + schedule: { interval: '10s' }, + params: { + bar: true, + }, + createdAt: new Date().toISOString(), + updatedAt: new Date().toISOString(), + actions: [ + { + frequency: { + notifyWhen: 'onActiveAlert' as const, + summary: false, + throttle: null, + }, + group: 'default', + params: {}, + actionRef: 'action_0', + actionTypeId: 'test-1', + uuid: '222', + }, + { + params: {}, + actionRef: 'system_action:system_action-id', + actionTypeId: 'test-2', + uuid: '222', + }, + ], + notifyWhen: 'onActiveAlert', + executionStatus: {}, + }, + references: [ + { + name: 'action_0', + type: 'action', + id: '1', + }, + ], + }, + }); + + const res = await rulesClient.resolve({ id: 'test-rule' }); + + expect(res.actions).toEqual([ + { + actionTypeId: 'test-1', + frequency: { notifyWhen: 'onActiveAlert', summary: false, throttle: null }, + group: 'default', + id: '1', + params: {}, + uuid: '222', + }, + ]); + + expect(res.systemActions).toEqual([ + { actionTypeId: 'test-2', id: 'system_action-id', params: {}, uuid: '222' }, + ]); + }); + }); +}); diff --git a/x-pack/plugins/alerting/server/application/rule/methods/resolve/resolve_rule.ts b/x-pack/plugins/alerting/server/application/rule/methods/resolve/resolve_rule.ts index de9f421a95924..359f3189958c5 100644 --- a/x-pack/plugins/alerting/server/application/rule/methods/resolve/resolve_rule.ts +++ b/x-pack/plugins/alerting/server/application/rule/methods/resolve/resolve_rule.ts @@ -66,13 +66,17 @@ Promise> { }) ); - const ruleDomain = transformRuleAttributesToRuleDomain(result.attributes, { - id: result.id, - logger: context.logger, - ruleType: context.ruleTypeRegistry.get(result.attributes.alertTypeId), - references: result.references, - includeSnoozeData, - }); + const ruleDomain = transformRuleAttributesToRuleDomain( + result.attributes, + { + id: result.id, + logger: context.logger, + ruleType: context.ruleTypeRegistry.get(result.attributes.alertTypeId), + references: result.references, + includeSnoozeData, + }, + context.isSystemAction + ); const rule = transformRuleDomainToRule(ruleDomain); diff --git a/x-pack/plugins/alerting/server/application/rule/methods/tags/get_rule_tags.test.ts b/x-pack/plugins/alerting/server/application/rule/methods/tags/get_rule_tags.test.ts index 6ae8285d7fa30..e6c99f779909d 100644 --- a/x-pack/plugins/alerting/server/application/rule/methods/tags/get_rule_tags.test.ts +++ b/x-pack/plugins/alerting/server/application/rule/methods/tags/get_rule_tags.test.ts @@ -23,6 +23,7 @@ import { auditLoggerMock } from '@kbn/security-plugin/server/audit/mocks'; import { getBeforeSetup } from '../../../../rules_client/tests/lib'; import { RecoveredActionGroup } from '../../../../../common'; import { RegistryRuleType } from '../../../../rule_type_registry'; +import { ConnectorAdapterRegistry } from '../../../../connector_adapters/connector_adapter_registry'; import { RULE_SAVED_OBJECT_TYPE } from '../../../../saved_objects'; const taskManager = taskManagerMock.createStart(); @@ -56,9 +57,11 @@ const rulesClientParams: jest.Mocked = { kibanaVersion, isAuthenticationTypeAPIKey: jest.fn(), getAuthenticationAPIKey: jest.fn(), + connectorAdapterRegistry: new ConnectorAdapterRegistry(), getAlertIndicesAlias: jest.fn(), alertsService: null, uiSettings: uiSettingsServiceMock.createStartContract(), + isSystemAction: jest.fn(), }; const listedTypes = new Set([ diff --git a/x-pack/plugins/alerting/server/application/rule/schemas/action_schemas.ts b/x-pack/plugins/alerting/server/application/rule/schemas/action_schemas.ts index 7cbadb6199081..fa60f230d6ab7 100644 --- a/x-pack/plugins/alerting/server/application/rule/schemas/action_schemas.ts +++ b/x-pack/plugins/alerting/server/application/rule/schemas/action_schemas.ts @@ -44,7 +44,7 @@ const actionFrequencySchema = schema.object({ /** * Unsanitized (domain) action schema, used by internal rules clients */ -export const actionDomainSchema = schema.object({ +export const defaultActionDomainSchema = schema.object({ uuid: schema.maybe(schema.string()), group: schema.string(), id: schema.string(), @@ -52,7 +52,14 @@ export const actionDomainSchema = schema.object({ params: actionParamsSchema, frequency: schema.maybe(actionFrequencySchema), alertsFilter: schema.maybe(actionDomainAlertsFilterSchema), - useAlertDataAsTemplate: schema.maybe(schema.boolean()), + useAlertDataForTemplate: schema.maybe(schema.boolean()), +}); + +export const systemActionDomainSchema = schema.object({ + id: schema.string(), + actionTypeId: schema.string(), + params: actionParamsSchema, + uuid: schema.maybe(schema.string()), }); export const actionAlertsFilterSchema = schema.object({ @@ -60,7 +67,7 @@ export const actionAlertsFilterSchema = schema.object({ timeframe: schema.maybe(actionAlertsFilterTimeFrameSchema), }); -export const actionSchema = schema.object({ +export const defaultActionSchema = schema.object({ uuid: schema.maybe(schema.string()), group: schema.string(), id: schema.string(), @@ -70,3 +77,11 @@ export const actionSchema = schema.object({ alertsFilter: schema.maybe(actionAlertsFilterSchema), useAlertDataForTemplate: schema.maybe(schema.boolean()), }); + +export const systemActionSchema = schema.object({ + id: schema.string(), + actionTypeId: schema.string(), + params: actionParamsSchema, + uuid: schema.maybe(schema.string()), + useAlertDataAsTemplate: schema.maybe(schema.boolean()), +}); diff --git a/x-pack/plugins/alerting/server/application/rule/schemas/index.ts b/x-pack/plugins/alerting/server/application/rule/schemas/index.ts index 06645e90d7baf..1c77fc9e43ee6 100644 --- a/x-pack/plugins/alerting/server/application/rule/schemas/index.ts +++ b/x-pack/plugins/alerting/server/application/rule/schemas/index.ts @@ -18,8 +18,8 @@ export { export { actionParamsSchema, - actionDomainSchema, - actionSchema, + defaultActionDomainSchema, + systemActionDomainSchema, actionAlertsFilterSchema, } from './action_schemas'; diff --git a/x-pack/plugins/alerting/server/application/rule/schemas/rule_schemas.ts b/x-pack/plugins/alerting/server/application/rule/schemas/rule_schemas.ts index 6041e475daf52..94676e9717052 100644 --- a/x-pack/plugins/alerting/server/application/rule/schemas/rule_schemas.ts +++ b/x-pack/plugins/alerting/server/application/rule/schemas/rule_schemas.ts @@ -15,7 +15,12 @@ import { import { rRuleSchema } from '../../r_rule/schemas'; import { dateSchema } from './date_schema'; import { notifyWhenSchema } from './notify_when_schema'; -import { actionDomainSchema, actionSchema } from './action_schemas'; +import { + defaultActionDomainSchema, + defaultActionSchema, + systemActionDomainSchema, + systemActionSchema, +} from './action_schemas'; export const ruleParamsSchema = schema.recordOf(schema.string(), schema.maybe(schema.any())); export const mappedParamsSchema = schema.recordOf(schema.string(), schema.maybe(schema.any())); @@ -147,7 +152,8 @@ export const ruleDomainSchema = schema.object({ alertTypeId: schema.string(), consumer: schema.string(), schedule: intervalScheduleSchema, - actions: schema.arrayOf(actionDomainSchema), + actions: schema.arrayOf(defaultActionDomainSchema), + systemActions: schema.maybe(schema.arrayOf(systemActionDomainSchema)), params: ruleParamsSchema, mapped_params: schema.maybe(mappedParamsSchema), scheduledTaskId: schema.maybe(schema.string()), @@ -186,7 +192,8 @@ export const ruleSchema = schema.object({ alertTypeId: schema.string(), consumer: schema.string(), schedule: intervalScheduleSchema, - actions: schema.arrayOf(actionSchema), + actions: schema.arrayOf(defaultActionSchema), + systemActions: schema.maybe(schema.arrayOf(systemActionSchema)), params: ruleParamsSchema, mapped_params: schema.maybe(mappedParamsSchema), scheduledTaskId: schema.maybe(schema.string()), diff --git a/x-pack/plugins/alerting/server/application/rule/transforms/index.ts b/x-pack/plugins/alerting/server/application/rule/transforms/index.ts index 69f2e1dc36844..d3933385ae898 100644 --- a/x-pack/plugins/alerting/server/application/rule/transforms/index.ts +++ b/x-pack/plugins/alerting/server/application/rule/transforms/index.ts @@ -8,3 +8,4 @@ export { transformRuleAttributesToRuleDomain } from './transform_rule_attributes_to_rule_domain'; export { transformRuleDomainToRuleAttributes } from './transform_rule_domain_to_rule_attributes'; export { transformRuleDomainToRule } from './transform_rule_domain_to_rule'; +export { transformRawActionsToDomainActions } from './transform_raw_actions_to_domain_actions'; diff --git a/x-pack/plugins/alerting/server/application/rule/transforms/transform_raw_actions_to_domain_actions.test.ts b/x-pack/plugins/alerting/server/application/rule/transforms/transform_raw_actions_to_domain_actions.test.ts new file mode 100644 index 0000000000000..591518e9b13ee --- /dev/null +++ b/x-pack/plugins/alerting/server/application/rule/transforms/transform_raw_actions_to_domain_actions.test.ts @@ -0,0 +1,91 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { RuleActionAttributes } from '../../../data/rule/types'; +import { + transformRawActionsToDomainActions, + transformRawActionsToDomainSystemActions, +} from './transform_raw_actions_to_domain_actions'; + +const defaultAction: RuleActionAttributes = { + group: 'default', + uuid: '1', + actionRef: 'default-action-ref', + actionTypeId: '.test', + params: {}, + frequency: { + summary: false, + notifyWhen: 'onThrottleInterval', + throttle: '1m', + }, + alertsFilter: { query: { kql: 'test:1', dsl: '{}', filters: [] } }, +}; + +const systemAction: RuleActionAttributes = { + actionRef: 'system_action:my-system-action-id', + uuid: '123', + actionTypeId: '.test-system-action', + params: {}, +}; + +const isSystemAction = (id: string) => id === 'my-system-action-id'; + +describe('transformRawActionsToDomainActions', () => { + it('transforms the actions correctly', () => { + const res = transformRawActionsToDomainActions({ + actions: [defaultAction, systemAction], + ruleId: 'test-rule', + references: [{ name: 'default-action-ref', id: 'default-action-id', type: 'action' }], + isSystemAction, + }); + + expect(res).toMatchInlineSnapshot(` + Array [ + Object { + "actionTypeId": ".test", + "alertsFilter": Object { + "query": Object { + "filters": Array [], + "kql": "test:1", + }, + }, + "frequency": Object { + "notifyWhen": "onThrottleInterval", + "summary": false, + "throttle": "1m", + }, + "group": "default", + "id": "default-action-id", + "params": Object {}, + "uuid": "1", + }, + ] + `); + }); +}); + +describe('transformRawActionsToDomainSystemActions', () => { + it('transforms the system actions correctly', () => { + const res = transformRawActionsToDomainSystemActions({ + actions: [defaultAction, systemAction], + ruleId: 'test-rule', + references: [{ name: 'default-action-ref', id: 'default-action-id', type: 'action' }], + isSystemAction, + }); + + expect(res).toMatchInlineSnapshot(` + Array [ + Object { + "actionTypeId": ".test-system-action", + "id": "my-system-action-id", + "params": Object {}, + "uuid": "123", + }, + ] + `); + }); +}); diff --git a/x-pack/plugins/alerting/server/application/rule/transforms/transform_raw_actions_to_domain_actions.ts b/x-pack/plugins/alerting/server/application/rule/transforms/transform_raw_actions_to_domain_actions.ts new file mode 100644 index 0000000000000..14f376c7d8176 --- /dev/null +++ b/x-pack/plugins/alerting/server/application/rule/transforms/transform_raw_actions_to_domain_actions.ts @@ -0,0 +1,86 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { omit } from 'lodash'; +import { SavedObjectReference } from '@kbn/core/server'; +import { injectReferencesIntoActions } from '../../../rules_client/common'; +import { RuleAttributes } from '../../../data/rule/types'; +import { RawRule } from '../../../types'; +import { RuleDomain } from '../types'; + +interface Args { + ruleId: string; + actions: RuleAttributes['actions'] | RawRule['actions']; + isSystemAction: (connectorId: string) => boolean; + omitGeneratedValues?: boolean; + references?: SavedObjectReference[]; +} + +export const transformRawActionsToDomainActions = ({ + actions, + ruleId, + references, + omitGeneratedValues = true, + isSystemAction, +}: Args): RuleDomain['actions'] => { + const actionsWithInjectedRefs = actions + ? injectReferencesIntoActions(ruleId, actions, references || []) + : []; + + const ruleDomainActions = actionsWithInjectedRefs + .filter((action) => !isSystemAction(action.id)) + .map((action) => { + const defaultAction = { + group: action.group ?? 'default', + id: action.id, + params: action.params, + actionTypeId: action.actionTypeId, + uuid: action.uuid, + ...(action.frequency ? { frequency: action.frequency } : {}), + ...(action.alertsFilter ? { alertsFilter: action.alertsFilter } : {}), + ...(action.useAlertDataAsTemplate + ? { useAlertDataAsTemplate: action.useAlertDataAsTemplate } + : {}), + }; + + if (omitGeneratedValues) { + return omit(defaultAction, 'alertsFilter.query.dsl'); + } + + return defaultAction; + }); + + return ruleDomainActions; +}; + +export const transformRawActionsToDomainSystemActions = ({ + actions, + ruleId, + references, + omitGeneratedValues = true, + isSystemAction, +}: Args): RuleDomain['systemActions'] => { + const actionsWithInjectedRefs = actions + ? injectReferencesIntoActions(ruleId, actions, references || []) + : []; + + const ruleDomainSystemActions = actionsWithInjectedRefs + .filter((action) => isSystemAction(action.id)) + .map((action) => { + return { + id: action.id, + params: action.params, + actionTypeId: action.actionTypeId, + uuid: action.uuid, + ...(action.useAlertDataAsTemplate + ? { useAlertDataAsTemplate: action.useAlertDataAsTemplate } + : {}), + }; + }); + + return ruleDomainSystemActions; +}; diff --git a/x-pack/plugins/alerting/server/application/rule/transforms/transform_rule_attributes_to_rule_domain.test.ts b/x-pack/plugins/alerting/server/application/rule/transforms/transform_rule_attributes_to_rule_domain.test.ts new file mode 100644 index 0000000000000..7b6fcb18ae219 --- /dev/null +++ b/x-pack/plugins/alerting/server/application/rule/transforms/transform_rule_attributes_to_rule_domain.test.ts @@ -0,0 +1,142 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { RecoveredActionGroup } from '../../../../common'; +import { loggingSystemMock } from '@kbn/core-logging-server-mocks'; +import { transformRuleAttributesToRuleDomain } from './transform_rule_attributes_to_rule_domain'; +import { UntypedNormalizedRuleType } from '../../../rule_type_registry'; +import { RuleActionAttributes } from '../../../data/rule/types'; + +const ruleType: jest.Mocked = { + id: 'test.rule-type', + name: 'My test rule', + actionGroups: [{ id: 'default', name: 'Default' }, RecoveredActionGroup], + defaultActionGroupId: 'default', + minimumLicenseRequired: 'basic', + isExportable: true, + recoveryActionGroup: RecoveredActionGroup, + executor: jest.fn(), + producer: 'alerts', + cancelAlertsOnRuleTimeout: true, + ruleTaskTimeout: '5m', + autoRecoverAlerts: true, + doesSetRecoveryContext: true, + validate: { + params: { validate: (params) => params }, + }, + alerts: { + context: 'test', + mappings: { fieldMap: { field: { type: 'keyword', required: false } } }, + shouldWrite: true, + }, + category: 'test', + validLegacyConsumers: [], +}; + +const defaultAction: RuleActionAttributes = { + group: 'default', + uuid: '1', + actionRef: 'default-action-ref', + actionTypeId: '.test', + params: {}, + frequency: { + summary: false, + notifyWhen: 'onThrottleInterval', + throttle: '1m', + }, + alertsFilter: { query: { kql: 'test:1', dsl: '{}', filters: [] } }, +}; + +const systemAction: RuleActionAttributes = { + actionRef: 'system_action:my-system-action-id', + uuid: '123', + actionTypeId: '.test-system-action', + params: {}, +}; + +const isSystemAction = (id: string) => id === 'my-system-action-id'; + +describe('transformRuleAttributesToRuleDomain', () => { + const MOCK_API_KEY = Buffer.from('123:abc').toString('base64'); + const logger = loggingSystemMock.create().get(); + const references = [{ name: 'default-action-ref', type: 'action', id: 'default-action-id' }]; + + const rule = { + enabled: false, + tags: ['foo'], + createdBy: 'user', + createdAt: '2019-02-12T21:01:22.479Z', + updatedAt: '2019-02-12T21:01:22.479Z', + legacyId: null, + muteAll: false, + mutedInstanceIds: [], + snoozeSchedule: [], + alertTypeId: 'myType', + schedule: { interval: '1m' }, + consumer: 'myApp', + scheduledTaskId: 'task-123', + executionStatus: { + lastExecutionDate: '2019-02-12T21:01:22.479Z', + status: 'pending' as const, + }, + params: {}, + throttle: null, + notifyWhen: null, + actions: [defaultAction, systemAction], + name: 'my rule name', + revision: 0, + updatedBy: 'user', + apiKey: MOCK_API_KEY, + apiKeyOwner: 'user', + }; + + it('transforms the actions correctly', () => { + const res = transformRuleAttributesToRuleDomain( + rule, + { + id: '1', + logger, + ruleType, + references, + }, + isSystemAction + ); + + expect(res.actions).toMatchInlineSnapshot(` + Array [ + Object { + "actionTypeId": ".test", + "alertsFilter": Object { + "query": Object { + "filters": Array [], + "kql": "test:1", + }, + }, + "frequency": Object { + "notifyWhen": "onThrottleInterval", + "summary": false, + "throttle": "1m", + }, + "group": "default", + "id": "default-action-id", + "params": Object {}, + "uuid": "1", + }, + ] + `); + expect(res.systemActions).toMatchInlineSnapshot(` + Array [ + Object { + "actionTypeId": ".test-system-action", + "id": "my-system-action-id", + "params": Object {}, + "uuid": "123", + }, + ] + `); + }); +}); diff --git a/x-pack/plugins/alerting/server/application/rule/transforms/transform_rule_attributes_to_rule_domain.ts b/x-pack/plugins/alerting/server/application/rule/transforms/transform_rule_attributes_to_rule_domain.ts index 9cc8131b1fa2d..41e251ff47c3e 100644 --- a/x-pack/plugins/alerting/server/application/rule/transforms/transform_rule_attributes_to_rule_domain.ts +++ b/x-pack/plugins/alerting/server/application/rule/transforms/transform_rule_attributes_to_rule_domain.ts @@ -4,20 +4,21 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ -import { omit, isEmpty } from 'lodash'; +import { isEmpty } from 'lodash'; import { Logger } from '@kbn/core/server'; import { SavedObjectReference } from '@kbn/core/server'; import { ruleExecutionStatusValues } from '../constants'; import { getRuleSnoozeEndTime } from '../../../lib'; import { RuleDomain, Monitoring, RuleParams } from '../types'; import { RuleAttributes } from '../../../data/rule/types'; -import { RawRule, PartialRule } from '../../../types'; +import { PartialRule } from '../../../types'; import { UntypedNormalizedRuleType } from '../../../rule_type_registry'; -import { - injectReferencesIntoActions, - injectReferencesIntoParams, -} from '../../../rules_client/common'; +import { injectReferencesIntoParams } from '../../../rules_client/common'; import { getActiveScheduledSnoozes } from '../../../lib/is_rule_snoozed'; +import { + transformRawActionsToDomainActions, + transformRawActionsToDomainSystemActions, +} from './transform_raw_actions_to_domain_actions'; const INITIAL_LAST_RUN_METRICS = { duration: 0, @@ -120,7 +121,8 @@ interface TransformEsToRuleParams { export const transformRuleAttributesToRuleDomain = ( esRule: RuleAttributes, - transformParams: TransformEsToRuleParams + transformParams: TransformEsToRuleParams, + isSystemAction: (connectorId: string) => boolean ): RuleDomain => { const { scheduledTaskId, executionStatus, monitoring, snoozeSchedule, lastRun } = esRule; @@ -141,6 +143,7 @@ export const transformRuleAttributesToRuleDomain = omit(ruleAction, 'alertsFilter.query.dsl')); - } + const ruleDomainActions: RuleDomain['actions'] = transformRawActionsToDomainActions({ + ruleId: id, + actions: esRule.actions, + references, + isSystemAction, + omitGeneratedValues, + }); + const ruleDomainSystemActions: RuleDomain['systemActions'] = + transformRawActionsToDomainSystemActions({ + ruleId: id, + actions: esRule.actions, + references, + isSystemAction, + omitGeneratedValues, + }); const params = injectReferencesIntoParams( id, @@ -177,7 +188,8 @@ export const transformRuleAttributesToRuleDomain = ( consumer: ruleDomain.consumer, schedule: ruleDomain.schedule, actions: ruleDomain.actions, + systemActions: ruleDomain.systemActions, params: ruleDomain.params, mapped_params: ruleDomain.mapped_params, scheduledTaskId: ruleDomain.scheduledTaskId, diff --git a/x-pack/plugins/alerting/server/application/rule/transforms/transform_rule_domain_to_rule_attributes.ts b/x-pack/plugins/alerting/server/application/rule/transforms/transform_rule_domain_to_rule_attributes.ts index e527bd6f31df0..0e70525981496 100644 --- a/x-pack/plugins/alerting/server/application/rule/transforms/transform_rule_domain_to_rule_attributes.ts +++ b/x-pack/plugins/alerting/server/application/rule/transforms/transform_rule_domain_to_rule_attributes.ts @@ -7,19 +7,24 @@ import { RuleDomain } from '../types'; import { RuleAttributes } from '../../../data/rule/types'; import { getMappedParams } from '../../../rules_client/common'; +import { DenormalizedAction } from '../../../rules_client'; interface TransformRuleToEsParams { legacyId: RuleAttributes['legacyId']; - actionsWithRefs: RuleAttributes['actions']; paramsWithRefs: RuleAttributes['params']; meta?: RuleAttributes['meta']; } -export const transformRuleDomainToRuleAttributes = ( - rule: Omit, - params: TransformRuleToEsParams -): RuleAttributes => { - const { legacyId, actionsWithRefs, paramsWithRefs, meta } = params; +export const transformRuleDomainToRuleAttributes = ({ + actionsWithRefs, + rule, + params, +}: { + actionsWithRefs: DenormalizedAction[]; + rule: Omit; + params: TransformRuleToEsParams; +}): RuleAttributes => { + const { legacyId, paramsWithRefs, meta } = params; const mappedParams = getMappedParams(paramsWithRefs); return { diff --git a/x-pack/plugins/alerting/server/application/rule/types/rule.ts b/x-pack/plugins/alerting/server/application/rule/types/rule.ts index f59056b382440..16b4a079862eb 100644 --- a/x-pack/plugins/alerting/server/application/rule/types/rule.ts +++ b/x-pack/plugins/alerting/server/application/rule/types/rule.ts @@ -12,7 +12,6 @@ import { ruleExecutionStatusValues, ruleExecutionStatusErrorReason, ruleExecutionStatusWarningReason, - filterStateStore, } from '../constants'; import { ruleParamsSchema, @@ -20,7 +19,6 @@ import { ruleExecutionStatusSchema, ruleLastRunSchema, monitoringSchema, - actionSchema, ruleSchema, ruleDomainSchema, } from '../schemas'; @@ -34,13 +32,11 @@ export type RuleExecutionStatusErrorReason = typeof ruleExecutionStatusErrorReason[keyof typeof ruleExecutionStatusErrorReason]; export type RuleExecutionStatusWarningReason = typeof ruleExecutionStatusWarningReason[keyof typeof ruleExecutionStatusWarningReason]; -export type FilterStateStore = typeof filterStateStore[keyof typeof filterStateStore]; export type RuleParams = TypeOf; export type RuleSnoozeSchedule = TypeOf; export type RuleLastRun = TypeOf; export type Monitoring = TypeOf; -export type Action = TypeOf; type RuleSchemaType = TypeOf; type RuleDomainSchemaType = TypeOf; @@ -62,6 +58,7 @@ export interface Rule { consumer: RuleSchemaType['consumer']; schedule: RuleSchemaType['schedule']; actions: RuleSchemaType['actions']; + systemActions?: RuleSchemaType['systemActions']; params: Params; mapped_params?: RuleSchemaType['mapped_params']; scheduledTaskId?: RuleSchemaType['scheduledTaskId']; @@ -97,6 +94,7 @@ export interface RuleDomain { consumer: RuleDomainSchemaType['consumer']; schedule: RuleDomainSchemaType['schedule']; actions: RuleDomainSchemaType['actions']; + systemActions: RuleDomainSchemaType['systemActions']; params: Params; mapped_params?: RuleDomainSchemaType['mapped_params']; scheduledTaskId?: RuleDomainSchemaType['scheduledTaskId']; diff --git a/x-pack/plugins/alerting/server/connector_adapters/connector_adapter_registry.test.ts b/x-pack/plugins/alerting/server/connector_adapters/connector_adapter_registry.test.ts new file mode 100644 index 0000000000000..2798a55268f9b --- /dev/null +++ b/x-pack/plugins/alerting/server/connector_adapters/connector_adapter_registry.test.ts @@ -0,0 +1,63 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { schema } from '@kbn/config-schema'; +import { ConnectorAdapterRegistry } from './connector_adapter_registry'; +import type { ConnectorAdapter } from './types'; + +describe('ConnectorAdapterRegistry', () => { + const connectorAdapter: ConnectorAdapter = { + connectorTypeId: '.test', + ruleActionParamsSchema: schema.object({}), + buildActionParams: jest.fn(), + }; + + let registry: ConnectorAdapterRegistry; + + beforeEach(() => { + registry = new ConnectorAdapterRegistry(); + }); + + describe('has', () => { + it('returns true if the connector adapter is registered', () => { + registry.register(connectorAdapter); + expect(registry.has('.test')).toBe(true); + }); + + it('returns false if the connector adapter is not registered', () => { + expect(registry.has('.not-exist')).toBe(false); + }); + }); + + describe('register', () => { + it('registers a connector adapter correctly', () => { + registry.register(connectorAdapter); + expect(registry.get('.test')).toEqual(connectorAdapter); + }); + + it('throws an error if the connector adapter exists', () => { + registry.register(connectorAdapter); + + expect(() => registry.register(connectorAdapter)).toThrowErrorMatchingInlineSnapshot( + `".test is already registered to the ConnectorAdapterRegistry"` + ); + }); + }); + + describe('get', () => { + it('gets a connector adapter correctly', () => { + registry.register(connectorAdapter); + expect(registry.get('.test')).toEqual(connectorAdapter); + }); + + it('throws an error if the connector adapter does not exists', () => { + expect(() => registry.get('.not-exists')).toThrowErrorMatchingInlineSnapshot( + `"Connector adapter \\".not-exists\\" is not registered."` + ); + }); + }); +}); diff --git a/x-pack/plugins/alerting/server/connector_adapters/connector_adapter_registry.ts b/x-pack/plugins/alerting/server/connector_adapters/connector_adapter_registry.ts new file mode 100644 index 0000000000000..4558ca5a2d3ee --- /dev/null +++ b/x-pack/plugins/alerting/server/connector_adapters/connector_adapter_registry.ts @@ -0,0 +1,53 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import Boom from '@hapi/boom'; +import { i18n } from '@kbn/i18n'; + +import { ConnectorAdapter, ConnectorAdapterParams } from './types'; + +export class ConnectorAdapterRegistry { + private readonly connectorAdapters: Map = new Map(); + + public has(connectorTypeId: string): boolean { + return this.connectorAdapters.has(connectorTypeId); + } + + public register< + RuleActionParams extends ConnectorAdapterParams = ConnectorAdapterParams, + ConnectorParams extends ConnectorAdapterParams = ConnectorAdapterParams + >(connectorAdapter: ConnectorAdapter) { + if (this.has(connectorAdapter.connectorTypeId)) { + throw new Error( + `${connectorAdapter.connectorTypeId} is already registered to the ConnectorAdapterRegistry` + ); + } + + this.connectorAdapters.set( + connectorAdapter.connectorTypeId, + connectorAdapter as unknown as ConnectorAdapter + ); + } + + public get(connectorTypeId: string): ConnectorAdapter { + if (!this.connectorAdapters.has(connectorTypeId)) { + throw Boom.badRequest( + i18n.translate( + 'xpack.alerting.connectorAdapterRegistry.get.missingConnectorAdapterErrorMessage', + { + defaultMessage: 'Connector adapter "{connectorTypeId}" is not registered.', + values: { + connectorTypeId, + }, + } + ) + ); + } + + return this.connectorAdapters.get(connectorTypeId)!; + } +} diff --git a/x-pack/plugins/alerting/server/connector_adapters/types.ts b/x-pack/plugins/alerting/server/connector_adapters/types.ts new file mode 100644 index 0000000000000..e2bd2d7ed2c93 --- /dev/null +++ b/x-pack/plugins/alerting/server/connector_adapters/types.ts @@ -0,0 +1,41 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { ObjectType } from '@kbn/config-schema'; +import type { RuleTypeParams, SanitizedRule } from '../../common'; +import { CombinedSummarizedAlerts } from '../types'; + +type Rule = Pick, 'id' | 'name' | 'tags'>; + +export interface ConnectorAdapterParams { + [x: string]: unknown; +} + +interface BuildActionParamsArgs { + alerts: CombinedSummarizedAlerts; + rule: Rule; + params: RuleActionParams; + spaceId: string; + ruleUrl?: string; +} + +export interface ConnectorAdapter< + RuleActionParams extends ConnectorAdapterParams = ConnectorAdapterParams, + ConnectorParams extends ConnectorAdapterParams = ConnectorAdapterParams +> { + connectorTypeId: string; + /** + * The schema of the action persisted + * in the rule. The schema will be validated + * when a rule is created or updated. + * The schema should be backwards compatible + * and should never introduce any breaking + * changes. + */ + ruleActionParamsSchema: ObjectType; + buildActionParams: (args: BuildActionParamsArgs) => ConnectorParams; +} diff --git a/x-pack/plugins/alerting/server/connector_adapters/validate_rule_action_params.test.ts b/x-pack/plugins/alerting/server/connector_adapters/validate_rule_action_params.test.ts new file mode 100644 index 0000000000000..42e7379dc0700 --- /dev/null +++ b/x-pack/plugins/alerting/server/connector_adapters/validate_rule_action_params.test.ts @@ -0,0 +1,92 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { schema } from '@kbn/config-schema'; +import { ConnectorAdapterRegistry } from './connector_adapter_registry'; +import type { ConnectorAdapter } from './types'; +import { + bulkValidateConnectorAdapterActionParams, + validateConnectorAdapterActionParams, +} from './validate_rule_action_params'; + +describe('validateRuleActionParams', () => { + const firstConnectorAdapter: ConnectorAdapter = { + connectorTypeId: '.test', + ruleActionParamsSchema: schema.object({ foo: schema.string() }), + buildActionParams: jest.fn(), + }; + + const secondConnectorAdapter: ConnectorAdapter = { + connectorTypeId: '.test-2', + ruleActionParamsSchema: schema.object({ bar: schema.string() }), + buildActionParams: jest.fn(), + }; + + let registry: ConnectorAdapterRegistry; + + beforeEach(() => { + registry = new ConnectorAdapterRegistry(); + }); + + describe('validateConnectorAdapterActionParams', () => { + it('should validate correctly invalid params', () => { + registry.register(firstConnectorAdapter); + + expect(() => + validateConnectorAdapterActionParams({ + connectorAdapterRegistry: registry, + connectorTypeId: firstConnectorAdapter.connectorTypeId, + params: { foo: 5 }, + }) + ).toThrowErrorMatchingInlineSnapshot( + `"Invalid system action params. System action type: .test - [foo]: expected value of type [string] but got [number]"` + ); + }); + + it('should not throw if the connectorTypeId is not defined', () => { + registry.register(firstConnectorAdapter); + + expect(() => + validateConnectorAdapterActionParams({ + connectorAdapterRegistry: registry, + params: {}, + }) + ).not.toThrow(); + }); + + it('should not throw if the connector adapter is not registered', () => { + expect(() => + validateConnectorAdapterActionParams({ + connectorAdapterRegistry: registry, + connectorTypeId: firstConnectorAdapter.connectorTypeId, + params: {}, + }) + ).not.toThrow(); + }); + }); + + describe('bulkValidateConnectorAdapterActionParams', () => { + it('should validate correctly invalid params with multiple actions', () => { + const actions = [ + { actionTypeId: firstConnectorAdapter.connectorTypeId, params: { foo: 5 } }, + { actionTypeId: secondConnectorAdapter.connectorTypeId, params: { bar: 'test' } }, + ]; + + registry.register(firstConnectorAdapter); + registry.register(secondConnectorAdapter); + + expect(() => + bulkValidateConnectorAdapterActionParams({ + connectorAdapterRegistry: registry, + actions, + }) + ).toThrowErrorMatchingInlineSnapshot( + `"Invalid system action params. System action type: .test - [foo]: expected value of type [string] but got [number]"` + ); + }); + }); +}); diff --git a/x-pack/plugins/alerting/server/connector_adapters/validate_rule_action_params.ts b/x-pack/plugins/alerting/server/connector_adapters/validate_rule_action_params.ts new file mode 100644 index 0000000000000..abfd56f0e9079 --- /dev/null +++ b/x-pack/plugins/alerting/server/connector_adapters/validate_rule_action_params.ts @@ -0,0 +1,58 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import Boom from '@hapi/boom'; +import { ConnectorAdapterRegistry } from './connector_adapter_registry'; + +interface ValidateSchemaArgs { + connectorAdapterRegistry: ConnectorAdapterRegistry; + connectorTypeId?: string; + params: Record; +} + +interface BulkValidateSchemaArgs { + connectorAdapterRegistry: ConnectorAdapterRegistry; + actions: Array<{ actionTypeId: string; params: Record }>; +} + +export const validateConnectorAdapterActionParams = ({ + connectorAdapterRegistry, + connectorTypeId, + params, +}: ValidateSchemaArgs) => { + if (!connectorTypeId) { + return; + } + + if (!connectorAdapterRegistry.has(connectorTypeId)) { + return; + } + + const connectorAdapter = connectorAdapterRegistry.get(connectorTypeId); + const schema = connectorAdapter.ruleActionParamsSchema; + + try { + schema.validate(params); + } catch (error) { + throw Boom.badRequest( + `Invalid system action params. System action type: ${connectorAdapter.connectorTypeId} - ${error.message}` + ); + } +}; + +export const bulkValidateConnectorAdapterActionParams = ({ + connectorAdapterRegistry, + actions, +}: BulkValidateSchemaArgs) => { + for (const action of actions) { + validateConnectorAdapterActionParams({ + connectorAdapterRegistry, + connectorTypeId: action.actionTypeId, + params: action.params, + }); + } +}; diff --git a/x-pack/plugins/alerting/server/data/alerts_filter_query/types/alerts_filter_query_attributes.ts b/x-pack/plugins/alerting/server/data/alerts_filter_query/types/alerts_filter_query_attributes.ts index f740d8070abf2..33742f81daa77 100644 --- a/x-pack/plugins/alerting/server/data/alerts_filter_query/types/alerts_filter_query_attributes.ts +++ b/x-pack/plugins/alerting/server/data/alerts_filter_query/types/alerts_filter_query_attributes.ts @@ -5,7 +5,7 @@ * 2.0. */ -import type { FilterStateStore } from '../constants'; +import { FilterStateStore } from '@kbn/es-query'; export interface AlertsFilterAttributes { query?: Record; @@ -18,5 +18,5 @@ export interface AlertsFilterAttributes { export interface AlertsFilterQueryAttributes { kql: string; filters: AlertsFilterAttributes[]; - dsl?: string; + dsl: string; } diff --git a/x-pack/plugins/alerting/server/data/rule/types/rule_attributes.ts b/x-pack/plugins/alerting/server/data/rule/types/rule_attributes.ts index e047be1b9cddf..151c71b7d79f4 100644 --- a/x-pack/plugins/alerting/server/data/rule/types/rule_attributes.ts +++ b/x-pack/plugins/alerting/server/data/rule/types/rule_attributes.ts @@ -114,14 +114,14 @@ interface AlertsFilterTimeFrameAttributes { }; } -interface AlertsFilterAttributes { +export interface AlertsFilterAttributes { query?: AlertsFilterQueryAttributes; timeframe?: AlertsFilterTimeFrameAttributes; } export interface RuleActionAttributes { uuid: string; - group: string; + group?: string; actionRef: string; actionTypeId: string; params: SavedObjectAttributes; @@ -131,6 +131,7 @@ export interface RuleActionAttributes { throttle: string | null; }; alertsFilter?: AlertsFilterAttributes; + useAlertDataAsTemplate?: boolean; } type MappedParamsAttributes = SavedObjectAttributes & { diff --git a/x-pack/plugins/alerting/server/index.ts b/x-pack/plugins/alerting/server/index.ts index d27e6976b8b7a..c6746d33df67c 100644 --- a/x-pack/plugins/alerting/server/index.ts +++ b/x-pack/plugins/alerting/server/index.ts @@ -72,6 +72,7 @@ export { } from './alerts_service'; export { sanitizeBulkErrorResponse, AlertsClientError } from './alerts_client'; export { getDataStreamAdapter } from './alerts_service/lib/data_stream_adapter'; +export type { ConnectorAdapter } from './connector_adapters/types'; export const plugin = async (initContext: PluginInitializerContext) => { const { AlertingPlugin } = await import('./plugin'); 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 60ddb03c7c68a..60636e194e194 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 @@ -55,7 +55,7 @@ interface AlertOpts { maintenanceWindowIds?: string[]; } -interface ActionOpts { +export interface ActionOpts { id: string; typeId: string; alertId?: string; diff --git a/x-pack/plugins/alerting/server/lib/validate_system_actions.test.ts b/x-pack/plugins/alerting/server/lib/validate_system_actions.test.ts new file mode 100644 index 0000000000000..7cc7b4041aa08 --- /dev/null +++ b/x-pack/plugins/alerting/server/lib/validate_system_actions.test.ts @@ -0,0 +1,211 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor 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 { ActionsClient } from '@kbn/actions-plugin/server'; +import { actionsClientMock } from '@kbn/actions-plugin/server/mocks'; +import { schema } from '@kbn/config-schema'; +import { ConnectorAdapterRegistry } from '../connector_adapters/connector_adapter_registry'; +import { ConnectorAdapter } from '../connector_adapters/types'; +import { NormalizedSystemAction } from '../rules_client'; +import { RuleSystemAction } from '../types'; +import { validateSystemActions } from './validate_system_actions'; + +describe('validateSystemActionsWithoutRuleTypeId', () => { + const connectorAdapter: ConnectorAdapter = { + connectorTypeId: '.test', + ruleActionParamsSchema: schema.object({ foo: schema.string() }), + buildActionParams: jest.fn(), + }; + + let registry: ConnectorAdapterRegistry; + let actionsClient: jest.Mocked; + + beforeEach(() => { + registry = new ConnectorAdapterRegistry(); + actionsClient = actionsClientMock.create(); + actionsClient.getBulk.mockResolvedValue([ + { + id: 'system_action-id', + actionTypeId: '.test', + config: {}, + isMissingSecrets: false, + name: 'system action connector', + isPreconfigured: false, + isDeprecated: false, + isSystemAction: true, + }, + ]); + }); + + it('should not validate with empty system actions', async () => { + const res = await validateSystemActions({ + connectorAdapterRegistry: registry, + systemActions: [], + actionsClient, + }); + + expect(res).toBe(undefined); + expect(actionsClient.getBulk).not.toBeCalled(); + expect(actionsClient.isSystemAction).not.toBeCalled(); + }); + + it('should throw an error if the action is not a system action even if it is declared as one', async () => { + const systemActions: RuleSystemAction[] = [ + { + id: 'not-exist', + uuid: '123', + params: { foo: 'test' }, + actionTypeId: '.test', + }, + ]; + + registry.register(connectorAdapter); + + actionsClient.isSystemAction.mockReturnValue(false); + + await expect(() => + validateSystemActions({ + connectorAdapterRegistry: registry, + systemActions, + actionsClient, + }) + ).rejects.toThrowErrorMatchingInlineSnapshot(`"Action not-exist is not a system action"`); + }); + + it('should throw an error if the action is system action but is not returned from the actions client (getBulk)', async () => { + const systemActions: RuleSystemAction[] = [ + { + id: 'not-exist', + uuid: '123', + params: { foo: 'test' }, + actionTypeId: '.test', + }, + ]; + + registry.register(connectorAdapter); + + actionsClient.isSystemAction.mockReturnValue(true); + + await expect(() => + validateSystemActions({ + connectorAdapterRegistry: registry, + systemActions, + actionsClient, + }) + ).rejects.toThrowErrorMatchingInlineSnapshot(`"Action not-exist is not a system action"`); + }); + + it('should throw an error if the params are not valid', async () => { + const systemActions: RuleSystemAction[] = [ + { + id: 'system_action-id', + uuid: '123', + params: { 'not-exist': 'test' }, + actionTypeId: '.test', + }, + ]; + + registry.register(connectorAdapter); + + actionsClient.isSystemAction.mockReturnValue(true); + + await expect(() => + validateSystemActions({ + connectorAdapterRegistry: registry, + systemActions, + actionsClient, + }) + ).rejects.toThrowErrorMatchingInlineSnapshot( + `"Invalid system action params. System action type: .test - [foo]: expected value of type [string] but got [undefined]"` + ); + }); + + it('should throw an error if the same system action is being used', async () => { + const systemActions: RuleSystemAction[] = [ + { + id: 'system_action-id', + uuid: '123', + params: { foo: 'test' }, + actionTypeId: '.test', + }, + { + id: 'system_action-id', + uuid: '123', + params: { foo: 'test' }, + actionTypeId: '.test', + }, + ]; + + registry.register(connectorAdapter); + + actionsClient.isSystemAction.mockReturnValue(false); + + await expect(() => + validateSystemActions({ + connectorAdapterRegistry: registry, + systemActions, + actionsClient, + }) + ).rejects.toThrowErrorMatchingInlineSnapshot(`"Cannot use the same system action twice"`); + }); + + it('should call getBulk correctly', async () => { + const systemActions: Array = [ + { + id: 'system_action-id', + uuid: '123', + params: { foo: 'test' }, + }, + { + id: 'system_action-id-2', + uuid: '123', + params: { foo: 'test' }, + actionTypeId: '.test', + }, + ]; + + actionsClient.getBulk.mockResolvedValue([ + { + id: 'system_action-id', + actionTypeId: '.test', + config: {}, + isMissingSecrets: false, + name: 'system action connector', + isPreconfigured: false, + isDeprecated: false, + isSystemAction: true, + }, + { + id: 'system_action-id-2', + actionTypeId: '.test', + config: {}, + isMissingSecrets: false, + name: 'system action connector 2', + isPreconfigured: false, + isDeprecated: false, + isSystemAction: true, + }, + ]); + + registry.register(connectorAdapter); + + actionsClient.isSystemAction.mockReturnValue(true); + + const res = await validateSystemActions({ + connectorAdapterRegistry: registry, + systemActions, + actionsClient, + }); + + expect(res).toBe(undefined); + + expect(actionsClient.getBulk).toBeCalledWith({ + ids: ['system_action-id', 'system_action-id-2'], + throwIfSystemAction: false, + }); + }); +}); diff --git a/x-pack/plugins/alerting/server/lib/validate_system_actions.ts b/x-pack/plugins/alerting/server/lib/validate_system_actions.ts new file mode 100644 index 0000000000000..190b695efffd7 --- /dev/null +++ b/x-pack/plugins/alerting/server/lib/validate_system_actions.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 Boom from '@hapi/boom'; +import { ActionsClient } from '@kbn/actions-plugin/server'; +import { ConnectorAdapterRegistry } from '../connector_adapters/connector_adapter_registry'; +import { bulkValidateConnectorAdapterActionParams } from '../connector_adapters/validate_rule_action_params'; +import { NormalizedSystemAction } from '../rules_client'; +import { RuleSystemAction } from '../types'; +interface Params { + actionsClient: ActionsClient; + connectorAdapterRegistry: ConnectorAdapterRegistry; + systemActions: Array; +} + +export const validateSystemActions = async ({ + actionsClient, + connectorAdapterRegistry, + systemActions = [], +}: Params) => { + if (systemActions.length === 0) { + return; + } + + /** + * When updating or creating a rule the actions may not contain + * the actionTypeId. We need to getBulk using the + * actionsClient to get the actionTypeId of each action. + * The actionTypeId is needed to get the schema of + * the action params using the connector adapter registry + */ + const actionIds: Set = new Set(systemActions.map((action) => action.id)); + + if (actionIds.size !== systemActions.length) { + throw Boom.badRequest('Cannot use the same system action twice'); + } + + const actionResults = await actionsClient.getBulk({ + ids: Array.from(actionIds), + throwIfSystemAction: false, + }); + + const systemActionsWithActionTypeId: RuleSystemAction[] = []; + + for (const systemAction of systemActions) { + const isSystemAction = actionsClient.isSystemAction(systemAction.id); + const foundAction = actionResults.find((actionRes) => actionRes.id === systemAction.id); + + if (!isSystemAction || !foundAction) { + throw Boom.badRequest(`Action ${systemAction.id} is not a system action`); + } + + systemActionsWithActionTypeId.push({ + ...systemAction, + actionTypeId: foundAction.actionTypeId, + }); + } + + bulkValidateConnectorAdapterActionParams({ + connectorAdapterRegistry, + actions: systemActionsWithActionTypeId, + }); +}; diff --git a/x-pack/plugins/alerting/server/mocks.ts b/x-pack/plugins/alerting/server/mocks.ts index ab81e472f938b..1abd11703dd4f 100644 --- a/x-pack/plugins/alerting/server/mocks.ts +++ b/x-pack/plugins/alerting/server/mocks.ts @@ -36,6 +36,7 @@ const createSetupMock = () => { getContextInitializationPromise: jest.fn(), }, getDataStreamAdapter: jest.fn(), + registerConnectorAdapter: jest.fn(), }; return mock; }; diff --git a/x-pack/plugins/alerting/server/plugin.test.ts b/x-pack/plugins/alerting/server/plugin.test.ts index 010fa5bfab6ea..acbad4a27ffe8 100644 --- a/x-pack/plugins/alerting/server/plugin.test.ts +++ b/x-pack/plugins/alerting/server/plugin.test.ts @@ -27,6 +27,8 @@ import { PluginSetup as DataPluginSetup, } from '@kbn/data-plugin/server'; import { spacesMock } from '@kbn/spaces-plugin/server/mocks'; +import { schema } from '@kbn/config-schema'; +import { serverlessPluginMock } from '@kbn/serverless/server/mocks'; import { AlertsService } from './alerts_service/alerts_service'; import { alertsServiceMock } from './alerts_service/alerts_service.mock'; @@ -37,7 +39,6 @@ jest.mock('./alerts_service/alerts_service', () => ({ import { SharePluginStart } from '@kbn/share-plugin/server'; import { dataViewPluginMocks } from '@kbn/data-views-plugin/public/mocks'; import { generateAlertingConfig } from './test_utils'; -import { serverlessPluginMock } from '@kbn/serverless/server/mocks'; const sampleRuleType: RuleType = { id: 'test', @@ -240,6 +241,32 @@ describe('Alerting Plugin', () => { expect(ruleType.cancelAlertsOnRuleTimeout).toBe(false); }); }); + + describe('registerConnectorAdapter()', () => { + let setup: PluginSetupContract; + + beforeEach(async () => { + const context = coreMock.createPluginInitializerContext( + generateAlertingConfig() + ); + + plugin = new AlertingPlugin(context); + setup = await plugin.setup(setupMocks, mockPlugins); + }); + + it('should register a connector adapter', () => { + const adapter = { + connectorTypeId: '.test', + ruleActionParamsSchema: schema.object({}), + buildActionParams: jest.fn(), + }; + + setup.registerConnectorAdapter(adapter); + + // @ts-expect-error: private properties cannot be accessed + expect(plugin.connectorAdapterRegistry.get('.test')).toEqual(adapter); + }); + }); }); describe('start()', () => { diff --git a/x-pack/plugins/alerting/server/plugin.ts b/x-pack/plugins/alerting/server/plugin.ts index f5850abf5f99f..820670a63e21c 100644 --- a/x-pack/plugins/alerting/server/plugin.ts +++ b/x-pack/plugins/alerting/server/plugin.ts @@ -99,6 +99,8 @@ import { } from './alerts_service'; import { getRulesSettingsFeature } from './rules_settings_feature'; import { maintenanceWindowFeature } from './maintenance_window_feature'; +import { ConnectorAdapterRegistry } from './connector_adapters/connector_adapter_registry'; +import { ConnectorAdapter, ConnectorAdapterParams } from './connector_adapters/types'; import { DataStreamAdapter, getDataStreamAdapter } from './alerts_service/lib/data_stream_adapter'; import { createGetAlertIndicesAliasFn, GetAlertIndicesAlias } from './lib'; @@ -118,6 +120,12 @@ export const LEGACY_EVENT_LOG_ACTIONS = { }; export interface PluginSetupContract { + registerConnectorAdapter< + RuleActionParams extends ConnectorAdapterParams = ConnectorAdapterParams, + ConnectorParams extends ConnectorAdapterParams = ConnectorAdapterParams + >( + adapter: ConnectorAdapter + ): void; registerType< Params extends RuleTypeParams = RuleTypeParams, ExtractedParams extends RuleTypeParams = RuleTypeParams, @@ -216,6 +224,7 @@ export class AlertingPlugin { private pluginStop$: Subject; private dataStreamAdapter?: DataStreamAdapter; private nodeRoles: PluginInitializerContext['node']['roles']; + private readonly connectorAdapterRegistry = new ConnectorAdapterRegistry(); constructor(initializerContext: PluginInitializerContext) { this.config = initializerContext.config.get(); @@ -378,6 +387,14 @@ export class AlertingPlugin { }); return { + registerConnectorAdapter: < + RuleActionParams extends ConnectorAdapterParams = ConnectorAdapterParams, + ConnectorParams extends ConnectorAdapterParams = ConnectorAdapterParams + >( + adapter: ConnectorAdapter + ) => { + this.connectorAdapterRegistry.register(adapter); + }, registerType: < Params extends RuleTypeParams = never, ExtractedParams extends RuleTypeParams = never, @@ -507,6 +524,7 @@ export class AlertingPlugin { maxScheduledPerMinute: this.config.rules.maxScheduledPerMinute, getAlertIndicesAlias: createGetAlertIndicesAliasFn(this.ruleTypeRegistry!), alertsService: this.alertsService, + connectorAdapterRegistry: this.connectorAdapterRegistry, uiSettings: core.uiSettings, }); @@ -573,6 +591,7 @@ export class AlertingPlugin { usageCounter: this.usageCounter, getRulesSettingsClientWithRequest, getMaintenanceWindowClientWithRequest, + connectorAdapterRegistry: this.connectorAdapterRegistry, }); this.eventLogService!.registerSavedObjectProvider(RULE_SAVED_OBJECT_TYPE, (request) => { diff --git a/x-pack/plugins/alerting/server/routes/_mock_handler_arguments.ts b/x-pack/plugins/alerting/server/routes/_mock_handler_arguments.ts index 8673614ed2ec0..cab10e242cdad 100644 --- a/x-pack/plugins/alerting/server/routes/_mock_handler_arguments.ts +++ b/x-pack/plugins/alerting/server/routes/_mock_handler_arguments.ts @@ -9,6 +9,8 @@ import { KibanaRequest, KibanaResponseFactory } from '@kbn/core/server'; import { identity } from 'lodash'; import type { MethodKeysOf } from '@kbn/utility-types'; import { httpServerMock } from '@kbn/core/server/mocks'; +import { actionsClientMock } from '@kbn/actions-plugin/server/mocks'; +import type { ActionsClientMock } from '@kbn/actions-plugin/server/mocks'; import { rulesClientMock, RulesClientMock } from '../rules_client.mock'; import { rulesSettingsClientMock, RulesSettingsClientMock } from '../rules_settings_client.mock'; import { @@ -21,6 +23,7 @@ import type { AlertingRequestHandlerContext } from '../types'; export function mockHandlerArguments( { rulesClient = rulesClientMock.create(), + actionsClient = actionsClientMock.create(), rulesSettingsClient = rulesSettingsClientMock.create(), maintenanceWindowClient = maintenanceWindowClientMock.create(), listTypes: listTypesRes = [], @@ -28,6 +31,7 @@ export function mockHandlerArguments( areApiKeysEnabled, }: { rulesClient?: RulesClientMock; + actionsClient?: ActionsClientMock; rulesSettingsClient?: RulesSettingsClientMock; maintenanceWindowClient?: MaintenanceWindowClientMock; listTypes?: RuleType[]; @@ -43,6 +47,10 @@ export function mockHandlerArguments( KibanaResponseFactory ] { const listTypes = jest.fn(() => listTypesRes); + const actionsClientMocked = actionsClient || actionsClientMock.create(); + + actionsClient.isSystemAction.mockImplementation((id) => id === 'system_action-id'); + return [ { alerting: { @@ -59,6 +67,11 @@ export function mockHandlerArguments( getFrameworkHealth, areApiKeysEnabled: areApiKeysEnabled ? areApiKeysEnabled : () => Promise.resolve(true), }, + actions: { + getActionsClient() { + return actionsClientMocked; + }, + }, } as unknown as AlertingRequestHandlerContext, request as KibanaRequest, mockResponseFactory(response), diff --git a/x-pack/plugins/alerting/server/routes/bulk_enable_rules.test.ts b/x-pack/plugins/alerting/server/routes/bulk_enable_rules.test.ts index 367eb0c75ba96..1ede4d1895c4f 100644 --- a/x-pack/plugins/alerting/server/routes/bulk_enable_rules.test.ts +++ b/x-pack/plugins/alerting/server/routes/bulk_enable_rules.test.ts @@ -6,13 +6,15 @@ */ import { httpServiceMock } from '@kbn/core/server/mocks'; - +import { actionsClientMock } from '@kbn/actions-plugin/server/mocks'; import { bulkEnableRulesRoute } from './bulk_enable_rules'; import { licenseStateMock } from '../lib/license_state.mock'; import { mockHandlerArguments } from './_mock_handler_arguments'; import { rulesClientMock } from '../rules_client.mock'; import { RuleTypeDisabledError } from '../lib/errors/rule_type_disabled'; import { verifyApiAccess } from '../lib/license_api_access'; +import { RuleAction, RuleSystemAction } from '../types'; +import { Rule } from '../application/rule/types'; const rulesClient = rulesClientMock.create(); @@ -123,4 +125,121 @@ describe('bulkEnableRulesRoute', () => { expect(res.forbidden).toHaveBeenCalledWith({ body: { message: 'Fail' } }); }); + + describe('actions', () => { + const mockedRule: Rule<{}> = { + id: '1', + alertTypeId: '1', + schedule: { interval: '10s' }, + params: { + bar: true, + }, + createdAt: new Date(), + updatedAt: new Date(), + actions: [ + { + group: 'default', + id: '2', + actionTypeId: 'test', + params: { + foo: true, + }, + uuid: '123-456', + }, + ], + consumer: 'bar', + name: 'abc', + tags: ['foo'], + enabled: true, + muteAll: false, + notifyWhen: 'onActionGroupChange', + createdBy: '', + updatedBy: '', + apiKeyOwner: '', + throttle: '30s', + mutedInstanceIds: [], + executionStatus: { + status: 'unknown', + lastExecutionDate: new Date('2020-08-20T19:23:38Z'), + }, + revision: 0, + }; + + const action: RuleAction = { + actionTypeId: 'test', + group: 'default', + id: '2', + params: { + foo: true, + }, + uuid: '123-456', + }; + + const systemAction: RuleSystemAction = { + actionTypeId: 'test-2', + id: 'system_action-id', + params: { + foo: true, + }, + uuid: '123-456', + }; + + const mockedRules: Array> = [ + { + ...mockedRule, + actions: [action], + systemActions: [systemAction], + }, + ]; + + const bulkEnableActionsResult = { + rules: mockedRules, + errors: [], + total: 1, + taskIdsFailedToBeEnabled: [], + }; + + it('should merge actions and systemActions correctly before sending the response', async () => { + const licenseState = licenseStateMock.create(); + const router = httpServiceMock.createRouter(); + const actionsClient = actionsClientMock.create(); + actionsClient.isSystemAction.mockImplementation((id: string) => id === 'system_action-id'); + + bulkEnableRulesRoute({ router, licenseState }); + const [_, handler] = router.patch.mock.calls[0]; + + rulesClient.bulkEnableRules.mockResolvedValueOnce(bulkEnableActionsResult); + + const [context, req, res] = mockHandlerArguments( + { rulesClient, actionsClient }, + { + body: bulkEnableRequest, + }, + ['ok'] + ); + + const routeRes = await handler(context, req, res); + + // @ts-expect-error: body exists + expect(routeRes.body.rules[0].actions).toEqual([ + { + actionTypeId: 'test', + group: 'default', + id: '2', + params: { + foo: true, + }, + uuid: '123-456', + }, + { + actionTypeId: 'test-2', + id: 'system_action-id', + params: { + foo: true, + }, + uuid: '123-456', + }, + ]); + }); + }); }); diff --git a/x-pack/plugins/alerting/server/routes/bulk_enable_rules.ts b/x-pack/plugins/alerting/server/routes/bulk_enable_rules.ts index 50b35c3b16cb3..ddfce5905ca4a 100644 --- a/x-pack/plugins/alerting/server/routes/bulk_enable_rules.ts +++ b/x-pack/plugins/alerting/server/routes/bulk_enable_rules.ts @@ -35,8 +35,19 @@ export const bulkEnableRulesRoute = ({ const { filter, ids } = req.body; try { - const result = await rulesClient.bulkEnableRules({ filter, ids }); - return res.ok({ body: result }); + const bulkEnableResults = await rulesClient.bulkEnableRules({ filter, ids }); + + const resultBody = { + body: { + ...bulkEnableResults, + // TODO We need to fix this API to return snake case like every other API + rules: bulkEnableResults.rules.map(({ actions, systemActions, ...rule }) => { + return { ...rule, actions: [...actions, ...(systemActions ?? [])] }; + }), + }, + }; + + return res.ok(resultBody); } catch (e) { if (e instanceof RuleTypeDisabledError) { return e.sendResponse(res); diff --git a/x-pack/plugins/alerting/server/routes/clone_rule.test.ts b/x-pack/plugins/alerting/server/routes/clone_rule.test.ts index df090abee9bad..a721ce45ad6a7 100644 --- a/x-pack/plugins/alerting/server/routes/clone_rule.test.ts +++ b/x-pack/plugins/alerting/server/routes/clone_rule.test.ts @@ -13,8 +13,7 @@ import { mockHandlerArguments } from './_mock_handler_arguments'; import { rulesClientMock } from '../rules_client.mock'; import { RuleTypeDisabledError } from '../lib/errors/rule_type_disabled'; import { cloneRuleRoute } from './clone_rule'; -import { SanitizedRule } from '../types'; -import { AsApiContract } from './lib'; +import { RuleAction, RuleSystemAction, SanitizedRule } from '../types'; const rulesClient = rulesClientMock.create(); jest.mock('../lib/license_api_access', () => ({ @@ -29,6 +28,25 @@ describe('cloneRuleRoute', () => { const createdAt = new Date(); const updatedAt = new Date(); + const action: RuleAction = { + actionTypeId: 'test', + group: 'default', + id: '2', + params: { + foo: true, + }, + uuid: '123-456', + }; + + const systemAction: RuleSystemAction = { + actionTypeId: 'test-2', + id: 'system_action-id', + params: { + foo: true, + }, + uuid: '123-456', + }; + const mockedRule: SanitizedRule<{ bar: boolean }> = { alertTypeId: '1', consumer: 'bar', @@ -39,17 +57,7 @@ describe('cloneRuleRoute', () => { bar: true, }, throttle: '30s', - actions: [ - { - actionTypeId: 'test', - group: 'default', - id: '2', - params: { - foo: true, - }, - uuid: '123-456', - }, - ], + actions: [action], enabled: true, muteAll: false, createdBy: '', @@ -80,7 +88,7 @@ describe('cloneRuleRoute', () => { ], }; - const cloneResult: AsApiContract> = { + const cloneResult = { ...ruleToClone, mute_all: mockedRule.muteAll, created_by: mockedRule.createdBy, @@ -214,4 +222,54 @@ describe('cloneRuleRoute', () => { expect(res.forbidden).toHaveBeenCalledWith({ body: { message: 'Fail' } }); }); + + it('transforms the system actions in the response of the rules client correctly', async () => { + const licenseState = licenseStateMock.create(); + const router = httpServiceMock.createRouter(); + + cloneRuleRoute(router, licenseState); + + const [_, handler] = router.post.mock.calls[0]; + + rulesClient.clone.mockResolvedValueOnce({ + ...mockedRule, + actions: [action], + systemActions: [systemAction], + }); + + const [context, req, res] = mockHandlerArguments( + { rulesClient }, + { + params: { + id: '1', + }, + }, + ['ok'] + ); + + const routeRes = await handler(context, req, res); + + // @ts-expect-error: body exists + expect(routeRes.body.systemActions).toBeUndefined(); + // @ts-expect-error: body exists + expect(routeRes.body.actions).toEqual([ + { + connector_type_id: 'test', + group: 'default', + id: '2', + params: { + foo: true, + }, + uuid: '123-456', + }, + { + connector_type_id: 'test-2', + id: 'system_action-id', + params: { + foo: true, + }, + uuid: '123-456', + }, + ]); + }); }); diff --git a/x-pack/plugins/alerting/server/routes/clone_rule.ts b/x-pack/plugins/alerting/server/routes/clone_rule.ts index fa775e626d903..b6107a5fc6907 100644 --- a/x-pack/plugins/alerting/server/routes/clone_rule.ts +++ b/x-pack/plugins/alerting/server/routes/clone_rule.ts @@ -8,27 +8,23 @@ import { schema } from '@kbn/config-schema'; import { IRouter } from '@kbn/core/server'; import { ILicenseState, RuleTypeDisabledError } from '../lib'; -import { - verifyAccessAndContext, - RewriteResponseCase, - handleDisabledApiKeysError, - rewriteRuleLastRun, - rewriteActionsRes, -} from './lib'; +import { verifyAccessAndContext, handleDisabledApiKeysError, rewriteRuleLastRun } from './lib'; import { RuleTypeParams, AlertingRequestHandlerContext, INTERNAL_BASE_ALERTING_API_PATH, PartialRule, } from '../types'; +import { transformRuleActions } from './rule/transforms'; const paramSchema = schema.object({ id: schema.string(), newId: schema.maybe(schema.string()), }); -const rewriteBodyRes: RewriteResponseCase> = ({ +const rewriteBodyRes = ({ actions, + systemActions, alertTypeId, scheduledTaskId, createdBy, @@ -46,7 +42,7 @@ const rewriteBodyRes: RewriteResponseCase> = ({ lastRun, nextRun, ...rest -}) => ({ +}: PartialRule) => ({ ...rest, api_key_owner: apiKeyOwner, created_by: createdBy, @@ -71,7 +67,7 @@ const rewriteBodyRes: RewriteResponseCase> = ({ : {}), ...(actions ? { - actions: rewriteActionsRes(actions), + actions: transformRuleActions(actions, systemActions), } : {}), ...(lastRun ? { last_run: rewriteRuleLastRun(lastRun) } : {}), diff --git a/x-pack/plugins/alerting/server/routes/find_rules.test.ts b/x-pack/plugins/alerting/server/routes/find_rules.test.ts index afd621c6e7abd..69df6e978cd83 100644 --- a/x-pack/plugins/alerting/server/routes/find_rules.test.ts +++ b/x-pack/plugins/alerting/server/routes/find_rules.test.ts @@ -97,6 +97,219 @@ describe('findRulesRoute', () => { }); }); + it('should rewrite the rule and actions correctly', async () => { + const licenseState = licenseStateMock.create(); + const router = httpServiceMock.createRouter(); + + findRulesRoute(router, licenseState); + + const [config, handler] = router.get.mock.calls[0]; + + expect(config.path).toMatchInlineSnapshot(`"/api/alerting/rules/_find"`); + + const findResult = { + page: 1, + perPage: 1, + total: 0, + data: [ + { + id: '3d534c70-582b-11ec-8995-2b1578a3bc5d', + notifyWhen: 'onActiveAlert' as const, + alertTypeId: '.index-threshold', + name: 'stressing index-threshold 37/200', + consumer: 'alerts', + tags: [], + enabled: true, + throttle: null, + apiKey: null, + apiKeyOwner: '2889684073', + createdBy: 'elastic', + updatedBy: '2889684073', + muteAll: false, + mutedInstanceIds: [], + schedule: { + interval: '1s', + }, + actions: [ + { + actionTypeId: '.server-log', + params: { + message: 'alert 37: {{context.message}}', + }, + group: 'threshold met', + id: '3619a0d0-582b-11ec-8995-2b1578a3bc5d', + uuid: '123-456', + }, + ], + systemActions: [ + { actionTypeId: '.test', id: 'system_action-id', params: {}, uuid: '789' }, + ], + params: { x: 42 }, + updatedAt: '2024-03-21T13:15:00.498Z', + createdAt: '2024-03-21T13:15:00.498Z', + scheduledTaskId: '52125fb0-5895-11ec-ae69-bb65d1a71b72', + executionStatus: { + status: 'ok' as const, + lastExecutionDate: '2024-03-21T13:15:00.498Z', + lastDuration: 1194, + }, + revision: 0, + }, + ], + }; + + // @ts-expect-error: TS complains about group being undefined in the system action + rulesClient.find.mockResolvedValueOnce(findResult); + + const [context, req, res] = mockHandlerArguments( + { rulesClient }, + { + query: { + per_page: 1, + page: 1, + default_search_operator: 'OR', + }, + }, + ['ok'] + ); + + expect(await handler(context, req, res)).toMatchInlineSnapshot(` + Object { + "body": Object { + "data": Array [ + Object { + "actions": Array [ + Object { + "connector_type_id": ".server-log", + "group": "threshold met", + "id": "3619a0d0-582b-11ec-8995-2b1578a3bc5d", + "params": Object { + "message": "alert 37: {{context.message}}", + }, + "uuid": "123-456", + }, + Object { + "connector_type_id": ".test", + "id": "system_action-id", + "params": Object {}, + "uuid": "789", + }, + ], + "apiKey": null, + "api_key_owner": "2889684073", + "consumer": "alerts", + "created_at": "2024-03-21T13:15:00.498Z", + "created_by": "elastic", + "enabled": true, + "execution_status": Object { + "last_duration": 1194, + "last_execution_date": "2024-03-21T13:15:00.498Z", + "status": "ok", + }, + "id": "3d534c70-582b-11ec-8995-2b1578a3bc5d", + "mute_all": false, + "muted_alert_ids": Array [], + "name": "stressing index-threshold 37/200", + "notify_when": "onActiveAlert", + "params": Object { + "x": 42, + }, + "revision": 0, + "rule_type_id": ".index-threshold", + "schedule": Object { + "interval": "1s", + }, + "scheduled_task_id": "52125fb0-5895-11ec-ae69-bb65d1a71b72", + "snooze_schedule": undefined, + "tags": Array [], + "throttle": null, + "updated_at": "2024-03-21T13:15:00.498Z", + "updated_by": "2889684073", + }, + ], + "page": 1, + "per_page": 1, + "total": 0, + }, + } + `); + + expect(rulesClient.find).toHaveBeenCalledTimes(1); + expect(rulesClient.find.mock.calls[0]).toMatchInlineSnapshot(` + Array [ + Object { + "excludeFromPublicApi": true, + "includeSnoozeData": true, + "options": Object { + "defaultSearchOperator": "OR", + "filterConsumers": undefined, + "page": 1, + "perPage": 1, + }, + }, + ] + `); + + expect(res.ok).toHaveBeenCalledWith({ + body: { + page: 1, + per_page: 1, + total: 0, + data: [ + { + actions: [ + { + connector_type_id: '.server-log', + group: 'threshold met', + id: '3619a0d0-582b-11ec-8995-2b1578a3bc5d', + params: { + message: 'alert 37: {{context.message}}', + }, + uuid: '123-456', + }, + { + connector_type_id: '.test', + id: 'system_action-id', + params: {}, + uuid: '789', + }, + ], + apiKey: null, + api_key_owner: '2889684073', + consumer: 'alerts', + created_at: '2024-03-21T13:15:00.498Z', + created_by: 'elastic', + enabled: true, + execution_status: { + last_duration: 1194, + last_execution_date: '2024-03-21T13:15:00.498Z', + status: 'ok', + }, + id: '3d534c70-582b-11ec-8995-2b1578a3bc5d', + mute_all: false, + muted_alert_ids: [], + name: 'stressing index-threshold 37/200', + notify_when: 'onActiveAlert', + params: { + x: 42, + }, + revision: 0, + rule_type_id: '.index-threshold', + schedule: { + interval: '1s', + }, + scheduled_task_id: '52125fb0-5895-11ec-ae69-bb65d1a71b72', + snooze_schedule: undefined, + tags: [], + throttle: null, + updated_at: '2024-03-21T13:15:00.498Z', + updated_by: '2889684073', + }, + ], + }, + }); + }); + it('ensures the license allows finding rules', async () => { const licenseState = licenseStateMock.create(); const router = httpServiceMock.createRouter(); diff --git a/x-pack/plugins/alerting/server/routes/find_rules.ts b/x-pack/plugins/alerting/server/routes/find_rules.ts index 1eab92db82383..711baa3108f35 100644 --- a/x-pack/plugins/alerting/server/routes/find_rules.ts +++ b/x-pack/plugins/alerting/server/routes/find_rules.ts @@ -10,12 +10,7 @@ import { UsageCounter } from '@kbn/usage-collection-plugin/server'; import { schema } from '@kbn/config-schema'; import { ILicenseState } from '../lib'; import { FindOptions, FindResult } from '../rules_client'; -import { - RewriteRequestCase, - RewriteResponseCase, - verifyAccessAndContext, - rewriteRule, -} from './lib'; +import { RewriteRequestCase, verifyAccessAndContext, rewriteRule } from './lib'; import { RuleTypeParams, AlertingRequestHandlerContext, @@ -69,11 +64,7 @@ const rewriteQueryReq: RewriteRequestCase = ({ ...(hasReference ? { hasReference } : {}), ...(searchFields ? { searchFields } : {}), }); -const rewriteBodyRes: RewriteResponseCase> = ({ - perPage, - data, - ...restOfResult -}) => { +const rewriteBodyRes = ({ perPage, data, ...restOfResult }: FindResult) => { return { ...restOfResult, per_page: perPage, diff --git a/x-pack/plugins/alerting/server/routes/get_rule.test.ts b/x-pack/plugins/alerting/server/routes/get_rule.test.ts index 864a848b8ffa8..8750e382f7811 100644 --- a/x-pack/plugins/alerting/server/routes/get_rule.test.ts +++ b/x-pack/plugins/alerting/server/routes/get_rule.test.ts @@ -12,8 +12,7 @@ import { licenseStateMock } from '../lib/license_state.mock'; import { verifyApiAccess } from '../lib/license_api_access'; import { mockHandlerArguments } from './_mock_handler_arguments'; import { rulesClientMock } from '../rules_client.mock'; -import { SanitizedRule } from '../types'; -import { AsApiContract } from './lib'; +import { RuleAction, RuleSystemAction, SanitizedRule } from '../types'; const rulesClient = rulesClientMock.create(); jest.mock('../lib/license_api_access', () => ({ @@ -25,6 +24,37 @@ beforeEach(() => { }); describe('getRuleRoute', () => { + const action: RuleAction = { + group: 'default', + id: '2', + actionTypeId: 'test', + params: { + foo: true, + }, + uuid: '123-456', + alertsFilter: { + query: { + kql: 'name:test', + dsl: '{"must": {"term": { "name": "test" }}}', + filters: [], + }, + timeframe: { + days: [1], + hours: { start: '08:00', end: '17:00' }, + timezone: 'UTC', + }, + }, + }; + + const systemAction: RuleSystemAction = { + actionTypeId: 'test-2', + id: 'system_action-id', + params: { + foo: true, + }, + uuid: '123-456', + }; + const mockedAlert: SanitizedRule<{ bar: boolean; }> = { @@ -36,30 +66,7 @@ describe('getRuleRoute', () => { }, createdAt: new Date(), updatedAt: new Date(), - actions: [ - { - group: 'default', - id: '2', - actionTypeId: 'test', - params: { - foo: true, - }, - uuid: '123-456', - alertsFilter: { - query: { - kql: 'name:test', - // @ts-expect-error upgrade typescript v4.9.5 - dsl: '{"must": {"term": { "name": "test" }}}', - filters: [], - }, - timeframe: { - days: [1], - hours: { start: '08:00', end: '17:00' }, - timezone: 'UTC', - }, - }, - }, - ], + actions: [action], consumer: 'bar', name: 'abc', tags: ['foo'], @@ -78,7 +85,8 @@ describe('getRuleRoute', () => { revision: 0, }; - const getResult: AsApiContract> = { + const mockedAction0 = mockedAlert.actions[0]; + const getResult = { ...pick(mockedAlert, 'consumer', 'name', 'schedule', 'tags', 'params', 'throttle', 'enabled'), rule_type_id: mockedAlert.alertTypeId, notify_when: mockedAlert.notifyWhen, @@ -97,12 +105,12 @@ describe('getRuleRoute', () => { }, actions: [ { - group: mockedAlert.actions[0].group, - id: mockedAlert.actions[0].id, - params: mockedAlert.actions[0].params, - connector_type_id: mockedAlert.actions[0].actionTypeId, - uuid: mockedAlert.actions[0].uuid, - alerts_filter: mockedAlert.actions[0].alertsFilter, + group: mockedAction0.group, + id: mockedAction0.id, + params: mockedAction0.params, + connector_type_id: mockedAction0.actionTypeId, + uuid: mockedAction0.uuid, + alerts_filter: mockedAction0.alertsFilter, }, ], }; @@ -184,4 +192,66 @@ describe('getRuleRoute', () => { expect(verifyApiAccess).toHaveBeenCalledWith(licenseState); }); + + it('transforms the system actions in the response of the rules client correctly', async () => { + const licenseState = licenseStateMock.create(); + const router = httpServiceMock.createRouter(); + + getRuleRoute(router, licenseState); + const [_, handler] = router.get.mock.calls[0]; + + rulesClient.get.mockResolvedValueOnce({ + ...mockedAlert, + actions: [action], + systemActions: [systemAction], + }); + + const [context, req, res] = mockHandlerArguments( + { rulesClient }, + { + params: { id: '1' }, + }, + ['ok'] + ); + + const routeRes = await handler(context, req, res); + + // @ts-expect-error: body exists + expect(routeRes.body.systemActions).toBeUndefined(); + // @ts-expect-error: body exists + expect(routeRes.body.actions).toEqual([ + { + alerts_filter: { + query: { + dsl: '{"must": {"term": { "name": "test" }}}', + filters: [], + kql: 'name:test', + }, + timeframe: { + days: [1], + hours: { + end: '17:00', + start: '08:00', + }, + timezone: 'UTC', + }, + }, + connector_type_id: 'test', + group: 'default', + id: '2', + params: { + foo: true, + }, + uuid: '123-456', + }, + { + connector_type_id: 'test-2', + id: 'system_action-id', + params: { + foo: true, + }, + uuid: '123-456', + }, + ]); + }); }); diff --git a/x-pack/plugins/alerting/server/routes/get_rule.ts b/x-pack/plugins/alerting/server/routes/get_rule.ts index bc7983f6acdf1..f739f946975e6 100644 --- a/x-pack/plugins/alerting/server/routes/get_rule.ts +++ b/x-pack/plugins/alerting/server/routes/get_rule.ts @@ -9,12 +9,7 @@ import { omit } from 'lodash'; import { schema } from '@kbn/config-schema'; import { IRouter } from '@kbn/core/server'; import { ILicenseState } from '../lib'; -import { - verifyAccessAndContext, - RewriteResponseCase, - rewriteRuleLastRun, - rewriteActionsRes, -} from './lib'; +import { verifyAccessAndContext, rewriteRuleLastRun } from './lib'; import { RuleTypeParams, AlertingRequestHandlerContext, @@ -22,12 +17,13 @@ import { INTERNAL_BASE_ALERTING_API_PATH, SanitizedRule, } from '../types'; +import { transformRuleActions } from './rule/transforms'; const paramSchema = schema.object({ id: schema.string(), }); -const rewriteBodyRes: RewriteResponseCase> = ({ +const rewriteBodyRes = ({ alertTypeId, createdBy, updatedBy, @@ -40,6 +36,7 @@ const rewriteBodyRes: RewriteResponseCase> = ({ mutedInstanceIds, executionStatus, actions, + systemActions, scheduledTaskId, snoozeSchedule, isSnoozedUntil, @@ -47,7 +44,7 @@ const rewriteBodyRes: RewriteResponseCase> = ({ nextRun, viewInAppRelativeUrl, ...rest -}) => ({ +}: SanitizedRule) => ({ ...rest, rule_type_id: alertTypeId, created_by: createdBy, @@ -66,7 +63,7 @@ const rewriteBodyRes: RewriteResponseCase> = ({ last_execution_date: executionStatus.lastExecutionDate, last_duration: executionStatus.lastDuration, }, - actions: rewriteActionsRes(actions), + actions: transformRuleActions(actions, systemActions), ...(lastRun ? { last_run: rewriteRuleLastRun(lastRun) } : {}), ...(nextRun ? { next_run: nextRun } : {}), ...(viewInAppRelativeUrl ? { view_in_app_relative_url: viewInAppRelativeUrl } : {}), diff --git a/x-pack/plugins/alerting/server/routes/legacy/create.test.ts b/x-pack/plugins/alerting/server/routes/legacy/create.test.ts index 548015ef55f03..e090cac73a932 100644 --- a/x-pack/plugins/alerting/server/routes/legacy/create.test.ts +++ b/x-pack/plugins/alerting/server/routes/legacy/create.test.ts @@ -12,7 +12,7 @@ import { licenseStateMock } from '../../lib/license_state.mock'; import { verifyApiAccess } from '../../lib/license_api_access'; import { mockHandlerArguments } from '../_mock_handler_arguments'; import { rulesClientMock } from '../../rules_client.mock'; -import { Rule } from '../../../common/rule'; +import { Rule, RuleSystemAction } from '../../../common/rule'; import { RuleTypeDisabledError } from '../../lib/errors/rule_type_disabled'; import { encryptedSavedObjectsMock } from '@kbn/encrypted-saved-objects-plugin/server/mocks'; import { trackLegacyRouteUsage } from '../../lib/track_legacy_route_usage'; @@ -57,6 +57,15 @@ describe('createAlertRoute', () => { ], }; + const systemAction: RuleSystemAction = { + actionTypeId: 'test-2', + id: 'system_action-id', + params: { + foo: true, + }, + uuid: '123-456', + }; + const createResult: Rule<{ bar: boolean }> = { ...mockedAlert, enabled: true, @@ -460,8 +469,81 @@ describe('createAlertRoute', () => { usageCounter: mockUsageCounter, }); const [, handler] = router.post.mock.calls[0]; + rulesClient.create.mockResolvedValueOnce(createResult); const [context, req, res] = mockHandlerArguments({ rulesClient }, {}, ['ok']); await handler(context, req, res); expect(trackLegacyRouteUsage).toHaveBeenCalledWith('create', mockUsageCounter); }); + + it('does not return system actions', async () => { + const licenseState = licenseStateMock.create(); + const router = httpServiceMock.createRouter(); + const encryptedSavedObjects = encryptedSavedObjectsMock.createSetup({ canEncrypt: true }); + const mockUsageCountersSetup = usageCountersServiceMock.createSetupContract(); + const mockUsageCounter = mockUsageCountersSetup.createUsageCounter('test'); + + createAlertRoute({ + router, + licenseState, + encryptedSavedObjects, + usageCounter: mockUsageCounter, + }); + + const [config, handler] = router.post.mock.calls[0]; + + expect(config.path).toMatchInlineSnapshot(`"/api/alerts/alert/{id?}"`); + + rulesClient.create.mockResolvedValueOnce({ ...createResult, systemActions: [systemAction] }); + + const [context, req, res] = mockHandlerArguments( + { rulesClient }, + { + body: mockedAlert, + }, + ['ok'] + ); + + expect(await handler(context, req, res)).toEqual({ body: createResult }); + + expect(mockUsageCounter.incrementCounter).not.toHaveBeenCalled(); + expect(rulesClient.create).toHaveBeenCalledTimes(1); + expect(rulesClient.create.mock.calls[0]).toMatchInlineSnapshot(` + Array [ + Object { + "data": Object { + "actions": Array [ + Object { + "group": "default", + "id": "2", + "params": Object { + "foo": true, + }, + }, + ], + "alertTypeId": "1", + "consumer": "bar", + "name": "abc", + "notifyWhen": "onActionGroupChange", + "params": Object { + "bar": true, + }, + "schedule": Object { + "interval": "10s", + }, + "tags": Array [ + "foo", + ], + "throttle": "30s", + }, + "options": Object { + "id": undefined, + }, + }, + ] + `); + + expect(res.ok).toHaveBeenCalledWith({ + body: createResult, + }); + }); }); diff --git a/x-pack/plugins/alerting/server/routes/legacy/create.ts b/x-pack/plugins/alerting/server/routes/legacy/create.ts index e7583033ae4ba..f586a253696b3 100644 --- a/x-pack/plugins/alerting/server/routes/legacy/create.ts +++ b/x-pack/plugins/alerting/server/routes/legacy/create.ts @@ -78,10 +78,11 @@ export const createAlertRoute = ({ router, licenseState, usageCounter }: RouteOp }); try { - const alertRes: SanitizedRule = await rulesClient.create({ - data: { ...alert, notifyWhen }, - options: { id: params?.id }, - }); + const { systemActions, ...alertRes }: SanitizedRule = + await rulesClient.create({ + data: { ...alert, notifyWhen }, + options: { id: params?.id }, + }); return res.ok({ body: alertRes, }); diff --git a/x-pack/plugins/alerting/server/routes/legacy/find.test.ts b/x-pack/plugins/alerting/server/routes/legacy/find.test.ts index 026a4f81688e3..60ca90865be1d 100644 --- a/x-pack/plugins/alerting/server/routes/legacy/find.test.ts +++ b/x-pack/plugins/alerting/server/routes/legacy/find.test.ts @@ -4,6 +4,8 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ + +import { omit } from 'lodash'; import { usageCountersServiceMock } from '@kbn/usage-collection-plugin/server/usage_counters/usage_counters_service.mock'; import { findAlertRoute } from './find'; import { httpServiceMock } from '@kbn/core/server/mocks'; @@ -160,6 +162,13 @@ describe('findAlertRoute', () => { findAlertRoute(router, licenseState, mockUsageCounter); const [, handler] = router.get.mock.calls[0]; + const findResult = { + page: 1, + perPage: 1, + total: 0, + data: [], + }; + rulesClient.find.mockResolvedValueOnce(findResult); const [context, req, res] = mockHandlerArguments({ rulesClient }, { params: {}, query: {} }, [ 'ok', ]); @@ -175,6 +184,14 @@ describe('findAlertRoute', () => { findAlertRoute(router, licenseState, mockUsageCounter); const [, handler] = router.get.mock.calls[0]; + + const findResult = { + page: 1, + perPage: 1, + total: 0, + data: [], + }; + rulesClient.find.mockResolvedValueOnce(findResult); const [context, req, res] = mockHandlerArguments( { rulesClient }, { @@ -204,6 +221,13 @@ describe('findAlertRoute', () => { findAlertRoute(router, licenseState, mockUsageCounter); const [, handler] = router.get.mock.calls[0]; + const findResult = { + page: 1, + perPage: 1, + total: 0, + data: [], + }; + rulesClient.find.mockResolvedValueOnce(findResult); const [context, req, res] = mockHandlerArguments( { rulesClient }, { @@ -221,4 +245,153 @@ describe('findAlertRoute', () => { incrementBy: 1, }); }); + + it('does not return system actions', async () => { + const licenseState = licenseStateMock.create(); + const router = httpServiceMock.createRouter(); + + findAlertRoute(router, licenseState); + + const [config, handler] = router.get.mock.calls[0]; + + expect(config.path).toMatchInlineSnapshot(`"/api/alerts/_find"`); + + const findResult = { + page: 1, + perPage: 1, + total: 0, + data: [ + { + id: '3d534c70-582b-11ec-8995-2b1578a3bc5d', + notifyWhen: 'onActiveAlert' as const, + alertTypeId: '.index-threshold', + name: 'stressing index-threshold 37/200', + consumer: 'alerts', + tags: [], + enabled: true, + throttle: null, + apiKey: null, + apiKeyOwner: '2889684073', + createdBy: 'elastic', + updatedBy: '2889684073', + muteAll: false, + mutedInstanceIds: [], + schedule: { + interval: '1s', + }, + actions: [ + { + actionTypeId: '.server-log', + params: { + message: 'alert 37: {{context.message}}', + }, + group: 'threshold met', + id: '3619a0d0-582b-11ec-8995-2b1578a3bc5d', + uuid: '123-456', + }, + ], + systemActions: [ + { actionTypeId: '.test', id: 'system_action-id', params: {}, uuid: '789' }, + ], + params: { x: 42 }, + updatedAt: '2024-03-21T13:15:00.498Z', + createdAt: '2024-03-21T13:15:00.498Z', + scheduledTaskId: '52125fb0-5895-11ec-ae69-bb65d1a71b72', + executionStatus: { + status: 'ok' as const, + lastExecutionDate: '2024-03-21T13:15:00.498Z', + lastDuration: 1194, + }, + revision: 0, + }, + ], + }; + + // @ts-expect-error: TS complains about dates being string and not a Date object + rulesClient.find.mockResolvedValueOnce(findResult); + + const [context, req, res] = mockHandlerArguments( + { rulesClient }, + { + query: { + per_page: 1, + page: 1, + default_search_operator: 'OR', + }, + }, + ['ok'] + ); + + expect(await handler(context, req, res)).toMatchInlineSnapshot(` + Object { + "body": Object { + "data": Array [ + Object { + "actions": Array [ + Object { + "actionTypeId": ".server-log", + "group": "threshold met", + "id": "3619a0d0-582b-11ec-8995-2b1578a3bc5d", + "params": Object { + "message": "alert 37: {{context.message}}", + }, + "uuid": "123-456", + }, + ], + "alertTypeId": ".index-threshold", + "apiKey": null, + "apiKeyOwner": "2889684073", + "consumer": "alerts", + "createdAt": "2024-03-21T13:15:00.498Z", + "createdBy": "elastic", + "enabled": true, + "executionStatus": Object { + "lastDuration": 1194, + "lastExecutionDate": "2024-03-21T13:15:00.498Z", + "status": "ok", + }, + "id": "3d534c70-582b-11ec-8995-2b1578a3bc5d", + "muteAll": false, + "mutedInstanceIds": Array [], + "name": "stressing index-threshold 37/200", + "notifyWhen": "onActiveAlert", + "params": Object { + "x": 42, + }, + "revision": 0, + "schedule": Object { + "interval": "1s", + }, + "scheduledTaskId": "52125fb0-5895-11ec-ae69-bb65d1a71b72", + "tags": Array [], + "throttle": null, + "updatedAt": "2024-03-21T13:15:00.498Z", + "updatedBy": "2889684073", + }, + ], + "page": 1, + "perPage": 1, + "total": 0, + }, + } + `); + + expect(rulesClient.find).toHaveBeenCalledTimes(1); + expect(rulesClient.find.mock.calls[0]).toMatchInlineSnapshot(` + Array [ + Object { + "excludeFromPublicApi": true, + "options": Object { + "defaultSearchOperator": "OR", + "page": 1, + "perPage": 1, + }, + }, + ] + `); + + expect(res.ok).toHaveBeenCalledWith({ + body: omit(findResult, 'data[0].systemActions'), + }); + }); }); diff --git a/x-pack/plugins/alerting/server/routes/legacy/find.ts b/x-pack/plugins/alerting/server/routes/legacy/find.ts index 9a33f5b2dc5fd..e0e4ffa34cf33 100644 --- a/x-pack/plugins/alerting/server/routes/legacy/find.ts +++ b/x-pack/plugins/alerting/server/routes/legacy/find.ts @@ -99,7 +99,10 @@ export const findAlertRoute = ( const findResult = await rulesClient.find({ options, excludeFromPublicApi: true }); return res.ok({ - body: findResult, + body: { + ...findResult, + data: findResult.data.map(({ systemActions, ...rule }) => rule), + }, }); }) ); diff --git a/x-pack/plugins/alerting/server/routes/legacy/get.test.ts b/x-pack/plugins/alerting/server/routes/legacy/get.test.ts index 403f7a5b42ac8..7d1a6ac656896 100644 --- a/x-pack/plugins/alerting/server/routes/legacy/get.test.ts +++ b/x-pack/plugins/alerting/server/routes/legacy/get.test.ts @@ -12,7 +12,7 @@ import { licenseStateMock } from '../../lib/license_state.mock'; import { verifyApiAccess } from '../../lib/license_api_access'; import { mockHandlerArguments } from '../_mock_handler_arguments'; import { rulesClientMock } from '../../rules_client.mock'; -import { Rule } from '../../../common'; +import { Rule, RuleSystemAction } from '../../../common'; import { trackLegacyRouteUsage } from '../../lib/track_legacy_route_usage'; const rulesClient = rulesClientMock.create(); @@ -69,6 +69,15 @@ describe('getAlertRoute', () => { revision: 0, }; + const systemAction: RuleSystemAction = { + actionTypeId: 'test-2', + id: 'system_action-id', + params: { + foo: true, + }, + uuid: '123-456', + }; + it('gets an alert with proper parameters', async () => { const licenseState = licenseStateMock.create(); const router = httpServiceMock.createRouter(); @@ -155,10 +164,41 @@ describe('getAlertRoute', () => { getAlertRoute(router, licenseState, mockUsageCounter); const [, handler] = router.get.mock.calls[0]; + + rulesClient.get.mockResolvedValueOnce(mockedAlert); + const [context, req, res] = mockHandlerArguments({ rulesClient }, { params: { id: '1' } }, [ 'ok', ]); await handler(context, req, res); expect(trackLegacyRouteUsage).toHaveBeenCalledWith('get', mockUsageCounter); }); + + it('does not return system actions', async () => { + const licenseState = licenseStateMock.create(); + const router = httpServiceMock.createRouter(); + + getAlertRoute(router, licenseState); + const [config, handler] = router.get.mock.calls[0]; + + expect(config.path).toMatchInlineSnapshot(`"/api/alerts/alert/{id}"`); + + rulesClient.get.mockResolvedValueOnce({ ...mockedAlert, systemActions: [systemAction] }); + + const [context, req, res] = mockHandlerArguments( + { rulesClient }, + { + params: { id: '1' }, + }, + ['ok'] + ); + await handler(context, req, res); + + expect(rulesClient.get).toHaveBeenCalledTimes(1); + expect(rulesClient.get.mock.calls[0][0].id).toEqual('1'); + + expect(res.ok).toHaveBeenCalledWith({ + body: mockedAlert, + }); + }); }); diff --git a/x-pack/plugins/alerting/server/routes/legacy/get.ts b/x-pack/plugins/alerting/server/routes/legacy/get.ts index 62fdde5507148..be9550f1f336e 100644 --- a/x-pack/plugins/alerting/server/routes/legacy/get.ts +++ b/x-pack/plugins/alerting/server/routes/legacy/get.ts @@ -37,8 +37,9 @@ export const getAlertRoute = ( trackLegacyRouteUsage('get', usageCounter); const rulesClient = (await context.alerting).getRulesClient(); const { id } = req.params; + const { systemActions, ...rule } = await rulesClient.get({ id, excludeFromPublicApi: true }); return res.ok({ - body: await rulesClient.get({ id, excludeFromPublicApi: true }), + body: rule, }); }) ); diff --git a/x-pack/plugins/alerting/server/routes/legacy/update.test.ts b/x-pack/plugins/alerting/server/routes/legacy/update.test.ts index 756e751e5c6e4..f6436fb91cbdc 100644 --- a/x-pack/plugins/alerting/server/routes/legacy/update.test.ts +++ b/x-pack/plugins/alerting/server/routes/legacy/update.test.ts @@ -13,7 +13,7 @@ import { verifyApiAccess } from '../../lib/license_api_access'; import { mockHandlerArguments } from '../_mock_handler_arguments'; import { rulesClientMock } from '../../rules_client.mock'; import { RuleTypeDisabledError } from '../../lib/errors/rule_type_disabled'; -import { RuleNotifyWhen } from '../../../common'; +import { RuleNotifyWhen, RuleSystemAction } from '../../../common'; import { trackLegacyRouteUsage } from '../../lib/track_legacy_route_usage'; const rulesClient = rulesClientMock.create(); @@ -53,6 +53,15 @@ describe('updateAlertRoute', () => { notifyWhen: RuleNotifyWhen.CHANGE, }; + const systemAction: RuleSystemAction = { + actionTypeId: 'test-2', + id: 'system_action-id', + params: { + foo: true, + }, + uuid: '123-456', + }; + it('updates an alert with proper parameters', async () => { const licenseState = licenseStateMock.create(); const router = httpServiceMock.createRouter(); @@ -249,10 +258,89 @@ describe('updateAlertRoute', () => { updateAlertRoute(router, licenseState, mockUsageCounter); const [, handler] = router.put.mock.calls[0]; + rulesClient.update.mockResolvedValueOnce(mockedResponse); const [context, req, res] = mockHandlerArguments({ rulesClient }, { params: {}, body: {} }, [ 'ok', ]); await handler(context, req, res); expect(trackLegacyRouteUsage).toHaveBeenCalledWith('update', mockUsageCounter); }); + + it('does not return system actions', async () => { + const licenseState = licenseStateMock.create(); + const router = httpServiceMock.createRouter(); + + updateAlertRoute(router, licenseState); + + const [config, handler] = router.put.mock.calls[0]; + + expect(config.path).toMatchInlineSnapshot(`"/api/alerts/alert/{id}"`); + + rulesClient.update.mockResolvedValueOnce({ ...mockedResponse, systemActions: [systemAction] }); + + const [context, req, res] = mockHandlerArguments( + { rulesClient }, + { + params: { + id: '1', + }, + body: { + throttle: null, + name: 'abc', + tags: ['bar'], + schedule: { interval: '12s' }, + params: { + otherField: false, + }, + actions: [ + { + group: 'default', + id: '2', + params: { + baz: true, + }, + }, + ], + notifyWhen: 'onActionGroupChange', + }, + }, + ['ok'] + ); + + expect(await handler(context, req, res)).toEqual({ body: mockedResponse }); + + expect(rulesClient.update).toHaveBeenCalledTimes(1); + expect(rulesClient.update.mock.calls[0]).toMatchInlineSnapshot(` + Array [ + Object { + "data": Object { + "actions": Array [ + Object { + "group": "default", + "id": "2", + "params": Object { + "baz": true, + }, + }, + ], + "name": "abc", + "notifyWhen": "onActionGroupChange", + "params": Object { + "otherField": false, + }, + "schedule": Object { + "interval": "12s", + }, + "tags": Array [ + "bar", + ], + "throttle": null, + }, + "id": "1", + }, + ] + `); + + expect(res.ok).toHaveBeenCalled(); + }); }); diff --git a/x-pack/plugins/alerting/server/routes/legacy/update.ts b/x-pack/plugins/alerting/server/routes/legacy/update.ts index 07bde524076c1..203352dd01a6b 100644 --- a/x-pack/plugins/alerting/server/routes/legacy/update.ts +++ b/x-pack/plugins/alerting/server/routes/legacy/update.ts @@ -68,7 +68,7 @@ export const updateAlertRoute = ( const { id } = req.params; const { name, actions, params, schedule, tags, throttle, notifyWhen } = req.body; try { - const alertRes = await rulesClient.update({ + const { systemActions, ...alertRes } = await rulesClient.update({ id, data: { name, diff --git a/x-pack/plugins/alerting/server/routes/lib/actions_schema.ts b/x-pack/plugins/alerting/server/routes/lib/actions_schema.ts index 76661b2d33ff2..8b3d136d84f46 100644 --- a/x-pack/plugins/alerting/server/routes/lib/actions_schema.ts +++ b/x-pack/plugins/alerting/server/routes/lib/actions_schema.ts @@ -13,7 +13,7 @@ import { validateHours } from './validate_hours'; export const actionsSchema = schema.arrayOf( schema.object({ - group: schema.string(), + group: schema.maybe(schema.string()), id: schema.string(), params: schema.recordOf(schema.string(), schema.any(), { defaultValue: {} }), frequency: schema.maybe( @@ -80,3 +80,14 @@ export const actionsSchema = schema.arrayOf( }), { defaultValue: [] } ); + +export const systemActionsSchema = schema.maybe( + schema.arrayOf( + schema.object({ + id: schema.string(), + params: schema.recordOf(schema.string(), schema.any(), { defaultValue: {} }), + uuid: schema.maybe(schema.string()), + }), + { defaultValue: [] } + ) +); diff --git a/x-pack/plugins/alerting/server/routes/lib/index.ts b/x-pack/plugins/alerting/server/routes/lib/index.ts index af4a5d4309f86..8ad24d232272a 100644 --- a/x-pack/plugins/alerting/server/routes/lib/index.ts +++ b/x-pack/plugins/alerting/server/routes/lib/index.ts @@ -18,7 +18,7 @@ export type { } from './rewrite_request_case'; export { verifyAccessAndContext } from './verify_access_and_context'; export { countUsageOfPredefinedIds } from './count_usage_of_predefined_ids'; -export { rewriteActionsReq, rewriteActionsRes } from './rewrite_actions'; +export { rewriteActionsReq, rewriteSystemActionsReq } from './rewrite_actions'; export { actionsSchema } from './actions_schema'; export { rewriteRule, rewriteRuleLastRun } from './rewrite_rule'; export { rewriteNamespaces } from './rewrite_namespaces'; diff --git a/x-pack/plugins/alerting/server/routes/lib/rewrite_actions.test.ts b/x-pack/plugins/alerting/server/routes/lib/rewrite_actions.test.ts index 61dc9282bbfa1..69083a3b2013f 100644 --- a/x-pack/plugins/alerting/server/routes/lib/rewrite_actions.test.ts +++ b/x-pack/plugins/alerting/server/routes/lib/rewrite_actions.test.ts @@ -5,63 +5,10 @@ * 2.0. */ -import { rewriteActionsReq, rewriteActionsRes } from './rewrite_actions'; +import { rewriteActionsReq, rewriteSystemActionsReq } from './rewrite_actions'; -describe('rewrite Actions', () => { - describe('rewriteActionsRes', () => { - it('rewrites the actions response correctly', () => { - expect( - rewriteActionsRes([ - { - uuid: '111', - group: 'default', - id: '1', - actionTypeId: '2', - params: { foo: 'bar' }, - frequency: { - summary: true, - notifyWhen: 'onThrottleInterval', - throttle: '1h', - }, - alertsFilter: { - query: { - kql: 'test:1s', - dsl: '{test:1}', - filters: [], - }, - timeframe: { - days: [1, 2, 3], - timezone: 'UTC', - hours: { - start: '00:00', - end: '15:00', - }, - }, - }, - }, - ]) - ).toEqual([ - { - alerts_filter: { - query: { dsl: '{test:1}', kql: 'test:1s', filters: [] }, - timeframe: { - days: [1, 2, 3], - hours: { end: '15:00', start: '00:00' }, - timezone: 'UTC', - }, - }, - connector_type_id: '2', - frequency: { notify_when: 'onThrottleInterval', summary: true, throttle: '1h' }, - group: 'default', - id: '1', - params: { foo: 'bar' }, - uuid: '111', - }, - ]); - }); - }); - - describe('rewriteActionsReq', () => { +describe('rewriteActionsReq', () => { + it('should rewrite actions correctly', () => { expect( rewriteActionsReq([ { @@ -121,3 +68,23 @@ describe('rewrite Actions', () => { ]); }); }); + +describe('rewriteSystemActionsReq', () => { + it('should rewrite system actions correctly', () => { + expect( + rewriteSystemActionsReq([ + { + uuid: '111', + id: 'system-1', + params: { foo: 'bar' }, + }, + ]) + ).toEqual([ + { + uuid: '111', + id: 'system-1', + params: { foo: 'bar' }, + }, + ]); + }); +}); diff --git a/x-pack/plugins/alerting/server/routes/lib/rewrite_actions.ts b/x-pack/plugins/alerting/server/routes/lib/rewrite_actions.ts index 96c2275d113a2..00cd7cbd45c3f 100644 --- a/x-pack/plugins/alerting/server/routes/lib/rewrite_actions.ts +++ b/x-pack/plugins/alerting/server/routes/lib/rewrite_actions.ts @@ -6,12 +6,11 @@ */ import { TypeOf } from '@kbn/config-schema/src/types/object_type'; import { omit } from 'lodash'; -import { NormalizedAlertAction } from '../../rules_client'; -import { RuleAction } from '../../types'; -import { actionsSchema } from './actions_schema'; +import { NormalizedAlertAction, NormalizedSystemAction } from '../../rules_client'; +import { actionsSchema, systemActionsSchema } from './actions_schema'; export const rewriteActionsReq = ( - actions?: TypeOf + actions: TypeOf ): NormalizedAlertAction[] => { if (!actions) return []; @@ -23,12 +22,17 @@ export const rewriteActionsReq = ( ...action }) => { return { - ...action, + group: action.group ?? 'default', + id: action.id, + params: action.params, + ...(action.uuid ? { uuid: action.uuid } : {}), ...(typeof useAlertDataForTemplate !== 'undefined' ? { useAlertDataForTemplate } : {}), ...(frequency ? { frequency: { ...omit(frequency, 'notify_when'), + summary: frequency.summary, + throttle: frequency.throttle, notifyWhen: frequency.notify_when, }, } @@ -39,25 +43,16 @@ export const rewriteActionsReq = ( ); }; -export const rewriteActionsRes = (actions?: RuleAction[]) => { - const rewriteFrequency = ({ notifyWhen, ...rest }: NonNullable) => ({ - ...rest, - notify_when: notifyWhen, - }); +export const rewriteSystemActionsReq = ( + actions: TypeOf +): NormalizedSystemAction[] => { if (!actions) return []; - return actions.map( - ({ actionTypeId, frequency, alertsFilter, useAlertDataForTemplate, ...action }) => ({ - ...action, - connector_type_id: actionTypeId, - ...(typeof useAlertDataForTemplate !== 'undefined' - ? { use_alert_data_for_template: useAlertDataForTemplate } - : {}), - ...(frequency ? { frequency: rewriteFrequency(frequency) } : {}), - ...(alertsFilter - ? { - alerts_filter: alertsFilter, - } - : {}), - }) - ); + + return actions.map((action) => { + return { + id: action.id, + params: action.params, + ...(action.uuid ? { uuid: action.uuid } : {}), + }; + }); }; diff --git a/x-pack/plugins/alerting/server/routes/lib/rewrite_rule.test.ts b/x-pack/plugins/alerting/server/routes/lib/rewrite_rule.test.ts index 4ad0dc817fbbe..854d6cc4294a5 100644 --- a/x-pack/plugins/alerting/server/routes/lib/rewrite_rule.test.ts +++ b/x-pack/plugins/alerting/server/routes/lib/rewrite_rule.test.ts @@ -35,16 +35,22 @@ const sampleRule: SanitizedRule & { activeSnoozes?: string[] } = actions: [ { group: 'default', - id: 'aaa', - actionTypeId: 'bbb', + id: '1001', + actionTypeId: '.test-system-action', params: {}, frequency: { summary: false, notifyWhen: 'onThrottleInterval', throttle: '1m', }, - // @ts-expect-error upgrade typescript v4.9.5 - alertsFilter: { query: { kql: 'test:1', dsl: '{}', filters: [] } }, + alertsFilter: { query: { kql: 'test:1', filters: [] } }, + }, + ], + systemActions: [ + { + id: 'ccc', + actionTypeId: 'ddd', + params: {}, }, ], scheduledTaskId: 'xyz456', @@ -82,13 +88,31 @@ describe('rewriteRule', () => { }); it('should rewrite actions correctly', () => { const rewritten = rewriteRule(sampleRule); - for (const action of rewritten.actions) { - expect(Object.keys(action)).toEqual( - expect.arrayContaining(['group', 'id', 'connector_type_id', 'params', 'frequency']) - ); - expect(Object.keys(action.frequency!)).toEqual( - expect.arrayContaining(['summary', 'notify_when', 'throttle']) - ); - } + expect(rewritten.actions).toMatchInlineSnapshot(` + Array [ + Object { + "alerts_filter": Object { + "query": Object { + "filters": Array [], + "kql": "test:1", + }, + }, + "connector_type_id": ".test-system-action", + "frequency": Object { + "notify_when": "onThrottleInterval", + "summary": false, + "throttle": "1m", + }, + "group": "default", + "id": "1001", + "params": Object {}, + }, + Object { + "connector_type_id": "ddd", + "id": "ccc", + "params": Object {}, + }, + ] + `); }); }); diff --git a/x-pack/plugins/alerting/server/routes/lib/rewrite_rule.ts b/x-pack/plugins/alerting/server/routes/lib/rewrite_rule.ts index d0e59278b13c5..a95e477d55ae1 100644 --- a/x-pack/plugins/alerting/server/routes/lib/rewrite_rule.ts +++ b/x-pack/plugins/alerting/server/routes/lib/rewrite_rule.ts @@ -31,6 +31,7 @@ export const rewriteRule = ({ mutedInstanceIds, executionStatus, actions, + systemActions, scheduledTaskId, snoozeSchedule, isSnoozedUntil, @@ -39,45 +40,71 @@ export const rewriteRule = ({ nextRun, alertDelay, ...rest -}: SanitizedRule & { activeSnoozes?: string[] }) => ({ - ...rest, - rule_type_id: alertTypeId, - created_by: createdBy, - updated_by: updatedBy, - created_at: createdAt, - updated_at: updatedAt, - api_key_owner: apiKeyOwner, - notify_when: notifyWhen, - mute_all: muteAll, - muted_alert_ids: mutedInstanceIds, - scheduled_task_id: scheduledTaskId, - snooze_schedule: snoozeSchedule, - ...(isSnoozedUntil != null ? { is_snoozed_until: isSnoozedUntil } : {}), - ...(activeSnoozes != null ? { active_snoozes: activeSnoozes } : {}), - execution_status: executionStatus && { - ...omit(executionStatus, 'lastExecutionDate', 'lastDuration'), - last_execution_date: executionStatus.lastExecutionDate, - last_duration: executionStatus.lastDuration, - }, - actions: actions.map(({ group, id, actionTypeId, params, frequency, uuid, alertsFilter }) => ({ - group, - id, - params, - connector_type_id: actionTypeId, - ...(frequency - ? { - frequency: { - summary: frequency.summary, - notify_when: frequency.notifyWhen, - throttle: frequency.throttle, - }, - } - : {}), - ...(uuid && { uuid }), - ...(alertsFilter && { alerts_filter: alertsFilter }), - })), - ...(lastRun ? { last_run: rewriteRuleLastRun(lastRun) } : {}), - ...(nextRun ? { next_run: nextRun } : {}), - ...(apiKeyCreatedByUser !== undefined ? { api_key_created_by_user: apiKeyCreatedByUser } : {}), - ...(alertDelay !== undefined ? { alert_delay: alertDelay } : {}), -}); +}: SanitizedRule & { activeSnoozes?: string[] }) => { + const actionsTemp: unknown[] = []; + actions.forEach((action) => { + const { + id, + actionTypeId, + params, + uuid, + useAlertDataForTemplate, + group, + frequency, + alertsFilter, + } = action; + actionsTemp.push({ + group, + id, + params, + connector_type_id: actionTypeId, + ...(frequency + ? { + frequency: { + summary: frequency.summary, + notify_when: frequency.notifyWhen, + throttle: frequency.throttle, + }, + } + : {}), + ...(uuid && { uuid }), + ...(alertsFilter && { alerts_filter: alertsFilter }), + ...(typeof useAlertDataForTemplate !== 'undefined' + ? { use_alert_data_for_template: useAlertDataForTemplate } + : {}), + }); + }); + (systemActions ?? []).forEach((systemAction) => { + const { actionTypeId, ...restSystemAction } = systemAction; + actionsTemp.push({ + ...restSystemAction, + connector_type_id: actionTypeId, + }); + }); + return { + ...rest, + rule_type_id: alertTypeId, + created_by: createdBy, + updated_by: updatedBy, + created_at: createdAt, + updated_at: updatedAt, + api_key_owner: apiKeyOwner, + notify_when: notifyWhen, + mute_all: muteAll, + muted_alert_ids: mutedInstanceIds, + scheduled_task_id: scheduledTaskId, + snooze_schedule: snoozeSchedule, + ...(isSnoozedUntil != null ? { is_snoozed_until: isSnoozedUntil } : {}), + ...(activeSnoozes != null ? { active_snoozes: activeSnoozes } : {}), + execution_status: executionStatus && { + ...omit(executionStatus, 'lastExecutionDate', 'lastDuration'), + last_execution_date: executionStatus.lastExecutionDate, + last_duration: executionStatus.lastDuration, + }, + actions: actionsTemp, + ...(lastRun ? { last_run: rewriteRuleLastRun(lastRun) } : {}), + ...(nextRun ? { next_run: nextRun } : {}), + ...(apiKeyCreatedByUser !== undefined ? { api_key_created_by_user: apiKeyCreatedByUser } : {}), + ...(alertDelay !== undefined ? { alert_delay: alertDelay } : {}), + }; +}; diff --git a/x-pack/plugins/alerting/server/routes/lib/validate_required_group_in_default_actions.test.ts b/x-pack/plugins/alerting/server/routes/lib/validate_required_group_in_default_actions.test.ts new file mode 100644 index 0000000000000..6e30fbe312585 --- /dev/null +++ b/x-pack/plugins/alerting/server/routes/lib/validate_required_group_in_default_actions.test.ts @@ -0,0 +1,30 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { validateRequiredGroupInDefaultActions } from './validate_required_group_in_default_actions'; + +describe('validateRequiredGroupInDefaultActions', () => { + const isSystemAction = jest.fn().mockImplementation((id) => id === 'system_action-id'); + + it('throws an error if the action is missing the group', () => { + expect(() => + validateRequiredGroupInDefaultActions([{ id: 'test' }], isSystemAction) + ).toThrowErrorMatchingInlineSnapshot(`"Group is not defined in action test"`); + }); + + it('does not throw an error if the action has the group', () => { + expect(() => + validateRequiredGroupInDefaultActions([{ id: 'test', group: 'default' }], isSystemAction) + ).not.toThrow(); + }); + + it('does not throw an error if the action is a system action and is missing the group', () => { + expect(() => + validateRequiredGroupInDefaultActions([{ id: 'system_action-id' }], isSystemAction) + ).not.toThrow(); + }); +}); diff --git a/x-pack/plugins/alerting/server/routes/lib/validate_required_group_in_default_actions.ts b/x-pack/plugins/alerting/server/routes/lib/validate_required_group_in_default_actions.ts new file mode 100644 index 0000000000000..f34b2a7046832 --- /dev/null +++ b/x-pack/plugins/alerting/server/routes/lib/validate_required_group_in_default_actions.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 Boom from '@hapi/boom'; + +export const validateRequiredGroupInDefaultActions = ( + actions: Array<{ id: string; group?: string }>, + isSystemAction: (id: string) => boolean +) => { + const defaultActions = actions.filter((action) => !isSystemAction(action.id)); + + for (const action of defaultActions) { + if (!action.group) { + throw Boom.badRequest(`Group is not defined in action ${action.id}`); + } + } +}; diff --git a/x-pack/plugins/alerting/server/routes/rule/apis/bulk_delete/bulk_delete_rules_route.test.ts b/x-pack/plugins/alerting/server/routes/rule/apis/bulk_delete/bulk_delete_rules_route.test.ts index 21099f6ba6086..3f2a31ac6cc96 100644 --- a/x-pack/plugins/alerting/server/routes/rule/apis/bulk_delete/bulk_delete_rules_route.test.ts +++ b/x-pack/plugins/alerting/server/routes/rule/apis/bulk_delete/bulk_delete_rules_route.test.ts @@ -6,13 +6,14 @@ */ import { httpServiceMock } from '@kbn/core/server/mocks'; - +import { actionsClientMock } from '@kbn/actions-plugin/server/mocks'; import { bulkDeleteRulesRoute } from './bulk_delete_rules_route'; import { licenseStateMock } from '../../../../lib/license_state.mock'; import { mockHandlerArguments } from '../../../_mock_handler_arguments'; import { rulesClientMock } from '../../../../rules_client.mock'; import { RuleTypeDisabledError } from '../../../../lib/errors/rule_type_disabled'; import { verifyApiAccess } from '../../../../lib/license_api_access'; +import { RuleAction, RuleSystemAction, SanitizedRule } from '../../../../types'; const rulesClient = rulesClientMock.create(); @@ -123,4 +124,119 @@ describe('bulkDeleteRulesRoute', () => { expect(res.forbidden).toHaveBeenCalledWith({ body: { message: 'Fail' } }); }); + + describe('actions', () => { + const mockedRule: SanitizedRule<{}> = { + id: '1', + alertTypeId: '1', + schedule: { interval: '10s' }, + params: { + bar: true, + }, + createdAt: new Date(), + updatedAt: new Date(), + actions: [ + { + group: 'default', + id: '2', + actionTypeId: 'test', + params: { + foo: true, + }, + uuid: '123-456', + }, + ], + consumer: 'bar', + name: 'abc', + tags: ['foo'], + enabled: true, + muteAll: false, + notifyWhen: 'onActionGroupChange', + createdBy: '', + updatedBy: '', + apiKeyOwner: '', + throttle: '30s', + mutedInstanceIds: [], + executionStatus: { + status: 'unknown', + lastExecutionDate: new Date('2020-08-20T19:23:38Z'), + }, + revision: 0, + }; + + const action: RuleAction = { + actionTypeId: 'test', + group: 'default', + id: '2', + params: { + foo: true, + }, + uuid: '123-456', + }; + + const systemAction: RuleSystemAction = { + actionTypeId: 'test-2', + id: 'system_action-id', + params: { + foo: true, + }, + uuid: '123-456', + }; + + const mockedRules: Array> = [ + { ...mockedRule, actions: [action], systemActions: [systemAction] }, + ]; + + const bulkDeleteActionsResult = { + rules: mockedRules, + errors: [], + total: 1, + taskIdsFailedToBeDeleted: [], + }; + + it('merges actions and systemActions correctly before sending the response', async () => { + const licenseState = licenseStateMock.create(); + const router = httpServiceMock.createRouter(); + const actionsClient = actionsClientMock.create(); + actionsClient.isSystemAction.mockImplementation((id: string) => id === 'system_action-id'); + + bulkDeleteRulesRoute({ router, licenseState }); + const [_, handler] = router.patch.mock.calls[0]; + + rulesClient.bulkDeleteRules.mockResolvedValueOnce(bulkDeleteActionsResult); + + const [context, req, res] = mockHandlerArguments( + { rulesClient, actionsClient }, + { + body: bulkDeleteRequest, + }, + ['ok'] + ); + + const routeRes = await handler(context, req, res); + + // @ts-expect-error: body exists + expect(routeRes.body.systemActions).toBeUndefined(); + // @ts-expect-error: body exists + expect(routeRes.body.rules[0].actions).toEqual([ + { + connector_type_id: 'test', + group: 'default', + id: '2', + params: { + foo: true, + }, + uuid: '123-456', + }, + { + connector_type_id: 'test-2', + id: 'system_action-id', + params: { + foo: true, + }, + uuid: '123-456', + }, + ]); + }); + }); }); diff --git a/x-pack/plugins/alerting/server/routes/rule/apis/bulk_disable/bulk_disable_rules_route.test.ts b/x-pack/plugins/alerting/server/routes/rule/apis/bulk_disable/bulk_disable_rules_route.test.ts index a53501060a39f..cd71a78a5f51c 100644 --- a/x-pack/plugins/alerting/server/routes/rule/apis/bulk_disable/bulk_disable_rules_route.test.ts +++ b/x-pack/plugins/alerting/server/routes/rule/apis/bulk_disable/bulk_disable_rules_route.test.ts @@ -13,6 +13,8 @@ import { mockHandlerArguments } from '../../../_mock_handler_arguments'; import { rulesClientMock } from '../../../../rules_client.mock'; import { RuleTypeDisabledError } from '../../../../lib/errors/rule_type_disabled'; import { verifyApiAccess } from '../../../../lib/license_api_access'; +import { actionsClientMock } from '@kbn/actions-plugin/server/mocks'; +import { RuleAction, RuleSystemAction, SanitizedRule } from '../../../../types'; const rulesClient = rulesClientMock.create(); @@ -123,4 +125,119 @@ describe('bulkDisableRulesRoute', () => { expect(res.forbidden).toHaveBeenCalledWith({ body: { message: 'Fail' } }); }); + + describe('actions', () => { + const mockedRule: SanitizedRule<{}> = { + id: '1', + alertTypeId: '1', + schedule: { interval: '10s' }, + params: { + bar: true, + }, + createdAt: new Date(), + updatedAt: new Date(), + actions: [ + { + group: 'default', + id: '2', + actionTypeId: 'test', + params: { + foo: true, + }, + uuid: '123-456', + }, + ], + consumer: 'bar', + name: 'abc', + tags: ['foo'], + enabled: false, + muteAll: false, + notifyWhen: 'onActionGroupChange', + createdBy: '', + updatedBy: '', + apiKeyOwner: '', + throttle: '30s', + mutedInstanceIds: [], + executionStatus: { + status: 'unknown', + lastExecutionDate: new Date('2020-08-20T19:23:38Z'), + }, + revision: 0, + }; + + const action: RuleAction = { + actionTypeId: 'test', + group: 'default', + id: '2', + params: { + foo: true, + }, + uuid: '123-456', + }; + + const systemAction: RuleSystemAction = { + actionTypeId: 'test-2', + id: 'system_action-id', + params: { + foo: true, + }, + uuid: '123-456', + }; + + const mockedRules: Array> = [ + { ...mockedRule, actions: [action], systemActions: [systemAction] }, + ]; + + const bulkDisableActionsResult = { + rules: mockedRules, + errors: [], + total: 1, + skipped: [], + }; + + it('merges actions and systemActions correctly before sending the response', async () => { + const licenseState = licenseStateMock.create(); + const router = httpServiceMock.createRouter(); + const actionsClient = actionsClientMock.create(); + actionsClient.isSystemAction.mockImplementation((id: string) => id === 'system_action-id'); + + bulkDisableRulesRoute({ router, licenseState }); + const [_, handler] = router.patch.mock.calls[0]; + + rulesClient.bulkDisableRules.mockResolvedValueOnce(bulkDisableActionsResult); + + const [context, req, res] = mockHandlerArguments( + { rulesClient, actionsClient }, + { + body: bulkDisableRequest, + }, + ['ok'] + ); + + const routeRes = await handler(context, req, res); + + // @ts-expect-error: body exists + expect(routeRes.body.systemActions).toBeUndefined(); + // @ts-expect-error: body exists + expect(routeRes.body.rules[0].actions).toEqual([ + { + connector_type_id: 'test', + group: 'default', + id: '2', + params: { + foo: true, + }, + uuid: '123-456', + }, + { + connector_type_id: 'test-2', + id: 'system_action-id', + params: { + foo: true, + }, + uuid: '123-456', + }, + ]); + }); + }); }); diff --git a/x-pack/plugins/alerting/server/routes/rule/apis/bulk_edit/bulk_edit_rules_route.test.ts b/x-pack/plugins/alerting/server/routes/rule/apis/bulk_edit/bulk_edit_rules_route.test.ts index 1b1ed454c5207..82480acf779f9 100644 --- a/x-pack/plugins/alerting/server/routes/rule/apis/bulk_edit/bulk_edit_rules_route.test.ts +++ b/x-pack/plugins/alerting/server/routes/rule/apis/bulk_edit/bulk_edit_rules_route.test.ts @@ -13,7 +13,9 @@ import { verifyApiAccess } from '../../../../lib/license_api_access'; import { RuleTypeDisabledError } from '../../../../lib/errors/rule_type_disabled'; import { mockHandlerArguments } from '../../../_mock_handler_arguments'; import { rulesClientMock } from '../../../../rules_client.mock'; -import { SanitizedRule } from '../../../../types'; +import { RuleAction, RuleSystemAction, SanitizedRule } from '../../../../types'; +import { actionsClientMock } from '@kbn/actions-plugin/server/mocks'; +import { omit } from 'lodash'; const rulesClient = rulesClientMock.create(); jest.mock('../../../../lib/license_api_access', () => ({ @@ -189,4 +191,176 @@ describe('bulkEditRulesRoute', () => { expect(res.forbidden).toHaveBeenCalledWith({ body: { message: 'Fail' } }); }); + + describe('actions', () => { + const action: RuleAction = { + actionTypeId: 'test', + group: 'default', + id: '2', + params: { + foo: true, + }, + uuid: '123-456', + }; + + const systemAction: RuleSystemAction = { + actionTypeId: 'test-2', + id: 'system_action-id', + params: { + foo: true, + }, + uuid: '123-456', + }; + + const mockedActionAlerts: Array> = [ + { ...mockedAlert, actions: [action], systemActions: [systemAction] }, + ]; + + const bulkEditActionsRequest = { + filter: '', + operations: [ + { + operation: 'add', + field: 'actions', + value: [action, systemAction], + }, + ], + }; + + const bulkEditActionsResult = { rules: mockedActionAlerts, errors: [], total: 1, skipped: [] }; + + it('passes the system actions correctly to the rules client', async () => { + const licenseState = licenseStateMock.create(); + const router = httpServiceMock.createRouter(); + const actionsClient = actionsClientMock.create(); + actionsClient.isSystemAction.mockImplementation((id: string) => id === 'system_action-id'); + + bulkEditInternalRulesRoute(router, licenseState); + + const [_, handler] = router.post.mock.calls[0]; + + rulesClient.bulkEdit.mockResolvedValueOnce(bulkEditActionsResult); + + const [context, req, res] = mockHandlerArguments( + { rulesClient, actionsClient }, + { + body: bulkEditActionsRequest, + }, + ['ok'] + ); + + await handler(context, req, res); + + expect(rulesClient.bulkEdit.mock.calls[0][0]).toMatchInlineSnapshot(` + Object { + "filter": "", + "ids": undefined, + "operations": Array [ + Object { + "field": "actions", + "operation": "add", + "value": Array [ + Object { + "frequency": undefined, + "group": "default", + "id": "2", + "params": Object { + "foo": true, + }, + "uuid": "123-456", + }, + Object { + "id": "system_action-id", + "params": Object { + "foo": true, + }, + "uuid": "123-456", + }, + ], + }, + ], + } + `); + }); + + it('transforms the system actions in the response of the rules client correctly', async () => { + const licenseState = licenseStateMock.create(); + const router = httpServiceMock.createRouter(); + const actionsClient = actionsClientMock.create(); + actionsClient.isSystemAction.mockImplementation((id: string) => id === 'system_action-id'); + + bulkEditInternalRulesRoute(router, licenseState); + + const [_, handler] = router.post.mock.calls[0]; + + rulesClient.bulkEdit.mockResolvedValueOnce(bulkEditActionsResult); + + const [context, req, res] = mockHandlerArguments( + { rulesClient, actionsClient }, + { + body: bulkEditActionsRequest, + }, + ['ok'] + ); + + const routeRes = await handler(context, req, res); + + // @ts-expect-error: body exists + expect(routeRes.body.systemActions).toBeUndefined(); + // @ts-expect-error: body exists + expect(routeRes.body.rules[0].actions).toEqual([ + { + connector_type_id: 'test', + group: 'default', + id: '2', + params: { + foo: true, + }, + uuid: '123-456', + }, + { + connector_type_id: 'test-2', + id: 'system_action-id', + params: { + foo: true, + }, + uuid: '123-456', + }, + ]); + }); + + it('throws an error if the default action does not specifies a group', async () => { + const licenseState = licenseStateMock.create(); + const router = httpServiceMock.createRouter(); + const actionsClient = actionsClientMock.create(); + actionsClient.isSystemAction.mockImplementation((id: string) => id === 'system_action-id'); + + bulkEditInternalRulesRoute(router, licenseState); + + const [_, handler] = router.post.mock.calls[0]; + + rulesClient.bulkEdit.mockResolvedValueOnce(bulkEditActionsResult); + + const [context, req, res] = mockHandlerArguments( + { rulesClient, actionsClient }, + { + body: { + ...bulkEditActionsRequest, + operations: [ + { + operation: 'add', + field: 'actions', + value: [omit(action, 'group')], + }, + ], + }, + }, + ['ok'] + ); + + await expect(handler(context, req, res)).rejects.toThrowErrorMatchingInlineSnapshot( + `"Group is not defined in action 2"` + ); + }); + }); }); diff --git a/x-pack/plugins/alerting/server/routes/rule/apis/bulk_edit/bulk_edit_rules_route.ts b/x-pack/plugins/alerting/server/routes/rule/apis/bulk_edit/bulk_edit_rules_route.ts index ae39ceba1ceb3..ef29536014b0c 100644 --- a/x-pack/plugins/alerting/server/routes/rule/apis/bulk_edit/bulk_edit_rules_route.ts +++ b/x-pack/plugins/alerting/server/routes/rule/apis/bulk_edit/bulk_edit_rules_route.ts @@ -20,6 +20,8 @@ import { Rule } from '../../../../application/rule/types'; import type { RuleParamsV1 } from '../../../../../common/routes/rule/response'; import { transformRuleToRuleResponseV1 } from '../../transforms'; +import { transformOperationsV1 } from './transforms'; +import { validateRequiredGroupInDefaultActions } from '../../../lib/validate_required_group_in_default_actions'; interface BuildBulkEditRulesRouteParams { licenseState: ILicenseState; @@ -39,15 +41,24 @@ const buildBulkEditRulesRoute = ({ licenseState, path, router }: BuildBulkEditRu router.handleLegacyErrors( verifyAccessAndContext(licenseState, async function (context, req, res) { const rulesClient = (await context.alerting).getRulesClient(); - const bulkEditData: BulkEditRulesRequestBodyV1 = req.body; + const actionsClient = (await context.actions).getActionsClient(); + const bulkEditData: BulkEditRulesRequestBodyV1 = req.body; const { filter, operations, ids } = bulkEditData; try { + validateRequiredGroupInDefaultActionsInOperations( + operations ?? [], + (connectorId: string) => actionsClient.isSystemAction(connectorId) + ); + const bulkEditResults = await rulesClient.bulkEdit({ filter, ids, - operations, + operations: transformOperationsV1({ + operations, + isSystemAction: (connectorId: string) => actionsClient.isSystemAction(connectorId), + }), }); const resultBody: BulkEditRulesResponseV1 = { @@ -82,3 +93,14 @@ export const bulkEditInternalRulesRoute = ( path: `${INTERNAL_BASE_ALERTING_API_PATH}/rules/_bulk_edit`, router, }); + +const validateRequiredGroupInDefaultActionsInOperations = ( + operations: BulkEditRulesRequestBodyV1['operations'], + isSystemAction: (connectorId: string) => boolean +) => { + for (const operation of operations) { + if (operation.field === 'actions') { + validateRequiredGroupInDefaultActions(operation.value, isSystemAction); + } + } +}; diff --git a/x-pack/plugins/alerting/common/routes/alerts_filter_query/constants/v1.ts b/x-pack/plugins/alerting/server/routes/rule/apis/bulk_edit/transforms/index.ts similarity index 57% rename from x-pack/plugins/alerting/common/routes/alerts_filter_query/constants/v1.ts rename to x-pack/plugins/alerting/server/routes/rule/apis/bulk_edit/transforms/index.ts index bce6890c22f2c..e7d1a1dc43478 100644 --- a/x-pack/plugins/alerting/common/routes/alerts_filter_query/constants/v1.ts +++ b/x-pack/plugins/alerting/server/routes/rule/apis/bulk_edit/transforms/index.ts @@ -5,9 +5,6 @@ * 2.0. */ -export const filterStateStore = { - APP_STATE: 'appState', - GLOBAL_STATE: 'globalState', -} as const; +export { transformOperations } from './transform_operations/latest'; -export type FilterStateStore = typeof filterStateStore[keyof typeof filterStateStore]; +export { transformOperations as transformOperationsV1 } from './transform_operations/v1'; diff --git a/x-pack/plugins/alerting/common/routes/alerts_filter_query/constants/latest.ts b/x-pack/plugins/alerting/server/routes/rule/apis/bulk_edit/transforms/transform_operations/latest.ts similarity index 74% rename from x-pack/plugins/alerting/common/routes/alerts_filter_query/constants/latest.ts rename to x-pack/plugins/alerting/server/routes/rule/apis/bulk_edit/transforms/transform_operations/latest.ts index 6c32b4867cc0d..25300c97a6d2e 100644 --- a/x-pack/plugins/alerting/common/routes/alerts_filter_query/constants/latest.ts +++ b/x-pack/plugins/alerting/server/routes/rule/apis/bulk_edit/transforms/transform_operations/latest.ts @@ -5,5 +5,4 @@ * 2.0. */ -export { filterStateStore } from './v1'; -export type { FilterStateStore } from './v1'; +export * from './v1'; diff --git a/x-pack/plugins/alerting/server/routes/rule/apis/bulk_edit/transforms/transform_operations/v1.test.ts b/x-pack/plugins/alerting/server/routes/rule/apis/bulk_edit/transforms/transform_operations/v1.test.ts new file mode 100644 index 0000000000000..23ec499a76c9e --- /dev/null +++ b/x-pack/plugins/alerting/server/routes/rule/apis/bulk_edit/transforms/transform_operations/v1.test.ts @@ -0,0 +1,65 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { transformOperations } from './v1'; + +describe('transformOperations', () => { + const isSystemAction = (id: string) => id === 'my-system-action-id'; + + describe('actions', () => { + const defaultAction = { + id: 'default-action', + params: {}, + }; + + const systemAction = { + id: 'my-system-action-id', + params: {}, + }; + + it('transform the actions correctly', async () => { + expect( + transformOperations({ + operations: [ + { field: 'actions', operation: 'add', value: [defaultAction, systemAction] }, + ], + isSystemAction, + }) + ).toEqual([ + { + field: 'actions', + operation: 'add', + value: [ + { + group: 'default', + id: 'default-action', + params: {}, + }, + { id: 'my-system-action-id', params: {} }, + ], + }, + ]); + }); + + it('returns an empty array if the operations are empty', async () => { + expect( + transformOperations({ + operations: [], + isSystemAction, + }) + ).toEqual([]); + }); + + it('returns an empty array if the operations are undefined', async () => { + expect( + transformOperations({ + isSystemAction, + }) + ).toEqual([]); + }); + }); +}); diff --git a/x-pack/plugins/alerting/server/routes/rule/apis/bulk_edit/transforms/transform_operations/v1.ts b/x-pack/plugins/alerting/server/routes/rule/apis/bulk_edit/transforms/transform_operations/v1.ts new file mode 100644 index 0000000000000..844617ab2700f --- /dev/null +++ b/x-pack/plugins/alerting/server/routes/rule/apis/bulk_edit/transforms/transform_operations/v1.ts @@ -0,0 +1,51 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { BulkEditOperation } from '../../../../../../application/rule/methods/bulk_edit'; +import { BulkEditRulesRequestBodyV1 } from '../../../../../../../common/routes/rule/apis/bulk_edit'; + +export const transformOperations = ({ + operations, + isSystemAction, +}: { + operations?: BulkEditRulesRequestBodyV1['operations']; + isSystemAction: (connectorId: string) => boolean; +}): BulkEditOperation[] => { + if (operations == null || operations.length === 0) { + return []; + } + + return operations.map((operation) => { + if (operation.field !== 'actions') { + return operation; + } + + const actions = operation.value.map((action) => { + if (isSystemAction(action.id)) { + return { + id: action.id, + params: action.params, + uuid: action.uuid, + }; + } + + return { + id: action.id, + group: action.group ?? 'default', + params: action.params, + uuid: action.uuid, + frequency: action.frequency, + }; + }); + + return { + field: operation.field, + operation: operation.operation, + value: actions, + }; + }); +}; diff --git a/x-pack/plugins/alerting/server/routes/rule/apis/create/create_rule_route.test.ts b/x-pack/plugins/alerting/server/routes/rule/apis/create/create_rule_route.test.ts index 512436475a395..47f05a90f9c7b 100644 --- a/x-pack/plugins/alerting/server/routes/rule/apis/create/create_rule_route.test.ts +++ b/x-pack/plugins/alerting/server/routes/rule/apis/create/create_rule_route.test.ts @@ -5,7 +5,7 @@ * 2.0. */ -import { pick } from 'lodash'; +import { omit, pick } from 'lodash'; import { createRuleRoute } from './create_rule_route'; import { httpServiceMock } from '@kbn/core/server/mocks'; import { licenseStateMock } from '../../../../lib/license_state.mock'; @@ -14,10 +14,10 @@ import { mockHandlerArguments } from '../../../_mock_handler_arguments'; import type { CreateRuleRequestBodyV1 } from '../../../../../common/routes/rule/apis/create'; import { rulesClientMock } from '../../../../rules_client.mock'; import { RuleTypeDisabledError } from '../../../../lib'; -import { AsApiContract } from '../../../lib'; -import { SanitizedRule } from '../../../../types'; +import { RuleAction, RuleSystemAction, SanitizedRule } from '../../../../types'; import { encryptedSavedObjectsMock } from '@kbn/encrypted-saved-objects-plugin/server/mocks'; import { usageCountersServiceMock } from '@kbn/usage-collection-plugin/server/usage_counters/usage_counters_service.mock'; +import { actionsClientMock } from '@kbn/actions-plugin/server/mocks'; const rulesClient = rulesClientMock.create(); @@ -32,6 +32,36 @@ beforeEach(() => { describe('createRuleRoute', () => { const createdAt = new Date(); const updatedAt = new Date(); + const action: RuleAction = { + actionTypeId: 'test', + group: 'default', + id: '2', + params: { + foo: true, + }, + uuid: '123-456', + alertsFilter: { + query: { + kql: 'name:test', + dsl: '{"must": {"term": { "name": "test" }}}', + filters: [], + }, + timeframe: { + days: [1], + hours: { start: '08:00', end: '17:00' }, + timezone: 'UTC', + }, + }, + }; + + const systemAction: RuleSystemAction = { + actionTypeId: 'test-2', + id: 'system_action-id', + params: { + foo: true, + }, + uuid: '123-456', + }; const mockedAlert: SanitizedRule<{ bar: boolean }> = { alertTypeId: '1', @@ -43,30 +73,7 @@ describe('createRuleRoute', () => { bar: true, }, throttle: '30s', - actions: [ - { - actionTypeId: 'test', - group: 'default', - id: '2', - params: { - foo: true, - }, - uuid: '123-456', - alertsFilter: { - query: { - kql: 'name:test', - // @ts-expect-error upgrade typescript v4.9.5 - dsl: '{"must": {"term": { "name": "test" }}}', - filters: [], - }, - timeframe: { - days: [1], - hours: { start: '08:00', end: '17:00' }, - timezone: 'UTC', - }, - }, - }, - ], + actions: [action], enabled: true, muteAll: false, createdBy: '', @@ -90,18 +97,21 @@ describe('createRuleRoute', () => { notify_when: mockedAlert.notifyWhen, actions: [ { - group: mockedAlert.actions[0].group, + group: action.group, id: mockedAlert.actions[0].id, params: mockedAlert.actions[0].params, alerts_filter: { - query: { kql: mockedAlert.actions[0].alertsFilter!.query!.kql, filters: [] }, - timeframe: mockedAlert.actions[0].alertsFilter?.timeframe!, + query: { + kql: action.alertsFilter!.query!.kql, + filters: [], + }, + timeframe: action.alertsFilter?.timeframe!, }, }, ], }; - const createResult: AsApiContract> = { + const createResult = { ...ruleToCreate, mute_all: mockedAlert.muteAll, created_by: mockedAlert.createdBy, @@ -120,8 +130,8 @@ describe('createRuleRoute', () => { { ...ruleToCreate.actions[0], alerts_filter: { - query: mockedAlert.actions[0].alertsFilter?.query!, - timeframe: mockedAlert.actions[0].alertsFilter!.timeframe!, + query: action.alertsFilter?.query!, + timeframe: action.alertsFilter!.timeframe!, }, connector_type_id: 'test', uuid: '123-456', @@ -212,6 +222,7 @@ describe('createRuleRoute', () => { "schedule": Object { "interval": "10s", }, + "systemActions": Array [], "tags": Array [ "foo", ], @@ -328,6 +339,7 @@ describe('createRuleRoute', () => { "schedule": Object { "interval": "10s", }, + "systemActions": Array [], "tags": Array [ "foo", ], @@ -445,6 +457,7 @@ describe('createRuleRoute', () => { "schedule": Object { "interval": "10s", }, + "systemActions": Array [], "tags": Array [ "foo", ], @@ -562,6 +575,7 @@ describe('createRuleRoute', () => { "schedule": Object { "interval": "10s", }, + "systemActions": Array [], "tags": Array [ "foo", ], @@ -647,4 +661,176 @@ describe('createRuleRoute', () => { expect(res.forbidden).toHaveBeenCalledWith({ body: { message: 'Fail' } }); }); + + describe('actions', () => { + it('passes the system actions correctly to the rules client', async () => { + const licenseState = licenseStateMock.create(); + const router = httpServiceMock.createRouter(); + const encryptedSavedObjects = encryptedSavedObjectsMock.createSetup({ canEncrypt: true }); + const mockUsageCountersSetup = usageCountersServiceMock.createSetupContract(); + const mockUsageCounter = mockUsageCountersSetup.createUsageCounter('test'); + const actionsClient = actionsClientMock.create(); + actionsClient.isSystemAction.mockImplementation((id: string) => id === 'system_action-id'); + + createRuleRoute({ + router, + licenseState, + encryptedSavedObjects, + usageCounter: mockUsageCounter, + }); + + const [_, handler] = router.post.mock.calls[0]; + + rulesClient.create.mockResolvedValueOnce({ + ...mockedAlert, + actions: [action], + systemActions: [systemAction], + }); + + const [context, req, res] = mockHandlerArguments( + { rulesClient, actionsClient }, + { + body: { ...ruleToCreate, actions: [action, systemAction] }, + }, + ['ok'] + ); + + await handler(context, req, res); + + expect(rulesClient.create.mock.calls[0]).toMatchInlineSnapshot(` + Array [ + Object { + "data": Object { + "actions": Array [ + Object { + "actionTypeId": "test", + "group": "default", + "id": "2", + "params": Object { + "foo": true, + }, + "uuid": "123-456", + }, + ], + "alertTypeId": "1", + "consumer": "bar", + "enabled": true, + "name": "abc", + "notifyWhen": "onActionGroupChange", + "params": Object { + "bar": true, + }, + "schedule": Object { + "interval": "10s", + }, + "systemActions": Array [ + Object { + "actionTypeId": "test-2", + "id": "system_action-id", + "params": Object { + "foo": true, + }, + "uuid": "123-456", + }, + ], + "tags": Array [ + "foo", + ], + "throttle": "30s", + }, + "options": Object { + "id": undefined, + }, + }, + ] + `); + }); + + it('transforms the system actions in the response of the rules client correctly', async () => { + const licenseState = licenseStateMock.create(); + const router = httpServiceMock.createRouter(); + const encryptedSavedObjects = encryptedSavedObjectsMock.createSetup({ canEncrypt: true }); + const mockUsageCountersSetup = usageCountersServiceMock.createSetupContract(); + const mockUsageCounter = mockUsageCountersSetup.createUsageCounter('test'); + const actionsClient = actionsClientMock.create(); + actionsClient.isSystemAction.mockImplementation((id: string) => id === 'system_action-id'); + + createRuleRoute({ + router, + licenseState, + encryptedSavedObjects, + usageCounter: mockUsageCounter, + }); + + const [_, handler] = router.post.mock.calls[0]; + + rulesClient.create.mockResolvedValueOnce({ + ...mockedAlert, + actions: [action], + systemActions: [systemAction], + }); + + const [context, req, res] = mockHandlerArguments( + { rulesClient, actionsClient }, + { + body: ruleToCreate, + }, + ['ok'] + ); + + const routeRes = await handler(context, req, res); + + // @ts-expect-error: body exists + expect(routeRes.body.actions).toEqual([ + { + alerts_filter: { + query: { dsl: '{"must": {"term": { "name": "test" }}}', filters: [], kql: 'name:test' }, + timeframe: { days: [1], hours: { end: '17:00', start: '08:00' }, timezone: 'UTC' }, + }, + connector_type_id: 'test', + group: 'default', + id: '2', + params: { foo: true }, + uuid: '123-456', + }, + { + connector_type_id: 'test-2', + id: 'system_action-id', + params: { foo: true }, + uuid: '123-456', + }, + ]); + }); + + it('throws an error if the default action does not specifies a group', async () => { + const licenseState = licenseStateMock.create(); + const router = httpServiceMock.createRouter(); + const encryptedSavedObjects = encryptedSavedObjectsMock.createSetup({ canEncrypt: true }); + const mockUsageCountersSetup = usageCountersServiceMock.createSetupContract(); + const mockUsageCounter = mockUsageCountersSetup.createUsageCounter('test'); + const actionsClient = actionsClientMock.create(); + actionsClient.isSystemAction.mockImplementation((id: string) => id === 'system_action-id'); + + createRuleRoute({ + router, + licenseState, + encryptedSavedObjects, + usageCounter: mockUsageCounter, + }); + + const [_, handler] = router.post.mock.calls[0]; + + const [context, req, res] = mockHandlerArguments( + { rulesClient, actionsClient }, + { + body: { ...ruleToCreate, actions: [omit(action, 'group')] }, + }, + ['ok'] + ); + + await expect(handler(context, req, res)).rejects.toThrowErrorMatchingInlineSnapshot( + `"Group is not defined in action 2"` + ); + }); + }); }); diff --git a/x-pack/plugins/alerting/server/routes/rule/apis/create/create_rule_route.ts b/x-pack/plugins/alerting/server/routes/rule/apis/create/create_rule_route.ts index 6b28b64284904..aca112b85d96a 100644 --- a/x-pack/plugins/alerting/server/routes/rule/apis/create/create_rule_route.ts +++ b/x-pack/plugins/alerting/server/routes/rule/apis/create/create_rule_route.ts @@ -26,6 +26,7 @@ import type { RuleParamsV1 } from '../../../../../common/routes/rule/response'; import { Rule } from '../../../../application/rule/types'; import { transformCreateBodyV1 } from './transforms'; import { transformRuleToRuleResponseV1 } from '../../transforms'; +import { validateRequiredGroupInDefaultActions } from '../../../lib/validate_required_group_in_default_actions'; export const createRuleRoute = ({ router, licenseState, usageCounter }: RouteOptions) => { router.post( @@ -40,6 +41,7 @@ export const createRuleRoute = ({ router, licenseState, usageCounter }: RouteOpt router.handleLegacyErrors( verifyAccessAndContext(licenseState, async function (context, req, res) { const rulesClient = (await context.alerting).getRulesClient(); + const actionsClient = (await context.actions).getActionsClient(); // Assert versioned inputs const createRuleData: CreateRuleRequestBodyV1 = req.body; @@ -52,10 +54,19 @@ export const createRuleRoute = ({ router, licenseState, usageCounter }: RouteOpt }); try { + /** + * Throws an error if the group is not defined in default actions + */ + validateRequiredGroupInDefaultActions(createRuleData.actions, (connectorId: string) => + actionsClient.isSystemAction(connectorId) + ); + // TODO (http-versioning): Remove this cast, this enables us to move forward // without fixing all of other solution types const createdRule: Rule = (await rulesClient.create({ - data: transformCreateBodyV1(createRuleData), + data: transformCreateBodyV1(createRuleData, (connectorId: string) => + actionsClient.isSystemAction(connectorId) + ), options: { id: params?.id }, })) as Rule; diff --git a/x-pack/plugins/alerting/server/routes/rule/apis/create/transforms/transform_create_body/v1.test.ts b/x-pack/plugins/alerting/server/routes/rule/apis/create/transforms/transform_create_body/v1.test.ts new file mode 100644 index 0000000000000..b6165ab910864 --- /dev/null +++ b/x-pack/plugins/alerting/server/routes/rule/apis/create/transforms/transform_create_body/v1.test.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 { + CreateRuleAction, + CreateRuleRequestBodyV1, +} from '../../../../../../../common/routes/rule/apis/create'; +import { transformCreateBody } from './v1'; + +describe('Transform actions V1', () => { + const defaultAction: CreateRuleAction = { + actionTypeId: 'test', + group: 'default', + id: '2', + params: {}, + uuid: '123-456', + use_alert_data_for_template: false, + }; + + const systemAction: CreateRuleAction = { + actionTypeId: 'test-2', + id: 'system_action-id', + params: {}, + uuid: '123-456', + }; + + const rule: CreateRuleRequestBodyV1<{}> = { + rule_type_id: '1', + consumer: 'bar', + name: 'abc', + schedule: { interval: '10s' }, + tags: ['foo'], + params: {}, + throttle: '30s', + actions: [defaultAction, systemAction], + enabled: true, + notify_when: 'onActionGroupChange', + }; + + describe('transformCreateBody', () => { + const isSystemAction = jest.fn().mockImplementation((id) => id === 'system_action-id'); + + it('should transform the system actions correctly', async () => { + expect(transformCreateBody(rule, isSystemAction)).toMatchInlineSnapshot(` + Object { + "actions": Array [ + Object { + "actionTypeId": "test", + "group": "default", + "id": "2", + "params": Object {}, + "useAlertDataForTemplate": false, + "uuid": "123-456", + }, + ], + "alertTypeId": "1", + "consumer": "bar", + "enabled": true, + "name": "abc", + "notifyWhen": "onActionGroupChange", + "params": Object {}, + "schedule": Object { + "interval": "10s", + }, + "systemActions": Array [ + Object { + "actionTypeId": "test-2", + "id": "system_action-id", + "params": Object {}, + "uuid": "123-456", + }, + ], + "tags": Array [ + "foo", + ], + "throttle": "30s", + } + `); + }); + }); +}); diff --git a/x-pack/plugins/alerting/server/routes/rule/apis/create/transforms/transform_create_body/v1.ts b/x-pack/plugins/alerting/server/routes/rule/apis/create/transforms/transform_create_body/v1.ts index 5dea295c40ed7..17edc64b637f4 100644 --- a/x-pack/plugins/alerting/server/routes/rule/apis/create/transforms/transform_create_body/v1.ts +++ b/x-pack/plugins/alerting/server/routes/rule/apis/create/transforms/transform_create_body/v1.ts @@ -12,40 +12,70 @@ import type { import type { CreateRuleData } from '../../../../../../application/rule/methods/create'; import type { RuleParams } from '../../../../../../application/rule/types'; -const transformCreateBodyActions = (actions: CreateRuleActionV1[]): CreateRuleData['actions'] => { - if (!actions) return []; - - return actions.map( - ({ - frequency, - alerts_filter: alertsFilter, - use_alert_data_for_template: useAlertDataForTemplate, - ...action - }) => { - return { - group: action.group, - id: action.id, - params: action.params, - actionTypeId: action.actionTypeId, - ...(typeof useAlertDataForTemplate !== 'undefined' ? { useAlertDataForTemplate } : {}), - ...(action.uuid ? { uuid: action.uuid } : {}), - ...(frequency - ? { - frequency: { - summary: frequency.summary, - throttle: frequency.throttle, - notifyWhen: frequency.notify_when, - }, - } - : {}), - ...(alertsFilter ? { alertsFilter } : {}), - }; - } - ); +const transformCreateBodyActions = ( + actions: CreateRuleActionV1[], + isSystemAction: (connectorId: string) => boolean +): CreateRuleData['actions'] => { + const defaultActions: CreateRuleData['actions'] = []; + if (!actions) return defaultActions; + + actions + .filter((action) => !isSystemAction(action.id)) + .forEach( + ({ + frequency, + alerts_filter: alertsFilter, + use_alert_data_for_template: useAlertDataForTemplate, + ...action + }) => { + defaultActions.push({ + group: action.group ?? 'default', + id: action.id, + params: action.params, + actionTypeId: action.actionTypeId, + ...(typeof useAlertDataForTemplate !== 'undefined' ? { useAlertDataForTemplate } : {}), + ...(action.uuid ? { uuid: action.uuid } : {}), + ...(frequency + ? { + frequency: { + summary: frequency.summary, + throttle: frequency.throttle, + notifyWhen: frequency.notify_when, + }, + } + : {}), + ...(alertsFilter ? { alertsFilter } : {}), + }); + } + ); + + return defaultActions; +}; + +const transformCreateBodySystemActions = ( + actions: CreateRuleActionV1[], + isSystemAction: (connectorId: string) => boolean +): CreateRuleData['systemActions'] => { + const defaultActions: CreateRuleData['systemActions'] = []; + if (!actions) return defaultActions; + + actions + .filter((action) => isSystemAction(action.id)) + .forEach((systemAction) => { + defaultActions.push({ + id: systemAction.id, + params: systemAction.params, + actionTypeId: systemAction.actionTypeId, + ...(systemAction.uuid ? { uuid: systemAction.uuid } : {}), + }); + }); + + return defaultActions; }; export const transformCreateBody = ( - createBody: CreateRuleRequestBodyV1 + createBody: CreateRuleRequestBodyV1, + isSystemAction: (connectorId: string) => boolean ): CreateRuleData => { return { name: createBody.name, @@ -56,7 +86,8 @@ export const transformCreateBody = ( ...(createBody.throttle ? { throttle: createBody.throttle } : {}), params: createBody.params, schedule: createBody.schedule, - actions: transformCreateBodyActions(createBody.actions), + actions: transformCreateBodyActions(createBody.actions, isSystemAction), + systemActions: transformCreateBodySystemActions(createBody.actions, isSystemAction), ...(createBody.notify_when ? { notifyWhen: createBody.notify_when } : {}), ...(createBody.alert_delay ? { alertDelay: createBody.alert_delay } : {}), }; diff --git a/x-pack/plugins/alerting/server/routes/rule/apis/resolve/resolve_rule_route.test.ts b/x-pack/plugins/alerting/server/routes/rule/apis/resolve/resolve_rule_route.test.ts index 929dc6ad2ccc1..b11096cd9343b 100644 --- a/x-pack/plugins/alerting/server/routes/rule/apis/resolve/resolve_rule_route.test.ts +++ b/x-pack/plugins/alerting/server/routes/rule/apis/resolve/resolve_rule_route.test.ts @@ -13,7 +13,8 @@ import { verifyApiAccess } from '../../../../lib/license_api_access'; import { mockHandlerArguments } from '../../../_mock_handler_arguments'; import { rulesClientMock } from '../../../../rules_client.mock'; import { ResolvedRule } from '../../../../application/rule/methods/resolve/types'; -import { ResolvedSanitizedRule } from '../../../../../common'; +import { ResolvedSanitizedRule, RuleAction, RuleSystemAction } from '../../../../../common'; +import { actionsClientMock } from '@kbn/actions-plugin/server/mocks'; const rulesClient = rulesClientMock.create(); jest.mock('../../../../lib/license_api_access', () => ({ @@ -25,6 +26,26 @@ beforeEach(() => { }); describe('resolveRuleRoute', () => { + const action: RuleAction = { + group: 'default', + id: '2', + actionTypeId: 'test', + params: { + foo: true, + }, + uuid: '123-456', + useAlertDataForTemplate: false, + }; + + const systemAction: RuleSystemAction = { + actionTypeId: 'test-2', + id: 'system_action-id', + params: { + foo: true, + }, + uuid: '123-456', + }; + const mockedRule: ResolvedRule<{ bar: boolean; }> = { @@ -36,18 +57,7 @@ describe('resolveRuleRoute', () => { }, createdAt: new Date(), updatedAt: new Date(), - actions: [ - { - group: 'default', - id: '2', - actionTypeId: 'test', - params: { - foo: true, - }, - uuid: '123-456', - useAlertDataForTemplate: false, - }, - ], + actions: [action], consumer: 'bar', name: 'abc', tags: ['foo'], @@ -102,7 +112,7 @@ describe('resolveRuleRoute', () => { params: mockedRule.actions[0].params, connector_type_id: mockedRule.actions[0].actionTypeId, uuid: mockedRule.actions[0].uuid, - use_alert_data_for_template: mockedRule.actions[0].useAlertDataForTemplate, + use_alert_data_for_template: (mockedRule.actions[0] as RuleAction).useAlertDataForTemplate, }, ], outcome: 'aliasMatch', @@ -191,4 +201,56 @@ describe('resolveRuleRoute', () => { expect(verifyApiAccess).toHaveBeenCalledWith(licenseState); }); + + it('transforms the system actions in the response of the rules client correctly', async () => { + const licenseState = licenseStateMock.create(); + const router = httpServiceMock.createRouter(); + const actionsClient = actionsClientMock.create(); + actionsClient.isSystemAction.mockImplementation((id: string) => id === 'system_action-id'); + + resolveRuleRoute(router, licenseState); + const [_, handler] = router.get.mock.calls[0]; + + // TODO (http-versioning): Remove this cast, this enables us to move forward + // without fixing all of other solution types + rulesClient.resolve.mockResolvedValueOnce({ + ...mockedRule, + actions: [action], + systemActions: [systemAction], + } as ResolvedSanitizedRule); + + const [context, req, res] = mockHandlerArguments( + { rulesClient, actionsClient }, + { + params: { id: '1' }, + }, + ['ok'] + ); + + const routeRes = await handler(context, req, res); + + // @ts-expect-error: body exists + expect(routeRes.body.systemActions).toBeUndefined(); + // @ts-expect-error: body exists + expect(routeRes.body.actions).toEqual([ + { + connector_type_id: 'test', + group: 'default', + id: '2', + params: { + foo: true, + }, + use_alert_data_for_template: false, + uuid: '123-456', + }, + { + connector_type_id: 'test-2', + id: 'system_action-id', + params: { + foo: true, + }, + uuid: '123-456', + }, + ]); + }); }); diff --git a/x-pack/plugins/alerting/server/routes/rule/transforms/index.ts b/x-pack/plugins/alerting/server/routes/rule/transforms/index.ts index c589df53347f3..6e7ba66e752be 100644 --- a/x-pack/plugins/alerting/server/routes/rule/transforms/index.ts +++ b/x-pack/plugins/alerting/server/routes/rule/transforms/index.ts @@ -7,3 +7,5 @@ export { transformRuleToRuleResponse } from './transform_rule_to_rule_response/latest'; export { transformRuleToRuleResponse as transformRuleToRuleResponseV1 } from './transform_rule_to_rule_response/v1'; +export { transformRuleActions } from './transform_rule_to_rule_response/latest'; +export { transformRuleActions as transformRuleActionsV1 } from './transform_rule_to_rule_response/v1'; diff --git a/x-pack/plugins/alerting/server/routes/rule/transforms/transform_rule_to_rule_response/latest.ts b/x-pack/plugins/alerting/server/routes/rule/transforms/transform_rule_to_rule_response/latest.ts index 11e717fe2d16d..f0561497b5d17 100644 --- a/x-pack/plugins/alerting/server/routes/rule/transforms/transform_rule_to_rule_response/latest.ts +++ b/x-pack/plugins/alerting/server/routes/rule/transforms/transform_rule_to_rule_response/latest.ts @@ -5,4 +5,4 @@ * 2.0. */ -export { transformRuleToRuleResponse } from './v1'; +export { transformRuleToRuleResponse, transformRuleActions } from './v1'; diff --git a/x-pack/plugins/alerting/server/routes/rule/transforms/transform_rule_to_rule_response/v1.test.ts b/x-pack/plugins/alerting/server/routes/rule/transforms/transform_rule_to_rule_response/v1.test.ts new file mode 100644 index 0000000000000..2a5b2e77f313e --- /dev/null +++ b/x-pack/plugins/alerting/server/routes/rule/transforms/transform_rule_to_rule_response/v1.test.ts @@ -0,0 +1,102 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor 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 { RuleAction, RuleSystemAction } from '../../../../../common'; +import { transformRuleToRuleResponse } from './v1'; + +describe('transformRuleToRuleResponse', () => { + const defaultAction: RuleAction = { + id: '1', + uuid: '111', + params: { foo: 'bar' }, + group: 'default', + actionTypeId: '.test', + frequency: { notifyWhen: 'onThrottleInterval', summary: true, throttle: '1h' }, + alertsFilter: { + query: { dsl: '{test:1}', kql: 'test:1s', filters: [] }, + timeframe: { + days: [1, 2, 3], + hours: { end: '15:00', start: '00:00' }, + timezone: 'UTC', + }, + }, + }; + + const systemAction: RuleSystemAction = { + id: '1', + uuid: '111', + params: { foo: 'bar' }, + actionTypeId: '.test', + }; + + const rule = { + id: '3d534c70-582b-11ec-8995-2b1578a3bc5d', + enabled: true, + name: 'stressing index-threshold 37/200', + tags: [], + alertTypeId: '.index-threshold', + consumer: 'alerts', + schedule: { + interval: '1s', + }, + actions: [], + params: {}, + createdBy: 'elastic', + updatedBy: '2889684073', + createdAt: new Date('2023-08-01T09:16:35.368Z'), + updatedAt: new Date('2023-08-01T09:16:35.368Z'), + notifyWhen: 'onActiveAlert' as const, + throttle: null, + apiKey: null, + apiKeyOwner: '2889684073', + muteAll: false, + mutedInstanceIds: [], + scheduledTaskId: '52125fb0-5895-11ec-ae69-bb65d1a71b72', + executionStatus: { + status: 'ok' as const, + lastExecutionDate: new Date('2023-08-01T09:16:35.368Z'), + lastDuration: 1194, + }, + revision: 0, + }; + + describe('actions', () => { + it('transforms a default action correctly', () => { + const res = transformRuleToRuleResponse({ ...rule, actions: [defaultAction] }); + expect(res.actions).toEqual([ + { + alerts_filter: { + query: { dsl: '{test:1}', filters: [], kql: 'test:1s' }, + timeframe: { + days: [1, 2, 3], + hours: { end: '15:00', start: '00:00' }, + timezone: 'UTC', + }, + }, + connector_type_id: '.test', + frequency: { notify_when: 'onThrottleInterval', summary: true, throttle: '1h' }, + group: 'default', + id: '1', + params: { foo: 'bar' }, + uuid: '111', + }, + ]); + }); + + it('transforms a system action correctly', () => { + const res = transformRuleToRuleResponse({ ...rule, systemActions: [systemAction] }); + expect(res.actions).toEqual([ + { + id: '1', + uuid: '111', + params: { foo: 'bar' }, + connector_type_id: '.test', + }, + ]); + }); + }); +}); diff --git a/x-pack/plugins/alerting/server/routes/rule/transforms/transform_rule_to_rule_response/v1.ts b/x-pack/plugins/alerting/server/routes/rule/transforms/transform_rule_to_rule_response/v1.ts index f4fdedfc6f436..30bbf77c45ba3 100644 --- a/x-pack/plugins/alerting/server/routes/rule/transforms/transform_rule_to_rule_response/v1.ts +++ b/x-pack/plugins/alerting/server/routes/rule/transforms/transform_rule_to_rule_response/v1.ts @@ -38,6 +38,56 @@ const transformMonitoring = (monitoring: Monitoring): MonitoringV1 => { }; }; +export const transformRuleActions = ( + actions: Rule['actions'], + systemActions: Rule['systemActions'] = [] +): RuleResponseV1['actions'] => { + return [ + ...actions.map((action) => { + const { + group, + id, + actionTypeId, + params, + frequency, + uuid, + alertsFilter, + useAlertDataForTemplate, + } = action; + + return { + group, + id, + params, + connector_type_id: actionTypeId, + ...(frequency + ? { + frequency: { + summary: frequency.summary, + notify_when: frequency.notifyWhen, + throttle: frequency.throttle, + }, + } + : {}), + ...(uuid && { uuid }), + ...(alertsFilter && { alerts_filter: alertsFilter }), + ...(useAlertDataForTemplate !== undefined && { + use_alert_data_for_template: useAlertDataForTemplate, + }), + }; + }), + ...systemActions.map((sActions) => { + const { id, actionTypeId, params, uuid } = sActions; + return { + id, + params, + uuid, + connector_type_id: actionTypeId, + }; + }), + ]; +}; + export const transformRuleToRuleResponse = ( rule: Rule ): RuleResponseV1 => ({ @@ -48,37 +98,7 @@ export const transformRuleToRuleResponse = ( rule_type_id: rule.alertTypeId, consumer: rule.consumer, schedule: rule.schedule, - actions: rule.actions.map( - ({ - group, - id, - actionTypeId, - params, - frequency, - uuid, - alertsFilter, - useAlertDataForTemplate, - }) => ({ - group, - id, - params, - connector_type_id: actionTypeId, - ...(typeof useAlertDataForTemplate !== 'undefined' - ? { use_alert_data_for_template: useAlertDataForTemplate } - : {}), - ...(frequency - ? { - frequency: { - summary: frequency.summary, - notify_when: frequency.notifyWhen, - throttle: frequency.throttle, - }, - } - : {}), - ...(uuid && { uuid }), - ...(alertsFilter && { alerts_filter: alertsFilter }), - }) - ), + actions: transformRuleActions(rule.actions, rule.systemActions ?? []), params: rule.params, ...(rule.mapped_params ? { mapped_params: rule.mapped_params } : {}), ...(rule.scheduledTaskId !== undefined ? { scheduled_task_id: rule.scheduledTaskId } : {}), diff --git a/x-pack/plugins/alerting/server/routes/update_rule.test.ts b/x-pack/plugins/alerting/server/routes/update_rule.test.ts index b48e6d72bef3f..9519a8111efbd 100644 --- a/x-pack/plugins/alerting/server/routes/update_rule.test.ts +++ b/x-pack/plugins/alerting/server/routes/update_rule.test.ts @@ -5,18 +5,15 @@ * 2.0. */ -import { pick } from 'lodash'; -import { updateRuleRoute } from './update_rule'; +import { omit, pick } from 'lodash'; +import { UpdateRequestBody, updateRuleRoute } from './update_rule'; import { httpServiceMock } from '@kbn/core/server/mocks'; import { licenseStateMock } from '../lib/license_state.mock'; import { verifyApiAccess } from '../lib/license_api_access'; import { mockHandlerArguments } from './_mock_handler_arguments'; -import { UpdateOptions } from '../rules_client'; import { rulesClientMock } from '../rules_client.mock'; import { RuleTypeDisabledError } from '../lib/errors/rule_type_disabled'; import { RuleNotifyWhen } from '../../common'; -import { AsApiContract } from './lib'; -import { PartialRule } from '../types'; const rulesClient = rulesClientMock.create(); jest.mock('../lib/license_api_access', () => ({ @@ -58,22 +55,37 @@ describe('updateRuleRoute', () => { }, }, ], + systemActions: [ + { + actionTypeId: '.test-system-action', + uuid: '1234-5678', + id: 'system_action-id', + params: {}, + }, + ], notifyWhen: RuleNotifyWhen.CHANGE, alertDelay: { active: 10, }, }; - const updateRequest: AsApiContract['data']> = { + const mockedAction0 = mockedAlert.actions[0]; + + const updateRequest: UpdateRequestBody = { ...pick(mockedAlert, 'name', 'tags', 'schedule', 'params', 'throttle'), notify_when: mockedAlert.notifyWhen, actions: [ { uuid: '1234-5678', - group: mockedAlert.actions[0].group, - id: mockedAlert.actions[0].id, - params: mockedAlert.actions[0].params, - alerts_filter: mockedAlert.actions[0].alertsFilter, + group: mockedAction0.group, + id: mockedAction0.id, + params: mockedAction0.params, + alerts_filter: mockedAction0.alertsFilter, + }, + { + uuid: '1234-5678', + id: 'system_action-id', + params: {}, }, ], alert_delay: { @@ -81,17 +93,36 @@ describe('updateRuleRoute', () => { }, }; - const updateResult: AsApiContract> = { + const updateResult = { ...updateRequest, id: mockedAlert.id, updated_at: mockedAlert.updatedAt, created_at: mockedAlert.createdAt, rule_type_id: mockedAlert.alertTypeId, - actions: mockedAlert.actions.map(({ actionTypeId, alertsFilter, ...rest }) => ({ - ...rest, - connector_type_id: actionTypeId, - alerts_filter: alertsFilter, - })), + actions: [ + { + uuid: '1234-5678', + group: 'default', + id: '2', + connector_type_id: 'test', + params: { + baz: true, + }, + alerts_filter: { + query: { + kql: 'name:test', + dsl: '{"must": {"term": { "name": "test" }}}', + filters: [], + }, + }, + }, + { + connector_type_id: '.test-system-action', + uuid: '1234-5678', + id: 'system_action-id', + params: {}, + }, + ], alert_delay: mockedAlert.alertDelay, }; @@ -153,6 +184,13 @@ describe('updateRuleRoute', () => { "schedule": Object { "interval": "12s", }, + "systemActions": Array [ + Object { + "id": "system_action-id", + "params": Object {}, + "uuid": "1234-5678", + }, + ], "tags": Array [ "foo", ], @@ -241,4 +279,32 @@ describe('updateRuleRoute', () => { expect(res.forbidden).toHaveBeenCalledWith({ body: { message: 'Fail' } }); }); + + it('throws an error if the default action does not specifies a group', async () => { + const licenseState = licenseStateMock.create(); + const router = httpServiceMock.createRouter(); + + updateRuleRoute(router, licenseState); + + const [config, handler] = router.put.mock.calls[0]; + + expect(config.path).toMatchInlineSnapshot(`"/api/alerting/rule/{id}"`); + + rulesClient.update.mockResolvedValueOnce(mockedAlert); + + const [context, req, res] = mockHandlerArguments( + { rulesClient }, + { + params: { + id: '1', + }, + body: { ...updateRequest, actions: [omit(updateRequest.actions[0], 'group')] }, + }, + ['ok'] + ); + + await expect(handler(context, req, res)).rejects.toThrowErrorMatchingInlineSnapshot( + `"Group is not defined in action 2"` + ); + }); }); diff --git a/x-pack/plugins/alerting/server/routes/update_rule.ts b/x-pack/plugins/alerting/server/routes/update_rule.ts index 9419d84d06341..354cf9c1b65e7 100644 --- a/x-pack/plugins/alerting/server/routes/update_rule.ts +++ b/x-pack/plugins/alerting/server/routes/update_rule.ts @@ -6,18 +6,18 @@ */ import { schema } from '@kbn/config-schema'; +import { TypeOf } from '@kbn/config-schema/src/types/object_type'; import { IRouter } from '@kbn/core/server'; import { ILicenseState, RuleTypeDisabledError, validateDurationSchema } from '../lib'; import { UpdateOptions } from '../rules_client'; import { verifyAccessAndContext, - RewriteResponseCase, - RewriteRequestCase, + AsApiContract, handleDisabledApiKeysError, - rewriteActionsReq, - rewriteActionsRes, actionsSchema, rewriteRuleLastRun, + rewriteActionsReq, + rewriteSystemActionsReq, } from './lib'; import { RuleTypeParams, @@ -26,6 +26,14 @@ import { validateNotifyWhenType, PartialRule, } from '../types'; +import { transformRuleActions } from './rule/transforms'; +import { RuleResponse } from '../../common/routes/rule/response'; +import { validateRequiredGroupInDefaultActions } from './lib/validate_required_group_in_default_actions'; + +export type UpdateRequestBody = TypeOf; +interface RuleUpdateOptionsResult extends Omit, 'data'> { + data: UpdateRequestBody; +} const paramSchema = schema.object({ id: schema.string(), @@ -59,20 +67,26 @@ const bodySchema = schema.object({ ), }); -const rewriteBodyReq: RewriteRequestCase> = (result) => { - const { notify_when: notifyWhen, alert_delay: alertDelay, actions, ...rest } = result.data; +const rewriteBodyReq = ( + result: RuleUpdateOptionsResult, + isSystemAction: (connectorId: string) => boolean +): UpdateOptions => { + const { notify_when: notifyWhen, alert_delay: alertDelay, actions = [], ...rest } = result.data; return { ...result, data: { ...rest, notifyWhen, - actions: rewriteActionsReq(actions), + actions: rewriteActionsReq(actions.filter((action) => !isSystemAction(action.id))), + systemActions: rewriteSystemActionsReq(actions.filter((action) => isSystemAction(action.id))), alertDelay, }, }; }; -const rewriteBodyRes: RewriteResponseCase> = ({ + +const rewriteBodyRes = ({ actions, + systemActions, alertTypeId, scheduledTaskId, createdBy, @@ -91,7 +105,10 @@ const rewriteBodyRes: RewriteResponseCase> = ({ nextRun, alertDelay, ...rest -}) => ({ +}: PartialRule): Omit< + AsApiContract & { actions?: RuleResponse['actions'] }>, + 'actions' +> => ({ ...rest, api_key_owner: apiKeyOwner, created_by: createdBy, @@ -116,7 +133,7 @@ const rewriteBodyRes: RewriteResponseCase> = ({ : {}), ...(actions ? { - actions: rewriteActionsRes(actions), + actions: transformRuleActions(actions, systemActions), } : {}), ...(lastRun ? { last_run: rewriteRuleLastRun(lastRun) } : {}), @@ -141,10 +158,23 @@ export const updateRuleRoute = ( router.handleLegacyErrors( verifyAccessAndContext(licenseState, async function (context, req, res) { const rulesClient = (await context.alerting).getRulesClient(); + const actionsClient = (await context.actions).getActionsClient(); + const { id } = req.params; const rule = req.body; try { - const alertRes = await rulesClient.update(rewriteBodyReq({ id, data: rule })); + /** + * Throws an error if the group is not defined in default actions + */ + validateRequiredGroupInDefaultActions(rule.actions ?? [], (connectorId: string) => + actionsClient.isSystemAction(connectorId) + ); + + const alertRes = await rulesClient.update( + rewriteBodyReq({ id, data: rule }, (connectorId: string) => + actionsClient.isSystemAction(connectorId) + ) + ); return res.ok({ body: rewriteBodyRes(alertRes), }); diff --git a/x-pack/plugins/alerting/server/rules_client/common/apply_bulk_edit_operation.test.ts b/x-pack/plugins/alerting/server/rules_client/common/apply_bulk_edit_operation.test.ts index 49ed183ceb39d..d88ff3ef2b024 100644 --- a/x-pack/plugins/alerting/server/rules_client/common/apply_bulk_edit_operation.test.ts +++ b/x-pack/plugins/alerting/server/rules_client/common/apply_bulk_edit_operation.test.ts @@ -6,7 +6,7 @@ */ import { applyBulkEditOperation } from './apply_bulk_edit_operation'; -import type { Rule } from '../../types'; +import { Rule } from '../../types'; describe('applyBulkEditOperation', () => { describe('tags operations', () => { @@ -182,41 +182,75 @@ describe('applyBulkEditOperation', () => { const ruleMock = { actions: [{ id: 'mock-action-id', group: 'default', params: {} }], }; + const { modifiedAttributes, isAttributeModified } = applyBulkEditOperation( { field: 'actions', value: [ - { id: 'mock-add-action-id-1', group: 'default', params: {} }, - { id: 'mock-add-action-id-2', group: 'default', params: {} }, + { + id: 'mock-add-action-id-1', + group: 'default', + params: {}, + }, + { + id: 'mock-add-action-id-2', + group: 'default', + params: {}, + }, ], operation: 'add', }, ruleMock ); + expect(modifiedAttributes).toHaveProperty('actions', [ { id: 'mock-action-id', group: 'default', params: {} }, { id: 'mock-add-action-id-1', group: 'default', params: {} }, { id: 'mock-add-action-id-2', group: 'default', params: {} }, ]); + expect(isAttributeModified).toBe(true); }); test('should add action with different params and same id', () => { const ruleMock = { - actions: [{ id: 'mock-action-id', group: 'default', params: { test: 1 } }], + actions: [ + { + id: 'mock-action-id', + group: 'default', + params: { test: 1 }, + }, + ], }; + const { modifiedAttributes, isAttributeModified } = applyBulkEditOperation( { field: 'actions', - value: [{ id: 'mock-action-id', group: 'default', params: { test: 2 } }], + value: [ + { + id: 'mock-action-id', + group: 'default', + params: { test: 2 }, + }, + ], operation: 'add', }, ruleMock ); + expect(modifiedAttributes).toHaveProperty('actions', [ - { id: 'mock-action-id', group: 'default', params: { test: 1 } }, - { id: 'mock-action-id', group: 'default', params: { test: 2 } }, + { + id: 'mock-action-id', + group: 'default', + params: { test: 1 }, + }, + { + id: 'mock-action-id', + group: 'default', + params: { test: 2 }, + }, ]); + expect(isAttributeModified).toBe(true); }); @@ -224,17 +258,30 @@ describe('applyBulkEditOperation', () => { const ruleMock = { actions: [{ id: 'mock-action-id', group: 'default', params: {} }], }; + const { modifiedAttributes, isAttributeModified } = applyBulkEditOperation( { field: 'actions', - value: [{ id: 'mock-rewrite-action-id-1', group: 'default', params: {} }], + value: [ + { + id: 'mock-rewrite-action-id-1', + group: 'default', + params: {}, + }, + ], operation: 'set', }, ruleMock ); + expect(modifiedAttributes).toHaveProperty('actions', [ - { id: 'mock-rewrite-action-id-1', group: 'default', params: {} }, + { + id: 'mock-rewrite-action-id-1', + group: 'default', + params: {}, + }, ]); + expect(isAttributeModified).toBe(true); }); }); diff --git a/x-pack/plugins/alerting/server/rules_client/common/inject_references.ts b/x-pack/plugins/alerting/server/rules_client/common/inject_references.ts index bbf192e4f1cf4..2938c91372325 100644 --- a/x-pack/plugins/alerting/server/rules_client/common/inject_references.ts +++ b/x-pack/plugins/alerting/server/rules_client/common/inject_references.ts @@ -9,7 +9,7 @@ import Boom from '@hapi/boom'; import { omit } from 'lodash'; import { SavedObjectReference, SavedObjectAttributes } from '@kbn/core/server'; import { UntypedNormalizedRuleType } from '../../rule_type_registry'; -import { Rule, RawRule, RuleTypeParams } from '../../types'; +import { RawRule, RuleTypeParams } from '../../types'; import { RuleActionAttributes } from '../../data/rule/types'; import { preconfiguredConnectorActionRefPrefix, @@ -45,7 +45,7 @@ export function injectReferencesIntoActions( ...omit(action, 'actionRef'), id: reference.id, }; - }) as Rule['actions']; + }); } export function injectReferencesIntoParams< diff --git a/x-pack/plugins/alerting/server/rules_client/lib/add_generated_action_values.test.ts b/x-pack/plugins/alerting/server/rules_client/lib/add_generated_action_values.test.ts index 1c04ca38bf655..871debb9b4e9c 100644 --- a/x-pack/plugins/alerting/server/rules_client/lib/add_generated_action_values.test.ts +++ b/x-pack/plugins/alerting/server/rules_client/lib/add_generated_action_values.test.ts @@ -6,7 +6,7 @@ */ import { addGeneratedActionValues } from './add_generated_action_values'; -import { RuleAction } from '../../../common'; +import { RuleAction, RuleSystemAction } from '../../../common'; import { ActionsAuthorization } from '@kbn/actions-plugin/server'; import { actionsAuthorizationMock } from '@kbn/actions-plugin/server/mocks'; import { loggingSystemMock } from '@kbn/core-logging-server-mocks'; @@ -21,6 +21,7 @@ import { AlertingAuthorization } from '../../authorization'; import { alertingAuthorizationMock } from '../../authorization/alerting_authorization.mock'; import { ruleTypeRegistryMock } from '../../rule_type_registry.mock'; import { ConstructorOptions } from '../rules_client'; +import { ConnectorAdapterRegistry } from '../../connector_adapters/connector_adapter_registry'; jest.mock('uuid', () => ({ v4: () => '111-222', @@ -62,6 +63,8 @@ describe('addGeneratedActionValues()', () => { getAlertIndicesAlias: jest.fn(), alertsService: null, uiSettings: uiSettingsServiceMock.createStartContract(), + connectorAdapterRegistry: new ConnectorAdapterRegistry(), + isSystemAction: jest.fn(), }; const mockAction: RuleAction = { @@ -92,24 +95,54 @@ describe('addGeneratedActionValues()', () => { }, }; + const mockSystemAction: RuleSystemAction = { + id: '1', + actionTypeId: 'slack', + params: {}, + }; + test('adds uuid', async () => { - const actionWithGeneratedValues = await addGeneratedActionValues([mockAction], { - ...rulesClientParams, - fieldsToExcludeFromPublicApi: [], - minimumScheduleIntervalInMs: 0, + const actionWithGeneratedValues = await addGeneratedActionValues( + [mockAction], + [mockSystemAction], + { + ...rulesClientParams, + fieldsToExcludeFromPublicApi: [], + minimumScheduleIntervalInMs: 0, + } + ); + + expect(actionWithGeneratedValues.actions[0].uuid).toBe('111-222'); + + expect(actionWithGeneratedValues.systemActions[0]).toEqual({ + actionTypeId: 'slack', + id: '1', + params: {}, + uuid: '111-222', }); - expect(actionWithGeneratedValues[0].uuid).toBe('111-222'); }); test('adds DSL', async () => { - const actionWithGeneratedValues = await addGeneratedActionValues([mockAction], { - ...rulesClientParams, - fieldsToExcludeFromPublicApi: [], - minimumScheduleIntervalInMs: 0, - }); - expect(actionWithGeneratedValues[0].alertsFilter?.query?.dsl).toBe( + const actionWithGeneratedValues = await addGeneratedActionValues( + [mockAction], + [mockSystemAction], + { + ...rulesClientParams, + fieldsToExcludeFromPublicApi: [], + minimumScheduleIntervalInMs: 0, + } + ); + + expect(actionWithGeneratedValues.actions[0].alertsFilter?.query?.dsl).toBe( '{"bool":{"must":[],"filter":[{"bool":{"should":[{"match":{"test":"testValue"}}],"minimum_should_match":1}},{"match_phrase":{"foo":"bar "}}],"should":[],"must_not":[]}}' ); + + expect(actionWithGeneratedValues.systemActions[0]).toEqual({ + actionTypeId: 'slack', + id: '1', + params: {}, + uuid: '111-222', + }); }); test('throws error if KQL is not valid', async () => { @@ -121,6 +154,7 @@ describe('addGeneratedActionValues()', () => { alertsFilter: { query: { kql: 'foo:bar:1', filters: [] } }, }, ], + [mockSystemAction], { ...rulesClientParams, fieldsToExcludeFromPublicApi: [], diff --git a/x-pack/plugins/alerting/server/rules_client/lib/add_generated_action_values.ts b/x-pack/plugins/alerting/server/rules_client/lib/add_generated_action_values.ts index 551c1ab1d2819..d50e773d880a0 100644 --- a/x-pack/plugins/alerting/server/rules_client/lib/add_generated_action_values.ts +++ b/x-pack/plugins/alerting/server/rules_client/lib/add_generated_action_values.ts @@ -11,14 +11,20 @@ import Boom from '@hapi/boom'; import { UI_SETTINGS } from '@kbn/data-plugin/common'; import { NormalizedAlertAction, - NormalizedAlertActionWithGeneratedValues, + NormalizedAlertDefaultActionWithGeneratedValues, + NormalizedAlertSystemActionWithGeneratedValues, + NormalizedSystemAction, RulesClientContext, } from '..'; export async function addGeneratedActionValues( actions: NormalizedAlertAction[] = [], + systemActions: NormalizedSystemAction[] = [], context: RulesClientContext -): Promise { +): Promise<{ + actions: NormalizedAlertDefaultActionWithGeneratedValues[]; + systemActions: NormalizedAlertSystemActionWithGeneratedValues[]; +}> { const uiSettingClient = context.uiSettings.asScopedToClient(context.unsecuredSavedObjectsClient); const [allowLeadingWildcards, queryStringOptions, ignoreFilterIfFieldNotInIndex] = await Promise.all([ @@ -31,33 +37,40 @@ export async function addGeneratedActionValues( queryStringOptions, ignoreFilterIfFieldNotInIndex, }; - return actions.map(({ uuid, alertsFilter, ...action }) => { - const generateDSL = (kql: string, filters: Filter[]) => { - try { - return JSON.stringify( - buildEsQuery(undefined, [{ query: kql, language: 'kuery' }], filters, esQueryConfig) - ); - } catch (e) { - throw Boom.badRequest(`Error creating DSL query: invalid KQL`); - } - }; + const generateDSL = (kql: string, filters: Filter[]): string => { + try { + return JSON.stringify( + buildEsQuery(undefined, [{ query: kql, language: 'kuery' }], filters, esQueryConfig) + ); + } catch (e) { + throw Boom.badRequest(`Error creating DSL query: invalid KQL`); + } + }; - return { - ...action, - uuid: uuid || v4(), - ...(alertsFilter - ? { - alertsFilter: { - ...alertsFilter, - query: alertsFilter.query - ? { - ...alertsFilter.query, - dsl: generateDSL(alertsFilter.query.kql, alertsFilter.query.filters), - } - : undefined, - }, - } - : {}), - }; - }); + return { + actions: actions.map((action) => { + const { alertsFilter, uuid, ...restAction } = action; + return { + ...restAction, + uuid: uuid || v4(), + ...(alertsFilter + ? { + alertsFilter: { + ...alertsFilter, + query: alertsFilter.query + ? { + ...alertsFilter.query, + dsl: generateDSL(alertsFilter.query.kql, alertsFilter.query.filters) ?? '', + } + : undefined, + }, + } + : {}), + }; + }), + systemActions: systemActions.map((systemAction) => ({ + ...systemAction, + uuid: systemAction.uuid || v4(), + })), + }; } diff --git a/x-pack/plugins/alerting/server/rules_client/lib/create_new_api_key_set.test.ts b/x-pack/plugins/alerting/server/rules_client/lib/create_new_api_key_set.test.ts index 44fb6d2e73c36..93b43545b9747 100644 --- a/x-pack/plugins/alerting/server/rules_client/lib/create_new_api_key_set.test.ts +++ b/x-pack/plugins/alerting/server/rules_client/lib/create_new_api_key_set.test.ts @@ -22,6 +22,7 @@ import { RawRule } from '../../types'; import { getBeforeSetup, mockedDateString } from '../tests/lib'; import { createNewAPIKeySet } from './create_new_api_key_set'; import { RulesClientContext } from '../types'; +import { ConnectorAdapterRegistry } from '../../connector_adapters/connector_adapter_registry'; const taskManager = taskManagerMock.createStart(); const ruleTypeRegistry = ruleTypeRegistryMock.create(); @@ -53,9 +54,11 @@ const rulesClientParams: jest.Mocked = { fieldsToExcludeFromPublicApi: [], isAuthenticationTypeAPIKey: jest.fn(), getAuthenticationAPIKey: jest.fn(), + connectorAdapterRegistry: new ConnectorAdapterRegistry(), getAlertIndicesAlias: jest.fn(), alertsService: null, uiSettings: uiSettingsServiceMock.createStartContract(), + isSystemAction: jest.fn(), }; const username = 'test'; diff --git a/x-pack/plugins/alerting/server/rules_client/lib/denormalize_actions.ts b/x-pack/plugins/alerting/server/rules_client/lib/denormalize_actions.ts index db1dca186aee9..3cd1113a13628 100644 --- a/x-pack/plugins/alerting/server/rules_client/lib/denormalize_actions.ts +++ b/x-pack/plugins/alerting/server/rules_client/lib/denormalize_actions.ts @@ -4,21 +4,24 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ - import { SavedObjectReference } from '@kbn/core/server'; -import { RawRule } from '../../types'; import { preconfiguredConnectorActionRefPrefix, systemConnectorActionRefPrefix, } from '../common/constants'; -import { NormalizedAlertActionWithGeneratedValues, RulesClientContext } from '../types'; +import { + DenormalizedAction, + NormalizedAlertActionWithGeneratedValues, + RulesClientContext, +} from '../types'; export async function denormalizeActions( context: RulesClientContext, alertActions: NormalizedAlertActionWithGeneratedValues[] -): Promise<{ actions: RawRule['actions']; references: SavedObjectReference[] }> { +): Promise<{ actions: DenormalizedAction[]; references: SavedObjectReference[] }> { const references: SavedObjectReference[] = []; - const actions: RawRule['actions'] = []; + const actions: DenormalizedAction[] = []; + if (alertActions.length) { const actionsClient = await context.getActionsClient(); const actionIds = [...new Set(alertActions.map((alertAction) => alertAction.id))]; @@ -29,12 +32,15 @@ export async function denormalizeActions( }); const actionTypeIds = [...new Set(actionResults.map((action) => action.actionTypeId))]; + actionTypeIds.forEach((id) => { // Notify action type usage via "isActionTypeEnabled" function actionsClient.isActionTypeEnabled(id, { notifyUsage: true }); }); + alertActions.forEach(({ id, ...alertAction }, i) => { const actionResultValue = actionResults.find((action) => action.id === id); + if (actionResultValue) { if (actionsClient.isPreconfigured(id)) { actions.push({ @@ -50,11 +56,13 @@ export async function denormalizeActions( }); } else { const actionRef = `action_${i}`; + references.push({ id, name: actionRef, type: 'action', }); + actions.push({ ...alertAction, actionRef, diff --git a/x-pack/plugins/alerting/server/rules_client/lib/extract_references.ts b/x-pack/plugins/alerting/server/rules_client/lib/extract_references.ts index a7525cf9b5b19..9dfca0897ca08 100644 --- a/x-pack/plugins/alerting/server/rules_client/lib/extract_references.ts +++ b/x-pack/plugins/alerting/server/rules_client/lib/extract_references.ts @@ -6,9 +6,9 @@ */ import { SavedObjectReference } from '@kbn/core/server'; -import { RawRule, RuleTypeParams } from '../../types'; +import { RuleTypeParams } from '../../types'; import { UntypedNormalizedRuleType } from '../../rule_type_registry'; -import { NormalizedAlertActionWithGeneratedValues } from '../types'; +import { DenormalizedAction, NormalizedAlertActionWithGeneratedValues } from '../types'; import { extractedSavedObjectParamReferenceNamePrefix } from '../common/constants'; import { RulesClientContext } from '../types'; import { denormalizeActions } from './denormalize_actions'; @@ -22,7 +22,7 @@ export async function extractReferences< ruleActions: NormalizedAlertActionWithGeneratedValues[], ruleParams: Params ): Promise<{ - actions: RawRule['actions']; + actions: DenormalizedAction[]; params: ExtractedParams; references: SavedObjectReference[]; }> { diff --git a/x-pack/plugins/alerting/server/rules_client/lib/get_alert_from_raw.ts b/x-pack/plugins/alerting/server/rules_client/lib/get_alert_from_raw.ts index ddf1e40728b28..ac337001099bd 100644 --- a/x-pack/plugins/alerting/server/rules_client/lib/get_alert_from_raw.ts +++ b/x-pack/plugins/alerting/server/rules_client/lib/get_alert_from_raw.ts @@ -23,8 +23,12 @@ import { } from '../../lib'; import { UntypedNormalizedRuleType } from '../../rule_type_registry'; import { getActiveScheduledSnoozes } from '../../lib/is_rule_snoozed'; -import { injectReferencesIntoActions, injectReferencesIntoParams } from '../common'; +import { injectReferencesIntoParams } from '../common'; import { RulesClientContext } from '../types'; +import { + transformRawActionsToDomainActions, + transformRawActionsToDomainSystemActions, +} from '../../application/rule/transforms/transform_raw_actions_to_domain_actions'; export interface GetAlertFromRawParams { id: string; @@ -93,6 +97,7 @@ export function getPartialRuleFromRaw( actions, snoozeSchedule, lastRun, + isSnoozedUntil: DoNotUseIsSNoozedUntil, ...partialRawRule }: Partial, references: SavedObjectReference[] | undefined, @@ -118,6 +123,7 @@ export function getPartialRuleFromRaw( }) : null; const includeMonitoring = monitoring && !excludeFromPublicApi; + const rule: PartialRule = { id, notifyWhen, @@ -125,7 +131,24 @@ export function getPartialRuleFromRaw( // we currently only support the Interval Schedule type // Once we support additional types, this type signature will likely change schedule: schedule as IntervalSchedule, - actions: actions ? injectReferencesIntoActions(id, actions, references || []) : [], + actions: actions + ? transformRawActionsToDomainActions({ + ruleId: id, + actions, + references: references || [], + isSystemAction: context.isSystemAction, + omitGeneratedValues, + }) + : [], + systemActions: actions + ? transformRawActionsToDomainSystemActions({ + ruleId: id, + actions, + references: references || [], + isSystemAction: context.isSystemAction, + omitGeneratedValues, + }) + : [], params: injectReferencesIntoParams(id, ruleType, params, references || []) as Params, ...(excludeFromPublicApi ? {} : { snoozeSchedule: snoozeScheduleDates ?? [] }), ...(includeSnoozeData && !excludeFromPublicApi @@ -134,7 +157,7 @@ export function getPartialRuleFromRaw( snoozeSchedule, muteAll: partialRawRule.muteAll ?? false, })?.map((s) => s.id), - isSnoozedUntil, + isSnoozedUntil: isSnoozedUntil as PartialRule['isSnoozedUntil'], } : {}), ...(updatedAt ? { updatedAt: new Date(updatedAt) } : {}), @@ -161,7 +184,9 @@ export function getPartialRuleFromRaw( if (omitGeneratedValues) { if (rule.actions) { - rule.actions = rule.actions.map((ruleAction) => omit(ruleAction, 'alertsFilter.query.dsl')); + rule.actions = rule.actions.map((ruleAction) => { + return omit(ruleAction, 'alertsFilter.query.dsl'); + }); } } diff --git a/x-pack/plugins/alerting/server/rules_client/lib/siem_legacy_actions/format_legacy_actions.ts b/x-pack/plugins/alerting/server/rules_client/lib/siem_legacy_actions/format_legacy_actions.ts index 74a56d1964ada..68c6a73b098d6 100644 --- a/x-pack/plugins/alerting/server/rules_client/lib/siem_legacy_actions/format_legacy_actions.ts +++ b/x-pack/plugins/alerting/server/rules_client/lib/siem_legacy_actions/format_legacy_actions.ts @@ -99,7 +99,9 @@ export const legacyGetBulkRuleActionsSavedObject = async ({ legacyRawActions, savedObject.references ) // remove uuid from action, as this uuid is not persistent - .map(({ uuid, ...action }) => action), + .map(({ uuid, ...action }) => ({ + ...action, + })) as RuleAction[], }; } else { logger.error( diff --git a/x-pack/plugins/alerting/server/rules_client/lib/siem_legacy_actions/migrate_legacy_actions.ts b/x-pack/plugins/alerting/server/rules_client/lib/siem_legacy_actions/migrate_legacy_actions.ts index 75bcb39e522b9..da14fe60e47fb 100644 --- a/x-pack/plugins/alerting/server/rules_client/lib/siem_legacy_actions/migrate_legacy_actions.ts +++ b/x-pack/plugins/alerting/server/rules_client/lib/siem_legacy_actions/migrate_legacy_actions.ts @@ -7,15 +7,13 @@ import Boom from '@hapi/boom'; import { i18n } from '@kbn/i18n'; - import { AlertConsumers } from '@kbn/rule-data-utils'; - import type { SavedObjectReference } from '@kbn/core/server'; import type { RulesClientContext } from '../..'; import { RawRuleAction, RawRule } from '../../../types'; import { validateActions } from '../validate_actions'; -import { injectReferencesIntoActions } from '../../common'; import { retrieveMigratedLegacyActions } from './retrieve_migrated_legacy_actions'; +import { transformRawActionsToDomainActions } from '../../../application/rule/transforms/transform_raw_actions_to_domain_actions'; type MigrateLegacyActions = ( context: RulesClientContext, @@ -66,7 +64,12 @@ export const migrateLegacyActions: MigrateLegacyActions = async ( // set to undefined to avoid both per-actin and rule level values clashing throttle: undefined, notifyWhen: undefined, - actions: injectReferencesIntoActions(ruleId, legacyActions, legacyActionsReferences), + actions: transformRawActionsToDomainActions({ + ruleId, + actions: legacyActions, + references: legacyActionsReferences, + isSystemAction: context.isSystemAction, + }), }); }; diff --git a/x-pack/plugins/alerting/server/rules_client/lib/validate_actions.test.ts b/x-pack/plugins/alerting/server/rules_client/lib/validate_actions.test.ts index 74fd3b3291d4e..e565f8b1b51ed 100644 --- a/x-pack/plugins/alerting/server/rules_client/lib/validate_actions.test.ts +++ b/x-pack/plugins/alerting/server/rules_client/lib/validate_actions.test.ts @@ -7,8 +7,8 @@ import { validateActions, ValidateActionsData } from './validate_actions'; import { UntypedNormalizedRuleType } from '../../rule_type_registry'; -import { AlertsFilter, RecoveredActionGroup, RuleNotifyWhen } from '../../../common'; -import { RulesClientContext } from '..'; +import { AlertsFilter, RecoveredActionGroup, RuleAction, RuleNotifyWhen } from '../../../common'; +import { NormalizedAlertAction, NormalizedSystemAction, RulesClientContext } from '..'; describe('validateActions', () => { const loggerErrorMock = jest.fn(); @@ -22,7 +22,6 @@ describe('validateActions', () => { isExportable: true, recoveryActionGroup: RecoveredActionGroup, executor: jest.fn(), - category: 'test', producer: 'alerts', cancelAlertsOnRuleTimeout: true, ruleTaskTimeout: '5m', @@ -33,28 +32,35 @@ describe('validateActions', () => { context: 'context', mappings: { fieldMap: { field: { type: 'fieldType', required: false } } }, }, + category: 'test', validLegacyConsumers: [], }; + const defaultAction: NormalizedAlertAction = { + uuid: '111', + group: 'default', + id: '1', + params: {}, + frequency: { + summary: false, + notifyWhen: RuleNotifyWhen.ACTIVE, + throttle: null, + }, + alertsFilter: { + query: { kql: 'test:1', filters: [] }, + timeframe: { days: [1], hours: { start: '10:00', end: '17:00' }, timezone: 'UTC' }, + }, + }; + + const systemAction: NormalizedSystemAction = { + uuid: '111', + id: '1', + params: {}, + }; + const data = { schedule: { interval: '1m' }, - actions: [ - { - uuid: '111', - group: 'default', - id: '1', - params: {}, - frequency: { - summary: false, - notifyWhen: RuleNotifyWhen.ACTIVE, - throttle: null, - }, - alertsFilter: { - query: { kql: 'test:1', filters: [] }, - timeframe: { days: [1], hours: { start: '10:00', end: '17:00' }, timezone: 'UTC' }, - }, - }, - ], + actions: [defaultAction], } as unknown as ValidateActionsData; const context = { @@ -91,6 +97,23 @@ describe('validateActions', () => { ); }); + it('should return error message if actions have duplicated uuid and there is a system action', async () => { + await expect( + validateActions( + context as unknown as RulesClientContext, + ruleType, + { + ...data, + actions: [defaultAction], + systemActions: [systemAction], + }, + false + ) + ).rejects.toThrowErrorMatchingInlineSnapshot( + '"Failed to validate actions due to the following error: Actions have duplicated UUIDs"' + ); + }); + it('should return error message if any action have isMissingSecrets', async () => { getBulkMock.mockResolvedValue([{ isMissingSecrets: true, name: 'test name' }]); await expect( @@ -105,7 +128,7 @@ describe('validateActions', () => { validateActions( context as unknown as RulesClientContext, ruleType, - { ...data, actions: [{ ...data.actions[0], group: 'invalid' }] }, + { ...data, actions: [{ ...data.actions[0], group: 'invalid' } as RuleAction] }, false ) ).rejects.toThrowErrorMatchingInlineSnapshot( @@ -144,7 +167,7 @@ describe('validateActions', () => { validateActions( context as unknown as RulesClientContext, ruleType, - { ...data, actions: [{ ...data.actions[0], frequency: undefined }] }, + { ...data, actions: [{ ...data.actions[0], frequency: undefined } as RuleAction] }, false ) ).rejects.toThrowErrorMatchingInlineSnapshot( @@ -163,7 +186,7 @@ describe('validateActions', () => { { ...data.actions[0], frequency: { summary: false, notifyWhen: 'onThrottleInterval', throttle: '1s' }, - }, + } as RuleAction, ], }, false @@ -184,7 +207,7 @@ describe('validateActions', () => { { ...data.actions[0], alertsFilter: {} as AlertsFilter, - }, + } as RuleAction, ], }, false @@ -208,7 +231,7 @@ describe('validateActions', () => { query: { kql: 'test:1', filters: [] }, timeframe: { days: [1], hours: { start: '30:00', end: '17:00' }, timezone: 'UTC' }, }, - }, + } as NormalizedAlertAction, ], }, false @@ -255,6 +278,7 @@ describe('validateActions', () => { '"Failed to validate actions due to the following error: Action\'s alertsFilter timeframe has missing fields: days, hours or timezone: 111"' ); }); + it('should return error message if any action has alertsFilter timeframe has invalid days', async () => { await expect( validateActions( diff --git a/x-pack/plugins/alerting/server/rules_client/lib/validate_actions.ts b/x-pack/plugins/alerting/server/rules_client/lib/validate_actions.ts index 5a1e9911b5fcb..4f5be980bae89 100644 --- a/x-pack/plugins/alerting/server/rules_client/lib/validate_actions.ts +++ b/x-pack/plugins/alerting/server/rules_client/lib/validate_actions.ts @@ -11,12 +11,13 @@ import { i18n } from '@kbn/i18n'; import { validateHours } from '../../routes/lib/validate_hours'; import { RawRule, RuleNotifyWhen } from '../../types'; import { UntypedNormalizedRuleType } from '../../rule_type_registry'; -import { NormalizedAlertAction } from '../types'; +import { NormalizedAlertAction, NormalizedSystemAction } from '../types'; import { RulesClientContext } from '../types'; import { parseDuration } from '../../lib'; export type ValidateActionsData = Pick & { actions: NormalizedAlertAction[]; + systemActions?: NormalizedSystemAction[]; }; export async function validateActions( @@ -25,7 +26,7 @@ export async function validateActions( data: ValidateActionsData, allowMissingConnectorSecrets?: boolean ): Promise { - const { actions, notifyWhen, throttle } = data; + const { actions, notifyWhen, throttle, systemActions = [] } = data; const hasRuleLevelNotifyWhen = typeof notifyWhen !== 'undefined'; const hasRuleLevelThrottle = Boolean(throttle); if (actions.length === 0) { @@ -34,8 +35,9 @@ export async function validateActions( const errors = []; - const uniqueActions = new Set(actions.map((action) => action.uuid)); - if (uniqueActions.size < actions.length) { + const allActions = [...actions, ...systemActions]; + const uniqueActions = new Set(allActions.map((action) => action.uuid)); + if (uniqueActions.size < allActions.length) { errors.push( i18n.translate('xpack.alerting.rulesClient.validateActions.hasDuplicatedUuid', { defaultMessage: 'Actions have duplicated UUIDs', @@ -95,6 +97,7 @@ export async function validateActions( // check for actions using frequency params if the rule has rule-level frequency params defined if (hasRuleLevelNotifyWhen || hasRuleLevelThrottle) { const actionsWithFrequency = actions.filter((action) => Boolean(action.frequency)); + if (actionsWithFrequency.length) { errors.push( i18n.translate('xpack.alerting.rulesClient.validateActions.mixAndMatchFreqParams', { @@ -108,6 +111,7 @@ export async function validateActions( } } else { const actionsWithoutFrequency = actions.filter((action) => !action.frequency); + if (actionsWithoutFrequency.length) { errors.push( i18n.translate('xpack.alerting.rulesClient.validateActions.notAllActionsWithFreq', { diff --git a/x-pack/plugins/alerting/server/rules_client/methods/bulk_enable.ts b/x-pack/plugins/alerting/server/rules_client/methods/bulk_enable.ts index 3d315279c2305..397d49a2751cf 100644 --- a/x-pack/plugins/alerting/server/rules_client/methods/bulk_enable.ts +++ b/x-pack/plugins/alerting/server/rules_client/methods/bulk_enable.ts @@ -24,7 +24,6 @@ import { getRuleCircuitBreakerErrorMessage } from '../../../common'; import { getAuthorizationFilter, checkAuthorizationAndGetTotal, - getAlertFromRaw, updateMeta, createNewAPIKeySet, migrateLegacyActions, @@ -32,6 +31,12 @@ import { import { RulesClientContext, BulkOperationError, BulkOptions } from '../types'; import { validateScheduleLimit } from '../../application/rule/methods/get_schedule_frequency'; import { RuleAttributes } from '../../data/rule/types'; +import { + transformRuleAttributesToRuleDomain, + transformRuleDomainToRule, +} from '../../application/rule/transforms'; +import type { RuleParams } from '../../application/rule/types'; +import { ruleDomainSchema } from '../../application/rule/schemas'; import { RULE_SAVED_OBJECT_TYPE } from '../../saved_objects'; const getShouldScheduleTask = async ( @@ -58,8 +63,12 @@ const getShouldScheduleTask = async ( } }; -export const bulkEnableRules = async (context: RulesClientContext, options: BulkOptions) => { +export const bulkEnableRules = async ( + context: RulesClientContext, + options: BulkOptions +) => { const { ids, filter } = getAndValidateCommonBulkOptions(options); + const actionsClient = await context.getActionsClient(); const kueryNodeFilter = ids ? convertRuleIdsToKueryNode(ids) : buildKueryNodeFilter(filter); const authorizationFilter = await getAuthorizationFilter(context, { action: 'ENABLE' }); @@ -90,18 +99,32 @@ export const bulkEnableRules = async (context: RulesClientContext, options: Bulk taskManager: context.taskManager, }); - const updatedRules = rules.map(({ id, attributes, references }) => { - return getAlertFromRaw( - context, - id, - attributes.alertTypeId as string, - attributes as RawRule, - references, - false + const enabledRules = rules.map(({ id, attributes, references }) => { + // TODO (http-versioning): alertTypeId should never be null, but we need to + // fix the type cast from SavedObjectsBulkUpdateObject to SavedObjectsBulkUpdateObject + // when we are doing the bulk disable and this should fix itself + const ruleType = context.ruleTypeRegistry.get(attributes.alertTypeId!); + const ruleDomain = transformRuleAttributesToRuleDomain( + attributes as RuleAttributes, + { + id, + logger: context.logger, + ruleType, + references, + omitGeneratedValues: false, + }, + (connectorId: string) => actionsClient.isSystemAction(connectorId) ); + + try { + ruleDomainSchema.validate(ruleDomain); + } catch (e) { + context.logger.warn(`Error validating bulk enabled rule domain object for id: ${id}, ${e}`); + } + return transformRuleDomainToRule(ruleDomain); }); - return { errors, rules: updatedRules, total, taskIdsFailedToBeEnabled }; + return { errors, rules: enabledRules, total, taskIdsFailedToBeEnabled }; }; const bulkEnableRulesWithOCC = async ( diff --git a/x-pack/plugins/alerting/server/rules_client/methods/update.ts b/x-pack/plugins/alerting/server/rules_client/methods/update.ts index 1255173beefe4..1b54dfcb18d94 100644 --- a/x-pack/plugins/alerting/server/rules_client/methods/update.ts +++ b/x-pack/plugins/alerting/server/rules_client/methods/update.ts @@ -22,7 +22,7 @@ import { retryIfConflicts } from '../../lib/retry_if_conflicts'; import { bulkMarkApiKeysForInvalidation } from '../../invalidate_pending_api_keys/bulk_mark_api_keys_for_invalidation'; import { ruleAuditEvent, RuleAuditAction } from '../common/audit_events'; import { getMappedParams } from '../common/mapped_params_utils'; -import { NormalizedAlertAction, RulesClientContext } from '../types'; +import { NormalizedAlertAction, NormalizedSystemAction, RulesClientContext } from '../types'; import { validateActions, extractReferences, @@ -37,7 +37,10 @@ import { validateScheduleLimit, ValidateScheduleLimitResult, } from '../../application/rule/methods/get_schedule_frequency'; +import { validateSystemActions } from '../../lib/validate_system_actions'; +import { transformRawActionsToDomainActions } from '../../application/rule/transforms'; import { RULE_SAVED_OBJECT_TYPE } from '../../saved_objects'; +import { transformRawActionsToDomainSystemActions } from '../../application/rule/transforms/transform_raw_actions_to_domain_actions'; type ShouldIncrementRevision = (params?: RuleTypeParams) => boolean; @@ -48,6 +51,7 @@ export interface UpdateOptions { tags: string[]; schedule: IntervalSchedule; actions: NormalizedAlertAction[]; + systemActions?: NormalizedSystemAction[]; params: Params; throttle?: string | null; notifyWhen?: RuleNotifyWhenType | null; @@ -219,16 +223,33 @@ async function updateAlert( currentRule: SavedObject ): Promise> { const { attributes, version } = currentRule; + const { actions: genAction, systemActions: genSystemActions } = await addGeneratedActionValues( + initialData.actions, + initialData.systemActions, + context + ); const data = { ...initialData, - actions: await addGeneratedActionValues(initialData.actions, context), + actions: genAction, + systemActions: genSystemActions, }; - + const actionsClient = await context.getActionsClient(); const ruleType = context.ruleTypeRegistry.get(attributes.alertTypeId); + try { + validateActionsSchema(data.actions, data.systemActions); + } catch (error) { + throw Boom.badRequest(`Error validating actions - ${error.message}`); + } + // Validate const validatedAlertTypeParams = validateRuleTypeParams(data.params, ruleType.validate.params); await validateActions(context, ruleType, data, allowMissingConnectorSecrets); + await validateSystemActions({ + actionsClient, + connectorAdapterRegistry: context.connectorAdapterRegistry, + systemActions: data.systemActions, + }); // Throw error if schedule interval is less than the minimum and we are enforcing it const intervalInMs = parseDuration(data.schedule.interval); @@ -241,12 +262,13 @@ async function updateAlert( ); } + const allActions = [...data.actions, ...(data.systemActions ?? [])]; // Extract saved object references for this rule const { references, params: updatedParams, - actions, - } = await extractReferences(context, ruleType, data.actions, validatedAlertTypeParams); + actions: actionsWithRefs, + } = await extractReferences(context, ruleType, allActions, validatedAlertTypeParams); const username = await context.getUserName(); @@ -269,12 +291,13 @@ async function updateAlert( : currentRule.attributes.revision; let updatedObject: SavedObject; + const { systemActions, ...restData } = data; const createAttributes = updateMeta(context, { ...attributes, - ...data, + ...restData, ...apiKeyAttributes, params: updatedParams as RawRule['params'], - actions, + actions: actionsWithRefs, notifyWhen, revision, updatedBy: username, @@ -323,8 +346,7 @@ async function updateAlert( `Rule schedule interval (${data.schedule.interval}) for "${ruleType.id}" rule type with ID "${id}" is less than the minimum value (${context.minimumScheduleInterval.value}). Running rules at this interval may impact alerting performance. Set "xpack.alerting.rules.minimumScheduleInterval.enforce" to true to prevent such changes.` ); } - - return getPartialRuleFromRaw( + const rules = getPartialRuleFromRaw( context, id, ruleType, @@ -333,4 +355,47 @@ async function updateAlert( false, true ); + + return { + ...rules, + actions: transformRawActionsToDomainActions({ + ruleId: id, + references: updatedObject.references, + actions: updatedObject.attributes.actions, + omitGeneratedValues: false, + isSystemAction: (connectorId: string) => actionsClient.isSystemAction(connectorId), + }), + systemActions: transformRawActionsToDomainSystemActions({ + ruleId: id, + references: updatedObject.references, + actions: updatedObject.attributes.actions, + omitGeneratedValues: false, + isSystemAction: (connectorId: string) => actionsClient.isSystemAction(connectorId), + }), + }; } + +/** + * This is a temporary validation to ensure that actions + * contain the expected properties. When the method is migrated to + * use a schema for validation, like the create_rule method, the + * function should be deleted in favor of the schema validation. + */ +const validateActionsSchema = ( + actions: NormalizedAlertAction[], + systemActions: NormalizedSystemAction[] +) => { + for (const action of actions) { + if (!action.group) { + // Simulating kbn-schema error message + throw new Error('[actions.0.group]: expected value of type [string] but got [undefined]'); + } + } + + for (const systemAction of systemActions) { + if ('group' in systemAction || 'frequency' in systemAction || 'alertsFilter' in systemAction) { + // Simulating kbn-schema error message + throw new Error('definition for this key is missing'); + } + } +}; diff --git a/x-pack/plugins/alerting/server/rules_client/rules_client.ts b/x-pack/plugins/alerting/server/rules_client/rules_client.ts index 39b4353525f7e..002b0a8d1d0b2 100644 --- a/x-pack/plugins/alerting/server/rules_client/rules_client.ts +++ b/x-pack/plugins/alerting/server/rules_client/rules_client.ts @@ -103,6 +103,7 @@ export const fieldsToExcludeFromRevisionUpdates: ReadonlySet { @@ -87,6 +94,8 @@ const rulesClientParams: jest.Mocked = { minimumScheduleInterval: { value: '1m', enforce: false }, isAuthenticationTypeAPIKey: jest.fn(), getAuthenticationAPIKey: jest.fn(), + connectorAdapterRegistry: new ConnectorAdapterRegistry(), + isSystemAction: jest.fn(), getAlertIndicesAlias: jest.fn(), alertsService: null, uiSettings: uiSettingsServiceMock.createStartContract(), @@ -113,6 +122,7 @@ setGlobalDate(); describe('bulkEnableRules', () => { let rulesClient: RulesClient; + let actionsClient: jest.Mocked; const mockCreatePointInTimeFinderAsInternalUser = ( response = { saved_objects: [disabledRule1, disabledRule2] } @@ -154,11 +164,14 @@ describe('bulkEnableRules', () => { }); mockCreatePointInTimeFinderAsInternalUser(); mockUnsecuredSavedObjectFind(2); + actionsClient = (await rulesClientParams.getActionsClient()) as jest.Mocked; + actionsClient.isSystemAction.mockImplementation((id: string) => id === 'system_action:id'); + rulesClientParams.getActionsClient.mockResolvedValue(actionsClient); }); test('should enable two rule', async () => { unsecuredSavedObjectsClient.bulkCreate.mockResolvedValue({ - saved_objects: [enabledRule1, enabledRule2], + saved_objects: [enabledRuleForBulkOps1, enabledRuleForBulkOps2], }); const result = await rulesClient.bulkEnableRules({ filter: 'fake_filter' }); @@ -189,15 +202,48 @@ describe('bulkEnableRules', () => { expect(result).toStrictEqual({ errors: [], - rules: [returnedRule1, returnedRule2], + rules: [returnedRuleForBulkOps1, returnedRuleForBulkOps2], total: 2, taskIdsFailedToBeEnabled: [], }); }); + test('should enable two rule and return right actions', async () => { + unsecuredSavedObjectsClient.bulkCreate.mockResolvedValue({ + saved_objects: [enabledRuleForBulkOpsWithActions1, enabledRuleForBulkOpsWithActions2], + }); + + const result = await rulesClient.bulkDisableRules({ filter: 'fake_filter' }); + + expect(unsecuredSavedObjectsClient.bulkCreate).toHaveBeenCalledTimes(1); + expect(unsecuredSavedObjectsClient.bulkCreate).toHaveBeenCalledWith( + expect.arrayContaining([ + expect.objectContaining({ + id: 'id1', + attributes: expect.objectContaining({ + enabled: false, + }), + }), + expect.objectContaining({ + id: 'id2', + attributes: expect.objectContaining({ + enabled: false, + }), + }), + ]), + { overwrite: true } + ); + + expect(result).toStrictEqual({ + errors: [], + rules: [returnedRuleForBulkEnableWithActions1, returnedRuleForBulkEnableWithActions2], + total: 2, + }); + }); + test('should try to enable rules, one successful and one with 500 error', async () => { unsecuredSavedObjectsClient.bulkCreate.mockResolvedValue({ - saved_objects: [enabledRule1, savedObjectWith500Error], + saved_objects: [enabledRuleForBulkOps1, savedObjectWith500Error], }); const result = await rulesClient.bulkEnableRules({ filter: 'fake_filter' }); @@ -217,7 +263,7 @@ describe('bulkEnableRules', () => { expect(result).toStrictEqual({ errors: [{ message: 'UPS', rule: { id: 'id2', name: 'fakeName' }, status: 500 }], - rules: [returnedRule1], + rules: [returnedRuleForBulkOps1], total: 2, taskIdsFailedToBeEnabled: [], }); @@ -226,7 +272,7 @@ describe('bulkEnableRules', () => { test('should try to enable rules, one successful and one with 409 error, which will not be deleted with retry', async () => { unsecuredSavedObjectsClient.bulkCreate .mockResolvedValueOnce({ - saved_objects: [enabledRule1, savedObjectWith409Error], + saved_objects: [enabledRuleForBulkOps1, savedObjectWith409Error], }) .mockResolvedValueOnce({ saved_objects: [savedObjectWith409Error], @@ -270,7 +316,7 @@ describe('bulkEnableRules', () => { expect(unsecuredSavedObjectsClient.bulkCreate).toHaveBeenCalledTimes(4); expect(result).toStrictEqual({ errors: [{ message: 'UPS', rule: { id: 'id2', name: 'fakeName' }, status: 409 }], - rules: [returnedRule1], + rules: [returnedRuleForBulkOps1], total: 2, taskIdsFailedToBeEnabled: [], }); @@ -279,10 +325,10 @@ describe('bulkEnableRules', () => { test('should try to enable rules, one successful and one with 409 error, which successfully will be deleted with retry', async () => { unsecuredSavedObjectsClient.bulkCreate .mockResolvedValueOnce({ - saved_objects: [enabledRule1, savedObjectWith409Error], + saved_objects: [enabledRuleForBulkOps1, savedObjectWith409Error], }) .mockResolvedValueOnce({ - saved_objects: [enabledRule2], + saved_objects: [enabledRuleForBulkOps2], }); encryptedSavedObjects.createPointInTimeFinderDecryptedAsInternalUser = jest @@ -311,7 +357,7 @@ describe('bulkEnableRules', () => { expect(unsecuredSavedObjectsClient.bulkCreate).toHaveBeenCalledTimes(2); expect(result).toStrictEqual({ errors: [], - rules: [returnedRule1, returnedRule2], + rules: [returnedRuleForBulkOps1, returnedRuleForBulkOps2], total: 2, taskIdsFailedToBeEnabled: [], }); @@ -386,10 +432,10 @@ describe('bulkEnableRules', () => { test('should skip rule if it is already enabled', async () => { mockCreatePointInTimeFinderAsInternalUser({ - saved_objects: [disabledRule1, enabledRule2], + saved_objects: [disabledRule1, enabledRuleForBulkOps2], }); unsecuredSavedObjectsClient.bulkCreate.mockResolvedValue({ - saved_objects: [enabledRule1], + saved_objects: [enabledRuleForBulkOps1], }); const result = await rulesClient.bulkEnableRules({ filter: 'fake_filter' }); @@ -409,7 +455,7 @@ describe('bulkEnableRules', () => { expect(result).toStrictEqual({ errors: [], - rules: [returnedRule1], + rules: [returnedRuleForBulkOps1], total: 2, taskIdsFailedToBeEnabled: [], }); @@ -418,7 +464,7 @@ describe('bulkEnableRules', () => { describe('taskManager', () => { test('should return task id if enabling task failed', async () => { unsecuredSavedObjectsClient.bulkCreate.mockResolvedValue({ - saved_objects: [enabledRule1, enabledRule2], + saved_objects: [enabledRuleForBulkOps1, enabledRuleForBulkOps2], }); taskManager.bulkEnable.mockImplementation(async () => ({ tasks: [taskManagerMock.createTask({ id: 'id1' })], @@ -446,7 +492,7 @@ describe('bulkEnableRules', () => { expect(result).toStrictEqual({ errors: [], - rules: [returnedRule1, returnedRule2], + rules: [returnedRuleForBulkOps1, returnedRuleForBulkOps2], total: 2, taskIdsFailedToBeEnabled: ['id2'], }); @@ -454,7 +500,7 @@ describe('bulkEnableRules', () => { test('should not throw an error if taskManager throw an error', async () => { unsecuredSavedObjectsClient.bulkCreate.mockResolvedValue({ - saved_objects: [enabledRule1, enabledRule2], + saved_objects: [enabledRuleForBulkOps1, enabledRuleForBulkOps2], }); taskManager.bulkEnable.mockImplementation(() => { throw new Error('UPS'); @@ -469,7 +515,7 @@ describe('bulkEnableRules', () => { expect(result).toStrictEqual({ errors: [], - rules: [returnedRule1, returnedRule2], + rules: [returnedRuleForBulkOps1, returnedRuleForBulkOps2], taskIdsFailedToBeEnabled: ['id1', 'id2'], total: 2, }); @@ -477,7 +523,7 @@ describe('bulkEnableRules', () => { test('should call task manager bulkEnable for two tasks', async () => { unsecuredSavedObjectsClient.bulkCreate.mockResolvedValue({ - saved_objects: [enabledRule1, enabledRule2], + saved_objects: [enabledRuleForBulkOps1, enabledRuleForBulkOps2], }); await rulesClient.bulkEnableRules({ filter: 'fake_filter' }); @@ -488,7 +534,7 @@ describe('bulkEnableRules', () => { test('should should call task manager bulkEnable only for one task, if one rule have an error', async () => { unsecuredSavedObjectsClient.bulkCreate.mockResolvedValue({ - saved_objects: [enabledRule1, savedObjectWith500Error], + saved_objects: [enabledRuleForBulkOps1, savedObjectWith500Error], }); await rulesClient.bulkEnableRules({ filter: 'fake_filter' }); @@ -499,10 +545,10 @@ describe('bulkEnableRules', () => { test('should skip task if rule is already enabled', async () => { mockCreatePointInTimeFinderAsInternalUser({ - saved_objects: [disabledRule1, enabledRule2], + saved_objects: [disabledRule1, enabledRuleForBulkOps2], }); unsecuredSavedObjectsClient.bulkCreate.mockResolvedValue({ - saved_objects: [enabledRule1], + saved_objects: [enabledRuleForBulkOps1], }); taskManager.bulkEnable.mockImplementation( @@ -529,7 +575,7 @@ describe('bulkEnableRules', () => { // One rule gets the task successfully, one rule doesn't so only one task should be scheduled taskManager.get.mockRejectedValueOnce(new Error('Failed to get task!')); unsecuredSavedObjectsClient.bulkCreate.mockResolvedValue({ - saved_objects: [enabledRule1, enabledRule2], + saved_objects: [enabledRuleForBulkOps1, enabledRuleForBulkOps2], }); const result = await rulesClient.bulkEnableRules({ ids: ['id1', 'id2'] }); @@ -578,7 +624,7 @@ describe('bulkEnableRules', () => { expect(result).toStrictEqual({ errors: [], - rules: [returnedRule1, returnedRule2], + rules: [returnedRuleForBulkOps1, returnedRuleForBulkOps2], total: 2, taskIdsFailedToBeEnabled: [], }); @@ -602,7 +648,7 @@ describe('bulkEnableRules', () => { }, }); unsecuredSavedObjectsClient.bulkCreate.mockResolvedValue({ - saved_objects: [enabledRule1, enabledRule2], + saved_objects: [enabledRuleForBulkOps1, enabledRuleForBulkOps2], }); const result = await rulesClient.bulkEnableRules({ ids: ['id1', 'id2'] }); @@ -650,7 +696,7 @@ describe('bulkEnableRules', () => { expect(result).toStrictEqual({ errors: [], - rules: [returnedRule1, returnedRule2], + rules: [returnedRuleForBulkOps1, returnedRuleForBulkOps2], total: 2, taskIdsFailedToBeEnabled: [], }); @@ -674,7 +720,7 @@ describe('bulkEnableRules', () => { enabled: false, }); unsecuredSavedObjectsClient.bulkCreate.mockResolvedValue({ - saved_objects: [enabledRule1, enabledRule2], + saved_objects: [enabledRuleForBulkOps1, enabledRuleForBulkOps2], }); const result = await rulesClient.bulkEnableRules({ ids: ['id1', 'id2'] }); @@ -725,7 +771,7 @@ describe('bulkEnableRules', () => { expect(result).toStrictEqual({ errors: [], - rules: [returnedRule1, returnedRule2], + rules: [returnedRuleForBulkOps1, returnedRuleForBulkOps2], total: 2, taskIdsFailedToBeEnabled: [], }); @@ -737,7 +783,7 @@ describe('bulkEnableRules', () => { test('logs audit event when enabling rules', async () => { unsecuredSavedObjectsClient.bulkCreate.mockResolvedValue({ - saved_objects: [enabledRule1], + saved_objects: [enabledRuleForBulkOps1], }); await rulesClient.bulkEnableRules({ filter: 'fake_filter' }); @@ -788,32 +834,38 @@ describe('bulkEnableRules', () => { .mockResolvedValueOnce({ close: jest.fn(), find: function* asyncGenerator() { - yield { saved_objects: [disabledRule1, siemRule1, siemRule2] }; + yield { + saved_objects: [ + disabledRuleForBulkDisable1, + siemRuleForBulkOps1, + siemRuleForBulkOps2, + ], + }; }, }); unsecuredSavedObjectsClient.bulkCreate.mockResolvedValue({ - saved_objects: [disabledRule1, siemRule1, siemRule2], + saved_objects: [disabledRuleForBulkDisable1, siemRuleForBulkOps1, siemRuleForBulkOps2], }); await rulesClient.bulkEnableRules({ filter: 'fake_filter' }); expect(migrateLegacyActions).toHaveBeenCalledTimes(3); expect(migrateLegacyActions).toHaveBeenCalledWith(expect.any(Object), { - attributes: disabledRule1.attributes, - ruleId: disabledRule1.id, + attributes: disabledRuleForBulkDisable1.attributes, + ruleId: disabledRuleForBulkDisable1.id, actions: [], references: [], }); expect(migrateLegacyActions).toHaveBeenCalledWith(expect.any(Object), { attributes: expect.objectContaining({ consumer: AlertConsumers.SIEM }), - ruleId: siemRule1.id, + ruleId: siemRuleForBulkOps1.id, actions: [], references: [], }); expect(migrateLegacyActions).toHaveBeenCalledWith(expect.any(Object), { attributes: expect.objectContaining({ consumer: AlertConsumers.SIEM }), - ruleId: siemRule2.id, + ruleId: siemRuleForBulkOps2.id, actions: [], references: [], }); diff --git a/x-pack/plugins/alerting/server/rules_client/tests/clear_expired_snoozes.test.ts b/x-pack/plugins/alerting/server/rules_client/tests/clear_expired_snoozes.test.ts index a9673c8aa0f96..a5b691894b77c 100644 --- a/x-pack/plugins/alerting/server/rules_client/tests/clear_expired_snoozes.test.ts +++ b/x-pack/plugins/alerting/server/rules_client/tests/clear_expired_snoozes.test.ts @@ -26,6 +26,7 @@ import { getBeforeSetup, mockedDateString } from './lib'; import { eventLoggerMock } from '@kbn/event-log-plugin/server/event_logger.mock'; import { TaskStatus } from '@kbn/task-manager-plugin/server'; import { RuleSnooze } from '../../types'; +import { ConnectorAdapterRegistry } from '../../connector_adapters/connector_adapter_registry'; import { RULE_SAVED_OBJECT_TYPE } from '../../saved_objects'; jest.mock('../../invalidate_pending_api_keys/bulk_mark_api_keys_for_invalidation', () => ({ @@ -71,9 +72,11 @@ const rulesClientParams: jest.Mocked = { eventLogger, isAuthenticationTypeAPIKey: jest.fn(), getAuthenticationAPIKey: jest.fn(), + connectorAdapterRegistry: new ConnectorAdapterRegistry(), getAlertIndicesAlias: jest.fn(), alertsService: null, uiSettings: uiSettingsServiceMock.createStartContract(), + isSystemAction: jest.fn(), }; describe('clearExpiredSnoozes()', () => { diff --git a/x-pack/plugins/alerting/server/rules_client/tests/clone.test.ts b/x-pack/plugins/alerting/server/rules_client/tests/clone.test.ts new file mode 100644 index 0000000000000..a23d9b159d79b --- /dev/null +++ b/x-pack/plugins/alerting/server/rules_client/tests/clone.test.ts @@ -0,0 +1,178 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { + savedObjectsClientMock, + loggingSystemMock, + savedObjectsRepositoryMock, + uiSettingsServiceMock, +} from '@kbn/core/server/mocks'; +import { taskManagerMock } from '@kbn/task-manager-plugin/server/mocks'; +import { ruleTypeRegistryMock } from '../../rule_type_registry.mock'; +import { alertingAuthorizationMock } from '../../authorization/alerting_authorization.mock'; +import { encryptedSavedObjectsMock } from '@kbn/encrypted-saved-objects-plugin/server/mocks'; +import { actionsAuthorizationMock } from '@kbn/actions-plugin/server/mocks'; +import { AlertingAuthorization } from '../../authorization/alerting_authorization'; +import { ActionsAuthorization } from '@kbn/actions-plugin/server'; +import { auditLoggerMock } from '@kbn/security-plugin/server/audit/mocks'; +import { ConnectorAdapterRegistry } from '../../connector_adapters/connector_adapter_registry'; +import { RULE_SAVED_OBJECT_TYPE } from '../../saved_objects'; +import { getBeforeSetup } from './lib'; +import { RuleDomain } from '../../application/rule/types'; +import { ConstructorOptions, RulesClient } from '../rules_client'; + +describe('clone', () => { + const taskManager = taskManagerMock.createStart(); + const ruleTypeRegistry = ruleTypeRegistryMock.create(); + const unsecuredSavedObjectsClient = savedObjectsClientMock.create(); + const encryptedSavedObjects = encryptedSavedObjectsMock.createClient(); + const authorization = alertingAuthorizationMock.create(); + const actionsAuthorization = actionsAuthorizationMock.create(); + const auditLogger = auditLoggerMock.create(); + const internalSavedObjectsRepository = savedObjectsRepositoryMock.create(); + + const kibanaVersion = 'v8.2.0'; + const createAPIKeyMock = jest.fn(); + const isAuthenticationTypeApiKeyMock = jest.fn(); + const getAuthenticationApiKeyMock = jest.fn(); + + const rulesClientParams: jest.Mocked = { + taskManager, + ruleTypeRegistry, + unsecuredSavedObjectsClient, + authorization: authorization as unknown as AlertingAuthorization, + actionsAuthorization: actionsAuthorization as unknown as ActionsAuthorization, + spaceId: 'default', + namespace: 'default', + getUserName: jest.fn(), + createAPIKey: createAPIKeyMock, + logger: loggingSystemMock.create().get(), + internalSavedObjectsRepository, + encryptedSavedObjectsClient: encryptedSavedObjects, + getActionsClient: jest.fn(), + getEventLogClient: jest.fn(), + kibanaVersion, + auditLogger, + maxScheduledPerMinute: 10000, + minimumScheduleInterval: { value: '1m', enforce: false }, + isAuthenticationTypeAPIKey: isAuthenticationTypeApiKeyMock, + getAuthenticationAPIKey: getAuthenticationApiKeyMock, + connectorAdapterRegistry: new ConnectorAdapterRegistry(), + isSystemAction: jest.fn(), + getAlertIndicesAlias: jest.fn(), + alertsService: null, + uiSettings: uiSettingsServiceMock.createStartContract(), + }; + + let rulesClient: RulesClient; + + beforeEach(() => { + jest.clearAllMocks(); + getBeforeSetup(rulesClientParams, taskManager, ruleTypeRegistry); + rulesClient = new RulesClient(rulesClientParams); + }); + + describe('actions', () => { + const rule = { + id: 'test-rule', + type: RULE_SAVED_OBJECT_TYPE, + attributes: { + name: 'My rule', + alertTypeId: '123', + schedule: { interval: '10s' }, + params: { + bar: true, + }, + createdAt: new Date().toISOString(), + updatedAt: new Date().toISOString(), + actions: [ + { + frequency: { + notifyWhen: 'onActiveAlert' as const, + summary: false, + throttle: null, + }, + group: 'default', + params: {}, + actionRef: 'action_0', + actionTypeId: 'test-1', + uuid: '222', + }, + { + params: {}, + actionRef: 'system_action:system_action-id', + actionTypeId: 'test-2', + uuid: '222', + }, + ], + notifyWhen: 'onActiveAlert', + executionStatus: {}, + apiKey: Buffer.from('123:abc').toString('base64'), + }, + references: [ + { + name: 'action_0', + type: 'action', + id: '1', + }, + ], + }; + + it('transform actions correctly', async () => { + encryptedSavedObjects.getDecryptedAsInternalUser.mockResolvedValue(rule); + unsecuredSavedObjectsClient.create.mockResolvedValue(rule); + + const res = await rulesClient.clone('test-rule', { newId: 'test-rule-2' }); + + expect(res.actions).toEqual([ + { + actionTypeId: 'test-1', + frequency: { notifyWhen: 'onActiveAlert', summary: false, throttle: null }, + group: 'default', + id: '1', + params: {}, + uuid: '222', + }, + ]); + + expect(res.systemActions).toEqual([ + { actionTypeId: 'test-2', id: 'system_action-id', params: {}, uuid: '222' }, + ]); + }); + + it('clones the actions correctly', async () => { + encryptedSavedObjects.getDecryptedAsInternalUser.mockResolvedValue(rule); + unsecuredSavedObjectsClient.create.mockResolvedValue(rule); + + await rulesClient.clone('test-rule', { newId: 'test-rule-2' }); + const results = unsecuredSavedObjectsClient.create.mock.calls[0][1] as RuleDomain; + + expect(results.actions).toMatchInlineSnapshot(` + Array [ + Object { + "actionRef": "action_0", + "actionTypeId": "test-1", + "frequency": Object { + "notifyWhen": "onActiveAlert", + "summary": false, + "throttle": null, + }, + "group": "default", + "params": Object {}, + "uuid": "222", + }, + Object { + "actionRef": "system_action:system_action-id", + "actionTypeId": "test-2", + "params": Object {}, + "uuid": "222", + }, + ] + `); + }); + }); +}); diff --git a/x-pack/plugins/alerting/server/rules_client/tests/delete.test.ts b/x-pack/plugins/alerting/server/rules_client/tests/delete.test.ts index c7988b7644fcd..20ea58cad66ab 100644 --- a/x-pack/plugins/alerting/server/rules_client/tests/delete.test.ts +++ b/x-pack/plugins/alerting/server/rules_client/tests/delete.test.ts @@ -25,6 +25,7 @@ import { auditLoggerMock } from '@kbn/security-plugin/server/audit/mocks'; import { getBeforeSetup } from './lib'; import { bulkMarkApiKeysForInvalidation } from '../../invalidate_pending_api_keys/bulk_mark_api_keys_for_invalidation'; import { migrateLegacyActions } from '../lib'; +import { ConnectorAdapterRegistry } from '../../connector_adapters/connector_adapter_registry'; import { RULE_SAVED_OBJECT_TYPE } from '../../saved_objects'; jest.mock('../lib/siem_legacy_actions/migrate_legacy_actions', () => { @@ -73,9 +74,11 @@ const rulesClientParams: jest.Mocked = { auditLogger, isAuthenticationTypeAPIKey: jest.fn(), getAuthenticationAPIKey: jest.fn(), + connectorAdapterRegistry: new ConnectorAdapterRegistry(), getAlertIndicesAlias: jest.fn(), alertsService: null, uiSettings: uiSettingsServiceMock.createStartContract(), + isSystemAction: jest.fn(), }; beforeEach(() => { diff --git a/x-pack/plugins/alerting/server/rules_client/tests/disable.test.ts b/x-pack/plugins/alerting/server/rules_client/tests/disable.test.ts index 9e7073da8a18d..9d21fa3477fb9 100644 --- a/x-pack/plugins/alerting/server/rules_client/tests/disable.test.ts +++ b/x-pack/plugins/alerting/server/rules_client/tests/disable.test.ts @@ -26,6 +26,7 @@ import { eventLoggerMock } from '@kbn/event-log-plugin/server/event_logger.mock' import { TaskStatus } from '@kbn/task-manager-plugin/server'; import { migrateLegacyActions } from '../lib'; import { migrateLegacyActionsMock } from '../lib/siem_legacy_actions/retrieve_migrated_legacy_actions.mock'; +import { ConnectorAdapterRegistry } from '../../connector_adapters/connector_adapter_registry'; import { RULE_SAVED_OBJECT_TYPE } from '../../saved_objects'; jest.mock('../lib/siem_legacy_actions/migrate_legacy_actions', () => { @@ -75,9 +76,11 @@ const rulesClientParams: jest.Mocked = { eventLogger, isAuthenticationTypeAPIKey: jest.fn(), getAuthenticationAPIKey: jest.fn(), + connectorAdapterRegistry: new ConnectorAdapterRegistry(), getAlertIndicesAlias: jest.fn(), alertsService: null, uiSettings: uiSettingsServiceMock.createStartContract(), + isSystemAction: jest.fn(), }; beforeEach(() => { diff --git a/x-pack/plugins/alerting/server/rules_client/tests/enable.test.ts b/x-pack/plugins/alerting/server/rules_client/tests/enable.test.ts index 580e557e7fdf4..dd48bbe5412c7 100644 --- a/x-pack/plugins/alerting/server/rules_client/tests/enable.test.ts +++ b/x-pack/plugins/alerting/server/rules_client/tests/enable.test.ts @@ -25,6 +25,7 @@ import { auditLoggerMock } from '@kbn/security-plugin/server/audit/mocks'; import { getBeforeSetup, setGlobalDate } from './lib'; import { migrateLegacyActions } from '../lib'; import { migrateLegacyActionsMock } from '../lib/siem_legacy_actions/retrieve_migrated_legacy_actions.mock'; +import { ConnectorAdapterRegistry } from '../../connector_adapters/connector_adapter_registry'; import { RULE_SAVED_OBJECT_TYPE } from '../../saved_objects'; jest.mock('../lib/siem_legacy_actions/migrate_legacy_actions', () => { @@ -72,9 +73,11 @@ const rulesClientParams: jest.Mocked = { auditLogger, isAuthenticationTypeAPIKey: jest.fn(), getAuthenticationAPIKey: jest.fn(), + connectorAdapterRegistry: new ConnectorAdapterRegistry(), getAlertIndicesAlias: jest.fn(), alertsService: null, uiSettings: uiSettingsServiceMock.createStartContract(), + isSystemAction: jest.fn(), }; setGlobalDate(); diff --git a/x-pack/plugins/alerting/server/rules_client/tests/find.test.ts b/x-pack/plugins/alerting/server/rules_client/tests/find.test.ts index 2281264adaf35..70e9ef57b2e6a 100644 --- a/x-pack/plugins/alerting/server/rules_client/tests/find.test.ts +++ b/x-pack/plugins/alerting/server/rules_client/tests/find.test.ts @@ -27,6 +27,7 @@ import { RegistryRuleType } from '../../rule_type_registry'; import { schema } from '@kbn/config-schema'; import { enabledRule1, enabledRule2, siemRule1, siemRule2 } from './test_helpers'; import { formatLegacyActions } from '../lib'; +import { ConnectorAdapterRegistry } from '../../connector_adapters/connector_adapter_registry'; import { RULE_SAVED_OBJECT_TYPE } from '../../saved_objects'; jest.mock('../lib/siem_legacy_actions/format_legacy_actions', () => { @@ -65,9 +66,11 @@ const rulesClientParams: jest.Mocked = { kibanaVersion, isAuthenticationTypeAPIKey: jest.fn(), getAuthenticationAPIKey: jest.fn(), + connectorAdapterRegistry: new ConnectorAdapterRegistry(), getAlertIndicesAlias: jest.fn(), alertsService: null, uiSettings: uiSettingsServiceMock.createStartContract(), + isSystemAction: jest.fn().mockImplementation((id) => id === 'system_action-id'), }; beforeEach(() => { @@ -100,10 +103,12 @@ describe('find()', () => { validLegacyConsumers: [], }, ]); + beforeEach(() => { authorization.getFindAuthorizationFilter.mockResolvedValue({ ensureRuleTypeIsAuthorized() {}, }); + unsecuredSavedObjectsClient.find.mockResolvedValueOnce({ total: 1, per_page: 10, @@ -123,11 +128,13 @@ describe('find()', () => { notifyWhen: 'onActiveAlert', actions: [ { + actionTypeId: 'test-action-id', group: 'default', actionRef: 'action_0', params: { foo: true, }, + uuid: 100, }, ], }, @@ -142,6 +149,7 @@ describe('find()', () => { }, ], }); + ruleTypeRegistry.list.mockReturnValue(listedTypes); authorization.filterByRuleTypeAuthorization.mockResolvedValue( new Set([ @@ -176,11 +184,13 @@ describe('find()', () => { Object { "actions": Array [ Object { + "actionTypeId": "test-action-id", "group": "default", "id": "1", "params": Object { "foo": true, }, + "uuid": 100, }, ], "alertTypeId": "myType", @@ -194,6 +204,7 @@ describe('find()', () => { "interval": "10s", }, "snoozeSchedule": Array [], + "systemActions": Array [], "updatedAt": 2019-02-12T21:01:22.479Z, }, ], @@ -270,18 +281,22 @@ describe('find()', () => { Object { "actions": Array [ Object { + "actionTypeId": undefined, "group": "default", "id": "1", "params": Object { "foo": true, }, + "uuid": undefined, }, Object { + "actionTypeId": undefined, "group": "default", "id": "preconfigured", "params": Object { "foo": true, }, + "uuid": undefined, }, ], "alertTypeId": "myType", @@ -295,6 +310,7 @@ describe('find()', () => { "interval": "10s", }, "snoozeSchedule": Array [], + "systemActions": Array [], "updatedAt": 2019-02-12T21:01:22.479Z, }, ], @@ -361,24 +377,23 @@ describe('find()', () => { }, ], }); + const rulesClient = new RulesClient(rulesClientParams); const result = await rulesClient.find({ options: {} }); + expect(result).toMatchInlineSnapshot(` Object { "data": Array [ Object { "actions": Array [ Object { + "actionTypeId": undefined, "group": "default", "id": "1", "params": Object { "foo": true, }, - }, - Object { - "group": "default", - "id": "system_action-id", - "params": Object {}, + "uuid": undefined, }, ], "alertTypeId": "myType", @@ -392,6 +407,14 @@ describe('find()', () => { "interval": "10s", }, "snoozeSchedule": Array [], + "systemActions": Array [ + Object { + "actionTypeId": undefined, + "id": "system_action-id", + "params": Object {}, + "uuid": undefined, + }, + ], "updatedAt": 2019-02-12T21:01:22.479Z, }, ], @@ -613,11 +636,13 @@ describe('find()', () => { Object { "actions": Array [ Object { + "actionTypeId": undefined, "group": "default", "id": "1", "params": Object { "foo": true, }, + "uuid": undefined, }, ], "alertTypeId": "myType", @@ -631,16 +656,19 @@ describe('find()', () => { "interval": "10s", }, "snoozeSchedule": Array [], + "systemActions": Array [], "updatedAt": 2019-02-12T21:01:22.479Z, }, Object { "actions": Array [ Object { + "actionTypeId": undefined, "group": "default", "id": "1", "params": Object { "foo": true, }, + "uuid": undefined, }, ], "alertTypeId": "123", @@ -655,6 +683,7 @@ describe('find()', () => { "interval": "20s", }, "snoozeSchedule": Array [], + "systemActions": Array [], "updatedAt": 2019-02-12T21:01:22.479Z, }, ], @@ -881,6 +910,7 @@ describe('find()', () => { "params": undefined, "schedule": undefined, "snoozeSchedule": Array [], + "systemActions": Array [], "tags": Array [ "myTag", ], diff --git a/x-pack/plugins/alerting/server/rules_client/tests/get.test.ts b/x-pack/plugins/alerting/server/rules_client/tests/get.test.ts index e377c334d0b1f..1f136a0c181b3 100644 --- a/x-pack/plugins/alerting/server/rules_client/tests/get.test.ts +++ b/x-pack/plugins/alerting/server/rules_client/tests/get.test.ts @@ -24,6 +24,7 @@ import { auditLoggerMock } from '@kbn/security-plugin/server/audit/mocks'; import { getBeforeSetup, setGlobalDate } from './lib'; import { RecoveredActionGroup } from '../../../common'; import { formatLegacyActions } from '../lib'; +import { ConnectorAdapterRegistry } from '../../connector_adapters/connector_adapter_registry'; import { RULE_SAVED_OBJECT_TYPE } from '../../saved_objects'; jest.mock('../lib/siem_legacy_actions/format_legacy_actions', () => { @@ -62,9 +63,11 @@ const rulesClientParams: jest.Mocked = { kibanaVersion, isAuthenticationTypeAPIKey: jest.fn(), getAuthenticationAPIKey: jest.fn(), + connectorAdapterRegistry: new ConnectorAdapterRegistry(), getAlertIndicesAlias: jest.fn(), alertsService: null, uiSettings: uiSettingsServiceMock.createStartContract(), + isSystemAction: jest.fn(), }; beforeEach(() => { @@ -112,11 +115,13 @@ describe('get()', () => { Object { "actions": Array [ Object { + "actionTypeId": undefined, "group": "default", "id": "1", "params": Object { "foo": true, }, + "uuid": undefined, }, ], "alertTypeId": "123", @@ -130,6 +135,7 @@ describe('get()', () => { "interval": "10s", }, "snoozeSchedule": Array [], + "systemActions": Array [], "updatedAt": 2019-02-12T21:01:22.479Z, } `); @@ -186,18 +192,22 @@ describe('get()', () => { Object { "actions": Array [ Object { + "actionTypeId": undefined, "group": "default", "id": "1", "params": Object { "foo": true, }, + "uuid": undefined, }, Object { + "actionTypeId": undefined, "group": "default", "id": "preconfigured", "params": Object { "foo": true, }, + "uuid": undefined, }, ], "alertTypeId": "123", @@ -211,6 +221,7 @@ describe('get()', () => { "interval": "10s", }, "snoozeSchedule": Array [], + "systemActions": Array [], "updatedAt": 2019-02-12T21:01:22.479Z, } `); @@ -265,16 +276,13 @@ describe('get()', () => { Object { "actions": Array [ Object { + "actionTypeId": undefined, "group": "default", "id": "1", "params": Object { "foo": true, }, - }, - Object { - "group": "default", - "id": "system_action-id", - "params": Object {}, + "uuid": undefined, }, ], "alertTypeId": "123", @@ -288,6 +296,14 @@ describe('get()', () => { "interval": "10s", }, "snoozeSchedule": Array [], + "systemActions": Array [ + Object { + "actionTypeId": undefined, + "id": "system_action-id", + "params": Object {}, + "uuid": undefined, + }, + ], "updatedAt": 2019-02-12T21:01:22.479Z, } `); @@ -377,11 +393,13 @@ describe('get()', () => { Object { "actions": Array [ Object { + "actionTypeId": undefined, "group": "default", "id": "1", "params": Object { "foo": true, }, + "uuid": undefined, }, ], "alertTypeId": "123", @@ -396,6 +414,7 @@ describe('get()', () => { "interval": "10s", }, "snoozeSchedule": Array [], + "systemActions": Array [], "updatedAt": 2019-02-12T21:01:22.479Z, } `); diff --git a/x-pack/plugins/alerting/server/rules_client/tests/get_action_error_log.test.ts b/x-pack/plugins/alerting/server/rules_client/tests/get_action_error_log.test.ts index 761aca1972dd5..0cf207500fdb7 100644 --- a/x-pack/plugins/alerting/server/rules_client/tests/get_action_error_log.test.ts +++ b/x-pack/plugins/alerting/server/rules_client/tests/get_action_error_log.test.ts @@ -26,6 +26,7 @@ import { SavedObject } from '@kbn/core/server'; import { RawRule } from '../../types'; import { auditLoggerMock } from '@kbn/security-plugin/server/audit/mocks'; import { getBeforeSetup, mockedDateString, setGlobalDate } from './lib'; +import { ConnectorAdapterRegistry } from '../../connector_adapters/connector_adapter_registry'; import { RULE_SAVED_OBJECT_TYPE } from '../../saved_objects'; const taskManager = taskManagerMock.createStart(); @@ -61,9 +62,11 @@ const rulesClientParams: jest.Mocked = { auditLogger, isAuthenticationTypeAPIKey: jest.fn(), getAuthenticationAPIKey: jest.fn(), + connectorAdapterRegistry: new ConnectorAdapterRegistry(), getAlertIndicesAlias: jest.fn(), alertsService: null, uiSettings: uiSettingsServiceMock.createStartContract(), + isSystemAction: jest.fn(), }; beforeEach(() => { diff --git a/x-pack/plugins/alerting/server/rules_client/tests/get_alert_state.test.ts b/x-pack/plugins/alerting/server/rules_client/tests/get_alert_state.test.ts index dc20013b2ad29..94db9bd1c9629 100644 --- a/x-pack/plugins/alerting/server/rules_client/tests/get_alert_state.test.ts +++ b/x-pack/plugins/alerting/server/rules_client/tests/get_alert_state.test.ts @@ -21,6 +21,7 @@ import { actionsAuthorizationMock } from '@kbn/actions-plugin/server/mocks'; import { AlertingAuthorization } from '../../authorization/alerting_authorization'; import { ActionsAuthorization } from '@kbn/actions-plugin/server'; import { getBeforeSetup } from './lib'; +import { ConnectorAdapterRegistry } from '../../connector_adapters/connector_adapter_registry'; import { RULE_SAVED_OBJECT_TYPE } from '../../saved_objects'; import { SavedObjectsErrorHelpers } from '@kbn/core-saved-objects-server'; @@ -54,9 +55,11 @@ const rulesClientParams: jest.Mocked = { kibanaVersion, isAuthenticationTypeAPIKey: jest.fn(), getAuthenticationAPIKey: jest.fn(), + connectorAdapterRegistry: new ConnectorAdapterRegistry(), getAlertIndicesAlias: jest.fn(), alertsService: null, uiSettings: uiSettingsServiceMock.createStartContract(), + isSystemAction: jest.fn(), }; beforeEach(() => { diff --git a/x-pack/plugins/alerting/server/rules_client/tests/get_alert_summary.test.ts b/x-pack/plugins/alerting/server/rules_client/tests/get_alert_summary.test.ts index 0dcc87889b8c4..60769bc20cd6d 100644 --- a/x-pack/plugins/alerting/server/rules_client/tests/get_alert_summary.test.ts +++ b/x-pack/plugins/alerting/server/rules_client/tests/get_alert_summary.test.ts @@ -26,6 +26,7 @@ import { SavedObject } from '@kbn/core/server'; import { EventsFactory } from '../../lib/alert_summary_from_event_log.test'; import { RawRule } from '../../types'; import { getBeforeSetup, mockedDateString, setGlobalDate } from './lib'; +import { ConnectorAdapterRegistry } from '../../connector_adapters/connector_adapter_registry'; import { RULE_SAVED_OBJECT_TYPE } from '../../saved_objects'; const taskManager = taskManagerMock.createStart(); @@ -59,9 +60,11 @@ const rulesClientParams: jest.Mocked = { kibanaVersion, isAuthenticationTypeAPIKey: jest.fn(), getAuthenticationAPIKey: jest.fn(), + connectorAdapterRegistry: new ConnectorAdapterRegistry(), getAlertIndicesAlias: jest.fn(), alertsService: null, uiSettings: uiSettingsServiceMock.createStartContract(), + isSystemAction: jest.fn(), }; beforeEach(() => { diff --git a/x-pack/plugins/alerting/server/rules_client/tests/get_execution_log.test.ts b/x-pack/plugins/alerting/server/rules_client/tests/get_execution_log.test.ts index 82e07adea061a..f90f5835dbcd4 100644 --- a/x-pack/plugins/alerting/server/rules_client/tests/get_execution_log.test.ts +++ b/x-pack/plugins/alerting/server/rules_client/tests/get_execution_log.test.ts @@ -27,6 +27,7 @@ import { auditLoggerMock } from '@kbn/security-plugin/server/audit/mocks'; import { getBeforeSetup, mockedDateString, setGlobalDate } from './lib'; import { getExecutionLogAggregation } from '../../lib/get_execution_log_aggregation'; import { fromKueryExpression } from '@kbn/es-query'; +import { ConnectorAdapterRegistry } from '../../connector_adapters/connector_adapter_registry'; import { RULE_SAVED_OBJECT_TYPE } from '../../saved_objects'; const taskManager = taskManagerMock.createStart(); @@ -62,9 +63,11 @@ const rulesClientParams: jest.Mocked = { auditLogger, isAuthenticationTypeAPIKey: jest.fn(), getAuthenticationAPIKey: jest.fn(), + connectorAdapterRegistry: new ConnectorAdapterRegistry(), getAlertIndicesAlias: jest.fn(), alertsService: null, uiSettings: uiSettingsServiceMock.createStartContract(), + isSystemAction: jest.fn(), }; beforeEach(() => { diff --git a/x-pack/plugins/alerting/server/rules_client/tests/lib.ts b/x-pack/plugins/alerting/server/rules_client/tests/lib.ts index 994d87320b0b6..386cfe8f41eec 100644 --- a/x-pack/plugins/alerting/server/rules_client/tests/lib.ts +++ b/x-pack/plugins/alerting/server/rules_client/tests/lib.ts @@ -132,4 +132,6 @@ export function getBeforeSetup( rulesClientParams.getEventLogClient.mockResolvedValue( eventLogClient ?? eventLogClientMock.create() ); + + rulesClientParams.isSystemAction.mockImplementation((id) => id === 'system_action-id'); } diff --git a/x-pack/plugins/alerting/server/rules_client/tests/list_rule_types.test.ts b/x-pack/plugins/alerting/server/rules_client/tests/list_rule_types.test.ts index a9dd69f1a0e9f..e6f3978987c53 100644 --- a/x-pack/plugins/alerting/server/rules_client/tests/list_rule_types.test.ts +++ b/x-pack/plugins/alerting/server/rules_client/tests/list_rule_types.test.ts @@ -25,6 +25,7 @@ import { ActionsAuthorization } from '@kbn/actions-plugin/server'; import { getBeforeSetup } from './lib'; import { RecoveredActionGroup } from '../../../common'; import { RegistryRuleType } from '../../rule_type_registry'; +import { ConnectorAdapterRegistry } from '../../connector_adapters/connector_adapter_registry'; const taskManager = taskManagerMock.createStart(); const ruleTypeRegistry = ruleTypeRegistryMock.create(); @@ -56,9 +57,11 @@ const rulesClientParams: jest.Mocked = { kibanaVersion, isAuthenticationTypeAPIKey: jest.fn(), getAuthenticationAPIKey: jest.fn(), + connectorAdapterRegistry: new ConnectorAdapterRegistry(), getAlertIndicesAlias: jest.fn(), alertsService: null, uiSettings: uiSettingsServiceMock.createStartContract(), + isSystemAction: jest.fn(), }; beforeEach(() => { diff --git a/x-pack/plugins/alerting/server/rules_client/tests/mute_all.test.ts b/x-pack/plugins/alerting/server/rules_client/tests/mute_all.test.ts index fcafeea9a640c..7911735644403 100644 --- a/x-pack/plugins/alerting/server/rules_client/tests/mute_all.test.ts +++ b/x-pack/plugins/alerting/server/rules_client/tests/mute_all.test.ts @@ -21,6 +21,7 @@ import { AlertingAuthorization } from '../../authorization/alerting_authorizatio import { ActionsAuthorization } from '@kbn/actions-plugin/server'; import { auditLoggerMock } from '@kbn/security-plugin/server/audit/mocks'; import { getBeforeSetup, setGlobalDate } from './lib'; +import { ConnectorAdapterRegistry } from '../../connector_adapters/connector_adapter_registry'; import { RULE_SAVED_OBJECT_TYPE } from '../../saved_objects'; const taskManager = taskManagerMock.createStart(); @@ -53,9 +54,11 @@ const rulesClientParams: jest.Mocked = { kibanaVersion, isAuthenticationTypeAPIKey: jest.fn(), getAuthenticationAPIKey: jest.fn(), + connectorAdapterRegistry: new ConnectorAdapterRegistry(), getAlertIndicesAlias: jest.fn(), alertsService: null, uiSettings: uiSettingsServiceMock.createStartContract(), + isSystemAction: jest.fn(), }; beforeEach(() => { diff --git a/x-pack/plugins/alerting/server/rules_client/tests/mute_instance.test.ts b/x-pack/plugins/alerting/server/rules_client/tests/mute_instance.test.ts index 9c06960ee91ca..36bba7e734748 100644 --- a/x-pack/plugins/alerting/server/rules_client/tests/mute_instance.test.ts +++ b/x-pack/plugins/alerting/server/rules_client/tests/mute_instance.test.ts @@ -21,6 +21,7 @@ import { AlertingAuthorization } from '../../authorization/alerting_authorizatio import { ActionsAuthorization } from '@kbn/actions-plugin/server'; import { auditLoggerMock } from '@kbn/security-plugin/server/audit/mocks'; import { getBeforeSetup, setGlobalDate } from './lib'; +import { ConnectorAdapterRegistry } from '../../connector_adapters/connector_adapter_registry'; import { RULE_SAVED_OBJECT_TYPE } from '../../saved_objects'; const taskManager = taskManagerMock.createStart(); @@ -53,9 +54,11 @@ const rulesClientParams: jest.Mocked = { kibanaVersion, isAuthenticationTypeAPIKey: jest.fn(), getAuthenticationAPIKey: jest.fn(), + connectorAdapterRegistry: new ConnectorAdapterRegistry(), getAlertIndicesAlias: jest.fn(), alertsService: null, uiSettings: uiSettingsServiceMock.createStartContract(), + isSystemAction: jest.fn(), }; beforeEach(() => { diff --git a/x-pack/plugins/alerting/server/rules_client/tests/resolve.test.ts b/x-pack/plugins/alerting/server/rules_client/tests/resolve.test.ts index dcf28f3d03290..bab353225b28b 100644 --- a/x-pack/plugins/alerting/server/rules_client/tests/resolve.test.ts +++ b/x-pack/plugins/alerting/server/rules_client/tests/resolve.test.ts @@ -24,6 +24,7 @@ import { auditLoggerMock } from '@kbn/security-plugin/server/audit/mocks'; import { getBeforeSetup, setGlobalDate } from './lib'; import { RecoveredActionGroup } from '../../../common'; import { formatLegacyActions } from '../lib'; +import { ConnectorAdapterRegistry } from '../../connector_adapters/connector_adapter_registry'; import { RULE_SAVED_OBJECT_TYPE } from '../../saved_objects'; jest.mock('../lib/siem_legacy_actions/format_legacy_actions', () => { @@ -62,9 +63,11 @@ const rulesClientParams: jest.Mocked = { kibanaVersion, isAuthenticationTypeAPIKey: jest.fn(), getAuthenticationAPIKey: jest.fn(), + connectorAdapterRegistry: new ConnectorAdapterRegistry(), getAlertIndicesAlias: jest.fn(), alertsService: null, uiSettings: uiSettingsServiceMock.createStartContract(), + isSystemAction: jest.fn(), }; beforeEach(() => { @@ -121,11 +124,13 @@ describe('resolve()', () => { Object { "actions": Array [ Object { + "actionTypeId": undefined, "group": "default", "id": "1", "params": Object { "foo": true, }, + "uuid": undefined, }, ], "alertTypeId": "123", @@ -145,6 +150,7 @@ describe('resolve()', () => { "interval": "10s", }, "snoozeSchedule": Array [], + "systemActions": Array [], "updatedAt": 2019-02-12T21:01:22.479Z, } `); @@ -301,11 +307,13 @@ describe('resolve()', () => { Object { "actions": Array [ Object { + "actionTypeId": undefined, "group": "default", "id": "1", "params": Object { "foo": true, }, + "uuid": undefined, }, ], "alertTypeId": "123", @@ -326,6 +334,7 @@ describe('resolve()', () => { "interval": "10s", }, "snoozeSchedule": Array [], + "systemActions": Array [], "updatedAt": 2019-02-12T21:01:22.479Z, } `); @@ -440,6 +449,106 @@ describe('resolve()', () => { ); }); + test('resolves a rule with actions using system connectors', async () => { + const rulesClient = new RulesClient(rulesClientParams); + unsecuredSavedObjectsClient.resolve.mockResolvedValueOnce({ + saved_object: { + id: '1', + type: RULE_SAVED_OBJECT_TYPE, + attributes: { + alertTypeId: '123', + schedule: { interval: '10s' }, + params: { + bar: true, + }, + createdAt: new Date().toISOString(), + updatedAt: new Date().toISOString(), + actions: [ + { + group: 'default', + actionRef: 'action_0', + params: { + foo: true, + }, + }, + { + group: 'default', + actionRef: 'system_action:system_action-id', + params: {}, + }, + ], + notifyWhen: 'onActiveAlert', + executionStatus: { + status: 'ok', + last_execution_date: new Date().toISOString(), + last_duration: 10, + }, + }, + references: [ + { + name: 'action_0', + type: 'action', + id: '1', + }, + ], + }, + outcome: 'aliasMatch', + alias_target_id: '2', + }); + + const result = await rulesClient.resolve({ id: '1' }); + expect(result).toMatchInlineSnapshot(` + Object { + "actions": Array [ + Object { + "actionTypeId": undefined, + "group": "default", + "id": "1", + "params": Object { + "foo": true, + }, + "uuid": undefined, + }, + ], + "alertTypeId": "123", + "alias_target_id": "2", + "createdAt": 2019-02-12T21:01:22.479Z, + "executionStatus": Object { + "lastExecutionDate": 2019-02-12T21:01:22.479Z, + "status": "ok", + }, + "id": "1", + "notifyWhen": "onActiveAlert", + "outcome": "aliasMatch", + "params": Object { + "bar": true, + }, + "schedule": Object { + "interval": "10s", + }, + "snoozeSchedule": Array [], + "systemActions": Array [ + Object { + "actionTypeId": undefined, + "id": "system_action-id", + "params": Object {}, + "uuid": undefined, + }, + ], + "updatedAt": 2019-02-12T21:01:22.479Z, + } + `); + + expect(unsecuredSavedObjectsClient.resolve).toHaveBeenCalledTimes(1); + expect(unsecuredSavedObjectsClient.resolve.mock.calls[0]).toMatchInlineSnapshot(` + Array [ + "alert", + "1", + undefined, + ] + `); + }); + describe('authorization', () => { beforeEach(() => { unsecuredSavedObjectsClient.resolve.mockResolvedValueOnce({ diff --git a/x-pack/plugins/alerting/server/rules_client/tests/run_soon.test.ts b/x-pack/plugins/alerting/server/rules_client/tests/run_soon.test.ts index 2874759f98d1e..653e7dd807c8a 100644 --- a/x-pack/plugins/alerting/server/rules_client/tests/run_soon.test.ts +++ b/x-pack/plugins/alerting/server/rules_client/tests/run_soon.test.ts @@ -22,6 +22,7 @@ import { ActionsAuthorization } from '@kbn/actions-plugin/server'; import { TaskStatus } from '@kbn/task-manager-plugin/server'; import { auditLoggerMock } from '@kbn/security-plugin/server/audit/mocks'; import { getBeforeSetup, setGlobalDate } from './lib'; +import { ConnectorAdapterRegistry } from '../../connector_adapters/connector_adapter_registry'; import { RULE_SAVED_OBJECT_TYPE } from '../../saved_objects'; const taskManager = taskManagerMock.createStart(); @@ -55,9 +56,11 @@ const rulesClientParams: jest.Mocked = { auditLogger, isAuthenticationTypeAPIKey: jest.fn(), getAuthenticationAPIKey: jest.fn(), + connectorAdapterRegistry: new ConnectorAdapterRegistry(), getAlertIndicesAlias: jest.fn(), alertsService: null, uiSettings: uiSettingsServiceMock.createStartContract(), + isSystemAction: jest.fn(), }; setGlobalDate(); diff --git a/x-pack/plugins/alerting/server/rules_client/tests/test_helpers.ts b/x-pack/plugins/alerting/server/rules_client/tests/test_helpers.ts index f060187775a8e..869b38f1762ac 100644 --- a/x-pack/plugins/alerting/server/rules_client/tests/test_helpers.ts +++ b/x-pack/plugins/alerting/server/rules_client/tests/test_helpers.ts @@ -148,6 +148,122 @@ export const enabledRuleForBulkOps2 = { }, }; +export const enabledRuleForBulkOpsWithActions1 = { + ...defaultRuleForBulkDelete, + attributes: { + ...defaultRuleForBulkDelete.attributes, + enabled: true, + scheduledTaskId: 'id1', + apiKey: Buffer.from('123:abc').toString('base64'), + actions: [ + { + uuid: '1', + id: 'system_action:id', + actionTypeId: '1', + actionRef: '1', + params: { + foo: true, + }, + }, + ], + }, + references: [ + { + id: 'system_action:id', + name: '1', + type: 'action', + }, + ], +}; + +export const enabledRuleForBulkOpsWithActions2 = { + ...defaultRuleForBulkDelete, + id: 'id2', + attributes: { + ...defaultRuleForBulkDelete.attributes, + enabled: true, + scheduledTaskId: 'id2', + apiKey: Buffer.from('321:abc').toString('base64'), + actions: [ + { + uuid: '2', + id: 'default_action:id', + group: 'default', + actionTypeId: '2', + actionRef: '2', + params: { + foo: true, + }, + }, + ], + }, + references: [ + { + id: 'default_action:id', + name: '2', + type: 'action', + }, + ], +}; + +export const disabledRuleForBulkOpsWithActions1 = { + ...defaultRuleForBulkDelete, + attributes: { + ...defaultRuleForBulkDelete.attributes, + enabled: false, + scheduledTaskId: 'id1', + apiKey: Buffer.from('123:abc').toString('base64'), + actions: [ + { + uuid: '1', + id: 'system_action:id', + actionTypeId: '1', + actionRef: '1', + params: { + foo: true, + }, + }, + ], + }, + references: [ + { + id: 'system_action:id', + name: '1', + type: 'action', + }, + ], +}; + +export const disabledRuleForBulkOpsWithActions2 = { + ...defaultRuleForBulkDelete, + id: 'id2', + attributes: { + ...defaultRuleForBulkDelete.attributes, + enabled: false, + scheduledTaskId: 'id2', + apiKey: Buffer.from('321:abc').toString('base64'), + actions: [ + { + uuid: '2', + id: 'default_action:id', + group: 'default', + actionTypeId: '2', + actionRef: '2', + params: { + foo: true, + }, + }, + ], + }, + references: [ + { + id: 'default_action:id', + name: '2', + type: 'action', + }, + ], +}; + export const enabledRuleForBulkOps3 = { ...defaultRuleForBulkDelete, id: 'id3', @@ -304,7 +420,7 @@ export const returnedRule2 = { snoozeSchedule: [], }; -export const returnedRuleForBulkDelete1 = { +export const returnedRuleForBulkOps1 = { actions: [], alertTypeId: 'fakeType', consumer: 'fakeConsumer', @@ -322,6 +438,7 @@ export const returnedRuleForBulkDelete1 = { }, scheduledTaskId: 'id1', snoozeSchedule: [], + systemActions: [], tags: ['ups'], params: { param: 1 }, muteAll: false, @@ -329,7 +446,7 @@ export const returnedRuleForBulkDelete1 = { revision: 1, }; -export const returnedRuleForBulkDelete2 = { +export const returnedRuleForBulkOps2 = { actions: [], alertTypeId: 'fakeType', consumer: 'fakeConsumer', @@ -347,6 +464,7 @@ export const returnedRuleForBulkDelete2 = { }, scheduledTaskId: 'id2', snoozeSchedule: [], + systemActions: [], tags: ['ups'], params: { param: 1 }, muteAll: false, @@ -354,7 +472,7 @@ export const returnedRuleForBulkDelete2 = { revision: 1, }; -export const returnedRuleForBulkDelete3 = { +export const returnedRuleForBulkOps3 = { actions: [], alertTypeId: 'fakeType', apiKeyCreatedByUser: true, @@ -373,6 +491,7 @@ export const returnedRuleForBulkDelete3 = { }, scheduledTaskId: 'id3', snoozeSchedule: [], + systemActions: [], tags: ['ups'], params: { param: 1 }, muteAll: false, @@ -381,15 +500,73 @@ export const returnedRuleForBulkDelete3 = { }; export const returnedRuleForBulkDisable1 = { - ...returnedRuleForBulkDelete1, + ...returnedRuleForBulkOps1, enabled: false, }; export const returnedRuleForBulkDisable2 = { - ...returnedRuleForBulkDelete2, + ...returnedRuleForBulkOps2, enabled: false, }; +export const returnedRuleForBulkDisableWithActions1 = { + ...returnedRuleForBulkDisable1, + systemActions: [ + { + actionTypeId: '1', + id: 'system_action:id', + params: { + foo: true, + }, + uuid: '1', + }, + ], +}; + +export const returnedRuleForBulkDisableWithActions2 = { + ...returnedRuleForBulkDisable2, + actions: [ + { + actionTypeId: '2', + group: 'default', + id: 'default_action:id', + params: { + foo: true, + }, + uuid: '2', + }, + ], +}; + +export const returnedRuleForBulkEnableWithActions1 = { + ...returnedRuleForBulkOps1, + systemActions: [ + { + actionTypeId: '1', + id: 'system_action:id', + params: { + foo: true, + }, + uuid: '1', + }, + ], +}; + +export const returnedRuleForBulkEnableWithActions2 = { + ...returnedRuleForBulkOps2, + actions: [ + { + actionTypeId: '2', + group: 'default', + id: 'default_action:id', + params: { + foo: true, + }, + uuid: '2', + }, + ], +}; + export const returnedDisabledRule1 = { ...returnedRule1, enabled: false, diff --git a/x-pack/plugins/alerting/server/rules_client/tests/unmute_all.test.ts b/x-pack/plugins/alerting/server/rules_client/tests/unmute_all.test.ts index d999a7604e9b9..20eae2a147dda 100644 --- a/x-pack/plugins/alerting/server/rules_client/tests/unmute_all.test.ts +++ b/x-pack/plugins/alerting/server/rules_client/tests/unmute_all.test.ts @@ -21,6 +21,7 @@ import { AlertingAuthorization } from '../../authorization/alerting_authorizatio import { ActionsAuthorization } from '@kbn/actions-plugin/server'; import { auditLoggerMock } from '@kbn/security-plugin/server/audit/mocks'; import { getBeforeSetup, setGlobalDate } from './lib'; +import { ConnectorAdapterRegistry } from '../../connector_adapters/connector_adapter_registry'; import { RULE_SAVED_OBJECT_TYPE } from '../../saved_objects'; const taskManager = taskManagerMock.createStart(); @@ -53,9 +54,11 @@ const rulesClientParams: jest.Mocked = { kibanaVersion, isAuthenticationTypeAPIKey: jest.fn(), getAuthenticationAPIKey: jest.fn(), + connectorAdapterRegistry: new ConnectorAdapterRegistry(), getAlertIndicesAlias: jest.fn(), alertsService: null, uiSettings: uiSettingsServiceMock.createStartContract(), + isSystemAction: jest.fn(), }; beforeEach(() => { diff --git a/x-pack/plugins/alerting/server/rules_client/tests/unmute_instance.test.ts b/x-pack/plugins/alerting/server/rules_client/tests/unmute_instance.test.ts index 35ba9b37bfff1..8275e0a88d8ba 100644 --- a/x-pack/plugins/alerting/server/rules_client/tests/unmute_instance.test.ts +++ b/x-pack/plugins/alerting/server/rules_client/tests/unmute_instance.test.ts @@ -21,6 +21,7 @@ import { AlertingAuthorization } from '../../authorization/alerting_authorizatio import { ActionsAuthorization } from '@kbn/actions-plugin/server'; import { auditLoggerMock } from '@kbn/security-plugin/server/audit/mocks'; import { getBeforeSetup, setGlobalDate } from './lib'; +import { ConnectorAdapterRegistry } from '../../connector_adapters/connector_adapter_registry'; import { RULE_SAVED_OBJECT_TYPE } from '../../saved_objects'; const taskManager = taskManagerMock.createStart(); @@ -53,9 +54,11 @@ const rulesClientParams: jest.Mocked = { kibanaVersion, isAuthenticationTypeAPIKey: jest.fn(), getAuthenticationAPIKey: jest.fn(), + connectorAdapterRegistry: new ConnectorAdapterRegistry(), getAlertIndicesAlias: jest.fn(), alertsService: null, uiSettings: uiSettingsServiceMock.createStartContract(), + isSystemAction: jest.fn(), }; beforeEach(() => { diff --git a/x-pack/plugins/alerting/server/rules_client/tests/update.test.ts b/x-pack/plugins/alerting/server/rules_client/tests/update.test.ts index 7384ab467a8e9..d66b17b5ff6b6 100644 --- a/x-pack/plugins/alerting/server/rules_client/tests/update.test.ts +++ b/x-pack/plugins/alerting/server/rules_client/tests/update.test.ts @@ -29,7 +29,9 @@ import { auditLoggerMock } from '@kbn/security-plugin/server/audit/mocks'; import { getBeforeSetup, setGlobalDate } from './lib'; import { bulkMarkApiKeysForInvalidation } from '../../invalidate_pending_api_keys/bulk_mark_api_keys_for_invalidation'; import { migrateLegacyActions } from '../lib'; +import { ConnectorAdapterRegistry } from '../../connector_adapters/connector_adapter_registry'; import { RULE_SAVED_OBJECT_TYPE } from '../../saved_objects'; +import { RuleDomain } from '../../application/rule/types'; jest.mock('../lib/siem_legacy_actions/migrate_legacy_actions', () => { return { @@ -92,6 +94,8 @@ const rulesClientParams: jest.Mocked = { minimumScheduleInterval: { value: '1m', enforce: false }, isAuthenticationTypeAPIKey: jest.fn(), getAuthenticationAPIKey: jest.fn(), + connectorAdapterRegistry: new ConnectorAdapterRegistry(), + isSystemAction: jest.fn(), getAlertIndicesAlias: jest.fn(), alertsService: null, uiSettings: uiSettingsServiceMock.createStartContract(), @@ -107,6 +111,7 @@ setGlobalDate(); describe('update()', () => { let rulesClient: RulesClient; let actionsClient: jest.Mocked; + const existingAlert = { id: '1', type: RULE_SAVED_OBJECT_TYPE, @@ -352,6 +357,7 @@ describe('update()', () => { "params": Object { "foo": true, }, + "uuid": undefined, }, Object { "actionTypeId": "test", @@ -360,6 +366,7 @@ describe('update()', () => { "params": Object { "foo": true, }, + "uuid": undefined, }, Object { "actionTypeId": "test2", @@ -368,6 +375,7 @@ describe('update()', () => { "params": Object { "foo": true, }, + "uuid": undefined, }, ], "alertDelay": Object { @@ -385,6 +393,7 @@ describe('update()', () => { "interval": "1m", }, "scheduledTaskId": "task-123", + "systemActions": Array [], "updatedAt": 2019-02-12T21:01:22.479Z, } `); @@ -550,10 +559,8 @@ describe('update()', () => { isSystemAction: false, }, ]); - actionsClient.isPreconfigured.mockReset(); - actionsClient.isPreconfigured.mockReturnValueOnce(false); - actionsClient.isPreconfigured.mockReturnValueOnce(true); - actionsClient.isPreconfigured.mockReturnValueOnce(true); + actionsClient.isPreconfigured.mockImplementation((id: string) => id === 'preconfigured'); + unsecuredSavedObjectsClient.create.mockResolvedValueOnce({ id: '1', type: RULE_SAVED_OBJECT_TYPE, @@ -714,6 +721,7 @@ describe('update()', () => { "params": Object { "foo": true, }, + "uuid": undefined, }, Object { "actionTypeId": "test", @@ -722,6 +730,7 @@ describe('update()', () => { "params": Object { "foo": true, }, + "uuid": undefined, }, Object { "actionTypeId": "test", @@ -730,6 +739,7 @@ describe('update()', () => { "params": Object { "foo": true, }, + "uuid": undefined, }, ], "createdAt": 2019-02-12T21:01:22.479Z, @@ -744,6 +754,7 @@ describe('update()', () => { "interval": "1m", }, "scheduledTaskId": "task-123", + "systemActions": Array [], "updatedAt": 2019-02-12T21:01:22.479Z, } `); @@ -755,10 +766,10 @@ describe('update()', () => { } ); expect(unsecuredSavedObjectsClient.get).not.toHaveBeenCalled(); - expect(actionsClient.isPreconfigured).toHaveBeenCalledTimes(3); + expect(actionsClient.isPreconfigured).toHaveBeenCalled(); }); - test('should update a rule with some system actions', async () => { + test('should update a rule with system actions', async () => { actionsClient.getBulk.mockReset(); actionsClient.getBulk.mockResolvedValue([ { @@ -806,10 +817,9 @@ describe('update()', () => { isSystemAction: true, }, ]); - actionsClient.isSystemAction.mockReset(); - actionsClient.isSystemAction.mockReturnValueOnce(false); - actionsClient.isSystemAction.mockReturnValueOnce(true); - actionsClient.isSystemAction.mockReturnValueOnce(true); + + actionsClient.isSystemAction.mockImplementation((id: string) => id === 'system_action-id'); + unsecuredSavedObjectsClient.create.mockResolvedValueOnce({ id: '1', type: RULE_SAVED_OBJECT_TYPE, @@ -829,13 +839,6 @@ describe('update()', () => { }, }, { - group: 'default', - actionRef: 'system_action:system_action-id', - actionTypeId: 'test', - params: {}, - }, - { - group: 'custom', actionRef: 'system_action:system_action-id', actionTypeId: 'test', params: {}, @@ -860,6 +863,7 @@ describe('update()', () => { }, ], }); + const result = await rulesClient.update({ id: '1', data: { @@ -879,13 +883,9 @@ describe('update()', () => { foo: true, }, }, + ], + systemActions: [ { - group: 'default', - id: 'system_action-id', - params: {}, - }, - { - group: 'custom', id: 'system_action-id', params: {}, }, @@ -908,19 +908,11 @@ describe('update()', () => { uuid: '106', }, { - group: 'default', actionRef: 'system_action:system_action-id', actionTypeId: 'test', params: {}, uuid: '107', }, - { - group: 'custom', - actionRef: 'system_action:system_action-id', - actionTypeId: 'test', - params: {}, - uuid: '108', - }, ], alertTypeId: 'myType', apiKey: null, @@ -958,18 +950,7 @@ describe('update()', () => { "params": Object { "foo": true, }, - }, - Object { - "actionTypeId": "test", - "group": "default", - "id": "system_action-id", - "params": Object {}, - }, - Object { - "actionTypeId": "test", - "group": "custom", - "id": "system_action-id", - "params": Object {}, + "uuid": undefined, }, ], "createdAt": 2019-02-12T21:01:22.479Z, @@ -984,9 +965,18 @@ describe('update()', () => { "interval": "1m", }, "scheduledTaskId": "task-123", + "systemActions": Array [ + Object { + "actionTypeId": "test", + "id": "system_action-id", + "params": Object {}, + "uuid": undefined, + }, + ], "updatedAt": 2019-02-12T21:01:22.479Z, } `); + expect(encryptedSavedObjects.getDecryptedAsInternalUser).toHaveBeenCalledWith( RULE_SAVED_OBJECT_TYPE, '1', @@ -994,8 +984,9 @@ describe('update()', () => { namespace: 'default', } ); + expect(unsecuredSavedObjectsClient.get).not.toHaveBeenCalled(); - expect(actionsClient.isSystemAction).toHaveBeenCalledTimes(3); + expect(actionsClient.isSystemAction).toHaveBeenCalled(); }); test('should call useSavedObjectReferences.extractReferences and useSavedObjectReferences.injectReferences if defined for rule type', async () => { @@ -1112,7 +1103,7 @@ describe('update()', () => { actionTypeId: 'test', group: 'default', params: { foo: true }, - uuid: '109', + uuid: '108', }, ], alertTypeId: 'myType', @@ -1161,6 +1152,7 @@ describe('update()', () => { "params": Object { "foo": true, }, + "uuid": undefined, }, ], "createdAt": 2019-02-12T21:01:22.479Z, @@ -1176,6 +1168,7 @@ describe('update()', () => { "interval": "1m", }, "scheduledTaskId": "task-123", + "systemActions": Array [], "updatedAt": 2019-02-12T21:01:22.479Z, } `); @@ -1253,6 +1246,7 @@ describe('update()', () => { "params": Object { "foo": true, }, + "uuid": undefined, }, ], "apiKey": "MTIzOmFiYw==", @@ -1268,6 +1262,7 @@ describe('update()', () => { "interval": "1m", }, "scheduledTaskId": "task-123", + "systemActions": Array [], "updatedAt": 2019-02-12T21:01:22.479Z, } `); @@ -1292,7 +1287,7 @@ describe('update()', () => { "params": Object { "foo": true, }, - "uuid": "110", + "uuid": "109", }, ], "alertTypeId": "myType", @@ -1414,6 +1409,7 @@ describe('update()', () => { "params": Object { "foo": true, }, + "uuid": undefined, }, ], "apiKey": null, @@ -1429,6 +1425,7 @@ describe('update()', () => { "interval": "1m", }, "scheduledTaskId": "task-123", + "systemActions": Array [], "updatedAt": 2019-02-12T21:01:22.479Z, } `); @@ -1445,7 +1442,7 @@ describe('update()', () => { "params": Object { "foo": true, }, - "uuid": "111", + "uuid": "110", }, ], "alertTypeId": "myType", @@ -1689,6 +1686,7 @@ describe('update()', () => { params: { foo: true, }, + frequency: { summary: false, notifyWhen: 'onActionGroupChange', @@ -1834,6 +1832,7 @@ describe('update()', () => { params: { foo: true, }, + frequency: { summary: false, notifyWhen: 'onThrottleInterval', @@ -1846,6 +1845,7 @@ describe('update()', () => { params: { foo: true, }, + frequency: { summary: false, notifyWhen: 'onThrottleInterval', @@ -1858,6 +1858,7 @@ describe('update()', () => { params: { foo: true, }, + frequency: { summary: false, notifyWhen: 'onThrottleInterval', @@ -1896,6 +1897,7 @@ describe('update()', () => { params: { foo: true, }, + frequency: { summary: false, notifyWhen: 'onActionGroupChange', @@ -2029,6 +2031,7 @@ describe('update()', () => { params: { foo: true, }, + frequency: { summary: false, notifyWhen: 'onActionGroupChange', @@ -2064,6 +2067,7 @@ describe('update()', () => { params: { foo: true, }, + frequency: { summary: false, notifyWhen: 'onActionGroupChange', @@ -2101,6 +2105,7 @@ describe('update()', () => { params: { foo: true, }, + frequency: { summary: false, notifyWhen: 'onActionGroupChange', @@ -2113,6 +2118,7 @@ describe('update()', () => { params: { foo: true, }, + frequency: { summary: false, notifyWhen: 'onActionGroupChange', @@ -2147,6 +2153,7 @@ describe('update()', () => { params: { foo: true, }, + frequency: { summary: false, notifyWhen: 'onActionGroupChange', @@ -2231,6 +2238,7 @@ describe('update()', () => { params: { foo: true, }, + frequency: { summary: false, notifyWhen: 'onActionGroupChange', @@ -2279,6 +2287,7 @@ describe('update()', () => { params: { foo: true, }, + frequency: { summary: false, notifyWhen: 'onActionGroupChange', @@ -2395,10 +2404,8 @@ describe('update()', () => { isSystemAction: false, }, ]); - actionsClient.isPreconfigured.mockReset(); - actionsClient.isPreconfigured.mockReturnValueOnce(false); - actionsClient.isPreconfigured.mockReturnValueOnce(true); - actionsClient.isPreconfigured.mockReturnValueOnce(true); + actionsClient.isPreconfigured.mockImplementation((id: string) => id === 'preconfigured'); + unsecuredSavedObjectsClient.create.mockResolvedValueOnce({ id: '1', type: RULE_SAVED_OBJECT_TYPE, @@ -2473,7 +2480,7 @@ describe('update()', () => { params: { foo: true, }, - uuid: '147', + uuid: '146', }, ], alertTypeId: 'myType', @@ -2512,6 +2519,7 @@ describe('update()', () => { "params": Object { "foo": true, }, + "uuid": undefined, }, ], "createdAt": 2019-02-12T21:01:22.479Z, @@ -2526,6 +2534,7 @@ describe('update()', () => { "interval": "1m", }, "scheduledTaskId": "task-123", + "systemActions": Array [], "updatedAt": 2019-02-12T21:01:22.479Z, } `); @@ -2537,7 +2546,7 @@ describe('update()', () => { } ); expect(unsecuredSavedObjectsClient.get).not.toHaveBeenCalled(); - expect(actionsClient.isPreconfigured).toHaveBeenCalledTimes(1); + expect(actionsClient.isPreconfigured).toHaveBeenCalled(); }); test('logs warning when creating with an interval less than the minimum configured one when enforce = false', async () => { @@ -2988,6 +2997,7 @@ describe('update()', () => { params: { foo: true, }, + frequency: { notifyWhen: 'onActiveAlert', throttle: null, @@ -3001,6 +3011,7 @@ describe('update()', () => { params: { foo: true, }, + frequency: { notifyWhen: 'onActiveAlert', throttle: null, @@ -3028,7 +3039,7 @@ describe('update()', () => { frequency: { notifyWhen: 'onActiveAlert', summary: false, throttle: null }, group: 'default', params: { foo: true }, - uuid: '154', + uuid: '153', }, ], alertTypeId: 'myType', @@ -3198,6 +3209,7 @@ describe('update()', () => { "params": Object { "foo": true, }, + "uuid": undefined, }, ], "apiKey": "MTIzOmFiYw==", @@ -3214,6 +3226,7 @@ describe('update()', () => { "interval": "1m", }, "scheduledTaskId": "task-123", + "systemActions": Array [], "updatedAt": 2019-02-12T21:01:22.479Z, } `); @@ -3232,7 +3245,7 @@ describe('update()', () => { "params": Object { "foo": true, }, - "uuid": "155", + "uuid": "154", }, ], "alertTypeId": "myType", @@ -3302,6 +3315,7 @@ describe('update()', () => { params: { foo: true, }, + frequency: { summary: false, notifyWhen: 'onActionGroupChange', @@ -3321,4 +3335,507 @@ describe('update()', () => { expect.any(Object) ); }); + + describe('actions', () => { + beforeEach(() => { + actionsClient.isSystemAction.mockImplementation((id: string) => id === 'system_action-id'); + unsecuredSavedObjectsClient.create.mockResolvedValueOnce({ + id: '1', + type: RULE_SAVED_OBJECT_TYPE, + attributes: { + enabled: true, + schedule: { interval: '1m' }, + params: { + bar: true, + }, + actions: [ + { + group: 'default', + actionRef: 'action_0', + actionTypeId: 'test', + params: { + foo: true, + }, + }, + { + actionRef: 'system_action:system_action-id', + actionTypeId: 'test', + params: {}, + }, + ], + notifyWhen: 'onActiveAlert', + revision: 1, + scheduledTaskId: 'task-123', + createdAt: new Date().toISOString(), + updatedAt: new Date().toISOString(), + }, + references: [ + { + name: 'action_0', + type: 'action', + id: '1', + }, + { + name: 'param:soRef_0', + type: 'someSavedObjectType', + id: '9', + }, + ], + }); + + actionsClient.getBulk.mockResolvedValue([ + { + id: '1', + actionTypeId: 'test', + config: { + from: 'me@me.com', + hasAuth: false, + host: 'hello', + port: 22, + secure: null, + service: null, + }, + isMissingSecrets: false, + name: 'email connector', + isPreconfigured: false, + isDeprecated: false, + isSystemAction: false, + }, + { + id: '2', + actionTypeId: 'test2', + config: { + from: 'me@me.com', + hasAuth: false, + host: 'hello', + port: 22, + secure: null, + service: null, + }, + isMissingSecrets: false, + name: 'another email connector', + isPreconfigured: false, + isDeprecated: false, + isSystemAction: false, + }, + { + id: 'system_action-id', + actionTypeId: 'test', + config: {}, + isMissingSecrets: false, + name: 'system action connector', + isPreconfigured: false, + isDeprecated: false, + isSystemAction: true, + }, + ]); + }); + + test('update a rule with system actions and default actions', async () => { + const result = await rulesClient.update({ + id: '1', + data: { + schedule: { interval: '1m' }, + name: 'abc', + tags: ['foo'], + params: { + bar: true, + }, + throttle: null, + notifyWhen: 'onActiveAlert', + actions: [ + { + group: 'default', + id: '1', + params: { + foo: true, + }, + }, + ], + systemActions: [ + { + id: 'system_action-id', + params: {}, + }, + ], + }, + }); + + expect(unsecuredSavedObjectsClient.create).toHaveBeenNthCalledWith( + 1, + RULE_SAVED_OBJECT_TYPE, + { + actions: [ + { + group: 'default', + actionRef: 'action_0', + actionTypeId: 'test', + params: { + foo: true, + }, + uuid: '156', + }, + { + actionRef: 'system_action:system_action-id', + actionTypeId: 'test', + params: {}, + uuid: '157', + }, + ], + alertTypeId: 'myType', + apiKey: null, + apiKeyOwner: null, + apiKeyCreatedByUser: null, + consumer: 'myApp', + enabled: true, + meta: { versionApiKeyLastmodified: 'v7.10.0' }, + name: 'abc', + notifyWhen: 'onActiveAlert', + params: { bar: true }, + revision: 1, + schedule: { interval: '1m' }, + scheduledTaskId: 'task-123', + tags: ['foo'], + throttle: null, + updatedAt: '2019-02-12T21:01:22.479Z', + updatedBy: 'elastic', + }, + { + id: '1', + overwrite: true, + references: [{ id: '1', name: 'action_0', type: 'action' }], + version: '123', + } + ); + + expect(result).toMatchInlineSnapshot(` + Object { + "actions": Array [ + Object { + "actionTypeId": "test", + "group": "default", + "id": "1", + "params": Object { + "foo": true, + }, + "uuid": undefined, + }, + ], + "createdAt": 2019-02-12T21:01:22.479Z, + "enabled": true, + "id": "1", + "notifyWhen": "onActiveAlert", + "params": Object { + "bar": true, + }, + "revision": 1, + "schedule": Object { + "interval": "1m", + }, + "scheduledTaskId": "task-123", + "systemActions": Array [ + Object { + "actionTypeId": "test", + "id": "system_action-id", + "params": Object {}, + "uuid": undefined, + }, + ], + "updatedAt": 2019-02-12T21:01:22.479Z, + } + `); + + expect(encryptedSavedObjects.getDecryptedAsInternalUser).toHaveBeenCalledWith( + RULE_SAVED_OBJECT_TYPE, + '1', + { + namespace: 'default', + } + ); + + expect(unsecuredSavedObjectsClient.get).not.toHaveBeenCalled(); + expect(actionsClient.isSystemAction).toHaveBeenCalled(); + }); + + test('should construct the refs correctly and persist the actions to ES correctly', async () => { + await rulesClient.update({ + id: '1', + data: { + schedule: { interval: '1m' }, + name: 'abc', + tags: ['foo'], + params: { + bar: true, + }, + throttle: null, + notifyWhen: 'onActiveAlert', + actions: [ + { + group: 'default', + id: '1', + params: { + foo: true, + }, + }, + ], + systemActions: [ + { + id: 'system_action-id', + params: {}, + }, + ], + }, + }); + + const rule = unsecuredSavedObjectsClient.create.mock.calls[0][1] as RuleDomain; + + expect(rule.actions).toEqual([ + { + group: 'default', + actionRef: 'action_0', + actionTypeId: 'test', + params: { + foo: true, + }, + uuid: '158', + }, + { + actionRef: 'system_action:system_action-id', + actionTypeId: 'test', + params: {}, + uuid: '159', + }, + ]); + }); + + test('should transforms the actions from ES correctly', async () => { + const result = await rulesClient.update({ + id: '1', + data: { + schedule: { interval: '1m' }, + name: 'abc', + tags: ['foo'], + params: { + bar: true, + }, + throttle: null, + notifyWhen: 'onActiveAlert', + actions: [ + { + group: 'default', + id: '1', + params: { + foo: true, + }, + }, + ], + systemActions: [ + { + id: 'system_action-id', + params: {}, + }, + ], + }, + }); + + expect(result.actions).toMatchInlineSnapshot(` + Array [ + Object { + "actionTypeId": "test", + "group": "default", + "id": "1", + "params": Object { + "foo": true, + }, + "uuid": undefined, + }, + ] + `); + + expect(result.systemActions).toMatchInlineSnapshot(` + Array [ + Object { + "actionTypeId": "test", + "id": "system_action-id", + "params": Object {}, + "uuid": undefined, + }, + ] + `); + }); + + test('should throw an error if the system action does not exist', async () => { + await expect(() => + rulesClient.update({ + id: '1', + data: { + schedule: { interval: '1m' }, + name: 'abc', + tags: ['foo'], + params: { + bar: true, + }, + throttle: null, + notifyWhen: 'onActiveAlert', + actions: [], + systemActions: [ + { + id: 'fake-system-action', + params: {}, + }, + ], + }, + }) + ).rejects.toMatchInlineSnapshot(`[Error: Action fake-system-action is not a system action]`); + }); + + test('should throw an error if the system action contains the group', async () => { + await expect(() => + rulesClient.update({ + id: '1', + data: { + schedule: { interval: '1m' }, + name: 'abc', + tags: ['foo'], + params: { + bar: true, + }, + throttle: null, + notifyWhen: 'onActiveAlert', + actions: [], + systemActions: [ + { + id: 'system_action-id', + params: {}, + // @ts-expect-error: testing validation + group: 'default', + }, + ], + }, + }) + ).rejects.toMatchInlineSnapshot( + `[Error: Error validating actions - definition for this key is missing]` + ); + }); + + test('should throw an error if the system action contains the frequency', async () => { + await expect(() => + rulesClient.update({ + id: '1', + data: { + schedule: { interval: '1m' }, + name: 'abc', + tags: ['foo'], + params: { + bar: true, + }, + throttle: null, + notifyWhen: 'onActiveAlert', + actions: [], + systemActions: [ + { + id: 'system_action-id', + params: {}, + // @ts-expect-error: testing validation + frequency: { + summary: false, + notifyWhen: 'onActionGroupChange', + throttle: null, + }, + }, + ], + }, + }) + ).rejects.toMatchInlineSnapshot( + `[Error: Error validating actions - definition for this key is missing]` + ); + }); + + test('should throw an error if the system action contains the alertsFilter', async () => { + await expect(() => + rulesClient.update({ + id: '1', + data: { + schedule: { interval: '1m' }, + name: 'abc', + tags: ['foo'], + params: { + bar: true, + }, + throttle: null, + notifyWhen: 'onActiveAlert', + actions: [], + systemActions: [ + { + id: 'system_action-id', + params: {}, + // @ts-expect-error: testing validation + alertsFilter: { + query: { kql: 'test:1', filters: [] }, + }, + }, + ], + }, + }) + ).rejects.toMatchInlineSnapshot( + `[Error: Error validating actions - definition for this key is missing]` + ); + }); + + test('should throw an error if the same system action is used twice', async () => { + await expect(() => + rulesClient.update({ + id: '1', + data: { + schedule: { interval: '1m' }, + name: 'abc', + tags: ['foo'], + params: { + bar: true, + }, + throttle: null, + notifyWhen: 'onActiveAlert', + actions: [], + systemActions: [ + { + id: 'system_action-id', + params: {}, + }, + { + id: 'system_action-id', + params: {}, + }, + ], + }, + }) + ).rejects.toMatchInlineSnapshot(`[Error: Cannot use the same system action twice]`); + }); + + test('should throw an error if the default action does not contain the group', async () => { + await expect(() => + rulesClient.update({ + id: '1', + data: { + schedule: { interval: '1m' }, + name: 'abc', + tags: ['foo'], + params: { + bar: true, + }, + throttle: null, + notifyWhen: 'onActiveAlert', + actions: [ + // @ts-expect-error: testing validation + { + id: 'action-id-1', + params: {}, + }, + ], + }, + }) + ).rejects.toMatchInlineSnapshot( + `[Error: Error validating actions - [actions.0.group]: expected value of type [string] but got [undefined]]` + ); + }); + }); }); diff --git a/x-pack/plugins/alerting/server/rules_client/tests/update_api_key.test.ts b/x-pack/plugins/alerting/server/rules_client/tests/update_api_key.test.ts index b0b943cd78f68..66881241021ef 100644 --- a/x-pack/plugins/alerting/server/rules_client/tests/update_api_key.test.ts +++ b/x-pack/plugins/alerting/server/rules_client/tests/update_api_key.test.ts @@ -22,6 +22,7 @@ import { ActionsAuthorization } from '@kbn/actions-plugin/server'; import { auditLoggerMock } from '@kbn/security-plugin/server/audit/mocks'; import { getBeforeSetup, setGlobalDate } from './lib'; import { bulkMarkApiKeysForInvalidation } from '../../invalidate_pending_api_keys/bulk_mark_api_keys_for_invalidation'; +import { ConnectorAdapterRegistry } from '../../connector_adapters/connector_adapter_registry'; import { RULE_SAVED_OBJECT_TYPE } from '../../saved_objects'; jest.mock('../../invalidate_pending_api_keys/bulk_mark_api_keys_for_invalidation', () => ({ @@ -60,9 +61,11 @@ const rulesClientParams: jest.Mocked = { auditLogger, isAuthenticationTypeAPIKey: jest.fn(), getAuthenticationAPIKey: jest.fn(), + connectorAdapterRegistry: new ConnectorAdapterRegistry(), getAlertIndicesAlias: jest.fn(), alertsService: null, uiSettings: uiSettingsServiceMock.createStartContract(), + isSystemAction: jest.fn(), }; beforeEach(() => { diff --git a/x-pack/plugins/alerting/server/rules_client/types.ts b/x-pack/plugins/alerting/server/rules_client/types.ts index 6b3abf6fe3a38..6ca3b69967002 100644 --- a/x-pack/plugins/alerting/server/rules_client/types.ts +++ b/x-pack/plugins/alerting/server/rules_client/types.ts @@ -22,17 +22,20 @@ import { EncryptedSavedObjectsClient } from '@kbn/encrypted-saved-objects-plugin import { TaskManagerStartContract } from '@kbn/task-manager-plugin/server'; import { IEventLogClient, IEventLogger } from '@kbn/event-log-plugin/server'; import { AuditLogger } from '@kbn/security-plugin/server'; +import { DistributiveOmit } from '@elastic/eui'; import { RegistryRuleType } from '../rule_type_registry'; import { RuleTypeRegistry, - RuleAction, IntervalSchedule, SanitizedRule, RuleSnoozeSchedule, RawRuleAlertsFilter, + RuleSystemAction, + RuleAction, } from '../types'; import { AlertingAuthorization } from '../authorization'; import { AlertingRulesConfig } from '../config'; +import { ConnectorAdapterRegistry } from '../connector_adapters/connector_adapter_registry'; import { GetAlertIndicesAlias } from '../lib'; import { AlertsService } from '../alerts_service'; @@ -77,21 +80,33 @@ export interface RulesClientContext { readonly fieldsToExcludeFromPublicApi: Array; readonly isAuthenticationTypeAPIKey: () => boolean; readonly getAuthenticationAPIKey: (name: string) => CreateAPIKeyResult; + readonly connectorAdapterRegistry: ConnectorAdapterRegistry; readonly getAlertIndicesAlias: GetAlertIndicesAlias; readonly alertsService: AlertsService | null; + readonly isSystemAction: (actionId: string) => boolean; readonly uiSettings: UiSettingsServiceStart; } -export type NormalizedAlertAction = Omit; +export type NormalizedAlertAction = DistributiveOmit; +export type NormalizedSystemAction = Omit; -export type NormalizedAlertActionWithGeneratedValues = Omit< - NormalizedAlertAction, - 'uuid' | 'alertsFilter' +export type NormalizedAlertDefaultActionWithGeneratedValues = Omit< + RuleAction, + 'uuid' | 'alertsFilter' | 'actionTypeId' > & { uuid: string; alertsFilter?: RawRuleAlertsFilter; }; +export type NormalizedAlertSystemActionWithGeneratedValues = Omit< + RuleSystemAction, + 'uuid' | 'actionTypeId' +> & { uuid: string }; + +export type NormalizedAlertActionWithGeneratedValues = + | NormalizedAlertDefaultActionWithGeneratedValues + | NormalizedAlertSystemActionWithGeneratedValues; + export interface RegistryAlertTypeWithAuth extends RegistryRuleType { authorizedConsumers: string[]; } @@ -127,7 +142,6 @@ export interface IndexType { [key: string]: unknown; } -// TODO: remove once all mute endpoints have been migrated to RuleMuteAlertOptions export interface MuteOptions extends IndexType { alertId: string; alertInstanceId: string; @@ -166,3 +180,11 @@ export interface RuleBulkOperationAggregation { }>; }; } + +export type DenormalizedAction = DistributiveOmit< + NormalizedAlertActionWithGeneratedValues, + 'id' +> & { + actionRef: string; + actionTypeId: string; +}; diff --git a/x-pack/plugins/alerting/server/rules_client_conflict_retries.test.ts b/x-pack/plugins/alerting/server/rules_client_conflict_retries.test.ts index d94e402a4e528..2df24f879e982 100644 --- a/x-pack/plugins/alerting/server/rules_client_conflict_retries.test.ts +++ b/x-pack/plugins/alerting/server/rules_client_conflict_retries.test.ts @@ -25,6 +25,7 @@ import { SavedObjectsErrorHelpers } from '@kbn/core/server'; import { RetryForConflictsAttempts } from './lib/retry_if_conflicts'; import { TaskStatus } from '@kbn/task-manager-plugin/server/task'; import { RecoveredActionGroup } from '../common'; +import { ConnectorAdapterRegistry } from './connector_adapters/connector_adapter_registry'; import { RULE_SAVED_OBJECT_TYPE } from './saved_objects'; jest.mock('./application/rule/methods/get_schedule_frequency', () => ({ @@ -70,6 +71,8 @@ const rulesClientParams: jest.Mocked = { getAuthenticationAPIKey: jest.fn(), getAlertIndicesAlias: jest.fn(), alertsService: null, + connectorAdapterRegistry: new ConnectorAdapterRegistry(), + isSystemAction: jest.fn(), uiSettings: uiSettingsServiceMock.createStartContract(), }; diff --git a/x-pack/plugins/alerting/server/rules_client_factory.test.ts b/x-pack/plugins/alerting/server/rules_client_factory.test.ts index a0b83ca344688..b02321c5da1d1 100644 --- a/x-pack/plugins/alerting/server/rules_client_factory.test.ts +++ b/x-pack/plugins/alerting/server/rules_client_factory.test.ts @@ -27,6 +27,7 @@ import { AlertingAuthorization } from './authorization'; import { AlertingAuthorizationClientFactory } from './alerting_authorization_client_factory'; import { SECURITY_EXTENSION_ID } from '@kbn/core-saved-objects-server'; import { mockRouter } from '@kbn/core-http-router-server-mocks'; +import { ConnectorAdapterRegistry } from './connector_adapters/connector_adapter_registry'; import { RULE_SAVED_OBJECT_TYPE } from './saved_objects'; jest.mock('./rules_client'); @@ -48,8 +49,6 @@ const rulesClientFactoryParams: jest.Mocked = { ruleTypeRegistry: ruleTypeRegistryMock.create(), getSpaceId: jest.fn(), spaceIdToNamespace: jest.fn(), - getAlertIndicesAlias: jest.fn(), - alertsService: null, maxScheduledPerMinute: 10000, minimumScheduleInterval: { value: '1m', enforce: false }, internalSavedObjectsRepository, @@ -59,7 +58,10 @@ const rulesClientFactoryParams: jest.Mocked = { kibanaVersion: '7.10.0', authorization: alertingAuthorizationClientFactory as unknown as AlertingAuthorizationClientFactory, + connectorAdapterRegistry: new ConnectorAdapterRegistry(), uiSettings: uiSettingsServiceMock.createStartContract(), + getAlertIndicesAlias: jest.fn(), + alertsService: null, }; const actionsAuthorization = actionsAuthorizationMock.create(); @@ -119,6 +121,8 @@ test('creates a rules client with proper constructor arguments when security is minimumScheduleInterval: { value: '1m', enforce: false }, isAuthenticationTypeAPIKey: expect.any(Function), getAuthenticationAPIKey: expect.any(Function), + connectorAdapterRegistry: expect.any(ConnectorAdapterRegistry), + isSystemAction: expect.any(Function), getAlertIndicesAlias: expect.any(Function), alertsService: null, uiSettings: rulesClientFactoryParams.uiSettings, @@ -164,6 +168,8 @@ test('creates a rules client with proper constructor arguments', async () => { minimumScheduleInterval: { value: '1m', enforce: false }, isAuthenticationTypeAPIKey: expect.any(Function), getAuthenticationAPIKey: expect.any(Function), + connectorAdapterRegistry: expect.any(ConnectorAdapterRegistry), + isSystemAction: expect.any(Function), getAlertIndicesAlias: expect.any(Function), alertsService: null, uiSettings: rulesClientFactoryParams.uiSettings, diff --git a/x-pack/plugins/alerting/server/rules_client_factory.ts b/x-pack/plugins/alerting/server/rules_client_factory.ts index 66ee93a1f4aeb..d5402355930fa 100644 --- a/x-pack/plugins/alerting/server/rules_client_factory.ts +++ b/x-pack/plugins/alerting/server/rules_client_factory.ts @@ -29,6 +29,7 @@ import { AlertingAuthorizationClientFactory } from './alerting_authorization_cli import { AlertingRulesConfig } from './config'; import { GetAlertIndicesAlias } from './lib'; import { AlertsService } from './alerts_service/alerts_service'; +import { ConnectorAdapterRegistry } from './connector_adapters/connector_adapter_registry'; import { RULE_SAVED_OBJECT_TYPE } from './saved_objects'; export interface RulesClientFactoryOpts { logger: Logger; @@ -49,6 +50,7 @@ export interface RulesClientFactoryOpts { maxScheduledPerMinute: AlertingRulesConfig['maxScheduledPerMinute']; getAlertIndicesAlias: GetAlertIndicesAlias; alertsService: AlertsService | null; + connectorAdapterRegistry: ConnectorAdapterRegistry; uiSettings: CoreStart['uiSettings']; } @@ -72,6 +74,7 @@ export class RulesClientFactory { private maxScheduledPerMinute!: AlertingRulesConfig['maxScheduledPerMinute']; private getAlertIndicesAlias!: GetAlertIndicesAlias; private alertsService!: AlertsService | null; + private connectorAdapterRegistry!: ConnectorAdapterRegistry; private uiSettings!: CoreStart['uiSettings']; public initialize(options: RulesClientFactoryOpts) { @@ -97,6 +100,7 @@ export class RulesClientFactory { this.maxScheduledPerMinute = options.maxScheduledPerMinute; this.getAlertIndicesAlias = options.getAlertIndicesAlias; this.alertsService = options.alertsService; + this.connectorAdapterRegistry = options.connectorAdapterRegistry; this.uiSettings = options.uiSettings; } @@ -128,6 +132,7 @@ export class RulesClientFactory { auditLogger: securityPluginSetup?.audit.asScoped(request), getAlertIndicesAlias: this.getAlertIndicesAlias, alertsService: this.alertsService, + connectorAdapterRegistry: this.connectorAdapterRegistry, uiSettings: this.uiSettings, async getUserName() { @@ -187,6 +192,9 @@ export class RulesClientFactory { } return { apiKeysEnabled: false }; }, + isSystemAction(actionId: string) { + return actions.isSystemActionConnector(actionId); + }, }); } } diff --git a/x-pack/plugins/alerting/server/saved_objects/migrations/7.16/index.ts b/x-pack/plugins/alerting/server/saved_objects/migrations/7.16/index.ts index 3984f3e5fb96e..992fe8bfa17a9 100644 --- a/x-pack/plugins/alerting/server/saved_objects/migrations/7.16/index.ts +++ b/x-pack/plugins/alerting/server/saved_objects/migrations/7.16/index.ts @@ -5,7 +5,7 @@ * 2.0. */ -import { SavedObjectAttribute, SavedObjectReference } from '@kbn/core-saved-objects-server'; +import { SavedObjectReference } from '@kbn/core-saved-objects-server'; import { SavedObjectUnsanitizedDoc } from '@kbn/core-saved-objects-server'; import { EncryptedSavedObjectsPluginSetup } from '@kbn/encrypted-saved-objects-plugin/server'; import { isString } from 'lodash/fp'; @@ -34,15 +34,13 @@ function getRemovePreconfiguredConnectorsFromReferencesFn( } function getCorrespondingAction( - actions: SavedObjectAttribute, + actions: RawRuleAction | RawRuleAction[], connectorRef: string ): RawRuleAction | null { if (!Array.isArray(actions)) { return null; } else { - return actions.find( - (action) => (action as RawRuleAction)?.actionRef === connectorRef - ) as RawRuleAction; + return actions.find((action) => action.actionRef === connectorRef) ?? null; } } diff --git a/x-pack/plugins/alerting/server/saved_objects/migrations/8.3/index.ts b/x-pack/plugins/alerting/server/saved_objects/migrations/8.3/index.ts index 833971a71dbbe..ba083773e5a9a 100644 --- a/x-pack/plugins/alerting/server/saved_objects/migrations/8.3/index.ts +++ b/x-pack/plugins/alerting/server/saved_objects/migrations/8.3/index.ts @@ -61,8 +61,12 @@ function removeInternalTags( }; } +interface ConvertSnoozes extends RawRule { + snoozeEndTime?: string; +} + function convertSnoozes( - doc: SavedObjectUnsanitizedDoc + doc: SavedObjectUnsanitizedDoc ): SavedObjectUnsanitizedDoc { const { attributes: { snoozeEndTime }, diff --git a/x-pack/plugins/alerting/server/saved_objects/migrations/index.ts b/x-pack/plugins/alerting/server/saved_objects/migrations/index.ts index b94fb907d8275..613feffa062d1 100644 --- a/x-pack/plugins/alerting/server/saved_objects/migrations/index.ts +++ b/x-pack/plugins/alerting/server/saved_objects/migrations/index.ts @@ -16,6 +16,7 @@ import { EncryptedSavedObjectsPluginSetup } from '@kbn/encrypted-saved-objects-p import { MigrateFunctionsObject, MigrateFunction } from '@kbn/kibana-utils-plugin/common'; import { mergeSavedObjectMigrationMaps } from '@kbn/core/server'; import { isSerializedSearchSource, SerializedSearchSourceFields } from '@kbn/data-plugin/common'; +import { Serializable } from '@kbn/utility-types'; import { RawRule } from '../../types'; import { getMigrations7100 } from './7.10'; import { getMigrations7110, getMigrations7112 } from './7.11'; @@ -111,8 +112,7 @@ function mapSearchSourceMigrationFunc( migrateSerializedSearchSourceFields: MigrateFunction ): MigrateFunction { return (doc) => { - const _doc = doc as { attributes: RawRule }; - + const _doc = doc as { attributes: { params: { searchConfiguration: Serializable } } }; const serializedSearchSource = _doc.attributes.params.searchConfiguration; if (isSerializedSearchSource(serializedSearchSource)) { diff --git a/x-pack/plugins/alerting/server/saved_objects/partially_update_rule.ts b/x-pack/plugins/alerting/server/saved_objects/partially_update_rule.ts index 59b934a824e11..2665845a1110f 100644 --- a/x-pack/plugins/alerting/server/saved_objects/partially_update_rule.ts +++ b/x-pack/plugins/alerting/server/saved_objects/partially_update_rule.ts @@ -59,12 +59,7 @@ export async function partiallyUpdateRule( ); try { - await savedObjectsClient.update( - RULE_SAVED_OBJECT_TYPE, - id, - attributeUpdates, - updateOptions - ); + await savedObjectsClient.update(RULE_SAVED_OBJECT_TYPE, id, attributeUpdates, updateOptions); } catch (err) { if (options?.ignore404 && SavedObjectsErrorHelpers.isNotFoundError(err)) { return; diff --git a/x-pack/plugins/alerting/server/saved_objects/schemas/raw_rule/v1.ts b/x-pack/plugins/alerting/server/saved_objects/schemas/raw_rule/v1.ts index e0641e9b275ea..caa5f47a959f1 100644 --- a/x-pack/plugins/alerting/server/saved_objects/schemas/raw_rule/v1.ts +++ b/x-pack/plugins/alerting/server/saved_objects/schemas/raw_rule/v1.ts @@ -194,7 +194,7 @@ const rawRuleAlertsFilterSchema = schema.object({ const rawRuleActionSchema = schema.object({ uuid: schema.maybe(schema.string()), - group: schema.string(), + group: schema.maybe(schema.string()), actionRef: schema.string(), actionTypeId: schema.string(), params: schema.recordOf(schema.string(), schema.any()), diff --git a/x-pack/plugins/alerting/server/task_runner/execution_handler.test.ts b/x-pack/plugins/alerting/server/task_runner/execution_handler.test.ts index 56a0bbaa7d774..5f5a97c842e33 100644 --- a/x-pack/plugins/alerting/server/task_runner/execution_handler.test.ts +++ b/x-pack/plugins/alerting/server/task_runner/execution_handler.test.ts @@ -33,6 +33,7 @@ import { asSavedObjectExecutionSource } from '@kbn/actions-plugin/server'; import sinon from 'sinon'; import { mockAAD } from './fixtures'; import { schema } from '@kbn/config-schema'; +import { ConnectorAdapterRegistry } from '../connector_adapters/connector_adapter_registry'; import { alertsClientMock } from '../alerts_client/alerts_client.mock'; import { ExecutionResponseType } from '@kbn/actions-plugin/server/create_execute_function'; import { RULE_SAVED_OBJECT_TYPE } from '../saved_objects'; @@ -127,6 +128,7 @@ const defaultExecutionParams = { }, }, actionsPlugin: mockActionsPlugin, + connectorAdapterRegistry: new ConnectorAdapterRegistry(), } as unknown as TaskRunnerContext, apiKey, ruleConsumer: 'rule-consumer', @@ -2387,4 +2389,270 @@ describe('Execution Handler', () => { `); }); }); + + describe('System actions', () => { + beforeEach(() => { + jest.clearAllMocks(); + mockActionsPlugin.isSystemActionConnector.mockReturnValue(true); + }); + + test('triggers system actions with summarization per rule run', async () => { + const actionsParams = { myParams: 'test' }; + + alertsClient.getSummarizedAlerts.mockResolvedValue({ + new: { + count: 1, + data: [mockAAD], + }, + ongoing: { count: 0, data: [] }, + recovered: { count: 0, data: [] }, + }); + + const executorParams = generateExecutionParams({ + rule: { + ...defaultExecutionParams.rule, + systemActions: [ + { + id: '1', + actionTypeId: '.test-system-action', + params: actionsParams, + uui: 'test', + }, + ], + }, + }); + + const buildActionParams = jest.fn().mockReturnValue({ ...actionsParams, foo: 'bar' }); + + executorParams.taskRunnerContext.connectorAdapterRegistry.register({ + connectorTypeId: '.test-system-action', + ruleActionParamsSchema: schema.object({}), + buildActionParams, + }); + + executorParams.actionsClient.isSystemAction.mockReturnValue(true); + executorParams.taskRunnerContext.kibanaBaseUrl = 'https://example.com'; + + const executionHandler = new ExecutionHandler(generateExecutionParams(executorParams)); + + const res = await executionHandler.run(generateAlert({ id: 1 })); + + /** + * Verifies that system actions are not throttled + */ + expect(res).toEqual({ throttledSummaryActions: {} }); + + /** + * Verifies that system actions + * work only with summarized alerts + */ + expect(alertsClient.getSummarizedAlerts).toHaveBeenCalledWith({ + executionUuid: '5f6aa57d-3e22-484e-bae8-cbed868f4d28', + ruleId: '1', + spaceId: 'test1', + excludedAlertInstanceIds: [], + alertsFilter: undefined, + }); + + expect(buildActionParams).toHaveBeenCalledWith({ + alerts: { + all: { + count: 1, + data: [mockAAD], + }, + new: { + count: 1, + data: [mockAAD], + }, + ongoing: { count: 0, data: [] }, + recovered: { count: 0, data: [] }, + }, + params: actionsParams, + rule: { + id: rule.id, + name: rule.name, + tags: rule.tags, + }, + ruleUrl: + 'https://example.com/s/test1/app/management/insightsAndAlerting/triggersActions/rule/1', + spaceId: 'test1', + }); + + expect(actionsClient.bulkEnqueueExecution).toHaveBeenCalledTimes(1); + expect(actionsClient.bulkEnqueueExecution.mock.calls[0]).toMatchInlineSnapshot(` + Array [ + Array [ + Object { + "actionTypeId": ".test-system-action", + "apiKey": "MTIzOmFiYw==", + "consumer": "rule-consumer", + "executionId": "5f6aa57d-3e22-484e-bae8-cbed868f4d28", + "id": "1", + "params": Object { + "foo": "bar", + "myParams": "test", + }, + "relatedSavedObjects": Array [ + Object { + "id": "1", + "namespace": "test1", + "type": "alert", + "typeId": "test", + }, + ], + "source": Object { + "source": Object { + "id": "1", + "type": "alert", + }, + "type": "SAVED_OBJECT", + }, + "spaceId": "test1", + }, + ], + ] + `); + + expect(alertingEventLogger.logAction).toBeCalledWith({ + alertSummary: { new: 1, ongoing: 0, recovered: 0 }, + id: '1', + typeId: '.test-system-action', + }); + }); + + test('does not execute if the connector adapter is not configured', async () => { + const actionsParams = { myParams: 'test' }; + + alertsClient.getSummarizedAlerts.mockResolvedValue({ + new: { + count: 1, + data: [mockAAD], + }, + ongoing: { count: 0, data: [] }, + recovered: { count: 0, data: [] }, + }); + + const executorParams = generateExecutionParams({ + rule: { + ...defaultExecutionParams.rule, + systemActions: [ + { + id: 'action-id', + actionTypeId: '.connector-adapter-not-exists', + params: actionsParams, + uui: 'test', + }, + ], + }, + }); + + const buildActionParams = jest.fn().mockReturnValue({ ...actionsParams, foo: 'bar' }); + + executorParams.actionsClient.isSystemAction.mockReturnValue(true); + executorParams.taskRunnerContext.kibanaBaseUrl = 'https://example.com'; + + const executionHandler = new ExecutionHandler(generateExecutionParams(executorParams)); + + const res = await executionHandler.run(generateAlert({ id: 1 })); + + /** + * Verifies that system actions are not throttled + */ + expect(res).toEqual({ throttledSummaryActions: {} }); + + /** + * Verifies that system actions + * work only with summarized alerts + */ + expect(alertsClient.getSummarizedAlerts).toHaveBeenCalledWith({ + executionUuid: '5f6aa57d-3e22-484e-bae8-cbed868f4d28', + ruleId: '1', + spaceId: 'test1', + excludedAlertInstanceIds: [], + alertsFilter: undefined, + }); + + expect(buildActionParams).not.toHaveBeenCalledWith(); + expect(actionsClient.ephemeralEnqueuedExecution).not.toHaveBeenCalled(); + expect(actionsClient.bulkEnqueueExecution).not.toHaveBeenCalled(); + expect(alertingEventLogger.logAction).not.toHaveBeenCalled(); + expect(executorParams.logger.warn).toHaveBeenCalledWith( + 'Rule "1" skipped scheduling system action "action-id" because no connector adapter is configured' + ); + }); + + test('do not execute if the rule type does not support summarized alerts', async () => { + const actionsParams = { myParams: 'test' }; + + const executorParams = generateExecutionParams({ + ruleType: { + ...ruleType, + alerts: undefined, + }, + rule: { + ...defaultExecutionParams.rule, + systemActions: [ + { + id: 'action-id', + actionTypeId: '.test-system-action', + params: actionsParams, + uui: 'test', + }, + ], + }, + }); + + const buildActionParams = jest.fn().mockReturnValue({ ...actionsParams, foo: 'bar' }); + + executorParams.actionsClient.isSystemAction.mockReturnValue(true); + executorParams.taskRunnerContext.kibanaBaseUrl = 'https://example.com'; + + const executionHandler = new ExecutionHandler(generateExecutionParams(executorParams)); + + const res = await executionHandler.run(generateAlert({ id: 1 })); + + expect(res).toEqual({ throttledSummaryActions: {} }); + expect(buildActionParams).not.toHaveBeenCalled(); + expect(alertsClient.getSummarizedAlerts).not.toHaveBeenCalled(); + expect(actionsClient.ephemeralEnqueuedExecution).not.toHaveBeenCalled(); + expect(actionsClient.bulkEnqueueExecution).not.toHaveBeenCalled(); + expect(alertingEventLogger.logAction).not.toHaveBeenCalled(); + }); + + test('do not execute system actions if the rule type does not support summarized alerts', async () => { + const actionsParams = { myParams: 'test' }; + + const executorParams = generateExecutionParams({ + rule: { + ...defaultExecutionParams.rule, + systemActions: [ + { + id: '1', + actionTypeId: '.test-system-action', + params: actionsParams, + uui: 'test', + }, + ], + }, + ruleType: { + ...defaultExecutionParams.ruleType, + alerts: undefined, + }, + }); + + const buildActionParams = jest.fn().mockReturnValue({ ...actionsParams, foo: 'bar' }); + + executorParams.actionsClient.isSystemAction.mockReturnValue(true); + executorParams.taskRunnerContext.kibanaBaseUrl = 'https://example.com'; + + const executionHandler = new ExecutionHandler(generateExecutionParams(executorParams)); + + await executionHandler.run(generateAlert({ id: 1 })); + + expect(alertsClient.getSummarizedAlerts).not.toHaveBeenCalled(); + expect(buildActionParams).not.toHaveBeenCalled(); + expect(actionsClient.bulkEnqueueExecution).not.toHaveBeenCalled(); + expect(alertingEventLogger.logAction).not.toHaveBeenCalled(); + }); + }); }); diff --git a/x-pack/plugins/alerting/server/task_runner/execution_handler.ts b/x-pack/plugins/alerting/server/task_runner/execution_handler.ts index 23fb063384b3e..f794133c69dc7 100644 --- a/x-pack/plugins/alerting/server/task_runner/execution_handler.ts +++ b/x-pack/plugins/alerting/server/task_runner/execution_handler.ts @@ -44,6 +44,7 @@ import { SanitizedRule, RuleAlertData, RuleNotifyWhen, + RuleSystemAction, } from '../../common'; import { generateActionHash, @@ -55,6 +56,7 @@ import { isSummaryActionThrottled, } from './rule_action_helper'; import { RULE_SAVED_OBJECT_TYPE } from '../saved_objects'; +import { ConnectorAdapter } from '../connector_adapters/types'; enum Reasons { MUTED = 'muted', @@ -74,6 +76,35 @@ interface LogAction { }; } +interface RunSummarizedActionArgs { + action: RuleAction; + summarizedAlerts: CombinedSummarizedAlerts; + spaceId: string; + bulkActions: EnqueueExecutionOptions[]; +} + +interface RunSystemActionArgs { + action: RuleSystemAction; + connectorAdapter: ConnectorAdapter; + summarizedAlerts: CombinedSummarizedAlerts; + rule: SanitizedRule; + spaceId: string; + bulkActions: EnqueueExecutionOptions[]; +} + +interface RunActionArgs< + State extends AlertInstanceState, + Context extends AlertInstanceContext, + ActionGroupIds extends string, + RecoveryActionGroupId extends string +> { + action: RuleAction; + alert: Alert; + ruleId: string; + spaceId: string; + bulkActions: EnqueueExecutionOptions[]; +} + export interface RunResult { throttledSummaryActions: ThrottledActions; } @@ -186,233 +217,330 @@ export class ExecutionHandler< }); const executables = await this.generateExecutables(alerts, throttledSummaryActions); - if (!!executables.length) { - const { - CHUNK_SIZE, - logger, - alertingEventLogger, - ruleRunMetricsStore, - taskRunnerContext: { actionsConfigMap, actionsPlugin }, - taskInstance: { - params: { spaceId, alertId: ruleId }, - }, - } = this; + if (executables.length === 0) { + return { throttledSummaryActions }; + } - const logActions: Record = {}; - const bulkActions: EnqueueExecutionOptions[] = []; - let bulkActionsResponse: ExecutionResponseItem[] = []; + const { + CHUNK_SIZE, + logger, + alertingEventLogger, + ruleRunMetricsStore, + taskRunnerContext: { actionsConfigMap }, + taskInstance: { + params: { spaceId, alertId: ruleId }, + }, + } = this; - this.ruleRunMetricsStore.incrementNumberOfGeneratedActions(executables.length); + const logActions: Record = {}; + const bulkActions: EnqueueExecutionOptions[] = []; + let bulkActionsResponse: ExecutionResponseItem[] = []; - for (const { action, alert, summarizedAlerts } of executables) { - const { actionTypeId } = action; - const actionGroup = action.group as ActionGroupIds; + this.ruleRunMetricsStore.incrementNumberOfGeneratedActions(executables.length); - ruleRunMetricsStore.incrementNumberOfGeneratedActionsByConnectorType(actionTypeId); + for (const { action, alert, summarizedAlerts } of executables) { + const { actionTypeId } = action; - if (ruleRunMetricsStore.hasReachedTheExecutableActionsLimit(actionsConfigMap)) { - ruleRunMetricsStore.setTriggeredActionsStatusByConnectorType({ - actionTypeId, - status: ActionsCompletion.PARTIAL, - }); + ruleRunMetricsStore.incrementNumberOfGeneratedActionsByConnectorType(actionTypeId); + if (ruleRunMetricsStore.hasReachedTheExecutableActionsLimit(actionsConfigMap)) { + ruleRunMetricsStore.setTriggeredActionsStatusByConnectorType({ + actionTypeId, + status: ActionsCompletion.PARTIAL, + }); + logger.debug( + `Rule "${this.rule.id}" skipped scheduling action "${action.id}" because the maximum number of allowed actions has been reached.` + ); + break; + } + + if ( + ruleRunMetricsStore.hasReachedTheExecutableActionsLimitByConnectorType({ + actionTypeId, + actionsConfigMap, + }) + ) { + if (!ruleRunMetricsStore.hasConnectorTypeReachedTheLimit(actionTypeId)) { logger.debug( - `Rule "${this.rule.id}" skipped scheduling action "${action.id}" because the maximum number of allowed actions has been reached.` + `Rule "${this.rule.id}" skipped scheduling action "${action.id}" because the maximum number of allowed actions for connector type ${actionTypeId} has been reached.` ); - break; } + ruleRunMetricsStore.setTriggeredActionsStatusByConnectorType({ + actionTypeId, + status: ActionsCompletion.PARTIAL, + }); + continue; + } - if ( - ruleRunMetricsStore.hasReachedTheExecutableActionsLimitByConnectorType({ - actionTypeId, - actionsConfigMap, - }) - ) { - if (!ruleRunMetricsStore.hasConnectorTypeReachedTheLimit(actionTypeId)) { - logger.debug( - `Rule "${this.rule.id}" skipped scheduling action "${action.id}" because the maximum number of allowed actions for connector type ${actionTypeId} has been reached.` - ); - } - ruleRunMetricsStore.setTriggeredActionsStatusByConnectorType({ - actionTypeId, - status: ActionsCompletion.PARTIAL, - }); - continue; + if (!this.isExecutableAction(action)) { + this.logger.warn( + `Rule "${this.taskInstance.params.alertId}" skipped scheduling action "${action.id}" because it is disabled` + ); + continue; + } + + ruleRunMetricsStore.incrementNumberOfTriggeredActions(); + ruleRunMetricsStore.incrementNumberOfTriggeredActionsByConnectorType(actionTypeId); + + if (!this.isSystemAction(action) && summarizedAlerts) { + const defaultAction = action as RuleAction; + if (isActionOnInterval(action)) { + throttledSummaryActions[defaultAction.uuid!] = { date: new Date().toISOString() }; } - if (!this.isExecutableAction(action)) { + logActions[defaultAction.id] = await this.runSummarizedAction({ + action, + summarizedAlerts, + spaceId, + bulkActions, + }); + } else if (summarizedAlerts && this.isSystemAction(action)) { + const hasConnectorAdapter = this.taskRunnerContext.connectorAdapterRegistry.has( + action.actionTypeId + ); + /** + * System actions without an adapter + * cannot be executed + * + */ + if (!hasConnectorAdapter) { this.logger.warn( - `Rule "${this.taskInstance.params.alertId}" skipped scheduling action "${action.id}" because it is disabled` + `Rule "${this.taskInstance.params.alertId}" skipped scheduling system action "${action.id}" because no connector adapter is configured` ); + continue; } - ruleRunMetricsStore.incrementNumberOfTriggeredActions(); - ruleRunMetricsStore.incrementNumberOfTriggeredActionsByConnectorType(actionTypeId); - - if (summarizedAlerts) { - const { start, end } = getSummaryActionTimeBounds( - action, - this.rule.schedule, - this.previousStartedAt - ); - const ruleUrl = this.buildRuleUrl(spaceId, start, end); - const actionToRun = { - ...action, - params: injectActionParams({ - actionTypeId, - ruleUrl, - ruleName: this.rule.name, - actionParams: transformSummaryActionParams({ - alerts: summarizedAlerts, - rule: this.rule, - ruleTypeId: this.ruleType.id, - actionId: action.id, - actionParams: action.params, - spaceId, - actionsPlugin, - actionTypeId, - kibanaBaseUrl: this.taskRunnerContext.kibanaBaseUrl, - ruleUrl: ruleUrl?.absoluteUrl, - }), - }), - }; - - await this.actionRunOrAddToBulk({ - enqueueOptions: this.getEnqueueOptions(actionToRun), - bulkActions, - }); + const connectorAdapter = this.taskRunnerContext.connectorAdapterRegistry.get( + action.actionTypeId + ); + logActions[action.id] = await this.runSystemAction({ + action, + connectorAdapter, + summarizedAlerts, + rule: this.rule, + spaceId, + bulkActions, + }); + } else if (!this.isSystemAction(action) && alert) { + const defaultAction = action as RuleAction; + logActions[defaultAction.id] = await this.runAction({ + action, + spaceId, + alert, + ruleId, + bulkActions, + }); + const actionGroup = defaultAction.group; + if (!this.isRecoveredAlert(actionGroup)) { if (isActionOnInterval(action)) { - throttledSummaryActions[action.uuid!] = { date: new Date().toISOString() }; - } - - logActions[action.id] = { - id: action.id, - typeId: action.actionTypeId, - alertSummary: { - new: summarizedAlerts.new.count, - ongoing: summarizedAlerts.ongoing.count, - recovered: summarizedAlerts.recovered.count, - }, - }; - } else { - const ruleUrl = this.buildRuleUrl(spaceId); - const executableAlert = alert!; - const transformActionParamsOptions: TransformActionParamsOptions = { - actionsPlugin, - alertId: ruleId, - alertType: this.ruleType.id, - actionTypeId, - alertName: this.rule.name, - spaceId, - tags: this.rule.tags, - alertInstanceId: executableAlert.getId(), - alertUuid: executableAlert.getUuid(), - alertActionGroup: actionGroup, - alertActionGroupName: this.ruleTypeActionGroups!.get(actionGroup)!, - context: executableAlert.getContext(), - actionId: action.id, - state: executableAlert.getState(), - kibanaBaseUrl: this.taskRunnerContext.kibanaBaseUrl, - alertParams: this.rule.params, - actionParams: action.params, - flapping: executableAlert.getFlapping(), - ruleUrl: ruleUrl?.absoluteUrl, - consecutiveMatches: executableAlert.getActiveCount(), - }; - - if (executableAlert.isAlertAsData()) { - transformActionParamsOptions.aadAlert = executableAlert.getAlertAsData(); - } - - const actionToRun = { - ...action, - params: injectActionParams({ - actionTypeId, - ruleUrl, - ruleName: this.rule.name, - actionParams: transformActionParams(transformActionParamsOptions), - }), - }; - - await this.actionRunOrAddToBulk({ - enqueueOptions: this.getEnqueueOptions(actionToRun), - bulkActions, - }); - - logActions[action.id] = { - id: action.id, - typeId: action.actionTypeId, - alertId: alert.getId(), - alertGroup: action.group, - }; - - if (!this.isRecoveredAlert(actionGroup)) { - if (isActionOnInterval(action)) { - alert.updateLastScheduledActions( - action.group as ActionGroupIds, - generateActionHash(action), - action.uuid - ); - } else { - alert.updateLastScheduledActions(action.group as ActionGroupIds); - } - alert.unscheduleActions(); + alert.updateLastScheduledActions( + defaultAction.group as ActionGroupIds, + generateActionHash(action), + defaultAction.uuid + ); + } else { + alert.updateLastScheduledActions(defaultAction.group as ActionGroupIds); } + alert.unscheduleActions(); } } + } - if (!!bulkActions.length) { - for (const c of chunk(bulkActions, CHUNK_SIZE)) { - let enqueueResponse; - try { - enqueueResponse = await this.actionsClient!.bulkEnqueueExecution(c); - } catch (e) { - if (e.statusCode === 404) { - throw createTaskRunError(e, TaskErrorSource.USER); - } - throw createTaskRunError(e, TaskErrorSource.FRAMEWORK); - } - if (enqueueResponse.errors) { - bulkActionsResponse = bulkActionsResponse.concat( - enqueueResponse.items.filter( - (i) => i.response === ExecutionResponseType.QUEUED_ACTIONS_LIMIT_ERROR - ) - ); + if (!!bulkActions.length) { + for (const c of chunk(bulkActions, CHUNK_SIZE)) { + let enqueueResponse; + try { + enqueueResponse = await this.actionsClient!.bulkEnqueueExecution(c); + } catch (e) { + if (e.statusCode === 404) { + throw createTaskRunError(e, TaskErrorSource.USER); } + throw createTaskRunError(e, TaskErrorSource.FRAMEWORK); + } + if (enqueueResponse.errors) { + bulkActionsResponse = bulkActionsResponse.concat( + enqueueResponse.items.filter( + (i) => i.response === ExecutionResponseType.QUEUED_ACTIONS_LIMIT_ERROR + ) + ); } } + } - if (!!bulkActionsResponse.length) { - for (const r of bulkActionsResponse) { - if (r.response === ExecutionResponseType.QUEUED_ACTIONS_LIMIT_ERROR) { - ruleRunMetricsStore.setHasReachedQueuedActionsLimit(true); - ruleRunMetricsStore.decrementNumberOfTriggeredActions(); - ruleRunMetricsStore.decrementNumberOfTriggeredActionsByConnectorType(r.actionTypeId); - ruleRunMetricsStore.setTriggeredActionsStatusByConnectorType({ - actionTypeId: r.actionTypeId, - status: ActionsCompletion.PARTIAL, - }); - - logger.debug( - `Rule "${this.rule.id}" skipped scheduling action "${r.id}" because the maximum number of queued actions has been reached.` - ); + if (!!bulkActionsResponse.length) { + for (const r of bulkActionsResponse) { + if (r.response === ExecutionResponseType.QUEUED_ACTIONS_LIMIT_ERROR) { + ruleRunMetricsStore.setHasReachedQueuedActionsLimit(true); + ruleRunMetricsStore.decrementNumberOfTriggeredActions(); + ruleRunMetricsStore.decrementNumberOfTriggeredActionsByConnectorType(r.actionTypeId); + ruleRunMetricsStore.setTriggeredActionsStatusByConnectorType({ + actionTypeId: r.actionTypeId, + status: ActionsCompletion.PARTIAL, + }); - delete logActions[r.id]; - } + logger.debug( + `Rule "${this.rule.id}" skipped scheduling action "${r.id}" because the maximum number of queued actions has been reached.` + ); + + delete logActions[r.id]; } } + } - const logActionsValues = Object.values(logActions); - if (!!logActionsValues.length) { - for (const action of logActionsValues) { - alertingEventLogger.logAction(action); - } + const logActionsValues = Object.values(logActions); + if (!!logActionsValues.length) { + for (const action of logActionsValues) { + alertingEventLogger.logAction(action); } } + return { throttledSummaryActions }; } + private async runSummarizedAction({ + action, + summarizedAlerts, + spaceId, + bulkActions, + }: RunSummarizedActionArgs): Promise { + const { start, end } = getSummaryActionTimeBounds( + action, + this.rule.schedule, + this.previousStartedAt + ); + const ruleUrl = this.buildRuleUrl(spaceId, start, end); + const actionToRun = { + ...action, + params: injectActionParams({ + actionTypeId: action.actionTypeId, + ruleUrl, + ruleName: this.rule.name, + actionParams: transformSummaryActionParams({ + alerts: summarizedAlerts, + rule: this.rule, + ruleTypeId: this.ruleType.id, + actionId: action.id, + actionParams: action.params, + spaceId, + actionsPlugin: this.taskRunnerContext.actionsPlugin, + actionTypeId: action.actionTypeId, + kibanaBaseUrl: this.taskRunnerContext.kibanaBaseUrl, + ruleUrl: ruleUrl?.absoluteUrl, + }), + }), + }; + + await this.actionRunOrAddToBulk({ + enqueueOptions: this.getEnqueueOptions(actionToRun), + bulkActions, + }); + + return { + id: action.id, + typeId: action.actionTypeId, + alertSummary: { + new: summarizedAlerts.new.count, + ongoing: summarizedAlerts.ongoing.count, + recovered: summarizedAlerts.recovered.count, + }, + }; + } + + private async runSystemAction({ + action, + spaceId, + connectorAdapter, + summarizedAlerts, + rule, + bulkActions, + }: RunSystemActionArgs): Promise { + const ruleUrl = this.buildRuleUrl(spaceId); + + const connectorAdapterActionParams = connectorAdapter.buildActionParams({ + alerts: summarizedAlerts, + rule: { id: rule.id, tags: rule.tags, name: rule.name }, + ruleUrl: ruleUrl?.absoluteUrl, + spaceId, + params: action.params, + }); + + const actionToRun = Object.assign(action, { params: connectorAdapterActionParams }); + + await this.actionRunOrAddToBulk({ + enqueueOptions: this.getEnqueueOptions(actionToRun), + bulkActions, + }); + + return { + id: action.id, + typeId: action.actionTypeId, + alertSummary: { + new: summarizedAlerts.new.count, + ongoing: summarizedAlerts.ongoing.count, + recovered: summarizedAlerts.recovered.count, + }, + }; + } + + private async runAction({ + action, + spaceId, + alert, + ruleId, + bulkActions, + }: RunActionArgs): Promise { + const ruleUrl = this.buildRuleUrl(spaceId); + const executableAlert = alert!; + const actionGroup = action.group as ActionGroupIds; + const transformActionParamsOptions: TransformActionParamsOptions = { + actionsPlugin: this.taskRunnerContext.actionsPlugin, + alertId: ruleId, + alertType: this.ruleType.id, + actionTypeId: action.actionTypeId, + alertName: this.rule.name, + spaceId, + tags: this.rule.tags, + alertInstanceId: executableAlert.getId(), + alertUuid: executableAlert.getUuid(), + alertActionGroup: actionGroup, + alertActionGroupName: this.ruleTypeActionGroups!.get(actionGroup)!, + context: executableAlert.getContext(), + actionId: action.id, + state: executableAlert.getState(), + kibanaBaseUrl: this.taskRunnerContext.kibanaBaseUrl, + alertParams: this.rule.params, + actionParams: action.params, + flapping: executableAlert.getFlapping(), + ruleUrl: ruleUrl?.absoluteUrl, + }; + + if (executableAlert.isAlertAsData()) { + transformActionParamsOptions.aadAlert = executableAlert.getAlertAsData(); + } + const actionToRun = { + ...action, + params: injectActionParams({ + actionTypeId: action.actionTypeId, + ruleUrl, + ruleName: this.rule.name, + actionParams: transformActionParams(transformActionParamsOptions), + }), + }; + + await this.actionRunOrAddToBulk({ + enqueueOptions: this.getEnqueueOptions(actionToRun), + bulkActions, + }); + + return { + id: action.id, + typeId: action.actionTypeId, + alertId: alert.getId(), + alertGroup: action.group, + }; + } + private logNumberOfFilteredAlerts({ numberOfAlerts = 0, numberOfSummarizedAlerts = 0, @@ -420,7 +548,7 @@ export class ExecutionHandler< }: { numberOfAlerts: number; numberOfSummarizedAlerts: number; - action: RuleAction; + action: RuleAction | RuleSystemAction; }) { const count = numberOfAlerts - numberOfSummarizedAlerts; if (count > 0) { @@ -449,12 +577,16 @@ export class ExecutionHandler< return false; } - private isExecutableAction(action: RuleAction) { + private isExecutableAction(action: RuleAction | RuleSystemAction) { return this.taskRunnerContext.actionsPlugin.isActionExecutable(action.id, action.actionTypeId, { notifyUsage: true, }); } + private isSystemAction(action?: RuleAction | RuleSystemAction): action is RuleSystemAction { + return this.taskRunnerContext.actionsPlugin.isSystemActionConnector(action?.id ?? ''); + } + private isRecoveredAlert(actionGroup: string) { return actionGroup === this.ruleType.recoveryActionGroup.id; } @@ -548,7 +680,7 @@ export class ExecutionHandler< } } - private getEnqueueOptions(action: RuleAction): EnqueueExecutionOptions { + private getEnqueueOptions(action: RuleAction | RuleSystemAction): EnqueueExecutionOptions { const { apiKey, ruleConsumer, @@ -589,14 +721,15 @@ export class ExecutionHandler< const executables = []; for (const action of this.rule.actions) { const alertsArray = Object.entries(alerts); - let summarizedAlerts = null; + if (this.shouldGetSummarizedAlerts({ action, throttledSummaryActions })) { summarizedAlerts = await this.getSummarizedAlerts({ action, spaceId: this.taskInstance.params.spaceId, ruleId: this.taskInstance.params.alertId, }); + if (!isSummaryActionOnInterval(action)) { this.logNumberOfFilteredAlerts({ numberOfAlerts: alertsArray.length, @@ -641,7 +774,7 @@ export class ExecutionHandler< // notifications for flapping pending recovered alerts if ( alert.getPendingRecoveredCount() > 0 && - action.frequency?.notifyWhen !== RuleNotifyWhen.CHANGE + action?.frequency?.notifyWhen !== RuleNotifyWhen.CHANGE ) { continue; } @@ -666,6 +799,22 @@ export class ExecutionHandler< } } + if (!this.canGetSummarizedAlerts()) { + return executables; + } + + for (const systemAction of this.rule?.systemActions ?? []) { + const summarizedAlerts = await this.getSummarizedAlerts({ + action: systemAction, + spaceId: this.taskInstance.params.spaceId, + ruleId: this.taskInstance.params.alertId, + }); + + if (summarizedAlerts && summarizedAlerts.all.count !== 0) { + executables.push({ action: systemAction, summarizedAlerts }); + } + } + return executables; } @@ -688,6 +837,7 @@ export class ExecutionHandler< } return false; } + if (action.useAlertDataForTemplate) { return true; } @@ -714,7 +864,7 @@ export class ExecutionHandler< ruleId, spaceId, }: { - action: RuleAction; + action: RuleAction | RuleSystemAction; ruleId: string; spaceId: string; }): Promise { @@ -722,13 +872,13 @@ export class ExecutionHandler< ruleId, spaceId, excludedAlertInstanceIds: this.rule.mutedInstanceIds, - alertsFilter: action.alertsFilter, + alertsFilter: this.isSystemAction(action) ? undefined : (action as RuleAction).alertsFilter, }; let options: GetSummarizedAlertsParams; - if (isActionOnInterval(action)) { - const throttleMills = parseDuration(action.frequency!.throttle!); + if (!this.isSystemAction(action) && isActionOnInterval(action)) { + const throttleMills = parseDuration((action as RuleAction).frequency!.throttle!); const start = new Date(Date.now() - throttleMills); options = { diff --git a/x-pack/plugins/alerting/server/task_runner/fixtures.ts b/x-pack/plugins/alerting/server/task_runner/fixtures.ts index b2a984ea5768f..ae8eccfcb1f86 100644 --- a/x-pack/plugins/alerting/server/task_runner/fixtures.ts +++ b/x-pack/plugins/alerting/server/task_runner/fixtures.ts @@ -15,6 +15,7 @@ import { RuleLastRunOutcomeOrderMap, RuleLastRunOutcomes, SanitizedRule, + SanitizedRuleAction, } from '../../common'; import { getDefaultMonitoring } from '../lib/monitoring'; import { UntypedNormalizedRuleType } from '../rule_type_registry'; @@ -284,7 +285,7 @@ export const mockedRule: SanitizedRule return { ...action, id: action.uuid, - }; + } as SanitizedRuleAction; }), isSnoozedUntil: undefined, }; diff --git a/x-pack/plugins/alerting/server/task_runner/get_maintenance_windows.test.ts b/x-pack/plugins/alerting/server/task_runner/get_maintenance_windows.test.ts index 3cc5e54283dc8..f8a0508346a24 100644 --- a/x-pack/plugins/alerting/server/task_runner/get_maintenance_windows.test.ts +++ b/x-pack/plugins/alerting/server/task_runner/get_maintenance_windows.test.ts @@ -20,6 +20,7 @@ import { } from './get_maintenance_windows'; import { getFakeKibanaRequest } from './rule_loader'; import { TaskRunnerContext } from './types'; +import { FilterStateStore } from '@kbn/es-query'; const logger = loggingSystemMock.create().get(); const mockBasePathService = { set: jest.fn() }; @@ -206,7 +207,7 @@ describe('filterMaintenanceWindows', () => { type: 'phrase', }, $state: { - store: 'appState', + store: FilterStateStore.APP_STATE, }, query: { match_phrase: { @@ -274,7 +275,7 @@ describe('filterMaintenanceWindowsIds', () => { type: 'phrase', }, $state: { - store: 'appState', + store: FilterStateStore.APP_STATE, }, query: { match_phrase: { diff --git a/x-pack/plugins/alerting/server/task_runner/rule_action_helper.test.ts b/x-pack/plugins/alerting/server/task_runner/rule_action_helper.test.ts index c2ac1ab38a2fa..2edd66bc6f43c 100644 --- a/x-pack/plugins/alerting/server/task_runner/rule_action_helper.test.ts +++ b/x-pack/plugins/alerting/server/task_runner/rule_action_helper.test.ts @@ -60,6 +60,7 @@ describe('rule_action_helper', () => { const result = isSummaryAction(mockAction); expect(result).toBe(false); }); + test('should return false if the action does not have frequency field', () => { const result = isSummaryAction(mockOldAction); expect(result).toBe(false); diff --git a/x-pack/plugins/alerting/server/task_runner/rule_action_helper.ts b/x-pack/plugins/alerting/server/task_runner/rule_action_helper.ts index a23323ffee2b7..8845988e06bd4 100644 --- a/x-pack/plugins/alerting/server/task_runner/rule_action_helper.ts +++ b/x-pack/plugins/alerting/server/task_runner/rule_action_helper.ts @@ -15,16 +15,16 @@ import { } from '../../common'; export const isSummaryAction = (action?: RuleAction) => { - return action?.frequency?.summary || false; + return action?.frequency?.summary ?? false; }; export const isActionOnInterval = (action?: RuleAction) => { - if (!action?.frequency) { + if (action?.frequency == null) { return false; } return ( - action.frequency.notifyWhen === RuleNotifyWhenTypeValues[2] && - typeof action.frequency.throttle === 'string' + action?.frequency.notifyWhen === RuleNotifyWhenTypeValues[2] && + typeof action?.frequency.throttle === 'string' ); }; @@ -44,14 +44,19 @@ export const isSummaryActionThrottled = ({ if (!isActionOnInterval(action)) { return false; } + if (!throttledSummaryActions) { return false; } + const throttledAction = throttledSummaryActions[action?.uuid!]; + if (!throttledAction) { return false; } + let throttleMills = 0; + try { throttleMills = parseDuration(action?.frequency!.throttle!); } catch (e) { @@ -86,6 +91,7 @@ export const getSummaryActionsFromTaskState = ({ (action) => action.frequency?.summary && (action.uuid === key || generateActionHash(action) === key) ); + if (actionExists) { // replace hash with uuid newObj[actionExists.uuid!] = val; @@ -102,6 +108,7 @@ export const getSummaryActionTimeBounds = ( if (!isSummaryAction(action)) { return { start: undefined, end: undefined }; } + let startDate: Date; const now = Date.now(); diff --git a/x-pack/plugins/alerting/server/task_runner/task_runner.test.ts b/x-pack/plugins/alerting/server/task_runner/task_runner.test.ts index a75a73a124953..172c490cdbc8c 100644 --- a/x-pack/plugins/alerting/server/task_runner/task_runner.test.ts +++ b/x-pack/plugins/alerting/server/task_runner/task_runner.test.ts @@ -83,6 +83,7 @@ import { DataViewsServerPluginStart } from '@kbn/data-views-plugin/server'; import { rulesSettingsClientMock } from '../rules_settings_client.mock'; import { maintenanceWindowClientMock } from '../maintenance_window_client.mock'; import { alertsServiceMock } from '../alerts_service/alerts_service.mock'; +import { ConnectorAdapterRegistry } from '../connector_adapters/connector_adapter_registry'; import { getMockMaintenanceWindow } from '../data/maintenance_window/test_helpers'; import { alertsClientMock } from '../alerts_client/alerts_client.mock'; import { MaintenanceWindow } from '../application/maintenance_window/types'; @@ -148,6 +149,7 @@ describe('Task Runner', () => { } as DataViewsServerPluginStart; const alertsService = alertsServiceMock.create(); const maintenanceWindowClient = maintenanceWindowClientMock.create(); + const connectorAdapterRegistry = new ConnectorAdapterRegistry(); type TaskRunnerFactoryInitializerParamsType = jest.Mocked & { actionsPlugin: jest.Mocked; @@ -186,6 +188,7 @@ describe('Task Runner', () => { }, getRulesSettingsClientWithRequest: jest.fn().mockReturnValue(rulesSettingsClientMock.create()), getMaintenanceWindowClientWithRequest: jest.fn().mockReturnValue(maintenanceWindowClient), + connectorAdapterRegistry, }; const ephemeralTestParams: Array< diff --git a/x-pack/plugins/alerting/server/task_runner/task_runner_alerts_client.test.ts b/x-pack/plugins/alerting/server/task_runner/task_runner_alerts_client.test.ts index ed58d75dc1038..f28c849b2bc37 100644 --- a/x-pack/plugins/alerting/server/task_runner/task_runner_alerts_client.test.ts +++ b/x-pack/plugins/alerting/server/task_runner/task_runner_alerts_client.test.ts @@ -98,6 +98,7 @@ import { VERSION, ALERT_CONSECUTIVE_MATCHES, } from '@kbn/rule-data-utils'; +import { ConnectorAdapterRegistry } from '../connector_adapters/connector_adapter_registry'; jest.mock('uuid', () => ({ v4: () => '5f6aa57d-3e22-484e-bae8-cbed868f4d28', @@ -171,6 +172,7 @@ describe('Task Runner', () => { const mockLegacyAlertsClient = legacyAlertsClientMock.create(); const ruleRunMetricsStore = ruleRunMetricsStoreMock.create(); const maintenanceWindowClient = maintenanceWindowClientMock.create(); + const connectorAdapterRegistry = new ConnectorAdapterRegistry(); type TaskRunnerFactoryInitializerParamsType = jest.Mocked & { actionsPlugin: jest.Mocked; @@ -211,6 +213,7 @@ describe('Task Runner', () => { .fn() .mockReturnValue(rulesSettingsClientMock.create()), getMaintenanceWindowClientWithRequest: jest.fn().mockReturnValue(maintenanceWindowClient), + connectorAdapterRegistry, }; describe(`using ${label} for alert indices`, () => { diff --git a/x-pack/plugins/alerting/server/task_runner/task_runner_cancel.test.ts b/x-pack/plugins/alerting/server/task_runner/task_runner_cancel.test.ts index 8723fa98ef4fc..08d3cbb81244b 100644 --- a/x-pack/plugins/alerting/server/task_runner/task_runner_cancel.test.ts +++ b/x-pack/plugins/alerting/server/task_runner/task_runner_cancel.test.ts @@ -57,6 +57,7 @@ import { dataViewPluginMocks } from '@kbn/data-views-plugin/public/mocks'; import { rulesSettingsClientMock } from '../rules_settings_client.mock'; import { maintenanceWindowClientMock } from '../maintenance_window_client.mock'; import { alertsServiceMock } from '../alerts_service/alerts_service.mock'; +import { ConnectorAdapterRegistry } from '../connector_adapters/connector_adapter_registry'; import { RULE_SAVED_OBJECT_TYPE } from '../saved_objects'; import { TaskRunnerContext } from './types'; @@ -111,6 +112,7 @@ describe('Task Runner Cancel', () => { const uiSettingsService = uiSettingsServiceMock.createStartContract(); const dataPlugin = dataPluginMock.createStartContract(); const inMemoryMetrics = inMemoryMetricsMock.create(); + const connectorAdapterRegistry = new ConnectorAdapterRegistry(); type TaskRunnerFactoryInitializerParamsType = jest.Mocked & { actionsPlugin: jest.Mocked; @@ -151,6 +153,7 @@ describe('Task Runner Cancel', () => { getMaintenanceWindowClientWithRequest: jest .fn() .mockReturnValue(maintenanceWindowClientMock.create()), + connectorAdapterRegistry, }; beforeEach(() => { diff --git a/x-pack/plugins/alerting/server/task_runner/task_runner_factory.test.ts b/x-pack/plugins/alerting/server/task_runner/task_runner_factory.test.ts index 8b3d3c7b6e27d..43a870d57c08a 100644 --- a/x-pack/plugins/alerting/server/task_runner/task_runner_factory.test.ts +++ b/x-pack/plugins/alerting/server/task_runner/task_runner_factory.test.ts @@ -33,6 +33,7 @@ import { rulesSettingsClientMock } from '../rules_settings_client.mock'; import { maintenanceWindowClientMock } from '../maintenance_window_client.mock'; import { alertsServiceMock } from '../alerts_service/alerts_service.mock'; import { schema } from '@kbn/config-schema'; +import { ConnectorAdapterRegistry } from '../connector_adapters/connector_adapter_registry'; import { TaskRunnerContext } from './types'; const inMemoryMetrics = inMemoryMetricsMock.create(); @@ -98,6 +99,7 @@ describe('Task Runner Factory', () => { const encryptedSavedObjectsPlugin = encryptedSavedObjectsMock.createStart(); const rulesClient = rulesClientMock.create(); + const connectorAdapterRegistry = new ConnectorAdapterRegistry(); const taskRunnerFactoryInitializerParams: jest.Mocked = { data: dataPlugin, @@ -132,6 +134,7 @@ describe('Task Runner Factory', () => { getMaintenanceWindowClientWithRequest: jest .fn() .mockReturnValue(maintenanceWindowClientMock.create()), + connectorAdapterRegistry, }; beforeEach(() => { diff --git a/x-pack/plugins/alerting/server/task_runner/types.ts b/x-pack/plugins/alerting/server/task_runner/types.ts index 5c80f7b083636..dd7b14fc7f4aa 100644 --- a/x-pack/plugins/alerting/server/task_runner/types.ts +++ b/x-pack/plugins/alerting/server/task_runner/types.ts @@ -39,6 +39,7 @@ import { RuleTypeState, RuleAction, RuleAlertData, + RuleSystemAction, RulesSettingsFlappingProperties, RulesSettingsQueryDelayProperties, } from '../../common'; @@ -55,6 +56,7 @@ import { } from '../types'; import { RuleRunMetrics, RuleRunMetricsStore } from '../lib/rule_run_metrics_store'; import { AlertingEventLogger } from '../lib/alerting_event_logger/alerting_event_logger'; +import { ConnectorAdapterRegistry } from '../connector_adapters/connector_adapter_registry'; export interface RuleTaskRunResult { state: RuleTaskState; @@ -124,7 +126,7 @@ export type Executable< ActionGroupIds extends string, RecoveryActionGroupId extends string > = { - action: RuleAction; + action: RuleAction | RuleSystemAction; } & ( | { alert: Alert; @@ -174,4 +176,5 @@ export interface TaskRunnerContext { supportsEphemeralTasks: boolean; uiSettings: UiSettingsServiceStart; usageCounter?: UsageCounter; + connectorAdapterRegistry: ConnectorAdapterRegistry; } diff --git a/x-pack/plugins/alerting/server/types.ts b/x-pack/plugins/alerting/server/types.ts index eeb13576ce39d..5316bcaa1a5ae 100644 --- a/x-pack/plugins/alerting/server/types.ts +++ b/x-pack/plugins/alerting/server/types.ts @@ -27,6 +27,7 @@ import { SharePluginStart } from '@kbn/share-plugin/server'; import type { DefaultAlert, FieldMap } from '@kbn/alerts-as-data-utils'; import { Alert } from '@kbn/alerts-as-data-utils'; import { Filter } from '@kbn/es-query'; +import { ActionsApiRequestHandlerContext } from '@kbn/actions-plugin/server'; import { RuleTypeRegistry as OrigruleTypeRegistry } from './rule_type_registry'; import { PluginSetupContract, PluginStartContract } from './plugin'; import { RulesClient } from './rules_client'; @@ -87,6 +88,7 @@ export interface AlertingApiRequestHandlerContext { */ export type AlertingRequestHandlerContext = CustomRequestHandlerContext<{ alerting: AlertingApiRequestHandlerContext; + actions: ActionsApiRequestHandlerContext; }>; /** @@ -435,7 +437,7 @@ export interface RawRuleAlertsFilter extends AlertsFilter { export interface RawRuleAction extends SavedObjectAttributes { uuid: string; - group: string; + group?: string; actionRef: string; actionTypeId: string; params: RuleActionParams; @@ -445,6 +447,7 @@ export interface RawRuleAction extends SavedObjectAttributes { throttle: string | null; }; alertsFilter?: RawRuleAlertsFilter; + useAlertDataAsTemplate?: boolean; } // note that the `error` property is "null-able", as we're doing a partial diff --git a/x-pack/plugins/cloud_integrations/cloud_links/public/maybe_add_cloud_links/theme_darkmode_hook.ts b/x-pack/plugins/cloud_integrations/cloud_links/public/maybe_add_cloud_links/theme_darkmode_hook.ts index c0a4fcc3a09ac..0e062b693a24f 100644 --- a/x-pack/plugins/cloud_integrations/cloud_links/public/maybe_add_cloud_links/theme_darkmode_hook.ts +++ b/x-pack/plugins/cloud_integrations/cloud_links/public/maybe_add_cloud_links/theme_darkmode_hook.ts @@ -9,6 +9,7 @@ import { useCallback, useEffect, useState } from 'react'; import { i18n } from '@kbn/i18n'; import { IUiSettingsClient } from '@kbn/core-ui-settings-browser'; import { useUpdateUserProfile } from '@kbn/user-profile-components'; +import useMountedState from 'react-use/lib/useMountedState'; interface Deps { uiSettingsClient: IUiSettingsClient; @@ -16,6 +17,8 @@ interface Deps { export const useThemeDarkmodeToggle = ({ uiSettingsClient }: Deps) => { const [isDarkModeOn, setIsDarkModeOn] = useState(false); + const isMounted = useMountedState(); + // If a value is set in kibana.yml (uiSettings.overrides.theme:darkMode) // we don't allow the user to change the theme color. const valueSetInKibanaConfig = uiSettingsClient.isOverridden('theme:darkMode'); @@ -37,14 +40,23 @@ export const useThemeDarkmodeToggle = ({ uiSettingsClient }: Deps) => { }, }); - const { userSettings: { darkMode: colorScheme } = { darkMode: undefined } } = - userProfileData ?? {}; + const { + userSettings: { + darkMode: colorScheme = uiSettingsClient.get('theme:darkMode') === true ? 'dark' : 'light', + } = {}, + } = userProfileData ?? { + userSettings: {}, + }; const toggle = useCallback( (on: boolean) => { if (isLoading) { return; } + + // optimistic update + setIsDarkModeOn(on); + update({ userSettings: { darkMode: on ? 'dark' : 'light', @@ -55,17 +67,9 @@ export const useThemeDarkmodeToggle = ({ uiSettingsClient }: Deps) => { ); useEffect(() => { - let updatedValue = false; - - if (typeof colorScheme !== 'string') { - // User profile does not have yet any preference -> default to space dark mode value - updatedValue = uiSettingsClient.get('theme:darkMode') ?? false; - } else { - updatedValue = colorScheme === 'dark'; - } - - setIsDarkModeOn(updatedValue); - }, [colorScheme, uiSettingsClient]); + if (!isMounted()) return; + setIsDarkModeOn(colorScheme === 'dark'); + }, [isMounted, colorScheme]); return { isVisible: valueSetInKibanaConfig ? false : Boolean(userProfileData), diff --git a/x-pack/plugins/ecs_data_quality_dashboard/server/__mocks__/request_context.ts b/x-pack/plugins/ecs_data_quality_dashboard/server/__mocks__/request_context.ts index eaa90f75c5890..895a0b4116d9a 100644 --- a/x-pack/plugins/ecs_data_quality_dashboard/server/__mocks__/request_context.ts +++ b/x-pack/plugins/ecs_data_quality_dashboard/server/__mocks__/request_context.ts @@ -28,7 +28,7 @@ export const createMockClients = () => { type MockClients = ReturnType; -const convertRequestContextMock = (context: T) => { +const convertRequestContextMock = >(context: T) => { return coreMock.createCustomRequestHandlerContext(context); }; diff --git a/x-pack/plugins/elastic_assistant/server/__mocks__/conversations_schema.mock.ts b/x-pack/plugins/elastic_assistant/server/__mocks__/conversations_schema.mock.ts index e7914ccb60acd..8a834903d7a85 100644 --- a/x-pack/plugins/elastic_assistant/server/__mocks__/conversations_schema.mock.ts +++ b/x-pack/plugins/elastic_assistant/server/__mocks__/conversations_schema.mock.ts @@ -13,10 +13,10 @@ import { ConversationResponse, ConversationUpdateProps, } from '@kbn/elastic-assistant-common'; -import { SearchEsConversationSchema } from '../ai_assistant_data_clients/conversations/types'; +import { EsConversationSchema } from '../ai_assistant_data_clients/conversations/types'; export const getConversationSearchEsMock = () => { - const searchResponse: estypes.SearchResponse = { + const searchResponse: estypes.SearchResponse = { took: 3, timed_out: false, _shards: { @@ -130,7 +130,6 @@ export const getConversationMock = ( connectorId: '1', defaultSystemPromptId: 'Default', }, - replacements: [], title: 'test', ...params, createdAt: '2019-12-13T16:40:33.400Z', diff --git a/x-pack/plugins/elastic_assistant/server/__mocks__/response.ts b/x-pack/plugins/elastic_assistant/server/__mocks__/response.ts index 3b71dfcfbb67c..50de6d50e67d5 100644 --- a/x-pack/plugins/elastic_assistant/server/__mocks__/response.ts +++ b/x-pack/plugins/elastic_assistant/server/__mocks__/response.ts @@ -8,7 +8,7 @@ import { httpServerMock } from '@kbn/core/server/mocks'; import { getConversationSearchEsMock } from './conversations_schema.mock'; import { estypes } from '@elastic/elasticsearch'; -import { SearchEsConversationSchema } from '../ai_assistant_data_clients/conversations/types'; +import { EsConversationSchema } from '../ai_assistant_data_clients/conversations/types'; import { FindResponse } from '../ai_assistant_data_clients/find'; import { ConversationResponse } from '@kbn/elastic-assistant-common'; import { SearchEsPromptsSchema } from '../ai_assistant_data_clients/prompts/types'; @@ -20,20 +20,19 @@ export const responseMock = { create: httpServerMock.createResponseFactory, }; -export const getEmptyFindResult = (): FindResponse => ({ +export const getEmptyFindResult = (): FindResponse => ({ page: 1, perPage: 1, total: 0, data: getBasicEmptySearchResponse(), }); -export const getFindConversationsResultWithSingleHit = - (): FindResponse => ({ - page: 1, - perPage: 1, - total: 1, - data: getConversationSearchEsMock(), - }); +export const getFindConversationsResultWithSingleHit = (): FindResponse => ({ + page: 1, + perPage: 1, + total: 1, + data: getConversationSearchEsMock(), +}); export const getFindPromptsResultWithSingleHit = (): FindResponse => ({ page: 1, @@ -50,17 +49,16 @@ export const getFindAnonymizationFieldsResultWithSingleHit = data: getAnonymizationFieldsSearchEsMock(), }); -export const getBasicEmptySearchResponse = - (): estypes.SearchResponse => ({ - took: 1, - timed_out: false, - _shards: { total: 1, successful: 1, skipped: 0, failed: 0 }, - hits: { - hits: [], - total: { relation: 'eq', value: 0 }, - max_score: 0, - }, - }); +export const getBasicEmptySearchResponse = (): estypes.SearchResponse => ({ + took: 1, + timed_out: false, + _shards: { total: 1, successful: 1, skipped: 0, failed: 0 }, + hits: { + hits: [], + total: { relation: 'eq', value: 0 }, + max_score: 0, + }, +}); export const getConversationResponseMock = ( timestamp: string = new Date().toISOString() @@ -75,7 +73,7 @@ export const getConversationResponseMock = ( }, excludeFromLastConversationStorage: false, messages: [], - replacements: [], + replacements: {}, createdAt: timestamp, namespace: 'default', isDefault: false, diff --git a/x-pack/plugins/elastic_assistant/server/ai_assistant_data_clients/conversations/create_conversation.test.ts b/x-pack/plugins/elastic_assistant/server/ai_assistant_data_clients/conversations/create_conversation.test.ts index 2ac98390ccef0..e6573173cd9bc 100644 --- a/x-pack/plugins/elastic_assistant/server/ai_assistant_data_clients/conversations/create_conversation.test.ts +++ b/x-pack/plugins/elastic_assistant/server/ai_assistant_data_clients/conversations/create_conversation.test.ts @@ -9,7 +9,7 @@ import { elasticsearchClientMock } from '@kbn/core-elasticsearch-client-server-m import { createConversation } from './create_conversation'; import { loggingSystemMock } from '@kbn/core-logging-server-mocks'; import { estypes } from '@elastic/elasticsearch'; -import { SearchEsConversationSchema } from './types'; +import { EsConversationSchema } from './types'; import { getConversation } from './get_conversation'; import { ConversationCreateProps, ConversationResponse } from '@kbn/elastic-assistant-common'; import { AuthenticatedUser } from '@kbn/security-plugin-types-common'; @@ -37,7 +37,7 @@ export const getCreateConversationMock = (): ConversationCreateProps => ({ excludeFromLastConversationStorage: false, isDefault: false, messages: [], - replacements: [], + replacements: {}, category: 'assistant', }); @@ -52,7 +52,7 @@ export const getConversationResponseMock = (): ConversationResponse => ({ }, excludeFromLastConversationStorage: false, messages: [], - replacements: [], + replacements: {}, createdAt: '2024-01-28T04:20:02.394Z', namespace: 'test', isDefault: false, @@ -66,54 +66,53 @@ export const getConversationResponseMock = (): ConversationResponse => ({ ], }); -export const getSearchConversationMock = - (): estypes.SearchResponse => ({ - _scroll_id: '123', - _shards: { - failed: 0, - skipped: 0, - successful: 0, - total: 0, - }, - hits: { - hits: [ - { - _id: '1', - _index: '', - _score: 0, - _source: { - '@timestamp': '2020-04-20T15:25:31.830Z', - created_at: '2020-04-20T15:25:31.830Z', - title: 'title-1', - updated_at: '2020-04-20T15:25:31.830Z', - messages: [], - category: 'assistant', - id: '1', - namespace: 'default', - is_default: true, - exclude_from_last_conversation_storage: false, - api_config: { - connector_id: 'c1', - default_system_prompt_id: 'prompt-1', - model: 'test', - provider: 'Azure OpenAI', - }, - users: [ - { - id: '1111', - name: 'elastic', - }, - ], - replacements: undefined, +export const getSearchConversationMock = (): estypes.SearchResponse => ({ + _scroll_id: '123', + _shards: { + failed: 0, + skipped: 0, + successful: 0, + total: 0, + }, + hits: { + hits: [ + { + _id: '1', + _index: '', + _score: 0, + _source: { + '@timestamp': '2020-04-20T15:25:31.830Z', + created_at: '2020-04-20T15:25:31.830Z', + title: 'title-1', + updated_at: '2020-04-20T15:25:31.830Z', + messages: [], + category: 'assistant', + id: '1', + namespace: 'default', + is_default: true, + exclude_from_last_conversation_storage: false, + api_config: { + connector_id: 'c1', + default_system_prompt_id: 'prompt-1', + model: 'test', + provider: 'Azure OpenAI', }, + users: [ + { + id: '1111', + name: 'elastic', + }, + ], + replacements: undefined, }, - ], - max_score: 0, - total: 1, - }, - timed_out: false, - took: 10, - }); + }, + ], + max_score: 0, + total: 1, + }, + timed_out: false, + took: 10, +}); describe('createConversation', () => { let logger: ReturnType; diff --git a/x-pack/plugins/elastic_assistant/server/ai_assistant_data_clients/conversations/create_conversation.ts b/x-pack/plugins/elastic_assistant/server/ai_assistant_data_clients/conversations/create_conversation.ts index 7dc36f9d4bd36..4d4462cc7bdb1 100644 --- a/x-pack/plugins/elastic_assistant/server/ai_assistant_data_clients/conversations/create_conversation.ts +++ b/x-pack/plugins/elastic_assistant/server/ai_assistant_data_clients/conversations/create_conversation.ts @@ -105,7 +105,12 @@ export const transformToCreateScheme = ( }, })), updated_at: createdAt, - replacements, + replacements: replacements + ? Object.keys(replacements).map((key) => ({ + uuid: key, + value: replacements[key], + })) + : undefined, namespace: spaceId, }; }; diff --git a/x-pack/plugins/elastic_assistant/server/ai_assistant_data_clients/conversations/get_conversation.test.ts b/x-pack/plugins/elastic_assistant/server/ai_assistant_data_clients/conversations/get_conversation.test.ts index e0b10ab8df5c4..ac2a22c1786ee 100644 --- a/x-pack/plugins/elastic_assistant/server/ai_assistant_data_clients/conversations/get_conversation.test.ts +++ b/x-pack/plugins/elastic_assistant/server/ai_assistant_data_clients/conversations/get_conversation.test.ts @@ -9,7 +9,7 @@ import type { Logger } from '@kbn/core/server'; import { elasticsearchClientMock } from '@kbn/core-elasticsearch-client-server-mocks'; import { getConversation } from './get_conversation'; import { estypes } from '@elastic/elasticsearch'; -import { SearchEsConversationSchema } from './types'; +import { EsConversationSchema } from './types'; import { loggingSystemMock } from '@kbn/core-logging-server-mocks'; import { ConversationResponse } from '@kbn/elastic-assistant-common'; import { AuthenticatedUser } from '@kbn/security-plugin-types-common'; @@ -51,57 +51,56 @@ const mockUser1 = { }, } as AuthenticatedUser; -export const getSearchConversationMock = - (): estypes.SearchResponse => ({ - _scroll_id: '123', - _shards: { - failed: 0, - skipped: 0, - successful: 0, - total: 0, - }, - hits: { - hits: [ - { - _id: '1', - _index: '', - _score: 0, - _source: { - '@timestamp': '2020-04-20T15:25:31.830Z', - created_at: '2020-04-20T15:25:31.830Z', - title: 'title-1', - updated_at: '2020-04-20T15:25:31.830Z', - messages: [], - id: '1', - namespace: 'default', - is_default: true, - exclude_from_last_conversation_storage: false, - api_config: { - connector_id: 'c1', - default_system_prompt_id: 'prompt-1', - model: 'test', - provider: 'Azure OpenAI', - }, - summary: { - content: 'test', - }, - category: 'assistant', - users: [ - { - id: '1111', - name: 'elastic', - }, - ], - replacements: undefined, +export const getSearchConversationMock = (): estypes.SearchResponse => ({ + _scroll_id: '123', + _shards: { + failed: 0, + skipped: 0, + successful: 0, + total: 0, + }, + hits: { + hits: [ + { + _id: '1', + _index: '', + _score: 0, + _source: { + '@timestamp': '2020-04-20T15:25:31.830Z', + created_at: '2020-04-20T15:25:31.830Z', + title: 'title-1', + updated_at: '2020-04-20T15:25:31.830Z', + messages: [], + id: '1', + namespace: 'default', + is_default: true, + exclude_from_last_conversation_storage: false, + api_config: { + connector_id: 'c1', + default_system_prompt_id: 'prompt-1', + model: 'test', + provider: 'Azure OpenAI', + }, + summary: { + content: 'test', }, + category: 'assistant', + users: [ + { + id: '1111', + name: 'elastic', + }, + ], + replacements: undefined, }, - ], - max_score: 0, - total: 1, - }, - timed_out: false, - took: 10, - }); + }, + ], + max_score: 0, + total: 1, + }, + timed_out: false, + took: 10, +}); describe('getConversation', () => { let loggerMock: Logger; diff --git a/x-pack/plugins/elastic_assistant/server/ai_assistant_data_clients/conversations/get_conversation.ts b/x-pack/plugins/elastic_assistant/server/ai_assistant_data_clients/conversations/get_conversation.ts index 1418c872dd3ac..7d011c9f21433 100644 --- a/x-pack/plugins/elastic_assistant/server/ai_assistant_data_clients/conversations/get_conversation.ts +++ b/x-pack/plugins/elastic_assistant/server/ai_assistant_data_clients/conversations/get_conversation.ts @@ -8,8 +8,8 @@ import { ElasticsearchClient, Logger } from '@kbn/core/server'; import { ConversationResponse } from '@kbn/elastic-assistant-common'; import { AuthenticatedUser } from '@kbn/security-plugin/common'; -import { SearchEsConversationSchema } from './types'; -import { transformESToConversations } from './transforms'; +import { EsConversationSchema } from './types'; +import { transformESSearchToConversations } from './transforms'; export interface GetConversationParams { esClient: ElasticsearchClient; @@ -47,7 +47,7 @@ export const getConversation = async ({ ] : []; try { - const response = await esClient.search({ + const response = await esClient.search({ body: { query: { bool: { @@ -73,7 +73,7 @@ export const getConversation = async ({ index: conversationIndex, seq_no_primary_term: true, }); - const conversation = transformESToConversations(response); + const conversation = transformESSearchToConversations(response); return conversation[0] ?? null; } catch (err) { logger.error(`Error fetching conversation: ${err} with id: ${id}`); diff --git a/x-pack/plugins/elastic_assistant/server/ai_assistant_data_clients/conversations/helpers.ts b/x-pack/plugins/elastic_assistant/server/ai_assistant_data_clients/conversations/helpers.ts index baaa0a6e94a9f..505e9710a9e93 100644 --- a/x-pack/plugins/elastic_assistant/server/ai_assistant_data_clients/conversations/helpers.ts +++ b/x-pack/plugins/elastic_assistant/server/ai_assistant_data_clients/conversations/helpers.ts @@ -20,6 +20,8 @@ export const getUpdateScript = ({ if (ctx._source.api_config != null) { if (params.assignEmpty == true || params.api_config.containsKey('connector_id')) { ctx._source.api_config.connector_id = params.api_config.connector_id; + ctx._source.api_config.remove('model'); + ctx._source.api_config.remove('provider'); } if (params.assignEmpty == true || params.api_config.containsKey('default_system_prompt_id')) { ctx._source.api_config.default_system_prompt_id = params.api_config.default_system_prompt_id; diff --git a/x-pack/plugins/elastic_assistant/server/ai_assistant_data_clients/conversations/transforms.ts b/x-pack/plugins/elastic_assistant/server/ai_assistant_data_clients/conversations/transforms.ts index fa70914d07e9b..1f938a3435afc 100644 --- a/x-pack/plugins/elastic_assistant/server/ai_assistant_data_clients/conversations/transforms.ts +++ b/x-pack/plugins/elastic_assistant/server/ai_assistant_data_clients/conversations/transforms.ts @@ -8,13 +8,13 @@ import { estypes } from '@elastic/elasticsearch'; import { ConversationResponse, - Replacement, + Replacements, replaceOriginalValuesWithUuidValues, } from '@kbn/elastic-assistant-common'; -import { SearchEsConversationSchema } from './types'; +import { EsConversationSchema } from './types'; -export const transformESToConversations = ( - response: estypes.SearchResponse +export const transformESSearchToConversations = ( + response: estypes.SearchResponse ): ConversationResponse[] => { return response.hits.hits .filter((hit) => hit._source !== undefined) @@ -50,10 +50,18 @@ export const transformESToConversations = ( conversationSchema.messages?.map((message: Record) => ({ timestamp: message['@timestamp'], // always return anonymized data from the client - content: replaceOriginalValuesWithUuidValues({ - messageContent: message.content, - replacements: conversationSchema.replacements ?? [], - }), + content: conversationSchema.replacements + ? replaceOriginalValuesWithUuidValues({ + messageContent: message.content, + replacements: conversationSchema.replacements?.reduce( + (acc: Record, r) => { + acc[r.uuid] = r.value; + return acc; + }, + {} + ), + }) + : message.content, ...(message.is_error ? { isError: message.is_error } : {}), ...(message.reader ? { reader: message.reader } : {}), role: message.role, @@ -67,7 +75,10 @@ export const transformESToConversations = ( : {}), })) ?? [], updatedAt: conversationSchema.updated_at, - replacements: conversationSchema.replacements as Replacement[], + replacements: conversationSchema.replacements?.reduce((acc: Record, r) => { + acc[r.uuid] = r.value; + return acc; + }, {}), namespace: conversationSchema.namespace, id: hit._id, }; @@ -75,3 +86,68 @@ export const transformESToConversations = ( return conversation; }); }; + +export const transformESToConversations = ( + response: EsConversationSchema[] +): ConversationResponse[] => { + return response.map((conversationSchema) => { + const replacements = conversationSchema.replacements?.reduce( + (acc: Record, r) => { + acc[r.uuid] = r.value; + return acc; + }, + {} + ) as Replacements; + const conversation: ConversationResponse = { + timestamp: conversationSchema['@timestamp'], + createdAt: conversationSchema.created_at, + users: + conversationSchema.users?.map((user) => ({ + id: user.id, + name: user.name, + })) ?? [], + title: conversationSchema.title, + category: conversationSchema.category, + summary: conversationSchema.summary, + ...(conversationSchema.api_config + ? { + apiConfig: { + connectorId: conversationSchema.api_config.connector_id, + defaultSystemPromptId: conversationSchema.api_config.default_system_prompt_id, + model: conversationSchema.api_config.model, + provider: conversationSchema.api_config.provider, + }, + } + : {}), + excludeFromLastConversationStorage: conversationSchema.exclude_from_last_conversation_storage, + isDefault: conversationSchema.is_default, + messages: + // eslint-disable-next-line @typescript-eslint/no-explicit-any + conversationSchema.messages?.map((message: Record) => ({ + timestamp: message['@timestamp'], + // always return anonymized data from the client + content: replaceOriginalValuesWithUuidValues({ + messageContent: message.content, + replacements, + }), + ...(message.is_error ? { isError: message.is_error } : {}), + ...(message.reader ? { reader: message.reader } : {}), + role: message.role, + ...(message.trace_data + ? { + traceData: { + traceId: message.trace_data?.trace_id, + transactionId: message.trace_data?.transaction_id, + }, + } + : {}), + })) ?? [], + updatedAt: conversationSchema.updated_at, + replacements, + namespace: conversationSchema.namespace, + id: conversationSchema.id, + }; + + return conversation; + }); +}; diff --git a/x-pack/plugins/elastic_assistant/server/ai_assistant_data_clients/conversations/types.ts b/x-pack/plugins/elastic_assistant/server/ai_assistant_data_clients/conversations/types.ts index 24ef8b2e8a941..ef69d82b5ef8c 100644 --- a/x-pack/plugins/elastic_assistant/server/ai_assistant_data_clients/conversations/types.ts +++ b/x-pack/plugins/elastic_assistant/server/ai_assistant_data_clients/conversations/types.ts @@ -11,10 +11,14 @@ import { MessageRole, Provider, Reader, - Replacement, } from '@kbn/elastic-assistant-common'; -export interface SearchEsConversationSchema { +export interface EsReplacementSchema { + value: string; + uuid: string; +} + +export interface EsConversationSchema { id: string; '@timestamp': string; created_at: string; @@ -45,7 +49,7 @@ export interface SearchEsConversationSchema { }; is_default?: boolean; exclude_from_last_conversation_storage?: boolean; - replacements?: Replacement[]; + replacements?: EsReplacementSchema[]; users?: Array<{ id?: string; name?: string; @@ -79,7 +83,7 @@ export interface CreateMessageSchema { }; is_default?: boolean; exclude_from_last_conversation_storage?: boolean; - replacements?: Replacement[]; + replacements?: EsReplacementSchema[]; users: Array<{ id?: string; name?: string; diff --git a/x-pack/plugins/elastic_assistant/server/ai_assistant_data_clients/conversations/update_conversation.test.ts b/x-pack/plugins/elastic_assistant/server/ai_assistant_data_clients/conversations/update_conversation.test.ts index 0d2679e839c71..ad6a7799115ac 100644 --- a/x-pack/plugins/elastic_assistant/server/ai_assistant_data_clients/conversations/update_conversation.test.ts +++ b/x-pack/plugins/elastic_assistant/server/ai_assistant_data_clients/conversations/update_conversation.test.ts @@ -27,7 +27,7 @@ export const getUpdateConversationOptionsMock = (): ConversationUpdateProps => ( }, excludeFromLastConversationStorage: false, messages: [], - replacements: [], + replacements: {}, }); const mockUser1 = { @@ -61,7 +61,7 @@ export const getConversationResponseMock = (): ConversationResponse => ({ timestamp: '2024-02-14T22:29:43.862Z', }, ], - replacements: [], + replacements: {}, createdAt: '2020-04-20T15:25:31.830Z', namespace: 'default', isDefault: false, diff --git a/x-pack/plugins/elastic_assistant/server/ai_assistant_data_clients/conversations/update_conversation.ts b/x-pack/plugins/elastic_assistant/server/ai_assistant_data_clients/conversations/update_conversation.ts index 8395c26f9916e..9a2fc08da23d9 100644 --- a/x-pack/plugins/elastic_assistant/server/ai_assistant_data_clients/conversations/update_conversation.ts +++ b/x-pack/plugins/elastic_assistant/server/ai_assistant_data_clients/conversations/update_conversation.ts @@ -8,7 +8,6 @@ import { ElasticsearchClient, Logger } from '@kbn/core/server'; import { ConversationResponse, - Replacement, Reader, ConversationUpdateProps, Provider, @@ -19,6 +18,7 @@ import { import { AuthenticatedUser } from '@kbn/security-plugin/common'; import { getConversation } from './get_conversation'; import { getUpdateScript } from './helpers'; +import { EsReplacementSchema } from './types'; export interface UpdateConversationSchema { id: UUID; @@ -43,7 +43,7 @@ export interface UpdateConversationSchema { }; summary?: ConversationSummary; exclude_from_last_conversation_storage?: boolean; - replacements?: Replacement[]; + replacements?: EsReplacementSchema[]; updated_at?: string; } @@ -122,7 +122,12 @@ export const transformToUpdateScheme = ( provider: apiConfig?.provider, }, exclude_from_last_conversation_storage: excludeFromLastConversationStorage, - replacements, + replacements: replacements + ? Object.keys(replacements).map((key) => ({ + uuid: key, + value: replacements[key], + })) + : undefined, messages: messages?.map((message) => ({ '@timestamp': message.timestamp, content: message.content, diff --git a/x-pack/plugins/elastic_assistant/server/ai_assistant_data_clients/find.test.ts b/x-pack/plugins/elastic_assistant/server/ai_assistant_data_clients/find.test.ts index bc4f8c574a8a9..d715a11e1c0de 100644 --- a/x-pack/plugins/elastic_assistant/server/ai_assistant_data_clients/find.test.ts +++ b/x-pack/plugins/elastic_assistant/server/ai_assistant_data_clients/find.test.ts @@ -11,7 +11,7 @@ import { estypes } from '@elastic/elasticsearch'; import { loggingSystemMock } from '@kbn/core-logging-server-mocks'; import { ConversationResponse } from '@kbn/elastic-assistant-common'; import { findDocuments } from './find'; -import { SearchEsConversationSchema } from './conversations/types'; +import { EsConversationSchema } from './conversations/types'; export const findDocumentsResponseMock = (): ConversationResponse => ({ createdAt: '2020-04-20T15:25:31.830Z', @@ -42,57 +42,56 @@ export const findDocumentsResponseMock = (): ConversationResponse => ({ replacements: undefined, }); -export const getSearchConversationMock = - (): estypes.SearchResponse => ({ - _scroll_id: '123', - _shards: { - failed: 0, - skipped: 0, - successful: 0, - total: 0, - }, - hits: { - hits: [ - { - _id: '1', - _index: '', - _score: 0, - _source: { - '@timestamp': '2020-04-20T15:25:31.830Z', - created_at: '2020-04-20T15:25:31.830Z', - title: 'title-1', - updated_at: '2020-04-20T15:25:31.830Z', - messages: [], - id: '1', - namespace: 'default', - is_default: true, - exclude_from_last_conversation_storage: false, - api_config: { - connector_id: 'c1', - default_system_prompt_id: 'prompt-1', - model: 'test', - provider: 'Azure OpenAI', - }, - summary: { - content: 'test', - }, - category: 'assistant', - users: [ - { - id: '1111', - name: 'elastic', - }, - ], - replacements: undefined, +export const getSearchConversationMock = (): estypes.SearchResponse => ({ + _scroll_id: '123', + _shards: { + failed: 0, + skipped: 0, + successful: 0, + total: 0, + }, + hits: { + hits: [ + { + _id: '1', + _index: '', + _score: 0, + _source: { + '@timestamp': '2020-04-20T15:25:31.830Z', + created_at: '2020-04-20T15:25:31.830Z', + title: 'title-1', + updated_at: '2020-04-20T15:25:31.830Z', + messages: [], + id: '1', + namespace: 'default', + is_default: true, + exclude_from_last_conversation_storage: false, + api_config: { + connector_id: 'c1', + default_system_prompt_id: 'prompt-1', + model: 'test', + provider: 'Azure OpenAI', + }, + summary: { + content: 'test', }, + category: 'assistant', + users: [ + { + id: '1111', + name: 'elastic', + }, + ], + replacements: undefined, }, - ], - max_score: 0, - total: 1, - }, - timed_out: false, - took: 10, - }); + }, + ], + max_score: 0, + total: 1, + }, + timed_out: false, + took: 10, +}); describe('findDocuments', () => { let loggerMock: Logger; diff --git a/x-pack/plugins/elastic_assistant/server/lib/data_stream/documents_data_writer.ts b/x-pack/plugins/elastic_assistant/server/lib/data_stream/documents_data_writer.ts index b0488fe73a0be..76248304f0a8e 100644 --- a/x-pack/plugins/elastic_assistant/server/lib/data_stream/documents_data_writer.ts +++ b/x-pack/plugins/elastic_assistant/server/lib/data_stream/documents_data_writer.ts @@ -28,7 +28,7 @@ interface WriterBulkResponse { errors: BulkOperationError[]; docs_created: string[]; docs_deleted: string[]; - docs_updated: string[]; + docs_updated: unknown[]; took: number; } @@ -78,13 +78,13 @@ export class DocumentsDataWriter implements DocumentsDataWriter { errors: errors ? this.formatErrorsResponse(items) : [], docs_created: items .filter((item) => item.create?.status === 201 || item.create?.status === 200) - .map((item) => item.create?._id ?? ''), + .map((item) => item.create?._id), docs_deleted: items .filter((item) => item.delete?.status === 201 || item.delete?.status === 200) - .map((item) => item.delete?._id ?? ''), + .map((item) => item.delete?._id), docs_updated: items .filter((item) => item.update?.status === 201 || item.update?.status === 200) - .map((item) => item.update?._id ?? ''), + .map((item) => item.update?.get?._source), took, } as WriterBulkResponse; } catch (e) { @@ -170,6 +170,7 @@ export class DocumentsDataWriter implements DocumentsDataWriter { update: { _id: document.id, _index: responseToUpdate?.hits.hits.find((c) => c._id === document.id)?._index, + _source: true, }, }, { diff --git a/x-pack/plugins/elastic_assistant/server/lib/executor.test.ts b/x-pack/plugins/elastic_assistant/server/lib/executor.test.ts index c500144f6192f..8cf25b5e5ca0f 100644 --- a/x-pack/plugins/elastic_assistant/server/lib/executor.test.ts +++ b/x-pack/plugins/elastic_assistant/server/lib/executor.test.ts @@ -31,7 +31,7 @@ const testProps: Omit = { subAction: 'invokeAI', subActionParams: { messages: [{ content: 'hello', role: 'user' }] }, }, - llmType: '.bedrock', + actionTypeId: '.bedrock', request, connectorId, onLlmResponse, diff --git a/x-pack/plugins/elastic_assistant/server/lib/executor.ts b/x-pack/plugins/elastic_assistant/server/lib/executor.ts index 0ea3145ca8019..9d9eca872662c 100644 --- a/x-pack/plugins/elastic_assistant/server/lib/executor.ts +++ b/x-pack/plugins/elastic_assistant/server/lib/executor.ts @@ -19,7 +19,7 @@ export interface Props { connectorId: string; params: InvokeAIActionsParams; request: KibanaRequest; - llmType: string; + actionTypeId: string; logger: Logger; } interface StaticResponse { @@ -47,7 +47,7 @@ export const executeAction = async ({ actions, params, connectorId, - llmType, + actionTypeId, request, logger, }: Props): Promise => { @@ -81,7 +81,12 @@ export const executeAction = async ({ } // do not await, blocks stream for UI - handleStreamStorage({ responseStream: readable, llmType, onMessageSent: onLlmResponse, logger }); + handleStreamStorage({ + responseStream: readable, + actionTypeId, + onMessageSent: onLlmResponse, + logger, + }); return readable.pipe(new PassThrough()); }; diff --git a/x-pack/plugins/elastic_assistant/server/lib/langchain/execute_custom_llm_chain/index.test.ts b/x-pack/plugins/elastic_assistant/server/lib/langchain/execute_custom_llm_chain/index.test.ts index b2f0d87fcc291..3e69aee574413 100644 --- a/x-pack/plugins/elastic_assistant/server/lib/langchain/execute_custom_llm_chain/index.test.ts +++ b/x-pack/plugins/elastic_assistant/server/lib/langchain/execute_custom_llm_chain/index.test.ts @@ -69,7 +69,7 @@ const defaultProps = { request: mockRequest, kbResource: ESQL_RESOURCE, telemetry: mockTelemetry, - replacements: [], + replacements: {}, }; describe('callAgentExecutor', () => { beforeEach(() => { @@ -123,7 +123,7 @@ describe('callAgentExecutor', () => { connector_id: 'mock-connector-id', data: mockActionResponse.message, status: 'ok', - replacements: [], + replacements: {}, }); }); }); diff --git a/x-pack/plugins/elastic_assistant/server/lib/langchain/executors/types.ts b/x-pack/plugins/elastic_assistant/server/lib/langchain/executors/types.ts index 4454971e7cbe8..ca2392db8a296 100644 --- a/x-pack/plugins/elastic_assistant/server/lib/langchain/executors/types.ts +++ b/x-pack/plugins/elastic_assistant/server/lib/langchain/executors/types.ts @@ -12,7 +12,7 @@ import { Logger } from '@kbn/logging'; import { KibanaRequest } from '@kbn/core-http-server'; import type { LangChainTracer } from 'langchain/callbacks'; import type { AnalyticsServiceSetup } from '@kbn/core-analytics-server'; -import { ExecuteConnectorRequestBody, Replacement } from '@kbn/elastic-assistant-common'; +import { ExecuteConnectorRequestBody, Replacements } from '@kbn/elastic-assistant-common'; import { ResponseBody } from '../types'; import type { AssistantTool } from '../../../types'; @@ -29,8 +29,8 @@ export interface AgentExecutorParams { langChainMessages: BaseMessage[]; llmType?: string; logger: Logger; - onNewReplacements?: (newReplacements: Replacement[]) => void; - replacements: Replacement[]; + onNewReplacements?: (newReplacements: Replacements) => void; + replacements: Replacements; request: KibanaRequest; size?: number; elserId?: string; diff --git a/x-pack/plugins/elastic_assistant/server/lib/langchain/helpers.test.ts b/x-pack/plugins/elastic_assistant/server/lib/langchain/helpers.test.ts index ee0c77c3b6444..1584084eee0fb 100644 --- a/x-pack/plugins/elastic_assistant/server/lib/langchain/helpers.test.ts +++ b/x-pack/plugins/elastic_assistant/server/lib/langchain/helpers.test.ts @@ -119,7 +119,7 @@ describe('helpers', () => { body: { allow: ['a', 'b', 'c'], allowReplacement: ['b', 'c'], - replacements: [{ uuid: 'key', value: 'value' }], + replacements: { key: 'value' }, }, } as unknown as KibanaRequest; @@ -133,7 +133,7 @@ describe('helpers', () => { body: { // allow is undefined allowReplacement: ['b', 'c'], - replacements: [{ uuid: 'key', value: 'value' }], + replacements: { key: 'value' }, }, } as unknown as KibanaRequest; @@ -147,7 +147,7 @@ describe('helpers', () => { body: { allow: [], // <-- empty allowReplacement: ['b', 'c'], - replacements: [{ uuid: 'key', value: 'value' }], + replacements: { key: 'value' }, }, } as unknown as KibanaRequest; @@ -161,7 +161,7 @@ describe('helpers', () => { body: { allow: ['a', 9876, 'c'], // <-- non-string value allowReplacement: ['b', 'c'], - replacements: [{ uuid: 'key', value: 'value' }], + replacements: { key: 'value' }, }, } as unknown as KibanaRequest; @@ -175,7 +175,7 @@ describe('helpers', () => { body: { allow: ['a', 'b', 'c'], allowReplacement: [], - replacements: [{ uuid: 'key', value: 'value' }], + replacements: { key: 'value' }, }, } as unknown as KibanaRequest; @@ -189,7 +189,7 @@ describe('helpers', () => { body: { allow: ['a', 'b', 'c'], allowReplacement: ['b', 12345], // <-- non-string value - replacements: [{ uuid: 'key', value: 'value' }], + replacements: { key: 'value' }, }, } as unknown as KibanaRequest; @@ -203,7 +203,7 @@ describe('helpers', () => { body: { allow: ['a', 'b', 'c'], allowReplacement: ['b', 'c'], - replacements: [], + replacements: {}, }, } as unknown as KibanaRequest; @@ -217,7 +217,7 @@ describe('helpers', () => { body: { allow: ['a', 'b', 'c'], allowReplacement: ['b', 'c'], - replacements: [{ uuid: 'key', value: 76543 }], // <-- non-string value + replacements: { key: 76543 }, // <-- non-string value }, } as unknown as KibanaRequest; diff --git a/x-pack/plugins/elastic_assistant/server/lib/langchain/helpers.ts b/x-pack/plugins/elastic_assistant/server/lib/langchain/helpers.ts index 880aa2705a0c3..1c56b2bb4c9d4 100644 --- a/x-pack/plugins/elastic_assistant/server/lib/langchain/helpers.ts +++ b/x-pack/plugins/elastic_assistant/server/lib/langchain/helpers.ts @@ -50,8 +50,8 @@ export const requestHasRequiredAnonymizationParams = ( const replacementsIsValid = typeof replacements === 'object' && - replacements.every( - (replacement) => typeof replacement === 'object' && typeof replacement.value === 'string' + Object.keys(replacements).every( + (key) => typeof key === 'string' && typeof replacements[key] === 'string' ); return allowIsValid && allowReplacementIsValid && replacementsIsValid; diff --git a/x-pack/plugins/elastic_assistant/server/lib/langchain/llm/actions_client_llm.ts b/x-pack/plugins/elastic_assistant/server/lib/langchain/llm/actions_client_llm.ts index 8d8621cc29ed1..4584b2f1be06a 100644 --- a/x-pack/plugins/elastic_assistant/server/lib/langchain/llm/actions_client_llm.ts +++ b/x-pack/plugins/elastic_assistant/server/lib/langchain/llm/actions_client_llm.ts @@ -81,7 +81,7 @@ export class ActionsClientLlm extends LLM { subActionParams: { model: this.#request.body.model, messages: [assistantMessage], // the assistant message - ...(this.llmType === '.gen-ai' + ...(this.llmType === 'openai' ? { n: 1, stop: null, temperature: 0.2 } : { temperature: 0, stopSequences: [] }), }, diff --git a/x-pack/plugins/elastic_assistant/server/lib/langchain/types.ts b/x-pack/plugins/elastic_assistant/server/lib/langchain/types.ts index beb58cb3985a5..551eabcfb0190 100644 --- a/x-pack/plugins/elastic_assistant/server/lib/langchain/types.ts +++ b/x-pack/plugins/elastic_assistant/server/lib/langchain/types.ts @@ -5,12 +5,12 @@ * 2.0. */ -import { Replacement } from '@kbn/elastic-assistant-common'; +import { Replacements } from '@kbn/elastic-assistant-common'; export interface ResponseBody { data: string; connector_id: string; - replacements?: Replacement[]; + replacements?: Replacements; status: string; trace_data?: { transaction_id: string; diff --git a/x-pack/plugins/elastic_assistant/server/lib/parse_stream.test.ts b/x-pack/plugins/elastic_assistant/server/lib/parse_stream.test.ts index 8842ef181ce7d..a60ff40caf26f 100644 --- a/x-pack/plugins/elastic_assistant/server/lib/parse_stream.test.ts +++ b/x-pack/plugins/elastic_assistant/server/lib/parse_stream.test.ts @@ -47,7 +47,7 @@ describe('handleStreamStorage', () => { }; let defaultProps = { responseStream: jest.fn() as unknown as Readable, - llmType: '.gen-ai', + actionTypeId: '.gen-ai', onMessageSent, logger: mockLogger, }; @@ -58,7 +58,7 @@ describe('handleStreamStorage', () => { stream.write(`data: ${JSON.stringify(chunk)}`); defaultProps = { responseStream: stream.transform, - llmType: '.gen-ai', + actionTypeId: '.gen-ai', onMessageSent, logger: mockLogger, }; @@ -85,7 +85,7 @@ describe('handleStreamStorage', () => { stream.write(encodeBedrockResponse('Simple.')); defaultProps = { responseStream: stream.transform, - llmType: '.gen-ai', + actionTypeId: 'openai', onMessageSent, logger: mockLogger, }; @@ -93,11 +93,11 @@ describe('handleStreamStorage', () => { it('saves the final string successful streaming event', async () => { stream.complete(); - await handleStreamStorage({ ...defaultProps, llmType: '.bedrock' }); + await handleStreamStorage({ ...defaultProps, actionTypeId: '.bedrock' }); expect(onMessageSent).toHaveBeenCalledWith('Simple.'); }); it('saves the error message on a failed streaming event', async () => { - const tokenPromise = handleStreamStorage({ ...defaultProps, llmType: '.bedrock' }); + const tokenPromise = handleStreamStorage({ ...defaultProps, actionTypeId: '.bedrock' }); stream.fail(); await expect(tokenPromise).resolves.not.toThrow(); diff --git a/x-pack/plugins/elastic_assistant/server/lib/parse_stream.ts b/x-pack/plugins/elastic_assistant/server/lib/parse_stream.ts index ccf484834431f..23fa2cc91f542 100644 --- a/x-pack/plugins/elastic_assistant/server/lib/parse_stream.ts +++ b/x-pack/plugins/elastic_assistant/server/lib/parse_stream.ts @@ -14,17 +14,17 @@ type StreamParser = (responseStream: Readable, logger: Logger) => Promise void; logger: Logger; }): Promise => { try { - const parser = llmType === '.bedrock' ? parseBedrockStream : parseOpenAIStream; + const parser = actionTypeId === '.bedrock' ? parseBedrockStream : parseOpenAIStream; // TODO @steph add abort signal const parsedResponse = await parser(responseStream, logger); if (onMessageSent) { diff --git a/x-pack/plugins/elastic_assistant/server/routes/evaluate/post_evaluate.ts b/x-pack/plugins/elastic_assistant/server/routes/evaluate/post_evaluate.ts index 5a5dd8a8768a3..31bda1e64e790 100644 --- a/x-pack/plugins/elastic_assistant/server/routes/evaluate/post_evaluate.ts +++ b/x-pack/plugins/elastic_assistant/server/routes/evaluate/post_evaluate.ts @@ -149,7 +149,7 @@ export const postEvaluateRoute = ( allow: [], allowReplacement: [], subAction: 'invokeAI', - replacements: [], + replacements: {}, size: DEFAULT_SIZE, isEnabledKnowledgeBase: true, isEnabledRAGAlerts: true, @@ -195,7 +195,7 @@ export const postEvaluateRoute = ( ], tracers: getLangSmithTracer(detailedRunName, exampleId, logger), }, - replacements: [], + replacements: {}, }), metadata: { connectorName, diff --git a/x-pack/plugins/elastic_assistant/server/routes/evaluate/utils.ts b/x-pack/plugins/elastic_assistant/server/routes/evaluate/utils.ts index 11f8cb9c2f692..e4d2dcbf86826 100644 --- a/x-pack/plugins/elastic_assistant/server/routes/evaluate/utils.ts +++ b/x-pack/plugins/elastic_assistant/server/routes/evaluate/utils.ts @@ -6,7 +6,6 @@ */ import { Client } from 'langsmith'; -import { OpenAiProviderType } from '@kbn/stack-connectors-plugin/common/openai/constants'; import type { ActionResult } from '@kbn/actions-plugin/server'; import type { Logger } from '@kbn/core/server'; import type { Run } from 'langsmith/schemas'; @@ -14,6 +13,10 @@ import { ToolingLog } from '@kbn/tooling-log'; import { LangChainTracer } from 'langchain/callbacks'; import { Dataset } from '@kbn/elastic-assistant-common'; +export const llmTypeDictionary: Record = { + '.gen-ai': 'openai', + '.bedrock': 'bedrock', +}; /** * Returns the LangChain `llmType` for the given connectorId/connectors * @@ -23,11 +26,11 @@ import { Dataset } from '@kbn/elastic-assistant-common'; export const getLlmType = (connectorId: string, connectors: ActionResult[]): string | undefined => { const connector = connectors.find((c) => c.id === connectorId); // Note: Pre-configured connectors do not have an accessible `apiProvider` field - const apiProvider = (connector?.config?.apiProvider as string) ?? undefined; + const actionTypeId = connector?.actionTypeId; - if (apiProvider === OpenAiProviderType.OpenAi) { + if (actionTypeId) { // See: https://github.com/langchain-ai/langchainjs/blob/fb699647a310c620140842776f4a7432c53e02fa/langchain/src/agents/openai/index.ts#L185 - return 'openai'; + return llmTypeDictionary[actionTypeId]; } // TODO: Add support for Amazon Bedrock Connector once merged // Note: Doesn't appear to be a difference between Azure and OpenAI LLM types, so TBD for functions agent on Azure diff --git a/x-pack/plugins/elastic_assistant/server/routes/post_actions_connector_execute.ts b/x-pack/plugins/elastic_assistant/server/routes/post_actions_connector_execute.ts index a2831a94fa97e..d44c93abd2dd0 100644 --- a/x-pack/plugins/elastic_assistant/server/routes/post_actions_connector_execute.ts +++ b/x-pack/plugins/elastic_assistant/server/routes/post_actions_connector_execute.ts @@ -13,7 +13,7 @@ import { ELASTIC_AI_ASSISTANT_INTERNAL_API_VERSION, ExecuteConnectorRequestBody, Message, - Replacement, + Replacements, replaceAnonymizedValuesWithOriginalValues, } from '@kbn/elastic-assistant-common'; import { buildRouteValidationWithZod } from '@kbn/elastic-assistant-common/impl/schemas/common'; @@ -33,6 +33,7 @@ import { getMessageFromRawResponse, getPluginNameFromRequest, } from './helpers'; +import { getLlmType } from './evaluate/utils'; export const postActionsConnectorExecuteRoute = ( router: IRouter, @@ -76,25 +77,9 @@ export const postActionsConnectorExecuteRoute = ( } const dataClient = await assistantContext.getAIAssistantConversationsDataClient(); - let latestReplacements: Replacement[] = request.body.replacements; - const onNewReplacements = (newReplacements: Replacement[]) => { - const latestReplacementsDict = latestReplacements.reduce( - (acc: Record, r) => { - acc[r.value] = r.uuid; - return acc; - }, - {} - ); - const newReplacementsDict = newReplacements.reduce((acc: Record, r) => { - acc[r.value] = r.uuid; - return acc; - }, {}); - - const updatedReplacements = { ...latestReplacementsDict, ...newReplacementsDict }; - latestReplacements = Object.keys(updatedReplacements).map((key) => ({ - value: key, - uuid: updatedReplacements[key], - })); + let latestReplacements: Replacements = request.body.replacements; + const onNewReplacements = (newReplacements: Replacements) => { + latestReplacements = { ...latestReplacements, ...newReplacements }; }; let onLlmResponse; @@ -179,7 +164,7 @@ export const postActionsConnectorExecuteRoute = ( ], }); } - if (latestReplacements.length > 0) { + if (Object.keys(latestReplacements).length > 0) { await dataClient?.updateConversation({ conversationUpdateProps: { id: conversationId, @@ -208,7 +193,7 @@ export const postActionsConnectorExecuteRoute = ( actions, request, connectorId, - llmType: connectors[0]?.actionTypeId, + actionTypeId: connectors[0]?.actionTypeId, params: { subAction: request.body.subAction, subActionParams: { @@ -255,6 +240,7 @@ export const postActionsConnectorExecuteRoute = ( const elserId = await getElser(request, (await context.core).savedObjects.getClient()); + const llmType = getLlmType(connectorId, connectors); const langChainResponseBody = await callAgentExecutor({ alertsIndexPattern: request.body.alertsIndexPattern, allow: request.body.allow, @@ -265,7 +251,7 @@ export const postActionsConnectorExecuteRoute = ( connectorId, elserId, esClient, - llmType: connectors[0]?.actionTypeId, + llmType, kbResource: ESQL_RESOURCE, langChainMessages, logger, diff --git a/x-pack/plugins/elastic_assistant/server/routes/user_conversations/bulk_actions_route.test.ts b/x-pack/plugins/elastic_assistant/server/routes/user_conversations/bulk_actions_route.test.ts index 7572cd84a49db..09247b1c47cb4 100644 --- a/x-pack/plugins/elastic_assistant/server/routes/user_conversations/bulk_actions_route.test.ts +++ b/x-pack/plugins/elastic_assistant/server/routes/user_conversations/bulk_actions_route.test.ts @@ -74,14 +74,14 @@ describe('Perform bulk action route', () => { expect(response.status).toEqual(200); expect(response.body).toEqual({ success: true, - conversations_count: 2, + conversations_count: 3, attributes: { results: someBulkActionResults(), summary: { failed: 0, skipped: 0, - succeeded: 2, - total: 2, + succeeded: 3, + total: 3, }, }, }); @@ -94,7 +94,7 @@ describe('Perform bulk action route', () => { (await clients.elasticAssistant.getAIAssistantConversationsDataClient.getWriter()) .bulk as jest.Mock ).mockResolvedValue({ - docs_created: [mockConversation, mockConversation], + docs_created: ['49403909-ca9b-49ba-9d7a-7e5320e68d04'], docs_updated: [], docs_deleted: [], errors: [ @@ -130,9 +130,9 @@ describe('Perform bulk action route', () => { attributes: { summary: { failed: 3, - succeeded: 2, + succeeded: 1, skipped: 0, - total: 5, + total: 4, }, errors: [ { diff --git a/x-pack/plugins/elastic_assistant/server/routes/user_conversations/bulk_actions_route.ts b/x-pack/plugins/elastic_assistant/server/routes/user_conversations/bulk_actions_route.ts index b7cb5e5c2d046..6f8c924d040ef 100644 --- a/x-pack/plugins/elastic_assistant/server/routes/user_conversations/bulk_actions_route.ts +++ b/x-pack/plugins/elastic_assistant/server/routes/user_conversations/bulk_actions_route.ts @@ -26,12 +26,15 @@ import { ElasticAssistantPluginRouter } from '../../types'; import { buildResponse } from '../utils'; import { getUpdateScript } from '../../ai_assistant_data_clients/conversations/helpers'; import { transformToCreateScheme } from '../../ai_assistant_data_clients/conversations/create_conversation'; -import { transformESToConversations } from '../../ai_assistant_data_clients/conversations/transforms'; +import { + transformESToConversations, + transformESSearchToConversations, +} from '../../ai_assistant_data_clients/conversations/transforms'; import { UpdateConversationSchema, transformToUpdateScheme, } from '../../ai_assistant_data_clients/conversations/update_conversation'; -import { SearchEsConversationSchema } from '../../ai_assistant_data_clients/conversations/types'; +import { EsConversationSchema } from '../../ai_assistant_data_clients/conversations/types'; export interface BulkOperationError { message: string; @@ -163,7 +166,7 @@ export const bulkActionConversationsRoute = ( } if (body.create && body.create.length > 0) { - const result = await dataClient?.findDocuments({ + const result = await dataClient?.findDocuments({ perPage: 100, page: 1, filter: `users:{ id: "${authenticatedUser?.profile_uid}" } AND (${body.create @@ -174,7 +177,7 @@ export const bulkActionConversationsRoute = ( if (result?.data != null && result.total > 0) { return assistantResponse.error({ statusCode: 409, - body: `conversations titles: "${transformESToConversations(result.data) + body: `conversations titles: "${transformESSearchToConversations(result.data) .map((c) => c.title) .join(',')}" already exists`, }); @@ -199,23 +202,20 @@ export const bulkActionConversationsRoute = ( getUpdateScript: (document: UpdateConversationSchema) => getUpdateScript({ conversation: document, isPatch: true }), }); - - const created = await dataClient?.findDocuments({ - page: 1, - perPage: 1000, - filter: docsCreated.map((c) => `id:${c}`).join(' OR '), - fields: ['id'], - }); - const updated = await dataClient?.findDocuments({ - page: 1, - perPage: 1000, - filter: docsUpdated.map((c) => `id:${c}`).join(' OR '), - fields: ['id'], - }); + const created = + docsCreated.length > 0 + ? await dataClient?.findDocuments({ + page: 1, + perPage: 100, + filter: docsCreated.map((c) => `_id:${c}`).join(' OR '), + }) + : undefined; return buildBulkResponse(response, { - updated: updated?.data ? transformESToConversations(updated?.data) : [], - created: created?.data ? transformESToConversations(created?.data) : [], + updated: docsUpdated + ? transformESToConversations(docsUpdated as EsConversationSchema[]) + : [], + created: created?.data ? transformESSearchToConversations(created?.data) : [], deleted: docsDeleted ?? [], errors, }); diff --git a/x-pack/plugins/elastic_assistant/server/routes/user_conversations/find_route.ts b/x-pack/plugins/elastic_assistant/server/routes/user_conversations/find_route.ts index 306d1d90a8e5a..e26f24a3b1fff 100644 --- a/x-pack/plugins/elastic_assistant/server/routes/user_conversations/find_route.ts +++ b/x-pack/plugins/elastic_assistant/server/routes/user_conversations/find_route.ts @@ -19,8 +19,8 @@ import { import { buildRouteValidationWithZod } from '@kbn/elastic-assistant-common/impl/schemas/common'; import { ElasticAssistantPluginRouter } from '../../types'; import { buildResponse } from '../utils'; -import { SearchEsConversationSchema } from '../../ai_assistant_data_clients/conversations/types'; -import { transformESToConversations } from '../../ai_assistant_data_clients/conversations/transforms'; +import { EsConversationSchema } from '../../ai_assistant_data_clients/conversations/types'; +import { transformESSearchToConversations } from '../../ai_assistant_data_clients/conversations/transforms'; export const findUserConversationsRoute = (router: ElasticAssistantPluginRouter) => { router.versioned @@ -49,7 +49,7 @@ export const findUserConversationsRoute = (router: ElasticAssistantPluginRouter) const currentUser = ctx.elasticAssistant.getCurrentUser(); const additionalFilter = query.filter ? ` AND ${query.filter}` : ''; - const result = await dataClient?.findDocuments({ + const result = await dataClient?.findDocuments({ perPage: query.per_page, page: query.page, sortField: query.sort_field, @@ -64,7 +64,7 @@ export const findUserConversationsRoute = (router: ElasticAssistantPluginRouter) perPage: result.perPage, page: result.page, total: result.total, - data: transformESToConversations(result.data), + data: transformESSearchToConversations(result.data), }, }); } diff --git a/x-pack/plugins/elastic_assistant/server/types.ts b/x-pack/plugins/elastic_assistant/server/types.ts index 917b3a06c50cb..0c65b2f479e46 100755 --- a/x-pack/plugins/elastic_assistant/server/types.ts +++ b/x-pack/plugins/elastic_assistant/server/types.ts @@ -29,7 +29,7 @@ import { ElasticsearchClient } from '@kbn/core/server'; import { AssistantFeatures, ExecuteConnectorRequestBody, - Replacement, + Replacements, } from '@kbn/elastic-assistant-common'; import { AIAssistantConversationsDataClient } from './ai_assistant_data_clients/conversations'; import type { GetRegisteredFeatures, GetRegisteredTools } from './services/app_context'; @@ -204,8 +204,8 @@ export interface AssistantToolParams { chain: RetrievalQAChain; esClient: ElasticsearchClient; modelExists: boolean; - onNewReplacements?: (newReplacements: Replacement[]) => void; - replacements?: Replacement[]; + onNewReplacements?: (newReplacements: Replacements) => void; + replacements?: Replacements; request: KibanaRequest; size?: number; } diff --git a/x-pack/plugins/elastic_assistant/tsconfig.json b/x-pack/plugins/elastic_assistant/tsconfig.json index 4722dfb25de62..bb68bb37baa68 100644 --- a/x-pack/plugins/elastic_assistant/tsconfig.json +++ b/x-pack/plugins/elastic_assistant/tsconfig.json @@ -28,7 +28,6 @@ "@kbn/tooling-log", "@kbn/core-elasticsearch-server", "@kbn/logging", - "@kbn/stack-connectors-plugin", "@kbn/ml-plugin", "@kbn/apm-utils", "@kbn/core-analytics-server", diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/connector_detail/connector_configuration.tsx b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/connector_detail/connector_configuration.tsx index cb6a0f16eea92..17f23d63d2caa 100644 --- a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/connector_detail/connector_configuration.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/connector_detail/connector_configuration.tsx @@ -101,7 +101,8 @@ export const ConnectorConfiguration: React.FC = () => { i18n.translate( 'xpack.enterpriseSearch.content.connectorDetail.configuration.apiKey.noApiKeyLabel', { - defaultMessage: 'Please set an index name before generating an API key', + defaultMessage: + 'Before you can generate an API key, you need to attach an index. Scroll to the bottom of this page for instructions.', } ) ), @@ -117,41 +118,21 @@ export const ConnectorConfiguration: React.FC = () => { { children: ( <> - - - {i18n.translate( - 'xpack.enterpriseSearch.content.connector_detail.configurationConnector.connectorPackage.clientExamplesLink', - { defaultMessage: 'connector client examples' } - )} - - ), - }} - /> - {i18n.translate( 'xpack.enterpriseSearch.content.connector_detail.configurationConnector.connectorPackage.configurationFileLink', - { defaultMessage: 'configuration file' } + { defaultMessage: 'example config file' } )} ), @@ -171,13 +152,24 @@ export const ConnectorConfiguration: React.FC = () => { - {i18n.translate( - 'xpack.enterpriseSearch.content.connector_detail.configurationConnector.connectorPackage.connectorDeployedText', - { - defaultMessage: - 'Once configured, deploy the connector on your infrastructure.', - } - )} + + {i18n.translate( + 'xpack.enterpriseSearch.content.connector_detail.configurationConnector.connectorPackage.deploymentModeLink', + { defaultMessage: 'documentation' } + )} + + ), + }} + /> ), @@ -188,7 +180,7 @@ export const ConnectorConfiguration: React.FC = () => { title: i18n.translate( 'xpack.enterpriseSearch.content.connector_detail.configurationConnector.steps.deployConnector.title', { - defaultMessage: 'Deploy connector', + defaultMessage: 'Set up and deploy connector', } ), titleSize: 'xs', @@ -264,7 +256,7 @@ export const ConnectorConfiguration: React.FC = () => { title: i18n.translate( 'xpack.enterpriseSearch.content.connector_detail.configurationConnector.steps.enhance.title', { - defaultMessage: 'Enhance your connector client', + defaultMessage: 'Configure your connector', } ), titleSize: 'xs', diff --git a/x-pack/plugins/enterprise_search/public/applications/shared/doc_links/doc_links.ts b/x-pack/plugins/enterprise_search/public/applications/shared/doc_links/doc_links.ts index 3706a73af2627..f6cddf36057ae 100644 --- a/x-pack/plugins/enterprise_search/public/applications/shared/doc_links/doc_links.ts +++ b/x-pack/plugins/enterprise_search/public/applications/shared/doc_links/doc_links.ts @@ -65,6 +65,7 @@ class DocLinks { public clientsRustOverview: string; public cloudIndexManagement: string; public connectors: string; + public connectorsClientDeploy: string; public connectorsMappings: string; public connectorsAzureBlobStorage: string; public connectorsBox: string; @@ -240,6 +241,7 @@ class DocLinks { this.clientsRustOverview = ''; this.cloudIndexManagement = ''; this.connectors = ''; + this.connectorsClientDeploy = ''; this.connectorsMappings = ''; this.connectorsAzureBlobStorage = ''; this.connectorsBox = ''; @@ -418,6 +420,7 @@ class DocLinks { this.connectors = docLinks.links.enterpriseSearch.connectors; this.connectorsMappings = docLinks.links.enterpriseSearch.connectorsMappings; this.connectorsAzureBlobStorage = docLinks.links.enterpriseSearch.connectorsAzureBlobStorage; + this.connectorsClientDeploy = docLinks.links.enterpriseSearch.connectorsClientDeploy; this.connectorsBox = docLinks.links.enterpriseSearch.connectorsBox; this.connectorsConfluence = docLinks.links.enterpriseSearch.connectorsConfluence; this.connectorsContentExtraction = docLinks.links.enterpriseSearch.connectorsContentExtraction; diff --git a/x-pack/plugins/fleet/common/services/check_fleet_server_versions.test.ts b/x-pack/plugins/fleet/common/services/check_fleet_server_versions.test.ts index 03d00bb969958..3ed6f2fb81ae0 100644 --- a/x-pack/plugins/fleet/common/services/check_fleet_server_versions.test.ts +++ b/x-pack/plugins/fleet/common/services/check_fleet_server_versions.test.ts @@ -5,7 +5,11 @@ * 2.0. */ -import { checkFleetServerVersion } from './check_fleet_server_versions'; +import { + checkFleetServerVersion, + getFleetServerVersionMessage, + isAgentVersionLessThanFleetServer, +} from './check_fleet_server_versions'; describe('checkFleetServerVersion', () => { it('should not throw if no force is specified and patch is newer', () => { @@ -36,4 +40,57 @@ describe('checkFleetServerVersion', () => { 'Cannot force upgrade to version 8.5.1 because it does not satisfy the major and minor of the latest fleet server version 8.4.0.' ); }); + + it('should not throw in serverless if there is not in fleetServers', () => { + const fleetServers = [] as any; + expect(() => checkFleetServerVersion('8.5.1', fleetServers, true)).not.toThrow(); + }); +}); + +describe('isAgentVersionLessThanFleetServer', () => { + it('should return true if there is no fleet server (serverless)', () => { + expect(isAgentVersionLessThanFleetServer('8.12.0', [])).toBe(true); + }); + + it('should return true if version is less than fleet server ', () => { + const fleetServers = [ + { local_metadata: { elastic: { agent: { version: '8.3.0' } } } }, + { local_metadata: { elastic: { agent: { version: '8.4.0' } } } }, + ] as any; + expect(isAgentVersionLessThanFleetServer('8.3.0', fleetServers)).toBe(true); + }); + + it('should return false if version is more than fleet server ', () => { + const fleetServers = [ + { local_metadata: { elastic: { agent: { version: '8.3.0' } } } }, + { local_metadata: { elastic: { agent: { version: '8.4.0' } } } }, + ] as any; + expect(isAgentVersionLessThanFleetServer('8.5.0', fleetServers)).toBe(false); + }); + + it('should not throw if version is not a semver', () => { + const fleetServers = [ + { local_metadata: { elastic: { agent: { version: '8.13.0' } } } }, + { local_metadata: { elastic: { agent: { version: '8.14.0' } } } }, + ] as any; + const version = '8.14'; + + const result = isAgentVersionLessThanFleetServer(version, fleetServers); + + expect(result).toEqual(false); + }); +}); + +describe('getFleetServerVersionMessage', () => { + it('should not throw if version is not a semver', () => { + const fleetServers = [ + { local_metadata: { elastic: { agent: { version: '8.13.0' } } } }, + { local_metadata: { elastic: { agent: { version: '8.14.0' } } } }, + ] as any; + const version = '8.14'; + + const result = getFleetServerVersionMessage(version, fleetServers); + + expect(result).toEqual('Invalid Version: 8.14'); + }); }); diff --git a/x-pack/plugins/fleet/common/services/check_fleet_server_versions.ts b/x-pack/plugins/fleet/common/services/check_fleet_server_versions.ts index f7e2751671733..000332c66aa89 100644 --- a/x-pack/plugins/fleet/common/services/check_fleet_server_versions.ts +++ b/x-pack/plugins/fleet/common/services/check_fleet_server_versions.ts @@ -41,24 +41,27 @@ export const getFleetServerVersionMessage = ( if (!maxFleetServerVersion || !versionToUpgradeNumber) { return; } - - if ( - !force && - semverGt(versionToUpgradeNumber, maxFleetServerVersion) && - !differsOnlyInPatch(versionToUpgradeNumber, maxFleetServerVersion) - ) { - return `Cannot upgrade to version ${versionToUpgradeNumber} because it is higher than the latest fleet server version ${maxFleetServerVersion}.`; - } - - const fleetServerMajorGt = - semverMajor(maxFleetServerVersion) > semverMajor(versionToUpgradeNumber); - const fleetServerMajorEqMinorGte = - semverMajor(maxFleetServerVersion) === semverMajor(versionToUpgradeNumber) && - semverMinor(maxFleetServerVersion) >= semverMinor(versionToUpgradeNumber); - - // When force is enabled, only the major and minor versions are checked - if (force && !(fleetServerMajorGt || fleetServerMajorEqMinorGte)) { - return `Cannot force upgrade to version ${versionToUpgradeNumber} because it does not satisfy the major and minor of the latest fleet server version ${maxFleetServerVersion}.`; + try { + if ( + !force && + semverGt(versionToUpgradeNumber, maxFleetServerVersion) && + !differsOnlyInPatch(versionToUpgradeNumber, maxFleetServerVersion) + ) { + return `Cannot upgrade to version ${versionToUpgradeNumber} because it is higher than the latest fleet server version ${maxFleetServerVersion}.`; + } + + const fleetServerMajorGt = + semverMajor(maxFleetServerVersion) > semverMajor(versionToUpgradeNumber); + const fleetServerMajorEqMinorGte = + semverMajor(maxFleetServerVersion) === semverMajor(versionToUpgradeNumber) && + semverMinor(maxFleetServerVersion) >= semverMinor(versionToUpgradeNumber); + + // When force is enabled, only the major and minor versions are checked + if (force && !(fleetServerMajorGt || fleetServerMajorEqMinorGte)) { + return `Cannot force upgrade to version ${versionToUpgradeNumber} because it does not satisfy the major and minor of the latest fleet server version ${maxFleetServerVersion}.`; + } + } catch (e) { + return e.message; } }; @@ -67,6 +70,10 @@ export const isAgentVersionLessThanFleetServer = ( fleetServerAgents: Agent[], force = false ) => { + // For serverless it should not have any fleet server agents + if (fleetServerAgents.length === 0) { + return true; + } const fleetServerVersions = fleetServerAgents.map( (agent) => agent.local_metadata.elastic.agent.version ) as string[]; @@ -76,21 +83,25 @@ export const isAgentVersionLessThanFleetServer = ( if (!maxFleetServerVersion || !versionToUpgradeNumber) { return false; } - if ( - !force && - semverGt(versionToUpgradeNumber, maxFleetServerVersion) && - !differsOnlyInPatch(versionToUpgradeNumber, maxFleetServerVersion) - ) - return false; - - const fleetServerMajorGt = - semverMajor(maxFleetServerVersion) > semverMajor(versionToUpgradeNumber); - const fleetServerMajorEqMinorGte = - semverMajor(maxFleetServerVersion) === semverMajor(versionToUpgradeNumber) && - semverMinor(maxFleetServerVersion) >= semverMinor(versionToUpgradeNumber); - - // When force is enabled, only the major and minor versions are checked - if (force && !(fleetServerMajorGt || fleetServerMajorEqMinorGte)) { + try { + if ( + !force && + semverGt(versionToUpgradeNumber, maxFleetServerVersion) && + !differsOnlyInPatch(versionToUpgradeNumber, maxFleetServerVersion) + ) + return false; + + const fleetServerMajorGt = + semverMajor(maxFleetServerVersion) > semverMajor(versionToUpgradeNumber); + const fleetServerMajorEqMinorGte = + semverMajor(maxFleetServerVersion) === semverMajor(versionToUpgradeNumber) && + semverMinor(maxFleetServerVersion) >= semverMinor(versionToUpgradeNumber); + + // When force is enabled, only the major and minor versions are checked + if (force && !(fleetServerMajorGt || fleetServerMajorEqMinorGte)) { + return false; + } + } catch (e) { return false; } diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/multi_page_layout/components/add_first_integration_splash.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/multi_page_layout/components/add_first_integration_splash.tsx index d09178f499717..7dbfae011d64f 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/multi_page_layout/components/add_first_integration_splash.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/multi_page_layout/components/add_first_integration_splash.tsx @@ -60,7 +60,7 @@ const CenteredEuiStepNumber = styled(EuiStepNumber)` // step numbers are not centered in smaller layouts without this const CenteredEuiImage = (props: EuiImageProps) => (
- +
); @@ -93,10 +93,7 @@ const AddIntegrationStepsIllustrations = () => {
- +
@@ -124,10 +121,7 @@ const AddIntegrationStepsIllustrations = () => { - + @@ -136,10 +130,7 @@ const AddIntegrationStepsIllustrations = () => { - + @@ -166,10 +157,7 @@ const AddIntegrationStepsIllustrations = () => { - + @@ -178,10 +166,7 @@ const AddIntegrationStepsIllustrations = () => { - + diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agents/components/agent_upgrade_modal/index.test.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/agents/components/agent_upgrade_modal/index.test.tsx index fd25be2132584..dc08d052a9152 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/sections/agents/components/agent_upgrade_modal/index.test.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agents/components/agent_upgrade_modal/index.test.tsx @@ -169,6 +169,46 @@ describe('AgentUpgradeAgentModal', () => { }); }); + it('should display invalid input if version is not a valid semver', async () => { + const { utils } = renderAgentUpgradeAgentModal({ + agents: [ + { + id: 'agent1', + local_metadata: { host: 'abc', elastic: { agent: { version: '8.12.0' } } }, + }, + ] as any, + agentCount: 1, + }); + + await waitFor(() => { + const input = utils.getByTestId('agentUpgradeModal.VersionInput'); + fireEvent.input(input, { target: { value: '8.14' } }); + expect( + utils.getByText('Invalid version, please use a valid semver version, e.g. 8.14.0') + ).toBeInTheDocument(); + }); + }); + + it('should not display invalid input if version is a valid semver', async () => { + const { utils } = renderAgentUpgradeAgentModal({ + agents: [ + { + id: 'agent1', + local_metadata: { host: 'abc', elastic: { agent: { version: '8.12.0' } } }, + }, + ] as any, + agentCount: 1, + }); + + await waitFor(() => { + const input = utils.getByTestId('agentUpgradeModal.VersionInput'); + fireEvent.input(input, { target: { value: '8.14.0+build123456789' } }); + expect( + utils.queryByText('Invalid version, please use a valid semver version, e.g. 8.14.0') + ).toBeNull(); + }); + }); + it('should display available version options', async () => { mockSendGetAgentsAvailableVersions.mockClear(); mockSendGetAgentsAvailableVersions.mockResolvedValue({ diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agents/components/agent_upgrade_modal/index.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/agents/components/agent_upgrade_modal/index.tsx index e86dac0385dc2..89531c2b01181 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/sections/agents/components/agent_upgrade_modal/index.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agents/components/agent_upgrade_modal/index.tsx @@ -28,6 +28,7 @@ import type { EuiComboBoxOptionOption } from '@elastic/eui'; import semverGt from 'semver/functions/gt'; import semverLt from 'semver/functions/lt'; +import semverValid from 'semver/functions/valid'; import { AGENT_UPGRADE_COOLDOWN_IN_MIN, @@ -267,6 +268,18 @@ export const AgentUpgradeAgentModal: React.FunctionComponent { + if (!selectedVersion[0].value) return undefined; + if (!semverValid(selectedVersion[0].value)) { + return ( + + ); + } + }, [selectedVersion]); + const [selectedMaintenanceWindow, setSelectedMaintenanceWindow] = useState([ isSmallBatch ? maintenanceOptions[0] : maintenanceOptions[1], ]); @@ -501,13 +514,15 @@ export const AgentUpgradeAgentModal: React.FunctionComponent + ) : !!semverErrors ? ( + semverErrors ) : undefined } > @@ -522,6 +537,7 @@ export const AgentUpgradeAgentModal: React.FunctionComponent ) : ( - - - - - - - + + + + + + + + + ); }; diff --git a/x-pack/plugins/lens/public/trigger_actions/open_lens_config/edit_action_helpers.ts b/x-pack/plugins/lens/public/trigger_actions/open_lens_config/edit_action_helpers.ts index 9e738dea11ed7..476ac6f3e6505 100644 --- a/x-pack/plugins/lens/public/trigger_actions/open_lens_config/edit_action_helpers.ts +++ b/x-pack/plugins/lens/public/trigger_actions/open_lens_config/edit_action_helpers.ts @@ -45,6 +45,7 @@ export async function executeEditAction({ const rootEmbeddable = embeddable.getRoot(); const overlayTracker = tracksOverlays(rootEmbeddable) ? rootEmbeddable : undefined; const ConfigPanel = await embeddable.openConfingPanel(startDependencies, isNewPanel, deletePanel); + if (ConfigPanel) { const handle = overlays.openFlyout( toMountPoint( diff --git a/x-pack/plugins/lists/server/services/extension_points/extension_point_storage.test.ts b/x-pack/plugins/lists/server/services/extension_points/extension_point_storage.test.ts index 5e52618578e88..07cc2c5ddcadf 100644 --- a/x-pack/plugins/lists/server/services/extension_points/extension_point_storage.test.ts +++ b/x-pack/plugins/lists/server/services/extension_points/extension_point_storage.test.ts @@ -36,14 +36,6 @@ describe('When using ExtensionPointStorage', () => { expect(storageService.get('exceptionsListPreCreateItem')).toBeUndefined(); }); - it('should capture `.stack` from where extension point was registered', () => { - storageService.add(preCreateExtensionPointMock); - - expect(storageService.getExtensionRegistrationSource(preCreateExtensionPointMock)).toContain( - 'extension_point_storage.test' - ); - }); - it('should clear() all extensions', () => { storageService.clear(); diff --git a/x-pack/plugins/lists/server/services/extension_points/extension_point_storage.ts b/x-pack/plugins/lists/server/services/extension_points/extension_point_storage.ts index 03a7d9a7ccc63..6d05f2194fd6f 100644 --- a/x-pack/plugins/lists/server/services/extension_points/extension_point_storage.ts +++ b/x-pack/plugins/lists/server/services/extension_points/extension_point_storage.ts @@ -17,7 +17,6 @@ import { ExtensionPointStorageClient } from './extension_point_storage_client'; export class ExtensionPointStorage implements ExtensionPointStorageInterface { private readonly store = new Map>(); - private readonly registeredFrom = new Map(); constructor(private readonly logger: Logger) {} @@ -30,24 +29,11 @@ export class ExtensionPointStorage implements ExtensionPointStorageInterface { if (extensionPointsForType) { extensionPointsForType.add(extension); - - // Capture stack trace from where this extension point was registered, so that it can be used when - // errors occur or callbacks don't return the expected result - const from = new Error('REGISTERED FROM:').stack ?? 'REGISTERED FROM: unknown'; - this.registeredFrom.set( - extension, - from.substring(from.indexOf('REGISTERED FROM:')).concat('\n ----------------------') - ); } } clear(): void { this.store.clear(); - this.registeredFrom.clear(); - } - - getExtensionRegistrationSource(extensionPoint: ExtensionPoint): string | undefined { - return this.registeredFrom.get(extensionPoint); } get( diff --git a/x-pack/plugins/lists/server/services/extension_points/extension_point_storage_client.ts b/x-pack/plugins/lists/server/services/extension_points/extension_point_storage_client.ts index b3ada7ac69cd5..056a1bf76c2d6 100644 --- a/x-pack/plugins/lists/server/services/extension_points/extension_point_storage_client.ts +++ b/x-pack/plugins/lists/server/services/extension_points/extension_point_storage_client.ts @@ -61,9 +61,6 @@ export class ExtensionPointStorageClient implements ExtensionPointStorageClientI } for (const externalExtension of externalExtensions) { - const extensionRegistrationSource = - this.storage.getExtensionRegistrationSource(externalExtension); - inputArgument = await externalExtension.callback({ context: callbackContext, data: inputArgument as ExtensionPointCallbackDataArgument, @@ -76,7 +73,11 @@ export class ExtensionPointStorageClient implements ExtensionPointStorageClientI if (validationError) { this.logger.error( new ExtensionPointError( - `Extension point for ${externalExtension.type} returned data that failed validation: ${extensionRegistrationSource}`, + `Extension point for ${ + externalExtension.type + } returned data that failed validation: ${ + validationError.message + }\nCallback src: ${externalExtension.callback.toString().substring(0, 300)}...`, { validationError, } diff --git a/x-pack/plugins/lists/server/services/extension_points/types.ts b/x-pack/plugins/lists/server/services/extension_points/types.ts index 2a298e96d2656..2546f6bf898c8 100644 --- a/x-pack/plugins/lists/server/services/extension_points/types.ts +++ b/x-pack/plugins/lists/server/services/extension_points/types.ts @@ -200,8 +200,6 @@ export interface ExtensionPointStorageInterface { clear(): void; - getExtensionRegistrationSource(extensionPoint: ExtensionPoint): string | undefined; - get( extensionType: T ): Set> | undefined; diff --git a/x-pack/plugins/monitoring/public/alerts/alert_form.test.tsx b/x-pack/plugins/monitoring/public/alerts/alert_form.test.tsx index 7ac7d435f89d9..2cca4638876c9 100644 --- a/x-pack/plugins/monitoring/public/alerts/alert_form.test.tsx +++ b/x-pack/plugins/monitoring/public/alerts/alert_form.test.tsx @@ -29,13 +29,6 @@ import { I18nProvider } from '@kbn/i18n-react'; import { createKibanaReactContext } from '@kbn/kibana-react-plugin/public'; import { QueryClient, QueryClientProvider } from '@tanstack/react-query'; -interface AlertAction { - group: string; - id: string; - actionTypeId: string; - params: unknown; -} - jest.mock('@kbn/triggers-actions-ui-plugin/public/application/lib/action_connector_api', () => ({ loadAllActions: jest.fn(), loadActionTypes: jest.fn(), @@ -264,7 +257,7 @@ describe('alert_form', () => { setActionIdByIndex={(id: string, index: number) => { initialAlert.actions[index].id = id; }} - setActions={(_updatedActions: AlertAction[]) => {}} + setActions={() => {}} setActionParamsProperty={(key: string, value: unknown, index: number) => (initialAlert.actions[index] = { ...initialAlert.actions[index], [key]: value }) } diff --git a/x-pack/plugins/monitoring/server/rules/base_rule.ts b/x-pack/plugins/monitoring/server/rules/base_rule.ts index b457185704b76..da9f8edb94b2e 100644 --- a/x-pack/plugins/monitoring/server/rules/base_rule.ts +++ b/x-pack/plugins/monitoring/server/rules/base_rule.ts @@ -170,19 +170,21 @@ export class BaseRule { if (!action) { continue; } - ruleActions.push({ - group: 'default', - id: actionData.id, - params: { - message: '{{context.internalShortMessage}}', - ...actionData.config, - }, - frequency: { - summary: false, - notifyWhen: RuleNotifyWhen.THROTTLE, - throttle, - }, - }); + if (!action.isSystemAction) { + ruleActions.push({ + group: 'default', + id: actionData.id, + params: { + message: '{{context.internalShortMessage}}', + ...actionData.config, + }, + frequency: { + summary: false, + notifyWhen: RuleNotifyWhen.THROTTLE, + throttle, + }, + }); + } } return await rulesClient.create({ diff --git a/x-pack/plugins/observability_solution/dataset_quality/common/api_types.ts b/x-pack/plugins/observability_solution/dataset_quality/common/api_types.ts index 9fba1352fa944..4c88ae2cadb10 100644 --- a/x-pack/plugins/observability_solution/dataset_quality/common/api_types.ts +++ b/x-pack/plugins/observability_solution/dataset_quality/common/api_types.ts @@ -21,6 +21,20 @@ export const dataStreamStatRt = rt.intersection([ export type DataStreamStat = rt.TypeOf; +export const dashboardRT = rt.type({ + id: rt.string, + title: rt.string, +}); + +export const integrationDashboardsRT = rt.type({ + dashboards: rt.array(dashboardRT), +}); + +export type IntegrationDashboards = rt.TypeOf; +export type Dashboard = rt.TypeOf; + +export const getIntegrationDashboardsResponseRt = rt.exact(integrationDashboardsRT); + export const integrationIconRt = rt.intersection([ rt.type({ path: rt.string, @@ -42,6 +56,7 @@ export const integrationRt = rt.intersection([ version: rt.string, icons: rt.array(integrationIconRt), datasets: rt.record(rt.string, rt.string), + dashboards: rt.array(dashboardRT), }), ]); diff --git a/x-pack/plugins/observability_solution/dataset_quality/common/data_streams_stats/integration.ts b/x-pack/plugins/observability_solution/dataset_quality/common/data_streams_stats/integration.ts index 68a394c4b41c5..97d0a5f001d69 100644 --- a/x-pack/plugins/observability_solution/dataset_quality/common/data_streams_stats/integration.ts +++ b/x-pack/plugins/observability_solution/dataset_quality/common/data_streams_stats/integration.ts @@ -5,19 +5,21 @@ * 2.0. */ -import { IntegrationType } from './types'; +import { DashboardType, IntegrationType } from './types'; export class Integration { name: IntegrationType['name']; title: string; version: string; icons?: IntegrationType['icons']; + dashboards?: DashboardType[]; private constructor(integration: Integration) { this.name = integration.name; this.title = integration.title || integration.name; this.version = integration.version || '1.0.0'; this.icons = integration.icons; + this.dashboards = integration.dashboards || []; } public static create(integration: IntegrationType) { @@ -25,6 +27,7 @@ export class Integration { ...integration, title: integration.title || integration.name, version: integration.version || '1.0.0', + dashboards: integration.dashboards || [], }; return new Integration(integrationProps); diff --git a/x-pack/plugins/observability_solution/dataset_quality/common/data_streams_stats/types.ts b/x-pack/plugins/observability_solution/dataset_quality/common/data_streams_stats/types.ts index b8f8c37e46851..a2fc5f42fed4a 100644 --- a/x-pack/plugins/observability_solution/dataset_quality/common/data_streams_stats/types.ts +++ b/x-pack/plugins/observability_solution/dataset_quality/common/data_streams_stats/types.ts @@ -41,5 +41,11 @@ export type GetDataStreamsEstimatedDataInBytesParams = export type GetDataStreamsEstimatedDataInBytesResponse = APIReturnType<`GET /internal/dataset_quality/data_streams/estimated_data`>; +export type GetIntegrationDashboardsParams = + APIClientRequestParamsOf<`GET /internal/dataset_quality/integrations/{integration}/dashboards`>['params']['path']; +export type GetIntegrationDashboardsResponse = + APIReturnType<`GET /internal/dataset_quality/integrations/{integration}/dashboards`>; +export type DashboardType = GetIntegrationDashboardsResponse['dashboards'][0]; + export type { DataStreamStat } from './data_stream_stat'; export type { DataStreamDetails } from '../api_types'; diff --git a/x-pack/plugins/observability_solution/dataset_quality/public/components/flyout/fields_list.tsx b/x-pack/plugins/observability_solution/dataset_quality/public/components/flyout/fields_list.tsx index 0824e85ddd363..a203d08365d2b 100644 --- a/x-pack/plugins/observability_solution/dataset_quality/public/components/flyout/fields_list.tsx +++ b/x-pack/plugins/observability_solution/dataset_quality/public/components/flyout/fields_list.tsx @@ -20,15 +20,20 @@ import { export function FieldsList({ title, fields, + actionsMenu: ActionsMenu, }: { title: string; fields: Array<{ fieldTitle: string; fieldValue: ReactNode }>; + actionsMenu?: ReactNode; }) { return ( - - {title} - + + + {title} + + {ActionsMenu} + {fields.map(({ fieldTitle, fieldValue }, index) => ( diff --git a/x-pack/plugins/observability_solution/dataset_quality/public/components/flyout/integration_actions_menu.tsx b/x-pack/plugins/observability_solution/dataset_quality/public/components/flyout/integration_actions_menu.tsx new file mode 100644 index 0000000000000..7a3b2715f1782 --- /dev/null +++ b/x-pack/plugins/observability_solution/dataset_quality/public/components/flyout/integration_actions_menu.tsx @@ -0,0 +1,166 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React, { useMemo } from 'react'; +import { i18n } from '@kbn/i18n'; +import { + EuiButtonEmpty, + EuiButtonIcon, + EuiContextMenu, + EuiContextMenuPanelDescriptor, + EuiContextMenuPanelItemDescriptor, + EuiPopover, +} from '@elastic/eui'; +import { css } from '@emotion/react'; +import { RouterLinkProps } from '@kbn/router-utils/src/get_router_link_props'; +import { Integration } from '../../../common/data_streams_stats/integration'; +import { useDatasetQualityFlyout } from '../../hooks'; +import { useFlyoutIntegrationActions } from '../../hooks/use_flyout_integration_actions'; +const seeIntegrationText = i18n.translate('xpack.datasetQuality.flyoutSeeIntegrationActionText', { + defaultMessage: 'See integration', +}); + +const indexTemplateText = i18n.translate('xpack.datasetQuality.flyoutIndexTemplateActionText', { + defaultMessage: 'Index template', +}); + +const viewDashboardsText = i18n.translate('xpack.datasetQuality.flyoutViewDashboardsActionText', { + defaultMessage: 'View dashboards', +}); + +export function IntegrationActionsMenu({ integration }: { integration: Integration }) { + const { type, name } = useDatasetQualityFlyout().dataStreamStat!; + const { dashboards = [], version, name: integrationName } = integration; + const { + isOpen, + handleCloseMenu, + handleToggleMenu, + getIntegrationOverviewLinkProps, + getIndexManagementLinkProps, + getDashboardLinkProps, + } = useFlyoutIntegrationActions(); + + const actionButton = ( + + ); + + const MenuActionItem = ({ + dataTestSubject, + buttonText, + routerLinkProps, + iconType, + }: { + dataTestSubject: string; + buttonText: string; + routerLinkProps: RouterLinkProps; + iconType: string; + }) => ( + + {buttonText} + + ); + + const panelItems = useMemo(() => { + const firstLevelItems: EuiContextMenuPanelItemDescriptor[] = [ + { + renderItem: () => ( + + ), + }, + { + renderItem: () => ( + + ), + }, + { + isSeparator: true, + key: 'sep', + }, + ]; + + if (dashboards.length) { + firstLevelItems.push({ + icon: 'dashboardApp', + panel: 1, + name: viewDashboardsText, + 'data-test-subj': 'datasetQualityFlyoutIntegrationActionViewDashboards', + }); + } + + const panel: EuiContextMenuPanelDescriptor[] = [ + { + id: 0, + items: firstLevelItems, + }, + { + id: 1, + title: viewDashboardsText, + items: dashboards.map((dashboard) => { + return { + renderItem: () => ( + + ), + }; + }), + }, + ]; + + return panel; + }, [ + dashboards, + getDashboardLinkProps, + getIndexManagementLinkProps, + getIntegrationOverviewLinkProps, + integrationName, + name, + type, + version, + ]); + + return ( + + + + ); +} diff --git a/x-pack/plugins/observability_solution/dataset_quality/public/components/flyout/integration_summary.tsx b/x-pack/plugins/observability_solution/dataset_quality/public/components/flyout/integration_summary.tsx index 19b147bcea1fd..d77c68e4ac33b 100644 --- a/x-pack/plugins/observability_solution/dataset_quality/public/components/flyout/integration_summary.tsx +++ b/x-pack/plugins/observability_solution/dataset_quality/public/components/flyout/integration_summary.tsx @@ -16,12 +16,16 @@ import { import { Integration } from '../../../common/data_streams_stats/integration'; import { IntegrationIcon } from '../common'; import { FieldsList } from './fields_list'; +import { IntegrationActionsMenu } from './integration_actions_menu'; export function IntegrationSummary({ integration }: { integration: Integration }) { const { name, version } = integration; + + const integrationActionsMenu = ; return ( + ({ core, dataStreamStatsClient, dataStreamDetailsClient }: Dependencies) => async ({ initialState = DEFAULT_CONTEXT, }: { @@ -38,6 +40,7 @@ export const createDatasetQualityControllerFactory = initialContext, toasts: core.notifications.toasts, dataStreamStatsClient, + dataStreamDetailsClient, }); const service = interpret(machine, { diff --git a/x-pack/plugins/observability_solution/dataset_quality/public/hooks/use_dataset_quality_flyout.tsx b/x-pack/plugins/observability_solution/dataset_quality/public/hooks/use_dataset_quality_flyout.tsx index cbbd6b8d6bc46..0a60c3c2ed2b5 100644 --- a/x-pack/plugins/observability_solution/dataset_quality/public/hooks/use_dataset_quality_flyout.tsx +++ b/x-pack/plugins/observability_solution/dataset_quality/public/hooks/use_dataset_quality_flyout.tsx @@ -23,8 +23,11 @@ export const useDatasetQualityFlyout = () => { } = useSelector(service, (state) => state.context.flyout); const { timeRange } = useSelector(service, (state) => state.context.filters); - const dataStreamDetailsLoading = useSelector(service, (state) => - state.matches('datasets.loaded.flyoutOpen.fetching') + const dataStreamDetailsLoading = useSelector( + service, + (state) => + state.matches('datasets.loaded.flyoutOpen.fetching') || + state.matches('flyout.initializing.dataStreamDetails.fetching') ); return { diff --git a/x-pack/plugins/observability_solution/dataset_quality/public/hooks/use_flyout_integration_actions.tsx b/x-pack/plugins/observability_solution/dataset_quality/public/hooks/use_flyout_integration_actions.tsx new file mode 100644 index 0000000000000..29faaec2788ea --- /dev/null +++ b/x-pack/plugins/observability_solution/dataset_quality/public/hooks/use_flyout_integration_actions.tsx @@ -0,0 +1,78 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor 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 { getRouterLinkProps } from '@kbn/router-utils'; +import { useMemo, useCallback } from 'react'; +import useToggle from 'react-use/lib/useToggle'; +import { MANAGEMENT_APP_LOCATOR } from '@kbn/deeplinks-management/constants'; +import { DASHBOARD_APP_LOCATOR } from '@kbn/deeplinks-analytics'; +import { DashboardType } from '../../common/data_streams_stats'; +import { useKibanaContextForPlugin } from '../utils'; + +export const useFlyoutIntegrationActions = () => { + const { + services: { + application: { navigateToUrl }, + http: { basePath }, + share, + }, + } = useKibanaContextForPlugin(); + + const [isOpen, toggleIsOpen] = useToggle(false); + + const dashboardLocator = useMemo( + () => share.url.locators.get(DASHBOARD_APP_LOCATOR), + [share.url.locators] + ); + const indexManagementLocator = useMemo( + () => share.url.locators.get(MANAGEMENT_APP_LOCATOR), + [share.url.locators] + ); + + const handleCloseMenu = useCallback(() => { + toggleIsOpen(); + }, [toggleIsOpen]); + const handleToggleMenu = useCallback(() => { + toggleIsOpen(); + }, [toggleIsOpen]); + + const getIntegrationOverviewLinkProps = useCallback( + (name: string, version: string) => { + const href = basePath.prepend(`/app/integrations/detail/${name}-${version}/overview`); + return getRouterLinkProps({ + href, + onClick: () => navigateToUrl(href), + }); + }, + [basePath, navigateToUrl] + ); + const getIndexManagementLinkProps = useCallback( + (params: { sectionId: string; appId: string }) => + getRouterLinkProps({ + href: indexManagementLocator?.getRedirectUrl(params), + onClick: () => indexManagementLocator?.navigate(params), + }), + [indexManagementLocator] + ); + const getDashboardLinkProps = useCallback( + (dashboard: DashboardType) => + getRouterLinkProps({ + href: dashboardLocator?.getRedirectUrl({ dashboardId: dashboard?.id } || ''), + onClick: () => dashboardLocator?.navigate({ dashboardId: dashboard?.id } || ''), + }), + [dashboardLocator] + ); + + return { + isOpen, + handleCloseMenu, + handleToggleMenu, + getIntegrationOverviewLinkProps, + getIndexManagementLinkProps, + getDashboardLinkProps, + }; +}; diff --git a/x-pack/plugins/observability_solution/dataset_quality/public/plugin.tsx b/x-pack/plugins/observability_solution/dataset_quality/public/plugin.tsx index 88ab62eff4703..6ea2655450607 100644 --- a/x-pack/plugins/observability_solution/dataset_quality/public/plugin.tsx +++ b/x-pack/plugins/observability_solution/dataset_quality/public/plugin.tsx @@ -9,6 +9,7 @@ import { CoreSetup, CoreStart, Plugin, PluginInitializerContext } from '@kbn/cor import { createDatasetQuality } from './components/dataset_quality'; import { createDatasetQualityControllerLazyFactory } from './controller/lazy_create_controller'; import { DataStreamsStatsService } from './services/data_streams_stats'; +import { DataStreamDetailsService } from './services/data_stream_details'; import { DatasetQualityPluginSetup, DatasetQualityPluginStart, @@ -30,6 +31,10 @@ export class DatasetQualityPlugin http: core.http, }).client; + const dataStreamDetailsClient = new DataStreamDetailsService().start({ + http: core.http, + }).client; + const DatasetQuality = createDatasetQuality({ core, plugins, @@ -39,6 +44,7 @@ export class DatasetQualityPlugin const createDatasetQualityController = createDatasetQualityControllerLazyFactory({ core, dataStreamStatsClient, + dataStreamDetailsClient, }); return { DatasetQuality, createDatasetQualityController }; diff --git a/x-pack/plugins/observability_solution/dataset_quality/public/services/data_stream_details/data_stream_details_client.ts b/x-pack/plugins/observability_solution/dataset_quality/public/services/data_stream_details/data_stream_details_client.ts new file mode 100644 index 0000000000000..1dd5b2d2220cd --- /dev/null +++ b/x-pack/plugins/observability_solution/dataset_quality/public/services/data_stream_details/data_stream_details_client.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 { HttpStart } from '@kbn/core/public'; +import { decodeOrThrow } from '@kbn/io-ts-utils'; +import { + getDataStreamsDetailsResponseRt, + integrationDashboardsRT, +} from '../../../common/api_types'; +import { + GetDataStreamsStatsError, + GetDataStreamDetailsParams, + GetDataStreamDetailsResponse, + GetIntegrationDashboardsParams, + GetIntegrationDashboardsResponse, +} from '../../../common/data_streams_stats'; +import { DataStreamDetails } from '../../../common/data_streams_stats'; +import { IDataStreamDetailsClient } from './types'; + +export class DataStreamDetailsClient implements IDataStreamDetailsClient { + constructor(private readonly http: HttpStart) {} + + public async getDataStreamDetails({ dataStream }: GetDataStreamDetailsParams) { + const response = await this.http + .get( + `/internal/dataset_quality/data_streams/${dataStream}/details` + ) + .catch((error) => { + throw new GetDataStreamsStatsError(`Failed to fetch data stream details": ${error}`); + }); + + const dataStreamDetails = decodeOrThrow( + getDataStreamsDetailsResponseRt, + (message: string) => + new GetDataStreamsStatsError(`Failed to decode data stream details response: ${message}"`) + )(response); + + return dataStreamDetails as DataStreamDetails; + } + + public async getIntegrationDashboards({ integration }: GetIntegrationDashboardsParams) { + const response = await this.http + .get( + `/internal/dataset_quality/integrations/${integration}/dashboards` + ) + .catch((error) => { + throw new GetDataStreamsStatsError(`Failed to fetch integration dashboards": ${error}`); + }); + + const integrationDashboards = decodeOrThrow( + integrationDashboardsRT, + (message: string) => + new GetDataStreamsStatsError( + `Failed to decode integration dashboards response: ${message}"` + ) + )(response); + + return integrationDashboards; + } +} diff --git a/x-pack/plugins/observability_solution/dataset_quality/public/services/data_stream_details/data_stream_details_service.ts b/x-pack/plugins/observability_solution/dataset_quality/public/services/data_stream_details/data_stream_details_service.ts new file mode 100644 index 0000000000000..21d9eb492cc2a --- /dev/null +++ b/x-pack/plugins/observability_solution/dataset_quality/public/services/data_stream_details/data_stream_details_service.ts @@ -0,0 +1,27 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { DataStreamDetailsClient } from './data_stream_details_client'; +import { + DataStreamDetailsServiceSetup, + DataStreamDetailsServiceStartDeps, + DataStreamDetailsServiceStart, +} from './types'; + +export class DataStreamDetailsService { + constructor() {} + + public setup(): DataStreamDetailsServiceSetup {} + + public start({ http }: DataStreamDetailsServiceStartDeps): DataStreamDetailsServiceStart { + const client = new DataStreamDetailsClient(http); + + return { + client, + }; + } +} diff --git a/x-pack/plugins/observability_solution/dataset_quality/public/services/data_stream_details/index.ts b/x-pack/plugins/observability_solution/dataset_quality/public/services/data_stream_details/index.ts new file mode 100644 index 0000000000000..7d142e5f2a3cf --- /dev/null +++ b/x-pack/plugins/observability_solution/dataset_quality/public/services/data_stream_details/index.ts @@ -0,0 +1,10 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +export * from './data_stream_details_client'; +export * from './data_stream_details_service'; +export * from './types'; diff --git a/x-pack/plugins/observability_solution/dataset_quality/public/services/data_stream_details/types.ts b/x-pack/plugins/observability_solution/dataset_quality/public/services/data_stream_details/types.ts new file mode 100644 index 0000000000000..068b36bab4fb1 --- /dev/null +++ b/x-pack/plugins/observability_solution/dataset_quality/public/services/data_stream_details/types.ts @@ -0,0 +1,31 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor 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 { HttpStart } from '@kbn/core/public'; +import { + GetDataStreamDetailsParams, + DataStreamDetails, + GetIntegrationDashboardsParams, + GetIntegrationDashboardsResponse, +} from '../../../common/data_streams_stats'; + +export type DataStreamDetailsServiceSetup = void; + +export interface DataStreamDetailsServiceStart { + client: IDataStreamDetailsClient; +} + +export interface DataStreamDetailsServiceStartDeps { + http: HttpStart; +} + +export interface IDataStreamDetailsClient { + getDataStreamDetails(params: GetDataStreamDetailsParams): Promise; + getIntegrationDashboards( + params: GetIntegrationDashboardsParams + ): Promise; +} diff --git a/x-pack/plugins/observability_solution/dataset_quality/public/services/data_streams_stats/data_streams_stats_client.ts b/x-pack/plugins/observability_solution/dataset_quality/public/services/data_streams_stats/data_streams_stats_client.ts index 625eafce2a282..69e8b2364006e 100644 --- a/x-pack/plugins/observability_solution/dataset_quality/public/services/data_streams_stats/data_streams_stats_client.ts +++ b/x-pack/plugins/observability_solution/dataset_quality/public/services/data_streams_stats/data_streams_stats_client.ts @@ -12,7 +12,6 @@ import { Integration } from '../../../common/data_streams_stats/integration'; import { getDataStreamsDegradedDocsStatsResponseRt, getDataStreamsStatsResponseRt, - getDataStreamsDetailsResponseRt, getDataStreamsEstimatedDataInBytesResponseRt, } from '../../../common/api_types'; import { DEFAULT_DATASET_TYPE, NONE } from '../../../common/constants'; @@ -23,12 +22,9 @@ import { GetDataStreamsStatsError, GetDataStreamsStatsQuery, GetDataStreamsStatsResponse, - GetDataStreamDetailsParams, - GetDataStreamDetailsResponse, GetDataStreamsEstimatedDataInBytesParams, GetDataStreamsEstimatedDataInBytesResponse, } from '../../../common/data_streams_stats'; -import { DataStreamDetails } from '../../../common/data_streams_stats'; import { DataStreamStat } from '../../../common/data_streams_stats/data_stream_stat'; import { IDataStreamsStatsClient } from './types'; @@ -95,24 +91,6 @@ export class DataStreamsStatsClient implements IDataStreamsStatsClient { return degradedDocs; } - public async getDataStreamDetails({ dataStream }: GetDataStreamDetailsParams) { - const response = await this.http - .get( - `/internal/dataset_quality/data_streams/${dataStream}/details` - ) - .catch((error) => { - throw new GetDataStreamsStatsError(`Failed to fetch data stream details": ${error}`); - }); - - const dataStreamDetails = decodeOrThrow( - getDataStreamsDetailsResponseRt, - (message: string) => - new GetDataStreamsStatsError(`Failed to decode data stream details response: ${message}"`) - )(response); - - return dataStreamDetails as DataStreamDetails; - } - public async getDataStreamsEstimatedDataInBytes( params: GetDataStreamsEstimatedDataInBytesParams ) { diff --git a/x-pack/plugins/observability_solution/dataset_quality/public/services/data_streams_stats/types.ts b/x-pack/plugins/observability_solution/dataset_quality/public/services/data_streams_stats/types.ts index a2627ec4bf222..ed454233d36eb 100644 --- a/x-pack/plugins/observability_solution/dataset_quality/public/services/data_streams_stats/types.ts +++ b/x-pack/plugins/observability_solution/dataset_quality/public/services/data_streams_stats/types.ts @@ -11,8 +11,6 @@ import { DataStreamStatServiceResponse, GetDataStreamsDegradedDocsStatsQuery, GetDataStreamsStatsQuery, - GetDataStreamDetailsParams, - DataStreamDetails, GetDataStreamsEstimatedDataInBytesParams, GetDataStreamsEstimatedDataInBytesResponse, } from '../../../common/data_streams_stats'; @@ -32,7 +30,6 @@ export interface IDataStreamsStatsClient { getDataStreamsDegradedStats( params?: GetDataStreamsDegradedDocsStatsQuery ): Promise; - getDataStreamDetails(params: GetDataStreamDetailsParams): Promise; getDataStreamsEstimatedDataInBytes( params: GetDataStreamsEstimatedDataInBytesParams ): Promise; diff --git a/x-pack/plugins/observability_solution/dataset_quality/public/state_machines/dataset_quality_controller/src/notifications.ts b/x-pack/plugins/observability_solution/dataset_quality/public/state_machines/dataset_quality_controller/src/notifications.ts index c3cb600951463..ca249e3fa6022 100644 --- a/x-pack/plugins/observability_solution/dataset_quality/public/state_machines/dataset_quality_controller/src/notifications.ts +++ b/x-pack/plugins/observability_solution/dataset_quality/public/state_machines/dataset_quality_controller/src/notifications.ts @@ -35,6 +35,15 @@ export const fetchDegradedStatsFailedNotifier = (toasts: IToasts, error: Error) }); }; +export const fetchIntegrationDashboardsFailedNotifier = (toasts: IToasts, error: Error) => { + toasts.addDanger({ + title: i18n.translate('xpack.datasetQuality.fetchIntegrationDashboardsFailed', { + defaultMessage: "We couldn't get your integration dashboards.", + }), + text: error.message, + }); +}; + export const noDatasetSelected = i18n.translate( 'xpack.datasetQuality.fetchDatasetDetailsFailed.noDatasetSelected', { diff --git a/x-pack/plugins/observability_solution/dataset_quality/public/state_machines/dataset_quality_controller/src/state_machine.ts b/x-pack/plugins/observability_solution/dataset_quality/public/state_machines/dataset_quality_controller/src/state_machine.ts index 4fd6d19e2012a..cef0f8496e4ae 100644 --- a/x-pack/plugins/observability_solution/dataset_quality/public/state_machines/dataset_quality_controller/src/state_machine.ts +++ b/x-pack/plugins/observability_solution/dataset_quality/public/state_machines/dataset_quality_controller/src/state_machine.ts @@ -8,7 +8,9 @@ import { IToasts } from '@kbn/core/public'; import { getDateISORange } from '@kbn/timerange'; import { assign, createMachine, DoneInvokeEvent, InterpreterFrom } from 'xstate'; +import { IDataStreamDetailsClient } from '../../../services/data_stream_details'; import { + DashboardType, DataStreamDetails, DataStreamStatServiceResponse, GetDataStreamsStatsQuery, @@ -23,6 +25,7 @@ import { fetchDatasetDetailsFailedNotifier, fetchDatasetStatsFailedNotifier, fetchDegradedStatsFailedNotifier, + fetchIntegrationDashboardsFailedNotifier, noDatasetSelected, } from './notifications'; import { @@ -35,7 +38,7 @@ import { export const createPureDatasetQualityControllerStateMachine = ( initialContext: DatasetQualityControllerContext ) => - /** @xstate-layout N4IgpgJg5mDOIC5QBECGAXVszoIoFdUAbAS3QE8BhAewDt0AnaoosBgOggyx1nYDMcAYwAWJWlADEEOmHbiAbtQDWctJmx5CpCjXpMWbTt019B6UeKgJF1IRhJ0A2gAYAuq7eJQAB2qwyR1pvEAAPRAAWADYARnYATgBmAHYo+OSADniYlwBWGIAaEHJEZIAmDPYI+JcImJissuSXDIiAXzai9R4tYjIqOkZmVg4uDV4BYTEJSTYmDh8iDH5qBgBbdm7NAj7dQYMR43H0MymrG1ole3Qgz08QvwCbuhDwhCjKiNqY3OTE9LKEWqZSKJQQ5Vy7GS0Xq8Si0KBETKHS6JhwOx0A30wyMYx6fCI1FQEEgkgAqgAFZAAQQAKgBRAD6tOpACEADJMygAJQAkgy+dT7kgQI9Ai8RW9ctl2IkYpkMnL4oqIolQYgcnkEmVclEorkXPEykb4SiQFt0dp+nohoZRmiTuxCcTSbSAPIAcQ9nMZvIActTKLTeQA1Jk0lkAZXptMjwt8-nFwUliDSyXYaUSZUNSWVLnK6oQGRa7DKctyBuVcpciSiZotvUxNoOuIdBKJJIgkndXp9ADEyez2YyI9To7TGQGALL0uPuB6J57J0BvbOVeFRMvy3IRHcufWFzWQo26-WG41lPX1h0Y637HGjMBQBguiDIOynCzTKQyWhyWyqJsN5Wns2J2pwT4vp275CJ+lgSBcVwOM47jxqKi5BK8pQZJCyQxNmubpBEyTpIeTRxBkZQ6k0NapHWnTmsBuxYrahwks+r4wXB36zAw8zsIsyyrBsDa3qBrG4pBnEfpMX7nLY1y3Kh84imKS5Ye8UTsDEqrlC4LiAlkMTxPEZE4aWGTGS0yQ2Yk+7XscYksS2HD8EQ5DUPg6CSNGnJBpO9IAOojnSY4xmhamYSm4JxFRHzKmUOTRCkpnFIg-yQrq0TxF8dQxGkDk9E5zYPgI7med5lDsm60aMn27IAJpumStIRRhEorqUsWXlklFJVEKWHjpspVoqOVGkaeGFdsIHOaVbkeV5snwT+sjyJcKhqExTb3uBC0Vct36IXYyG0HcKkJk8UWdUWrTsAa8KpJZ+U2alYKJNE2q5JRmR1FE+aJNNlrMSVe3lUt5grbx-GCegKzrEBjmzaDhz7RDZwIQpp3nV4qntcuYSIBkfwZtCMQpNmOT-IWiVaYlLj5RkA2Klk9kMaJyO7aj4PoE6HaklVNVMvVTUtW1V0dYTCDxN991ZNCgJGsTTSHiWlkVv8Ro-ArMRA42d5gdzi2886nbklSdJMv6ka8h6AASsbMryM6Mty1J+h69Li0mGn4SkVQ-GWmSxJThRpQg9SQuT8rQi08J5BkevFVzRho7zQiEtgXZuhS9J+nVjXNa1F3oRLBNvHUETsMW0o5Sktb6eTNO5GU2k1Dp+GbsHyQdAxtDUCS8AihzIMpwwC5lxpAC0xrV7qTQmck+QtAzhZT-q1d2VmBkA9U+FJ5zhutscQ+XT70XZlU3y-P85RAtkUSFrkiTHjUSRZtCGSWci7PbQbEn2hPodKwE9z43RolCD4Nl6hJV6oWTciRZTkxVNWXcP9URI1HkfQB+I+avlAepaK+R4jaRIvCP2ek3qIGyOmOytcMppBlgfLBACIIcWgh+Ah10pYNG0rpbMBkIhGRMkNfIsoG4KjGkvdBjFME7WwWwqCkAuLAIkFwyWFctQ1hlvhLIz8cIRDIokdc310gmWMjuAazD5GsPYkot8MlTaQHUeXSIX9Sy6h3NUOyu4GaP3DuTKIVctbQmaNkGsQjrH-xcmVY2LiNJllLD1BK-UUjExpnEYEA1mhNHlqqKJ4kYlp1UVAeJ0V-qQlqF8Q0vxizEVyDTPUCRdz1DwvUAaG4ClzTBsbPBnYyk3XqEIue1QGapA+pRGmmQMzeOVPlXUTNchdJRqnHm7AM7+GcXjSe0U7Lpi8fKB+0JEgv0PP7FuqpdQtEZkkH+HQgA */ + /** @xstate-layout N4IgpgJg5mDOIC5QBECGAXVszoIoFdUAbAS3QE8BhAewDt0AnaoosBgOggyx1gGIAqgAVkAQQAqAUQD64gJIBZGQCVRAOQDikgNoAGALqJQAB2qwyJOkZAAPRAE4A7ABoQ5RACYAjLoAs7AFYPAA4PR2CAgN1HewBmXwBfBNc0TGw8QlIKGnomFjZObnT+ZUkAMVKAZQAJaTFxUT1DJBBTc3RLWms7BCdXdwQPXQD-R10wxwA2X2jfey8klKKcAmIyKjpGZlYOLjTeQREJGTk1KQ1VeQB5NUqm6zaLKxaevrdEL0dfR3Zg+xHdMFJhFdMDgosQKkeBk1tlNnkdoV9uh+MJ6jI1KIlJUhKJKJI7gYHmYnl0Xg4XO8EF5gr5YuxProfLEvJNYvZJqyIVD0qsshtctsCntoaijlJpLgBJJlABNe4tR4dZ6gHqTDz0rwBP6g0LTLUefofbX0jxOL5gnwBSbc5Yw-k5Lb5XZ22DsABmOAAxgALEi0KB8CB0MDsf0AN2oAGtQzyVpl1o6EcLXR7vX6AwgI9QvRhOk0FSYScqyarEAF7PZ2LE-h5JpMgqzHI5DVSvPYgdXZlEZvFAbbkXzE-ChS7kW7Pehff7A2wmBxjEQMO7qAwALbsOP24eC51I0VpqcZqBZ2iR3MlgtExXFzrdcuV6u1+uNybN1sDbzDBn-AKORt0t4sQDtCQ5wruiIisU7BENQqAQJAhzorIogAEIADIyJQyhyFIOGNNeRbtHe5IIH8ATsICHjalM9bhLEkxGoMTJVqCvi+ECXzBBE3wgbyCbgU6kGprB8GIeIVwaBomHSKceLyAAajI9SiJUkjiISzREaS95kc2DK+NMsQeHWQI9kxNJOAy-66ICsQsvYgF8fGsICkJKbjjBcEIRAfASVJMllAI6HoXUEiqep0iYtihatLeKq2IgQLBOwYzWtqXjePW1EWcETLsPYtkmeMMzNokySQnaYFucmuxgFADBiRAyA5mKyHyEo0iqJoOiEXFxEJT0LZMRqMTVr4QweHMZq6JW5VLIOAk1aOnD1Y1PktV6JTlFUtQqbFSokWWCDDVSGpmoErJBGEEQxI4znboJtWrQ1TWbRO6YzkGIZhme0axlVS1JitCGvRtrWHtOmbZhe+YGAd8WlolCAjBR0zan4JlhBWI0zF41aVsZ7H2QxXwPdVwN7qD62QO9kPHnwc6ruwi7LquG5bhTI5U2tb0Q5OUMnjDeZ0FeWn9TppGo+w6N5RNJn-vYI2xLNDLUdxc3BO2bLk0D3OIu6RDkNQ+DoHwamYZQ4hRZIADqYUNGp4gIwNSNDR+iDsv4oQsvEYS6MZWu665lMG0bJtm5Q6FXGp0hlOhspXAIzt9Ydg2IKdn50jLfg0ir8yTfYwcOvrBSG8bpu-RYawAF5fcGtChrAmDoADi0h6XHDlxHVcdLXM4u5Lx2OZqRUB3+oRml4Fk0vjPF0uyLbxFMxc7u5Xfh5X-rV6QdcBvulSMGAqBrsgOCoCQRAfUe9c-dmMaboDHcQWXm-oL3JD9-vIqHwwx+n+fS+19BannPCLWgYtiSu10r4dsT57Bmi+IZWabILKzR+FqXwQQA6shKvdCqnM9Yvw3hXd+28+67xnAfI+J8z6YCAfTL6TMFxLnQCudcj924l2IR6N+H8v5QGoX-WhgCr6MOhn9WGot4ap0RjAuBNYEFOHYpMFBsQ0F+F+FrQEJodGxHwQtUCRD168NIfwyh+9-St1eiWNAsAfQACM4IMAgMAhmDdQz3zbkY5+Jju5b1oDvEge9BFWN5rYrAjjnGuPEULSR4DIE3mgaRdiFEhj52CF8WaZkAgWXZPjEqLINT-G4oZVeT0Vr+LIYEihwSqFhJsZ0OxUTUAuLcUwhg84WasPYRzJ+3C-F8PIZ-CxoT6DhKaZEpxrSYkC2PKAnMCSZHizTm7RAqT2DpOyVkjsDZcltjNClGymSYg+EcF4eI5Tlp7iqV5JqfAo4xxkPHROydB4ll0ggpiS9KJOAQRqTkcwyYEP6WvZ6tzRI+SQscWStw5AaGqBpWQigVDqC0O8o6yNMqaJutaEEnIvCZTQaEVK3xbLtjpNEYYVzQ6vzMV6WC2BfJXCEJINQccE5JxTisuRpEGJVkUSrJkxkWRErOgEfGwITQ1n0eMWywEIS0GoAheALRCG+NqlAoeyMAC0WCmLAgKiaaaIwaQhHBCCrhYKQaui1R8lJSs2z1l0IEFscxPgvm1EXS1PiBnPSgrwWJdrMU9Hshg7i4RgQNn+BxfZn5AQpX+CrOsXx5grx9fxDVNrPKQsgMG9OvQGwFXGGm9GPhCUjUJQK9kYQOIzBKjSzuL0abNVavmtZgxfAjRMnPEtjhk0ciBI2nh1M+ZbSDUk7VPQzQUT+A5GsWo8qckpJ+Yy+NjKIIXpMeYNoM0uT9SDXm4Nx25ogO23SWt8YxC1uaoYhlYhxs8GG1KzqOw1g1L2YdgzSHnr5RZa0myb31k+HWJRX7wVDJqSMupAZf3HQlUxYIKtNkPtA6o2adJvWGMzQem5kGgkhKEf-OhF8r5weRpWJiabAgZWBAXeI+jwOVPw7UwjP8aEAPoWIuZM5yM9C8N8NBnxKLhAuYVTBWsDGVStRUvDZjhkCKIyIrjboPF8cQNul15zATNkiFg2kj7qQBw8DLQqgIdH9u8B4Jjcme4KdGb9axjUIn2OmW09TvRHUDEyoS9gE0YgjDylWyINmw7yag4phpznJmueie02Dk77XHQEyuj4LIXWORiOlpNsDd3Yf3da2zASCP1PGY0ugzS3MxLU4lkNGcANzCBN8QL-5OR5LrDRjsLY4jnMvaFulPdT0ecJRxAqYn1RVvmFqGeSHKJZQ5BNbWCr8uPWuWFnuDKzB5tqwWusJnMqk3ZDxBBqXBj1j8xyKYAQWS6Z8LupIQA */ createMachine< DatasetQualityControllerContext, DatasetQualityControllerEvent, @@ -132,18 +135,55 @@ export const createPureDatasetQualityControllerStateMachine = ( flyout: { initial: 'closed', states: { - fetching: { - invoke: { - src: 'loadDataStreamDetails', - onDone: { - target: 'loaded', - actions: ['storeDatasetDetails'], + initializing: { + type: 'parallel', + states: { + dataStreamDetails: { + initial: 'fetching', + states: { + fetching: { + invoke: { + src: 'loadDataStreamDetails', + onDone: { + target: 'done', + actions: ['storeDatasetDetails'], + }, + onError: { + target: 'done', + actions: ['fetchDatasetDetailsFailedNotifier'], + }, + }, + }, + done: { + type: 'final', + }, + }, }, - onError: { - target: 'loaded', - actions: ['fetchDatasetDetailsFailedNotifier'], + integrationDashboards: { + initial: 'fetching', + states: { + fetching: { + invoke: { + src: 'loadIntegrationDashboards', + onDone: { + target: 'done', + actions: ['storeIntegrationDashboards'], + }, + onError: { + target: 'done', + actions: ['notifyFetchIntegrationDashboardsFailed'], + }, + }, + }, + done: { + type: 'final', + }, + }, }, }, + onDone: { + target: '#DatasetQualityController.flyout.loaded', + }, }, loaded: { on: { @@ -159,7 +199,7 @@ export const createPureDatasetQualityControllerStateMachine = ( closed: { on: { OPEN_FLYOUT: { - target: '#DatasetQualityController.flyout.fetching', + target: '#DatasetQualityController.flyout.initializing', actions: ['storeFlyoutOptions'], }, }, @@ -167,7 +207,7 @@ export const createPureDatasetQualityControllerStateMachine = ( }, on: { SELECT_NEW_DATASET: { - target: '#DatasetQualityController.flyout.fetching', + target: '#DatasetQualityController.flyout.initializing', actions: ['storeFlyoutOptions'], }, CLOSE_FLYOUT: { @@ -292,6 +332,22 @@ export const createPureDatasetQualityControllerStateMachine = ( } : {}; }), + storeIntegrationDashboards: assign((context, event) => { + return 'data' in event && 'dashboards' in event.data + ? { + flyout: { + ...context.flyout, + dataset: { + ...context.flyout.dataset, + integration: { + ...context.flyout.dataset?.integration, + dashboards: event.data.dashboards as DashboardType[], + }, + } as FlyoutDataset, + }, + } + : {}; + }), storeDatasets: assign((context, _event) => { return context.dataStreamStats && context.degradedDocStats ? { @@ -312,12 +368,14 @@ export interface DatasetQualityControllerStateMachineDependencies { initialContext?: DatasetQualityControllerContext; toasts: IToasts; dataStreamStatsClient: IDataStreamsStatsClient; + dataStreamDetailsClient: IDataStreamDetailsClient; } export const createDatasetQualityControllerStateMachine = ({ initialContext = DEFAULT_CONTEXT, toasts, dataStreamStatsClient, + dataStreamDetailsClient, }: DatasetQualityControllerStateMachineDependencies) => createPureDatasetQualityControllerStateMachine(initialContext).withConfig({ actions: { @@ -327,6 +385,8 @@ export const createDatasetQualityControllerStateMachine = ({ fetchDegradedStatsFailedNotifier(toasts, event.data), notifyFetchDatasetDetailsFailed: (_context, event: DoneInvokeEvent) => fetchDatasetDetailsFailedNotifier(toasts, event.data), + notifyFetchIntegrationDashboardsFailed: (_context, event: DoneInvokeEvent) => + fetchIntegrationDashboardsFailedNotifier(toasts, event.data), }, services: { loadDataStreamStats: (context) => @@ -353,7 +413,7 @@ export const createDatasetQualityControllerStateMachine = ({ const { type, name: dataset, namespace } = context.flyout.dataset; - return dataStreamStatsClient.getDataStreamDetails({ + return dataStreamDetailsClient.getDataStreamDetails({ dataStream: dataStreamPartsToIndexName({ type: type as DataStreamType, dataset, @@ -361,6 +421,19 @@ export const createDatasetQualityControllerStateMachine = ({ }), }); }, + loadIntegrationDashboards: (context) => { + if (!context.flyout.dataset) { + fetchDatasetDetailsFailedNotifier(toasts, new Error(noDatasetSelected)); + + return Promise.resolve({}); + } + + const { integration } = context.flyout.dataset; + + return integration + ? dataStreamDetailsClient.getIntegrationDashboards({ integration: integration.name }) + : Promise.resolve({}); + }, }, }); diff --git a/x-pack/plugins/observability_solution/dataset_quality/public/state_machines/dataset_quality_controller/src/types.ts b/x-pack/plugins/observability_solution/dataset_quality/public/state_machines/dataset_quality_controller/src/types.ts index 967458b54bf9b..64c54d088ce1b 100644 --- a/x-pack/plugins/observability_solution/dataset_quality/public/state_machines/dataset_quality_controller/src/types.ts +++ b/x-pack/plugins/observability_solution/dataset_quality/public/state_machines/dataset_quality_controller/src/types.ts @@ -11,6 +11,7 @@ import { Integration } from '../../../../common/data_streams_stats/integration'; import { Direction, SortField } from '../../../hooks'; import { DegradedDocsStat } from '../../../../common/data_streams_stats/malformed_docs_stat'; import { + DashboardType, DataStreamDegradedDocsStatServiceResponse, DataStreamDetails, DataStreamStatServiceResponse, @@ -115,6 +116,14 @@ export type DatasetQualityControllerTypeState = | { value: 'datasets.loaded'; context: DefaultDatasetQualityStateContext; + } + | { + value: 'flyout.initializing.dataStreamDetails.fetching'; + context: DefaultDatasetQualityStateContext; + } + | { + value: 'flyout.initializing.integrationDashboards.fetching'; + context: DefaultDatasetQualityStateContext; }; export type DatasetQualityControllerContext = DatasetQualityControllerTypeState['context']; @@ -165,5 +174,6 @@ export type DatasetQualityControllerEvent = query: string; } | DoneInvokeEvent + | DoneInvokeEvent | DoneInvokeEvent | DoneInvokeEvent; diff --git a/x-pack/plugins/observability_solution/dataset_quality/server/routes/data_streams/get_integrations.ts b/x-pack/plugins/observability_solution/dataset_quality/server/routes/data_streams/get_integrations.ts index 44ffdc02a6731..f13e3661942c0 100644 --- a/x-pack/plugins/observability_solution/dataset_quality/server/routes/data_streams/get_integrations.ts +++ b/x-pack/plugins/observability_solution/dataset_quality/server/routes/data_streams/get_integrations.ts @@ -5,9 +5,56 @@ * 2.0. */ +import { SavedObjectsClientContract } from '@kbn/core-saved-objects-api-server'; +import { DASHBOARD_SAVED_OBJECT_TYPE } from '@kbn/deeplinks-analytics/constants'; import { PackageClient } from '@kbn/fleet-plugin/server'; import { PackageNotFoundError } from '@kbn/fleet-plugin/server/errors'; -import { DataStreamStat, Integration } from '../../../common/api_types'; +import { Dashboard, DataStreamStat, Integration } from '../../../common/api_types'; + +export async function getIntegrationDashboards( + packageClient: PackageClient, + savedObjectsClient: SavedObjectsClientContract, + integration: string +): Promise { + // Retrieve integration savedObject + const integrationSavedObjects = await packageClient.getInstallation(integration); + if (!integrationSavedObjects) return []; + + // Extract dashboard ids + const dashboardIds: string[] = []; + integrationSavedObjects.installed_kibana.forEach((intSavedObject) => { + if (intSavedObject.type === DASHBOARD_SAVED_OBJECT_TYPE) { + dashboardIds.push(intSavedObject.id); + } + }); + + // Fetch dashboards savedObject + // We are directly querying the SO here + // The dashboard service is not exposed from the server side at the moment + // https://github.com/elastic/kibana/issues/179759 + const dashboardsSavedObjects = await savedObjectsClient.bulkGet<{ + title?: string; + }>( + dashboardIds.map((id) => ({ + id, + type: DASHBOARD_SAVED_OBJECT_TYPE, + fields: ['title'], + })) + ); + + // Ignore faulty dashboards + const allValidDashboardSavedObjects = dashboardsSavedObjects.saved_objects.filter( + (so) => !so.error + ); + + // Construct dashboard result + const packageDashboards = allValidDashboardSavedObjects.map((so) => ({ + id: so.id, + title: so.attributes.title || so.id, + })); + + return packageDashboards; +} export async function getIntegrations(options: { packageClient: PackageClient; diff --git a/x-pack/plugins/observability_solution/dataset_quality/server/routes/data_streams/routes.ts b/x-pack/plugins/observability_solution/dataset_quality/server/routes/data_streams/routes.ts index dbca7e1d0b4bd..2bd5b3435acf2 100644 --- a/x-pack/plugins/observability_solution/dataset_quality/server/routes/data_streams/routes.ts +++ b/x-pack/plugins/observability_solution/dataset_quality/server/routes/data_streams/routes.ts @@ -13,6 +13,7 @@ import { DataStreamStat, DegradedDocs, Integration, + IntegrationDashboards, } from '../../../common/api_types'; import { rangeRt, typeRt } from '../../types/default_api_types'; import { createDatasetQualityServerRoute } from '../create_datasets_quality_server_route'; @@ -20,7 +21,7 @@ import { getDataStreamDetails } from './get_data_stream_details'; import { getDataStreams } from './get_data_streams'; import { getDataStreamsStats } from './get_data_streams_stats'; import { getDegradedDocsPaginated } from './get_degraded_docs'; -import { getIntegrations } from './get_integrations'; +import { getIntegrationDashboards, getIntegrations } from './get_integrations'; import { getEstimatedDataInBytes } from './get_estimated_data_in_bytes'; const statsRoute = createDatasetQualityServerRoute({ @@ -163,9 +164,40 @@ const estimatedDataInBytesRoute = createDatasetQualityServerRoute({ }, }); +const integrationDashboardsRoute = createDatasetQualityServerRoute({ + endpoint: 'GET /internal/dataset_quality/integrations/{integration}/dashboards', + params: t.type({ + path: t.type({ + integration: t.string, + }), + }), + options: { + tags: [], + }, + async handler(resources): Promise { + const { context, params, plugins } = resources; + const { integration } = params.path; + const { savedObjects } = await context.core; + + const fleetPluginStart = await plugins.fleet.start(); + const packageClient = fleetPluginStart.packageService.asInternalUser; + + const integrationDashboards = await getIntegrationDashboards( + packageClient, + savedObjects.client, + integration + ); + + return { + dashboards: integrationDashboards, + }; + }, +}); + export const dataStreamsRouteRepository = { ...statsRoute, ...degradedDocsRoute, ...dataStreamDetailsRoute, ...estimatedDataInBytesRoute, + ...integrationDashboardsRoute, }; diff --git a/x-pack/plugins/observability_solution/dataset_quality/tsconfig.json b/x-pack/plugins/observability_solution/dataset_quality/tsconfig.json index e59d2baacde27..37201afd73082 100644 --- a/x-pack/plugins/observability_solution/dataset_quality/tsconfig.json +++ b/x-pack/plugins/observability_solution/dataset_quality/tsconfig.json @@ -36,7 +36,10 @@ "@kbn/data-views-plugin", "@kbn/shared-ux-error-boundary", "@kbn/embeddable-plugin", - "@kbn/es-query" + "@kbn/es-query", + "@kbn/core-saved-objects-api-server", + "@kbn/deeplinks-management", + "@kbn/deeplinks-analytics" ], "exclude": ["target/**/*"] } diff --git a/x-pack/plugins/observability_solution/observability_ai_assistant/public/service/create_chat_service.ts b/x-pack/plugins/observability_solution/observability_ai_assistant/public/service/create_chat_service.ts index 3b94b29bd0d32..e0ec99719d68a 100644 --- a/x-pack/plugins/observability_solution/observability_ai_assistant/public/service/create_chat_service.ts +++ b/x-pack/plugins/observability_solution/observability_ai_assistant/public/service/create_chat_service.ts @@ -27,6 +27,7 @@ import { StreamingChatResponseEventType, type StreamingChatResponseEventWithoutError, type StreamingChatResponseEvent, + TokenCountEvent, } from '../../common/conversation_complete'; import { FunctionRegistry, @@ -163,10 +164,17 @@ export async function createChatService({ const subscription = toObservable(response) .pipe( - map((line) => JSON.parse(line) as StreamingChatResponseEvent | BufferFlushEvent), + map( + (line) => + JSON.parse(line) as + | StreamingChatResponseEvent + | BufferFlushEvent + | TokenCountEvent + ), filter( (line): line is StreamingChatResponseEvent => - line.type !== StreamingChatResponseEventType.BufferFlush + line.type !== StreamingChatResponseEventType.BufferFlush && + line.type !== StreamingChatResponseEventType.TokenCount ), throwSerializedChatCompletionErrors() ) diff --git a/x-pack/plugins/observability_solution/observability_ai_assistant/scripts/evaluation/kibana_client.ts b/x-pack/plugins/observability_solution/observability_ai_assistant/scripts/evaluation/kibana_client.ts index 89c74661b9724..a3385be2950ff 100644 --- a/x-pack/plugins/observability_solution/observability_ai_assistant/scripts/evaluation/kibana_client.ts +++ b/x-pack/plugins/observability_solution/observability_ai_assistant/scripts/evaluation/kibana_client.ts @@ -21,6 +21,7 @@ import { MessageAddEvent, StreamingChatResponseEvent, StreamingChatResponseEventType, + TokenCountEvent, } from '../../common/conversation_complete'; import { ObservabilityAIAssistantScreenContext } from '../../common/types'; import { concatenateChatCompletionChunks } from '../../common/utils/concatenate_chat_completion_chunks'; @@ -240,11 +241,15 @@ export class KibanaClient { .split('\n') .map((line) => line.trim()) .filter(Boolean) - .map((line) => JSON.parse(line) as StreamingChatResponseEvent | BufferFlushEvent) + .map( + (line) => + JSON.parse(line) as StreamingChatResponseEvent | BufferFlushEvent | TokenCountEvent + ) ), filter( (line): line is ChatCompletionChunkEvent | ChatCompletionErrorEvent => - line.type !== StreamingChatResponseEventType.BufferFlush + line.type === StreamingChatResponseEventType.ChatCompletionChunk || + line.type === StreamingChatResponseEventType.ChatCompletionError ), throwSerializedChatCompletionErrors(), concatenateChatCompletionChunks() @@ -329,7 +334,13 @@ export class KibanaClient { .split('\n') .map((line) => line.trim()) .filter(Boolean) - .map((line) => JSON.parse(line) as StreamingChatResponseEvent | BufferFlushEvent) + .map( + (line) => + JSON.parse(line) as + | StreamingChatResponseEvent + | BufferFlushEvent + | TokenCountEvent + ) ), filter( (event): event is MessageAddEvent | ConversationCreateEvent => diff --git a/x-pack/plugins/observability_solution/observability_ai_assistant/scripts/evaluation/read_kibana_config.ts b/x-pack/plugins/observability_solution/observability_ai_assistant/scripts/evaluation/read_kibana_config.ts index 5b64bb2f56189..a765adcf52f5b 100644 --- a/x-pack/plugins/observability_solution/observability_ai_assistant/scripts/evaluation/read_kibana_config.ts +++ b/x-pack/plugins/observability_solution/observability_ai_assistant/scripts/evaluation/read_kibana_config.ts @@ -13,7 +13,7 @@ import { identity, pickBy } from 'lodash'; export type KibanaConfig = ReturnType; export const readKibanaConfig = () => { - const kibanaConfigDir = path.join(__filename, '../../../../../../config'); + const kibanaConfigDir = path.join(__filename, '../../../../../../../config'); const kibanaDevConfig = path.join(kibanaConfigDir, 'kibana.dev.yml'); const kibanaConfig = path.join(kibanaConfigDir, 'kibana.yml'); diff --git a/x-pack/plugins/observability_solution/observability_ai_assistant/server/service/client/index.ts b/x-pack/plugins/observability_solution/observability_ai_assistant/server/service/client/index.ts index b4a23bffc5331..679b1fca1f249 100644 --- a/x-pack/plugins/observability_solution/observability_ai_assistant/server/service/client/index.ts +++ b/x-pack/plugins/observability_solution/observability_ai_assistant/server/service/client/index.ts @@ -436,7 +436,7 @@ export class ObservabilityAIAssistantClient { `Token count for conversation: ${JSON.stringify(tokenCountResult)}` ); - apm.addLabels({ + apm.currentTransaction?.addLabels({ tokenCountPrompt: tokenCountResult.prompt, tokenCountCompletion: tokenCountResult.completion, tokenCountTotal: tokenCountResult.total, @@ -632,28 +632,37 @@ export class ObservabilityAIAssistantClient { signal.addEventListener('abort', () => response.destroy()); const response$ = adapter.streamIntoObservable(response).pipe(shareReplay()); + response$ .pipe(rejectTokenCountEvents(), concatenateChatCompletionChunks(), lastOperator()) .subscribe({ error: (error) => { this.dependencies.logger.debug('Error in chat response'); this.dependencies.logger.debug(error); + span?.setOutcome('failure'); + span?.end(); }, next: (message) => { this.dependencies.logger.debug(`Received message:\n${JSON.stringify(message)}`); }, + complete: () => { + span?.setOutcome('success'); + span?.end(); + }, }); - lastValueFrom(response$) - .then(() => { - span?.setOutcome('success'); - }) - .catch(() => { - span?.setOutcome('failure'); - }) - .finally(() => { - span?.end(); - }); + response$.subscribe({ + next: (event) => { + if (event.type === StreamingChatResponseEventType.TokenCount) { + span?.addLabels({ + tokenCountPrompt: event.tokens.prompt, + tokenCountCompletion: event.tokens.completion, + tokenCountTotal: event.tokens.total, + }); + } + }, + error: () => {}, + }); return response$; } catch (error) { diff --git a/x-pack/plugins/observability_solution/observability_ai_assistant_app/public/components/buttons/new_chat_button.tsx b/x-pack/plugins/observability_solution/observability_ai_assistant_app/public/components/buttons/new_chat_button.tsx index 6dadba00f2394..75cede6344c59 100644 --- a/x-pack/plugins/observability_solution/observability_ai_assistant_app/public/components/buttons/new_chat_button.tsx +++ b/x-pack/plugins/observability_solution/observability_ai_assistant_app/public/components/buttons/new_chat_button.tsx @@ -4,7 +4,7 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ -import React, { SVGProps } from 'react'; +import React from 'react'; import { EuiButton, EuiButtonIcon } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; @@ -13,8 +13,11 @@ export function NewChatButton( ) { const { collapsed, ...nextProps } = props; return !props.collapsed ? ( - - + {i18n.translate('xpack.observabilityAiAssistant.newChatButton', { defaultMessage: 'New chat', })} @@ -22,23 +25,9 @@ export function NewChatButton( ) : ( ); } - -// To-do: replace with Eui icon once https://github.com/elastic/eui/pull/7524 is merged. -export function EuiIconNewChat({ ...props }: SVGProps) { - return ( - - - - - ); -} diff --git a/x-pack/plugins/observability_solution/observability_ai_assistant_app/public/components/chat/chat_body.tsx b/x-pack/plugins/observability_solution/observability_ai_assistant_app/public/components/chat/chat_body.tsx index b5cd2cd0683e3..4a5d7d38c3e63 100644 --- a/x-pack/plugins/observability_solution/observability_ai_assistant_app/public/components/chat/chat_body.tsx +++ b/x-pack/plugins/observability_solution/observability_ai_assistant_app/public/components/chat/chat_body.tsx @@ -90,6 +90,11 @@ const animClassName = css` ${euiThemeVars.euiAnimSlightBounce} ${euiThemeVars.euiAnimSpeedNormal} forwards; `; +const containerClassName = css` + min-width: 0; + max-height: 100%; +`; + const PADDING_AND_BORDER = 32; export function ChatBody({ @@ -154,12 +159,6 @@ export function ChatBody({ } } - const containerClassName = css` - background: white; - min-width: 0; - max-height: 100%; - `; - const headerContainerClassName = css` padding-right: ${showLinkToConversationsApp ? '32px' : '0'}; `; diff --git a/x-pack/plugins/observability_solution/observability_ai_assistant_app/public/components/chat/chat_flyout.tsx b/x-pack/plugins/observability_solution/observability_ai_assistant_app/public/components/chat/chat_flyout.tsx index 46f64e7a9f003..4194f9a2ca0c4 100644 --- a/x-pack/plugins/observability_solution/observability_ai_assistant_app/public/components/chat/chat_flyout.tsx +++ b/x-pack/plugins/observability_solution/observability_ai_assistant_app/public/components/chat/chat_flyout.tsx @@ -147,6 +147,10 @@ export function ChatFlyout({ }} > { diff --git a/x-pack/plugins/observability_solution/synthetics/server/routes/default_alerts/default_alert_service.ts b/x-pack/plugins/observability_solution/synthetics/server/routes/default_alerts/default_alert_service.ts index 95b899d1fe03f..1526a78cbf75f 100644 --- a/x-pack/plugins/observability_solution/synthetics/server/routes/default_alerts/default_alert_service.ts +++ b/x-pack/plugins/observability_solution/synthetics/server/routes/default_alerts/default_alert_service.ts @@ -82,12 +82,11 @@ export class DefaultAlertService { }, }); - const alert = data?.[0]; - if (!alert) { + if (data.length === 0) { return; } - - return { ...alert, ruleTypeId: alert.alertTypeId }; + const { actions = [], systemActions = [], ...alert } = data[0]; + return { ...alert, actions: [...actions, ...systemActions], ruleTypeId: alert.alertTypeId }; } async createDefaultAlertIfNotExist(ruleType: DefaultRuleType, name: string, interval: string) { const alert = await this.getExistingAlert(ruleType); @@ -96,9 +95,12 @@ export class DefaultAlertService { } const actions = await this.getAlertActions(ruleType); - const rulesClient = (await this.context.alerting)?.getRulesClient(); - const newAlert = await rulesClient.create<{}>({ + const { + actions: actionsFromRules = [], + systemActions = [], + ...newAlert + } = await rulesClient.create<{}>({ data: { actions, params: {}, @@ -111,7 +113,12 @@ export class DefaultAlertService { throttle: null, }, }); - return { ...newAlert, ruleTypeId: newAlert.alertTypeId }; + + return { + ...newAlert, + actions: [...actionsFromRules, ...systemActions], + ruleTypeId: newAlert.alertTypeId, + }; } updateStatusRule() { @@ -127,7 +134,11 @@ export class DefaultAlertService { const alert = await this.getExistingAlert(ruleType); if (alert) { const actions = await this.getAlertActions(ruleType); - const updatedAlert = await rulesClient.update({ + const { + actions: actionsFromRules = [], + systemActions = [], + ...updatedAlert + } = await rulesClient.update({ id: alert.id, data: { actions, @@ -137,7 +148,11 @@ export class DefaultAlertService { params: alert.params, }, }); - return { ...updatedAlert, ruleTypeId: updatedAlert.alertTypeId }; + return { + ...updatedAlert, + actions: [...actionsFromRules, ...systemActions], + ruleTypeId: updatedAlert.alertTypeId, + }; } return await this.createDefaultAlertIfNotExist(ruleType, name, interval); diff --git a/x-pack/plugins/security/public/account_management/user_profile/user_profile_api_client.test.ts b/x-pack/plugins/security/public/account_management/user_profile/user_profile_api_client.test.ts index 0c275db2c1a90..f84113d6afddd 100644 --- a/x-pack/plugins/security/public/account_management/user_profile/user_profile_api_client.test.ts +++ b/x-pack/plugins/security/public/account_management/user_profile/user_profile_api_client.test.ts @@ -5,7 +5,10 @@ * 2.0. */ +import { firstValueFrom } from 'rxjs'; + import { coreMock } from '@kbn/core/public/mocks'; +import { kibanaResponseFactory } from '@kbn/core-http-router-server-internal'; import { UserProfileAPIClient } from './user_profile_api_client'; @@ -20,6 +23,35 @@ describe('UserProfileAPIClient', () => { apiClient = new UserProfileAPIClient(coreStart.http); }); + it('should enable the user profile after fetching the data', async () => { + const promiseEnabled = firstValueFrom(apiClient.enabled$); + apiClient.start(); // Start will fetch the user profile data + const enabled = await promiseEnabled; + expect(enabled).toBe(true); + }); + + it('should not enable the user profile if we get a 404 from fetching the profile', async () => { + const err = new Error('Awwww'); + (err as any).response = kibanaResponseFactory.notFound(); + coreStart.http.get.mockRejectedValue(err); + + const promiseEnabled = firstValueFrom(apiClient.enabled$); + apiClient.start(); // Start will fetch the user profile data + + const enabled = await promiseEnabled; + expect(enabled).toBe(false); + }); + + it('should enable the user profile for any other error than a 404', async () => { + coreStart.http.get.mockRejectedValue(new Error('Awwww')); + + const promiseEnabled = firstValueFrom(apiClient.enabled$); + apiClient.start(); + + const enabled = await promiseEnabled; + expect(enabled).toBe(true); + }); + it('should get user profile without retrieving any user data', async () => { await apiClient.getCurrent(); expect(coreStart.http.get).toHaveBeenCalledWith('/internal/security/user_profile', { diff --git a/x-pack/plugins/security/public/account_management/user_profile/user_profile_api_client.ts b/x-pack/plugins/security/public/account_management/user_profile/user_profile_api_client.ts index 597b93236a2a5..9dbd3fa50b595 100644 --- a/x-pack/plugins/security/public/account_management/user_profile/user_profile_api_client.ts +++ b/x-pack/plugins/security/public/account_management/user_profile/user_profile_api_client.ts @@ -7,7 +7,7 @@ import { merge } from 'lodash'; import type { Observable } from 'rxjs'; -import { BehaviorSubject, Subject } from 'rxjs'; +import { BehaviorSubject, distinctUntilChanged, skipWhile, Subject, switchMap } from 'rxjs'; import type { HttpStart } from '@kbn/core/public'; import type { @@ -20,6 +20,8 @@ import type { UserProfileData } from '@kbn/user-profile-components'; import type { GetUserProfileResponse, UserProfile } from '../../../common'; +const DEFAULT_DATAPATHS = 'avatar,userSettings'; + export class UserProfileAPIClient implements UserProfileAPIClientType { private readonly internalDataUpdates$: Subject = new Subject(); @@ -30,11 +32,31 @@ export class UserProfileAPIClient implements UserProfileAPIClientType { this.internalDataUpdates$.asObservable(); private readonly _userProfile$ = new BehaviorSubject(null); + private readonly _enabled$ = new BehaviorSubject(false); + private readonly _userProfileLoaded$ = new BehaviorSubject(false); /** Observable of the current user profile data */ public readonly userProfile$ = this._userProfile$.asObservable(); + public readonly userProfileLoaded$ = this._userProfileLoaded$ + .asObservable() + .pipe(distinctUntilChanged()); + public enabled$: Observable; + + constructor(private readonly http: HttpStart) { + this.enabled$ = this.userProfileLoaded$.pipe( + skipWhile((loaded) => !loaded), + switchMap(() => this._enabled$.asObservable()), + distinctUntilChanged() + ); + } - constructor(private readonly http: HttpStart) {} + public start() { + // Fetch the user profile with default path to initialize the user profile observable. + // This will also enable or not the user profile for the user by checking if we receive a 404 on this request. + this.getCurrent({ dataPath: DEFAULT_DATAPATHS }).catch(() => { + // silently ignore the error + }); + } /** * Retrieves the user profile of the current user. If the profile isn't available, e.g. for the anonymous users or @@ -51,8 +73,20 @@ export class UserProfileAPIClient implements UserProfileAPIClientType { .then((response) => { const data = response?.data ?? {}; const updated = merge(this._userProfile$.getValue(), data); + this._userProfile$.next(updated); + this._enabled$.next(true); + this._userProfileLoaded$.next(true); + return response; + }) + .catch((err) => { + // If we receive a 404 on the request, it means there are no user profile for the user. + const notFound = err?.response?.status === 404; + this._enabled$.next(notFound ? false : true); + this._userProfileLoaded$.next(true); + + return Promise.reject(err); }); } @@ -112,4 +146,13 @@ export class UserProfileAPIClient implements UserProfileAPIClientType { return Promise.reject(err); }); } + + /** + * Updates user profile data of the current user. + * @param data Application data to be written (merged with existing data). + */ + public partialUpdate>(data: D) { + const updated = merge(this._userProfile$.getValue(), data); + return this.update(updated); + } } diff --git a/x-pack/plugins/security/public/mocks.ts b/x-pack/plugins/security/public/mocks.ts index 2e3b19016fd2e..edfc42aae585e 100644 --- a/x-pack/plugins/security/public/mocks.ts +++ b/x-pack/plugins/security/public/mocks.ts @@ -31,7 +31,10 @@ function createStartMock() { bulkGet: jest.fn(), suggest: jest.fn(), update: jest.fn(), + partialUpdate: jest.fn(), userProfile$: of({}), + userProfileLoaded$: of(true), + enabled$: of(true), }, uiApi: getUiApiMock.createStart(), }; diff --git a/x-pack/plugins/security/public/plugin.test.tsx b/x-pack/plugins/security/public/plugin.test.tsx index 4a66f7b0cbfdf..d3dadbfb87665 100644 --- a/x-pack/plugins/security/public/plugin.test.tsx +++ b/x-pack/plugins/security/public/plugin.test.tsx @@ -15,25 +15,29 @@ import { licensingMock } from '@kbn/licensing-plugin/public/mocks'; import { managementPluginMock } from '@kbn/management-plugin/public/mocks'; import { stubBroadcastChannel } from '@kbn/test-jest-helpers'; +import { UserProfileAPIClient } from './account_management'; import { ManagementService } from './management'; import type { PluginStartDependencies } from './plugin'; import { SecurityPlugin } from './plugin'; stubBroadcastChannel(); +const getCoreSetupMock = () => { + const coreSetup = coreMock.createSetup({ + basePath: '/some-base-path', + }); + coreSetup.http.get.mockResolvedValue({}); + return coreSetup; +}; + describe('Security Plugin', () => { describe('#setup', () => { it('should be able to setup if optional plugins are not available', () => { const plugin = new SecurityPlugin(coreMock.createPluginInitializerContext()); expect( - plugin.setup( - coreMock.createSetup({ - basePath: '/some-base-path', - }) as CoreSetup, - { - licensing: licensingMock.createSetup(), - } - ) + plugin.setup(getCoreSetupMock(), { + licensing: licensingMock.createSetup(), + }) ).toEqual({ authc: { getCurrentUser: expect.any(Function), areAPIKeysEnabled: expect.any(Function) }, authz: { isRoleManagementEnabled: expect.any(Function) }, @@ -49,7 +53,7 @@ describe('Security Plugin', () => { }); it('setups Management Service if `management` plugin is available', () => { - const coreSetupMock = coreMock.createSetup({ basePath: '/some-base-path' }); + const coreSetupMock = getCoreSetupMock(); const setupManagementServiceMock = jest .spyOn(ManagementService.prototype, 'setup') .mockImplementation(() => {}); @@ -81,7 +85,7 @@ describe('Security Plugin', () => { }); it('calls core.security.registerSecurityApi', () => { - const coreSetupMock = coreMock.createSetup({ basePath: '/some-base-path' }); + const coreSetupMock = getCoreSetupMock(); const plugin = new SecurityPlugin(coreMock.createPluginInitializerContext()); @@ -96,10 +100,7 @@ describe('Security Plugin', () => { describe('#start', () => { it('should be able to setup if optional plugins are not available', () => { const plugin = new SecurityPlugin(coreMock.createPluginInitializerContext()); - plugin.setup( - coreMock.createSetup({ basePath: '/some-base-path' }) as CoreSetup, - { licensing: licensingMock.createSetup() } - ); + plugin.setup(getCoreSetupMock(), { licensing: licensingMock.createSetup() }); expect( plugin.start(coreMock.createStart({ basePath: '/some-base-path' }), { @@ -127,7 +128,31 @@ describe('Security Plugin', () => { }, "userProfiles": Object { "bulkGet": [Function], + "enabled$": Observable { + "operator": [Function], + "source": Observable { + "operator": [Function], + "source": Observable { + "operator": [Function], + "source": Observable { + "operator": [Function], + "source": Observable { + "source": BehaviorSubject { + "_value": false, + "closed": false, + "currentObservers": null, + "hasError": false, + "isStopped": false, + "observers": Array [], + "thrownError": null, + }, + }, + }, + }, + }, + }, "getCurrent": [Function], + "partialUpdate": [Function], "suggest": [Function], "update": [Function], "userProfile$": Observable { @@ -141,6 +166,20 @@ describe('Security Plugin', () => { "thrownError": null, }, }, + "userProfileLoaded$": Observable { + "operator": [Function], + "source": Observable { + "source": BehaviorSubject { + "_value": false, + "closed": false, + "currentObservers": null, + "hasError": false, + "isStopped": false, + "observers": Array [], + "thrownError": null, + }, + }, + }, }, } `); @@ -156,13 +195,10 @@ describe('Security Plugin', () => { const plugin = new SecurityPlugin(coreMock.createPluginInitializerContext()); - plugin.setup( - coreMock.createSetup({ basePath: '/some-base-path' }) as CoreSetup, - { - licensing: licensingMock.createSetup(), - management: managementSetupMock, - } - ); + plugin.setup(getCoreSetupMock(), { + licensing: licensingMock.createSetup(), + management: managementSetupMock, + }); const coreStart = coreMock.createStart({ basePath: '/some-base-path' }); plugin.start(coreStart, { @@ -173,15 +209,28 @@ describe('Security Plugin', () => { expect(startManagementServiceMock).toHaveBeenCalledTimes(1); }); + + it('calls UserProfileAPIClient start() to fetch the user profile', () => { + const startUserProfileAPIClient = jest + .spyOn(UserProfileAPIClient.prototype, 'start') + .mockImplementation(() => {}); + const plugin = new SecurityPlugin(coreMock.createPluginInitializerContext()); + plugin.setup(getCoreSetupMock(), { licensing: licensingMock.createSetup() }); + + const coreStart = coreMock.createStart({ basePath: '/some-base-path' }); + plugin.start(coreStart, { + dataViews: {} as DataViewsPublicPluginStart, + features: {} as FeaturesPluginStart, + }); + + expect(startUserProfileAPIClient).toHaveBeenCalledTimes(1); + }); }); describe('#stop', () => { it('does not fail if called before `start`.', () => { const plugin = new SecurityPlugin(coreMock.createPluginInitializerContext()); - plugin.setup( - coreMock.createSetup({ basePath: '/some-base-path' }) as CoreSetup, - { licensing: licensingMock.createSetup() } - ); + plugin.setup(getCoreSetupMock(), { licensing: licensingMock.createSetup() }); expect(() => plugin.stop()).not.toThrow(); }); @@ -189,10 +238,7 @@ describe('Security Plugin', () => { it('does not fail if called during normal plugin life cycle.', () => { const plugin = new SecurityPlugin(coreMock.createPluginInitializerContext()); - plugin.setup( - coreMock.createSetup({ basePath: '/some-base-path' }) as CoreSetup, - { licensing: licensingMock.createSetup() } - ); + plugin.setup(getCoreSetupMock(), { licensing: licensingMock.createSetup() }); plugin.start(coreMock.createStart({ basePath: '/some-base-path' }), { dataViews: {} as DataViewsPublicPluginStart, diff --git a/x-pack/plugins/security/public/plugin.tsx b/x-pack/plugins/security/public/plugin.tsx index b99d2898bc344..1aaf7a882d6a2 100644 --- a/x-pack/plugins/security/public/plugin.tsx +++ b/x-pack/plugins/security/public/plugin.tsx @@ -200,6 +200,7 @@ export class SecurityPlugin this.sessionTimeout.start(); this.securityCheckupService.start({ http, notifications, docLinks }); + this.securityApiClients.userProfiles.start(); if (management) { this.managementService.start({ @@ -231,7 +232,12 @@ export class SecurityPlugin update: this.securityApiClients.userProfiles.update.bind( this.securityApiClients.userProfiles ), + partialUpdate: this.securityApiClients.userProfiles.partialUpdate.bind( + this.securityApiClients.userProfiles + ), userProfile$: this.securityApiClients.userProfiles.userProfile$, + userProfileLoaded$: this.securityApiClients.userProfiles.userProfileLoaded$, + enabled$: this.securityApiClients.userProfiles.enabled$, }, }; } diff --git a/x-pack/plugins/security/tsconfig.json b/x-pack/plugins/security/tsconfig.json index 9fdbe3fd0e372..46f4e3f51d973 100644 --- a/x-pack/plugins/security/tsconfig.json +++ b/x-pack/plugins/security/tsconfig.json @@ -75,6 +75,7 @@ "@kbn/code-editor-mock", "@kbn/core-security-browser", "@kbn/core-security-server", + "@kbn/core-http-router-server-internal", ], "exclude": [ "target/**/*", diff --git a/x-pack/plugins/security_solution/public/app/home/template_wrapper/index.test.tsx b/x-pack/plugins/security_solution/public/app/home/template_wrapper/index.test.tsx index 322249daeaac1..c1d3760813a15 100644 --- a/x-pack/plugins/security_solution/public/app/home/template_wrapper/index.test.tsx +++ b/x-pack/plugins/security_solution/public/app/home/template_wrapper/index.test.tsx @@ -8,7 +8,7 @@ import { render, waitFor } from '@testing-library/react'; import React from 'react'; import { TestProviders } from '../../../common/mock'; -import { SecuritySolutionTemplateWrapper } from '.'; +import { SecuritySolutionTemplateWrapper, type SecuritySolutionTemplateWrapperProps } from '.'; import { SecurityPageName } from '../../types'; const mockUseShowTimeline = jest.fn((): [boolean] => [false]); @@ -56,15 +56,15 @@ jest.mock('../../../common/utils/route/use_route_spy', () => ({ useRouteSpy: () => mockUseRouteSpy(), })); -const renderComponent = () => { - return render( +const renderComponent = ({ + children =
{'child of wrapper'}
, + ...props +}: SecuritySolutionTemplateWrapperProps = {}) => + render( - -
{'child of wrapper'}
-
+ {children}
); -}; describe('SecuritySolutionTemplateWrapper', () => { beforeEach(() => { @@ -91,4 +91,16 @@ describe('SecuritySolutionTemplateWrapper', () => { expect(queryByText('Timeline')).not.toBeInTheDocument(); }); }); + + it('Should render emptyPageBody when isEmptyState is true', async () => { + mockUseShowTimeline.mockReturnValue([false]); + + const { getByText } = renderComponent({ + isEmptyState: true, + emptyPageBody:
{'empty page body'}
, + children: null, + }); + + expect(getByText('empty page body')).toBeInTheDocument(); + }); }); diff --git a/x-pack/plugins/security_solution/public/app/home/template_wrapper/index.tsx b/x-pack/plugins/security_solution/public/app/home/template_wrapper/index.tsx index 34e339e974a23..19e8d55aa2dd5 100644 --- a/x-pack/plugins/security_solution/public/app/home/template_wrapper/index.tsx +++ b/x-pack/plugins/security_solution/public/app/home/template_wrapper/index.tsx @@ -5,7 +5,7 @@ * 2.0. */ -import React, { useMemo } from 'react'; +import React, { type ReactNode, useMemo } from 'react'; import styled from 'styled-components'; import { EuiThemeProvider, useEuiTheme, type EuiThemeComputed } from '@elastic/eui'; import { IS_DRAGGING_CLASS_NAME } from '@kbn/securitysolution-t-grid'; @@ -46,7 +46,14 @@ const StyledKibanaPageTemplate = styled(KibanaPageTemplate)< } `; -export const SecuritySolutionTemplateWrapper: React.FC> = +export type SecuritySolutionTemplateWrapperProps = Omit & { + /** + * Combined with isEmptyState, this prop allows complete override of the empty page + */ + emptyPageBody?: ReactNode; +}; + +export const SecuritySolutionTemplateWrapper: React.FC = React.memo(({ children, ...rest }) => { const solutionNavProps = useSecuritySolutionNavigation(); const [isTimelineBottomBarVisible] = useShowTimeline(); @@ -61,6 +68,10 @@ export const SecuritySolutionTemplateWrapper: React.FC - - - - {children} - - - - {isTimelineBottomBarVisible && ( - - - - - + {isNotEmpty && ( + <> + + + + {children} + - - + + {isTimelineBottomBarVisible && ( + + + + + + + + + )} + )} ); diff --git a/x-pack/plugins/security_solution/public/assistant/content/conversations/index.tsx b/x-pack/plugins/security_solution/public/assistant/content/conversations/index.tsx index 2b580e1257a8a..53fb26d205720 100644 --- a/x-pack/plugins/security_solution/public/assistant/content/conversations/index.tsx +++ b/x-pack/plugins/security_solution/public/assistant/content/conversations/index.tsx @@ -22,7 +22,7 @@ export const BASE_SECURITY_CONVERSATIONS: Record = { category: 'assistant', isDefault: true, messages: [], - replacements: [], + replacements: {}, }, [DATA_QUALITY_DASHBOARD_CONVERSATION_ID]: { id: '', @@ -30,7 +30,7 @@ export const BASE_SECURITY_CONVERSATIONS: Record = { category: 'assistant', isDefault: true, messages: [], - replacements: [], + replacements: {}, }, [DETECTION_RULES_CONVERSATION_ID]: { id: '', @@ -38,7 +38,7 @@ export const BASE_SECURITY_CONVERSATIONS: Record = { category: 'assistant', isDefault: true, messages: [], - replacements: [], + replacements: {}, }, [EVENT_SUMMARY_CONVERSATION_ID]: { id: '', @@ -46,7 +46,7 @@ export const BASE_SECURITY_CONVERSATIONS: Record = { category: 'assistant', isDefault: true, messages: [], - replacements: [], + replacements: {}, }, [TIMELINE_CONVERSATION_TITLE]: { excludeFromLastConversationStorage: true, @@ -55,7 +55,7 @@ export const BASE_SECURITY_CONVERSATIONS: Record = { category: 'assistant', isDefault: true, messages: [], - replacements: [], + replacements: {}, }, [WELCOME_CONVERSATION_TITLE]: { id: '', @@ -63,6 +63,6 @@ export const BASE_SECURITY_CONVERSATIONS: Record = { category: 'assistant', isDefault: true, messages: [], - replacements: [], + replacements: {}, }, }; diff --git a/x-pack/plugins/security_solution/public/assistant/get_comments/index.test.tsx b/x-pack/plugins/security_solution/public/assistant/get_comments/index.test.tsx index 88380450974df..ebae8b35b25b5 100644 --- a/x-pack/plugins/security_solution/public/assistant/get_comments/index.test.tsx +++ b/x-pack/plugins/security_solution/public/assistant/get_comments/index.test.tsx @@ -15,7 +15,7 @@ const currentConversation = { connectorId: 'c29c28a0-20fe-11ee-9306-a1f4d42ec542', provider: OpenAiProviderType.OpenAi, }, - replacements: [], + replacements: {}, category: 'assistant', id: '1', title: '1', @@ -50,7 +50,7 @@ describe('getComments', () => { connectorId: 'c29c28a0-20fe-11ee-9306-a1f4d42ec542', provider: OpenAiProviderType.OpenAi, }, - replacements: [], + replacements: {}, id: '1', title: '1', messages: [ diff --git a/x-pack/plugins/security_solution/public/assistant/get_comments/index.tsx b/x-pack/plugins/security_solution/public/assistant/get_comments/index.tsx index 42fd3f02f8da7..8ceb89d908586 100644 --- a/x-pack/plugins/security_solution/public/assistant/get_comments/index.tsx +++ b/x-pack/plugins/security_solution/public/assistant/get_comments/index.tsx @@ -11,7 +11,7 @@ import { EuiAvatar, EuiLoadingSpinner } from '@elastic/eui'; import React from 'react'; import { AssistantAvatar } from '@kbn/elastic-assistant'; -import type { Replacement } from '@kbn/elastic-assistant-common'; +import type { Replacements } from '@kbn/elastic-assistant-common'; import { replaceAnonymizedValuesWithOriginalValues } from '@kbn/elastic-assistant-common'; import { StreamComment } from './stream'; import { CommentActions } from '../comment_actions'; @@ -29,7 +29,7 @@ const transformMessageWithReplacements = ({ message: Message; content: string; showAnonymizedValues: boolean; - replacements: Replacement[]; + replacements: Replacements; }): ContentMessage => { return { ...message, diff --git a/x-pack/plugins/security_solution/public/assistant/provider.tsx b/x-pack/plugins/security_solution/public/assistant/provider.tsx index 5cee36334542e..d749e10701f01 100644 --- a/x-pack/plugins/security_solution/public/assistant/provider.tsx +++ b/x-pack/plugins/security_solution/public/assistant/provider.tsx @@ -86,12 +86,7 @@ export const createConversations = async ( ...c, messages: (c.messages ?? []).map(transformMessage), title: c.id, - replacements: c.replacements - ? Object.keys(c.replacements).map((uuid) => ({ - uuid, - value: c.replacements[uuid], - })) - : [], + replacements: c.replacements, }; return res; }, {}), diff --git a/x-pack/plugins/security_solution/public/assistant/use_assistant_telemetry/index.test.tsx b/x-pack/plugins/security_solution/public/assistant/use_assistant_telemetry/index.test.tsx index a60fa24a9f489..d714781ee11c9 100644 --- a/x-pack/plugins/security_solution/public/assistant/use_assistant_telemetry/index.test.tsx +++ b/x-pack/plugins/security_solution/public/assistant/use_assistant_telemetry/index.test.tsx @@ -16,7 +16,7 @@ const mockedConversations = { [customId]: { id: customId, apiConfig: {}, - replacements: [], + replacements: {}, messages: [], }, }; @@ -49,7 +49,7 @@ jest.mock('@kbn/elastic-assistant', () => ({ id: customId, title: 'Custom', apiConfig: {}, - replacements: [], + replacements: {}, messages: [], }), })); diff --git a/x-pack/plugins/security_solution/public/detections/components/alerts_table/timeline_actions/use_investigate_in_timeline.tsx b/x-pack/plugins/security_solution/public/detections/components/alerts_table/timeline_actions/use_investigate_in_timeline.tsx index be65aeb78d989..0cbd2daa58221 100644 --- a/x-pack/plugins/security_solution/public/detections/components/alerts_table/timeline_actions/use_investigate_in_timeline.tsx +++ b/x-pack/plugins/security_solution/public/detections/components/alerts_table/timeline_actions/use_investigate_in_timeline.tsx @@ -168,7 +168,12 @@ export const useInvestigateInTimeline = ({ ruleNote, }); }, - [updateTimeline, updateTimelineIsLoading, clearActiveTimeline, unifiedComponentsInTimelineEnabled] + [ + updateTimeline, + updateTimelineIsLoading, + clearActiveTimeline, + unifiedComponentsInTimelineEnabled, + ] ); const investigateInTimelineAlertClick = useCallback(async () => { diff --git a/x-pack/plugins/security_solution/public/entity_analytics/api/api.ts b/x-pack/plugins/security_solution/public/entity_analytics/api/api.ts index 32ed3999bc747..dd80bf7989235 100644 --- a/x-pack/plugins/security_solution/public/entity_analytics/api/api.ts +++ b/x-pack/plugins/security_solution/public/entity_analytics/api/api.ts @@ -5,6 +5,7 @@ * 2.0. */ +import { useMemo } from 'react'; import type { AssetCriticalityRecord } from '../../../common/api/entity_analytics/asset_criticality'; import type { RiskScoreEntity } from '../../../common/search_strategy'; import { @@ -36,150 +37,152 @@ import { useKibana } from '../../common/lib/kibana/kibana_react'; export const useEntityAnalyticsRoutes = () => { const http = useKibana().services.http; - /** - * Fetches preview risks scores - */ - const fetchRiskScorePreview = ({ - signal, - params, - }: { - signal?: AbortSignal; - params: RiskScorePreviewRequestSchema; - }) => - http.fetch(RISK_SCORE_PREVIEW_URL, { - version: '1', - method: 'POST', - body: JSON.stringify(params), + return useMemo(() => { + /** + * Fetches preview risks scores + */ + const fetchRiskScorePreview = ({ signal, - }); - - /** - * Fetches risks engine status - */ - const fetchRiskEngineStatus = ({ signal }: { signal?: AbortSignal }) => - http.fetch(RISK_ENGINE_STATUS_URL, { - version: '1', - method: 'GET', - signal, - }); - - /** - * Init risk score engine - */ - const initRiskEngine = () => - http.fetch(RISK_ENGINE_INIT_URL, { - version: '1', - method: 'POST', - }); - - /** - * Enable risk score engine - */ - const enableRiskEngine = () => - http.fetch(RISK_ENGINE_ENABLE_URL, { - version: '1', - method: 'POST', - }); - - /** - * Disable risk score engine - */ - const disableRiskEngine = () => - http.fetch(RISK_ENGINE_DISABLE_URL, { - version: '1', - method: 'POST', - }); - - /** - * Get risk engine privileges - */ - const fetchRiskEnginePrivileges = () => - http.fetch(RISK_ENGINE_PRIVILEGES_URL, { - version: '1', - method: 'GET', - }); - - /** - * Get asset criticality privileges - */ - const fetchAssetCriticalityPrivileges = () => - http.fetch(ASSET_CRITICALITY_PRIVILEGES_URL, { - version: '1', - method: 'GET', - }); - - /** - * Create asset criticality - */ - const createAssetCriticality = async ( - params: Pick - ): Promise => - http.fetch(ASSET_CRITICALITY_URL, { - version: '1', - method: 'POST', - body: JSON.stringify({ - id_value: params.idValue, - id_field: params.idField, - criticality_level: params.criticalityLevel, - }), - }); - - /** - * Get asset criticality - */ - const fetchAssetCriticality = async ( - params: Pick - ): Promise => { - return http.fetch(ASSET_CRITICALITY_URL, { - version: '1', - method: 'GET', - query: { id_value: params.idValue, id_field: params.idField }, - }); - }; - - const getRiskScoreIndexStatus = ({ - query, - signal, - }: { - query: { - indexName: string; - entity: RiskScoreEntity; + params, + }: { + signal?: AbortSignal; + params: RiskScorePreviewRequestSchema; + }) => + http.fetch(RISK_SCORE_PREVIEW_URL, { + version: '1', + method: 'POST', + body: JSON.stringify(params), + signal, + }); + + /** + * Fetches risks engine status + */ + const fetchRiskEngineStatus = ({ signal }: { signal?: AbortSignal }) => + http.fetch(RISK_ENGINE_STATUS_URL, { + version: '1', + method: 'GET', + signal, + }); + + /** + * Init risk score engine + */ + const initRiskEngine = () => + http.fetch(RISK_ENGINE_INIT_URL, { + version: '1', + method: 'POST', + }); + + /** + * Enable risk score engine + */ + const enableRiskEngine = () => + http.fetch(RISK_ENGINE_ENABLE_URL, { + version: '1', + method: 'POST', + }); + + /** + * Disable risk score engine + */ + const disableRiskEngine = () => + http.fetch(RISK_ENGINE_DISABLE_URL, { + version: '1', + method: 'POST', + }); + + /** + * Get risk engine privileges + */ + const fetchRiskEnginePrivileges = () => + http.fetch(RISK_ENGINE_PRIVILEGES_URL, { + version: '1', + method: 'GET', + }); + + /** + * Get asset criticality privileges + */ + const fetchAssetCriticalityPrivileges = () => + http.fetch(ASSET_CRITICALITY_PRIVILEGES_URL, { + version: '1', + method: 'GET', + }); + + /** + * Create asset criticality + */ + const createAssetCriticality = async ( + params: Pick + ): Promise => + http.fetch(ASSET_CRITICALITY_URL, { + version: '1', + method: 'POST', + body: JSON.stringify({ + id_value: params.idValue, + id_field: params.idField, + criticality_level: params.criticalityLevel, + }), + }); + + /** + * Get asset criticality + */ + const fetchAssetCriticality = async ( + params: Pick + ): Promise => { + return http.fetch(ASSET_CRITICALITY_URL, { + version: '1', + method: 'GET', + query: { id_value: params.idValue, id_field: params.idField }, + }); }; - signal?: AbortSignal; - }): Promise<{ - isDeprecated: boolean; - isEnabled: boolean; - }> => - http.fetch<{ isDeprecated: boolean; isEnabled: boolean }>(RISK_SCORE_INDEX_STATUS_API_URL, { - version: '1', - method: 'GET', + + const getRiskScoreIndexStatus = ({ query, - asSystemRequest: true, signal, - }); - - /** - * Fetches risk engine settings - */ - const fetchRiskEngineSettings = () => - http.fetch(RISK_ENGINE_SETTINGS_URL, { - version: '1', - method: 'GET', - }); - - return { - fetchRiskScorePreview, - fetchRiskEngineStatus, - initRiskEngine, - enableRiskEngine, - disableRiskEngine, - fetchRiskEnginePrivileges, - fetchAssetCriticalityPrivileges, - createAssetCriticality, - fetchAssetCriticality, - getRiskScoreIndexStatus, - fetchRiskEngineSettings, - }; + }: { + query: { + indexName: string; + entity: RiskScoreEntity; + }; + signal?: AbortSignal; + }): Promise<{ + isDeprecated: boolean; + isEnabled: boolean; + }> => + http.fetch<{ isDeprecated: boolean; isEnabled: boolean }>(RISK_SCORE_INDEX_STATUS_API_URL, { + version: '1', + method: 'GET', + query, + asSystemRequest: true, + signal, + }); + + /** + * Fetches risk engine settings + */ + const fetchRiskEngineSettings = () => + http.fetch(RISK_ENGINE_SETTINGS_URL, { + version: '1', + method: 'GET', + }); + + return { + fetchRiskScorePreview, + fetchRiskEngineStatus, + initRiskEngine, + enableRiskEngine, + disableRiskEngine, + fetchRiskEnginePrivileges, + fetchAssetCriticalityPrivileges, + createAssetCriticality, + fetchAssetCriticality, + getRiskScoreIndexStatus, + fetchRiskEngineSettings, + }; + }, [http]); }; export type AssetCriticality = SnakeToCamelCase; diff --git a/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/components/form.test.tsx b/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/components/form.test.tsx index 7babe79513c6a..e0cd4ff26a3e2 100644 --- a/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/components/form.test.tsx +++ b/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/components/form.test.tsx @@ -533,11 +533,25 @@ describe('Trusted apps form', () => { describe('and a wildcard value is used with the IS operator', () => { beforeEach(() => render()); - it('shows callout warning and help text warning', () => { + it('shows warning callout and help text warning if the field is PATH', async () => { + const propsItem: Partial = { + entries: [createEntry(ConditionEntryField.PATH, 'match', '')], + }; + latestUpdatedItem = { ...formProps.item, ...propsItem }; + rerenderWithLatestProps(); + + act(() => { + setTextFieldValue(getConditionValue(getCondition()), 'somewildcard*'); + }); + + expect(renderResult.getByTestId('wildcardWithWrongOperatorCallout')); + expect(renderResult.getByText(INPUT_ERRORS.wildcardWithWrongOperatorWarning(0))).toBeTruthy(); + }); + + it('shows a warning if field is HASH or SIGNATURE', () => { setTextFieldValue(getConditionValue(getCondition()), 'somewildcard*'); rerenderWithLatestProps(); - expect(renderResult.getByTestId('wildcardWithWrongOperatorCallout')).toBeTruthy(); - expect(renderResult.getByText(INPUT_ERRORS.wildcardWithWrongOperatorWarning(0))); + expect(renderResult.getByText(INPUT_ERRORS.wildcardWithWrongField(0))).toBeTruthy(); }); }); diff --git a/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/components/form.tsx b/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/components/form.tsx index 2c17f1d18209a..465cefdf13279 100644 --- a/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/components/form.tsx +++ b/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/components/form.tsx @@ -171,13 +171,22 @@ const validateValues = (values: ArtifactFormComponentProps['item']): ValidationR value: (entry as TrustedAppConditionEntry).value, }) ) { - extraWarning = true; - addResultToValidation( - validation, - 'entries', - 'warnings', - INPUT_ERRORS.wildcardWithWrongOperatorWarning(index) - ); + if (entry.field === ConditionEntryField.PATH) { + extraWarning = true; + addResultToValidation( + validation, + 'entries', + 'warnings', + INPUT_ERRORS.wildcardWithWrongOperatorWarning(index) + ); + } else { + addResultToValidation( + validation, + 'entries', + 'warnings', + INPUT_ERRORS.wildcardWithWrongField(index) + ); + } } if (!entry.field || !(entry as TrustedAppConditionEntry).value.trim()) { diff --git a/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/translations.ts b/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/translations.ts index 7c4142609cb45..420eb2e7a3c0e 100644 --- a/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/translations.ts +++ b/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/translations.ts @@ -159,6 +159,11 @@ export const INPUT_ERRORS = { defaultMessage: `[{row}] Using a '*' or a '?' in the value with the 'IS' operator can make the entry ineffective. Change the operator to 'matches' to ensure wildcards run properly.`, values: { row: index + 1 }, }), + wildcardWithWrongField: (index: number) => + i18n.translate('xpack.securitySolution.trustedapps.create.conditionWrongFieldMsg', { + defaultMessage: `[{row}] Wildcards (? or *) are not supported for hash or signature entries.`, + values: { row: index + 1 }, + }), }; export const CONFIRM_WARNING_MODAL_LABELS = { diff --git a/x-pack/plugins/security_solution/public/threat_intelligence/routes.tsx b/x-pack/plugins/security_solution/public/threat_intelligence/routes.tsx index 23eb66c345fda..0cc2891077b68 100644 --- a/x-pack/plugins/security_solution/public/threat_intelligence/routes.tsx +++ b/x-pack/plugins/security_solution/public/threat_intelligence/routes.tsx @@ -5,7 +5,7 @@ * 2.0. */ -import React, { memo } from 'react'; +import React, { memo, useMemo } from 'react'; import type { SecuritySolutionPluginContext } from '@kbn/threat-intelligence-plugin/public'; import { THREAT_INTELLIGENCE_BASE_PATH } from '@kbn/threat-intelligence-plugin/public'; import type { Store } from 'redux'; @@ -40,49 +40,53 @@ const ThreatIntelligence = memo(() => { const securitySolutionStore = getStore() as Store; - const securitySolutionContext: SecuritySolutionPluginContext = { - securitySolutionStore, + const canWriteBlocklist = useUserPrivileges().endpointPrivileges.canWriteBlocklist; - getFiltersGlobalComponent: () => FiltersGlobal, - getPageWrapper: () => SecuritySolutionPageWrapper, - licenseService, - sourcererDataView: sourcererDataView as unknown as SelectedDataView, - getUseInvestigateInTimeline: useInvestigateInTimeline, + const securitySolutionContext: SecuritySolutionPluginContext = useMemo( + () => ({ + securitySolutionStore, + getFiltersGlobalComponent: () => FiltersGlobal, + getPageWrapper: () => SecuritySolutionPageWrapper, + licenseService, + sourcererDataView: sourcererDataView as unknown as SelectedDataView, + getUseInvestigateInTimeline: useInvestigateInTimeline, - blockList: { - canWriteBlocklist: useUserPrivileges().endpointPrivileges.canWriteBlocklist, - exceptionListApiClient: BlocklistsApiClient.getInstance(http), - useSetUrlParams, - // @ts-ignore - getFlyoutComponent: () => ArtifactFlyout, - // @ts-ignore - getFormComponent: () => BlockListForm, - }, + blockList: { + canWriteBlocklist, + exceptionListApiClient: BlocklistsApiClient.getInstance(http), + useSetUrlParams, + // @ts-ignore + getFlyoutComponent: () => ArtifactFlyout, + // @ts-ignore + getFormComponent: () => BlockListForm, + } as unknown as SecuritySolutionPluginContext['blockList'], - useQuery: () => useSelector(inputsSelectors.globalQuerySelector()), - useFilters: () => useSelector(inputsSelectors.globalFiltersQuerySelector()), - useGlobalTime, + useQuery: () => useSelector(inputsSelectors.globalQuerySelector()), + useFilters: () => useSelector(inputsSelectors.globalFiltersQuerySelector()), + useGlobalTime, - registerQuery: (query) => - securitySolutionStore.dispatch( - setQuery({ - inputId: InputsModelId.global, - id: query.id, - refetch: query.refetch, - inspect: null, - loading: query.loading, - }) - ), - deregisterQuery: (query) => - securitySolutionStore.dispatch( - deleteOneQuery({ - inputId: InputsModelId.global, - id: query.id, - }) - ), + registerQuery: (query) => + securitySolutionStore.dispatch( + setQuery({ + inputId: InputsModelId.global, + id: query.id, + refetch: query.refetch, + inspect: null, + loading: query.loading, + }) + ), + deregisterQuery: (query) => + securitySolutionStore.dispatch( + deleteOneQuery({ + inputId: InputsModelId.global, + id: query.id, + }) + ), - SiemSearchBar, - }; + SiemSearchBar, + }), + [canWriteBlocklist, http, securitySolutionStore, sourcererDataView] + ); return ( diff --git a/x-pack/plugins/security_solution/public/timelines/components/open_timeline/helpers.test.ts b/x-pack/plugins/security_solution/public/timelines/components/open_timeline/helpers.test.ts index 01ab0b8d57f4c..3732e818dffcc 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/open_timeline/helpers.test.ts +++ b/x-pack/plugins/security_solution/public/timelines/components/open_timeline/helpers.test.ts @@ -7,6 +7,7 @@ import { cloneDeep, getOr, omit } from 'lodash/fp'; import { renderHook } from '@testing-library/react-hooks'; +import { waitFor } from '@testing-library/react'; import { mockTimelineResults, mockGetOneTimelineResult } from '../../../common/mock'; import { timelineDefaults } from '../../store/defaults'; @@ -773,39 +774,34 @@ describe('helpers', () => { }); }); describe('open a timeline when unifiedComponentsInTimelineEnabled is true', () => { - const updateIsLoading = jest.fn(); const untitledTimeline = { ...mockSelectedTimeline, title: '' }; const onOpenTimeline = jest.fn(); - - const updateTimelineHandler = jest.fn(); - - const updateTimeline = jest.fn(() => updateTimelineHandler); - afterEach(() => { jest.clearAllMocks(); }); it('should update timeline correctly when timeline is untitled', async () => { - const args: QueryTimelineById<{}> = { + const args: QueryTimelineById = { duplicate: false, graphEventId: '', timelineId: undefined, timelineType: TimelineType.default, onOpenTimeline, openTimeline: true, - updateIsLoading, - updateTimeline, unifiedComponentsInTimelineEnabled: true, }; (resolveTimeline as jest.Mock).mockResolvedValue(untitledTimeline); - queryTimelineById<{}>(args as unknown as QueryTimelineById<{}>); + renderHook(async () => { + const queryTimelineById = useQueryTimelineById(); + queryTimelineById(args); + }); - expect(updateIsLoading).toHaveBeenCalledWith({ + expect(dispatchUpdateIsLoading).toHaveBeenCalledWith({ id: TimelineId.active, isLoading: true, }); - expect(updateTimeline).toHaveBeenNthCalledWith( + expect(mockUpdateTimeline).toHaveBeenNthCalledWith( 1, expect.objectContaining({ id: TimelineId.active, @@ -814,72 +810,80 @@ describe('helpers', () => { }), }) ); - expect(updateIsLoading).toHaveBeenCalledWith({ + expect(dispatchUpdateIsLoading).toHaveBeenCalledWith({ id: TimelineId.active, isLoading: false, }); }); it('should update timeline correctly when timeline is already saved and onOpenTimeline is not provided', async () => { - const args: QueryTimelineById<{}> = { + const args: QueryTimelineById = { duplicate: false, graphEventId: '', timelineId: TimelineId.active, timelineType: TimelineType.default, onOpenTimeline: undefined, openTimeline: true, - updateIsLoading, - updateTimeline, unifiedComponentsInTimelineEnabled: true, }; (resolveTimeline as jest.Mock).mockResolvedValue(mockSelectedTimeline); + renderHook(async () => { + const queryTimelineById = useQueryTimelineById(); + queryTimelineById(args); + }); - await queryTimelineById<{}>(args as unknown as QueryTimelineById<{}>); + expect(dispatchUpdateIsLoading).toHaveBeenCalledWith({ + id: TimelineId.active, + isLoading: true, + }); - expect(updateTimeline).toHaveBeenNthCalledWith( - 1, - expect.objectContaining({ - timeline: expect.objectContaining({ - columns: mockSelectedTimeline.data.timeline.columns.map((col) => ({ - columnHeaderType: col.columnHeaderType, - id: col.id, - initialWidth: defaultUdtHeaders.find((defaultCol) => col.id === defaultCol.id) - ?.initialWidth, - })), - }), - }) - ); + await waitFor(() => { + expect(mockUpdateTimeline).toHaveBeenNthCalledWith( + 1, + expect.objectContaining({ + timeline: expect.objectContaining({ + columns: mockSelectedTimeline.data.timeline.columns.map((col) => ({ + columnHeaderType: col.columnHeaderType, + id: col.id, + initialWidth: defaultUdtHeaders.find((defaultCol) => col.id === defaultCol.id) + ?.initialWidth, + })), + }), + }) + ); + }); }); it('should update timeline correctly when timeline is already saved and onOpenTimeline IS provided', async () => { - const args: QueryTimelineById<{}> = { + const args: QueryTimelineById = { duplicate: false, graphEventId: '', timelineId: TimelineId.active, timelineType: TimelineType.default, onOpenTimeline, openTimeline: true, - updateIsLoading, - updateTimeline, unifiedComponentsInTimelineEnabled: true, }; (resolveTimeline as jest.Mock).mockResolvedValue(mockSelectedTimeline); + renderHook(async () => { + const queryTimelineById = useQueryTimelineById(); + queryTimelineById(args); + }); - await queryTimelineById<{}>(args as unknown as QueryTimelineById<{}>); - - expect(onOpenTimeline).toHaveBeenNthCalledWith( - 1, - expect.objectContaining({ - columns: mockSelectedTimeline.data.timeline.columns.map((col) => ({ - columnHeaderType: col.columnHeaderType, - id: col.id, - initialWidth: defaultUdtHeaders.find((defaultCol) => col.id === defaultCol.id) - ?.initialWidth, - })), - }) - ); + waitFor(() => { + expect(onOpenTimeline).toHaveBeenCalledWith( + expect.objectContaining({ + columns: mockSelectedTimeline.data.timeline.columns.map((col) => ({ + columnHeaderType: col.columnHeaderType, + id: col.id, + initialWidth: defaultUdtHeaders.find((defaultCol) => col.id === defaultCol.id) + ?.initialWidth, + })), + }) + ); + }); }); }); }); diff --git a/x-pack/plugins/security_solution/public/timelines/components/open_timeline/helpers.ts b/x-pack/plugins/security_solution/public/timelines/components/open_timeline/helpers.ts index 50ec27d5ee22f..835862c04ced8 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/open_timeline/helpers.ts +++ b/x-pack/plugins/security_solution/public/timelines/components/open_timeline/helpers.ts @@ -374,7 +374,7 @@ export const useQueryTimelineById = () => { timelineToOpen, duplicate, timelineType, - unifiedComponentsInTimelineEnabled, + unifiedComponentsInTimelineEnabled ); if (onOpenTimeline != null) { diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/tabs/pinned_tab_content/index.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/tabs/pinned_tab_content/index.tsx index 271edcef059c8..b71746c09031f 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/tabs/pinned_tab_content/index.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/tabs/pinned_tab_content/index.tsx @@ -37,7 +37,13 @@ import { ExitFullScreen } from '../../../../../common/components/exit_full_scree import { getDefaultControlColumn } from '../../body/control_columns'; import { useLicense } from '../../../../../common/hooks/use_license'; import { HeaderActions } from '../../../../../common/components/header_actions/header_actions'; -import { FullWidthFlexGroup, ScrollableFlexItem, StyledEuiFlyoutBody, StyledEuiFlyoutFooter, VerticalRule } from '../shared/layout'; +import { + FullWidthFlexGroup, + ScrollableFlexItem, + StyledEuiFlyoutBody, + StyledEuiFlyoutFooter, + VerticalRule, +} from '../shared/layout'; import { TimelineTabCommonProps } from '../shared/types'; const ExitFullScreenContainer = styled.div` diff --git a/x-pack/plugins/security_solution/scripts/endpoint/common/fleet_services.ts b/x-pack/plugins/security_solution/scripts/endpoint/common/fleet_services.ts index c208e87ab166e..5a99dcc8322db 100644 --- a/x-pack/plugins/security_solution/scripts/endpoint/common/fleet_services.ts +++ b/x-pack/plugins/security_solution/scripts/endpoint/common/fleet_services.ts @@ -517,7 +517,7 @@ export const getLatestAgentDownloadVersion = async ( log?: ToolingLog ): Promise => { const artifactsUrl = 'https://artifacts-api.elastic.co/v1/versions'; - const semverMatch = `<=${version}`; + const semverMatch = `<=${version.replace(`-SNAPSHOT`, '')}`; const artifactVersionsResponse: { versions: string[] } = await nodeFetch(artifactsUrl).then( (response) => { if (!response.ok) { @@ -551,6 +551,8 @@ export const getLatestAgentDownloadVersion = async ( semverMatch ); + log?.verbose(`Matched [${matchedVersion}] for .maxStatisfying(${semverMatch})`); + if (!matchedVersion) { throw new Error(`Unable to find a semver version that meets ${semverMatch}`); } diff --git a/x-pack/plugins/security_solution/scripts/endpoint/common/vm_services.ts b/x-pack/plugins/security_solution/scripts/endpoint/common/vm_services.ts index 4209b554b2732..084e068768e8f 100644 --- a/x-pack/plugins/security_solution/scripts/endpoint/common/vm_services.ts +++ b/x-pack/plugins/security_solution/scripts/endpoint/common/vm_services.ts @@ -170,15 +170,12 @@ export const generateVmName = (identifier: string = baseGenerator.randomUser()): * @param threshold */ export const getMultipassVmCountNotice = async (threshold: number = 1): Promise => { - const response = await execa.command(`multipass list --format=json`); + const listOfVMs = await findVm('multipass'); - const output: { list: Array<{ ipv4: string; name: string; release: string; state: string }> } = - JSON.parse(response.stdout); - - if (output.list.length > threshold) { + if (listOfVMs.data.length > threshold) { return `----------------------------------------------------------------- ${chalk.red('NOTE:')} ${chalk.bold( - chalk.red(`You currently have ${chalk.red(output.list.length)} VMs running.`) + chalk.red(`You currently have ${chalk.red(listOfVMs.data.length)} VMs running.`) )} Remember to delete those no longer being used. View running VMs: ${chalk.cyan('multipass list')} @@ -378,3 +375,55 @@ export const getHostVmClient = ( ? createVagrantHostVmClient(hostname, vagrantFile, log) : createMultipassHostVmClient(hostname, log); }; + +/** + * Retrieve a list of running VM names + * @param type + * @param name + * @param log + */ +export const findVm = async ( + type: SupportedVmManager, + /** Filter results by VM name */ + name?: string | RegExp, + log: ToolingLog = createToolingLogger() +): Promise<{ data: string[] }> => { + log.verbose(`Finding [${type}] VMs with name [${name}]`); + + if (type === 'multipass') { + const list = JSON.parse((await execa.command(`multipass list --format json`)).stdout) as { + list: Array<{ + ipv4: string[]; + name: string; + release: string; + state: string; + }>; + }; + + log.verbose(`List of VM running:`, list); + + if (!list.list || list.list.length === 0) { + return { data: [] }; + } + + return { + data: !name + ? list.list.map((vmEntry) => vmEntry.name) + : list.list.reduce((acc, vmEntry) => { + if (typeof name === 'string') { + if (vmEntry.name === name) { + acc.push(vmEntry.name); + } + } else { + if (name.test(vmEntry.name)) { + acc.push(vmEntry.name); + } + } + + return acc; + }, [] as string[]), + }; + } + + throw new Error(`findVm() does not yet have support for [${type}]`); +}; diff --git a/x-pack/plugins/security_solution/scripts/endpoint/sentinelone_host/index.ts b/x-pack/plugins/security_solution/scripts/endpoint/sentinelone_host/index.ts index 871d5bcf83606..2811fe9e57d09 100644 --- a/x-pack/plugins/security_solution/scripts/endpoint/sentinelone_host/index.ts +++ b/x-pack/plugins/security_solution/scripts/endpoint/sentinelone_host/index.ts @@ -27,7 +27,13 @@ import { installSentinelOneAgent, S1Client, } from './common'; -import { createVm, generateVmName, getMultipassVmCountNotice } from '../common/vm_services'; +import { + createMultipassHostVmClient, + createVm, + findVm, + generateVmName, + getMultipassVmCountNotice, +} from '../common/vm_services'; import { createKbnClient } from '../common/stack_services'; export const cli = async () => { @@ -51,7 +57,7 @@ console and pushes the data to Elasticsearch.`, 's1ApiToken', 'vmName', ], - boolean: ['forceFleetServer'], + boolean: ['forceFleetServer', 'forceNewS1Host'], default: { kibanaUrl: 'http://127.0.0.1:5601', username: 'elastic', @@ -69,6 +75,10 @@ console and pushes the data to Elasticsearch.`, Default: re-uses existing dev policy (if found) or creates a new one --forceFleetServer Optional. If fleet server should be started/configured even if it seems like it is already setup. + --forceNewS1Host Optional. Force a new VM host to be created and enrolled with SentinelOne. + By default, a check is done to see if a host running SentinelOne is + already running and if so, a new one will not be created - unless this + option is used --username Optional. User name to be used for auth against elasticsearch and kibana (Default: elastic). --password Optional. Password associated with the username (Default: changeme) @@ -86,6 +96,7 @@ const runCli: RunFn = async ({ log, flags }) => { const s1ApiToken = flags.s1ApiToken as string; const policy = flags.policy as string; const forceFleetServer = flags.forceFleetServer as boolean; + const forceNewS1Host = flags.forceNewS1Host as boolean; const getRequiredArgMessage = (argName: string) => `${argName} argument is required`; createToolingLogger.setDefaultLogLevelFromCliFlags(flags); @@ -102,22 +113,40 @@ const runCli: RunFn = async ({ log, flags }) => { password, }); - const hostVm = await createVm({ - type: 'multipass', - name: vmName, - log, - memory: '2G', - disk: '10G', - }); - - const s1Info = await installSentinelOneAgent({ - hostVm, - log, - s1Client, - }); - - log.info(`SentinelOne Agent Status: -${s1Info.status}`); + const runningS1VMs = ( + await findVm( + 'multipass', + flags.vmName ? vmName : new RegExp(`^${vmName.substring(0, vmName.lastIndexOf('-'))}`) + ) + ).data; + + // Avoid enrolling another VM with SentinelOne if we already have one running + const hostVm = + forceNewS1Host || runningS1VMs.length === 0 + ? await createVm({ + type: 'multipass', + name: vmName, + log, + memory: '2G', + disk: '10G', + }).then((vm) => { + return installSentinelOneAgent({ + hostVm: vm, + log, + s1Client, + }).then((s1Info) => { + log.info(`SentinelOne Agent Status:\n${s1Info.status}`); + + return vm; + }); + }) + : await Promise.resolve(createMultipassHostVmClient(runningS1VMs[0], log)).then((vm) => { + log.info( + `A host VM running SentinelOne Agent is already running - will reuse it.\nTIP: Use 'forceNewS1Host' to force the creation of a new one if desired` + ); + + return vm; + }); const { id: agentPolicyId, diff --git a/x-pack/plugins/security_solution/server/assistant/tools/alert_counts/alert_counts_tool.test.ts b/x-pack/plugins/security_solution/server/assistant/tools/alert_counts/alert_counts_tool.test.ts index 1cd837bcaf2bd..f76adfdbd0894 100644 --- a/x-pack/plugins/security_solution/server/assistant/tools/alert_counts/alert_counts_tool.test.ts +++ b/x-pack/plugins/security_solution/server/assistant/tools/alert_counts/alert_counts_tool.test.ts @@ -19,7 +19,7 @@ describe('AlertCountsTool', () => { const esClient = { search: jest.fn().mockResolvedValue({}), } as unknown as ElasticsearchClient; - const replacements = [{ uuid: 'key', value: 'value' }]; + const replacements = { key: 'value' }; const request = { body: { isEnabledKnowledgeBase: false, diff --git a/x-pack/plugins/security_solution/server/assistant/tools/open_and_acknowledged_alerts/open_and_acknowledged_alerts_tool.test.ts b/x-pack/plugins/security_solution/server/assistant/tools/open_and_acknowledged_alerts/open_and_acknowledged_alerts_tool.test.ts index 650a55d45b1c3..d0dd3ed291648 100644 --- a/x-pack/plugins/security_solution/server/assistant/tools/open_and_acknowledged_alerts/open_and_acknowledged_alerts_tool.test.ts +++ b/x-pack/plugins/security_solution/server/assistant/tools/open_and_acknowledged_alerts/open_and_acknowledged_alerts_tool.test.ts @@ -21,7 +21,7 @@ describe('OpenAndAcknowledgedAlertsTool', () => { const esClient = { search: jest.fn().mockResolvedValue(mockAlertsFieldsApi), } as unknown as ElasticsearchClient; - const replacements = [{ uuid: 'key', value: 'value' }]; + const replacements = { key: 'value' }; const request = { body: { isEnabledKnowledgeBase: false, diff --git a/x-pack/plugins/security_solution/server/assistant/tools/open_and_acknowledged_alerts/open_and_acknowledged_alerts_tool.ts b/x-pack/plugins/security_solution/server/assistant/tools/open_and_acknowledged_alerts/open_and_acknowledged_alerts_tool.ts index a96e1a96cfdc1..8c376fe52b4ef 100644 --- a/x-pack/plugins/security_solution/server/assistant/tools/open_and_acknowledged_alerts/open_and_acknowledged_alerts_tool.ts +++ b/x-pack/plugins/security_solution/server/assistant/tools/open_and_acknowledged_alerts/open_and_acknowledged_alerts_tool.ts @@ -6,7 +6,7 @@ */ import type { SearchResponse } from '@elastic/elasticsearch/lib/api/types'; -import type { Replacement } from '@kbn/elastic-assistant-common'; +import type { Replacements } from '@kbn/elastic-assistant-common'; import { getAnonymizedValue, transformRawData } from '@kbn/elastic-assistant-common'; import { DynamicTool } from 'langchain/tools'; import { requestHasRequiredAnonymizationParams } from '@kbn/elastic-assistant-plugin/server/lib/langchain/helpers'; @@ -68,24 +68,9 @@ export const OPEN_AND_ACKNOWLEDGED_ALERTS_TOOL: AssistantTool = { // Accumulate replacements locally so we can, for example use the same // replacement for a hostname when we see it in multiple alerts: - let localReplacements: Replacement[] = replacements ?? []; - const localOnNewReplacements = (newReplacements: Replacement[]) => { - const localReplacementsDict = localReplacements.reduce( - (acc: Record, r) => { - acc[r.value] = r.uuid; - return acc; - }, - {} - ); - const newReplacementsDict = newReplacements.reduce((acc: Record, r) => { - acc[r.value] = r.uuid; - return acc; - }, {}); - const updatedReplacements = { ...localReplacementsDict, ...newReplacementsDict }; - localReplacements = Object.keys(updatedReplacements).map((key) => ({ - value: key, - uuid: updatedReplacements[key], - })); + let localReplacements: Replacements = replacements ?? {}; + const localOnNewReplacements = (newReplacements: Replacements) => { + localReplacements = { ...localReplacements, ...newReplacements }; onNewReplacements?.(localReplacements); // invoke the callback with the latest replacements return Promise.resolve(localReplacements); }; diff --git a/x-pack/plugins/security_solution/server/endpoint/endpoint_app_context_services.ts b/x-pack/plugins/security_solution/server/endpoint/endpoint_app_context_services.ts index 250ac6f9be599..fa435135681f1 100644 --- a/x-pack/plugins/security_solution/server/endpoint/endpoint_app_context_services.ts +++ b/x-pack/plugins/security_solution/server/endpoint/endpoint_app_context_services.ts @@ -23,9 +23,9 @@ import type { import type { PluginStartContract as AlertsPluginStartContract } from '@kbn/alerting-plugin/server'; import type { CloudSetup } from '@kbn/cloud-plugin/server'; import type { FleetActionsClientInterface } from '@kbn/fleet-plugin/server/services/actions/types'; -import { EndpointError } from '../../common/endpoint/errors'; +import type { PluginStartContract as ActionsPluginStartContract } from '@kbn/actions-plugin/server'; import type { ResponseActionsClient } from './services'; -import { EndpointActionsClient } from './services'; +import { getResponseActionsClient } from './services'; import { getAgentPolicyCreateCallback, getAgentPolicyUpdateCallback, @@ -54,7 +54,6 @@ import type { FeatureUsageService } from './services/feature_usage/service'; import type { ExperimentalFeatures } from '../../common/experimental_features'; import type { ProductFeaturesService } from '../lib/product_features_service/product_features_service'; import type { ResponseActionAgentType } from '../../common/endpoint/service/response_actions/constants'; - export interface EndpointAppContextServiceSetupContract { securitySolutionRequestContextFactory: IRequestContextFactory; cloud: CloudSetup; @@ -83,6 +82,7 @@ export interface EndpointAppContextServiceStartContract { esClient: ElasticsearchClient; productFeaturesService: ProductFeaturesService; savedObjectsClient: SavedObjectsClientContract; + connectorActions: ActionsPluginStartContract; } /** @@ -276,34 +276,57 @@ export class EndpointAppContextService { public getInternalResponseActionsClient({ agentType = 'endpoint', username = 'elastic', + taskId, + taskType, }: { agentType?: ResponseActionAgentType; username?: string; + /** Used with background task and needed for `UnsecuredActionsClient` */ + taskId?: string; + /** Used with background task and needed for `UnsecuredActionsClient` */ + taskType?: string; }): ResponseActionsClient { if (!this.startDependencies?.esClient) { throw new EndpointAppContentServicesNotStartedError(); } - if (agentType !== `endpoint`) { - throw new EndpointError( - `Agent type [${agentType}] does not support usage of response actions via non-HTTP requests!` - ); + let connectorActionsClient = + this.startDependencies.connectorActions.getUnsecuredActionsClient(); + + // If we have a task id and type, then call is coming from a background task and we need to use those + // values with the Action's plugin `UnsecuredActionsClient`'s `.execute()` method. To do so in a + // transparent way to the existing response action client, we create a Proxy here and trap the + // `GET execute` property and wrap it a function that will automatically inject this data into + // `execute()` calls + if (taskId && taskType) { + connectorActionsClient = new Proxy(connectorActionsClient, { + get(target, prop, receiver) { + if (prop === 'execute') { + return function (execArgs: Parameters[0]) { + return target.execute({ + ...execArgs, + relatedSavedObjects: [ + ...(execArgs.relatedSavedObjects ?? []), + { + id: taskId, + type: taskType, + }, + ], + }); + }; + } + + return Reflect.get(target, prop, receiver); + }, + }); } - // TODO:PT switch to using `getResponseActionsClient()` instead once we support getting internal versions of connectorsActions - // return getResponseActionsClient(agentType, { - // endpointService: this, - // esClient: this.startDependencies.esClient, - // username: 'elastic', - // isAutomated: true, - // connectorActions: undefined, // FIXME:PT get internal client here - // }); - - return new EndpointActionsClient({ - username, - esClient: this.startDependencies.esClient, + return getResponseActionsClient(agentType, { endpointService: this, + esClient: this.startDependencies.esClient, + username, isAutomated: true, + connectorActions: connectorActionsClient, }); } diff --git a/x-pack/plugins/security_solution/server/endpoint/lib/response_actions/complete_external_actions_task.ts b/x-pack/plugins/security_solution/server/endpoint/lib/response_actions/complete_external_actions_task.ts index ba60fcc37a045..a9c81fbd69e2c 100644 --- a/x-pack/plugins/security_solution/server/endpoint/lib/response_actions/complete_external_actions_task.ts +++ b/x-pack/plugins/security_solution/server/endpoint/lib/response_actions/complete_external_actions_task.ts @@ -98,10 +98,14 @@ export class CompleteExternalResponseActionsTask { ); } + const { id: taskId, taskType } = taskInstance; + return new CompleteExternalActionsTaskRunner( this.options.endpointAppContext.service, this.esClient, - this.taskInterval + this.taskInterval, + taskId, + taskType ); }, }, diff --git a/x-pack/plugins/security_solution/server/endpoint/lib/response_actions/complete_external_actions_task_runner.test.ts b/x-pack/plugins/security_solution/server/endpoint/lib/response_actions/complete_external_actions_task_runner.test.ts index a5f7b3e1d14b5..90c392d450adf 100644 --- a/x-pack/plugins/security_solution/server/endpoint/lib/response_actions/complete_external_actions_task_runner.test.ts +++ b/x-pack/plugins/security_solution/server/endpoint/lib/response_actions/complete_external_actions_task_runner.test.ts @@ -13,6 +13,7 @@ import { responseActionsClientMock } from '../../services/actions/clients/mocks' import { EndpointActionGenerator } from '../../../../common/endpoint/data_generators/endpoint_action_generator'; import { ENDPOINT_ACTION_RESPONSES_INDEX } from '../../../../common/endpoint/constants'; import { waitFor } from '@testing-library/react'; +import { ResponseActionsConnectorNotConfiguredError } from '../../services/actions/clients/errors'; describe('CompleteExternalTaskRunner class', () => { let endpointContextServicesMock: ReturnType; @@ -52,6 +53,21 @@ describe('CompleteExternalTaskRunner class', () => { ); }); + it('should NOT log an error if agentType is not configured with a connector', async () => { + (endpointContextServicesMock.getInternalResponseActionsClient as jest.Mock).mockImplementation( + () => { + const clientMock = responseActionsClientMock.create(); + (clientMock.processPendingActions as jest.Mock).mockImplementation(async () => { + throw new ResponseActionsConnectorNotConfiguredError('foo'); + }); + return clientMock; + } + ); + await runnerInstance.run(); + + expect(endpointContextServicesMock.createLogger().error).not.toHaveBeenCalled(); + }); + it('should call `processPendingAction` for each external agent type', async () => { await runnerInstance.run(); const getInternalResponseActionsClientMock = ( diff --git a/x-pack/plugins/security_solution/server/endpoint/lib/response_actions/complete_external_actions_task_runner.ts b/x-pack/plugins/security_solution/server/endpoint/lib/response_actions/complete_external_actions_task_runner.ts index c42127ad09cc1..bb6fcc0c897d7 100644 --- a/x-pack/plugins/security_solution/server/endpoint/lib/response_actions/complete_external_actions_task_runner.ts +++ b/x-pack/plugins/security_solution/server/endpoint/lib/response_actions/complete_external_actions_task_runner.ts @@ -8,6 +8,7 @@ import type { CancellableTask, RunContext, RunResult } from '@kbn/task-manager-plugin/server/task'; import type { Logger, ElasticsearchClient } from '@kbn/core/server'; import type { BulkRequest } from '@elastic/elasticsearch/lib/api/types'; +import { ResponseActionsConnectorNotConfiguredError } from '../../services/actions/clients/errors'; import { catchAndWrapError } from '../../utils'; import { stringify } from '../../utils/stringify'; import { RESPONSE_ACTION_AGENT_TYPE } from '../../../../common/endpoint/service/response_actions/constants'; @@ -32,7 +33,9 @@ export class CompleteExternalActionsTaskRunner constructor( private readonly endpointContextServices: EndpointAppContextService, private readonly esClient: ElasticsearchClient, - private readonly nextRunInterval: string = '60s' + private readonly nextRunInterval: string = '60s', + private readonly taskId?: string, + private readonly taskType?: string ) { this.log = this.endpointContextServices.createLogger( // Adding a unique identifier to the end of the class name to help identify log entries related to this run @@ -111,15 +114,30 @@ export class CompleteExternalActionsTaskRunner return null; } - // FIXME:PT need to implement logic that first checks if a given agent type is currently supported - like do we have what we need to "talk" to them? - const agentTypeActionsClient = - this.endpointContextServices.getInternalResponseActionsClient({ agentType }); - - return agentTypeActionsClient.processPendingActions({ - abortSignal: this.abortController.signal, - addToQueue: this.updatesQueue.addToQueue.bind(this.updatesQueue), - }); + this.endpointContextServices.getInternalResponseActionsClient({ + agentType, + taskType: this.taskType, + taskId: this.taskId, + }); + + return agentTypeActionsClient + .processPendingActions({ + abortSignal: this.abortController.signal, + addToQueue: this.updatesQueue.addToQueue.bind(this.updatesQueue), + }) + .catch((err) => { + // ignore errors due to connector not being configured - no point in logging errors if a customer + // is not using response actions for the given agent type + if (err instanceof ResponseActionsConnectorNotConfiguredError) { + this.log.debug( + `Skipping agentType [${agentType}]: No stack connector configured for this agent type` + ); + return null; + } + + this.errors.push(err.message); + }); } ) ); diff --git a/x-pack/plugins/security_solution/server/endpoint/mocks.ts b/x-pack/plugins/security_solution/server/endpoint/mocks.ts index 344d3bbd1f2f9..f3d39737a7cf2 100644 --- a/x-pack/plugins/security_solution/server/endpoint/mocks.ts +++ b/x-pack/plugins/security_solution/server/endpoint/mocks.ts @@ -46,6 +46,8 @@ import { elasticsearchClientMock } from '@kbn/core-elasticsearch-client-server-m import { casesPluginMock } from '@kbn/cases-plugin/server/mocks'; import { createCasesClientMock } from '@kbn/cases-plugin/server/client/mocks'; import type { AddVersionOpts, VersionedRouteConfig } from '@kbn/core-http-server'; +import { unsecuredActionsClientMock } from '@kbn/actions-plugin/server/unsecured_actions_client/unsecured_actions_client.mock'; +import type { PluginStartContract } from '@kbn/actions-plugin/server'; import { responseActionsClientMock } from './services/actions/clients/mocks'; import { getEndpointAuthzInitialStateMock } from '../../common/endpoint/service/authz/mocks'; import { createMockConfig, requestContextMock } from '../lib/detection_engine/routes/__mocks__'; @@ -72,7 +74,6 @@ import { EndpointFleetServicesFactory } from './services/fleet'; import { createLicenseServiceMock } from '../../common/license/mocks'; import { createFeatureUsageServiceMock } from './services/feature_usage/mocks'; import { createProductFeaturesServiceMock } from '../lib/product_features_service/mocks'; - /** * Creates a mocked EndpointAppContext. */ @@ -229,6 +230,9 @@ export const createMockEndpointAppContextServiceStartContract = esClient: elasticsearchClientMock.createElasticsearchClient(), productFeaturesService, savedObjectsClient: savedObjectsClientMock.create(), + connectorActions: { + getUnsecuredActionsClient: jest.fn().mockReturnValue(unsecuredActionsClientMock.create()), + } as unknown as jest.Mocked, }; }; diff --git a/x-pack/plugins/security_solution/server/endpoint/services/actions/clients/errors.ts b/x-pack/plugins/security_solution/server/endpoint/services/actions/clients/errors.ts index 2e644f9a3a760..d79986e899fe0 100644 --- a/x-pack/plugins/security_solution/server/endpoint/services/actions/clients/errors.ts +++ b/x-pack/plugins/security_solution/server/endpoint/services/actions/clients/errors.ts @@ -44,3 +44,13 @@ export class UnsupportedResponseActionsAgentTypeError extends ResponseActionsCli super(message, statusCode, meta); } } + +export class ResponseActionsConnectorNotConfiguredError extends ResponseActionsClientError { + constructor( + connectorTypeId: string, + public readonly statusCode: number = 400, + public readonly meta?: unknown + ) { + super(`No stack connector instance configured for [${connectorTypeId}]`, statusCode, meta); + } +} diff --git a/x-pack/plugins/security_solution/server/endpoint/services/actions/clients/lib/normalized_external_connector_client.test.ts b/x-pack/plugins/security_solution/server/endpoint/services/actions/clients/lib/normalized_external_connector_client.test.ts new file mode 100644 index 0000000000000..5c9d6154226e8 --- /dev/null +++ b/x-pack/plugins/security_solution/server/endpoint/services/actions/clients/lib/normalized_external_connector_client.test.ts @@ -0,0 +1,156 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import type { ActionsClientMock } from '@kbn/actions-plugin/server/actions_client/actions_client.mock'; +import { responseActionsClientMock } from '../mocks'; +import { loggingSystemMock } from '@kbn/core-logging-server-mocks'; +import type { Logger } from '@kbn/logging'; +import type { NormalizedExternalConnectorClientExecuteOptions } from './normalized_external_connector_client'; +import { NormalizedExternalConnectorClient } from './normalized_external_connector_client'; +import { ResponseActionsConnectorNotConfiguredError } from '../errors'; +import type { IUnsecuredActionsClient } from '@kbn/actions-plugin/server'; +import { unsecuredActionsClientMock } from '@kbn/actions-plugin/server/unsecured_actions_client/unsecured_actions_client.mock'; + +describe('`NormalizedExternalConnectorClient` class', () => { + let logger: Logger; + let executeInputOptions: NormalizedExternalConnectorClientExecuteOptions; + + beforeEach(() => { + logger = loggingSystemMock.create().get('mock'); + executeInputOptions = { params: { subAction: 'sub-action-1', subActionParams: {} } }; + }); + + describe('#getConnectorInstance()', () => { + let actionPluginConnectorClient: ActionsClientMock; + + beforeEach(() => { + actionPluginConnectorClient = responseActionsClientMock.createConnectorActionsClient({ + getAllResponse: [responseActionsClientMock.createConnector({ actionTypeId: 'foo' })], + }); + }); + + it('should search for the connector when first API call is done', async () => { + const testInstance = new NormalizedExternalConnectorClient( + 'foo', + actionPluginConnectorClient, + logger + ); + + expect(actionPluginConnectorClient.getAll).not.toHaveBeenCalled(); + + await testInstance.execute({ params: { subAction: 'sub-action-1', subActionParams: {} } }); + + expect(actionPluginConnectorClient.getAll).toHaveBeenCalledTimes(1); + + // Subsequent calls to `.execute()` should not trigger logic to find Connector instance again + await testInstance.execute(executeInputOptions); + + expect(actionPluginConnectorClient.getAll).toHaveBeenCalledTimes(1); + }); + + it('should error if unable to retrieve all connector instances (`.getAll()`) ', async () => { + (actionPluginConnectorClient.getAll as jest.Mock).mockImplementation(async () => { + throw new Error('oh oh'); + }); + const testInstance = new NormalizedExternalConnectorClient( + 'foo', + actionPluginConnectorClient, + logger + ); + const executePromise = testInstance.execute(executeInputOptions); + + await expect(executePromise).rejects.toHaveProperty( + 'message', + 'Unable to retrieve list of stack connectors in order to find one for [foo]: oh oh' + ); + await expect(executePromise).rejects.toHaveProperty('statusCode', 400); + }); + + it.each([ + ['is not defined', () => []], + [ + 'is deprecated', + () => [ + responseActionsClientMock.createConnector({ actionTypeId: 'foo', isDeprecated: true }), + ], + ], + [ + 'is missing secrets', + () => [ + responseActionsClientMock.createConnector({ + actionTypeId: 'foo', + isMissingSecrets: true, + }), + ], + ], + ])('should error if a connector instance %s', async (_, getResponse) => { + (actionPluginConnectorClient.getAll as jest.Mock).mockResolvedValue(getResponse()); + const testInstance = new NormalizedExternalConnectorClient( + 'foo', + actionPluginConnectorClient, + logger + ); + const executePromise = testInstance.execute(executeInputOptions); + + await expect(executePromise).rejects.toEqual( + new ResponseActionsConnectorNotConfiguredError('foo') + ); + }); + }); + + describe('with ActionClient', () => { + let actionPluginConnectorClient: ActionsClientMock; + + beforeEach(() => { + actionPluginConnectorClient = responseActionsClientMock.createConnectorActionsClient({ + getAllResponse: [responseActionsClientMock.createConnector({ actionTypeId: 'foo' })], + }); + }); + + it('should call Action Plugin client `.execute()` with expected arguments', async () => { + const testInstance = new NormalizedExternalConnectorClient( + 'foo', + actionPluginConnectorClient, + logger + ); + await testInstance.execute(executeInputOptions); + + expect(actionPluginConnectorClient.execute).toHaveBeenCalledWith({ + actionId: 'connector-mock-id-1', + params: executeInputOptions.params, + }); + }); + }); + + describe('with IUnsecuredActionsClient', () => { + let actionPluginConnectorClient: IUnsecuredActionsClient; + + beforeEach(() => { + actionPluginConnectorClient = unsecuredActionsClientMock.create(); + + (actionPluginConnectorClient.getAll as jest.Mock).mockResolvedValue([ + responseActionsClientMock.createConnector({ actionTypeId: 'foo' }), + ]); + }); + + it('should call Action Plugin client `.execute()` with expected arguments', async () => { + const testInstance = new NormalizedExternalConnectorClient( + 'foo', + actionPluginConnectorClient, + logger + ); + await testInstance.execute(executeInputOptions); + + expect(actionPluginConnectorClient.execute).toHaveBeenCalledWith({ + id: 'connector-mock-id-1', + requesterId: 'background_task', + spaceId: 'default', + params: executeInputOptions.params, + }); + }); + }); +}); diff --git a/x-pack/plugins/security_solution/server/endpoint/services/actions/clients/lib/normalized_external_connector_client.ts b/x-pack/plugins/security_solution/server/endpoint/services/actions/clients/lib/normalized_external_connector_client.ts new file mode 100644 index 0000000000000..2c0647b43ad73 --- /dev/null +++ b/x-pack/plugins/security_solution/server/endpoint/services/actions/clients/lib/normalized_external_connector_client.ts @@ -0,0 +1,113 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor 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 @typescript-eslint/no-explicit-any */ + +import type { IUnsecuredActionsClient, ActionsClient } from '@kbn/actions-plugin/server'; +import type { ActionTypeExecutorResult } from '@kbn/actions-plugin/common'; +import type { Logger } from '@kbn/logging'; +import type { ConnectorWithExtraFindData } from '@kbn/actions-plugin/server/application/connector/types'; +import { once } from 'lodash'; +import { stringify } from '../../../../utils/stringify'; +import { ResponseActionsClientError, ResponseActionsConnectorNotConfiguredError } from '../errors'; + +export interface NormalizedExternalConnectorClientExecuteOptions< + TParams extends Record = Record, + TSubAction = unknown +> { + params: { + subAction: TSubAction; + subActionParams: TParams; + }; + spaceId?: string; +} + +/** + * Handles setting up the usage of Stack Connectors for Response Actions and normalizes usage of + * Connector's Sub-Actions plugin between the `ActionsClient` and the `IUnsecuredActionsClient` + * client interfaces. It also provides better typing support. + */ +export class NormalizedExternalConnectorClient { + protected readonly getConnectorInstance: () => Promise; + + constructor( + protected readonly connectorTypeId: string, + protected readonly connectorsClient: ActionsClient | IUnsecuredActionsClient, + protected readonly log: Logger + ) { + this.getConnectorInstance = once(async () => { + let connectorList: ConnectorWithExtraFindData[] = []; + + try { + connectorList = await this.getAll(); + } catch (err) { + throw new ResponseActionsClientError( + `Unable to retrieve list of stack connectors in order to find one for [${this.connectorTypeId}]: ${err.message}`, + // failure here is likely due to Authz, but because we don't have a good way to determine that, + // the `statusCode` below is set to `400` instead of `401`. + 400, + err + ); + } + const connector = connectorList.find(({ actionTypeId, isDeprecated, isMissingSecrets }) => { + return actionTypeId === this.connectorTypeId && !isDeprecated && !isMissingSecrets; + }); + + if (!connector) { + this.log.debug(stringify(connectorList)); + throw new ResponseActionsConnectorNotConfiguredError(this.connectorTypeId); + } + + this.log.debug( + `Using [${this.connectorTypeId}] stack connector: "${connector.name}" (ID: ${connector.id})` + ); + + return connector; + }); + } + + private isUnsecuredActionsClient( + client: ActionsClient | IUnsecuredActionsClient + ): client is IUnsecuredActionsClient { + // The methods below only exist in the normal `ActionsClient` + return !('create' in client) && !('delete' in client) && !('update' in client); + } + + public async execute< + TResponse = unknown, + TParams extends Record = Record + >({ + spaceId = 'default', + params, + }: NormalizedExternalConnectorClientExecuteOptions): Promise< + ActionTypeExecutorResult + > { + const { id: connectorId } = await this.getConnectorInstance(); + + if (this.isUnsecuredActionsClient(this.connectorsClient)) { + return this.connectorsClient.execute({ + requesterId: 'background_task', + id: connectorId, + spaceId, + params, + }) as Promise>; + } + + return this.connectorsClient.execute({ + actionId: connectorId, + params, + }) as Promise>; + } + + protected async getAll(spaceId: string = 'default'): ReturnType { + if (this.isUnsecuredActionsClient(this.connectorsClient)) { + return this.connectorsClient.getAll(spaceId); + } + + return this.connectorsClient.getAll(); + } +} diff --git a/x-pack/plugins/security_solution/server/endpoint/services/actions/clients/mocks.ts b/x-pack/plugins/security_solution/server/endpoint/services/actions/clients/mocks.ts index 62e13de5002b3..39497e18f18e3 100644 --- a/x-pack/plugins/security_solution/server/endpoint/services/actions/clients/mocks.ts +++ b/x-pack/plugins/security_solution/server/endpoint/services/actions/clients/mocks.ts @@ -5,6 +5,7 @@ * 2.0. */ +import type { ActionsClientMock } from '@kbn/actions-plugin/server/actions_client/actions_client.mock'; import { actionsClientMock } from '@kbn/actions-plugin/server/actions_client/actions_client.mock'; import type { ConnectorWithExtraFindData } from '@kbn/actions-plugin/server/application/connector/types'; import type { DeepPartial } from 'utility-types'; @@ -252,6 +253,20 @@ const createConnectorActionExecuteResponseMock = ( return merge(result, overrides); }; +const createConnectorActionsClientMock = ({ + getAllResponse, +}: { + getAllResponse?: ConnectorWithExtraFindData[]; +} = {}): ActionsClientMock => { + const client = actionsClientMock.create(); + + (client.getAll as jest.Mock).mockImplementation(async () => { + return getAllResponse ?? []; + }); + + return client; +}; + export const responseActionsClientMock = Object.freeze({ create: createResponseActionClientMock, createConstructorOptions: createConstructorOptionsMock, @@ -268,7 +283,8 @@ export const responseActionsClientMock = Object.freeze({ createIndexedResponse: createEsIndexTransportResponseMock, // Some common mocks when working with connector actions - createConnectorActionsClient: actionsClientMock.create, + createConnectorActionsClient: createConnectorActionsClientMock, + /** Create a mock connector instance */ createConnector: createConnectorMock, createConnectorActionExecuteResponse: createConnectorActionExecuteResponseMock, }); diff --git a/x-pack/plugins/security_solution/server/endpoint/services/actions/clients/sentinelone/sentinel_one_actions_client.test.ts b/x-pack/plugins/security_solution/server/endpoint/services/actions/clients/sentinelone/sentinel_one_actions_client.test.ts index 0fdd9612bdaa2..b2e521060edfc 100644 --- a/x-pack/plugins/security_solution/server/endpoint/services/actions/clients/sentinelone/sentinel_one_actions_client.test.ts +++ b/x-pack/plugins/security_solution/server/endpoint/services/actions/clients/sentinelone/sentinel_one_actions_client.test.ts @@ -9,7 +9,7 @@ import type { ResponseActionsClient } from '../lib/types'; import { responseActionsClientMock } from '../mocks'; import { SentinelOneActionsClient } from './sentinel_one_actions_client'; import { getActionDetailsById as _getActionDetailsById } from '../../action_details_by_id'; -import { ResponseActionsClientError, ResponseActionsNotSupportedError } from '../errors'; +import { ResponseActionsNotSupportedError } from '../errors'; import type { ActionsClientMock } from '@kbn/actions-plugin/server/actions_client/actions_client.mock'; import type { SentinelOneActionsClientOptionsMock } from './mocks'; import { sentinelOneMock } from './mocks'; @@ -64,50 +64,6 @@ describe('SentinelOneActionsClient class', () => { } ); - it('should error if unable to retrieve list of connectors', async () => { - connectorActionsMock.getAll.mockImplementation(async () => { - throw new Error('oh oh'); - }); - const responsePromise = s1ActionsClient.isolate(createS1IsolationOptions()); - - await expect(responsePromise).rejects.toBeInstanceOf(ResponseActionsClientError); - await expect(responsePromise).rejects.toHaveProperty( - 'message', - expect.stringContaining('Unable to retrieve list of stack connectors:') - ); - await expect(responsePromise).rejects.toHaveProperty('statusCode', 400); - }); - - it('should error if retrieving connectors fails', async () => { - (connectorActionsMock.getAll as jest.Mock).mockImplementation(async () => { - throw new Error('oh oh'); - }); - - await expect(s1ActionsClient.isolate(createS1IsolationOptions())).rejects.toMatchObject({ - message: `Unable to retrieve list of stack connectors: oh oh`, - statusCode: 400, - }); - }); - - it.each([ - ['no connector defined', async () => []], - [ - 'deprecated connector', - async () => [responseActionsClientMock.createConnector({ isDeprecated: true })], - ], - [ - 'missing secrets', - async () => [responseActionsClientMock.createConnector({ isMissingSecrets: true })], - ], - ])('should error if: %s', async (_, getAllImplementation) => { - (connectorActionsMock.getAll as jest.Mock).mockImplementation(getAllImplementation); - - await expect(s1ActionsClient.isolate(createS1IsolationOptions())).rejects.toMatchObject({ - message: `No SentinelOne stack connector found`, - statusCode: 400, - }); - }); - it('should error if multiple agent ids are received', async () => { const payload = createS1IsolationOptions(); payload.endpoint_ids.push('second-host-id'); diff --git a/x-pack/plugins/security_solution/server/endpoint/services/actions/clients/sentinelone/sentinel_one_actions_client.ts b/x-pack/plugins/security_solution/server/endpoint/services/actions/clients/sentinelone/sentinel_one_actions_client.ts index b0c6dd6c0211f..ac40e5726997c 100644 --- a/x-pack/plugins/security_solution/server/endpoint/services/actions/clients/sentinelone/sentinel_one_actions_client.ts +++ b/x-pack/plugins/security_solution/server/endpoint/services/actions/clients/sentinelone/sentinel_one_actions_client.ts @@ -5,78 +5,49 @@ * 2.0. */ -import type { ActionsClient } from '@kbn/actions-plugin/server'; +import type { ActionsClient, IUnsecuredActionsClient } from '@kbn/actions-plugin/server'; import { SENTINELONE_CONNECTOR_ID, SUB_ACTION, } from '@kbn/stack-connectors-plugin/common/sentinelone/constants'; -import type { ConnectorWithExtraFindData } from '@kbn/actions-plugin/server/application/connector/types'; -import { once } from 'lodash'; import type { ActionTypeExecutorResult } from '@kbn/actions-plugin/common'; import type { - SentinelOneGetAgentsResponse, SentinelOneGetAgentsParams, + SentinelOneGetAgentsResponse, } from '@kbn/stack-connectors-plugin/common/sentinelone/types'; +import type { NormalizedExternalConnectorClientExecuteOptions } from '../lib/normalized_external_connector_client'; +import { NormalizedExternalConnectorClient } from '../lib/normalized_external_connector_client'; import type { CommonResponseActionMethodOptions, ProcessPendingActionsMethodOptions, } from '../../..'; import type { ResponseActionAgentType } from '../../../../../../common/endpoint/service/response_actions/constants'; -import type { SentinelOneConnectorExecuteOptions } from './types'; import { stringify } from '../../../../utils/stringify'; import { ResponseActionsClientError } from '../errors'; import type { ActionDetails, LogsEndpointAction } from '../../../../../../common/endpoint/types'; import type { IsolationRouteRequestBody } from '../../../../../../common/api/endpoint'; import type { ResponseActionsClientOptions, - ResponseActionsClientWriteActionRequestToEndpointIndexOptions, ResponseActionsClientValidateRequestResponse, + ResponseActionsClientWriteActionRequestToEndpointIndexOptions, } from '../lib/base_response_actions_client'; import { ResponseActionsClientImpl } from '../lib/base_response_actions_client'; export type SentinelOneActionsClientOptions = ResponseActionsClientOptions & { - connectorActions: ActionsClient; + connectorActions: ActionsClient | IUnsecuredActionsClient; }; export class SentinelOneActionsClient extends ResponseActionsClientImpl { protected readonly agentType: ResponseActionAgentType = 'sentinel_one'; - private readonly connectorActionsClient: ActionsClient; - private readonly getConnector: () => Promise; + private readonly connectorActionsClient: NormalizedExternalConnectorClient; constructor({ connectorActions, ...options }: SentinelOneActionsClientOptions) { super(options); - this.connectorActionsClient = connectorActions; - - this.getConnector = once(async () => { - let connectorList: ConnectorWithExtraFindData[] = []; - - try { - connectorList = await this.connectorActionsClient.getAll(); - } catch (err) { - throw new ResponseActionsClientError( - `Unable to retrieve list of stack connectors: ${err.message}`, - // failure here is likely due to Authz, but because we don't have a good way to determine that, - // the `statusCode` below is set to `400` instead of `401`. - 400, - err - ); - } - const connector = connectorList.find(({ actionTypeId, isDeprecated, isMissingSecrets }) => { - return actionTypeId === SENTINELONE_CONNECTOR_ID && !isDeprecated && !isMissingSecrets; - }); - - if (!connector) { - throw new ResponseActionsClientError( - `No SentinelOne stack connector found`, - 400, - connectorList - ); - } - - this.log.debug(`Using SentinelOne stack connector: ${connector.name} (${connector.id})`); - - return connector; - }); + this.connectorActionsClient = new NormalizedExternalConnectorClient( + SENTINELONE_CONNECTOR_ID, + connectorActions, + this.log + ); } protected async writeActionRequestToEndpointIndex( @@ -101,9 +72,7 @@ export class SentinelOneActionsClient extends ResponseActionsClientImpl { actionType: SUB_ACTION, actionParams: object ): Promise> { - const { id: connectorId } = await this.getConnector(); const executeOptions: Parameters[0] = { - actionId: connectorId, params: { subAction: actionType, subActionParams: actionParams, @@ -133,10 +102,12 @@ export class SentinelOneActionsClient extends ResponseActionsClientImpl { return actionSendResponse; } + /** Gets agent details directly from SentinelOne */ private async getAgentDetails(id: string): Promise { - const { id: connectorId } = await this.getConnector(); - const executeOptions: SentinelOneConnectorExecuteOptions = { - actionId: connectorId, + const executeOptions: NormalizedExternalConnectorClientExecuteOptions< + SentinelOneGetAgentsParams, + SUB_ACTION + > = { params: { subAction: SUB_ACTION.GET_AGENTS, subActionParams: { @@ -145,23 +116,15 @@ export class SentinelOneActionsClient extends ResponseActionsClientImpl { }, }; - let s1ApiResponse: SentinelOneGetAgentsResponse | undefined; - - try { - const response = (await this.connectorActionsClient.execute( + const s1ApiResponse: SentinelOneGetAgentsResponse | undefined = ( + (await this.connectorActionsClient.execute( executeOptions - )) as ActionTypeExecutorResult; + )) as ActionTypeExecutorResult + ).data; - this.log.debug(`Response for SentinelOne agent id [${id}] returned:\n${stringify(response)}`); - - s1ApiResponse = response.data; - } catch (err) { - throw new ResponseActionsClientError( - `Error while attempting to retrieve SentinelOne host with agent id [${id}]`, - 500, - err - ); - } + this.log.debug( + `Response for SentinelOne agent id [${id}] returned:\n${stringify(s1ApiResponse)}` + ); if (!s1ApiResponse || !s1ApiResponse.data[0]) { throw new ResponseActionsClientError(`SentinelOne agent id [${id}] not found`, 404); @@ -309,5 +272,7 @@ export class SentinelOneActionsClient extends ResponseActionsClientImpl { // if (abortSignal.aborted) { // return; // } + // Dev test entry below + // await this.getAgentDetails('123').catch(() => {}); } } diff --git a/x-pack/plugins/security_solution/server/plugin.ts b/x-pack/plugins/security_solution/server/plugin.ts index ec0e3cb16160c..4fd430038bc4e 100644 --- a/x-pack/plugins/security_solution/server/plugin.ts +++ b/x-pack/plugins/security_solution/server/plugin.ts @@ -645,6 +645,7 @@ export class Plugin implements ISecuritySolutionPlugin { esClient: core.elasticsearch.client.asInternalUser, productFeaturesService, savedObjectsClient, + connectorActions: plugins.actions, }); if (plugins.taskManager) { diff --git a/x-pack/plugins/security_solution/server/plugin_contract.ts b/x-pack/plugins/security_solution/server/plugin_contract.ts index 1a0dd6bccc15c..e441b0d3f5e03 100644 --- a/x-pack/plugins/security_solution/server/plugin_contract.ts +++ b/x-pack/plugins/security_solution/server/plugin_contract.ts @@ -42,6 +42,7 @@ import type { SharePluginStart } from '@kbn/share-plugin/server'; import type { GuidedOnboardingPluginSetup } from '@kbn/guided-onboarding-plugin/server'; import type { PluginSetup as UnifiedSearchServerPluginSetup } from '@kbn/unified-search-plugin/server'; import type { ElasticAssistantPluginStart } from '@kbn/elastic-assistant-plugin/server'; +import type { PluginStartContract as ActionsPluginStartContract } from '@kbn/actions-plugin/server'; import type { ProductFeaturesService } from './lib/product_features_service/product_features_service'; import type { ExperimentalFeatures } from '../common'; @@ -84,6 +85,7 @@ export interface SecuritySolutionPluginStartDependencies { taskManager?: TaskManagerPluginStart; telemetry?: TelemetryPluginStart; share: SharePluginStart; + actions: ActionsPluginStartContract; } export interface SecuritySolutionPluginSetup { diff --git a/x-pack/plugins/security_solution_serverless/public/navigation/side_navigation/index.tsx b/x-pack/plugins/security_solution_serverless/public/navigation/side_navigation/index.tsx index 1e2d3a45827b2..5f7a5511d5ab2 100644 --- a/x-pack/plugins/security_solution_serverless/public/navigation/side_navigation/index.tsx +++ b/x-pack/plugins/security_solution_serverless/public/navigation/side_navigation/index.tsx @@ -10,6 +10,9 @@ import { type Services } from '../../common/services'; export const initSideNavigation = (services: Services) => { import('./project_navigation').then(({ init }) => { const { navigationTree$, panelContentProvider, dataTestSubj } = init(services); - services.serverless.initNavigation(navigationTree$, { panelContentProvider, dataTestSubj }); + services.serverless.initNavigation('security', navigationTree$, { + panelContentProvider, + dataTestSubj, + }); }); }; diff --git a/x-pack/plugins/serverless/public/plugin.tsx b/x-pack/plugins/serverless/public/plugin.tsx index d26e9b66b8d47..95a9ff43c4270 100644 --- a/x-pack/plugins/serverless/public/plugin.tsx +++ b/x-pack/plugins/serverless/public/plugin.tsx @@ -81,8 +81,8 @@ export class ServerlessPlugin return { setSideNavComponentDeprecated: (sideNavigationComponent) => project.setSideNavComponent(sideNavigationComponent), - initNavigation: (navigationTree$, { panelContentProvider, dataTestSubj } = {}) => { - project.initNavigation(navigationTree$); + initNavigation: (id, navigationTree$, { panelContentProvider, dataTestSubj } = {}) => { + project.initNavigation(id, navigationTree$); project.setSideNavComponent(() => ( void; setProjectHome(homeHref: string): void; initNavigation( + id: string, navigationTree$: Observable, config?: { dataTestSubj?: string; diff --git a/x-pack/plugins/serverless_observability/public/plugin.ts b/x-pack/plugins/serverless_observability/public/plugin.ts index d7936f5cd6225..76c278f9e374a 100644 --- a/x-pack/plugins/serverless_observability/public/plugin.ts +++ b/x-pack/plugins/serverless_observability/public/plugin.ts @@ -55,7 +55,7 @@ export class ServerlessObservabilityPlugin const navigationTree$ = of(navigationTree); serverless.setProjectHome('/app/observability/landing'); - serverless.initNavigation(navigationTree$, { dataTestSubj: 'svlObservabilitySideNav' }); + serverless.initNavigation('oblt', navigationTree$, { dataTestSubj: 'svlObservabilitySideNav' }); const extendCardNavDefinitions = serverless.getNavigationCards( security.authz.isRoleManagementEnabled(), diff --git a/x-pack/plugins/serverless_search/public/plugin.ts b/x-pack/plugins/serverless_search/public/plugin.ts index 027d651a6b23a..d9019f911444a 100644 --- a/x-pack/plugins/serverless_search/public/plugin.ts +++ b/x-pack/plugins/serverless_search/public/plugin.ts @@ -125,7 +125,7 @@ export class ServerlessSearchPlugin serverless.setProjectHome('/app/elasticsearch'); const navigationTree$ = of(navigationTree); - serverless.initNavigation(navigationTree$, { dataTestSubj: 'svlSearchSideNav' }); + serverless.initNavigation('search', navigationTree$, { dataTestSubj: 'svlSearchSideNav' }); const extendCardNavDefinitions = serverless.getNavigationCards( security.authz.isRoleManagementEnabled() diff --git a/x-pack/plugins/task_manager/server/metrics/create_aggregator.test.ts b/x-pack/plugins/task_manager/server/metrics/create_aggregator.test.ts index 196170f716d29..07323cb5b203d 100644 --- a/x-pack/plugins/task_manager/server/metrics/create_aggregator.test.ts +++ b/x-pack/plugins/task_manager/server/metrics/create_aggregator.test.ts @@ -117,47 +117,102 @@ describe('createAggregator', () => { .subscribe((metrics: Array>) => { expect(metrics[0]).toEqual({ key: 'task_claim', - value: { success: 1, total: 1, duration: { counts: [1], values: [100] } }, + value: { + success: 1, + total: 1, + duration: { counts: [1], values: [100] }, + duration_values: [10], + }, }); expect(metrics[1]).toEqual({ key: 'task_claim', - value: { success: 2, total: 2, duration: { counts: [2], values: [100] } }, + value: { + success: 2, + total: 2, + duration: { counts: [2], values: [100] }, + duration_values: [10, 10], + }, }); expect(metrics[2]).toEqual({ key: 'task_claim', - value: { success: 3, total: 3, duration: { counts: [3], values: [100] } }, + value: { + success: 3, + total: 3, + duration: { counts: [3], values: [100] }, + duration_values: [10, 10, 10], + }, }); expect(metrics[3]).toEqual({ key: 'task_claim', - value: { success: 4, total: 4, duration: { counts: [4], values: [100] } }, + value: { + success: 4, + total: 4, + duration: { counts: [4], values: [100] }, + duration_values: [10, 10, 10, 10], + }, }); expect(metrics[4]).toEqual({ key: 'task_claim', - value: { success: 4, total: 5, duration: { counts: [4], values: [100] } }, + value: { + success: 4, + total: 5, + duration: { counts: [4], values: [100] }, + duration_values: [10, 10, 10, 10], + }, }); expect(metrics[5]).toEqual({ key: 'task_claim', - value: { success: 5, total: 6, duration: { counts: [5], values: [100] } }, + value: { + success: 5, + total: 6, + duration: { counts: [5], values: [100] }, + duration_values: [10, 10, 10, 10, 10], + }, }); expect(metrics[6]).toEqual({ key: 'task_claim', - value: { success: 6, total: 7, duration: { counts: [6], values: [100] } }, + value: { + success: 6, + total: 7, + duration: { counts: [6], values: [100] }, + duration_values: [10, 10, 10, 10, 10, 10], + }, }); expect(metrics[7]).toEqual({ key: 'task_claim', - value: { success: 7, total: 8, duration: { counts: [7], values: [100] } }, + value: { + success: 7, + total: 8, + duration: { counts: [7], values: [100] }, + duration_values: [10, 10, 10, 10, 10, 10, 10], + }, }); expect(metrics[8]).toEqual({ key: 'task_claim', - value: { success: 8, total: 9, duration: { counts: [8], values: [100] } }, + value: { + success: 8, + total: 9, + duration: { counts: [8], values: [100] }, + duration_values: [10, 10, 10, 10, 10, 10, 10, 10], + }, }); expect(metrics[9]).toEqual({ key: 'task_claim', - value: { success: 8, total: 10, duration: { counts: [8], values: [100] } }, + value: { + success: 8, + total: 10, + duration: { counts: [8], values: [100] }, + duration_values: [10, 10, 10, 10, 10, 10, 10, 10], + }, }); expect(metrics[10]).toEqual({ key: 'task_claim', - value: { success: 9, total: 11, duration: { counts: [9], values: [100] } }, + value: { + success: 9, + total: 11, + duration: { counts: [9], values: [100] }, + duration_values: [10, 10, 10, 10, 10, 10, 10, 10, 10], + }, }); resolve(); }); @@ -209,48 +264,103 @@ describe('createAggregator', () => { .subscribe((metrics: Array>) => { expect(metrics[0]).toEqual({ key: 'task_claim', - value: { success: 1, total: 1, duration: { counts: [1], values: [100] } }, + value: { + success: 1, + total: 1, + duration: { counts: [1], values: [100] }, + duration_values: [10], + }, }); expect(metrics[1]).toEqual({ key: 'task_claim', - value: { success: 2, total: 2, duration: { counts: [2], values: [100] } }, + value: { + success: 2, + total: 2, + duration: { counts: [2], values: [100] }, + duration_values: [10, 10], + }, }); expect(metrics[2]).toEqual({ key: 'task_claim', - value: { success: 3, total: 3, duration: { counts: [3], values: [100] } }, + value: { + success: 3, + total: 3, + duration: { counts: [3], values: [100] }, + duration_values: [10, 10, 10], + }, }); expect(metrics[3]).toEqual({ key: 'task_claim', - value: { success: 4, total: 4, duration: { counts: [4], values: [100] } }, + value: { + success: 4, + total: 4, + duration: { counts: [4], values: [100] }, + duration_values: [10, 10, 10, 10], + }, }); expect(metrics[4]).toEqual({ key: 'task_claim', - value: { success: 4, total: 5, duration: { counts: [4], values: [100] } }, + value: { + success: 4, + total: 5, + duration: { counts: [4], values: [100] }, + duration_values: [10, 10, 10, 10], + }, }); expect(metrics[5]).toEqual({ key: 'task_claim', - value: { success: 5, total: 6, duration: { counts: [5], values: [100] } }, + value: { + success: 5, + total: 6, + duration: { counts: [5], values: [100] }, + duration_values: [10, 10, 10, 10, 10], + }, }); // reset event should have been received here expect(metrics[6]).toEqual({ key: 'task_claim', - value: { success: 1, total: 1, duration: { counts: [1], values: [100] } }, + value: { + success: 1, + total: 1, + duration: { counts: [1], values: [100] }, + duration_values: [10], + }, }); expect(metrics[7]).toEqual({ key: 'task_claim', - value: { success: 1, total: 2, duration: { counts: [1], values: [100] } }, + value: { + success: 1, + total: 2, + duration: { counts: [1], values: [100] }, + duration_values: [10], + }, }); expect(metrics[8]).toEqual({ key: 'task_claim', - value: { success: 1, total: 3, duration: { counts: [1], values: [100] } }, + value: { + success: 1, + total: 3, + duration: { counts: [1], values: [100] }, + duration_values: [10], + }, }); expect(metrics[9]).toEqual({ key: 'task_claim', - value: { success: 2, total: 4, duration: { counts: [2], values: [100] } }, + value: { + success: 2, + total: 4, + duration: { counts: [2], values: [100] }, + duration_values: [10, 10], + }, }); expect(metrics[10]).toEqual({ key: 'task_claim', - value: { success: 3, total: 5, duration: { counts: [3], values: [100] } }, + value: { + success: 3, + total: 5, + duration: { counts: [3], values: [100] }, + duration_values: [10, 10, 10], + }, }); resolve(); }); @@ -310,48 +420,103 @@ describe('createAggregator', () => { .subscribe((metrics: Array>) => { expect(metrics[0]).toEqual({ key: 'task_claim', - value: { success: 1, total: 1, duration: { counts: [1], values: [100] } }, + value: { + success: 1, + total: 1, + duration: { counts: [1], values: [100] }, + duration_values: [10], + }, }); expect(metrics[1]).toEqual({ key: 'task_claim', - value: { success: 2, total: 2, duration: { counts: [2], values: [100] } }, + value: { + success: 2, + total: 2, + duration: { counts: [2], values: [100] }, + duration_values: [10, 10], + }, }); expect(metrics[2]).toEqual({ key: 'task_claim', - value: { success: 3, total: 3, duration: { counts: [3], values: [100] } }, + value: { + success: 3, + total: 3, + duration: { counts: [3], values: [100] }, + duration_values: [10, 10, 10], + }, }); expect(metrics[3]).toEqual({ key: 'task_claim', - value: { success: 4, total: 4, duration: { counts: [4], values: [100] } }, + value: { + success: 4, + total: 4, + duration: { counts: [4], values: [100] }, + duration_values: [10, 10, 10, 10], + }, }); expect(metrics[4]).toEqual({ key: 'task_claim', - value: { success: 4, total: 5, duration: { counts: [4], values: [100] } }, + value: { + success: 4, + total: 5, + duration: { counts: [4], values: [100] }, + duration_values: [10, 10, 10, 10], + }, }); expect(metrics[5]).toEqual({ key: 'task_claim', - value: { success: 5, total: 6, duration: { counts: [5], values: [100] } }, + value: { + success: 5, + total: 6, + duration: { counts: [5], values: [100] }, + duration_values: [10, 10, 10, 10, 10], + }, }); // reset interval should have fired here expect(metrics[6]).toEqual({ key: 'task_claim', - value: { success: 1, total: 1, duration: { counts: [1], values: [100] } }, + value: { + success: 1, + total: 1, + duration: { counts: [1], values: [100] }, + duration_values: [10], + }, }); expect(metrics[7]).toEqual({ key: 'task_claim', - value: { success: 1, total: 2, duration: { counts: [1], values: [100] } }, + value: { + success: 1, + total: 2, + duration: { counts: [1], values: [100] }, + duration_values: [10], + }, }); expect(metrics[8]).toEqual({ key: 'task_claim', - value: { success: 1, total: 3, duration: { counts: [1], values: [100] } }, + value: { + success: 1, + total: 3, + duration: { counts: [1], values: [100] }, + duration_values: [10], + }, }); expect(metrics[9]).toEqual({ key: 'task_claim', - value: { success: 2, total: 4, duration: { counts: [2], values: [100] } }, + value: { + success: 2, + total: 4, + duration: { counts: [2], values: [100] }, + duration_values: [10, 10], + }, }); expect(metrics[10]).toEqual({ key: 'task_claim', - value: { success: 3, total: 5, duration: { counts: [3], values: [100] } }, + value: { + success: 3, + total: 5, + duration: { counts: [3], values: [100] }, + duration_values: [10, 10, 10], + }, }); resolve(); }); @@ -423,6 +588,7 @@ describe('createAggregator', () => { not_timed_out: 0, total: 0, delay: { counts: [1], values: [10] }, + delay_values: [3], framework_errors: 0, user_errors: 0, total_errors: 0, @@ -437,6 +603,7 @@ describe('createAggregator', () => { not_timed_out: 1, total: 1, delay: { counts: [1], values: [10] }, + delay_values: [3], framework_errors: 0, user_errors: 0, total_errors: 0, @@ -469,6 +636,7 @@ describe('createAggregator', () => { not_timed_out: 1, total: 1, delay: { counts: [1, 1], values: [10, 20] }, + delay_values: [3, 10], framework_errors: 0, user_errors: 0, total_errors: 0, @@ -501,6 +669,7 @@ describe('createAggregator', () => { not_timed_out: 2, total: 2, delay: { counts: [1, 1], values: [10, 20] }, + delay_values: [3, 10], framework_errors: 0, user_errors: 0, total_errors: 0, @@ -541,6 +710,7 @@ describe('createAggregator', () => { not_timed_out: 2, total: 2, delay: { counts: [2, 1], values: [10, 20] }, + delay_values: [3, 10, 3], framework_errors: 0, user_errors: 0, total_errors: 0, @@ -581,6 +751,7 @@ describe('createAggregator', () => { not_timed_out: 3, total: 3, delay: { counts: [2, 1], values: [10, 20] }, + delay_values: [3, 10, 3], framework_errors: 0, user_errors: 0, total_errors: 0, @@ -621,6 +792,7 @@ describe('createAggregator', () => { not_timed_out: 3, total: 3, delay: { counts: [2, 1, 0, 1], values: [10, 20, 30, 40] }, + delay_values: [3, 10, 3, 35], framework_errors: 0, user_errors: 0, total_errors: 0, @@ -661,6 +833,7 @@ describe('createAggregator', () => { not_timed_out: 4, total: 4, delay: { counts: [2, 1, 0, 1], values: [10, 20, 30, 40] }, + delay_values: [3, 10, 3, 35], framework_errors: 0, user_errors: 0, total_errors: 0, @@ -709,6 +882,7 @@ describe('createAggregator', () => { not_timed_out: 4, total: 4, delay: { counts: [3, 1, 0, 1], values: [10, 20, 30, 40] }, + delay_values: [3, 10, 3, 35, 9], framework_errors: 0, user_errors: 0, total_errors: 0, @@ -757,6 +931,7 @@ describe('createAggregator', () => { not_timed_out: 5, total: 5, delay: { counts: [3, 1, 0, 1], values: [10, 20, 30, 40] }, + delay_values: [3, 10, 3, 35, 9], framework_errors: 1, user_errors: 0, total_errors: 1, @@ -805,6 +980,7 @@ describe('createAggregator', () => { not_timed_out: 5, total: 5, delay: { counts: [4, 1, 0, 1], values: [10, 20, 30, 40] }, + delay_values: [3, 10, 3, 35, 9, 5], framework_errors: 1, user_errors: 0, total_errors: 1, @@ -853,6 +1029,7 @@ describe('createAggregator', () => { not_timed_out: 6, total: 6, delay: { counts: [4, 1, 0, 1], values: [10, 20, 30, 40] }, + delay_values: [3, 10, 3, 35, 9, 5], framework_errors: 1, user_errors: 0, total_errors: 1, @@ -909,6 +1086,7 @@ describe('createAggregator', () => { not_timed_out: 6, total: 6, delay: { counts: [4, 2, 0, 1], values: [10, 20, 30, 40] }, + delay_values: [3, 10, 3, 35, 9, 5, 12], framework_errors: 1, user_errors: 0, total_errors: 1, @@ -965,6 +1143,7 @@ describe('createAggregator', () => { not_timed_out: 7, total: 7, delay: { counts: [4, 2, 0, 1], values: [10, 20, 30, 40] }, + delay_values: [3, 10, 3, 35, 9, 5, 12], framework_errors: 1, user_errors: 0, total_errors: 1, @@ -1021,6 +1200,7 @@ describe('createAggregator', () => { not_timed_out: 7, total: 7, delay: { counts: [5, 2, 0, 1], values: [10, 20, 30, 40] }, + delay_values: [3, 10, 3, 35, 9, 5, 12, 4], framework_errors: 1, user_errors: 0, total_errors: 1, @@ -1077,6 +1257,7 @@ describe('createAggregator', () => { not_timed_out: 8, total: 8, delay: { counts: [5, 2, 0, 1], values: [10, 20, 30, 40] }, + delay_values: [3, 10, 3, 35, 9, 5, 12, 4], framework_errors: 2, user_errors: 0, total_errors: 2, @@ -1133,6 +1314,7 @@ describe('createAggregator', () => { not_timed_out: 8, total: 8, delay: { counts: [6, 2, 0, 1], values: [10, 20, 30, 40] }, + delay_values: [3, 10, 3, 35, 9, 5, 12, 4, 4], framework_errors: 2, user_errors: 0, total_errors: 2, @@ -1189,6 +1371,7 @@ describe('createAggregator', () => { not_timed_out: 9, total: 9, delay: { counts: [6, 2, 0, 1], values: [10, 20, 30, 40] }, + delay_values: [3, 10, 3, 35, 9, 5, 12, 4, 4], framework_errors: 2, user_errors: 0, total_errors: 2, @@ -1245,6 +1428,7 @@ describe('createAggregator', () => { not_timed_out: 9, total: 9, delay: { counts: [7, 2, 0, 1], values: [10, 20, 30, 40] }, + delay_values: [3, 10, 3, 35, 9, 5, 12, 4, 4, 3], framework_errors: 2, user_errors: 0, total_errors: 2, @@ -1301,6 +1485,7 @@ describe('createAggregator', () => { not_timed_out: 10, total: 10, delay: { counts: [7, 2, 0, 1], values: [10, 20, 30, 40] }, + delay_values: [3, 10, 3, 35, 9, 5, 12, 4, 4, 3], framework_errors: 3, user_errors: 0, total_errors: 3, @@ -1431,6 +1616,7 @@ describe('createAggregator', () => { not_timed_out: 0, total: 0, delay: { counts: [1], values: [10] }, + delay_values: [3], framework_errors: 0, user_errors: 0, total_errors: 0, @@ -1445,6 +1631,7 @@ describe('createAggregator', () => { not_timed_out: 1, total: 1, delay: { counts: [1], values: [10] }, + delay_values: [3], framework_errors: 0, user_errors: 0, total_errors: 0, @@ -1477,6 +1664,7 @@ describe('createAggregator', () => { not_timed_out: 1, total: 1, delay: { counts: [1, 1], values: [10, 20] }, + delay_values: [3, 10], framework_errors: 0, user_errors: 0, total_errors: 0, @@ -1509,6 +1697,7 @@ describe('createAggregator', () => { not_timed_out: 2, total: 2, delay: { counts: [1, 1], values: [10, 20] }, + delay_values: [3, 10], framework_errors: 0, user_errors: 0, total_errors: 0, @@ -1549,6 +1738,7 @@ describe('createAggregator', () => { not_timed_out: 2, total: 2, delay: { counts: [2, 1], values: [10, 20] }, + delay_values: [3, 10, 3], framework_errors: 0, user_errors: 0, total_errors: 0, @@ -1589,6 +1779,7 @@ describe('createAggregator', () => { not_timed_out: 3, total: 3, delay: { counts: [2, 1], values: [10, 20] }, + delay_values: [3, 10, 3], framework_errors: 0, user_errors: 0, total_errors: 0, @@ -1629,6 +1820,7 @@ describe('createAggregator', () => { not_timed_out: 3, total: 3, delay: { counts: [2, 1, 0, 1], values: [10, 20, 30, 40] }, + delay_values: [3, 10, 3, 35], framework_errors: 0, user_errors: 0, total_errors: 0, @@ -1669,6 +1861,7 @@ describe('createAggregator', () => { not_timed_out: 4, total: 4, delay: { counts: [2, 1, 0, 1], values: [10, 20, 30, 40] }, + delay_values: [3, 10, 3, 35], framework_errors: 0, user_errors: 0, total_errors: 0, @@ -1717,6 +1910,7 @@ describe('createAggregator', () => { not_timed_out: 4, total: 4, delay: { counts: [3, 1, 0, 1], values: [10, 20, 30, 40] }, + delay_values: [3, 10, 3, 35, 9], framework_errors: 0, user_errors: 0, total_errors: 0, @@ -1765,6 +1959,7 @@ describe('createAggregator', () => { not_timed_out: 5, total: 5, delay: { counts: [3, 1, 0, 1], values: [10, 20, 30, 40] }, + delay_values: [3, 10, 3, 35, 9], framework_errors: 1, user_errors: 0, total_errors: 1, @@ -1814,6 +2009,7 @@ describe('createAggregator', () => { not_timed_out: 0, total: 0, delay: { counts: [1], values: [10] }, + delay_values: [5], framework_errors: 0, user_errors: 0, total_errors: 0, @@ -1862,6 +2058,7 @@ describe('createAggregator', () => { not_timed_out: 1, total: 1, delay: { counts: [1], values: [10] }, + delay_values: [5], framework_errors: 0, user_errors: 0, total_errors: 0, @@ -1910,6 +2107,7 @@ describe('createAggregator', () => { not_timed_out: 1, total: 1, delay: { counts: [1, 1], values: [10, 20] }, + delay_values: [5, 12], framework_errors: 0, user_errors: 0, total_errors: 0, @@ -1958,6 +2156,7 @@ describe('createAggregator', () => { not_timed_out: 2, total: 2, delay: { counts: [1, 1], values: [10, 20] }, + delay_values: [5, 12], framework_errors: 0, user_errors: 0, total_errors: 0, @@ -2006,6 +2205,7 @@ describe('createAggregator', () => { not_timed_out: 2, total: 2, delay: { counts: [2, 1], values: [10, 20] }, + delay_values: [5, 12, 4], framework_errors: 0, user_errors: 0, total_errors: 0, @@ -2054,6 +2254,7 @@ describe('createAggregator', () => { not_timed_out: 3, total: 3, delay: { counts: [2, 1], values: [10, 20] }, + delay_values: [5, 12, 4], framework_errors: 1, user_errors: 0, total_errors: 1, @@ -2102,6 +2303,7 @@ describe('createAggregator', () => { not_timed_out: 3, total: 3, delay: { counts: [3, 1], values: [10, 20] }, + delay_values: [5, 12, 4, 4], framework_errors: 1, user_errors: 0, total_errors: 1, @@ -2150,6 +2352,7 @@ describe('createAggregator', () => { not_timed_out: 4, total: 4, delay: { counts: [3, 1], values: [10, 20] }, + delay_values: [5, 12, 4, 4], framework_errors: 1, user_errors: 0, total_errors: 1, @@ -2198,6 +2401,7 @@ describe('createAggregator', () => { not_timed_out: 4, total: 4, delay: { counts: [4, 1], values: [10, 20] }, + delay_values: [5, 12, 4, 4, 3], framework_errors: 1, user_errors: 0, total_errors: 1, @@ -2246,6 +2450,7 @@ describe('createAggregator', () => { not_timed_out: 5, total: 5, delay: { counts: [4, 1], values: [10, 20] }, + delay_values: [5, 12, 4, 4, 3], framework_errors: 2, user_errors: 0, total_errors: 2, @@ -2376,6 +2581,7 @@ describe('createAggregator', () => { not_timed_out: 0, total: 0, delay: { counts: [1], values: [10] }, + delay_values: [3], framework_errors: 0, user_errors: 0, total_errors: 0, @@ -2390,6 +2596,7 @@ describe('createAggregator', () => { not_timed_out: 1, total: 1, delay: { counts: [1], values: [10] }, + delay_values: [3], framework_errors: 0, user_errors: 0, total_errors: 0, @@ -2422,6 +2629,7 @@ describe('createAggregator', () => { not_timed_out: 1, total: 1, delay: { counts: [1, 1], values: [10, 20] }, + delay_values: [3, 10], framework_errors: 0, user_errors: 0, total_errors: 0, @@ -2454,6 +2662,7 @@ describe('createAggregator', () => { not_timed_out: 2, total: 2, delay: { counts: [1, 1], values: [10, 20] }, + delay_values: [3, 10], framework_errors: 0, user_errors: 0, total_errors: 0, @@ -2494,6 +2703,7 @@ describe('createAggregator', () => { not_timed_out: 2, total: 2, delay: { counts: [2, 1], values: [10, 20] }, + delay_values: [3, 10, 3], framework_errors: 0, user_errors: 0, total_errors: 0, @@ -2534,6 +2744,7 @@ describe('createAggregator', () => { not_timed_out: 3, total: 3, delay: { counts: [2, 1], values: [10, 20] }, + delay_values: [3, 10, 3], framework_errors: 0, user_errors: 0, total_errors: 0, @@ -2574,6 +2785,7 @@ describe('createAggregator', () => { not_timed_out: 3, total: 3, delay: { counts: [2, 1, 0, 1], values: [10, 20, 30, 40] }, + delay_values: [3, 10, 3, 35], framework_errors: 0, user_errors: 0, total_errors: 0, @@ -2614,6 +2826,7 @@ describe('createAggregator', () => { not_timed_out: 4, total: 4, delay: { counts: [2, 1, 0, 1], values: [10, 20, 30, 40] }, + delay_values: [3, 10, 3, 35], framework_errors: 0, user_errors: 0, total_errors: 0, @@ -2662,6 +2875,7 @@ describe('createAggregator', () => { not_timed_out: 4, total: 4, delay: { counts: [3, 1, 0, 1], values: [10, 20, 30, 40] }, + delay_values: [3, 10, 3, 35, 9], framework_errors: 0, user_errors: 0, total_errors: 0, @@ -2710,6 +2924,7 @@ describe('createAggregator', () => { not_timed_out: 5, total: 5, delay: { counts: [3, 1, 0, 1], values: [10, 20, 30, 40] }, + delay_values: [3, 10, 3, 35, 9], framework_errors: 1, user_errors: 0, total_errors: 1, @@ -2759,6 +2974,7 @@ describe('createAggregator', () => { not_timed_out: 0, total: 0, delay: { counts: [1], values: [10] }, + delay_values: [5], framework_errors: 0, user_errors: 0, total_errors: 0, @@ -2807,6 +3023,7 @@ describe('createAggregator', () => { not_timed_out: 1, total: 1, delay: { counts: [1], values: [10] }, + delay_values: [5], framework_errors: 0, user_errors: 0, total_errors: 0, @@ -2855,6 +3072,7 @@ describe('createAggregator', () => { not_timed_out: 1, total: 1, delay: { counts: [1, 1], values: [10, 20] }, + delay_values: [5, 12], framework_errors: 0, user_errors: 0, total_errors: 0, @@ -2903,6 +3121,7 @@ describe('createAggregator', () => { not_timed_out: 2, total: 2, delay: { counts: [1, 1], values: [10, 20] }, + delay_values: [5, 12], framework_errors: 0, user_errors: 0, total_errors: 0, @@ -2951,6 +3170,7 @@ describe('createAggregator', () => { not_timed_out: 2, total: 2, delay: { counts: [2, 1], values: [10, 20] }, + delay_values: [5, 12, 4], framework_errors: 0, user_errors: 0, total_errors: 0, @@ -2999,6 +3219,7 @@ describe('createAggregator', () => { not_timed_out: 3, total: 3, delay: { counts: [2, 1], values: [10, 20] }, + delay_values: [5, 12, 4], framework_errors: 1, user_errors: 0, total_errors: 1, @@ -3047,6 +3268,7 @@ describe('createAggregator', () => { not_timed_out: 3, total: 3, delay: { counts: [3, 1], values: [10, 20] }, + delay_values: [5, 12, 4, 4], framework_errors: 1, user_errors: 0, total_errors: 1, @@ -3095,6 +3317,7 @@ describe('createAggregator', () => { not_timed_out: 4, total: 4, delay: { counts: [3, 1], values: [10, 20] }, + delay_values: [5, 12, 4, 4], framework_errors: 1, user_errors: 0, total_errors: 1, @@ -3143,6 +3366,7 @@ describe('createAggregator', () => { not_timed_out: 4, total: 4, delay: { counts: [4, 1], values: [10, 20] }, + delay_values: [5, 12, 4, 4, 3], framework_errors: 1, user_errors: 0, total_errors: 1, @@ -3191,6 +3415,7 @@ describe('createAggregator', () => { not_timed_out: 5, total: 5, delay: { counts: [4, 1], values: [10, 20] }, + delay_values: [5, 12, 4, 4, 3], framework_errors: 2, user_errors: 0, total_errors: 2, @@ -3339,6 +3564,7 @@ describe('createAggregator', () => { counts: [3, 0, 2, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1], values: [10, 20, 30, 40, 50, 60, 70, 80, 90, 100, 110, 120, 130], }, + overdue_by_values: [0, 0, 0, 20, 20, 40, 120], }, by_type: { 'alerting:example': { @@ -3346,36 +3572,42 @@ describe('createAggregator', () => { counts: [0, 0, 0, 0, 1], values: [10, 20, 30, 40, 50], }, + overdue_by_values: [40], }, 'alerting:__index-threshold': { overdue_by: { counts: [0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1], values: [10, 20, 30, 40, 50, 60, 70, 80, 90, 100, 110, 120, 130], }, + overdue_by_values: [20, 20, 120], }, alerting: { overdue_by: { counts: [0, 0, 2, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1], values: [10, 20, 30, 40, 50, 60, 70, 80, 90, 100, 110, 120, 130], }, + overdue_by_values: [40, 20, 20, 120], }, 'actions:webhook': { overdue_by: { counts: [2], values: [10], }, + overdue_by_values: [0, 0], }, 'actions:__email': { overdue_by: { counts: [1], values: [10], }, + overdue_by_values: [0], }, actions: { overdue_by: { counts: [3], values: [10], }, + overdue_by_values: [0, 0, 0], }, }, }, @@ -3383,7 +3615,10 @@ describe('createAggregator', () => { expect(metrics[1]).toEqual({ key: 'task_overdue', value: { - overall: { overdue_by: { counts: [], values: [] } }, + overall: { + overdue_by: { counts: [], values: [] }, + overdue_by_values: [], + }, by_type: {}, }, }); @@ -3395,6 +3630,9 @@ describe('createAggregator', () => { counts: [16, 0, 1, 2, 0, 1], values: [10, 20, 30, 40, 50, 60], }, + overdue_by_values: [ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 20, 30, 30, 50, + ], }, by_type: { telemetry: { @@ -3402,30 +3640,35 @@ describe('createAggregator', () => { counts: [1, 0, 1], values: [10, 20, 30], }, + overdue_by_values: [0, 20], }, reporting: { overdue_by: { counts: [1], values: [10], }, + overdue_by_values: [0], }, 'actions:webhook': { overdue_by: { counts: [3, 0, 0, 2, 0, 1], values: [10, 20, 30, 40, 50, 60], }, + overdue_by_values: [0, 0, 0, 30, 30, 50], }, 'actions:__email': { overdue_by: { counts: [11], values: [10], }, + overdue_by_values: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], }, actions: { overdue_by: { counts: [14, 0, 0, 2, 0, 1], values: [10, 20, 30, 40, 50, 60], }, + overdue_by_values: [0, 0, 0, 30, 30, 50, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], }, }, }, diff --git a/x-pack/plugins/task_manager/server/metrics/lib/simple_histogram.test.ts b/x-pack/plugins/task_manager/server/metrics/lib/simple_histogram.test.ts index 9c24aabbdd575..a574eb9e809ec 100644 --- a/x-pack/plugins/task_manager/server/metrics/lib/simple_histogram.test.ts +++ b/x-pack/plugins/task_manager/server/metrics/lib/simple_histogram.test.ts @@ -28,6 +28,7 @@ describe('SimpleHistogram', () => { { value: 90, count: 0 }, { value: 100, count: 0 }, ]); + expect(histogram.getAllValues()).toEqual([]); }); test('should correctly initialize when bucketSize does not evenly divides range', () => { @@ -49,6 +50,7 @@ describe('SimpleHistogram', () => { { value: 98, count: 0 }, { value: 105, count: 0 }, ]); + expect(histogram.getAllValues()).toEqual([]); }); test('should correctly record values', () => { @@ -77,6 +79,7 @@ describe('SimpleHistogram', () => { { value: 90, count: 0 }, { value: 100, count: 1 }, ]); + expect(histogram.getAllValues()).toEqual([0, 10, 23, 34, 21, 56, 78, 33, 99, 1, 2]); }); test('should correctly record values with specific increment', () => { @@ -104,6 +107,9 @@ describe('SimpleHistogram', () => { { value: 90, count: 0 }, { value: 100, count: 5 }, ]); + expect(histogram.getAllValues()).toEqual([ + 0, 23, 23, 34, 21, 56, 78, 33, 33, 33, 33, 99, 99, 99, 99, 99, 1, 2, + ]); }); test('should ignore values less than 0 and greater than max', () => { @@ -119,11 +125,13 @@ describe('SimpleHistogram', () => { histogram.record(2); const hist1 = histogram.get(); + const hist1AllValues = histogram.getAllValues(); histogram.record(-1); histogram.record(200); expect(histogram.get()).toEqual(hist1); + expect(histogram.getAllValues()).toEqual(hist1AllValues); }); test('should correctly reset values', () => { @@ -150,6 +158,7 @@ describe('SimpleHistogram', () => { { value: 90, count: 0 }, { value: 100, count: 1 }, ]); + expect(histogram.getAllValues()).toEqual([23, 34, 21, 56, 78, 33, 99, 1, 2]); histogram.reset(); @@ -165,6 +174,7 @@ describe('SimpleHistogram', () => { { value: 90, count: 0 }, { value: 100, count: 0 }, ]); + expect(histogram.getAllValues()).toEqual([]); }); test('should correctly truncate zero values', () => { @@ -189,6 +199,7 @@ describe('SimpleHistogram', () => { { value: 90, count: 0 }, { value: 100, count: 0 }, ]); + expect(histogram.getAllValues()).toEqual([23, 34, 21, 56, 33, 1, 2]); expect(histogram.get(true)).toEqual([ { value: 10, count: 2 }, @@ -204,6 +215,7 @@ describe('SimpleHistogram', () => { const histogram = new SimpleHistogram(100, 10); expect(histogram.get(true)).toEqual([]); + expect(histogram.getAllValues()).toEqual([]); }); test('should correctly serialize histogram data', () => { diff --git a/x-pack/plugins/task_manager/server/metrics/lib/simple_histogram.ts b/x-pack/plugins/task_manager/server/metrics/lib/simple_histogram.ts index 7940ee3c48ee5..460369099def0 100644 --- a/x-pack/plugins/task_manager/server/metrics/lib/simple_histogram.ts +++ b/x-pack/plugins/task_manager/server/metrics/lib/simple_histogram.ts @@ -23,6 +23,7 @@ export class SimpleHistogram { private maxValue: number; private bucketSize: number; private histogramBuckets: Bucket[] = []; + private allValues: number[] = []; constructor(max: number, bucketSize: number) { if (bucketSize > max) { @@ -38,6 +39,7 @@ export class SimpleHistogram { for (let i = 0; i < this.histogramBuckets.length; i++) { this.histogramBuckets[i].count = 0; } + this.allValues = []; } public record(value: number, increment: number = 1) { @@ -52,6 +54,10 @@ export class SimpleHistogram { break; } } + + for (let i = 0; i < increment; i++) { + this.allValues.push(value); + } } public get(truncate: boolean = false) { @@ -74,6 +80,10 @@ export class SimpleHistogram { })); } + public getAllValues(truncate: boolean = false) { + return this.allValues.slice(); + } + public serialize(): SerializedHistogram { const counts: number[] = []; const values: number[] = []; diff --git a/x-pack/plugins/task_manager/server/metrics/task_claim_metrics_aggregator.test.ts b/x-pack/plugins/task_manager/server/metrics/task_claim_metrics_aggregator.test.ts index cfcf4bfdf8d0b..0874940675287 100644 --- a/x-pack/plugins/task_manager/server/metrics/task_claim_metrics_aggregator.test.ts +++ b/x-pack/plugins/task_manager/server/metrics/task_claim_metrics_aggregator.test.ts @@ -47,6 +47,7 @@ describe('TaskClaimMetricsAggregator', () => { success: 0, total: 0, duration: { counts: [], values: [] }, + duration_values: [], }); }); @@ -55,6 +56,7 @@ describe('TaskClaimMetricsAggregator', () => { success: 0, total: 0, duration: { counts: [], values: [] }, + duration_values: [], }); }); @@ -65,6 +67,7 @@ describe('TaskClaimMetricsAggregator', () => { success: 2, total: 2, duration: { counts: [2], values: [100] }, + duration_values: [10, 10], }); }); @@ -75,6 +78,7 @@ describe('TaskClaimMetricsAggregator', () => { success: 0, total: 2, duration: { counts: [], values: [] }, + duration_values: [], }); }); @@ -90,6 +94,7 @@ describe('TaskClaimMetricsAggregator', () => { success: 4, total: 7, duration: { counts: [4], values: [100] }, + duration_values: [10, 10, 10, 10], }); taskClaimMetricsAggregator.reset(); @@ -97,6 +102,7 @@ describe('TaskClaimMetricsAggregator', () => { success: 0, total: 0, duration: { counts: [], values: [] }, + duration_values: [], }); }); }); diff --git a/x-pack/plugins/task_manager/server/metrics/task_claim_metrics_aggregator.ts b/x-pack/plugins/task_manager/server/metrics/task_claim_metrics_aggregator.ts index 8bb1269bae2fb..486a3f9aa5fd7 100644 --- a/x-pack/plugins/task_manager/server/metrics/task_claim_metrics_aggregator.ts +++ b/x-pack/plugins/task_manager/server/metrics/task_claim_metrics_aggregator.ts @@ -26,6 +26,7 @@ interface TaskClaimCounts extends JsonObject { export type TaskClaimMetric = TaskClaimCounts & { duration: SerializedHistogram; + duration_values: number[]; }; export class TaskClaimMetricsAggregator implements ITaskMetricsAggregator { @@ -38,12 +39,14 @@ export class TaskClaimMetricsAggregator implements ITaskMetricsAggregator { test('should correctly initialize', () => { expect(taskOverdueMetricsAggregator.collect()).toEqual({ - overall: { overdue_by: { counts: [], values: [] } }, + overall: { + overdue_by: { counts: [], values: [] }, + overdue_by_values: [], + }, by_type: {}, }); }); test('should correctly return initialMetrics', () => { expect(taskOverdueMetricsAggregator.initialMetric()).toEqual({ - overall: { overdue_by: { counts: [], values: [] } }, + overall: { + overdue_by: { counts: [], values: [] }, + overdue_by_values: [], + }, by_type: {}, }); }); @@ -50,9 +56,15 @@ describe('TaskOverdueMetricsAggregator', () => { }) ); expect(taskOverdueMetricsAggregator.collect()).toEqual({ - overall: { overdue_by: { counts: [1, 0, 1], values: [10, 20, 30] } }, + overall: { + overdue_by: { counts: [1, 0, 1], values: [10, 20, 30] }, + overdue_by_values: [0, 20], + }, by_type: { - telemetry: { overdue_by: { counts: [1, 0, 1], values: [10, 20, 30] } }, + telemetry: { + overdue_by: { counts: [1, 0, 1], values: [10, 20, 30] }, + overdue_by_values: [0, 20], + }, }, }); }); @@ -66,7 +78,10 @@ describe('TaskOverdueMetricsAggregator', () => { }) ); expect(taskOverdueMetricsAggregator.collect()).toEqual({ - overall: { overdue_by: { counts: [], values: [] } }, + overall: { + overdue_by: { counts: [], values: [] }, + overdue_by_values: [], + }, by_type: {}, }); }); @@ -95,9 +110,15 @@ describe('TaskOverdueMetricsAggregator', () => { }) ); expect(taskOverdueMetricsAggregator.collect()).toEqual({ - overall: { overdue_by: { counts: [0, 0, 0, 0, 1], values: [10, 20, 30, 40, 50] } }, + overall: { + overdue_by: { counts: [0, 0, 0, 0, 1], values: [10, 20, 30, 40, 50] }, + overdue_by_values: [40], + }, by_type: { - telemetry: { overdue_by: { counts: [0, 0, 0, 0, 1], values: [10, 20, 30, 40, 50] } }, + telemetry: { + overdue_by: { counts: [0, 0, 0, 0, 1], values: [10, 20, 30, 40, 50] }, + overdue_by_values: [40], + }, }, }); }); @@ -129,6 +150,7 @@ describe('TaskOverdueMetricsAggregator', () => { counts: [3, 0, 2, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1], values: [10, 20, 30, 40, 50, 60, 70, 80, 90, 100, 110, 120, 130], }, + overdue_by_values: [0, 0, 0, 20, 20, 40, 120], }, by_type: { 'alerting:example': { @@ -136,36 +158,42 @@ describe('TaskOverdueMetricsAggregator', () => { counts: [0, 0, 0, 0, 1], values: [10, 20, 30, 40, 50], }, + overdue_by_values: [40], }, 'alerting:__index-threshold': { overdue_by: { counts: [0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1], values: [10, 20, 30, 40, 50, 60, 70, 80, 90, 100, 110, 120, 130], }, + overdue_by_values: [20, 20, 120], }, alerting: { overdue_by: { counts: [0, 0, 2, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1], values: [10, 20, 30, 40, 50, 60, 70, 80, 90, 100, 110, 120, 130], }, + overdue_by_values: [40, 20, 20, 120], }, 'actions:webhook': { overdue_by: { counts: [2], values: [10], }, + overdue_by_values: [0, 0], }, 'actions:__email': { overdue_by: { counts: [1], values: [10], }, + overdue_by_values: [0], }, actions: { overdue_by: { counts: [3], values: [10], }, + overdue_by_values: [0, 0, 0], }, }, }); @@ -198,44 +226,50 @@ describe('TaskOverdueMetricsAggregator', () => { counts: [3, 0, 2, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1], values: [10, 20, 30, 40, 50, 60, 70, 80, 90, 100, 110, 120, 130], }, + overdue_by_values: [0, 0, 0, 20, 20, 40, 120], }, by_type: { 'alerting:example': { overdue_by: { counts: [0, 0, 0, 0, 1], - values: [10, 20, 30, 40, 50], }, + overdue_by_values: [40], }, 'alerting:__index-threshold': { overdue_by: { counts: [0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1], values: [10, 20, 30, 40, 50, 60, 70, 80, 90, 100, 110, 120, 130], }, + overdue_by_values: [20, 20, 120], }, alerting: { overdue_by: { counts: [0, 0, 2, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1], values: [10, 20, 30, 40, 50, 60, 70, 80, 90, 100, 110, 120, 130], }, + overdue_by_values: [40, 20, 20, 120], }, 'actions:webhook': { overdue_by: { counts: [2], values: [10], }, + overdue_by_values: [0, 0], }, 'actions:__email': { overdue_by: { counts: [1], values: [10], }, + overdue_by_values: [0], }, actions: { overdue_by: { counts: [3], values: [10], }, + overdue_by_values: [0, 0, 0], }, }, }); diff --git a/x-pack/plugins/task_manager/server/metrics/task_overdue_metrics_aggregator.ts b/x-pack/plugins/task_manager/server/metrics/task_overdue_metrics_aggregator.ts index 4743889e7ebf9..bd2f6a97372aa 100644 --- a/x-pack/plugins/task_manager/server/metrics/task_overdue_metrics_aggregator.ts +++ b/x-pack/plugins/task_manager/server/metrics/task_overdue_metrics_aggregator.ts @@ -6,16 +6,12 @@ */ import { JsonObject } from '@kbn/utility-types'; -import { keys, mapValues } from 'lodash'; +import { keys } from 'lodash'; +import { set } from '@kbn/safer-lodash-set'; import { isOk, unwrap } from '../lib/result_type'; import { TaskLifecycleEvent } from '../polling_lifecycle'; import { TaskManagerMetric } from '../task_events'; -import { - unflattenObject, - getTaskTypeGroup, - type SerializedHistogram, - SimpleHistogram, -} from './lib'; +import { getTaskTypeGroup, type SerializedHistogram, SimpleHistogram } from './lib'; import { TaskManagerMetrics } from './task_metrics_collector'; import { ITaskMetricsAggregator } from './types'; @@ -23,6 +19,7 @@ const HDR_HISTOGRAM_MAX = 5400; // 90 minutes const HDR_HISTOGRAM_BUCKET_SIZE = 10; // 10 seconds const OVERDUE_BY_KEY = 'overdue_by'; +const OVERDUE_BY_VALUES_KEY = 'overdue_by_values'; enum TaskOverdueMetricKeys { OVERALL = 'overall', @@ -31,6 +28,7 @@ enum TaskOverdueMetricKeys { interface TaskOverdueHistogram extends JsonObject { [OVERDUE_BY_KEY]: SerializedHistogram; + [OVERDUE_BY_VALUES_KEY]: number[]; } export interface TaskOverdueMetric extends JsonObject { [TaskOverdueMetricKeys.OVERALL]: TaskOverdueHistogram; @@ -45,18 +43,26 @@ export class TaskOverdueMetricsAggregator implements ITaskMetricsAggregator hist.serialize())); + + for (const prop of Object.keys(this.histograms)) { + const hist = this.histograms[prop]; + set(result, prop, hist.serialize()); + set(result, `${prop}_values`, hist.getAllValues()); + } + + return result; } public reset() { diff --git a/x-pack/plugins/task_manager/server/metrics/task_run_metrics_aggregator.test.ts b/x-pack/plugins/task_manager/server/metrics/task_run_metrics_aggregator.test.ts index 19c022dea662f..d156685885191 100644 --- a/x-pack/plugins/task_manager/server/metrics/task_run_metrics_aggregator.test.ts +++ b/x-pack/plugins/task_manager/server/metrics/task_run_metrics_aggregator.test.ts @@ -94,6 +94,7 @@ describe('TaskRunMetricsAggregator', () => { user_errors: 0, delay: { counts: [], values: [] }, total_errors: 0, + delay_values: [], }, }); }); @@ -108,6 +109,7 @@ describe('TaskRunMetricsAggregator', () => { user_errors: 0, delay: { counts: [], values: [] }, total_errors: 0, + delay_values: [], }, by_type: {}, }); @@ -125,6 +127,7 @@ describe('TaskRunMetricsAggregator', () => { user_errors: 0, delay: { counts: [], values: [] }, total_errors: 0, + delay_values: [], }, by_type: { telemetry: { @@ -150,6 +153,7 @@ describe('TaskRunMetricsAggregator', () => { user_errors: 0, delay: { counts: [1], values: [10] }, total_errors: 0, + delay_values: [3], }, }); }); @@ -167,6 +171,7 @@ describe('TaskRunMetricsAggregator', () => { user_errors: 0, delay: { counts: [], values: [] }, total_errors: 0, + delay_values: [], }, }); }); @@ -183,6 +188,7 @@ describe('TaskRunMetricsAggregator', () => { user_errors: 0, delay: { counts: [], values: [] }, total_errors: 0, + delay_values: [], }, by_type: { telemetry: { @@ -209,6 +215,7 @@ describe('TaskRunMetricsAggregator', () => { user_errors: 0, delay: { counts: [], values: [] }, total_errors: 2, + delay_values: [], }, by_type: { telemetry: { @@ -235,6 +242,7 @@ describe('TaskRunMetricsAggregator', () => { user_errors: 0, delay: { counts: [], values: [] }, total_errors: 2, + delay_values: [], }, by_type: { telemetry: { @@ -263,6 +271,7 @@ describe('TaskRunMetricsAggregator', () => { user_errors: 0, delay: { counts: [], values: [] }, total_errors: 1, + delay_values: [], }, by_type: { report: { @@ -315,6 +324,7 @@ describe('TaskRunMetricsAggregator', () => { user_errors: 0, delay: { counts: [], values: [] }, total_errors: 3, + delay_values: [], }, by_type: { actions: { @@ -417,6 +427,7 @@ describe('TaskRunMetricsAggregator', () => { not_timed_out: 12, total: 14, delay: { counts: [3, 0, 1], values: [10, 20, 30] }, + delay_values: [3, 25, 6, 9], framework_errors: 3, user_errors: 0, total_errors: 3, @@ -499,6 +510,7 @@ describe('TaskRunMetricsAggregator', () => { user_errors: 0, delay: { counts: [], values: [] }, total_errors: 0, + delay_values: [], }, by_type: { actions: { diff --git a/x-pack/plugins/task_manager/server/metrics/task_run_metrics_aggregator.ts b/x-pack/plugins/task_manager/server/metrics/task_run_metrics_aggregator.ts index bfa230ac9bf5e..c0c80e4021498 100644 --- a/x-pack/plugins/task_manager/server/metrics/task_run_metrics_aggregator.ts +++ b/x-pack/plugins/task_manager/server/metrics/task_run_metrics_aggregator.ts @@ -57,6 +57,7 @@ export interface TaskRunMetrics extends JsonObject { export interface TaskRunMetric extends JsonObject { overall: TaskRunMetrics['overall'] & { delay: SerializedHistogram; + delay_values: number[]; }; by_type: TaskRunMetrics['by_type']; } @@ -71,12 +72,20 @@ export class TaskRunMetricsAggregator implements ITaskMetricsAggregator { +export const EnterpriseGuard: FC = memo(({ children }) => { const { licenseService } = useSecurityContext(); if (licenseService.isEnterprise()) { @@ -23,4 +23,4 @@ export const EnterpriseGuard: FC = ({ children }) => { ); -}; +}); diff --git a/x-pack/plugins/threat_intelligence/public/containers/indicators_page_wrapper.tsx b/x-pack/plugins/threat_intelligence/public/containers/indicators_page_wrapper.tsx index 5202e481af37d..23fe00175f7e2 100644 --- a/x-pack/plugins/threat_intelligence/public/containers/indicators_page_wrapper.tsx +++ b/x-pack/plugins/threat_intelligence/public/containers/indicators_page_wrapper.tsx @@ -5,7 +5,7 @@ * 2.0. */ -import React, { VFC } from 'react'; +import React, { memo, useMemo, VFC } from 'react'; import { QueryClient, QueryClientProvider } from '@tanstack/react-query'; import { CasesPermissions } from '@kbn/cases-plugin/common'; import { IndicatorsPage } from '../modules/indicators/pages/indicators'; @@ -15,15 +15,17 @@ import { useKibana } from '../hooks/use_kibana'; export const APP_ID = 'securitySolution'; +const queryClient = new QueryClient(); + +const casesContextOwner = [APP_ID]; + export const IndicatorsPageWrapper: VFC = () => { const { cases } = useKibana().services; - const CasesContext = cases.ui.getCasesContext(); - const permissions: CasesPermissions = cases.helpers.canUseCases(); - - const queryClient = new QueryClient(); + const CasesContext = useMemo(() => cases.ui.getCasesContext(), [cases.ui]); + const permissions: CasesPermissions = useMemo(() => cases.helpers.canUseCases(), [cases.helpers]); return ( - + @@ -37,4 +39,4 @@ export const IndicatorsPageWrapper: VFC = () => { // Note: This is for lazy loading // eslint-disable-next-line import/no-default-export -export default IndicatorsPageWrapper; +export default memo(IndicatorsPageWrapper); diff --git a/x-pack/plugins/threat_intelligence/public/containers/integrations_guard.test.tsx b/x-pack/plugins/threat_intelligence/public/containers/integrations_guard.test.tsx index 8fe9345edee31..f4abeb68bafa7 100644 --- a/x-pack/plugins/threat_intelligence/public/containers/integrations_guard.test.tsx +++ b/x-pack/plugins/threat_intelligence/public/containers/integrations_guard.test.tsx @@ -75,18 +75,19 @@ describe('IntegrationsGuard', () => { }); it('should render loading when integrations only are loading', async () => { - ( - useIndicatorsTotalCount as jest.MockedFunction - ).mockReturnValue({ - count: 0, - isLoading: true, - }); ( useIntegrationsPageLink as jest.MockedFunction ).mockReturnValue(''); (useTIDocumentationLink as jest.MockedFunction).mockReturnValue( '' ); + + ( + useIndicatorsTotalCount as jest.MockedFunction + ).mockReturnValue({ + count: 0, + isLoading: true, + }); (useIntegrations as jest.MockedFunction).mockReturnValue({ isLoading: true, data: [], diff --git a/x-pack/plugins/threat_intelligence/public/containers/integrations_guard.tsx b/x-pack/plugins/threat_intelligence/public/containers/integrations_guard.tsx index cb2064680c178..aac955e2434de 100644 --- a/x-pack/plugins/threat_intelligence/public/containers/integrations_guard.tsx +++ b/x-pack/plugins/threat_intelligence/public/containers/integrations_guard.tsx @@ -5,8 +5,8 @@ * 2.0. */ -import { EuiLoadingLogo } from '@elastic/eui'; -import React, { FC } from 'react'; +import { EuiLoadingLogo, EuiPageTemplate } from '@elastic/eui'; +import React, { FC, memo } from 'react'; import { LOADING_LOGO_TEST_ID } from './test_ids'; import { useIntegrations } from '../hooks/use_integrations'; import { EmptyPage } from '../modules/empty_page/empty_page'; @@ -19,21 +19,28 @@ import { SecuritySolutionPluginTemplateWrapper } from './security_solution_plugi * If none are received, show the EmptyPage with a link to go install integrations. * While the indicators call and the integrations call are loading, display a loading screen. */ -export const IntegrationsGuard: FC = ({ children }) => { +export const IntegrationsGuard: FC = memo(({ children }) => { const { isLoading: indicatorsTotalCountLoading, count: indicatorsTotalCount } = useIndicatorsTotalCount(); - const { isLoading: integrationLoading, data: installedTIIntegrations } = useIntegrations(); + const { isLoading: integrationLoading, data: installedTIIntegrations } = useIntegrations({ + enabled: !indicatorsTotalCountLoading, + }); if (integrationLoading || indicatorsTotalCountLoading) { return ( - - - + + + + } + /> ); } // show indicators page if there are indicators, or if some ti integrations have been added const showIndicatorsPage = indicatorsTotalCount > 0 || (installedTIIntegrations || []).length > 0; return showIndicatorsPage ? <>{children} : ; -}; +}); diff --git a/x-pack/plugins/threat_intelligence/public/hooks/use_integrations.ts b/x-pack/plugins/threat_intelligence/public/hooks/use_integrations.ts index b0e9b0432c0e4..caf337275b46d 100644 --- a/x-pack/plugins/threat_intelligence/public/hooks/use_integrations.ts +++ b/x-pack/plugins/threat_intelligence/public/hooks/use_integrations.ts @@ -5,7 +5,8 @@ * 2.0. */ -import { useQuery, useQueryClient } from '@tanstack/react-query'; +import { QueryFunctionContext, useQuery, useQueryClient } from '@tanstack/react-query'; +import { useCallback, useEffect, useRef } from 'react'; import { filterIntegrations } from '../utils/filter_integrations'; import { useKibana } from './use_kibana'; @@ -25,27 +26,50 @@ export interface Integration { status: IntegrationInstallStatus; } +const queryKey = ['integrations']; + /** * Retrieves integrations from the Fleet plugin endpoint /api/fleet/epm/packages. * The integrations are then filtered, and we only keep the installed ones, * with category threat_intel and excluding the ti_utils integration. * We cancel the query in case it's taking too long to not block the Indicators page for the user. */ -export const useIntegrations = () => { +export const useIntegrations = ({ enabled }: { enabled: boolean }) => { + const timeoutRef = useRef(); + const { http } = useKibana().services; - const queryKey = ['integrations']; // retrieving the list of integrations from the fleet plugin's endpoint - const fetchIntegrations = () => http.get(INTEGRATIONS_URL); + const fetchIntegrations = useCallback( + (context: QueryFunctionContext) => + http.get(INTEGRATIONS_URL, { + version: '2023-10-31', + signal: context.signal, + }), + [http] + ); const query = useQuery(queryKey, fetchIntegrations, { select: (data: IntegrationResponse) => (data ? filterIntegrations(data.items) : []), + enabled, }); const queryClient = useQueryClient(); - // cancel slow integrations call to unblock the UI - setTimeout(() => queryClient.cancelQueries(queryKey), INTEGRATIONS_CALL_TIMEOUT); + useEffect(() => { + // cancel slow integrations call to unblock the UI + timeoutRef.current = setTimeout(() => { + queryClient.cancelQueries(queryKey); + }, INTEGRATIONS_CALL_TIMEOUT) as unknown as number; + + return () => { + if (!timeoutRef.current) { + return; + } + + clearTimeout(timeoutRef.current); + }; + }, [queryClient]); return query; }; diff --git a/x-pack/plugins/threat_intelligence/public/mocks/mock_security_context.tsx b/x-pack/plugins/threat_intelligence/public/mocks/mock_security_context.tsx index 6cd464a35d352..4e1ecf0bbe885 100644 --- a/x-pack/plugins/threat_intelligence/public/mocks/mock_security_context.tsx +++ b/x-pack/plugins/threat_intelligence/public/mocks/mock_security_context.tsx @@ -14,10 +14,6 @@ export const getSecuritySolutionContextMock = (): SecuritySolutionPluginContext () => ({ children }) =>
{children}
, - getPageWrapper: - () => - ({ children }) => -
{children}
, licenseService: { isEnterprise(): boolean { return true; @@ -26,7 +22,14 @@ export const getSecuritySolutionContextMock = (): SecuritySolutionPluginContext return true; }, }, + getPageWrapper: + () => + ({ children }) => +
{children}
, sourcererDataView: { + sourcererDataView: { + id: 'security-solution-default', + }, browserFields: {}, selectedPatterns: [], indexPattern: { fields: [], title: '' }, diff --git a/x-pack/plugins/threat_intelligence/public/mocks/story_providers.tsx b/x-pack/plugins/threat_intelligence/public/mocks/story_providers.tsx index 249a9d05afbc9..900a677911ee2 100644 --- a/x-pack/plugins/threat_intelligence/public/mocks/story_providers.tsx +++ b/x-pack/plugins/threat_intelligence/public/mocks/story_providers.tsx @@ -57,8 +57,12 @@ export interface StoryProvidersComponentProps { const securityLayout = { getPluginWrapper: (): FC => - ({ children }) => -
{children}
, + ({ children, isEmptyState, emptyPageBody }: any) => { + if (isEmptyState && emptyPageBody) { + return <>{emptyPageBody}; + } + return <>{children}; + }, }; const defaultServices = { diff --git a/x-pack/plugins/threat_intelligence/public/mocks/test_providers.tsx b/x-pack/plugins/threat_intelligence/public/mocks/test_providers.tsx index 57e1dee846c0a..638d91f0a42f0 100644 --- a/x-pack/plugins/threat_intelligence/public/mocks/test_providers.tsx +++ b/x-pack/plugins/threat_intelligence/public/mocks/test_providers.tsx @@ -126,7 +126,10 @@ export const mockedServices = { securityLayout: { getPluginWrapper: () => - ({ children }: any) => { + ({ children, isEmptyState, emptyPageBody }: any) => { + if (isEmptyState && emptyPageBody) { + return <>{emptyPageBody}; + } return <>{children}; }, }, diff --git a/x-pack/plugins/threat_intelligence/public/modules/empty_page/empty_page.tsx b/x-pack/plugins/threat_intelligence/public/modules/empty_page/empty_page.tsx index 4ed55fc9731f5..eb6f04d0e3ad8 100644 --- a/x-pack/plugins/threat_intelligence/public/modules/empty_page/empty_page.tsx +++ b/x-pack/plugins/threat_intelligence/public/modules/empty_page/empty_page.tsx @@ -24,76 +24,79 @@ export const EmptyPage: VFC = () => { const documentationLink = useTIDocumentationLink(); return ( - - } - title={ -

- -

- } - titleSize="s" - layout="horizontal" - color="transparent" - body={ - <> -

+ } + title={ +

-

-

- +

+ +

+

+ -

-

- - - - ), - }} + values={{ + docsLink: ( + + + + ), + }} + /> +

+ + } + actions={ + + -

- - } - actions={ - - - - } - data-test-subj={EMPTY_PROMPT_TEST_ID} - /> - +
+ } + data-test-subj={EMPTY_PROMPT_TEST_ID} + /> + } + /> ); }; diff --git a/x-pack/plugins/threat_intelligence/public/modules/query_bar/components/filter_in.test.tsx b/x-pack/plugins/threat_intelligence/public/modules/query_bar/components/filter_in.test.tsx index f4cb5f231e2e8..0a44481f5cb25 100644 --- a/x-pack/plugins/threat_intelligence/public/modules/query_bar/components/filter_in.test.tsx +++ b/x-pack/plugins/threat_intelligence/public/modules/query_bar/components/filter_in.test.tsx @@ -18,6 +18,8 @@ import { FilterInContextMenu, } from './filter_in'; +import { TestProvidersComponent } from '../../../mocks/test_providers'; + jest.mock('../../indicators/hooks/use_filters_context'); const mockIndicator: Indicator = generateMockIndicator(); @@ -33,20 +35,27 @@ describe(' ' }); it('should render null (wrong data input)', () => { - const { container } = render(); + const { container } = render(, { + wrapper: TestProvidersComponent, + }); expect(container).toBeEmptyDOMElement(); }); it('should render null (wrong field input)', () => { - const { container } = render(); + const { container } = render(, { + wrapper: TestProvidersComponent, + }); expect(container).toBeEmptyDOMElement(); }); it('should render one EuiButtonIcon', () => { const { getByTestId } = render( - + , + { + wrapper: TestProvidersComponent, + } ); expect(getByTestId(TEST_ID)).toHaveClass('euiButtonIcon'); @@ -54,7 +63,10 @@ describe(' ' it('should render one EuiButtonEmpty', () => { const { getByTestId } = render( - + , + { + wrapper: TestProvidersComponent, + } ); expect(getByTestId(TEST_ID)).toHaveClass('euiButtonEmpty'); @@ -62,7 +74,10 @@ describe(' ' it('should render one EuiContextMenuItem (for EuiContextMenu use)', () => { const { getByTestId } = render( - + , + { + wrapper: TestProvidersComponent, + } ); expect(getByTestId(TEST_ID)).toHaveClass('euiContextMenuItem'); @@ -83,7 +98,10 @@ describe(' ' field={mockField} Component={mockComponent} data-test-subj={TEST_ID} - /> + />, + { + wrapper: TestProvidersComponent, + } ); expect(getByTestId(TEST_ID)).toBeInTheDocument(); diff --git a/x-pack/plugins/threat_intelligence/public/modules/query_bar/components/filter_out.test.tsx b/x-pack/plugins/threat_intelligence/public/modules/query_bar/components/filter_out.test.tsx index 3155805c05a2d..1bc4d0e4da158 100644 --- a/x-pack/plugins/threat_intelligence/public/modules/query_bar/components/filter_out.test.tsx +++ b/x-pack/plugins/threat_intelligence/public/modules/query_bar/components/filter_out.test.tsx @@ -17,6 +17,7 @@ import { FilterOutCellAction, FilterOutContextMenu, } from './filter_out'; +import { TestProvidersComponent } from '../../../mocks/test_providers'; jest.mock('../../indicators/hooks/use_filters_context'); @@ -33,20 +34,27 @@ describe(' { - const { container } = render(); + const { container } = render(, { + wrapper: TestProvidersComponent, + }); expect(container).toBeEmptyDOMElement(); }); it('should render an empty component (wrong field input)', () => { - const { container } = render(); + const { container } = render(, { + wrapper: TestProvidersComponent, + }); expect(container).toBeEmptyDOMElement(); }); it('should render one EuiButtonIcon', () => { const { getByTestId } = render( - + , + { + wrapper: TestProvidersComponent, + } ); expect(getByTestId(TEST_ID)).toHaveClass('euiButtonIcon'); @@ -54,7 +62,10 @@ describe(' { const { getByTestId } = render( - + , + { + wrapper: TestProvidersComponent, + } ); expect(getByTestId(TEST_ID)).toHaveClass('euiButtonEmpty'); @@ -62,7 +73,10 @@ describe(' { const { getByTestId } = render( - + , + { + wrapper: TestProvidersComponent, + } ); expect(getByTestId(TEST_ID)).toHaveClass('euiContextMenuItem'); @@ -83,7 +97,10 @@ describe(' + />, + { + wrapper: TestProvidersComponent, + } ); expect(getByTestId(TEST_ID)).toBeInTheDocument(); diff --git a/x-pack/plugins/threat_intelligence/public/modules/query_bar/hooks/use_filter_in_out.test.ts b/x-pack/plugins/threat_intelligence/public/modules/query_bar/hooks/use_filter_in_out.test.ts index 9e22684bb4c31..7e435d944145b 100644 --- a/x-pack/plugins/threat_intelligence/public/modules/query_bar/hooks/use_filter_in_out.test.ts +++ b/x-pack/plugins/threat_intelligence/public/modules/query_bar/hooks/use_filter_in_out.test.ts @@ -14,6 +14,9 @@ import { import { TestProvidersComponent } from '../../../mocks/test_providers'; import { useFilterInOut, UseFilterInValue } from './use_filter_in_out'; import { FilterIn } from '../utils/filter'; +import { updateFiltersArray } from '../utils/filter'; + +jest.mock('../utils/filter', () => ({ updateFiltersArray: jest.fn() })); describe('useFilterInOut()', () => { let hookResult: RenderHookResult<{}, UseFilterInValue, Renderer>; @@ -53,4 +56,28 @@ describe('useFilterInOut()', () => { expect(hookResult.result.current).toHaveProperty('filterFn'); }); + + describe('calling filterFn', () => { + it('should call dependencies ', () => { + const indicator: string = '0.0.0.0'; + const field: string = 'threat.indicator.name'; + const filterType = FilterIn; + + hookResult = renderHook(() => useFilterInOut({ indicator, field, filterType }), { + wrapper: TestProvidersComponent, + }); + + expect(hookResult.result.current).toHaveProperty('filterFn'); + + hookResult.result.current.filterFn?.(); + + expect(jest.mocked(updateFiltersArray)).toHaveBeenCalledWith( + [], + 'threat.indicator.name', + '0.0.0.0', + undefined, + 'security-solution-default' + ); + }); + }); }); diff --git a/x-pack/plugins/threat_intelligence/public/modules/query_bar/hooks/use_filter_in_out.ts b/x-pack/plugins/threat_intelligence/public/modules/query_bar/hooks/use_filter_in_out.ts index 72388d8a9a8a6..b6526cf76d456 100644 --- a/x-pack/plugins/threat_intelligence/public/modules/query_bar/hooks/use_filter_in_out.ts +++ b/x-pack/plugins/threat_intelligence/public/modules/query_bar/hooks/use_filter_in_out.ts @@ -11,6 +11,7 @@ import { fieldAndValueValid, getIndicatorFieldAndValue } from '../../indicators/ import { useIndicatorsFiltersContext } from '../../indicators/hooks/use_filters_context'; import { Indicator } from '../../../../common/types/indicator'; import { FilterIn, FilterOut, updateFiltersArray } from '../utils/filter'; +import { useSourcererDataView } from '../../indicators/hooks/use_sourcerer_data_view'; export interface UseFilterInParam { /** @@ -44,6 +45,7 @@ export const useFilterInOut = ({ filterType, }: UseFilterInParam): UseFilterInValue => { const { filterManager } = useIndicatorsFiltersContext(); + const { sourcererDataView } = useSourcererDataView(); const { key, value } = typeof indicator === 'string' @@ -52,9 +54,15 @@ export const useFilterInOut = ({ const filterFn = useCallback((): void => { const existingFilters = filterManager.getFilters(); - const newFilters: Filter[] = updateFiltersArray(existingFilters, key, value, filterType); + const newFilters: Filter[] = updateFiltersArray( + existingFilters, + key, + value, + filterType, + sourcererDataView?.id + ); filterManager.setFilters(newFilters); - }, [filterManager, filterType, key, value]); + }, [filterManager, filterType, key, sourcererDataView?.id, value]); if (!fieldAndValueValid(key, value)) { return {} as unknown as UseFilterInValue; diff --git a/x-pack/plugins/threat_intelligence/public/modules/query_bar/utils/filter.ts b/x-pack/plugins/threat_intelligence/public/modules/query_bar/utils/filter.ts index 1bb1661a4e750..451132ce234f9 100644 --- a/x-pack/plugins/threat_intelligence/public/modules/query_bar/utils/filter.ts +++ b/x-pack/plugins/threat_intelligence/public/modules/query_bar/utils/filter.ts @@ -17,7 +17,17 @@ export const FilterOut = false; * @param negate Set to true when we create a negated filter (e.g. NOT threat.indicator.type: url) * @returns The new {@link Filter} */ -const createFilter = (key: string, value: string, negate: boolean): Filter => ({ +const createFilter = ({ + key, + value, + negate, + index, +}: { + key: string; + value: string; + negate: boolean; + index?: string; +}): Filter => ({ meta: { alias: null, negate, @@ -25,6 +35,7 @@ const createFilter = (key: string, value: string, negate: boolean): Filter => ({ type: 'phrase', key, params: { query: value }, + index, }, query: { match_phrase: { [key]: value } }, }); @@ -71,9 +82,10 @@ export const updateFiltersArray = ( existingFilters: Filter[], key: string, value: string | null, - filterType: boolean + filterType: boolean, + index?: string ): Filter[] => { - const newFilter = createFilter(key, value as string, !filterType); + const newFilter = createFilter({ key, value: value as string, negate: !filterType, index }); const filter: Filter | undefined = filterExistsInFiltersArray( existingFilters, diff --git a/x-pack/plugins/threat_intelligence/public/types.ts b/x-pack/plugins/threat_intelligence/public/types.ts index 39e26c50486b4..45da610592cee 100644 --- a/x-pack/plugins/threat_intelligence/public/types.ts +++ b/x-pack/plugins/threat_intelligence/public/types.ts @@ -92,6 +92,17 @@ export interface BlockListFormProps { item: CreateExceptionListItemSchema; } +export interface Blocking { + canWriteBlocklist: boolean; + exceptionListApiClient: unknown; + useSetUrlParams: () => ( + params: Record, + replace?: boolean | undefined + ) => void; + getFlyoutComponent: () => NamedExoticComponent; + getFormComponent: () => NamedExoticComponent; +} + /** * Methods exposed from the security solution to the threat intelligence application. */ @@ -151,16 +162,7 @@ export interface SecuritySolutionPluginContext { /** * Add to blocklist feature */ - blockList: { - canWriteBlocklist: boolean; - exceptionListApiClient: unknown; - useSetUrlParams: () => ( - params: Record, - replace?: boolean | undefined - ) => void; - getFlyoutComponent: () => NamedExoticComponent; - getFormComponent: () => NamedExoticComponent; - }; + blockList: Blocking; } /** diff --git a/x-pack/plugins/translations/translations/fr-FR.json b/x-pack/plugins/translations/translations/fr-FR.json index ada9af7cce3ed..a0057befbed14 100644 --- a/x-pack/plugins/translations/translations/fr-FR.json +++ b/x-pack/plugins/translations/translations/fr-FR.json @@ -13923,8 +13923,6 @@ "xpack.enterpriseSearch.connectorStats.incompleteBadgeLabel": "{number} incomplet(s)", "xpack.enterpriseSearch.connectorStats.runningSyncsTextLabel": "{syncs} synchronisations en cours", "xpack.enterpriseSearch.content.connector_detail.configurationConnector.connectorPackage.connectorConnected": "Votre connecteur {name} s'est bien connecté à Search.", - "xpack.enterpriseSearch.content.connector_detail.configurationConnector.connectorPackage.description.secondParagraph": "Le référentiel de connecteurs contient plusieurs {link}. Utilisez notre cadre pour accélérer le développement de connecteurs pour des sources de données personnalisées.", - "xpack.enterpriseSearch.content.connector_detail.configurationConnector.connectorPackage.description.thirdParagraph": "Dans cette étape, vous devez cloner ou dupliquer le référentiel, puis copier la clé d'API et l'ID de connecteur générés au {link} associé. L'ID de connecteur identifiera ce connecteur auprès de Search. Le type de service détermine pour quel type de source de données le connecteur est configuré.", "xpack.enterpriseSearch.content.connectors.connectorsTable.columns.actions.viewIndex.caption": "Voir l'index {connectorName}", "xpack.enterpriseSearch.content.connectors.connectorTable.column.actions.deleteIndex": "Supprimer le connecteur {connectorName}", "xpack.enterpriseSearch.content.connectors.deleteModal.syncsWarning.indexNameDescription": "Cette action ne peut pas être annulée. Veuillez saisir {connectorName} pour confirmer.", @@ -15212,9 +15210,7 @@ "xpack.enterpriseSearch.content.analytics.api.generateAnalyticsApiKeyModal.title": "Créer une clé d'API d'analyse", "xpack.enterpriseSearch.content.cannotConnect.body": "En savoir plus.", "xpack.enterpriseSearch.content.cannotConnect.title": "Impossible de se connecter à Enterprise Search", - "xpack.enterpriseSearch.content.connector_detail.configurationConnector.connectorPackage.clientExamplesLink": "exemples de clients connecteurs", "xpack.enterpriseSearch.content.connector_detail.configurationConnector.connectorPackage.configurationFileLink": "fichier de configuration", - "xpack.enterpriseSearch.content.connector_detail.configurationConnector.connectorPackage.connectorDeployedText": "Une fois le connecteur configuré, déployez-le dans votre infrastructure.", "xpack.enterpriseSearch.content.connector_detail.configurationConnector.connectorPackage.waitingForConnector.button.label": "Revérifier maintenant", "xpack.enterpriseSearch.content.connector_detail.configurationConnector.connectorPackage.waitingForConnectorText": "Votre connecteur ne s'est pas connecté à Search. Résolvez vos problèmes de configuration et actualisez la page.", "xpack.enterpriseSearch.content.connector_detail.configurationConnector.connectorPackage.waitingForConnectorTitle": "En attente de votre connecteur", diff --git a/x-pack/plugins/translations/translations/ja-JP.json b/x-pack/plugins/translations/translations/ja-JP.json index 9eef765b09d0d..06a3c6238fb94 100644 --- a/x-pack/plugins/translations/translations/ja-JP.json +++ b/x-pack/plugins/translations/translations/ja-JP.json @@ -13902,8 +13902,6 @@ "xpack.enterpriseSearch.connectorStats.incompleteBadgeLabel": "{number}件が未完了です", "xpack.enterpriseSearch.connectorStats.runningSyncsTextLabel": "{syncs}実行中の同期", "xpack.enterpriseSearch.content.connector_detail.configurationConnector.connectorPackage.connectorConnected": "コネクター{name}は、正常にSearchに接続されました。", - "xpack.enterpriseSearch.content.connector_detail.configurationConnector.connectorPackage.description.secondParagraph": "コネクターリポジトリには複数の{link}が含まれています。当社のフレームワークを使用すると、カスタムデータソース用のコネクターの開発を加速できます。", - "xpack.enterpriseSearch.content.connector_detail.configurationConnector.connectorPackage.description.thirdParagraph": "このステップでは、リポジトリを複製または分割し、関連付けられた{link}に対する生成されたAPIキーとコネクターIDをコピーします。コネクターIDは、Searchに対するこのコネクターを特定します。サービスタイプは、コネクターが構成されているデータソースのタイプを決定します。", "xpack.enterpriseSearch.content.connectors.connectorsTable.columns.actions.viewIndex.caption": "インデックス{connectorName}を表示", "xpack.enterpriseSearch.content.connectors.connectorTable.column.actions.deleteIndex": "コネクター\"{connectorName}\"を削除", "xpack.enterpriseSearch.content.connectors.deleteModal.syncsWarning.indexNameDescription": "この操作は元に戻すことができません。{connectorName}を入力して確認してください。", @@ -15190,9 +15188,7 @@ "xpack.enterpriseSearch.content.analytics.api.generateAnalyticsApiKeyModal.title": "分析APIキーを作成", "xpack.enterpriseSearch.content.cannotConnect.body": "詳細。", "xpack.enterpriseSearch.content.cannotConnect.title": "エンタープライズ サーチに接続できません", - "xpack.enterpriseSearch.content.connector_detail.configurationConnector.connectorPackage.clientExamplesLink": "コネクタークライアントの例", "xpack.enterpriseSearch.content.connector_detail.configurationConnector.connectorPackage.configurationFileLink": "構成ファイル", - "xpack.enterpriseSearch.content.connector_detail.configurationConnector.connectorPackage.connectorDeployedText": "構成したら、インフラでコネクターをデプロイします。", "xpack.enterpriseSearch.content.connector_detail.configurationConnector.connectorPackage.waitingForConnector.button.label": "今すぐ再確認", "xpack.enterpriseSearch.content.connector_detail.configurationConnector.connectorPackage.waitingForConnectorText": "コネクターはSearchに接続されていません。構成のトラブルシューティングを行い、ページを更新してください。", "xpack.enterpriseSearch.content.connector_detail.configurationConnector.connectorPackage.waitingForConnectorTitle": "コネクターを待機しています", diff --git a/x-pack/plugins/translations/translations/zh-CN.json b/x-pack/plugins/translations/translations/zh-CN.json index b372891385ec8..12c75c281fbbf 100644 --- a/x-pack/plugins/translations/translations/zh-CN.json +++ b/x-pack/plugins/translations/translations/zh-CN.json @@ -13928,8 +13928,6 @@ "xpack.enterpriseSearch.connectorStats.incompleteBadgeLabel": "{number} 个未完成", "xpack.enterpriseSearch.connectorStats.runningSyncsTextLabel": "{syncs} 个正在运行的同步", "xpack.enterpriseSearch.content.connector_detail.configurationConnector.connectorPackage.connectorConnected": "您的连接器 {name} 已成功连接到 Search。", - "xpack.enterpriseSearch.content.connector_detail.configurationConnector.connectorPackage.description.secondParagraph": "连接器存储库包含几个 {link}。使用我们的框架可加速为定制数据源开发连接器。", - "xpack.enterpriseSearch.content.connector_detail.configurationConnector.connectorPackage.description.thirdParagraph": "在此步骤中,您需要克隆或分叉存储库,然后将生成的 API 密钥和连接器 ID 复制到关联的 {link}。连接器 ID 会将此连接器标识到 Search。此服务类型将决定要将连接器配置用于哪些类型的数据源。", "xpack.enterpriseSearch.content.connectors.connectorsTable.columns.actions.viewIndex.caption": "查看索引 {connectorName}", "xpack.enterpriseSearch.content.connectors.connectorTable.column.actions.deleteIndex": "删除连接器 {connectorName}", "xpack.enterpriseSearch.content.connectors.deleteModal.syncsWarning.indexNameDescription": "此操作无法撤消。请尝试 {connectorName} 以确认。", @@ -15217,9 +15215,7 @@ "xpack.enterpriseSearch.content.analytics.api.generateAnalyticsApiKeyModal.title": "创建分析 API 密钥", "xpack.enterpriseSearch.content.cannotConnect.body": "更多信息。", "xpack.enterpriseSearch.content.cannotConnect.title": "无法连接到 Enterprise Search", - "xpack.enterpriseSearch.content.connector_detail.configurationConnector.connectorPackage.clientExamplesLink": "连接器客户端示例", "xpack.enterpriseSearch.content.connector_detail.configurationConnector.connectorPackage.configurationFileLink": "配置文件", - "xpack.enterpriseSearch.content.connector_detail.configurationConnector.connectorPackage.connectorDeployedText": "配置后,请在您的基础设施上部署连接器。", "xpack.enterpriseSearch.content.connector_detail.configurationConnector.connectorPackage.waitingForConnector.button.label": "立即重新检查", "xpack.enterpriseSearch.content.connector_detail.configurationConnector.connectorPackage.waitingForConnectorText": "您的连接器尚未连接到 Search。排除配置故障并刷新页面。", "xpack.enterpriseSearch.content.connector_detail.configurationConnector.connectorPackage.waitingForConnectorTitle": "等候您的连接器", diff --git a/x-pack/plugins/triggers_actions_ui/public/application/hooks/use_fetch_rule_action_connectors.ts b/x-pack/plugins/triggers_actions_ui/public/application/hooks/use_fetch_rule_action_connectors.ts index 039fd963137f7..7b1e140c6c5a9 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/hooks/use_fetch_rule_action_connectors.ts +++ b/x-pack/plugins/triggers_actions_ui/public/application/hooks/use_fetch_rule_action_connectors.ts @@ -49,6 +49,7 @@ export function useFetchRuleActionConnectors({ ruleActions }: FetchRuleActionCon } const allActions = await loadAllActions({ http, + includeSystemActions: true, }); setActionConnector((oldState: FetchActionConnectors) => ({ ...oldState, diff --git a/x-pack/plugins/triggers_actions_ui/public/application/lib/action_connector_api/connector_types.test.ts b/x-pack/plugins/triggers_actions_ui/public/application/lib/action_connector_api/connector_types.test.ts index 182abc1507a34..e5575ba28fad5 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/lib/action_connector_api/connector_types.test.ts +++ b/x-pack/plugins/triggers_actions_ui/public/application/lib/action_connector_api/connector_types.test.ts @@ -47,6 +47,7 @@ describe('loadActionTypes', () => { expect(http.get.mock.calls[0]).toMatchInlineSnapshot(` Array [ "/api/actions/connector_types", + Object {}, ] `); }); @@ -92,4 +93,131 @@ describe('loadActionTypes', () => { ] `); }); + + test('should call the internal list types API if includeSystemActions=true', async () => { + const apiResponseValue = [ + { + id: '.test-system-action', + name: 'System action name', + enabled: true, + enabled_in_config: true, + enabled_in_license: true, + supported_feature_ids: ['alerting'], + minimum_license_required: 'basic', + is_system_action_type: true, + }, + { + id: 'test', + name: 'Test', + enabled: true, + enabled_in_config: true, + enabled_in_license: true, + supported_feature_ids: ['alerting'], + minimum_license_required: 'basic', + is_system_action_type: false, + }, + ]; + + http.get.mockResolvedValueOnce(apiResponseValue); + + const resolvedValue: ActionType[] = [ + { + id: '.test-system-action', + name: 'System action name', + enabled: true, + enabledInConfig: true, + enabledInLicense: true, + supportedFeatureIds: ['alerting'], + minimumLicenseRequired: 'basic', + isSystemActionType: true, + }, + { + id: 'test', + name: 'Test', + enabled: true, + enabledInConfig: true, + enabledInLicense: true, + supportedFeatureIds: ['alerting'], + minimumLicenseRequired: 'basic', + isSystemActionType: false, + }, + ]; + + const result = await loadActionTypes({ http, includeSystemActions: true }); + expect(result).toEqual(resolvedValue); + expect(http.get.mock.calls[0]).toMatchInlineSnapshot(` + Array [ + "/internal/actions/connector_types", + Object {}, + ] + `); + }); + + test('should call the internal list types API with query parameter if specified and includeSystemActions=true', async () => { + const apiResponseValue = [ + { + id: '.test-system-action', + name: 'System action name', + enabled: true, + enabled_in_config: true, + enabled_in_license: true, + supported_feature_ids: ['alerting'], + minimum_license_required: 'basic', + is_system_action_type: true, + }, + { + id: 'test', + name: 'Test', + enabled: true, + enabled_in_config: true, + enabled_in_license: true, + supported_feature_ids: ['alerting'], + minimum_license_required: 'basic', + is_system_action_type: false, + }, + ]; + + http.get.mockResolvedValueOnce(apiResponseValue); + + const resolvedValue: ActionType[] = [ + { + id: '.test-system-action', + name: 'System action name', + enabled: true, + enabledInConfig: true, + enabledInLicense: true, + supportedFeatureIds: ['alerting'], + minimumLicenseRequired: 'basic', + isSystemActionType: true, + }, + { + id: 'test', + name: 'Test', + enabled: true, + enabledInConfig: true, + enabledInLicense: true, + supportedFeatureIds: ['alerting'], + minimumLicenseRequired: 'basic', + isSystemActionType: false, + }, + ]; + + const result = await loadActionTypes({ + http, + featureId: 'alerting', + includeSystemActions: true, + }); + + expect(result).toEqual(resolvedValue); + expect(http.get.mock.calls[0]).toMatchInlineSnapshot(` + Array [ + "/internal/actions/connector_types", + Object { + "query": Object { + "feature_id": "alerting", + }, + }, + ] + `); + }); }); diff --git a/x-pack/plugins/triggers_actions_ui/public/application/lib/action_connector_api/connector_types.ts b/x-pack/plugins/triggers_actions_ui/public/application/lib/action_connector_api/connector_types.ts index be16cfc65309e..8ec463113b6a0 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/lib/action_connector_api/connector_types.ts +++ b/x-pack/plugins/triggers_actions_ui/public/application/lib/action_connector_api/connector_types.ts @@ -6,7 +6,11 @@ */ import { HttpSetup } from '@kbn/core/public'; -import { AsApiContract, RewriteRequestCase } from '@kbn/actions-plugin/common'; +import { + AsApiContract, + INTERNAL_BASE_ACTION_API_PATH, + RewriteRequestCase, +} from '@kbn/actions-plugin/common'; import { BASE_ACTION_API_PATH } from '../../constants'; import type { ActionType } from '../../../types'; @@ -33,21 +37,22 @@ const rewriteBodyReq: RewriteRequestCase = ({ export async function loadActionTypes({ http, featureId, + includeSystemActions = false, }: { http: HttpSetup; featureId?: string; + includeSystemActions?: boolean; }): Promise { + const path = includeSystemActions + ? `${INTERNAL_BASE_ACTION_API_PATH}/connector_types` + : `${BASE_ACTION_API_PATH}/connector_types`; + const res = featureId - ? await http.get[0]>( - `${BASE_ACTION_API_PATH}/connector_types`, - { - query: { - feature_id: featureId, - }, - } - ) - : await http.get[0]>( - `${BASE_ACTION_API_PATH}/connector_types` - ); + ? await http.get[0]>(path, { + query: { + feature_id: featureId, + }, + }) + : await http.get[0]>(path, {}); return rewriteResponseRes(res); } diff --git a/x-pack/plugins/triggers_actions_ui/public/application/lib/action_connector_api/connectors.test.ts b/x-pack/plugins/triggers_actions_ui/public/application/lib/action_connector_api/connectors.test.ts index a9216eaec2be5..3633e4234e32c 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/lib/action_connector_api/connectors.test.ts +++ b/x-pack/plugins/triggers_actions_ui/public/application/lib/action_connector_api/connectors.test.ts @@ -6,6 +6,7 @@ */ import { httpServiceMock } from '@kbn/core/public/mocks'; +import { ActionConnectorProps } from '../../../types'; import { loadAllActions } from '.'; const http = httpServiceMock.createStartContract(); @@ -14,14 +15,88 @@ beforeEach(() => jest.resetAllMocks()); describe('loadAllActions', () => { test('should call getAll actions API', async () => { - http.get.mockResolvedValueOnce([]); + const apiResponseValue = [ + { + id: 'test-connector', + name: 'Test', + connector_type_id: 'test', + is_preconfigured: false, + is_deprecated: false, + is_missing_secrets: false, + is_system_action: false, + referenced_by_count: 0, + secrets: {}, + config: {}, + }, + ]; + + const resolvedValue: Array> = [ + { + id: 'test-connector', + name: 'Test', + actionTypeId: 'test', + isPreconfigured: false, + isDeprecated: false, + isMissingSecrets: false, + isSystemAction: false, + referencedByCount: 0, + secrets: {}, + config: {}, + }, + ]; + + http.get.mockResolvedValueOnce(apiResponseValue); const result = await loadAllActions({ http }); - expect(result).toEqual([]); + + expect(result).toEqual(resolvedValue); expect(http.get.mock.calls[0]).toMatchInlineSnapshot(` Array [ "/api/actions/connectors", ] `); }); + + test('should call the internal getAll actions API if includeSystemActions=true', async () => { + const apiResponseValue = [ + { + id: '.test-system-action', + name: 'System action name', + connector_type_id: 'test', + is_preconfigured: false, + is_deprecated: false, + is_missing_secrets: false, + is_system_action: true, + referenced_by_count: 0, + secrets: {}, + config: {}, + }, + ]; + + const resolvedValue: Array> = [ + { + id: '.test-system-action', + name: 'System action name', + actionTypeId: 'test', + isPreconfigured: false, + isDeprecated: false, + isMissingSecrets: false, + isSystemAction: true, + referencedByCount: 0, + secrets: {}, + config: {}, + }, + ]; + + http.get.mockResolvedValueOnce(apiResponseValue); + + const result = await loadAllActions({ http, includeSystemActions: true }); + + expect(result).toEqual(resolvedValue); + expect(http.get.mock.calls[0]).toMatchInlineSnapshot(` + Array [ + "/internal/actions/connectors", + ] + `); + }); }); diff --git a/x-pack/plugins/triggers_actions_ui/public/application/lib/action_connector_api/connectors.ts b/x-pack/plugins/triggers_actions_ui/public/application/lib/action_connector_api/connectors.ts index 6938654b294ac..3e5fea634b479 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/lib/action_connector_api/connectors.ts +++ b/x-pack/plugins/triggers_actions_ui/public/application/lib/action_connector_api/connectors.ts @@ -5,8 +5,12 @@ * 2.0. */ import { HttpSetup } from '@kbn/core/public'; -import { AsApiContract, RewriteRequestCase } from '@kbn/actions-plugin/common'; -import { BASE_ACTION_API_PATH } from '../../constants'; +import { + AsApiContract, + BASE_ACTION_API_PATH, + INTERNAL_BASE_ACTION_API_PATH, + RewriteRequestCase, +} from '@kbn/actions-plugin/common'; import type { ActionConnector, ActionConnectorProps } from '../../../types'; const rewriteResponseRes = ( @@ -37,10 +41,21 @@ const transformConnector: RewriteRequestCase< ...res, }); -export async function loadAllActions({ http }: { http: HttpSetup }): Promise { - const res = await http.get[0]>( - `${BASE_ACTION_API_PATH}/connectors` - ); +export async function loadAllActions({ + http, + includeSystemActions = false, +}: { + http: HttpSetup; + includeSystemActions?: boolean; +}): Promise { + // Use the internal get_all_system route to load all action connectors and preconfigured system action connectors + // This is necessary to load UI elements that require system action connectors, even if they're not selectable and + // editable from the connector selection UI like a normal action connector. + const path = includeSystemActions + ? `${INTERNAL_BASE_ACTION_API_PATH}/connectors` + : `${BASE_ACTION_API_PATH}/connectors`; + + const res = await http.get[0]>(path); return rewriteResponseRes(res) as ActionConnector[]; } diff --git a/x-pack/plugins/triggers_actions_ui/public/application/lib/rule_api/common_transformations.ts b/x-pack/plugins/triggers_actions_ui/public/application/lib/rule_api/common_transformations.ts index b00b874b079ef..e0ba1b232f283 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/lib/rule_api/common_transformations.ts +++ b/x-pack/plugins/triggers_actions_ui/public/application/lib/rule_api/common_transformations.ts @@ -6,35 +6,34 @@ */ import { RuleExecutionStatus } from '@kbn/alerting-plugin/common'; import { AsApiContract, RewriteRequestCase } from '@kbn/actions-plugin/common'; -import type { Rule, RuleAction, ResolvedRule, RuleLastRun } from '../../../types'; +import type { Rule, RuleUiAction, ResolvedRule, RuleLastRun } from '../../../types'; -const transformAction: RewriteRequestCase = ({ - uuid, - group, - id, - connector_type_id: actionTypeId, - params, - frequency, - alerts_filter: alertsFilter, - use_alert_data_for_template: useAlertDataForTemplate, -}) => ({ - group, - id, - params, - actionTypeId, - ...(typeof useAlertDataForTemplate !== 'undefined' ? { useAlertDataForTemplate } : {}), - ...(frequency - ? { - frequency: { - summary: frequency.summary, - notifyWhen: frequency.notify_when, - throttle: frequency.throttle, - }, - } - : {}), - ...(alertsFilter ? { alertsFilter } : {}), - ...(uuid && { uuid }), -}); +const transformAction: RewriteRequestCase = (action) => { + const { uuid, id, connector_type_id: actionTypeId, params } = action; + return { + ...('group' in action && action.group ? { group: action.group } : {}), + id, + params, + actionTypeId, + ...('use_alert_data_for_template' in action && + typeof action.use_alert_data_for_template !== 'undefined' + ? { useAlertDataForTemplate: action.use_alert_data_for_template } + : {}), + ...('frequency' in action && action.frequency + ? { + frequency: { + summary: action.frequency.summary, + notifyWhen: action.frequency.notify_when, + throttle: action.frequency.throttle, + }, + } + : {}), + ...('alerts_filter' in action && action.alerts_filter + ? { alertsFilter: action.alerts_filter } + : {}), + ...(uuid && { uuid }), + }; +}; const transformExecutionStatus: RewriteRequestCase = ({ last_execution_date: lastExecutionDate, @@ -92,7 +91,7 @@ export const transformRule: RewriteRequestCase = ({ snoozeSchedule, executionStatus: executionStatus ? transformExecutionStatus(executionStatus) : undefined, actions: actions - ? actions.map((action: AsApiContract) => transformAction(action)) + ? actions.map((action: AsApiContract) => transformAction(action)) : [], scheduledTaskId, isSnoozedUntil, diff --git a/x-pack/plugins/triggers_actions_ui/public/application/lib/rule_api/create.test.ts b/x-pack/plugins/triggers_actions_ui/public/application/lib/rule_api/create.test.ts index b27d9cad0c056..cf32f04a2bc23 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/lib/rule_api/create.test.ts +++ b/x-pack/plugins/triggers_actions_ui/public/application/lib/rule_api/create.test.ts @@ -47,6 +47,11 @@ describe('createRule', () => { summary: false, }, }, + { + id: '.test-system-action', + params: {}, + connector_type_id: '.system-action', + }, ], scheduled_task_id: '1', execution_status: { status: 'pending', last_execution_date: '2021-04-01T21:33:13.250Z' }, @@ -56,6 +61,7 @@ describe('createRule', () => { active: 10, }, }; + const ruleToCreate: Omit< RuleUpdates, 'createdBy' | 'updatedBy' | 'muteAll' | 'mutedInstanceIds' | 'executionStatus' @@ -94,6 +100,11 @@ describe('createRule', () => { summary: false, }, }, + { + id: '.test-system-action', + params: {}, + actionTypeId: '.system-action', + }, ], createdAt: new Date('2021-04-01T21:33:13.247Z'), updatedAt: new Date('2021-04-01T21:33:13.247Z'), @@ -122,6 +133,11 @@ describe('createRule', () => { summary: false, }, }, + { + id: '.test-system-action', + params: {}, + actionTypeId: '.system-action', + }, ], ruleTypeId: '.index-threshold', apiKeyOwner: undefined, diff --git a/x-pack/plugins/triggers_actions_ui/public/application/lib/rule_api/create.ts b/x-pack/plugins/triggers_actions_ui/public/application/lib/rule_api/create.ts index 48fa1783f3c1f..c9e9dd270b049 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/lib/rule_api/create.ts +++ b/x-pack/plugins/triggers_actions_ui/public/application/lib/rule_api/create.ts @@ -28,22 +28,32 @@ const rewriteBodyRequest: RewriteResponseCase = ({ }): any => ({ ...res, rule_type_id: ruleTypeId, - actions: actions.map( - ({ group, id, params, frequency, alertsFilter, useAlertDataForTemplate }) => ({ - group, + actions: actions.map((action) => { + const { id, params } = action; + return { + ...('group' in action && action.group ? { group: action.group } : {}), id, params, - frequency: { - notify_when: frequency!.notifyWhen, - throttle: frequency!.throttle, - summary: frequency!.summary, - }, - alerts_filter: alertsFilter, - ...(typeof useAlertDataForTemplate !== 'undefined' - ? { use_alert_data_for_template: useAlertDataForTemplate } + ...('frequency' in action && action.frequency + ? { + frequency: { + notify_when: action.frequency!.notifyWhen, + throttle: action.frequency!.throttle, + summary: action.frequency!.summary, + }, + } : {}), - }) - ), + ...('alertsFilter' in action && action.alertsFilter + ? { + alerts_filter: action.alertsFilter, + } + : {}), + ...('useAlertDataForTemplate' in action && + typeof action.useAlertDataForTemplate !== 'undefined' + ? { use_alert_data_for_template: action.useAlertDataForTemplate } + : {}), + }; + }), ...(alertDelay ? { alert_delay: alertDelay } : {}), }); diff --git a/x-pack/plugins/triggers_actions_ui/public/application/lib/rule_api/update.test.ts b/x-pack/plugins/triggers_actions_ui/public/application/lib/rule_api/update.test.ts index 591cdc83e86cf..ebe187ba88ed0 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/lib/rule_api/update.test.ts +++ b/x-pack/plugins/triggers_actions_ui/public/application/lib/rule_api/update.test.ts @@ -21,7 +21,6 @@ describe('updateRule', () => { interval: '1m', }, params: {}, - actions: [], createdAt: new Date('1970-01-01T00:00:00.000Z'), updatedAt: new Date('1970-01-01T00:00:00.000Z'), apiKey: null, @@ -30,7 +29,27 @@ describe('updateRule', () => { alertDelay: { active: 10, }, + actions: [ + { + group: 'default', + id: '2', + actionTypeId: 'test', + params: {}, + useAlertDataForTemplate: false, + frequency: { + notifyWhen: 'onActionGroupChange', + throttle: null, + summary: false, + }, + }, + { + id: '.test-system-action', + params: {}, + actionTypeId: '.system-action', + }, + ], }; + const resolvedValue: Rule = { ...ruleToUpdate, id: '12/3', @@ -46,15 +65,59 @@ describe('updateRule', () => { }, revision: 1, }; - http.put.mockResolvedValueOnce(resolvedValue); + + http.put.mockResolvedValueOnce({ + ...resolvedValue, + actions: [ + { + group: 'default', + id: '2', + connector_type_id: 'test', + params: {}, + use_alert_data_for_template: false, + frequency: { + notify_when: 'onActionGroupChange', + throttle: null, + summary: false, + }, + }, + { + id: '.test-system-action', + params: {}, + connector_type_id: '.system-action', + }, + ], + }); const result = await updateRule({ http, id: '12/3', rule: ruleToUpdate }); - expect(result).toEqual(resolvedValue); + + expect(result).toEqual({ + ...resolvedValue, + actions: [ + { + group: 'default', + id: '2', + actionTypeId: 'test', + params: {}, + useAlertDataForTemplate: false, + frequency: { + notifyWhen: 'onActionGroupChange', + throttle: null, + summary: false, + }, + }, + { + id: '.test-system-action', + params: {}, + actionTypeId: '.system-action', + }, + ], + }); expect(http.put.mock.calls[0]).toMatchInlineSnapshot(` Array [ "/api/alerting/rule/12%2F3", Object { - "body": "{\\"name\\":\\"test\\",\\"tags\\":[\\"foo\\"],\\"schedule\\":{\\"interval\\":\\"1m\\"},\\"params\\":{},\\"actions\\":[],\\"alert_delay\\":{\\"active\\":10}}", + "body": "{\\"name\\":\\"test\\",\\"tags\\":[\\"foo\\"],\\"schedule\\":{\\"interval\\":\\"1m\\"},\\"params\\":{},\\"actions\\":[{\\"group\\":\\"default\\",\\"id\\":\\"2\\",\\"params\\":{},\\"frequency\\":{\\"notify_when\\":\\"onActionGroupChange\\",\\"throttle\\":null,\\"summary\\":false},\\"use_alert_data_for_template\\":false},{\\"id\\":\\".test-system-action\\",\\"params\\":{}}],\\"alert_delay\\":{\\"active\\":10}}", }, ] `); diff --git a/x-pack/plugins/triggers_actions_ui/public/application/lib/rule_api/update.ts b/x-pack/plugins/triggers_actions_ui/public/application/lib/rule_api/update.ts index 80346ff2f65da..652eeb064a429 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/lib/rule_api/update.ts +++ b/x-pack/plugins/triggers_actions_ui/public/application/lib/rule_api/update.ts @@ -21,23 +21,31 @@ const rewriteBodyRequest: RewriteResponseCase = ({ ...res }): any => ({ ...res, - actions: actions.map( - ({ group, id, params, frequency, uuid, alertsFilter, useAlertDataForTemplate }) => ({ - group, + actions: actions.map((action) => { + const { id, params, uuid } = action; + return { + ...('group' in action ? { group: action.group } : {}), id, params, - frequency: { - notify_when: frequency!.notifyWhen, - throttle: frequency!.throttle, - summary: frequency!.summary, - }, - alerts_filter: alertsFilter, - ...(typeof useAlertDataForTemplate !== 'undefined' - ? { use_alert_data_for_template: useAlertDataForTemplate } + ...('frequency' in action + ? { + frequency: action.frequency + ? { + notify_when: action.frequency!.notifyWhen, + throttle: action.frequency!.throttle, + summary: action.frequency!.summary, + } + : undefined, + } + : {}), + ...('alertsFilter' in action ? { alerts_filter: action.alertsFilter } : {}), + ...('useAlertDataForTemplate' in action && + typeof action.useAlertDataForTemplate !== 'undefined' + ? { use_alert_data_for_template: action.useAlertDataForTemplate } : {}), ...(uuid && { uuid }), - }) - ), + }; + }), ...(alertDelay ? { alert_delay: alertDelay } : {}), }); diff --git a/x-pack/plugins/triggers_actions_ui/public/application/lib/value_validators.ts b/x-pack/plugins/triggers_actions_ui/public/application/lib/value_validators.ts index 96c01d201b0a0..bb98c2664141d 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/lib/value_validators.ts +++ b/x-pack/plugins/triggers_actions_ui/public/application/lib/value_validators.ts @@ -8,7 +8,7 @@ import { set } from '@kbn/safer-lodash-set'; import { constant, get } from 'lodash'; import { i18n } from '@kbn/i18n'; -import { UserConfiguredActionConnector, IErrorObject, Rule, RuleAction } from '../../types'; +import { UserConfiguredActionConnector, IErrorObject, Rule, RuleUiAction } from '../../types'; const filterQueryRequiredError = i18n.translate( 'xpack.triggersActionsUI.sections.actionTypeForm.error.requiredFilterQuery', @@ -17,10 +17,12 @@ const filterQueryRequiredError = i18n.translate( } ); -export const validateActionFilterQuery = (actionItem: RuleAction): string | null => { - const query = actionItem.alertsFilter?.query; - if (query && !query.kql) { - return filterQueryRequiredError; +export const validateActionFilterQuery = (actionItem: RuleUiAction): string | null => { + if ('alertsFilter' in actionItem) { + const query = actionItem?.alertsFilter?.query; + if (query && !query.kql) { + return filterQueryRequiredError; + } } return null; }; @@ -70,17 +72,17 @@ export function getConnectorWithInvalidatedFields( baseConnectorErrors: IErrorObject ) { Object.keys(configErrors).forEach((errorKey) => { - if (configErrors[errorKey].length >= 1 && get(connector.config, errorKey) === undefined) { + if (configErrors[errorKey].length && get(connector.config, errorKey) === undefined) { set(connector.config, errorKey, null); } }); Object.keys(secretsErrors).forEach((errorKey) => { - if (secretsErrors[errorKey].length >= 1 && get(connector.secrets, errorKey) === undefined) { + if (secretsErrors[errorKey].length && get(connector.secrets, errorKey) === undefined) { set(connector.secrets, errorKey, null); } }); Object.keys(baseConnectorErrors).forEach((errorKey) => { - if (baseConnectorErrors[errorKey].length >= 1 && get(connector, errorKey) === undefined) { + if (baseConnectorErrors[errorKey].length && get(connector, errorKey) === undefined) { set(connector, errorKey, null); } }); @@ -94,12 +96,12 @@ export function getRuleWithInvalidatedFields( actionsErrors: IErrorObject[] ) { Object.keys(paramsErrors).forEach((errorKey) => { - if (paramsErrors[errorKey].length >= 1 && get(rule.params, errorKey) === undefined) { + if (paramsErrors[errorKey].length && get(rule.params, errorKey) === undefined) { set(rule.params, errorKey, null); } }); Object.keys(baseAlertErrors).forEach((errorKey) => { - if (baseAlertErrors[errorKey].length >= 1 && get(rule, errorKey) === undefined) { + if (baseAlertErrors[errorKey].length && get(rule, errorKey) === undefined) { set(rule, errorKey, null); } }); @@ -107,7 +109,7 @@ export function getRuleWithInvalidatedFields( const actionToValidate = rule.actions.length > index ? rule.actions[index] : null; if (actionToValidate) { Object.keys(error).forEach((errorKey) => { - if (error[errorKey].length >= 1 && get(actionToValidate!.params, errorKey) === undefined) { + if (error[errorKey].length && get(actionToValidate!.params, errorKey) === undefined) { set(actionToValidate!.params, errorKey, null); } }); diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/action_form.test.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/action_form.test.tsx index 176936aabea5d..74b528ba7a64a 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/action_form.test.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/action_form.test.tsx @@ -11,12 +11,13 @@ import { EuiAccordion } from '@elastic/eui'; import { coreMock } from '@kbn/core/public/mocks'; import { act } from 'react-dom/test-utils'; import { actionTypeRegistryMock } from '../../action_type_registry.mock'; -import { ValidationResult, Rule, RuleAction, GenericValidationResult } from '../../../types'; +import { ValidationResult, GenericValidationResult, RuleUiAction } from '../../../types'; import ActionForm from './action_form'; import { useKibana } from '../../../common/lib/kibana'; import { RecoveredActionGroup, isActionGroupDisabledForActionTypeId, + SanitizedRuleAction, } from '@kbn/alerting-plugin/common'; jest.mock('../../../common/lib/kibana'); @@ -24,7 +25,7 @@ jest.mock('../../lib/action_connector_api', () => ({ loadAllActions: jest.fn(), loadActionTypes: jest.fn(), })); -const { loadActionTypes } = jest.requireMock('../../lib/action_connector_api'); +const { loadActionTypes, loadAllActions } = jest.requireMock('../../lib/action_connector_api'); const setHasActionsWithBrokenConnector = jest.fn(); describe('action_form', () => { @@ -106,6 +107,19 @@ describe('action_form', () => { actionParamsFields: mockedActionParamsFields, }; + const systemActionType = { + id: 'my-system-action-type', + iconClass: 'test', + selectMessage: 'system action', + validateParams: (): Promise> => { + const validationResult = { errors: {} }; + return Promise.resolve(validationResult); + }, + actionConnectorFields: null, + actionParamsFields: mockedActionParamsFields, + actionTypeTitle: 'system-action-type-title', + }; + const allActions = [ { secrets: {}, @@ -177,18 +191,28 @@ describe('action_form', () => { isPreconfigured: false, isDeprecated: false, }, + { + secrets: {}, + isMissingSecrets: false, + id: 'test', + actionTypeId: systemActionType.id, + name: 'Test system connector', + config: {}, + isPreconfigured: false, + isDeprecated: false, + isSystemAction: true, + }, ]; const useKibanaMock = useKibana as jest.Mocked; async function setup( - customActions?: RuleAction[], + customActions?: RuleUiAction[], customRecoveredActionGroup?: string, isExperimental?: boolean ) { const actionTypeRegistry = actionTypeRegistryMock.create(); - const { loadAllActions } = jest.requireMock('../../lib/action_connector_api'); loadAllActions.mockResolvedValueOnce(allActions); const mocks = coreMock.createSetup(); const [ @@ -209,13 +233,16 @@ describe('action_form', () => { ...actionType, isExperimental, }; + actionTypeRegistry.list.mockReturnValue([ newActionType, disabledByConfigActionType, disabledByLicenseActionType, disabledByActionType, preconfiguredOnly, + systemActionType, ]); + actionTypeRegistry.has.mockReturnValue(true); actionTypeRegistry.get.mockReturnValue(newActionType); const initialAlert = { @@ -226,7 +253,7 @@ describe('action_form', () => { schedule: { interval: '1m', }, - actions: customActions + actions: (customActions ? customActions : [ { @@ -237,12 +264,12 @@ describe('action_form', () => { message: '', }, }, - ], + ]) as SanitizedRuleAction[], tags: [], muteAll: false, enabled: false, mutedInstanceIds: [], - } as unknown as Rule; + }; loadActionTypes.mockResolvedValue([ { @@ -299,6 +326,16 @@ describe('action_form', () => { minimumLicenseRequired: 'basic', supportedFeatureIds: ['alerting'], }, + { + id: 'my-system-action-type', + name: 'System action', + enabled: true, + enabledInConfig: true, + enabledInLicense: true, + minimumLicenseRequired: 'basic', + supportedFeatureIds: ['alerting'], + isSystemActionType: true, + }, ]); const defaultActionMessage = 'Alert [{{context.metadata.name}}] has exceeded the threshold'; @@ -338,14 +375,17 @@ describe('action_form', () => { setActionGroupIdByIndex={(group: string, index: number) => { initialAlert.actions[index].group = group; }} - setActions={(_updatedActions: RuleAction[]) => {}} + setActions={(_updatedActions: RuleUiAction[]) => {}} setActionParamsProperty={(key: string, value: any, index: number) => (initialAlert.actions[index] = { ...initialAlert.actions[index], [key]: value }) } setActionFrequencyProperty={(key: string, value: any, index: number) => (initialAlert.actions[index] = { ...initialAlert.actions[index], - frequency: { ...initialAlert.actions[index].frequency!, [key]: value }, + frequency: { + ...initialAlert.actions[index].frequency!, + [key]: value, + }, }) } setActionAlertsFilterProperty={(key: string, value: any, index: number) => @@ -383,10 +423,18 @@ describe('action_form', () => { .find(`EuiToolTip [data-test-subj="${actionType.id}-alerting-ActionTypeSelectOption"]`) .exists() ).toBeFalsy(); + expect(setHasActionsWithBrokenConnector).toHaveBeenLastCalledWith(false); expect(loadActionTypes).toBeCalledWith( expect.objectContaining({ featureId: 'alerting', + includeSystemActions: true, + }) + ); + + expect(loadAllActions).toBeCalledWith( + expect.objectContaining({ + includeSystemActions: true, }) ); }); @@ -664,6 +712,7 @@ describe('action_form', () => { wrapper.find('EuiBetaBadge[data-test-subj="action-type-form-beta-badge"]').exists() ).toBeFalsy(); }); + it(`does not render beta badge when isExperimental=false`, async () => { const wrapper = await setup(undefined, undefined, false); expect(wrapper.find('EuiKeyPadMenuItem EuiBetaBadge').exists()).toBeFalsy(); @@ -671,6 +720,7 @@ describe('action_form', () => { wrapper.find('EuiBetaBadge[data-test-subj="action-type-form-beta-badge"]').exists() ).toBeFalsy(); }); + it(`renders beta badge when isExperimental=true`, async () => { const wrapper = await setup(undefined, undefined, true); expect(wrapper.find('EuiKeyPadMenuItem EuiBetaBadge').exists()).toBeTruthy(); @@ -679,4 +729,28 @@ describe('action_form', () => { ).toBeTruthy(); }); }); + + describe('system actions', () => { + it('renders system action types correctly', async () => { + const wrapper = await setup(); + const actionOption = wrapper.find( + `[data-test-subj="${systemActionType.id}-alerting-ActionTypeSelectOption"]` + ); + + expect(actionOption.exists()).toBeTruthy(); + expect(actionOption.at(1).prop('disabled')).toBe(false); + }); + + it('disables the system action type if it is already selected', async () => { + const wrapper = await setup([ + { id: 'system-connector-.cases', actionTypeId: systemActionType.id, params: {} }, + ]); + + const actionOption = wrapper.find( + `[data-test-subj="${systemActionType.id}-alerting-ActionTypeSelectOption"]` + ); + + expect(actionOption.at(1).prop('disabled')).toBe(true); + }); + }); }); diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/action_form.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/action_form.tsx index 6d91e1837b830..097036a152de5 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/action_form.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/action_form.tsx @@ -18,24 +18,28 @@ import { EuiKeyPadMenuItem, EuiToolTip, EuiLink, + EuiEmptyPrompt, + EuiText, } from '@elastic/eui'; import { ActionGroup, RuleActionAlertsFilterProperty, RuleActionFrequency, RuleActionParam, + RuleSystemAction, } from '@kbn/alerting-plugin/common'; import { v4 as uuidv4 } from 'uuid'; import { betaBadgeProps } from './beta_badge_props'; import { loadActionTypes, loadAllActions as loadConnectors } from '../../lib/action_connector_api'; import { ActionTypeModel, - RuleAction, ActionTypeIndex, ActionConnector, ActionVariables, ActionTypeRegistryContract, NotifyWhenSelectOptions, + RuleUiAction, + RuleAction, } from '../../../types'; import { SectionLoading } from '../../components/section_loading'; import { ActionTypeForm } from './action_type_form'; @@ -47,21 +51,21 @@ import { useKibana } from '../../../common/lib/kibana'; import { ConnectorAddModal } from '.'; import { suspendedComponentWithProps } from '../../lib/suspended_component_with_props'; import { OmitMessageVariablesType } from '../../lib/action_variables'; +import { SystemActionTypeForm } from './system_action_type_form'; export interface ActionGroupWithMessageVariables extends ActionGroup { omitMessageVariables?: OmitMessageVariablesType; defaultActionMessage?: string; } - export interface ActionAccordionFormProps { - actions: RuleAction[]; + actions: RuleUiAction[]; defaultActionGroupId: string; actionGroups?: ActionGroupWithMessageVariables[]; defaultActionMessage?: string; setActionIdByIndex: (id: string, index: number) => void; setActionGroupIdByIndex?: (group: string, index: number) => void; setActionUseAlertDataForTemplate?: (enabled: boolean, index: number) => void; - setActions: (actions: RuleAction[]) => void; + setActions: (actions: RuleUiAction[]) => void; setActionParamsProperty: (key: string, value: RuleActionParam, index: number) => void; setActionFrequencyProperty: (key: string, value: RuleActionParam, index: number) => void; setActionAlertsFilterProperty: ( @@ -152,9 +156,13 @@ export const ActionForm = ({ (async () => { try { setIsLoadingActionTypes(true); - const registeredActionTypes = (await loadActionTypes({ http, featureId })).sort((a, b) => - a.name.localeCompare(b.name) - ); + const registeredActionTypes = ( + await loadActionTypes({ + http, + featureId, + includeSystemActions: true, + }) + ).sort((a, b) => a.name.localeCompare(b.name)); const index: ActionTypeIndex = {}; for (const actionTypeItem of registeredActionTypes) { index[actionTypeItem.id] = actionTypeItem; @@ -179,8 +187,12 @@ export const ActionForm = ({ (async () => { try { setIsLoadingConnectors(true); - const loadedConnectors = await loadConnectors({ http }); - setConnectors(loadedConnectors.filter((connector) => !connector.isMissingSecrets)); + const loadedConnectors = await loadConnectors({ http, includeSystemActions: true }); + setConnectors( + loadedConnectors.filter( + (connector) => !connector.isMissingSecrets || connector.isSystemAction + ) + ); } catch (e) { toasts.addDanger({ title: i18n.translate( @@ -240,54 +252,53 @@ export const ActionForm = ({ } setIsAddActionPanelOpen(false); const allowGroupConnector = (actionTypeModel?.subtype ?? []).map((atm) => atm.id); + const isSystemActionType = Boolean( + actionTypesIndex && actionTypesIndex[actionTypeModel.id]?.isSystemActionType + ); let actionTypeConnectors = connectors.filter( (field) => field.actionTypeId === actionTypeModel.id ); - if (actionTypeConnectors.length > 0) { - actions.push({ - id: '', - actionTypeId: actionTypeModel.id, - group: defaultActionGroupId, - params: {}, - frequency: defaultRuleFrequency, - uuid: uuidv4(), - }); - setActionIdByIndex(actionTypeConnectors[0].id, actions.length - 1); - } else { - actionTypeConnectors = connectors.filter((field) => - allowGroupConnector.includes(field.actionTypeId) - ); - if (actionTypeConnectors.length > 0) { - actions.push({ + const actionToPush = isSystemActionType + ? { id: '', - actionTypeId: actionTypeConnectors[0].actionTypeId, + actionTypeId: actionTypeModel.id, + params: {}, + uuid: uuidv4(), + } + : { + id: '', + actionTypeId: actionTypeModel.id, group: defaultActionGroupId, params: {}, - frequency: DEFAULT_FREQUENCY, + frequency: defaultRuleFrequency, uuid: uuidv4(), - }); - setActionIdByIndex(actionTypeConnectors[0].id, actions.length - 1); - } - } + }; if (actionTypeConnectors.length === 0) { - // if no connectors exists or all connectors is already assigned an action under current alert - // set actionType as id to be able to create new connector within the alert form - actions.push({ - id: '', - actionTypeId: actionTypeModel.id, - group: defaultActionGroupId, - params: {}, - frequency: defaultRuleFrequency, - }); - setActionIdByIndex(actions.length.toString(), actions.length - 1); - setEmptyActionsIds([...emptyActionsIds, actions.length.toString()]); + actionTypeConnectors = connectors.filter((field) => + allowGroupConnector.includes(field.actionTypeId) + ); + if (actionTypeConnectors.length > 0) { + // If a connector was successfully found, update the actionTypeId + actions.push({ ...actionToPush, actionTypeId: actionTypeConnectors[0].actionTypeId }); + setActionIdByIndex(actionTypeConnectors[0].id, actions.length - 1); + } else { + // if no connectors exists or all connectors is already assigned an action under current alert + // set actionType as id to be able to create new connector within the alert form + actions.push(actionToPush); + setActionIdByIndex(actions.length.toString(), actions.length - 1); + setEmptyActionsIds([...emptyActionsIds, actions.length.toString()]); + } + } else { + actions.push(actionToPush); + setActionIdByIndex(actionTypeConnectors[0].id, actions.length - 1); } } let actionTypeNodes: Array | null = null; let hasDisabledByLicenseActionTypes = false; + if (actionTypesIndex) { const preconfiguredConnectors = connectors.filter((connector) => connector.isPreconfigured); actionTypeNodes = actionTypeRegistry @@ -311,10 +322,17 @@ export const ActionForm = ({ hasDisabledByLicenseActionTypes = true; } + const isSystemActionSelected = Boolean( + actionTypesIndex[item.id].isSystemActionType && + actions.find((action) => action.actionTypeId === item.id) + ); + + const isDisabled = !checkEnabledResult.isEnabled || isSystemActionSelected; + const keyPadItem = ( )} {actionTypesIndex && - actions.map((actionItem: RuleAction, index: number) => { + actions.map((actionItem: RuleUiAction, index: number) => { + const isSystemActionType = Boolean( + actionTypesIndex[actionItem.actionTypeId]?.isSystemActionType + ); + const actionConnector = connectors.find((field) => field.id === actionItem.id); - // connectors doesn't exists + + const onDeleteAction = () => { + const updatedActions = actions.filter((_item: RuleUiAction, i: number) => i !== index); + setActions(updatedActions); + setIsAddActionPanelOpen( + updatedActions.filter((item: RuleUiAction) => item.id !== actionItem.id).length === 0 + ); + setActiveActionItem(undefined); + }; + + if (isSystemActionType && !actionConnector) { + return ( + + + + } + /> + ); + } + // If connector does not exist if (!actionConnector) { return ( { - const updatedActions = actions.filter( - (_item: RuleAction, i: number) => i !== index - ); - setActions(updatedActions); - setIsAddActionPanelOpen( - updatedActions.filter((item: RuleAction) => item.id !== actionItem.id) - .length === 0 - ); - setActiveActionItem(undefined); - }} + onDeleteConnector={onDeleteAction} onAddConnector={() => { setActiveActionItem({ actionTypeId: actionItem.actionTypeId, indices: actions - .map((item: RuleAction, idx: number) => + .map((item: RuleUiAction, idx: number) => item.id === actionItem.id ? idx : -1 ) .filter((idx: number) => idx >= 0), @@ -411,7 +447,7 @@ export const ActionForm = ({ if (newConnector && newConnector.actionTypeId) { const actionTypeRegistered = actionTypeRegistry.get(newConnector.actionTypeId); if (actionTypeRegistered.convertParamsBetweenGroups) { - const updatedActions = actions.map((_item: RuleAction, i: number) => { + const updatedActions = actions.map((_item: RuleUiAction, i: number) => { if (i === index) { return { ..._item, @@ -433,9 +469,33 @@ export const ActionForm = ({ ); } + if (isSystemActionType) { + return ( + + ); + } + return ( { + const updatedActions = actions.map((_item: RuleUiAction, i: number) => { if (i === index) { return { ..._item, @@ -488,7 +548,7 @@ export const ActionForm = ({ actionTypeRegistry={actionTypeRegistry} onDeleteAction={() => { const updatedActions = actions.filter( - (_item: RuleAction, i: number) => i !== index + (_item: RuleUiAction, i: number) => i !== index ); setActions(updatedActions); setIsAddActionPanelOpen(updatedActions.length === 0); diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/action_notify_when.test.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/action_notify_when.test.tsx index a33f5714afb1e..1df0d90e6e6f9 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/action_notify_when.test.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/action_notify_when.test.tsx @@ -9,14 +9,13 @@ import React from 'react'; import { mountWithIntl, nextTick } from '@kbn/test-jest-helpers'; import { EuiSuperSelectProps } from '@elastic/eui'; import { act } from 'react-dom/test-utils'; -import { RuleAction } from '../../../types'; import { ActionNotifyWhen } from './action_notify_when'; -import { RuleNotifyWhen } from '@kbn/alerting-plugin/common'; +import { RuleNotifyWhen, SanitizedRuleAction } from '@kbn/alerting-plugin/common'; import { DEFAULT_FREQUENCY } from '../../../common/constants'; describe('action_notify_when', () => { async function setup( - frequency: RuleAction['frequency'] = DEFAULT_FREQUENCY, + frequency: SanitizedRuleAction['frequency'] = DEFAULT_FREQUENCY, hasAlertsMappings: boolean = true ) { const wrapper = mountWithIntl( diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/action_notify_when.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/action_notify_when.tsx index 16b9fee7e9233..6be43f797b7a4 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/action_notify_when.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/action_notify_when.tsx @@ -5,7 +5,7 @@ * 2.0. */ -import { RuleNotifyWhen } from '@kbn/alerting-plugin/common'; +import { RuleAction, RuleNotifyWhen } from '@kbn/alerting-plugin/common'; import React, { useState, useEffect, useCallback, useMemo } from 'react'; import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n-react'; @@ -27,7 +27,7 @@ import { import { some, filter, map } from 'fp-ts/lib/Option'; import { pipe } from 'fp-ts/lib/pipeable'; import { getTimeOptions } from '../../../common/lib/get_time_options'; -import { RuleNotifyWhenType, RuleAction, NotifyWhenSelectOptions } from '../../../types'; +import { RuleNotifyWhenType, NotifyWhenSelectOptions } from '../../../types'; import { DEFAULT_FREQUENCY } from '../../../common/constants'; export const NOTIFY_WHEN_OPTIONS: NotifyWhenSelectOptions[] = [ diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/action_type_form.test.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/action_type_form.test.tsx index b7bdb57a4d656..cbf6c17e78481 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/action_type_form.test.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/action_type_form.test.tsx @@ -11,7 +11,6 @@ import { actionTypeRegistryMock } from '../../action_type_registry.mock'; import { ActionConnector, ActionType, - RuleAction, GenericValidationResult, ActionConnectorMode, ActionVariables, @@ -23,7 +22,11 @@ import { I18nProvider, __IntlProvider as IntlProvider } from '@kbn/i18n-react'; import { render, waitFor, screen } from '@testing-library/react'; import { DEFAULT_FREQUENCY } from '../../../common/constants'; import { transformActionVariables } from '../../lib/action_variables'; -import { RuleNotifyWhen, RuleNotifyWhenType } from '@kbn/alerting-plugin/common'; +import { + RuleNotifyWhen, + RuleNotifyWhenType, + SanitizedRuleAction, +} from '@kbn/alerting-plugin/common'; import { AlertConsumers } from '@kbn/rule-data-utils'; const CUSTOM_NOTIFY_WHEN_OPTIONS: NotifyWhenSelectOptions[] = [ @@ -654,7 +657,7 @@ function getActionTypeForm({ }: { index?: number; actionConnector?: ActionConnector, Record>; - actionItem?: RuleAction; + actionItem?: SanitizedRuleAction; defaultActionGroupId?: string; connectors?: Array, Record>>; actionTypeIndex?: Record; @@ -686,7 +689,7 @@ function getActionTypeForm({ secrets: {}, }; - const actionItemDefault: RuleAction = { + const actionItemDefault = { id: '123', actionTypeId: '.pagerduty', group: 'trigger', diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/connector_add_inline.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/connector_add_inline.tsx index ec5e4ef01ceec..7811b1b88b6bb 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/connector_add_inline.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/connector_add_inline.tsx @@ -25,16 +25,16 @@ import { EuiBetaBadge, } from '@elastic/eui'; import { betaBadgeProps } from './beta_badge_props'; -import { RuleAction, ActionTypeIndex, ActionConnector } from '../../../types'; +import { RuleUiAction, ActionTypeIndex, ActionConnector } from '../../../types'; import { hasSaveActionsCapability } from '../../lib/capabilities'; import { ActionAccordionFormProps } from './action_form'; import { useKibana } from '../../../common/lib/kibana'; import { getValidConnectors } from '../common/connectors'; import { ConnectorsSelection } from './connectors_selection'; -type AddConnectorInFormProps = { +export type AddConnectorInFormProps = { actionTypesIndex: ActionTypeIndex; - actionItem: RuleAction; + actionItem: RuleUiAction; connectors: ActionConnector[]; index: number; onAddConnector: () => void; diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/connectors_selection.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/connectors_selection.tsx index 736d8c44ea927..6554709846d4c 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/connectors_selection.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/connectors_selection.tsx @@ -9,7 +9,7 @@ import { EuiComboBox, EuiComboBoxOptionOption } from '@elastic/eui'; import React, { useCallback, useMemo, useState } from 'react'; import { i18n } from '@kbn/i18n'; -import { ActionConnector, ActionTypeIndex, ActionTypeModel, RuleAction } from '../../../types'; +import { ActionConnector, ActionTypeIndex, ActionTypeModel, RuleUiAction } from '../../../types'; import { getValidConnectors } from '../common/connectors'; interface ConnectorOption { @@ -20,7 +20,7 @@ interface ConnectorOption { interface SelectionProps { allowGroupConnector?: string[]; - actionItem: RuleAction; + actionItem: RuleUiAction; accordionIndex: number; actionTypesIndex: ActionTypeIndex; actionTypeRegistered: ActionTypeModel; diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/system_action_type_form.test.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/system_action_type_form.test.tsx new file mode 100644 index 0000000000000..ef92d61fbc303 --- /dev/null +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/system_action_type_form.test.tsx @@ -0,0 +1,212 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ +import * as React from 'react'; +import { render, screen, waitFor } from '@testing-library/react'; +import { SystemActionTypeForm } from './system_action_type_form'; +import { actionTypeRegistryMock } from '../../action_type_registry.mock'; +import { ActionType, GenericValidationResult, ActionParamsProps } from '../../../types'; +import { EuiButton } from '@elastic/eui'; +import { AlertConsumers } from '@kbn/rule-data-utils'; +import { I18nProvider } from '@kbn/i18n-react'; +import userEvent from '@testing-library/user-event'; + +const actionTypeRegistry = actionTypeRegistryMock.create(); + +jest.mock('../../../common/lib/kibana'); + +jest.mock('../../lib/action_variables', () => { + const original = jest.requireActual('../../lib/action_variables'); + return { + ...original, + transformActionVariables: jest.fn(), + }; +}); + +jest.mock('../../hooks/use_rule_aad_template_fields', () => ({ + useRuleTypeAadTemplateFields: () => ({ + isLoading: false, + fields: [], + }), +})); + +const actionConnector = { + actionTypeId: '.test-system-action', + config: {}, + id: 'test', + isPreconfigured: false as const, + isDeprecated: false, + isSystemAction: true, + name: 'test name', + secrets: {}, +}; + +const actionItem = { + id: '123', + actionTypeId: '.test-system-action', + params: {}, +}; + +const connectors = [actionConnector]; + +const actionTypeIndexDefault: Record = { + '.test-system-action': { + id: '.test-system-action', + enabled: true, + name: 'Test', + enabledInConfig: true, + enabledInLicense: true, + minimumLicenseRequired: 'basic', + supportedFeatureIds: ['alerting'], + isSystemActionType: true, + }, +}; + +const mockedActionParamsFields = React.lazy(async () => ({ + default(props: ActionParamsProps<{}>) { + return ( + <> + { + props.editAction('my-key', 'my-value', 1); + }} + /> + + ); + }, +})); + +describe('action_type_form', () => { + beforeEach(() => { + const actionType = actionTypeRegistryMock.createMockActionTypeModel({ + id: '.test-system-action', + iconClass: 'test', + selectMessage: 'test', + validateParams: (): Promise> => { + const validationResult = { errors: {} }; + return Promise.resolve(validationResult); + }, + actionConnectorFields: null, + actionParamsFields: mockedActionParamsFields, + defaultActionParams: { + dedupKey: 'test', + eventAction: 'resolve', + }, + isSystemActionType: true, + }); + + actionTypeRegistry.get.mockReturnValue(actionType); + + jest.clearAllMocks(); + }); + + it('should render the system action correctly', async () => { + render( + + + + ); + + expect(await screen.findByTestId('test-button')).toBeInTheDocument(); + }); + + it('should render the name of the system action correctly', async () => { + render( + + + + ); + + expect(await screen.findByText('test name')).toBeInTheDocument(); + }); + + it('calls onDeleteAction correctly', async () => { + const onDelete = jest.fn(); + + render( + + + + ); + + userEvent.click(await screen.findByTestId('system-action-delete-button')); + + await waitFor(() => { + expect(onDelete).toHaveBeenCalled(); + }); + }); + + it('calls setActionParamsProperty correctly', async () => { + const setActionParamsProperty = jest.fn(); + + render( + + + + ); + + userEvent.click(await screen.findByTestId('test-button')); + + expect(setActionParamsProperty).toHaveBeenCalledWith('my-key', 'my-value', 1); + }); +}); diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/system_action_type_form.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/system_action_type_form.tsx new file mode 100644 index 0000000000000..62263a7f87ad4 --- /dev/null +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/system_action_type_form.tsx @@ -0,0 +1,355 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor 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, useCallback, useEffect, useMemo, useState } from 'react'; +import { i18n } from '@kbn/i18n'; +import { FormattedMessage } from '@kbn/i18n-react'; +import { + EuiFlexGroup, + EuiFlexItem, + EuiIcon, + EuiSpacer, + EuiAccordion, + EuiButtonIcon, + EuiText, + EuiBadge, + EuiErrorBoundary, + EuiToolTip, + EuiBetaBadge, + EuiSplitPanel, + EuiCallOut, + IconType, +} from '@elastic/eui'; +import { isEmpty, partition, some } from 'lodash'; +import { ActionVariable, RuleActionParam } from '@kbn/alerting-plugin/common'; +import { betaBadgeProps } from './beta_badge_props'; +import { + IErrorObject, + RuleSystemAction, + ActionTypeIndex, + ActionConnector, + ActionVariables, + ActionTypeRegistryContract, + ActionConnectorMode, +} from '../../../types'; +import { ActionAccordionFormProps, ActionGroupWithMessageVariables } from './action_form'; +import { transformActionVariables } from '../../lib/action_variables'; +import { useKibana } from '../../../common/lib/kibana'; +import { validateParamsForWarnings } from '../../lib/validate_params_for_warnings'; +import { useRuleTypeAadTemplateFields } from '../../hooks/use_rule_aad_template_fields'; + +export type SystemActionTypeFormProps = { + actionItem: RuleSystemAction; + actionConnector: ActionConnector; + index: number; + onDeleteAction: () => void; + setActionParamsProperty: (key: string, value: RuleActionParam, index: number) => void; + actionTypesIndex: ActionTypeIndex; + connectors: ActionConnector[]; + actionTypeRegistry: ActionTypeRegistryContract; + featureId: string; + producerId: string; + ruleTypeId?: string; + disableErrorMessages?: boolean; +} & Pick< + ActionAccordionFormProps, + | 'setActionParamsProperty' + | 'messageVariables' + | 'summaryMessageVariables' + | 'defaultActionMessage' + | 'defaultSummaryMessage' +>; + +export const SystemActionTypeForm = ({ + actionItem, + actionConnector, + index, + onDeleteAction, + setActionParamsProperty, + actionTypesIndex, + connectors, + defaultActionMessage, + messageVariables, + summaryMessageVariables, + actionTypeRegistry, + defaultSummaryMessage, + producerId, + featureId, + ruleTypeId, + disableErrorMessages, +}: SystemActionTypeFormProps) => { + const { http } = useKibana().services; + const [isOpen, setIsOpen] = useState(true); + const [actionParamsErrors, setActionParamsErrors] = useState<{ errors: IErrorObject }>({ + errors: {}, + }); + + const [warning, setWarning] = useState(null); + + const { fields: aadTemplateFields } = useRuleTypeAadTemplateFields(http, ruleTypeId, true); + + const getDefaultParams = useCallback(() => { + const connectorType = actionTypeRegistry.get(actionItem.actionTypeId); + + return connectorType.defaultActionParams; + }, [actionItem.actionTypeId, actionTypeRegistry]); + + const availableActionVariables = useMemo( + () => + messageVariables + ? getAvailableActionVariables(messageVariables, summaryMessageVariables, undefined, true) + : [], + [messageVariables, summaryMessageVariables] + ); + + useEffect(() => { + const defaultParams = getDefaultParams(); + + if (defaultParams) { + for (const [key, paramValue] of Object.entries(defaultParams)) { + const defaultAADParams: typeof defaultParams = {}; + if (actionItem.params[key] === undefined || actionItem.params[key] === null) { + setActionParamsProperty(key, paramValue, index); + // Add default param to AAD defaults only if it does not contain any template code + if (typeof paramValue !== 'string' || !paramValue.match(/{{.*?}}/g)) { + defaultAADParams[key] = paramValue; + } + } + } + } + }, [ + actionItem.params, + getDefaultParams, + index, + messageVariables, + setActionParamsProperty, + summaryMessageVariables, + ]); + + useEffect(() => { + const defaultParams = getDefaultParams(); + + if (defaultParams) { + const defaultAADParams: typeof defaultParams = {}; + for (const [key, paramValue] of Object.entries(defaultParams)) { + setActionParamsProperty(key, paramValue, index); + if (!paramValue.match(/{{.*?}}/g)) { + defaultAADParams[key] = paramValue; + } + } + } + }, [getDefaultParams, index, setActionParamsProperty]); + + useEffect(() => { + (async () => { + if (disableErrorMessages) { + setActionParamsErrors({ errors: {} }); + return; + } + const res: { errors: IErrorObject } = await actionTypeRegistry + .get(actionItem.actionTypeId) + ?.validateParams(actionItem.params); + setActionParamsErrors(res); + })(); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [actionItem, disableErrorMessages]); + + const actionTypeRegistered = actionTypeRegistry.get(actionConnector.actionTypeId); + if (!actionTypeRegistered) return null; + + const showActionGroupErrorIcon = (): boolean => { + return !isOpen && some(actionParamsErrors.errors, (error) => !isEmpty(error)); + }; + + const ParamsFieldsComponent = actionTypeRegistered.actionParamsFields; + + const accordionContent = ( + <> + + {ParamsFieldsComponent ? ( + + + + + { + setWarning( + validateParamsForWarnings( + value, + http.basePath.publicBaseUrl, + availableActionVariables + ) + ); + setActionParamsProperty(key, value, i); + }} + messageVariables={aadTemplateFields} + defaultMessage={defaultSummaryMessage} + useDefaultMessage={true} + actionConnector={actionConnector} + executionMode={ActionConnectorMode.ActionForm} + ruleTypeId={ruleTypeId} + /> + {warning ? ( + <> + + + + ) : null} + + + + + ) : null} + + + ); + + return ( + <> + + + } + extraAction={ + + } + > + {accordionContent} + + + + + ); +}; + +function getAvailableActionVariables( + actionVariables: ActionVariables, + summaryActionVariables?: ActionVariables, + actionGroup?: ActionGroupWithMessageVariables, + isSummaryAction?: boolean +) { + const transformedActionVariables: ActionVariable[] = transformActionVariables( + actionVariables, + summaryActionVariables, + actionGroup?.omitMessageVariables, + isSummaryAction + ); + + // partition deprecated items so they show up last + const partitionedActionVariables = partition( + transformedActionVariables, + (v) => v.deprecated !== true + ); + return partitionedActionVariables.reduce((acc, curr) => { + return [ + ...acc, + ...curr.sort((a, b) => a.name.toUpperCase().localeCompare(b.name.toUpperCase())), + ]; + }, []); +} + +const ButtonContent: React.FC<{ + showActionGroupErrorIcon: boolean; + iconClass: string | IconType; + connectorName: string; + showWarning: boolean; + isExperimental: boolean; +}> = ({ showActionGroupErrorIcon, iconClass, showWarning, isExperimental, connectorName }) => { + return ( + + {showActionGroupErrorIcon ? ( + + + + + + ) : ( + + + + )} + + +
+ + + + + {showWarning && ( + + + {i18n.translate( + 'xpack.triggersActionsUI.sections.actionTypeForm.actionWarningsTitle', + { + defaultMessage: '1 warning', + } + )} + + + )} + +
+
+
+ {isExperimental && ( + + + + )} +
+ ); +}; diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/common/components/with_actions_api_operations.test.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/common/components/with_actions_api_operations.test.tsx index dac08978a54c9..888e42524bf7c 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/common/components/with_actions_api_operations.test.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/common/components/with_actions_api_operations.test.tsx @@ -41,6 +41,6 @@ describe('with_action_api_operations', () => { component.find('button').simulate('click'); expect(actionApis.loadActionTypes).toHaveBeenCalledTimes(1); - expect(actionApis.loadActionTypes).toHaveBeenCalledWith({ http }); + expect(actionApis.loadActionTypes).toHaveBeenCalledWith({ http, includeSystemActions: true }); }); }); diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/common/components/with_actions_api_operations.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/common/components/with_actions_api_operations.tsx index 7b2233ded244c..1ba823dc3c41d 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/common/components/with_actions_api_operations.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/common/components/with_actions_api_operations.tsx @@ -40,7 +40,7 @@ export function withActionOperations( return ( loadActionTypes({ http })} + loadActionTypes={async () => loadActionTypes({ http, includeSystemActions: true })} loadGlobalConnectorExecutionLogAggregations={async ( loadProps: LoadGlobalConnectorExecutionLogAggregationsProps ) => diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/common/connectors.ts b/x-pack/plugins/triggers_actions_ui/public/application/sections/common/connectors.ts index af09d984f9417..3ce8e1e178f57 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/common/connectors.ts +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/common/connectors.ts @@ -5,11 +5,11 @@ * 2.0. */ -import { ActionConnector, ActionTypeIndex, RuleAction } from '../../../types'; +import { ActionConnector, ActionTypeIndex, RuleUiAction } from '../../../types'; export const getValidConnectors = ( connectors: ActionConnector[], - actionItem: RuleAction, + actionItem: RuleUiAction, actionTypesIndex: ActionTypeIndex, allowGroupConnector: string[] = [] ): ActionConnector[] => { diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/rule_details/components/rule_actions.test.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/rule_details/components/rule_actions.test.tsx index 6e6aee64aaf58..a27241d9cef9b 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/rule_details/components/rule_actions.test.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/rule_details/components/rule_actions.test.tsx @@ -8,17 +8,30 @@ import React from 'react'; import { mount } from 'enzyme'; import { nextTick } from '@kbn/test-jest-helpers'; import { act } from 'react-dom/test-utils'; +import { screen, render } from '@testing-library/react'; import { RuleActions } from './rule_actions'; import { actionTypeRegistryMock } from '../../../action_type_registry.mock'; -import { ActionConnector, ActionTypeModel, RuleAction } from '../../../../types'; +import { ActionConnector, ActionTypeModel } from '../../../../types'; import * as useFetchRuleActionConnectorsHook from '../../../hooks/use_fetch_rule_action_connectors'; const actionTypeRegistry = actionTypeRegistryMock.create(); +const actionType = { + id: 'test', + name: 'Test', + isSystemActionType: false, +} as unknown as ActionTypeModel; + const mockedUseFetchRuleActionConnectorsHook = jest.spyOn( useFetchRuleActionConnectorsHook, 'useFetchRuleActionConnectors' ); + describe('Rule Actions', () => { + beforeEach(() => { + jest.clearAllMocks(); + actionTypeRegistry.get.mockReturnValue(actionType); + }); + async function setup() { const ruleActions = [ { @@ -33,7 +46,7 @@ describe('Rule Actions', () => { actionTypeId: '.slack', params: {}, }, - ] as RuleAction[]; + ]; mockedUseFetchRuleActionConnectorsHook.mockReturnValue({ isLoadingActionConnectors: false, @@ -169,4 +182,40 @@ describe('Rule Actions', () => { expect(wrapper.find('[data-test-subj="actionConnectorName-3-slack1"]').exists).toBeTruthy(); expect(wrapper.find('[data-test-subj="actionConnectorName-4-slack2"]').exists).toBeTruthy(); }); + + it('shows the correct notify text for system actions', async () => { + const ruleActions = [ + { + id: 'system-connector-.test-system-action', + actionTypeId: '.test-system-action', + params: {}, + }, + ]; + + actionTypeRegistry.list.mockReturnValue([ + { id: '.test-system-action', iconClass: 'logsApp' }, + ] as ActionTypeModel[]); + + actionTypeRegistry.get.mockReturnValue({ + ...actionType, + isSystemActionType: true, + id: '.test-system-action', + }); + + mockedUseFetchRuleActionConnectorsHook.mockReturnValue({ + isLoadingActionConnectors: false, + actionConnectors: [ + { + id: 'system-connector-.test-system-action', + actionTypeId: '.test-system-action', + }, + ] as Array>>, + errorActionConnectors: undefined, + reloadRuleActionConnectors: jest.fn(), + }); + + render(); + + expect(await screen.findByText('On check intervals')).toBeInTheDocument(); + }); }); diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/rule_details/components/rule_actions.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/rule_details/components/rule_actions.tsx index 6ff952e7f30bb..a4e0ea59e685c 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/rule_details/components/rule_actions.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/rule_details/components/rule_actions.tsx @@ -16,12 +16,13 @@ import { } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { RuleNotifyWhenType } from '@kbn/alerting-plugin/common'; -import { ActionTypeRegistryContract, RuleAction, suspendedComponentWithProps } from '../../../..'; +import { ActionTypeRegistryContract, suspendedComponentWithProps } from '../../../..'; import { useFetchRuleActionConnectors } from '../../../hooks/use_fetch_rule_action_connectors'; import { NOTIFY_WHEN_OPTIONS } from '../../rule_form/rule_notify_when'; +import { RuleUiAction } from '../../../../types'; export interface RuleActionsProps { - ruleActions: RuleAction[]; + ruleActions: RuleUiAction[]; actionTypeRegistry: ActionTypeRegistryContract; legacyNotifyWhen?: RuleNotifyWhenType | null; } @@ -51,11 +52,19 @@ export function RuleActions({ ); } - const getNotifyText = (action: RuleAction) => - (NOTIFY_WHEN_OPTIONS.find((options) => options.value === action.frequency?.notifyWhen) - ?.inputDisplay || - action.frequency?.notifyWhen) ?? - legacyNotifyWhen; + const getNotifyText = (action: RuleUiAction, isSystemAction?: boolean) => { + if (isSystemAction) { + return NOTIFY_WHEN_OPTIONS[1].inputDisplay; + } + + return ( + ('frequency' in action && + (NOTIFY_WHEN_OPTIONS.find((options) => options.value === action.frequency?.notifyWhen) + ?.inputDisplay || + action.frequency?.notifyWhen)) ?? + legacyNotifyWhen + ); + }; const getActionIconClass = (actionGroupId?: string): IconType | undefined => { const actionGroup = actionTypeRegistry.list().find((group) => group.id === actionGroupId); @@ -70,6 +79,7 @@ export function RuleActions({ }; if (isLoadingActionConnectors) return ; + return ( {ruleActions.map((action, index) => { @@ -98,7 +108,12 @@ export function RuleActions({ data-test-subj={`actionConnectorName-${index}-${actionName || actionTypeId}`} size="xs" > - {String(getNotifyText(action))} + {String( + getNotifyText( + action, + actionTypeRegistry.get(actionTypeId).isSystemActionType + ) + )} diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/rule_details/components/rule_details.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/rule_details/components/rule_details.tsx index a7f46a8e554c5..8bb60c0706f15 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/rule_details/components/rule_details.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/rule_details/components/rule_details.tsx @@ -6,7 +6,7 @@ */ import { i18n } from '@kbn/i18n'; -import React, { useState, useEffect, useReducer } from 'react'; +import React, { useState, useEffect, useReducer, useMemo } from 'react'; import { useHistory } from 'react-router-dom'; import { EuiPageHeader, @@ -58,7 +58,7 @@ import { rulesWarningReasonTranslationsMapping, } from '../../rules_list/translations'; import { useKibana } from '../../../../common/lib/kibana'; -import { ruleReducer } from '../../rule_form/rule_reducer'; +import { getRuleReducer } from '../../rule_form/rule_reducer'; import { loadAllActions as loadConnectors } from '../../../lib/action_connector_api'; import { triggersActionsUiConfig } from '../../../../common/lib/config_api'; import { runRule } from '../../../lib/run_rule'; @@ -109,6 +109,7 @@ export const RuleDetails: React.FunctionComponent = ({ http, notifications: { toasts }, } = useKibana().services; + const ruleReducer = useMemo(() => getRuleReducer(actionTypeRegistry), [actionTypeRegistry]); const [{}, dispatch] = useReducer(ruleReducer, { rule }); const setInitialRule = (value: Rule) => { dispatch({ command: { type: 'setRule' }, payload: { key: 'rule', value } }); @@ -141,7 +142,7 @@ export const RuleDetails: React.FunctionComponent = ({ (async () => { let loadedConnectors: ActionConnector[] = []; try { - loadedConnectors = await loadConnectors({ http }); + loadedConnectors = await loadConnectors({ http, includeSystemActions: true }); } catch (err) { loadedConnectors = []; } diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/rule_form/rule_add.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/rule_form/rule_add.tsx index 19eb8da4bf0d3..1b12fbffa4a1b 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/rule_form/rule_add.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/rule_form/rule_add.tsx @@ -26,7 +26,7 @@ import { } from '../../../types'; import { RuleForm } from './rule_form'; import { getRuleActionErrors, getRuleErrors, isValidRule } from './rule_errors'; -import { ruleReducer, InitialRule, InitialRuleReducer } from './rule_reducer'; +import { InitialRule, getRuleReducer } from './rule_reducer'; import { createRule } from '../../lib/rule_api/create'; import { loadRuleTypes } from '../../lib/rule_api/rule_types'; import { HealthCheck } from '../../components/health_check'; @@ -91,7 +91,8 @@ const RuleAdd = < ...(initialValues ? initialValues : {}), }; }, [ruleTypeId, consumer, initialValues]); - const [{ rule }, dispatch] = useReducer(ruleReducer as InitialRuleReducer, { + const ruleReducer = useMemo(() => getRuleReducer(actionTypeRegistry), [actionTypeRegistry]); + const [{ rule }, dispatch] = useReducer(ruleReducer, { rule: initialRule, }); const [config, setConfig] = useState({ isUsingSecurity: false }); @@ -234,9 +235,10 @@ const RuleAdd = < : {}), } as Rule, ruleType, - config + config, + actionTypeRegistry ), - [rule, selectedConsumer, selectableConsumer, ruleType, config] + [rule, selectableConsumer, selectedConsumer, ruleType, config, actionTypeRegistry] ); // Confirm before saving if user is able to add actions but hasn't added any to this rule diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/rule_form/rule_edit.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/rule_form/rule_edit.tsx index 975881e516e45..e4fcd86eef445 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/rule_form/rule_edit.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/rule_form/rule_edit.tsx @@ -5,7 +5,7 @@ * 2.0. */ -import React, { useReducer, useState, useEffect, useCallback } from 'react'; +import React, { useReducer, useState, useEffect, useCallback, useMemo } from 'react'; import { FormattedMessage } from '@kbn/i18n-react'; import { RuleNotifyWhen } from '@kbn/alerting-plugin/common'; import { @@ -38,10 +38,12 @@ import { RuleTypeMetaData, TriggersActionsUiConfig, RuleNotifyWhenType, + RuleUiAction, + RuleAction, } from '../../../types'; import { RuleForm } from './rule_form'; import { getRuleActionErrors, getRuleErrors, isValidRule } from './rule_errors'; -import { ruleReducer, ConcreteRuleReducer } from './rule_reducer'; +import { getRuleReducer } from './rule_reducer'; import { updateRule } from '../../lib/rule_api/update'; import { loadRuleTypes } from '../../lib/rule_api/rule_types'; import { HealthCheck } from '../../components/health_check'; @@ -60,6 +62,14 @@ const defaultUpdateRuleErrorMessage = i18n.translate( } ); +// Separate function for determining if an untyped action has a group property or not, which helps determine if +// it is a default action or a system action. Consolidated here to deal with type definition complexity +const actionHasDefinedGroup = (action: RuleUiAction): action is RuleAction => { + if (!('group' in action)) return false; + // If the group property is present, ensure that it isn't null or undefined + return Boolean(action.group); +}; + const cloneAndMigrateRule = (initialRule: Rule) => { const clonedRule = cloneDeep(omit(initialRule, 'notifyWhen', 'throttle')); @@ -75,10 +85,16 @@ const cloneAndMigrateRule = (initialRule: Rule) => { initialRule.notifyWhen === RuleNotifyWhen.THROTTLE ? initialRule.throttle! : null, } : { summary: false, notifyWhen: RuleNotifyWhen.THROTTLE, throttle: initialRule.throttle! }; - clonedRule.actions = clonedRule.actions.map((action) => ({ - ...action, - frequency, - })); + + clonedRule.actions = clonedRule.actions.map((action: RuleUiAction) => { + if (actionHasDefinedGroup(action)) { + return { + ...action, + frequency, + }; + } + return action; + }); } return clonedRule; }; @@ -100,7 +116,8 @@ export const RuleEdit = < ...props }: RuleEditProps) => { const onSaveHandler = onSave ?? reloadRules; - const [{ rule }, dispatch] = useReducer(ruleReducer as ConcreteRuleReducer, { + const ruleReducer = useMemo(() => getRuleReducer(actionTypeRegistry), [actionTypeRegistry]); + const [{ rule }, dispatch] = useReducer(ruleReducer, { rule: cloneAndMigrateRule(initialRule), }); const [isSaving, setIsSaving] = useState(false); @@ -160,7 +177,8 @@ export const RuleEdit = < const { ruleBaseErrors, ruleErrors, ruleParamsErrors } = getRuleErrors( rule as Rule, ruleType, - config + config, + actionTypeRegistry ); const checkForChangesAndCloseFlyout = () => { diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/rule_form/rule_errors.test.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/rule_form/rule_errors.test.tsx index ad4f081e3d625..4457c2daa1038 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/rule_form/rule_errors.test.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/rule_form/rule_errors.test.tsx @@ -16,14 +16,16 @@ import { } from './rule_errors'; import { Rule, RuleTypeModel } from '../../../types'; import { actionTypeRegistryMock } from '../../action_type_registry.mock'; +import { ActionTypeModel } from '../../..'; +const actionTypeRegistry = actionTypeRegistryMock.create(); const config = { isUsingSecurity: true, minimumScheduleInterval: { value: '1m', enforce: false } }; describe('rule_errors', () => { describe('validateBaseProperties()', () => { it('should validate the name', () => { const rule = mockRule(); rule.name = ''; - const result = validateBaseProperties(rule, config); + const result = validateBaseProperties(rule, config, actionTypeRegistry); expect(result.errors).toStrictEqual({ name: ['Name is required.'], 'schedule.interval': [], @@ -36,7 +38,7 @@ describe('rule_errors', () => { it('should validate the interval', () => { const rule = mockRule(); rule.schedule.interval = ''; - const result = validateBaseProperties(rule, config); + const result = validateBaseProperties(rule, config, actionTypeRegistry); expect(result.errors).toStrictEqual({ name: [], 'schedule.interval': ['Check interval is required.'], @@ -49,7 +51,7 @@ describe('rule_errors', () => { it('should validate the minimumScheduleInterval if enforce = false', () => { const rule = mockRule(); rule.schedule.interval = '2s'; - const result = validateBaseProperties(rule, config); + const result = validateBaseProperties(rule, config, actionTypeRegistry); expect(result.errors).toStrictEqual({ name: [], 'schedule.interval': [], @@ -62,10 +64,14 @@ describe('rule_errors', () => { it('should validate the minimumScheduleInterval if enforce = true', () => { const rule = mockRule(); rule.schedule.interval = '2s'; - const result = validateBaseProperties(rule, { - isUsingSecurity: true, - minimumScheduleInterval: { value: '1m', enforce: true }, - }); + const result = validateBaseProperties( + rule, + { + isUsingSecurity: true, + minimumScheduleInterval: { value: '1m', enforce: true }, + }, + actionTypeRegistry + ); expect(result.errors).toStrictEqual({ name: [], 'schedule.interval': ['Interval must be at least 1 minute.'], @@ -78,7 +84,7 @@ describe('rule_errors', () => { it('should validate the ruleTypeId', () => { const rule = mockRule(); rule.ruleTypeId = ''; - const result = validateBaseProperties(rule, config); + const result = validateBaseProperties(rule, config, actionTypeRegistry); expect(result.errors).toStrictEqual({ name: [], 'schedule.interval': [], @@ -91,7 +97,7 @@ describe('rule_errors', () => { it('should get an error when consumer is null', () => { const rule = mockRule(); rule.consumer = null as unknown as string; - const result = validateBaseProperties(rule, config); + const result = validateBaseProperties(rule, config, actionTypeRegistry); expect(result.errors).toStrictEqual({ name: [], 'schedule.interval': [], @@ -104,7 +110,7 @@ describe('rule_errors', () => { it('should not get an error when consumer is undefined', () => { const rule = mockRule(); rule.consumer = undefined as unknown as string; - const result = validateBaseProperties(rule, config); + const result = validateBaseProperties(rule, config, actionTypeRegistry); expect(result.errors).toStrictEqual({ name: [], 'schedule.interval': [], @@ -126,7 +132,13 @@ describe('rule_errors', () => { }, }, ]; - const result = validateBaseProperties(rule, config); + const actionType = { + id: 'test', + name: 'Test', + isSystemActionType: false, + } as unknown as ActionTypeModel; + actionTypeRegistry.get.mockReturnValue(actionType); + const result = validateBaseProperties(rule, config, actionTypeRegistry); expect(result.errors).toStrictEqual({ name: [], 'schedule.interval': [], @@ -135,6 +147,35 @@ describe('rule_errors', () => { consumer: [], }); }); + + it('should not throw an error for system actions', () => { + const rule = mockRule(); + + rule.actions = [ + { + id: '1234', + actionTypeId: '.test-system-action', + params: {}, + }, + ]; + + const actionType = { + id: '.test-system-action', + name: 'Test', + isSystemActionType: true, + } as unknown as ActionTypeModel; + + actionTypeRegistry.get.mockReturnValue(actionType); + const result = validateBaseProperties(rule, config, actionTypeRegistry); + + expect(result.errors).toStrictEqual({ + name: [], + 'schedule.interval': [], + ruleTypeId: [], + actionConnectors: [], + consumer: [], + }); + }); }); describe('getRuleErrors()', () => { @@ -150,7 +191,8 @@ describe('rule_errors', () => { }, }), }), - config + config, + actionTypeRegistry ); expect(result).toStrictEqual({ ruleParamsErrors: { field: ['This is wrong'] }, @@ -175,7 +217,6 @@ describe('rule_errors', () => { describe('getRuleActionErrors()', () => { it('should return an array of errors', async () => { - const actionTypeRegistry = actionTypeRegistryMock.create(); actionTypeRegistry.get.mockImplementation((actionTypeId: string) => ({ ...actionTypeRegistryMock.createMockActionTypeModel(), validateParams: jest.fn().mockImplementation(() => ({ diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/rule_form/rule_errors.ts b/x-pack/plugins/triggers_actions_ui/public/application/sections/rule_form/rule_errors.ts index 0b5c511843472..2577474d35691 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/rule_form/rule_errors.ts +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/rule_form/rule_errors.ts @@ -6,24 +6,26 @@ */ import { isObject } from 'lodash'; import { i18n } from '@kbn/i18n'; -import { RuleNotifyWhen } from '@kbn/alerting-plugin/common'; +import { RuleNotifyWhen, SanitizedRuleAction } from '@kbn/alerting-plugin/common'; import { formatDuration, parseDuration } from '@kbn/alerting-plugin/common/parse_duration'; import { RuleTypeModel, Rule, IErrorObject, - RuleAction, ValidationResult, ActionTypeRegistryContract, TriggersActionsUiConfig, + RuleUiAction, } from '../../../types'; import { InitialRule } from './rule_reducer'; export function validateBaseProperties( ruleObject: InitialRule, - config: TriggersActionsUiConfig + config: TriggersActionsUiConfig, + actionTypeRegistry: ActionTypeRegistryContract ): ValidationResult { const validationResult = { errors: {} }; + const errors = { name: new Array(), 'schedule.interval': new Array(), @@ -31,7 +33,9 @@ export function validateBaseProperties( ruleTypeId: new Array(), actionConnectors: new Array(), }; + validationResult.errors = errors; + if (!ruleObject.name) { errors.name.push( i18n.translate('xpack.triggersActionsUI.sections.ruleForm.error.requiredNameText', { @@ -39,6 +43,7 @@ export function validateBaseProperties( }) ); } + if (ruleObject.consumer === null) { errors.consumer.push( i18n.translate('xpack.triggersActionsUI.sections.ruleForm.error.requiredConsumerText', { @@ -46,6 +51,7 @@ export function validateBaseProperties( }) ); } + if (ruleObject.schedule.interval.length < 2) { errors['schedule.interval'].push( i18n.translate('xpack.triggersActionsUI.sections.ruleForm.error.requiredIntervalText', { @@ -68,16 +74,23 @@ export function validateBaseProperties( } const invalidThrottleActions = ruleObject.actions.filter((a) => { - if (!a.frequency?.throttle) return false; - const throttleDuration = parseDuration(a.frequency.throttle); + if (actionTypeRegistry.get(a.actionTypeId).isSystemActionType) return false; + + const defaultAction = a as SanitizedRuleAction; + if (!defaultAction.frequency?.throttle) return false; + + const throttleDuration = parseDuration(defaultAction.frequency.throttle); const intervalDuration = ruleObject.schedule.interval && ruleObject.schedule.interval.length > 1 ? parseDuration(ruleObject.schedule.interval) : 0; + return ( - a.frequency?.notifyWhen === RuleNotifyWhen.THROTTLE && throttleDuration < intervalDuration + defaultAction.frequency?.notifyWhen === RuleNotifyWhen.THROTTLE && + throttleDuration < intervalDuration ); }); + if (invalidThrottleActions.length) { errors['schedule.interval'].push( i18n.translate( @@ -97,9 +110,11 @@ export function validateBaseProperties( }) ); } + const emptyConnectorActions = ruleObject.actions.find( (actionItem) => /^\d+$/.test(actionItem.id) && Object.keys(actionItem.params).length > 0 ); + if (emptyConnectorActions !== undefined) { errors.actionConnectors.push( i18n.translate('xpack.triggersActionsUI.sections.ruleForm.error.requiredActionConnector', { @@ -108,18 +123,23 @@ export function validateBaseProperties( }) ); } + return validationResult; } export function getRuleErrors( rule: Rule, ruleTypeModel: RuleTypeModel | null, - config: TriggersActionsUiConfig + config: TriggersActionsUiConfig, + actionTypeRegistry: ActionTypeRegistryContract ) { const ruleParamsErrors: IErrorObject = ruleTypeModel ? ruleTypeModel.validate(rule.params).errors - : []; - const ruleBaseErrors = validateBaseProperties(rule, config).errors as IErrorObject; + : {}; + + const ruleBaseErrors = validateBaseProperties(rule, config, actionTypeRegistry) + .errors as IErrorObject; + const ruleErrors = { ...ruleParamsErrors, ...ruleBaseErrors, @@ -133,12 +153,12 @@ export function getRuleErrors( } export async function getRuleActionErrors( - actions: RuleAction[], + actions: RuleUiAction[], actionTypeRegistry: ActionTypeRegistryContract ): Promise { return await Promise.all( actions.map( - async (ruleAction: RuleAction) => + async (ruleAction: RuleUiAction) => ( await actionTypeRegistry.get(ruleAction.actionTypeId)?.validateParams(ruleAction.params) ).errors diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/rule_form/rule_form.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/rule_form/rule_form.tsx index 83035607cb725..6319d75c5eaf7 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/rule_form/rule_form.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/rule_form/rule_form.tsx @@ -58,6 +58,7 @@ import { RecoveredActionGroup, isActionGroupDisabledForActionTypeId, RuleActionAlertsFilterProperty, + RuleActionKey, } from '@kbn/alerting-plugin/common'; import { AlertingConnectorFeatureId } from '@kbn/actions-plugin/common'; import { AlertConsumers } from '@kbn/rule-data-utils'; @@ -66,12 +67,12 @@ import { RuleTypeModel, Rule, IErrorObject, - RuleAction, RuleType, RuleTypeRegistryContract, ActionTypeRegistryContract, TriggersActionsUiConfig, RuleCreationValidConsumer, + RuleUiAction, } from '../../../types'; import { getTimeOptions } from '../../../common/lib/get_time_options'; import { ActionForm } from '../action_connector_form'; @@ -360,7 +361,7 @@ export const RuleForm = ({ ); const setActions = useCallback( - (updatedActions: RuleAction[]) => setRuleProperty('actions', updatedActions), + (updatedActions: RuleUiAction[]) => setRuleProperty('actions', updatedActions), [setRuleProperty] ); @@ -372,9 +373,9 @@ export const RuleForm = ({ dispatch({ command: { type: 'setScheduleProperty' }, payload: { key, value } }); }; - const setActionProperty = ( + const setActionProperty = ( key: Key, - value: RuleAction[Key] | null, + value: RuleActionParam | null, index: number ) => { dispatch({ command: { type: 'setRuleActionProperty' }, payload: { key, value, index } }); @@ -632,7 +633,7 @@ export const RuleForm = ({ } // No help text if there is an error - if (errors['schedule.interval'].length > 0) { + if (errors['schedule.interval'].length) { return ''; } @@ -910,7 +911,7 @@ export const RuleForm = ({ rule.ruleTypeId && selectedRuleType ? ( <> - {errors.actionConnectors.length >= 1 ? ( + {!!errors.actionConnectors.length ? ( <> @@ -982,13 +983,13 @@ export const RuleForm = ({ defaultMessage="Name" /> } - isInvalid={errors.name.length > 0 && rule.name !== undefined} + isInvalid={!!errors.name.length && rule.name !== undefined} error={errors.name} > 0 && rule.name !== undefined} + isInvalid={!!errors.name.length && rule.name !== undefined} name="name" data-test-subj="ruleNameInput" value={rule.name || ''} @@ -1118,7 +1119,7 @@ export const RuleForm = ({ - {errors.ruleTypeId.length >= 1 && rule.ruleTypeId !== undefined ? ( + {!!errors.ruleTypeId.length && rule.ruleTypeId !== undefined ? ( <> diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/rule_form/rule_reducer.test.ts b/x-pack/plugins/triggers_actions_ui/public/application/sections/rule_form/rule_reducer.test.ts index 6eadf1fce5ff4..373cb70e0ca36 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/rule_form/rule_reducer.test.ts +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/rule_form/rule_reducer.test.ts @@ -5,10 +5,20 @@ * 2.0. */ -import { ruleReducer } from './rule_reducer'; -import { Rule } from '../../../types'; +import { getRuleReducer } from './rule_reducer'; +import { ActionTypeModel, Rule } from '../../../types'; +import { SanitizedRuleAction } from '@kbn/alerting-plugin/common'; +import { actionTypeRegistryMock } from '../../action_type_registry.mock'; +const actionTypeRegistry = actionTypeRegistryMock.create(); +const actionType = { + id: 'test', + name: 'Test', + isSystemActionType: false, +} as unknown as ActionTypeModel; +actionTypeRegistry.get.mockReturnValue(actionType); describe('rule reducer', () => { + const ruleReducer = getRuleReducer(actionTypeRegistry); let initialRule: Rule; beforeAll(() => { initialRule = { @@ -190,7 +200,7 @@ describe('rule reducer', () => { }, } ); - expect(updatedRule.rule.actions[0].group).toBe('Warning'); + expect((updatedRule.rule.actions[0] as SanitizedRuleAction).group).toBe('Warning'); }); test('if rule action frequency was updated', () => { @@ -212,7 +222,9 @@ describe('rule reducer', () => { }, } ); - expect(updatedRule.rule.actions[0].frequency?.notifyWhen).toBe('onThrottleInterval'); + expect((updatedRule.rule.actions[0] as SanitizedRuleAction).frequency?.notifyWhen).toBe( + 'onThrottleInterval' + ); }); test('if initial alert delay property was updated', () => { diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/rule_form/rule_reducer.ts b/x-pack/plugins/triggers_actions_ui/public/application/sections/rule_form/rule_reducer.ts index 257df764ebc1e..c11dd0edb3e71 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/rule_form/rule_reducer.ts +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/rule_form/rule_reducer.ts @@ -12,10 +12,12 @@ import { RuleActionParam, IntervalSchedule, RuleActionAlertsFilterProperty, + AlertsFilter, AlertDelay, + SanitizedRuleAction, } from '@kbn/alerting-plugin/common'; import { isEmpty } from 'lodash/fp'; -import { Rule, RuleAction } from '../../../types'; +import { ActionTypeRegistryContract, Rule, RuleUiAction } from '../../../types'; import { DEFAULT_FREQUENCY } from '../../../common/constants'; export type InitialRule = Partial & @@ -52,9 +54,9 @@ interface RulePayload { index?: number; } -interface RuleActionPayload { +interface RuleActionPayload { key: Key; - value: RuleAction[Key] | null; + value: RuleUiAction[Key] | null; index?: number; } @@ -111,204 +113,207 @@ export type RuleReducerAction = export type InitialRuleReducer = Reducer<{ rule: InitialRule }, RuleReducerAction>; export type ConcreteRuleReducer = Reducer<{ rule: Rule }, RuleReducerAction>; -export const ruleReducer = ( - state: { rule: RulePhase }, - action: RuleReducerAction -) => { - const { rule } = state; +export const getRuleReducer = + (actionTypeRegistry: ActionTypeRegistryContract) => + (state: { rule: RulePhase }, action: RuleReducerAction) => { + const { rule } = state; - switch (action.command.type) { - case 'setRule': { - const { key, value } = action.payload as Payload<'rule', RulePhase>; - if (key === 'rule') { - return { - ...state, - rule: value, - }; - } else { - return state; - } - } - case 'setProperty': { - const { key, value } = action.payload as RulePayload; - if (isEqual(rule[key], value)) { - return state; - } else { - return { - ...state, - rule: { - ...rule, - [key]: value, - }, - }; + switch (action.command.type) { + case 'setRule': { + const { key, value } = action.payload as Payload<'rule', RulePhase>; + if (key === 'rule') { + return { + ...state, + rule: value, + }; + } else { + return state; + } } - } - case 'setScheduleProperty': { - const { key, value } = action.payload as RuleSchedulePayload; - if (rule.schedule && isEqual(rule.schedule[key], value)) { - return state; - } else { - return { - ...state, - rule: { - ...rule, - schedule: { - ...rule.schedule, + case 'setProperty': { + const { key, value } = action.payload as RulePayload; + if (isEqual(rule[key], value)) { + return state; + } else { + return { + ...state, + rule: { + ...rule, [key]: value, }, - }, - }; + }; + } } - } - case 'setRuleParams': { - const { key, value } = action.payload as Payload>; - if (isEqual(rule.params[key], value)) { - return state; - } else { - return { - ...state, - rule: { - ...rule, - params: { - ...rule.params, - [key]: value, + case 'setScheduleProperty': { + const { key, value } = action.payload as RuleSchedulePayload; + if (rule.schedule && isEqual(rule.schedule[key], value)) { + return state; + } else { + return { + ...state, + rule: { + ...rule, + schedule: { + ...rule.schedule, + [key]: value, + }, }, - }, - }; + }; + } } - } - case 'setRuleActionParams': { - const { key, value, index } = action.payload as Payload< - keyof RuleAction, - SavedObjectAttribute - >; - if ( - index === undefined || - rule.actions[index] == null || - (!!rule.actions[index][key] && isEqual(rule.actions[index][key], value)) - ) { - return state; - } else { - const oldAction = rule.actions.splice(index, 1)[0]; - const updatedAction = { - ...oldAction, - params: { - ...oldAction.params, - [key]: value, - }, - }; - rule.actions.splice(index, 0, updatedAction); - return { - ...state, - rule: { - ...rule, - actions: [...rule.actions], - }, - }; + case 'setRuleParams': { + const { key, value } = action.payload as Payload>; + if (isEqual(rule.params[key], value)) { + return state; + } else { + return { + ...state, + rule: { + ...rule, + params: { + ...rule.params, + [key]: value, + }, + }, + }; + } } - } - case 'setRuleActionFrequency': { - const { key, value, index } = action.payload as Payload< - keyof RuleAction, - SavedObjectAttribute - >; - if ( - index === undefined || - rule.actions[index] == null || - (!!rule.actions[index][key] && isEqual(rule.actions[index][key], value)) - ) { - return state; - } else { - const oldAction = rule.actions.splice(index, 1)[0]; - const updatedAction = { - ...oldAction, - frequency: { - ...(oldAction.frequency ?? DEFAULT_FREQUENCY), - [key]: value, - }, - }; - rule.actions.splice(index, 0, updatedAction); - return { - ...state, - rule: { - ...rule, - actions: [...rule.actions], - }, - }; + case 'setRuleActionParams': { + const { key, value, index } = action.payload as Payload< + keyof RuleUiAction, + SavedObjectAttribute + >; + if ( + index === undefined || + rule.actions[index] == null || + (!!rule.actions[index][key] && isEqual(rule.actions[index][key], value)) + ) { + return state; + } else { + const oldAction = rule.actions.splice(index, 1)[0]; + const updatedAction = { + ...oldAction, + params: { + ...oldAction.params, + [key]: value, + }, + }; + rule.actions.splice(index, 0, updatedAction); + return { + ...state, + rule: { + ...rule, + actions: [...rule.actions], + }, + }; + } } - } - case 'setRuleActionAlertsFilter': { - const { key, value, index } = action.payload as Payload< - keyof RuleAction, - SavedObjectAttribute - >; - if ( - index === undefined || - rule.actions[index] == null || - (!!rule.actions[index][key] && isEqual(rule.actions[index][key], value)) - ) { - return state; - } else { - const oldAction = rule.actions.splice(index, 1)[0]; - const { alertsFilter, ...rest } = oldAction; - const updatedAlertsFilter = { ...alertsFilter }; - - if (value) { - updatedAlertsFilter[key] = value; + case 'setRuleActionFrequency': { + const { key, value, index } = action.payload as Payload< + keyof RuleUiAction, + SavedObjectAttribute + >; + if ( + index === undefined || + rule.actions[index] == null || + (!!rule.actions[index][key] && isEqual(rule.actions[index][key], value)) + ) { + return state; } else { - delete updatedAlertsFilter[key]; + const oldAction = rule.actions.splice(index, 1)[0]; + if (actionTypeRegistry.get(oldAction.actionTypeId).isSystemActionType) { + return state; + } + const oldSanitizedAction = oldAction as SanitizedRuleAction; + const updatedAction = { + ...oldSanitizedAction, + frequency: { + ...(oldSanitizedAction?.frequency ?? DEFAULT_FREQUENCY), + [key]: value, + }, + }; + rule.actions.splice(index, 0, updatedAction); + return { + ...state, + rule: { + ...rule, + actions: [...rule.actions], + }, + }; } + } + case 'setRuleActionAlertsFilter': { + const { key, value, index } = action.payload as Payload< + keyof AlertsFilter, + RuleActionAlertsFilterProperty + >; + if (index === undefined || rule.actions[index] == null) { + return state; + } else { + const oldAction = rule.actions.splice(index, 1)[0]; + if (actionTypeRegistry.get(oldAction.actionTypeId).isSystemActionType) { + return state; + } + const oldSanitizedAction = oldAction as SanitizedRuleAction; + if ( + oldSanitizedAction.alertsFilter && + isEqual(oldSanitizedAction.alertsFilter[key], value) + ) + return state; + + const { alertsFilter, ...rest } = oldSanitizedAction; + const updatedAlertsFilter = { ...alertsFilter, [key]: value }; - const updatedAction = { - ...rest, - ...(!isEmpty(updatedAlertsFilter) ? { alertsFilter: updatedAlertsFilter } : {}), - }; - rule.actions.splice(index, 0, updatedAction); - return { - ...state, - rule: { - ...rule, - actions: [...rule.actions], - }, - }; + const updatedAction = { + ...rest, + ...(!isEmpty(updatedAlertsFilter) ? { alertsFilter: updatedAlertsFilter } : {}), + }; + rule.actions.splice(index, 0, updatedAction); + return { + ...state, + rule: { + ...rule, + actions: [...rule.actions], + }, + }; + } } - } - case 'setRuleActionProperty': { - const { key, value, index } = action.payload as RuleActionPayload; - if (index === undefined || isEqual(rule.actions[index][key], value)) { - return state; - } else { - const oldAction = rule.actions.splice(index, 1)[0]; - const updatedAction = { - ...oldAction, - [key]: value, - }; - rule.actions.splice(index, 0, updatedAction); - return { - ...state, - rule: { - ...rule, - actions: [...rule.actions], - }, - }; + case 'setRuleActionProperty': { + const { key, value, index } = action.payload as RuleActionPayload; + if (index === undefined || isEqual(rule.actions[index][key], value)) { + return state; + } else { + const oldAction = rule.actions.splice(index, 1)[0]; + const updatedAction = { + ...oldAction, + [key]: value, + }; + rule.actions.splice(index, 0, updatedAction); + return { + ...state, + rule: { + ...rule, + actions: [...rule.actions], + }, + }; + } } - } - case 'setAlertDelayProperty': { - const { key, value } = action.payload as Payload; - if (rule.alertDelay && isEqual(rule.alertDelay[key], value)) { - return state; - } else { - return { - ...state, - rule: { - ...rule, - alertDelay: { - ...rule.alertDelay, - [key]: value, + case 'setAlertDelayProperty': { + const { key, value } = action.payload as Payload; + if (rule.alertDelay && isEqual(rule.alertDelay[key], value)) { + return state; + } else { + return { + ...state, + rule: { + ...rule, + alertDelay: { + ...rule.alertDelay, + [key]: value, + }, }, - }, - }; + }; + } } } - } -}; + }; diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/rules_list/components/collapsed_item_actions.test.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/rules_list/components/collapsed_item_actions.test.tsx index 9bcab7c092421..a8c07077ed389 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/rules_list/components/collapsed_item_actions.test.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/rules_list/components/collapsed_item_actions.test.tsx @@ -62,7 +62,12 @@ describe('CollapsedItemActions', () => { consumer: 'rules', schedule: { interval: '5d' }, actions: [ - { id: 'test', actionTypeId: 'the_connector', group: 'rule', params: { message: 'test' } }, + { + id: 'test', + actionTypeId: 'the_connector', + group: 'rule', + params: { message: 'test' }, + }, ], params: { name: 'test rule type name' }, createdBy: null, diff --git a/x-pack/plugins/triggers_actions_ui/public/index.ts b/x-pack/plugins/triggers_actions_ui/public/index.ts index fbbbf04ea8cfe..a23388e24abd8 100644 --- a/x-pack/plugins/triggers_actions_ui/public/index.ts +++ b/x-pack/plugins/triggers_actions_ui/public/index.ts @@ -11,8 +11,8 @@ import type { PluginInitializerContext } from '@kbn/core/server'; import { Plugin } from './plugin'; export type { - RuleAction, Rule, + RuleAction, RuleType, RuleTypeIndex, RuleTypeModel, diff --git a/x-pack/plugins/triggers_actions_ui/public/mocks.ts b/x-pack/plugins/triggers_actions_ui/public/mocks.ts index 48691c15ed62f..1c09a3895f9fd 100644 --- a/x-pack/plugins/triggers_actions_ui/public/mocks.ts +++ b/x-pack/plugins/triggers_actions_ui/public/mocks.ts @@ -5,6 +5,7 @@ * 2.0. */ +import { RuleAction } from '@kbn/alerting-plugin/common'; import { getAlertsTableDefaultAlertActionsLazy } from './common/get_alerts_table_default_row_actions'; import type { TriggersAndActionsUIPublicPluginStart } from './plugin'; @@ -22,6 +23,7 @@ import { RuleTagBadgeProps, RuleEventLogListOptions, RuleEventLogListProps, + RuleUiAction, } from './types'; import { getAlertsTableLazy } from './common/get_alerts_table'; import { getRuleStatusDropdownLazy } from './common/get_rule_status_dropdown'; @@ -58,8 +60,18 @@ function createStartMock(): TriggersAndActionsUIPublicPluginStart { actionTypeRegistry, ruleTypeRegistry, alertsTableConfigurationRegistry, - getActionForm: (props: Omit) => { - return getActionFormLazy({ ...props, actionTypeRegistry, connectorServices }); + getActionForm: ( + props: Omit & { + setActions: (actions: RuleAction[]) => void; + } + ) => { + const { setActions, ...restProps } = props; + return getActionFormLazy({ + ...restProps, + setActions: setActions as (actions: RuleUiAction[]) => void, + actionTypeRegistry, + connectorServices, + }); }, getAddConnectorFlyout: (props: Omit) => { return getAddConnectorFlyoutLazy({ ...props, actionTypeRegistry, connectorServices }); diff --git a/x-pack/plugins/triggers_actions_ui/public/plugin.ts b/x-pack/plugins/triggers_actions_ui/public/plugin.ts index a8c4dd1f71646..174d3816792bf 100644 --- a/x-pack/plugins/triggers_actions_ui/public/plugin.ts +++ b/x-pack/plugins/triggers_actions_ui/public/plugin.ts @@ -30,8 +30,9 @@ import { ExpressionsStart } from '@kbn/expressions-plugin/public'; import { ServerlessPluginStart } from '@kbn/serverless/public'; import { FieldFormatsRegistry } from '@kbn/field-formats-plugin/common'; import { LensPublicStart } from '@kbn/lens-plugin/public'; +import { RuleAction } from '@kbn/alerting-plugin/common'; import { getAlertsTableDefaultAlertActionsLazy } from './common/get_alerts_table_default_row_actions'; -import type { AlertActionsProps } from './types'; +import type { AlertActionsProps, RuleUiAction } from './types'; import type { AlertsSearchBarProps } from './application/sections/alerts_search_bar'; import { TypeRegistry } from './application/type_registry'; @@ -108,7 +109,9 @@ export interface TriggersAndActionsUIPublicPluginStart { ruleTypeRegistry: TypeRegistry>; alertsTableConfigurationRegistry: AlertTableConfigRegistry; getActionForm: ( - props: Omit + props: Omit & { + setActions: (actions: RuleAction[]) => void; + } ) => ReactElement; getAddConnectorFlyout: ( props: Omit @@ -457,9 +460,16 @@ export class Plugin actionTypeRegistry: this.actionTypeRegistry, ruleTypeRegistry: this.ruleTypeRegistry, alertsTableConfigurationRegistry: this.alertsTableConfigurationRegistry, - getActionForm: (props: Omit) => { + getActionForm: ( + props: Omit & { + setActions: (actions: RuleAction[]) => void; + } + ) => { + const { setActions, ...restProps } = props; return getActionFormLazy({ - ...props, + ...restProps, + // TODO remove this cast when every solution is ready to use system actions + setActions: setActions as (actions: RuleUiAction[]) => void, actionTypeRegistry: this.actionTypeRegistry, connectorServices: this.connectorServices!, }); diff --git a/x-pack/plugins/triggers_actions_ui/public/types.ts b/x-pack/plugins/triggers_actions_ui/public/types.ts index 0bbcc5e90c7a9..7533746c87236 100644 --- a/x-pack/plugins/triggers_actions_ui/public/types.ts +++ b/x-pack/plugins/triggers_actions_ui/public/types.ts @@ -42,7 +42,7 @@ import { RuleActionParam, SanitizedRule as AlertingSanitizedRule, ResolvedSanitizedRule, - RuleAction, + RuleSystemAction, RuleTaskState, AlertSummary as RuleSummary, ExecutionDuration, @@ -55,6 +55,7 @@ import { ActionVariable, RuleLastRun, MaintenanceWindow, + SanitizedRuleAction as RuleAction, } from '@kbn/alerting-plugin/common'; import type { BulkOperationError } from '@kbn/alerting-plugin/server'; import { RuleRegistrySearchRequestPagination } from '@kbn/rule-registry-plugin/common'; @@ -104,22 +105,31 @@ export { OPTIONAL_ACTION_VARIABLES, } from '@kbn/triggers-actions-ui-types'; +type RuleUiAction = RuleAction | RuleSystemAction; + // In Triggers and Actions we treat all `Alert`s as `SanitizedRule` // so the `Params` is a black-box of Record type SanitizedRule = Omit< AlertingSanitizedRule, - 'alertTypeId' + 'alertTypeId' | 'actions' | 'systemActions' > & { ruleTypeId: AlertingSanitizedRule['alertTypeId']; + actions: RuleUiAction[]; }; type Rule = SanitizedRule; -type ResolvedRule = Omit, 'alertTypeId'> & { +type ResolvedRule = Omit< + ResolvedSanitizedRule, + 'alertTypeId' | 'actions' | 'systemActions' +> & { ruleTypeId: ResolvedSanitizedRule['alertTypeId']; + actions: RuleUiAction[]; }; export type { Rule, RuleAction, + RuleSystemAction, + RuleUiAction, RuleTaskState, RuleSummary, ExecutionDuration, @@ -287,6 +297,7 @@ export interface ActionTypeModel ActionParams | {}; hideInUi?: boolean; modalWidth?: number; + isSystemActionType?: boolean; } export interface GenericValidationResult { diff --git a/x-pack/test/alerting_api_integration/common/config.ts b/x-pack/test/alerting_api_integration/common/config.ts index cefba18db5b9b..30a8b86b1e630 100644 --- a/x-pack/test/alerting_api_integration/common/config.ts +++ b/x-pack/test/alerting_api_integration/common/config.ts @@ -72,6 +72,7 @@ const enabledActionTypes = [ 'test.capped', 'test.system-action', 'test.system-action-kibana-privileges', + 'test.system-action-connector-adapter', ]; export function createTestConfig(name: string, options: CreateTestConfigOptions) { diff --git a/x-pack/test/alerting_api_integration/common/lib/alert_utils.ts b/x-pack/test/alerting_api_integration/common/lib/alert_utils.ts index 6cf75ee6ad635..90653108aa500 100644 --- a/x-pack/test/alerting_api_integration/common/lib/alert_utils.ts +++ b/x-pack/test/alerting_api_integration/common/lib/alert_utils.ts @@ -354,6 +354,36 @@ export class AlertUtils { return response; } + public async createAlwaysFiringSystemAction({ + objectRemover, + overwrites = {}, + reference, + }: CreateAlertWithActionOpts) { + const objRemover = objectRemover || this.objectRemover; + + if (!objRemover) { + throw new Error('objectRemover is required'); + } + + let request = this.supertestWithoutAuth + .post(`${getUrlPrefix(this.space.id)}/api/alerting/rule`) + .set('kbn-xsrf', 'foo'); + + if (this.user) { + request = request.auth(this.user.username, this.user.password); + } + + const rule = getAlwaysFiringRuleWithSystemAction(reference); + + const response = await request.send({ ...rule, ...overwrites }); + + if (response.statusCode === 200) { + objRemover.add(this.space.id, response.body.id, 'rule', 'alerting'); + } + + return response; + } + public async updateAlwaysFiringAction({ alertId, actionId, @@ -658,3 +688,32 @@ function getPatternFiringRuleWithSummaryAction( ], }; } + +function getAlwaysFiringRuleWithSystemAction(reference: string) { + return { + enabled: true, + name: 'abc', + schedule: { interval: '1m' }, + tags: ['tag-A', 'tag-B'], + rule_type_id: 'test.always-firing-alert-as-data', + consumer: 'alertsFixture', + params: { + index: ES_TEST_INDEX_NAME, + reference, + }, + actions: [ + { + id: 'system-connector-test.system-action-connector-adapter', + /** + * The injected param required by the action will be set by the corresponding + * connector adapter. Setting it here it will lead to a 400 error by the + * rules API as only the connector adapter can set the injected property. + * + * Adapter: x-pack/test/alerting_api_integration/common/plugins/alerts/server/connector_adapters.ts + * Connector type: x-pack/test/alerting_api_integration/common/plugins/alerts/server/action_types.ts + */ + params: { myParam: 'param from rule action', index: ES_TEST_INDEX_NAME, reference }, + }, + ], + }; +} diff --git a/x-pack/test/alerting_api_integration/common/plugins/alerts/server/action_types.ts b/x-pack/test/alerting_api_integration/common/plugins/alerts/server/action_types.ts index 37d66450962b4..27df735ad73dc 100644 --- a/x-pack/test/alerting_api_integration/common/plugins/alerts/server/action_types.ts +++ b/x-pack/test/alerting_api_integration/common/plugins/alerts/server/action_types.ts @@ -82,6 +82,7 @@ export function defineActionTypes( */ actions.registerType(getSystemActionType()); actions.registerType(getSystemActionTypeWithKibanaPrivileges()); + actions.registerType(getSystemActionTypeWithConnectorAdapter()); /** Sub action framework */ @@ -486,3 +487,67 @@ function getSystemActionTypeWithKibanaPrivileges() { return result; } + +function getSystemActionTypeWithConnectorAdapter() { + const result: ActionType< + {}, + {}, + { myParam: string; injected: string; index?: string; reference?: string } + > = { + id: 'test.system-action-connector-adapter', + name: 'Test system action with a connector adapter set', + minimumLicenseRequired: 'platinum', + supportedFeatureIds: ['alerting'], + validate: { + params: { + /** + * The injected params will be set by the + * connector adapter while executing the action. + * + * Adapter: x-pack/test/alerting_api_integration/common/plugins/alerts/server/connector_adapters.ts + */ + schema: schema.object({ + myParam: schema.string(), + injected: schema.string(), + index: schema.maybe(schema.string()), + reference: schema.maybe(schema.string()), + }), + }, + + config: { + schema: schema.any(), + }, + secrets: { + schema: schema.any(), + }, + }, + isSystemActionType: true, + /** + * The executor writes a doc to the + * testing index. The test uses the doc + * to verify that the action is executed + * correctly + */ + async executor({ params, services, actionId }) { + const { index, reference } = params; + + if (index == null || reference == null) { + return { status: 'ok', actionId }; + } + + await services.scopedClusterClient.index({ + index, + refresh: 'wait_for', + body: { + params, + reference, + source: 'action:test.system-action-connector-adapter', + }, + }); + + return { status: 'ok', actionId }; + }, + }; + + return result; +} diff --git a/x-pack/test/alerting_api_integration/common/plugins/alerts/server/connector_adapters.ts b/x-pack/test/alerting_api_integration/common/plugins/alerts/server/connector_adapters.ts new file mode 100644 index 0000000000000..41526e0949de3 --- /dev/null +++ b/x-pack/test/alerting_api_integration/common/plugins/alerts/server/connector_adapters.ts @@ -0,0 +1,37 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { ConnectorAdapter } from '@kbn/alerting-plugin/server'; +import { CoreSetup } from '@kbn/core/server'; +import { schema } from '@kbn/config-schema'; +import { FixtureStartDeps, FixtureSetupDeps } from './plugin'; + +export function defineConnectorAdapters( + core: CoreSetup, + { alerting }: Pick +) { + const systemActionConnectorAdapter: ConnectorAdapter = { + connectorTypeId: 'test.system-action-connector-adapter', + ruleActionParamsSchema: schema.object({ + myParam: schema.string(), + index: schema.maybe(schema.string()), + reference: schema.maybe(schema.string()), + }), + /** + * The connector adapter will inject a new param property which is required + * by the action. The injected value cannot be set in the actions of the rule + * as the schema validation will thrown an error. Only through the connector + * adapter this value can be set. The tests are using the connector adapter test + * that the new property is injected correctly + */ + buildActionParams: ({ alerts, rule, params, spaceId, ruleUrl }) => { + return { ...params, injected: 'param from connector adapter' }; + }, + }; + + alerting.registerConnectorAdapter(systemActionConnectorAdapter); +} diff --git a/x-pack/test/alerting_api_integration/common/plugins/alerts/server/plugin.ts b/x-pack/test/alerting_api_integration/common/plugins/alerts/server/plugin.ts index b315c2f884761..dffb9df550f3a 100644 --- a/x-pack/test/alerting_api_integration/common/plugins/alerts/server/plugin.ts +++ b/x-pack/test/alerting_api_integration/common/plugins/alerts/server/plugin.ts @@ -28,6 +28,7 @@ import { RULE_SAVED_OBJECT_TYPE } from '@kbn/alerting-plugin/server'; import { defineRoutes } from './routes'; import { defineActionTypes } from './action_types'; import { defineAlertTypes } from './alert_types'; +import { defineConnectorAdapters } from './connector_adapters'; export interface FixtureSetupDeps { features: FeaturesPluginSetup; @@ -163,6 +164,7 @@ export class FixturePlugin implements Plugin, + res: KibanaResponseFactory + ): Promise> => { + const [_, { actions }] = await core.getStartServices(); + + const actionsClient = await actions.getActionsClientWithRequest(req); + + try { + return res.ok({ + body: await actionsClient.execute({ + actionId: req.params.id, + params: req.body.params, + source: { + type: ActionExecutionSourceType.HTTP_REQUEST, + source: req, + }, + relatedSavedObjects: [], + }), + }); + } catch (err) { + if (err.isBoom && err.output.statusCode === 403) { + return res.forbidden({ body: err }); + } + + throw err; + } + } + ); } diff --git a/x-pack/test/alerting_api_integration/observability/synthetics_rule.ts b/x-pack/test/alerting_api_integration/observability/synthetics_rule.ts index 3e836987939b3..dcc227761d581 100644 --- a/x-pack/test/alerting_api_integration/observability/synthetics_rule.ts +++ b/x-pack/test/alerting_api_integration/observability/synthetics_rule.ts @@ -122,8 +122,10 @@ const compareRules = (rule1: SanitizedRule, rule2: SanitizedRule) => { const getActionById = (rule: SanitizedRule, id: string) => { const actions = rule.actions.filter((action) => action.id === id); - const recoveredAction = actions.find((action) => action.group === 'recovered'); - const firingAction = actions.find((action) => action.group !== 'recovered'); + const recoveredAction = actions.find( + (action) => 'group' in action && action.group === 'recovered' + ); + const firingAction = actions.find((action) => 'group' in action && action.group !== 'recovered'); return { recoveredAction: omit(recoveredAction, ['uuid']), firingAction: omit(firingAction, ['uuid']), diff --git a/x-pack/test/alerting_api_integration/security_and_spaces/group1/tests/alerting/find.ts b/x-pack/test/alerting_api_integration/security_and_spaces/group1/tests/alerting/find.ts index 295350e6dc18e..22c2f9ce6489e 100644 --- a/x-pack/test/alerting_api_integration/security_and_spaces/group1/tests/alerting/find.ts +++ b/x-pack/test/alerting_api_integration/security_and_spaces/group1/tests/alerting/find.ts @@ -9,7 +9,7 @@ import expect from '@kbn/expect'; import { SuperTest, Test } from 'supertest'; import { chunk, omit } from 'lodash'; import { v4 as uuidv4 } from 'uuid'; -import { UserAtSpaceScenarios } from '../../../scenarios'; +import { SuperuserAtSpace1, UserAtSpaceScenarios } from '../../../scenarios'; import { getUrlPrefix, getTestRuleData, ObjectRemover } from '../../../../common/lib'; import { FtrProviderContext } from '../../../../common/ftr_provider_context'; @@ -573,6 +573,71 @@ const findTestUtils = ( }); } }); + + describe('Actions', () => { + const { user, space } = SuperuserAtSpace1; + + it('should return the actions correctly', async () => { + const { body: createdAction } = await supertest + .post(`${getUrlPrefix(space.id)}/api/actions/connector`) + .set('kbn-xsrf', 'foo') + .send({ + name: 'MY action', + connector_type_id: 'test.noop', + config: {}, + secrets: {}, + }) + .expect(200); + + const { body: createdRule1 } = await supertest + .post(`${getUrlPrefix(space.id)}/api/alerting/rule`) + .set('kbn-xsrf', 'foo') + .send( + getTestRuleData({ + enabled: true, + actions: [ + { + id: createdAction.id, + group: 'default', + params: {}, + }, + { + id: 'system-connector-test.system-action', + params: {}, + }, + ], + }) + ) + .expect(200); + + objectRemover.add(space.id, createdRule1.id, 'rule', 'alerting'); + + const response = await supertestWithoutAuth + .get(`${getUrlPrefix(space.id)}/api/alerting/rules/_find`) + .set('kbn-xsrf', 'foo') + .auth(user.username, user.password); + + const action = response.body.data[0].actions[0]; + const systemAction = response.body.data[0].actions[1]; + const { uuid, ...restAction } = action; + const { uuid: systemActionUuid, ...restSystemAction } = systemAction; + + expect([restAction, restSystemAction]).to.eql([ + { + id: createdAction.id, + connector_type_id: 'test.noop', + group: 'default', + params: {}, + }, + { + id: 'system-connector-test.system-action', + connector_type_id: 'test.system-action', + params: {}, + }, + , + ]); + }); + }); }; // eslint-disable-next-line import/no-default-export diff --git a/x-pack/test/alerting_api_integration/security_and_spaces/group1/tests/alerting/get.ts b/x-pack/test/alerting_api_integration/security_and_spaces/group1/tests/alerting/get.ts index edacff0fbfd13..6d245ed28cc00 100644 --- a/x-pack/test/alerting_api_integration/security_and_spaces/group1/tests/alerting/get.ts +++ b/x-pack/test/alerting_api_integration/security_and_spaces/group1/tests/alerting/get.ts @@ -7,7 +7,7 @@ import expect from '@kbn/expect'; import { SuperTest, Test } from 'supertest'; -import { UserAtSpaceScenarios } from '../../../scenarios'; +import { SuperuserAtSpace1, UserAtSpaceScenarios } from '../../../scenarios'; import { getUrlPrefix, getTestRuleData, @@ -315,6 +315,71 @@ const getTestUtils = ( }); } }); + + describe('Actions', () => { + const { user, space } = SuperuserAtSpace1; + + it('should return the actions correctly', async () => { + const { body: createdAction } = await supertest + .post(`${getUrlPrefix(space.id)}/api/actions/connector`) + .set('kbn-xsrf', 'foo') + .send({ + name: 'MY action', + connector_type_id: 'test.noop', + config: {}, + secrets: {}, + }) + .expect(200); + + const { body: createdRule } = await supertest + .post(`${getUrlPrefix(space.id)}/api/alerting/rule`) + .set('kbn-xsrf', 'foo') + .send( + getTestRuleData({ + enabled: true, + actions: [ + { + id: createdAction.id, + group: 'default', + params: {}, + }, + { + id: 'system-connector-test.system-action', + params: {}, + }, + ], + }) + ) + .expect(200); + + objectRemover.add(space.id, createdRule.id, 'rule', 'alerting'); + + const response = await supertestWithoutAuth + .get(`${getUrlPrefix(space.id)}/api/alerting/rule/${createdRule.id}`) + .set('kbn-xsrf', 'foo') + .auth(user.username, user.password); + + const action = response.body.actions[0]; + const systemAction = response.body.actions[1]; + const { uuid, ...restAction } = action; + const { uuid: systemActionUuid, ...restSystemAction } = systemAction; + + expect([restAction, restSystemAction]).to.eql([ + { + id: createdAction.id, + connector_type_id: 'test.noop', + group: 'default', + params: {}, + }, + { + id: 'system-connector-test.system-action', + connector_type_id: 'test.system-action', + params: {}, + }, + , + ]); + }); + }); }; // eslint-disable-next-line import/no-default-export diff --git a/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/connector_types/bedrock.ts b/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/connector_types/bedrock.ts index a3c97f6b000f5..e0f737d3c6e53 100644 --- a/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/connector_types/bedrock.ts +++ b/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/connector_types/bedrock.ts @@ -447,7 +447,7 @@ export default function bedrockTest({ getService }: FtrProviderContext) { message: 'Hello world', isEnabledKnowledgeBase: false, isEnabledRAGAlerts: false, - replacements: [], + replacements: {}, }) .pipe(passThrough); const responseBuffer: Uint8Array[] = []; diff --git a/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/connector_types_system.ts b/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/connector_types_system.ts new file mode 100644 index 0000000000000..f9ea6cf44ddba --- /dev/null +++ b/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/connector_types_system.ts @@ -0,0 +1,92 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import expect from '@kbn/expect'; +import { UserAtSpaceScenarios } from '../../../scenarios'; +import { getUrlPrefix } from '../../../../common/lib/space_test_utils'; +import { FtrProviderContext } from '../../../../common/ftr_provider_context'; + +// eslint-disable-next-line import/no-default-export +export default function listActionTypesTests({ getService }: FtrProviderContext) { + const supertestWithoutAuth = getService('supertestWithoutAuth'); + + describe('connector_types', () => { + for (const scenario of UserAtSpaceScenarios) { + const { user, space } = scenario; + describe(scenario.id, () => { + it('should return 200 with list of action types containing defaults', async () => { + const response = await supertestWithoutAuth + .get(`${getUrlPrefix(space.id)}/internal/actions/connector_types`) + .auth(user.username, user.password); + + function createActionTypeMatcher(id: string, name: string) { + return (actionType: { id: string; name: string }) => { + return actionType.id === id && actionType.name === name; + }; + } + + expect(response.statusCode).to.eql(200); + switch (scenario.id) { + case 'no_kibana_privileges at space1': + case 'space_1_all_alerts_none_actions at space1': + case 'space_1_all at space2': + case 'global_read at space1': + case 'superuser at space1': + case 'space_1_all at space1': + case 'space_1_all_with_restricted_fixture at space1': + expect(response.statusCode).to.eql(200); + // Check for values explicitly in order to avoid this test failing each time plugins register + // a new action type + expect( + response.body.some( + createActionTypeMatcher('test.index-record', 'Test: Index Record') + ) + ).to.be(true); + break; + default: + throw new Error(`Scenario untested: ${JSON.stringify(scenario)}`); + } + }); + + it('should return 200 with list of action types containing system connectors', async () => { + const response = await supertestWithoutAuth + .get(`${getUrlPrefix(space.id)}/internal/actions/connector_types`) + .auth(user.username, user.password); + + function createActionTypeMatcher(id: string, name: string) { + return (actionType: { id: string; name: string }) => { + return actionType.id === id && actionType.name === name; + }; + } + + expect(response.statusCode).to.eql(200); + switch (scenario.id) { + case 'no_kibana_privileges at space1': + case 'space_1_all_alerts_none_actions at space1': + case 'space_1_all at space2': + case 'global_read at space1': + case 'superuser at space1': + case 'space_1_all at space1': + case 'space_1_all_with_restricted_fixture at space1': + expect(response.statusCode).to.eql(200); + // Check for values explicitly in order to avoid this test failing each time plugins register + // a new action type + expect( + response.body.some( + createActionTypeMatcher('test.system-action', 'Test system action') + ) + ).to.be(true); + + break; + default: + throw new Error(`Scenario untested: ${JSON.stringify(scenario)}`); + } + }); + }); + } + }); +} diff --git a/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/execute.ts b/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/execute.ts index d12bc59aca4df..448296a4ae00c 100644 --- a/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/execute.ts +++ b/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/execute.ts @@ -511,8 +511,14 @@ export default function ({ getService }: FtrProviderContext) { const name = 'System action: test.system-action-kibana-privileges'; const reference = `actions-enqueue-${scenario.id}:${space.id}:${connectorId}`; + /** + * The test are using a test endpoint that calls the actions client. + * The route is defined here x-pack/test/alerting_api_integration/common/plugins/alerts/server/routes.ts. + * The public execute API does not allows the execution of system actions. We use the + * test route to test the execution of system actions + */ const response = await supertestWithoutAuth - .post(`${getUrlPrefix(space.id)}/api/actions/connector/${connectorId}/_execute`) + .post(`${getUrlPrefix(space.id)}/api/alerts_fixture/${connectorId}/_execute_connector`) .auth(user.username, user.password) .set('kbn-xsrf', 'foo') .send({ @@ -536,7 +542,7 @@ export default function ({ getService }: FtrProviderContext) { expect(response.body).to.eql({ statusCode: 403, error: 'Forbidden', - message: 'Unauthorized to execute actions', + message: 'Unauthorized to execute a "test.system-action-kibana-privileges" action', }); break; /** diff --git a/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/get_all_system.ts b/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/get_all_system.ts new file mode 100644 index 0000000000000..2811c7e2d4ce2 --- /dev/null +++ b/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/get_all_system.ts @@ -0,0 +1,590 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import expect from '@kbn/expect'; +import { RULE_SAVED_OBJECT_TYPE } from '@kbn/alerting-plugin/server'; +import { SuperuserAtSpace1, UserAtSpaceScenarios } from '../../../scenarios'; +import { getUrlPrefix, getTestRuleData, ObjectRemover } from '../../../../common/lib'; +import { FtrProviderContext } from '../../../../common/ftr_provider_context'; + +// eslint-disable-next-line import/no-default-export +export default function getAllActionTests({ getService }: FtrProviderContext) { + const supertest = getService('supertest'); + const supertestWithoutAuth = getService('supertestWithoutAuth'); + + describe('getAllSystem', () => { + const objectRemover = new ObjectRemover(supertest); + + afterEach(() => objectRemover.removeAll()); + + for (const scenario of UserAtSpaceScenarios) { + const { user, space } = scenario; + describe(scenario.id, () => { + it('should handle get all action request appropriately', async () => { + const { body: createdAction } = await supertest + .post(`${getUrlPrefix(space.id)}/api/actions/connector`) + .set('kbn-xsrf', 'foo') + .send({ + name: 'My action', + connector_type_id: 'test.index-record', + config: { + unencrypted: `This value shouldn't get encrypted`, + }, + secrets: { + encrypted: 'This value should be encrypted', + }, + }) + .expect(200); + objectRemover.add(space.id, createdAction.id, 'action', 'actions'); + + const response = await supertestWithoutAuth + .get(`${getUrlPrefix(space.id)}/internal/actions/connectors`) + .auth(user.username, user.password); + + switch (scenario.id) { + case 'no_kibana_privileges at space1': + case 'space_1_all_alerts_none_actions at space1': + case 'space_1_all at space2': + expect(response.statusCode).to.eql(403); + expect(response.body).to.eql({ + statusCode: 403, + error: 'Forbidden', + message: 'Unauthorized to get actions', + }); + break; + case 'global_read at space1': + case 'superuser at space1': + case 'space_1_all at space1': + case 'space_1_all_with_restricted_fixture at space1': + expect(response.statusCode).to.eql(200); + + // the custom ssl connectors have dynamic ports, so remove them before + // comparing to what we expect + const nonCustomSslConnectors = response.body.filter( + (conn: { id: string }) => !conn.id.startsWith('custom.ssl.') + ); + expect(nonCustomSslConnectors).to.eql([ + { + id: createdAction.id, + is_preconfigured: false, + is_system_action: false, + is_deprecated: false, + name: 'My action', + connector_type_id: 'test.index-record', + is_missing_secrets: false, + config: { + unencrypted: `This value shouldn't get encrypted`, + }, + referenced_by_count: 0, + }, + { + connector_type_id: '.email', + id: 'notification-email', + is_deprecated: false, + is_system_action: false, + is_preconfigured: true, + name: 'Notification Email Connector', + referenced_by_count: 0, + }, + { + id: 'preconfigured-es-index-action', + is_preconfigured: true, + is_system_action: false, + is_deprecated: false, + connector_type_id: '.index', + name: 'preconfigured_es_index_action', + referenced_by_count: 0, + }, + { + connector_type_id: '.servicenow', + id: 'my-deprecated-servicenow', + is_preconfigured: true, + is_system_action: false, + is_deprecated: true, + name: 'ServiceNow#xyz', + referenced_by_count: 0, + }, + { + connector_type_id: '.servicenow', + id: 'my-deprecated-servicenow-default', + is_preconfigured: true, + is_system_action: false, + is_deprecated: true, + name: 'ServiceNow#xyz', + referenced_by_count: 0, + }, + { + id: 'my-slack1', + is_preconfigured: true, + is_system_action: false, + is_deprecated: false, + connector_type_id: '.slack', + name: 'Slack#xyz', + referenced_by_count: 0, + }, + { + connector_type_id: 'test.system-action', + id: 'system-connector-test.system-action', + is_deprecated: false, + is_preconfigured: false, + is_system_action: true, + name: 'System action: test.system-action', + referenced_by_count: 0, + }, + { + connector_type_id: 'test.system-action-connector-adapter', + id: 'system-connector-test.system-action-connector-adapter', + is_deprecated: false, + is_preconfigured: false, + is_system_action: true, + name: 'System action: test.system-action-connector-adapter', + referenced_by_count: 0, + }, + { + connector_type_id: 'test.system-action-kibana-privileges', + id: 'system-connector-test.system-action-kibana-privileges', + is_deprecated: false, + is_preconfigured: false, + is_system_action: true, + name: 'System action: test.system-action-kibana-privileges', + referenced_by_count: 0, + }, + { + id: 'custom-system-abc-connector', + is_preconfigured: true, + is_system_action: false, + is_deprecated: false, + connector_type_id: 'system-abc-action-type', + name: 'SystemABC', + referenced_by_count: 0, + }, + { + id: 'preconfigured.test.index-record', + is_preconfigured: true, + is_system_action: false, + is_deprecated: false, + connector_type_id: 'test.index-record', + name: 'Test:_Preconfigured_Index_Record', + referenced_by_count: 0, + }, + { + id: 'my-test-email', + is_preconfigured: true, + is_system_action: false, + is_deprecated: false, + connector_type_id: '.email', + name: 'TestEmail#xyz', + referenced_by_count: 0, + }, + ]); + break; + default: + throw new Error(`Scenario untested: ${JSON.stringify(scenario)}`); + } + }); + + it('should handle get all request appropriately with proper referenced_by_count', async () => { + const { body: createdAction } = await supertest + .post(`${getUrlPrefix(space.id)}/api/actions/connector`) + .set('kbn-xsrf', 'foo') + .send({ + name: 'My action', + connector_type_id: 'test.index-record', + config: { + unencrypted: `This value shouldn't get encrypted`, + }, + secrets: { + encrypted: 'This value should be encrypted', + }, + }) + .expect(200); + objectRemover.add(space.id, createdAction.id, 'action', 'actions'); + + const { body: createdAlert } = await supertest + .post(`${getUrlPrefix(space.id)}/api/alerting/rule`) + .set('kbn-xsrf', 'foo') + .send( + getTestRuleData({ + actions: [ + { + group: 'default', + id: createdAction.id, + params: {}, + }, + { + group: 'default', + id: 'my-slack1', + params: { + message: 'test', + }, + }, + ], + }) + ) + .expect(200); + objectRemover.add(space.id, createdAlert.id, RULE_SAVED_OBJECT_TYPE, 'alerts'); + + const response = await supertestWithoutAuth + .get(`${getUrlPrefix(space.id)}/internal/actions/connectors`) + .auth(user.username, user.password); + + switch (scenario.id) { + case 'no_kibana_privileges at space1': + case 'space_1_all_alerts_none_actions at space1': + case 'space_1_all at space2': + expect(response.statusCode).to.eql(403); + expect(response.body).to.eql({ + statusCode: 403, + error: 'Forbidden', + message: 'Unauthorized to get actions', + }); + break; + case 'global_read at space1': + case 'superuser at space1': + case 'space_1_all at space1': + case 'space_1_all_with_restricted_fixture at space1': + expect(response.statusCode).to.eql(200); + + // the custom ssl connectors have dynamic ports, so remove them before + // comparing to what we expect + const nonCustomSslConnectors = response.body.filter( + (conn: { id: string }) => !conn.id.startsWith('custom.ssl.') + ); + expect(nonCustomSslConnectors).to.eql([ + { + id: createdAction.id, + is_preconfigured: false, + is_system_action: false, + is_deprecated: false, + name: 'My action', + connector_type_id: 'test.index-record', + is_missing_secrets: false, + config: { + unencrypted: `This value shouldn't get encrypted`, + }, + referenced_by_count: 1, + }, + { + connector_type_id: '.email', + id: 'notification-email', + is_deprecated: false, + is_preconfigured: true, + is_system_action: false, + name: 'Notification Email Connector', + referenced_by_count: 0, + }, + { + id: 'preconfigured-es-index-action', + is_preconfigured: true, + is_system_action: false, + is_deprecated: false, + connector_type_id: '.index', + name: 'preconfigured_es_index_action', + referenced_by_count: 0, + }, + { + connector_type_id: '.servicenow', + id: 'my-deprecated-servicenow', + is_deprecated: true, + is_preconfigured: true, + is_system_action: false, + name: 'ServiceNow#xyz', + referenced_by_count: 0, + }, + { + connector_type_id: '.servicenow', + id: 'my-deprecated-servicenow-default', + is_preconfigured: true, + is_system_action: false, + is_deprecated: true, + name: 'ServiceNow#xyz', + referenced_by_count: 0, + }, + { + id: 'my-slack1', + is_preconfigured: true, + is_system_action: false, + is_deprecated: false, + connector_type_id: '.slack', + name: 'Slack#xyz', + referenced_by_count: 0, + }, + { + connector_type_id: 'test.system-action', + id: 'system-connector-test.system-action', + is_deprecated: false, + is_preconfigured: false, + is_system_action: true, + name: 'System action: test.system-action', + referenced_by_count: 0, + }, + { + connector_type_id: 'test.system-action-connector-adapter', + id: 'system-connector-test.system-action-connector-adapter', + is_deprecated: false, + is_preconfigured: false, + is_system_action: true, + name: 'System action: test.system-action-connector-adapter', + referenced_by_count: 0, + }, + { + connector_type_id: 'test.system-action-kibana-privileges', + id: 'system-connector-test.system-action-kibana-privileges', + is_deprecated: false, + is_preconfigured: false, + is_system_action: true, + name: 'System action: test.system-action-kibana-privileges', + referenced_by_count: 0, + }, + { + id: 'custom-system-abc-connector', + is_preconfigured: true, + is_system_action: false, + is_deprecated: false, + connector_type_id: 'system-abc-action-type', + name: 'SystemABC', + referenced_by_count: 0, + }, + { + id: 'preconfigured.test.index-record', + is_preconfigured: true, + is_system_action: false, + is_deprecated: false, + connector_type_id: 'test.index-record', + name: 'Test:_Preconfigured_Index_Record', + referenced_by_count: 0, + }, + { + id: 'my-test-email', + is_preconfigured: true, + is_system_action: false, + is_deprecated: false, + connector_type_id: '.email', + name: 'TestEmail#xyz', + referenced_by_count: 0, + }, + ]); + break; + default: + throw new Error(`Scenario untested: ${JSON.stringify(scenario)}`); + } + }); + + it(`shouldn't get actions from another space`, async () => { + const { body: createdAction } = await supertest + .post(`${getUrlPrefix(space.id)}/api/actions/connector`) + .set('kbn-xsrf', 'foo') + .send({ + name: 'My action', + connector_type_id: 'test.index-record', + config: { + unencrypted: `This value shouldn't get encrypted`, + }, + secrets: { + encrypted: 'This value should be encrypted', + }, + }) + .expect(200); + objectRemover.add(space.id, createdAction.id, 'action', 'actions'); + + const response = await supertestWithoutAuth + .get(`${getUrlPrefix('other')}/internal/actions/connectors`) + .auth(user.username, user.password); + + switch (scenario.id) { + case 'no_kibana_privileges at space1': + case 'space_1_all_alerts_none_actions at space1': + case 'space_1_all at space2': + case 'space_1_all at space1': + case 'space_1_all_with_restricted_fixture at space1': + expect(response.statusCode).to.eql(403); + expect(response.body).to.eql({ + statusCode: 403, + error: 'Forbidden', + message: 'Unauthorized to get actions', + }); + break; + case 'global_read at space1': + case 'superuser at space1': + expect(response.statusCode).to.eql(200); + + // the custom ssl connectors have dynamic ports, so remove them before + // comparing to what we expect + const nonCustomSslConnectors = response.body.filter( + (conn: { id: string }) => !conn.id.startsWith('custom.ssl.') + ); + expect(nonCustomSslConnectors).to.eql([ + { + connector_type_id: '.email', + id: 'notification-email', + is_deprecated: false, + is_preconfigured: true, + is_system_action: false, + name: 'Notification Email Connector', + referenced_by_count: 0, + }, + { + id: 'preconfigured-es-index-action', + is_preconfigured: true, + is_system_action: false, + is_deprecated: false, + connector_type_id: '.index', + name: 'preconfigured_es_index_action', + referenced_by_count: 0, + }, + { + connector_type_id: '.servicenow', + id: 'my-deprecated-servicenow', + is_preconfigured: true, + is_system_action: false, + is_deprecated: true, + name: 'ServiceNow#xyz', + referenced_by_count: 0, + }, + { + connector_type_id: '.servicenow', + id: 'my-deprecated-servicenow-default', + is_preconfigured: true, + is_system_action: false, + is_deprecated: true, + name: 'ServiceNow#xyz', + referenced_by_count: 0, + }, + { + id: 'my-slack1', + is_preconfigured: true, + is_system_action: false, + is_deprecated: false, + connector_type_id: '.slack', + name: 'Slack#xyz', + referenced_by_count: 0, + }, + { + connector_type_id: 'test.system-action', + id: 'system-connector-test.system-action', + is_deprecated: false, + is_preconfigured: false, + is_system_action: true, + name: 'System action: test.system-action', + referenced_by_count: 0, + }, + { + connector_type_id: 'test.system-action-connector-adapter', + id: 'system-connector-test.system-action-connector-adapter', + is_deprecated: false, + is_preconfigured: false, + is_system_action: true, + name: 'System action: test.system-action-connector-adapter', + referenced_by_count: 0, + }, + { + connector_type_id: 'test.system-action-kibana-privileges', + id: 'system-connector-test.system-action-kibana-privileges', + is_deprecated: false, + is_preconfigured: false, + is_system_action: true, + name: 'System action: test.system-action-kibana-privileges', + referenced_by_count: 0, + }, + { + id: 'custom-system-abc-connector', + is_preconfigured: true, + is_system_action: false, + is_deprecated: false, + connector_type_id: 'system-abc-action-type', + name: 'SystemABC', + referenced_by_count: 0, + }, + { + id: 'preconfigured.test.index-record', + is_preconfigured: true, + is_system_action: false, + is_deprecated: false, + connector_type_id: 'test.index-record', + name: 'Test:_Preconfigured_Index_Record', + referenced_by_count: 0, + }, + { + id: 'my-test-email', + is_preconfigured: true, + is_system_action: false, + is_deprecated: false, + connector_type_id: '.email', + name: 'TestEmail#xyz', + referenced_by_count: 0, + }, + ]); + break; + default: + throw new Error(`Scenario untested: ${JSON.stringify(scenario)}`); + } + }); + }); + } + + describe('References', () => { + const systemAction = { + id: 'system-connector-test.system-action', + params: {}, + }; + + it('calculates the references correctly', async () => { + const { user, space } = SuperuserAtSpace1; + + const { body: createdAction } = await supertest + .post(`${getUrlPrefix(space.id)}/api/actions/connector`) + .set('kbn-xsrf', 'foo') + .auth(user.username, user.password) + .send({ + name: 'My action', + connector_type_id: 'test.index-record', + config: { + unencrypted: `This value shouldn't get encrypted`, + }, + secrets: { + encrypted: 'This value should be encrypted', + }, + }) + .expect(200); + + objectRemover.add(space.id, createdAction.id, 'action', 'actions'); + + const ruleRes = await supertest + .post(`${getUrlPrefix(space.id)}/api/alerting/rule`) + .set('kbn-xsrf', 'foo') + .send( + getTestRuleData({ + actions: [ + systemAction, + { + id: createdAction.id, + group: 'default', + params: {}, + }, + ], + }) + ) + .expect(200); + + objectRemover.add(space.id, ruleRes.body.id, 'rule', 'alerting'); + + const response = await supertestWithoutAuth + .get(`${getUrlPrefix(space.id)}/internal/actions/connectors`) + .auth(user.username, user.password) + .expect(200); + + const connectors = response.body as Array<{ id: string; referenced_by_count: number }>; + + const createdConnector = connectors.find((connector) => connector.id === createdAction.id); + const systemConnector = connectors.find((connector) => connector.id === systemAction.id); + + expect(createdConnector?.referenced_by_count).to.be(1); + expect(systemConnector?.referenced_by_count).to.be(0); + }); + }); + }); +} diff --git a/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/index.ts b/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/index.ts index dc1876d6e784b..0ccc0b5770ea2 100644 --- a/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/index.ts +++ b/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/index.ts @@ -47,8 +47,10 @@ export default function connectorsTests({ loadTestFile, getService }: FtrProvide loadTestFile(require.resolve('./delete')); loadTestFile(require.resolve('./execute')); loadTestFile(require.resolve('./get_all')); + loadTestFile(require.resolve('./get_all_system')); loadTestFile(require.resolve('./get')); loadTestFile(require.resolve('./connector_types')); + loadTestFile(require.resolve('./connector_types_system')); loadTestFile(require.resolve('./update')); loadTestFile(require.resolve('./bulk_enqueue')); diff --git a/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/alerting/update.ts b/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/alerting/update.ts index b4a775b7a6635..3771039e392c9 100644 --- a/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/alerting/update.ts +++ b/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/alerting/update.ts @@ -7,8 +7,11 @@ import expect from '@kbn/expect'; import { Response as SupertestResponse } from 'supertest'; -import { RULE_SAVED_OBJECT_TYPE } from '@kbn/alerting-plugin/server'; -import { UserAtSpaceScenarios } from '../../../scenarios'; +import { RuleNotifyWhen, RULE_SAVED_OBJECT_TYPE } from '@kbn/alerting-plugin/server'; +import { RawRule } from '@kbn/alerting-plugin/server/types'; +import { ALERTING_CASES_SAVED_OBJECT_INDEX } from '@kbn/core-saved-objects-server'; +import { SavedObject } from '@kbn/core-saved-objects-api-server'; +import { SuperuserAtSpace1, UserAtSpaceScenarios } from '../../../scenarios'; import { checkAAD, getUrlPrefix, @@ -24,6 +27,7 @@ export default function createUpdateTests({ getService }: FtrProviderContext) { const supertest = getService('supertest'); const supertestWithoutAuth = getService('supertestWithoutAuth'); const retry = getService('retry'); + const es = getService('es'); function getAlertingTaskById(taskId: string) { return supertest @@ -992,5 +996,287 @@ export default function createUpdateTests({ getService }: FtrProviderContext) { }); }); } + + describe('Actions', () => { + const { user, space } = SuperuserAtSpace1; + + it('should update a rule with actions correctly', async () => { + const { body: createdAction } = await supertest + .post(`${getUrlPrefix(space.id)}/api/actions/connector`) + .set('kbn-xsrf', 'foo') + .send({ + name: 'MY action', + connector_type_id: 'test.noop', + config: {}, + secrets: {}, + }) + .expect(200); + + const { body: createdRule } = await supertest + .post(`${getUrlPrefix(space.id)}/api/alerting/rule`) + .set('kbn-xsrf', 'foo') + .send(getTestRuleData()) + .expect(200); + + const updateResponse = await supertestWithoutAuth + .put(`${getUrlPrefix(space.id)}/api/alerting/rule/${createdRule.id}`) + .set('kbn-xsrf', 'foo') + .auth(user.username, user.password) + .send({ + name: 'bcd', + tags: ['bar'], + params: { + foo: true, + }, + schedule: { interval: '12s' }, + throttle: '1m', + notify_when: 'onThrottleInterval', + actions: [ + { + id: createdAction.id, + group: 'default', + params: {}, + }, + { + id: 'system-connector-test.system-action', + params: {}, + }, + ], + }) + .expect(200); + + objectRemover.add(space.id, updateResponse.body.id, 'rule', 'alerting'); + + const action = updateResponse.body.actions[0]; + const systemAction = updateResponse.body.actions[1]; + const { uuid, ...restAction } = action; + const { uuid: systemActionUuid, ...restSystemAction } = systemAction; + + expect([restAction, restSystemAction]).to.eql([ + { + id: createdAction.id, + connector_type_id: 'test.noop', + group: 'default', + params: {}, + }, + { + id: 'system-connector-test.system-action', + connector_type_id: 'test.system-action', + params: {}, + }, + , + ]); + + const esResponse = await es.get>( + { + index: ALERTING_CASES_SAVED_OBJECT_INDEX, + id: `alert:${updateResponse.body.id}`, + }, + { meta: true } + ); + + expect(esResponse.statusCode).to.eql(200); + expect((esResponse.body._source as any)?.alert.systemActions).to.be(undefined); + const rawActions = (esResponse.body._source as any)?.alert.actions ?? []; + + const rawAction = rawActions[0]; + const rawSystemAction = rawActions[1]; + + const { uuid: rawActionUuid, ...rawActionRest } = rawAction; + const { uuid: rawSystemActionUuid, ...rawSystemActionRest } = rawSystemAction; + + expect(rawActionRest).to.eql({ + actionRef: 'action_0', + actionTypeId: 'test.noop', + params: {}, + group: 'default', + }); + + expect(rawSystemActionRest).to.eql({ + actionRef: 'system_action:system-connector-test.system-action', + actionTypeId: 'test.system-action', + params: {}, + }); + + expect(rawActionUuid).to.not.be(undefined); + expect(rawSystemActionUuid).to.not.be(undefined); + }); + + it('should throw 400 if the system action is missing required params', async () => { + const { body: createdRule } = await supertest + .post(`${getUrlPrefix(space.id)}/api/alerting/rule`) + .set('kbn-xsrf', 'foo') + .send(getTestRuleData()) + .expect(200); + + objectRemover.add(space.id, createdRule.id, 'rule', 'alerting'); + + await supertestWithoutAuth + .put(`${getUrlPrefix(space.id)}/api/alerting/rule/${createdRule.id}`) + .set('kbn-xsrf', 'foo') + .auth(user.username, user.password) + .send({ + name: 'bcd', + tags: ['bar'], + params: { + foo: true, + }, + schedule: { interval: '12s' }, + throttle: '1m', + notify_when: 'onThrottleInterval', + actions: [ + { + params: {}, + }, + ], + }) + .expect(400); + }); + + it('strips out properties from system actions that are part of the default actions', async () => { + const { body: createdRule } = await supertest + .post(`${getUrlPrefix(space.id)}/api/alerting/rule`) + .set('kbn-xsrf', 'foo') + .send(getTestRuleData()) + .expect(200); + + objectRemover.add(space.id, createdRule.id, 'rule', 'alerting'); + + for (const propertyToAdd of [ + { group: 'default' }, + { + frequency: { + summary: false, + throttle: '1s', + notify_when: RuleNotifyWhen.THROTTLE, + }, + }, + { + alerts_filter: { + query: { kql: 'kibana.alert.rule.name:abc', filters: [] }, + }, + }, + ]) { + const updateResponse = await supertestWithoutAuth + .put(`${getUrlPrefix(space.id)}/api/alerting/rule/${createdRule.id}`) + .set('kbn-xsrf', 'foo') + .auth(user.username, user.password) + .send({ + name: 'bcd', + tags: ['bar'], + params: { + foo: true, + }, + schedule: { interval: '12s' }, + throttle: '1m', + notify_when: 'onThrottleInterval', + actions: [ + { + id: 'system-connector-test.system-action', + params: {}, + ...propertyToAdd, + }, + ], + }) + .expect(200); + + expect(updateResponse.body.actions[0][Object.keys(propertyToAdd)[0]]).to.be(undefined); + + const esResponse = await es.get>( + { + index: ALERTING_CASES_SAVED_OBJECT_INDEX, + id: `alert:${updateResponse.body.id}`, + }, + { meta: true } + ); + + expect(esResponse.statusCode).to.eql(200); + expect((esResponse.body._source as any)?.alert.systemActions).to.be(undefined); + + const rawActions = (esResponse.body._source as any)?.alert.actions ?? []; + expect(rawActions[0][Object.keys(propertyToAdd)[0]]).to.be(undefined); + } + }); + + it('should throw 400 when using the same system action twice', async () => { + const { body: createdRule } = await supertest + .post(`${getUrlPrefix(space.id)}/api/alerting/rule`) + .set('kbn-xsrf', 'foo') + .send(getTestRuleData()) + .expect(200); + + objectRemover.add(space.id, createdRule.id, 'rule', 'alerting'); + + await supertestWithoutAuth + .put(`${getUrlPrefix(space.id)}/api/alerting/rule/${createdRule.id}`) + .set('kbn-xsrf', 'foo') + .auth(user.username, user.password) + .send({ + name: 'bcd', + tags: ['bar'], + params: { + foo: true, + }, + schedule: { interval: '12s' }, + throttle: '1m', + notify_when: 'onThrottleInterval', + actions: [ + { + id: 'system-connector-test.system-action', + params: {}, + }, + { + id: 'system-connector-test.system-action', + params: {}, + }, + ], + }) + .expect(400); + }); + + it('should not allow creating a default action without group', async () => { + const { body: createdAction } = await supertest + .post(`${getUrlPrefix(space.id)}/api/actions/connector`) + .set('kbn-xsrf', 'foo') + .send({ + name: 'MY action', + connector_type_id: 'test.noop', + config: {}, + secrets: {}, + }) + .expect(200); + + const { body: createdRule } = await supertest + .post(`${getUrlPrefix(space.id)}/api/alerting/rule`) + .set('kbn-xsrf', 'foo') + .send(getTestRuleData()) + .expect(200); + + objectRemover.add(space.id, createdRule.id, 'rule', 'alerting'); + + await supertestWithoutAuth + .put(`${getUrlPrefix(space.id)}/api/alerting/rule/${createdRule.id}`) + .set('kbn-xsrf', 'foo') + .auth(user.username, user.password) + .send({ + name: 'bcd', + tags: ['bar'], + params: { + foo: true, + }, + schedule: { interval: '12s' }, + throttle: '1m', + notify_when: 'onThrottleInterval', + actions: [ + { + // group is missing + id: createdAction.id, + params: {}, + }, + ], + }) + .expect(400); + }); + }); }); } diff --git a/x-pack/test/alerting_api_integration/security_and_spaces/group3/tests/alerting/bulk_delete.ts b/x-pack/test/alerting_api_integration/security_and_spaces/group3/tests/alerting/bulk_delete.ts index 4d2e9781688c9..fc3526eebc6a4 100644 --- a/x-pack/test/alerting_api_integration/security_and_spaces/group3/tests/alerting/bulk_delete.ts +++ b/x-pack/test/alerting_api_integration/security_and_spaces/group3/tests/alerting/bulk_delete.ts @@ -674,5 +674,68 @@ export default ({ getService }: FtrProviderContext) => { }); }); }); + + describe('Actions', () => { + const { user, space } = SuperuserAtSpace1; + + it('should return the actions correctly', async () => { + const { body: createdAction } = await supertest + .post(`${getUrlPrefix(space.id)}/api/actions/connector`) + .set('kbn-xsrf', 'foo') + .send({ + name: 'MY action', + connector_type_id: 'test.noop', + config: {}, + secrets: {}, + }) + .expect(200); + + const { body: createdRule1 } = await supertest + .post(`${getUrlPrefix(space.id)}/api/alerting/rule`) + .set('kbn-xsrf', 'foo') + .send( + getTestRuleData({ + actions: [ + { + id: createdAction.id, + group: 'default', + params: {}, + }, + { + id: 'system-connector-test.system-action', + params: {}, + }, + ], + }) + ) + .expect(200); + + const response = await supertestWithoutAuth + .patch(`${getUrlPrefix(space.id)}/internal/alerting/rules/_bulk_delete`) + .set('kbn-xsrf', 'foo') + .send({ ids: [createdRule1.id] }) + .auth(user.username, user.password); + + const action = response.body.rules[0].actions[0]; + const systemAction = response.body.rules[0].actions[1]; + const { uuid, ...restAction } = action; + const { uuid: systemActionUuid, ...restSystemAction } = systemAction; + + expect([restAction, restSystemAction]).to.eql([ + { + id: createdAction.id, + connector_type_id: 'test.noop', + group: 'default', + params: {}, + }, + { + id: 'system-connector-test.system-action', + connector_type_id: 'test.system-action', + params: {}, + }, + , + ]); + }); + }); }); }; diff --git a/x-pack/test/alerting_api_integration/security_and_spaces/group3/tests/alerting/bulk_disable.ts b/x-pack/test/alerting_api_integration/security_and_spaces/group3/tests/alerting/bulk_disable.ts index bf31e09fa5819..d69055a92e1f4 100644 --- a/x-pack/test/alerting_api_integration/security_and_spaces/group3/tests/alerting/bulk_disable.ts +++ b/x-pack/test/alerting_api_integration/security_and_spaces/group3/tests/alerting/bulk_disable.ts @@ -541,5 +541,71 @@ export default ({ getService }: FtrProviderContext) => { }); }); }); + + describe('Actions', () => { + const { user, space } = SuperuserAtSpace1; + + it('should return the actions correctly', async () => { + const { body: createdAction } = await supertest + .post(`${getUrlPrefix(space.id)}/api/actions/connector`) + .set('kbn-xsrf', 'foo') + .send({ + name: 'MY action', + connector_type_id: 'test.noop', + config: {}, + secrets: {}, + }) + .expect(200); + + const { body: createdRule1 } = await supertest + .post(`${getUrlPrefix(space.id)}/api/alerting/rule`) + .set('kbn-xsrf', 'foo') + .send( + getTestRuleData({ + enabled: true, + actions: [ + { + id: createdAction.id, + group: 'default', + params: {}, + }, + { + id: 'system-connector-test.system-action', + params: {}, + }, + ], + }) + ) + .expect(200); + + objectRemover.add(space.id, createdRule1.id, 'rule', 'alerting'); + + const response = await supertestWithoutAuth + .patch(`${getUrlPrefix(space.id)}/internal/alerting/rules/_bulk_disable`) + .set('kbn-xsrf', 'foo') + .send({ ids: [createdRule1.id] }) + .auth(user.username, user.password); + + const action = response.body.rules[0].actions[0]; + const systemAction = response.body.rules[0].actions[1]; + const { uuid, ...restAction } = action; + const { uuid: systemActionUuid, ...restSystemAction } = systemAction; + + expect([restAction, restSystemAction]).to.eql([ + { + id: createdAction.id, + connector_type_id: 'test.noop', + group: 'default', + params: {}, + }, + { + id: 'system-connector-test.system-action', + connector_type_id: 'test.system-action', + params: {}, + }, + , + ]); + }); + }); }); }; diff --git a/x-pack/test/alerting_api_integration/security_and_spaces/group3/tests/alerting/bulk_edit.ts b/x-pack/test/alerting_api_integration/security_and_spaces/group3/tests/alerting/bulk_edit.ts index 8dec0a90e8d31..f09ab4b2fe66d 100644 --- a/x-pack/test/alerting_api_integration/security_and_spaces/group3/tests/alerting/bulk_edit.ts +++ b/x-pack/test/alerting_api_integration/security_and_spaces/group3/tests/alerting/bulk_edit.ts @@ -6,9 +6,12 @@ */ import expect from '@kbn/expect'; -import type { SanitizedRule } from '@kbn/alerting-plugin/common'; +import { SavedObject } from '@kbn/core/server'; +import { RuleNotifyWhen, SanitizedRule } from '@kbn/alerting-plugin/common'; import { RULE_SAVED_OBJECT_TYPE } from '@kbn/alerting-plugin/server'; -import { UserAtSpaceScenarios } from '../../../scenarios'; +import { RawRule } from '@kbn/alerting-plugin/server/types'; +import { ALERTING_CASES_SAVED_OBJECT_INDEX } from '@kbn/core-saved-objects-server'; +import { SuperuserAtSpace1, UserAtSpaceScenarios } from '../../../scenarios'; import { checkAAD, getUrlPrefix, @@ -24,6 +27,7 @@ export default function createUpdateTests({ getService }: FtrProviderContext) { const supertestWithoutAuth = getService('supertestWithoutAuth'); describe('bulkEdit', () => { + const es = getService('es'); const objectRemover = new ObjectRemover(supertest); after(() => objectRemover.removeAll()); @@ -601,8 +605,6 @@ export default function createUpdateTests({ getService }: FtrProviderContext) { } describe('do NOT delete reference for rule type like', () => { - const es = getService('es'); - it('.esquery', async () => { const space1 = UserAtSpaceScenarios[1].space.id; const { body: createdRule } = await supertest @@ -677,5 +679,279 @@ export default function createUpdateTests({ getService }: FtrProviderContext) { ); }); }); + + describe('Actions', () => { + const { space } = SuperuserAtSpace1; + + it('should add the actions correctly', async () => { + const { body: createdAction } = await supertest + .post(`${getUrlPrefix(space.id)}/api/actions/connector`) + .set('kbn-xsrf', 'foo') + .send({ + name: 'MY action', + connector_type_id: 'test.noop', + config: {}, + secrets: {}, + }) + .expect(200); + + const { body: createdRule } = await supertest + .post(`${getUrlPrefix(space.id)}/api/alerting/rule`) + .set('kbn-xsrf', 'foo') + .send(getTestRuleData()) + .expect(200); + + objectRemover.add(space.id, createdRule.id, 'rule', 'alerting'); + + const response = await supertest + .post(`${getUrlPrefix(space.id)}/internal/alerting/rules/_bulk_edit`) + .set('kbn-xsrf', 'foo') + .send({ + ids: [createdRule.id], + operations: [ + { + operation: 'add', + field: 'actions', + value: [ + { + id: createdAction.id, + group: 'default', + params: {}, + }, + { + id: 'system-connector-test.system-action', + params: {}, + }, + ], + }, + ], + }); + + expect(response.status).to.eql(200); + + const action = response.body.rules[0].actions[0]; + const systemAction = response.body.rules[0].actions[1]; + const { uuid, ...restAction } = action; + const { uuid: systemActionUuid, ...restSystemAction } = systemAction; + + expect([restAction, restSystemAction]).to.eql([ + { + id: createdAction.id, + connector_type_id: 'test.noop', + group: 'default', + params: {}, + }, + { + id: 'system-connector-test.system-action', + connector_type_id: 'test.system-action', + params: {}, + }, + , + ]); + + const esResponse = await es.get>( + { + index: ALERTING_CASES_SAVED_OBJECT_INDEX, + id: `alert:${response.body.rules[0].id}`, + }, + { meta: true } + ); + + expect(esResponse.statusCode).to.eql(200); + expect((esResponse.body._source as any)?.alert.systemActions).to.be(undefined); + const rawActions = (esResponse.body._source as any)?.alert.actions ?? []; + + const rawAction = rawActions[0]; + const rawSystemAction = rawActions[1]; + + const { uuid: rawActionUuid, ...rawActionRest } = rawAction; + const { uuid: rawSystemActionUuid, ...rawSystemActionRest } = rawSystemAction; + + expect(rawActionRest).to.eql({ + actionRef: 'action_0', + actionTypeId: 'test.noop', + params: {}, + group: 'default', + }); + + expect(rawSystemActionRest).to.eql({ + actionRef: 'system_action:system-connector-test.system-action', + actionTypeId: 'test.system-action', + params: {}, + }); + + expect(rawActionUuid).to.not.be(undefined); + expect(rawSystemActionUuid).to.not.be(undefined); + }); + + it('should not allow creating a default action without group', async () => { + const { body: createdRule } = await supertest + .post(`${getUrlPrefix(space.id)}/api/alerting/rule`) + .set('kbn-xsrf', 'foo') + .send(getTestRuleData()) + .expect(200); + + objectRemover.add(space.id, createdRule.id, 'rule', 'alerting'); + + const response = await supertest + .post(`${getUrlPrefix(space.id)}/internal/alerting/rules/_bulk_edit`) + .set('kbn-xsrf', 'foo') + .send({ + ids: [createdRule.id], + operations: [ + { + operation: 'add', + field: 'actions', + value: [ + { + id: 'test-id', + params: {}, + }, + ], + }, + ], + }); + + expect(response.status).to.eql(400); + + expect(response.body).to.eql({ + statusCode: 400, + error: 'Bad Request', + message: 'Group is not defined in action test-id', + }); + }); + + it('should throw 400 if the system action is missing required params', async () => { + const { body: createdRule } = await supertest + .post(`${getUrlPrefix(space.id)}/api/alerting/rule`) + .set('kbn-xsrf', 'foo') + .send(getTestRuleData()) + .expect(200); + + objectRemover.add(space.id, createdRule.id, 'rule', 'alerting'); + + const response = await supertest + .post(`${getUrlPrefix(space.id)}/internal/alerting/rules/_bulk_edit`) + .set('kbn-xsrf', 'foo') + .send({ + ids: [createdRule.id], + operations: [ + { + operation: 'add', + field: 'actions', + value: [ + { + id: 'system-connector-test.system-action-connector-adapter', + params: {}, + }, + ], + }, + ], + }); + + expect(response.status).to.eql(200); + expect(response.body.errors.length).to.eql(1); + + expect(response.body.errors[0].message).to.eql( + 'Invalid system action params. System action type: test.system-action-connector-adapter - [myParam]: expected value of type [string] but got [undefined]' + ); + }); + + it('strips out properties from system actions that are part of the default actions', async () => { + const { body: createdRule } = await supertest + .post(`${getUrlPrefix(space.id)}/api/alerting/rule`) + .set('kbn-xsrf', 'foo') + .send(getTestRuleData()) + .expect(200); + + objectRemover.add(space.id, createdRule.id, 'rule', 'alerting'); + + for (const propertyToAdd of [ + { group: 'default' }, + { + frequency: { + summary: false, + throttle: '1s', + notifyWhen: RuleNotifyWhen.THROTTLE, + }, + }, + ]) { + const systemActionWithProperty = { + id: 'system-connector-test.system-action', + params: {}, + ...propertyToAdd, + }; + + const response = await supertest + .post(`${getUrlPrefix(space.id)}/internal/alerting/rules/_bulk_edit`) + .set('kbn-xsrf', 'foo') + .send({ + ids: [createdRule.id], + operations: [ + { + operation: 'add', + field: 'actions', + value: [systemActionWithProperty], + }, + ], + }); + + expect(response.status).to.eql(200); + expect(response.body.rules[0].actions[0][Object.keys(propertyToAdd)[0]]).to.be(undefined); + + const esResponse = await es.get>( + { + index: ALERTING_CASES_SAVED_OBJECT_INDEX, + id: `alert:${response.body.rules[0].id}`, + }, + { meta: true } + ); + + expect(esResponse.statusCode).to.eql(200); + expect((esResponse.body._source as any)?.alert.systemActions).to.be(undefined); + + const rawActions = (esResponse.body._source as any)?.alert.actions ?? []; + expect(rawActions[0][Object.keys(propertyToAdd)[0]]).to.be(undefined); + } + }); + + it('should throw 400 when using the same system action twice', async () => { + const { body: createdRule } = await supertest + .post(`${getUrlPrefix(space.id)}/api/alerting/rule`) + .set('kbn-xsrf', 'foo') + .send(getTestRuleData()) + .expect(200); + + objectRemover.add(space.id, createdRule.id, 'rule', 'alerting'); + + const response = await supertest + .post(`${getUrlPrefix(space.id)}/internal/alerting/rules/_bulk_edit`) + .set('kbn-xsrf', 'foo') + .send({ + ids: [createdRule.id], + operations: [ + { + operation: 'add', + field: 'actions', + value: [ + { + id: 'system-connector-test.system-action', + params: {}, + }, + { + id: 'system-connector-test.system-action', + params: {}, + }, + ], + }, + ], + }); + + expect(response.status).to.eql(200); + expect(response.body.errors.length).to.eql(1); + + expect(response.body.errors[0].message).to.eql('Cannot use the same system action twice'); + }); + }); }); } diff --git a/x-pack/test/alerting_api_integration/security_and_spaces/group3/tests/alerting/bulk_enable.ts b/x-pack/test/alerting_api_integration/security_and_spaces/group3/tests/alerting/bulk_enable.ts index 0366eca2ad24d..d61810bb72fd3 100644 --- a/x-pack/test/alerting_api_integration/security_and_spaces/group3/tests/alerting/bulk_enable.ts +++ b/x-pack/test/alerting_api_integration/security_and_spaces/group3/tests/alerting/bulk_enable.ts @@ -513,5 +513,71 @@ export default ({ getService }: FtrProviderContext) => { }); }); }); + + describe('Actions', () => { + const { user, space } = SuperuserAtSpace1; + + it('should return the actions correctly', async () => { + const { body: createdAction } = await supertest + .post(`${getUrlPrefix(space.id)}/api/actions/connector`) + .set('kbn-xsrf', 'foo') + .send({ + name: 'MY action', + connector_type_id: 'test.noop', + config: {}, + secrets: {}, + }) + .expect(200); + + const { body: createdRule1 } = await supertest + .post(`${getUrlPrefix(space.id)}/api/alerting/rule`) + .set('kbn-xsrf', 'foo') + .send( + getTestRuleData({ + enabled: false, + actions: [ + { + id: createdAction.id, + group: 'default', + params: {}, + }, + { + id: 'system-connector-test.system-action', + params: {}, + }, + ], + }) + ) + .expect(200); + + objectRemover.add(space.id, createdRule1.id, 'rule', 'alerting'); + + const response = await supertestWithoutAuth + .patch(`${getUrlPrefix(space.id)}/internal/alerting/rules/_bulk_enable`) + .set('kbn-xsrf', 'foo') + .send({ ids: [createdRule1.id] }) + .auth(user.username, user.password); + + const action = response.body.rules[0].actions[0]; + const systemAction = response.body.rules[0].actions[1]; + const { uuid, ...restAction } = action; + const { uuid: systemActionUuid, ...restSystemAction } = systemAction; + + expect([restAction, restSystemAction]).to.eql([ + { + id: createdAction.id, + actionTypeId: 'test.noop', + group: 'default', + params: {}, + }, + { + id: 'system-connector-test.system-action', + actionTypeId: 'test.system-action', + params: {}, + }, + , + ]); + }); + }); }); }; diff --git a/x-pack/test/alerting_api_integration/security_and_spaces/group3/tests/alerting/clone.ts b/x-pack/test/alerting_api_integration/security_and_spaces/group3/tests/alerting/clone.ts index 90748d1b2d4cd..d76c4dbd1a239 100644 --- a/x-pack/test/alerting_api_integration/security_and_spaces/group3/tests/alerting/clone.ts +++ b/x-pack/test/alerting_api_integration/security_and_spaces/group3/tests/alerting/clone.ts @@ -7,6 +7,8 @@ import expect from '@kbn/expect'; import { RULE_SAVED_OBJECT_TYPE } from '@kbn/alerting-plugin/server'; +import { ALERTING_CASES_SAVED_OBJECT_INDEX, SavedObject } from '@kbn/core-saved-objects-server'; +import { RawRule } from '@kbn/alerting-plugin/server/types'; import { Spaces, UserAtSpaceScenarios } from '../../../scenarios'; import { checkAAD, @@ -249,5 +251,103 @@ export default function createAlertTests({ getService }: FtrProviderContext) { expect(cloneRuleResponse.body.scheduled_task_id).to.eql(undefined); }); + + describe('Actions', () => { + it('should clone a rule with actions correctly', async () => { + const { body: createdAction } = await supertest + .post(`${getUrlPrefix(space1)}/api/actions/connector`) + .set('kbn-xsrf', 'foo') + .send({ + name: 'MY action', + connector_type_id: 'test.noop', + config: {}, + secrets: {}, + }) + .expect(200); + + const ruleCreated = await supertest + .post(`${getUrlPrefix(space1)}/api/alerting/rule`) + .set('kbn-xsrf', 'foo') + .send( + getTestRuleData({ + actions: [ + { + id: createdAction.id, + group: 'default', + params: {}, + }, + { + id: 'system-connector-test.system-action', + params: {}, + }, + ], + }) + ); + + objectRemover.add(space1, ruleCreated.body.id, 'rule', 'alerting'); + + const cloneRuleResponse = await supertest + .post(`${getUrlPrefix(space1)}/internal/alerting/rule/${ruleCreated.body.id}/_clone`) + .set('kbn-xsrf', 'foo') + .send() + .expect(200); + + objectRemover.add(space1, cloneRuleResponse.body.id, 'rule', 'alerting'); + + const action = cloneRuleResponse.body.actions[0]; + const systemAction = cloneRuleResponse.body.actions[1]; + const { uuid, ...restAction } = action; + const { uuid: systemActionUuid, ...restSystemAction } = systemAction; + + expect([restAction, restSystemAction]).to.eql([ + { + id: createdAction.id, + connector_type_id: 'test.noop', + group: 'default', + params: {}, + }, + { + id: 'system-connector-test.system-action', + connector_type_id: 'test.system-action', + params: {}, + }, + , + ]); + + const esResponse = await es.get>( + { + index: ALERTING_CASES_SAVED_OBJECT_INDEX, + id: `alert:${cloneRuleResponse.body.id}`, + }, + { meta: true } + ); + + expect(esResponse.statusCode).to.eql(200); + expect((esResponse.body._source as any)?.alert.systemActions).to.be(undefined); + const rawActions = (esResponse.body._source as any)?.alert.actions ?? []; + + const rawAction = rawActions[0]; + const rawSystemAction = rawActions[1]; + + const { uuid: rawActionUuid, ...rawActionRest } = rawAction; + const { uuid: rawSystemActionUuid, ...rawSystemActionRest } = rawSystemAction; + + expect(rawActionRest).to.eql({ + actionRef: 'action_0', + actionTypeId: 'test.noop', + params: {}, + group: 'default', + }); + + expect(rawSystemActionRest).to.eql({ + actionRef: 'system_action:system-connector-test.system-action', + actionTypeId: 'test.system-action', + params: {}, + }); + + expect(rawActionUuid).to.not.be(undefined); + expect(rawSystemActionUuid).to.not.be(undefined); + }); + }); }); } diff --git a/x-pack/test/alerting_api_integration/security_and_spaces/group3/tests/alerting/index.ts b/x-pack/test/alerting_api_integration/security_and_spaces/group3/tests/alerting/index.ts index f9af26be6def7..999f4acbefa78 100644 --- a/x-pack/test/alerting_api_integration/security_and_spaces/group3/tests/alerting/index.ts +++ b/x-pack/test/alerting_api_integration/security_and_spaces/group3/tests/alerting/index.ts @@ -33,6 +33,7 @@ export default function alertingTests({ loadTestFile, getService }: FtrProviderC loadTestFile(require.resolve('./user_managed_api_key')); loadTestFile(require.resolve('./get_query_delay_settings')); loadTestFile(require.resolve('./update_query_delay_settings')); + loadTestFile(require.resolve('./resolve')); }); }); } diff --git a/x-pack/test/alerting_api_integration/security_and_spaces/group3/tests/alerting/resolve.ts b/x-pack/test/alerting_api_integration/security_and_spaces/group3/tests/alerting/resolve.ts new file mode 100644 index 0000000000000..fe973b9dde73c --- /dev/null +++ b/x-pack/test/alerting_api_integration/security_and_spaces/group3/tests/alerting/resolve.ts @@ -0,0 +1,88 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import expect from '@kbn/expect'; +import { SuperuserAtSpace1 } from '../../../scenarios'; +import { FtrProviderContext } from '../../../../common/ftr_provider_context'; +import { getUrlPrefix, getTestRuleData, ObjectRemover } from '../../../../common/lib'; + +// eslint-disable-next-line import/no-default-export +export default ({ getService }: FtrProviderContext) => { + const supertest = getService('supertest'); + const supertestWithoutAuth = getService('supertestWithoutAuth'); + + describe('resolve', () => { + const objectRemover = new ObjectRemover(supertest); + + afterEach(() => objectRemover.removeAll()); + + describe('Actions', () => { + const { user, space } = SuperuserAtSpace1; + + it('should return the actions correctly', async () => { + const { body: createdAction } = await supertest + .post(`${getUrlPrefix(space.id)}/api/actions/connector`) + .set('kbn-xsrf', 'foo') + .send({ + name: 'MY action', + connector_type_id: 'test.noop', + config: {}, + secrets: {}, + }) + .expect(200); + + const { body: createdRule1 } = await supertest + .post(`${getUrlPrefix(space.id)}/api/alerting/rule`) + .set('kbn-xsrf', 'foo') + .send( + getTestRuleData({ + enabled: true, + actions: [ + { + id: createdAction.id, + group: 'default', + params: {}, + }, + { + id: 'system-connector-test.system-action', + params: {}, + }, + ], + }) + ) + .expect(200); + + objectRemover.add(space.id, createdRule1.id, 'rule', 'alerting'); + + const response = await supertestWithoutAuth + .get(`${getUrlPrefix(space.id)}/internal/alerting/rule/${createdRule1.id}/_resolve`) + .set('kbn-xsrf', 'foo') + .auth(user.username, user.password); + + const action = response.body.actions[0]; + const systemAction = response.body.actions[1]; + const { uuid, ...restAction } = action; + const { uuid: systemActionUuid, ...restSystemAction } = systemAction; + + expect([restAction, restSystemAction]).to.eql([ + { + id: createdAction.id, + connector_type_id: 'test.noop', + group: 'default', + params: {}, + }, + { + id: 'system-connector-test.system-action', + connector_type_id: 'test.system-action', + params: {}, + }, + , + ]); + }); + }); + }); +}; diff --git a/x-pack/test/alerting_api_integration/security_and_spaces/group4/tests/alerting/alerts.ts b/x-pack/test/alerting_api_integration/security_and_spaces/group4/tests/alerting/alerts.ts index ab3c9ad93d54f..34f07f70f0216 100644 --- a/x-pack/test/alerting_api_integration/security_and_spaces/group4/tests/alerting/alerts.ts +++ b/x-pack/test/alerting_api_integration/security_and_spaces/group4/tests/alerting/alerts.ts @@ -14,7 +14,7 @@ import { TaskRunning, TaskRunningStage } from '@kbn/task-manager-plugin/server/t import { ConcreteTaskInstance } from '@kbn/task-manager-plugin/server'; import { ESTestIndexTool, ES_TEST_INDEX_NAME } from '@kbn/alerting-api-integration-helpers'; import { RULE_SAVED_OBJECT_TYPE } from '@kbn/alerting-plugin/server'; -import { UserAtSpaceScenarios, Superuser } from '../../../scenarios'; +import { UserAtSpaceScenarios, Superuser, SuperuserAtSpace1 } from '../../../scenarios'; import { FtrProviderContext } from '../../../../common/ftr_provider_context'; import { getUrlPrefix, @@ -45,7 +45,9 @@ export default function alertTests({ getService }: FtrProviderContext) { await esTestIndexTool.setup(); await es.indices.create({ index: authorizationIndex }); }); + afterEach(() => objectRemover.removeAll()); + after(async () => { await esTestIndexTool.destroy(); await es.indices.delete({ index: authorizationIndex }); @@ -1873,6 +1875,76 @@ instanceStateValue: true }); }); } + + describe('connector adapters', () => { + const space = SuperuserAtSpace1.space; + + const connectorId = 'system-connector-test.system-action-connector-adapter'; + const name = 'System action: test.system-action-connector-adapter'; + + it('should use connector adapters correctly on system actions', async () => { + const alertUtils = new AlertUtils({ + supertestWithoutAuth, + objectRemover, + space, + user: SuperuserAtSpace1.user, + }); + + const startDate = new Date().toISOString(); + const reference = alertUtils.generateReference(); + /** + * Creates a rule that always fire with a system action + * that has configured a connector adapter. + * + * System action: x-pack/test/alerting_api_integration/common/plugins/alerts/server/action_types.ts + * Adapter: x-pack/test/alerting_api_integration/common/plugins/alerts/server/connector_adapters.ts + */ + const response = await alertUtils.createAlwaysFiringSystemAction({ + reference, + overwrites: { schedule: { interval: '1s' } }, + }); + + expect(response.status).to.eql(200); + + await validateSystemActionEventLog({ + spaceId: space.id, + connectorId, + outcome: 'success', + message: `action executed: test.system-action-connector-adapter:${connectorId}: ${name}`, + startDate, + }); + + /** + * The executor function of the system action + * writes the params in the test index. We + * get the doc to verify that the connector adapter + * injected the param correctly. + */ + await esTestIndexTool.waitForDocs( + 'action:test.system-action-connector-adapter', + reference, + 1 + ); + + const docs = await esTestIndexTool.search( + 'action:test.system-action-connector-adapter', + reference + ); + + const doc = docs.body.hits.hits[0]._source as { params: Record }; + + expect(doc.params).to.eql({ + myParam: 'param from rule action', + index: '.kibana-alerting-test-data', + reference: 'alert-utils-ref:1:superuser', + /** + * Param was injected by the connector adapter in + * x-pack/test/alerting_api_integration/common/plugins/alerts/server/connector_adapters.ts + */ + injected: 'param from connector adapter', + }); + }); + }); }); interface ValidateEventLogParams { @@ -1977,4 +2049,46 @@ instanceStateValue: true expect(event?.error?.message).to.eql(errorMessage); } } + + interface ValidateSystemActionEventLogParams { + spaceId: string; + connectorId: string; + outcome: string; + message: string; + startDate: string; + errorMessage?: string; + } + + const validateSystemActionEventLog = async ( + params: ValidateSystemActionEventLogParams + ): Promise => { + const { spaceId, connectorId, outcome, message, startDate, errorMessage } = params; + + const events: IValidatedEvent[] = await retry.try(async () => { + const events_ = await getEventLog({ + getService, + spaceId, + type: 'action', + id: connectorId, + provider: 'actions', + actions: new Map([['execute', { gte: 1 }]]), + }); + + const filteredEvents = events_.filter((event) => event!['@timestamp']! >= startDate); + if (filteredEvents.length < 1) throw new Error('no recent events found yet'); + + return filteredEvents; + }); + + expect(events.length).to.be(1); + + const event = events[0]; + + expect(event?.message).to.eql(message); + expect(event?.event?.outcome).to.eql(outcome); + + if (errorMessage) { + expect(event?.error?.message).to.eql(errorMessage); + } + }; } diff --git a/x-pack/test/alerting_api_integration/spaces_only/tests/actions/connector_types_system.ts b/x-pack/test/alerting_api_integration/spaces_only/tests/actions/connector_types_system.ts new file mode 100644 index 0000000000000..d1eb87310e444 --- /dev/null +++ b/x-pack/test/alerting_api_integration/spaces_only/tests/actions/connector_types_system.ts @@ -0,0 +1,47 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import expect from '@kbn/expect'; +import { Spaces } from '../../scenarios'; +import { getUrlPrefix } from '../../../common/lib/space_test_utils'; +import { FtrProviderContext } from '../../../common/ftr_provider_context'; + +// eslint-disable-next-line import/no-default-export +export default function listActionTypesTests({ getService }: FtrProviderContext) { + const supertest = getService('supertest'); + + describe('connector_types', () => { + it('should return 200 with list of connector types containing defaults', async () => { + const response = await supertest.get( + `${getUrlPrefix(Spaces.space1.id)}/internal/actions/connector_types` + ); + + function createActionTypeMatcher(id: string, name: string) { + return (actionType: { id: string; name: string }) => { + return actionType.id === id && actionType.name === name; + }; + } + + expect(response.status).to.eql(200); + // Check for values explicitly in order to avoid this test failing each time plugins register + // a new action type + expect( + response.body.some(createActionTypeMatcher('test.index-record', 'Test: Index Record')) + ).to.be(true); + }); + + it('should return system action types', async () => { + const response = await supertest.get( + `${getUrlPrefix(Spaces.space1.id)}/internal/actions/connector_types` + ); + + const actionTypes = response.body as Array<{ is_system_action_type: boolean }>; + + expect(actionTypes.some((actionType) => actionType.is_system_action_type)).to.be(true); + }); + }); +} diff --git a/x-pack/test/alerting_api_integration/spaces_only/tests/actions/execute.ts b/x-pack/test/alerting_api_integration/spaces_only/tests/actions/execute.ts index fb10eba9774ad..f9b3d1c4120b6 100644 --- a/x-pack/test/alerting_api_integration/spaces_only/tests/actions/execute.ts +++ b/x-pack/test/alerting_api_integration/spaces_only/tests/actions/execute.ts @@ -31,6 +31,7 @@ export default function ({ getService }: FtrProviderContext) { await esTestIndexTool.setup(); await es.indices.create({ index: authorizationIndex }); }); + after(async () => { await esTestIndexTool.destroy(); await es.indices.delete({ index: authorizationIndex }); @@ -333,12 +334,20 @@ export default function ({ getService }: FtrProviderContext) { }); }); + /** + * The test are using a test endpoint that calls the actions client. + * The route is defined here x-pack/test/alerting_api_integration/common/plugins/alerts/server/routes.ts. + * The public execute API does not allows the execution of system actions. We use the + * test route to test the execution of system actions + */ it('should execute system actions correctly', async () => { const connectorId = 'system-connector-test.system-action'; const name = 'System action: test.system-action'; const response = await supertest - .post(`${getUrlPrefix(Spaces.space1.id)}/api/actions/connector/${connectorId}/_execute`) + .post( + `${getUrlPrefix(Spaces.space1.id)}/api/alerts_fixture/${connectorId}/_execute_connector` + ) .set('kbn-xsrf', 'foo') .send({ params: {}, @@ -358,12 +367,20 @@ export default function ({ getService }: FtrProviderContext) { }); }); + /** + * The test are using a test endpoint that calls the actions client. + * The route is defined here x-pack/test/alerting_api_integration/common/plugins/alerts/server/routes.ts. + * The public execute API does not allows the execution of system actions. We use the + * test route to test the execution of system actions + */ it('should execute system actions with kibana privileges correctly', async () => { const connectorId = 'system-connector-test.system-action-kibana-privileges'; const name = 'System action: test.system-action-kibana-privileges'; const response = await supertest - .post(`${getUrlPrefix(Spaces.space1.id)}/api/actions/connector/${connectorId}/_execute`) + .post( + `${getUrlPrefix(Spaces.space1.id)}/api/alerts_fixture/${connectorId}/_execute_connector` + ) .set('kbn-xsrf', 'foo') .send({ params: {}, @@ -382,6 +399,21 @@ export default function ({ getService }: FtrProviderContext) { spaceAgnostic: true, }); }); + + /** + * The public execute API does not allows the execution of system actions. + */ + it('should not allow the execution of system actions through the public execute endpoint', async () => { + const connectorId = 'system-connector-test.system-action-kibana-privileges'; + + await supertest + .post(`${getUrlPrefix(Spaces.space1.id)}/api/actions/connector/${connectorId}/_execute`) + .set('kbn-xsrf', 'foo') + .send({ + params: {}, + }) + .expect(400); + }); }); interface ValidateEventLogParams { diff --git a/x-pack/test/alerting_api_integration/spaces_only/tests/actions/get_all_system.ts b/x-pack/test/alerting_api_integration/spaces_only/tests/actions/get_all_system.ts new file mode 100644 index 0000000000000..014a894db7d31 --- /dev/null +++ b/x-pack/test/alerting_api_integration/spaces_only/tests/actions/get_all_system.ts @@ -0,0 +1,313 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import expect from '@kbn/expect'; +import { Spaces } from '../../scenarios'; +import { getUrlPrefix, ObjectRemover } from '../../../common/lib'; +import { FtrProviderContext } from '../../../common/ftr_provider_context'; + +// eslint-disable-next-line import/no-default-export +export default function getAllActionTests({ getService }: FtrProviderContext) { + const supertest = getService('supertest'); + + describe('getAllSystem', () => { + const objectRemover = new ObjectRemover(supertest); + + afterEach(() => objectRemover.removeAll()); + + it('should handle get all action request appropriately', async () => { + const { body: createdAction } = await supertest + .post(`${getUrlPrefix(Spaces.space1.id)}/api/actions/action`) + .set('kbn-xsrf', 'foo') + .send({ + name: 'My action', + actionTypeId: 'test.index-record', + config: { + unencrypted: `This value shouldn't get encrypted`, + }, + secrets: { + encrypted: 'This value should be encrypted', + }, + }) + .expect(200); + objectRemover.add(Spaces.space1.id, createdAction.id, 'action', 'actions'); + + const { body: connectors } = await supertest + .get(`${getUrlPrefix(Spaces.space1.id)}/internal/actions/connectors`) + .expect(200); + + // the custom ssl connectors have dynamic ports, so remove them before + // comparing to what we expect + const nonCustomSslConnectors = connectors.filter( + (conn: { id: string }) => !conn.id.startsWith('custom.ssl.') + ); + + expect(nonCustomSslConnectors).to.eql([ + { + id: 'preconfigured-alert-history-es-index', + name: 'Alert history Elasticsearch index', + connector_type_id: '.index', + is_preconfigured: true, + is_deprecated: false, + is_system_action: false, + referenced_by_count: 0, + }, + { + id: createdAction.id, + is_preconfigured: false, + is_deprecated: false, + name: 'My action', + connector_type_id: 'test.index-record', + is_missing_secrets: false, + is_system_action: false, + config: { + unencrypted: `This value shouldn't get encrypted`, + }, + referenced_by_count: 0, + }, + { + connector_type_id: '.email', + id: 'notification-email', + is_deprecated: false, + is_preconfigured: true, + is_system_action: false, + name: 'Notification Email Connector', + referenced_by_count: 0, + }, + { + id: 'preconfigured-es-index-action', + is_preconfigured: true, + is_deprecated: false, + is_system_action: false, + connector_type_id: '.index', + name: 'preconfigured_es_index_action', + referenced_by_count: 0, + }, + { + connector_type_id: '.servicenow', + id: 'my-deprecated-servicenow', + is_deprecated: true, + is_preconfigured: true, + is_system_action: false, + name: 'ServiceNow#xyz', + referenced_by_count: 0, + }, + { + connector_type_id: '.servicenow', + id: 'my-deprecated-servicenow-default', + is_preconfigured: true, + is_deprecated: true, + is_system_action: false, + name: 'ServiceNow#xyz', + referenced_by_count: 0, + }, + { + id: 'my-slack1', + is_preconfigured: true, + is_deprecated: false, + connector_type_id: '.slack', + is_system_action: false, + name: 'Slack#xyz', + referenced_by_count: 0, + }, + { + connector_type_id: 'test.system-action', + id: 'system-connector-test.system-action', + is_deprecated: false, + is_preconfigured: false, + is_system_action: true, + name: 'System action: test.system-action', + referenced_by_count: 0, + }, + { + connector_type_id: 'test.system-action-connector-adapter', + id: 'system-connector-test.system-action-connector-adapter', + is_deprecated: false, + is_preconfigured: false, + is_system_action: true, + name: 'System action: test.system-action-connector-adapter', + referenced_by_count: 0, + }, + { + connector_type_id: 'test.system-action-kibana-privileges', + id: 'system-connector-test.system-action-kibana-privileges', + is_deprecated: false, + is_preconfigured: false, + is_system_action: true, + name: 'System action: test.system-action-kibana-privileges', + referenced_by_count: 0, + }, + { + id: 'custom-system-abc-connector', + is_preconfigured: true, + is_deprecated: false, + connector_type_id: 'system-abc-action-type', + is_system_action: false, + name: 'SystemABC', + referenced_by_count: 0, + }, + { + id: 'preconfigured.test.index-record', + is_preconfigured: true, + is_deprecated: false, + connector_type_id: 'test.index-record', + is_system_action: false, + name: 'Test:_Preconfigured_Index_Record', + referenced_by_count: 0, + }, + { + id: 'my-test-email', + is_preconfigured: true, + is_deprecated: false, + is_system_action: false, + connector_type_id: '.email', + name: 'TestEmail#xyz', + referenced_by_count: 0, + }, + ]); + }); + + it(`shouldn't get all action from another space`, async () => { + const { body: createdAction } = await supertest + .post(`${getUrlPrefix(Spaces.space1.id)}/api/actions/action`) + .set('kbn-xsrf', 'foo') + .send({ + name: 'My action', + actionTypeId: 'test.index-record', + config: { + unencrypted: `This value shouldn't get encrypted`, + }, + secrets: { + encrypted: 'This value should be encrypted', + }, + }) + .expect(200); + objectRemover.add(Spaces.space1.id, createdAction.id, 'action', 'actions'); + + const { body: connectors } = await supertest + .get(`${getUrlPrefix(Spaces.other.id)}/internal/actions/connectors`) + .expect(200); + + // the custom ssl connectors have dynamic ports, so remove them before + // comparing to what we expect + const nonCustomSslConnectors = connectors.filter( + (conn: { id: string }) => !conn.id.startsWith('custom.ssl.') + ); + + expect(nonCustomSslConnectors).to.eql([ + { + id: 'preconfigured-alert-history-es-index', + name: 'Alert history Elasticsearch index', + connector_type_id: '.index', + is_preconfigured: true, + is_deprecated: false, + is_system_action: false, + referenced_by_count: 0, + }, + { + connector_type_id: '.email', + id: 'notification-email', + is_deprecated: false, + is_preconfigured: true, + is_system_action: false, + name: 'Notification Email Connector', + referenced_by_count: 0, + }, + { + id: 'preconfigured-es-index-action', + is_preconfigured: true, + is_deprecated: false, + is_system_action: false, + connector_type_id: '.index', + name: 'preconfigured_es_index_action', + referenced_by_count: 0, + }, + { + connector_type_id: '.servicenow', + id: 'my-deprecated-servicenow', + is_deprecated: true, + is_preconfigured: true, + is_system_action: false, + name: 'ServiceNow#xyz', + referenced_by_count: 0, + }, + { + connector_type_id: '.servicenow', + id: 'my-deprecated-servicenow-default', + is_preconfigured: true, + is_deprecated: true, + is_system_action: false, + name: 'ServiceNow#xyz', + referenced_by_count: 0, + }, + { + id: 'my-slack1', + is_preconfigured: true, + is_deprecated: false, + is_system_action: false, + connector_type_id: '.slack', + name: 'Slack#xyz', + referenced_by_count: 0, + }, + { + connector_type_id: 'test.system-action', + id: 'system-connector-test.system-action', + is_deprecated: false, + is_preconfigured: false, + is_system_action: true, + name: 'System action: test.system-action', + referenced_by_count: 0, + }, + { + connector_type_id: 'test.system-action-connector-adapter', + id: 'system-connector-test.system-action-connector-adapter', + is_deprecated: false, + is_preconfigured: false, + is_system_action: true, + name: 'System action: test.system-action-connector-adapter', + referenced_by_count: 0, + }, + { + connector_type_id: 'test.system-action-kibana-privileges', + id: 'system-connector-test.system-action-kibana-privileges', + is_deprecated: false, + is_preconfigured: false, + is_system_action: true, + name: 'System action: test.system-action-kibana-privileges', + referenced_by_count: 0, + }, + { + id: 'custom-system-abc-connector', + is_preconfigured: true, + is_deprecated: false, + is_system_action: false, + connector_type_id: 'system-abc-action-type', + name: 'SystemABC', + referenced_by_count: 0, + }, + { + id: 'preconfigured.test.index-record', + is_preconfigured: true, + is_deprecated: false, + is_system_action: false, + connector_type_id: 'test.index-record', + name: 'Test:_Preconfigured_Index_Record', + referenced_by_count: 0, + }, + { + id: 'my-test-email', + is_preconfigured: true, + is_deprecated: false, + is_system_action: false, + connector_type_id: '.email', + name: 'TestEmail#xyz', + referenced_by_count: 0, + }, + ]); + }); + }); +} diff --git a/x-pack/test/alerting_api_integration/spaces_only/tests/actions/index.ts b/x-pack/test/alerting_api_integration/spaces_only/tests/actions/index.ts index 4f5832debebda..c746186d9d100 100644 --- a/x-pack/test/alerting_api_integration/spaces_only/tests/actions/index.ts +++ b/x-pack/test/alerting_api_integration/spaces_only/tests/actions/index.ts @@ -17,8 +17,10 @@ export default function actionsTests({ loadTestFile, getService }: FtrProviderCo loadTestFile(require.resolve('./create')); loadTestFile(require.resolve('./delete')); loadTestFile(require.resolve('./get_all')); + loadTestFile(require.resolve('./get_all_system')); loadTestFile(require.resolve('./get')); loadTestFile(require.resolve('./connector_types')); + loadTestFile(require.resolve('./connector_types_system')); loadTestFile(require.resolve('./update')); loadTestFile(require.resolve('./monitoring_collection')); loadTestFile(require.resolve('./execute')); diff --git a/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/group1/create.ts b/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/group1/create.ts index 3d97d39097348..555c578612225 100644 --- a/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/group1/create.ts +++ b/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/group1/create.ts @@ -7,8 +7,9 @@ import expect from '@kbn/expect'; import { SavedObject } from '@kbn/core/server'; -import { RawRule } from '@kbn/alerting-plugin/server/types'; +import { RawRule, RuleNotifyWhen } from '@kbn/alerting-plugin/server/types'; import { ALERTING_CASES_SAVED_OBJECT_INDEX } from '@kbn/core-saved-objects-server'; +import { omit } from 'lodash'; import { RULE_SAVED_OBJECT_TYPE } from '@kbn/alerting-plugin/server'; import { Spaces } from '../../../scenarios'; import { @@ -158,11 +159,6 @@ export default function createAlertTests({ getService }: FtrProviderContext) { message: 'something important happened!', }, }, - { - id: 'system-connector-test.system-action', - group: 'default', - params: {}, - }, ], }) ); @@ -192,13 +188,6 @@ export default function createAlertTests({ getService }: FtrProviderContext) { }, uuid: response.body.actions[1].uuid, }, - { - id: 'system-connector-test.system-action', - group: 'default', - connector_type_id: 'test.system-action', - params: {}, - uuid: response.body.actions[2].uuid, - }, ], enabled: true, rule_type_id: 'test.noop', @@ -253,13 +242,6 @@ export default function createAlertTests({ getService }: FtrProviderContext) { }, uuid: rawActions[1].uuid, }, - { - actionRef: 'system_action:system-connector-test.system-action', - actionTypeId: 'test.system-action', - group: 'default', - params: {}, - uuid: rawActions[2].uuid, - }, ]); const references = esResponse.body._source?.references ?? []; @@ -465,6 +447,191 @@ export default function createAlertTests({ getService }: FtrProviderContext) { expect(response.body.scheduledTaskId).to.eql(undefined); }); + it('should not allow creating a default action without group', async () => { + const customId = '1'; + const response = await supertest + .post(`${getUrlPrefix(Spaces.space1.id)}/api/alerting/rule/${customId}`) + .set('kbn-xsrf', 'foo') + .send( + getTestRuleData({ + actions: [ + { + // group is missing + id: 'test-id', + params: {}, + }, + ], + }) + ); + + expect(response.status).to.eql(400); + expect(response.body).to.eql({ + statusCode: 400, + error: 'Bad Request', + message: 'Group is not defined in action test-id', + }); + }); + + describe('system actions', () => { + const systemAction = { + id: 'system-connector-test.system-action', + params: {}, + }; + + it('should create a rule with a system action correctly', async () => { + const response = await supertest + .post(`${getUrlPrefix(Spaces.space1.id)}/api/alerting/rule`) + .set('kbn-xsrf', 'foo') + .send( + getTestRuleData({ + actions: [systemAction], + }) + ); + + expect(response.status).to.eql(200); + expect(response.body.actions.length).to.eql(1); + + objectRemover.add(Spaces.space1.id, response.body.id, 'rule', 'alerting'); + + const action = response.body.actions[0]; + const { uuid, ...rest } = action; + + expect(rest).to.eql({ + id: 'system-connector-test.system-action', + connector_type_id: 'test.system-action', + params: {}, + }); + + expect(uuid).to.not.be(undefined); + + const esResponse = await es.get>( + { + index: ALERTING_CASES_SAVED_OBJECT_INDEX, + id: `alert:${response.body.id}`, + }, + { meta: true } + ); + + expect(esResponse.statusCode).to.eql(200); + expect((esResponse.body._source as any)?.alert.systemActions).to.be(undefined); + + const rawActions = (esResponse.body._source as any)?.alert.actions ?? []; + const rawAction = rawActions[0]; + const { uuid: rawActionUuid, ...rawActionRest } = rawAction; + + expect(rawActionRest).to.eql({ + actionRef: 'system_action:system-connector-test.system-action', + actionTypeId: 'test.system-action', + params: {}, + }); + + expect(uuid).to.not.be(undefined); + + const references = esResponse.body._source?.references ?? []; + + expect(references.length).to.eql(0); + }); + + it('should throw 400 if the system action is missing required properties', async () => { + for (const propertyToOmit of ['id']) { + const systemActionWithoutProperty = omit(systemAction, propertyToOmit); + + await supertest + .post(`${getUrlPrefix(Spaces.space1.id)}/api/alerting/rule`) + .set('kbn-xsrf', 'foo') + .send( + getTestRuleData({ + actions: [systemActionWithoutProperty], + }) + ) + .expect(400); + } + }); + + it('should throw 400 if the system action is missing required params', async () => { + const res = await supertest + .post(`${getUrlPrefix(Spaces.space1.id)}/api/alerting/rule`) + .set('kbn-xsrf', 'foo') + .send( + getTestRuleData({ + actions: [ + { + ...systemAction, + params: {}, + id: 'system-connector-test.system-action-connector-adapter', + actionTypeId: 'test.test.system-action-connector-adapter', + }, + ], + }) + ) + .expect(400); + + expect(res.body.message).to.eql( + 'Invalid system action params. System action type: test.system-action-connector-adapter - [myParam]: expected value of type [string] but got [undefined]' + ); + }); + + it('strips out properties from system actions that are part of the default actions', async () => { + for (const propertyToAdd of [ + { group: 'default' }, + { + frequency: { + summary: false, + throttle: '1s', + notify_when: RuleNotifyWhen.THROTTLE, + }, + }, + { + alerts_filter: { + query: { kql: 'kibana.alert.rule.name:abc', filters: [] }, + }, + }, + ]) { + const systemActionWithProperty = { ...systemAction, ...propertyToAdd }; + + const response = await supertest + .post(`${getUrlPrefix(Spaces.space1.id)}/api/alerting/rule`) + .set('kbn-xsrf', 'foo') + .send( + getTestRuleData({ + actions: [systemActionWithProperty], + }) + ); + + expect(response.status).to.eql(200); + expect(response.body.actions[0][Object.keys(propertyToAdd)[0]]).to.be(undefined); + + objectRemover.add(Spaces.space1.id, response.body.id, 'rule', 'alerting'); + + const esResponse = await es.get>( + { + index: ALERTING_CASES_SAVED_OBJECT_INDEX, + id: `alert:${response.body.id}`, + }, + { meta: true } + ); + + expect(esResponse.statusCode).to.eql(200); + expect((esResponse.body._source as any)?.alert.systemActions).to.be(undefined); + + const rawActions = (esResponse.body._source as any)?.alert.actions ?? []; + expect(rawActions[0][Object.keys(propertyToAdd)[0]]).to.be(undefined); + } + }); + + it('should throw 400 when using the same system action twice', async () => { + await supertest + .post(`${getUrlPrefix(Spaces.space1.id)}/api/alerting/rule`) + .set('kbn-xsrf', 'foo') + .send( + getTestRuleData({ + actions: [systemAction, systemAction], + }) + ) + .expect(400); + }); + }); + describe('legacy', () => { it('should handle create alert request appropriately', async () => { const { body: createdAction } = await supertest diff --git a/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/group2/bulk_edit.ts b/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/group2/bulk_edit.ts new file mode 100644 index 0000000000000..b21485736f398 --- /dev/null +++ b/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/group2/bulk_edit.ts @@ -0,0 +1,174 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import expect from '@kbn/expect'; +import { omit } from 'lodash'; +import { Spaces } from '../../../scenarios'; +import { getUrlPrefix, getTestRuleData, ObjectRemover } from '../../../../common/lib'; +import { FtrProviderContext } from '../../../../common/ftr_provider_context'; + +// eslint-disable-next-line import/no-default-export +export default function createUpdateTests({ getService }: FtrProviderContext) { + const supertest = getService('supertest'); + + describe('bulkEdit', () => { + const objectRemover = new ObjectRemover(supertest); + + after(() => objectRemover.removeAll()); + + describe('system actions', () => { + const systemAction = { + id: 'system-connector-test.system-action', + uuid: '123', + params: {}, + }; + + it('should bulk edit system actions correctly', async () => { + const { body: rule } = await supertest + .post(`${getUrlPrefix(Spaces.space1.id)}/api/alerting/rule`) + .set('kbn-xsrf', 'foo') + .send(getTestRuleData()) + .expect(200); + + objectRemover.add(Spaces.space1.id, rule.id, 'rule', 'alerting'); + + const payload = { + ids: [rule.id], + operations: [ + { + operation: 'add', + field: 'actions', + value: [systemAction], + }, + ], + }; + + const res = await supertest + .post(`${getUrlPrefix(Spaces.space1.id)}/internal/alerting/rules/_bulk_edit`) + .set('kbn-xsrf', 'foo') + .send(payload) + .expect(200); + + expect(res.body.rules[0].actions).to.eql([ + { + id: 'system-connector-test.system-action', + connector_type_id: 'test.system-action', + params: {}, + uuid: '123', + }, + ]); + }); + + it('should throw 400 if the system action is missing required properties', async () => { + const { body: rule } = await supertest + .post(`${getUrlPrefix(Spaces.space1.id)}/api/alerting/rule`) + .set('kbn-xsrf', 'foo') + .send(getTestRuleData()) + .expect(200); + + objectRemover.add(Spaces.space1.id, rule.id, 'rule', 'alerting'); + + for (const propertyToOmit of ['id']) { + const systemActionWithoutProperty = omit(systemAction, propertyToOmit); + + const payload = { + ids: [rule.id], + operations: [ + { + operation: 'add', + field: 'actions', + value: [systemActionWithoutProperty], + }, + ], + }; + + await supertest + .post(`${getUrlPrefix(Spaces.space1.id)}/internal/alerting/rules/_bulk_edit`) + .set('kbn-xsrf', 'foo') + .send(payload) + .expect(400); + } + }); + + it('should throw 400 if the system action is missing required params', async () => { + const { body: rule } = await supertest + .post(`${getUrlPrefix(Spaces.space1.id)}/api/alerting/rule`) + .set('kbn-xsrf', 'foo') + .send(getTestRuleData()) + .expect(200); + + objectRemover.add(Spaces.space1.id, rule.id, 'rule', 'alerting'); + + const payload = { + ids: [rule.id], + operations: [ + { + operation: 'add', + field: 'actions', + value: [ + { + ...systemAction, + params: {}, + id: 'system-connector-test.system-action-connector-adapter', + }, + ], + }, + ], + }; + + const res = await supertest + .post(`${getUrlPrefix(Spaces.space1.id)}/internal/alerting/rules/_bulk_edit`) + .set('kbn-xsrf', 'foo') + .send(payload) + .expect(200); + + expect(res.body.errors[0].message).to.eql( + 'Invalid system action params. System action type: test.system-action-connector-adapter - [myParam]: expected value of type [string] but got [undefined]' + ); + }); + + it('should throw 400 if the default action is missing the group', async () => { + const { body: rule } = await supertest + .post(`${getUrlPrefix(Spaces.space1.id)}/api/alerting/rule`) + .set('kbn-xsrf', 'foo') + .send(getTestRuleData()) + .expect(200); + + objectRemover.add(Spaces.space1.id, rule.id, 'rule', 'alerting'); + + const payload = { + ids: [rule.id], + operations: [ + { + operation: 'add', + field: 'actions', + value: [ + { + // group is missing + id: 'test-id', + params: {}, + }, + ], + }, + ], + }; + + const response = await supertest + .post(`${getUrlPrefix(Spaces.space1.id)}/internal/alerting/rules/_bulk_edit`) + .set('kbn-xsrf', 'foo') + .send(payload); + + expect(response.status).to.eql(400); + expect(response.body).to.eql({ + statusCode: 400, + error: 'Bad Request', + message: 'Group is not defined in action test-id', + }); + }); + }); + }); +} diff --git a/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/group2/index.ts b/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/group2/index.ts index 1ccbb1c8f722d..5b03010a60235 100644 --- a/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/group2/index.ts +++ b/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/group2/index.ts @@ -27,5 +27,6 @@ export default function alertingTests({ loadTestFile, getService }: FtrProviderC loadTestFile(require.resolve('./alerts_default_space')); loadTestFile(require.resolve('./transform_rule_types')); loadTestFile(require.resolve('./ml_rule_types')); + loadTestFile(require.resolve('./bulk_edit')); }); } diff --git a/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/group2/update.ts b/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/group2/update.ts index 623a2efc3f005..867e3c0ec8de0 100644 --- a/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/group2/update.ts +++ b/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/group2/update.ts @@ -128,6 +128,48 @@ export default function createUpdateTests({ getService }: FtrProviderContext) { }); }); + it('should not allow updating default action without group', async () => { + const { body: createdAlert } = await supertest + .post(`${getUrlPrefix(Spaces.space1.id)}/api/alerting/rule`) + .set('kbn-xsrf', 'foo') + .send(getTestRuleData()) + .expect(200); + + objectRemover.add(Spaces.space1.id, createdAlert.id, 'rule', 'alerting'); + + const updatedData = { + name: 'bcd', + tags: ['bar'], + params: { + foo: true, + risk_score: 40, + severity: 'medium', + }, + schedule: { interval: '12s' }, + actions: [ + { + // group is missing + id: 'test-id', + params: {}, + }, + ], + throttle: '1m', + notify_when: 'onThrottleInterval', + }; + + const response = await supertest + .put(`${getUrlPrefix(Spaces.space1.id)}/api/alerting/rule/${createdAlert.id}`) + .set('kbn-xsrf', 'foo') + .send(updatedData); + + expect(response.status).to.eql(400); + expect(response.body).to.eql({ + statusCode: 400, + error: 'Bad Request', + message: 'Group is not defined in action test-id', + }); + }); + describe('legacy', () => { it('should handle update alert request appropriately', async () => { const { body: createdAlert } = await supertest diff --git a/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/group4/migrations.ts b/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/group4/migrations.ts index b339f9492b054..2cb5a616320eb 100644 --- a/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/group4/migrations.ts +++ b/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/group4/migrations.ts @@ -208,7 +208,7 @@ export default function createGetTests({ getService }: FtrProviderContext) { }); it('7.16.0 migrates existing alerts to contain legacyId field', async () => { - const searchResult = await es.search( + const searchResult = await es.search<{ alert: RawRule }>( { index: ALERTING_CASES_SAVED_OBJECT_INDEX, body: { @@ -224,13 +224,11 @@ export default function createGetTests({ getService }: FtrProviderContext) { expect(searchResult.statusCode).toEqual(200); expect((searchResult.body.hits.total as estypes.SearchTotalHits).value).toEqual(1); const hit = searchResult.body.hits.hits[0]; - expect((hit!._source!.alert! as RawRule).legacyId).toEqual( - '74f3e6d7-b7bb-477d-ac28-92ee22728e6e' - ); + expect(hit!._source!.alert.legacyId).toEqual('74f3e6d7-b7bb-477d-ac28-92ee22728e6e'); }); it('7.16.0 migrates existing rules so predefined connectors are not stored in references', async () => { - const searchResult = await es.search( + const searchResult = await es.search<{ alert: RawRule; references: {} }>( { index: ALERTING_CASES_SAVED_OBJECT_INDEX, body: { @@ -246,7 +244,7 @@ export default function createGetTests({ getService }: FtrProviderContext) { expect(searchResult.statusCode).toEqual(200); expect((searchResult.body.hits.total as estypes.SearchTotalHits).value).toEqual(1); const hit = searchResult.body.hits.hits[0]; - expect((hit!._source!.alert! as RawRule).actions! as RawRuleAction[]).toEqual([ + expect(hit!._source!.alert.actions! as RawRuleAction[]).toEqual([ expect.objectContaining({ actionRef: 'action_0', actionTypeId: 'test.noop', @@ -448,7 +446,7 @@ export default function createGetTests({ getService }: FtrProviderContext) { }); it('8.4.1 removes IsSnoozedUntil', async () => { - const searchResult = await es.search( + const searchResult = await es.search<{ alert: RawRule }>( { index: ALERTING_CASES_SAVED_OBJECT_INDEX, body: { @@ -464,7 +462,7 @@ export default function createGetTests({ getService }: FtrProviderContext) { expect(searchResult.statusCode).toEqual(200); const hit = searchResult.body.hits.hits[0]; - expect((hit!._source!.alert! as RawRule).isSnoozedUntil).toBe(undefined); + expect(hit!._source!.alert.isSnoozedUntil).toBe(undefined); }); it('8.5.0 removes runtime and field params from older ES Query rules', async () => { @@ -587,7 +585,7 @@ export default function createGetTests({ getService }: FtrProviderContext) { }); it('8.7.0 adds aggType and groupBy to ES query rules', async () => { - const response = await es.search( + const response = await es.search<{ alert: RawRule }>( { index: ALERTING_CASES_SAVED_OBJECT_INDEX, body: { @@ -608,8 +606,8 @@ export default function createGetTests({ getService }: FtrProviderContext) { ); expect(response.statusCode).toEqual(200); response.body.hits.hits.forEach((hit) => { - expect((hit?._source?.alert as RawRule)?.params?.aggType).toEqual('count'); - expect((hit?._source?.alert as RawRule)?.params?.groupBy).toEqual('all'); + expect(hit?._source?.alert?.params?.aggType).toEqual('count'); + expect(hit?._source?.alert?.params?.groupBy).toEqual('all'); }); }); diff --git a/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/group4/migrations/8_2_0.ts b/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/group4/migrations/8_2_0.ts index a075fdaa387a5..7547682ebf485 100644 --- a/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/group4/migrations/8_2_0.ts +++ b/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/group4/migrations/8_2_0.ts @@ -31,7 +31,7 @@ export default function createGetTests({ getService }: FtrProviderContext) { describe('rule with null snoozeEndTime value', async () => { it('has snoozeEndTime removed', async () => { - const response = await es.get<{ alert: RawRule }>( + const response = await es.get<{ alert: RawRule & { snoozeEndTime?: string } }>( { index: ALERTING_CASES_SAVED_OBJECT_INDEX, id: 'alert:bdfce750-fba0-11ec-9157-2f379249da99', @@ -62,7 +62,7 @@ export default function createGetTests({ getService }: FtrProviderContext) { describe('rules with snoozeEndTime value', async () => { it('has snoozeEndTime migrated to snoozeSchedule', async () => { - const response = await es.get<{ alert: RawRule }>( + const response = await es.get<{ alert: RawRule & { snoozeEndTime?: string } }>( { index: ALERTING_CASES_SAVED_OBJECT_INDEX, id: 'alert:402084f0-fbb8-11ec-856c-39466bd4c433', diff --git a/x-pack/test/dataset_quality_api_integration/tests/data_streams/integration_dashboards.spec.ts b/x-pack/test/dataset_quality_api_integration/tests/data_streams/integration_dashboards.spec.ts new file mode 100644 index 0000000000000..e29b934223a31 --- /dev/null +++ b/x-pack/test/dataset_quality_api_integration/tests/data_streams/integration_dashboards.spec.ts @@ -0,0 +1,116 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import expect from '@kbn/expect'; +import { DatasetQualityApiClientKey } from '../../common/config'; +import { FtrProviderContext } from '../../common/ftr_provider_context'; + +interface IntegrationPackage { + name: string; + version: string; +} + +export default function ApiTest({ getService }: FtrProviderContext) { + const registry = getService('registry'); + const supertest = getService('supertest'); + const datasetQualityApiClient = getService('datasetQualityApiClient'); + + const integrationPackages: IntegrationPackage[] = [ + { + // with dashboards + name: 'postgresql', + version: '1.19.0', + }, + { + // without dashboards + name: 'apm', + version: '8.4.2', + }, + ]; + + async function installPackage({ name, version }: IntegrationPackage) { + return supertest + .post(`/api/fleet/epm/packages/${name}/${version}`) + .set('kbn-xsrf', 'xxxx') + .send({ force: true }); + } + + async function uninstallPackage({ name, version }: IntegrationPackage) { + return supertest.delete(`/api/fleet/epm/packages/${name}/${version}`).set('kbn-xsrf', 'xxxx'); + } + + async function callApiAs(integration: string) { + const user = 'datasetQualityLogsUser' as DatasetQualityApiClientKey; + return await datasetQualityApiClient[user]({ + endpoint: 'GET /internal/dataset_quality/integrations/{integration}/dashboards', + params: { + path: { + integration, + }, + }, + }); + } + + registry.when('Integration dashboards', { config: 'basic' }, () => { + describe('gets the installed integration dashboards', () => { + before(async () => { + await Promise.all( + integrationPackages.map((pkg: IntegrationPackage) => installPackage(pkg)) + ); + }); + + it('returns a non-empty body', async () => { + const resp = await callApiAs(integrationPackages[0].name); + expect(resp.body).not.empty(); + }); + + it('returns a list of dashboards in the correct format', async () => { + const expectedResult = { + dashboards: [ + { + id: 'postgresql-158be870-87f4-11e7-ad9c-db80de0bf8d3', + title: '[Logs PostgreSQL] Overview', + }, + { + id: 'postgresql-4288b790-b79f-11e9-a579-f5c0a5d81340', + title: '[Metrics PostgreSQL] Database Overview', + }, + { + id: 'postgresql-e4c5f230-87f3-11e7-ad9c-db80de0bf8d3', + title: '[Logs PostgreSQL] Query Duration Overview', + }, + ], + }; + const resp = await callApiAs(integrationPackages[0].name); + expect(resp.body).to.eql(expectedResult); + }); + + it('returns an empty array for an integration without dashboards', async () => { + const expectedResult = { + dashboards: [], + }; + const resp = await callApiAs(integrationPackages[1].name); + expect(resp.body).to.eql(expectedResult); + }); + + it('returns an empty array for an invalid integration', async () => { + const expectedResult = { + dashboards: [], + }; + const resp = await callApiAs('invalid'); + expect(resp.body).to.eql(expectedResult); + }); + + after( + async () => + await Promise.all( + integrationPackages.map((pkg: IntegrationPackage) => uninstallPackage(pkg)) + ) + ); + }); + }); +} diff --git a/x-pack/test/fleet_api_integration/apis/agent_policy/index.js b/x-pack/test/fleet_api_integration/apis/agent_policy/index.js index 21aa25cd3c0d3..559367c1a6b81 100644 --- a/x-pack/test/fleet_api_integration/apis/agent_policy/index.js +++ b/x-pack/test/fleet_api_integration/apis/agent_policy/index.js @@ -10,5 +10,6 @@ export default function loadTests({ loadTestFile }) { loadTestFile(require.resolve('./agent_policy_with_agents_setup')); loadTestFile(require.resolve('./agent_policy')); loadTestFile(require.resolve('./agent_policy_datastream_permissions')); + loadTestFile(require.resolve('./privileges')); }); } diff --git a/x-pack/test/fleet_api_integration/apis/agent_policy/privileges.ts b/x-pack/test/fleet_api_integration/apis/agent_policy/privileges.ts new file mode 100644 index 0000000000000..179375fffdbbb --- /dev/null +++ b/x-pack/test/fleet_api_integration/apis/agent_policy/privileges.ts @@ -0,0 +1,174 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { FtrProviderContext } from '../../../api_integration/ftr_provider_context'; +import { runPrivilegeTests } from '../../privileges_helpers'; +import { setupTestUsers, testUsers } from '../test_users'; + +const READ_SCENARIOS = [ + { + user: testUsers.fleet_all_only, + statusCode: 200, + }, + { + user: testUsers.fleet_read_only, + statusCode: 200, + }, + { + user: testUsers.fleet_agent_policies_read_only, + statusCode: 200, + }, + { + user: testUsers.fleet_agent_policies_all_only, + statusCode: 200, + }, + { + // Expect minimal access + user: testUsers.fleet_agents_read_only, + statusCode: 200, + }, + { + user: testUsers.fleet_no_access, + statusCode: 403, + }, + { + user: testUsers.fleet_minimal_all_only, + statusCode: 403, + }, + { + user: testUsers.fleet_minimal_read_only, + statusCode: 403, + }, + { + user: testUsers.fleet_settings_read_only, + statusCode: 403, + }, +]; + +const ALL_SCENARIOS = [ + { + user: testUsers.fleet_all_only, + statusCode: 200, + }, + { + user: testUsers.fleet_agent_policies_all_only, + statusCode: 200, + }, + { + user: testUsers.fleet_read_only, + statusCode: 403, + }, + { + user: testUsers.fleet_agent_policies_read_only, + statusCode: 403, + }, + { + user: testUsers.fleet_agents_read_only, + statusCode: 403, + }, + { + user: testUsers.fleet_no_access, + statusCode: 403, + }, + { + user: testUsers.fleet_minimal_all_only, + statusCode: 403, + }, + { + user: testUsers.fleet_minimal_read_only, + statusCode: 403, + }, + { + user: testUsers.fleet_settings_read_only, + statusCode: 403, + }, +]; + +export default function (providerContext: FtrProviderContext) { + const { getService } = providerContext; + const supertest = getService('supertest'); + const supertestWithoutAuth = getService('supertestWithoutAuth'); + const esArchiver = getService('esArchiver'); + const kibanaServer = getService('kibanaServer'); + + const ROUTES = [ + { + method: 'GET', + path: '/api/fleet/agent_policies', + scenarios: READ_SCENARIOS, + }, + { + method: 'GET', + path: '/api/fleet/agent_policies/policy-test-privileges-1', + scenarios: READ_SCENARIOS, + }, + { + method: 'POST', + path: '/api/fleet/agent_policies', + scenarios: ALL_SCENARIOS, + send: { + id: 'policy-test-privileges-2', + name: `TEST ${Date.now()}`, + namespace: 'default', + }, + afterEach: async () => { + await supertest + .post(`/api/fleet/agent_policies/delete`) + .set('kbn-xsrf', 'xxxx') + .send({ agentPolicyId: 'policy-test-privileges-2' }); + }, + }, + { + method: 'POST', + path: '/api/fleet/agent_policies/delete', + send: { agentPolicyId: 'policy-test-privileges-2' }, + scenarios: ALL_SCENARIOS, + beforeEach: async () => { + await supertest + .post(`/api/fleet/agent_policies`) + .send({ + id: 'policy-test-privileges-2', + name: `TEST ${Date.now()}`, + namespace: 'default', + }) + .set('kbn-xsrf', 'xxxx'); + }, + afterEach: async () => { + await supertest + .post(`/api/fleet/agent_policies/delete`) + .set('kbn-xsrf', 'xxxx') + .send({ agentPolicyId: 'policy-test-privileges-2' }); + }, + }, + ]; + + describe('fleet_agent_policies_privileges', () => { + before(async () => { + await esArchiver.load('x-pack/test/functional/es_archives/fleet/empty_fleet_server'); + await kibanaServer.savedObjects.cleanStandardList(); + await setupTestUsers(getService('security')); + }); + + before(async () => { + await supertest + .post(`/api/fleet/agent_policies`) + .set('kbn-xsrf', 'xxxx') + .send({ + id: 'policy-test-privileges-1', + name: `TEST ${Date.now()}`, + namespace: 'default', + }) + .expect(200); + }); + + after(async () => { + await kibanaServer.savedObjects.cleanStandardList(); + }); + + runPrivilegeTests(ROUTES, supertestWithoutAuth); + }); +} diff --git a/x-pack/test/fleet_api_integration/apis/agents/delete.ts b/x-pack/test/fleet_api_integration/apis/agents/delete.ts index 6b05d3e66cb39..43be03e3182a7 100644 --- a/x-pack/test/fleet_api_integration/apis/agents/delete.ts +++ b/x-pack/test/fleet_api_integration/apis/agents/delete.ts @@ -64,17 +64,6 @@ export default function ({ getService }: FtrProviderContext) { after(async () => { await esArchiver.unload('x-pack/test/functional/es_archives/fleet/agents'); }); - it('should return a 403 if user lacks fleet all permissions', async () => { - const { body: apiResponse } = await supertest - .delete(`/api/fleet/agents/agent1`) - .auth(users.fleet_user.username, users.fleet_user.password) - .set('kbn-xsrf', 'xx') - .expect(403); - - expect(apiResponse).not.to.eql({ - action: 'deleted', - }); - }); it('should return a 404 if there is no agent to delete', async () => { await supertest diff --git a/x-pack/test/fleet_api_integration/apis/agents/index.js b/x-pack/test/fleet_api_integration/apis/agents/index.js index 82e6b14a67712..1bfc45740a687 100644 --- a/x-pack/test/fleet_api_integration/apis/agents/index.js +++ b/x-pack/test/fleet_api_integration/apis/agents/index.js @@ -26,5 +26,6 @@ export default function loadTests({ loadTestFile, getService }) { loadTestFile(require.resolve('./request_diagnostics')); loadTestFile(require.resolve('./uploads')); loadTestFile(require.resolve('./get_agents_by_actions')); + loadTestFile(require.resolve('./privileges')); }); } diff --git a/x-pack/test/fleet_api_integration/apis/agents/list.ts b/x-pack/test/fleet_api_integration/apis/agents/list.ts index c6fd4a0a1024a..535047afc6b71 100644 --- a/x-pack/test/fleet_api_integration/apis/agents/list.ts +++ b/x-pack/test/fleet_api_integration/apis/agents/list.ts @@ -9,11 +9,9 @@ import expect from '@kbn/expect'; import { type Agent, FLEET_ELASTIC_AGENT_PACKAGE, AGENTS_INDEX } from '@kbn/fleet-plugin/common'; import { FtrProviderContext } from '../../../api_integration/ftr_provider_context'; -import { testUsers } from '../test_users'; export default function ({ getService }: FtrProviderContext) { const esArchiver = getService('esArchiver'); - const supertestWithoutAuth = getService('supertestWithoutAuth'); const supertest = getService('supertest'); const es = getService('es'); let elasticAgentpkgVersion: string; @@ -46,34 +44,6 @@ export default function ({ getService }: FtrProviderContext) { }); }); - it('should return a 200 if a user with the fleet all try to access the list', async () => { - await supertestWithoutAuth - .get(`/api/fleet/agents`) - .auth(testUsers.fleet_all_only.username, testUsers.fleet_all_only.password) - .expect(200); - }); - - it('should return a 200 if a user with the fleet read try to access the list', async () => { - await supertestWithoutAuth - .get(`/api/fleet/agents`) - .auth(testUsers.fleet_read_only.username, testUsers.fleet_read_only.password) - .expect(200); - }); - - it('should return a 200 if a user with the fleet agents read try to access the list', async () => { - await supertestWithoutAuth - .get(`/api/fleet/agents`) - .auth(testUsers.fleet_agents_read_only.username, testUsers.fleet_agents_read_only.password) - .expect(200); - }); - - it('should not return the list of agents when requesting as a user without fleet permissions', async () => { - await supertestWithoutAuth - .get(`/api/fleet/agents`) - .auth(testUsers.fleet_no_access.username, testUsers.fleet_no_access.password) - .expect(403); - }); - it('should return the list of agents when requesting as admin', async () => { const { body: apiResponse } = await supertest.get(`/api/fleet/agents`).expect(200); diff --git a/x-pack/test/fleet_api_integration/apis/agents/privileges.ts b/x-pack/test/fleet_api_integration/apis/agents/privileges.ts new file mode 100644 index 0000000000000..82999acd9f7c6 --- /dev/null +++ b/x-pack/test/fleet_api_integration/apis/agents/privileges.ts @@ -0,0 +1,257 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor 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 { + FLEET_ELASTIC_AGENT_PACKAGE, + AGENTS_INDEX, + PACKAGE_POLICY_SAVED_OBJECT_TYPE, +} from '@kbn/fleet-plugin/common'; + +import { FtrProviderContext } from '../../../api_integration/ftr_provider_context'; +import { generateAgent } from '../../helpers'; +import { runPrivilegeTests } from '../../privileges_helpers'; +import { testUsers } from '../test_users'; + +export default function (providerContext: FtrProviderContext) { + const { getService } = providerContext; + const esArchiver = getService('esArchiver'); + const supertestWithoutAuth = getService('supertestWithoutAuth'); + const supertest = getService('supertest'); + const kibanaServer = getService('kibanaServer'); + const es = getService('es'); + let elasticAgentpkgVersion: string; + + describe('fleet_agents_api_privileges', () => { + before(async () => { + await esArchiver.loadIfNeeded('x-pack/test/functional/es_archives/fleet/agents'); + const getPkRes = await supertest + .get(`/api/fleet/epm/packages/${FLEET_ELASTIC_AGENT_PACKAGE}`) + .set('kbn-xsrf', 'xxxx') + .expect(200); + elasticAgentpkgVersion = getPkRes.body.item.version; + // Install latest version of the package + await supertest + .post(`/api/fleet/epm/packages/${FLEET_ELASTIC_AGENT_PACKAGE}/${elasticAgentpkgVersion}`) + .send({ + force: true, + }) + .set('kbn-xsrf', 'xxxx') + .expect(200); + }); + after(async () => { + await esArchiver.unload('x-pack/test/functional/es_archives/fleet/agents'); + await supertest + .delete(`/api/fleet/epm/packages/${FLEET_ELASTIC_AGENT_PACKAGE}/${elasticAgentpkgVersion}`) + .set('kbn-xsrf', 'xxxx'); + await es.transport + .request({ + method: 'DELETE', + path: `/_data_stream/metrics-elastic_agent.elastic_agent-default`, + }) + .catch(() => {}); + }); + + const fleetServerVersion = '8.14.0'; + + before(async () => { + await supertest.post(`/api/fleet/agent_policies`).set('kbn-xsrf', 'kibana').send({ + name: 'Fleet Server policy 1', + id: 'fleet-server-policy', + namespace: 'default', + has_fleet_server: true, + }); + + await kibanaServer.savedObjects.create({ + id: `package-policy-test`, + type: PACKAGE_POLICY_SAVED_OBJECT_TYPE, + overwrite: true, + attributes: { + policy_id: 'fleet-server-policy', + name: 'Fleet Server', + package: { + name: 'fleet_server', + }, + }, + }); + await generateAgent( + providerContext, + 'healthy', + 'agentWithFS', + 'fleet-server-policy', + fleetServerVersion + ); + }); + + const READ_SCENARIOS = [ + { + user: testUsers.fleet_all_only, + statusCode: 200, + }, + { + user: testUsers.fleet_read_only, + statusCode: 200, + }, + { + user: testUsers.fleet_agents_read_only, + statusCode: 200, + }, + { + user: testUsers.fleet_no_access, + statusCode: 403, + }, + { + user: testUsers.fleet_minimal_all_only, + statusCode: 403, + }, + { + user: testUsers.fleet_minimal_read_only, + statusCode: 403, + }, + { + user: testUsers.fleet_settings_read_only, + statusCode: 403, + }, + ]; + + const ALL_SCENARIOS = [ + { + user: testUsers.fleet_all_only, + statusCode: 200, + }, + { + user: testUsers.fleet_agents_all_only, + statusCode: 200, + }, + { + user: testUsers.fleet_agents_read_only, + statusCode: 403, + }, + { + user: testUsers.fleet_read_only, + statusCode: 403, + }, + { + user: testUsers.fleet_no_access, + statusCode: 403, + }, + { + user: testUsers.fleet_minimal_all_only, + statusCode: 403, + }, + { + user: testUsers.fleet_minimal_read_only, + statusCode: 403, + }, + { + user: testUsers.fleet_settings_read_only, + statusCode: 403, + }, + ]; + + let agentDoc: any; + const updateAgentBeforeEach = async () => { + const res = await es.get({ + id: 'agent1', + index: AGENTS_INDEX, + }); + + agentDoc = res._source; + }; + const updateAgentAfterEach = async () => { + await es.update({ + id: 'agent1', + refresh: 'wait_for', + index: AGENTS_INDEX, + doc_as_upsert: true, + doc: { + upgrade_details: null, + upgrade_started_at: null, + upgraded_at: null, + unenrollment_started_at: null, + ...agentDoc, + }, + }); + }; + + const ROUTES = [ + // READ scenarios + { + method: 'GET', + path: '/api/fleet/agents', + scenarios: READ_SCENARIOS, + }, + { + method: 'GET', + path: '/api/fleet/agent-status', + scenarios: READ_SCENARIOS, + }, + { + method: 'GET', + path: '/api/fleet/agents/action_status', + scenarios: READ_SCENARIOS, + }, + { + method: 'GET', + path: '/api/fleet/agents/agent1', + scenarios: READ_SCENARIOS, + }, + { + method: 'POST', + path: '/api/fleet/agents/agent1/request_diagnostics', + scenarios: READ_SCENARIOS, + }, + + // ALL scenarios + { + method: 'PUT', + path: '/api/fleet/agents/agent1', + scenarios: ALL_SCENARIOS, + send: { + tags: ['tag1'], + }, + }, + { + method: 'POST', + path: '/api/fleet/agents/agent1/unenroll', + scenarios: ALL_SCENARIOS, + beforeEach: updateAgentBeforeEach, + afterEach: updateAgentAfterEach, + }, + { + method: 'POST', + path: '/api/fleet/agents/agent1/upgrade', + scenarios: ALL_SCENARIOS, + send: { + version: fleetServerVersion, + }, + beforeEach: updateAgentBeforeEach, + afterEach: updateAgentAfterEach, + }, + { + method: 'DELETE', + path: '/api/fleet/agents/agent1', + scenarios: ALL_SCENARIOS, + beforeEach: updateAgentBeforeEach, + afterEach: updateAgentAfterEach, + }, + ]; + before(async () => { + // Make agent 1 upgradeable + await es.update({ + id: 'agent1', + refresh: 'wait_for', + index: AGENTS_INDEX, + doc: { + local_metadata: { + elastic: { agent: { upgradeable: true, version: '8.13.0' } }, + }, + }, + }); + }); + runPrivilegeTests(ROUTES, supertestWithoutAuth); + }); +} diff --git a/x-pack/test/fleet_api_integration/apis/agents/update_agent_tags.ts b/x-pack/test/fleet_api_integration/apis/agents/update_agent_tags.ts index 592f2d964b748..92e0500f5e365 100644 --- a/x-pack/test/fleet_api_integration/apis/agents/update_agent_tags.ts +++ b/x-pack/test/fleet_api_integration/apis/agents/update_agent_tags.ts @@ -8,13 +8,11 @@ import expect from '@kbn/expect'; import { FtrProviderContext } from '../../../api_integration/ftr_provider_context'; import { setupFleetAndAgents } from './services'; -import { testUsers } from '../test_users'; export default function (providerContext: FtrProviderContext) { const { getService } = providerContext; const esArchiver = getService('esArchiver'); const supertest = getService('supertest'); - const supertestWithoutAuth = getService('supertestWithoutAuth'); describe('fleet_update_agent_tags', () => { before(async () => { @@ -170,19 +168,6 @@ export default function (providerContext: FtrProviderContext) { .expect(400); }); - it('should return a 403 if user lacks "fleet all" permissions', async () => { - await supertestWithoutAuth - .post(`/api/fleet/agents/bulk_update_agent_tags`) - .auth(testUsers.fleet_no_access.username, testUsers.fleet_no_access.password) - .set('kbn-xsrf', 'xxx') - .send({ - agents: ['agent2', 'agent3'], - tagsToAdd: ['newTag'], - tagsToRemove: ['existingTag'], - }) - .expect(403); - }); - it('should not update tags of hosted agent', async () => { // move agent2 to policy2 to keep it regular await supertest.put(`/api/fleet/agents/agent2/reassign`).set('kbn-xsrf', 'xxx').send({ diff --git a/x-pack/test/fleet_api_integration/apis/enrollment_api_keys/crud.ts b/x-pack/test/fleet_api_integration/apis/enrollment_api_keys/crud.ts index 9597c3ac38e68..d5e59733a008b 100644 --- a/x-pack/test/fleet_api_integration/apis/enrollment_api_keys/crud.ts +++ b/x-pack/test/fleet_api_integration/apis/enrollment_api_keys/crud.ts @@ -52,13 +52,6 @@ export default function (providerContext: FtrProviderContext) { .expect(200); }); - it('should return 403 if the user does not have correct permissions', async () => { - await supertestWithoutAuth - .get(`/api/fleet/enrollment_api_keys`) - .auth(testUsers.integr_all_only.username, testUsers.integr_all_only.password) - .expect(403); - }); - it('should return 200 if the passed kuery is correct', async () => { await supertest .get(`/api/fleet/enrollment_api_keys?kuery=fleet-enrollment-api-keys.policy_id:policy1`) @@ -105,13 +98,6 @@ export default function (providerContext: FtrProviderContext) { .auth(testUsers.setup.username, testUsers.setup.password) .expect(200); }); - - it('should return 403 if the user does not have correct permissions', async () => { - await supertestWithoutAuth - .get(`/api/fleet/enrollment_api_keys/${ENROLLMENT_KEY_ID}`) - .auth(testUsers.integr_all_only.username, testUsers.integr_all_only.password) - .expect(403); - }); }); describe('DELETE /fleet/enrollment_api_keys/{id}', async () => { @@ -140,14 +126,6 @@ export default function (providerContext: FtrProviderContext) { expect(apiKeys).length(1); expect(apiKeys[0].invalidated).eql(true); }); - - it('should return 403 if the user does not have correct permissions', async () => { - await supertestWithoutAuth - .delete(`/api/fleet/enrollment_api_keys/${keyId}`) - .auth(testUsers.integr_all_only.username, testUsers.integr_all_only.password) - .set('kbn-xsrf', 'xxx') - .expect(403); - }); }); describe('POST /fleet/enrollment_api_keys', () => { @@ -185,17 +163,6 @@ export default function (providerContext: FtrProviderContext) { expect(apiResponse.item).to.have.keys('id', 'api_key', 'api_key_id', 'name', 'policy_id'); }); - it('should return 403 if the user does not have correct permissions', async () => { - await supertestWithoutAuth - .post(`/api/fleet/enrollment_api_keys`) - .auth(testUsers.integr_all_only.username, testUsers.integr_all_only.password) - .set('kbn-xsrf', 'xxx') - .send({ - policy_id: 'policy1', - }) - .expect(403); - }); - it('should allow to create an enrollment api key with agent policy and unique name', async () => { const { body: noSpacesRes } = await supertest .post(`/api/fleet/enrollment_api_keys`) diff --git a/x-pack/test/fleet_api_integration/apis/enrollment_api_keys/privileges.ts b/x-pack/test/fleet_api_integration/apis/enrollment_api_keys/privileges.ts new file mode 100644 index 0000000000000..187fcacab570d --- /dev/null +++ b/x-pack/test/fleet_api_integration/apis/enrollment_api_keys/privileges.ts @@ -0,0 +1,105 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { FtrProviderContext } from '../../../api_integration/ftr_provider_context'; +import { skipIfNoDockerRegistry } from '../../helpers'; +import { runPrivilegeTests } from '../../privileges_helpers'; +import { setupFleetAndAgents } from '../agents/services'; +import { testUsers } from '../test_users'; + +export default function (providerContext: FtrProviderContext) { + const { getService } = providerContext; + const esArchiver = getService('esArchiver'); + const supertestWithoutAuth = getService('supertestWithoutAuth'); + const supertest = getService('supertest'); + + describe('fleet_enrollment_api_keys_privileges', () => { + before(async () => { + await esArchiver.loadIfNeeded('x-pack/test/functional/es_archives/fleet/agents'); + }); + + after(async () => { + await esArchiver.unload('x-pack/test/functional/es_archives/fleet/agents'); + }); + + skipIfNoDockerRegistry(providerContext); + setupFleetAndAgents(providerContext); + + const SCENARIOS = [ + { + user: testUsers.fleet_all_only, + statusCode: 200, + }, + { + user: testUsers.fleet_agents_all_only, + statusCode: 200, + }, + { + user: testUsers.fleet_agents_read_only, + statusCode: 403, + }, + { + user: testUsers.fleet_read_only, + statusCode: 403, + }, + { + user: testUsers.fleet_no_access, + statusCode: 403, + }, + { + user: testUsers.fleet_minimal_all_only, + statusCode: 403, + }, + { + user: testUsers.fleet_minimal_read_only, + statusCode: 403, + }, + { + user: testUsers.fleet_settings_read_only, + statusCode: 403, + }, + ]; + + let enrollmentKeyId = ''; + + const ROUTES = [ + { + method: 'GET', + path: '/api/fleet/enrollment_api_keys', + scenarios: SCENARIOS, + }, + { + method: 'POST', + path: '/api/fleet/enrollment_api_keys', + scenarios: SCENARIOS, + send: { + policy_id: 'policy1', + }, + }, + { + method: 'DELETE', + path: () => `/api/fleet/enrollment_api_keys/${enrollmentKeyId}`, + scenarios: SCENARIOS, + send: { + policy_id: 'policy1', + }, + beforeEach: async () => { + const { body: apiResponse } = await supertest + .post(`/api/fleet/enrollment_api_keys`) + .set('kbn-xsrf', 'xxx') + .send({ + policy_id: 'policy1', + }) + .expect(200); + enrollmentKeyId = apiResponse.item.id; + }, + }, + ]; + before(async () => {}); + runPrivilegeTests(ROUTES, supertestWithoutAuth); + }); +} diff --git a/x-pack/test/fleet_api_integration/apis/fleet_settings_privileges.ts b/x-pack/test/fleet_api_integration/apis/fleet_settings_privileges.ts new file mode 100644 index 0000000000000..599204ab0f342 --- /dev/null +++ b/x-pack/test/fleet_api_integration/apis/fleet_settings_privileges.ts @@ -0,0 +1,315 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { FtrProviderContext } from '../../api_integration/ftr_provider_context'; +import { runPrivilegeTests } from '../privileges_helpers'; +import { setupTestUsers, testUsers } from './test_users'; + +const READ_SCENARIOS = [ + { + user: testUsers.fleet_all_only, + statusCode: 200, + }, + { + user: testUsers.fleet_read_only, + statusCode: 200, + }, + { + user: testUsers.fleet_settings_all_only, + statusCode: 200, + }, + { + user: testUsers.fleet_settings_read_only, + statusCode: 200, + }, + { + user: testUsers.fleet_agent_policies_read_only, + statusCode: 403, + }, + { + user: testUsers.fleet_agent_policies_all_only, + statusCode: 403, + }, + { + user: testUsers.fleet_agents_read_only, + statusCode: 403, + }, + { + user: testUsers.fleet_no_access, + statusCode: 403, + }, + { + user: testUsers.fleet_minimal_all_only, + statusCode: 403, + }, + { + user: testUsers.fleet_minimal_read_only, + statusCode: 403, + }, +]; + +const ALL_SCENARIOS = [ + { + user: testUsers.fleet_all_only, + statusCode: 200, + }, + { + user: testUsers.fleet_settings_all_only, + statusCode: 200, + }, + { + user: testUsers.fleet_settings_read_only, + statusCode: 403, + }, + { + user: testUsers.fleet_read_only, + statusCode: 403, + }, + { + user: testUsers.fleet_agent_policies_read_only, + statusCode: 403, + }, + { + user: testUsers.fleet_agent_policies_all_only, + statusCode: 403, + }, + { + user: testUsers.fleet_agents_read_only, + statusCode: 403, + }, + { + user: testUsers.fleet_no_access, + statusCode: 403, + }, + { + user: testUsers.fleet_minimal_all_only, + statusCode: 403, + }, + { + user: testUsers.fleet_minimal_read_only, + statusCode: 403, + }, +]; + +export default function (providerContext: FtrProviderContext) { + const { getService } = providerContext; + const supertest = getService('supertest'); + const supertestWithoutAuth = getService('supertestWithoutAuth'); + const esArchiver = getService('esArchiver'); + const kibanaServer = getService('kibanaServer'); + + const ROUTES = [ + { + method: 'GET', + path: '/api/fleet/outputs', + scenarios: READ_SCENARIOS, + }, + { + method: 'GET', + path: '/api/fleet/outputs/test-privileges-output-1', + scenarios: READ_SCENARIOS, + }, + { + method: 'POST', + path: '/api/fleet/outputs', + scenarios: ALL_SCENARIOS, + send: { + id: 'test-privileges-output-2', + name: 'Test privileges es output 2' + new Date().toISOString(), + type: 'elasticsearch', + hosts: ['https://test.fr'], + }, + afterEach: async () => { + await supertest + .delete(`/api/fleet/outputs/test-privileges-output-2`) + .set('kbn-xsrf', 'xxxx'); + }, + }, + { + method: 'DELETE', + path: '/api/fleet/outputs/test-privileges-output-2', + scenarios: ALL_SCENARIOS, + beforeEach: async () => { + await supertest + .post(`/api/fleet/outputs`) + .set('kbn-xsrf', 'xxxx') + .send({ + id: 'test-privileges-output-2', + name: 'Test privileges es output 2' + new Date().toISOString(), + type: 'elasticsearch', + hosts: ['https://test.fr'], + }) + .expect(200); + }, + afterEach: async () => { + await supertest + .delete(`/api/fleet/outputs/test-privileges-output-2`) + .set('kbn-xsrf', 'xxxx'); + }, + }, + { + method: 'GET', + path: '/api/fleet/fleet_server_hosts', + scenarios: READ_SCENARIOS, + }, + { + method: 'GET', + path: '/api/fleet/fleet_server_hosts/test-privileges-fleet-server-hosts-1', + scenarios: READ_SCENARIOS, + }, + { + method: 'POST', + path: '/api/fleet/fleet_server_hosts', + scenarios: ALL_SCENARIOS, + send: { + id: 'test-privileges-fleet-server-hosts-2', + name: 'Test privileges fleet server host 2' + new Date().toISOString(), + is_default: false, + host_urls: ['https://test.fr:8080', 'https://test.fr:8081'], + }, + afterEach: async () => { + await supertest + .delete(`/api/fleet/fleet_server_hosts/test-privileges-fleet-server-hosts-2`) + .set('kbn-xsrf', 'xxxx'); + }, + }, + { + method: 'DELETE', + path: '/api/fleet/fleet_server_hosts/test-privileges-fleet-server-hosts-2', + scenarios: ALL_SCENARIOS, + beforeEach: async () => { + await supertest + .post(`/api/fleet/fleet_server_hosts`) + .set('kbn-xsrf', 'xxxx') + .send({ + id: 'test-privileges-fleet-server-hosts-2', + name: 'Test privileges fleet server host 2' + new Date().toISOString(), + is_default: false, + host_urls: ['https://test.fr:8080', 'https://test.fr:8081'], + }); + }, + afterEach: async () => { + await supertest + .delete(`/api/fleet/fleet_server_hosts/test-privileges-fleet-server-hosts-2`) + .set('kbn-xsrf', 'xxxx'); + }, + }, + { + method: 'GET', + path: '/api/fleet/proxies', + scenarios: READ_SCENARIOS, + }, + { + method: 'GET', + path: '/api/fleet/proxies/test-privileges-fleet-proxy-1', + scenarios: READ_SCENARIOS, + }, + { + method: 'POST', + path: '/api/fleet/proxies', + scenarios: ALL_SCENARIOS, + send: { + id: 'test-privileges-fleet-proxy-2', + name: 'Test privileges proxy 2 ' + new Date().toISOString(), + url: 'https://test.fr:3232', + }, + afterEach: async () => { + await supertest + .delete(`/api/fleet/proxies/test-privileges-fleet-proxy-2`) + .set('kbn-xsrf', 'xxxx'); + }, + }, + { + method: 'GET', + path: '/api/fleet/agent_download_sources', + scenarios: READ_SCENARIOS, + }, + { + method: 'GET', + path: '/api/fleet/agent_download_sources/test-privileges-download-source-1', + scenarios: READ_SCENARIOS, + }, + { + method: 'POST', + path: '/api/fleet/agent_download_sources', + scenarios: ALL_SCENARIOS, + send: { + id: 'test-privileges-download-source-2', + name: 'Test download source 2 ' + new Date().toISOString(), + host: 'http://test.fr:443', + is_default: false, + }, + afterEach: async () => { + await supertest + .delete(`/api/fleet/agent_download_sources/test-privileges-download-source-2`) + .set('kbn-xsrf', 'xxxx'); + }, + }, + ]; + + describe('fleet_settings_privileges (Outputs, FleetServerHosts, Proxies, ...)', () => { + before(async () => { + await esArchiver.load('x-pack/test/functional/es_archives/fleet/empty_fleet_server'); + await kibanaServer.savedObjects.cleanStandardList(); + await setupTestUsers(getService('security')); + }); + + before('create output', () => + supertest + .post(`/api/fleet/outputs`) + .set('kbn-xsrf', 'xxxx') + .send({ + id: 'test-privileges-output-1', + name: 'Test privileges es output ' + new Date().toISOString(), + type: 'elasticsearch', + hosts: ['https://test.fr'], + }) + .expect(200) + ); + before('create fleet server hosts', () => + supertest + .post(`/api/fleet/fleet_server_hosts`) + .set('kbn-xsrf', 'xxxx') + .send({ + id: 'test-privileges-fleet-server-hosts-1', + name: 'Test privileges fleet server host ' + new Date().toISOString(), + is_default: false, + host_urls: ['https://test.fr:8080', 'https://test.fr:8081'], + }) + .expect(200) + ); + before('create proxy', () => + supertest + .post(`/api/fleet/proxies`) + .set('kbn-xsrf', 'xxxx') + .send({ + id: 'test-privileges-fleet-proxy-1', + name: 'Test privileges proxy 1 ' + new Date().toISOString(), + url: 'https://test.fr:3232', + }) + .expect(200) + ); + before('create download source', () => + supertest + .post(`/api/fleet/agent_download_sources`) + .set('kbn-xsrf', 'xxxx') + .send({ + id: 'test-privileges-download-source-1', + name: 'Test download source 1 ' + new Date().toISOString(), + host: 'http://test.fr:443', + is_default: false, + }) + .expect(200) + ); + + after(async () => { + await kibanaServer.savedObjects.cleanStandardList(); + }); + + runPrivilegeTests(ROUTES, supertestWithoutAuth); + }); +} diff --git a/x-pack/test/fleet_api_integration/apis/index.js b/x-pack/test/fleet_api_integration/apis/index.js index d1ad9e0b269e1..90930b343fb9b 100644 --- a/x-pack/test/fleet_api_integration/apis/index.js +++ b/x-pack/test/fleet_api_integration/apis/index.js @@ -20,6 +20,7 @@ export default function ({ loadTestFile, getService }) { loadTestFile(require.resolve('./policy_secrets')); // ~40s loadTestFile(require.resolve('./enrollment_api_keys/crud')); // ~ 20s + loadTestFile(require.resolve('./enrollment_api_keys/privileges')); // ~ 20s // Data Streams loadTestFile(require.resolve('./data_streams')); // ~ 20s @@ -50,5 +51,9 @@ export default function ({ loadTestFile, getService }) { // Uninstall tokens loadTestFile(require.resolve('./uninstall_token/get')); // ~ 20s + loadTestFile(require.resolve('./uninstall_token/privileges')); // ~ 20s + + // Uninstall tokens + loadTestFile(require.resolve('./fleet_settings_privileges')); // ~ 1m }); } diff --git a/x-pack/test/fleet_api_integration/apis/test_users.ts b/x-pack/test/fleet_api_integration/apis/test_users.ts index b5546d149eb98..bbb1b489fcca5 100644 --- a/x-pack/test/fleet_api_integration/apis/test_users.ts +++ b/x-pack/test/fleet_api_integration/apis/test_users.ts @@ -41,6 +41,16 @@ export const testUsers: { username: 'fleet_minimal_all_only', password: 'changeme', }, + fleet_minimal_read_only: { + permissions: { + feature: { + fleetv2: ['minimal_read'], + }, + spaces: ['*'], + }, + username: 'fleet_minimal_read_only', + password: 'changeme', + }, fleet_agents_read_only: { permissions: { feature: { @@ -51,6 +61,16 @@ export const testUsers: { username: 'fleet_agents_read_only', password: 'changeme', }, + fleet_agents_all_only: { + permissions: { + feature: { + fleetv2: ['agents_all'], + }, + spaces: ['*'], + }, + username: 'fleet_agents_all_only', + password: 'changeme', + }, fleet_settings_read_only: { permissions: { feature: { @@ -61,6 +81,36 @@ export const testUsers: { username: 'fleet_settings_read_only', password: 'changeme', }, + fleet_settings_all_only: { + permissions: { + feature: { + fleetv2: ['settings_all'], + }, + spaces: ['*'], + }, + username: 'fleet_settings_all_only', + password: 'changeme', + }, + fleet_agent_policies_read_only: { + permissions: { + feature: { + fleetv2: ['agent_policies_read'], + }, + spaces: ['*'], + }, + username: 'fleet_agent_policies_read_only', + password: 'changeme', + }, + fleet_agent_policies_all_only: { + permissions: { + feature: { + fleetv2: ['agent_policies_all'], + }, + spaces: ['*'], + }, + username: 'fleet_agent_policies_all_only', + password: 'changeme', + }, setup: { permissions: { feature: { diff --git a/x-pack/test/fleet_api_integration/apis/uninstall_token/privileges.ts b/x-pack/test/fleet_api_integration/apis/uninstall_token/privileges.ts new file mode 100644 index 0000000000000..ff348f2b9f4e6 --- /dev/null +++ b/x-pack/test/fleet_api_integration/apis/uninstall_token/privileges.ts @@ -0,0 +1,86 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { AgentPolicy } from '@kbn/fleet-plugin/common'; +import { FtrProviderContext } from '../../../api_integration/ftr_provider_context'; +import { skipIfNoDockerRegistry } from '../../helpers'; +import { runPrivilegeTests } from '../../privileges_helpers'; +import { setupFleetAndAgents } from '../agents/services'; +import { testUsers } from '../test_users'; +import { generateNPolicies } from '../../helpers'; + +export default function (providerContext: FtrProviderContext) { + const { getService } = providerContext; + const supertestWithoutAuth = getService('supertestWithoutAuth'); + const supertest = getService('supertest'); + const kibanaServer = getService('kibanaServer'); + + describe('fleet_uninstall_token_privileges', () => { + before(async () => { + await kibanaServer.savedObjects.cleanStandardList(); + }); + + after(async () => { + await kibanaServer.savedObjects.cleanStandardList(); + }); + let generatedPolicies: Map; + before(async () => { + const generatedPoliciesArray = await generateNPolicies(supertest, 2); + + generatedPolicies = new Map(); + generatedPoliciesArray.forEach((policy) => generatedPolicies.set(policy.id, policy)); + }); + + skipIfNoDockerRegistry(providerContext); + setupFleetAndAgents(providerContext); + + const SCENARIOS = [ + { + user: testUsers.fleet_all_only, + statusCode: 200, + }, + { + user: testUsers.fleet_agents_all_only, + statusCode: 200, + }, + { + user: testUsers.fleet_agents_read_only, + statusCode: 403, + }, + { + user: testUsers.fleet_read_only, + statusCode: 403, + }, + { + user: testUsers.fleet_no_access, + statusCode: 403, + }, + { + user: testUsers.fleet_minimal_all_only, + statusCode: 403, + }, + { + user: testUsers.fleet_minimal_read_only, + statusCode: 403, + }, + { + user: testUsers.fleet_settings_read_only, + statusCode: 403, + }, + ]; + + const ROUTES = [ + { + method: 'GET', + path: '/api/fleet/uninstall_tokens', + scenarios: SCENARIOS, + }, + ]; + before(async () => {}); + runPrivilegeTests(ROUTES, supertestWithoutAuth); + }); +} diff --git a/x-pack/test/fleet_api_integration/privileges_helpers.ts b/x-pack/test/fleet_api_integration/privileges_helpers.ts new file mode 100644 index 0000000000000..7a9ea3753867e --- /dev/null +++ b/x-pack/test/fleet_api_integration/privileges_helpers.ts @@ -0,0 +1,93 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import supertest, { type SuperTest } from 'supertest'; + +interface PrivilegeTestScenario { + user: { + username: string; + password: string; + }; + statusCode: number; +} + +interface PrivilegeTestRoute { + method: string; + path: string | (() => string); + send?: any; + beforeEach?: () => any; + afterEach?: () => any; + scenarios: PrivilegeTestScenario[]; +} + +export function runPrivilegeTests( + routes: PrivilegeTestRoute[], + supertestWithoutAuth: SuperTest +) { + for (const route of routes) { + describe(`${route.method} ${route.path}`, () => { + if (route.beforeEach) { + beforeEach(() => { + return route.beforeEach ? route.beforeEach() : undefined; + }); + } + if (route.afterEach) { + afterEach(() => { + return route.afterEach ? route.afterEach() : undefined; + }); + } + for (const scenario of route.scenarios) { + const expectFn = (res: supertest.Response) => { + if (res.status !== scenario.statusCode) { + let message = ''; + try { + message = res.body.error + ? `${res.body.error}:${res.body.message}` + : res.body.message ?? ''; + } catch (err) { + // swallow error + } + throw new Error( + `Expected status ${scenario.statusCode}, got: ${res.status} ${message}` + ); + } + }; + it(`should return a ${scenario.statusCode} for user: ${scenario.user.username}`, async () => { + const path = typeof route.path === 'function' ? route.path() : route.path; + if (route.method === 'GET') { + return supertestWithoutAuth + .get(path) + .auth(scenario.user.username, scenario.user.password) + .expect(expectFn); + } else if (route.method === 'PUT') { + return supertestWithoutAuth + .put(path) + .set('kbn-xsrf', 'xx') + .auth(scenario.user.username, scenario.user.password) + .send(route.send) + .expect(expectFn); + } else if (route.method === 'DELETE') { + return supertestWithoutAuth + .delete(path) + .set('kbn-xsrf', 'xx') + .auth(scenario.user.username, scenario.user.password) + .expect(expectFn); + } else if (route.method === 'POST') { + await supertestWithoutAuth + .post(path) + .set('kbn-xsrf', 'xx') + .auth(scenario.user.username, scenario.user.password) + .send(route.send) + .expect(expectFn); + } else { + throw new Error('not implemented'); + } + }); + } + }); + } +} diff --git a/x-pack/test/functional/apps/dataset_quality/dataset_quality_flyout.ts b/x-pack/test/functional/apps/dataset_quality/dataset_quality_flyout.ts index b458574273e18..78060f5c29571 100644 --- a/x-pack/test/functional/apps/dataset_quality/dataset_quality_flyout.ts +++ b/x-pack/test/functional/apps/dataset_quality/dataset_quality_flyout.ts @@ -9,6 +9,12 @@ import expect from '@kbn/expect'; import { DatasetQualityFtrProviderContext } from './config'; import { datasetNames, getInitialTestLogs, getLogsForDataset } from './data'; +const integrationActions = { + overview: 'Overview', + template: 'Template', + viewDashboards: 'ViewDashboards', +}; + export default function ({ getService, getPageObjects }: DatasetQualityFtrProviderContext) { const PageObjects = getPageObjects([ 'common', @@ -18,6 +24,8 @@ export default function ({ getService, getPageObjects }: DatasetQualityFtrProvid ]); const testSubjects = getService('testSubjects'); const synthtrace = getService('logSynthtraceEsClient'); + const retry = getService('retry'); + const browser = getService('browser'); const to = '2024-01-01T12:00:00.000Z'; describe('Dataset quality flyout', () => { @@ -109,5 +117,164 @@ export default function ({ getService, getPageObjects }: DatasetQualityFtrProvid await PageObjects.observabilityLogsExplorer.getDataSourceSelectorButtonText(); expect(datasetSelectorText).to.eql(testDatasetName); }); + + it('Integration actions menu is present with correct actions', async () => { + const apacheAccessDatasetName = 'apache.access'; + const apacheAccessDatasetHumanName = 'Apache access logs'; + + await PageObjects.observabilityLogsExplorer.navigateTo(); + + // Add initial integrations + await PageObjects.observabilityLogsExplorer.setupInitialIntegrations(); + + // Index 10 logs for `logs-apache.access` dataset + await synthtrace.index( + getLogsForDataset({ to, count: 10, dataset: apacheAccessDatasetName }) + ); + + await PageObjects.datasetQuality.navigateTo(); + + await PageObjects.datasetQuality.openDatasetFlyout(apacheAccessDatasetHumanName); + await PageObjects.datasetQuality.openIntegrationActionsMenu(); + + const actions = await Promise.all( + Object.values(integrationActions).map((action) => + PageObjects.datasetQuality.getIntegrationActionButtonByAction(action) + ) + ); + + expect(actions.length).to.eql(3); + }); + + it('Integration dashboard action hidden for integrations without dashboards', async () => { + const bitbucketDatasetName = 'atlassian_bitbucket.audit'; + const bitbucketDatasetHumanName = 'Bitbucket Audit Logs'; + + await PageObjects.observabilityLogsExplorer.navigateTo(); + + // Add initial integrations + await PageObjects.observabilityLogsExplorer.installPackage({ + name: 'atlassian_bitbucket', + version: '1.14.0', + }); + + // Index 10 logs for `atlassian_bitbucket.audit` dataset + await synthtrace.index(getLogsForDataset({ to, count: 10, dataset: bitbucketDatasetName })); + + await PageObjects.datasetQuality.navigateTo(); + + await PageObjects.datasetQuality.openDatasetFlyout(bitbucketDatasetHumanName); + await PageObjects.datasetQuality.openIntegrationActionsMenu(); + + await testSubjects.missingOrFail( + PageObjects.datasetQuality.testSubjectSelectors.datasetQualityFlyoutIntegrationAction( + integrationActions.viewDashboards + ) + ); + }); + + it('Integration overview action should navigate to the integration overview page', async () => { + const bitbucketDatasetName = 'atlassian_bitbucket.audit'; + const bitbucketDatasetHumanName = 'Bitbucket Audit Logs'; + + await PageObjects.observabilityLogsExplorer.navigateTo(); + + // Add initial integrations + await PageObjects.observabilityLogsExplorer.installPackage({ + name: 'atlassian_bitbucket', + version: '1.14.0', + }); + + // Index 10 logs for `atlassian_bitbucket.audit` dataset + await synthtrace.index(getLogsForDataset({ to, count: 10, dataset: bitbucketDatasetName })); + + await PageObjects.datasetQuality.navigateTo(); + + await PageObjects.datasetQuality.openDatasetFlyout(bitbucketDatasetHumanName); + await PageObjects.datasetQuality.openIntegrationActionsMenu(); + + const action = await PageObjects.datasetQuality.getIntegrationActionButtonByAction( + integrationActions.overview + ); + + await action.click(); + + await retry.tryForTime(5000, async () => { + const currentUrl = await browser.getCurrentUrl(); + const parsedUrl = new URL(currentUrl); + + expect(parsedUrl.pathname).to.contain('/app/integrations/detail/atlassian_bitbucket'); + }); + }); + + it('Integration template action should navigate to the index template page', async () => { + const apacheAccessDatasetName = 'apache.access'; + const apacheAccessDatasetHumanName = 'Apache access logs'; + + await PageObjects.observabilityLogsExplorer.navigateTo(); + + // Add initial integrations + await PageObjects.observabilityLogsExplorer.setupInitialIntegrations(); + + // Index 10 logs for `logs-apache.access` dataset + await synthtrace.index( + getLogsForDataset({ to, count: 10, dataset: apacheAccessDatasetName }) + ); + + await PageObjects.datasetQuality.navigateTo(); + + await PageObjects.datasetQuality.openDatasetFlyout(apacheAccessDatasetHumanName); + await PageObjects.datasetQuality.openIntegrationActionsMenu(); + + const action = await PageObjects.datasetQuality.getIntegrationActionButtonByAction( + integrationActions.template + ); + + await action.click(); + + await retry.tryForTime(5000, async () => { + const currentUrl = await browser.getCurrentUrl(); + const parsedUrl = new URL(currentUrl); + expect(parsedUrl.pathname).to.contain( + `/app/management/data/index_management/templates/logs-${apacheAccessDatasetName}` + ); + }); + }); + + it('Integration dashboard action should navigate to the selected dashboard', async () => { + const apacheAccessDatasetName = 'apache.access'; + const apacheAccessDatasetHumanName = 'Apache access logs'; + + await PageObjects.observabilityLogsExplorer.navigateTo(); + + // Add initial integrations + await PageObjects.observabilityLogsExplorer.setupInitialIntegrations(); + + // Index 10 logs for `logs-apache.access` dataset + await synthtrace.index( + getLogsForDataset({ to, count: 10, dataset: apacheAccessDatasetName }) + ); + + await PageObjects.datasetQuality.navigateTo(); + + await PageObjects.datasetQuality.openDatasetFlyout(apacheAccessDatasetHumanName); + await PageObjects.datasetQuality.openIntegrationActionsMenu(); + + const action = await PageObjects.datasetQuality.getIntegrationActionButtonByAction( + integrationActions.viewDashboards + ); + + await action.click(); + + const dashboardButtons = await PageObjects.datasetQuality.getIntegrationDashboardButtons(); + const firstDashboardButton = await dashboardButtons[0]; + const dashboardText = await firstDashboardButton.getVisibleText(); + + await firstDashboardButton.click(); + + const breadcrumbText = await testSubjects.getVisibleText('breadcrumb last'); + + expect(breadcrumbText).to.eql(dashboardText); + }); }); } diff --git a/x-pack/test/functional/apps/lens/open_in_lens/tsvb/dashboard.ts b/x-pack/test/functional/apps/lens/open_in_lens/tsvb/dashboard.ts index 24fac3f726bc9..e5a2a8f6a8971 100644 --- a/x-pack/test/functional/apps/lens/open_in_lens/tsvb/dashboard.ts +++ b/x-pack/test/functional/apps/lens/open_in_lens/tsvb/dashboard.ts @@ -28,7 +28,8 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { const dashboardAddPanel = getService('dashboardAddPanel'); const filterBar = getService('filterBar'); - describe('Dashboard to TSVB to Lens', function describeIndexTests() { + // FLAKY: https://github.com/elastic/kibana/issues/179307 + describe.skip('Dashboard to TSVB to Lens', function describeIndexTests() { before(async () => { await visualize.initTests(); }); diff --git a/x-pack/test/functional/apps/navigation/config.ts b/x-pack/test/functional/apps/navigation/config.ts new file mode 100644 index 0000000000000..00b98c1db316a --- /dev/null +++ b/x-pack/test/functional/apps/navigation/config.ts @@ -0,0 +1,53 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { FtrConfigProviderContext } from '@kbn/test'; +import { services, pageObjects } from './ftr_provider_context'; + +export default async function ({ readConfigFile }: FtrConfigProviderContext) { + const kibanaFunctionalConfig = await readConfigFile(require.resolve('../../config.base.js')); + + return { + testFiles: [require.resolve('./tests')], + servers: { + ...kibanaFunctionalConfig.get('servers'), + }, + services, + pageObjects, + + junit: { + reportName: 'X-Pack Navigation Functional Tests', + }, + + esTestCluster: { + ...kibanaFunctionalConfig.get('esTestCluster'), + license: 'trial', + serverArgs: [`xpack.license.self_generated.type='trial'`], + }, + apps: { + ...kibanaFunctionalConfig.get('apps'), + }, + + kbnTestServer: { + ...kibanaFunctionalConfig.get('kbnTestServer'), + serverArgs: [ + ...kibanaFunctionalConfig.get('kbnTestServer.serverArgs'), + '--navigation.solutionNavigation.featureOn=true', + '--navigation.solutionNavigation.enabled=true', + '--navigation.solutionNavigation.optInStatus=visible', + '--navigation.solutionNavigation.defaultSolution=es', + // Note: the base64 string in the cloud.id config contains the ES endpoint required in the functional tests + '--xpack.cloud.id=ftr_fake_cloud_id:aGVsbG8uY29tOjQ0MyRFUzEyM2FiYyRrYm4xMjNhYmM=', + '--xpack.cloud.base_url=https://cloud.elastic.co', + '--xpack.cloud.deployment_url=/deployments/deploymentId', + '--xpack.cloud.organization_url=/organization/organizationId', + '--xpack.cloud.billing_url=/billing', + '--xpack.cloud.profile_url=/user/userId', + ], + }, + }; +} diff --git a/x-pack/test/functional/apps/navigation/ftr_provider_context.ts b/x-pack/test/functional/apps/navigation/ftr_provider_context.ts new file mode 100644 index 0000000000000..31e162e9731a7 --- /dev/null +++ b/x-pack/test/functional/apps/navigation/ftr_provider_context.ts @@ -0,0 +1,13 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { GenericFtrProviderContext } from '@kbn/test'; +import { services } from '../../services'; +import { pageObjects } from '../../page_objects'; + +export type FtrProviderContext = GenericFtrProviderContext; +export { services, pageObjects }; diff --git a/x-pack/test/functional/apps/navigation/tests/index.ts b/x-pack/test/functional/apps/navigation/tests/index.ts new file mode 100644 index 0000000000000..3f0f6acaf2231 --- /dev/null +++ b/x-pack/test/functional/apps/navigation/tests/index.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 { FtrProviderContext } from '../ftr_provider_context'; + +export default function ({ loadTestFile }: FtrProviderContext) { + describe('navigation - functional tests', function () { + loadTestFile(require.resolve('./user_optin_optout')); + }); +} diff --git a/x-pack/test/functional/apps/navigation/tests/user_optin_optout.ts b/x-pack/test/functional/apps/navigation/tests/user_optin_optout.ts new file mode 100644 index 0000000000000..59d4733564d46 --- /dev/null +++ b/x-pack/test/functional/apps/navigation/tests/user_optin_optout.ts @@ -0,0 +1,36 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { FtrProviderContext } from '../ftr_provider_context'; + +export default function ({ getService, getPageObjects }: FtrProviderContext) { + const PageObjects = getPageObjects(['common', 'header', 'home', 'dashboard', 'security']); + const testSubjects = getService('testSubjects'); + const userMenu = getService('userMenu'); + + describe('user opt in/out', function describeIndexTests() { + it('should allow the user to opt in or out', async () => { + await PageObjects.common.navigateToApp('home'); + + // we are in the new nav, search solution + await testSubjects.existOrFail('kibanaProjectHeader'); + + await userMenu.openMenu(); + await testSubjects.existOrFail('solutionNavToggle'); + + // Opt OUT of the new navigation + await testSubjects.click('solutionNavToggle'); + // we are in the old nav + await testSubjects.missingOrFail('kibanaProjectHeader'); + + // Opt back IN to the new navigation + await userMenu.openMenu(); + await testSubjects.click('solutionNavToggle'); + await testSubjects.existOrFail('kibanaProjectHeader'); + }); + }); +} diff --git a/x-pack/test/functional/page_objects/dataset_quality.ts b/x-pack/test/functional/page_objects/dataset_quality.ts index ba1f2dbe296fc..5663ffe077011 100644 --- a/x-pack/test/functional/page_objects/dataset_quality.ts +++ b/x-pack/test/functional/page_objects/dataset_quality.ts @@ -60,6 +60,9 @@ export function DatasetQualityPageObject({ getPageObjects, getService }: FtrProv datasetQualityFlyoutTitle: 'datasetQualityFlyoutTitle', datasetQualityHeaderButton: 'datasetQualityHeaderButton', datasetQualityFlyoutFieldValue: 'datasetQualityFlyoutFieldValue', + datasetQualityFlyoutIntegrationActionsButton: 'datasetQualityFlyoutIntegrationActionsButton', + datasetQualityFlyoutIntegrationAction: (action: string) => + `datasetQualityFlyoutIntegrationAction${action}`, datasetQualityFilterBarFieldSearch: 'datasetQualityFilterBarFieldSearch', datasetQualityIntegrationsSelectable: 'datasetQualityIntegrationsSelectable', datasetQualityIntegrationsSelectableButton: 'datasetQualityIntegrationsSelectableButton', @@ -237,6 +240,20 @@ export function DatasetQualityPageObject({ getPageObjects, getService }: FtrProv return testSubjects.find(testSubjectSelectors.datasetQualityHeaderButton); }, + openIntegrationActionsMenu() { + return testSubjects.click(testSubjectSelectors.datasetQualityFlyoutIntegrationActionsButton); + }, + + getIntegrationActionButtonByAction(action: string) { + return testSubjects.find(testSubjectSelectors.datasetQualityFlyoutIntegrationAction(action)); + }, + + getIntegrationDashboardButtons() { + return testSubjects.findAll( + testSubjectSelectors.datasetQualityFlyoutIntegrationAction('Dashboard') + ); + }, + async doestTextExistInFlyout(text: string, elementSelector: string) { const flyoutContainer: WebElementWrapper = await testSubjects.find( testSubjectSelectors.datasetQualityFlyoutBody diff --git a/x-pack/test/functional/services/user_menu.js b/x-pack/test/functional/services/user_menu.js index e3f589386fc2e..2b77cd086563d 100644 --- a/x-pack/test/functional/services/user_menu.js +++ b/x-pack/test/functional/services/user_menu.js @@ -29,6 +29,10 @@ export function UserMenuProvider({ getService }) { return await testSubjects.exists('userMenu > logoutLink'); } + async openMenu() { + await this._ensureMenuOpen(); + } + async closeMenu() { if (!(await testSubjects.exists('userMenu'))) { return; diff --git a/x-pack/test/security_solution_api_integration/test_suites/detections_response/detection_engine/alerts/basic_license_essentials_tier/alert_status/alert_status.ts b/x-pack/test/security_solution_api_integration/test_suites/detections_response/detection_engine/alerts/basic_license_essentials_tier/alert_status/alert_status.ts index c9bfadb7b3bfa..17e4590bc0023 100644 --- a/x-pack/test/security_solution_api_integration/test_suites/detections_response/detection_engine/alerts/basic_license_essentials_tier/alert_status/alert_status.ts +++ b/x-pack/test/security_solution_api_integration/test_suites/detections_response/detection_engine/alerts/basic_license_essentials_tier/alert_status/alert_status.ts @@ -43,7 +43,8 @@ export default ({ getService }: FtrProviderContext) => { const dataPathBuilder = new EsArchivePathBuilder(isServerless); const path = dataPathBuilder.getPath('auditbeat/hosts'); - describe('@ess @serverless change alert status endpoints', () => { + // Failing: See https://github.com/elastic/kibana/issues/179704 + describe.skip('@ess @serverless change alert status endpoints', () => { describe('validation checks', () => { describe('update by ids', () => { it('should not give errors when querying and the alerts index does not exist yet', async () => { diff --git a/x-pack/test/security_solution_api_integration/test_suites/genai/invoke_ai/trial_license_complete_tier/basic.ts b/x-pack/test/security_solution_api_integration/test_suites/genai/invoke_ai/trial_license_complete_tier/basic.ts index 86477a7277586..f0b7250f3b5ab 100644 --- a/x-pack/test/security_solution_api_integration/test_suites/genai/invoke_ai/trial_license_complete_tier/basic.ts +++ b/x-pack/test/security_solution_api_integration/test_suites/genai/invoke_ai/trial_license_complete_tier/basic.ts @@ -19,7 +19,7 @@ const mockRequest = { subAction: 'invokeAI', isEnabledKnowledgeBase: false, isEnabledRAGAlerts: false, - replacements: [], + replacements: {}, }; export default ({ getService }: FtrProviderContext) => { diff --git a/x-pack/test/security_solution_cypress/cypress/e2e/entity_analytics/dashboards/entity_analytics/legacy_risk_score.cy.ts b/x-pack/test/security_solution_cypress/cypress/e2e/entity_analytics/dashboards/entity_analytics/legacy_risk_score.cy.ts index 380866b8d7f12..32b27077ed9d3 100644 --- a/x-pack/test/security_solution_cypress/cypress/e2e/entity_analytics/dashboards/entity_analytics/legacy_risk_score.cy.ts +++ b/x-pack/test/security_solution_cypress/cypress/e2e/entity_analytics/dashboards/entity_analytics/legacy_risk_score.cy.ts @@ -46,251 +46,256 @@ const SIEM_KIBANA_HOST_NAME = 'siem-kibana'; const DATE_FORMAT = 'MMM D, YYYY @ HH:mm:ss.SSS'; const DATE_BEFORE_ALERT_CREATION = moment().format(DATE_FORMAT); -describe('Entity Analytics Dashboard', { tags: ['@ess', '@serverless'] }, () => { - before(() => { - cy.task('esArchiverLoad', { archiveName: 'auditbeat_multiple' }); - }); - - after(() => { - cy.task('esArchiverUnload', { archiveName: 'auditbeat_multiple' }); - }); - - describe('legacy risk score', () => { - describe('Without data', () => { - beforeEach(() => { - login(); - visitWithTimeRange(ENTITY_ANALYTICS_URL); - }); - - it('shows enable host risk button', () => { - cy.get(ENABLE_HOST_RISK_SCORE_BUTTON).should('be.visible'); - }); - - it('shows enable user risk button', () => { - cy.get(ENABLE_USER_RISK_SCORE_BUTTON).should('be.visible'); - }); +// https://github.com/elastic/kibana/issues/179686 +describe( + 'Entity Analytics Dashboard', + { tags: ['@ess', '@serverless', '@brokenInServerlessQA'] }, + () => { + before(() => { + cy.task('esArchiverLoad', { archiveName: 'auditbeat_multiple' }); }); - describe('Risk Score enabled but still no data', () => { - before(() => { - cy.task('esArchiverLoad', { archiveName: 'risk_hosts_no_data' }); - cy.task('esArchiverLoad', { archiveName: 'risk_users_no_data' }); - }); - - beforeEach(() => { - login(); - visitWithTimeRange(ENTITY_ANALYTICS_URL); - }); - - after(() => { - cy.task('esArchiverUnload', { archiveName: 'risk_hosts_no_data' }); - cy.task('esArchiverUnload', { archiveName: 'risk_users_no_data' }); - }); - - it('shows no data detected prompt for host risk score module', () => { - cy.get(HOST_RISK_SCORE_NO_DATA_DETECTED).should('be.visible'); - }); - - it('shows no data detected prompt for user risk score module', () => { - cy.get(USER_RISK_SCORE_NO_DATA_DETECTED).should('be.visible'); - }); + after(() => { + cy.task('esArchiverUnload', { archiveName: 'auditbeat_multiple' }); }); - describe('With Legacy data', () => { - before(() => { - cy.task('esArchiverLoad', { archiveName: 'risk_hosts_legacy_data' }); - cy.task('esArchiverLoad', { archiveName: 'risk_users_legacy_data' }); - }); - - beforeEach(() => { - login(); - visitWithTimeRange(ENTITY_ANALYTICS_URL); - }); - - after(() => { - cy.task('esArchiverUnload', { archiveName: 'risk_hosts_legacy_data' }); - cy.task('esArchiverUnload', { archiveName: 'risk_users_legacy_data' }); - }); - - it('shows enable host risk button', () => { - cy.get(ENABLE_HOST_RISK_SCORE_BUTTON).should('be.visible'); - }); + describe('legacy risk score', () => { + describe('Without data', () => { + beforeEach(() => { + login(); + visitWithTimeRange(ENTITY_ANALYTICS_URL); + }); - it('shows enable user risk button', () => { - cy.get(ENABLE_USER_RISK_SCORE_BUTTON).should('be.visible'); - }); - }); + it('shows enable host risk button', () => { + cy.get(ENABLE_HOST_RISK_SCORE_BUTTON).should('be.visible'); + }); - describe('With host risk data', () => { - before(() => { - cy.task('esArchiverLoad', { archiveName: 'risk_hosts' }); + it('shows enable user risk button', () => { + cy.get(ENABLE_USER_RISK_SCORE_BUTTON).should('be.visible'); + }); }); - beforeEach(() => { - login(); - visitWithTimeRange(ENTITY_ANALYTICS_URL); - }); + describe('Risk Score enabled but still no data', () => { + before(() => { + cy.task('esArchiverLoad', { archiveName: 'risk_hosts_no_data' }); + cy.task('esArchiverLoad', { archiveName: 'risk_users_no_data' }); + }); - after(() => { - cy.task('esArchiverUnload', { archiveName: 'risk_hosts' }); - }); + beforeEach(() => { + login(); + visitWithTimeRange(ENTITY_ANALYTICS_URL); + }); - it('renders donut chart', () => { - cy.get(HOSTS_DONUT_CHART).should('include.text', '6Total'); - }); + after(() => { + cy.task('esArchiverUnload', { archiveName: 'risk_hosts_no_data' }); + cy.task('esArchiverUnload', { archiveName: 'risk_users_no_data' }); + }); - it('renders table', () => { - cy.get(HOSTS_TABLE).should('be.visible'); - cy.get(HOSTS_TABLE_ROWS).should('have.length', 5); - }); + it('shows no data detected prompt for host risk score module', () => { + cy.get(HOST_RISK_SCORE_NO_DATA_DETECTED).should('be.visible'); + }); - it('renders alerts column', () => { - cy.get(HOSTS_TABLE_ALERT_CELL).should('have.length', 5); + it('shows no data detected prompt for user risk score module', () => { + cy.get(USER_RISK_SCORE_NO_DATA_DETECTED).should('be.visible'); + }); }); - it('filters by risk level', () => { - cy.get(HOSTS_TABLE).should('be.visible'); - cy.get(HOSTS_TABLE_ROWS).should('have.length', 5); - openRiskTableFilterAndSelectTheLowOption(); + describe('With Legacy data', () => { + before(() => { + cy.task('esArchiverLoad', { archiveName: 'risk_hosts_legacy_data' }); + cy.task('esArchiverLoad', { archiveName: 'risk_users_legacy_data' }); + }); - cy.get(HOSTS_DONUT_CHART).should('include.text', '1Total'); - cy.get(HOSTS_TABLE_ROWS).should('have.length', 1); + beforeEach(() => { + login(); + visitWithTimeRange(ENTITY_ANALYTICS_URL); + }); - removeLowFilterAndCloseRiskTableFilter(); - }); + after(() => { + cy.task('esArchiverUnload', { archiveName: 'risk_hosts_legacy_data' }); + cy.task('esArchiverUnload', { archiveName: 'risk_users_legacy_data' }); + }); - it('filters the host risk table with KQL search bar query', () => { - kqlSearch(`host.name : ${SIEM_KIBANA_HOST_NAME}{enter}`); + it('shows enable host risk button', () => { + cy.get(ENABLE_HOST_RISK_SCORE_BUTTON).should('be.visible'); + }); - cy.get(HOSTS_DONUT_CHART).should('include.text', '1Total'); - cy.get(HOSTS_TABLE_ROWS).should('have.length', 1); + it('shows enable user risk button', () => { + cy.get(ENABLE_USER_RISK_SCORE_BUTTON).should('be.visible'); + }); }); - // FLAKY: https://github.com/elastic/kibana/issues/178914 - describe.skip('With alerts data', () => { + describe('With host risk data', () => { before(() => { - createRule(getNewRule()); + cy.task('esArchiverLoad', { archiveName: 'risk_hosts' }); }); beforeEach(() => { login(); - visitWithTimeRange(ALERTS_URL); - waitForAlertsToPopulate(); visitWithTimeRange(ENTITY_ANALYTICS_URL); }); after(() => { - deleteAlertsAndRules(); + cy.task('esArchiverUnload', { archiveName: 'risk_hosts' }); }); - it('populates alerts column', () => { - cy.get(HOSTS_TABLE_ALERT_CELL).first().should('include.text', SIEM_KIBANA_HOST_ALERTS); + it('renders donut chart', () => { + cy.get(HOSTS_DONUT_CHART).should('include.text', '6Total'); }); - it('filters the alerts count with time range', () => { - setEndDate(DATE_BEFORE_ALERT_CREATION); - - updateDates(); + it('renders table', () => { + cy.get(HOSTS_TABLE).should('be.visible'); + cy.get(HOSTS_TABLE_ROWS).should('have.length', 5); + }); - cy.get(HOSTS_TABLE_ALERT_CELL).first().should('include.text', 0); + it('renders alerts column', () => { + cy.get(HOSTS_TABLE_ALERT_CELL).should('have.length', 5); }); + it('filters by risk level', () => { + cy.get(HOSTS_TABLE).should('be.visible'); + cy.get(HOSTS_TABLE_ROWS).should('have.length', 5); - it('opens alerts page when alerts count is clicked', () => { - clickOnFirstHostsAlerts(); - cy.url().should('include', ALERTS_URL); + openRiskTableFilterAndSelectTheLowOption(); - cy.get(OPTION_LIST_LABELS).eq(0).should('include.text', 'Status'); - cy.get(OPTION_LIST_VALUES(0)).should('include.text', 'open'); - cy.get(OPTION_LIST_LABELS).eq(1).should('include.text', 'Host'); - cy.get(OPTION_LIST_VALUES(1)).should('include.text', SIEM_KIBANA_HOST_NAME); - }); - }); - }); + cy.get(HOSTS_DONUT_CHART).should('include.text', '1Total'); + cy.get(HOSTS_TABLE_ROWS).should('have.length', 1); - describe('With user risk data', () => { - before(() => { - cy.task('esArchiverLoad', { archiveName: 'risk_users' }); - }); + removeLowFilterAndCloseRiskTableFilter(); + }); - beforeEach(() => { - login(); - visitWithTimeRange(ENTITY_ANALYTICS_URL); - }); + it('filters the host risk table with KQL search bar query', () => { + kqlSearch(`host.name : ${SIEM_KIBANA_HOST_NAME}{enter}`); - after(() => { - cy.task('esArchiverUnload', { archiveName: 'risk_users' }); - }); + cy.get(HOSTS_DONUT_CHART).should('include.text', '1Total'); + cy.get(HOSTS_TABLE_ROWS).should('have.length', 1); + }); - it('renders donut chart', () => { - cy.get(USERS_DONUT_CHART).should('include.text', '7Total'); - }); + // FLAKY: https://github.com/elastic/kibana/issues/178914 + describe.skip('With alerts data', () => { + before(() => { + createRule(getNewRule()); + }); - it('renders table', () => { - cy.get(USERS_TABLE).should('be.visible'); - cy.get(USERS_TABLE_ROWS).should('have.length', 5); - }); + beforeEach(() => { + login(); + visitWithTimeRange(ALERTS_URL); + waitForAlertsToPopulate(); + visitWithTimeRange(ENTITY_ANALYTICS_URL); + }); - it('renders alerts column', () => { - cy.get(USERS_TABLE_ALERT_CELL).should('have.length', 5); - }); + after(() => { + deleteAlertsAndRules(); + }); - it('filters by risk level', () => { - cy.get(USERS_TABLE).should('be.visible'); - cy.get(USERS_TABLE_ROWS).should('have.length', 5); + it('populates alerts column', () => { + cy.get(HOSTS_TABLE_ALERT_CELL).first().should('include.text', SIEM_KIBANA_HOST_ALERTS); + }); - openRiskTableFilterAndSelectTheLowOption(); + it('filters the alerts count with time range', () => { + setEndDate(DATE_BEFORE_ALERT_CREATION); - cy.get(USERS_DONUT_CHART).should('include.text', '2Total'); - cy.get(USERS_TABLE_ROWS).should('have.length', 2); + updateDates(); - removeLowFilterAndCloseRiskTableFilter(); - }); + cy.get(HOSTS_TABLE_ALERT_CELL).first().should('include.text', 0); + }); - it('filters the host risk table with KQL search bar query', () => { - kqlSearch(`user.name : ${TEST_USER_NAME}{enter}`); + it('opens alerts page when alerts count is clicked', () => { + clickOnFirstHostsAlerts(); + cy.url().should('include', ALERTS_URL); - cy.get(USERS_DONUT_CHART).should('include.text', '1Total'); - cy.get(USERS_TABLE_ROWS).should('have.length', 1); + cy.get(OPTION_LIST_LABELS).eq(0).should('include.text', 'Status'); + cy.get(OPTION_LIST_VALUES(0)).should('include.text', 'open'); + cy.get(OPTION_LIST_LABELS).eq(1).should('include.text', 'Host'); + cy.get(OPTION_LIST_VALUES(1)).should('include.text', SIEM_KIBANA_HOST_NAME); + }); + }); }); - describe('With alerts data', () => { + describe('With user risk data', () => { before(() => { - createRule(getNewRule()); + cy.task('esArchiverLoad', { archiveName: 'risk_users' }); }); beforeEach(() => { login(); - visitWithTimeRange(ALERTS_URL); - waitForAlertsToPopulate(); visitWithTimeRange(ENTITY_ANALYTICS_URL); }); after(() => { - deleteAlertsAndRules(); + cy.task('esArchiverUnload', { archiveName: 'risk_users' }); }); - it('populates alerts column', () => { - cy.get(USERS_TABLE_ALERT_CELL).first().should('include.text', TEST_USER_ALERTS); + it('renders donut chart', () => { + cy.get(USERS_DONUT_CHART).should('include.text', '7Total'); }); - it('filters the alerts count with time range', () => { - setEndDate(DATE_BEFORE_ALERT_CREATION); - updateDates(); + it('renders table', () => { + cy.get(USERS_TABLE).should('be.visible'); + cy.get(USERS_TABLE_ROWS).should('have.length', 5); + }); - cy.get(USERS_TABLE_ALERT_CELL).first().should('include.text', 0); + it('renders alerts column', () => { + cy.get(USERS_TABLE_ALERT_CELL).should('have.length', 5); }); - it('opens alerts page when alerts count is clicked', () => { - clickOnFirstUsersAlerts(); + it('filters by risk level', () => { + cy.get(USERS_TABLE).should('be.visible'); + cy.get(USERS_TABLE_ROWS).should('have.length', 5); + + openRiskTableFilterAndSelectTheLowOption(); + + cy.get(USERS_DONUT_CHART).should('include.text', '2Total'); + cy.get(USERS_TABLE_ROWS).should('have.length', 2); + + removeLowFilterAndCloseRiskTableFilter(); + }); + + it('filters the host risk table with KQL search bar query', () => { + kqlSearch(`user.name : ${TEST_USER_NAME}{enter}`); + + cy.get(USERS_DONUT_CHART).should('include.text', '1Total'); + cy.get(USERS_TABLE_ROWS).should('have.length', 1); + }); + + describe('With alerts data', () => { + before(() => { + createRule(getNewRule()); + }); + + beforeEach(() => { + login(); + visitWithTimeRange(ALERTS_URL); + waitForAlertsToPopulate(); + visitWithTimeRange(ENTITY_ANALYTICS_URL); + }); + + after(() => { + deleteAlertsAndRules(); + }); + + it('populates alerts column', () => { + cy.get(USERS_TABLE_ALERT_CELL).first().should('include.text', TEST_USER_ALERTS); + }); + + it('filters the alerts count with time range', () => { + setEndDate(DATE_BEFORE_ALERT_CREATION); + updateDates(); + + cy.get(USERS_TABLE_ALERT_CELL).first().should('include.text', 0); + }); + + it('opens alerts page when alerts count is clicked', () => { + clickOnFirstUsersAlerts(); - cy.url().should('include', ALERTS_URL); + cy.url().should('include', ALERTS_URL); - cy.get(OPTION_LIST_LABELS).eq(0).should('include.text', 'Status'); - cy.get(OPTION_LIST_VALUES(0)).should('include.text', 'open'); - cy.get(OPTION_LIST_LABELS).eq(1).should('include.text', 'User'); - cy.get(OPTION_LIST_VALUES(1)).should('include.text', TEST_USER_NAME); + cy.get(OPTION_LIST_LABELS).eq(0).should('include.text', 'Status'); + cy.get(OPTION_LIST_VALUES(0)).should('include.text', 'open'); + cy.get(OPTION_LIST_LABELS).eq(1).should('include.text', 'User'); + cy.get(OPTION_LIST_VALUES(1)).should('include.text', TEST_USER_NAME); + }); }); }); }); - }); -}); + } +); diff --git a/x-pack/test/security_solution_cypress/cypress/e2e/entity_analytics/dashboards/entity_analytics/new_risk_score.cy.ts b/x-pack/test/security_solution_cypress/cypress/e2e/entity_analytics/dashboards/entity_analytics/new_risk_score.cy.ts index 75fe79d95b120..6bacd88defad7 100644 --- a/x-pack/test/security_solution_cypress/cypress/e2e/entity_analytics/dashboards/entity_analytics/new_risk_score.cy.ts +++ b/x-pack/test/security_solution_cypress/cypress/e2e/entity_analytics/dashboards/entity_analytics/new_risk_score.cy.ts @@ -75,7 +75,8 @@ describe('Entity Analytics Dashboard', { tags: ['@ess', '@serverless'] }, () => }); }); - describe('When risk engine is enabled', () => { + // https://github.com/elastic/kibana/issues/179687 + describe('When risk engine is enabled', { tags: ['@brokenInServerlessQA'] }, () => { beforeEach(() => { login(); mockRiskEngineEnabled(); diff --git a/x-pack/test/security_solution_endpoint_api_int/apis/endpoint_response_actions/agent_type_support.ts b/x-pack/test/security_solution_endpoint_api_int/apis/endpoint_response_actions/agent_type_support.ts index 2be0752f62475..87167a8926030 100644 --- a/x-pack/test/security_solution_endpoint_api_int/apis/endpoint_response_actions/agent_type_support.ts +++ b/x-pack/test/security_solution_endpoint_api_int/apis/endpoint_response_actions/agent_type_support.ts @@ -25,7 +25,7 @@ export default function ({ getService }: FtrProviderContext) { .expect(400, { statusCode: 400, error: 'Bad Request', - message: 'No SentinelOne stack connector found', + message: 'No stack connector instance configured for [.sentinelone]', }); }); }); diff --git a/x-pack/test/tsconfig.json b/x-pack/test/tsconfig.json index 17eea1bbd8f93..70fb283c1c4e8 100644 --- a/x-pack/test/tsconfig.json +++ b/x-pack/test/tsconfig.json @@ -171,5 +171,6 @@ "@kbn/observability-ai-assistant-app-plugin", "@kbn/aiops-log-rate-analysis", "@kbn/apm-data-view", + "@kbn/core-saved-objects-api-server", ] } diff --git a/x-pack/test_serverless/functional/test_suites/common/management/data_views/serverless.ts b/x-pack/test_serverless/functional/test_suites/common/management/data_views/serverless.ts index bf7b6b4115d6c..83f70a465a49e 100644 --- a/x-pack/test_serverless/functional/test_suites/common/management/data_views/serverless.ts +++ b/x-pack/test_serverless/functional/test_suites/common/management/data_views/serverless.ts @@ -5,7 +5,6 @@ * 2.0. */ -import expect from 'expect'; import { DATA_VIEW_PATH } from '@kbn/data-views-plugin/server'; import { ELASTIC_HTTP_VERSION_HEADER } from '@kbn/core-http-common'; import { INITIAL_REST_VERSION } from '@kbn/data-views-plugin/server/constants'; @@ -19,6 +18,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { const esArchiver = getService('esArchiver'); const supertest = getService('supertest'); const testSubjects = getService('testSubjects'); + const svlCommonApi = getService('svlCommonApi'); describe('Serverless tests', function () { describe('disables scripted fields', function () { @@ -27,7 +27,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { before(async () => { await esArchiver.load(archivePath); - const response = await supertest + const { body, status } = await supertest .post(DATA_VIEW_PATH) .set('kbn-xsrf', 'some-xsrf-token') .send({ @@ -37,8 +37,8 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { override: true, }); - expect(response.status).toBe(200); - dataViewId = response.body.data_view.id; + svlCommonApi.assertResponseStatusCode(200, status, body); + dataViewId = body.data_view.id; }); after(async () => { @@ -65,7 +65,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { 'test/api_integration/fixtures/es_archiver/index_patterns/basic_index' ); - const response = await supertest + const { body, status } = await supertest .post(DATA_VIEW_PATH) .set('kbn-xsrf', 'some-xsrf-token') .send({ @@ -76,7 +76,8 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { override: true, }) .set(ELASTIC_HTTP_VERSION_HEADER, INITIAL_REST_VERSION); - dataViewId = response.body.data_view.id; + svlCommonApi.assertResponseStatusCode(200, status, body); + dataViewId = body.data_view.id; }); after(async () => { diff --git a/x-pack/test_serverless/functional/test_suites/observability/dataset_quality/dataset_quality_flyout.ts b/x-pack/test_serverless/functional/test_suites/observability/dataset_quality/dataset_quality_flyout.ts index 068e0b04088a1..75c423d618f86 100644 --- a/x-pack/test_serverless/functional/test_suites/observability/dataset_quality/dataset_quality_flyout.ts +++ b/x-pack/test_serverless/functional/test_suites/observability/dataset_quality/dataset_quality_flyout.ts @@ -9,6 +9,12 @@ import expect from '@kbn/expect'; import { FtrProviderContext } from '../../../ftr_provider_context'; import { datasetNames, getInitialTestLogs, getLogsForDataset } from './data'; +const integrationActions = { + overview: 'Overview', + template: 'Template', + viewDashboards: 'ViewDashboards', +}; + export default function ({ getService, getPageObjects }: FtrProviderContext) { const PageObjects = getPageObjects([ 'common', @@ -20,6 +26,8 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { ]); const testSubjects = getService('testSubjects'); const synthtrace = getService('svlLogsSynthtraceClient'); + const retry = getService('retry'); + const browser = getService('browser'); const to = '2024-01-01T12:00:00.000Z'; describe('Dataset quality flyout', () => { @@ -113,5 +121,164 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { await PageObjects.observabilityLogsExplorer.getDataSourceSelectorButtonText(); expect(datasetSelectorText).to.eql(testDatasetName); }); + + it('Integration actions menu is present with correct actions', async () => { + const apacheAccessDatasetName = 'apache.access'; + const apacheAccessDatasetHumanName = 'Apache access logs'; + + await PageObjects.observabilityLogsExplorer.navigateTo(); + + // Add initial integrations + await PageObjects.observabilityLogsExplorer.setupInitialIntegrations(); + + // Index 10 logs for `logs-apache.access` dataset + await synthtrace.index( + getLogsForDataset({ to, count: 10, dataset: apacheAccessDatasetName }) + ); + + await PageObjects.datasetQuality.navigateTo(); + + await PageObjects.datasetQuality.openDatasetFlyout(apacheAccessDatasetHumanName); + await PageObjects.datasetQuality.openIntegrationActionsMenu(); + + const actions = await Promise.all( + Object.values(integrationActions).map((action) => + PageObjects.datasetQuality.getIntegrationActionButtonByAction(action) + ) + ); + + expect(actions.length).to.eql(3); + }); + + it('Integration dashboard action hidden for integrations without dashboards', async () => { + const bitbucketDatasetName = 'atlassian_bitbucket.audit'; + const bitbucketDatasetHumanName = 'Bitbucket Audit Logs'; + + await PageObjects.observabilityLogsExplorer.navigateTo(); + + // Add initial integrations + await PageObjects.observabilityLogsExplorer.installPackage({ + name: 'atlassian_bitbucket', + version: '1.14.0', + }); + + // Index 10 logs for `atlassian_bitbucket.audit` dataset + await synthtrace.index(getLogsForDataset({ to, count: 10, dataset: bitbucketDatasetName })); + + await PageObjects.datasetQuality.navigateTo(); + + await PageObjects.datasetQuality.openDatasetFlyout(bitbucketDatasetHumanName); + await PageObjects.datasetQuality.openIntegrationActionsMenu(); + + await testSubjects.missingOrFail( + PageObjects.datasetQuality.testSubjectSelectors.datasetQualityFlyoutIntegrationAction( + integrationActions.viewDashboards + ) + ); + }); + + it('Integration overview action should navigate to the integration overview page', async () => { + const bitbucketDatasetName = 'atlassian_bitbucket.audit'; + const bitbucketDatasetHumanName = 'Bitbucket Audit Logs'; + + await PageObjects.observabilityLogsExplorer.navigateTo(); + + // Add initial integrations + await PageObjects.observabilityLogsExplorer.installPackage({ + name: 'atlassian_bitbucket', + version: '1.14.0', + }); + + // Index 10 logs for `atlassian_bitbucket.audit` dataset + await synthtrace.index(getLogsForDataset({ to, count: 10, dataset: bitbucketDatasetName })); + + await PageObjects.datasetQuality.navigateTo(); + + await PageObjects.datasetQuality.openDatasetFlyout(bitbucketDatasetHumanName); + await PageObjects.datasetQuality.openIntegrationActionsMenu(); + + const action = await PageObjects.datasetQuality.getIntegrationActionButtonByAction( + integrationActions.overview + ); + + await action.click(); + + await retry.tryForTime(5000, async () => { + const currentUrl = await browser.getCurrentUrl(); + const parsedUrl = new URL(currentUrl); + + expect(parsedUrl.pathname).to.contain('/app/integrations/detail/atlassian_bitbucket'); + }); + }); + + it('Integration template action should navigate to the index template page', async () => { + const apacheAccessDatasetName = 'apache.access'; + const apacheAccessDatasetHumanName = 'Apache access logs'; + + await PageObjects.observabilityLogsExplorer.navigateTo(); + + // Add initial integrations + await PageObjects.observabilityLogsExplorer.setupInitialIntegrations(); + + // Index 10 logs for `logs-apache.access` dataset + await synthtrace.index( + getLogsForDataset({ to, count: 10, dataset: apacheAccessDatasetName }) + ); + + await PageObjects.datasetQuality.navigateTo(); + + await PageObjects.datasetQuality.openDatasetFlyout(apacheAccessDatasetHumanName); + await PageObjects.datasetQuality.openIntegrationActionsMenu(); + + const action = await PageObjects.datasetQuality.getIntegrationActionButtonByAction( + integrationActions.template + ); + + await action.click(); + + await retry.tryForTime(5000, async () => { + const currentUrl = await browser.getCurrentUrl(); + const parsedUrl = new URL(currentUrl); + expect(parsedUrl.pathname).to.contain( + `/app/management/data/index_management/templates/logs-${apacheAccessDatasetName}` + ); + }); + }); + + it('Integration dashboard action should navigate to the selected dashboard', async () => { + const apacheAccessDatasetName = 'apache.access'; + const apacheAccessDatasetHumanName = 'Apache access logs'; + + await PageObjects.observabilityLogsExplorer.navigateTo(); + + // Add initial integrations + await PageObjects.observabilityLogsExplorer.setupInitialIntegrations(); + + // Index 10 logs for `logs-apache.access` dataset + await synthtrace.index( + getLogsForDataset({ to, count: 10, dataset: apacheAccessDatasetName }) + ); + + await PageObjects.datasetQuality.navigateTo(); + + await PageObjects.datasetQuality.openDatasetFlyout(apacheAccessDatasetHumanName); + await PageObjects.datasetQuality.openIntegrationActionsMenu(); + + const action = await PageObjects.datasetQuality.getIntegrationActionButtonByAction( + integrationActions.viewDashboards + ); + + await action.click(); + + const dashboardButtons = await PageObjects.datasetQuality.getIntegrationDashboardButtons(); + const firstDashboardButton = await dashboardButtons[0]; + const dashboardText = await firstDashboardButton.getVisibleText(); + + await firstDashboardButton.click(); + + const breadcrumbText = await testSubjects.getVisibleText('breadcrumb last'); + + expect(breadcrumbText).to.eql(dashboardText); + }); }); } diff --git a/yarn.lock b/yarn.lock index e919f6749b997..e518c0672b74e 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1746,10 +1746,10 @@ resolved "https://registry.yarnpkg.com/@elastic/eslint-plugin-eui/-/eslint-plugin-eui-0.0.2.tgz#56b9ef03984a05cc213772ae3713ea8ef47b0314" integrity sha512-IoxURM5zraoQ7C8f+mJb9HYSENiZGgRVcG4tLQxE61yHNNRDXtGDWTZh8N1KIHcsqN1CEPETjuzBXkJYF/fDiQ== -"@elastic/eui@93.5.1": - version "93.5.1" - resolved "https://registry.yarnpkg.com/@elastic/eui/-/eui-93.5.1.tgz#e663017052a1461ec0c67e825f1d7c1346f3c3ee" - integrity sha512-40zFwuYakCDxzEDnh2ePgUh4KVfmy8p6sXc8FFES07zoboRt82YxQxwmgouqu02TZHaXir1bXSpmEyXe4T7CMg== +"@elastic/eui@93.5.2": + version "93.5.2" + resolved "https://registry.yarnpkg.com/@elastic/eui/-/eui-93.5.2.tgz#42bb985a960a7b2d46fe7e25846a476d30900b71" + integrity sha512-/1k7k7xYMPj9AM0OTgMilvKI6IdnVOPyQ7YQwE57NKrmBYfj9H91AKQNZ+rXze7QuuuFi73dUMSfbeE9ncqGZA== dependencies: "@hello-pangea/dnd" "^16.3.0" "@types/lodash" "^4.14.202" @@ -1767,8 +1767,8 @@ react-focus-on "^3.9.1" react-is "^17.0.2" react-remove-scroll-bar "^2.3.4" - react-virtualized-auto-sizer "^1.0.20" - react-window "^1.8.9" + react-virtualized-auto-sizer "^1.0.24" + react-window "^1.8.10" refractor "^3.6.0" rehype-raw "^5.1.0" rehype-react "^6.2.1" @@ -10349,13 +10349,6 @@ "@types/prop-types" "*" "@types/react" "*" -"@types/react-window@^1.8.5": - version "1.8.5" - resolved "https://registry.yarnpkg.com/@types/react-window/-/react-window-1.8.5.tgz#285fcc5cea703eef78d90f499e1457e9b5c02fc1" - integrity sha512-V9q3CvhC9Jk9bWBOysPGaWy/Z0lxYcTXLtLipkt2cnRj1JOSFNF7wqGpkScSXMgBwC+fnVRg/7shwgddBG5ICw== - dependencies: - "@types/react" "*" - "@types/react-window@^1.8.8": version "1.8.8" resolved "https://registry.yarnpkg.com/@types/react-window/-/react-window-1.8.8.tgz#c20645414d142364fbe735818e1c1e0a145696e3" @@ -26173,10 +26166,10 @@ react-use@^15.3.8: ts-easing "^0.2.0" tslib "^2.0.0" -react-virtualized-auto-sizer@^1.0.20: - version "1.0.20" - resolved "https://registry.yarnpkg.com/react-virtualized-auto-sizer/-/react-virtualized-auto-sizer-1.0.20.tgz#d9a907253a7c221c52fa57dc775a6ef40c182645" - integrity sha512-OdIyHwj4S4wyhbKHOKM1wLSj/UDXm839Z3Cvfg2a9j+He6yDa6i5p0qQvEiCnyQlGO/HyfSnigQwuxvYalaAXA== +react-virtualized-auto-sizer@^1.0.24: + version "1.0.24" + resolved "https://registry.yarnpkg.com/react-virtualized-auto-sizer/-/react-virtualized-auto-sizer-1.0.24.tgz#3ebdc92f4b05ad65693b3cc8e7d8dd54924c0227" + integrity sha512-3kCn7N9NEb3FlvJrSHWGQ4iVl+ydQObq2fHMn12i5wbtm74zHOPhz/i64OL3c1S1vi9i2GXtZqNqUJTQ+BnNfg== react-virtualized@^9.22.5: version "9.22.5" @@ -26190,10 +26183,10 @@ react-virtualized@^9.22.5: prop-types "^15.7.2" react-lifecycles-compat "^3.0.4" -react-window@^1.8.9: - version "1.8.9" - resolved "https://registry.yarnpkg.com/react-window/-/react-window-1.8.9.tgz#24bc346be73d0468cdf91998aac94e32bc7fa6a8" - integrity sha512-+Eqx/fj1Aa5WnhRfj9dJg4VYATGwIUP2ItwItiJ6zboKWA6EX3lYDAXfGF2hyNqplEprhbtjbipiADEcwQ823Q== +react-window@^1.8.10: + version "1.8.10" + resolved "https://registry.yarnpkg.com/react-window/-/react-window-1.8.10.tgz#9e6b08548316814b443f7002b1cf8fd3a1bdde03" + integrity sha512-Y0Cx+dnU6NLa5/EvoHukUD0BklJ8qITCtVEPY1C/nL8wwoZ0b5aEw8Ff1dOVHw7fCzMt55XfJDd8S8W8LCaUCg== dependencies: "@babel/runtime" "^7.0.0" memoize-one ">=3.1.1 <6" @@ -31187,9 +31180,9 @@ webpack-dev-middleware@^3.7.3: webpack-log "^2.0.0" webpack-dev-middleware@^5.3.1: - version "5.3.3" - resolved "https://registry.yarnpkg.com/webpack-dev-middleware/-/webpack-dev-middleware-5.3.3.tgz#efae67c2793908e7311f1d9b06f2a08dcc97e51f" - integrity sha512-hj5CYrY0bZLB+eTO+x/j67Pkrquiy7kWepMHmUMoPsmcUaeEnQJqFzHJOyxgWlq746/wUuA64p9ta34Kyb01pA== + version "5.3.4" + resolved "https://registry.yarnpkg.com/webpack-dev-middleware/-/webpack-dev-middleware-5.3.4.tgz#eb7b39281cbce10e104eb2b8bf2b63fce49a3517" + integrity sha512-BVdTqhhs+0IfoeAf7EoH5WE+exCmqGerHfDM0IL096Px60Tq2Mn9MAbnaGUe6HiMa41KMCYF19gyzZmBcq/o4Q== dependencies: colorette "^2.0.10" memfs "^3.4.3"